diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 01:14:29 +0000 |
commit | fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8 (patch) | |
tree | 4c1ccaf5486d4f2009f9a338a98a83e886e29c97 /third_party/jpeg-xl | |
parent | Releasing progress-linux version 124.0.1-1~progress7.99u1. (diff) | |
download | firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.tar.xz firefox-fbaf0bb26397aa498eb9156f06d5a6fe34dd7dd8.zip |
Merging upstream version 125.0.1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/jpeg-xl')
373 files changed, 6821 insertions, 5816 deletions
diff --git a/third_party/jpeg-xl/.clang-tidy b/third_party/jpeg-xl/.clang-tidy index 06c9875624..42d386276b 100644 --- a/third_party/jpeg-xl/.clang-tidy +++ b/third_party/jpeg-xl/.clang-tidy @@ -25,16 +25,31 @@ Checks: >- modernize-*, performance-*, readability-*, + -bugprone-narrowing-conversions, + -bugprone-branch-clone, + -bugprone-easily-swappable-parameters, + -bugprone-implicit-widening-of-multiplication-result, + -bugprone-infinite-loop, + -bugprone-unused-local-non-trivial-variable, + -modernize-avoid-c-arrays, -modernize-deprecated-headers, -modernize-return-braced-init-list, -modernize-use-auto, -modernize-use-default-member-init, -modernize-use-trailing-return-type, -modernize-use-using, + -performance-enum-size, + -readability-avoid-nested-conditional-operator, -readability-else-after-return, -readability-function-cognitive-complexity, + -readability-identifier-length, + -readability-magic-numbers, + -readability-redundant-access-specifiers, + -readability-simplify-boolean-expr, -readability-static-accessed-through-instance, + -readability-suspicious-call-argument, -readability-uppercase-literal-suffix, + -readability-use-anyofallof, WarningsAsErrors: >- @@ -57,11 +72,13 @@ WarningsAsErrors: >- HeaderFilterRegex: '^.*/(lib|tools)/.*\.h$' CheckOptions: - - key: readability-braces-around-statements.ShortStatementLines - value: '2' - - key: google-readability-braces-around-statements.ShortStatementLines - value: '2' - - key: readability-implicit-bool-conversion.AllowPointerConditions - value: '1' - - key: readability-implicit-bool-conversion.AllowIntegerConditions - value: '1' + - key: readability-braces-around-statements.ShortStatementLines + value: '2' + - key: google-readability-braces-around-statements.ShortStatementLines + value: '2' + - key: readability-implicit-bool-conversion.AllowPointerConditions + value: '1' + - key: readability-implicit-bool-conversion.AllowIntegerConditions + value: '1' + - key: bugprone-signed-char-misuse.CharTypdefsToIgnore + value: 'int8_t' diff --git a/third_party/jpeg-xl/AUTHORS b/third_party/jpeg-xl/AUTHORS index 04d436b8fe..ed6d72db66 100644 --- a/third_party/jpeg-xl/AUTHORS +++ b/third_party/jpeg-xl/AUTHORS @@ -32,6 +32,7 @@ Zoltan Szabadka <szabadka@google.com> # Individuals: a-shvedov Aditya Patadia <adityapatadia@users.noreply.github.com> +Ahmad Amsyar Asyadiq Bin Syaiful Bahri <27284123+Ahmad-Amsyar@users.noreply.github.com> Alex Xu (Hello71) <alex_y_xu@yahoo.ca> Alexander Sago <cagelight@gmail.com> Alistair Barrow @@ -74,6 +75,7 @@ Misaki Kasumi <misakikasumi@outlook.com> Moonchild Straver <moonchild@palemoon.org> Nicholas Hayes <0xC0000054@users.noreply.github.com> Nigel Tao <nigeltao@golang.org> +oupson <oupson1er@gmail.com> Petr Diblík Pieter Wuille roland-rollo @@ -93,4 +95,3 @@ xiota Yonatan Nebenzhal <yonatan.nebenzhl@gmail.com> Ziemowit Zabawa <ziemek.zabawa@outlook.com> 源文雨 <41315874+fumiama@users.noreply.github.com> -oupson <oupson1er@gmail.com> diff --git a/third_party/jpeg-xl/CHANGELOG.md b/third_party/jpeg-xl/CHANGELOG.md index add193bf67..3c62a1f2df 100644 --- a/third_party/jpeg-xl/CHANGELOG.md +++ b/third_party/jpeg-xl/CHANGELOG.md @@ -13,6 +13,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed / clarified +### Fixed + + +## [0.10.0] - 2024-02-21 + +### Added + - decoder API: added `JxlDecoderGetBoxSizeContents` for getting the size of the + content of a box without the headers. + - encoder API: implemented new api functions for streaming encoding. + +### Changed / clarified + - decoder/encoder API: return failure when surface allocation fail + - encoder API / cjxl: updated modular effort levels to faster settings; the + effort range is now 1-10, with 11 available in advanced mode. + +## [0.9.2] - 2024-02-07 + +### Fixed + - bugs in the gdk-pixbuf plugin + - some build issues + +## [0.9.1] - 2024-01-08 + +### Fixed + - multiple build issues + ## [0.9.0] - 2023-12-22 ### Added @@ -57,6 +83,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - fixed how large boxes are decoded (#2958) - fixed encoding files with unreadable patches (#3042, #3046) +## [0.8.2] - 2023-06-14 + +### Changed + - Security: Fix an integer underflow bug in patch decoding (#2551- CVE-2023-35790). + +## [0.8.1] - 2023-02-03 + +### Changed + - Allow fast-lossless for 16-bit float input (#2093) + - Fix bug in palette (#2120) + - Security: Fix OOB read in exif.h (#2101 - [CVE-2023-0645](https://www.cve.org/cverecord?id=CVE-2023-0645)) + ## [0.8.0] - 2023-01-18 ### Added diff --git a/third_party/jpeg-xl/CMakeLists.txt b/third_party/jpeg-xl/CMakeLists.txt index 85f3c265c2..9b74537f1c 100644 --- a/third_party/jpeg-xl/CMakeLists.txt +++ b/third_party/jpeg-xl/CMakeLists.txt @@ -161,13 +161,13 @@ set(JPEGXL_ENABLE_AVX512_SPR false CACHE BOOL set(JPEGXL_ENABLE_AVX512_ZEN4 false CACHE BOOL "Build with Zen4-optimized AVX512 support (faster on CPUs that support it, but larger binary size).") set(JPEGXL_ENABLE_WASM_TRHEADS true CACHE BOOL - "Builds WASM modules with threads suppurt") + "Builds WASM modules with threads support") # Force system dependencies. set(JPEGXL_FORCE_SYSTEM_BROTLI false CACHE BOOL "Force using system installed brotli instead of third_party/brotli source.") set(JPEGXL_FORCE_SYSTEM_GTEST false CACHE BOOL - "Force using system installed googletest (gtest/gmock) instead of third_party/googletest source.") + "Force using system installed googletest (gtest) instead of third_party/googletest source.") set(JPEGXL_FORCE_SYSTEM_LCMS2 false CACHE BOOL "Force using system installed lcms2 instead of third_party/lcms source.") set(JPEGXL_FORCE_SYSTEM_HWY false CACHE BOOL diff --git a/third_party/jpeg-xl/MODULE.bazel b/third_party/jpeg-xl/MODULE.bazel index c838384185..64a44711b3 100644 --- a/third_party/jpeg-xl/MODULE.bazel +++ b/third_party/jpeg-xl/MODULE.bazel @@ -1,4 +1,7 @@ bazel_dep(name = "bazel_skylib", version = "1.5.0") +bazel_dep(name = "giflib", version = "5.2.1") bazel_dep(name = "googletest", version = "1.14.0") -bazel_dep(name = "openexr", version = "3.2.1") +bazel_dep(name = "libjpeg_turbo", version = "2.1.91") bazel_dep(name = "libpng", version = "1.6.40") +bazel_dep(name = "libwebp", version = "1.3.2") +bazel_dep(name = "openexr", version = "3.2.1") diff --git a/third_party/jpeg-xl/README.md b/third_party/jpeg-xl/README.md index bcea13ff23..d4520fff21 100644 --- a/third_party/jpeg-xl/README.md +++ b/third_party/jpeg-xl/README.md @@ -73,11 +73,14 @@ To decode a JPEG XL file run: djxl input.jxl output.png ``` -When possible `cjxl`/`djxl` are able to read/write the following -image formats: .exr, .gif, .jpeg/.jpg, .pfm, .pgm/.ppm, .pgx, .png. +When possible, `cjxl`/`djxl` are able to read/write the following image formats: +OpenEXR (`.exr`), GIF (`.gif`), JPEG (`.jpg`/`.jpeg`), NetPBM (`.pam`/`.pgm`/`.ppm`), +Portable FloatMap (`.pfm`), PGX Test Format (`.pgx`), Portable Network Graphics (`.png`), +Animated PNG (`.png`/`.apng`), and JPEG XL itself (`.jxl`). + Specifically for JPEG files, the default `cjxl` behavior is to apply lossless recompression and the default `djxl` behavior is to reconstruct the original -JPEG file (when the extension of the output file is .jpg). +JPEG file (when the extension of the output file is `.jpg`). ### Benchmarking diff --git a/third_party/jpeg-xl/WORKSPACE b/third_party/jpeg-xl/WORKSPACE index 4e0e1326bb..1383b44e0d 100644 --- a/third_party/jpeg-xl/WORKSPACE +++ b/third_party/jpeg-xl/WORKSPACE @@ -30,185 +30,3 @@ cc_library( """, path = "third_party/skcms", ) - -new_git_repository( - name = "libjpeg_turbo", - build_file_content = """ -load("@bazel_skylib//rules:expand_template.bzl", "expand_template") -SUBSTITUTIONS = { - "@BUILD@" : "20230208", - "@CMAKE_PROJECT_NAME@" : "libjpeg-turbo", - "@COPYRIGHT_YEAR@" : "2023", - "@INLINE@" : "__inline__", - "@JPEG_LIB_VERSION@" : "62", - "@LIBJPEG_TURBO_VERSION_NUMBER@" : "2001091", - "@SIZE_T@" : "8", - "@THREAD_LOCAL@" : "__thread", - "@VERSION@" : "2.1.91", -} -YES_DEFINES = [ - "C_ARITH_CODING_SUPPORTED", "D_ARITH_CODING_SUPPORTED", - "HAVE_BUILTIN_CTZL", "MEM_SRCDST_SUPPORTED" -] -NO_DEFINES = [ - "WITH_SIMD", "RIGHT_SHIFT_IS_UNSIGNED", "HAVE_INTRIN_H" -] -SUBSTITUTIONS.update({ - "#cmakedefine " + key : "#define " + key for key in YES_DEFINES -}) -SUBSTITUTIONS.update({ - "#cmakedefine " + key : "// #define " + key for key in NO_DEFINES -}) -[ - expand_template( - name = "expand_" + src, - template = src + ".in", - out = src, - substitutions = SUBSTITUTIONS, - visibility = ["//visibility:public"], - ) for src in ["jconfig.h", "jconfigint.h", "jversion.h"] -] -JPEG16_SOURCES = [ - "jccolor.c", - "jcdiffct.c", - "jclossls.c", - "jcmainct.c", - "jcprepct.c", - "jcsample.c", - "jdcolor.c", - "jddiffct.c", - "jdlossls.c", - "jdmainct.c", - "jdmerge.c", - "jdpostct.c", - "jdsample.c", - "jquant1.c", - "jquant2.c", - "jutils.c", -] -JPEG12_SOURCES = JPEG16_SOURCES + [ - "jccoefct.c", - "jcdctmgr.c", - "jdcoefct.c", - "jddctmgr.c", - "jfdctfst.c", - "jfdctint.c", - "jidctflt.c", - "jidctfst.c", - "jidctint.c", - "jidctred.c", -] -JPEG_SOURCES = JPEG12_SOURCES + [ - "jaricom.c", - "jcapimin.c", - "jcapistd.c", - "jcarith.c", - "jchuff.c", - "jcicc.c", - "jcinit.c", - "jclhuff.c", - "jcmarker.c", - "jcmaster.c", - "jcomapi.c", - "jcparam.c", - "jcphuff.c", - "jdapimin.c", - "jdapistd.c", - "jdarith.c", - "jdatadst.c", - "jdatasrc.c", - "jdhuff.c", - "jdicc.c", - "jdinput.c", - "jdlhuff.c", - "jdmarker.c", - "jdmaster.c", - "jdphuff.c", - "jdtrans.c", - "jerror.c", - "jfdctflt.c", - "jmemmgr.c", - "jmemnobs.c", -] -JPEG_HEADERS = [ - "jccolext.c", - "jchuff.h", - "jcmaster.h", - "jconfig.h", - "jconfigint.h", - "jdcoefct.h", - "jdcol565.c", - "jdcolext.c", - "jdct.h", - "jdhuff.h", - "jdmainct.h", - "jdmaster.h", - "jdmerge.h", - "jdmrg565.c", - "jdmrgext.c", - "jdsample.h", - "jerror.h", - "jinclude.h", - "jlossls.h", - "jmemsys.h", - "jmorecfg.h", - "jpeg_nbits_table.h", - "jpegapicomp.h", - "jpegint.h", - "jpeglib.h", - "jsamplecomp.h", - "jsimd.h", - "jsimddct.h", - "jstdhuff.c", - "jversion.h", -] -cc_library( - name = "jpeg16", - srcs = JPEG16_SOURCES, - hdrs = JPEG_HEADERS, - local_defines = ["BITS_IN_JSAMPLE=16"], - visibility = ["//visibility:public"], -) -cc_library( - name = "jpeg12", - srcs = JPEG12_SOURCES, - hdrs = JPEG_HEADERS, - local_defines = ["BITS_IN_JSAMPLE=12"], - visibility = ["//visibility:public"], -) -cc_library( - name = "jpeg", - srcs = JPEG_SOURCES, - hdrs = JPEG_HEADERS, - deps = [":jpeg16", ":jpeg12"], - includes = ["."], - visibility = ["//visibility:public"], -) -exports_files([ - "jmorecfg.h", - "jpeglib.h", -]) - """, - remote = "https://github.com/libjpeg-turbo/libjpeg-turbo.git", - tag = "2.1.91", -) - -http_archive( - name = "gif", - build_file_content = """ -cc_library( - name = "gif", - srcs = [ - "dgif_lib.c", "egif_lib.c", "gifalloc.c", "gif_err.c", "gif_font.c", - "gif_hash.c", "openbsd-reallocarray.c", "gif_hash.h", - "gif_lib_private.h" - ], - hdrs = ["gif_lib.h"], - includes = ["."], - visibility = ["//visibility:public"], -) - """, - sha256 = "31da5562f44c5f15d63340a09a4fd62b48c45620cd302f77a6d9acf0077879bd", - strip_prefix = "giflib-5.2.1", - url = "https://netcologne.dl.sourceforge.net/project/giflib/giflib-5.2.1.tar.gz", -) diff --git a/third_party/jpeg-xl/bash_test.sh b/third_party/jpeg-xl/bash_test.sh index 9bd28f413b..d24ca976e6 100755 --- a/third_party/jpeg-xl/bash_test.sh +++ b/third_party/jpeg-xl/bash_test.sh @@ -106,12 +106,6 @@ test_printf_size_t() { ret=1 fi - if grep -n -E 'gmock\.h' \ - $(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$' | grep -v -F /testing.h); then - echo "Don't include gmock directly, instead include 'testing.h'. " >&2 - ret=1 - fi - local f for f in $(git ls-files | grep -E "\.cc$" | xargs grep 'PRI[udx]S' | cut -f 1 -d : | uniq); do diff --git a/third_party/jpeg-xl/ci.sh b/third_party/jpeg-xl/ci.sh index f33efd9693..448a7f9927 100755 --- a/third_party/jpeg-xl/ci.sh +++ b/third_party/jpeg-xl/ci.sh @@ -16,6 +16,8 @@ MYDIR=$(dirname $(realpath "$0")) ### Environment parameters: TEST_STACK_LIMIT="${TEST_STACK_LIMIT:-256}" +BENCHMARK_NUM_THREADS="${BENCHMARK_NUM_THREADS:-0}" +BUILD_CONFIG=${BUILD_CONFIG:-} CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-RelWithDebInfo} CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH:-} CMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER:-} @@ -79,6 +81,12 @@ if [[ "${ENABLE_WASM_SIMD}" -eq "2" ]]; then CMAKE_C_FLAGS="${CMAKE_C_FLAGS} -DHWY_WANT_WASM2" fi +if [[ -z "${BUILD_CONFIG}" ]]; then + TOOLS_DIR="${BUILD_DIR}/tools" +else + TOOLS_DIR="${BUILD_DIR}/tools/${BUILD_CONFIG}" +fi + if [[ ! -z "${HWY_BASELINE_TARGETS}" ]]; then CMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS} -DHWY_BASELINE_TARGETS=${HWY_BASELINE_TARGETS}" fi @@ -128,17 +136,17 @@ if [[ "${BUILD_TARGET%%-*}" != "arm" ]]; then ) fi -CLANG_TIDY_BIN=$(which clang-tidy-6.0 clang-tidy-7 clang-tidy-8 clang-tidy | head -n 1) +CLANG_TIDY_BIN=$(which clang-tidy-6.0 clang-tidy-7 clang-tidy-8 clang-tidy 2>/dev/null | head -n 1) # Default to "cat" if "colordiff" is not installed or if stdout is not a tty. if [[ -t 1 ]]; then - COLORDIFF_BIN=$(which colordiff cat | head -n 1) + COLORDIFF_BIN=$(which colordiff cat 2>/dev/null | head -n 1) else COLORDIFF_BIN="cat" fi -FIND_BIN=$(which gfind find | head -n 1) +FIND_BIN=$(which gfind find 2>/dev/null | head -n 1) # "false" will disable wine64 when not installed. This won't allow # cross-compiling. -WINE_BIN=$(which wine64 false | head -n 1) +WINE_BIN=$(which wine64 false 2>/dev/null | head -n 1) CLANG_VERSION="${CLANG_VERSION:-}" # Detect the clang version suffix and store it in CLANG_VERSION. For example, @@ -411,7 +419,7 @@ cmake_build_and_test() { if [[ "${PACK_TEST:-}" == "1" ]]; then (cd "${BUILD_DIR}" ${FIND_BIN} -name '*.cmake' -a '!' -path '*CMakeFiles*' - # gtest / gmock / gtest_main shared libs + # gtest / gtest_main shared libs ${FIND_BIN} lib/ -name 'libg*.so*' ${FIND_BIN} -type d -name tests -a '!' -path '*CMakeFiles*' ) | tar -C "${BUILD_DIR}" -cf "${BUILD_DIR}/tests.tar.xz" -T - \ @@ -791,9 +799,13 @@ cmd_ossfuzz_ninja() { cmd_fast_benchmark() { local small_corpus_tar="${BENCHMARK_CORPORA}/jyrki-full.tar" + local small_corpus_url="https://storage.googleapis.com/artifacts.jpegxl.appspot.com/corpora/jyrki-full.tar" mkdir -p "${BENCHMARK_CORPORA}" - curl --show-error -o "${small_corpus_tar}" -z "${small_corpus_tar}" \ - "https://storage.googleapis.com/artifacts.jpegxl.appspot.com/corpora/jyrki-full.tar" + if [ -f "${small_corpus_tar}" ]; then + curl --show-error -o "${small_corpus_tar}" -z "${small_corpus_tar}" "${small_corpus_url}" + else + curl --show-error -o "${small_corpus_tar}" "${small_corpus_url}" + fi local tmpdir=$(mktemp -d) CLEANUP_FILES+=("${tmpdir}") @@ -831,7 +843,7 @@ cmd_benchmark() { png_filename="${filename%.ppm}.png" png_filename=$(echo "${png_filename}" | tr '/' '_') sem --bg --id "${sem_id}" -j"${nprocs}" -- \ - "${BUILD_DIR}/tools/decode_and_encode" \ + "${TOOLS_DIR}/decode_and_encode" \ "${tmpdir}/${filename}" "${mode}" "${tmpdir}/${png_filename}" images+=( "${png_filename}" ) done < <(cd "${tmpdir}"; ${FIND_BIN} . -name '*.ppm' -type f) @@ -844,6 +856,8 @@ cmd_benchmark() { get_mem_available() { if [[ "${OS}" == "Darwin" ]]; then echo $(vm_stat | grep -F 'Pages free:' | awk '{print $3 * 4}') + elif [[ "${OS}" == MINGW* ]]; then + echo $(vmstat | tail -n 1 | awk '{print $4 * 4}') else echo $(grep -F MemAvailable: /proc/meminfo | awk '{print $2}') fi @@ -856,15 +870,24 @@ run_benchmark() { local output_dir="${BUILD_DIR}/benchmark_results" mkdir -p "${output_dir}" - # The memory available at the beginning of the benchmark run in kB. The number - # of threads depends on the available memory, and the passed memory per - # thread. We also add a 2 GiB of constant memory. - local mem_available="$(get_mem_available)" - # Check that we actually have a MemAvailable value. - [[ -n "${mem_available}" ]] - local num_threads=$(( (${mem_available} - 1048576) / ${mem_per_thread} )) - if [[ ${num_threads} -le 0 ]]; then - num_threads=1 + if [[ "${OS}" == MINGW* ]]; then + src_img_dir=`cygpath -w "${src_img_dir}"` + fi + + local num_threads=1 + if [[ ${BENCHMARK_NUM_THREADS} -gt 0 ]]; then + num_threads=${BENCHMARK_NUM_THREADS} + else + # The memory available at the beginning of the benchmark run in kB. The number + # of threads depends on the available memory, and the passed memory per + # thread. We also add a 2 GiB of constant memory. + local mem_available="$(get_mem_available)" + # Check that we actually have a MemAvailable value. + [[ -n "${mem_available}" ]] + num_threads=$(( (${mem_available} - 1048576) / ${mem_per_thread} )) + if [[ ${num_threads} -le 0 ]]; then + num_threads=1 + fi fi local benchmark_args=( @@ -873,20 +896,20 @@ run_benchmark() { --output_dir "${output_dir}" --show_progress --num_threads="${num_threads}" + --decode_reps=11 + --encode_reps=11 ) if [[ "${STORE_IMAGES}" == "1" ]]; then benchmark_args+=(--save_decompressed --save_compressed) fi ( [[ "${TEST_STACK_LIMIT}" == "none" ]] || ulimit -s "${TEST_STACK_LIMIT}" - "${BUILD_DIR}/tools/benchmark_xl" "${benchmark_args[@]}" | \ + "${TOOLS_DIR}/benchmark_xl" "${benchmark_args[@]}" | \ tee "${output_dir}/results.txt" # Check error code for benckmark_xl command. This will exit if not. return ${PIPESTATUS[0]} ) - - } # Helper function to wait for the CPU temperature to cool down on ARM. @@ -1027,7 +1050,7 @@ cmd_arm_benchmark() { local src_img for src_img in "${jpg_images[@]}" "${images[@]}"; do local src_img_hash=$(sha1sum "${src_img}" | cut -f 1 -d ' ') - local enc_binaries=("${BUILD_DIR}/tools/cjxl") + local enc_binaries=("${TOOLS_DIR}/cjxl") local src_ext="${src_img##*.}" for enc_binary in "${enc_binaries[@]}"; do local enc_binary_base=$(basename "${enc_binary}") @@ -1076,7 +1099,7 @@ cmd_arm_benchmark() { local dec_output wait_for_temp - dec_output=$("${BUILD_DIR}/tools/djxl" "${enc_file}" \ + dec_output=$("${TOOLS_DIR}/djxl" "${enc_file}" \ --num_reps=5 --num_threads="${num_threads}" 2>&1 | tee /dev/stderr | grep -E "M[BP]/s \[") local img_size=$(echo "${dec_output}" | cut -f 1 -d ',') @@ -1092,7 +1115,7 @@ cmd_arm_benchmark() { if [[ "${src_ext}" == "jpg" ]]; then wait_for_temp local dec_file="${BUILD_DIR}/arm_benchmark/${enc_file_hash}.jpg" - dec_output=$("${BUILD_DIR}/tools/djxl" "${enc_file}" \ + dec_output=$("${TOOLS_DIR}/djxl" "${enc_file}" \ "${dec_file}" --num_reps=5 --num_threads="${num_threads}" 2>&1 | \ tee /dev/stderr | grep -E "M[BP]/s \[") local jpeg_dec_mps_speed=$(_speed_from_output "${dec_output}") @@ -1122,12 +1145,12 @@ cmd_fuzz() { local fuzzer_crash_dir=$(realpath "${BUILD_DIR}/fuzzer_crash") mkdir -p "${corpus_dir}" "${fuzzer_crash_dir}" # Generate step. - "${BUILD_DIR}/tools/fuzzer_corpus" "${corpus_dir}" + "${TOOLS_DIR}/fuzzer_corpus" "${corpus_dir}" # Run step: local nprocs=$(nproc --all || echo 1) ( - cd "${BUILD_DIR}" - "tools/djxl_fuzzer" "${fuzzer_crash_dir}" "${corpus_dir}" \ + cd "${TOOLS_DIR}" + djxl_fuzzer "${fuzzer_crash_dir}" "${corpus_dir}" \ -max_total_time="${FUZZER_MAX_TIME}" -jobs=${nprocs} \ -artifact_prefix="${fuzzer_crash_dir}/" ) diff --git a/third_party/jpeg-xl/debian/control b/third_party/jpeg-xl/debian/control index f5dc5ce0cc..e0169a3488 100644 --- a/third_party/jpeg-xl/debian/control +++ b/third_party/jpeg-xl/debian/control @@ -11,7 +11,6 @@ Build-Depends: libgdk-pixbuf-2.0-dev | libgdk-pixbuf2.0-dev, libgif-dev, libgimp2.0-dev, - libgmock-dev, libgoogle-perftools-dev, libgtest-dev, libhwy-dev (>= 1.0.0), diff --git a/third_party/jpeg-xl/deps.sh b/third_party/jpeg-xl/deps.sh index 70b3a15f07..6c51a5cd4a 100755 --- a/third_party/jpeg-xl/deps.sh +++ b/third_party/jpeg-xl/deps.sh @@ -15,7 +15,7 @@ MYDIR=$(dirname $(realpath "$0")) # update a git submodule. TESTDATA="873045a9c42ed60721756e26e2a6b32e17415205" THIRD_PARTY_BROTLI="36533a866ed1ca4b75cf049f4521e4ec5fe24727" -THIRD_PARTY_HIGHWAY="ba0900a4957b929390ab73827235557959234fea" +THIRD_PARTY_HIGHWAY="58b52a717469e62b2d9b8eaa2f5dddb44d4a4cbf" THIRD_PARTY_SKCMS="42030a771244ba67f86b1c1c76a6493f873c5f91" THIRD_PARTY_SJPEG="e5ab13008bb214deb66d5f3e17ca2f8dbff150bf" THIRD_PARTY_ZLIB="51b7f2abdade71cd9bb0e7a373ef2610ec6f9daf" # v1.3.1 diff --git a/third_party/jpeg-xl/examples/CMakeLists.txt b/third_party/jpeg-xl/examples/CMakeLists.txt index aee5fd43ee..8cdb09217e 100644 --- a/third_party/jpeg-xl/examples/CMakeLists.txt +++ b/third_party/jpeg-xl/examples/CMakeLists.txt @@ -11,17 +11,15 @@ project(SAMPLE_LIBJXL LANGUAGES C CXX) # Use pkg-config to find libjxl. find_package(PkgConfig) -pkg_check_modules(Jxl REQUIRED IMPORTED_TARGET libjxl) -pkg_check_modules(JxlCms REQUIRED IMPORTED_TARGET libjxl_cms) -pkg_check_modules(JxlThreads REQUIRED IMPORTED_TARGET libjxl_threads) +pkg_check_modules(Jxl REQUIRED IMPORTED_TARGET libjxl libjxl_cms libjxl_threads) # Build the example encoder/decoder binaries using the default shared libraries # installed. add_executable(decode_oneshot decode_oneshot.cc) -target_link_libraries(decode_oneshot PkgConfig::Jxl PkgConfig::JxlCms PkgConfig::JxlThreads) +target_link_libraries(decode_oneshot PkgConfig::Jxl) add_executable(decode_progressive decode_progressive.cc) -target_link_libraries(decode_progressive PkgConfig::Jxl PkgConfig::JxlCms PkgConfig::JxlThreads) +target_link_libraries(decode_progressive PkgConfig::Jxl) add_executable(encode_oneshot encode_oneshot.cc) -target_link_libraries(encode_oneshot PkgConfig::Jxl PkgConfig::JxlCms PkgConfig::JxlThreads) +target_link_libraries(encode_oneshot PkgConfig::Jxl) diff --git a/third_party/jpeg-xl/examples/decode_exif_metadata.cc b/third_party/jpeg-xl/examples/decode_exif_metadata.cc index 8ec999e4f4..d5f11705bd 100644 --- a/third_party/jpeg-xl/examples/decode_exif_metadata.cc +++ b/third_party/jpeg-xl/examples/decode_exif_metadata.cc @@ -11,6 +11,7 @@ #include <jxl/decode_cxx.h> #include <limits.h> #include <stdint.h> +#include <stdio.h> #include <string.h> #include <vector> @@ -55,8 +56,9 @@ bool DecodeJpegXlExif(const uint8_t* jxl, size_t size, return true; } JxlBoxType type; - if (JXL_DEC_SUCCESS != - JxlDecoderGetBoxType(dec.get(), type, support_decompression)) { + status = JxlDecoderGetBoxType(dec.get(), type, + TO_JXL_BOOL(support_decompression)); + if (JXL_DEC_SUCCESS != status) { fprintf(stderr, "Error, failed to get box type\n"); return false; } diff --git a/third_party/jpeg-xl/examples/decode_oneshot.cc b/third_party/jpeg-xl/examples/decode_oneshot.cc index f8d21710bf..a24bd73bfb 100644 --- a/third_party/jpeg-xl/examples/decode_oneshot.cc +++ b/third_party/jpeg-xl/examples/decode_oneshot.cc @@ -7,11 +7,6 @@ // available at once). The example outputs the pixels and color information to a // floating point image and an ICC profile on disk. -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - -#include <inttypes.h> #include <jxl/codestream_header.h> #include <jxl/decode.h> #include <jxl/decode_cxx.h> @@ -102,13 +97,13 @@ bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size, return false; } if (buffer_size != *xsize * *ysize * 16) { - fprintf(stderr, "Invalid out buffer size %" PRIu64 " %" PRIu64 "\n", - static_cast<uint64_t>(buffer_size), - static_cast<uint64_t>(*xsize * *ysize * 16)); + fprintf(stderr, "Invalid out buffer size %d %d\n", + static_cast<int>(buffer_size), + static_cast<int>(*xsize * *ysize * 16)); return false; } pixels->resize(*xsize * *ysize * 4); - void* pixels_buffer = (void*)pixels->data(); + void* pixels_buffer = static_cast<void*>(pixels->data()); size_t pixels_buffer_size = pixels->size() * sizeof(float); if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, pixels_buffer, @@ -147,8 +142,8 @@ bool WritePFM(const char* filename, const float* pixels, size_t xsize, uint8_t little_endian[4]; memcpy(little_endian, &endian_test, 4); - fprintf(file, "PF\n%d %d\n%s\n", (int)xsize, (int)ysize, - little_endian[0] ? "-1.0" : "1.0"); + fprintf(file, "PF\n%d %d\n%s\n", static_cast<int>(xsize), + static_cast<int>(ysize), little_endian[0] ? "-1.0" : "1.0"); for (int y = ysize - 1; y >= 0; y--) { for (size_t x = 0; x < xsize; x++) { for (size_t c = 0; c < 3; c++) { @@ -233,7 +228,8 @@ int main(int argc, char* argv[]) { std::vector<float> pixels; std::vector<uint8_t> icc_profile; - size_t xsize = 0, ysize = 0; + size_t xsize = 0; + size_t ysize = 0; if (!DecodeJpegXlOneShot(jxl.data(), jxl.size(), &pixels, &xsize, &ysize, &icc_profile)) { fprintf(stderr, "Error while decoding the jxl file\n"); diff --git a/third_party/jpeg-xl/examples/decode_progressive.cc b/third_party/jpeg-xl/examples/decode_progressive.cc index a094cbeb4f..fa7f3df663 100644 --- a/third_party/jpeg-xl/examples/decode_progressive.cc +++ b/third_party/jpeg-xl/examples/decode_progressive.cc @@ -6,10 +6,6 @@ // This C++ example decodes a JPEG XL image progressively (input bytes are // passed in chunks). The example outputs the intermediate steps to PAM files. -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - #include <inttypes.h> #include <jxl/decode.h> #include <jxl/decode_cxx.h> @@ -29,10 +25,9 @@ bool WritePAM(const char* filename, const uint8_t* buffer, size_t w, size_t h) { return false; } fprintf(fp, - "P7\nWIDTH %" PRIu64 "\nHEIGHT %" PRIu64 - "\nDEPTH 4\nMAXVAL 255\nTUPLTYPE " + "P7\nWIDTH %d\nHEIGHT %d\nDEPTH 4\nMAXVAL 255\nTUPLTYPE " "RGB_ALPHA\nENDHDR\n", - static_cast<uint64_t>(w), static_cast<uint64_t>(h)); + static_cast<int>(w), static_cast<int>(h)); size_t num_bytes = w * h * 4; if (fwrite(buffer, 1, num_bytes, fp) != num_bytes) { fclose(fp); @@ -51,7 +46,8 @@ bool DecodeJpegXlProgressive(const uint8_t* jxl, size_t size, const char* filename, size_t chunksize) { std::vector<uint8_t> pixels; std::vector<uint8_t> icc_profile; - size_t xsize = 0, ysize = 0; + size_t xsize = 0; + size_t ysize = 0; // Multi-threaded parallel runner. auto runner = JxlResizableParallelRunnerMake(nullptr); diff --git a/third_party/jpeg-xl/examples/encode_oneshot.cc b/third_party/jpeg-xl/examples/encode_oneshot.cc index 336446ff17..1582570432 100644 --- a/third_party/jpeg-xl/examples/encode_oneshot.cc +++ b/third_party/jpeg-xl/examples/encode_oneshot.cc @@ -39,8 +39,9 @@ bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize, return false; } uint32_t endian_test = 1; - uint8_t little_endian[4]; - memcpy(little_endian, &endian_test, 4); + uint8_t little_endian_check[4]; + memcpy(little_endian_check, &endian_test, 4); + bool little_endian = (little_endian_check[0] == 1); if (fseek(file, 0, SEEK_END) != 0) { fclose(file); @@ -63,7 +64,7 @@ bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize, data.resize(size); size_t readsize = fread(data.data(), 1, size, file); - if ((long)readsize != size) { + if (static_cast<long>(readsize) != size) { fclose(file); return false; } @@ -116,12 +117,13 @@ bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize, fprintf(stderr, "%s doesn't seem to be a Portable FloatMap file (pixel data bytes " "are %d, but expected %d * %d * 3 * 4 + %d (%d).\n", - filename, (int)data.size(), (int)*ysize, (int)*xsize, (int)offset, - (int)(*ysize * *xsize * 3 * 4 + offset)); + filename, static_cast<int>(data.size()), static_cast<int>(*ysize), + static_cast<int>(*xsize), static_cast<int>(offset), + static_cast<int>(*ysize * *xsize * 3 * 4 + offset)); return false; } - if (!!little_endian[0] != input_little_endian) { + if (little_endian != input_little_endian) { fprintf(stderr, "%s has a different endianness than we do, conversion is not " "supported.\n", @@ -132,7 +134,7 @@ bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize, pixels->resize(*ysize * *xsize * 3); for (int y = *ysize - 1; y >= 0; y--) { - for (int x = 0; x < (int)*xsize; x++) { + for (int x = 0; x < static_cast<int>(*xsize); x++) { for (int c = 0; c < 3; c++) { memcpy(pixels->data() + (y * *xsize + x) * 3 + c, data.data() + offset, sizeof(float)); @@ -180,8 +182,8 @@ bool EncodeJxlOneshot(const std::vector<float>& pixels, const uint32_t xsize, } JxlColorEncoding color_encoding = {}; - JxlColorEncodingSetToSRGB(&color_encoding, - /*is_gray=*/pixel_format.num_channels < 3); + JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3); + JxlColorEncodingSetToSRGB(&color_encoding, is_gray); if (JXL_ENC_SUCCESS != JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) { fprintf(stderr, "JxlEncoderSetColorEncoding failed\n"); @@ -193,7 +195,7 @@ bool EncodeJxlOneshot(const std::vector<float>& pixels, const uint32_t xsize, if (JXL_ENC_SUCCESS != JxlEncoderAddImageFrame(frame_settings, &pixel_format, - (void*)pixels.data(), + static_cast<const void*>(pixels.data()), sizeof(float) * pixels.size())) { fprintf(stderr, "JxlEncoderAddImageFrame failed\n"); return false; diff --git a/third_party/jpeg-xl/flake.nix b/third_party/jpeg-xl/flake.nix index 4832f5b144..64a36311ab 100644 --- a/third_party/jpeg-xl/flake.nix +++ b/third_party/jpeg-xl/flake.nix @@ -19,7 +19,6 @@ cmake pkg-config gtest - gmock doxygen graphviz python3 diff --git a/third_party/jpeg-xl/lib/BUILD b/third_party/jpeg-xl/lib/BUILD index 39e61de0eb..d93858f22d 100644 --- a/third_party/jpeg-xl/lib/BUILD +++ b/third_party/jpeg-xl/lib/BUILD @@ -23,8 +23,8 @@ load( "libjxl_enc_sources", "libjxl_extras_for_tools_sources", "libjxl_extras_sources", - #'libjxl_gbench_sources', - "libjxl_jpegli_lib_version", + # "libjxl_gbench_sources", + # "libjxl_jpegli_lib_version", "libjxl_jpegli_libjpeg_helper_files", "libjxl_jpegli_sources", "libjxl_jpegli_testlib_files", @@ -51,13 +51,14 @@ load( "libjxl_deps_png", "libjxl_deps_runfiles", "libjxl_deps_skcms", - "libjxl_deps_testdata", + # "libjxl_deps_testdata", + # "libjxl_deps_webp", "libjxl_root_package", "libjxl_test_shards", "libjxl_test_timeouts", ) -load("@bazel_skylib//rules:expand_template.bzl", "expand_template") load("@bazel_skylib//rules:copy_file.bzl", "copy_file") +load("@bazel_skylib//rules:expand_template.bzl", "expand_template") DEFAULT_VISIBILITY = ["//:__subpackages__"] @@ -66,7 +67,7 @@ DEFAULT_COMPATIBILITY = [] INCLUDES_DIR = "include" package( - default_visibility = ["//:__subpackages__"], + default_visibility = DEFAULT_VISIBILITY, ) licenses(["notice"]) diff --git a/third_party/jpeg-xl/lib/CMakeLists.txt b/third_party/jpeg-xl/lib/CMakeLists.txt index 938f15dcbb..1e6f4abf74 100644 --- a/third_party/jpeg-xl/lib/CMakeLists.txt +++ b/third_party/jpeg-xl/lib/CMakeLists.txt @@ -149,6 +149,16 @@ else() set(PKGCONFIG_TARGET_LIBS "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") endif() +include(CheckCXXSymbolExists) +set(PKGCONFIG_CXX_LIB "") +check_cxx_symbol_exists(__GLIBCXX__ iostream LIBSTDCXX) +check_cxx_symbol_exists(_LIBCPP_VERSION iostream LIBCXX) +if(LIBSTDCXX) + set(PKGCONFIG_CXX_LIB "-lstdc++") +elseif(LIBCXX) + set(PKGCONFIG_CXX_LIB "-lc++") +endif() + # The jxl_cms library definition. include(jxl_cms.cmake) # The jxl library definition. diff --git a/third_party/jpeg-xl/lib/extras/alpha_blend.cc b/third_party/jpeg-xl/lib/extras/alpha_blend.cc index 50c141c6fb..afb37b0a7d 100644 --- a/third_party/jpeg-xl/lib/extras/alpha_blend.cc +++ b/third_party/jpeg-xl/lib/extras/alpha_blend.cc @@ -12,7 +12,7 @@ namespace extras { namespace { -void AlphaBlend(PackedFrame* frame, float background[3]) { +void AlphaBlend(PackedFrame* frame, const float background[3]) { if (!frame) return; const PackedImage& im = frame->color; JxlPixelFormat format = im.format; @@ -20,7 +20,8 @@ void AlphaBlend(PackedFrame* frame, float background[3]) { return; } --format.num_channels; - PackedImage blended(im.xsize, im.ysize, format); + JXL_ASSIGN_OR_DIE(PackedImage blended, + PackedImage::Create(im.xsize, im.ysize, format)); // TODO(szabadka) SIMDify this and make it work for float16. for (size_t y = 0; y < im.ysize; ++y) { for (size_t x = 0; x < im.xsize; ++x) { @@ -48,7 +49,7 @@ void AlphaBlend(PackedFrame* frame, float background[3]) { } // namespace -void AlphaBlend(PackedPixelFile* ppf, float background[3]) { +void AlphaBlend(PackedPixelFile* ppf, const float background[3]) { if (!ppf || ppf->info.alpha_bits == 0) { return; } diff --git a/third_party/jpeg-xl/lib/extras/alpha_blend.h b/third_party/jpeg-xl/lib/extras/alpha_blend.h index 4d78e8681b..e49349393c 100644 --- a/third_party/jpeg-xl/lib/extras/alpha_blend.h +++ b/third_party/jpeg-xl/lib/extras/alpha_blend.h @@ -11,7 +11,7 @@ namespace jxl { namespace extras { -void AlphaBlend(PackedPixelFile* ppf, float background[3]); +void AlphaBlend(PackedPixelFile* ppf, const float background[3]); } // namespace extras } // namespace jxl diff --git a/third_party/jpeg-xl/lib/extras/codec.h b/third_party/jpeg-xl/lib/extras/codec.h index b7bc2a397e..9fd61503cb 100644 --- a/third_party/jpeg-xl/lib/extras/codec.h +++ b/third_party/jpeg-xl/lib/extras/codec.h @@ -43,7 +43,7 @@ JXL_INLINE Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io, orig_codec); } -Status Encode(const extras::PackedPixelFile& ppf, const extras::Codec codec, +Status Encode(const extras::PackedPixelFile& ppf, extras::Codec codec, std::vector<uint8_t>* bytes, ThreadPool* pool); Status Encode(const extras::PackedPixelFile& ppf, const std::string& pathname, diff --git a/third_party/jpeg-xl/lib/extras/codec_test.cc b/third_party/jpeg-xl/lib/extras/codec_test.cc index 4d8a60cb33..50d5e37bc9 100644 --- a/third_party/jpeg-xl/lib/extras/codec_test.cc +++ b/third_party/jpeg-xl/lib/extras/codec_test.cc @@ -40,12 +40,6 @@ using test::ThreadPoolForTests; namespace extras { namespace { -using ::testing::AllOf; -using ::testing::Contains; -using ::testing::Field; -using ::testing::IsEmpty; -using ::testing::SizeIs; - std::string ExtensionFromCodec(Codec codec, const bool is_gray, const bool has_alpha, const size_t bits_per_sample) { @@ -78,8 +72,8 @@ void VerifySameImage(const PackedImage& im0, size_t bits_per_sample0, }; double factor0 = get_factor(im0.format, bits_per_sample0); double factor1 = get_factor(im1.format, bits_per_sample1); - auto pixels0 = static_cast<const uint8_t*>(im0.pixels()); - auto pixels1 = static_cast<const uint8_t*>(im1.pixels()); + const auto* pixels0 = static_cast<const uint8_t*>(im0.pixels()); + const auto* pixels1 = static_cast<const uint8_t*>(im1.pixels()); auto rgba0 = test::ConvertToRGBA32(pixels0, im0.xsize, im0.ysize, im0.format, factor0); auto rgba1 = @@ -227,19 +221,23 @@ void CreateTestImage(const TestImageParams& params, PackedPixelFile* ppf) { ppf->info.exponent_bits_per_sample = params.bits_per_sample == 32 ? 8 : 0; ppf->info.num_color_channels = params.is_gray ? 1 : 3; ppf->info.alpha_bits = params.add_alpha ? params.bits_per_sample : 0; - ppf->info.alpha_premultiplied = (params.codec == Codec::kEXR); + ppf->info.alpha_premultiplied = TO_JXL_BOOL(params.codec == Codec::kEXR); JxlColorEncoding color_encoding = CreateTestColorEncoding(params.is_gray); ppf->icc = GenerateICC(color_encoding); ppf->color_encoding = color_encoding; - PackedFrame frame(params.xsize, params.ysize, params.PixelFormat()); + JXL_ASSIGN_OR_DIE( + PackedFrame frame, + PackedFrame::Create(params.xsize, params.ysize, params.PixelFormat())); FillPackedImage(params.bits_per_sample, &frame.color); if (params.add_extra_channels) { for (size_t i = 0; i < 7; ++i) { JxlPixelFormat ec_format = params.PixelFormat(); ec_format.num_channels = 1; - PackedImage ec(params.xsize, params.ysize, ec_format); + JXL_ASSIGN_OR_DIE( + PackedImage ec, + PackedImage::Create(params.xsize, params.ysize, ec_format)); FillPackedImage(params.bits_per_sample, &ec); frame.extra_channels.emplace_back(std::move(ec)); PackedExtraChannel pec; @@ -432,15 +430,17 @@ TEST(CodecTest, EncodeToPNG) { ASSERT_TRUE(extras::DecodeBytes(Bytes(original_png), ColorHints(), &ppf)); const JxlPixelFormat& format = ppf.frames.front().color.format; - ASSERT_THAT( - png_encoder->AcceptedFormats(), - Contains(AllOf(Field(&JxlPixelFormat::num_channels, format.num_channels), - Field(&JxlPixelFormat::data_type, format.data_type), - Field(&JxlPixelFormat::endianness, format.endianness)))); + const auto& format_matcher = [&format](const JxlPixelFormat& candidate) { + return (candidate.num_channels == format.num_channels) && + (candidate.data_type == format.data_type) && + (candidate.endianness == format.endianness); + }; + const auto formats = png_encoder->AcceptedFormats(); + ASSERT_TRUE(std::any_of(formats.begin(), formats.end(), format_matcher)); EncodedImage encoded_png; ASSERT_TRUE(png_encoder->Encode(ppf, &encoded_png, pool)); - EXPECT_THAT(encoded_png.icc, IsEmpty()); - ASSERT_THAT(encoded_png.bitstreams, SizeIs(1)); + EXPECT_TRUE(encoded_png.icc.empty()); + ASSERT_EQ(encoded_png.bitstreams.size(), 1); PackedPixelFile decoded_ppf; ASSERT_TRUE(extras::DecodeBytes(Bytes(encoded_png.bitstreams.front()), diff --git a/third_party/jpeg-xl/lib/extras/dec/apng.cc b/third_party/jpeg-xl/lib/extras/dec/apng.cc index f77dab77d1..8b0da06eb1 100644 --- a/third_party/jpeg-xl/lib/extras/dec/apng.cc +++ b/third_party/jpeg-xl/lib/extras/dec/apng.cc @@ -71,9 +71,7 @@ const png_byte kIgnoredPngChunks[] = { }; // Returns floating-point value from the PNG encoding (times 10^5). -static double F64FromU32(const uint32_t x) { - return static_cast<int32_t>(x) * 1E-5; -} +double F64FromU32(const uint32_t x) { return static_cast<int32_t>(x) * 1E-5; } Status DecodeSRGB(const unsigned char* payload, const size_t payload_size, JxlColorEncoding* color_encoding) { @@ -402,7 +400,8 @@ class BlobsReaderPNG { } if (pos + 2 >= encoded_end) return false; // Truncated base16 2; - uint32_t nibble0, nibble1; + uint32_t nibble0; + uint32_t nibble1; JXL_RETURN_IF_ERROR(DecodeNibble(pos[0], &nibble0)); JXL_RETURN_IF_ERROR(DecodeNibble(pos[1], &nibble1)); bytes->push_back(static_cast<uint8_t>((nibble0 << 4) + nibble1)); @@ -432,9 +431,22 @@ constexpr uint32_t kId_cHRM = 0x4D524863; constexpr uint32_t kId_eXIf = 0x66495865; struct APNGFrame { - std::vector<uint8_t> pixels; + APNGFrame() : pixels(nullptr, free) {} + std::unique_ptr<void, decltype(free)*> pixels; + size_t pixels_size = 0; std::vector<uint8_t*> rows; unsigned int w, h, delay_num, delay_den; + Status Resize(size_t new_size) { + if (new_size > pixels_size) { + pixels.reset(malloc(new_size)); + if (!pixels.get()) { + // TODO(szabadka): use specialized OOM error code + return JXL_FAILURE("Failed to allocate memory for image buffer"); + } + pixels_size = new_size; + } + return true; + } }; struct Reader { @@ -447,7 +459,7 @@ struct Reader { next += to_copy; return (len == to_copy); } - bool Eof() { return next == last; } + bool Eof() const { return next == last; } }; const unsigned long cMaxPNGSize = 1000000UL; @@ -463,10 +475,11 @@ void info_fn(png_structp png_ptr, png_infop info_ptr) { void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { - APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr); + APNGFrame* frame = + reinterpret_cast<APNGFrame*>(png_get_progressive_ptr(png_ptr)); JXL_CHECK(frame); JXL_CHECK(row_num < frame->rows.size()); - JXL_CHECK(frame->rows[row_num] < frame->pixels.data() + frame->pixels.size()); + JXL_CHECK(frame->rows[row_num] < frame->rows[0] + frame->pixels_size); png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row); } @@ -494,12 +507,13 @@ int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr, unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10}; // Cleanup prior decoder, if any. - png_destroy_read_struct(&png_ptr, &info_ptr, 0); + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); // Just in case. Not all versions on libpng wipe-out the pointers. png_ptr = nullptr; info_ptr = nullptr; - png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_ptr = + png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); info_ptr = png_create_info_struct(png_ptr); if (!png_ptr || !info_ptr) return 1; @@ -508,18 +522,17 @@ int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr, } png_set_keep_unknown_chunks(png_ptr, 1, kIgnoredPngChunks, - (int)sizeof(kIgnoredPngChunks) / 5); + static_cast<int>(sizeof(kIgnoredPngChunks) / 5)); png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE); - png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, NULL); + png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, nullptr); png_process_data(png_ptr, info_ptr, header, 8); png_process_data(png_ptr, info_ptr, chunkIHDR.data(), chunkIHDR.size()); if (hasInfo) { - for (unsigned int i = 0; i < chunksInfo.size(); i++) { - png_process_data(png_ptr, info_ptr, chunksInfo[i].data(), - chunksInfo[i].size()); + for (auto& chunk : chunksInfo) { + png_process_data(png_ptr, info_ptr, chunk.data(), chunk.size()); } } return 0; @@ -575,8 +588,6 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, const SizeConstraints* constraints) { #if JPEGXL_ENABLE_APNG Reader r; - unsigned int id, j, w, h, w0, h0, x0, y0; - unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize; unsigned char sig[8]; png_structp png_ptr = nullptr; png_infop info_ptr = nullptr; @@ -588,7 +599,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, bool seenFctl = false; APNGFrame frameRaw = {}; uint32_t num_channels; - JxlPixelFormat format; + JxlPixelFormat format = {}; unsigned int bytes_per_pixel = 0; struct FrameInfo { @@ -604,7 +615,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, // Make sure png memory is released in any case. auto scope_guard = MakeScopeGuard([&]() { - png_destroy_read_struct(&png_ptr, &info_ptr, 0); + png_destroy_read_struct(&png_ptr, &info_ptr, nullptr); // Just in case. Not all versions on libpng wipe-out the pointers. png_ptr = nullptr; info_ptr = nullptr; @@ -616,7 +627,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, if (!r.Read(sig, 8) || memcmp(sig, png_signature, 8) != 0) { return false; } - id = read_chunk(&r, &chunkIHDR); + unsigned int id = read_chunk(&r, &chunkIHDR); ppf->info.exponent_bits_per_sample = 0; ppf->info.alpha_exponent_bits = 0; @@ -625,18 +636,22 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, ppf->frames.clear(); bool have_color = false; - bool have_cicp = false, have_iccp = false, have_srgb = false; + bool have_cicp = false; + bool have_iccp = false; + bool have_srgb = false; bool errorstate = true; if (id == kId_IHDR && chunkIHDR.size() == 25) { - x0 = 0; - y0 = 0; - delay_num = 1; - delay_den = 10; - dop = 0; - bop = 0; - - w0 = w = png_get_uint_32(chunkIHDR.data() + 8); - h0 = h = png_get_uint_32(chunkIHDR.data() + 12); + unsigned int x0 = 0; + unsigned int y0 = 0; + unsigned int delay_num = 1; + unsigned int delay_den = 10; + unsigned int dop = 0; + unsigned int bop = 0; + + unsigned int w = png_get_uint_32(chunkIHDR.data() + 8); + unsigned int h = png_get_uint_32(chunkIHDR.data() + 12); + unsigned int w0 = w; + unsigned int h0 = h; if (w > cMaxPNGSize || h > cMaxPNGSize) { return false; } @@ -648,8 +663,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB; ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_RELATIVE; - if (!processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo, - chunkIHDR, chunksInfo)) { + if (!processing_start(png_ptr, info_ptr, static_cast<void*>(&frameRaw), + hasInfo, chunkIHDR, chunksInfo)) { while (!r.Eof()) { id = read_chunk(&r, &chunk); if (!id) break; @@ -657,7 +672,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, if (id == kId_acTL && !hasInfo && !isAnimated) { isAnimated = true; - ppf->info.have_animation = true; + ppf->info.have_animation = JXL_TRUE; ppf->info.animation.tps_numerator = 1000; ppf->info.animation.tps_denominator = 1; } else if (id == kId_IEND || @@ -666,8 +681,10 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, if (!processing_finish(png_ptr, info_ptr, &ppf->metadata)) { // Allocates the frame buffer. uint32_t duration = delay_num * 1000 / delay_den; - frames.push_back(FrameInfo{PackedImage(w0, h0, format), duration, - x0, w0, y0, h0, dop, bop}); + JXL_ASSIGN_OR_RETURN(PackedImage image, + PackedImage::Create(w0, h0, format)); + frames.push_back(FrameInfo{std::move(image), duration, x0, w0, y0, + h0, dop, bop}); auto& frame = frames.back().data; for (size_t y = 0; y < h0; ++y) { memcpy(static_cast<uint8_t*>(frame.pixels()) + frame.stride * y, @@ -707,7 +724,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, if (hasInfo) { memcpy(chunkIHDR.data() + 8, chunk.data() + 12, 8); - if (processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo, + if (processing_start(png_ptr, info_ptr, + static_cast<void*>(&frameRaw), hasInfo, chunkIHDR, chunksInfo)) { break; } @@ -724,7 +742,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, int colortype = png_get_color_type(png_ptr, info_ptr); int png_bit_depth = png_get_bit_depth(png_ptr, info_ptr); ppf->info.bits_per_sample = png_bit_depth; - png_color_8p sigbits = NULL; + png_color_8p sigbits = nullptr; png_get_sBIT(png_ptr, info_ptr, &sigbits); if (colortype & 1) { // palette will actually be 8-bit regardless of the index bitdepth @@ -784,12 +802,18 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, } bytes_per_pixel = num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1); - rowbytes = w * bytes_per_pixel; - imagesize = h * rowbytes; - frameRaw.pixels.resize(imagesize); + size_t rowbytes = w * bytes_per_pixel; + if (h > std::numeric_limits<size_t>::max() / rowbytes) { + return JXL_FAILURE("Image too big."); + } + size_t imagesize = h * rowbytes; + JXL_RETURN_IF_ERROR(frameRaw.Resize(imagesize)); frameRaw.rows.resize(h); - for (j = 0; j < h; j++) - frameRaw.rows[j] = frameRaw.pixels.data() + j * rowbytes; + for (size_t j = 0; j < h; j++) { + frameRaw.rows[j] = + reinterpret_cast<uint8_t*>(frameRaw.pixels.get()) + + j * rowbytes; + } if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) { break; @@ -813,6 +837,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, have_cicp = true; have_color = true; ppf->icc.clear(); + ppf->primary_color_representation = + PackedPixelFile::kColorEncodingIsPrimary; } } else if (!have_cicp && id == kId_iCCP) { if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) { @@ -830,6 +856,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, &profile, &proflen); if (ok && proflen) { ppf->icc.assign(profile, profile + proflen); + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; have_color = true; have_iccp = true; } else { @@ -922,7 +949,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, py0 + pys >= y0 + ysize && use_for_next_frame) { // If the new frame is contained within the old frame, we can pad the // new frame with zeros and not blend. - PackedImage new_data(pxs, pys, frame.data.format); + JXL_ASSIGN_OR_RETURN(PackedImage new_data, + PackedImage::Create(pxs, pys, frame.data.format)); memset(new_data.pixels(), 0, new_data.pixels_size); for (size_t y = 0; y < ysize; y++) { size_t bytes_per_pixel = @@ -944,7 +972,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, ppf->frames.emplace_back(std::move(new_data)); } else { // If all else fails, insert a placeholder blank frame with kReplace. - PackedImage blank(pxs, pys, frame.data.format); + JXL_ASSIGN_OR_RETURN(PackedImage blank, + PackedImage::Create(pxs, pys, frame.data.format)); memset(blank.pixels(), 0, blank.pixels_size); ppf->frames.emplace_back(std::move(blank)); auto& pframe = ppf->frames.back(); @@ -984,7 +1013,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes, has_nontrivial_background && frame.dispose_op == DISPOSE_OP_BACKGROUND; } if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded"); - ppf->frames.back().frame_info.is_last = true; + ppf->frames.back().frame_info.is_last = JXL_TRUE; return true; #else diff --git a/third_party/jpeg-xl/lib/extras/dec/color_description.cc b/third_party/jpeg-xl/lib/extras/dec/color_description.cc index 54f6aa4206..bf229632d0 100644 --- a/third_party/jpeg-xl/lib/extras/dec/color_description.cc +++ b/third_party/jpeg-xl/lib/extras/dec/color_description.cc @@ -9,6 +9,8 @@ #include <cmath> +#include "lib/jxl/base/common.h" + namespace jxl { namespace { @@ -19,49 +21,46 @@ struct EnumName { T value; }; -const EnumName<JxlColorSpace> kJxlColorSpaceNames[] = { - {"RGB", JXL_COLOR_SPACE_RGB}, - {"Gra", JXL_COLOR_SPACE_GRAY}, - {"XYB", JXL_COLOR_SPACE_XYB}, - {"CS?", JXL_COLOR_SPACE_UNKNOWN}, -}; - -const EnumName<JxlWhitePoint> kJxlWhitePointNames[] = { - {"D65", JXL_WHITE_POINT_D65}, - {"Cst", JXL_WHITE_POINT_CUSTOM}, - {"EER", JXL_WHITE_POINT_E}, - {"DCI", JXL_WHITE_POINT_DCI}, -}; - -const EnumName<JxlPrimaries> kJxlPrimariesNames[] = { - {"SRG", JXL_PRIMARIES_SRGB}, - {"Cst", JXL_PRIMARIES_CUSTOM}, - {"202", JXL_PRIMARIES_2100}, - {"DCI", JXL_PRIMARIES_P3}, -}; - -const EnumName<JxlTransferFunction> kJxlTransferFunctionNames[] = { - {"709", JXL_TRANSFER_FUNCTION_709}, - {"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN}, - {"Lin", JXL_TRANSFER_FUNCTION_LINEAR}, - {"SRG", JXL_TRANSFER_FUNCTION_SRGB}, - {"PeQ", JXL_TRANSFER_FUNCTION_PQ}, - {"DCI", JXL_TRANSFER_FUNCTION_DCI}, - {"HLG", JXL_TRANSFER_FUNCTION_HLG}, - {"", JXL_TRANSFER_FUNCTION_GAMMA}, -}; - -const EnumName<JxlRenderingIntent> kJxlRenderingIntentNames[] = { - {"Per", JXL_RENDERING_INTENT_PERCEPTUAL}, - {"Rel", JXL_RENDERING_INTENT_RELATIVE}, - {"Sat", JXL_RENDERING_INTENT_SATURATION}, - {"Abs", JXL_RENDERING_INTENT_ABSOLUTE}, -}; - -template <typename T> -Status ParseEnum(const std::string& token, const EnumName<T>* enum_values, - size_t enum_len, T* value) { - for (size_t i = 0; i < enum_len; i++) { +constexpr auto kJxlColorSpaceNames = + to_array<EnumName<JxlColorSpace>>({{"RGB", JXL_COLOR_SPACE_RGB}, + {"Gra", JXL_COLOR_SPACE_GRAY}, + {"XYB", JXL_COLOR_SPACE_XYB}, + {"CS?", JXL_COLOR_SPACE_UNKNOWN}}); + +constexpr auto kJxlWhitePointNames = + to_array<EnumName<JxlWhitePoint>>({{"D65", JXL_WHITE_POINT_D65}, + {"Cst", JXL_WHITE_POINT_CUSTOM}, + {"EER", JXL_WHITE_POINT_E}, + {"DCI", JXL_WHITE_POINT_DCI}}); + +constexpr auto kJxlPrimariesNames = + to_array<EnumName<JxlPrimaries>>({{"SRG", JXL_PRIMARIES_SRGB}, + {"Cst", JXL_PRIMARIES_CUSTOM}, + {"202", JXL_PRIMARIES_2100}, + {"DCI", JXL_PRIMARIES_P3}}); + +constexpr auto kJxlTransferFunctionNames = + to_array<EnumName<JxlTransferFunction>>( + {{"709", JXL_TRANSFER_FUNCTION_709}, + {"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN}, + {"Lin", JXL_TRANSFER_FUNCTION_LINEAR}, + {"SRG", JXL_TRANSFER_FUNCTION_SRGB}, + {"PeQ", JXL_TRANSFER_FUNCTION_PQ}, + {"DCI", JXL_TRANSFER_FUNCTION_DCI}, + {"HLG", JXL_TRANSFER_FUNCTION_HLG}, + {"", JXL_TRANSFER_FUNCTION_GAMMA}}); + +constexpr auto kJxlRenderingIntentNames = + to_array<EnumName<JxlRenderingIntent>>( + {{"Per", JXL_RENDERING_INTENT_PERCEPTUAL}, + {"Rel", JXL_RENDERING_INTENT_RELATIVE}, + {"Sat", JXL_RENDERING_INTENT_SATURATION}, + {"Abs", JXL_RENDERING_INTENT_ABSOLUTE}}); + +template <typename T, size_t N> +Status ParseEnum(const std::string& token, + const std::array<EnumName<T>, N>& enum_values, T* value) { + for (size_t i = 0; i < enum_values.size(); i++) { if (enum_values[i].name == token) { *value = enum_values[i].value; return true; @@ -69,9 +68,6 @@ Status ParseEnum(const std::string& token, const EnumName<T>* enum_values, } return false; } -#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) -#define PARSE_ENUM(type, token, value) \ - ParseEnum<type>(token, k##type##Names, ARRAY_SIZE(k##type##Names), value) class Tokenizer { public: @@ -122,7 +118,7 @@ Status ParseColorSpace(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); JxlColorSpace cs; - if (PARSE_ENUM(JxlColorSpace, str, &cs)) { + if (ParseEnum(str, kJxlColorSpaceNames, &cs)) { c->color_space = cs; return true; } @@ -139,7 +135,7 @@ Status ParseWhitePoint(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (PARSE_ENUM(JxlWhitePoint, str, &c->white_point)) return true; + if (ParseEnum(str, kJxlWhitePointNames, &c->white_point)) return true; Tokenizer xy_tokenizer(&str, ';'); c->white_point = JXL_WHITE_POINT_CUSTOM; @@ -157,7 +153,7 @@ Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (PARSE_ENUM(JxlPrimaries, str, &c->primaries)) return true; + if (ParseEnum(str, kJxlPrimariesNames, &c->primaries)) return true; Tokenizer xy_tokenizer(&str, ';'); JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 0)); @@ -174,7 +170,8 @@ Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) { Status ParseRenderingIntent(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (PARSE_ENUM(JxlRenderingIntent, str, &c->rendering_intent)) return true; + if (ParseEnum(str, kJxlRenderingIntentNames, &c->rendering_intent)) + return true; return JXL_FAILURE("Invalid RenderingIntent %s\n", str.c_str()); } @@ -189,7 +186,7 @@ Status ParseTransferFunction(Tokenizer* tokenizer, JxlColorEncoding* c) { std::string str; JXL_RETURN_IF_ERROR(tokenizer->Next(&str)); - if (PARSE_ENUM(JxlTransferFunction, str, &c->transfer_function)) { + if (ParseEnum(str, kJxlTransferFunctionNames, &c->transfer_function)) { return true; } diff --git a/third_party/jpeg-xl/lib/extras/dec/color_hints.cc b/third_party/jpeg-xl/lib/extras/dec/color_hints.cc index 5c6d7b84a0..b1edd9f6ea 100644 --- a/third_party/jpeg-xl/lib/extras/dec/color_hints.cc +++ b/third_party/jpeg-xl/lib/extras/dec/color_hints.cc @@ -43,20 +43,21 @@ Status ApplyColorHints(const ColorHints& color_hints, } else if (key == "icc") { const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data()); std::vector<uint8_t> icc(data, data + value.size()); - ppf->icc.swap(icc); + ppf->icc = std::move(icc); + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; got_color_space = true; } else if (key == "exif") { const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data()); std::vector<uint8_t> blob(data, data + value.size()); - ppf->metadata.exif.swap(blob); + ppf->metadata.exif = std::move(blob); } else if (key == "xmp") { const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data()); std::vector<uint8_t> blob(data, data + value.size()); - ppf->metadata.xmp.swap(blob); + ppf->metadata.xmp = std::move(blob); } else if (key == "jumbf") { const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data()); std::vector<uint8_t> blob(data, data + value.size()); - ppf->metadata.jumbf.swap(blob); + ppf->metadata.jumbf = std::move(blob); } else { JXL_WARNING("Ignoring %s hint", key.c_str()); } diff --git a/third_party/jpeg-xl/lib/extras/dec/decode.cc b/third_party/jpeg-xl/lib/extras/dec/decode.cc index b3ca711bb2..3546cb65c0 100644 --- a/third_party/jpeg-xl/lib/extras/dec/decode.cc +++ b/third_party/jpeg-xl/lib/extras/dec/decode.cc @@ -35,7 +35,8 @@ std::string GetExtension(const std::string& path) { } // namespace -Codec CodecFromPath(std::string path, size_t* JXL_RESTRICT bits_per_sample, +Codec CodecFromPath(const std::string& path, + size_t* JXL_RESTRICT bits_per_sample, std::string* extension) { std::string ext = GetExtension(path); if (extension) { @@ -98,7 +99,7 @@ Status DecodeBytes(const Span<const uint8_t> bytes, *ppf = extras::PackedPixelFile(); // Default values when not set by decoders. - ppf->info.uses_original_profile = true; + ppf->info.uses_original_profile = JXL_TRUE; ppf->info.orientation = JXL_ORIENT_IDENTITY; const auto choose_codec = [&]() -> Codec { @@ -116,6 +117,7 @@ Status DecodeBytes(const Span<const uint8_t> bytes, dparams.accepted_formats.push_back( {num_channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, /*align=*/0}); } + dparams.output_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; size_t decoded_bytes; if (DecodeImageJXL(bytes.data(), bytes.size(), dparams, &decoded_bytes, ppf) && diff --git a/third_party/jpeg-xl/lib/extras/dec/decode.h b/third_party/jpeg-xl/lib/extras/dec/decode.h index 0d7dfcbef2..1a90f4c6a3 100644 --- a/third_party/jpeg-xl/lib/extras/dec/decode.h +++ b/third_party/jpeg-xl/lib/extras/dec/decode.h @@ -40,7 +40,7 @@ bool CanDecode(Codec codec); // If and only if extension is ".pfm", *bits_per_sample is updated to 32 so // that Encode() would encode to PFM instead of PPM. -Codec CodecFromPath(std::string path, +Codec CodecFromPath(const std::string& path, size_t* JXL_RESTRICT bits_per_sample = nullptr, std::string* extension = nullptr); diff --git a/third_party/jpeg-xl/lib/extras/dec/exr.cc b/third_party/jpeg-xl/lib/extras/dec/exr.cc index 821e0f4b21..d5903155e9 100644 --- a/third_party/jpeg-xl/lib/extras/dec/exr.cc +++ b/third_party/jpeg-xl/lib/extras/dec/exr.cc @@ -121,7 +121,12 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints, }; ppf->frames.clear(); // Allocates the frame buffer. - ppf->frames.emplace_back(image_size.x, image_size.y, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(image_size.x, image_size.y, format)); + ppf->frames.emplace_back(std::move(frame)); + } const auto& frame = ppf->frames.back(); const int row_size = input.dataWindow().size().x + 1; @@ -188,7 +193,7 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints, if (has_alpha) { ppf->info.alpha_bits = kExrAlphaBits; ppf->info.alpha_exponent_bits = ppf->info.exponent_bits_per_sample; - ppf->info.alpha_premultiplied = true; + ppf->info.alpha_premultiplied = JXL_TRUE; } ppf->info.intensity_target = intensity_target; return true; diff --git a/third_party/jpeg-xl/lib/extras/dec/gif.cc b/third_party/jpeg-xl/lib/extras/dec/gif.cc index 3d963941c0..3f89d460b8 100644 --- a/third_party/jpeg-xl/lib/extras/dec/gif.cc +++ b/third_party/jpeg-xl/lib/extras/dec/gif.cc @@ -50,8 +50,10 @@ void ensure_have_alpha(PackedFrame* frame) { /*endianness=*/JXL_NATIVE_ENDIAN, /*align=*/0, }; - frame->extra_channels.emplace_back(frame->color.xsize, frame->color.ysize, - alpha_format); + JXL_ASSIGN_OR_DIE(PackedImage image, + PackedImage::Create(frame->color.xsize, frame->color.ysize, + alpha_format)); + frame->extra_channels.emplace_back(std::move(image)); // We need to set opaque-by-default. std::fill_n(static_cast<uint8_t*>(frame->extra_channels[0].pixels()), frame->color.xsize * frame->color.ysize, 255u); @@ -136,7 +138,7 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints, } if (gif->ImageCount > 1) { - ppf->info.have_animation = true; + ppf->info.have_animation = JXL_TRUE; // Delays in GIF are specified in 100ths of a second. ppf->info.animation.tps_numerator = 100; ppf->info.animation.tps_denominator = 1; @@ -186,7 +188,9 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints, } const PackedRgba background_rgba{background_color.Red, background_color.Green, background_color.Blue, 0}; - PackedFrame canvas(gif->SWidth, gif->SHeight, canvas_format); + JXL_ASSIGN_OR_RETURN( + PackedFrame canvas, + PackedFrame::Create(gif->SWidth, gif->SHeight, canvas_format)); std::fill_n(static_cast<PackedRgba*>(canvas.color.pixels()), canvas.color.xsize * canvas.color.ysize, background_rgba); Rect canvas_rect{0, 0, canvas.color.xsize, canvas.color.ysize}; @@ -230,8 +234,14 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints, } // Allocates the frame buffer. - ppf->frames.emplace_back(total_rect.xsize(), total_rect.ysize(), - packed_frame_format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(total_rect.xsize(), total_rect.ysize(), + packed_frame_format)); + ppf->frames.emplace_back(std::move(frame)); + } + PackedFrame* frame = &ppf->frames.back(); // We cannot tell right from the start whether there will be a @@ -301,8 +311,10 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints, } // Update the canvas by creating a copy first. - PackedImage new_canvas_image(canvas.color.xsize, canvas.color.ysize, - canvas.color.format); + JXL_ASSIGN_OR_RETURN( + PackedImage new_canvas_image, + PackedImage::Create(canvas.color.xsize, canvas.color.ysize, + canvas.color.format)); memcpy(new_canvas_image.pixels(), canvas.color.pixels(), new_canvas_image.pixels_size); for (size_t y = 0, byte_index = 0; y < image_rect.ysize(); ++y) { diff --git a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc index ffa1b79c25..443dfe86ba 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc @@ -27,7 +27,7 @@ constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, constexpr int kExifMarker = JPEG_APP0 + 1; constexpr int kICCMarker = JPEG_APP0 + 2; -static inline bool IsJPG(const std::vector<uint8_t>& bytes) { +inline bool IsJPG(const std::vector<uint8_t>& bytes) { if (bytes.size() < 2) return false; if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false; return true; @@ -188,7 +188,11 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed, } else if (dparams.force_grayscale) { cinfo.out_color_space = JCS_GRAYSCALE; } - if (!ReadICCProfile(&cinfo, &ppf->icc)) { + if (ReadICCProfile(&cinfo, &ppf->icc)) { + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; + } else { + ppf->primary_color_representation = + PackedPixelFile::kColorEncodingIsPrimary; ppf->icc.clear(); // Default to SRGB ppf->color_encoding.color_space = @@ -214,7 +218,7 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed, } else { return failure("unsupported data type"); } - ppf->info.uses_original_profile = true; + ppf->info.uses_original_profile = JXL_TRUE; // No alpha in JPG ppf->info.alpha_bits = 0; @@ -227,8 +231,8 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed, if (dparams.num_colors > 0) { cinfo.quantize_colors = TRUE; cinfo.desired_number_of_colors = dparams.num_colors; - cinfo.two_pass_quantize = dparams.two_pass_quant; - cinfo.dither_mode = (J_DITHER_MODE)dparams.dither_mode; + cinfo.two_pass_quantize = static_cast<boolean>(dparams.two_pass_quant); + cinfo.dither_mode = static_cast<J_DITHER_MODE>(dparams.dither_mode); } jpegli_start_decompress(&cinfo); @@ -242,7 +246,12 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed, }; ppf->frames.clear(); // Allocates the frame buffer. - ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(cinfo.image_width, cinfo.image_height, format)); + ppf->frames.emplace_back(std::move(frame)); + } const auto& frame = ppf->frames.back(); JXL_ASSERT(sizeof(JSAMPLE) * cinfo.out_color_components * cinfo.image_width <= diff --git a/third_party/jpeg-xl/lib/extras/dec/jpg.cc b/third_party/jpeg-xl/lib/extras/dec/jpg.cc index 3c8a4bccfe..4a3e0d3b21 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jpg.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jpg.cc @@ -34,7 +34,7 @@ constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69, 0x66, 0x00, 0x00}; constexpr int kExifMarker = JPEG_APP0 + 1; -static inline bool IsJPG(const Span<const uint8_t> bytes) { +inline bool IsJPG(const Span<const uint8_t> bytes) { if (bytes.size() < 2) return false; if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false; return true; @@ -242,7 +242,11 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, if (nbcomp != 1 && nbcomp != 3) { return failure("unsupported number of components in JPEG"); } - if (!ReadICCProfile(&cinfo, &ppf->icc)) { + if (ReadICCProfile(&cinfo, &ppf->icc)) { + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; + } else { + ppf->primary_color_representation = + PackedPixelFile::kColorEncodingIsPrimary; ppf->icc.clear(); // Default to SRGB // Actually, (cinfo.output_components == nbcomp) will be checked after @@ -266,7 +270,7 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, ppf->info.bits_per_sample = BITS_IN_JSAMPLE; JXL_ASSERT(BITS_IN_JSAMPLE == 8 || BITS_IN_JSAMPLE == 16); ppf->info.exponent_bits_per_sample = 0; - ppf->info.uses_original_profile = true; + ppf->info.uses_original_profile = JXL_TRUE; // No alpha in JPG ppf->info.alpha_bits = 0; @@ -278,8 +282,8 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, if (dparams && dparams->num_colors > 0) { cinfo.quantize_colors = TRUE; cinfo.desired_number_of_colors = dparams->num_colors; - cinfo.two_pass_quantize = dparams->two_pass_quant; - cinfo.dither_mode = (J_DITHER_MODE)dparams->dither_mode; + cinfo.two_pass_quantize = static_cast<boolean>(dparams->two_pass_quant); + cinfo.dither_mode = static_cast<J_DITHER_MODE>(dparams->dither_mode); } jpeg_start_decompress(&cinfo); @@ -295,19 +299,25 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, }; ppf->frames.clear(); // Allocates the frame buffer. - ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(cinfo.image_width, cinfo.image_height, format)); + ppf->frames.emplace_back(std::move(frame)); + } const auto& frame = ppf->frames.back(); JXL_ASSERT(sizeof(JSAMPLE) * cinfo.out_color_components * cinfo.image_width <= frame.color.stride); if (cinfo.quantize_colors) { - jxl::msan::UnpoisonMemory(cinfo.colormap, cinfo.out_color_components * - sizeof(cinfo.colormap[0])); + JSAMPLE** colormap = cinfo.colormap; + jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(colormap), + cinfo.out_color_components * sizeof(JSAMPLE*)); for (int c = 0; c < cinfo.out_color_components; ++c) { jxl::msan::UnpoisonMemory( - cinfo.colormap[c], - cinfo.actual_number_of_colors * sizeof(cinfo.colormap[c][0])); + reinterpret_cast<void*>(colormap[c]), + cinfo.actual_number_of_colors * sizeof(JSAMPLE)); } } for (size_t y = 0; y < cinfo.image_height; ++y) { diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.cc b/third_party/jpeg-xl/lib/extras/dec/jxl.cc index f3e62c970a..5b7fa03f02 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jxl.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jxl.cc @@ -16,20 +16,23 @@ #include "lib/extras/dec/color_description.h" #include "lib/jxl/base/exif.h" #include "lib/jxl/base/printf_macros.h" +#include "lib/jxl/base/status.h" namespace jxl { namespace extras { namespace { struct BoxProcessor { - BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); } + explicit BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); } void InitializeOutput(std::vector<uint8_t>* out) { + JXL_ASSERT(out != nullptr); box_data_ = out; AddMoreOutput(); } bool AddMoreOutput() { + JXL_ASSERT(box_data_ != nullptr); Flush(); static const size_t kBoxOutputChunkSize = 1 << 16; box_data_->resize(box_data_->size() + kBoxOutputChunkSize); @@ -126,7 +129,7 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, return false; } - JxlPixelFormat format; + JxlPixelFormat format = {}; // Initialize to calm down clang-tidy. std::vector<JxlPixelFormat> accepted_formats = dparams.accepted_formats; JxlColorEncoding color_encoding; @@ -177,18 +180,18 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, return false; } if (jpeg_bytes == nullptr) { - if (JXL_DEC_SUCCESS != - JxlDecoderSetRenderSpotcolors(dec, dparams.render_spotcolors)) { + if (JXL_DEC_SUCCESS != JxlDecoderSetRenderSpotcolors( + dec, TO_JXL_BOOL(dparams.render_spotcolors))) { fprintf(stderr, "JxlDecoderSetRenderSpotColors failed\n"); return false; } - if (JXL_DEC_SUCCESS != - JxlDecoderSetKeepOrientation(dec, dparams.keep_orientation)) { + if (JXL_DEC_SUCCESS != JxlDecoderSetKeepOrientation( + dec, TO_JXL_BOOL(dparams.keep_orientation))) { fprintf(stderr, "JxlDecoderSetKeepOrientation failed\n"); return false; } - if (JXL_DEC_SUCCESS != - JxlDecoderSetUnpremultiplyAlpha(dec, dparams.unpremultiply_alpha)) { + if (JXL_DEC_SUCCESS != JxlDecoderSetUnpremultiplyAlpha( + dec, TO_JXL_BOOL(dparams.unpremultiply_alpha))) { fprintf(stderr, "JxlDecoderSetUnpremultiplyAlpha failed\n"); return false; } @@ -267,6 +270,7 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, return false; } } else if (status == JXL_DEC_JPEG_NEED_MORE_OUTPUT) { + JXL_ASSERT(jpeg_bytes != nullptr); // Help clang-tidy. // Decoded a chunk to JPEG. size_t used_jpeg_output = jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec); @@ -309,7 +313,7 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, } else { if (dparams.unpremultiply_alpha) { // Mark in the basic info that alpha was unpremultiplied. - ppf->info.alpha_premultiplied = false; + ppf->info.alpha_premultiplied = JXL_FALSE; } } bool alpha_found = false; @@ -327,7 +331,8 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, } std::string name(eci.name_length + 1, 0); if (JXL_DEC_SUCCESS != - JxlDecoderGetExtraChannelName(dec, i, &name[0], name.size())) { + JxlDecoderGetExtraChannelName( + dec, i, const_cast<char*>(name.data()), name.size())) { fprintf(stderr, "JxlDecoderGetExtraChannelName failed\n"); return false; } @@ -351,24 +356,26 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, } size_t icc_size = 0; JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_DATA; - ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN; - if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsEncodedProfile( - dec, target, &ppf->color_encoding) || - dparams.need_icc) { - // only get ICC if it is not an Enum color encoding - if (JXL_DEC_SUCCESS != - JxlDecoderGetICCProfileSize(dec, target, &icc_size)) { - fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); - } - if (icc_size != 0) { - ppf->icc.resize(icc_size); - if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile( - dec, target, ppf->icc.data(), icc_size)) { - fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); - return false; - } + if (JXL_DEC_SUCCESS != + JxlDecoderGetICCProfileSize(dec, target, &icc_size)) { + fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n"); + } + if (icc_size != 0) { + ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary; + ppf->icc.resize(icc_size); + if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile( + dec, target, ppf->icc.data(), icc_size)) { + fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n"); + return false; } } + if (JXL_DEC_SUCCESS == JxlDecoderGetColorAsEncodedProfile( + dec, target, &ppf->color_encoding)) { + ppf->primary_color_representation = + PackedPixelFile::kColorEncodingIsPrimary; + } else { + ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN; + } icc_size = 0; target = JXL_COLOR_PROFILE_TARGET_ORIGINAL; if (JXL_DEC_SUCCESS != @@ -385,14 +392,21 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, } } } else if (status == JXL_DEC_FRAME) { - jxl::extras::PackedFrame frame(ppf->info.xsize, ppf->info.ysize, format); + auto frame_or = jxl::extras::PackedFrame::Create(ppf->info.xsize, + ppf->info.ysize, format); + if (!frame_or.status()) { + fprintf(stderr, "Failed to create image frame."); + return false; + } + jxl::extras::PackedFrame frame = std::move(frame_or).value(); if (JXL_DEC_SUCCESS != JxlDecoderGetFrameHeader(dec, &frame.frame_info)) { fprintf(stderr, "JxlDecoderGetFrameHeader failed\n"); return false; } frame.name.resize(frame.frame_info.name_length + 1, 0); if (JXL_DEC_SUCCESS != - JxlDecoderGetFrameName(dec, &frame.name[0], frame.name.size())) { + JxlDecoderGetFrameName(dec, const_cast<char*>(frame.name.data()), + frame.name.size())) { fprintf(stderr, "JxlDecoderGetFrameName failed\n"); return false; } @@ -423,9 +437,16 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, fprintf(stderr, "JxlDecoderPreviewOutBufferSize failed\n"); return false; } + auto preview_image_or = jxl::extras::PackedImage::Create( + ppf->info.preview.xsize, ppf->info.preview.ysize, format); + if (!preview_image_or.status()) { + fprintf(stderr, "Failed to create preview image\n"); + return false; + } + jxl::extras::PackedImage preview_image = + std::move(preview_image_or).value(); ppf->preview_frame = std::unique_ptr<jxl::extras::PackedFrame>( - new jxl::extras::PackedFrame(ppf->info.preview.xsize, - ppf->info.preview.ysize, format)); + new jxl::extras::PackedFrame(std::move(preview_image))); if (buffer_size != ppf->preview_frame->color.pixels_size) { fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n", buffer_size, ppf->preview_frame->color.pixels_size); @@ -492,8 +513,13 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size, JxlPixelFormat ec_format = format; ec_format.num_channels = 1; for (auto& eci : ppf->extra_channels_info) { - frame.extra_channels.emplace_back(jxl::extras::PackedImage( - ppf->info.xsize, ppf->info.ysize, ec_format)); + auto image = jxl::extras::PackedImage::Create( + ppf->info.xsize, ppf->info.ysize, ec_format); + if (!image.status()) { + fprintf(stderr, "Failed to create extra channel image\n"); + return false; + } + frame.extra_channels.emplace_back(std::move(image).value()); auto& ec = frame.extra_channels.back(); size_t buffer_size; if (JXL_DEC_SUCCESS != JxlDecoderExtraChannelBufferSize( diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.h b/third_party/jpeg-xl/lib/extras/dec/jxl.h index cbada1f6dd..5f4ed7f683 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jxl.h +++ b/third_party/jpeg-xl/lib/extras/dec/jxl.h @@ -41,10 +41,6 @@ struct JXLDecompressParams { // Whether truncated input should be treated as an error. bool allow_partial_input = false; - // Set to true if an ICC profile has to be synthesized for Enum color - // encodings - bool need_icc = false; - // How many passes to decode at most. By default, decode everything. uint32_t max_passes = std::numeric_limits<uint32_t>::max(); diff --git a/third_party/jpeg-xl/lib/extras/dec/pgx.cc b/third_party/jpeg-xl/lib/extras/dec/pgx.cc index a99eb0f4ee..4499069d77 100644 --- a/third_party/jpeg-xl/lib/extras/dec/pgx.cc +++ b/third_party/jpeg-xl/lib/extras/dec/pgx.cc @@ -150,7 +150,7 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes, const SizeConstraints* constraints) { Parser parser(bytes); HeaderPGX header = {}; - const uint8_t* pos; + const uint8_t* pos = nullptr; if (!parser.ParseHeader(&header, &pos)) return false; JXL_RETURN_IF_ERROR( VerifyDimensions(constraints, header.xsize, header.ysize)); @@ -165,7 +165,7 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes, // Original data is uint, so exponent_bits_per_sample = 0. ppf->info.bits_per_sample = header.bits_per_sample; ppf->info.exponent_bits_per_sample = 0; - ppf->info.uses_original_profile = true; + ppf->info.uses_original_profile = JXL_TRUE; // No alpha in PGX ppf->info.alpha_bits = 0; @@ -188,7 +188,12 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes, }; ppf->frames.clear(); // Allocates the frame buffer. - ppf->frames.emplace_back(header.xsize, header.ysize, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(header.xsize, header.ysize, format)); + ppf->frames.emplace_back(std::move(frame)); + } const auto& frame = ppf->frames.back(); size_t pgx_remaining_size = bytes.data() + bytes.size() - pos; if (pgx_remaining_size < frame.color.pixels_size) { diff --git a/third_party/jpeg-xl/lib/extras/dec/pnm.cc b/third_party/jpeg-xl/lib/extras/dec/pnm.cc index 4c4618d41d..040c0bff81 100644 --- a/third_party/jpeg-xl/lib/extras/dec/pnm.cc +++ b/third_party/jpeg-xl/lib/extras/dec/pnm.cc @@ -9,6 +9,7 @@ #include <string.h> #include <cmath> +#include <cstdint> #include <mutex> #include "jxl/encode.h" @@ -55,8 +56,10 @@ class Parser { case 'f': header->is_gray = true; return ParseHeaderPFM(header, pos); + + default: + return false; } - return false; } // Exposed for testing @@ -160,11 +163,12 @@ class Parser { Status MatchString(const char* keyword, bool skipws = true) { const uint8_t* ppos = pos_; - while (*keyword) { + const uint8_t* kw = reinterpret_cast<const uint8_t*>(keyword); + while (*kw) { if (ppos >= end_) return JXL_FAILURE("PAM: unexpected end of input"); - if (*keyword != *ppos) return false; + if (*kw != *ppos) return false; ppos++; - keyword++; + kw++; } pos_ = ppos; if (skipws) { @@ -387,8 +391,8 @@ StatusOr<ChunkedPNMDecoder> ChunkedPNMDecoder::Init(const char* path) { const size_t num_channels = dec.header_.is_gray ? 1 : 3; const size_t bytes_per_pixel = num_channels * bytes_per_channel; size_t row_size = dec.header_.xsize * bytes_per_pixel; - if (header.ysize * row_size + dec.data_start_ < size) { - return JXL_FAILURE("Invalid ppm"); + if (size < header.ysize * row_size + dec.data_start_) { + return JXL_FAILURE("PNM file too small"); } return dec; } @@ -495,10 +499,18 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes, }; const JxlPixelFormat ec_format{1, format.data_type, format.endianness, 0}; ppf->frames.clear(); - ppf->frames.emplace_back(header.xsize, header.ysize, format); + { + JXL_ASSIGN_OR_RETURN( + PackedFrame frame, + PackedFrame::Create(header.xsize, header.ysize, format)); + ppf->frames.emplace_back(std::move(frame)); + } auto* frame = &ppf->frames.back(); for (size_t i = 0; i < header.ec_types.size(); ++i) { - frame->extra_channels.emplace_back(header.xsize, header.ysize, ec_format); + JXL_ASSIGN_OR_RETURN( + PackedImage ec, + PackedImage::Create(header.xsize, header.ysize, ec_format)); + frame->extra_channels.emplace_back(std::move(ec)); } size_t pnm_remaining_size = bytes.data() + bytes.size() - pos; if (pnm_remaining_size < frame->color.pixels_size) { @@ -533,6 +545,9 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes, } } } + if (ppf->info.exponent_bits_per_sample == 0) { + ppf->input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; + } return true; } diff --git a/third_party/jpeg-xl/lib/extras/enc/apng.cc b/third_party/jpeg-xl/lib/extras/enc/apng.cc index 413a9c8081..40aa876e84 100644 --- a/third_party/jpeg-xl/lib/extras/enc/apng.cc +++ b/third_party/jpeg-xl/lib/extras/enc/apng.cc @@ -86,7 +86,7 @@ class APNGEncoder : public Encoder { std::vector<uint8_t>* bytes) const; }; -static void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) { +void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) { std::vector<uint8_t>* bytes = static_cast<std::vector<uint8_t>*>(png_get_io_ptr(png_ptr)); bytes->insert(bytes->end(), data, data + length); @@ -137,7 +137,7 @@ class BlobsWriterPNG { std::vector<std::string>* strings) { // Encoding: base16 with newline after 72 chars. const size_t base16_size = - 2 * bytes.size() + DivCeil(bytes.size(), size_t(36)) + 1; + 2 * bytes.size() + DivCeil(bytes.size(), static_cast<size_t>(36)) + 1; std::string base16; base16.reserve(base16_size); for (size_t i = 0; i < bytes.size(); ++i) { @@ -155,7 +155,7 @@ class BlobsWriterPNG { snprintf(header, sizeof(header), "\n%s\n%8" PRIuS, type.c_str(), bytes.size()); - strings->push_back(std::string(key)); + strings->emplace_back(key); strings->push_back(std::string(header) + base16); return true; } @@ -303,7 +303,7 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( out[i] = static_cast<uint8_t>(in[i] * mul + 0.5); } } else { - memcpy(&out[0], in, out_size); + memcpy(out.data(), in, out_size); } } else if (format.data_type == JXL_TYPE_UINT16) { if (ppf.info.bits_per_sample < 16 || @@ -317,20 +317,21 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( StoreBE16(static_cast<uint32_t>(val * mul + 0.5), p_out); } } else { - memcpy(&out[0], in, out_size); + memcpy(out.data(), in, out_size); } } png_structp png_ptr; png_infop info_ptr; - png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, + nullptr); if (!png_ptr) return JXL_FAILURE("Could not init png encoder"); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) return JXL_FAILURE("Could not init png info struct"); - png_set_write_fn(png_ptr, bytes, PngWrite, NULL); + png_set_write_fn(png_ptr, bytes, PngWrite, nullptr); png_set_flush(png_ptr, 0); int width = xsize; @@ -344,11 +345,13 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (count == 0) { - if (!ppf.icc.empty()) { - png_set_benign_errors(png_ptr, 1); - png_set_iCCP(png_ptr, info_ptr, "1", 0, ppf.icc.data(), ppf.icc.size()); - } else if (!MaybeAddSRGB(ppf.color_encoding, png_ptr, info_ptr)) { + if (!MaybeAddSRGB(ppf.color_encoding, png_ptr, info_ptr)) { MaybeAddCICP(ppf.color_encoding, png_ptr, info_ptr); + if (!ppf.icc.empty()) { + png_set_benign_errors(png_ptr, 1); + png_set_iCCP(png_ptr, info_ptr, "1", 0, ppf.icc.data(), + ppf.icc.size()); + } MaybeAddCHRM(ppf.color_encoding, png_ptr, info_ptr); MaybeAddGAMA(ppf.color_encoding, png_ptr, info_ptr); } @@ -404,7 +407,7 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( png_write_flush(png_ptr); const size_t pos = bytes->size(); - png_write_image(png_ptr, &rows[0]); + png_write_image(png_ptr, rows.data()); png_write_flush(png_ptr); if (count > 0) { std::vector<uint8_t> fdata(4); @@ -428,7 +431,7 @@ Status APNGEncoder::EncodePackedPixelFileToAPNG( count++; if (count == ppf.frames.size() || !ppf.info.have_animation) { - png_write_end(png_ptr, NULL); + png_write_end(png_ptr, nullptr); } png_destroy_write_struct(&png_ptr, &info_ptr); diff --git a/third_party/jpeg-xl/lib/extras/enc/encode.cc b/third_party/jpeg-xl/lib/extras/enc/encode.cc index 8c9a148b27..c5e22d8c7e 100644 --- a/third_party/jpeg-xl/lib/extras/enc/encode.cc +++ b/third_party/jpeg-xl/lib/extras/enc/encode.cc @@ -54,7 +54,7 @@ Status Encoder::VerifyBitDepth(JxlDataType data_type, uint32_t bits_per_sample, (bits_per_sample > 16 || exponent_bits > 5))) { return JXL_FAILURE( "Incompatible data_type %d and bit depth %u with exponent bits %u", - (int)data_type, bits_per_sample, exponent_bits); + static_cast<int>(data_type), bits_per_sample, exponent_bits); } return true; } diff --git a/third_party/jpeg-xl/lib/extras/enc/encode.h b/third_party/jpeg-xl/lib/extras/enc/encode.h index da5f509838..2502d9976b 100644 --- a/third_party/jpeg-xl/lib/extras/enc/encode.h +++ b/third_party/jpeg-xl/lib/extras/enc/encode.h @@ -56,7 +56,7 @@ class Encoder { // Any existing data in encoded_image is discarded. virtual Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const = 0; + ThreadPool* pool) const = 0; void SetOption(std::string name, std::string value) { options_[std::move(name)] = std::move(value); diff --git a/third_party/jpeg-xl/lib/extras/enc/exr.cc b/third_party/jpeg-xl/lib/extras/enc/exr.cc index d4005c3097..5a4f7d768c 100644 --- a/third_party/jpeg-xl/lib/extras/enc/exr.cc +++ b/third_party/jpeg-xl/lib/extras/enc/exr.cc @@ -84,7 +84,7 @@ Status EncodeImageEXR(const PackedImage& image, const JxlBasicInfo& info, const size_t xsize = info.xsize; const size_t ysize = info.ysize; const bool has_alpha = info.alpha_bits > 0; - const bool alpha_is_premultiplied = info.alpha_premultiplied; + const bool alpha_is_premultiplied = FROM_JXL_BOOL(info.alpha_premultiplied); if (info.num_color_channels != 3 || c_enc.color_space != JXL_COLOR_SPACE_RGB || @@ -177,7 +177,7 @@ class EXREncoder : public Encoder { return formats; } Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const override { + ThreadPool* pool) const override { JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); encoded_image->icc.clear(); encoded_image->bitstreams.clear(); diff --git a/third_party/jpeg-xl/lib/extras/enc/jpegli.cc b/third_party/jpeg-xl/lib/extras/enc/jpegli.cc index 7e1aa426df..aa10b584d0 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jpegli.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jpegli.cc @@ -34,6 +34,7 @@ #include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/enc_xyb.h" #include "lib/jxl/image.h" +#include "lib/jxl/simd_util.h" namespace jxl { namespace extras { @@ -53,6 +54,10 @@ Status VerifyInput(const PackedPixelFile& ppf) { if (ppf.frames.size() != 1) { return JXL_FAILURE("JPEG input must have exactly one frame."); } + if (info.num_color_channels != 1 && info.num_color_channels != 3) { + return JXL_FAILURE("Invalid number of color channels %d", + info.num_color_channels); + } const PackedImage& image = ppf.frames[0].color; JXL_RETURN_IF_ERROR(Encoder::VerifyImageSize(image, info)); if (image.format.data_type == JXL_TYPE_FLOAT16) { @@ -71,7 +76,7 @@ Status VerifyInput(const PackedPixelFile& ppf) { Status GetColorEncoding(const PackedPixelFile& ppf, ColorEncoding* color_encoding) { - if (!ppf.icc.empty()) { + if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) { IccBytes icc = ppf.icc; JXL_RETURN_IF_ERROR( color_encoding->SetICC(std::move(icc), JxlGetDefaultCms())); @@ -122,12 +127,12 @@ Status WriteAppData(j_compress_ptr cinfo, return true; } -static constexpr int kICCMarker = 0xe2; +constexpr int kICCMarker = 0xe2; constexpr unsigned char kICCSignature[12] = { 0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00}; -static constexpr uint8_t kUnknownTf = 2; -static constexpr unsigned char kCICPTagSignature[4] = {0x63, 0x69, 0x63, 0x70}; -static constexpr size_t kCICPTagSize = 12; +constexpr uint8_t kUnknownTf = 2; +constexpr unsigned char kCICPTagSignature[4] = {0x63, 0x69, 0x63, 0x70}; +constexpr size_t kCICPTagSize = 12; bool FindCICPTag(const uint8_t* icc_data, size_t len, bool is_first_chunk, size_t* cicp_offset, size_t* cicp_length, uint8_t* cicp_tag, @@ -248,32 +253,48 @@ JpegliEndianness ConvertEndianness(JxlEndianness endianness) { } } -void ToFloatRow(const uint8_t* row_in, JxlPixelFormat format, size_t len, - float* row_out) { +void ToFloatRow(const uint8_t* row_in, JxlPixelFormat format, size_t xsize, + size_t c_out, float* row_out) { bool is_little_endian = (format.endianness == JXL_LITTLE_ENDIAN || (format.endianness == JXL_NATIVE_ENDIAN && IsLittleEndian())); static constexpr double kMul8 = 1.0 / 255.0; static constexpr double kMul16 = 1.0 / 65535.0; + const size_t c_in = format.num_channels; if (format.data_type == JXL_TYPE_UINT8) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = row_in[x] * kMul8; + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = row_in[ix] * kMul8; + } } } else if (format.data_type == JXL_TYPE_UINT16 && is_little_endian) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = LoadLE16(&row_in[2 * x]) * kMul16; + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = LoadLE16(&row_in[2 * ix]) * kMul16; + } } } else if (format.data_type == JXL_TYPE_UINT16 && !is_little_endian) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = LoadBE16(&row_in[2 * x]) * kMul16; + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = LoadBE16(&row_in[2 * ix]) * kMul16; + } } } else if (format.data_type == JXL_TYPE_FLOAT && is_little_endian) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = LoadLEFloat(&row_in[4 * x]); + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = LoadLEFloat(&row_in[4 * ix]); + } } } else if (format.data_type == JXL_TYPE_FLOAT && !is_little_endian) { - for (size_t x = 0; x < len; ++x) { - row_out[x] = LoadBEFloat(&row_in[4 * x]); + for (size_t x = 0; x < xsize; ++x) { + for (size_t c = 0; c < c_out; ++c) { + const size_t ix = c_in * x + c; + row_out[c_out * x + c] = LoadBEFloat(&row_in[4 * ix]); + } } } } @@ -352,9 +373,6 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, ColorSpaceTransform c_transform(*JxlGetDefaultCms()); ColorEncoding xyb_encoding; if (jpeg_settings.xyb) { - if (ppf.info.num_color_channels != 3) { - return JXL_FAILURE("Only RGB input is supported in XYB mode."); - } if (HasICCProfile(jpeg_settings.app_data)) { return JXL_FAILURE("APP data ICC profile is not supported in XYB mode."); } @@ -374,11 +392,11 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, unsigned char* output_buffer = nullptr; unsigned long output_size = 0; std::vector<uint8_t> row_bytes; - size_t rowlen = RoundUpTo(ppf.info.xsize, VectorSize()); + size_t rowlen = RoundUpTo(ppf.info.xsize, MaxVectorSize()); hwy::AlignedFreeUniquePtr<float[]> xyb_tmp = hwy::AllocateAligned<float>(6 * rowlen); hwy::AlignedFreeUniquePtr<float[]> premul_absorb = - hwy::AllocateAligned<float>(VectorSize() * 12); + hwy::AllocateAligned<float>(MaxVectorSize() * 12); ComputePremulAbsorb(255.0f, premul_absorb.get()); jpeg_compress_struct cinfo; @@ -401,6 +419,8 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, cinfo.input_components == 1 ? JCS_GRAYSCALE : JCS_RGB; if (jpeg_settings.xyb) { jpegli_set_xyb_mode(&cinfo); + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; } else if (jpeg_settings.use_std_quant_tables) { jpegli_use_standard_quant_tables(&cinfo); } @@ -436,7 +456,7 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, } } jpegli_enable_adaptive_quantization( - &cinfo, jpeg_settings.use_adaptive_quantization); + &cinfo, TO_JXL_BOOL(jpeg_settings.use_adaptive_quantization)); if (jpeg_settings.psnr_target > 0.0) { jpegli_set_psnr(&cinfo, jpeg_settings.psnr_target, jpeg_settings.search_tolerance, @@ -448,11 +468,11 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, jpegli_set_distance(&cinfo, jpeg_settings.distance, TRUE); } jpegli_set_progressive_level(&cinfo, jpeg_settings.progressive_level); - cinfo.optimize_coding = jpeg_settings.optimize_coding; + cinfo.optimize_coding = TO_JXL_BOOL(jpeg_settings.optimize_coding); if (!jpeg_settings.app_data.empty()) { // Make sure jpegli_start_compress() does not write any APP markers. - cinfo.write_JFIF_header = false; - cinfo.write_Adobe_marker = false; + cinfo.write_JFIF_header = JXL_FALSE; + cinfo.write_Adobe_marker = JXL_FALSE; } const PackedImage& image = ppf.frames[0].color; if (jpeg_settings.xyb) { @@ -476,10 +496,10 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, float* dst_buf = c_transform.BufDst(0); for (size_t y = 0; y < image.ysize; ++y) { // convert to float - ToFloatRow(&pixels[y * image.stride], image.format, 3 * image.xsize, - src_buf); + ToFloatRow(&pixels[y * image.stride], image.format, image.xsize, + info.num_color_channels, src_buf); // convert to linear srgb - if (!c_transform.Run(0, src_buf, dst_buf)) { + if (!c_transform.Run(0, src_buf, dst_buf, image.xsize)) { return false; } // deinterleave channels @@ -508,9 +528,9 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, } } else { row_bytes.resize(image.stride); - if (cinfo.num_components == (int)image.format.num_channels) { + if (cinfo.num_components == static_cast<int>(image.format.num_channels)) { for (size_t y = 0; y < info.ysize; ++y) { - memcpy(&row_bytes[0], pixels + y * image.stride, image.stride); + memcpy(row_bytes.data(), pixels + y * image.stride, image.stride); JSAMPROW row[] = {row_bytes.data()}; jpegli_write_scanlines(&cinfo, row, 1); } diff --git a/third_party/jpeg-xl/lib/extras/enc/jpg.cc b/third_party/jpeg-xl/lib/extras/enc/jpg.cc index c34dc6c13f..de0228fc0d 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jpg.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jpg.cc @@ -23,6 +23,7 @@ #include <vector> #include "lib/extras/exif.h" +#include "lib/jxl/base/common.h" #include "lib/jxl/base/status.h" #include "lib/jxl/sanitizers.h" #if JPEGXL_ENABLE_SJPEG @@ -50,23 +51,21 @@ enum class JpegEncoder { kSJpeg, }; -#define ARRAY_SIZE(X) (sizeof(X) / sizeof((X)[0])) - // Popular jpeg scan scripts // The fields of the individual scans are: // comps_in_scan, component_index[], Ss, Se, Ah, Al -static constexpr jpeg_scan_info kScanScript1[] = { +constexpr auto kScanScript1 = to_array<jpeg_scan_info>({ {1, {0}, 0, 0, 0, 0}, // {1, {1}, 0, 0, 0, 0}, // {1, {2}, 0, 0, 0, 0}, // {1, {0}, 1, 8, 0, 0}, // {1, {0}, 9, 63, 0, 0}, // {1, {1}, 1, 63, 0, 0}, // - {1, {2}, 1, 63, 0, 0}, // -}; -static constexpr size_t kNumScans1 = ARRAY_SIZE(kScanScript1); + {1, {2}, 1, 63, 0, 0} // +}); +constexpr size_t kNumScans1 = kScanScript1.size(); -static constexpr jpeg_scan_info kScanScript2[] = { +constexpr auto kScanScript2 = to_array<jpeg_scan_info>({ {1, {0}, 0, 0, 0, 0}, // {1, {1}, 0, 0, 0, 0}, // {1, {2}, 0, 0, 0, 0}, // @@ -74,11 +73,11 @@ static constexpr jpeg_scan_info kScanScript2[] = { {1, {0}, 3, 63, 0, 1}, // {1, {0}, 1, 63, 1, 0}, // {1, {1}, 1, 63, 0, 0}, // - {1, {2}, 1, 63, 0, 0}, // -}; -static constexpr size_t kNumScans2 = ARRAY_SIZE(kScanScript2); + {1, {2}, 1, 63, 0, 0} // +}); +constexpr size_t kNumScans2 = kScanScript2.size(); -static constexpr jpeg_scan_info kScanScript3[] = { +constexpr auto kScanScript3 = to_array<jpeg_scan_info>({ {1, {0}, 0, 0, 0, 0}, // {1, {1}, 0, 0, 0, 0}, // {1, {2}, 0, 0, 0, 0}, // @@ -86,11 +85,11 @@ static constexpr jpeg_scan_info kScanScript3[] = { {1, {0}, 1, 63, 2, 1}, // {1, {0}, 1, 63, 1, 0}, // {1, {1}, 1, 63, 0, 0}, // - {1, {2}, 1, 63, 0, 0}, // -}; -static constexpr size_t kNumScans3 = ARRAY_SIZE(kScanScript3); + {1, {2}, 1, 63, 0, 0} // +}); +constexpr size_t kNumScans3 = kScanScript3.size(); -static constexpr jpeg_scan_info kScanScript4[] = { +constexpr auto kScanScript4 = to_array<jpeg_scan_info>({ {3, {0, 1, 2}, 0, 0, 0, 1}, // {1, {0}, 1, 5, 0, 2}, // {1, {2}, 1, 63, 0, 1}, // @@ -100,11 +99,11 @@ static constexpr jpeg_scan_info kScanScript4[] = { {3, {0, 1, 2}, 0, 0, 1, 0}, // {1, {2}, 1, 63, 1, 0}, // {1, {1}, 1, 63, 1, 0}, // - {1, {0}, 1, 63, 1, 0}, // -}; -static constexpr size_t kNumScans4 = ARRAY_SIZE(kScanScript4); + {1, {0}, 1, 63, 1, 0} // +}); +constexpr size_t kNumScans4 = kScanScript4.size(); -static constexpr jpeg_scan_info kScanScript5[] = { +constexpr auto kScanScript5 = to_array<jpeg_scan_info>({ {3, {0, 1, 2}, 0, 0, 0, 1}, // {1, {0}, 1, 5, 0, 2}, // {1, {1}, 1, 5, 0, 2}, // @@ -118,12 +117,12 @@ static constexpr jpeg_scan_info kScanScript5[] = { {3, {0, 1, 2}, 0, 0, 1, 0}, // {1, {0}, 1, 63, 1, 0}, // {1, {1}, 1, 63, 1, 0}, // - {1, {2}, 1, 63, 1, 0}, // -}; -static constexpr size_t kNumScans5 = ARRAY_SIZE(kScanScript5); + {1, {2}, 1, 63, 1, 0} // +}); +constexpr size_t kNumScans5 = kScanScript5.size(); // default progressive mode of jpegli -static constexpr jpeg_scan_info kScanScript6[] = { +constexpr auto kScanScript6 = to_array<jpeg_scan_info>({ {3, {0, 1, 2}, 0, 0, 0, 0}, // {1, {0}, 1, 2, 0, 0}, // {1, {1}, 1, 2, 0, 0}, // @@ -137,8 +136,8 @@ static constexpr jpeg_scan_info kScanScript6[] = { {1, {0}, 3, 63, 1, 0}, // {1, {1}, 3, 63, 1, 0}, // {1, {2}, 3, 63, 1, 0}, // -}; -static constexpr size_t kNumScans6 = ARRAY_SIZE(kScanScript6); +}); +constexpr size_t kNumScans6 = kScanScript6.size(); // Adapt RGB scan info to grayscale jpegs. void FilterScanComponents(const jpeg_compress_struct* cinfo, @@ -163,12 +162,12 @@ Status SetJpegProgression(int progressive_id, jpeg_simple_progression(cinfo); return true; } - constexpr const jpeg_scan_info* kScanScripts[] = {kScanScript1, kScanScript2, - kScanScript3, kScanScript4, - kScanScript5, kScanScript6}; - constexpr size_t kNumScans[] = {kNumScans1, kNumScans2, kNumScans3, - kNumScans4, kNumScans5, kNumScans6}; - if (progressive_id > static_cast<int>(ARRAY_SIZE(kNumScans))) { + const jpeg_scan_info* kScanScripts[] = { + kScanScript1.data(), kScanScript2.data(), kScanScript3.data(), + kScanScript4.data(), kScanScript5.data(), kScanScript6.data()}; + constexpr auto kNumScans = to_array<size_t>( + {kNumScans1, kNumScans2, kNumScans3, kNumScans4, kNumScans5, kNumScans6}); + if (progressive_id > static_cast<int>(kNumScans.size())) { return JXL_FAILURE("Unknown jpeg scan script id %d", progressive_id); } const jpeg_scan_info* scan_script = kScanScripts[progressive_id - 1]; @@ -178,7 +177,7 @@ Status SetJpegProgression(int progressive_id, jpeg_scan_info scan_info = scan_script[i]; FilterScanComponents(cinfo, &scan_info); if (scan_info.comps_in_scan > 0) { - scan_infos->emplace_back(std::move(scan_info)); + scan_infos->emplace_back(scan_info); } } cinfo->scan_info = scan_infos->data(); @@ -217,8 +216,8 @@ void WriteExif(jpeg_compress_struct* const cinfo, for (const unsigned char c : kExifSignature) { jpeg_write_m_byte(cinfo, c); } - for (size_t i = 0; i < exif.size(); ++i) { - jpeg_write_m_byte(cinfo, exif[i]); + for (uint8_t c : exif) { + jpeg_write_m_byte(cinfo, c); } } @@ -284,7 +283,7 @@ Status EncodeWithLibJpeg(const PackedImage& image, const JxlBasicInfo& info, cinfo.input_components = info.num_color_channels; cinfo.in_color_space = info.num_color_channels == 1 ? JCS_GRAYSCALE : JCS_RGB; jpeg_set_defaults(&cinfo); - cinfo.optimize_coding = params.optimize_coding; + cinfo.optimize_coding = static_cast<boolean>(params.optimize_coding); if (cinfo.input_components == 3) { JXL_RETURN_IF_ERROR( SetChromaSubsampling(params.chroma_subsampling, &cinfo)); @@ -310,10 +309,10 @@ Status EncodeWithLibJpeg(const PackedImage& image, const JxlBasicInfo& info, std::vector<uint8_t> row_bytes(image.stride); const uint8_t* pixels = reinterpret_cast<const uint8_t*>(image.pixels()); - if (cinfo.num_components == (int)image.format.num_channels && + if (cinfo.num_components == static_cast<int>(image.format.num_channels) && image.format.data_type == JXL_TYPE_UINT8) { for (size_t y = 0; y < info.ysize; ++y) { - memcpy(&row_bytes[0], pixels + y * image.stride, image.stride); + memcpy(row_bytes.data(), pixels + y * image.stride, image.stride); JSAMPROW row[] = {row_bytes.data()}; jpeg_write_scanlines(&cinfo, row, 1); } @@ -401,7 +400,7 @@ struct MySearchHook : public sjpeg::SearchHook { } bool Update(float result) override { value = result; - if (fabs(value - target) < tolerance * target) { + if (std::fabs(value - target) < tolerance * target) { return true; } if (value > target) { @@ -420,9 +419,9 @@ struct MySearchHook : public sjpeg::SearchHook { } else { q = (qmin + qmax) / 2.; } - return (pass > 0 && fabs(q - last_q) < q_precision); + return (pass > 0 && std::fabs(q - last_q) < q_precision); } - ~MySearchHook() override {} + ~MySearchHook() override = default; }; #endif @@ -539,7 +538,7 @@ class JPEGEncoder : public Encoder { return formats; } Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const override { + ThreadPool* pool) const override { JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); JpegEncoder jpeg_encoder = JpegEncoder::kLibJpeg; JpegParams params; diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.cc b/third_party/jpeg-xl/lib/extras/enc/jxl.cc index 00adbb7dda..d563f298e6 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jxl.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jxl.cc @@ -7,6 +7,7 @@ #include <jxl/encode.h> #include <jxl/encode_cxx.h> +#include <jxl/types.h> #include "lib/jxl/base/exif.h" @@ -132,7 +133,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, return false; } - auto settings = JxlEncoderFrameSettingsCreate(enc, nullptr); + auto* settings = JxlEncoderFrameSettingsCreate(enc, nullptr); size_t option_idx = 0; if (!SetFrameOptions(params.options, 0, &option_idx, settings)) { return false; @@ -150,10 +151,11 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, JxlEncoderCollectStats(settings, params.stats); } + bool has_jpeg_bytes = (jpeg_bytes != nullptr); bool use_boxes = !ppf.metadata.exif.empty() || !ppf.metadata.xmp.empty() || !ppf.metadata.jumbf.empty() || !ppf.metadata.iptc.empty(); bool use_container = params.use_container || use_boxes || - (jpeg_bytes && params.jpeg_store_metadata); + (has_jpeg_bytes && params.jpeg_store_metadata); if (JXL_ENC_SUCCESS != JxlEncoderUseContainer(enc, static_cast<int>(use_container))) { @@ -161,12 +163,22 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, return false; } - if (jpeg_bytes) { + if (has_jpeg_bytes) { if (params.jpeg_store_metadata && JXL_ENC_SUCCESS != JxlEncoderStoreJPEGMetadata(enc, JXL_TRUE)) { fprintf(stderr, "Storing JPEG metadata failed.\n"); return false; } + if (params.jpeg_store_metadata && params.jpeg_strip_exif) { + fprintf(stderr, + "Cannot store metadata and strip exif at the same time.\n"); + return false; + } + if (params.jpeg_store_metadata && params.jpeg_strip_xmp) { + fprintf(stderr, + "Cannot store metadata and strip xmp at the same time.\n"); + return false; + } if (!params.jpeg_store_metadata && params.jpeg_strip_exif) { JxlEncoderFrameSettingsSetOption(settings, JXL_ENC_FRAME_SETTING_JPEG_KEEP_EXIF, 0); @@ -210,8 +222,8 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, basic_info.num_extra_channels = std::max<uint32_t>(num_alpha_channels, ppf.info.num_extra_channels); basic_info.num_color_channels = ppf.info.num_color_channels; - const bool lossless = params.distance == 0; - basic_info.uses_original_profile = lossless; + const bool lossless = (params.distance == 0); + basic_info.uses_original_profile = TO_JXL_BOOL(lossless); if (params.override_bitdepth != 0) { basic_info.bits_per_sample = params.override_bitdepth; basic_info.exponent_bits_per_sample = @@ -233,7 +245,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, return false; } if (JXL_ENC_SUCCESS != - JxlEncoderSetFrameBitDepth(settings, ¶ms.input_bitdepth)) { + JxlEncoderSetFrameBitDepth(settings, &ppf.input_bitdepth)) { fprintf(stderr, "JxlEncoderSetFrameBitDepth() failed.\n"); return false; } @@ -248,7 +260,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, fprintf(stderr, "JxlEncoderSetFrameLossless() failed.\n"); return false; } - if (!ppf.icc.empty()) { + if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) { if (JXL_ENC_SUCCESS != JxlEncoderSetICCProfile(enc, ppf.icc.data(), ppf.icc.size())) { fprintf(stderr, "JxlEncoderSetICCProfile() failed.\n"); @@ -284,14 +296,15 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, {"jumb", ppf.metadata.jumbf}, {"xml ", ppf.metadata.iptc}, }; - for (size_t i = 0; i < sizeof boxes / sizeof *boxes; ++i) { - const BoxInfo& box = boxes[i]; - if (!box.bytes.empty() && - JXL_ENC_SUCCESS != JxlEncoderAddBox(enc, box.type, box.bytes.data(), - box.bytes.size(), - params.compress_boxes)) { - fprintf(stderr, "JxlEncoderAddBox() failed (%s).\n", box.type); - return false; + for (auto box : boxes) { + if (!box.bytes.empty()) { + if (JXL_ENC_SUCCESS != + JxlEncoderAddBox(enc, box.type, box.bytes.data(), + box.bytes.size(), + TO_JXL_BOOL(params.compress_boxes))) { + fprintf(stderr, "JxlEncoderAddBox() failed (%s).\n", box.type); + return false; + } } } JxlEncoderCloseBoxes(enc); @@ -336,7 +349,7 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, } const bool last_frame = fi + 1 == ppf.chunked_frames.size(); if (JXL_ENC_SUCCESS != - JxlEncoderAddChunkedFrame(settings, last_frame, + JxlEncoderAddChunkedFrame(settings, TO_JXL_BOOL(last_frame), chunked_frame.GetInputSource())) { fprintf(stderr, "JxlEncoderAddChunkedFrame() failed.\n"); return false; diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.h b/third_party/jpeg-xl/lib/extras/enc/jxl.h index b8ca5bda2f..2b3793c0c4 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jxl.h +++ b/third_party/jpeg-xl/lib/extras/enc/jxl.h @@ -38,7 +38,7 @@ struct JXLCompressParams { std::vector<JXLOption> options; // Target butteraugli distance, 0.0 means lossless. float distance = 1.0f; - float alpha_distance = 1.0f; + float alpha_distance = 0.0f; // If set to true, forces container mode. bool use_container = false; // Whether to enable/disable byte-exact jpeg reconstruction for jpeg inputs. @@ -57,8 +57,6 @@ struct JXLCompressParams { size_t override_bitdepth = 0; int32_t codestream_level = -1; int32_t premultiply = -1; - // Override input buffer interpretation. - JxlBitDepth input_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0}; // If runner_opaque is set, the decoder uses this parallel runner. JxlParallelRunner runner = JxlThreadParallelRunner; void* runner_opaque = nullptr; @@ -69,10 +67,10 @@ struct JXLCompressParams { bool allow_expert_options = false; void AddOption(JxlEncoderFrameSettingId id, int64_t val) { - options.emplace_back(JXLOption(id, val, 0)); + options.emplace_back(id, val, 0); } void AddFloatOption(JxlEncoderFrameSettingId id, float val) { - options.emplace_back(JXLOption(id, val, 0)); + options.emplace_back(id, val, 0); } bool HasOutputProcessor() const { return (output_processor.get_buffer != nullptr && diff --git a/third_party/jpeg-xl/lib/extras/enc/npy.cc b/third_party/jpeg-xl/lib/extras/enc/npy.cc index ae8cf13cc4..8d9954ef31 100644 --- a/third_party/jpeg-xl/lib/extras/enc/npy.cc +++ b/third_party/jpeg-xl/lib/extras/enc/npy.cc @@ -7,11 +7,13 @@ #include <jxl/types.h> +#include <memory> #include <sstream> #include <string> #include <vector> #include "lib/extras/packed_image.h" +#include "lib/jxl/base/common.h" namespace jxl { namespace extras { @@ -52,14 +54,17 @@ class JSONDict : public JSONField { static_assert(std::is_convertible<T*, JSONField*>::value, "T must be a JSONField"); T* ret = new T(); - values_.emplace_back( - key, std::unique_ptr<JSONField>(static_cast<JSONField*>(ret))); + JSONField* field = static_cast<JSONField*>(ret); + auto handle = std::unique_ptr<JSONField>(field); + values_.emplace_back(key, std::move(handle)); return ret; } template <typename T> void Add(const std::string& key, const T& value) { - values_.emplace_back(key, std::unique_ptr<JSONField>(new JSONValue(value))); + JSONField* field = static_cast<JSONField*>(new JSONValue(value)); + auto handle = std::unique_ptr<JSONField>(field); + values_.emplace_back(key, std::move(handle)); } void Write(std::ostream& o, uint32_t indent) const override { @@ -71,11 +76,11 @@ class JSONDict : public JSONField { o << ","; } is_first = false; - o << std::endl << indent_str << " \"" << key_value.first << "\": "; + o << "\n" << indent_str << " \"" << key_value.first << "\": "; key_value.second->Write(o, indent + 2); } if (!values_.empty()) { - o << std::endl << indent_str; + o << "\n" << indent_str; } o << "}"; } @@ -112,11 +117,11 @@ class JSONArray : public JSONField { o << ","; } is_first = false; - o << std::endl << indent_str << " "; + o << "\n" << indent_str << " "; value->Write(o, indent + 2); } if (!values_.empty()) { - o << std::endl << indent_str; + o << "\n" << indent_str; } o << "]"; } @@ -160,13 +165,13 @@ void GenerateMetadata(const PackedPixelFile& ppf, std::vector<uint8_t>* out) { } { - auto ectype = meta.AddEmpty<JSONArray>("extra_channel_type"); - auto bps = meta.AddEmpty<JSONArray>("bits_per_sample"); - auto ebps = meta.AddEmpty<JSONArray>("exp_bits_per_sample"); + auto* ectype = meta.AddEmpty<JSONArray>("extra_channel_type"); + auto* bps = meta.AddEmpty<JSONArray>("bits_per_sample"); + auto* ebps = meta.AddEmpty<JSONArray>("exp_bits_per_sample"); bps->Add(ppf.info.bits_per_sample); ebps->Add(ppf.info.exponent_bits_per_sample); - for (size_t i = 0; i < ppf.extra_channels_info.size(); i++) { - switch (ppf.extra_channels_info[i].ec_info.type) { + for (const auto& eci : ppf.extra_channels_info) { + switch (eci.ec_info.type) { case JXL_CHANNEL_ALPHA: { ectype->Add(std::string("Alpha")); break; @@ -200,8 +205,8 @@ void GenerateMetadata(const PackedPixelFile& ppf, std::vector<uint8_t>* out) { break; } } - bps->Add(ppf.extra_channels_info[i].ec_info.bits_per_sample); - ebps->Add(ppf.extra_channels_info[i].ec_info.exponent_bits_per_sample); + bps->Add(eci.ec_info.bits_per_sample); + ebps->Add(eci.ec_info.exponent_bits_per_sample); } } @@ -282,7 +287,7 @@ bool WriteNPYArray(const PackedPixelFile& ppf, std::vector<uint8_t>* out) { class NumPyEncoder : public Encoder { public: Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const override { + ThreadPool* pool) const override { JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); GenerateMetadata(ppf, &encoded_image->metadata); encoded_image->bitstreams.emplace_back(); diff --git a/third_party/jpeg-xl/lib/extras/enc/pgx.cc b/third_party/jpeg-xl/lib/extras/enc/pgx.cc index d4809e38b6..eb8eab4271 100644 --- a/third_party/jpeg-xl/lib/extras/enc/pgx.cc +++ b/third_party/jpeg-xl/lib/extras/enc/pgx.cc @@ -60,7 +60,7 @@ Status EncodeImagePGX(const PackedFrame& frame, const JxlBasicInfo& info, std::vector<uint8_t> pixels(num_samples * bytes_per_sample); if (format.data_type == JXL_TYPE_UINT8) { - memcpy(&pixels[0], in, num_samples * bytes_per_sample); + memcpy(pixels.data(), in, num_samples * bytes_per_sample); } else if (format.data_type == JXL_TYPE_UINT16) { if (format.endianness != JXL_BIG_ENDIAN) { const uint8_t* p_in = in; @@ -69,7 +69,7 @@ Status EncodeImagePGX(const PackedFrame& frame, const JxlBasicInfo& info, StoreBE16(LoadLE16(p_in), p_out); } } else { - memcpy(&pixels[0], in, num_samples * bytes_per_sample); + memcpy(pixels.data(), in, num_samples * bytes_per_sample); } } else { return JXL_FAILURE("Unsupported pixel data type"); diff --git a/third_party/jpeg-xl/lib/extras/enc/pnm.cc b/third_party/jpeg-xl/lib/extras/enc/pnm.cc index 4183900198..966611cfca 100644 --- a/third_party/jpeg-xl/lib/extras/enc/pnm.cc +++ b/third_party/jpeg-xl/lib/extras/enc/pnm.cc @@ -31,7 +31,7 @@ constexpr size_t kMaxHeaderSize = 200; class BasePNMEncoder : public Encoder { public: Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image, - ThreadPool* pool = nullptr) const override { + ThreadPool* pool) const override { JXL_RETURN_IF_ERROR(VerifyBasicInfo(ppf.info)); if (!ppf.metadata.exif.empty() || !ppf.metadata.iptc.empty() || !ppf.metadata.jumbf.empty() || !ppf.metadata.xmp.empty()) { diff --git a/third_party/jpeg-xl/lib/extras/jpegli_test.cc b/third_party/jpeg-xl/lib/extras/jpegli_test.cc index 66c18617a6..3049049a64 100644 --- a/third_party/jpeg-xl/lib/extras/jpegli_test.cc +++ b/third_party/jpeg-xl/lib/extras/jpegli_test.cc @@ -85,7 +85,7 @@ Status EncodeWithLibjpeg(const PackedPixelFile& ppf, int quality, std::unique_ptr<Encoder> encoder = GetJPEGEncoder(); encoder->SetOption("q", std::to_string(quality)); EncodedImage encoded; - JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded)); + JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded, nullptr)); JXL_RETURN_IF_ERROR(!encoded.bitstreams.empty()); *compressed = std::move(encoded.bitstreams[0]); return true; @@ -156,8 +156,8 @@ TEST(JpegliTest, JpegliXYBEncodeTest) { PackedPixelFile ppf_out; ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.45f)); - EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.32f)); + EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.45f); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.32f); } TEST(JpegliTest, JpegliDecodeTestLargeSmoothArea) { @@ -205,8 +205,8 @@ TEST(JpegliTest, JpegliYUVEncodeTest) { PackedPixelFile ppf_out; ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.7f)); - EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.32f)); + EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.7f); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.32f); } TEST(JpegliTest, JpegliYUVChromaSubsamplingEncodeTest) { @@ -247,8 +247,8 @@ TEST(JpegliTest, JpegliYUVEncodeTestNoAq) { PackedPixelFile ppf_out; ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out)); - EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.85f)); - EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.25f)); + EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 1.85f); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.25f); } TEST(JpegliTest, JpegliHDRRoundtripTest) { @@ -267,8 +267,8 @@ TEST(JpegliTest, JpegliHDRRoundtripTest) { JpegDecompressParams dparams; dparams.output_data_type = JXL_TYPE_UINT16; ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf_out)); - EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(2.95f)); - EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.05f)); + EXPECT_SLIGHTLY_BELOW(BitsPerPixel(ppf_in, compressed), 2.95f); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(ppf_in, ppf_out), 1.05f); } TEST(JpegliTest, JpegliSetAppData) { diff --git a/third_party/jpeg-xl/lib/extras/mmap.cc b/third_party/jpeg-xl/lib/extras/mmap.cc index 7852831ebd..9f5bba97ed 100644 --- a/third_party/jpeg-xl/lib/extras/mmap.cc +++ b/third_party/jpeg-xl/lib/extras/mmap.cc @@ -10,7 +10,8 @@ #include "lib/jxl/base/common.h" -#if __unix__ +#if defined(__unix__) || defined(__unix) || \ + defined(__APPLE__) && defined(__MACH__) #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> @@ -54,7 +55,7 @@ struct MemoryMappedFileImpl { } // namespace jxl -#elif __WIN32__ +#elif defined(_WIN32) #include <string.h> #include <windows.h> @@ -97,6 +98,8 @@ struct MemoryMappedFileImpl { return f; } + ~MemoryMappedFileImpl() { UnmapViewOfFile(ptr); } + const uint8_t* data() const { return reinterpret_cast<const uint8_t*>(ptr); } size_t size() const { return fsize.QuadPart; } diff --git a/third_party/jpeg-xl/lib/extras/packed_image.h b/third_party/jpeg-xl/lib/extras/packed_image.h index edd5f1be75..a66ddfbd70 100644 --- a/third_party/jpeg-xl/lib/extras/packed_image.h +++ b/third_party/jpeg-xl/lib/extras/packed_image.h @@ -37,11 +37,18 @@ namespace extras { // Class representing an interleaved image with a bunch of channels. class PackedImage { public: - PackedImage(size_t xsize, size_t ysize, const JxlPixelFormat& format) - : PackedImage(xsize, ysize, format, CalcStride(format, xsize)) {} + static StatusOr<PackedImage> Create(size_t xsize, size_t ysize, + const JxlPixelFormat& format) { + PackedImage image(xsize, ysize, format, CalcStride(format, xsize)); + if (!image.pixels()) { + // TODO(szabadka): use specialized OOM error code + return JXL_FAILURE("Failed to allocate memory for image"); + } + return image; + } PackedImage Copy() const { - PackedImage copy(xsize, ysize, format); + PackedImage copy(xsize, ysize, format, CalcStride(format, xsize)); memcpy(reinterpret_cast<uint8_t*>(copy.pixels()), reinterpret_cast<const uint8_t*>(pixels()), pixels_size); return copy; @@ -108,7 +115,7 @@ class PackedImage { } } - void SetPixelValue(size_t y, size_t x, size_t c, float val) { + void SetPixelValue(size_t y, size_t x, size_t c, float val) const { uint8_t* data = pixels(y, x, c); switch (format.data_type) { case JXL_TYPE_UINT8: @@ -169,17 +176,25 @@ class PackedImage { // as all other frames in the same image. class PackedFrame { public: - template <typename... Args> - explicit PackedFrame(Args&&... args) : color(std::forward<Args>(args)...) {} + explicit PackedFrame(PackedImage&& image) : color(std::move(image)) {} + + static StatusOr<PackedFrame> Create(size_t xsize, size_t ysize, + const JxlPixelFormat& format) { + JXL_ASSIGN_OR_RETURN(PackedImage image, + PackedImage::Create(xsize, ysize, format)); + PackedFrame frame(std::move(image)); + return frame; + } - PackedFrame Copy() const { - PackedFrame copy(color.xsize, color.ysize, color.format); + StatusOr<PackedFrame> Copy() const { + JXL_ASSIGN_OR_RETURN( + PackedFrame copy, + PackedFrame::Create(color.xsize, color.ysize, color.format)); copy.frame_info = frame_info; copy.name = name; copy.color = color.Copy(); - for (size_t i = 0; i < extra_channels.size(); ++i) { - PackedImage ec = extra_channels[i].Copy(); - copy.extra_channels.emplace_back(std::move(ec)); + for (const auto& ec : extra_channels) { + copy.extra_channels.emplace_back(ec.Copy()); } return copy; } @@ -244,12 +259,26 @@ class PackedPixelFile { std::vector<PackedExtraChannel> extra_channels_info; // Color information of the decoded pixels. - // If the icc is empty, the JxlColorEncoding should be used instead. - std::vector<uint8_t> icc; + // `primary_color_representation` indicates whether `color_encoding` or `icc` + // is the “authoritative” encoding of the colorspace, as opposed to a fallback + // encoding. For example, if `color_encoding` is the primary one, as would + // occur when decoding a jxl file with such a representation, then `enc/jxl` + // will use it and ignore the ICC profile, whereas `enc/png` will include the + // ICC profile for compatibility. + // If `icc` is the primary representation, `enc/jxl` will preserve it when + // compressing losslessly, but *may* encode it as a color_encoding when + // compressing lossily. + enum { + kColorEncodingIsPrimary, + kIccIsPrimary + } primary_color_representation = kColorEncodingIsPrimary; JxlColorEncoding color_encoding = {}; + std::vector<uint8_t> icc; // The icc profile of the original image. std::vector<uint8_t> orig_icc; + JxlBitDepth input_bitdepth = {JXL_BIT_DEPTH_FROM_PIXEL_FORMAT, 0, 0}; + std::unique_ptr<PackedFrame> preview_frame; std::vector<PackedFrame> frames; mutable std::vector<ChunkedPackedFrame> chunked_frames; diff --git a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc index 56f3b044a4..2ad001bf09 100644 --- a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc +++ b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc @@ -22,15 +22,15 @@ namespace jxl { namespace extras { Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info, + const JxlBitDepth& input_bitdepth, const PackedFrame& frame, const CodecInOut& io, ThreadPool* pool, ImageBundle* bundle) { JXL_ASSERT(frame.color.pixels() != nullptr); - const bool float_in = frame.color.format.data_type == JXL_TYPE_FLOAT16 || - frame.color.format.data_type == JXL_TYPE_FLOAT; size_t frame_bits_per_sample = - float_in ? PackedImage::BitsPerChannel(frame.color.format.data_type) - : info.bits_per_sample; + input_bitdepth.type == JXL_BIT_DEPTH_FROM_PIXEL_FORMAT + ? PackedImage::BitsPerChannel(frame.color.format.data_type) + : info.bits_per_sample; JXL_ASSERT(frame_bits_per_sample != 0); // It is ok for the frame.color.format.num_channels to not match the // number of channels on the image. @@ -64,7 +64,8 @@ Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info, bundle->extra_channels().resize(io.metadata.m.extra_channel_info.size()); for (size_t i = 0; i < frame.extra_channels.size(); i++) { const auto& ppf_ec = frame.extra_channels[i]; - bundle->extra_channels()[i] = ImageF(ppf_ec.xsize, ppf_ec.ysize); + JXL_ASSIGN_OR_RETURN(bundle->extra_channels()[i], + ImageF::Create(ppf_ec.xsize, ppf_ec.ysize)); JXL_CHECK(BufferToImageF(ppf_ec.format, ppf_ec.xsize, ppf_ec.ysize, ppf_ec.pixels(), ppf_ec.pixels_size, pool, &bundle->extra_channels()[i])); @@ -97,23 +98,23 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, ppf.info.exponent_bits_per_sample == 0 && ppf.info.bits_per_sample <= 12; io->metadata.m.SetAlphaBits(ppf.info.alpha_bits, - ppf.info.alpha_premultiplied); + FROM_JXL_BOOL(ppf.info.alpha_premultiplied)); ExtraChannelInfo* alpha = io->metadata.m.Find(ExtraChannel::kAlpha); if (alpha) alpha->bit_depth = io->metadata.m.bit_depth; - io->metadata.m.xyb_encoded = !ppf.info.uses_original_profile; + io->metadata.m.xyb_encoded = !FROM_JXL_BOOL(ppf.info.uses_original_profile); JXL_ASSERT(ppf.info.orientation > 0 && ppf.info.orientation <= 8); io->metadata.m.orientation = ppf.info.orientation; // Convert animation metadata JXL_ASSERT(ppf.frames.size() == 1 || ppf.info.have_animation); - io->metadata.m.have_animation = ppf.info.have_animation; + io->metadata.m.have_animation = FROM_JXL_BOOL(ppf.info.have_animation); io->metadata.m.animation.tps_numerator = ppf.info.animation.tps_numerator; io->metadata.m.animation.tps_denominator = ppf.info.animation.tps_denominator; io->metadata.m.animation.num_loops = ppf.info.animation.num_loops; // Convert the color encoding. - if (!ppf.icc.empty()) { + if (ppf.primary_color_representation == PackedPixelFile::kIccIsPrimary) { IccBytes icc = ppf.icc; if (!io->metadata.m.color_encoding.SetICC(std::move(icc), JxlGetDefaultCms())) { @@ -170,15 +171,16 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, JXL_RETURN_IF_ERROR( io->metadata.m.preview_size.Set(preview_xsize, preview_ysize)); JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle( - ppf.info, *ppf.preview_frame, *io, pool, &io->preview_frame)); + ppf.info, ppf.input_bitdepth, *ppf.preview_frame, *io, pool, + &io->preview_frame)); } // Convert the pixels io->frames.clear(); for (const auto& frame : ppf.frames) { ImageBundle bundle(&io->metadata.m); - JXL_RETURN_IF_ERROR( - ConvertPackedFrameToImageBundle(ppf.info, frame, *io, pool, &bundle)); + JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle( + ppf.info, ppf.input_bitdepth, frame, *io, pool, &bundle)); io->frames.push_back(std::move(bundle)); } @@ -210,7 +212,8 @@ PackedPixelFile ConvertImage3FToPackedPixelFile(const Image3F& image, : 0; ppf.color_encoding = c_enc.ToExternal(); ppf.frames.clear(); - PackedFrame frame(image.xsize(), image.ysize(), format); + JXL_ASSIGN_OR_DIE(PackedFrame frame, + PackedFrame::Create(image.xsize(), image.ysize(), format)); const ImageF* channels[3]; for (int c = 0; c < 3; ++c) { channels[c] = &image.Plane(c); @@ -242,7 +245,8 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, ppf->info.alpha_bits = alpha_channel->bit_depth.bits_per_sample; ppf->info.alpha_exponent_bits = alpha_channel->bit_depth.exponent_bits_per_sample; - ppf->info.alpha_premultiplied = alpha_channel->alpha_associated; + ppf->info.alpha_premultiplied = + TO_JXL_BOOL(alpha_channel->alpha_associated); } // Convert the image metadata @@ -257,9 +261,9 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, ppf->info.linear_below = io.metadata.m.tone_mapping.linear_below; ppf->info.min_nits = io.metadata.m.tone_mapping.min_nits; ppf->info.relative_to_max_display = - io.metadata.m.tone_mapping.relative_to_max_display; + TO_JXL_BOOL(io.metadata.m.tone_mapping.relative_to_max_display); - ppf->info.uses_original_profile = !io.metadata.m.xyb_encoded; + ppf->info.uses_original_profile = TO_JXL_BOOL(!io.metadata.m.xyb_encoded); JXL_ASSERT(0 < io.metadata.m.orientation && io.metadata.m.orientation <= 8); ppf->info.orientation = static_cast<JxlOrientation>(io.metadata.m.orientation); @@ -267,13 +271,16 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, // Convert animation metadata JXL_ASSERT(io.frames.size() == 1 || io.metadata.m.have_animation); - ppf->info.have_animation = io.metadata.m.have_animation; + ppf->info.have_animation = TO_JXL_BOOL(io.metadata.m.have_animation); ppf->info.animation.tps_numerator = io.metadata.m.animation.tps_numerator; ppf->info.animation.tps_denominator = io.metadata.m.animation.tps_denominator; ppf->info.animation.num_loops = io.metadata.m.animation.num_loops; // Convert the color encoding ppf->icc.assign(c_desired.ICC().begin(), c_desired.ICC().end()); + ppf->primary_color_representation = + c_desired.WantICC() ? PackedPixelFile::kIccIsPrimary + : PackedPixelFile::kColorEncodingIsPrimary; ppf->color_encoding = c_desired.ToExternal(); // Convert the extra blobs @@ -289,22 +296,24 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, JXL_ASSERT(frame.metadata()->bit_depth.bits_per_sample != 0); // It is ok for the frame.color().kNumPlanes to not match the // number of channels on the image. + const uint32_t alpha_channels = has_alpha ? 1 : 0; const uint32_t num_channels = - frame.metadata()->color_encoding.Channels() + has_alpha; + frame.metadata()->color_encoding.Channels() + alpha_channels; JxlPixelFormat format{/*num_channels=*/num_channels, /*data_type=*/pixel_format.data_type, /*endianness=*/pixel_format.endianness, /*align=*/pixel_format.align}; - PackedFrame packed_frame(frame.oriented_xsize(), frame.oriented_ysize(), - format); + JXL_ASSIGN_OR_RETURN(PackedFrame packed_frame, + PackedFrame::Create(frame.oriented_xsize(), + frame.oriented_ysize(), format)); const size_t bits_per_sample = float_out ? packed_frame.color.BitsPerChannel(pixel_format.data_type) : ppf->info.bits_per_sample; packed_frame.name = frame.name; packed_frame.frame_info.name_length = frame.name.size(); // Color transform - ImageBundle ib = frame.Copy(); + JXL_ASSIGN_OR_RETURN(ImageBundle ib, frame.Copy()); const ImageBundle* to_color_transform = &ib; ImageMetadata metadata = io.metadata.m; ImageBundle store(&metadata); diff --git a/third_party/jpeg-xl/lib/extras/tone_mapping.cc b/third_party/jpeg-xl/lib/extras/tone_mapping.cc index 3d0269524b..39df304501 100644 --- a/third_party/jpeg-xl/lib/extras/tone_mapping.cc +++ b/third_party/jpeg-xl/lib/extras/tone_mapping.cc @@ -19,7 +19,7 @@ HWY_BEFORE_NAMESPACE(); namespace jxl { namespace HWY_NAMESPACE { -static constexpr float rec2020_luminances[3] = {0.2627f, 0.6780f, 0.0593f}; +static constexpr Vector3 rec2020_luminances{0.2627f, 0.6780f, 0.0593f}; Status ToneMapFrame(const std::pair<float, float> display_nits, ImageBundle* const ib, ThreadPool* const pool) { diff --git a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc index 34cbdde781..8fc928a4fd 100644 --- a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc +++ b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc @@ -6,11 +6,12 @@ #include "benchmark/benchmark.h" #include "lib/extras/codec.h" #include "lib/extras/tone_mapping.h" +#include "lib/jxl/image.h" namespace jxl { static void BM_ToneMapping(benchmark::State& state) { - Image3F color(2268, 1512); + JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(2268, 1512)); FillImage(0.5f, &color); // Use linear Rec. 2020 so that `ToneMapTo` doesn't have to convert to it and @@ -25,7 +26,8 @@ static void BM_ToneMapping(benchmark::State& state) { for (auto _ : state) { state.PauseTiming(); CodecInOut tone_mapping_input; - Image3F color2(color.xsize(), color.ysize()); + JXL_ASSIGN_OR_DIE(Image3F color2, + Image3F::Create(color.xsize(), color.ysize())); CopyImageTo(color, &color2); tone_mapping_input.SetFromImage(std::move(color2), linear_rec2020); tone_mapping_input.metadata.m.SetIntensityTarget(255); diff --git a/third_party/jpeg-xl/lib/include/jxl/cms_interface.h b/third_party/jpeg-xl/lib/include/jxl/cms_interface.h index c164eaccb5..25c700867a 100644 --- a/third_party/jpeg-xl/lib/include/jxl/cms_interface.h +++ b/third_party/jpeg-xl/lib/include/jxl/cms_interface.h @@ -29,10 +29,10 @@ extern "C" { /** Parses an ICC profile and populates @p c and @p cmyk with the data. * - * @param user_data JxlCmsInterface::set_fields_data passed as-is. + * @param user_data @ref JxlCmsInterface::set_fields_data passed as-is. * @param icc_data the ICC data to parse. * @param icc_size how many bytes of icc_data are valid. - * @param c a JxlColorEncoding to populate if applicable. + * @param c a @ref JxlColorEncoding to populate if applicable. * @param cmyk a boolean to set to whether the colorspace is a CMYK colorspace. * @return Whether the relevant fields in @p c were successfully populated. */ @@ -66,22 +66,23 @@ typedef struct { /** Allocates and returns the data needed for @p num_threads parallel transforms * from the @p input colorspace to @p output, with up to @p pixels_per_thread - * pixels to transform per call to JxlCmsInterface::run. @p init_data comes - * directly from the JxlCmsInterface instance. Since @c run only receives the - * data returned by @c init, a reference to @p init_data should be kept there - * if access to it is desired in @c run. Likewise for JxlCmsInterface::destroy. + * pixels to transform per call to @ref JxlCmsInterface::run. @p init_data comes + * directly from the @ref JxlCmsInterface instance. Since @c run only receives + * the data returned by @c init, a reference to @p init_data should be kept + * there if access to it is desired in @c run. Likewise for @ref + * JxlCmsInterface::destroy. * * The ICC data in @p input and @p output is guaranteed to outlive the @c init / * @c run / @c destroy cycle. * - * @param init_data JxlCmsInterface::init_data passed as-is. + * @param init_data @ref JxlCmsInterface::init_data passed as-is. * @param num_threads the maximum number of threads from which - * JxlCmsInterface::run will be called. + * @ref JxlCmsInterface::run will be called. * @param pixels_per_thread the maximum number of pixels that each call to - * JxlCmsInterface::run will have to transform. + * @ref JxlCmsInterface::run will have to transform. * @param input_profile the input colorspace for the transform. - * @param output_profile the colorspace to which JxlCmsInterface::run should - * convert the input data. + * @param output_profile the colorspace to which @ref JxlCmsInterface::run + * should convert the input data. * @param intensity_target for colorspaces where luminance is relative * (essentially: not PQ), indicates the luminance at which (1, 1, 1) will * be displayed. This is useful for conversions between PQ and a relative @@ -135,7 +136,7 @@ typedef float* (*jpegxl_cms_get_buffer_func)(void* user_data, size_t thread); * @param output_buffer the buffer receiving the transformed pixel data. * @param num_pixels the number of pixels to transform from @p input to * @p output. - * @return JXL_TRUE on success, JXL_FALSE on failure. + * @return ::JXL_TRUE on success, ::JXL_FALSE on failure. */ typedef JXL_BOOL (*jpegxl_cms_run_func)(void* user_data, size_t thread, const float* input_buffer, @@ -226,7 +227,7 @@ typedef void (*jpegxl_cms_destroy_func)(void*); typedef struct { /** CMS-specific data that will be passed to @ref set_fields_from_icc. */ void* set_fields_data; - /** Populates a JxlColorEncoding from an ICC profile. */ + /** Populates a @ref JxlColorEncoding from an ICC profile. */ jpegxl_cms_set_fields_from_icc_func set_fields_from_icc; /** CMS-specific data that will be passed to @ref init. */ diff --git a/third_party/jpeg-xl/lib/include/jxl/codestream_header.h b/third_party/jpeg-xl/lib/include/jxl/codestream_header.h index fb71484233..e60eb03355 100644 --- a/third_party/jpeg-xl/lib/include/jxl/codestream_header.h +++ b/third_party/jpeg-xl/lib/include/jxl/codestream_header.h @@ -71,7 +71,7 @@ typedef struct { } JxlPreviewHeader; /** The codestream animation header, optionally present in the beginning of - * the codestream, and if it is it applies to all animation frames, unlike + * the codestream, and if it is it applies to all animation frames, unlike @ref * JxlFrameHeader which applies to an individual frame. */ typedef struct { @@ -166,12 +166,12 @@ typedef struct { * it to to the original color profile. The decoder also does not convert to * the target display color profile. To convert the pixel data produced by * the decoder to the original color profile, one of the JxlDecoderGetColor* - * functions needs to be called with @ref JXL_COLOR_PROFILE_TARGET_DATA to get - * the color profile of the decoder output, and then an external CMS can be - * used for conversion. - * Note that for lossy compression, this should be set to false for most use - * cases, and if needed, the image should be converted to the original color - * profile after decoding, as described above. + * functions needs to be called with + * ::JXL_COLOR_PROFILE_TARGET_DATA to get the color profile of the decoder + * output, and then an external CMS can be used for conversion. Note that for + * lossy compression, this should be set to false for most use cases, and if + * needed, the image should be converted to the original color profile after + * decoding, as described above. */ JXL_BOOL uses_original_profile; @@ -194,17 +194,19 @@ typedef struct { * grayscale data, or 3 for colored data. This count does not include * the alpha channel or other extra channels. To check presence of an alpha * channel, such as in the case of RGBA color, check alpha_bits != 0. - * If and only if this is 1, the JxlColorSpace in the JxlColorEncoding is - * JXL_COLOR_SPACE_GRAY. + * If and only if this is 1, the @ref JxlColorSpace in the @ref + * JxlColorEncoding is + * ::JXL_COLOR_SPACE_GRAY. */ uint32_t num_color_channels; /** Number of additional image channels. This includes the main alpha channel, * but can also include additional channels such as depth, additional alpha * channels, spot colors, and so on. Information about the extra channels - * can be queried with JxlDecoderGetExtraChannelInfo. The main alpha channel, - * if it exists, also has its information available in the alpha_bits, - * alpha_exponent_bits and alpha_premultiplied fields in this JxlBasicInfo. + * can be queried with @ref JxlDecoderGetExtraChannelInfo. The main alpha + * channel, if it exists, also has its information available in the + * alpha_bits, alpha_exponent_bits and alpha_premultiplied fields in this @ref + * JxlBasicInfo. */ uint32_t num_extra_channels; @@ -388,7 +390,8 @@ typedef struct { /** The header of one displayed frame or non-coalesced layer. */ typedef struct { /** How long to wait after rendering in ticks. The duration in seconds of a - * tick is given by tps_numerator and tps_denominator in JxlAnimationHeader. + * tick is given by tps_numerator and tps_denominator in @ref + * JxlAnimationHeader. */ uint32_t duration; @@ -396,9 +399,9 @@ typedef struct { * interpreted from most-significant to least-significant as hour, minute, * second, and frame. If timecode is nonzero, it is strictly larger than that * of a previous frame with nonzero duration. These values are only available - * if have_timecodes in JxlAnimationHeader is JXL_TRUE. - * This value is only used if have_timecodes in JxlAnimationHeader is - * JXL_TRUE. + * if have_timecodes in @ref JxlAnimationHeader is ::JXL_TRUE. + * This value is only used if have_timecodes in @ref JxlAnimationHeader is + * ::JXL_TRUE. */ uint32_t timecode; diff --git a/third_party/jpeg-xl/lib/include/jxl/color_encoding.h b/third_party/jpeg-xl/lib/include/jxl/color_encoding.h index 928117e8dd..e6325dcb30 100644 --- a/third_party/jpeg-xl/lib/include/jxl/color_encoding.h +++ b/third_party/jpeg-xl/lib/include/jxl/color_encoding.h @@ -24,9 +24,9 @@ extern "C" { typedef enum { /** Tristimulus RGB */ JXL_COLOR_SPACE_RGB, - /** Luminance based, the primaries in JxlColorEncoding must be ignored. This - * value implies that num_color_channels in JxlBasicInfo is 1, any other value - * implies num_color_channels is 3. */ + /** Luminance based, the primaries in @ref JxlColorEncoding must be ignored. + * This value implies that num_color_channels in @ref JxlBasicInfo is 1, any + * other value implies num_color_channels is 3. */ JXL_COLOR_SPACE_GRAY, /** XYB (opsin) color space */ JXL_COLOR_SPACE_XYB, @@ -35,18 +35,18 @@ typedef enum { } JxlColorSpace; /** Built-in whitepoints for color encoding. When decoding, the numerical xy - * whitepoint value can be read from the JxlColorEncoding white_point field + * whitepoint value can be read from the @ref JxlColorEncoding white_point field * regardless of the enum value. When encoding, enum values except - * JXL_WHITE_POINT_CUSTOM override the numerical fields. Some enum values match - * a subset of CICP (Rec. ITU-T H.273 | ISO/IEC 23091-2:2019(E)), however the - * white point and RGB primaries are separate enums here. + * ::JXL_WHITE_POINT_CUSTOM override the numerical fields. Some enum values + * match a subset of CICP (Rec. ITU-T H.273 | ISO/IEC 23091-2:2019(E)), however + * the white point and RGB primaries are separate enums here. */ typedef enum { /** CIE Standard Illuminant D65: 0.3127, 0.3290 */ JXL_WHITE_POINT_D65 = 1, - /** White point must be read from the JxlColorEncoding white_point field, or - * as ICC profile. This enum value is not an exact match of the corresponding - * CICP value. */ + /** White point must be read from the @ref JxlColorEncoding white_point field, + * or as ICC profile. This enum value is not an exact match of the + * corresponding CICP value. */ JXL_WHITE_POINT_CUSTOM = 2, /** CIE Standard Illuminant E (equal-energy): 1/3, 1/3 */ JXL_WHITE_POINT_E = 10, @@ -55,10 +55,10 @@ typedef enum { } JxlWhitePoint; /** Built-in primaries for color encoding. When decoding, the primaries can be - * read from the JxlColorEncoding primaries_red_xy, primaries_green_xy and + * read from the @ref JxlColorEncoding primaries_red_xy, primaries_green_xy and * primaries_blue_xy fields regardless of the enum value. When encoding, the - * enum values except JXL_PRIMARIES_CUSTOM override the numerical fields. Some - * enum values match a subset of CICP (Rec. ITU-T H.273 | ISO/IEC + * enum values except ::JXL_PRIMARIES_CUSTOM override the numerical fields. + * Some enum values match a subset of CICP (Rec. ITU-T H.273 | ISO/IEC * 23091-2:2019(E)), however the white point and RGB primaries are separate * enums here. */ @@ -66,7 +66,7 @@ typedef enum { /** The CIE xy values of the red, green and blue primaries are: 0.639998686, 0.330010138; 0.300003784, 0.600003357; 0.150002046, 0.059997204 */ JXL_PRIMARIES_SRGB = 1, - /** Primaries must be read from the JxlColorEncoding primaries_red_xy, + /** Primaries must be read from the @ref JxlColorEncoding primaries_red_xy, * primaries_green_xy and primaries_blue_xy fields, or as ICC profile. This * enum value is not an exact match of the corresponding CICP value. */ JXL_PRIMARIES_CUSTOM = 2, @@ -94,7 +94,7 @@ typedef enum { JXL_TRANSFER_FUNCTION_DCI = 17, /** As specified in Rec. ITU-R BT.2100-1 (HLG) */ JXL_TRANSFER_FUNCTION_HLG = 18, - /** Transfer function follows power law given by the gamma value in + /** Transfer function follows power law given by the gamma value in @ref JxlColorEncoding. Not a CICP value. */ JXL_TRANSFER_FUNCTION_GAMMA = 65535, } JxlTransferFunction; @@ -118,7 +118,7 @@ typedef struct { */ JxlColorSpace color_space; - /** Built-in white point. If this value is JXL_WHITE_POINT_CUSTOM, must + /** Built-in white point. If this value is ::JXL_WHITE_POINT_CUSTOM, must * use the numerical whitepoint values from white_point_xy. */ JxlWhitePoint white_point; @@ -126,10 +126,10 @@ typedef struct { /** Numerical whitepoint values in CIE xy space. */ double white_point_xy[2]; - /** Built-in RGB primaries. If this value is JXL_PRIMARIES_CUSTOM, must + /** Built-in RGB primaries. If this value is ::JXL_PRIMARIES_CUSTOM, must * use the numerical primaries values below. This field and the custom values * below are unused and must be ignored if the color space is - * JXL_COLOR_SPACE_GRAY or JXL_COLOR_SPACE_XYB. + * ::JXL_COLOR_SPACE_GRAY or ::JXL_COLOR_SPACE_XYB. */ JxlPrimaries primaries; @@ -145,7 +145,8 @@ typedef struct { /** Transfer function if have_gamma is 0 */ JxlTransferFunction transfer_function; - /** Gamma value used when transfer_function is JXL_TRANSFER_FUNCTION_GAMMA + /** Gamma value used when transfer_function is @ref + * JXL_TRANSFER_FUNCTION_GAMMA */ double gamma; diff --git a/third_party/jpeg-xl/lib/include/jxl/decode.h b/third_party/jpeg-xl/lib/include/jxl/decode.h index eaee70fa61..599b8336f3 100644 --- a/third_party/jpeg-xl/lib/include/jxl/decode.h +++ b/third_party/jpeg-xl/lib/include/jxl/decode.h @@ -66,12 +66,12 @@ typedef enum { * @p size doesn't need to be a full image, only the beginning of the file. * * @return a flag indicating if a JPEG XL signature was found and what type. - * - @ref JXL_SIG_NOT_ENOUGH_BYTES if not enough bytes were passed to + * - ::JXL_SIG_NOT_ENOUGH_BYTES if not enough bytes were passed to * determine if a valid signature is there. - * - @ref JXL_SIG_INVALID if no valid signature found for JPEG XL decoding. - * - @ref JXL_SIG_CODESTREAM if a valid JPEG XL codestream signature was + * - ::JXL_SIG_INVALID if no valid signature found for JPEG XL decoding. + * - ::JXL_SIG_CODESTREAM if a valid JPEG XL codestream signature was * found. - * - @ref JXL_SIG_CONTAINER if a valid JPEG XL container signature was found. + * - ::JXL_SIG_CONTAINER if a valid JPEG XL container signature was found. */ JXL_EXPORT JxlSignature JxlSignatureCheck(const uint8_t* buf, size_t len); @@ -115,7 +115,7 @@ JXL_EXPORT void JxlDecoderDestroy(JxlDecoder* dec); /** * Return value for @ref JxlDecoderProcessInput. - * The values from @ref JXL_DEC_BASIC_INFO onwards are optional informative + * The values from ::JXL_DEC_BASIC_INFO onwards are optional informative * events that can be subscribed to, they are never returned if they * have not been registered with @ref JxlDecoderSubscribeEvents. */ @@ -123,12 +123,12 @@ typedef enum { /** Function call finished successfully, or decoding is finished and there is * nothing more to be done. * - * Note that @ref JxlDecoderProcessInput will return JXL_DEC_SUCCESS if all - * events that were registered with @ref JxlDecoderSubscribeEvents were + * Note that @ref JxlDecoderProcessInput will return ::JXL_DEC_SUCCESS if + * all events that were registered with @ref JxlDecoderSubscribeEvents were * processed, even before the end of the JPEG XL codestream. * * In this case, the return value @ref JxlDecoderReleaseInput will be the same - * as it was at the last signaled event. E.g. if JXL_DEC_FULL_IMAGE was + * as it was at the last signaled event. E.g. if ::JXL_DEC_FULL_IMAGE was * subscribed to, then all bytes from the end of the JPEG XL codestream * (including possible boxes needed for jpeg reconstruction) will be returned * as unprocessed. @@ -151,14 +151,14 @@ typedef enum { * In most cases, @ref JxlDecoderReleaseInput will return no unprocessed bytes * at this event, the only exceptions are if the previously set input ended * within (a) the raw codestream signature, (b) the signature box, (c) a box - * header, or (d) the first 4 bytes of a brob, ftyp, or jxlp box. In any of - * these cases the number of unprocessed bytes is less than 20. + * header, or (d) the first 4 bytes of a `brob`, `ftyp`, or `jxlp` box. In any + * of these cases the number of unprocessed bytes is less than 20. */ JXL_DEC_NEED_MORE_INPUT = 2, /** The decoder is able to decode a preview image and requests setting a * preview output buffer using @ref JxlDecoderSetPreviewOutBuffer. This occurs - * if @ref JXL_DEC_PREVIEW_IMAGE is requested and it is possible to decode a + * if ::JXL_DEC_PREVIEW_IMAGE is requested and it is possible to decode a * preview image from the codestream and the preview out buffer was not yet * set. There is maximum one preview image in a codestream. * In this case, @ref JxlDecoderReleaseInput will return all bytes from the @@ -179,13 +179,13 @@ typedef enum { /** The JPEG reconstruction buffer is too small for reconstructed JPEG * codestream to fit. @ref JxlDecoderSetJPEGBuffer must be called again to * make room for remaining bytes. This event may occur multiple times - * after @ref JXL_DEC_JPEG_RECONSTRUCTION. + * after ::JXL_DEC_JPEG_RECONSTRUCTION. */ JXL_DEC_JPEG_NEED_MORE_OUTPUT = 6, /** The box contents output buffer is too small. @ref JxlDecoderSetBoxBuffer * must be called again to make room for remaining bytes. This event may occur - * multiple times after @ref JXL_DEC_BOX. + * multiple times after ::JXL_DEC_BOX. */ JXL_DEC_BOX_NEED_MORE_OUTPUT = 7, @@ -201,7 +201,7 @@ typedef enum { /** Informative event by @ref JxlDecoderProcessInput * "JxlDecoderProcessInput": Color encoding or ICC profile from the * codestream header. This event occurs max once per image and always later - * than @ref JXL_DEC_BASIC_INFO and earlier than any pixel data. + * than ::JXL_DEC_BASIC_INFO and earlier than any pixel data. * In this case, @ref JxlDecoderReleaseInput will return all bytes from the * end of the image header (which is the start of the first frame) as * unprocessed. @@ -212,7 +212,7 @@ typedef enum { * "JxlDecoderProcessInput": Preview image, a small frame, decoded. This * event can only happen if the image has a preview frame encoded. This event * occurs max once for the codestream and always later than @ref - * JXL_DEC_COLOR_ENCODING and before @ref JXL_DEC_FRAME. + * JXL_DEC_COLOR_ENCODING and before ::JXL_DEC_FRAME. * In this case, @ref JxlDecoderReleaseInput will return all bytes from the * end of the preview frame as unprocessed. */ @@ -223,19 +223,19 @@ typedef enum { * JxlDecoderGetFrameHeader can be used at this point. A note on frames: * a JPEG XL image can have internal frames that are not intended to be * displayed (e.g. used for compositing a final frame), but this only returns - * displayed frames, unless @ref JxlDecoderSetCoalescing was set to JXL_FALSE: - * in that case, the individual layers are returned, without blending. Note - * that even when coalescing is disabled, only frames of type kRegularFrame - * are returned; frames of type kReferenceOnly and kLfFrame are always for - * internal purposes only and cannot be accessed. A displayed frame either has - * an animation duration or is the only or last frame in the image. This event - * occurs max once per displayed frame, always later than @ref - * JXL_DEC_COLOR_ENCODING, and always earlier than any pixel data. While - * JPEG XL supports encoding a single frame as the composition of multiple - * internal sub-frames also called frames, this event is not indicated for the - * internal frames. - * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the frame header (including ToC) as unprocessed. + * displayed frames, unless @ref JxlDecoderSetCoalescing was set to @ref + * JXL_FALSE "JXL_FALSE": in that case, the individual layers are returned, + * without blending. Note that even when coalescing is disabled, only frames + * of type kRegularFrame are returned; frames of type kReferenceOnly + * and kLfFrame are always for internal purposes only and cannot be accessed. + * A displayed frame either has an animation duration or is the only or last + * frame in the image. This event occurs max once per displayed frame, always + * later than ::JXL_DEC_COLOR_ENCODING, and always earlier than any pixel + * data. While JPEG XL supports encoding a single frame as the composition of + * multiple internal sub-frames also called frames, this event is not + * indicated for the internal frames. In this case, @ref + * JxlDecoderReleaseInput will return all bytes from the end of the frame + * header (including ToC) as unprocessed. */ JXL_DEC_FRAME = 0x400, @@ -246,7 +246,7 @@ typedef enum { * not this return status only indicates we're past this point in the * codestream. This event occurs max once per frame. * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the frame (or if @ref JXL_DEC_JPEG_RECONSTRUCTION is subscribed to, + * end of the frame (or if ::JXL_DEC_JPEG_RECONSTRUCTION is subscribed to, * from the end of the last box that is needed for jpeg reconstruction) as * unprocessed. */ @@ -259,9 +259,9 @@ typedef enum { * is set a byte stream identical to the JPEG codestream used to encode the * image will be written to the JPEG reconstruction buffer instead of pixels * to the image out buffer. This event occurs max once per image and always - * before @ref JXL_DEC_FULL_IMAGE. + * before ::JXL_DEC_FULL_IMAGE. * In this case, @ref JxlDecoderReleaseInput will return all bytes from the - * end of the 'jbrd' box as unprocessed. + * end of the `jbrd` box as unprocessed. */ JXL_DEC_JPEG_RECONSTRUCTION = 0x2000, @@ -290,8 +290,8 @@ typedef enum { * * The buffer set with @ref JxlDecoderSetBoxBuffer must be set again for each * next box to be obtained, or can be left unset to skip outputting this box. - * The output buffer contains the full box data when the next @ref JXL_DEC_BOX - * event or @ref JXL_DEC_SUCCESS occurs. @ref JXL_DEC_BOX occurs for all + * The output buffer contains the full box data when the next ::JXL_DEC_BOX + * event or ::JXL_DEC_SUCCESS occurs. ::JXL_DEC_BOX occurs for all * boxes, including non-metadata boxes such as the signature box or codestream * boxes. To check whether the box is a metadata type for respectively EXIF, * XMP or JUMBF, use @ref JxlDecoderGetBoxType and check for types "Exif", @@ -324,26 +324,40 @@ typedef enum { * Setting a progressive detail with value N implies all progressive details * with smaller or equal value. Currently only the following level of * progressive detail is implemented: - * - kDC (which implies kFrames) - * - kLastPasses (which implies kDC and kFrames) - * - kPasses (which implies kLastPasses, kDC and kFrames) + * - @ref kDC (which implies kFrames) + * - @ref kLastPasses (which implies @ref kDC and @ref kFrames) + * - @ref kPasses (which implies @ref kLastPasses, kDC and @ref kFrames) */ typedef enum { - // after completed kRegularFrames + /** + * after completed kRegularFrames + */ kFrames = 0, - // after completed DC (1:8) + /** + * after completed DC (1:8) + */ kDC = 1, - // after completed AC passes that are the last pass for their resolution - // target. + /** + * after completed AC passes that are the last pass for their resolution + * target. + */ kLastPasses = 2, - // after completed AC passes that are not the last pass for their resolution - // target. + /** + * after completed AC passes that are not the last pass for their resolution + * target. + */ kPasses = 3, - // during DC frame when lower resolution are completed (1:32, 1:16) + /** + * during DC frame when lower resolution are completed (1:32, 1:16) + */ kDCProgressive = 4, - // after completed groups + /** + * after completed groups + */ kDCGroups = 5, - // after completed groups + /** + * after completed groups + */ kGroups = 6, } JxlProgressiveDetail; @@ -354,8 +368,8 @@ typedef enum { * more efficiently with @ref JxlDecoderSkipFrames. Settings such as parallel * runner or subscribed events are kept. After rewind, @ref * JxlDecoderSubscribeEvents can be used again, and it is feasible to leave out - * events that were already handled before, such as @ref JXL_DEC_BASIC_INFO - * and @ref JXL_DEC_COLOR_ENCODING, since they will provide the same information + * events that were already handled before, such as ::JXL_DEC_BASIC_INFO + * and ::JXL_DEC_COLOR_ENCODING, since they will provide the same information * as before. * The difference to @ref JxlDecoderReset is that some state is kept, namely * settings set by a call to @@ -376,14 +390,14 @@ JXL_EXPORT void JxlDecoderRewind(JxlDecoder* dec); * the input, but will not output the frame events. It can be more efficient * when skipping frames, and even more so when using this after @ref * JxlDecoderRewind. If the decoder is already processing a frame (could - * have emitted @ref JXL_DEC_FRAME but not yet @ref JXL_DEC_FULL_IMAGE), it + * have emitted ::JXL_DEC_FRAME but not yet ::JXL_DEC_FULL_IMAGE), it * starts skipping from the next frame. If the amount is larger than the amount * of frames remaining in the image, all remaining frames are skipped. Calling * this function multiple times adds the amount to skip to the already existing * amount. * * A frame here is defined as a frame that without skipping emits events such - * as @ref JXL_DEC_FRAME and @ref JXL_DEC_FULL_IMAGE, frames that are internal + * as ::JXL_DEC_FRAME and ::JXL_DEC_FULL_IMAGE, frames that are internal * to the file format but are not rendered as part of an animation, or are not * the final still frame of a still image, are not counted. * @@ -394,14 +408,14 @@ JXL_EXPORT void JxlDecoderSkipFrames(JxlDecoder* dec, size_t amount); /** * Skips processing the current frame. Can be called after frame processing - * already started, signaled by a @ref JXL_DEC_NEED_IMAGE_OUT_BUFFER event, - * but before the corresponding @ref JXL_DEC_FULL_IMAGE event. The next signaled - * event will be another @ref JXL_DEC_FRAME, or @ref JXL_DEC_SUCCESS if there + * already started, signaled by a ::JXL_DEC_NEED_IMAGE_OUT_BUFFER event, + * but before the corresponding ::JXL_DEC_FULL_IMAGE event. The next signaled + * event will be another ::JXL_DEC_FRAME, or ::JXL_DEC_SUCCESS if there * are no more frames. If pixel data is required from the already processed part * of the frame, @ref JxlDecoderFlushImage must be called before this. * * @param dec decoder object - * @return @ref JXL_DEC_SUCCESS if there is a frame to skip, and @ref + * @return ::JXL_DEC_SUCCESS if there is a frame to skip, and @ref * JXL_DEC_ERROR if the function was not called during frame processing. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSkipCurrentFrame(JxlDecoder* dec); @@ -415,7 +429,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSkipCurrentFrame(JxlDecoder* dec); * be NULL to use the default, single-threaded, runner. A multithreaded * runner should be set to reach fast performance. * @param parallel_runner_opaque opaque pointer for parallel_runner. - * @return @ref JXL_DEC_SUCCESS if the runner was set, @ref JXL_DEC_ERROR + * @return ::JXL_DEC_SUCCESS if the runner was set, ::JXL_DEC_ERROR * otherwise (the previous runner remains set). */ JXL_EXPORT JxlDecoderStatus @@ -439,7 +453,7 @@ JxlDecoderSetParallelRunner(JxlDecoder* dec, JxlParallelRunner parallel_runner, */ JXL_EXPORT size_t JxlDecoderSizeHintBasicInfo(const JxlDecoder* dec); -/** Select for which informative events, i.e. @ref JXL_DEC_BASIC_INFO, etc., the +/** Select for which informative events, i.e. ::JXL_DEC_BASIC_INFO, etc., the * decoder should return with a status. It is not required to subscribe to any * events, data can still be requested from the decoder as soon as it available. * By default, the decoder is subscribed to no events (events_wanted == 0), and @@ -449,7 +463,7 @@ JXL_EXPORT size_t JxlDecoderSizeHintBasicInfo(const JxlDecoder* dec); * * @param dec decoder object * @param events_wanted bitfield of desired events. - * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise. + * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec, int events_wanted); @@ -459,14 +473,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec, * indicating that the decoder must perform a rotation and/or * mirroring to the encoded image data. * - * - If skip_reorientation is JXL_FALSE (the default): the decoder + * - If skip_reorientation is ::JXL_FALSE (the default): the decoder * will apply the transformation from the orientation setting, hence * rendering the image according to its specified intent. When - * producing a JxlBasicInfo, the decoder will always set the + * producing a @ref JxlBasicInfo, the decoder will always set the * orientation field to JXL_ORIENT_IDENTITY (matching the returned * pixel data) and also align xsize and ysize so that they correspond * to the width and the height of the returned pixel data. - * - If skip_reorientation is JXL_TRUE: the decoder will skip + * - If skip_reorientation is ::JXL_TRUE "JXL_TRUE": the decoder will skip * applying the transformation from the orientation setting, returning * the image in the as-in-bitstream pixeldata orientation. * This may be faster to decode since the decoder doesn't have to apply the @@ -483,17 +497,18 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec, * * @param dec decoder object * @param skip_reorientation JXL_TRUE to enable, JXL_FALSE to disable. - * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise. + * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL skip_reorientation); /** * Enables or disables preserving of associated alpha channels. If - * unpremul_alpha is set to JXL_FALSE then for associated alpha channel, the - * pixel data is returned with premultiplied colors. If it is set to JXL_TRUE, - * The colors will be unpremultiplied based on the alpha channel. This function - * has no effect if the image does not have an associated alpha channel. + * unpremul_alpha is set to ::JXL_FALSE then for associated alpha channel, + * the pixel data is returned with premultiplied colors. If it is set to @ref + * JXL_TRUE, The colors will be unpremultiplied based on the alpha channel. This + * function has no effect if the image does not have an associated alpha + * channel. * * By default, this option is disabled, and the returned pixel data "as is". * @@ -501,20 +516,20 @@ JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL skip_reorientation); * * @param dec decoder object * @param unpremul_alpha JXL_TRUE to enable, JXL_FALSE to disable. - * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise. + * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetUnpremultiplyAlpha(JxlDecoder* dec, JXL_BOOL unpremul_alpha); /** Enables or disables rendering spot colors. By default, spot colors * are rendered, which is OK for viewing the decoded image. If render_spotcolors - * is JXL_FALSE, then spot colors are not rendered, and have to be retrieved - * separately using @ref JxlDecoderSetExtraChannelBuffer. This is useful for - * e.g. printing applications. + * is ::JXL_FALSE, then spot colors are not rendered, and have to be + * retrieved separately using @ref JxlDecoderSetExtraChannelBuffer. This is + * useful for e.g. printing applications. * * @param dec decoder object * @param render_spotcolors JXL_TRUE to enable (default), JXL_FALSE to disable. - * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise. + * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetRenderSpotcolors(JxlDecoder* dec, JXL_BOOL render_spotcolors); @@ -530,7 +545,7 @@ JxlDecoderSetRenderSpotcolors(JxlDecoder* dec, JXL_BOOL render_spotcolors); * @param dec decoder object * @param coalescing JXL_TRUE to enable coalescing (default), JXL_FALSE to * disable it. - * @return @ref JXL_DEC_SUCCESS if no error, @ref JXL_DEC_ERROR otherwise. + * @return ::JXL_DEC_SUCCESS if no error, ::JXL_DEC_ERROR otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec, JXL_BOOL coalescing); @@ -547,32 +562,32 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec, * * The returned status indicates whether the decoder needs more input bytes, or * more output buffer for a certain type of output data. No matter what the - * returned status is (other than @ref JXL_DEC_ERROR), new information, such + * returned status is (other than ::JXL_DEC_ERROR), new information, such * as @ref JxlDecoderGetBasicInfo, may have become available after this call. - * When the return value is not @ref JXL_DEC_ERROR or @ref JXL_DEC_SUCCESS, the + * When the return value is not ::JXL_DEC_ERROR or ::JXL_DEC_SUCCESS, the * decoding requires more @ref JxlDecoderProcessInput calls to continue. * * @param dec decoder object - * @return @ref JXL_DEC_SUCCESS when decoding finished and all events handled. + * @return ::JXL_DEC_SUCCESS when decoding finished and all events handled. * If you still have more unprocessed input data anyway, then you can still * continue by using @ref JxlDecoderSetInput and calling @ref * JxlDecoderProcessInput again, similar to handling @ref - * JXL_DEC_NEED_MORE_INPUT. @ref JXL_DEC_SUCCESS can occur instead of @ref + * JXL_DEC_NEED_MORE_INPUT. ::JXL_DEC_SUCCESS can occur instead of @ref * JXL_DEC_NEED_MORE_INPUT when, for example, the input data ended right at * the boundary of a box of the container format, all essential codestream * boxes were already decoded, but extra metadata boxes are still present in * the next data. @ref JxlDecoderProcessInput cannot return success if all * codestream boxes have not been seen yet. - * @return @ref JXL_DEC_ERROR when decoding failed, e.g. invalid codestream. + * @return ::JXL_DEC_ERROR when decoding failed, e.g. invalid codestream. * TODO(lode): document the input data mechanism - * @return @ref JXL_DEC_NEED_MORE_INPUT when more input data is necessary. - * @return @ref JXL_DEC_BASIC_INFO when basic info such as image dimensions is + * @return ::JXL_DEC_NEED_MORE_INPUT when more input data is necessary. + * @return ::JXL_DEC_BASIC_INFO when basic info such as image dimensions is * available and this informative event is subscribed to. - * @return @ref JXL_DEC_COLOR_ENCODING when color profile information is + * @return ::JXL_DEC_COLOR_ENCODING when color profile information is * available and this informative event is subscribed to. - * @return @ref JXL_DEC_PREVIEW_IMAGE when preview pixel information is + * @return ::JXL_DEC_PREVIEW_IMAGE when preview pixel information is * available and output in the preview buffer. - * @return @ref JXL_DEC_FULL_IMAGE when all pixel information at highest detail + * @return ::JXL_DEC_FULL_IMAGE when all pixel information at highest detail * is available and has been output in the pixel buffer. */ JXL_EXPORT JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec); @@ -588,8 +603,8 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec); * @param dec decoder object * @param data pointer to next bytes to read from * @param size amount of bytes available starting from data - * @return @ref JXL_DEC_ERROR if input was already set without releasing or @ref - * JxlDecoderCloseInput was already called, @ref JXL_DEC_SUCCESS otherwise. + * @return ::JXL_DEC_ERROR if input was already set without releasing or @ref + * JxlDecoderCloseInput was already called, ::JXL_DEC_SUCCESS otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec, const uint8_t* data, @@ -602,17 +617,17 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec, * whenever any input is already set and new input needs to be added with @ref * JxlDecoderSetInput, but is not required before @ref JxlDecoderDestroy or @ref * JxlDecoderReset. Calling @ref JxlDecoderReleaseInput when no input is set is - * not an error and returns 0. + * not an error and returns `0`. * * @param dec decoder object * @return The amount of bytes the decoder has not yet processed that are still - * remaining in the data set by @ref JxlDecoderSetInput, or 0 if no input is - * set or @ref JxlDecoderReleaseInput was already called. For a next call - * to @ref JxlDecoderProcessInput, the buffer must start with these - * unprocessed bytes. From this value it is possible to infer the position - * of certain JPEG XL codestream elements (e.g. end of headers, frame - * start/end). See the documentation of individual values of @ref - * JxlDecoderStatus for more information. + * remaining in the data set by @ref JxlDecoderSetInput, or `0` if no input + * is set or @ref JxlDecoderReleaseInput was already called. For a next call to + * @ref JxlDecoderProcessInput, the buffer must start with these unprocessed + * bytes. From this value it is possible to infer the position of certain JPEG + * XL codestream elements (e.g. end of headers, frame start/end). See the + * documentation of individual values of @ref JxlDecoderStatus for more + * information. */ JXL_EXPORT size_t JxlDecoderReleaseInput(JxlDecoder* dec); @@ -621,9 +636,9 @@ JXL_EXPORT size_t JxlDecoderReleaseInput(JxlDecoder* dec); * will be called. This function allows the decoder to determine correctly if it * should return success, need more input or error in certain cases. For * backwards compatibility with a previous version of the API, using this - * function is optional when not using the @ref JXL_DEC_BOX event (the decoder + * function is optional when not using the ::JXL_DEC_BOX event (the decoder * is able to determine the end of the image frames without marking the end), - * but using this function is required when using @ref JXL_DEC_BOX for getting + * but using this function is required when using ::JXL_DEC_BOX for getting * metadata box contents. This function does not replace @ref * JxlDecoderReleaseInput, that function should still be called if its return * value is needed. @@ -643,8 +658,8 @@ JXL_EXPORT void JxlDecoderCloseInput(JxlDecoder* dec); * @param dec decoder object * @param info struct to copy the information into, or NULL to only check * whether the information is available through the return value. - * @return @ref JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR + * @return ::JXL_DEC_SUCCESS if the value is available, @ref + * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR * in case of other error conditions. */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec, @@ -652,14 +667,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec, /** * Outputs information for extra channel at the given index. The index must be - * smaller than num_extra_channels in the associated JxlBasicInfo. + * smaller than num_extra_channels in the associated @ref JxlBasicInfo. * * @param dec decoder object * @param index index of the extra channel to query. * @param info struct to copy the information into, or NULL to only check * whether the information is available through the return value. - * @return @ref JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR + * @return ::JXL_DEC_SUCCESS if the value is available, @ref + * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR * in case of other error conditions. */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelInfo( @@ -667,16 +682,16 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelInfo( /** * Outputs name for extra channel at the given index in UTF-8. The index must be - * smaller than num_extra_channels in the associated JxlBasicInfo. The buffer - * for name must have at least name_length + 1 bytes allocated, gotten from - * the associated JxlExtraChannelInfo. + * smaller than `num_extra_channels` in the associated @ref JxlBasicInfo. The + * buffer for name must have at least `name_length + 1` bytes allocated, gotten + * from the associated @ref JxlExtraChannelInfo. * * @param dec decoder object * @param index index of the extra channel to query. * @param name buffer to copy the name into * @param size size of the name buffer in bytes - * @return @ref JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR + * @return ::JXL_DEC_SUCCESS if the value is available, @ref + * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR * in case of other error conditions. */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelName(const JxlDecoder* dec, @@ -719,7 +734,7 @@ typedef enum { * problematic, in that: while ICC profiles can encode a transfer function * that happens to approximate those of PQ and HLG (HLG for only one given * system gamma at a time, and necessitating a 3D LUT if gamma is to be - * different from 1), they cannot (before ICCv4.4) semantically signal that + * different from `1`), they cannot (before ICCv4.4) semantically signal that * this is the color space that they represent. Therefore, they will * typically not actually be interpreted as representing an HDR color space. * This is especially detrimental to PQ which will then be interpreted as if @@ -741,8 +756,8 @@ typedef enum { * or the color profile of the decoded pixels. * @param color_encoding struct to copy the information into, or NULL to only * check whether the information is available through the return value. - * @return @ref JXL_DEC_SUCCESS if the data is available and returned, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR in + * @return ::JXL_DEC_SUCCESS if the data is available and returned, @ref + * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR in * case the encoded structured color profile does not exist in the * codestream. */ @@ -766,10 +781,10 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetColorAsEncodedProfile( * or the color profile of the decoded pixels. * @param size variable to output the size into, or NULL to only check the * return status. - * @return @ref JXL_DEC_SUCCESS if the ICC profile is available, @ref + * @return ::JXL_DEC_SUCCESS if the ICC profile is available, @ref * JXL_DEC_NEED_MORE_INPUT if the decoder has not yet received enough * input data to determine whether an ICC profile is available or what its - * size is, @ref JXL_DEC_ERROR in case the ICC profile is not available and + * size is, ::JXL_DEC_ERROR in case the ICC profile is not available and * cannot be generated. */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetICCProfileSize( @@ -785,8 +800,8 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetICCProfileSize( * or the color profile of the decoded pixels. * @param icc_profile buffer to copy the ICC profile into * @param size size of the icc_profile buffer in bytes - * @return @ref JXL_DEC_SUCCESS if the profile was successfully returned is - * available, @ref JXL_DEC_NEED_MORE_INPUT if not yet available, @ref + * @return ::JXL_DEC_SUCCESS if the profile was successfully returned is + * available, ::JXL_DEC_NEED_MORE_INPUT if not yet available, @ref * JXL_DEC_ERROR if the profile doesn't exist or the output size is not * large enough. */ @@ -801,7 +816,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetColorAsICCProfile( * * @param dec decoder object * @param color_encoding the default color encoding to set - * @return @ref JXL_DEC_SUCCESS if the preference was set successfully, @ref + * @return ::JXL_DEC_SUCCESS if the preference was set successfully, @ref * JXL_DEC_ERROR otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreferredColorProfile( @@ -814,7 +829,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreferredColorProfile( * change from version to version. * @param dec decoder object * @param desired_intensity_target the intended target peak luminance - * @return @ref JXL_DEC_SUCCESS if the preference was set successfully, @ref + * @return ::JXL_DEC_SUCCESS if the preference was set successfully, @ref * JXL_DEC_ERROR otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDesiredIntensityTarget( @@ -823,7 +838,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDesiredIntensityTarget( /** * Sets the desired output color profile of the decoded image either from a * color encoding or an ICC profile. Valid calls of this function have either @c - * color_encoding or @c icc_data set to NULL and @c icc_size must be 0 if and + * color_encoding or @c icc_data set to NULL and @c icc_size must be `0` if and * only if @c icc_data is NULL. * * Depending on whether a color management system (CMS) has been set the @@ -848,17 +863,17 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDesiredIntensityTarget( * If called with an ICC profile (after a call to @ref JxlDecoderSetCms), the * ICC profile has to be a valid RGB or grayscale color profile. * - * Can only be set after the @ref JXL_DEC_COLOR_ENCODING event occurred and + * Can only be set after the ::JXL_DEC_COLOR_ENCODING event occurred and * before any other event occurred, and should be used before getting - * JXL_COLOR_PROFILE_TARGET_DATA. + * ::JXL_COLOR_PROFILE_TARGET_DATA. * - * This function must not be called before JxlDecoderSetCms. + * This function must not be called before @ref JxlDecoderSetCms. * * @param dec decoder orbject * @param color_encoding the output color encoding * @param icc_data bytes of the icc profile * @param icc_size size of the icc profile in bytes - * @return @ref JXL_DEC_SUCCESS if the color profile was set successfully, @ref + * @return ::JXL_DEC_SUCCESS if the color profile was set successfully, @ref * JXL_DEC_ERROR otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetOutputColorProfile( @@ -891,7 +906,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetCms(JxlDecoder* dec, * @param dec decoder object * @param format format of pixels * @param size output value, buffer size in bytes - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as * information not available yet. */ JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize( @@ -901,15 +916,15 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize( * Sets the buffer to write the small resolution preview image * to. The size of the buffer must be at least as large as given by @ref * JxlDecoderPreviewOutBufferSize. The buffer follows the format described - * by JxlPixelFormat. The preview image dimensions are given by the - * JxlPreviewHeader. The buffer is owned by the caller. + * by @ref JxlPixelFormat. The preview image dimensions are given by the + * @ref JxlPreviewHeader. The buffer is owned by the caller. * * @param dec decoder object * @param format format of pixels. Object owned by user and its contents are * copied internally. * @param buffer buffer type to output the pixel data to * @param size size of buffer in bytes - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as * size too small. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer( @@ -917,14 +932,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer( /** * Outputs the information from the frame, such as duration when have_animation. - * This function can be called when @ref JXL_DEC_FRAME occurred for the current + * This function can be called when ::JXL_DEC_FRAME occurred for the current * frame, even when have_animation in the JxlBasicInfo is JXL_FALSE. * * @param dec decoder object * @param header struct to copy the information into, or NULL to only check * whether the information is available through the return value. - * @return @ref JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR in + * @return ::JXL_DEC_SUCCESS if the value is available, @ref + * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR in * case of other error conditions. */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec, @@ -932,14 +947,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec, /** * Outputs name for the current frame. The buffer for name must have at least - * name_length + 1 bytes allocated, gotten from the associated JxlFrameHeader. + * `name_length + 1` bytes allocated, gotten from the associated JxlFrameHeader. * * @param dec decoder object * @param name buffer to copy the name into * @param size size of the name buffer in bytes, including zero termination - * character, so this must be at least JxlFrameHeader.name_length + 1. - * @return @ref JXL_DEC_SUCCESS if the value is available, @ref - * JXL_DEC_NEED_MORE_INPUT if not yet available, @ref JXL_DEC_ERROR in + * character, so this must be at least @ref JxlFrameHeader.name_length + 1. + * @return ::JXL_DEC_SUCCESS if the value is available, @ref + * JXL_DEC_NEED_MORE_INPUT if not yet available, ::JXL_DEC_ERROR in * case of other error conditions. */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameName(const JxlDecoder* dec, @@ -947,15 +962,15 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameName(const JxlDecoder* dec, /** * Outputs the blend information for the current frame for a specific extra - * channel. This function can be called when @ref JXL_DEC_FRAME occurred for the - * current frame, even when have_animation in the JxlBasicInfo is JXL_FALSE. - * This information is only useful if coalescing is disabled; otherwise the - * decoder will have performed blending already. + * channel. This function can be called when ::JXL_DEC_FRAME occurred for the + * current frame, even when have_animation in the @ref JxlBasicInfo is @ref + * JXL_FALSE. This information is only useful if coalescing is disabled; + * otherwise the decoder will have performed blending already. * * @param dec decoder object * @param index the index of the extra channel * @param blend_info struct to copy the information into - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelBlendInfo( const JxlDecoder* dec, size_t index, JxlBlendInfo* blend_info); @@ -965,14 +980,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelBlendInfo( * given format. This is the buffer for @ref JxlDecoderSetImageOutBuffer. * Requires that the basic image information is available in the decoder in the * case of coalescing enabled (default). In case coalescing is disabled, this - * can only be called after the @ref JXL_DEC_FRAME event occurs. In that case, + * can only be called after the ::JXL_DEC_FRAME event occurs. In that case, * it will return the size required to store the possibly cropped frame (which * can be larger or smaller than the image dimensions). * * @param dec decoder object * @param format format of the pixels. * @param size output value, buffer size in bytes - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as * information not available yet. */ JXL_EXPORT JxlDecoderStatus JxlDecoderImageOutBufferSize( @@ -980,18 +995,18 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderImageOutBufferSize( /** * Sets the buffer to write the full resolution image to. This can be set when - * the @ref JXL_DEC_FRAME event occurs, must be set when the @ref + * the ::JXL_DEC_FRAME event occurs, must be set when the @ref * JXL_DEC_NEED_IMAGE_OUT_BUFFER event occurs, and applies only for the * current frame. The size of the buffer must be at least as large as given * by @ref JxlDecoderImageOutBufferSize. The buffer follows the format described - * by JxlPixelFormat. The buffer is owned by the caller. + * by @ref JxlPixelFormat. The buffer is owned by the caller. * * @param dec decoder object * @param format format of the pixels. Object owned by user and its contents * are copied internally. * @param buffer buffer type to output the pixel data to * @param size size of buffer in bytes - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as * size too small. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetImageOutBuffer( @@ -1062,15 +1077,15 @@ typedef void (*JxlImageOutDestroyCallback)(void* run_opaque); /** * Sets pixel output callback. This is an alternative to @ref - * JxlDecoderSetImageOutBuffer. This can be set when the @ref JXL_DEC_FRAME - * event occurs, must be set when the @ref JXL_DEC_NEED_IMAGE_OUT_BUFFER event + * JxlDecoderSetImageOutBuffer. This can be set when the ::JXL_DEC_FRAME + * event occurs, must be set when the ::JXL_DEC_NEED_IMAGE_OUT_BUFFER event * occurs, and applies only for the current frame. Only one of @ref * JxlDecoderSetImageOutBuffer or @ref JxlDecoderSetImageOutCallback may be used * for the same frame, not both at the same time. * * The callback will be called multiple times, to receive the image * data in small chunks. The callback receives a horizontal stripe of pixel - * data, 1 pixel high, xsize pixels wide, called a scanline. The xsize here is + * data, `1` pixel high, xsize pixels wide, called a scanline. The xsize here is * not the same as the full image width, the scanline may be a partial section, * and xsize may differ between calls. The user can then process and/or copy the * partial scanline to an image buffer. The callback may be called @@ -1099,7 +1114,7 @@ typedef void (*JxlImageOutDestroyCallback)(void* run_opaque); * data. * @param opaque optional user data, which will be passed on to the callback, * may be NULL. - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such * as @ref JxlDecoderSetImageOutBuffer already set. */ JXL_EXPORT JxlDecoderStatus @@ -1122,7 +1137,7 @@ JxlDecoderSetImageOutCallback(JxlDecoder* dec, const JxlPixelFormat* format, * @param init_opaque optional user data passed to @c init_callback, may be NULL * (unlike the return value from @c init_callback which may only be NULL if * initialization failed). - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such * as @ref JxlDecoderSetImageOutBuffer having already been called. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetMultithreadedImageOutCallback( @@ -1137,12 +1152,12 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetMultithreadedImageOutCallback( * * @param dec decoder object * @param format format of the pixels. The num_channels value is ignored and is - * always treated to be 1. + * always treated to be `1`. * @param size output value, buffer size in bytes * @param index which extra channel to get, matching the index used in @ref * JxlDecoderGetExtraChannelInfo. Must be smaller than num_extra_channels in - * the associated JxlBasicInfo. - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as + * the associated @ref JxlBasicInfo. + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as * information not available yet or invalid index. */ JXL_EXPORT JxlDecoderStatus JxlDecoderExtraChannelBufferSize( @@ -1151,13 +1166,13 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderExtraChannelBufferSize( /** * Sets the buffer to write an extra channel to. This can be set when - * the @ref JXL_DEC_FRAME or @ref JXL_DEC_NEED_IMAGE_OUT_BUFFER event occurs, + * the ::JXL_DEC_FRAME or ::JXL_DEC_NEED_IMAGE_OUT_BUFFER event occurs, * and applies only for the current frame. The size of the buffer must be at * least as large as given by @ref JxlDecoderExtraChannelBufferSize. The buffer - * follows the format described by JxlPixelFormat, but where num_channels is 1. - * The buffer is owned by the caller. The amount of extra channels is given by - * the num_extra_channels field in the associated JxlBasicInfo, and the - * information of individual extra channels can be queried with @ref + * follows the format described by @ref JxlPixelFormat, but where num_channels + * is `1`. The buffer is owned by the caller. The amount of extra channels is + * given by the num_extra_channels field in the associated @ref JxlBasicInfo, + * and the information of individual extra channels can be queried with @ref * JxlDecoderGetExtraChannelInfo. To get multiple extra channels, this function * must be called multiple times, once for each wanted index. Not all images * have extra channels. The alpha channel is an extra channel and can be gotten @@ -1170,13 +1185,13 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderExtraChannelBufferSize( * @param dec decoder object * @param format format of the pixels. Object owned by user and its contents * are copied internally. The num_channels value is ignored and is always - * treated to be 1. + * treated to be `1`. * @param buffer buffer type to output the pixel data to * @param size size of buffer in bytes * @param index which extra channel to get, matching the index used in @ref * JxlDecoderGetExtraChannelInfo. Must be smaller than num_extra_channels in - * the associated JxlBasicInfo. - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as + * the associated @ref JxlBasicInfo. + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as * size too small or invalid index. */ JXL_EXPORT JxlDecoderStatus @@ -1197,8 +1212,8 @@ JxlDecoderSetExtraChannelBuffer(JxlDecoder* dec, const JxlPixelFormat* format, * @param dec decoder object * @param data pointer to next bytes to write to * @param size amount of bytes available starting from data - * @return @ref JXL_DEC_ERROR if output buffer was already set and @ref - * JxlDecoderReleaseJPEGBuffer was not called on it, @ref JXL_DEC_SUCCESS + * @return ::JXL_DEC_ERROR if output buffer was already set and @ref + * JxlDecoderReleaseJPEGBuffer was not called on it, ::JXL_DEC_SUCCESS * otherwise */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec, @@ -1213,11 +1228,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec, * JxlDecoderDestroy or @ref JxlDecoderReset. * * Calling @ref JxlDecoderReleaseJPEGBuffer when no buffer is set is - * not an error and returns 0. + * not an error and returns `0`. * * @param dec decoder object * @return the amount of bytes the decoder has not yet written to of the data - * set by @ref JxlDecoderSetJPEGBuffer, or 0 if no buffer is set or @ref + * set by @ref JxlDecoderSetJPEGBuffer, or `0` if no buffer is set or @ref * JxlDecoderReleaseJPEGBuffer was already called. */ JXL_EXPORT size_t JxlDecoderReleaseJPEGBuffer(JxlDecoder* dec); @@ -1233,15 +1248,15 @@ JXL_EXPORT size_t JxlDecoderReleaseJPEGBuffer(JxlDecoder* dec); * JxlDecoderReleaseBoxBuffer, bytes that the decoder has already output * should not be included, only the remaining bytes output must be set. * - * The @ref JxlDecoderReleaseBoxBuffer must be used at the next @ref JXL_DEC_BOX - * event or final @ref JXL_DEC_SUCCESS event to compute the size of the output + * The @ref JxlDecoderReleaseBoxBuffer must be used at the next ::JXL_DEC_BOX + * event or final ::JXL_DEC_SUCCESS event to compute the size of the output * box bytes. * * @param dec decoder object * @param data pointer to next bytes to write to * @param size amount of bytes available starting from data - * @return @ref JXL_DEC_ERROR if output buffer was already set and @ref - * JxlDecoderReleaseBoxBuffer was not called on it, @ref JXL_DEC_SUCCESS + * @return ::JXL_DEC_ERROR if output buffer was already set and @ref + * JxlDecoderReleaseBoxBuffer was not called on it, ::JXL_DEC_SUCCESS * otherwise */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetBoxBuffer(JxlDecoder* dec, @@ -1256,11 +1271,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetBoxBuffer(JxlDecoder* dec, * JxlDecoderDestroy or @ref JxlDecoderReset. * * Calling @ref JxlDecoderReleaseBoxBuffer when no buffer is set is - * not an error and returns 0. + * not an error and returns `0`. * * @param dec decoder object * @return the amount of bytes the decoder has not yet written to of the data - * set by @ref JxlDecoderSetBoxBuffer, or 0 if no buffer is set or @ref + * set by @ref JxlDecoderSetBoxBuffer, or `0` if no buffer is set or @ref * JxlDecoderReleaseBoxBuffer was already called. */ JXL_EXPORT size_t JxlDecoderReleaseBoxBuffer(JxlDecoder* dec); @@ -1274,23 +1289,23 @@ JXL_EXPORT size_t JxlDecoderReleaseBoxBuffer(JxlDecoder* dec); * finished. * * The default mode is raw. This setting can only be changed before decoding, or - * directly after a @ref JXL_DEC_BOX event, and is remembered until the decoder + * directly after a ::JXL_DEC_BOX event, and is remembered until the decoder * is reset or destroyed. * * Enabling decompressed mode requires Brotli support from the library. * * @param dec decoder object - * @param decompress JXL_TRUE to transparently decompress, JXL_FALSE to get - * boxes in raw mode. - * @return @ref JXL_DEC_ERROR if decompressed mode is set and Brotli is not - * available, @ref JXL_DEC_SUCCESS otherwise. + * @param decompress ::JXL_TRUE to transparently decompress, ::JXL_FALSE + * to get boxes in raw mode. + * @return ::JXL_DEC_ERROR if decompressed mode is set and Brotli is not + * available, ::JXL_DEC_SUCCESS otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec, JXL_BOOL decompress); /** - * Outputs the type of the current box, after a @ref JXL_DEC_BOX event occurred, - * as 4 characters without null termination character. In case of a compressed + * Outputs the type of the current box, after a ::JXL_DEC_BOX event occurred, + * as `4` characters without null termination character. In case of a compressed * "brob" box, this will return "brob" if the decompressed argument is * JXL_FALSE, or the underlying box type if the decompressed argument is * JXL_TRUE. @@ -1306,15 +1321,15 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec, * - "xml ": a box with XML data, in particular XMP metadata. * - "jumb": a JUMBF superbox (JPEG Universal Metadata Box Format, ISO/IEC * 19566-5). - * - "JXL ": mandatory signature box, must come first, 12 bytes long including - * the box header - * - "ftyp": a second mandatory signature box, must come second, 20 bytes long - * including the box header - * - "jxll": a JXL level box. This indicates if the codestream is level 5 or - * level 10 compatible. If not present, it is level 5. Level 10 allows more - * features such as very high image resolution and bit-depths above 16 bits - * per channel. Added automatically by the encoder when - * JxlEncoderSetCodestreamLevel is used + * - "JXL ": mandatory signature box, must come first, `12` bytes long + * including the box header + * - "ftyp": a second mandatory signature box, must come second, `20` bytes + * long including the box header + * - "jxll": a JXL level box. This indicates if the codestream is level `5` or + * level `10` compatible. If not present, it is level `5`. Level `10` allows + * more features such as very high image resolution and bit-depths above `16` + * bits per channel. Added automatically by the encoder when + * @ref JxlEncoderSetCodestreamLevel is used * - "jxlc": a box with the image codestream, in case the codestream is not * split across multiple boxes. The codestream contains the JPEG XL image * itself, including the basic info such as image dimensions, ICC color @@ -1350,7 +1365,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec, * @param type buffer to copy the type into * @param decompressed which box type to get: JXL_FALSE to get the raw box type, * which can be "brob", JXL_TRUE, get the underlying box type. - * @return @ref JXL_DEC_SUCCESS if the value is available, @ref JXL_DEC_ERROR if + * @return ::JXL_DEC_SUCCESS if the value is available, ::JXL_DEC_ERROR if * not, for example the JXL file does not use the container format. */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec, @@ -1363,13 +1378,29 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec, * * @param dec decoder object * @param size raw size of the box in bytes - * @return @ref JXL_DEC_ERROR if no box size is available, @ref JXL_DEC_SUCCESS + * @return ::JXL_DEC_ERROR if no box size is available, ::JXL_DEC_SUCCESS * otherwise. */ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec, uint64_t* size); /** + * Returns the size of the contents of a box, after the @ref + * JXL_DEC_BOX event. This does not include any of the headers of the box. For + * compressed "brob" boxes, this is the size of the compressed content. Even + * when @ref JxlDecoderSetDecompressBoxes is enabled, the return value of + * function does not change, and the decompressed size is not known before it + * has already been decompressed and output. + * + * @param dec decoder object + * @param size size of the payload of the box in bytes + * @return @ref JXL_DEC_ERROR if no box size is available, @ref JXL_DEC_SUCCESS + * otherwise. + */ +JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeContents(const JxlDecoder* dec, + uint64_t* size); + +/** * Configures at which progressive steps in frame decoding these @ref * JXL_DEC_FRAME_PROGRESSION event occurs. The default value for the level * of detail if this function is never called is `kDC`. @@ -1377,7 +1408,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec, * @param dec decoder object * @param detail at which level of detail to trigger @ref * JXL_DEC_FRAME_PROGRESSION - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as * an invalid value for the progressive detail. */ JXL_EXPORT JxlDecoderStatus @@ -1385,11 +1416,11 @@ JxlDecoderSetProgressiveDetail(JxlDecoder* dec, JxlProgressiveDetail detail); /** * Returns the intended downsampling ratio for the progressive frame produced - * by @ref JxlDecoderFlushImage after the latest @ref JXL_DEC_FRAME_PROGRESSION + * by @ref JxlDecoderFlushImage after the latest ::JXL_DEC_FRAME_PROGRESSION * event. * * @param dec decoder object - * @return The intended downsampling ratio, can be 1, 2, 4 or 8. + * @return The intended downsampling ratio, can be `1`, `2`, `4` or `8`. */ JXL_EXPORT size_t JxlDecoderGetIntendedDownsamplingRatio(JxlDecoder* dec); @@ -1399,12 +1430,12 @@ JXL_EXPORT size_t JxlDecoderGetIntendedDownsamplingRatio(JxlDecoder* dec); * JxlDecoderSetImageOutBuffer will contain partial image data. * * Can be called when @ref JxlDecoderProcessInput returns @ref - * JXL_DEC_NEED_MORE_INPUT, after the @ref JXL_DEC_FRAME event already occurred - * and before the @ref JXL_DEC_FULL_IMAGE event occurred for a frame. + * JXL_DEC_NEED_MORE_INPUT, after the ::JXL_DEC_FRAME event already occurred + * and before the ::JXL_DEC_FULL_IMAGE event occurred for a frame. * * @param dec decoder object - * @return @ref JXL_DEC_SUCCESS if image data was flushed to the output buffer, - * or @ref JXL_DEC_ERROR when no flush was done, e.g. if not enough image + * @return ::JXL_DEC_SUCCESS if image data was flushed to the output buffer, + * or ::JXL_DEC_ERROR when no flush was done, e.g. if not enough image * data was available yet even for flush, or no output buffer was set yet. * This error is not fatal, it only indicates no flushed image is available * right now. Regular decoding can still be performed. @@ -1416,11 +1447,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderFlushImage(JxlDecoder* dec); * * Can be called after @ref JxlDecoderSetImageOutBuffer or @ref * JxlDecoderSetImageOutCallback. For float pixel data types, only the default - * @ref JXL_BIT_DEPTH_FROM_PIXEL_FORMAT setting is supported. + * ::JXL_BIT_DEPTH_FROM_PIXEL_FORMAT setting is supported. * * @param dec decoder object * @param bit_depth the bit depth setting of the pixel output - * @return @ref JXL_DEC_SUCCESS on success, @ref JXL_DEC_ERROR on error, such as + * @return ::JXL_DEC_SUCCESS on success, ::JXL_DEC_ERROR on error, such as * incompatible custom bit depth and pixel data type. */ JXL_EXPORT JxlDecoderStatus diff --git a/third_party/jpeg-xl/lib/include/jxl/encode.h b/third_party/jpeg-xl/lib/include/jxl/encode.h index 2a87821f06..bb11dd7572 100644 --- a/third_party/jpeg-xl/lib/include/jxl/encode.h +++ b/third_party/jpeg-xl/lib/include/jxl/encode.h @@ -41,8 +41,8 @@ JXL_EXPORT uint32_t JxlEncoderVersion(void); /** * Opaque structure that holds the JPEG XL encoder. * - * Allocated and initialized with JxlEncoderCreate(). - * Cleaned up and deallocated with JxlEncoderDestroy(). + * Allocated and initialized with @ref JxlEncoderCreate(). + * Cleaned up and deallocated with @ref JxlEncoderDestroy(). */ typedef struct JxlEncoderStruct JxlEncoder; @@ -50,9 +50,9 @@ typedef struct JxlEncoderStruct JxlEncoder; * Settings and metadata for a single image frame. This includes encoder options * for a frame such as compression quality and speed. * - * Allocated and initialized with JxlEncoderFrameSettingsCreate(). + * Allocated and initialized with @ref JxlEncoderFrameSettingsCreate(). * Cleaned up and deallocated when the encoder is destroyed with - * JxlEncoderDestroy(). + * @ref JxlEncoderDestroy(). */ typedef struct JxlEncoderFrameSettingsStruct JxlEncoderFrameSettings; @@ -126,7 +126,7 @@ typedef enum { typedef enum { /** Sets encoder effort/speed level without affecting decoding speed. Valid * values are, from faster to slower speed: 1:lightning 2:thunder 3:falcon - * 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise. + * 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise 10:glacier. * Default: squirrel (7). */ JXL_ENC_FRAME_SETTING_EFFORT = 0, @@ -145,7 +145,7 @@ typedef enum { */ JXL_ENC_FRAME_SETTING_RESAMPLING = 2, - /** Similar to JXL_ENC_FRAME_SETTING_RESAMPLING, but for extra channels. + /** Similar to ::JXL_ENC_FRAME_SETTING_RESAMPLING, but for extra channels. * Integer option, use -1 for the default behavior (depends on encoder * implementation), 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for * 4x4 downsampling, 8 for 8x8 downsampling. @@ -158,7 +158,7 @@ typedef enum { * downsampled resolution, not the full image resolution. The downsampled * resolution is given by ceil(xsize / resampling), ceil(ysize / resampling) * with xsize and ysize the dimensions given in the basic info, and resampling - * the factor set with @ref JXL_ENC_FRAME_SETTING_RESAMPLING. + * the factor set with ::JXL_ENC_FRAME_SETTING_RESAMPLING. * Use 0 to disable, 1 to enable. Default value is 0. */ JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED = 4, @@ -171,8 +171,8 @@ typedef enum { JXL_ENC_FRAME_SETTING_PHOTON_NOISE = 5, /** Enables adaptive noise generation. This setting is not recommended for - * use, please use JXL_ENC_FRAME_SETTING_PHOTON_NOISE instead. Use -1 for the - * default (encoder chooses), 0 to disable, 1 to enable. + * use, please use ::JXL_ENC_FRAME_SETTING_PHOTON_NOISE instead. Use -1 for + * the default (encoder chooses), 0 to disable, 1 to enable. */ JXL_ENC_FRAME_SETTING_NOISE = 6, @@ -320,27 +320,28 @@ typedef enum { * 1 = index this frame within the Frame Index Box. * If any frames are indexed, the first frame needs to * be indexed, too. If the first frame is not indexed, and - * a later frame is attempted to be indexed, JXL_ENC_ERROR will occur. + * a later frame is attempted to be indexed, ::JXL_ENC_ERROR will occur. * If non-keyframes, i.e., frames with cropping, blending or patches are - * attempted to be indexed, JXL_ENC_ERROR will occur. + * attempted to be indexed, ::JXL_ENC_ERROR will occur. */ JXL_ENC_FRAME_INDEX_BOX = 31, - /** Sets brotli encode effort for use in JPEG recompression and compressed - * metadata boxes (brob). Can be -1 (default) or 0 (fastest) to 11 (slowest). - * Default is based on the general encode effort in case of JPEG + /** Sets brotli encode effort for use in JPEG recompression and + * compressed metadata boxes (brob). Can be -1 (default) or 0 (fastest) to 11 + * (slowest). Default is based on the general encode effort in case of JPEG * recompression, and 4 for brob boxes. */ JXL_ENC_FRAME_SETTING_BROTLI_EFFORT = 32, /** Enables or disables brotli compression of metadata boxes derived from - * a JPEG frame when using JxlEncoderAddJPEGFrame. This has no effect on boxes - * added using JxlEncoderAddBox. - * -1 = default, 0 = disable compression, 1 = enable compression. + * a JPEG frame when using @ref JxlEncoderAddJPEGFrame. This has no effect on + * boxes added using @ref JxlEncoderAddBox. -1 = default, 0 = disable + * compression, 1 = enable compression. */ JXL_ENC_FRAME_SETTING_JPEG_COMPRESS_BOXES = 33, /** Control what kind of buffering is used, when using chunked image frames. + * -1 = default (let the encoder decide) * 0 = buffers everything, basically the same as non-streamed code path (mainly for testing) * 1 = buffers everything for images that are smaller than 2048 x 2048, and @@ -351,30 +352,30 @@ typedef enum { * * When using streaming input and output the encoder minimizes memory usage at * the cost of compression density. Also note that images produced with - * streaming mode might can not be decoded progressively. + * streaming mode might not be progressively decodeable. */ JXL_ENC_FRAME_SETTING_BUFFERING = 34, /** Keep or discard Exif metadata boxes derived from a JPEG frame when using - * JxlEncoderAddJPEGFrame. This has no effect on boxes added using - * JxlEncoderAddBox. When JxlEncoderStoreJPEGMetadata is set to 1, this option - * cannot be set to 0. Even when Exif metadata is discarded, the orientation - * will still be applied. 0 = discard Exif metadata, 1 = keep Exif metadata - * (default). + * @ref JxlEncoderAddJPEGFrame. This has no effect on boxes added using + * @ref JxlEncoderAddBox. When @ref JxlEncoderStoreJPEGMetadata is set to 1, + * this option cannot be set to 0. Even when Exif metadata is discarded, the + * orientation will still be applied. 0 = discard Exif metadata, 1 = keep Exif + * metadata (default). */ JXL_ENC_FRAME_SETTING_JPEG_KEEP_EXIF = 35, /** Keep or discard XMP metadata boxes derived from a JPEG frame when using - * JxlEncoderAddJPEGFrame. This has no effect on boxes added using - * JxlEncoderAddBox. When JxlEncoderStoreJPEGMetadata is set to 1, this option - * cannot be set to 0. 0 = discard XMP metadata, 1 = keep XMP metadata - * (default). + * @ref JxlEncoderAddJPEGFrame. This has no effect on boxes added using + * @ref JxlEncoderAddBox. When @ref JxlEncoderStoreJPEGMetadata is set to 1, + * this option cannot be set to 0. 0 = discard XMP metadata, 1 = keep XMP + * metadata (default). */ JXL_ENC_FRAME_SETTING_JPEG_KEEP_XMP = 36, /** Keep or discard JUMBF metadata boxes derived from a JPEG frame when using - * JxlEncoderAddJPEGFrame. This has no effect on boxes added using - * JxlEncoderAddBox. 0 = discard JUMBF metadata, 1 = keep JUMBF metadata + * @ref JxlEncoderAddJPEGFrame. This has no effect on boxes added using + * @ref JxlEncoderAddBox. 0 = discard JUMBF metadata, 1 = keep JUMBF metadata * (default). */ JXL_ENC_FRAME_SETTING_JPEG_KEEP_JUMBF = 37, @@ -395,7 +396,7 @@ typedef enum { } JxlEncoderFrameSettingId; /** - * Creates an instance of JxlEncoder and initializes it. + * Creates an instance of @ref JxlEncoder and initializes it. * * @p memory_manager will be used for all the library dynamic allocations made * from this instance. The parameter may be NULL, in which case the default @@ -404,21 +405,21 @@ typedef enum { * @param memory_manager custom allocator function. It may be NULL. The memory * manager will be copied internally. * @return @c NULL if the instance can not be allocated or initialized - * @return pointer to initialized JxlEncoder otherwise + * @return pointer to initialized @ref JxlEncoder otherwise */ JXL_EXPORT JxlEncoder* JxlEncoderCreate(const JxlMemoryManager* memory_manager); /** - * Re-initializes a JxlEncoder instance, so it can be re-used for encoding + * Re-initializes a @ref JxlEncoder instance, so it can be re-used for encoding * another image. All state and settings are reset as if the object was - * newly created with JxlEncoderCreate, but the memory manager is kept. + * newly created with @ref JxlEncoderCreate, but the memory manager is kept. * * @param enc instance to be re-initialized. */ JXL_EXPORT void JxlEncoderReset(JxlEncoder* enc); /** - * Deinitializes and frees JxlEncoder instance. + * Deinitializes and frees a @ref JxlEncoder instance. * * @param enc instance to be cleaned up and deallocated. */ @@ -430,8 +431,8 @@ JXL_EXPORT void JxlEncoderDestroy(JxlEncoder* enc); * left unset, the default CMS implementation will be used. * * @param enc encoder object. - * @param cms structure representing a CMS implementation. See JxlCmsInterface - * for more details. + * @param cms structure representing a CMS implementation. See @ref + * JxlCmsInterface for more details. */ JXL_EXPORT void JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms); @@ -444,7 +445,7 @@ JXL_EXPORT void JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms); * be NULL to use the default, single-threaded, runner. A multithreaded * runner should be set to reach fast performance. * @param parallel_runner_opaque opaque pointer for parallel_runner. - * @return JXL_ENC_SUCCESS if the runner was set, JXL_ENC_ERROR + * @return ::JXL_ENC_SUCCESS if the runner was set, ::JXL_ENC_ERROR * otherwise (the previous runner remains set). */ JXL_EXPORT JxlEncoderStatus @@ -452,16 +453,16 @@ JxlEncoderSetParallelRunner(JxlEncoder* enc, JxlParallelRunner parallel_runner, void* parallel_runner_opaque); /** - * Get the (last) error code in case JXL_ENC_ERROR was returned. + * Get the (last) error code in case ::JXL_ENC_ERROR was returned. * * @param enc encoder object. - * @return the JxlEncoderError that caused the (last) JXL_ENC_ERROR to be - * returned. + * @return the @ref JxlEncoderError that caused the (last) ::JXL_ENC_ERROR to + * be returned. */ JXL_EXPORT JxlEncoderError JxlEncoderGetError(JxlEncoder* enc); /** - * Encodes JPEG XL file using the available bytes. @p *avail_out indicates how + * Encodes a JPEG XL file using the available bytes. @p *avail_out indicates how * many output bytes are available, and @p *next_out points to the input bytes. * *avail_out will be decremented by the amount of bytes that have been * processed by the encoder and *next_out will be incremented by the same @@ -469,12 +470,12 @@ JXL_EXPORT JxlEncoderError JxlEncoderGetError(JxlEncoder* enc); * bytes. * * The returned status indicates whether the encoder needs more output bytes. - * When the return value is not JXL_ENC_ERROR or JXL_ENC_SUCCESS, the encoding - * requires more JxlEncoderProcessOutput calls to continue. + * When the return value is not ::JXL_ENC_ERROR or ::JXL_ENC_SUCCESS, the + * encoding requires more @ref JxlEncoderProcessOutput calls to continue. * * The caller must guarantee that *avail_out >= 32 when calling - * JxlEncoderProcessOutput; otherwise, JXL_ENC_NEED_MORE_OUTPUT will be - * returned. It is guaranteed that, if *avail_out >= 32, at least one byte of + * @ref JxlEncoderProcessOutput; otherwise, ::JXL_ENC_NEED_MORE_OUTPUT will + * be returned. It is guaranteed that, if *avail_out >= 32, at least one byte of * output will be written. * * This encodes the frames and/or boxes added so far. If the last frame or last @@ -486,9 +487,9 @@ JXL_EXPORT JxlEncoderError JxlEncoderGetError(JxlEncoder* enc); * @param enc encoder object. * @param next_out pointer to next bytes to write to. * @param avail_out amount of bytes available starting from *next_out. - * @return JXL_ENC_SUCCESS when encoding finished and all events handled. - * @return JXL_ENC_ERROR when encoding failed, e.g. invalid input. - * @return JXL_ENC_NEED_MORE_OUTPUT more output buffer is necessary. + * @return ::JXL_ENC_SUCCESS when encoding finished and all events handled. + * @return ::JXL_ENC_ERROR when encoding failed, e.g. invalid input. + * @return ::JXL_ENC_NEED_MORE_OUTPUT more output buffer is necessary. */ JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc, uint8_t** next_out, @@ -509,13 +510,14 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc, * time duration of 0, making them form a composite still. See @ref * JxlFrameHeader for more information. * - * This information is stored in the JxlEncoderFrameSettings and so is used for - * any frame encoded with these JxlEncoderFrameSettings. It is ok to change - * between @ref JxlEncoderAddImageFrame calls, each added image frame will have - * the frame header that was set in the options at the time of calling - * JxlEncoderAddImageFrame. + * This information is stored in the @ref JxlEncoderFrameSettings and so is used + * for any frame encoded with these @ref JxlEncoderFrameSettings. It is ok to + * change between @ref JxlEncoderAddImageFrame calls, each added image frame + * will have the frame header that was set in the options at the time of calling + * @ref JxlEncoderAddImageFrame. * - * The is_last and name_length fields of the JxlFrameHeader are ignored, use + * The is_last and name_length fields of the @ref JxlFrameHeader are ignored, + * use * @ref JxlEncoderCloseFrames to indicate last frame, and @ref * JxlEncoderSetFrameName to indicate the name and its length instead. * Calling this function will clear any name that was previously set with @ref @@ -525,7 +527,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc, * includes reference to the encoder object. * @param frame_header frame header data to set. Object owned by the caller and * does not need to be kept in memory, its information is copied internally. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameHeader(JxlEncoderFrameSettings* frame_settings, @@ -540,7 +542,7 @@ JxlEncoderSetFrameHeader(JxlEncoderFrameSettings* frame_settings, * includes reference to the encoder object. * @param index index of the extra channel to use. * @param blend_info blend info to set for the extra channel - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBlendInfo( JxlEncoderFrameSettings* frame_settings, size_t index, @@ -550,8 +552,9 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBlendInfo( * Sets the name of the animation frame. This function is optional, frames are * not required to have a name. This setting is a part of the frame header, and * the same principles as for @ref JxlEncoderSetFrameHeader apply. The - * name_length field of JxlFrameHeader is ignored by the encoder, this function - * determines the name length instead as the length in bytes of the C string. + * name_length field of @ref JxlFrameHeader is ignored by the encoder, this + * function determines the name length instead as the length in bytes of the C + * string. * * The maximum possible name length is 1071 bytes (excluding terminating null * character). @@ -563,7 +566,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBlendInfo( * includes reference to the encoder object. * @param frame_name name of the next frame to be encoded, as a UTF-8 encoded C * string (zero terminated). Owned by the caller, and copied internally. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameName( JxlEncoderFrameSettings* frame_settings, const char* frame_name); @@ -571,15 +574,17 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameName( /** * Sets the bit depth of the input buffer. * - * For float pixel formats, only the default JXL_BIT_DEPTH_FROM_PIXEL_FORMAT + * For float pixel formats, only the default @ref + JXL_BIT_DEPTH_FROM_PIXEL_FORMAT * setting is allowed, while for unsigned pixel formats, - * JXL_BIT_DEPTH_FROM_CODESTREAM setting is also allowed. See the comment on + * ::JXL_BIT_DEPTH_FROM_CODESTREAM setting is also allowed. See the comment + on * @ref JxlEncoderAddImageFrame for the effects of the bit depth setting. * @param frame_settings set of options and metadata for this frame. Also * includes reference to the encoder object. * @param bit_depth the bit depth setting of the pixel input - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameBitDepth( JxlEncoderFrameSettings* frame_settings, const JxlBitDepth* bit_depth); @@ -587,13 +592,13 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameBitDepth( /** * Sets the buffer to read JPEG encoded bytes from for the next frame to encode. * - * If JxlEncoderSetBasicInfo has not yet been called, calling - * JxlEncoderAddJPEGFrame will implicitly call it with the parameters of the - * added JPEG frame. + * If @ref JxlEncoderSetBasicInfo has not yet been called, calling + * @ref JxlEncoderAddJPEGFrame will implicitly call it with the parameters of + * the added JPEG frame. * - * If JxlEncoderSetColorEncoding or JxlEncoderSetICCProfile has not yet been - * called, calling JxlEncoderAddJPEGFrame will implicitly call it with the - * parameters of the added JPEG frame. + * If @ref JxlEncoderSetColorEncoding or @ref JxlEncoderSetICCProfile has not + * yet been called, calling @ref JxlEncoderAddJPEGFrame will implicitly call it + * with the parameters of the added JPEG frame. * * If the encoder is set to store JPEG reconstruction metadata using @ref * JxlEncoderStoreJPEGMetadata and a single JPEG frame is added, it will be @@ -603,12 +608,16 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameBitDepth( * JxlEncoderCloseFrames must be called before the next * @ref JxlEncoderProcessOutput call. * + * Note, this can only be used to add JPEG frames for lossless compression. To + * encode with lossy compression, the JPEG must be decoded manually and a pixel + * buffer added using JxlEncoderAddImageFrame. + * * @param frame_settings set of options and metadata for this frame. Also * includes reference to the encoder object. * @param buffer bytes to read JPEG from. Owned by the caller and its contents * are copied internally. * @param size size of buffer in bytes. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error */ JXL_EXPORT JxlEncoderStatus JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings, @@ -616,33 +625,36 @@ JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings, /** * Sets the buffer to read pixels from for the next image to encode. Must call - * JxlEncoderSetBasicInfo before JxlEncoderAddImageFrame. + * @ref JxlEncoderSetBasicInfo before @ref JxlEncoderAddImageFrame. * * Currently only some data types for pixel formats are supported: - * - JXL_TYPE_UINT8, with range 0..255 - * - JXL_TYPE_UINT16, with range 0..65535 - * - JXL_TYPE_FLOAT16, with nominal range 0..1 - * - JXL_TYPE_FLOAT, with nominal range 0..1 + * - ::JXL_TYPE_UINT8, with range 0..255 + * - ::JXL_TYPE_UINT16, with range 0..65535 + * - ::JXL_TYPE_FLOAT16, with nominal range 0..1 + * - ::JXL_TYPE_FLOAT, with nominal range 0..1 * * Note: the sample data type in pixel_format is allowed to be different from - * what is described in the JxlBasicInfo. The type in pixel_format, together - * with an optional @ref JxlBitDepth parameter set by @ref + * what is described in the @ref JxlBasicInfo. The type in pixel_format, + * together with an optional @ref JxlBitDepth parameter set by @ref * JxlEncoderSetFrameBitDepth describes the format of the uncompressed pixel - * buffer. The bits_per_sample and exponent_bits_per_sample in the JxlBasicInfo - * describes what will actually be encoded in the JPEG XL codestream. - * For example, to encode a 12-bit image, you would set bits_per_sample to 12, - * while the input frame buffer can be in the following formats: - * - if pixel format is in JXL_TYPE_UINT16 with default bit depth setting - * (i.e. JXL_BIT_DEPTH_FROM_PIXEL_FORMAT), input sample values are rescaled - * to 16-bit, i.e. multiplied by 65535/4095; - * - if pixel format is in JXL_TYPE_UINT16 with JXL_BIT_DEPTH_FROM_CODESTREAM - * bit depth setting, input sample values are provided unscaled; - * - if pixel format is in JXL_TYPE_FLOAT, input sample values are rescaled - * to 0..1, i.e. multiplied by 1.f/4095.f. - * While it is allowed, it is obviously not recommended to use a pixel_format - * with lower precision than what is specified in the JxlBasicInfo. - * - * We support interleaved channels as described by the JxlPixelFormat: + * buffer. The bits_per_sample and exponent_bits_per_sample in the @ref + * JxlBasicInfo describes what will actually be encoded in the JPEG XL + * codestream. For example, to encode a 12-bit image, you would set + * bits_per_sample to 12, while the input frame buffer can be in the following + * formats: + * - if pixel format is in ::JXL_TYPE_UINT16 with default bit depth setting + * (i.e. ::JXL_BIT_DEPTH_FROM_PIXEL_FORMAT), input sample values are + * rescaled to 16-bit, i.e. multiplied by 65535/4095; + * - if pixel format is in ::JXL_TYPE_UINT16 with @ref + * JXL_BIT_DEPTH_FROM_CODESTREAM bit depth setting, input sample values are + * provided unscaled; + * - if pixel format is in ::JXL_TYPE_FLOAT, input sample values are + * rescaled to 0..1, i.e. multiplied by 1.f/4095.f. While it is allowed, it is + * obviously not recommended to use a pixel_format with lower precision than + * what is specified in the @ref JxlBasicInfo. + * + * We support interleaved channels as described by the @ref JxlPixelFormat + * "JxlPixelFormat": * - single-channel data, e.g. grayscale * - single-channel + alpha * - trichromatic, e.g. RGB @@ -654,10 +666,11 @@ JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings, * set to all-opaque (an alpha value of 1.0 everywhere). * * The pixels are assumed to be encoded in the original profile that is set with - * JxlEncoderSetColorEncoding or JxlEncoderSetICCProfile. If none of these - * functions were used, the pixels are assumed to be nonlinear sRGB for integer - * data types (JXL_TYPE_UINT8, JXL_TYPE_UINT16), and linear sRGB for floating - * point data types (JXL_TYPE_FLOAT16, JXL_TYPE_FLOAT). + * @ref JxlEncoderSetColorEncoding or @ref JxlEncoderSetICCProfile. If none of + * these functions were used, the pixels are assumed to be nonlinear sRGB for + * integer data types (::JXL_TYPE_UINT8, ::JXL_TYPE_UINT16), and linear + * sRGB for floating point data types (::JXL_TYPE_FLOAT16, @ref + * JXL_TYPE_FLOAT). * * Sample values in floating-point pixel formats are allowed to be outside the * nominal range, e.g. to represent out-of-sRGB-gamut colors in the @@ -676,14 +689,14 @@ JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings, * and its contents are copied internally. * @param size size of buffer in bytes. This size should match what is implied * by the frame dimensions and the pixel format. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error */ JXL_EXPORT JxlEncoderStatus JxlEncoderAddImageFrame( const JxlEncoderFrameSettings* frame_settings, const JxlPixelFormat* pixel_format, const void* buffer, size_t size); /** - * The JxlEncoderOutputProcessor structure provides an interface for the + * The @ref JxlEncoderOutputProcessor structure provides an interface for the * encoder's output processing. Users of the library, who want to do streaming * encoding, should implement the required callbacks for buffering, writing, * seeking (if supported), and setting a finalized position during the encoding @@ -785,7 +798,7 @@ struct JxlEncoderOutputProcessor { * @param enc encoder object. * @param output_processor the struct containing the callbacks for managing * output. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error. + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error. */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetOutputProcessor( JxlEncoder* enc, struct JxlEncoderOutputProcessor output_processor); @@ -801,7 +814,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetOutputProcessor( * This should not be used when using @ref JxlEncoderProcessOutput. * * @param enc encoder object. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error. + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error. */ JXL_EXPORT JxlEncoderStatus JxlEncoderFlushInput(JxlEncoder* enc); @@ -923,9 +936,9 @@ struct JxlChunkedFrameInputSource { * chunked or streaming manner, which can be especially useful when dealing with * large images that may not fit entirely in memory or when trying to optimize * memory usage. The input data is provided through callbacks defined in the - * `JxlChunkedFrameInputSource` struct. Once the frame data has been completely - * retrieved, this function will flush the input and close it if it is the last - * frame. + * @ref JxlChunkedFrameInputSource struct. Once the frame data has been + * completely retrieved, this function will flush the input and close it if it + * is the last frame. * * @param frame_settings set of options and metadata for this frame. Also * includes reference to the encoder object. @@ -943,7 +956,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddChunkedFrame( /** * Sets the buffer to read pixels from for an extra channel at a given index. * The index must be smaller than the num_extra_channels in the associated - * JxlBasicInfo. Must call @ref JxlEncoderSetExtraChannelInfo before + * @ref JxlBasicInfo. Must call @ref JxlEncoderSetExtraChannelInfo before @ref * JxlEncoderSetExtraChannelBuffer. * * TODO(firsching): mention what data types in pixel formats are supported. @@ -961,15 +974,15 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddChunkedFrame( * @param size size of buffer in bytes. This size should match what is implied * by the frame dimensions and the pixel format. * @param index index of the extra channel to use. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer( const JxlEncoderFrameSettings* frame_settings, const JxlPixelFormat* pixel_format, const void* buffer, size_t size, uint32_t index); -/** Adds a metadata box to the file format. JxlEncoderProcessOutput must be used - * to effectively write the box to the output. @ref JxlEncoderUseBoxes must +/** Adds a metadata box to the file format. @ref JxlEncoderProcessOutput must be + * used to effectively write the box to the output. @ref JxlEncoderUseBoxes must * be enabled before using this function. * * Boxes allow inserting application-specific data and metadata (Exif, XML/XMP, @@ -996,10 +1009,10 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer( * the encoder encodes the size header itself. Most boxes are written * automatically by the encoder as needed ("JXL ", "ftyp", "jxll", "jxlc", * "jxlp", "jxli", "jbrd"), and this function only needs to be called to add - * optional metadata when encoding from pixels (using JxlEncoderAddImageFrame). - * When recompressing JPEG files (using JxlEncoderAddJPEGFrame), if the input - * JPEG contains EXIF, XMP or JUMBF metadata, the corresponding boxes are - * already added automatically. + * optional metadata when encoding from pixels (using @ref + * JxlEncoderAddImageFrame). When recompressing JPEG files (using @ref + * JxlEncoderAddJPEGFrame), if the input JPEG contains EXIF, XMP or JUMBF + * metadata, the corresponding boxes are already added automatically. * * Box types are given by 4 characters. The following boxes can be added with * this function: @@ -1032,9 +1045,9 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer( * @param size size of the box contents. * @param compress_box Whether to compress this box as a "brob" box. Requires * Brotli support. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error, such as when - * using this function without JxlEncoderUseContainer, or adding a box type - * that would result in an invalid file format. + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error, such as + * when using this function without @ref JxlEncoderUseContainer, or adding a box + * type that would result in an invalid file format. */ JXL_EXPORT JxlEncoderStatus JxlEncoderAddBox(JxlEncoder* enc, const JxlBoxType type, @@ -1061,9 +1074,9 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderUseBoxes(JxlEncoder* enc); * the stream will be finished. It is not necessary to use this function if * @ref JxlEncoderUseBoxes is not used. Further frames may still be added. * - * Must be called between JxlEncoderAddBox of the last box - * and the next call to JxlEncoderProcessOutput, or @ref JxlEncoderProcessOutput - * won't output the last box correctly. + * Must be called between @ref JxlEncoderAddBox of the last box + * and the next call to @ref JxlEncoderProcessOutput, or @ref + * JxlEncoderProcessOutput won't output the last box correctly. * * NOTE: if you don't need to close frames and boxes at separate times, you can * use @ref JxlEncoderCloseInput instead to close both at once. @@ -1087,10 +1100,10 @@ JXL_EXPORT void JxlEncoderCloseBoxes(JxlEncoder* enc); JXL_EXPORT void JxlEncoderCloseFrames(JxlEncoder* enc); /** - * Closes any input to the encoder, equivalent to calling JxlEncoderCloseFrames - * as well as calling JxlEncoderCloseBoxes if needed. No further input of any - * kind may be given to the encoder, but further @ref JxlEncoderProcessOutput - * calls should be done to create the final output. + * Closes any input to the encoder, equivalent to calling @ref + * JxlEncoderCloseFrames as well as calling @ref JxlEncoderCloseBoxes if needed. + * No further input of any kind may be given to the encoder, but further @ref + * JxlEncoderProcessOutput calls should be done to create the final output. * * The requirements of both @ref JxlEncoderCloseFrames and @ref * JxlEncoderCloseBoxes apply to this function. Either this function or the @@ -1104,39 +1117,39 @@ JXL_EXPORT void JxlEncoderCloseInput(JxlEncoder* enc); /** * Sets the original color encoding of the image encoded by this encoder. This - * is an alternative to JxlEncoderSetICCProfile and only one of these two must - * be used. This one sets the color encoding as a @ref JxlColorEncoding, while - * the other sets it as ICC binary data. - * Must be called after JxlEncoderSetBasicInfo. + * is an alternative to @ref JxlEncoderSetICCProfile and only one of these two + * must be used. This one sets the color encoding as a @ref JxlColorEncoding, + * while the other sets it as ICC binary data. Must be called after @ref + * JxlEncoderSetBasicInfo. * * @param enc encoder object. * @param color color encoding. Object owned by the caller and its contents are * copied internally. - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR or - * JXL_ENC_NOT_SUPPORTED otherwise + * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref + * JXL_ENC_ERROR otherwise */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetColorEncoding(JxlEncoder* enc, const JxlColorEncoding* color); /** * Sets the original color encoding of the image encoded by this encoder as an - * ICC color profile. This is an alternative to JxlEncoderSetColorEncoding and - * only one of these two must be used. This one sets the color encoding as ICC - * binary data, while the other defines it as a @ref JxlColorEncoding. - * Must be called after JxlEncoderSetBasicInfo. + * ICC color profile. This is an alternative to @ref JxlEncoderSetColorEncoding + * and only one of these two must be used. This one sets the color encoding as + * ICC binary data, while the other defines it as a @ref JxlColorEncoding. Must + * be called after @ref JxlEncoderSetBasicInfo. * * @param enc encoder object. * @param icc_profile bytes of the original ICC profile * @param size size of the icc_profile buffer in bytes - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR or - * JXL_ENC_NOT_SUPPORTED otherwise + * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref + * JXL_ENC_ERROR otherwise */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetICCProfile(JxlEncoder* enc, const uint8_t* icc_profile, size_t size); /** - * Initializes a JxlBasicInfo struct to default values. + * Initializes a @ref JxlBasicInfo struct to default values. * For forwards-compatibility, this function has to be called before values * are assigned to the struct fields. * The default values correspond to an 8-bit RGB image, no alpha or any @@ -1147,7 +1160,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetICCProfile(JxlEncoder* enc, JXL_EXPORT void JxlEncoderInitBasicInfo(JxlBasicInfo* info); /** - * Initializes a JxlFrameHeader struct to default values. + * Initializes a @ref JxlFrameHeader struct to default values. * For forwards-compatibility, this function has to be called before values * are assigned to the struct fields. * The default values correspond to a frame with no animation duration and the @@ -1159,7 +1172,7 @@ JXL_EXPORT void JxlEncoderInitBasicInfo(JxlBasicInfo* info); JXL_EXPORT void JxlEncoderInitFrameHeader(JxlFrameHeader* frame_header); /** - * Initializes a JxlBlendInfo struct to default values. + * Initializes a @ref JxlBlendInfo struct to default values. * For forwards-compatibility, this function has to be called before values * are assigned to the struct fields. * @@ -1170,26 +1183,26 @@ JXL_EXPORT void JxlEncoderInitBlendInfo(JxlBlendInfo* blend_info); /** * Sets the global metadata of the image encoded by this encoder. * - * If the JxlBasicInfo contains information of extra channels beyond an alpha - * channel, then @ref JxlEncoderSetExtraChannelInfo must be called between - * JxlEncoderSetBasicInfo and @ref JxlEncoderAddImageFrame. In order to indicate - * extra channels, the value of `info.num_extra_channels` should be set to the - * number of extra channels, also counting the alpha channel if present. + * If the @ref JxlBasicInfo contains information of extra channels beyond an + * alpha channel, then @ref JxlEncoderSetExtraChannelInfo must be called between + * @ref JxlEncoderSetBasicInfo and @ref JxlEncoderAddImageFrame. In order to + * indicate extra channels, the value of `info.num_extra_channels` should be set + * to the number of extra channels, also counting the alpha channel if present. * * @param enc encoder object. * @param info global image metadata. Object owned by the caller and its * contents are copied internally. - * @return JXL_ENC_SUCCESS if the operation was successful, - * JXL_ENC_ERROR or JXL_ENC_NOT_SUPPORTED otherwise + * @return ::JXL_ENC_SUCCESS if the operation was successful, + * ::JXL_ENC_ERROR otherwise */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc, const JxlBasicInfo* info); /** * Sets the upsampling method the decoder will use in case there are frames - * with JXL_ENC_FRAME_SETTING_RESAMPLING set. This is useful in combination - * with the JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED option, to control the - * type of upsampling that will be used. + * with ::JXL_ENC_FRAME_SETTING_RESAMPLING set. This is useful in combination + * with the ::JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED option, to control + * the type of upsampling that will be used. * * @param enc encoder object. * @param factor upsampling factor to configure (1, 2, 4 or 8; for 1 this @@ -1198,15 +1211,15 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc, * -1: default (good for photographic images, no signaling overhead) * 0: nearest neighbor (good for pixel art) * 1: 'pixel dots' (same as NN for 2x, diamond-shaped 'pixel dots' for 4x/8x) - * @return JXL_ENC_SUCCESS if the operation was successful, - * JXL_ENC_ERROR or JXL_ENC_NOT_SUPPORTED otherwise + * @return ::JXL_ENC_SUCCESS if the operation was successful, + * ::JXL_ENC_ERROR otherwise */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetUpsamplingMode(JxlEncoder* enc, int64_t factor, int64_t mode); /** - * Initializes a JxlExtraChannelInfo struct to default values. + * Initializes a @ref JxlExtraChannelInfo struct to default values. * For forwards-compatibility, this function has to be called before values * are assigned to the struct fields. * The default values correspond to an 8-bit channel of the provided type. @@ -1220,23 +1233,24 @@ JXL_EXPORT void JxlEncoderInitExtraChannelInfo(JxlExtraChannelType type, /** * Sets information for the extra channel at the given index. The index - * must be smaller than num_extra_channels in the associated JxlBasicInfo. + * must be smaller than num_extra_channels in the associated @ref JxlBasicInfo. * * @param enc encoder object * @param index index of the extra channel to set. * @param info global extra channel metadata. Object owned by the caller and its * contents are copied internally. - * @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error + * @return ::JXL_ENC_SUCCESS on success, ::JXL_ENC_ERROR on error */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelInfo( JxlEncoder* enc, size_t index, const JxlExtraChannelInfo* info); /** * Sets the name for the extra channel at the given index in UTF-8. The index - * must be smaller than the num_extra_channels in the associated JxlBasicInfo. + * must be smaller than the num_extra_channels in the associated @ref + * JxlBasicInfo. * * TODO(lode): remove size parameter for consistency with - * JxlEncoderSetFrameName + * @ref JxlEncoderSetFrameName * * @param enc encoder object * @param index index of the extra channel to set. @@ -1252,17 +1266,18 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelName(JxlEncoder* enc, /** * Sets a frame-specific option of integer type to the encoder options. - * The JxlEncoderFrameSettingId argument determines which option is set. + * The @ref JxlEncoderFrameSettingId argument determines which option is set. * * @param frame_settings set of options and metadata for this frame. Also * includes reference to the encoder object. * @param option ID of the option to set. * @param value Integer value to set for this option. - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR in - * case of an error, such as invalid or unknown option id, or invalid integer - * value for the given option. If an error is returned, the state of the - * JxlEncoderFrameSettings object is still valid and is the same as before this - * function was called. + * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref + * JXL_ENC_ERROR in case of an error, such as invalid or unknown option id, or + * invalid integer value for the given option. If an error is returned, the + * state of the + * @ref JxlEncoderFrameSettings object is still valid and is the same as before + * this function was called. */ JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetOption( JxlEncoderFrameSettings* frame_settings, JxlEncoderFrameSettingId option, @@ -1270,17 +1285,18 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetOption( /** * Sets a frame-specific option of float type to the encoder options. - * The JxlEncoderFrameSettingId argument determines which option is set. + * The @ref JxlEncoderFrameSettingId argument determines which option is set. * * @param frame_settings set of options and metadata for this frame. Also * includes reference to the encoder object. * @param option ID of the option to set. * @param value Float value to set for this option. - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR in - * case of an error, such as invalid or unknown option id, or invalid integer - * value for the given option. If an error is returned, the state of the - * JxlEncoderFrameSettings object is still valid and is the same as before this - * function was called. + * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref + * JXL_ENC_ERROR in case of an error, such as invalid or unknown option id, or + * invalid integer value for the given option. If an error is returned, the + * state of the + * @ref JxlEncoderFrameSettings object is still valid and is the same as before + * this function was called. */ JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetFloatOption( JxlEncoderFrameSettings* frame_settings, JxlEncoderFrameSettingId option, @@ -1292,7 +1308,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetFloatOption( * When using @ref JxlEncoderUseBoxes, @ref JxlEncoderStoreJPEGMetadata or @ref * JxlEncoderSetCodestreamLevel with level 10, the encoder will automatically * also use the container format, it is not necessary to use - * JxlEncoderUseContainer for those use cases. + * @ref JxlEncoderUseContainer for those use cases. * * By default this setting is disabled. * @@ -1318,8 +1334,8 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc, * * @param enc encoder object. * @param store_jpeg_metadata true if the encoder should store JPEG metadata. - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR - * otherwise. + * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref + * JXL_ENC_ERROR otherwise. */ JXL_EXPORT JxlEncoderStatus JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata); @@ -1334,8 +1350,8 @@ JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata); * 268435456 pixels total with a maximum width or height of 262144 pixels, * maximum 16-bit color channel depth, maximum 120 frames per second for * animation, maximum ICC color profile size of 4 MiB, it allows all color - * models and extra channel types except CMYK and the JXL_CHANNEL_BLACK extra - * channel, and a maximum of 4 extra channels in addition to the 3 color + * models and extra channel types except CMYK and the JXL_CHANNEL_BLACK + * extra channel, and a maximum of 4 extra channels in addition to the 3 color * channels. It also sets boundaries to certain internally used coding tools. * * Level 10: this level removes or increases the bounds of most of the level @@ -1355,8 +1371,8 @@ JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata); * * @param enc encoder object. * @param level the level value to set, must be -1, 5, or 10. - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR - * otherwise. + * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref + * JXL_ENC_ERROR otherwise. */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetCodestreamLevel(JxlEncoder* enc, int level); @@ -1370,9 +1386,10 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetCodestreamLevel(JxlEncoder* enc, * the JPEG XL file. * * If this returns 5, nothing needs to be done and the codestream can be - * compatible with any decoder. If this returns 10, JxlEncoderSetCodestreamLevel - * has to be used to set the codestream level to 10, or the encoder can be - * configured differently to allow using the more compatible level 5. + * compatible with any decoder. If this returns 10, @ref + * JxlEncoderSetCodestreamLevel has to be used to set the codestream level to + * 10, or the encoder can be configured differently to allow using the more + * compatible level 5. * * @param enc encoder object. * @return -1 if no level can support the configuration (e.g. image dimensions @@ -1391,14 +1408,14 @@ JXL_EXPORT int JxlEncoderGetRequiredCodestreamLevel(const JxlEncoder* enc); * * When disabled, those options are not overridden, but since those options * could still have been manually set to a combination that operates losslessly, - * using this function with lossless set to JXL_DEC_FALSE does not guarantee - * lossy encoding, though the default set of options is lossy. + * using this function with lossless set to ::JXL_FALSE does not + * guarantee lossy encoding, though the default set of options is lossy. * * @param frame_settings set of options and metadata for this frame. Also * includes reference to the encoder object. * @param lossless whether to override options for lossless mode - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR - * otherwise. + * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref + * JXL_ENC_ERROR otherwise. */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameLossless( JxlEncoderFrameSettings* frame_settings, JXL_BOOL lossless); @@ -1406,7 +1423,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameLossless( /** * Sets the distance level for lossy compression: target max butteraugli * distance, lower = higher quality. Range: 0 .. 25. - * 0.0 = mathematically lossless (however, use JxlEncoderSetFrameLossless + * 0.0 = mathematically lossless (however, use @ref JxlEncoderSetFrameLossless * instead to use true lossless, as setting distance to 0 alone is not the only * requirement). 1.0 = visually lossless. Recommended range: 0.5 .. 3.0. Default * value: 1.0. @@ -1414,24 +1431,25 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameLossless( * @param frame_settings set of options and metadata for this frame. Also * includes reference to the encoder object. * @param distance the distance value to set. - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR - * otherwise. + * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref + * JXL_ENC_ERROR otherwise. */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameDistance( JxlEncoderFrameSettings* frame_settings, float distance); /** * Sets the distance level for lossy compression of extra channels. - * The distance is as in JxlEncoderSetFrameDistance (lower = higher quality). - * If not set, or if set to the special value -1, the distance that was set with - * JxlEncoderSetFrameDistance will be used. + * The distance is as in @ref JxlEncoderSetFrameDistance (lower = higher + * quality). If not set, or if set to the special value -1, the distance that + * was set with + * @ref JxlEncoderSetFrameDistance will be used. * * @param frame_settings set of options and metadata for this frame. Also * includes reference to the encoder object. * @param index index of the extra channel to set a distance value for. * @param distance the distance value to set. - * @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR - * otherwise. + * @return ::JXL_ENC_SUCCESS if the operation was successful, @ref + * JXL_ENC_ERROR otherwise. */ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelDistance( JxlEncoderFrameSettings* frame_settings, size_t index, float distance); @@ -1441,8 +1459,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelDistance( * * This function takes in input a JPEG-style quality factor `quality` and * produces as output a `distance` value suitable to be used with @ref - * JxlEncoderSetFrameDistance and - * @ref JxlEncoderSetExtraChannelDistance. + * JxlEncoderSetFrameDistance and @ref JxlEncoderSetExtraChannelDistance. * * The `distance` value influences the level of compression, with lower values * indicating higher quality: @@ -1479,10 +1496,10 @@ JXL_EXPORT float JxlEncoderDistanceFromQuality(float quality); * the @p source options, or set to default if @p source is NULL. * * The returned pointer is an opaque struct tied to the encoder and it will be - * deallocated by the encoder when JxlEncoderDestroy() is called. For functions - * taking both a @ref JxlEncoder and a @ref JxlEncoderFrameSettings, only - * JxlEncoderFrameSettings created with this function for the same encoder - * instance can be used. + * deallocated by the encoder when @ref JxlEncoderDestroy() is called. For + * functions taking both a @ref JxlEncoder and a @ref JxlEncoderFrameSettings, + * only @ref JxlEncoderFrameSettings created with this function for the same + * encoder instance can be used. * * @param enc encoder object. * @param source source options to copy initial values from, or NULL to get @@ -1513,7 +1530,7 @@ JXL_EXPORT void JxlColorEncodingSetToLinearSRGB( /** * Enables usage of expert options. * - * At the moment, the only expert option is setting an effort value of 10, + * At the moment, the only expert option is setting an effort value of 11, * which gives the best compression for pixel-lossless modes but is very slow. * * @param enc encoder object. diff --git a/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h b/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h index e71e0aa926..ea66685dbc 100644 --- a/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h +++ b/third_party/jpeg-xl/lib/include/jxl/parallel_runner.h @@ -46,39 +46,41 @@ extern "C" { /** Return code used in the JxlParallel* functions as return value. A value * of 0 means success and any other value means error. The special value - * JXL_PARALLEL_RET_RUNNER_ERROR can be used by the runner to indicate any + * ::JXL_PARALLEL_RET_RUNNER_ERROR can be used by the runner to indicate any * other error. */ typedef int JxlParallelRetCode; /** - * General error returned by the JxlParallelRunInit function to indicate + * General error returned by the @ref JxlParallelRunInit function to indicate * an error. */ #define JXL_PARALLEL_RET_RUNNER_ERROR (-1) /** - * Parallel run initialization callback. See JxlParallelRunner for details. + * Parallel run initialization callback. See @ref JxlParallelRunner for details. * * This function MUST be called by the JxlParallelRunner only once, on the - * same thread that called JxlParallelRunner, before any parallel execution. - * The purpose of this call is to provide the maximum number of threads that the - * JxlParallelRunner will use, which can be used by JPEG XL to allocate + * same thread that called @ref JxlParallelRunner, before any parallel + * execution. The purpose of this call is to provide the maximum number of + * threads that the + * @ref JxlParallelRunner will use, which can be used by JPEG XL to allocate * per-thread storage if needed. * * @param jpegxl_opaque the @p jpegxl_opaque handle provided to - * JxlParallelRunner() must be passed here. + * @ref JxlParallelRunner() must be passed here. * @param num_threads the maximum number of threads. This value must be * positive. * @return 0 if the initialization process was successful. * @return an error code if there was an error, which should be returned by - * JxlParallelRunner(). + * @ref JxlParallelRunner(). */ typedef JxlParallelRetCode (*JxlParallelRunInit)(void* jpegxl_opaque, size_t num_threads); /** - * Parallel run data processing callback. See JxlParallelRunner for details. + * Parallel run data processing callback. See @ref JxlParallelRunner for + * details. * * This function MUST be called once for every number in the range [start_range, * end_range) (including start_range but not including end_range) passing this @@ -86,11 +88,11 @@ typedef JxlParallelRetCode (*JxlParallelRunInit)(void* jpegxl_opaque, * different threads in parallel. * * @param jpegxl_opaque the @p jpegxl_opaque handle provided to - * JxlParallelRunner() must be passed here. + * @ref JxlParallelRunner() must be passed here. * @param value the number in the range [start_range, end_range) of the call. * @param thread_id the thread number where this function is being called from. * This must be lower than the @p num_threads value passed to - * JxlParallelRunInit. + * @ref JxlParallelRunInit. */ typedef void (*JxlParallelRunFunction)(void* jpegxl_opaque, uint32_t value, size_t thread_id); @@ -103,11 +105,12 @@ typedef void (*JxlParallelRunFunction)(void* jpegxl_opaque, uint32_t value, * number in the range [start_range, end_range) (including start_range but not * including end_range) possibly from different multiple threads in parallel. * - * The JxlParallelRunner function does not need to be re-entrant. This means - * that the same JxlParallelRunner function with the same runner_opaque - * provided parameter will not be called from the library from either @p init or + * The @ref JxlParallelRunner function does not need to be re-entrant. This + * means that the same @ref JxlParallelRunner function with the same + * runner_opaque provided parameter will not be called from the library from + * either @p init or * @p func in the same decoder or encoder instance. However, a single decoding - * or encoding instance may call the provided JxlParallelRunner multiple + * or encoding instance may call the provided @ref JxlParallelRunner multiple * times for different parts of the decoding or encoding process. * * @return 0 if the @p init call succeeded (returned 0) and no other error @@ -120,7 +123,7 @@ typedef JxlParallelRetCode (*JxlParallelRunner)( void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init, JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range); -/* The following is an example of a JxlParallelRunner that doesn't use any +/* The following is an example of a @ref JxlParallelRunner that doesn't use any * multi-threading. Note that this implementation doesn't store any state * between multiple calls of the ExampleSequentialRunner function, so the * runner_opaque value is not used. diff --git a/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h b/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h index a65015d861..c82b0bc23b 100644 --- a/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h +++ b/third_party/jpeg-xl/lib/include/jxl/resizable_parallel_runner.h @@ -16,7 +16,7 @@ * created can be changed after creation of the thread pool; the threads * (including the main thread) are re-used for every * ResizableParallelRunner::Runner call. Only one concurrent - * JxlResizableParallelRunner call per instance is allowed at a time. + * @ref JxlResizableParallelRunner call per instance is allowed at a time. * * This is a scalable, lower-overhead thread pool runner, especially suitable * for data-parallel computations in the fork-join model, where clients need to @@ -41,20 +41,20 @@ extern "C" { #endif -/** Parallel runner internally using std::thread. Use as JxlParallelRunner. +/** Parallel runner internally using std::thread. Use as @ref JxlParallelRunner. */ JXL_THREADS_EXPORT JxlParallelRetCode JxlResizableParallelRunner( void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init, JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range); -/** Creates the runner for JxlResizableParallelRunner. Use as the opaque +/** Creates the runner for @ref JxlResizableParallelRunner. Use as the opaque * runner. The runner will execute tasks on the calling thread until * @ref JxlResizableParallelRunnerSetThreads is called. */ JXL_THREADS_EXPORT void* JxlResizableParallelRunnerCreate( const JxlMemoryManager* memory_manager); -/** Changes the number of threads for JxlResizableParallelRunner. +/** Changes the number of threads for @ref JxlResizableParallelRunner. */ JXL_THREADS_EXPORT void JxlResizableParallelRunnerSetThreads( void* runner_opaque, size_t num_threads); @@ -64,7 +64,7 @@ JXL_THREADS_EXPORT void JxlResizableParallelRunnerSetThreads( JXL_THREADS_EXPORT uint32_t JxlResizableParallelRunnerSuggestThreads(uint64_t xsize, uint64_t ysize); -/** Destroys the runner created by JxlResizableParallelRunnerCreate. +/** Destroys the runner created by @ref JxlResizableParallelRunnerCreate. */ JXL_THREADS_EXPORT void JxlResizableParallelRunnerDestroy(void* runner_opaque); diff --git a/third_party/jpeg-xl/lib/include/jxl/stats.h b/third_party/jpeg-xl/lib/include/jxl/stats.h index c9359dc870..5ed440636f 100644 --- a/third_party/jpeg-xl/lib/include/jxl/stats.h +++ b/third_party/jpeg-xl/lib/include/jxl/stats.h @@ -23,15 +23,15 @@ extern "C" { /** * Opaque structure that holds the encoder statistics. * - * Allocated and initialized with JxlEncoderStatsCreate(). - * Cleaned up and deallocated with JxlEncoderStatsDestroy(). + * Allocated and initialized with @ref JxlEncoderStatsCreate(). + * Cleaned up and deallocated with @ref JxlEncoderStatsDestroy(). */ typedef struct JxlEncoderStatsStruct JxlEncoderStats; /** * Creates an instance of JxlEncoderStats and initializes it. * - * @return pointer to initialized JxlEncoderStats instance + * @return pointer to initialized @ref JxlEncoderStats instance */ JXL_EXPORT JxlEncoderStats* JxlEncoderStatsCreate(void); @@ -43,7 +43,7 @@ JXL_EXPORT JxlEncoderStats* JxlEncoderStatsCreate(void); */ JXL_EXPORT void JxlEncoderStatsDestroy(JxlEncoderStats* stats); -/** Data type for querying JxlEncoderStats object +/** Data type for querying @ref JxlEncoderStats object */ typedef enum { JXL_ENC_STAT_HEADER_BITS, diff --git a/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h b/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h index 6166fe7d36..933f373ce9 100644 --- a/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h +++ b/third_party/jpeg-xl/lib/include/jxl/thread_parallel_runner.h @@ -41,24 +41,24 @@ extern "C" { #endif -/** Parallel runner internally using std::thread. Use as JxlParallelRunner. +/** Parallel runner internally using std::thread. Use as @ref JxlParallelRunner. */ JXL_THREADS_EXPORT JxlParallelRetCode JxlThreadParallelRunner( void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init, JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range); -/** Creates the runner for JxlThreadParallelRunner. Use as the opaque +/** Creates the runner for @ref JxlThreadParallelRunner. Use as the opaque * runner. */ JXL_THREADS_EXPORT void* JxlThreadParallelRunnerCreate( const JxlMemoryManager* memory_manager, size_t num_worker_threads); -/** Destroys the runner created by JxlThreadParallelRunnerCreate. +/** Destroys the runner created by @ref JxlThreadParallelRunnerCreate. */ JXL_THREADS_EXPORT void JxlThreadParallelRunnerDestroy(void* runner_opaque); /** Returns a default num_worker_threads value for - * JxlThreadParallelRunnerCreate. + * @ref JxlThreadParallelRunnerCreate. */ JXL_THREADS_EXPORT size_t JxlThreadParallelRunnerDefaultNumWorkerThreads(void); diff --git a/third_party/jpeg-xl/lib/include/jxl/types.h b/third_party/jpeg-xl/lib/include/jxl/types.h index a47216f878..2538dbaa82 100644 --- a/third_party/jpeg-xl/lib/include/jxl/types.h +++ b/third_party/jpeg-xl/lib/include/jxl/types.h @@ -13,7 +13,6 @@ #ifndef JXL_TYPES_H_ #define JXL_TYPES_H_ -#include <jxl/jxl_export.h> #include <stddef.h> #include <stdint.h> @@ -32,15 +31,17 @@ extern "C" { #define JXL_TRUE 1 /** Portable @c false replacement. */ #define JXL_FALSE 0 -/** Converts of bool-like value to either JXL_TRUE or JXL_FALSE. */ +/** Converts of bool-like value to either ::JXL_TRUE or ::JXL_FALSE. */ #define TO_JXL_BOOL(C) (!!(C) ? JXL_TRUE : JXL_FALSE) +/** Converts JXL_BOOL to C++ bool. */ +#define FROM_JXL_BOOL(C) (static_cast<bool>(C)) /** Data type for the sample values per channel per pixel. */ typedef enum { /** Use 32-bit single-precision floating point values, with range 0.0-1.0 * (within gamut, may go outside this range for wide color gamut). Floating - * point output, either JXL_TYPE_FLOAT or JXL_TYPE_FLOAT16, is recommended + * point output, either ::JXL_TYPE_FLOAT or ::JXL_TYPE_FLOAT16, is recommended * for HDR and wide gamut images when color profile conversion is required. */ JXL_TYPE_FLOAT = 0, @@ -92,8 +93,7 @@ typedef struct { JxlDataType data_type; /** Whether multi-byte data types are represented in big endian or little - * endian format. This applies to JXL_TYPE_UINT16, JXL_TYPE_UINT32 - * and JXL_TYPE_FLOAT. + * endian format. This applies to ::JXL_TYPE_UINT16 and ::JXL_TYPE_FLOAT. */ JxlEndianness endianness; diff --git a/third_party/jpeg-xl/lib/jpegli.cmake b/third_party/jpeg-xl/lib/jpegli.cmake index f06912f438..a471c8b2ab 100644 --- a/third_party/jpeg-xl/lib/jpegli.cmake +++ b/third_party/jpeg-xl/lib/jpegli.cmake @@ -88,7 +88,6 @@ foreach (TESTFILE IN LISTS JPEGXL_INTERNAL_JPEGLI_TESTS) target_link_libraries(${TESTNAME} hwy jpegli-static - gmock GTest::GTest GTest::Main ${JPEG_LIBRARIES} diff --git a/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc b/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc index 6a8c4d3128..2039326cbd 100644 --- a/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc +++ b/third_party/jpeg-xl/lib/jpegli/adaptive_quantization.cc @@ -5,6 +5,7 @@ #include "lib/jpegli/adaptive_quantization.h" +#include <jxl/types.h> #include <stddef.h> #include <stdlib.h> @@ -46,7 +47,7 @@ using hwy::HWY_NAMESPACE::Sqrt; using hwy::HWY_NAMESPACE::Sub; using hwy::HWY_NAMESPACE::ZeroIfNegative; -static constexpr float kInputScaling = 1.0f / 255.0f; +constexpr float kInputScaling = 1.0f / 255.0f; // Primary template: default to actual division. template <typename T, class V> @@ -65,7 +66,7 @@ struct FastDivision<float, V> { } V operator()(const V n, const V d) const { -#if 1 // Faster on SKX +#if JXL_TRUE // Faster on SKX return Div(n, d); #else return n * ReciprocalNR(d); @@ -191,12 +192,12 @@ V ComputeMask(const D d, const V out_val) { } // mul and mul2 represent a scaling difference between jxl and butteraugli. -static const float kSGmul = 226.0480446705883f; -static const float kSGmul2 = 1.0f / 73.377132366608819f; -static const float kLog2 = 0.693147181f; +const float kSGmul = 226.0480446705883f; +const float kSGmul2 = 1.0f / 73.377132366608819f; +const float kLog2 = 0.693147181f; // Includes correction factor for std::log -> log2. -static const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2; -static const float kSGVOffset = 7.14672470003f; +const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2; +const float kSGVOffset = 7.14672470003f; template <bool invert, typename D, typename V> V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) { @@ -226,7 +227,7 @@ V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) { } template <bool invert = false> -static float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) { +float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) { using DScalar = HWY_CAPPED(float, 1); auto vscalar = Load(DScalar(), &v); return GetLane( @@ -503,7 +504,7 @@ HWY_EXPORT(PerBlockModulations); namespace { -static constexpr int kPreErosionBorder = 1; +constexpr int kPreErosionBorder = 1; } // namespace diff --git a/third_party/jpeg-xl/lib/jpegli/bitstream.cc b/third_party/jpeg-xl/lib/jpegli/bitstream.cc index 3448367dde..4dbeb738bb 100644 --- a/third_party/jpeg-xl/lib/jpegli/bitstream.cc +++ b/third_party/jpeg-xl/lib/jpegli/bitstream.cc @@ -90,8 +90,8 @@ bool EncodeDQT(j_compress_ptr cinfo, bool write_all_tables) { JPEGLI_ERROR("Missing quant table %d", i); } int precision = 0; - for (size_t k = 0; k < DCTSIZE2; ++k) { - if (quant_table->quantval[k] > 255) { + for (UINT16 q : quant_table->quantval) { + if (q > 255) { precision = 1; is_baseline = false; } @@ -123,7 +123,6 @@ bool EncodeDQT(j_compress_ptr cinfo, bool write_all_tables) { void EncodeSOF(j_compress_ptr cinfo, bool is_baseline) { if (cinfo->data_precision != kJpegPrecision) { - is_baseline = false; JPEGLI_ERROR("Unsupported data precision %d", cinfo->data_precision); } const uint8_t marker = cinfo->progressive_mode ? 0xc2 @@ -302,7 +301,7 @@ void WriteBlock(const int32_t* JXL_RESTRICT symbols, namespace { -static JXL_INLINE void EmitMarker(JpegBitWriter* bw, int marker) { +JXL_INLINE void EmitMarker(JpegBitWriter* bw, int marker) { bw->data[bw->pos++] = 0xFF; bw->data[bw->pos++] = marker; } diff --git a/third_party/jpeg-xl/lib/jpegli/bitstream.h b/third_party/jpeg-xl/lib/jpegli/bitstream.h index aa54c73d7e..bed441aefe 100644 --- a/third_party/jpeg-xl/lib/jpegli/bitstream.h +++ b/third_party/jpeg-xl/lib/jpegli/bitstream.h @@ -32,9 +32,8 @@ void EncodeSOS(j_compress_ptr cinfo, int scan_index); void WriteScanHeader(j_compress_ptr cinfo, int scan_index); void WriteBlock(const int32_t* JXL_RESTRICT symbols, - const int32_t* JXL_RESTRICT extra_bits, const int num_nonzeros, - const bool emit_eob, - const HuffmanCodeTable* JXL_RESTRICT dc_code, + const int32_t* JXL_RESTRICT extra_bits, int num_nonzeros, + bool emit_eob, const HuffmanCodeTable* JXL_RESTRICT dc_code, const HuffmanCodeTable* JXL_RESTRICT ac_code, JpegBitWriter* JXL_RESTRICT bw); void WriteScanData(j_compress_ptr cinfo, int scan_index); diff --git a/third_party/jpeg-xl/lib/jpegli/color_quantize.cc b/third_party/jpeg-xl/lib/jpegli/color_quantize.cc index e8357e2160..c4f32bf439 100644 --- a/third_party/jpeg-xl/lib/jpegli/color_quantize.cc +++ b/third_party/jpeg-xl/lib/jpegli/color_quantize.cc @@ -11,13 +11,14 @@ #include "lib/jpegli/decode_internal.h" #include "lib/jpegli/error.h" +#include "lib/jxl/base/status.h" namespace jpegli { namespace { -static constexpr int kNumColorCellBits[kMaxComponents] = {3, 4, 3, 3}; -static constexpr int kCompW[kMaxComponents] = {2, 3, 1, 1}; +constexpr int kNumColorCellBits[kMaxComponents] = {3, 4, 3, 3}; +constexpr int kCompW[kMaxComponents] = {2, 3, 1, 1}; int Pow(int a, int b) { int r = 1; @@ -102,8 +103,8 @@ namespace { // 2^13 priority levels for the PQ seems to be a good compromise between // accuracy, running time and stack space usage. -static const int kMaxPriority = 1 << 13; -static const int kMaxLevel = 3; +const int kMaxPriority = 1 << 13; +const int kMaxLevel = 3; // This function is used in the multi-resolution grid to be able to compute // the keys for the different resolutions by just shifting the first key. @@ -153,7 +154,7 @@ inline int ColorIntQuadDistanceRGB(uint8_t r1, uint8_t g1, uint8_t b1, } inline int ScaleQuadDistanceRGB(int d) { - return static_cast<int>(sqrt(d * 0.25) + 0.5); + return static_cast<int>(std::lround(sqrt(d * 0.25))); } // The function updates the minimal distances, the clustering and the @@ -216,9 +217,9 @@ struct WangHasher { // to a unique integer index assigned to the different colors in order of // appearance in the image. Return the number of unique colors found. // The colors are pre-quantized to 3 * 6 bits precision. -static int BuildRGBColorIndex(const uint8_t* const image, int const num_pixels, - int* const count, uint8_t* const red, - uint8_t* const green, uint8_t* const blue) { +int BuildRGBColorIndex(const uint8_t* const image, int const num_pixels, + int* const count, uint8_t* const red, + uint8_t* const green, uint8_t* const blue) { // Impossible because rgb are in the low 24 bits, and the upper 8 bits is 0. const uint32_t impossible_pixel_value = 0x10000000; std::unordered_map<uint32_t, int, RGBPixelHasher> index_map(1 << 12); @@ -264,7 +265,7 @@ void ChooseColorMap2Pass(j_decompress_ptr cinfo) { std::unique_ptr<uint8_t[]> blue(new uint8_t[max_color_count]); std::vector<int> count(max_color_count, 0); // number of colors - int n = BuildRGBColorIndex(m->pixels_, num_pixels, &count[0], &red[0], + int n = BuildRGBColorIndex(m->pixels_, num_pixels, count.data(), &red[0], &green[0], &blue[0]); std::vector<int> dist(n, std::numeric_limits<int>::max()); @@ -285,14 +286,14 @@ void ChooseColorMap2Pass(j_decompress_ptr cinfo) { winner = i; } if (!in_palette[i] && count[i] > count_threshold) { - AddToRGBPalette(&red[0], &green[0], &blue[0], &count[0], i, k++, n, - &dist[0], &cluster[0], ¢er[0], &error); + AddToRGBPalette(&red[0], &green[0], &blue[0], count.data(), i, k++, n, + dist.data(), cluster.data(), ¢er[0], &error); in_palette[i] = true; } } if (k == 0) { - AddToRGBPalette(&red[0], &green[0], &blue[0], &count[0], winner, k++, n, - &dist[0], &cluster[0], ¢er[0], &error); + AddToRGBPalette(&red[0], &green[0], &blue[0], count.data(), winner, k++, n, + dist.data(), cluster.data(), ¢er[0], &error); in_palette[winner] = true; } @@ -365,8 +366,8 @@ void ChooseColorMap2Pass(j_decompress_ptr cinfo) { if (priority < top_priority) { bucket_array[priority].push_back(i); } else { - AddToRGBPalette(&red[0], &green[0], &blue[0], &count[0], i, k++, n, - &dist[0], &cluster[0], ¢er[0], &error); + AddToRGBPalette(&red[0], &green[0], &blue[0], count.data(), i, k++, n, + dist.data(), cluster.data(), ¢er[0], &error); } bucket_array[top_priority].pop_back(); while (top_priority >= 0 && bucket_array[top_priority].empty()) { @@ -387,7 +388,7 @@ void ChooseColorMap2Pass(j_decompress_ptr cinfo) { namespace { -void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, int cell[], +void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, const int cell[], std::vector<uint8_t>* candidates) { int cell_min[kMaxComponents]; int cell_max[kMaxComponents]; @@ -404,7 +405,8 @@ void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, int cell[], int dmax = 0; for (int c = 0; c < ncomp; ++c) { int palette_c = cinfo->colormap[c][i]; - int dminc = 0, dmaxc; + int dminc = 0; + int dmaxc; if (palette_c < cell_min[c]) { dminc = cell_min[c] - palette_c; dmaxc = cell_max[c] - palette_c; @@ -436,6 +438,8 @@ void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, int cell[], void CreateInverseColorMap(j_decompress_ptr cinfo) { jpeg_decomp_master* m = cinfo->master; int ncomp = cinfo->out_color_components; + JXL_ASSERT(ncomp > 0); + JXL_ASSERT(ncomp <= kMaxComponents); int num_cells = 1; for (int c = 0; c < ncomp; ++c) { num_cells *= (1 << kNumColorCellBits[c]); @@ -455,7 +459,7 @@ void CreateInverseColorMap(j_decompress_ptr cinfo) { m->regenerate_inverse_colormap_ = false; } -int LookupColorIndex(j_decompress_ptr cinfo, JSAMPLE* pixel) { +int LookupColorIndex(j_decompress_ptr cinfo, const JSAMPLE* pixel) { jpeg_decomp_master* m = cinfo->master; int num_channels = cinfo->out_color_components; int index = 0; diff --git a/third_party/jpeg-xl/lib/jpegli/color_quantize.h b/third_party/jpeg-xl/lib/jpegli/color_quantize.h index 3dda1d8713..92b922f756 100644 --- a/third_party/jpeg-xl/lib/jpegli/color_quantize.h +++ b/third_party/jpeg-xl/lib/jpegli/color_quantize.h @@ -20,7 +20,7 @@ void CreateOrderedDitherTables(j_decompress_ptr cinfo); void InitFSDitherState(j_decompress_ptr cinfo); -int LookupColorIndex(j_decompress_ptr cinfo, JSAMPLE* pixel); +int LookupColorIndex(j_decompress_ptr cinfo, const JSAMPLE* pixel); } // namespace jpegli diff --git a/third_party/jpeg-xl/lib/jpegli/dct-inl.h b/third_party/jpeg-xl/lib/jpegli/dct-inl.h index 1cbe704002..66cc3b6b53 100644 --- a/third_party/jpeg-xl/lib/jpegli/dct-inl.h +++ b/third_party/jpeg-xl/lib/jpegli/dct-inl.h @@ -187,7 +187,7 @@ void DCT1D(const float* JXL_RESTRICT pixels, size_t pixels_stride, } } -static JXL_INLINE JXL_MAYBE_UNUSED void TransformFromPixels( +JXL_INLINE JXL_MAYBE_UNUSED void TransformFromPixels( const float* JXL_RESTRICT pixels, size_t pixels_stride, float* JXL_RESTRICT coefficients, float* JXL_RESTRICT scratch_space) { DCT1D(pixels, pixels_stride, scratch_space); @@ -196,14 +196,14 @@ static JXL_INLINE JXL_MAYBE_UNUSED void TransformFromPixels( Transpose8x8Block(scratch_space, coefficients); } -static JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec<DI>& ival, - int16_t* out) { +JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec<DI>& ival, + int16_t* out) { Rebind<int16_t, DI> di16; Store(DemoteTo(di16, ival), di16, out); } -static JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec<DI>& ival, - int32_t* out) { +JXL_INLINE JXL_MAYBE_UNUSED void StoreQuantizedValue(const Vec<DI>& ival, + int32_t* out) { DI di; Store(ival, di, out); } diff --git a/third_party/jpeg-xl/lib/jpegli/decode.cc b/third_party/jpeg-xl/lib/jpegli/decode.cc index 758babeb5e..9fdf68dd18 100644 --- a/third_party/jpeg-xl/lib/jpegli/decode.cc +++ b/third_party/jpeg-xl/lib/jpegli/decode.cc @@ -115,8 +115,10 @@ void InitProgressMonitor(j_decompress_ptr cinfo, bool coef_only) { cinfo->progress->total_passes = 1; } else { int input_passes = !cinfo->buffered_image && m->is_multiscan_ ? 1 : 0; - bool two_pass_quant = cinfo->quantize_colors && !cinfo->colormap && - cinfo->two_pass_quantize && cinfo->enable_2pass_quant; + bool two_pass_quant = FROM_JXL_BOOL(cinfo->quantize_colors) && + (cinfo->colormap != nullptr) && + FROM_JXL_BOOL(cinfo->two_pass_quantize) && + FROM_JXL_BOOL(cinfo->enable_2pass_quant); cinfo->progress->total_passes = input_passes + (two_pass_quant ? 2 : 1); } cinfo->progress->completed_passes = 0; @@ -175,7 +177,7 @@ void BuildHuffmanLookupTable(j_decompress_ptr cinfo, JHUFF_TBL* table, for (int i = 0; i < total_count; ++i) { int value = table->huffval[i]; if (values_seen[value]) { - return JPEGLI_ERROR("Duplicate Huffman code value %d", value); + JPEGLI_ERROR("Duplicate Huffman code value %d", value); } values_seen[value] = 1; values[i] = value; @@ -223,7 +225,7 @@ void PrepareForScan(j_decompress_ptr cinfo) { HuffmanTableEntry* huff_lut = &m->dc_huff_lut_[dc_tbl_idx * kJpegHuffmanLutSize]; if (!table) { - return JPEGLI_ERROR("DC Huffman table %d not found", dc_tbl_idx); + JPEGLI_ERROR("DC Huffman table %d not found", dc_tbl_idx); } BuildHuffmanLookupTable(cinfo, table, huff_lut); } @@ -233,7 +235,7 @@ void PrepareForScan(j_decompress_ptr cinfo) { HuffmanTableEntry* huff_lut = &m->ac_huff_lut_[ac_tbl_idx * kJpegHuffmanLutSize]; if (!table) { - return JPEGLI_ERROR("AC Huffman table %d not found", ac_tbl_idx); + JPEGLI_ERROR("AC Huffman table %d not found", ac_tbl_idx); } BuildHuffmanLookupTable(cinfo, table, huff_lut); } @@ -543,8 +545,8 @@ void jpegli_CreateDecompress(j_decompress_ptr cinfo, int version, cinfo->is_decompressor = TRUE; cinfo->progress = nullptr; cinfo->src = nullptr; - for (int i = 0; i < NUM_QUANT_TBLS; i++) { - cinfo->quant_tbl_ptrs[i] = nullptr; + for (auto& quant_tbl_ptr : cinfo->quant_tbl_ptrs) { + quant_tbl_ptr = nullptr; } for (int i = 0; i < NUM_HUFF_TBLS; i++) { cinfo->dc_huff_tbl_ptrs[i] = nullptr; @@ -555,8 +557,8 @@ void jpegli_CreateDecompress(j_decompress_ptr cinfo, int version, cinfo->rec_outbuf_height = 1; // output works with any buffer height cinfo->master = new jpeg_decomp_master; jpeg_decomp_master* m = cinfo->master; - for (int i = 0; i < 16; ++i) { - m->app_marker_parsers[i] = nullptr; + for (auto& app_marker_parser : m->app_marker_parsers) { + app_marker_parser = nullptr; } m->com_marker_parser = nullptr; memset(m->markers_to_save_, 0, sizeof(m->markers_to_save_)); @@ -661,7 +663,7 @@ boolean jpegli_read_icc_profile(j_decompress_ptr cinfo, JOCTET** icc_data_ptr, return FALSE; } *icc_data_len = m->icc_profile_.size(); - *icc_data_ptr = (JOCTET*)malloc(*icc_data_len); + *icc_data_ptr = static_cast<JOCTET*>(malloc(*icc_data_len)); if (*icc_data_ptr == nullptr) { JPEGLI_ERROR("jpegli_read_icc_profile: Out of memory"); } @@ -738,21 +740,26 @@ void jpegli_calc_output_dimensions(j_decompress_ptr cinfo) { } boolean jpegli_has_multiple_scans(j_decompress_ptr cinfo) { - if (cinfo->input_scan_number == 0) { - JPEGLI_ERROR("No SOS marker found."); + if (cinfo->global_state != jpegli::kDecHeaderDone && + cinfo->global_state != jpegli::kDecProcessScan && + cinfo->global_state != jpegli::kDecProcessMarkers) { + JPEGLI_ERROR("jpegli_has_multiple_scans: unexpected state %d", + cinfo->global_state); } - return cinfo->master->is_multiscan_; + return TO_JXL_BOOL(cinfo->master->is_multiscan_); } boolean jpegli_input_complete(j_decompress_ptr cinfo) { - return cinfo->master->found_eoi_; + return TO_JXL_BOOL(cinfo->master->found_eoi_); } boolean jpegli_start_decompress(j_decompress_ptr cinfo) { jpeg_decomp_master* m = cinfo->master; if (cinfo->global_state == jpegli::kDecHeaderDone) { - m->streaming_mode_ = !m->is_multiscan_ && !cinfo->buffered_image && - (!cinfo->quantize_colors || !cinfo->two_pass_quantize); + m->streaming_mode_ = !m->is_multiscan_ && + !FROM_JXL_BOOL(cinfo->buffered_image) && + (!FROM_JXL_BOOL(cinfo->quantize_colors) || + !FROM_JXL_BOOL(cinfo->two_pass_quantize)); jpegli::AllocateCoefficientBuffer(cinfo); jpegli_calc_output_dimensions(cinfo); jpegli::PrepareForScan(cinfo); diff --git a/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc b/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc index c48b9377c3..0cc5a194d7 100644 --- a/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc +++ b/third_party/jpeg-xl/lib/jpegli/decode_api_test.cc @@ -18,8 +18,8 @@ namespace jpegli { namespace { -static constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9}; -static constexpr size_t kNumSourceBuffers = 4; +constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9}; +constexpr size_t kNumSourceBuffers = 4; // Custom source manager that refills the input buffer in chunks, simulating // a file reader with a fixed buffer size. @@ -61,7 +61,7 @@ class SourceManager { static void init_source(j_decompress_ptr cinfo) {} static boolean fill_input_buffer(j_decompress_ptr cinfo) { - auto src = reinterpret_cast<SourceManager*>(cinfo->src); + auto* src = reinterpret_cast<SourceManager*>(cinfo->src); if (src->pos_ < src->len_) { size_t chunk_size = std::min(src->len_ - src->pos_, src->max_chunk_size_); size_t next_idx = ++src->chunk_idx_ % kNumSourceBuffers; @@ -79,7 +79,7 @@ class SourceManager { } static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) { - auto src = reinterpret_cast<SourceManager*>(cinfo->src); + auto* src = reinterpret_cast<SourceManager*>(cinfo->src); if (num_bytes <= 0) { return; } @@ -166,9 +166,9 @@ void ReadOutputImage(const DecompressParams& dparams, j_decompress_ptr cinfo, rowdata[c][i] = y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr; } - data[c] = &rowdata[c][0]; + data[c] = rowdata[c].data(); } - num_output_lines = jpegli_read_raw_data(cinfo, &data[0], max_lines); + num_output_lines = jpegli_read_raw_data(cinfo, data.data(), max_lines); } else { size_t max_output_lines = dparams.max_output_lines; if (max_output_lines == 0) max_output_lines = cinfo->output_height; @@ -189,7 +189,7 @@ void ReadOutputImage(const DecompressParams& dparams, j_decompress_ptr cinfo, scanlines[i] = &output->pixels[yidx * stride]; } num_output_lines = - jpegli_read_scanlines(cinfo, &scanlines[0], max_lines); + jpegli_read_scanlines(cinfo, scanlines.data(), max_lines); if (cinfo->quantize_colors) { for (size_t i = 0; i < num_output_lines; ++i) { UnmapColors(scanlines[i], cinfo->output_width, @@ -222,7 +222,7 @@ struct TestConfig { std::vector<uint8_t> GetTestJpegData(TestConfig& config) { std::vector<uint8_t> compressed; if (!config.fn.empty()) { - compressed = ReadTestData(config.fn.c_str()); + compressed = ReadTestData(config.fn); } else { GeneratePixels(&config.input); JXL_CHECK(EncodeWithJpegli(config.input, config.jparams, &compressed)); @@ -297,10 +297,10 @@ void TestAPIBuffered(const CompressParams& jparams, SetDecompressParams(dparams, cinfo); jpegli_set_output_format(cinfo, dparams.data_type, dparams.endianness); VerifyHeader(jparams, cinfo); + bool has_multiple_scans = FROM_JXL_BOOL(jpegli_has_multiple_scans(cinfo)); EXPECT_TRUE(jpegli_start_decompress(cinfo)); // start decompress should not read the whole input in buffered image mode EXPECT_FALSE(jpegli_input_complete(cinfo)); - bool has_multiple_scans = jpegli_has_multiple_scans(cinfo); EXPECT_EQ(0, cinfo->output_scan_number); int sos_marker_cnt = 1; // read_header reads the first SOS marker while (!jpegli_input_complete(cinfo)) { @@ -341,8 +341,11 @@ void TestAPIBuffered(const CompressParams& jparams, } TEST(DecodeAPITest, ReuseCinfo) { - TestImage input, output, expected; - std::vector<TestImage> output_progression, expected_output_progression; + TestImage input; + TestImage output; + TestImage expected; + std::vector<TestImage> output_progression; + std::vector<TestImage> expected_output_progression; CompressParams jparams; DecompressParams dparams; std::vector<uint8_t> compressed; @@ -383,8 +386,8 @@ TEST(DecodeAPITest, ReuseCinfo) { expected.Clear(); DecodeWithLibjpeg(jparams, dparams, compressed, &expected); output.Clear(); - cinfo.buffered_image = false; - cinfo.raw_data_out = false; + cinfo.buffered_image = JXL_FALSE; + cinfo.raw_data_out = JXL_FALSE; cinfo.scale_num = cinfo.scale_denom = 1; SourceManager src(compressed.data(), compressed.size(), 1u << 12); @@ -1245,7 +1248,8 @@ std::ostream& operator<<(std::ostream& os, const DecompressParams& dparams) { } os << IOMethodName(dparams.data_type, dparams.endianness); if (dparams.set_out_color_space) { - os << "OutColor" << ColorSpaceName((J_COLOR_SPACE)dparams.out_color_space); + os << "OutColor" + << ColorSpaceName(static_cast<J_COLOR_SPACE>(dparams.out_color_space)); } if (dparams.crop_output) { os << "Crop"; @@ -1265,7 +1269,8 @@ std::ostream& operator<<(std::ostream& os, const DecompressParams& dparams) { if (i > 0) os << "_"; const auto& sparam = dparams.scan_params[i]; os << QuantMode(sparam.color_quant_mode); - os << DitherMode((J_DITHER_MODE)sparam.dither_mode) << "Dither"; + os << DitherMode(static_cast<J_DITHER_MODE>(sparam.dither_mode)) + << "Dither"; } } if (dparams.skip_scans) { diff --git a/third_party/jpeg-xl/lib/jpegli/decode_internal.h b/third_party/jpeg-xl/lib/jpegli/decode_internal.h index ed7baa39e9..37dfcc4526 100644 --- a/third_party/jpeg-xl/lib/jpegli/decode_internal.h +++ b/third_party/jpeg-xl/lib/jpegli/decode_internal.h @@ -45,12 +45,13 @@ struct jpeg_decomp_master { size_t input_buffer_pos_; // Number of bits after codestream_pos_ that were already processed. size_t codestream_bits_ahead_; - bool streaming_mode_; // Coefficient buffers jvirt_barray_ptr* coef_arrays; JBLOCKARRAY coeff_rows[jpegli::kMaxComponents]; + bool streaming_mode_; + // // Marker data processing state. // @@ -58,6 +59,11 @@ struct jpeg_decomp_master { bool found_dri_; bool found_sof_; bool found_eoi_; + + // Whether this jpeg has multiple scans (progressive or non-interleaved + // sequential). + bool is_multiscan_; + size_t icc_index_; size_t icc_total_; std::vector<uint8_t> icc_profile_; @@ -66,9 +72,6 @@ struct jpeg_decomp_master { uint8_t markers_to_save_[32]; jpeg_marker_parser_method app_marker_parsers[16]; jpeg_marker_parser_method com_marker_parser; - // Whether this jpeg has multiple scans (progressive or non-interleaved - // sequential). - bool is_multiscan_; // Fields defined by SOF marker. size_t iMCU_cols_; @@ -96,9 +99,11 @@ struct jpeg_decomp_master { // int output_passes_done_; JpegliDataType output_data_type_ = JPEGLI_TYPE_UINT8; - bool swap_endianness_ = false; size_t xoffset_; + bool swap_endianness_ = false; bool need_context_rows_; + bool regenerate_inverse_colormap_; + bool apply_smoothing; int min_scaled_dct_size; int scaled_dct_size[jpegli::kMaxComponents]; @@ -127,7 +132,6 @@ struct jpeg_decomp_master { uint8_t* pixels_; JSAMPARRAY scanlines_; std::vector<std::vector<uint8_t>> candidate_lists_; - bool regenerate_inverse_colormap_; float* dither_[jpegli::kMaxComponents]; float* error_row_[2 * jpegli::kMaxComponents]; size_t dither_size_; @@ -145,7 +149,6 @@ struct jpeg_decomp_master { // i.e. the bottom half when rendering incomplete scans. int (*coef_bits_latch)[SAVED_COEFS]; int (*prev_coef_bits_latch)[SAVED_COEFS]; - bool apply_smoothing; }; #endif // LIB_JPEGLI_DECODE_INTERNAL_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/decode_marker.cc b/third_party/jpeg-xl/lib/jpegli/decode_marker.cc index c5c5790cdf..a9ed4df329 100644 --- a/third_party/jpeg-xl/lib/jpegli/decode_marker.cc +++ b/third_party/jpeg-xl/lib/jpegli/decode_marker.cc @@ -5,6 +5,7 @@ #include "lib/jpegli/decode_marker.h" +#include <jxl/types.h> #include <string.h> #include "lib/jpegli/common.h" @@ -22,23 +23,22 @@ constexpr uint8_t kIccProfileTag[12] = "ICC_PROFILE"; // Macros for commonly used error conditions. -#define JPEG_VERIFY_LEN(n) \ - if (pos + (n) > len) { \ - return JPEGLI_ERROR("Unexpected end of marker: pos=%" PRIuS \ - " need=%d len=%" PRIuS, \ - pos, static_cast<int>(n), len); \ +#define JPEG_VERIFY_LEN(n) \ + if (pos + (n) > len) { \ + JPEGLI_ERROR("Unexpected end of marker: pos=%" PRIuS \ + " need=%d len=%" PRIuS, \ + pos, static_cast<int>(n), len); \ } -#define JPEG_VERIFY_INPUT(var, low, high) \ - if ((var) < (low) || (var) > (high)) { \ - return JPEGLI_ERROR("Invalid " #var ": %d", static_cast<int>(var)); \ +#define JPEG_VERIFY_INPUT(var, low, high) \ + if ((var) < (low) || (var) > (high)) { \ + JPEGLI_ERROR("Invalid " #var ": %d", static_cast<int>(var)); \ } -#define JPEG_VERIFY_MARKER_END() \ - if (pos != len) { \ - return JPEGLI_ERROR("Invalid marker length: declared=%" PRIuS \ - " actual=%" PRIuS, \ - len, pos); \ +#define JPEG_VERIFY_MARKER_END() \ + if (pos != len) { \ + JPEGLI_ERROR("Invalid marker length: declared=%" PRIuS " actual=%" PRIuS, \ + len, pos); \ } inline int ReadUint8(const uint8_t* data, size_t* pos) { @@ -60,7 +60,7 @@ void ProcessSOF(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { JPEGLI_ERROR("Duplicate SOF marker."); } m->found_sof_ = true; - cinfo->progressive_mode = (cinfo->unread_marker == 0xc2); + cinfo->progressive_mode = TO_JXL_BOOL(cinfo->unread_marker == 0xc2); cinfo->arith_code = 0; size_t pos = 2; JPEG_VERIFY_LEN(6); @@ -181,7 +181,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { for (int i = 0; i < cinfo->comps_in_scan; ++i) { int id = ReadUint8(data, &pos); if (ids_seen[id]) { // (cf. section B.2.3, regarding CSj) - return JPEGLI_ERROR("Duplicate ID %d in SOS.", id); + JPEGLI_ERROR("Duplicate ID %d in SOS.", id); } ids_seen[id] = 1; jpeg_component_info* comp = nullptr; @@ -192,8 +192,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { } } if (!comp) { - return JPEGLI_ERROR("SOS marker: Could not find component with id %d", - id); + JPEGLI_ERROR("SOS marker: Could not find component with id %d", id); } int c = ReadUint8(data, &pos); comp->dc_tbl_no = c >> 4; @@ -222,7 +221,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { if (cinfo->input_scan_number == 0) { m->is_multiscan_ = (cinfo->comps_in_scan < cinfo->num_components || - cinfo->progressive_mode); + FROM_JXL_BOOL(cinfo->progressive_mode)); } if (cinfo->Ah != 0 && cinfo->Al != cinfo->Ah - 1) { // section G.1.1.1.2 : Successive approximation control only improves @@ -261,12 +260,12 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { int comp_idx = cinfo->cur_comp_info[i]->component_index; for (int k = cinfo->Ss; k <= cinfo->Se; ++k) { if (m->scan_progression_[comp_idx][k] & scan_bitmask) { - return JPEGLI_ERROR( + JPEGLI_ERROR( "Overlapping scans: component=%d k=%d prev_mask: %u cur_mask %u", comp_idx, k, m->scan_progression_[i][k], scan_bitmask); } if (m->scan_progression_[comp_idx][k] & refinement_bitmask) { - return JPEGLI_ERROR( + JPEGLI_ERROR( "Invalid scan order, a more refined scan was already done: " "component=%d k=%d prev_mask=%u cur_mask=%u", comp_idx, k, m->scan_progression_[i][k], scan_bitmask); @@ -275,7 +274,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { } } if (cinfo->Al > 10) { - return JPEGLI_ERROR("Scan parameter Al=%d is not supported.", cinfo->Al); + JPEGLI_ERROR("Scan parameter Al=%d is not supported.", cinfo->Al); } } @@ -285,7 +284,7 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { void ProcessDHT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { size_t pos = 2; if (pos == len) { - return JPEGLI_ERROR("DHT marker: no Huffman table found"); + JPEGLI_ERROR("DHT marker: no Huffman table found"); } while (pos < len) { JPEG_VERIFY_LEN(1 + kJpegHuffmanMaxBitLength); @@ -293,7 +292,7 @@ void ProcessDHT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { // component Huffman codes, 0x10 is added to the index. int slot_id = ReadUint8(data, &pos); int huffman_index = slot_id; - int is_ac_table = (slot_id & 0x10) != 0; + bool is_ac_table = ((slot_id & 0x10) != 0); JHUFF_TBL** table; if (is_ac_table) { huffman_index -= 0x10; @@ -343,7 +342,7 @@ void ProcessDQT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { } size_t pos = 2; if (pos == len) { - return JPEGLI_ERROR("DQT marker: no quantization table found"); + JPEGLI_ERROR("DQT marker: no quantization table found"); } while (pos < len) { JPEG_VERIFY_LEN(1); @@ -377,7 +376,7 @@ void ProcessDNL(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { void ProcessDRI(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { jpeg_decomp_master* m = cinfo->master; if (m->found_dri_) { - return JPEGLI_ERROR("Duplicate DRI marker."); + JPEGLI_ERROR("Duplicate DRI marker."); } m->found_dri_ = true; size_t pos = 2; @@ -411,24 +410,24 @@ void ProcessAPP(j_decompress_ptr cinfo, const uint8_t* data, size_t len) { payload += sizeof(kIccProfileTag); payload_size -= sizeof(kIccProfileTag); if (payload_size < 2) { - return JPEGLI_ERROR("ICC chunk is too small."); + JPEGLI_ERROR("ICC chunk is too small."); } uint8_t index = payload[0]; uint8_t total = payload[1]; ++m->icc_index_; if (m->icc_index_ != index) { - return JPEGLI_ERROR("Invalid ICC chunk order."); + JPEGLI_ERROR("Invalid ICC chunk order."); } if (total == 0) { - return JPEGLI_ERROR("Invalid ICC chunk total."); + JPEGLI_ERROR("Invalid ICC chunk total."); } if (m->icc_total_ == 0) { m->icc_total_ = total; } else if (m->icc_total_ != total) { - return JPEGLI_ERROR("Invalid ICC chunk total."); + JPEGLI_ERROR("Invalid ICC chunk total."); } if (m->icc_index_ > m->icc_total_) { - return JPEGLI_ERROR("Invalid ICC chunk index."); + JPEGLI_ERROR("Invalid ICC chunk index."); } m->icc_profile_.insert(m->icc_profile_.end(), payload + 2, payload + payload_size); @@ -494,8 +493,8 @@ uint8_t ProcessNextMarker(j_decompress_ptr cinfo, const uint8_t* const data, marker = data[*pos + 1]; if (num_skipped > 0) { if (m->found_soi_) { - JPEGLI_WARN("Skipped %d bytes before marker 0x%02x", (int)num_skipped, - marker); + JPEGLI_WARN("Skipped %d bytes before marker 0x%02x", + static_cast<int>(num_skipped), marker); } else { JPEGLI_ERROR("Did not find SOI marker."); } diff --git a/third_party/jpeg-xl/lib/jpegli/decode_marker.h b/third_party/jpeg-xl/lib/jpegli/decode_marker.h index fb24b3ee87..f3d47f6ad2 100644 --- a/third_party/jpeg-xl/lib/jpegli/decode_marker.h +++ b/third_party/jpeg-xl/lib/jpegli/decode_marker.h @@ -22,8 +22,8 @@ namespace jpegli { // EOI marker. Input buffer refill is handled by the caller; // * JPEG_REACHED_SOS, if the next SOS marker is found; // * JPEG_REACHED_EOR, if the end of the input is found. -int ProcessMarkers(j_decompress_ptr cinfo, const uint8_t* const data, - const size_t len, size_t* pos); +int ProcessMarkers(j_decompress_ptr cinfo, const uint8_t* data, size_t len, + size_t* pos); jpeg_marker_parser_method GetMarkerProcessor(j_decompress_ptr cinfo); diff --git a/third_party/jpeg-xl/lib/jpegli/decode_scan.cc b/third_party/jpeg-xl/lib/jpegli/decode_scan.cc index 05b1f37220..1b50792f0a 100644 --- a/third_party/jpeg-xl/lib/jpegli/decode_scan.cc +++ b/third_party/jpeg-xl/lib/jpegli/decode_scan.cc @@ -61,7 +61,7 @@ struct BitReaderState { if (bits_left_ <= 16) { while (bits_left_ <= 56) { val_ <<= 8; - val_ |= (uint64_t)GetNextByte(); + val_ |= static_cast<uint64_t>(GetNextByte()); bits_left_ += 8; } } @@ -427,7 +427,7 @@ void PrepareForiMCURow(j_decompress_ptr cinfo) { int offset = m->streaming_mode_ ? 0 : by0; m->coeff_rows[c] = (*cinfo->mem->access_virt_barray)( reinterpret_cast<j_common_ptr>(cinfo), m->coef_arrays[c], offset, - max_block_rows, true); + max_block_rows, TRUE); } } @@ -451,7 +451,8 @@ int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data, ++num_skipped; } if (num_skipped > 0) { - JPEGLI_WARN("Skipped %d bytes before restart marker", (int)num_skipped); + JPEGLI_WARN("Skipped %d bytes before restart marker", + static_cast<int>(num_skipped)); } if (*pos + 2 > len) { return kNeedMoreInput; @@ -471,7 +472,7 @@ int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data, } // Decode one MCU. - HWY_ALIGN_MAX coeff_t sink_block[DCTSIZE2]; + HWY_ALIGN_MAX static coeff_t sink_block[DCTSIZE2] = {0}; bool scan_ok = true; for (int i = 0; i < cinfo->comps_in_scan; ++i) { const jpeg_component_info* comp = cinfo->cur_comp_info[i]; diff --git a/third_party/jpeg-xl/lib/jpegli/decode_scan.h b/third_party/jpeg-xl/lib/jpegli/decode_scan.h index 1d7b18fc1a..dd1bfcd110 100644 --- a/third_party/jpeg-xl/lib/jpegli/decode_scan.h +++ b/third_party/jpeg-xl/lib/jpegli/decode_scan.h @@ -21,8 +21,8 @@ namespace jpegli { // * JPEG_SUSPENDED, if the input buffer ends before the end of an iMCU row; // * JPEG_ROW_COMPLETED, if the next iMCU row (but not the scan) is reached; // * JPEG_SCAN_COMPLETED, if the end of the scan is reached. -int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data, - const size_t len, size_t* pos, size_t* bit_pos); +int ProcessScan(j_decompress_ptr cinfo, const uint8_t* data, size_t len, + size_t* pos, size_t* bit_pos); void PrepareForiMCURow(j_decompress_ptr cinfo); diff --git a/third_party/jpeg-xl/lib/jpegli/destination_manager.cc b/third_party/jpeg-xl/lib/jpegli/destination_manager.cc index 9bc269f0c9..6548130866 100644 --- a/third_party/jpeg-xl/lib/jpegli/destination_manager.cc +++ b/third_party/jpeg-xl/lib/jpegli/destination_manager.cc @@ -19,13 +19,13 @@ struct StdioDestinationManager { uint8_t* buffer; static void init_destination(j_compress_ptr cinfo) { - auto dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest); + auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest); dest->pub.next_output_byte = dest->buffer; dest->pub.free_in_buffer = kDestBufferSize; } static boolean empty_output_buffer(j_compress_ptr cinfo) { - auto dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest); + auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest); if (fwrite(dest->buffer, 1, kDestBufferSize, dest->f) != kDestBufferSize) { JPEGLI_ERROR("Failed to write to output stream."); } @@ -35,7 +35,7 @@ struct StdioDestinationManager { } static void term_destination(j_compress_ptr cinfo) { - auto dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest); + auto* dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest); size_t bytes_left = kDestBufferSize - dest->pub.free_in_buffer; if (bytes_left && fwrite(dest->buffer, 1, bytes_left, dest->f) != bytes_left) { @@ -62,7 +62,7 @@ struct MemoryDestinationManager { static void init_destination(j_compress_ptr cinfo) {} static boolean empty_output_buffer(j_compress_ptr cinfo) { - auto dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest); + auto* dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest); uint8_t* next_buffer = reinterpret_cast<uint8_t*>(malloc(dest->buffer_size * 2)); memcpy(next_buffer, dest->current_buffer, dest->buffer_size); @@ -80,7 +80,7 @@ struct MemoryDestinationManager { } static void term_destination(j_compress_ptr cinfo) { - auto dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest); + auto* dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest); *dest->output_size = dest->buffer_size - dest->pub.free_in_buffer; } }; @@ -99,7 +99,7 @@ void jpegli_stdio_dest(j_compress_ptr cinfo, FILE* outfile) { cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>( jpegli::Allocate<jpegli::StdioDestinationManager>(cinfo, 1)); } - auto dest = reinterpret_cast<jpegli::StdioDestinationManager*>(cinfo->dest); + auto* dest = reinterpret_cast<jpegli::StdioDestinationManager*>(cinfo->dest); dest->f = outfile; dest->buffer = jpegli::Allocate<uint8_t>(cinfo, jpegli::kDestBufferSize); dest->pub.next_output_byte = dest->buffer; @@ -122,11 +122,11 @@ void jpegli_mem_dest(j_compress_ptr cinfo, unsigned char** outbuffer, JPEGLI_ERROR("jpegli_mem_dest: a different dest manager was already set"); } if (!cinfo->dest) { - auto dest = jpegli::Allocate<jpegli::MemoryDestinationManager>(cinfo, 1); + auto* dest = jpegli::Allocate<jpegli::MemoryDestinationManager>(cinfo, 1); dest->temp_buffer = nullptr; cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(dest); } - auto dest = reinterpret_cast<jpegli::MemoryDestinationManager*>(cinfo->dest); + auto* dest = reinterpret_cast<jpegli::MemoryDestinationManager*>(cinfo->dest); dest->pub.init_destination = jpegli::MemoryDestinationManager::init_destination; dest->pub.empty_output_buffer = diff --git a/third_party/jpeg-xl/lib/jpegli/downsample.cc b/third_party/jpeg-xl/lib/jpegli/downsample.cc index df2c156972..f1e945d509 100644 --- a/third_party/jpeg-xl/lib/jpegli/downsample.cc +++ b/third_party/jpeg-xl/lib/jpegli/downsample.cc @@ -29,7 +29,7 @@ void DownsampleRow2x1(const float* row_in, size_t len, float* row_out) { const size_t N = Lanes(d); const size_t len_out = len / 2; const auto mul = Set(d, 0.5f); - Vec<D> v0, v1; + Vec<D> v0, v1; // NOLINT for (size_t x = 0; x < len_out; x += N) { LoadInterleaved2(d, row_in + 2 * x, v0, v1); Store(Mul(mul, Add(v0, v1)), d, row_out + x); @@ -40,7 +40,7 @@ void DownsampleRow3x1(const float* row_in, size_t len, float* row_out) { const size_t N = Lanes(d); const size_t len_out = len / 3; const auto mul = Set(d, 1.0f / 3); - Vec<D> v0, v1, v2; + Vec<D> v0, v1, v2; // NOLINT for (size_t x = 0; x < len_out; x += N) { LoadInterleaved3(d, row_in + 3 * x, v0, v1, v2); Store(Mul(mul, Add(Add(v0, v1), v2)), d, row_out + x); @@ -51,7 +51,7 @@ void DownsampleRow4x1(const float* row_in, size_t len, float* row_out) { const size_t N = Lanes(d); const size_t len_out = len / 4; const auto mul = Set(d, 0.25f); - Vec<D> v0, v1, v2, v3; + Vec<D> v0, v1, v2, v3; // NOLINT for (size_t x = 0; x < len_out; x += N) { LoadInterleaved4(d, row_in + 4 * x, v0, v1, v2, v3); Store(Mul(mul, Add(Add(v0, v1), Add(v2, v3))), d, row_out + x); @@ -91,7 +91,7 @@ void Downsample2x2(float* rows_in[MAX_SAMP_FACTOR], size_t len, const auto mul = Set(d, 0.25f); float* row0 = rows_in[0]; float* row1 = rows_in[1]; - Vec<D> v0, v1, v2, v3; + Vec<D> v0, v1, v2, v3; // NOLINT for (size_t x = 0; x < len_out; x += N) { LoadInterleaved2(d, row0 + 2 * x, v0, v1); LoadInterleaved2(d, row1 + 2 * x, v2, v3); diff --git a/third_party/jpeg-xl/lib/jpegli/encode.cc b/third_party/jpeg-xl/lib/jpegli/encode.cc index 8a106e239a..5326f2cb0f 100644 --- a/third_party/jpeg-xl/lib/jpegli/encode.cc +++ b/third_party/jpeg-xl/lib/jpegli/encode.cc @@ -5,6 +5,8 @@ #include "lib/jpegli/encode.h" +#include <jxl/types.h> + #include <cmath> #include <initializer_list> #include <vector> @@ -323,8 +325,8 @@ void ProcessCompressionParams(j_compress_ptr cinfo) { if (cinfo->scan_info == nullptr) { SetDefaultScanScript(cinfo); } - cinfo->progressive_mode = - cinfo->scan_info->Ss != 0 || cinfo->scan_info->Se != DCTSIZE2 - 1; + cinfo->progressive_mode = TO_JXL_BOOL(cinfo->scan_info->Ss != 0 || + cinfo->scan_info->Se != DCTSIZE2 - 1); ValidateScanScript(cinfo); m->scan_token_info = Allocate<ScanTokenInfo>(cinfo, cinfo->num_scans, JPOOL_IMAGE); @@ -449,7 +451,7 @@ void AllocateBuffers(j_compress_ptr cinfo) { const size_t ysize_blocks = comp->height_in_blocks; m->coeff_buffers[c] = (*cinfo->mem->request_virt_barray)( reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, - /*pre_zero=*/false, xsize_blocks, ysize_blocks, comp->v_samp_factor); + /*pre_zero=*/FALSE, xsize_blocks, ysize_blocks, comp->v_samp_factor); } } if (m->use_adaptive_quantization) { @@ -663,8 +665,8 @@ void jpegli_CreateCompress(j_compress_ptr cinfo, int version, cinfo->num_components = 0; cinfo->jpeg_color_space = JCS_UNKNOWN; cinfo->comp_info = nullptr; - for (int i = 0; i < NUM_QUANT_TBLS; ++i) { - cinfo->quant_tbl_ptrs[i] = nullptr; + for (auto& quant_tbl_ptr : cinfo->quant_tbl_ptrs) { + quant_tbl_ptr = nullptr; } for (int i = 0; i < NUM_HUFF_TBLS; ++i) { cinfo->dc_huff_tbl_ptrs[i] = nullptr; @@ -673,7 +675,7 @@ void jpegli_CreateCompress(j_compress_ptr cinfo, int version, memset(cinfo->arith_dc_L, 0, sizeof(cinfo->arith_dc_L)); memset(cinfo->arith_dc_U, 0, sizeof(cinfo->arith_dc_U)); memset(cinfo->arith_ac_K, 0, sizeof(cinfo->arith_ac_K)); - cinfo->write_Adobe_marker = false; + cinfo->write_Adobe_marker = FALSE; cinfo->master = jpegli::Allocate<jpeg_comp_master>(cinfo, 1); jpegli::InitializeCompressParams(cinfo); cinfo->master->force_baseline = true; @@ -763,7 +765,7 @@ void jpegli_set_colorspace(j_compress_ptr cinfo, J_COLOR_SPACE colorspace) { JPEGLI_ERROR("Unsupported jpeg colorspace %d", colorspace); } // Adobe marker is only needed to distinguish CMYK and YCCK JPEGs. - cinfo->write_Adobe_marker = (cinfo->jpeg_color_space == JCS_YCCK); + cinfo->write_Adobe_marker = TO_JXL_BOOL(cinfo->jpeg_color_space == JCS_YCCK); if (cinfo->comp_info == nullptr) { cinfo->comp_info = jpegli::Allocate<jpeg_component_info>(cinfo, MAX_COMPONENTS); @@ -810,7 +812,7 @@ void jpegli_set_colorspace(j_compress_ptr cinfo, J_COLOR_SPACE colorspace) { void jpegli_set_distance(j_compress_ptr cinfo, float distance, boolean force_baseline) { CheckState(cinfo, jpegli::kEncStart); - cinfo->master->force_baseline = force_baseline; + cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline); float distances[NUM_QUANT_TBLS] = {distance, distance, distance}; jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/true); } @@ -834,7 +836,7 @@ void jpegli_set_psnr(j_compress_ptr cinfo, float psnr, float tolerance, void jpegli_set_quality(j_compress_ptr cinfo, int quality, boolean force_baseline) { CheckState(cinfo, jpegli::kEncStart); - cinfo->master->force_baseline = force_baseline; + cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline); float distance = jpegli_quality_to_distance(quality); float distances[NUM_QUANT_TBLS] = {distance, distance, distance}; jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false); @@ -843,7 +845,7 @@ void jpegli_set_quality(j_compress_ptr cinfo, int quality, void jpegli_set_linear_quality(j_compress_ptr cinfo, int scale_factor, boolean force_baseline) { CheckState(cinfo, jpegli::kEncStart); - cinfo->master->force_baseline = force_baseline; + cinfo->master->force_baseline = FROM_JXL_BOOL(force_baseline); float distance = jpegli::LinearQualityToDistance(scale_factor); float distances[NUM_QUANT_TBLS] = {distance, distance, distance}; jpegli::SetQuantMatrices(cinfo, distances, /*add_two_chroma_tables=*/false); @@ -894,7 +896,7 @@ void jpegli_add_quant_table(j_compress_ptr cinfo, int which_tbl, void jpegli_enable_adaptive_quantization(j_compress_ptr cinfo, boolean value) { CheckState(cinfo, jpegli::kEncStart); - cinfo->master->use_adaptive_quantization = value; + cinfo->master->use_adaptive_quantization = FROM_JXL_BOOL(value); } void jpegli_simple_progression(j_compress_ptr cinfo) { @@ -955,7 +957,7 @@ void jpegli_copy_critical_parameters(j_decompress_ptr srcinfo, jpegli_set_colorspace(dstinfo, srcinfo->jpeg_color_space); if (dstinfo->num_components != srcinfo->num_components) { const auto& cinfo = dstinfo; - return JPEGLI_ERROR("Mismatch between src colorspace and components"); + JPEGLI_ERROR("Mismatch between src colorspace and components"); } dstinfo->data_precision = srcinfo->data_precision; dstinfo->CCIR601_sampling = srcinfo->CCIR601_sampling; @@ -1005,7 +1007,7 @@ void jpegli_write_coefficients(j_compress_ptr cinfo, jvirt_barray_ptr* coef_arrays) { CheckState(cinfo, jpegli::kEncStart); cinfo->global_state = jpegli::kEncWriteCoeffs; - jpegli::InitCompress(cinfo, /*write_all_tables=*/true); + jpegli::InitCompress(cinfo, /*write_all_tables=*/TRUE); cinfo->master->coeff_buffers = coef_arrays; cinfo->next_scanline = cinfo->image_height; cinfo->master->next_input_row = cinfo->image_height; @@ -1047,7 +1049,7 @@ void jpegli_write_m_header(j_compress_ptr cinfo, int marker, marker_data[1] = marker; marker_data[2] = (datalen + 2) >> 8; marker_data[3] = (datalen + 2) & 0xff; - jpegli::WriteOutput(cinfo, &marker_data[0], 4); + jpegli::WriteOutput(cinfo, marker_data.data(), 4); } void jpegli_write_m_byte(j_compress_ptr cinfo, int val) { @@ -1213,7 +1215,8 @@ void jpegli_finish_compress(j_compress_ptr cinfo) { } const bool tokens_done = jpegli::IsStreamingSupported(cinfo); - const bool bitstream_done = tokens_done && !cinfo->optimize_coding; + const bool bitstream_done = + tokens_done && !FROM_JXL_BOOL(cinfo->optimize_coding); if (!tokens_done) { jpegli::TokenizeJpeg(cinfo); diff --git a/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc b/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc index 8d53557567..1afdcf610d 100644 --- a/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc +++ b/third_party/jpeg-xl/lib/jpegli/encode_api_test.cc @@ -133,12 +133,11 @@ TEST(EncodeAPITest, ReuseCinfoSameMemOutput) { jpegli_destroy_compress(&cinfo); } size_t pos = 0; - for (size_t i = 0; i < all_configs.size(); ++i) { + for (auto& config : all_configs) { TestImage output; - pos += - DecodeWithLibjpeg(all_configs[i].jparams, DecompressParams(), nullptr, - 0, buffer + pos, buffer_size - pos, &output); - VerifyOutputImage(all_configs[i].input, output, all_configs[i].max_dist); + pos += DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0, + buffer + pos, buffer_size - pos, &output); + VerifyOutputImage(config.input, output, config.max_dist); } if (buffer) free(buffer); } @@ -164,20 +163,21 @@ TEST(EncodeAPITest, ReuseCinfoSameStdOutput) { size_t total_size = ftell(tmpf); rewind(tmpf); std::vector<uint8_t> compressed(total_size); - JXL_CHECK(total_size == fread(&compressed[0], 1, total_size, tmpf)); + JXL_CHECK(total_size == fread(compressed.data(), 1, total_size, tmpf)); fclose(tmpf); size_t pos = 0; - for (size_t i = 0; i < all_configs.size(); ++i) { + for (auto& config : all_configs) { TestImage output; - pos += DecodeWithLibjpeg(all_configs[i].jparams, DecompressParams(), - nullptr, 0, &compressed[pos], - compressed.size() - pos, &output); - VerifyOutputImage(all_configs[i].input, output, all_configs[i].max_dist); + pos += + DecodeWithLibjpeg(config.jparams, DecompressParams(), nullptr, 0, + &compressed[pos], compressed.size() - pos, &output); + VerifyOutputImage(config.input, output, config.max_dist); } } TEST(EncodeAPITest, ReuseCinfoChangeParams) { - TestImage input, output; + TestImage input; + TestImage output; CompressParams jparams; DecompressParams dparams; uint8_t* buffer = nullptr; diff --git a/third_party/jpeg-xl/lib/jpegli/encode_finish.cc b/third_party/jpeg-xl/lib/jpegli/encode_finish.cc index 955676bdee..767a6532c5 100644 --- a/third_party/jpeg-xl/lib/jpegli/encode_finish.cc +++ b/third_party/jpeg-xl/lib/jpegli/encode_finish.cc @@ -213,6 +213,8 @@ float FindDistanceForPSNR(j_compress_ptr cinfo) { d = best_distance; if (sampling == 1 && PSNR_SEARCH_DBG) { printf("Final PSNR %.2f at distance %.4f\n", best_psnr, d); + } else { + (void)best_psnr; } } return d; diff --git a/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc b/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc index 7e50bbc3a7..515996a43d 100644 --- a/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc +++ b/third_party/jpeg-xl/lib/jpegli/entropy_coding.cc @@ -99,10 +99,16 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index, TokenArray* ta = &m->token_arrays[m->cur_token_array]; sti->token_offset = m->total_num_tokens + ta->num_tokens; sti->restarts = Allocate<size_t>(cinfo, num_restarts, JPOOL_IMAGE); + const auto emit_eob_run = [&]() { + int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run); + int symbol = nbits << 4u; + *m->next_token++ = Token(context, symbol, eob_run & ((1 << nbits) - 1)); + eob_run = 0; + }; for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) { JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)( reinterpret_cast<j_common_ptr>(cinfo), m->coeff_buffers[comp_idx], by, - 1, false); + 1, FALSE); // Each coefficient can appear in at most one token, but we have to reserve // one extra EOBrun token that was rolled over from the previous block-row // and has to be flushed at the end. @@ -121,13 +127,7 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index, } for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) { if (restart_interval > 0 && restarts_to_go == 0) { - if (eob_run > 0) { - int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run); - int symbol = nbits << 4u; - *m->next_token++ = - Token(context, symbol, eob_run & ((1 << nbits) - 1)); - eob_run = 0; - } + if (eob_run > 0) emit_eob_run(); ta->num_tokens = m->next_token - ta->tokens; sti->restarts[restart_idx++] = m->total_num_tokens + ta->num_tokens; restarts_to_go = restart_interval; @@ -139,7 +139,8 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index, int num_nzeros = 0; int num_future_nzeros = 0; for (int k = Ss; k <= Se; ++k) { - if ((temp = block[k]) == 0) { + temp = block[k]; + if (temp == 0) { r++; continue; } @@ -156,13 +157,7 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index, num_future_nzeros++; continue; } - if (eob_run > 0) { - int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run); - int symbol = nbits << 4u; - *m->next_token++ = - Token(context, symbol, eob_run & ((1 << nbits) - 1)); - eob_run = 0; - } + if (eob_run > 0) emit_eob_run(); while (r > 15) { *m->next_token++ = Token(context, 0xf0, 0); r -= 16; @@ -175,13 +170,7 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index, } if (r > 0) { ++eob_run; - if (eob_run == 0x7FFF) { - int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run); - int symbol = nbits << 4u; - *m->next_token++ = - Token(context, symbol, eob_run & ((1 << nbits) - 1)); - eob_run = 0; - } + if (eob_run == 0x7FFF) emit_eob_run(); } sti->num_nonzeros += num_nzeros; sti->num_future_nonzeros += num_future_nzeros; @@ -190,11 +179,8 @@ void TokenizeACProgressiveScan(j_compress_ptr cinfo, int scan_index, ta->num_tokens = m->next_token - ta->tokens; } if (eob_run > 0) { - int nbits = jxl::FloorLog2Nonzero<uint32_t>(eob_run); - int symbol = nbits << 4u; - *m->next_token++ = Token(context, symbol, eob_run & ((1 << nbits) - 1)); + emit_eob_run(); ++ta->num_tokens; - eob_run = 0; } sti->num_tokens = m->total_num_tokens + ta->num_tokens - sti->token_offset; sti->restarts[restart_idx++] = m->total_num_tokens + ta->num_tokens; @@ -229,7 +215,7 @@ void TokenizeACRefinementScan(j_compress_ptr cinfo, int scan_index, for (JDIMENSION by = 0; by < comp->height_in_blocks; ++by) { JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)( reinterpret_cast<j_common_ptr>(cinfo), m->coeff_buffers[comp_idx], by, - 1, false); + 1, FALSE); for (JDIMENSION bx = 0; bx < comp->width_in_blocks; ++bx) { if (restart_interval > 0 && restarts_to_go == 0) { sti->restarts[restart_idx++] = next_token - sti->tokens; @@ -337,7 +323,7 @@ void TokenizeScan(j_compress_ptr cinfo, size_t scan_index, int ac_ctx_offset, // "Non-interleaved" means color data comes in separate scans, in other words // each scan can contain only one color component. const bool is_interleaved = (scan_info->comps_in_scan > 1); - const bool is_progressive = cinfo->progressive_mode; + const bool is_progressive = FROM_JXL_BOOL(cinfo->progressive_mode); const int Ah = scan_info->Ah; const int Al = scan_info->Al; HWY_ALIGN constexpr coeff_t kSinkBlock[DCTSIZE2] = {0}; @@ -373,7 +359,7 @@ void TokenizeScan(j_compress_ptr cinfo, size_t scan_index, int ac_ctx_offset, int max_block_rows = std::min(n_blocks_y, block_rows_left); ba[i] = (*cinfo->mem->access_virt_barray)( reinterpret_cast<j_common_ptr>(cinfo), m->coeff_buffers[comp_idx], - by0, max_block_rows, false); + by0, max_block_rows, FALSE); } if (!cinfo->progressive_mode) { int max_tokens_per_mcu_row = MaxNumTokensPerMCURow(cinfo); @@ -557,7 +543,7 @@ float HistogramCost(const Histogram& histo) { } counts[kJpegHuffmanAlphabetSize] = 1; CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength, - &depths[0]); + depths.data()); size_t header_bits = (1 + kJpegHuffmanMaxBitLength) * 8; size_t data_bits = 0; for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) { @@ -576,8 +562,8 @@ void AddHistograms(const Histogram& a, const Histogram& b, Histogram* c) { } bool IsEmptyHistogram(const Histogram& histo) { - for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) { - if (histo.count[i]) return false; + for (int count : histo.count) { + if (count) return false; } return true; } @@ -668,7 +654,7 @@ void BuildJpegHuffmanTable(const Histogram& histo, JHUFF_TBL* table) { } counts[kJpegHuffmanAlphabetSize] = 1; CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength, - &depths[0]); + depths.data()); memset(table, 0, sizeof(JHUFF_TBL)); for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) { if (depths[i] > 0) { @@ -726,7 +712,7 @@ void OptimizeHuffmanCodes(j_compress_ptr cinfo) { jpeg_comp_master* m = cinfo->master; // Build DC and AC histograms. std::vector<Histogram> histograms(m->num_contexts); - BuildHistograms(cinfo, &histograms[0]); + BuildHistograms(cinfo, histograms.data()); // Cluster DC histograms. JpegClusteredHistograms dc_clusters; @@ -760,7 +746,7 @@ void OptimizeHuffmanCodes(j_compress_ptr cinfo) { m->context_map = Allocate<uint8_t>(cinfo, m->num_contexts, JPOOL_IMAGE); memset(m->context_map, 0, m->num_contexts); for (size_t i = 0; i < m->num_contexts; ++i) { - if (i < (size_t)cinfo->num_components) { + if (i < static_cast<size_t>(cinfo->num_components)) { m->context_map[i] = dc_clusters.histogram_indexes[i]; } else if (i >= 4) { m->context_map[i] = num_dc_huff + ac_clusters.histogram_indexes[i - 4]; diff --git a/third_party/jpeg-xl/lib/jpegli/error.h b/third_party/jpeg-xl/lib/jpegli/error.h index 4451abd416..5f266baee1 100644 --- a/third_party/jpeg-xl/lib/jpegli/error.h +++ b/third_party/jpeg-xl/lib/jpegli/error.h @@ -10,6 +10,7 @@ #include <stdint.h> #include "lib/jpegli/common.h" +#include "lib/jxl/base/status.h" namespace jpegli { @@ -17,10 +18,12 @@ bool FormatString(char* buffer, const char* format, ...); } // namespace jpegli +// `error_exit` should be no-return; but let's add some guarantees on our side. #define JPEGLI_ERROR(format, ...) \ jpegli::FormatString(cinfo->err->msg_parm.s, ("%s:%d: " format), __FILE__, \ __LINE__, ##__VA_ARGS__), \ - (*cinfo->err->error_exit)(reinterpret_cast<j_common_ptr>(cinfo)) + (*cinfo->err->error_exit)(reinterpret_cast<j_common_ptr>(cinfo)), \ + (void)jxl::Abort() #define JPEGLI_WARN(format, ...) \ jpegli::FormatString(cinfo->err->msg_parm.s, ("%s:%d: " format), __FILE__, \ diff --git a/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc b/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc index 0d481c572a..3eaf6a313b 100644 --- a/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc +++ b/third_party/jpeg-xl/lib/jpegli/error_handling_test.cc @@ -241,9 +241,10 @@ TEST(EncoderErrorHandlingTest, InvalidQuantValue) { cinfo.image_height = 1; cinfo.input_components = 1; jpegli_set_defaults(&cinfo); - cinfo.quant_tbl_ptrs[0] = jpegli_alloc_quant_table((j_common_ptr)&cinfo); - for (size_t k = 0; k < DCTSIZE2; ++k) { - cinfo.quant_tbl_ptrs[0]->quantval[k] = 0; + cinfo.quant_tbl_ptrs[0] = + jpegli_alloc_quant_table(reinterpret_cast<j_common_ptr>(&cinfo)); + for (UINT16& q : cinfo.quant_tbl_ptrs[0]->quantval) { + q = 0; } jpegli_start_compress(&cinfo, TRUE); JSAMPLE image[1] = {0}; @@ -992,7 +993,7 @@ TEST(EncoderErrorHandlingTest, AddOnTableNoStringParam) { jpegli_destroy_compress(&cinfo); } -static const uint8_t kCompressed0[] = { +const uint8_t kCompressed0[] = { // SOI 0xff, 0xd8, // // DQT @@ -1036,12 +1037,12 @@ static const uint8_t kCompressed0[] = { // EOI 0xff, 0xd9, // }; -static const size_t kLen0 = sizeof(kCompressed0); +const size_t kLen0 = sizeof(kCompressed0); -static const size_t kDQTOffset = 2; -static const size_t kSOFOffset = 71; -static const size_t kDHTOffset = 84; -static const size_t kSOSOffset = 296; +const size_t kDQTOffset = 2; +const size_t kSOFOffset = 71; +const size_t kDHTOffset = 84; +const size_t kSOSOffset = 296; TEST(DecoderErrorHandlingTest, MinimalSuccess) { JXL_CHECK(kCompressed0[kDQTOffset] == 0xff); @@ -1130,7 +1131,7 @@ TEST(DecoderErrorHandlingTest, NoReadScanlines) { jpegli_destroy_decompress(&cinfo); } -static const size_t kMaxImageWidth = 0xffff; +const size_t kMaxImageWidth = 0xffff; JSAMPLE kOutputBuffer[MAX_COMPONENTS * kMaxImageWidth]; bool ParseCompressed(const std::vector<uint8_t>& compressed) { diff --git a/third_party/jpeg-xl/lib/jpegli/huffman.cc b/third_party/jpeg-xl/lib/jpegli/huffman.cc index 1cf88a5536..5391030213 100644 --- a/third_party/jpeg-xl/lib/jpegli/huffman.cc +++ b/third_party/jpeg-xl/lib/jpegli/huffman.cc @@ -183,7 +183,8 @@ void CreateHuffmanTree(const uint32_t* data, const size_t length, size_t i = 0; // Points to the next leaf node. size_t j = n + 1; // Points to the next non-leaf node. for (size_t k = n - 1; k != 0; --k) { - size_t left, right; + size_t left; + size_t right; if (tree[i].total_count <= tree[j].total_count) { left = i; ++i; @@ -210,7 +211,7 @@ void CreateHuffmanTree(const uint32_t* data, const size_t length, tree.push_back(sentinel); } JXL_DASSERT(tree.size() == 2 * n + 1); - SetDepth(tree[2 * n - 1], &tree[0], depth, 0); + SetDepth(tree[2 * n - 1], tree.data(), depth, 0); // We need to pack the Huffman tree in tree_limit bits. // If this was not successful, add fake entities to the lowest values diff --git a/third_party/jpeg-xl/lib/jpegli/idct.cc b/third_party/jpeg-xl/lib/jpegli/idct.cc index 4d10563583..9859e8ef85 100644 --- a/third_party/jpeg-xl/lib/jpegli/idct.cc +++ b/third_party/jpeg-xl/lib/jpegli/idct.cc @@ -197,7 +197,7 @@ void InverseTransformBlock8x8(const int16_t* JXL_RESTRICT qblock, // Computes the N-point IDCT of in[], and stores the result in out[]. The in[] // array is at most 8 values long, values in[8:N-1] are assumed to be 0. -void Compute1dIDCT(float* in, float* out, size_t N) { +void Compute1dIDCT(const float* in, float* out, size_t N) { switch (N) { case 3: { static constexpr float kC3[3] = { @@ -608,6 +608,9 @@ void Compute1dIDCT(float* in, float* out, size_t N) { out[8] = even7 - odd7; break; } + default: + JXL_ABORT("Compute1dIDCT does not support N=%d", static_cast<int>(N)); + break; } } diff --git a/third_party/jpeg-xl/lib/jpegli/input.cc b/third_party/jpeg-xl/lib/jpegli/input.cc index 765bf98946..16299477f7 100644 --- a/third_party/jpeg-xl/lib/jpegli/input.cc +++ b/third_party/jpeg-xl/lib/jpegli/input.cc @@ -89,7 +89,7 @@ void ReadUint8RowInterleaved2(const uint8_t* row_in, size_t len, const size_t simd_len = len & (~(N - 1)); float* JXL_RESTRICT const row0 = row_out[0]; float* JXL_RESTRICT const row1 = row_out[1]; - Vec<DU8> out0, out1; + Vec<DU8> out0, out1; // NOLINT for (size_t x = 0; x < simd_len; x += N) { LoadInterleaved2(du8, row_in + 2 * x, out0, out1); Store(ConvertTo(d, PromoteTo(du, out0)), d, row0 + x); @@ -105,7 +105,7 @@ void ReadUint8RowInterleaved3(const uint8_t* row_in, size_t len, float* JXL_RESTRICT const row0 = row_out[0]; float* JXL_RESTRICT const row1 = row_out[1]; float* JXL_RESTRICT const row2 = row_out[2]; - Vec<DU8> out0, out1, out2; + Vec<DU8> out0, out1, out2; // NOLINT for (size_t x = 0; x < simd_len; x += N) { LoadInterleaved3(du8, row_in + 3 * x, out0, out1, out2); Store(ConvertTo(d, PromoteTo(du, out0)), d, row0 + x); @@ -123,7 +123,7 @@ void ReadUint8RowInterleaved4(const uint8_t* row_in, size_t len, float* JXL_RESTRICT const row1 = row_out[1]; float* JXL_RESTRICT const row2 = row_out[2]; float* JXL_RESTRICT const row3 = row_out[3]; - Vec<DU8> out0, out1, out2, out3; + Vec<DU8> out0, out1, out2, out3; // NOLINT for (size_t x = 0; x < simd_len; x += N) { LoadInterleaved4(du8, row_in + 4 * x, out0, out1, out2, out3); Store(ConvertTo(d, PromoteTo(du, out0)), d, row0 + x); @@ -158,7 +158,7 @@ void ReadUint16RowInterleaved2(const uint8_t* row_in, size_t len, reinterpret_cast<const uint16_t*>(row_in); float* JXL_RESTRICT const row0 = row_out[0]; float* JXL_RESTRICT const row1 = row_out[1]; - Vec<DU16> out0, out1; + Vec<DU16> out0, out1; // NOLINT for (size_t x = 0; x < simd_len; x += N) { LoadInterleaved2(du16, row + 2 * x, out0, out1); Store(Mul(mul, ConvertTo(d, PromoteTo(du, out0))), d, row0 + x); @@ -177,7 +177,7 @@ void ReadUint16RowInterleaved3(const uint8_t* row_in, size_t len, float* JXL_RESTRICT const row0 = row_out[0]; float* JXL_RESTRICT const row1 = row_out[1]; float* JXL_RESTRICT const row2 = row_out[2]; - Vec<DU16> out0, out1, out2; + Vec<DU16> out0, out1, out2; // NOLINT for (size_t x = 0; x < simd_len; x += N) { LoadInterleaved3(du16, row + 3 * x, out0, out1, out2); Store(Mul(mul, ConvertTo(d, PromoteTo(du, out0))), d, row0 + x); @@ -198,7 +198,7 @@ void ReadUint16RowInterleaved4(const uint8_t* row_in, size_t len, float* JXL_RESTRICT const row1 = row_out[1]; float* JXL_RESTRICT const row2 = row_out[2]; float* JXL_RESTRICT const row3 = row_out[3]; - Vec<DU16> out0, out1, out2, out3; + Vec<DU16> out0, out1, out2, out3; // NOLINT for (size_t x = 0; x < simd_len; x += N) { LoadInterleaved4(du16, row + 4 * x, out0, out1, out2, out3); Store(Mul(mul, ConvertTo(d, PromoteTo(du, out0))), d, row0 + x); @@ -250,7 +250,7 @@ void ReadFloatRowInterleaved2(const uint8_t* row_in, size_t len, const float* JXL_RESTRICT const row = reinterpret_cast<const float*>(row_in); float* JXL_RESTRICT const row0 = row_out[0]; float* JXL_RESTRICT const row1 = row_out[1]; - Vec<D> out0, out1; + Vec<D> out0, out1; // NOLINT for (size_t x = 0; x < simd_len; x += N) { LoadInterleaved2(d, row + 2 * x, out0, out1); Store(Mul(mul, out0), d, row0 + x); @@ -268,7 +268,7 @@ void ReadFloatRowInterleaved3(const uint8_t* row_in, size_t len, float* JXL_RESTRICT const row0 = row_out[0]; float* JXL_RESTRICT const row1 = row_out[1]; float* JXL_RESTRICT const row2 = row_out[2]; - Vec<D> out0, out1, out2; + Vec<D> out0, out1, out2; // NOLINT for (size_t x = 0; x < simd_len; x += N) { LoadInterleaved3(d, row + 3 * x, out0, out1, out2); Store(Mul(mul, out0), d, row0 + x); @@ -288,7 +288,7 @@ void ReadFloatRowInterleaved4(const uint8_t* row_in, size_t len, float* JXL_RESTRICT const row1 = row_out[1]; float* JXL_RESTRICT const row2 = row_out[2]; float* JXL_RESTRICT const row3 = row_out[3]; - Vec<D> out0, out1, out2, out3; + Vec<D> out0, out1, out2, out3; // NOLINT for (size_t x = 0; x < simd_len; x += N) { LoadInterleaved4(d, row + 4 * x, out0, out1, out2, out3); Store(Mul(mul, out0), d, row0 + x); diff --git a/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc b/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc index 09bafd9188..eb8b7ebc26 100644 --- a/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc +++ b/third_party/jpeg-xl/lib/jpegli/input_suspension_test.cc @@ -17,7 +17,7 @@ namespace jpegli { namespace { -static constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9}; +constexpr uint8_t kFakeEoiMarker[2] = {0xff, 0xd9}; struct SourceManager { SourceManager(const uint8_t* data, size_t len, size_t max_chunk_size, @@ -50,14 +50,14 @@ struct SourceManager { } if (pub_.bytes_in_buffer > 0) { EXPECT_LE(pub_.bytes_in_buffer, buffer_.size()); - memmove(&buffer_[0], pub_.next_input_byte, pub_.bytes_in_buffer); + memmove(buffer_.data(), pub_.next_input_byte, pub_.bytes_in_buffer); } size_t chunk_size = pos_ < len_ ? std::min(len_ - pos_, max_chunk_size_) : 2; buffer_.resize(pub_.bytes_in_buffer + chunk_size); memcpy(&buffer_[pub_.bytes_in_buffer], pos_ < len_ ? data_ + pos_ : kFakeEoiMarker, chunk_size); - pub_.next_input_byte = &buffer_[0]; + pub_.next_input_byte = buffer_.data(); pub_.bytes_in_buffer += chunk_size; pos_ += chunk_size; return true; @@ -73,7 +73,7 @@ struct SourceManager { bool is_partial_file_; static void init_source(j_decompress_ptr cinfo) { - auto src = reinterpret_cast<SourceManager*>(cinfo->src); + auto* src = reinterpret_cast<SourceManager*>(cinfo->src); src->pub_.next_input_byte = nullptr; src->pub_.bytes_in_buffer = 0; } @@ -81,7 +81,7 @@ struct SourceManager { static boolean fill_input_buffer(j_decompress_ptr cinfo) { return FALSE; } static void skip_input_data(j_decompress_ptr cinfo, long num_bytes) { - auto src = reinterpret_cast<SourceManager*>(cinfo->src); + auto* src = reinterpret_cast<SourceManager*>(cinfo->src); if (num_bytes <= 0) { return; } @@ -156,10 +156,10 @@ void ReadOutputImage(const DecompressParams& dparams, j_decompress_ptr cinfo, rowdata[c][i] = y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr; } - data[c] = &rowdata[c][0]; + data[c] = rowdata[c].data(); } while ((num_output_lines = - jpegli_read_raw_data(cinfo, &data[0], max_lines)) == 0) { + jpegli_read_raw_data(cinfo, data.data(), max_lines)) == 0) { JXL_CHECK(src && src->LoadNextChunk()); } } else { @@ -173,7 +173,7 @@ void ReadOutputImage(const DecompressParams& dparams, j_decompress_ptr cinfo, size_t yidx = cinfo->output_scanline + i; scanlines[i] = &output->pixels[yidx * stride]; } - while ((num_output_lines = jpegli_read_scanlines(cinfo, &scanlines[0], + while ((num_output_lines = jpegli_read_scanlines(cinfo, scanlines.data(), max_lines)) == 0) { JXL_CHECK(src && src->LoadNextChunk()); } @@ -197,7 +197,7 @@ struct TestConfig { std::vector<uint8_t> GetTestJpegData(TestConfig& config) { if (!config.fn.empty()) { - return ReadTestData(config.fn.c_str()); + return ReadTestData(config.fn); } GeneratePixels(&config.input); std::vector<uint8_t> compressed; @@ -249,7 +249,7 @@ TEST_P(InputSuspensionTestParam, InputOutputLockStepNonBuffered) { EXPECT_EQ(0, memcmp(markers_seen, kMarkerSequence, num_markers_seen)); } VerifyHeader(config.jparams, &cinfo); - cinfo.raw_data_out = dparams.output_mode == RAW_DATA; + cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA); if (dparams.output_mode == COEFFICIENTS) { jvirt_barray_ptr* coef_arrays; @@ -303,7 +303,7 @@ TEST_P(InputSuspensionTestParam, InputOutputLockStepBuffered) { jpegli_set_output_format(&cinfo, dparams.data_type, dparams.endianness); cinfo.buffered_image = TRUE; - cinfo.raw_data_out = dparams.output_mode == RAW_DATA; + cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA); EXPECT_TRUE(jpegli_start_decompress(&cinfo)); EXPECT_FALSE(jpegli_input_complete(&cinfo)); @@ -380,8 +380,8 @@ TEST_P(InputSuspensionTestParam, PreConsumeInputBuffered) { } EXPECT_EQ(JPEG_REACHED_SOS, jpegli_consume_input(&cinfo)); cinfo.buffered_image = TRUE; - cinfo.raw_data_out = dparams.output_mode == RAW_DATA; - cinfo.do_block_smoothing = dparams.do_block_smoothing; + cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA); + cinfo.do_block_smoothing = TO_JXL_BOOL(dparams.do_block_smoothing); EXPECT_TRUE(jpegli_start_decompress(&cinfo)); EXPECT_FALSE(jpegli_input_complete(&cinfo)); @@ -446,8 +446,8 @@ TEST_P(InputSuspensionTestParam, PreConsumeInputNonBuffered) { } } EXPECT_EQ(JPEG_REACHED_SOS, jpegli_consume_input(&cinfo)); - cinfo.raw_data_out = dparams.output_mode == RAW_DATA; - cinfo.do_block_smoothing = dparams.do_block_smoothing; + cinfo.raw_data_out = TO_JXL_BOOL(dparams.output_mode == RAW_DATA); + cinfo.do_block_smoothing = TO_JXL_BOOL(dparams.do_block_smoothing); if (dparams.output_mode == COEFFICIENTS) { jpegli_read_coefficients(&cinfo); diff --git a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc index de2303756e..020adf5e9e 100644 --- a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc +++ b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc @@ -37,12 +37,13 @@ void ReadOutputPass(j_decompress_ptr cinfo, const DecompressParams& dparams, output->ysize = ysize_cropped; output->components = cinfo->out_color_components; if (cinfo->quantize_colors) { - jxl::msan::UnpoisonMemory(cinfo->colormap, cinfo->out_color_components * - sizeof(cinfo->colormap[0])); + JSAMPLE** colormap = cinfo->colormap; + jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(colormap), + cinfo->out_color_components * sizeof(JSAMPLE*)); for (int c = 0; c < cinfo->out_color_components; ++c) { jxl::msan::UnpoisonMemory( - cinfo->colormap[c], - cinfo->actual_number_of_colors * sizeof(cinfo->colormap[c][0])); + reinterpret_cast<void*>(colormap[c]), + cinfo->actual_number_of_colors * sizeof(JSAMPLE)); } } if (!cinfo->raw_data_out) { @@ -89,10 +90,10 @@ void ReadOutputPass(j_decompress_ptr cinfo, const DecompressParams& dparams, rowdata[c][i] = y0 + i < ysize ? &output->raw_data[c][(y0 + i) * xsize] : nullptr; } - data[c] = &rowdata[c][0]; + data[c] = rowdata[c].data(); } JXL_CHECK(iMCU_height == - jpeg_read_raw_data(cinfo, &data[0], iMCU_height)); + jpeg_read_raw_data(cinfo, data.data(), iMCU_height)); } } JXL_CHECK(cinfo->total_iMCU_rows == @@ -113,7 +114,7 @@ void DecodeWithLibjpeg(const CompressParams& jparams, jpeg_read_header(cinfo, /*require_image=*/TRUE)); if (!jparams.icc.empty()) { uint8_t* icc_data = nullptr; - unsigned int icc_len; + unsigned int icc_len = 0; // "unpoison" via initialization JXL_CHECK(jpeg_read_icc_profile(cinfo, &icc_data, &icc_len)); JXL_CHECK(icc_data); jxl::msan::UnpoisonMemory(icc_data, icc_len); diff --git a/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc b/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc index b38d16f255..471b7c7192 100644 --- a/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc +++ b/third_party/jpeg-xl/lib/jpegli/libjpeg_wrapper.cc @@ -122,11 +122,11 @@ boolean jpeg_read_icc_profile(j_decompress_ptr cinfo, JOCTET **icc_data_ptr, } void jpeg_abort_decompress(j_decompress_ptr cinfo) { - return jpegli_abort_decompress(cinfo); + jpegli_abort_decompress(cinfo); } void jpeg_destroy_decompress(j_decompress_ptr cinfo) { - return jpegli_destroy_decompress(cinfo); + jpegli_destroy_decompress(cinfo); } void jpeg_CreateCompress(j_compress_ptr cinfo, int version, size_t structsize) { diff --git a/third_party/jpeg-xl/lib/jpegli/memory_manager.h b/third_party/jpeg-xl/lib/jpegli/memory_manager.h index 3e2bdabe06..c650caad49 100644 --- a/third_party/jpeg-xl/lib/jpegli/memory_manager.h +++ b/third_party/jpeg-xl/lib/jpegli/memory_manager.h @@ -19,7 +19,8 @@ void InitMemoryManager(j_common_ptr cinfo); template <typename T> T* Allocate(j_common_ptr cinfo, size_t len, int pool_id = JPOOL_PERMANENT) { - void* p = (*cinfo->mem->alloc_small)(cinfo, pool_id, len * sizeof(T)); + const size_t size = len * sizeof(T); // NOLINT + void* p = (*cinfo->mem->alloc_small)(cinfo, pool_id, size); return reinterpret_cast<T*>(p); } diff --git a/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc b/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc index 73db791727..3cb2fd3ee4 100644 --- a/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc +++ b/third_party/jpeg-xl/lib/jpegli/output_suspension_test.cc @@ -10,8 +10,8 @@ namespace jpegli { namespace { -static constexpr size_t kInitialBufferSize = 1024; -static constexpr size_t kFinalBufferSize = 18; +constexpr size_t kInitialBufferSize = 1024; +constexpr size_t kFinalBufferSize = 18; struct DestinationManager { jpeg_destination_mgr pub; @@ -37,7 +37,7 @@ struct DestinationManager { } static void init_destination(j_compress_ptr cinfo) { - auto us = reinterpret_cast<DestinationManager*>(cinfo->dest); + auto* us = reinterpret_cast<DestinationManager*>(cinfo->dest); us->buffer.resize(kInitialBufferSize); us->Rewind(); } @@ -84,7 +84,7 @@ TEST_P(OutputSuspensionTestParam, PixelData) { while (cinfo.next_scanline < cinfo.image_height) { size_t lines_left = cinfo.image_height - cinfo.next_scanline; size_t num_lines = std::min(config.lines_batch_size, lines_left); - memcpy(&row_bytes[0], &input.pixels[cinfo.next_scanline * stride], + memcpy(row_bytes.data(), &input.pixels[cinfo.next_scanline * stride], num_lines * stride); std::vector<JSAMPROW> rows(num_lines); for (size_t i = 0; i < num_lines; ++i) { @@ -142,7 +142,7 @@ TEST_P(OutputSuspensionTestParam, RawData) { std::vector<JSAMPARRAY> data(cinfo.num_components); for (int c = 0; c < cinfo.num_components; ++c) { rowdata[c].resize(config.jparams.v_samp(c) * DCTSIZE); - data[c] = &rowdata[c][0]; + data[c] = rowdata[c].data(); } while (cinfo.next_scanline < cinfo.image_height) { for (int c = 0; c < cinfo.num_components; ++c) { @@ -155,7 +155,7 @@ TEST_P(OutputSuspensionTestParam, RawData) { (y0 + i < cheight ? &raw_data[c][(y0 + i) * cwidth] : nullptr); } } - while (jpegli_write_raw_data(&cinfo, &data[0], max_lines) == 0) { + while (jpegli_write_raw_data(&cinfo, data.data(), max_lines) == 0) { dest.EmptyTo(&compressed, config.buffer_size); } } diff --git a/third_party/jpeg-xl/lib/jpegli/quant.cc b/third_party/jpeg-xl/lib/jpegli/quant.cc index 36f1df4cdd..14db6701b2 100644 --- a/third_party/jpeg-xl/lib/jpegli/quant.cc +++ b/third_party/jpeg-xl/lib/jpegli/quant.cc @@ -26,7 +26,7 @@ namespace { constexpr float kGlobalScaleXYB = 1.43951668f; constexpr float kGlobalScaleYCbCr = 1.73966010f; -static constexpr float kBaseQuantMatrixXYB[] = { +constexpr float kBaseQuantMatrixXYB[] = { // c = 0 7.5629935265f, 19.8247814178f, @@ -224,7 +224,7 @@ static constexpr float kBaseQuantMatrixXYB[] = { 63.6065597534f, }; -static const float kBaseQuantMatrixYCbCr[] = { +const float kBaseQuantMatrixYCbCr[] = { // c = 0 1.2397409345866273f, // 1.7227115097630963f, // @@ -422,8 +422,8 @@ static const float kBaseQuantMatrixYCbCr[] = { 114.89202448569779f, // }; -static const float k420GlobalScale = 1.22; -static const float k420Rescale[64] = { +const float k420GlobalScale = 1.22; +const float k420Rescale[64] = { 0.4093, 0.3209, 0.3477, 0.3333, 0.3144, 0.2823, 0.3214, 0.3354, // 0.3209, 0.3111, 0.3489, 0.2801, 0.3059, 0.3119, 0.4135, 0.3445, // 0.3477, 0.3489, 0.3586, 0.3257, 0.2727, 0.3754, 0.3369, 0.3484, // @@ -434,7 +434,7 @@ static const float k420Rescale[64] = { 0.3354, 0.3445, 0.3484, 0.3839, 0.3836, 0.0726, 0.0553, 0.3368, // }; -static const float kBaseQuantMatrixStd[] = { +const float kBaseQuantMatrixStd[] = { // c = 0 16.0f, 11.0f, 10.0f, 16.0f, 24.0f, 40.0f, 51.0f, 61.0f, // 12.0f, 12.0f, 14.0f, 19.0f, 26.0f, 58.0f, 60.0f, 55.0f, // @@ -455,7 +455,7 @@ static const float kBaseQuantMatrixStd[] = { 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, 99.0f, // }; -static const float kZeroBiasMulYCbCrLQ[] = { +const float kZeroBiasMulYCbCrLQ[] = { // c = 0 0.0000f, 0.0568f, 0.3880f, 0.6190f, 0.6190f, 0.4490f, 0.4490f, 0.6187f, // 0.0568f, 0.5829f, 0.6189f, 0.6190f, 0.6190f, 0.7190f, 0.6190f, 0.6189f, // @@ -485,7 +485,7 @@ static const float kZeroBiasMulYCbCrLQ[] = { 0.2960f, 0.2113f, 0.2426f, 0.1590f, 0.5403f, 0.3060f, 0.3060f, 0.3060f, // }; -static const float kZeroBiasMulYCbCrHQ[] = { +const float kZeroBiasMulYCbCrHQ[] = { // c = 0 0.0000f, 0.0044f, 0.2521f, 0.6547f, 0.8161f, 0.6130f, 0.8841f, 0.8155f, // 0.0044f, 0.6831f, 0.6553f, 0.6295f, 0.7848f, 0.7843f, 0.8474f, 0.7836f, // @@ -515,9 +515,9 @@ static const float kZeroBiasMulYCbCrHQ[] = { 0.4836f, 0.4897f, 0.2583f, 0.3565f, 0.5949f, 0.6629f, 0.6644f, 0.6644f, // }; -static const float kZeroBiasOffsetYCbCrDC[] = {0.0f, 0.0f, 0.0f}; +const float kZeroBiasOffsetYCbCrDC[] = {0.0f, 0.0f, 0.0f}; -static const float kZeroBiasOffsetYCbCrAC[] = { +const float kZeroBiasOffsetYCbCrAC[] = { 0.59082f, 0.58146f, 0.57988f, diff --git a/third_party/jpeg-xl/lib/jpegli/render.cc b/third_party/jpeg-xl/lib/jpegli/render.cc index 24e7e99618..c550f9a575 100644 --- a/third_party/jpeg-xl/lib/jpegli/render.cc +++ b/third_party/jpeg-xl/lib/jpegli/render.cc @@ -8,7 +8,6 @@ #include <string.h> #include <array> -#include <atomic> #include <cmath> #include <cstddef> #include <cstdint> @@ -203,12 +202,13 @@ void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[], if (cinfo->quantize_colors && m->quant_pass_ == 1) { float* error_row[kMaxComponents]; float* next_error_row[kMaxComponents]; - if (cinfo->dither_mode == JDITHER_ORDERED) { + J_DITHER_MODE dither_mode = cinfo->dither_mode; + if (dither_mode == JDITHER_ORDERED) { for (size_t c = 0; c < num_channels; ++c) { DitherRow(cinfo, &rows[c][xoffset], c, cinfo->output_scanline, cinfo->output_width); } - } else if (cinfo->dither_mode == JDITHER_FS) { + } else if (dither_mode == JDITHER_FS) { for (size_t c = 0; c < num_channels; ++c) { if (cinfo->output_scanline % 2 == 0) { error_row[c] = m->error_row_[c]; @@ -221,12 +221,12 @@ void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[], } } const float mul = 255.0f; - if (cinfo->dither_mode != JDITHER_FS) { + if (dither_mode != JDITHER_FS) { StoreUnsignedRow(rows, xoffset, len, num_channels, mul, scratch_space); } for (size_t i = 0; i < len; ++i) { uint8_t* pixel = &scratch_space[num_channels * i]; - if (cinfo->dither_mode == JDITHER_FS) { + if (dither_mode == JDITHER_FS) { for (size_t c = 0; c < num_channels; ++c) { float val = rows[c][i] * mul + LimitError(error_row[c][i]); pixel[c] = std::round(std::min(255.0f, std::max(0.0f, val))); @@ -234,7 +234,7 @@ void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[], } int index = LookupColorIndex(cinfo, pixel); output[i] = index; - if (cinfo->dither_mode == JDITHER_FS) { + if (dither_mode == JDITHER_FS) { size_t prev_i = i > 0 ? i - 1 : 0; size_t next_i = i + 1 < len ? i + 1 : len - 1; for (size_t c = 0; c < num_channels; ++c) { @@ -293,19 +293,18 @@ HWY_EXPORT(DecenterRow); void GatherBlockStats(const int16_t* JXL_RESTRICT coeffs, const size_t coeffs_size, int32_t* JXL_RESTRICT nonzeros, int32_t* JXL_RESTRICT sumabs) { - return HWY_DYNAMIC_DISPATCH(GatherBlockStats)(coeffs, coeffs_size, nonzeros, - sumabs); + HWY_DYNAMIC_DISPATCH(GatherBlockStats)(coeffs, coeffs_size, nonzeros, sumabs); } void WriteToOutput(j_decompress_ptr cinfo, float* JXL_RESTRICT rows[], size_t xoffset, size_t len, size_t num_channels, uint8_t* JXL_RESTRICT output) { - return HWY_DYNAMIC_DISPATCH(WriteToOutput)(cinfo, rows, xoffset, len, - num_channels, output); + HWY_DYNAMIC_DISPATCH(WriteToOutput) + (cinfo, rows, xoffset, len, num_channels, output); } void DecenterRow(float* row, size_t xsize) { - return HWY_DYNAMIC_DISPATCH(DecenterRow)(row, xsize); + HWY_DYNAMIC_DISPATCH(DecenterRow)(row, xsize); } bool ShouldApplyDequantBiases(j_decompress_ptr cinfo, int ci) { @@ -360,8 +359,8 @@ bool do_smoothing(j_decompress_ptr cinfo) { if (!cinfo->progressive_mode || cinfo->coef_bits == nullptr) { return false; } - auto coef_bits_latch = m->coef_bits_latch; - auto prev_coef_bits_latch = m->prev_coef_bits_latch; + auto* coef_bits_latch = m->coef_bits_latch; + auto* prev_coef_bits_latch = m->prev_coef_bits_latch; for (int ci = 0; ci < cinfo->num_components; ci++) { jpeg_component_info* compptr = &cinfo->comp_info[ci]; @@ -468,6 +467,7 @@ void PredictSmooth(j_decompress_ptr cinfo, JBLOCKARRAY blocks, int component, return swap_indices ? dc_values[j][i] : dc_values[i][j]; }; Al = coef_bits[coef_index]; + JXL_ASSERT(coef_index >= 0 && coef_index < 10); switch (coef_index) { case 0: // set the DC @@ -520,6 +520,7 @@ void PredictSmooth(j_decompress_ptr cinfo, JBLOCKARRAY blocks, int component, break; case 7: case 8: + default: // set Q12 and Q21 num = (dc(1, 1) - 3 * dc(1, 2) + dc(1, 3) - dc(3, 1) + 3 * dc(3, 2) - dc(3, 3)); @@ -551,7 +552,7 @@ void PredictSmooth(j_decompress_ptr cinfo, JBLOCKARRAY blocks, int component, void PrepareForOutput(j_decompress_ptr cinfo) { jpeg_decomp_master* m = cinfo->master; bool smoothing = do_smoothing(cinfo); - m->apply_smoothing = smoothing && cinfo->do_block_smoothing; + m->apply_smoothing = smoothing && FROM_JXL_BOOL(cinfo->do_block_smoothing); size_t coeffs_per_block = cinfo->num_components * DCTSIZE2; memset(m->nonzeros_, 0, coeffs_per_block * sizeof(m->nonzeros_[0])); memset(m->sumabs_, 0, coeffs_per_block * sizeof(m->sumabs_[0])); @@ -584,7 +585,7 @@ void DecodeCurrentiMCURow(j_decompress_ptr cinfo) { int offset = m->streaming_mode_ ? 0 : by0; ba[c] = (*cinfo->mem->access_virt_barray)( reinterpret_cast<j_common_ptr>(cinfo), m->coef_arrays[c], offset, - max_block_rows, false); + max_block_rows, FALSE); } for (int c = 0; c < cinfo->num_components; ++c) { size_t k0 = c * DCTSIZE2; diff --git a/third_party/jpeg-xl/lib/jpegli/source_manager.cc b/third_party/jpeg-xl/lib/jpegli/source_manager.cc index 0b8e0a5c8c..58adf803b1 100644 --- a/third_party/jpeg-xl/lib/jpegli/source_manager.cc +++ b/third_party/jpeg-xl/lib/jpegli/source_manager.cc @@ -39,7 +39,7 @@ struct StdioSourceManager { uint8_t* buffer; static boolean fill_input_buffer(j_decompress_ptr cinfo) { - auto src = reinterpret_cast<StdioSourceManager*>(cinfo->src); + auto* src = reinterpret_cast<StdioSourceManager*>(cinfo->src); size_t num_bytes_read = fread(src->buffer, 1, kStdioBufferSize, src->f); if (num_bytes_read == 0) { return EmitFakeEoiMarker(cinfo); @@ -77,7 +77,7 @@ void jpegli_stdio_src(j_decompress_ptr cinfo, FILE* infile) { cinfo->src = reinterpret_cast<jpeg_source_mgr*>( jpegli::Allocate<jpegli::StdioSourceManager>(cinfo, 1)); } - auto src = reinterpret_cast<jpegli::StdioSourceManager*>(cinfo->src); + auto* src = reinterpret_cast<jpegli::StdioSourceManager*>(cinfo->src); src->f = infile; src->buffer = jpegli::Allocate<uint8_t>(cinfo, jpegli::kStdioBufferSize); src->pub.next_input_byte = src->buffer; diff --git a/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc b/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc index 4e137876c9..59d12b001b 100644 --- a/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc +++ b/third_party/jpeg-xl/lib/jpegli/source_manager_test.cc @@ -50,7 +50,7 @@ FILE* MemOpen(const std::vector<uint8_t>& data) { TEST_P(SourceManagerTestParam, TestStdioSourceManager) { TestConfig config = GetParam(); - std::vector<uint8_t> compressed = ReadTestData(config.fn.c_str()); + std::vector<uint8_t> compressed = ReadTestData(config.fn); if (config.dparams.size_factor < 1.0) { compressed.resize(compressed.size() * config.dparams.size_factor); } @@ -77,7 +77,7 @@ TEST_P(SourceManagerTestParam, TestStdioSourceManager) { TEST_P(SourceManagerTestParam, TestMemSourceManager) { TestConfig config = GetParam(); - std::vector<uint8_t> compressed = ReadTestData(config.fn.c_str()); + std::vector<uint8_t> compressed = ReadTestData(config.fn); if (config.dparams.size_factor < 1.0f) { compressed.resize(compressed.size() * config.dparams.size_factor); } diff --git a/third_party/jpeg-xl/lib/jpegli/streaming_test.cc b/third_party/jpeg-xl/lib/jpegli/streaming_test.cc index 8d2e3577f3..2e6f7029b0 100644 --- a/third_party/jpeg-xl/lib/jpegli/streaming_test.cc +++ b/third_party/jpeg-xl/lib/jpegli/streaming_test.cc @@ -36,13 +36,13 @@ struct SourceManager { // input buffer. The buffer size is kept short because empty_output_buffer() is // called only when the output buffer is full, and we want to update the decoder // input frequently to demonstrate that streaming works. -static constexpr size_t kOutputBufferSize = 1024; +constexpr size_t kOutputBufferSize = 1024; struct DestinationManager { jpeg_destination_mgr pub; std::vector<uint8_t> buffer; SourceManager* dest; - DestinationManager(SourceManager* src) + explicit DestinationManager(SourceManager* src) : buffer(kOutputBufferSize), dest(src) { pub.next_output_byte = buffer.data(); pub.free_in_buffer = buffer.size(); @@ -54,7 +54,7 @@ struct DestinationManager { static void init_destination(j_compress_ptr cinfo) {} static boolean empty_output_buffer(j_compress_ptr cinfo) { - auto us = reinterpret_cast<DestinationManager*>(cinfo->dest); + auto* us = reinterpret_cast<DestinationManager*>(cinfo->dest); jpeg_destination_mgr* src = &us->pub; jpeg_source_mgr* dst = &us->dest->pub; std::vector<uint8_t>& src_buf = us->buffer; @@ -69,7 +69,7 @@ struct DestinationManager { dst->bytes_in_buffer = dst_buf.size(); src->next_output_byte = src_buf.data(); src->free_in_buffer = src_buf.size(); - return true; + return TRUE; } static void term_destination(j_compress_ptr cinfo) { @@ -87,6 +87,7 @@ class StreamingTestParam : public ::testing::TestWithParam<TestConfig> {}; TEST_P(StreamingTestParam, TestStreaming) { jpeg_decompress_struct dinfo = {}; jpeg_compress_struct cinfo = {}; + SourceManager src; TestConfig config = GetParam(); TestImage& input = config.input; TestImage output; @@ -99,7 +100,6 @@ TEST_P(StreamingTestParam, TestStreaming) { // compressor's output is connected to the decompressor's input. jpegli_create_decompress(&dinfo); jpegli_create_compress(&cinfo); - SourceManager src; dinfo.src = reinterpret_cast<jpeg_source_mgr*>(&src); DestinationManager dest(&src); cinfo.dest = reinterpret_cast<jpeg_destination_mgr*>(&dest); @@ -107,7 +107,7 @@ TEST_P(StreamingTestParam, TestStreaming) { cinfo.image_width = input.xsize; cinfo.image_height = input.ysize; cinfo.input_components = input.components; - cinfo.in_color_space = (J_COLOR_SPACE)input.color_space; + cinfo.in_color_space = static_cast<J_COLOR_SPACE>(input.color_space); jpegli_set_defaults(&cinfo); cinfo.comp_info[0].v_samp_factor = config.jparams.v_sampling[0]; jpegli_set_progressive_level(&cinfo, 0); @@ -122,13 +122,13 @@ TEST_P(StreamingTestParam, TestStreaming) { while (yin < cinfo.image_height) { // Feed one iMCU row at a time to the compressor. size_t lines_in = std::min(iMCU_height, cinfo.image_height - yin); - memcpy(&row_bytes[0], &input.pixels[yin * stride], lines_in * stride); + memcpy(row_bytes.data(), &input.pixels[yin * stride], lines_in * stride); std::vector<JSAMPROW> rows_in(lines_in); for (size_t i = 0; i < lines_in; ++i) { rows_in[i] = &row_bytes[i * stride]; } EXPECT_EQ(lines_in, - jpegli_write_scanlines(&cinfo, &rows_in[0], lines_in)); + jpegli_write_scanlines(&cinfo, rows_in.data(), lines_in)); yin += lines_in; if (yin == cinfo.image_height) { jpegli_finish_compress(&cinfo); @@ -180,7 +180,7 @@ TEST_P(StreamingTestParam, TestStreaming) { reinterpret_cast<JSAMPLE*>(&output.pixels[(yout + i) * stride]); } EXPECT_EQ(lines_out, - jpegli_read_scanlines(&dinfo, &rows_out[0], lines_out)); + jpegli_read_scanlines(&dinfo, rows_out.data(), lines_out)); VerifyOutputImage(input, output, yout, lines_out, 3.8f); yout += lines_out; diff --git a/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h b/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h index a454917187..4fbcb721e4 100644 --- a/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h +++ b/third_party/jpeg-xl/lib/jpegli/test_utils-inl.h @@ -8,20 +8,20 @@ // include paths for the jpeg headers. // Sequential non-interleaved. -static constexpr jpeg_scan_info kScript1[] = { +constexpr jpeg_scan_info kScript1[] = { {1, {0}, 0, 63, 0, 0}, {1, {1}, 0, 63, 0, 0}, {1, {2}, 0, 63, 0, 0}, }; // Sequential partially interleaved, chroma first. -static constexpr jpeg_scan_info kScript2[] = { +constexpr jpeg_scan_info kScript2[] = { {2, {1, 2}, 0, 63, 0, 0}, {1, {0}, 0, 63, 0, 0}, }; // Rest of the scan scripts are progressive. -static constexpr jpeg_scan_info kScript3[] = { +constexpr jpeg_scan_info kScript3[] = { // Interleaved full DC. {3, {0, 1, 2}, 0, 0, 0, 0}, // Full AC scans. @@ -29,7 +29,7 @@ static constexpr jpeg_scan_info kScript3[] = { {1, {1}, 1, 63, 0, 0}, {1, {2}, 1, 63, 0, 0}, }; -static constexpr jpeg_scan_info kScript4[] = { +constexpr jpeg_scan_info kScript4[] = { // Non-interleaved full DC. {1, {0}, 0, 0, 0, 0}, {1, {1}, 0, 0, 0, 0}, @@ -39,7 +39,7 @@ static constexpr jpeg_scan_info kScript4[] = { {1, {1}, 1, 63, 0, 0}, {1, {2}, 1, 63, 0, 0}, }; -static constexpr jpeg_scan_info kScript5[] = { +constexpr jpeg_scan_info kScript5[] = { // Partially interleaved full DC, chroma first. {2, {1, 2}, 0, 0, 0, 0}, {1, {0}, 0, 0, 0, 0}, @@ -52,7 +52,7 @@ static constexpr jpeg_scan_info kScript5[] = { {1, {1}, 1, 63, 1, 0}, {1, {2}, 1, 63, 1, 0}, }; -static constexpr jpeg_scan_info kScript6[] = { +constexpr jpeg_scan_info kScript6[] = { // Interleaved DC shifted by 2 bits. {3, {0, 1, 2}, 0, 0, 0, 2}, // Interleaved DC refinement scans. @@ -64,7 +64,7 @@ static constexpr jpeg_scan_info kScript6[] = { {1, {2}, 1, 63, 0, 0}, }; -static constexpr jpeg_scan_info kScript7[] = { +constexpr jpeg_scan_info kScript7[] = { // Non-interleaved DC shifted by 2 bits. {1, {0}, 0, 0, 0, 2}, {1, {1}, 0, 0, 0, 2}, @@ -83,7 +83,7 @@ static constexpr jpeg_scan_info kScript7[] = { {1, {2}, 1, 63, 0, 0}, }; -static constexpr jpeg_scan_info kScript8[] = { +constexpr jpeg_scan_info kScript8[] = { // Partially interleaved DC shifted by 2 bits, chroma first {2, {1, 2}, 0, 0, 0, 2}, {1, {0}, 0, 0, 0, 2}, @@ -99,7 +99,7 @@ static constexpr jpeg_scan_info kScript8[] = { {1, {2}, 1, 63, 0, 0}, }; -static constexpr jpeg_scan_info kScript9[] = { +constexpr jpeg_scan_info kScript9[] = { // Interleaved full DC. {3, {0, 1, 2}, 0, 0, 0, 0}, // AC scans for component 0 @@ -123,7 +123,7 @@ static constexpr jpeg_scan_info kScript9[] = { {1, {2}, 17, 63, 1, 0}, }; -static constexpr jpeg_scan_info kScript10[] = { +constexpr jpeg_scan_info kScript10[] = { // Interleaved full DC. {3, {0, 1, 2}, 0, 0, 0, 0}, // AC scans for spectral range 1..16 @@ -156,14 +156,14 @@ struct ScanScript { const jpeg_scan_info* scans; }; -static constexpr ScanScript kTestScript[] = { +constexpr ScanScript kTestScript[] = { {ARRAY_SIZE(kScript1), kScript1}, {ARRAY_SIZE(kScript2), kScript2}, {ARRAY_SIZE(kScript3), kScript3}, {ARRAY_SIZE(kScript4), kScript4}, {ARRAY_SIZE(kScript5), kScript5}, {ARRAY_SIZE(kScript6), kScript6}, {ARRAY_SIZE(kScript7), kScript7}, {ARRAY_SIZE(kScript8), kScript8}, {ARRAY_SIZE(kScript9), kScript9}, {ARRAY_SIZE(kScript10), kScript10}, }; -static constexpr int kNumTestScripts = ARRAY_SIZE(kTestScript); +constexpr int kNumTestScripts = ARRAY_SIZE(kTestScript); void SetScanDecompressParams(const DecompressParams& dparams, j_decompress_ptr cinfo, int scan_number) { @@ -178,7 +178,7 @@ void SetScanDecompressParams(const DecompressParams& dparams, return; } if (dparams.quantize_colors) { - cinfo->dither_mode = (J_DITHER_MODE)sparams->dither_mode; + cinfo->dither_mode = static_cast<J_DITHER_MODE>(sparams->dither_mode); if (sparams->color_quant_mode == CQUANT_1PASS) { cinfo->two_pass_quantize = FALSE; cinfo->colormap = nullptr; @@ -194,7 +194,8 @@ void SetScanDecompressParams(const DecompressParams& dparams, cinfo->colormap = (*cinfo->mem->alloc_sarray)( reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, cinfo->actual_number_of_colors, 3); - jxl::msan::UnpoisonMemory(cinfo->colormap, 3 * sizeof(JSAMPROW)); + jxl::msan::UnpoisonMemory(reinterpret_cast<void*>(cinfo->colormap), + 3 * sizeof(JSAMPLE*)); for (int i = 0; i < kTestColorMapNumColors; ++i) { cinfo->colormap[0][i] = (kTestColorMap[i] >> 16) & 0xff; cinfo->colormap[1][i] = (kTestColorMap[i] >> 8) & 0xff; @@ -212,20 +213,21 @@ void SetScanDecompressParams(const DecompressParams& dparams, void SetDecompressParams(const DecompressParams& dparams, j_decompress_ptr cinfo) { - cinfo->do_block_smoothing = dparams.do_block_smoothing; - cinfo->do_fancy_upsampling = dparams.do_fancy_upsampling; + cinfo->do_block_smoothing = dparams.do_block_smoothing ? 1 : 0; + cinfo->do_fancy_upsampling = dparams.do_fancy_upsampling ? 1 : 0; if (dparams.output_mode == RAW_DATA) { cinfo->raw_data_out = TRUE; } if (dparams.set_out_color_space) { - cinfo->out_color_space = (J_COLOR_SPACE)dparams.out_color_space; + cinfo->out_color_space = + static_cast<J_COLOR_SPACE>(dparams.out_color_space); if (dparams.out_color_space == JCS_UNKNOWN) { cinfo->jpeg_color_space = JCS_UNKNOWN; } } cinfo->scale_num = dparams.scale_num; cinfo->scale_denom = dparams.scale_denom; - cinfo->quantize_colors = dparams.quantize_colors; + cinfo->quantize_colors = dparams.quantize_colors ? 1 : 0; cinfo->desired_number_of_colors = dparams.desired_number_of_colors; if (!dparams.scan_params.empty()) { if (cinfo->buffered_image) { @@ -420,7 +422,7 @@ void CopyCoefficients(j_decompress_ptr cinfo, jvirt_barray_ptr* coef_arrays, DCTSIZE2); for (size_t by = 0; by < comp->height_in_blocks; ++by) { JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)(comptr, coef_arrays[c], - by, 1, true); + by, 1, TRUE); size_t stride = comp->width_in_blocks * sizeof(JBLOCK); size_t offset = by * comp->width_in_blocks * DCTSIZE2; memcpy(&coeffs[offset], ba[0], stride); diff --git a/third_party/jpeg-xl/lib/jpegli/test_utils.cc b/third_party/jpeg-xl/lib/jpegli/test_utils.cc index 232b937496..4e675070cf 100644 --- a/third_party/jpeg-xl/lib/jpegli/test_utils.cc +++ b/third_party/jpeg-xl/lib/jpegli/test_utils.cc @@ -153,7 +153,7 @@ bool ReadPNM(const std::vector<uint8_t>& data, size_t* xsize, size_t* ysize, return false; } pixels->resize(data.data() + data.size() - pos); - memcpy(&(*pixels)[0], pos, pixels->size()); + memcpy(pixels->data(), pos, pixels->size()); return true; } @@ -216,7 +216,8 @@ std::ostream& operator<<(std::ostream& os, const TestImage& input) { os << input.xsize << "x" << input.ysize; os << IOMethodName(input.data_type, input.endianness); if (input.color_space != JCS_RGB) { - os << "InputColor" << ColorSpaceName((J_COLOR_SPACE)input.color_space); + os << "InputColor" + << ColorSpaceName(static_cast<J_COLOR_SPACE>(input.color_space)); } if (input.color_space == JCS_UNKNOWN) { os << input.components; @@ -229,18 +230,18 @@ std::ostream& operator<<(std::ostream& os, const CompressParams& jparams) { os << SamplingId(jparams); if (jparams.set_jpeg_colorspace) { os << "JpegColor" - << ColorSpaceName((J_COLOR_SPACE)jparams.jpeg_color_space); + << ColorSpaceName(static_cast<J_COLOR_SPACE>(jparams.jpeg_color_space)); } if (!jparams.comp_ids.empty()) { os << "CID"; - for (size_t i = 0; i < jparams.comp_ids.size(); ++i) { - os << jparams.comp_ids[i]; + for (int cid : jparams.comp_ids) { + os << cid; } } if (!jparams.quant_indexes.empty()) { os << "QIDX"; - for (size_t i = 0; i < jparams.quant_indexes.size(); ++i) { - os << jparams.quant_indexes[i]; + for (int qi : jparams.quant_indexes) { + os << qi; } for (const auto& table : jparams.quant_tables) { os << "TABLE" << table.slot_idx << "T" << table.table_type << "F" @@ -320,7 +321,7 @@ void RGBToYCbCr(float r, float g, float b, float* y, float* cb, float* cr) { void ConvertPixel(const uint8_t* input_rgb, uint8_t* out, J_COLOR_SPACE colorspace, size_t num_channels, JpegliDataType data_type = JPEGLI_TYPE_UINT8, - bool swap_endianness = JPEGLI_NATIVE_ENDIAN) { + JXL_BOOL swap_endianness = JPEGLI_NATIVE_ENDIAN) { const float kMul = 255.0f; float r = input_rgb[0] / kMul; float g = input_rgb[1] / kMul; @@ -334,7 +335,9 @@ void ConvertPixel(const uint8_t* input_rgb, uint8_t* out, out8[c] = input_rgb[std::min<size_t>(2, c)]; } } else if (colorspace == JCS_YCbCr) { - float Y, Cb, Cr; + float Y; + float Cb; + float Cr; RGBToYCbCr(r, g, b, &Y, &Cb, &Cr); out8[0] = static_cast<uint8_t>(std::round(Y * kMul)); out8[1] = static_cast<uint8_t>(std::round(Cb * kMul)); @@ -350,7 +353,9 @@ void ConvertPixel(const uint8_t* input_rgb, uint8_t* out, out8[1] = static_cast<uint8_t>(std::round((1.0f - g) * kMul)); out8[2] = static_cast<uint8_t>(std::round((1.0f - b) * kMul)); } else if (colorspace == JCS_YCCK) { - float Y, Cb, Cr; + float Y; + float Cb; + float Cr; RGBToYCbCr(r, g, b, &Y, &Cb, &Cr); out8[0] = static_cast<uint8_t>(std::round(Y * kMul)); out8[1] = static_cast<uint8_t>(std::round(Cb * kMul)); @@ -399,7 +404,10 @@ void ConvertToGrayscale(TestImage* img) { void GeneratePixels(TestImage* img) { const std::vector<uint8_t> imgdata = ReadTestData("jxl/flower/flower.pnm"); - size_t xsize, ysize, channels, bitdepth; + size_t xsize; + size_t ysize; + size_t channels; + size_t bitdepth; std::vector<uint8_t> pixels; JXL_CHECK(ReadPNM(imgdata, &xsize, &ysize, &channels, &bitdepth, &pixels)); if (img->xsize == 0) img->xsize = xsize; @@ -412,7 +420,8 @@ void GeneratePixels(TestImage* img) { size_t in_stride = xsize * in_bytes_per_pixel; size_t x0 = (xsize - img->xsize) / 2; size_t y0 = (ysize - img->ysize) / 2; - SetNumChannels((J_COLOR_SPACE)img->color_space, &img->components); + SetNumChannels(static_cast<J_COLOR_SPACE>(img->color_space), + &img->components); size_t out_bytes_per_pixel = jpegli_bytes_per_sample(img->data_type) * img->components; size_t out_stride = img->xsize * out_bytes_per_pixel; @@ -427,8 +436,9 @@ void GeneratePixels(TestImage* img) { size_t idx_in = y * in_stride + x * in_bytes_per_pixel; size_t idx_out = iy * out_stride + ix * out_bytes_per_pixel; ConvertPixel(&pixels[idx_in], &img->pixels[idx_out], - (J_COLOR_SPACE)img->color_space, img->components, - img->data_type, swap_endianness); + static_cast<J_COLOR_SPACE>(img->color_space), + img->components, img->data_type, + TO_JXL_BOOL(swap_endianness)); } } } @@ -492,7 +502,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, jpegli_set_progressive_level(cinfo, 0); } jpegli_set_defaults(cinfo); - cinfo->in_color_space = (J_COLOR_SPACE)input.color_space; + cinfo->in_color_space = static_cast<J_COLOR_SPACE>(input.color_space); jpegli_default_colorspace(cinfo); if (jparams.override_JFIF >= 0) { cinfo->write_JFIF_header = jparams.override_JFIF; @@ -501,7 +511,8 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, cinfo->write_Adobe_marker = jparams.override_Adobe; } if (jparams.set_jpeg_colorspace) { - jpegli_set_colorspace(cinfo, (J_COLOR_SPACE)jparams.jpeg_color_space); + jpegli_set_colorspace(cinfo, + static_cast<J_COLOR_SPACE>(jparams.jpeg_color_space)); } if (!jparams.comp_ids.empty()) { for (int c = 0; c < cinfo->num_components; ++c) { @@ -522,15 +533,16 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, for (const auto& table : jparams.quant_tables) { if (table.add_raw) { cinfo->quant_tbl_ptrs[table.slot_idx] = - jpegli_alloc_quant_table((j_common_ptr)cinfo); + jpegli_alloc_quant_table(reinterpret_cast<j_common_ptr>(cinfo)); for (int k = 0; k < DCTSIZE2; ++k) { cinfo->quant_tbl_ptrs[table.slot_idx]->quantval[k] = table.quantval[k]; } cinfo->quant_tbl_ptrs[table.slot_idx]->sent_table = FALSE; } else { - jpegli_add_quant_table(cinfo, table.slot_idx, &table.basic_table[0], - table.scale_factor, table.force_baseline); + jpegli_add_quant_table(cinfo, table.slot_idx, table.basic_table.data(), + table.scale_factor, + TO_JXL_BOOL(table.force_baseline)); } } } @@ -546,7 +558,8 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, jpegli_set_progressive_level(cinfo, jparams.progressive_mode); } jpegli_set_input_format(cinfo, input.data_type, input.endianness); - jpegli_enable_adaptive_quantization(cinfo, jparams.use_adaptive_quantization); + jpegli_enable_adaptive_quantization( + cinfo, TO_JXL_BOOL(jparams.use_adaptive_quantization)); cinfo->restart_interval = jparams.restart_interval; cinfo->restart_in_rows = jparams.restart_in_rows; cinfo->smoothing_factor = jparams.smoothing_factor; @@ -555,7 +568,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, } else if (jparams.optimize_coding == 0) { cinfo->optimize_coding = FALSE; } - cinfo->raw_data_in = !input.raw_data.empty(); + cinfo->raw_data_in = TO_JXL_BOOL(!input.raw_data.empty()); if (jparams.optimize_coding == 0 && jparams.use_flat_dc_luma_code) { JHUFF_TBL* tbl = cinfo->dc_huff_tbl_ptrs[0]; memset(tbl, 0, sizeof(*tbl)); @@ -572,13 +585,13 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, cinfo->ac_huff_tbl_ptrs[0]->sent_table = TRUE; cinfo->ac_huff_tbl_ptrs[1]->sent_table = TRUE; } - jpegli_start_compress(cinfo, write_all_tables); + jpegli_start_compress(cinfo, TO_JXL_BOOL(write_all_tables)); if (jparams.add_marker) { jpegli_write_marker(cinfo, kSpecialMarker0, kMarkerData, sizeof(kMarkerData)); jpegli_write_m_header(cinfo, kSpecialMarker1, sizeof(kMarkerData)); - for (size_t p = 0; p < sizeof(kMarkerData); ++p) { - jpegli_write_m_byte(cinfo, kMarkerData[p]); + for (uint8_t c : kMarkerData) { + jpegli_write_m_byte(cinfo, c); } for (size_t i = 0; i < kMarkerSequenceLen; ++i) { jpegli_write_marker(cinfo, kMarkerSequence[i], kMarkerData, @@ -597,7 +610,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, std::vector<JSAMPARRAY> data(cinfo->num_components); for (int c = 0; c < cinfo->num_components; ++c) { rowdata[c].resize(jparams.v_samp(c) * DCTSIZE); - data[c] = &rowdata[c][0]; + data[c] = rowdata[c].data(); } while (cinfo->next_scanline < cinfo->image_height) { for (int c = 0; c < cinfo->num_components; ++c) { @@ -610,7 +623,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, (y0 + i < cheight ? &raw_data[c][(y0 + i) * cwidth] : nullptr); } } - size_t num_lines = jpegli_write_raw_data(cinfo, &data[0], max_lines); + size_t num_lines = jpegli_write_raw_data(cinfo, data.data(), max_lines); JXL_CHECK(num_lines == max_lines); } } else if (!input.coeffs.empty()) { @@ -630,15 +643,15 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, jpegli_write_marker(cinfo, kSpecialMarker0, kMarkerData, sizeof(kMarkerData)); jpegli_write_m_header(cinfo, kSpecialMarker1, sizeof(kMarkerData)); - for (size_t p = 0; p < sizeof(kMarkerData); ++p) { - jpegli_write_m_byte(cinfo, kMarkerData[p]); + for (uint8_t c : kMarkerData) { + jpegli_write_m_byte(cinfo, c); } } for (int c = 0; c < cinfo->num_components; ++c) { jpeg_component_info* comp = &cinfo->comp_info[c]; for (size_t by = 0; by < comp->height_in_blocks; ++by) { JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)( - comptr, coef_arrays[c], by, 1, true); + comptr, coef_arrays[c], by, 1, TRUE); size_t stride = comp->width_in_blocks * sizeof(JBLOCK); size_t offset = by * comp->width_in_blocks * DCTSIZE2; memcpy(ba[0], &input.coeffs[c][offset], stride); @@ -649,7 +662,7 @@ void EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, jpegli_bytes_per_sample(input.data_type); std::vector<uint8_t> row_bytes(stride); for (size_t y = 0; y < cinfo->image_height; ++y) { - memcpy(&row_bytes[0], &input.pixels[y * stride], stride); + memcpy(row_bytes.data(), &input.pixels[y * stride], stride); JSAMPROW row[] = {row_bytes.data()}; jpegli_write_scanlines(cinfo, row, 1); } @@ -681,15 +694,15 @@ bool EncodeWithJpegli(const TestImage& input, const CompressParams& jparams, int NumTestScanScripts() { return kNumTestScripts; } -void DumpImage(const TestImage& image, const std::string fn) { +void DumpImage(const TestImage& image, const std::string& fn) { JXL_CHECK(image.components == 1 || image.components == 3); size_t bytes_per_sample = jpegli_bytes_per_sample(image.data_type); uint32_t maxval = (1u << (8 * bytes_per_sample)) - 1; char type = image.components == 1 ? '5' : '6'; std::ofstream out(fn.c_str(), std::ofstream::binary); - out << "P" << type << std::endl - << image.xsize << " " << image.ysize << std::endl - << maxval << std::endl; + out << "P" << type << "\n" + << image.xsize << " " << image.ysize << "\n" + << maxval << "\n"; out.write(reinterpret_cast<const char*>(image.pixels.data()), image.pixels.size()); out.close(); diff --git a/third_party/jpeg-xl/lib/jpegli/testing.h b/third_party/jpeg-xl/lib/jpegli/testing.h index 873a0171e7..6a6e0ca638 100644 --- a/third_party/jpeg-xl/lib/jpegli/testing.h +++ b/third_party/jpeg-xl/lib/jpegli/testing.h @@ -6,15 +6,7 @@ #ifndef LIB_JPEGLI_TESTING_H_ #define LIB_JPEGLI_TESTING_H_ -// GTest/GMock specific macros / wrappers. - -// gmock unconditionally redefines those macros (to wrong values). -// Lets include it only here and mitigate the problem. -#pragma push_macro("PRIdS") -#pragma push_macro("PRIuS") -#include "gmock/gmock.h" -#pragma pop_macro("PRIuS") -#pragma pop_macro("PRIdS") +// GTest specific macros / wrappers. #include "gtest/gtest.h" @@ -28,8 +20,12 @@ // Ensures that we don't make our test bounds too lax, effectively disabling the // tests. -MATCHER_P(IsSlightlyBelow, max, "") { - return max * 0.75 <= arg && arg <= max * 1.0; -} +#define EXPECT_SLIGHTLY_BELOW(A, E) \ + { \ + double _actual = (A); \ + double _expected = (E); \ + EXPECT_LE(_actual, _expected); \ + EXPECT_GE(_actual, 0.75 * _expected); \ + } #endif // LIB_JPEGLI_TESTING_H_ diff --git a/third_party/jpeg-xl/lib/jpegli/transpose-inl.h b/third_party/jpeg-xl/lib/jpegli/transpose-inl.h index 9fdd222f4e..cdc289f96c 100644 --- a/third_party/jpeg-xl/lib/jpegli/transpose-inl.h +++ b/third_party/jpeg-xl/lib/jpegli/transpose-inl.h @@ -18,8 +18,8 @@ namespace HWY_NAMESPACE { namespace { #if HWY_CAP_GE256 -static JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from, - float* JXL_RESTRICT to) { +JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from, + float* JXL_RESTRICT to) { const HWY_CAPPED(float, 8) d; auto i0 = Load(d, from); auto i1 = Load(d, from + 1 * 8); @@ -67,8 +67,8 @@ static JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from, Store(i7, d, to + 7 * 8); } #elif HWY_TARGET != HWY_SCALAR -static JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from, - float* JXL_RESTRICT to) { +JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from, + float* JXL_RESTRICT to) { const HWY_CAPPED(float, 4) d; for (size_t n = 0; n < 8; n += 4) { for (size_t m = 0; m < 8; m += 4) { diff --git a/third_party/jpeg-xl/lib/jpegli/upsample.cc b/third_party/jpeg-xl/lib/jpegli/upsample.cc index 5559aa78a6..7dae841b8a 100644 --- a/third_party/jpeg-xl/lib/jpegli/upsample.cc +++ b/third_party/jpeg-xl/lib/jpegli/upsample.cc @@ -122,7 +122,7 @@ HWY_EXPORT(Upsample2Vertical); void Upsample2Horizontal(float* JXL_RESTRICT row, float* JXL_RESTRICT scratch_space, size_t len_out) { - return HWY_DYNAMIC_DISPATCH(Upsample2Horizontal)(row, scratch_space, len_out); + HWY_DYNAMIC_DISPATCH(Upsample2Horizontal)(row, scratch_space, len_out); } void Upsample2Vertical(const float* JXL_RESTRICT row_top, @@ -130,8 +130,8 @@ void Upsample2Vertical(const float* JXL_RESTRICT row_top, const float* JXL_RESTRICT row_bot, float* JXL_RESTRICT row_out0, float* JXL_RESTRICT row_out1, size_t len) { - return HWY_DYNAMIC_DISPATCH(Upsample2Vertical)(row_top, row_mid, row_bot, - row_out0, row_out1, len); + HWY_DYNAMIC_DISPATCH(Upsample2Vertical) + (row_top, row_mid, row_bot, row_out0, row_out1, len); } } // namespace jpegli #endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jxl.cmake b/third_party/jpeg-xl/lib/jxl.cmake index 8c7e711f52..86fa37151d 100644 --- a/third_party/jpeg-xl/lib/jxl.cmake +++ b/third_party/jpeg-xl/lib/jxl.cmake @@ -60,7 +60,7 @@ include(GenerateExportHeader) # CMake does not allow generate_export_header for INTERFACE library, so we # add this stub library just for file generation. -add_library(jxl_export OBJECT ${JPEGXL_INTERNAL_PUBLIC_HEADERS}) +add_library(jxl_export OBJECT ${JPEGXL_INTERNAL_PUBLIC_HEADERS} nothing.cc) set_target_properties(jxl_export PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN 1 @@ -269,8 +269,10 @@ set(JPEGXL_LIBRARY_REQUIRES if (BUILD_SHARED_LIBS) set(JPEGXL_REQUIRES_TYPE "Requires.private") + set(JPEGXL_PRIVATE_LIBS "-lm ${PKGCONFIG_CXX_LIB}") else() set(JPEGXL_REQUIRES_TYPE "Requires") + set(JPEGXL_PUBLIC_LIBS "-lm ${PKGCONFIG_CXX_LIB}") endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/jxl/libjxl.pc.in" diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy.cc b/third_party/jpeg-xl/lib/jxl/ac_strategy.cc index 3de477f71c..e7a72f5a33 100644 --- a/third_party/jpeg-xl/lib/jxl/ac_strategy.cc +++ b/third_party/jpeg-xl/lib/jxl/ac_strategy.cc @@ -8,12 +8,9 @@ #include <string.h> #include <algorithm> -#include <numeric> // iota -#include <type_traits> #include <utility> #include "lib/jxl/base/bits.h" -#include "lib/jxl/image_ops.h" namespace jxl { @@ -86,10 +83,12 @@ constexpr size_t AcStrategy::kMaxCoeffBlocks; constexpr size_t AcStrategy::kMaxBlockDim; constexpr size_t AcStrategy::kMaxCoeffArea; -AcStrategyImage::AcStrategyImage(size_t xsize, size_t ysize) - : layers_(xsize, ysize) { - row_ = layers_.Row(0); - stride_ = layers_.PixelsPerRow(); +StatusOr<AcStrategyImage> AcStrategyImage::Create(size_t xsize, size_t ysize) { + AcStrategyImage img; + JXL_ASSIGN_OR_RETURN(img.layers_, ImageB::Create(xsize, ysize)); + img.row_ = img.layers_.Row(0); + img.stride_ = img.layers_.PixelsPerRow(); + return img; } size_t AcStrategyImage::CountBlocks(AcStrategy::Type type) const { diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy.h b/third_party/jpeg-xl/lib/jxl/ac_strategy.h index ecdcbbbd32..9e5917ff1b 100644 --- a/third_party/jpeg-xl/lib/jxl/ac_strategy.h +++ b/third_party/jpeg-xl/lib/jxl/ac_strategy.h @@ -144,7 +144,7 @@ class AcStrategy { 8, 4, 8, 16, 8, 16, 32, 16, 32}; static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies, "Update LUT"); - return kLut[size_t(strategy_)]; + return kLut[static_cast<size_t>(strategy_)]; } JXL_INLINE size_t covered_blocks_y() const { @@ -153,7 +153,7 @@ class AcStrategy { 8, 8, 4, 16, 16, 8, 32, 32, 16}; static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies, "Update LUT"); - return kLut[size_t(strategy_)]; + return kLut[static_cast<size_t>(strategy_)]; } JXL_INLINE size_t log2_covered_blocks() const { @@ -162,7 +162,7 @@ class AcStrategy { 6, 5, 5, 8, 7, 7, 10, 9, 9}; static_assert(sizeof(kLut) / sizeof(*kLut) == kNumValidStrategies, "Update LUT"); - return kLut[size_t(strategy_)]; + return kLut[static_cast<size_t>(strategy_)]; } private: @@ -181,7 +181,9 @@ class AcStrategyRow { public: explicit AcStrategyRow(const uint8_t* row) : row_(row) {} AcStrategy operator[](size_t x) const { - return AcStrategy(static_cast<AcStrategy::Type>(row_[x] >> 1), row_[x] & 1); + AcStrategy::Type strategy = static_cast<AcStrategy::Type>(row_[x] >> 1); + bool is_first = static_cast<bool>(row_[x] & 1); + return AcStrategy(strategy, is_first); } private: @@ -191,7 +193,8 @@ class AcStrategyRow { class AcStrategyImage { public: AcStrategyImage() = default; - AcStrategyImage(size_t xsize, size_t ysize); + static StatusOr<AcStrategyImage> Create(size_t xsize, size_t ysize); + AcStrategyImage(AcStrategyImage&&) = default; AcStrategyImage& operator=(AcStrategyImage&&) = default; diff --git a/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc b/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc index 3745db2b32..b1d9103466 100644 --- a/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc +++ b/third_party/jpeg-xl/lib/jxl/ac_strategy_test.cc @@ -81,7 +81,8 @@ class AcStrategyRoundtrip : public ::hwy::TestWithParamTargetAndT<int> { HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T( AcStrategyRoundtrip, - ::testing::Range(0, int(AcStrategy::Type::kNumValidStrategies))); + ::testing::Range(0, + static_cast<int>(AcStrategy::Type::kNumValidStrategies))); TEST_P(AcStrategyRoundtrip, Test) { Run(); } @@ -141,7 +142,8 @@ class AcStrategyRoundtripDownsample HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T( AcStrategyRoundtripDownsample, - ::testing::Range(0, int(AcStrategy::Type::kNumValidStrategies))); + ::testing::Range(0, + static_cast<int>(AcStrategy::Type::kNumValidStrategies))); TEST_P(AcStrategyRoundtripDownsample, Test) { Run(); } @@ -205,7 +207,8 @@ class AcStrategyDownsample : public ::hwy::TestWithParamTargetAndT<int> { HWY_TARGET_INSTANTIATE_TEST_SUITE_P_T( AcStrategyDownsample, - ::testing::Range(0, int(AcStrategy::Type::kNumValidStrategies))); + ::testing::Range(0, + static_cast<int>(AcStrategy::Type::kNumValidStrategies))); TEST_P(AcStrategyDownsample, Test) { Run(); } diff --git a/third_party/jpeg-xl/lib/jxl/alpha_test.cc b/third_party/jpeg-xl/lib/jxl/alpha_test.cc index ddafd829ec..a93254f3dd 100644 --- a/third_party/jpeg-xl/lib/jxl/alpha_test.cc +++ b/third_party/jpeg-xl/lib/jxl/alpha_test.cc @@ -5,69 +5,59 @@ #include "lib/jxl/alpha.h" -#include "lib/jxl/test_utils.h" +#include <array> + +#include "lib/jxl/base/common.h" #include "lib/jxl/testing.h" namespace jxl { namespace { -using ::testing::_; -using ::testing::ElementsAre; -using ::testing::FloatNear; - TEST(AlphaTest, BlendingWithNonPremultiplied) { - const float bg_rgb[3] = {100, 110, 120}; + const Color bg_rgb{100, 110, 120}; const float bg_a = 180.f / 255; - const float fg_rgb[3] = {25, 21, 23}; + const Color fg_rgb{25, 21, 23}; const float fg_a = 15420.f / 65535; const float fg_a2 = 2.0f; - float out_rgb[3]; + Color out_rgb; float out_a; PerformAlphaBlending( /*bg=*/{&bg_rgb[0], &bg_rgb[1], &bg_rgb[2], &bg_a}, /*fg=*/{&fg_rgb[0], &fg_rgb[1], &fg_rgb[2], &fg_a}, /*out=*/{&out_rgb[0], &out_rgb[1], &out_rgb[2], &out_a}, 1, /*alpha_is_premultiplied=*/false, /*clamp=*/false); - EXPECT_THAT(out_rgb, - ElementsAre(FloatNear(77.2f, .05f), FloatNear(83.0f, .05f), - FloatNear(90.6f, .05f))); + EXPECT_ARRAY_NEAR(out_rgb, (Color{77.2f, 83.0f, 90.6f}), 0.05f); EXPECT_NEAR(out_a, 3174.f / 4095, 1e-5); PerformAlphaBlending( /*bg=*/{&bg_rgb[0], &bg_rgb[1], &bg_rgb[2], &bg_a}, /*fg=*/{&fg_rgb[0], &fg_rgb[1], &fg_rgb[2], &fg_a2}, /*out=*/{&out_rgb[0], &out_rgb[1], &out_rgb[2], &out_a}, 1, /*alpha_is_premultiplied=*/false, /*clamp=*/true); - EXPECT_THAT(out_rgb, ElementsAre(FloatNear(fg_rgb[0], .05f), - FloatNear(fg_rgb[1], .05f), - FloatNear(fg_rgb[2], .05f))); + EXPECT_ARRAY_NEAR(out_rgb, fg_rgb, 0.05f); EXPECT_NEAR(out_a, 1.0f, 1e-5); } TEST(AlphaTest, BlendingWithPremultiplied) { - const float bg_rgb[3] = {100, 110, 120}; + const Color bg_rgb{100, 110, 120}; const float bg_a = 180.f / 255; - const float fg_rgb[3] = {25, 21, 23}; + const Color fg_rgb{25, 21, 23}; const float fg_a = 15420.f / 65535; const float fg_a2 = 2.0f; - float out_rgb[3]; + Color out_rgb; float out_a; PerformAlphaBlending( /*bg=*/{&bg_rgb[0], &bg_rgb[1], &bg_rgb[2], &bg_a}, /*fg=*/{&fg_rgb[0], &fg_rgb[1], &fg_rgb[2], &fg_a}, /*out=*/{&out_rgb[0], &out_rgb[1], &out_rgb[2], &out_a}, 1, /*alpha_is_premultiplied=*/true, /*clamp=*/false); - EXPECT_THAT(out_rgb, - ElementsAre(FloatNear(101.5f, .05f), FloatNear(105.1f, .05f), - FloatNear(114.8f, .05f))); + EXPECT_ARRAY_NEAR(out_rgb, (Color{101.5f, 105.1f, 114.8f}), 0.05f); EXPECT_NEAR(out_a, 3174.f / 4095, 1e-5); PerformAlphaBlending( /*bg=*/{&bg_rgb[0], &bg_rgb[1], &bg_rgb[2], &bg_a}, /*fg=*/{&fg_rgb[0], &fg_rgb[1], &fg_rgb[2], &fg_a2}, /*out=*/{&out_rgb[0], &out_rgb[1], &out_rgb[2], &out_a}, 1, /*alpha_is_premultiplied=*/true, /*clamp=*/true); - EXPECT_THAT(out_rgb, ElementsAre(FloatNear(fg_rgb[0], .05f), - FloatNear(fg_rgb[1], .05f), - FloatNear(fg_rgb[2], .05f))); + EXPECT_ARRAY_NEAR(out_rgb, fg_rgb, 0.05f); EXPECT_NEAR(out_a, 1.0f, 1e-5); } @@ -76,58 +66,51 @@ TEST(AlphaTest, Mul) { const float fg = 25; float out; PerformMulBlending(&bg, &fg, &out, 1, /*clamp=*/false); - EXPECT_THAT(out, FloatNear(fg * bg, .05f)); + EXPECT_NEAR(out, fg * bg, .05f); PerformMulBlending(&bg, &fg, &out, 1, /*clamp=*/true); - EXPECT_THAT(out, FloatNear(bg, .05f)); + EXPECT_NEAR(out, bg, .05f); } TEST(AlphaTest, PremultiplyAndUnpremultiply) { - const float alpha[] = {0.f, 63.f / 255, 127.f / 255, 1.f}; - float r[] = {120, 130, 140, 150}; - float g[] = {124, 134, 144, 154}; - float b[] = {127, 137, 147, 157}; + using F4 = std::array<float, 4>; + const F4 alpha{0.f, 63.f / 255, 127.f / 255, 1.f}; + F4 r{120, 130, 140, 150}; + F4 g{124, 134, 144, 154}; + F4 b{127, 137, 147, 157}; - PremultiplyAlpha(r, g, b, alpha, 4); - EXPECT_THAT( - r, ElementsAre(FloatNear(0.f, 1e-5f), FloatNear(130 * 63.f / 255, 1e-5f), - FloatNear(140 * 127.f / 255, 1e-5f), 150)); - EXPECT_THAT( - g, ElementsAre(FloatNear(0.f, 1e-5f), FloatNear(134 * 63.f / 255, 1e-5f), - FloatNear(144 * 127.f / 255, 1e-5f), 154)); - EXPECT_THAT( - b, ElementsAre(FloatNear(0.f, 1e-5f), FloatNear(137 * 63.f / 255, 1e-5f), - FloatNear(147 * 127.f / 255, 1e-5f), 157)); + PremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size()); + EXPECT_ARRAY_NEAR(r, (F4{0.0f, 130 * 63.f / 255, 140 * 127.f / 255, 150}), + 1e-5f); + EXPECT_ARRAY_NEAR(g, (F4{0.0f, 134 * 63.f / 255, 144 * 127.f / 255, 154}), + 1e-5f); + EXPECT_ARRAY_NEAR(b, (F4{0.0f, 137 * 63.f / 255, 147 * 127.f / 255, 157}), + 1e-5f); - UnpremultiplyAlpha(r, g, b, alpha, 4); - EXPECT_THAT(r, ElementsAre(FloatNear(120, 1e-4f), FloatNear(130, 1e-4f), - FloatNear(140, 1e-4f), FloatNear(150, 1e-4f))); - EXPECT_THAT(g, ElementsAre(FloatNear(124, 1e-4f), FloatNear(134, 1e-4f), - FloatNear(144, 1e-4f), FloatNear(154, 1e-4f))); - EXPECT_THAT(b, ElementsAre(FloatNear(127, 1e-4f), FloatNear(137, 1e-4f), - FloatNear(147, 1e-4f), FloatNear(157, 1e-4f))); + UnpremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size()); + EXPECT_ARRAY_NEAR(r, (F4{120, 130, 140, 150}), 1e-4f); + EXPECT_ARRAY_NEAR(g, (F4{124, 134, 144, 154}), 1e-4f); + EXPECT_ARRAY_NEAR(b, (F4{127, 137, 147, 157}), 1e-4f); } TEST(AlphaTest, UnpremultiplyAndPremultiply) { - const float alpha[] = {0.f, 63.f / 255, 127.f / 255, 1.f}; - float r[] = {50, 60, 70, 80}; - float g[] = {54, 64, 74, 84}; - float b[] = {57, 67, 77, 87}; + using F4 = std::array<float, 4>; + const F4 alpha{0.f, 63.f / 255, 127.f / 255, 1.f}; + F4 r{50, 60, 70, 80}; + F4 g{54, 64, 74, 84}; + F4 b{57, 67, 77, 87}; - UnpremultiplyAlpha(r, g, b, alpha, 4); - EXPECT_THAT(r, ElementsAre(_, FloatNear(60 * 255.f / 63, 1e-4f), - FloatNear(70 * 255.f / 127, 1e-4f), 80)); - EXPECT_THAT(g, ElementsAre(_, FloatNear(64 * 255.f / 63, 1e-4f), - FloatNear(74 * 255.f / 127, 1e-4f), 84)); - EXPECT_THAT(b, ElementsAre(_, FloatNear(67 * 255.f / 63, 1e-4f), - FloatNear(77 * 255.f / 127, 1e-4f), 87)); + UnpremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size()); + EXPECT_ARRAY_NEAR( + r, (F4{50.0f * (1 << 26), 60 * 255.f / 63, 70 * 255.f / 127, 80}), 1e-4f); + EXPECT_ARRAY_NEAR( + g, (F4{54.0f * (1 << 26), 64 * 255.f / 63, 74 * 255.f / 127, 84}), 1e-4f); + EXPECT_ARRAY_NEAR( + b, (F4{57.0f * (1 << 26), 67 * 255.f / 63, 77 * 255.f / 127, 87}), 1e-4f); - PremultiplyAlpha(r, g, b, alpha, 4); - EXPECT_THAT(r, ElementsAre(FloatNear(50, 1e-4f), FloatNear(60, 1e-4f), - FloatNear(70, 1e-4f), FloatNear(80, 1e-4f))); - EXPECT_THAT(g, ElementsAre(FloatNear(54, 1e-4f), FloatNear(64, 1e-4f), - FloatNear(74, 1e-4f), FloatNear(84, 1e-4f))); - EXPECT_THAT(b, ElementsAre(FloatNear(57, 1e-4f), FloatNear(67, 1e-4f), - FloatNear(77, 1e-4f), FloatNear(87, 1e-4f))); + PremultiplyAlpha(r.data(), g.data(), b.data(), alpha.data(), alpha.size()); + EXPECT_ARRAY_NEAR(r, (F4{50, 60, 70, 80}), 1e-4); + EXPECT_ARRAY_NEAR(g, (F4{54, 64, 74, 84}), 1e-4); + EXPECT_ARRAY_NEAR(b, (F4{57, 67, 77, 87}), 1e-4); } } // namespace diff --git a/third_party/jpeg-xl/lib/jxl/ans_common.h b/third_party/jpeg-xl/lib/jxl/ans_common.h index fb5058e310..44b8e3fba1 100644 --- a/third_party/jpeg-xl/lib/jxl/ans_common.h +++ b/third_party/jpeg-xl/lib/jxl/ans_common.h @@ -24,7 +24,8 @@ namespace jxl { static JXL_INLINE uint32_t GetPopulationCountPrecision(uint32_t logcount, uint32_t shift) { int32_t r = std::min<int>( - logcount, int(shift) - int((ANS_LOG_TAB_SIZE - logcount) >> 1)); + logcount, static_cast<int>(shift) - + static_cast<int>((ANS_LOG_TAB_SIZE - logcount) >> 1)); if (r < 0) return 0; return r; } diff --git a/third_party/jpeg-xl/lib/jxl/ans_test.cc b/third_party/jpeg-xl/lib/jxl/ans_test.cc index c28daf7b85..5d6a5ef090 100644 --- a/third_party/jpeg-xl/lib/jxl/ans_test.cc +++ b/third_party/jpeg-xl/lib/jxl/ans_test.cc @@ -113,8 +113,8 @@ void RoundtripRandomUnbalancedStream(int alphabet_size) { Rng rng(0); for (size_t i = 0; i < kReps; i++) { std::vector<int> distributions[kNumHistograms] = {}; - for (int j = 0; j < kNumHistograms; j++) { - distributions[j].resize(kPrecision); + for (auto& distr : distributions) { + distr.resize(kPrecision); int symbol = 0; int remaining = 1; for (int k = 0; k < kPrecision; k++) { @@ -126,7 +126,7 @@ void RoundtripRandomUnbalancedStream(int alphabet_size) { // sufficiently dissimilar. remaining = rng.UniformU(0, kPrecision - k + 1); } - distributions[j][k] = symbol; + distr[k] = symbol; remaining--; } } @@ -158,7 +158,8 @@ TEST(ANSTest, RandomUnbalancedStreamRoundtripBig) { TEST(ANSTest, UintConfigRoundtrip) { for (size_t log_alpha_size = 5; log_alpha_size <= 8; log_alpha_size++) { - std::vector<HybridUintConfig> uint_config, uint_config_dec; + std::vector<HybridUintConfig> uint_config; + std::vector<HybridUintConfig> uint_config_dec; for (size_t i = 0; i < log_alpha_size; i++) { for (size_t j = 0; j <= i; j++) { for (size_t k = 0; k <= i - j; k++) { @@ -187,16 +188,16 @@ TEST(ANSTest, UintConfigRoundtrip) { void TestCheckpointing(bool ans, bool lz77) { std::vector<std::vector<Token>> input_values(1); for (size_t i = 0; i < 1024; i++) { - input_values[0].push_back(Token(0, i % 4)); + input_values[0].emplace_back(0, i % 4); } // up to lz77 window size. for (size_t i = 0; i < (1 << 20) - 1022; i++) { - input_values[0].push_back(Token(0, (i % 5) + 4)); + input_values[0].emplace_back(0, (i % 5) + 4); } // Ensure that when the window wraps around, new values are different. - input_values[0].push_back(Token(0, 0)); + input_values[0].emplace_back(0, 0); for (size_t i = 0; i < 1024; i++) { - input_values[0].push_back(Token(0, i % 4)); + input_values[0].emplace_back(0, i % 4); } std::vector<uint8_t> context_map; diff --git a/third_party/jpeg-xl/lib/jxl/base/bits.h b/third_party/jpeg-xl/lib/jxl/base/bits.h index 9f86118e72..a79fdc2c99 100644 --- a/third_party/jpeg-xl/lib/jxl/base/bits.h +++ b/third_party/jpeg-xl/lib/jxl/base/bits.h @@ -26,7 +26,8 @@ struct SizeTag {}; template <typename T> constexpr bool IsSigned() { - return T(0) > T(-1); + // TODO(eustas): remove dupes + return static_cast<T>(0) > static_cast<T>(-1); } // Undefined results for x == 0. diff --git a/third_party/jpeg-xl/lib/jxl/base/byte_order.h b/third_party/jpeg-xl/lib/jxl/base/byte_order.h index 8966834e08..cf8d7db082 100644 --- a/third_party/jpeg-xl/lib/jxl/base/byte_order.h +++ b/third_party/jpeg-xl/lib/jxl/base/byte_order.h @@ -237,22 +237,22 @@ struct OrderLE {}; // Wrappers for calling from generic code. static JXL_INLINE void Store16(OrderBE /*tag*/, const uint32_t native, uint8_t* p) { - return StoreBE16(native, p); + StoreBE16(native, p); } static JXL_INLINE void Store16(OrderLE /*tag*/, const uint32_t native, uint8_t* p) { - return StoreLE16(native, p); + StoreLE16(native, p); } static JXL_INLINE void Store32(OrderBE /*tag*/, const uint32_t native, uint8_t* p) { - return StoreBE32(native, p); + StoreBE32(native, p); } static JXL_INLINE void Store32(OrderLE /*tag*/, const uint32_t native, uint8_t* p) { - return StoreLE32(native, p); + StoreLE32(native, p); } static JXL_INLINE uint32_t Load16(OrderBE /*tag*/, const uint8_t* p) { diff --git a/third_party/jpeg-xl/lib/jxl/base/common.h b/third_party/jpeg-xl/lib/jxl/base/common.h index b7fe6ab0bc..0893ef26b5 100644 --- a/third_party/jpeg-xl/lib/jxl/base/common.h +++ b/third_party/jpeg-xl/lib/jxl/base/common.h @@ -8,11 +8,13 @@ // Shared constants and helper functions. +#include <array> #include <cstddef> #include <cstdint> #include <cstdio> #include <memory> #include <string> +#include <type_traits> #include "lib/jxl/base/compiler_specific.h" @@ -22,11 +24,11 @@ namespace jxl { constexpr size_t kBitsPerByte = 8; // more clear than CHAR_BIT constexpr inline size_t RoundUpBitsToByteMultiple(size_t bits) { - return (bits + 7) & ~size_t(7); + return (bits + 7) & ~static_cast<size_t>(7); } constexpr inline size_t RoundUpToBlockDim(size_t dim) { - return (dim + 7) & ~size_t(7); + return (dim + 7) & ~static_cast<size_t>(7); } static inline bool JXL_MAYBE_UNUSED SafeAdd(const uint64_t a, const uint64_t b, @@ -68,6 +70,37 @@ std::unique_ptr<T> make_unique(Args&&... args) { using std::make_unique; #endif +typedef std::array<float, 3> Color; + +// Backported std::experimental::to_array + +template <typename T> +using remove_cv_t = typename std::remove_cv<T>::type; + +template <size_t... I> +struct index_sequence {}; + +template <size_t N, size_t... I> +struct make_index_sequence : make_index_sequence<N - 1, N - 1, I...> {}; + +template <size_t... I> +struct make_index_sequence<0, I...> : index_sequence<I...> {}; + +namespace detail { + +template <typename T, size_t N, size_t... I> +constexpr auto to_array(T (&&arr)[N], index_sequence<I...> _) + -> std::array<remove_cv_t<T>, N> { + return {{std::move(arr[I])...}}; +} + +} // namespace detail + +template <typename T, size_t N> +constexpr auto to_array(T (&&arr)[N]) -> std::array<remove_cv_t<T>, N> { + return detail::to_array(std::move(arr), make_index_sequence<N>()); +} + template <typename T> JXL_INLINE T Clamp1(T val, T low, T hi) { return val < low ? low : val > hi ? hi : val; @@ -77,10 +110,10 @@ JXL_INLINE T Clamp1(T val, T low, T hi) { template <typename T> std::string ToString(T n) { char data[32] = {}; - if (T(0.1) != T(0)) { + if (std::is_floating_point<T>::value) { // float snprintf(data, sizeof(data), "%g", static_cast<double>(n)); - } else if (T(-1) > T(0)) { + } else if (std::is_unsigned<T>::value) { // unsigned snprintf(data, sizeof(data), "%llu", static_cast<unsigned long long>(n)); } else { @@ -90,6 +123,9 @@ std::string ToString(T n) { return data; } +#define JXL_JOIN(x, y) JXL_DO_JOIN(x, y) +#define JXL_DO_JOIN(x, y) x##y + } // namespace jxl #endif // LIB_JXL_BASE_COMMON_H_ diff --git a/third_party/jpeg-xl/lib/jxl/base/exif.h b/third_party/jpeg-xl/lib/jxl/base/exif.h index 2caafddc04..a3574a16ff 100644 --- a/third_party/jpeg-xl/lib/jxl/base/exif.h +++ b/third_party/jpeg-xl/lib/jxl/base/exif.h @@ -79,7 +79,6 @@ JXL_INLINE void InterpretExif(const std::vector<uint8_t>& exif, uint32_t count = (bigendian ? LoadBE32(t) : LoadLE32(t)); t += 4; uint16_t value = (bigendian ? LoadBE16(t) : LoadLE16(t)); - t += 4; if (type == 3 && count == 1 && value >= 1 && value <= 8) { *orientation = static_cast<JxlOrientation>(value); } diff --git a/third_party/jpeg-xl/lib/jxl/base/float.h b/third_party/jpeg-xl/lib/jxl/base/float.h index 00e112bb34..0f5b3b1f3a 100644 --- a/third_party/jpeg-xl/lib/jxl/base/float.h +++ b/third_party/jpeg-xl/lib/jxl/base/float.h @@ -17,9 +17,9 @@ namespace jxl { -namespace { +namespace detail { // Based on highway scalar implementation, for testing -float LoadFloat16(uint16_t bits16) { +static JXL_INLINE float LoadFloat16(uint16_t bits16) { const uint32_t sign = bits16 >> 15; const uint32_t biased_exp = (bits16 >> 10) & 0x1F; const uint32_t mantissa = bits16 & 0x3FF; @@ -40,7 +40,7 @@ float LoadFloat16(uint16_t bits16) { memcpy(&result, &bits32, 4); return result; } -} // namespace +} // namespace detail template <typename SaveFloatAtFn> static Status JXL_INLINE LoadFloatRow(const uint8_t* src, size_t count, @@ -83,11 +83,11 @@ static Status JXL_INLINE LoadFloatRow(const uint8_t* src, size_t count, case JXL_TYPE_FLOAT16: if (little_endian) { for (size_t i = 0; i < count; ++i) { - callback(i, LoadFloat16(LoadLE16(src + stride * i))); + callback(i, detail::LoadFloat16(LoadLE16(src + stride * i))); } } else { for (size_t i = 0; i < count; ++i) { - callback(i, LoadFloat16(LoadBE16(src + stride * i))); + callback(i, detail::LoadFloat16(LoadBE16(src + stride * i))); } } return true; diff --git a/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h b/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h index 1a969bd4f0..cde6a64b1e 100644 --- a/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h +++ b/third_party/jpeg-xl/lib/jxl/base/matrix_ops.h @@ -8,6 +8,7 @@ // 3x3 matrix operations. +#include <array> #include <cmath> // abs #include <cstddef> @@ -15,66 +16,67 @@ namespace jxl { +typedef std::array<float, 3> Vector3; +typedef std::array<double, 3> Vector3d; +typedef std::array<Vector3, 3> Matrix3x3; +typedef std::array<Vector3d, 3> Matrix3x3d; + // Computes C = A * B, where A, B, C are 3x3 matrices. -template <typename T> -void Mul3x3Matrix(const T* a, const T* b, T* c) { - alignas(16) T temp[3]; // For transposed column +template <typename Matrix> +void Mul3x3Matrix(const Matrix& a, const Matrix& b, Matrix& c) { for (size_t x = 0; x < 3; x++) { - for (size_t z = 0; z < 3; z++) { - temp[z] = b[z * 3 + x]; - } + alignas(16) Vector3d temp{b[0][x], b[1][x], b[2][x]}; // transpose for (size_t y = 0; y < 3; y++) { - double e = 0; - for (size_t z = 0; z < 3; z++) { - e += a[y * 3 + z] * temp[z]; - } - c[y * 3 + x] = e; + c[y][x] = a[y][0] * temp[0] + a[y][1] * temp[1] + a[y][2] * temp[2]; } } } // Computes C = A * B, where A is 3x3 matrix and B is vector. -template <typename T> -void Mul3x3Vector(const T* a, const T* b, T* c) { +template <typename Matrix, typename Vector> +void Mul3x3Vector(const Matrix& a, const Vector& b, Vector& c) { for (size_t y = 0; y < 3; y++) { double e = 0; for (size_t x = 0; x < 3; x++) { - e += a[y * 3 + x] * b[x]; + e += a[y][x] * b[x]; } c[y] = e; } } // Inverts a 3x3 matrix in place. -template <typename T> -Status Inv3x3Matrix(T* matrix) { +template <typename Matrix> +Status Inv3x3Matrix(Matrix& matrix) { // Intermediate computation is done in double precision. - double temp[9]; - temp[0] = static_cast<double>(matrix[4]) * matrix[8] - - static_cast<double>(matrix[5]) * matrix[7]; - temp[1] = static_cast<double>(matrix[2]) * matrix[7] - - static_cast<double>(matrix[1]) * matrix[8]; - temp[2] = static_cast<double>(matrix[1]) * matrix[5] - - static_cast<double>(matrix[2]) * matrix[4]; - temp[3] = static_cast<double>(matrix[5]) * matrix[6] - - static_cast<double>(matrix[3]) * matrix[8]; - temp[4] = static_cast<double>(matrix[0]) * matrix[8] - - static_cast<double>(matrix[2]) * matrix[6]; - temp[5] = static_cast<double>(matrix[2]) * matrix[3] - - static_cast<double>(matrix[0]) * matrix[5]; - temp[6] = static_cast<double>(matrix[3]) * matrix[7] - - static_cast<double>(matrix[4]) * matrix[6]; - temp[7] = static_cast<double>(matrix[1]) * matrix[6] - - static_cast<double>(matrix[0]) * matrix[7]; - temp[8] = static_cast<double>(matrix[0]) * matrix[4] - - static_cast<double>(matrix[1]) * matrix[3]; - double det = matrix[0] * temp[0] + matrix[1] * temp[3] + matrix[2] * temp[6]; + Matrix3x3d temp; + temp[0][0] = static_cast<double>(matrix[1][1]) * matrix[2][2] - + static_cast<double>(matrix[1][2]) * matrix[2][1]; + temp[0][1] = static_cast<double>(matrix[0][2]) * matrix[2][1] - + static_cast<double>(matrix[0][1]) * matrix[2][2]; + temp[0][2] = static_cast<double>(matrix[0][1]) * matrix[1][2] - + static_cast<double>(matrix[0][2]) * matrix[1][1]; + temp[1][0] = static_cast<double>(matrix[1][2]) * matrix[2][0] - + static_cast<double>(matrix[1][0]) * matrix[2][2]; + temp[1][1] = static_cast<double>(matrix[0][0]) * matrix[2][2] - + static_cast<double>(matrix[0][2]) * matrix[2][0]; + temp[1][2] = static_cast<double>(matrix[0][2]) * matrix[1][0] - + static_cast<double>(matrix[0][0]) * matrix[1][2]; + temp[2][0] = static_cast<double>(matrix[1][0]) * matrix[2][1] - + static_cast<double>(matrix[1][1]) * matrix[2][0]; + temp[2][1] = static_cast<double>(matrix[0][1]) * matrix[2][0] - + static_cast<double>(matrix[0][0]) * matrix[2][1]; + temp[2][2] = static_cast<double>(matrix[0][0]) * matrix[1][1] - + static_cast<double>(matrix[0][1]) * matrix[1][0]; + double det = matrix[0][0] * temp[0][0] + matrix[0][1] * temp[1][0] + + matrix[0][2] * temp[2][0]; if (std::abs(det) < 1e-10) { return JXL_FAILURE("Matrix determinant is too close to 0"); } double idet = 1.0 / det; - for (size_t i = 0; i < 9; i++) { - matrix[i] = temp[i] * idet; + for (size_t j = 0; j < 3; j++) { + for (size_t i = 0; i < 3; i++) { + matrix[j][i] = temp[j][i] * idet; + } } return true; } diff --git a/third_party/jpeg-xl/lib/jxl/base/override.h b/third_party/jpeg-xl/lib/jxl/base/override.h index 1f8b657974..da070e6e62 100644 --- a/third_party/jpeg-xl/lib/jxl/base/override.h +++ b/third_party/jpeg-xl/lib/jxl/base/override.h @@ -6,13 +6,15 @@ #ifndef LIB_JXL_BASE_OVERRIDE_H_ #define LIB_JXL_BASE_OVERRIDE_H_ +#include <cstdint> + // 'Trool' for command line arguments: force enable/disable, or use default. namespace jxl { // No effect if kDefault, otherwise forces a feature (typically a FrameHeader // flag) on or off. -enum class Override : int { kOn = 1, kOff = 0, kDefault = -1 }; +enum class Override : int8_t { kOn = 1, kOff = 0, kDefault = -1 }; static inline Override OverrideFromBool(bool flag) { return flag ? Override::kOn : Override::kOff; diff --git a/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h b/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h index e073937675..7a89c0bac1 100644 --- a/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h +++ b/third_party/jpeg-xl/lib/jxl/base/rational_polynomial-inl.h @@ -13,6 +13,7 @@ #define LIB_JXL_BASE_RATIONAL_POLYNOMIAL_INL_H_ #endif +#include <jxl/types.h> #include <stddef.h> #include <hwy/highway.h> @@ -42,7 +43,7 @@ struct FastDivision<float, V> { } V operator()(const V n, const V d) const { -#if 1 // Faster on SKX +#if JXL_TRUE // Faster on SKX return Div(n, d); #else return n * ReciprocalNR(d); diff --git a/third_party/jpeg-xl/lib/jxl/base/scope_guard.h b/third_party/jpeg-xl/lib/jxl/base/scope_guard.h index a18a44cb79..f060c5bc1d 100644 --- a/third_party/jpeg-xl/lib/jxl/base/scope_guard.h +++ b/third_party/jpeg-xl/lib/jxl/base/scope_guard.h @@ -24,8 +24,8 @@ class ScopeGuard { } template <typename CallbackParam> - explicit ScopeGuard(CallbackParam &&callback) - : callback_(std::forward<CallbackParam>(callback)), armed_(true) {} + ScopeGuard(CallbackParam &&callback, bool armed) + : callback_(std::forward<CallbackParam>(callback)), armed_(armed) {} ~ScopeGuard() { if (armed_) callback_(); @@ -40,7 +40,7 @@ class ScopeGuard { template <typename Callback> ScopeGuard<Callback> MakeScopeGuard(Callback &&callback) { - return ScopeGuard<Callback>{std::forward<Callback>(callback)}; + return ScopeGuard<Callback>{std::forward<Callback>(callback), true}; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/base/span.h b/third_party/jpeg-xl/lib/jxl/base/span.h index dc1c781b9d..ba09d62316 100644 --- a/third_party/jpeg-xl/lib/jxl/base/span.h +++ b/third_party/jpeg-xl/lib/jxl/base/span.h @@ -64,8 +64,8 @@ class Span { // NCT == non-const-T; compiler will complain if NCT is not compatible with T. template <typename NCT> - void AppendTo(std::vector<NCT>* dst) const { - dst->insert(dst->end(), begin(), end()); + void AppendTo(std::vector<NCT>& dst) const { + dst.insert(dst.end(), begin(), end()); } private: diff --git a/third_party/jpeg-xl/lib/jxl/base/status.h b/third_party/jpeg-xl/lib/jxl/base/status.h index b33bd64fc3..2e88ba68ae 100644 --- a/third_party/jpeg-xl/lib/jxl/base/status.h +++ b/third_party/jpeg-xl/lib/jxl/base/status.h @@ -16,6 +16,7 @@ #include <type_traits> #include <utility> +#include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/sanitizer_definitions.h" @@ -442,7 +443,7 @@ class JXL_MUST_USE_RESULT StatusOr { #define JXL_ASSIGN_OR_RETURN(lhs, statusor) \ PRIVATE_JXL_ASSIGN_OR_RETURN_IMPL( \ - assign_or_return_temporary_variable##__LINE__, lhs, statusor) + JXL_JOIN(assign_or_return_temporary_variable, __LINE__), lhs, statusor) // NOLINTBEGIN(bugprone-macro-parentheses) #define PRIVATE_JXL_ASSIGN_OR_RETURN_IMPL(name, lhs, statusor) \ @@ -451,6 +452,18 @@ class JXL_MUST_USE_RESULT StatusOr { lhs = std::move(name).value(); // NOLINTEND(bugprone-macro-parentheses) +// NB: do not use outside of tests / tools!!! +#define JXL_ASSIGN_OR_DIE(lhs, statusor) \ + PRIVATE_JXL_ASSIGN_OR_DIE_IMPL( \ + JXL_JOIN(assign_or_die_temporary_variable, __LINE__), lhs, statusor) + +// NOLINTBEGIN(bugprone-macro-parentheses) +#define PRIVATE_JXL_ASSIGN_OR_DIE_IMPL(name, lhs, statusor) \ + auto name = statusor; \ + if (!name.ok()) jxl::Abort(); \ + lhs = std::move(name).value(); +// NOLINTEND(bugprone-macro-parentheses) + } // namespace jxl #endif // LIB_JXL_BASE_STATUS_H_ diff --git a/third_party/jpeg-xl/lib/jxl/bits_test.cc b/third_party/jpeg-xl/lib/jxl/bits_test.cc index bd7aa548c8..45db8c0d6e 100644 --- a/third_party/jpeg-xl/lib/jxl/bits_test.cc +++ b/third_party/jpeg-xl/lib/jxl/bits_test.cc @@ -38,7 +38,8 @@ TEST(BitsTest, TestFloorLog2) { const size_t expected[7] = {0, 1, 1, 2, 2, 2, 2}; for (uint32_t i = 1; i <= 7; ++i) { EXPECT_EQ(expected[i - 1], FloorLog2Nonzero(i)) << " " << i; - EXPECT_EQ(expected[i - 1], FloorLog2Nonzero(uint64_t(i))) << " " << i; + EXPECT_EQ(expected[i - 1], FloorLog2Nonzero(static_cast<uint64_t>(i))) + << " " << i; } EXPECT_EQ(11u, FloorLog2Nonzero(0x00000fffu)); // 4095 @@ -63,7 +64,8 @@ TEST(BitsTest, TestCeilLog2) { const size_t expected[7] = {0, 1, 2, 2, 3, 3, 3}; for (uint32_t i = 1; i <= 7; ++i) { EXPECT_EQ(expected[i - 1], CeilLog2Nonzero(i)) << " " << i; - EXPECT_EQ(expected[i - 1], CeilLog2Nonzero(uint64_t(i))) << " " << i; + EXPECT_EQ(expected[i - 1], CeilLog2Nonzero(static_cast<uint64_t>(i))) + << " " << i; } EXPECT_EQ(12u, CeilLog2Nonzero(0x00000fffu)); // 4095 diff --git a/third_party/jpeg-xl/lib/jxl/blending.cc b/third_party/jpeg-xl/lib/jxl/blending.cc index ccb168ee45..7575ec6e4a 100644 --- a/third_party/jpeg-xl/lib/jxl/blending.cc +++ b/third_party/jpeg-xl/lib/jxl/blending.cc @@ -6,7 +6,6 @@ #include "lib/jxl/blending.h" #include "lib/jxl/alpha.h" -#include "lib/jxl/image_ops.h" namespace jxl { @@ -29,11 +28,11 @@ bool NeedsBlending(const FrameHeader& frame_header) { return true; } -void PerformBlending(const float* const* bg, const float* const* fg, - float* const* out, size_t x0, size_t xsize, - const PatchBlending& color_blending, - const PatchBlending* ec_blending, - const std::vector<ExtraChannelInfo>& extra_channel_info) { +Status PerformBlending( + const float* const* bg, const float* const* fg, float* const* out, + size_t x0, size_t xsize, const PatchBlending& color_blending, + const PatchBlending* ec_blending, + const std::vector<ExtraChannelInfo>& extra_channel_info) { bool has_alpha = false; size_t num_ec = extra_channel_info.size(); for (size_t i = 0; i < num_ec; i++) { @@ -42,7 +41,7 @@ void PerformBlending(const float* const* bg, const float* const* fg, break; } } - ImageF tmp(xsize, 3 + num_ec); + JXL_ASSIGN_OR_RETURN(ImageF tmp, ImageF::Create(xsize, 3 + num_ec)); // Blend extra channels first so that we use the pre-blending alpha. for (size_t i = 0; i < num_ec; i++) { if (ec_blending[i].mode == PatchBlendMode::kAdd) { @@ -146,6 +145,7 @@ void PerformBlending(const float* const* bg, const float* const* fg, for (size_t i = 0; i < 3 + num_ec; i++) { if (xsize != 0) memcpy(out[i] + x0, tmp.Row(i), xsize * sizeof(**out)); } + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/blending.h b/third_party/jpeg-xl/lib/jxl/blending.h index 3f23297f1d..94bead8dd6 100644 --- a/third_party/jpeg-xl/lib/jxl/blending.h +++ b/third_party/jpeg-xl/lib/jxl/blending.h @@ -16,11 +16,11 @@ namespace jxl { bool NeedsBlending(const FrameHeader& frame_header); -void PerformBlending(const float* const* bg, const float* const* fg, - float* const* out, size_t x0, size_t xsize, - const PatchBlending& color_blending, - const PatchBlending* ec_blending, - const std::vector<ExtraChannelInfo>& extra_channel_info); +Status PerformBlending(const float* const* bg, const float* const* fg, + float* const* out, size_t x0, size_t xsize, + const PatchBlending& color_blending, + const PatchBlending* ec_blending, + const std::vector<ExtraChannelInfo>& extra_channel_info); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/blending_test.cc b/third_party/jpeg-xl/lib/jxl/blending_test.cc index c34ab5c7ca..c363358592 100644 --- a/third_party/jpeg-xl/lib/jxl/blending_test.cc +++ b/third_party/jpeg-xl/lib/jxl/blending_test.cc @@ -20,8 +20,6 @@ namespace jxl { namespace { -using ::testing::SizeIs; - TEST(BlendingTest, Crops) { const std::vector<uint8_t> compressed = jxl::test::ReadTestData("jxl/blending/cropped_traffic_light.jxl"); @@ -30,7 +28,7 @@ TEST(BlendingTest, Crops) { extras::PackedPixelFile decoded; ASSERT_TRUE(DecodeImageJXL(compressed.data(), compressed.size(), dparams, /*decoded_bytes=*/nullptr, &decoded)); - ASSERT_THAT(decoded.frames, SizeIs(4)); + ASSERT_EQ(decoded.frames.size(), 4); int i = 0; for (auto&& decoded_frame : decoded.frames) { @@ -40,8 +38,10 @@ TEST(BlendingTest, Crops) { jxl::test::ReadTestData(filename.str()); extras::PackedPixelFile decoded_frame_ppf; decoded_frame_ppf.info = decoded.info; - decoded_frame_ppf.icc = decoded.icc; + decoded_frame_ppf.primary_color_representation = + decoded.primary_color_representation; decoded_frame_ppf.color_encoding = decoded.color_encoding; + decoded_frame_ppf.icc = decoded.icc; decoded_frame_ppf.extra_channels_info = decoded.extra_channels_info; decoded_frame_ppf.frames.emplace_back(std::move(decoded_frame)); extras::PackedPixelFile expected_frame_ppf; diff --git a/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc b/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc index c4cba3a31a..c7d87f4ca0 100644 --- a/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc +++ b/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc @@ -9,7 +9,7 @@ namespace jxl { -JxlBoxContentDecoder::JxlBoxContentDecoder() {} +JxlBoxContentDecoder::JxlBoxContentDecoder() = default; JxlBoxContentDecoder::~JxlBoxContentDecoder() { if (brotli_dec) { diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc index 66dde9afb1..2e06d79eba 100644 --- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc +++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc @@ -28,11 +28,12 @@ #include <string.h> #include <algorithm> -#include <array> #include <cmath> -#include <new> +#include <memory> #include <vector> +#include "lib/jxl/image.h" + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/butteraugli/butteraugli.cc" #include <hwy/foreach_target.h> @@ -222,8 +223,8 @@ void ConvolutionWithTranspose(const ImageF& in, // We retain a special case for 5x5 kernels (even faster than gauss_blur), // optionally use gauss_blur followed by fixup of the borders for large images, // or fall back to the previous truncated FIR followed by a transpose. -void Blur(const ImageF& in, float sigma, const ButteraugliParams& params, - BlurTemp* temp, ImageF* out) { +Status Blur(const ImageF& in, float sigma, const ButteraugliParams& params, + BlurTemp* temp, ImageF* out) { std::vector<float> kernel = ComputeKernel(sigma); // Separable5 does an in-place convolution, so this fast path is not safe if // in aliases out. @@ -241,12 +242,14 @@ void Blur(const ImageF& in, float sigma, const ButteraugliParams& params, {HWY_REP4(w0), HWY_REP4(w1), HWY_REP4(w2)}, }; Separable5(in, Rect(in), weights, /*pool=*/nullptr, out); - return; + return true; } - ImageF* JXL_RESTRICT temp_t = temp->GetTransposed(in); + ImageF* temp_t; + JXL_RETURN_IF_ERROR(temp->GetTransposed(in, &temp_t)); ConvolutionWithTranspose(in, kernel, temp_t); ConvolutionWithTranspose(*temp_t, kernel, out); + return true; } // Allows PaddedMaltaUnit to call either function via overloading. @@ -386,29 +389,32 @@ void Subtract(const ImageF& a, const ImageF& b, ImageF* c) { } } -void SeparateLFAndMF(const ButteraugliParams& params, const Image3F& xyb, - Image3F* lf, Image3F* mf, BlurTemp* blur_temp) { +Status SeparateLFAndMF(const ButteraugliParams& params, const Image3F& xyb, + Image3F* lf, Image3F* mf, BlurTemp* blur_temp) { static const double kSigmaLf = 7.15593339443; for (int i = 0; i < 3; ++i) { // Extract lf ... - Blur(xyb.Plane(i), kSigmaLf, params, blur_temp, &lf->Plane(i)); + JXL_RETURN_IF_ERROR( + Blur(xyb.Plane(i), kSigmaLf, params, blur_temp, &lf->Plane(i))); // ... and keep everything else in mf. Subtract(xyb.Plane(i), lf->Plane(i), &mf->Plane(i)); } XybLowFreqToVals(lf); + return true; } -void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf, - BlurTemp* blur_temp) { +Status SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf, + BlurTemp* blur_temp) { const HWY_FULL(float) d; static const double kSigmaHf = 3.22489901262; const size_t xsize = mf->xsize(); const size_t ysize = mf->ysize(); - hf[0] = ImageF(xsize, ysize); - hf[1] = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(hf[0], ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(hf[1], ImageF::Create(xsize, ysize)); for (int i = 0; i < 3; ++i) { if (i == 2) { - Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i)); + JXL_RETURN_IF_ERROR( + Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i))); break; } for (size_t y = 0; y < ysize; ++y) { @@ -418,7 +424,8 @@ void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf, Store(Load(d, row_mf + x), d, row_hf + x); } } - Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i)); + JXL_RETURN_IF_ERROR( + Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i))); static const double kRemoveMfRange = 0.29; static const double kAddMfRange = 0.1; if (i == 0) { @@ -450,16 +457,17 @@ void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf, } // Suppress red-green by intensity change in the high freq channels. SuppressXByY(hf[1], &hf[0]); + return true; } -void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf, - BlurTemp* blur_temp) { +Status SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, + ImageF* uhf, BlurTemp* blur_temp) { const HWY_FULL(float) d; const size_t xsize = hf[0].xsize(); const size_t ysize = hf[0].ysize(); static const double kSigmaUhf = 1.56416327805; - uhf[0] = ImageF(xsize, ysize); - uhf[1] = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(uhf[0], ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(uhf[1], ImageF::Create(xsize, ysize)); for (int i = 0; i < 2; ++i) { // Divide hf into hf and uhf. for (size_t y = 0; y < ysize; ++y) { @@ -469,7 +477,7 @@ void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf, row_uhf[x] = row_hf[x]; } } - Blur(hf[i], kSigmaUhf, params, blur_temp, &hf[i]); + JXL_RETURN_IF_ERROR(Blur(hf[i], kSigmaUhf, params, blur_temp, &hf[i])); static const double kRemoveHfRange = 1.5; static const double kAddHfRange = 0.132; static const double kRemoveUhfRange = 0.04; @@ -510,6 +518,7 @@ void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf, } } } + return true; } void DeallocateHFAndUHF(ImageF* hf, ImageF* uhf) { @@ -519,15 +528,16 @@ void DeallocateHFAndUHF(ImageF* hf, ImageF* uhf) { } } -static void SeparateFrequencies(size_t xsize, size_t ysize, - const ButteraugliParams& params, - BlurTemp* blur_temp, const Image3F& xyb, - PsychoImage& ps) { - ps.lf = Image3F(xyb.xsize(), xyb.ysize()); - ps.mf = Image3F(xyb.xsize(), xyb.ysize()); - SeparateLFAndMF(params, xyb, &ps.lf, &ps.mf, blur_temp); - SeparateMFAndHF(params, &ps.mf, &ps.hf[0], blur_temp); - SeparateHFAndUHF(params, &ps.hf[0], &ps.uhf[0], blur_temp); +Status SeparateFrequencies(size_t xsize, size_t ysize, + const ButteraugliParams& params, BlurTemp* blur_temp, + const Image3F& xyb, PsychoImage& ps) { + JXL_ASSIGN_OR_RETURN(ps.lf, Image3F::Create(xyb.xsize(), xyb.ysize())); + JXL_ASSIGN_OR_RETURN(ps.mf, Image3F::Create(xyb.xsize(), xyb.ysize())); + JXL_RETURN_IF_ERROR(SeparateLFAndMF(params, xyb, &ps.lf, &ps.mf, blur_temp)); + JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &ps.mf, &ps.hf[0], blur_temp)); + JXL_RETURN_IF_ERROR( + SeparateHFAndUHF(params, &ps.hf[0], &ps.uhf[0], blur_temp)); + return true; } namespace { @@ -1024,7 +1034,7 @@ static void MaltaDiffMapT(const Tag tag, const ImageF& lum0, const ImageF& lum1, } const HWY_FULL(float) df; - const size_t aligned_x = std::max(size_t(4), Lanes(df)); + const size_t aligned_x = std::max(static_cast<size_t>(4), Lanes(df)); const intptr_t stride = diffs->PixelsPerRow(); // Middle @@ -1097,7 +1107,7 @@ void CombineChannelsForMasking(const ImageF* hf, const ImageF* uhf, float xdiff = (row_x_uhf[x] + row_x_hf[x]) * muls[0]; float ydiff = row_y_uhf[x] * muls[1] + row_y_hf[x] * muls[2]; row[x] = xdiff * xdiff + ydiff * ydiff; - row[x] = sqrt(row[x]); + row[x] = std::sqrt(row[x]); } } } @@ -1106,13 +1116,13 @@ void DiffPrecompute(const ImageF& xyb, float mul, float bias_arg, ImageF* out) { const size_t xsize = xyb.xsize(); const size_t ysize = xyb.ysize(); const float bias = mul * bias_arg; - const float sqrt_bias = sqrt(bias); + const float sqrt_bias = std::sqrt(bias); for (size_t y = 0; y < ysize; ++y) { const float* BUTTERAUGLI_RESTRICT row_in = xyb.Row(y); float* BUTTERAUGLI_RESTRICT row_out = out->Row(y); for (size_t x = 0; x < xsize; ++x) { // kBias makes sqrt behave more linearly. - row_out[x] = sqrt(mul * std::abs(row_in[x]) + bias) - sqrt_bias; + row_out[x] = std::sqrt(mul * std::abs(row_in[x]) + bias) - sqrt_bias; } } } @@ -1188,25 +1198,25 @@ void FuzzyErosion(const ImageF& from, ImageF* to) { // Compute values of local frequency and dc masking based on the activity // in the two images. img_diff_ac may be null. -void Mask(const ImageF& mask0, const ImageF& mask1, - const ButteraugliParams& params, BlurTemp* blur_temp, - ImageF* BUTTERAUGLI_RESTRICT mask, - ImageF* BUTTERAUGLI_RESTRICT diff_ac) { +Status Mask(const ImageF& mask0, const ImageF& mask1, + const ButteraugliParams& params, BlurTemp* blur_temp, + ImageF* BUTTERAUGLI_RESTRICT mask, + ImageF* BUTTERAUGLI_RESTRICT diff_ac) { const size_t xsize = mask0.xsize(); const size_t ysize = mask0.ysize(); - *mask = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(*mask, ImageF::Create(xsize, ysize)); static const float kMul = 6.19424080439; static const float kBias = 12.61050594197; static const float kRadius = 2.7; - ImageF diff0(xsize, ysize); - ImageF diff1(xsize, ysize); - ImageF blurred0(xsize, ysize); - ImageF blurred1(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF diff0, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF diff1, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF blurred0, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF blurred1, ImageF::Create(xsize, ysize)); DiffPrecompute(mask0, kMul, kBias, &diff0); DiffPrecompute(mask1, kMul, kBias, &diff1); - Blur(diff0, kRadius, params, blur_temp, &blurred0); + JXL_RETURN_IF_ERROR(Blur(diff0, kRadius, params, blur_temp, &blurred0)); FuzzyErosion(blurred0, &diff0); - Blur(diff1, kRadius, params, blur_temp, &blurred1); + JXL_RETURN_IF_ERROR(Blur(diff1, kRadius, params, blur_temp, &blurred1)); for (size_t y = 0; y < ysize; ++y) { for (size_t x = 0; x < xsize; ++x) { mask->Row(y)[x] = diff0.Row(y)[x]; @@ -1217,19 +1227,21 @@ void Mask(const ImageF& mask0, const ImageF& mask1, } } } + return true; } // `diff_ac` may be null. -void MaskPsychoImage(const PsychoImage& pi0, const PsychoImage& pi1, - const size_t xsize, const size_t ysize, - const ButteraugliParams& params, BlurTemp* blur_temp, - ImageF* BUTTERAUGLI_RESTRICT mask, - ImageF* BUTTERAUGLI_RESTRICT diff_ac) { - ImageF mask0(xsize, ysize); - ImageF mask1(xsize, ysize); +Status MaskPsychoImage(const PsychoImage& pi0, const PsychoImage& pi1, + const size_t xsize, const size_t ysize, + const ButteraugliParams& params, BlurTemp* blur_temp, + ImageF* BUTTERAUGLI_RESTRICT mask, + ImageF* BUTTERAUGLI_RESTRICT diff_ac) { + JXL_ASSIGN_OR_RETURN(ImageF mask0, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF mask1, ImageF::Create(xsize, ysize)); CombineChannelsForMasking(&pi0.hf[0], &pi0.uhf[0], &mask0); CombineChannelsForMasking(&pi1.hf[0], &pi1.uhf[0], &mask1); - Mask(mask0, mask1, params, blur_temp, mask, diff_ac); + JXL_RETURN_IF_ERROR(Mask(mask0, mask1, params, blur_temp, mask, diff_ac)); + return true; } double MaskY(double delta) { @@ -1275,8 +1287,8 @@ void CombineChannelsToDiffmap(const ImageF& mask, const Image3F& block_diff_dc, } diff_ac[0] *= xmul; diff_dc[0] *= xmul; - row_out[x] = - sqrt(MaskColor(diff_dc, dc_maskval) + MaskColor(diff_ac, maskval)); + row_out[x] = std::sqrt(MaskColor(diff_dc, dc_maskval) + + MaskColor(diff_ac, maskval)); } } } @@ -1430,12 +1442,15 @@ BUTTERAUGLI_INLINE void OpsinAbsorbance(const DF df, const V& in0, const V& in1, } // `blurred` is a temporary image used inside this function and not returned. -void OpsinDynamicsImage(const Image3F& rgb, const ButteraugliParams& params, - Image3F* blurred, BlurTemp* blur_temp, Image3F* xyb) { +Status OpsinDynamicsImage(const Image3F& rgb, const ButteraugliParams& params, + Image3F* blurred, BlurTemp* blur_temp, Image3F* xyb) { const double kSigma = 1.2; - Blur(rgb.Plane(0), kSigma, params, blur_temp, &blurred->Plane(0)); - Blur(rgb.Plane(1), kSigma, params, blur_temp, &blurred->Plane(1)); - Blur(rgb.Plane(2), kSigma, params, blur_temp, &blurred->Plane(2)); + JXL_RETURN_IF_ERROR( + Blur(rgb.Plane(0), kSigma, params, blur_temp, &blurred->Plane(0))); + JXL_RETURN_IF_ERROR( + Blur(rgb.Plane(1), kSigma, params, blur_temp, &blurred->Plane(1))); + JXL_RETURN_IF_ERROR( + Blur(rgb.Plane(2), kSigma, params, blur_temp, &blurred->Plane(2))); const HWY_FULL(float) df; const auto intensity_target_multiplier = Set(df, params.intensity_target); for (size_t y = 0; y < rgb.ysize(); ++y) { @@ -1497,31 +1512,36 @@ void OpsinDynamicsImage(const Image3F& rgb, const ButteraugliParams& params, Store(cur_mixed2, df, row_out_b + x); } } + return true; } -void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, - const ButteraugliParams& params, - ImageF& diffmap) { +Status ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, + const ButteraugliParams& params, + ImageF& diffmap) { // image0 and image1 are in linear sRGB color space const size_t xsize = image0.xsize(); const size_t ysize = image0.ysize(); BlurTemp blur_temp; { // Convert image0 and image1 to XYB in-place - Image3F temp(xsize, ysize); - OpsinDynamicsImage(image0, params, &temp, &blur_temp, &image0); - OpsinDynamicsImage(image1, params, &temp, &blur_temp, &image1); + JXL_ASSIGN_OR_RETURN(Image3F temp, Image3F::Create(xsize, ysize)); + JXL_RETURN_IF_ERROR( + OpsinDynamicsImage(image0, params, &temp, &blur_temp, &image0)); + JXL_RETURN_IF_ERROR( + OpsinDynamicsImage(image1, params, &temp, &blur_temp, &image1)); } // image0 and image1 are in XYB color space - ImageF block_diff_dc(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF block_diff_dc, ImageF::Create(xsize, ysize)); ZeroFillImage(&block_diff_dc); { // separate out LF components from image0 and image1 and compute the dc // diff image from them - Image3F lf0 = Image3F(xsize, ysize); - Image3F lf1 = Image3F(xsize, ysize); - SeparateLFAndMF(params, image0, &lf0, &image0, &blur_temp); - SeparateLFAndMF(params, image1, &lf1, &image1, &blur_temp); + JXL_ASSIGN_OR_RETURN(Image3F lf0, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(Image3F lf1, Image3F::Create(xsize, ysize)); + JXL_RETURN_IF_ERROR( + SeparateLFAndMF(params, image0, &lf0, &image0, &blur_temp)); + JXL_RETURN_IF_ERROR( + SeparateLFAndMF(params, image1, &lf1, &image1, &blur_temp)); for (size_t c = 0; c < 3; ++c) { L2Diff(lf0.Plane(c), lf1.Plane(c), wmul[6 + c], &block_diff_dc); } @@ -1529,15 +1549,15 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, // image0 and image1 are MF residuals (before blurring) in XYB color space ImageF hf0[2]; ImageF hf1[2]; - SeparateMFAndHF(params, &image0, &hf0[0], &blur_temp); - SeparateMFAndHF(params, &image1, &hf1[0], &blur_temp); + JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &image0, &hf0[0], &blur_temp)); + JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &image1, &hf1[0], &blur_temp)); // image0 and image1 are MF-images in XYB color space - ImageF block_diff_ac(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF block_diff_ac, ImageF::Create(xsize, ysize)); ZeroFillImage(&block_diff_ac); // start accumulating ac diff image from MF images { - ImageF diffs(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize, ysize)); MaltaDiffMapLF(image0.Plane(1), image1.Plane(1), wMfMalta, wMfMalta, norm1Mf, &diffs, &block_diff_ac); MaltaDiffMapLF(image0.Plane(0), image1.Plane(0), wMfMaltaX, wMfMaltaX, @@ -1553,13 +1573,13 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, ImageF uhf0[2]; ImageF uhf1[2]; - SeparateHFAndUHF(params, &hf0[0], &uhf0[0], &blur_temp); - SeparateHFAndUHF(params, &hf1[0], &uhf1[0], &blur_temp); + JXL_RETURN_IF_ERROR(SeparateHFAndUHF(params, &hf0[0], &uhf0[0], &blur_temp)); + JXL_RETURN_IF_ERROR(SeparateHFAndUHF(params, &hf1[0], &uhf1[0], &blur_temp)); // continue accumulating ac diff image from HF and UHF images const float hf_asymmetry = params.hf_asymmetry; { - ImageF diffs(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize, ysize)); MaltaDiffMap(uhf0[1], uhf1[1], wUhfMalta * hf_asymmetry, wUhfMalta / hf_asymmetry, norm1Uhf, &diffs, &block_diff_ac); MaltaDiffMap(uhf0[0], uhf1[0], wUhfMaltaX * hf_asymmetry, @@ -1577,19 +1597,20 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, } // compute mask image from HF and UHF X and Y images - ImageF mask(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(xsize, ysize)); { - ImageF mask0(xsize, ysize); - ImageF mask1(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF mask0, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF mask1, ImageF::Create(xsize, ysize)); CombineChannelsForMasking(&hf0[0], &uhf0[0], &mask0); CombineChannelsForMasking(&hf1[0], &uhf1[0], &mask1); DeallocateHFAndUHF(&hf1[0], &uhf1[0]); DeallocateHFAndUHF(&hf0[0], &uhf0[0]); - Mask(mask0, mask1, params, &blur_temp, &mask, &block_diff_ac); + JXL_RETURN_IF_ERROR( + Mask(mask0, mask1, params, &blur_temp, &mask, &block_diff_ac)); } // compute final diffmap from mask image and ac and dc diff images - diffmap = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { const float* row_dc = block_diff_dc.Row(y); const float* row_ac = block_diff_ac.Row(y); @@ -1599,6 +1620,7 @@ void ButteraugliDiffmapInPlace(Image3F& image0, Image3F& image1, row_out[x] = sqrt(row_dc[x] * MaskDcY(val) + row_ac[x] * MaskY(val)); } } + return true; } // NOLINTNEXTLINE(google-readability-namespace-comments) @@ -1669,10 +1691,10 @@ static inline void CheckImage(const ImageF& image, const char* name) { // Calculate a 2x2 subsampled image for purposes of recursive butteraugli at // multiresolution. -static Image3F SubSample2x(const Image3F& in) { +static StatusOr<Image3F> SubSample2x(const Image3F& in) { size_t xs = (in.xsize() + 1) / 2; size_t ys = (in.ysize() + 1) / 2; - Image3F retval(xs, ys); + JXL_ASSIGN_OR_RETURN(Image3F retval, Image3F::Create(xs, ys)); for (size_t c = 0; c < 3; ++c) { for (size_t y = 0; y < ys; ++y) { for (size_t x = 0; x < xs; ++x) { @@ -1724,69 +1746,86 @@ Image3F* ButteraugliComparator::Temp() const { void ButteraugliComparator::ReleaseTemp() const { temp_in_use_.clear(); } -ButteraugliComparator::ButteraugliComparator(const Image3F& rgb0, +ButteraugliComparator::ButteraugliComparator(size_t xsize, size_t ysize, const ButteraugliParams& params) - : xsize_(rgb0.xsize()), - ysize_(rgb0.ysize()), - params_(params), - temp_(xsize_, ysize_) { - if (xsize_ < 8 || ysize_ < 8) { - return; + : xsize_(xsize), ysize_(ysize), params_(params) {} + +StatusOr<std::unique_ptr<ButteraugliComparator>> ButteraugliComparator::Make( + const Image3F& rgb0, const ButteraugliParams& params) { + size_t xsize = rgb0.xsize(); + size_t ysize = rgb0.ysize(); + std::unique_ptr<ButteraugliComparator> result = + std::unique_ptr<ButteraugliComparator>( + new ButteraugliComparator(xsize, ysize, params)); + JXL_ASSIGN_OR_RETURN(result->temp_, Image3F::Create(xsize, ysize)); + + if (xsize < 8 || ysize < 8) { + return result; } - Image3F xyb0(xsize_, ysize_); - HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage) - (rgb0, params, Temp(), &blur_temp_, &xyb0); - ReleaseTemp(); - HWY_DYNAMIC_DISPATCH(SeparateFrequencies) - (xsize_, ysize_, params_, &blur_temp_, xyb0, pi0_); + JXL_ASSIGN_OR_RETURN(Image3F xyb0, Image3F::Create(xsize, ysize)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)( + rgb0, params, result->Temp(), &result->blur_temp_, &xyb0)); + result->ReleaseTemp(); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(SeparateFrequencies)( + xsize, ysize, params, &result->blur_temp_, xyb0, result->pi0_)); // Awful recursive construction of samples of different resolution. // This is an after-thought and possibly somewhat parallel in // functionality with the PsychoImage multi-resolution approach. - sub_.reset(new ButteraugliComparator(SubSample2x(rgb0), params)); + JXL_ASSIGN_OR_RETURN(Image3F subsampledRgb0, SubSample2x(rgb0)); + StatusOr<std::unique_ptr<ButteraugliComparator>> sub = + ButteraugliComparator::Make(subsampledRgb0, params); + if (!sub.ok()) return sub.status(); + result->sub_ = std::move(sub).value(); + + return result; } -void ButteraugliComparator::Mask(ImageF* BUTTERAUGLI_RESTRICT mask) const { - HWY_DYNAMIC_DISPATCH(MaskPsychoImage) - (pi0_, pi0_, xsize_, ysize_, params_, &blur_temp_, mask, nullptr); +Status ButteraugliComparator::Mask(ImageF* BUTTERAUGLI_RESTRICT mask) const { + return HWY_DYNAMIC_DISPATCH(MaskPsychoImage)( + pi0_, pi0_, xsize_, ysize_, params_, &blur_temp_, mask, nullptr); } -void ButteraugliComparator::Diffmap(const Image3F& rgb1, ImageF& result) const { +Status ButteraugliComparator::Diffmap(const Image3F& rgb1, + ImageF& result) const { if (xsize_ < 8 || ysize_ < 8) { ZeroFillImage(&result); - return; + return true; } - Image3F xyb1(xsize_, ysize_); - HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage) - (rgb1, params_, Temp(), &blur_temp_, &xyb1); + JXL_ASSIGN_OR_RETURN(Image3F xyb1, Image3F::Create(xsize_, ysize_)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)( + rgb1, params_, Temp(), &blur_temp_, &xyb1)); ReleaseTemp(); - DiffmapOpsinDynamicsImage(xyb1, result); + JXL_RETURN_IF_ERROR(DiffmapOpsinDynamicsImage(xyb1, result)); if (sub_) { if (sub_->xsize_ < 8 || sub_->ysize_ < 8) { - return; + return true; } - Image3F sub_xyb(sub_->xsize_, sub_->ysize_); - HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage) - (SubSample2x(rgb1), params_, sub_->Temp(), &sub_->blur_temp_, &sub_xyb); + JXL_ASSIGN_OR_RETURN(Image3F sub_xyb, + Image3F::Create(sub_->xsize_, sub_->ysize_)); + JXL_ASSIGN_OR_RETURN(Image3F subsampledRgb1, SubSample2x(rgb1)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)( + subsampledRgb1, params_, sub_->Temp(), &sub_->blur_temp_, &sub_xyb)); sub_->ReleaseTemp(); ImageF subresult; - sub_->DiffmapOpsinDynamicsImage(sub_xyb, subresult); + JXL_RETURN_IF_ERROR(sub_->DiffmapOpsinDynamicsImage(sub_xyb, subresult)); AddSupersampled2x(subresult, 0.5, result); } + return true; } -void ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1, - ImageF& result) const { +Status ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1, + ImageF& result) const { if (xsize_ < 8 || ysize_ < 8) { ZeroFillImage(&result); - return; + return true; } PsychoImage pi1; - HWY_DYNAMIC_DISPATCH(SeparateFrequencies) - (xsize_, ysize_, params_, &blur_temp_, xyb1, pi1); - result = ImageF(xsize_, ysize_); - DiffmapPsychoImage(pi1, result); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(SeparateFrequencies)( + xsize_, ysize_, params_, &blur_temp_, xyb1, pi1)); + JXL_ASSIGN_OR_RETURN(result, ImageF::Create(xsize_, ysize_)); + return DiffmapPsychoImage(pi1, result); } namespace { @@ -1809,18 +1848,18 @@ void MaltaDiffMapLF(const ImageF& lum0, const ImageF& lum1, const double w_0gt1, } // namespace -void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, - ImageF& diffmap) const { +Status ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, + ImageF& diffmap) const { if (xsize_ < 8 || ysize_ < 8) { ZeroFillImage(&diffmap); - return; + return true; } const float hf_asymmetry_ = params_.hf_asymmetry; const float xmul_ = params_.xmul; - ImageF diffs(xsize_, ysize_); - Image3F block_diff_ac(xsize_, ysize_); + JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize_, ysize_)); + JXL_ASSIGN_OR_RETURN(Image3F block_diff_ac, Image3F::Create(xsize_, ysize_)); ZeroFillImage(&block_diff_ac); MaltaDiffMap(pi0_.uhf[1], pi1.uhf[1], wUhfMalta * hf_asymmetry_, wUhfMalta / hf_asymmetry_, norm1Uhf, &diffs, &block_diff_ac, 1); @@ -1838,7 +1877,7 @@ void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, MaltaDiffMapLF(pi0_.mf.Plane(0), pi1.mf.Plane(0), wMfMaltaX, wMfMaltaX, norm1MfX, &diffs, &block_diff_ac, 0); - Image3F block_diff_dc(xsize_, ysize_); + JXL_ASSIGN_OR_RETURN(Image3F block_diff_dc, Image3F::Create(xsize_, ysize_)); for (size_t c = 0; c < 3; ++c) { if (c < 2) { // No blue channel error accumulated at HF. HWY_DYNAMIC_DISPATCH(L2DiffAsymmetric) @@ -1852,12 +1891,13 @@ void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1, } ImageF mask; - HWY_DYNAMIC_DISPATCH(MaskPsychoImage) - (pi0_, pi1, xsize_, ysize_, params_, &blur_temp_, &mask, - &block_diff_ac.Plane(1)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(MaskPsychoImage)( + pi0_, pi1, xsize_, ysize_, params_, &blur_temp_, &mask, + &block_diff_ac.Plane(1))); HWY_DYNAMIC_DISPATCH(CombineChannelsToDiffmap) (mask, block_diff_dc, block_diff_ac, xmul_, &diffmap); + return true; } double ButteraugliScoreFromDiffmap(const ImageF& diffmap, @@ -1872,8 +1912,8 @@ double ButteraugliScoreFromDiffmap(const ImageF& diffmap, return retval; } -bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, - double hf_asymmetry, double xmul, ImageF& diffmap) { +Status ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, + double hf_asymmetry, double xmul, ImageF& diffmap) { ButteraugliParams params; params.hf_asymmetry = hf_asymmetry; params.xmul = xmul; @@ -1893,8 +1933,8 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1, size_t yborder = ysize < kMax ? (kMax - ysize) / 2 : 0; size_t xscaled = std::max<size_t>(kMax, xsize); size_t yscaled = std::max<size_t>(kMax, ysize); - Image3F scaled0(xscaled, yscaled); - Image3F scaled1(xscaled, yscaled); + JXL_ASSIGN_OR_RETURN(Image3F scaled0, Image3F::Create(xscaled, yscaled)); + JXL_ASSIGN_OR_RETURN(Image3F scaled1, Image3F::Create(xscaled, yscaled)); for (int i = 0; i < 3; ++i) { for (size_t y = 0; y < yscaled; ++y) { for (size_t x = 0; x < xscaled; ++x) { @@ -1907,7 +1947,7 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1, } ImageF diffmap_scaled; const bool ok = ButteraugliDiffmap(scaled0, scaled1, params, diffmap_scaled); - diffmap = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { for (size_t x = 0; x < xsize; ++x) { diffmap.Row(y)[x] = diffmap_scaled.Row(y + yborder)[x + xborder]; @@ -1916,8 +1956,8 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1, return ok; } -bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, - const ButteraugliParams& params, ImageF& diffmap) { +Status ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, + const ButteraugliParams& params, ImageF& diffmap) { const size_t xsize = rgb0.xsize(); const size_t ysize = rgb0.ysize(); if (xsize < 1 || ysize < 1) { @@ -1930,8 +1970,9 @@ bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1, if (xsize < kMax || ysize < kMax) { return ButteraugliDiffmapSmall<kMax>(rgb0, rgb1, params, diffmap); } - ButteraugliComparator butteraugli(rgb0, params); - butteraugli.Diffmap(rgb1, diffmap); + JXL_ASSIGN_OR_RETURN(std::unique_ptr<ButteraugliComparator> butteraugli, + ButteraugliComparator::Make(rgb0, params)); + JXL_RETURN_IF_ERROR(butteraugli->Diffmap(rgb1, diffmap)); return true; } @@ -1954,9 +1995,9 @@ bool ButteraugliInterface(const Image3F& rgb0, const Image3F& rgb1, return true; } -bool ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1, - const ButteraugliParams& params, - ImageF& diffmap, double& diffvalue) { +Status ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1, + const ButteraugliParams& params, + ImageF& diffmap, double& diffvalue) { const size_t xsize = rgb0.xsize(); const size_t ysize = rgb0.ysize(); if (xsize < 1 || ysize < 1) { @@ -1973,12 +2014,13 @@ bool ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1, } ImageF subdiffmap; if (xsize >= 15 && ysize >= 15) { - Image3F rgb0_sub = SubSample2x(rgb0); - Image3F rgb1_sub = SubSample2x(rgb1); - HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace) - (rgb0_sub, rgb1_sub, params, subdiffmap); + JXL_ASSIGN_OR_RETURN(Image3F rgb0_sub, SubSample2x(rgb0)); + JXL_ASSIGN_OR_RETURN(Image3F rgb1_sub, SubSample2x(rgb1)); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)( + rgb0_sub, rgb1_sub, params, subdiffmap)); } - HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)(rgb0, rgb1, params, diffmap); + JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)( + rgb0, rgb1, params, diffmap)); if (xsize >= 15 && ysize >= 15) { AddSupersampled2x(subdiffmap, 0.5, diffmap); } @@ -2066,9 +2108,11 @@ void ScoreToRgb(double score, double good_threshold, double bad_threshold, } // namespace -Image3F CreateHeatMapImage(const ImageF& distmap, double good_threshold, - double bad_threshold) { - Image3F heatmap(distmap.xsize(), distmap.ysize()); +StatusOr<Image3F> CreateHeatMapImage(const ImageF& distmap, + double good_threshold, + double bad_threshold) { + JXL_ASSIGN_OR_RETURN(Image3F heatmap, + Image3F::Create(distmap.xsize(), distmap.ysize())); for (size_t y = 0; y < distmap.ysize(); ++y) { const float* BUTTERAUGLI_RESTRICT row_distmap = distmap.ConstRow(y); float* BUTTERAUGLI_RESTRICT row_h0 = heatmap.PlaneRow(0, y); diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h index 29130e8768..e0bfd354e1 100644 --- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h +++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.h @@ -14,12 +14,12 @@ #include <atomic> #include <cmath> +#include <cstddef> #include <memory> -#include <vector> #include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/image.h" -#include "lib/jxl/image_ops.h" #define BUTTERAUGLI_ENABLE_CHECKS 0 #define BUTTERAUGLI_RESTRICT JXL_RESTRICT @@ -87,9 +87,9 @@ bool ButteraugliInterface(const Image3F &rgb0, const Image3F &rgb1, // Same as ButteraugliInterface, but reuses rgb0 and rgb1 for other purposes // inside the function after they are not needed any more, and it ignores // params.xmul. -bool ButteraugliInterfaceInPlace(Image3F &&rgb0, Image3F &&rgb1, - const ButteraugliParams ¶ms, - ImageF &diffmap, double &diffvalue); +Status ButteraugliInterfaceInPlace(Image3F &&rgb0, Image3F &&rgb1, + const ButteraugliParams ¶ms, + ImageF &diffmap, double &diffvalue); // Converts the butteraugli score into fuzzy class values that are continuous // at the class boundary. The class boundary location is based on human @@ -147,11 +147,13 @@ struct PsychoImage { // Blur needs a transposed image. // Hold it here and only allocate on demand to reduce memory usage. struct BlurTemp { - ImageF *GetTransposed(const ImageF &in) { + Status GetTransposed(const ImageF &in, ImageF **out) { if (transposed_temp.xsize() == 0) { - transposed_temp = ImageF(in.ysize(), in.xsize()); + JXL_ASSIGN_OR_RETURN(transposed_temp, + ImageF::Create(in.ysize(), in.xsize())); } - return &transposed_temp; + *out = &transposed_temp; + return true; } ImageF transposed_temp; @@ -162,22 +164,26 @@ class ButteraugliComparator { // Butteraugli is calibrated at xmul = 1.0. We add a multiplier here so that // we can test the hypothesis that a higher weighing of the X channel would // improve results at higher Butteraugli values. - ButteraugliComparator(const Image3F &rgb0, const ButteraugliParams ¶ms); virtual ~ButteraugliComparator() = default; + static StatusOr<std::unique_ptr<ButteraugliComparator>> Make( + const Image3F &rgb0, const ButteraugliParams ¶ms); + // Computes the butteraugli map between the original image given in the // constructor and the distorted image give here. - void Diffmap(const Image3F &rgb1, ImageF &result) const; + Status Diffmap(const Image3F &rgb1, ImageF &result) const; // Same as above, but OpsinDynamicsImage() was already applied. - void DiffmapOpsinDynamicsImage(const Image3F &xyb1, ImageF &result) const; + Status DiffmapOpsinDynamicsImage(const Image3F &xyb1, ImageF &result) const; // Same as above, but the frequency decomposition was already applied. - void DiffmapPsychoImage(const PsychoImage &pi1, ImageF &diffmap) const; + Status DiffmapPsychoImage(const PsychoImage &pi1, ImageF &diffmap) const; - void Mask(ImageF *BUTTERAUGLI_RESTRICT mask) const; + Status Mask(ImageF *BUTTERAUGLI_RESTRICT mask) const; private: + ButteraugliComparator(size_t xsize, size_t ysize, + const ButteraugliParams ¶ms); Image3F *Temp() const; void ReleaseTemp() const; @@ -196,18 +202,19 @@ class ButteraugliComparator { }; // Deprecated. -bool ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1, - double hf_asymmetry, double xmul, ImageF &diffmap); +Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1, + double hf_asymmetry, double xmul, ImageF &diffmap); -bool ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1, - const ButteraugliParams ¶ms, ImageF &diffmap); +Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1, + const ButteraugliParams ¶ms, ImageF &diffmap); double ButteraugliScoreFromDiffmap(const ImageF &diffmap, const ButteraugliParams *params = nullptr); // Generate rgb-representation of the distance between two images. -Image3F CreateHeatMapImage(const ImageF &distmap, double good_threshold, - double bad_threshold); +StatusOr<Image3F> CreateHeatMapImage(const ImageF &distmap, + double good_threshold, + double bad_threshold); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc index c2ccf56175..39df4bd473 100644 --- a/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc +++ b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli_test.cc @@ -30,7 +30,7 @@ using extras::PackedPixelFile; using test::TestImage; Image3F SinglePixelImage(float red, float green, float blue) { - Image3F img(1, 1); + JXL_ASSIGN_OR_DIE(Image3F img, Image3F::Create(1, 1)); img.PlaneRow(0, 0)[0] = red; img.PlaneRow(1, 0)[0] = green; img.PlaneRow(2, 0)[0] = blue; @@ -42,7 +42,7 @@ Image3F GetColorImage(const PackedPixelFile& ppf) { const PackedImage& image = ppf.frames[0].color; const JxlPixelFormat& format = image.format; const uint8_t* pixels = reinterpret_cast<const uint8_t*>(image.pixels()); - Image3F color(image.xsize, image.ysize); + JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(image.xsize, image.ysize)); for (size_t c = 0; c < format.num_channels; ++c) { JXL_CHECK(ConvertFromExternal(pixels, image.pixels_size, image.xsize, image.ysize, ppf.info.bits_per_sample, format, @@ -93,7 +93,7 @@ TEST(ButteraugliInPlaceTest, LargeImage) { TestImage img; img.SetDimensions(xsize, ysize).AddFrame().RandomFill(777); Image3F rgb0 = GetColorImage(img.ppf()); - Image3F rgb1(xsize, ysize); + JXL_ASSIGN_OR_DIE(Image3F rgb1, Image3F::Create(xsize, ysize)); CopyImageTo(rgb0, &rgb1); AddUniformNoise(&rgb1, 0.02f, 7777); AddEdge(&rgb1, 0.1f, xsize / 2, xsize / 2); diff --git a/third_party/jpeg-xl/lib/jxl/cache_aligned.cc b/third_party/jpeg-xl/lib/jxl/cache_aligned.cc index 992efc4d48..8a95634d68 100644 --- a/third_party/jpeg-xl/lib/jxl/cache_aligned.cc +++ b/third_party/jpeg-xl/lib/jxl/cache_aligned.cc @@ -5,6 +5,7 @@ #include "lib/jxl/cache_aligned.h" +#include <jxl/types.h> #include <stdio.h> #include <stdlib.h> @@ -95,7 +96,7 @@ void* CacheAligned::Allocate(const size_t payload_size, size_t offset) { aligned &= ~(kAlias - 1); #endif -#if 0 +#if JXL_FALSE // No effect. uintptr_t page_aligned = reinterpret_cast<uintptr_t>(allocated); page_aligned &= ~(4096 - 1); diff --git a/third_party/jpeg-xl/lib/jxl/cache_aligned.h b/third_party/jpeg-xl/lib/jxl/cache_aligned.h index d79d7be461..8480471e5c 100644 --- a/third_party/jpeg-xl/lib/jxl/cache_aligned.h +++ b/third_party/jpeg-xl/lib/jxl/cache_aligned.h @@ -49,7 +49,7 @@ class CacheAligned { // Avoids the need for a function pointer (deleter) in CacheAlignedUniquePtr. struct CacheAlignedDeleter { void operator()(uint8_t* aligned_pointer) const { - return CacheAligned::Free(aligned_pointer); + CacheAligned::Free(aligned_pointer); } }; diff --git a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc index 63d21cbb4b..69585c44cf 100644 --- a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc +++ b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc @@ -5,17 +5,25 @@ #include "lib/jxl/chroma_from_luma.h" +#include "lib/jxl/image_ops.h" + namespace jxl { -ColorCorrelationMap::ColorCorrelationMap(size_t xsize, size_t ysize, bool XYB) - : ytox_map(DivCeil(xsize, kColorTileDim), DivCeil(ysize, kColorTileDim)), - ytob_map(DivCeil(xsize, kColorTileDim), DivCeil(ysize, kColorTileDim)) { - ZeroFillImage(&ytox_map); - ZeroFillImage(&ytob_map); +StatusOr<ColorCorrelationMap> ColorCorrelationMap::Create(size_t xsize, + size_t ysize, + bool XYB) { + ColorCorrelationMap result; + size_t xblocks = DivCeil(xsize, kColorTileDim); + size_t yblocks = DivCeil(ysize, kColorTileDim); + JXL_ASSIGN_OR_RETURN(result.ytox_map, ImageSB::Create(xblocks, yblocks)); + JXL_ASSIGN_OR_RETURN(result.ytob_map, ImageSB::Create(xblocks, yblocks)); + ZeroFillImage(&result.ytox_map); + ZeroFillImage(&result.ytob_map); if (!XYB) { - base_correlation_b_ = 0; + result.base_correlation_b_ = 0; } - RecomputeDCFactors(); + result.RecomputeDCFactors(); + return result; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h index cb3b710762..1f2353d9af 100644 --- a/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h +++ b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.h @@ -12,19 +12,15 @@ #include <stddef.h> #include <stdint.h> -#include <vector> +#include <limits> -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/status.h" #include "lib/jxl/cms/opsin_params.h" #include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/entropy_coder.h" #include "lib/jxl/field_encodings.h" #include "lib/jxl/fields.h" #include "lib/jxl/frame_dimensions.h" #include "lib/jxl/image.h" -#include "lib/jxl/quant_weights.h" namespace jxl { @@ -55,7 +51,8 @@ struct ColorCorrelationMap { // xsize/ysize are in pixels // set XYB=false to do something close to no-op cmap (needed for now since // cmap is mandatory) - ColorCorrelationMap(size_t xsize, size_t ysize, bool XYB = true); + static StatusOr<ColorCorrelationMap> Create(size_t xsize, size_t ysize, + bool XYB = true); float YtoXRatio(int32_t x_factor) const { return base_correlation_x_ + x_factor * color_scale_; @@ -96,7 +93,7 @@ struct ColorCorrelationMap { color_factor_ == kDefaultColorFactor; } - int32_t RatioJPEG(int32_t factor) const { + static int32_t RatioJPEG(int32_t factor) { return factor * (1 << kCFLFixedPointPrecision) / kDefaultColorFactor; } diff --git a/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h b/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h index db61f820ca..3b7ae09d6f 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h +++ b/third_party/jpeg-xl/lib/jxl/cms/color_encoding_cms.h @@ -96,6 +96,8 @@ enum class RenderingIntent : uint32_t { // Chromaticity (Y is omitted because it is 1 for white points and implicit for // primaries) struct CIExy { + CIExy() = default; + CIExy(double x, double y) : x(x), y(y) {} double x = 0.0; double y = 0.0; }; @@ -516,7 +518,7 @@ struct ColorEncoding { JXL_RETURN_IF_ERROR(cms.set_fields_from_icc(cms.set_fields_data, new_icc.data(), new_icc.size(), &external, &new_cmyk)); - cmyk = new_cmyk; + cmyk = static_cast<bool>(new_cmyk); JXL_RETURN_IF_ERROR(FromExternal(external)); icc = std::move(new_icc); return true; diff --git a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc index dd00b8b81f..e35b3ce172 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc +++ b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc @@ -307,7 +307,7 @@ int DoColorSpaceTransform(void* t, size_t thread, const float* buf_src, #if JPEGXL_ENABLE_SKCMS -JXL_MUST_USE_RESULT CIExy CIExyFromXYZ(const float XYZ[3]) { +JXL_MUST_USE_RESULT CIExy CIExyFromXYZ(const Color& XYZ) { const float factor = 1.f / (XYZ[0] + XYZ[1] + XYZ[2]); CIExy xy; xy.x = XYZ[0] * factor; @@ -405,8 +405,8 @@ ColorSpace ColorSpaceFromProfile(const skcms_ICCProfile& profile) { } // vector_out := matmul(matrix, vector_in) -void MatrixProduct(const skcms_Matrix3x3& matrix, const float vector_in[3], - float vector_out[3]) { +void MatrixProduct(const skcms_Matrix3x3& matrix, const Color& vector_in, + Color& vector_out) { for (int i = 0; i < 3; ++i) { vector_out[i] = 0; for (int j = 0; j < 3; ++j) { @@ -418,8 +418,8 @@ void MatrixProduct(const skcms_Matrix3x3& matrix, const float vector_in[3], // Returns white point that was specified when creating the profile. JXL_MUST_USE_RESULT Status UnadaptedWhitePoint(const skcms_ICCProfile& profile, CIExy* out) { - float media_white_point_XYZ[3]; - if (!skcms_GetWTPT(&profile, media_white_point_XYZ)) { + Color media_white_point_XYZ; + if (!skcms_GetWTPT(&profile, media_white_point_XYZ.data())) { return JXL_FAILURE("ICC profile does not contain WhitePoint tag"); } skcms_Matrix3x3 CHAD; @@ -435,7 +435,7 @@ JXL_MUST_USE_RESULT Status UnadaptedWhitePoint(const skcms_ICCProfile& profile, if (!skcms_Matrix3x3_invert(&CHAD, &inverse_CHAD)) { return JXL_FAILURE("Non-invertible ChromaticAdaptation matrix"); } - float unadapted_white_point_XYZ[3]; + Color unadapted_white_point_XYZ; MatrixProduct(inverse_CHAD, media_white_point_XYZ, unadapted_white_point_XYZ); *out = CIExyFromXYZ(unadapted_white_point_XYZ); return true; @@ -445,7 +445,8 @@ Status IdentifyPrimaries(const skcms_ICCProfile& profile, const CIExy& wp_unadapted, ColorEncoding* c) { if (!c->HasPrimaries()) return true; - skcms_Matrix3x3 CHAD, inverse_CHAD; + skcms_Matrix3x3 CHAD; + skcms_Matrix3x3 inverse_CHAD; if (skcms_GetCHAD(&profile, &CHAD)) { JXL_RETURN_IF_ERROR(skcms_Matrix3x3_invert(&CHAD, &inverse_CHAD)); } else { @@ -457,11 +458,12 @@ Status IdentifyPrimaries(const skcms_ICCProfile& profile, {{0.9869929, -0.1470543, 0.1599627}, {0.4323053, 0.5183603, 0.0492912}, {-0.0085287, 0.0400428, 0.9684867}}}; - static constexpr float kWpD50XYZ[3] = {0.96420288, 1.0, 0.82490540}; - float wp_unadapted_XYZ[3]; + static constexpr Color kWpD50XYZ{0.96420288, 1.0, 0.82490540}; + Color wp_unadapted_XYZ; JXL_RETURN_IF_ERROR( CIEXYZFromWhiteCIExy(wp_unadapted.x, wp_unadapted.y, wp_unadapted_XYZ)); - float wp_D50_LMS[3], wp_unadapted_LMS[3]; + Color wp_D50_LMS; + Color wp_unadapted_LMS; MatrixProduct(kLMSFromXYZ, kWpD50XYZ, wp_D50_LMS); MatrixProduct(kLMSFromXYZ, wp_unadapted_XYZ, wp_unadapted_LMS); inverse_CHAD = {{{wp_unadapted_LMS[0] / wp_D50_LMS[0], 0, 0}, @@ -471,16 +473,16 @@ Status IdentifyPrimaries(const skcms_ICCProfile& profile, inverse_CHAD = skcms_Matrix3x3_concat(&inverse_CHAD, &kLMSFromXYZ); } - float XYZ[3]; + Color XYZ; PrimariesCIExy primaries; CIExy* const chromaticities[] = {&primaries.r, &primaries.g, &primaries.b}; for (int i = 0; i < 3; ++i) { float RGB[3] = {}; RGB[i] = 1; skcms_Transform(RGB, skcms_PixelFormat_RGB_fff, skcms_AlphaFormat_Opaque, - &profile, XYZ, skcms_PixelFormat_RGB_fff, + &profile, XYZ.data(), skcms_PixelFormat_RGB_fff, skcms_AlphaFormat_Opaque, skcms_XYZD50_profile(), 1); - float unadapted_XYZ[3]; + Color unadapted_XYZ; MatrixProduct(inverse_CHAD, XYZ, unadapted_XYZ); *chromaticities[i] = CIExyFromXYZ(unadapted_XYZ); } @@ -829,17 +831,17 @@ Status GetPrimariesLuminances(const ColorEncoding& encoding, // From there, by multiplying each total by its corresponding y, we get Y for // that primary. - float white_XYZ[3]; + Color white_XYZ; CIExy wp = encoding.GetWhitePoint(); JXL_RETURN_IF_ERROR(CIEXYZFromWhiteCIExy(wp.x, wp.y, white_XYZ)); const PrimariesCIExy primaries = encoding.GetPrimaries(); - double chromaticities[3][3] = { - {primaries.r.x, primaries.g.x, primaries.b.x}, - {primaries.r.y, primaries.g.y, primaries.b.y}, - {1 - primaries.r.x - primaries.r.y, 1 - primaries.g.x - primaries.g.y, - 1 - primaries.b.x - primaries.b.y}}; - JXL_RETURN_IF_ERROR(Inv3x3Matrix(&chromaticities[0][0])); + Matrix3x3d chromaticities{ + {{primaries.r.x, primaries.g.x, primaries.b.x}, + {primaries.r.y, primaries.g.y, primaries.b.y}, + {1 - primaries.r.x - primaries.r.y, 1 - primaries.g.x - primaries.g.y, + 1 - primaries.b.x - primaries.b.y}}}; + JXL_RETURN_IF_ERROR(Inv3x3Matrix(chromaticities)); const double ys[3] = {primaries.r.y, primaries.g.y, primaries.b.y}; for (size_t i = 0; i < 3; ++i) { luminances[i] = ys[i] * (chromaticities[i][0] * white_XYZ[0] + @@ -971,14 +973,31 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data, JXL_RETURN_IF_ERROR(skcms_Parse(icc_data, icc_size, &profile)); // skcms does not return the rendering intent, so get it from the file. It - // is encoded as big-endian 32-bit integer in bytes 60..63. - uint32_t rendering_intent32 = icc_data[67]; - if (rendering_intent32 > 3 || icc_data[64] != 0 || icc_data[65] != 0 || - icc_data[66] != 0) { - return JXL_FAILURE("Invalid rendering intent %u\n", rendering_intent32); + // should be encoded as big-endian 32-bit integer in bytes 60..63. + uint32_t big_endian_rendering_intent = icc_data[67] + (icc_data[66] << 8) + + (icc_data[65] << 16) + + (icc_data[64] << 24); + // Some files encode rendering intent as little endian, which is not spec + // compliant. However we accept those with a warning. + uint32_t little_endian_rendering_intent = (icc_data[67] << 24) + + (icc_data[66] << 16) + + (icc_data[65] << 8) + icc_data[64]; + uint32_t candidate_rendering_intent = + std::min(big_endian_rendering_intent, little_endian_rendering_intent); + if (candidate_rendering_intent != big_endian_rendering_intent) { + JXL_WARNING( + "Invalid rendering intent bytes: [0x%02X 0x%02X 0x%02X 0x%02X], " + "assuming %u was meant", + icc_data[64], icc_data[65], icc_data[66], icc_data[67], + candidate_rendering_intent); + } + if (candidate_rendering_intent > 3) { + return JXL_FAILURE("Invalid rendering intent %u\n", + candidate_rendering_intent); } // ICC and RenderingIntent have the same values (0..3). - c_enc.rendering_intent = static_cast<RenderingIntent>(rendering_intent32); + c_enc.rendering_intent = + static_cast<RenderingIntent>(candidate_rendering_intent); if (profile.has_CICP && ApplyCICP(profile.CICP.color_primaries, @@ -986,11 +1005,11 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data, profile.CICP.matrix_coefficients, profile.CICP.video_full_range_flag, &c_enc)) { *c = c_enc.ToExternal(); - return true; + return JXL_TRUE; } c_enc.color_space = ColorSpaceFromProfile(profile); - *cmyk = (profile.data_color_space == skcms_Signature_CMYK); + *cmyk = TO_JXL_BOOL(profile.data_color_space == skcms_Signature_CMYK); CIExy wp_unadapted; JXL_RETURN_IF_ERROR(UnadaptedWhitePoint(profile, &wp_unadapted)); @@ -1026,14 +1045,14 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data, ApplyCICP(cicp_buffer[8], cicp_buffer[9], cicp_buffer[10], cicp_buffer[11], &c_enc)) { *c = c_enc.ToExternal(); - return true; + return JXL_TRUE; } c_enc.color_space = ColorSpaceFromProfile(profile); if (cmsGetColorSpace(profile.get()) == cmsSigCmykData) { *cmyk = JXL_TRUE; *c = c_enc.ToExternal(); - return true; + return JXL_TRUE; } const cmsCIEXYZ wp_unadapted = UnadaptedWhitePoint(context, profile, c_enc); @@ -1049,7 +1068,7 @@ JXL_BOOL JxlCmsSetFieldsFromICC(void* user_data, const uint8_t* icc_data, #endif // JPEGXL_ENABLE_SKCMS *c = c_enc.ToExternal(); - return true; + return JXL_TRUE; } } // namespace @@ -1084,9 +1103,10 @@ void* JxlCmsInit(void* init_data, size_t num_threads, size_t xsize, const JxlColorProfile* input, const JxlColorProfile* output, float intensity_target) { JXL_ASSERT(init_data != nullptr); - auto cms = static_cast<const JxlCmsInterface*>(init_data); + const auto* cms = static_cast<const JxlCmsInterface*>(init_data); auto t = jxl::make_unique<JxlCms>(); - IccBytes icc_src, icc_dst; + IccBytes icc_src; + IccBytes icc_dst; if (input->icc.size == 0) { JXL_NOTIFY_ERROR("JxlCmsInit: empty input ICC"); return nullptr; @@ -1259,8 +1279,6 @@ void* JxlCmsInit(void* init_data, size_t num_threads, size_t xsize, // Not including alpha channel (copied separately). const size_t channels_src = (c_src.cmyk ? 4 : c_src.Channels()); const size_t channels_dst = c_dst.Channels(); - JXL_CHECK(channels_src == channels_dst || - (channels_src == 4 && channels_dst == 3)); #if JXL_CMS_VERBOSE printf("Channels: %" PRIuS "; Threads: %" PRIuS "\n", channels_src, num_threads); @@ -1294,13 +1312,14 @@ void* JxlCmsInit(void* init_data, size_t num_threads, size_t xsize, // outputs (or vice versa), we use floating point input/output. t->channels_src = channels_src; t->channels_dst = channels_dst; +#if !JPEGXL_ENABLE_SKCMS size_t actual_channels_src = channels_src; size_t actual_channels_dst = channels_dst; -#if JPEGXL_ENABLE_SKCMS +#else // SkiaCMS doesn't support grayscale float buffers, so we create space for RGB // float buffers anyway. - actual_channels_src = (channels_src == 4 ? 4 : 3); - actual_channels_dst = 3; + size_t actual_channels_src = (channels_src == 4 ? 4 : 3); + size_t actual_channels_dst = 3; #endif AllocateBuffer(xsize * actual_channels_src, num_threads, &t->src_storage, &t->buf_src); diff --git a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h index c00fe82d8c..7f59e688d0 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h +++ b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms_internal.h @@ -41,7 +41,7 @@ enum class ExtraTF { }; static Status PrimariesToXYZ(float rx, float ry, float gx, float gy, float bx, - float by, float wx, float wy, float matrix[9]) { + float by, float wx, float wy, Matrix3x3& matrix) { bool ok = (wx >= 0) && (wx <= 1) && (wy > 0) && (wy <= 1); if (!ok) { return JXL_FAILURE("Invalid white point"); @@ -49,51 +49,48 @@ static Status PrimariesToXYZ(float rx, float ry, float gx, float gy, float bx, // TODO(lode): also require rx, ry, gx, gy, bx, to be in range 0-1? ICC // profiles in theory forbid negative XYZ values, but in practice the ACES P0 // color space uses a negative y for the blue primary. - float primaries[9] = { - rx, gx, bx, ry, gy, by, 1.0f - rx - ry, 1.0f - gx - gy, 1.0f - bx - by}; - float primaries_inv[9]; - memcpy(primaries_inv, primaries, sizeof(float) * 9); + Matrix3x3 primaries{{{rx, gx, bx}, + {ry, gy, by}, + {1.0f - rx - ry, 1.0f - gx - gy, 1.0f - bx - by}}}; + Matrix3x3 primaries_inv; + primaries_inv = primaries; JXL_RETURN_IF_ERROR(Inv3x3Matrix(primaries_inv)); - float w[3] = {wx / wy, 1.0f, (1.0f - wx - wy) / wy}; + Vector3 w{wx / wy, 1.0f, (1.0f - wx - wy) / wy}; // 1 / tiny float can still overflow JXL_RETURN_IF_ERROR(std::isfinite(w[0]) && std::isfinite(w[2])); - float xyz[3]; + Vector3 xyz; Mul3x3Vector(primaries_inv, w, xyz); - float a[9] = { - xyz[0], 0, 0, 0, xyz[1], 0, 0, 0, xyz[2], - }; + Matrix3x3 a{{{xyz[0], 0, 0}, {0, xyz[1], 0}, {0, 0, xyz[2]}}}; Mul3x3Matrix(primaries, a, matrix); return true; } /* Chromatic adaptation matrices*/ -constexpr float kBradford[9] = { - 0.8951f, 0.2664f, -0.1614f, -0.7502f, 1.7135f, - 0.0367f, 0.0389f, -0.0685f, 1.0296f, -}; -constexpr float kBradfordInv[9] = { - 0.9869929f, -0.1470543f, 0.1599627f, 0.4323053f, 0.5183603f, - 0.0492912f, -0.0085287f, 0.0400428f, 0.9684867f, -}; +constexpr Matrix3x3 kBradford{{{0.8951f, 0.2664f, -0.1614f}, + {-0.7502f, 1.7135f, 0.0367f}, + {0.0389f, -0.0685f, 1.0296f}}}; +constexpr Matrix3x3 kBradfordInv{{{0.9869929f, -0.1470543f, 0.1599627f}, + {0.4323053f, 0.5183603f, 0.0492912f}, + {-0.0085287f, 0.0400428f, 0.9684867f}}}; // Adapts whitepoint x, y to D50 -static Status AdaptToXYZD50(float wx, float wy, float matrix[9]) { +static Status AdaptToXYZD50(float wx, float wy, Matrix3x3& matrix) { bool ok = (wx >= 0) && (wx <= 1) && (wy > 0) && (wy <= 1); if (!ok) { // Out of range values can cause division through zero // further down with the bradford adaptation too. return JXL_FAILURE("Invalid white point"); } - float w[3] = {wx / wy, 1.0f, (1.0f - wx - wy) / wy}; + Vector3 w{wx / wy, 1.0f, (1.0f - wx - wy) / wy}; // 1 / tiny float can still overflow JXL_RETURN_IF_ERROR(std::isfinite(w[0]) && std::isfinite(w[2])); - float w50[3] = {0.96422f, 1.0f, 0.82521f}; + Vector3 w50{0.96422f, 1.0f, 0.82521f}; - float lms[3]; - float lms50[3]; + Vector3 lms; + Vector3 lms50; Mul3x3Vector(kBradford, w, lms); Mul3x3Vector(kBradford, w50, lms50); @@ -101,15 +98,15 @@ static Status AdaptToXYZD50(float wx, float wy, float matrix[9]) { if (lms[0] == 0 || lms[1] == 0 || lms[2] == 0) { return JXL_FAILURE("Invalid white point"); } - float a[9] = { - // /----> 0, 1, 2, 3, /----> 4, 5, 6, 7, /----> 8, - lms50[0] / lms[0], 0, 0, 0, lms50[1] / lms[1], 0, 0, 0, lms50[2] / lms[2], - }; - if (!std::isfinite(a[0]) || !std::isfinite(a[4]) || !std::isfinite(a[8])) { + Matrix3x3 a{{{lms50[0] / lms[0], 0, 0}, + {0, lms50[1] / lms[1], 0}, + {0, 0, lms50[2] / lms[2]}}}; + if (!std::isfinite(a[0][0]) || !std::isfinite(a[1][1]) || + !std::isfinite(a[2][2])) { return JXL_FAILURE("Invalid white point"); } - float b[9]; + Matrix3x3 b; Mul3x3Matrix(a, kBradford, b); Mul3x3Matrix(kBradfordInv, b, matrix); @@ -118,10 +115,10 @@ static Status AdaptToXYZD50(float wx, float wy, float matrix[9]) { static Status PrimariesToXYZD50(float rx, float ry, float gx, float gy, float bx, float by, float wx, float wy, - float matrix[9]) { - float toXYZ[9]; + Matrix3x3& matrix) { + Matrix3x3 toXYZ; JXL_RETURN_IF_ERROR(PrimariesToXYZ(rx, ry, gx, gy, bx, by, wx, wy, toXYZ)); - float d50[9]; + Matrix3x3 d50; JXL_RETURN_IF_ERROR(AdaptToXYZD50(wx, wy, d50)); Mul3x3Matrix(d50, toXYZ, matrix); @@ -130,14 +127,13 @@ static Status PrimariesToXYZD50(float rx, float ry, float gx, float gy, static Status ToneMapPixel(const JxlColorEncoding& c, const float in[3], uint8_t pcslab_out[3]) { - float primaries_XYZ[9]; + Matrix3x3 primaries_XYZ; JXL_RETURN_IF_ERROR(PrimariesToXYZ( c.primaries_red_xy[0], c.primaries_red_xy[1], c.primaries_green_xy[0], c.primaries_green_xy[1], c.primaries_blue_xy[0], c.primaries_blue_xy[1], c.white_point_xy[0], c.white_point_xy[1], primaries_XYZ)); - const float luminances[3] = {primaries_XYZ[3], primaries_XYZ[4], - primaries_XYZ[5]}; - float linear[3]; + const Vector3 luminances = primaries_XYZ[1]; + Color linear; JxlTransferFunction tf = c.transfer_function; if (tf == JXL_TRANSFER_FUNCTION_PQ) { for (size_t i = 0; i < 3; ++i) { @@ -151,25 +147,25 @@ static Status ToneMapPixel(const JxlColorEncoding& c, const float in[3], } if (tf == JXL_TRANSFER_FUNCTION_PQ) { Rec2408ToneMapperBase tone_mapper({0, 10000}, {0, 250}, luminances); - tone_mapper.ToneMap(&linear[0], &linear[1], &linear[2]); + tone_mapper.ToneMap(linear); } else { HlgOOTF_Base ootf(/*source_luminance=*/300, /*target_luminance=*/80, luminances); - ootf.Apply(&linear[0], &linear[1], &linear[2]); + ootf.Apply(linear); } - GamutMapScalar(&linear[0], &linear[1], &linear[2], luminances, + GamutMapScalar(linear, luminances, /*preserve_saturation=*/0.3f); - float chad[9]; + Matrix3x3 chad; JXL_RETURN_IF_ERROR( AdaptToXYZD50(c.white_point_xy[0], c.white_point_xy[1], chad)); - float to_xyzd50[9]; + Matrix3x3 to_xyzd50; Mul3x3Matrix(chad, primaries_XYZ, to_xyzd50); - float xyz[3] = {0, 0, 0}; + Vector3 xyz{0, 0, 0}; for (size_t xyz_c = 0; xyz_c < 3; ++xyz_c) { for (size_t rgb_c = 0; rgb_c < 3; ++rgb_c) { - xyz[xyz_c] += linear[rgb_c] * to_xyzd50[3 * xyz_c + rgb_c]; + xyz[xyz_c] += linear[rgb_c] * to_xyzd50[xyz_c][rgb_c]; } } @@ -206,7 +202,7 @@ static std::vector<uint16_t> CreateTableCurve(uint32_t N, const ExtraTF tf, JXL_ASSERT(N <= 4096); // ICC MFT2 only allows 4K entries JXL_ASSERT(tf == ExtraTF::kPQ || tf == ExtraTF::kHLG); - static constexpr float kLuminances[] = {1.f / 3, 1.f / 3, 1.f / 3}; + static constexpr Vector3 kLuminances{1.f / 3, 1.f / 3, 1.f / 3}; Rec2408ToneMapperBase tone_mapper({0, kPQIntensityTarget}, {0, kDefaultIntensityTarget}, kLuminances); // No point using float - LCMS converts to 16-bit for A2B/MFT. @@ -220,9 +216,10 @@ static std::vector<uint16_t> CreateTableCurve(uint32_t N, const ExtraTF tf, : TF_PQ_Base::DisplayFromEncoded(kPQIntensityTarget, dx); if (tone_map && tf == ExtraTF::kPQ && kPQIntensityTarget > kDefaultIntensityTarget) { - float r = y * 10000 / kPQIntensityTarget, g = r, b = r; - tone_mapper.ToneMap(&r, &g, &b); - y = r; + float l = y * 10000 / kPQIntensityTarget; + Color gray{l, l, l}; + tone_mapper.ToneMap(gray); + y = gray[0]; } JXL_ASSERT(y >= 0.0); // Clamp to table range - necessary for HLG. @@ -233,7 +230,7 @@ static std::vector<uint16_t> CreateTableCurve(uint32_t N, const ExtraTF tf, return table; } -static Status CIEXYZFromWhiteCIExy(double wx, double wy, float XYZ[3]) { +static Status CIEXYZFromWhiteCIExy(double wx, double wy, Color& XYZ) { // Target Y = 1. if (std::abs(wy) < 1e-12) return JXL_FAILURE("Y value is too small"); const float factor = 1 / wy; @@ -292,10 +289,18 @@ static void ICCComputeMD5(const std::vector<uint8_t>& data, uint8_t sum[16]) 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, }; - uint32_t a0 = 0x67452301, b0 = 0xefcdab89, c0 = 0x98badcfe, d0 = 0x10325476; + uint32_t a0 = 0x67452301; + uint32_t b0 = 0xefcdab89; + uint32_t c0 = 0x98badcfe; + uint32_t d0 = 0x10325476; for (size_t i = 0; i < data64.size(); i += 64) { - uint32_t a = a0, b = b0, c = c0, d = d0, f, g; + uint32_t a = a0; + uint32_t b = b0; + uint32_t c = c0; + uint32_t d = d0; + uint32_t f; + uint32_t g; for (size_t j = 0; j < 64; j++) { if (j < 16) { f = (b & c) | ((~b) & d); @@ -310,8 +315,10 @@ static void ICCComputeMD5(const std::vector<uint8_t>& data, uint8_t sum[16]) f = c ^ (b | (~d)); g = (7 * j) & 0xf; } - uint32_t dg0 = data64[i + g * 4 + 0], dg1 = data64[i + g * 4 + 1], - dg2 = data64[i + g * 4 + 2], dg3 = data64[i + g * 4 + 3]; + uint32_t dg0 = data64[i + g * 4 + 0]; + uint32_t dg1 = data64[i + g * 4 + 1]; + uint32_t dg2 = data64[i + g * 4 + 2]; + uint32_t dg3 = data64[i + g * 4 + 3]; uint32_t u = dg0 | (dg1 << 8u) | (dg2 << 16u) | (dg3 << 24u); f += a + sineparts[j] + u; a = d; @@ -342,23 +349,23 @@ static void ICCComputeMD5(const std::vector<uint8_t>& data, uint8_t sum[16]) sum[15] = d0 >> 24u; } -static Status CreateICCChadMatrix(double wx, double wy, float result[9]) { - float m[9]; +static Status CreateICCChadMatrix(double wx, double wy, Matrix3x3& result) { + Matrix3x3 m; if (wy == 0) { // WhitePoint can not be pitch-black. return JXL_FAILURE("Invalid WhitePoint"); } JXL_RETURN_IF_ERROR(AdaptToXYZD50(wx, wy, m)); - memcpy(result, m, sizeof(float) * 9); + result = m; return true; } // Creates RGB to XYZ matrix given RGB primaries and whitepoint in xy. static Status CreateICCRGBMatrix(double rx, double ry, double gx, double gy, double bx, double by, double wx, double wy, - float result[9]) { - float m[9]; + Matrix3x3& result) { + Matrix3x3 m; JXL_RETURN_IF_ERROR(PrimariesToXYZD50(rx, ry, gx, gy, bx, by, wx, wy, m)); - memcpy(result, m, sizeof(float) * 9); + result = m; return true; } @@ -433,8 +440,12 @@ static Status CreateICCHeader(const JxlColorEncoding& c, // Three uint32_t's date/time encoding. // TODO(lode): encode actual date and time, this is a placeholder - uint32_t year = 2019, month = 12, day = 1; - uint32_t hour = 0, minute = 0, second = 0; + uint32_t year = 2019; + uint32_t month = 12; + uint32_t day = 1; + uint32_t hour = 0; + uint32_t minute = 0; + uint32_t second = 0; WriteICCUint16(year, 24, header); WriteICCUint16(month, 26, header); WriteICCUint16(day, 28, header); @@ -491,13 +502,13 @@ static void CreateICCMlucTag(const std::string& text, WriteICCTag("enUS", tags->size(), tags); WriteICCUint32(text.size() * 2, tags->size(), tags); WriteICCUint32(28, tags->size(), tags); - for (size_t i = 0; i < text.size(); i++) { + for (char c : text) { tags->push_back(0); // prepend 0 for UTF-16 - tags->push_back(text[i]); + tags->push_back(c); } } -static Status CreateICCXYZTag(float xyz[3], std::vector<uint8_t>* tags) { +static Status CreateICCXYZTag(const Color& xyz, std::vector<uint8_t>* tags) { WriteICCTag("XYZ ", tags->size(), tags); WriteICCUint32(0, tags->size(), tags); for (size_t i = 0; i < 3; ++i) { @@ -506,11 +517,14 @@ static Status CreateICCXYZTag(float xyz[3], std::vector<uint8_t>* tags) { return true; } -static Status CreateICCChadTag(float chad[9], std::vector<uint8_t>* tags) { +static Status CreateICCChadTag(const Matrix3x3& chad, + std::vector<uint8_t>* tags) { WriteICCTag("sf32", tags->size(), tags); WriteICCUint32(0, tags->size(), tags); - for (size_t i = 0; i < 9; i++) { - JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(chad[i], tags->size(), tags)); + for (size_t j = 0; j < 3; j++) { + for (size_t i = 0; i < 3; i++) { + JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(chad[j][i], tags->size(), tags)); + } } return true; } @@ -573,8 +587,8 @@ static Status CreateICCCurvParaTag(std::vector<float> params, size_t curve_type, WriteICCUint32(0, tags->size(), tags); WriteICCUint16(curve_type, tags->size(), tags); WriteICCUint16(0, tags->size(), tags); - for (size_t i = 0; i < params.size(); i++) { - JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(params[i], tags->size(), tags)); + for (float param : params) { + JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(param, tags->size(), tags)); } return true; } @@ -649,8 +663,8 @@ static Status CreateICCLutAtoBTagForXYB(std::vector<uint8_t>* tags) { -0.050022, 0.5683655, -0.018344, -1.387676, 1.1145555, 0.6857255}; // 12 * 4 = 48 bytes - for (size_t i = 0; i < 9; ++i) { - JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(matrix[i], tags->size(), tags)); + for (double v : matrix) { + JXL_RETURN_IF_ERROR(WriteICCS15Fixed16(v, tags->size(), tags)); } for (size_t i = 0; i < 3; ++i) { float intercept = 0; @@ -880,7 +894,9 @@ static std::string ColorEncodingDescriptionImpl(const JxlColorEncoding& c) { static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, std::vector<uint8_t>* icc) { - std::vector<uint8_t> header, tagtable, tags; + std::vector<uint8_t> header; + std::vector<uint8_t> tagtable; + std::vector<uint8_t> tags; JxlTransferFunction tf = c.transfer_function; if (c.color_space == JXL_COLOR_SPACE_UNKNOWN || tf == JXL_TRANSFER_FUNCTION_UNKNOWN) { @@ -910,7 +926,8 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, // tag count, deferred to later WriteICCUint32(0, tagtable.size(), &tagtable); - size_t tag_offset = 0, tag_size = 0; + size_t tag_offset = 0; + size_t tag_size = 0; CreateICCMlucTag(ColorEncodingDescriptionImpl(c), &tags); FinalizeICCTag(&tags, &tag_offset, &tag_size); @@ -923,12 +940,12 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, // TODO(eustas): isn't it the other way round: gray image has d50 WhitePoint? if (c.color_space == JXL_COLOR_SPACE_GRAY) { - float wtpt[3]; + Color wtpt; JXL_RETURN_IF_ERROR( CIEXYZFromWhiteCIExy(c.white_point_xy[0], c.white_point_xy[1], wtpt)); JXL_RETURN_IF_ERROR(CreateICCXYZTag(wtpt, &tags)); } else { - float d50[3] = {0.964203, 1.0, 0.824905}; + Color d50{0.964203, 1.0, 0.824905}; JXL_RETURN_IF_ERROR(CreateICCXYZTag(d50, &tags)); } FinalizeICCTag(&tags, &tag_offset, &tag_size); @@ -936,7 +953,7 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, if (c.color_space != JXL_COLOR_SPACE_GRAY) { // Chromatic adaptation matrix - float chad[9]; + Matrix3x3 chad; JXL_RETURN_IF_ERROR( CreateICCChadMatrix(c.white_point_xy[0], c.white_point_xy[1], chad)); @@ -949,14 +966,14 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, MaybeCreateICCCICPTag(c, &tags, &tag_offset, &tag_size, &tagtable, &offsets); - float m[9]; + Matrix3x3 m; JXL_RETURN_IF_ERROR(CreateICCRGBMatrix( c.primaries_red_xy[0], c.primaries_red_xy[1], c.primaries_green_xy[0], c.primaries_green_xy[1], c.primaries_blue_xy[0], c.primaries_blue_xy[1], c.white_point_xy[0], c.white_point_xy[1], m)); - float r[3] = {m[0], m[3], m[6]}; - float g[3] = {m[1], m[4], m[7]}; - float b[3] = {m[2], m[5], m[8]}; + Color r{m[0][0], m[1][0], m[2][0]}; + Color g{m[0][1], m[1][1], m[2][1]}; + Color b{m[0][2], m[1][2], m[2][2]}; JXL_RETURN_IF_ERROR(CreateICCXYZTag(r, &tags)); FinalizeICCTag(&tags, &tag_offset, &tag_size); @@ -1042,8 +1059,8 @@ static Status MaybeCreateProfileImpl(const JxlColorEncoding& c, WriteICCUint32(header.size() + tagtable.size() + tags.size(), 0, &header); *icc = header; - Bytes(tagtable).AppendTo(icc); - Bytes(tags).AppendTo(icc); + Bytes(tagtable).AppendTo(*icc); + Bytes(tags).AppendTo(*icc); // The MD5 checksum must be computed on the profile with profile flags, // rendering intent, and region of the checksum itself, set to 0. diff --git a/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h b/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h index 48e8e254f7..69dcd0b512 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h +++ b/third_party/jpeg-xl/lib/jxl/cms/opsin_params.h @@ -6,7 +6,7 @@ #ifndef LIB_JXL_CMS_OPSIN_PARAMS_H_ #define LIB_JXL_CMS_OPSIN_PARAMS_H_ -#include <array> +#include "lib/jxl/base/matrix_ops.h" // Constants that define the XYB color space. @@ -35,21 +35,20 @@ constexpr float kOpsinAbsorbanceBias1 = kOpsinAbsorbanceBias0; constexpr float kOpsinAbsorbanceBias2 = kOpsinAbsorbanceBias0; // Opsin absorbance matrix is now frozen. -constexpr std::array<float, 9> kOpsinAbsorbanceMatrix = { - kM00, kM01, kM02, kM10, kM11, kM12, kM20, kM21, kM22, -}; +constexpr Matrix3x3 kOpsinAbsorbanceMatrix{ + {{kM00, kM01, kM02}, {kM10, kM11, kM12}, {kM20, kM21, kM22}}}; -constexpr std::array<float, 9> kDefaultInverseOpsinAbsorbanceMatrix = { - 11.031566901960783f, -9.866943921568629f, -0.16462299647058826f, - -3.254147380392157f, 4.418770392156863f, -0.16462299647058826f, - -3.6588512862745097f, 2.7129230470588235f, 1.9459282392156863f}; +constexpr Matrix3x3 kDefaultInverseOpsinAbsorbanceMatrix{ + {{11.031566901960783f, -9.866943921568629f, -0.16462299647058826f}, + {-3.254147380392157f, 4.418770392156863f, -0.16462299647058826f}, + {-3.6588512862745097f, 2.7129230470588235f, 1.9459282392156863f}}}; // Must be the inverse matrix of kOpsinAbsorbanceMatrix and match the spec. -static inline const float* DefaultInverseOpsinAbsorbanceMatrix() { - return kDefaultInverseOpsinAbsorbanceMatrix.data(); +static inline const Matrix3x3& DefaultInverseOpsinAbsorbanceMatrix() { + return kDefaultInverseOpsinAbsorbanceMatrix; } -constexpr std::array<float, 3> kOpsinAbsorbanceBias = { +constexpr Vector3 kOpsinAbsorbanceBias = { kOpsinAbsorbanceBias0, kOpsinAbsorbanceBias1, kOpsinAbsorbanceBias2, @@ -63,14 +62,14 @@ constexpr float kScaledXYBOffset0 = 0.015386134f; constexpr float kScaledXYBOffset1 = 0.0f; constexpr float kScaledXYBOffset2 = 0.27770459f; -constexpr std::array<float, 3> kScaledXYBOffset = { - kScaledXYBOffset0, kScaledXYBOffset1, kScaledXYBOffset2}; +constexpr Vector3 kScaledXYBOffset = {kScaledXYBOffset0, kScaledXYBOffset1, + kScaledXYBOffset2}; constexpr float kScaledXYBScale0 = 22.995788804f; constexpr float kScaledXYBScale1 = 1.183000077f; constexpr float kScaledXYBScale2 = 1.502141333f; -constexpr std::array<float, 3> kScaledXYBScale = { +constexpr Vector3 kScaledXYBScale = { kScaledXYBScale0, kScaledXYBScale1, kScaledXYBScale2, diff --git a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h index 3d94ccea12..7d09beed32 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h +++ b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping-inl.h @@ -100,14 +100,14 @@ class HlgOOTF : HlgOOTF_Base { using HlgOOTF_Base::HlgOOTF_Base; static HlgOOTF FromSceneLight(float display_luminance, - const float primaries_luminances[3]) { + const Vector3& primaries_luminances) { return HlgOOTF(/*gamma=*/1.2f * std::pow(1.111f, std::log2(display_luminance / 1000.f)), primaries_luminances); } static HlgOOTF ToSceneLight(float display_luminance, - const float primaries_luminances[3]) { + const Vector3& primaries_luminances) { return HlgOOTF( /*gamma=*/(1 / 1.2f) * std::pow(1.111f, -std::log2(display_luminance / 1000.f)), @@ -132,7 +132,7 @@ class HlgOOTF : HlgOOTF_Base { }; template <typename V> -void GamutMap(V* red, V* green, V* blue, const float primaries_luminances[3], +void GamutMap(V* red, V* green, V* blue, const Vector3& primaries_luminances, float preserve_saturation = 0.1f) { hwy::HWY_NAMESPACE::DFromV<V> df; const V luminance = diff --git a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h index a114109ea6..81f301a31d 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h +++ b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping.h @@ -12,6 +12,7 @@ #include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/base/matrix_ops.h" #include "lib/jxl/cms/transfer_functions.h" namespace jxl { @@ -20,7 +21,7 @@ class Rec2408ToneMapperBase { public: explicit Rec2408ToneMapperBase(std::pair<float, float> source_range, std::pair<float, float> target_range, - const float primaries_luminances[3]) + const Vector3& primaries_luminances) : source_range_(source_range), target_range_(target_range), red_Y_(primaries_luminances[0]), @@ -28,10 +29,10 @@ class Rec2408ToneMapperBase { blue_Y_(primaries_luminances[2]) {} // TODO(eustas): test me - void ToneMap(float* red, float* green, float* blue) const { + void ToneMap(Color& rgb) const { const float luminance = source_range_.second * - (red_Y_ * *red + green_Y_ * *green + blue_Y_ * *blue); + (red_Y_ * rgb[0] + green_Y_ * rgb[1] + blue_Y_ * rgb[2]); const float normalized_pq = std::min(1.f, (InvEOTF(luminance) - pq_mastering_min_) * inv_pq_mastering_range_); @@ -49,8 +50,8 @@ class Rec2408ToneMapperBase { const float ratio = new_luminance / std::max(luminance, min_luminance); const float cap = new_luminance * inv_target_peak_; const float multiplier = ratio * normalizer_; - for (float* const val : {red, green, blue}) { - *val = use_cap ? cap : *val * multiplier; + for (size_t idx : {0, 1, 2}) { + rgb[idx] = use_cap ? cap : rgb[idx] * multiplier; } } @@ -96,23 +97,24 @@ class Rec2408ToneMapperBase { class HlgOOTF_Base { public: explicit HlgOOTF_Base(float source_luminance, float target_luminance, - const float primaries_luminances[3]) + const Vector3& primaries_luminances) : HlgOOTF_Base(/*gamma=*/std::pow(1.111f, std::log2(target_luminance / source_luminance)), primaries_luminances) {} // TODO(eustas): test me - void Apply(float* red, float* green, float* blue) const { + void Apply(Color& rgb) const { if (!apply_ootf_) return; - const float luminance = red_Y_ * *red + green_Y_ * *green + blue_Y_ * *blue; + const float luminance = + red_Y_ * rgb[0] + green_Y_ * rgb[1] + blue_Y_ * rgb[2]; const float ratio = std::min<float>(powf(luminance, exponent_), 1e9); - *red *= ratio; - *green *= ratio; - *blue *= ratio; + rgb[0] *= ratio; + rgb[1] *= ratio; + rgb[2] *= ratio; } protected: - explicit HlgOOTF_Base(float gamma, const float luminances[3]) + explicit HlgOOTF_Base(float gamma, const Vector3& luminances) : exponent_(gamma - 1), red_Y_(luminances[0]), green_Y_(luminances[1]), @@ -124,13 +126,12 @@ class HlgOOTF_Base { const float blue_Y_; }; -static JXL_MAYBE_UNUSED void GamutMapScalar(float* red, float* green, - float* blue, - const float primaries_luminances[3], +static JXL_MAYBE_UNUSED void GamutMapScalar(Color& rgb, + const Vector3& primaries_luminances, float preserve_saturation = 0.1f) { - const float luminance = primaries_luminances[0] * *red + - primaries_luminances[1] * *green + - primaries_luminances[2] * *blue; + const float luminance = primaries_luminances[0] * rgb[0] + + primaries_luminances[1] * rgb[1] + + primaries_luminances[2] * rgb[2]; // Desaturate out-of-gamut pixels. This is done by mixing each pixel // with just enough gray of the target luminance to make all @@ -142,8 +143,8 @@ static JXL_MAYBE_UNUSED void GamutMapScalar(float* red, float* green, // done by mixing in yet more gray. That will desaturate it further. float gray_mix_saturation = 0.0f; float gray_mix_luminance = 0.0f; - for (const float* ch : {red, green, blue}) { - const float& val = *ch; + for (size_t idx : {0, 1, 2}) { + const float& val = rgb[idx]; const float val_minus_gray = val - luminance; const float inv_val_minus_gray = 1.0f / ((val_minus_gray == 0.0f) ? 1.0f : val_minus_gray); @@ -162,15 +163,14 @@ static JXL_MAYBE_UNUSED void GamutMapScalar(float* red, float* green, Clamp1((preserve_saturation * (gray_mix_saturation - gray_mix_luminance) + gray_mix_luminance), 0.0f, 1.0f); - for (float* const ch : {red, green, blue}) { - float& val = *ch; + for (size_t idx : {0, 1, 2}) { + float& val = rgb[idx]; val = gray_mix * (luminance - val) + val; } - const float max_clr = std::max({1.0f, *red, *green, *blue}); + const float max_clr = std::max({1.0f, rgb[0], rgb[1], rgb[2]}); const float normalizer = 1.0f / max_clr; - for (float* const ch : {red, green, blue}) { - float& val = *ch; - val *= normalizer; + for (size_t idx : {0, 1, 2}) { + rgb[idx] *= normalizer; } } diff --git a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc index dda2bbb0aa..76165d26e7 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc +++ b/third_party/jpeg-xl/lib/jxl/cms/tone_mapping_test.cc @@ -30,17 +30,17 @@ HWY_NOINLINE void TestRec2408ToneMap() { for (size_t i = 0; i < kNumTrials; i++) { float src = 11000.0 + rng.UniformF(-150.0f, 150.0f); float tgt = 250 + rng.UniformF(-5.0f, 5.0f); - float luminances[3] = {rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), - rng.UniformF(0.2f, 0.4f)}; - float rgb[3] = {rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), - rng.UniformF(0.0f, 1.0f)}; + Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), + rng.UniformF(0.2f, 0.4f)}; + Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), + rng.UniformF(0.0f, 1.0f)}; Rec2408ToneMapper<decltype(d)> tone_mapper({0, src}, {0, tgt}, luminances); auto r = Set(d, rgb[0]); auto g = Set(d, rgb[1]); auto b = Set(d, rgb[2]); tone_mapper.ToneMap(&r, &g, &b); Rec2408ToneMapperBase tone_mapper_base({0, src}, {0, tgt}, luminances); - tone_mapper_base.ToneMap(&rgb[0], &rgb[1], &rgb[2]); + tone_mapper_base.ToneMap(rgb); const float actual_r = GetLane(r); const float expected_r = rgb[0]; const float abs_err_r = std::abs(expected_r - actual_r); @@ -66,17 +66,17 @@ HWY_NOINLINE void TestHlgOotfApply() { for (size_t i = 0; i < kNumTrials; i++) { float src = 300.0 + rng.UniformF(-50.0f, 50.0f); float tgt = 80 + rng.UniformF(-5.0f, 5.0f); - float luminances[3] = {rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), - rng.UniformF(0.2f, 0.4f)}; - float rgb[3] = {rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), - rng.UniformF(0.0f, 1.0f)}; + Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), + rng.UniformF(0.2f, 0.4f)}; + Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), + rng.UniformF(0.0f, 1.0f)}; HlgOOTF ootf(src, tgt, luminances); auto r = Set(d, rgb[0]); auto g = Set(d, rgb[1]); auto b = Set(d, rgb[2]); ootf.Apply(&r, &g, &b); HlgOOTF_Base ootf_base(src, tgt, luminances); - ootf_base.Apply(&rgb[0], &rgb[1], &rgb[2]); + ootf_base.Apply(rgb); const float actual_r = GetLane(r); const float expected_r = rgb[0]; const float abs_err_r = std::abs(expected_r - actual_r); @@ -101,15 +101,15 @@ HWY_NOINLINE void TestGamutMap() { HWY_FULL(float) d; for (size_t i = 0; i < kNumTrials; i++) { float preserve_saturation = rng.UniformF(0.2f, 0.4f); - float luminances[3] = {rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), - rng.UniformF(0.2f, 0.4f)}; - float rgb[3] = {rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), - rng.UniformF(0.0f, 1.0f)}; + Vector3 luminances{rng.UniformF(0.2f, 0.4f), rng.UniformF(0.2f, 0.4f), + rng.UniformF(0.2f, 0.4f)}; + Color rgb{rng.UniformF(0.0f, 1.0f), rng.UniformF(0.0f, 1.0f), + rng.UniformF(0.0f, 1.0f)}; auto r = Set(d, rgb[0]); auto g = Set(d, rgb[1]); auto b = Set(d, rgb[2]); GamutMap(&r, &g, &b, luminances, preserve_saturation); - GamutMapScalar(&rgb[0], &rgb[1], &rgb[2], luminances, preserve_saturation); + GamutMapScalar(rgb, luminances, preserve_saturation); const float actual_r = GetLane(r); const float expected_r = rgb[0]; const float abs_err_r = std::abs(expected_r - actual_r); diff --git a/third_party/jpeg-xl/lib/jxl/coeff_order.h b/third_party/jpeg-xl/lib/jxl/coeff_order.h index 75f6f99e9f..79c0c976c9 100644 --- a/third_party/jpeg-xl/lib/jxl/coeff_order.h +++ b/third_party/jpeg-xl/lib/jxl/coeff_order.h @@ -10,10 +10,10 @@ #include <stdint.h> #include "lib/jxl/ac_strategy.h" +#include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/status.h" #include "lib/jxl/coeff_order_fwd.h" -#include "lib/jxl/dct_util.h" #include "lib/jxl/frame_dimensions.h" namespace jxl { @@ -21,22 +21,21 @@ namespace jxl { class BitReader; // Those offsets get multiplied by kDCTBlockSize. -static constexpr size_t kCoeffOrderOffset[] = { + +static constexpr size_t kCoeffOrderLimit = 6156; + +static constexpr std::array<size_t, 3 * kNumOrders + 1> kCoeffOrderOffset = { 0, 1, 2, 3, 4, 5, 6, 10, 14, 18, 34, 50, 66, 68, 70, 72, 76, 80, 84, 92, 100, 108, 172, 236, 300, 332, 364, 396, 652, 908, - 1164, 1292, 1420, 1548, 2572, 3596, 4620, 5132, 5644, 6156, -}; -static_assert(3 * kNumOrders + 1 == - sizeof(kCoeffOrderOffset) / sizeof(*kCoeffOrderOffset), - "Update this array when adding or removing order types."); + 1164, 1292, 1420, 1548, 2572, 3596, 4620, 5132, 5644, kCoeffOrderLimit}; -static constexpr size_t CoeffOrderOffset(size_t order, size_t c) { - return kCoeffOrderOffset[3 * order + c] * kDCTBlockSize; -} +// TODO(eustas): rollback to constexpr once modern C++ becomes reuired. +#define CoeffOrderOffset(O, C) \ + (kCoeffOrderOffset[3 * (O) + (C)] * kDCTBlockSize) -static constexpr size_t kCoeffOrderMaxSize = - kCoeffOrderOffset[3 * kNumOrders] * kDCTBlockSize; +static JXL_MAYBE_UNUSED constexpr size_t kCoeffOrderMaxSize = + kCoeffOrderLimit * kDCTBlockSize; // Mapping from AC strategy to order bucket. Strategies with different natural // orders must have different buckets. @@ -49,7 +48,7 @@ static_assert(AcStrategy::kNumValidStrategies == sizeof(kStrategyOrder) / sizeof(*kStrategyOrder), "Update this array when adding or removing AC strategies."); -constexpr uint32_t kPermutationContexts = 8; +constexpr JXL_MAYBE_UNUSED uint32_t kPermutationContexts = 8; uint32_t CoeffOrderContext(uint32_t val); diff --git a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc index 19273dad3c..895faaa07d 100644 --- a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc +++ b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc @@ -47,12 +47,12 @@ std::array<ColorEncoding, 2> ColorEncoding::CreateC2(Primaries pr, const ColorEncoding& ColorEncoding::SRGB(bool is_gray) { static std::array<ColorEncoding, 2> c2 = CreateC2(Primaries::kSRGB, TransferFunction::kSRGB); - return c2[is_gray]; + return c2[is_gray ? 1 : 0]; } const ColorEncoding& ColorEncoding::LinearSRGB(bool is_gray) { static std::array<ColorEncoding, 2> c2 = CreateC2(Primaries::kSRGB, TransferFunction::kLinear); - return c2[is_gray]; + return c2[is_gray ? 1 : 0]; } Status ColorEncoding::SetWhitePointType(const WhitePoint& wp) { diff --git a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h index 0a104a12b2..61e4628dbd 100644 --- a/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h +++ b/third_party/jpeg-xl/lib/jxl/color_encoding_internal.h @@ -314,7 +314,6 @@ class ColorSpaceTransform { Status Init(const ColorEncoding& c_src, const ColorEncoding& c_dst, float intensity_target, size_t xsize, size_t num_threads) { - xsize_ = xsize; JxlColorProfile input_profile; icc_src_ = c_src.ICC(); input_profile.icc.data = icc_src_.data(); @@ -343,8 +342,10 @@ class ColorSpaceTransform { return cms_.get_dst_buf(cms_data_, thread); } - Status Run(const size_t thread, const float* buf_src, float* buf_dst) { - return cms_.run(cms_data_, thread, buf_src, buf_dst, xsize_); + Status Run(const size_t thread, const float* buf_src, float* buf_dst, + size_t xsize) { + // TODO(eustas): convert false to Status? + return FROM_JXL_BOOL(cms_.run(cms_data_, thread, buf_src, buf_dst, xsize)); } private: @@ -353,7 +354,6 @@ class ColorSpaceTransform { // The interface may retain pointers into these. IccBytes icc_src_; IccBytes icc_dst_; - size_t xsize_; }; } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/color_encoding_internal_test.cc b/third_party/jpeg-xl/lib/jxl/color_encoding_internal_test.cc index 4d2d3e8119..2a9f60427a 100644 --- a/third_party/jpeg-xl/lib/jxl/color_encoding_internal_test.cc +++ b/third_party/jpeg-xl/lib/jxl/color_encoding_internal_test.cc @@ -97,24 +97,29 @@ TEST(ColorEncodingTest, InternalExternalConversion) { ColorEncoding source_internal; ColorEncoding destination_internal; + const auto rand_float = []() { + return (static_cast<float>(rand()) / static_cast<float>(RAND_MAX) * 0.5) + + 0.25; + }; + for (int i = 0; i < 100; i++) { source_internal.color_space = static_cast<ColorSpace>(rand() % 4); CIExy wp; - wp.x = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25; - wp.y = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25; + wp.x = rand_float(); + wp.y = rand_float(); EXPECT_TRUE(source_internal.SetWhitePoint(wp)); if (source_internal.HasPrimaries()) { PrimariesCIExy primaries; - primaries.r.x = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25; - primaries.r.y = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25; - primaries.g.x = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25; - primaries.g.y = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25; - primaries.b.x = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25; - primaries.b.y = (float(rand()) / float((RAND_MAX)) * 0.5) + 0.25; + primaries.r.x = rand_float(); + primaries.r.y = rand_float(); + primaries.g.x = rand_float(); + primaries.g.y = rand_float(); + primaries.b.x = rand_float(); + primaries.b.y = rand_float(); EXPECT_TRUE(source_internal.SetPrimaries(primaries)); } jxl::cms::CustomTransferFunction tf; - EXPECT_TRUE(tf.SetGamma((float(rand()) / float((RAND_MAX)) * 0.5) + 0.25)); + EXPECT_TRUE(tf.SetGamma(rand_float())); source_internal.tf = tf; source_internal.rendering_intent = static_cast<RenderingIntent>(rand() % 4); diff --git a/third_party/jpeg-xl/lib/jxl/color_management_test.cc b/third_party/jpeg-xl/lib/jxl/color_management_test.cc index ca50c9960e..b2d47c73f9 100644 --- a/third_party/jpeg-xl/lib/jxl/color_management_test.cc +++ b/third_party/jpeg-xl/lib/jxl/color_management_test.cc @@ -45,55 +45,10 @@ std::ostream& operator<<(std::ostream& os, const PrimariesCIExy& primaries) { namespace { -using ::testing::ElementsAre; -using ::testing::FloatNear; - // Small enough to be fast. If changed, must update Generate*. -static constexpr size_t kWidth = 16; - -static constexpr size_t kNumThreads = 1; // only have a single row. +constexpr size_t kWidth = 16; -MATCHER_P(HasSameFieldsAs, expected, "") { - if (arg.GetRenderingIntent() != expected.GetRenderingIntent()) { - *result_listener << "which has a different rendering intent: " - << ToString(arg.GetRenderingIntent()) << " instead of " - << ToString(expected.GetRenderingIntent()); - return false; - } - if (arg.GetColorSpace() != expected.GetColorSpace()) { - *result_listener << "which has a different color space: " - << ToString(arg.GetColorSpace()) << " instead of " - << ToString(expected.GetColorSpace()); - return false; - } - if (arg.GetWhitePointType() != expected.GetWhitePointType()) { - *result_listener << "which has a different white point: " - << ToString(arg.GetWhitePointType()) << " instead of " - << ToString(expected.GetWhitePointType()); - return false; - } - if (arg.HasPrimaries() && - arg.GetPrimariesType() != expected.GetPrimariesType()) { - *result_listener << "which has different primaries: " - << ToString(arg.GetPrimariesType()) << " instead of " - << ToString(expected.GetPrimariesType()); - return false; - } - if (!arg.Tf().IsSame(expected.Tf())) { - static const auto tf_to_string = - [](const jxl::cms::CustomTransferFunction& tf) { - if (tf.have_gamma) { - return "g" + ToString(tf.GetGamma()); - } - return ToString(tf.transfer_function); - }; - *result_listener << "which has a different transfer function: " - << tf_to_string(arg.Tf()) << " instead of " - << tf_to_string(expected.Tf()); - return false; - } - return true; -} +constexpr size_t kNumThreads = 1; // only have a single row. struct Globals { // TODO(deymo): Make this a const. @@ -106,15 +61,15 @@ struct Globals { Globals() { in_gray = GenerateGray(); in_color = GenerateColor(); - out_gray = ImageF(kWidth, 1); - out_color = ImageF(kWidth * 3, 1); + JXL_ASSIGN_OR_DIE(out_gray, ImageF::Create(kWidth, 1)); + JXL_ASSIGN_OR_DIE(out_color, ImageF::Create(kWidth * 3, 1)); c_native = ColorEncoding::LinearSRGB(/*is_gray=*/false); c_gray = ColorEncoding::LinearSRGB(/*is_gray=*/true); } static ImageF GenerateGray() { - ImageF gray(kWidth, 1); + JXL_ASSIGN_OR_DIE(ImageF gray, ImageF::Create(kWidth, 1)); float* JXL_RESTRICT row = gray.Row(0); // Increasing left to right for (uint32_t x = 0; x < kWidth; ++x) { @@ -124,7 +79,7 @@ struct Globals { } static ImageF GenerateColor() { - ImageF image(kWidth * 3, 1); + JXL_ASSIGN_OR_DIE(ImageF image, ImageF::Create(kWidth * 3, 1)); float* JXL_RESTRICT interleaved = image.Row(0); std::fill(interleaved, interleaved + kWidth * 3, 0.0f); @@ -182,8 +137,10 @@ class ColorManagementTest const size_t thread = 0; const ImageF& in = c.IsGray() ? g->in_gray : g->in_color; ImageF* JXL_RESTRICT out = c.IsGray() ? &g->out_gray : &g->out_color; - ASSERT_TRUE(xform_fwd.Run(thread, in.Row(0), xform_fwd.BufDst(thread))); - ASSERT_TRUE(xform_rev.Run(thread, xform_fwd.BufDst(thread), out->Row(0))); + ASSERT_TRUE( + xform_fwd.Run(thread, in.Row(0), xform_fwd.BufDst(thread), kWidth)); + ASSERT_TRUE( + xform_rev.Run(thread, xform_fwd.BufDst(thread), out->Row(0), kWidth)); // With lcms2, this value is lower: 5E-5 double max_l1 = 7E-4; @@ -200,77 +157,108 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(ColorManagementTestInstantiation, // Exercises the ColorManagement interface for ALL ColorEncoding synthesizable // via enums. TEST_P(ColorManagementTest, VerifyAllProfiles) { - ColorEncoding c = ColorEncodingFromDescriptor(GetParam()); - printf("%s\n", Description(c).c_str()); + ColorEncoding actual = ColorEncodingFromDescriptor(GetParam()); + printf("%s\n", Description(actual).c_str()); // Can create profile. - ASSERT_TRUE(c.CreateICC()); + ASSERT_TRUE(actual.CreateICC()); // Can set an equivalent ColorEncoding from the generated ICC profile. - ColorEncoding c3; - ASSERT_TRUE(c3.SetICC(IccBytes(c.ICC()), JxlGetDefaultCms())); - EXPECT_THAT(c3, HasSameFieldsAs(c)); + ColorEncoding expected; + ASSERT_TRUE(expected.SetICC(IccBytes(actual.ICC()), JxlGetDefaultCms())); + + EXPECT_EQ(actual.GetRenderingIntent(), expected.GetRenderingIntent()) + << "different rendering intent: " << ToString(actual.GetRenderingIntent()) + << " instead of " << ToString(expected.GetRenderingIntent()); + EXPECT_EQ(actual.GetColorSpace(), expected.GetColorSpace()) + << "different color space: " << ToString(actual.GetColorSpace()) + << " instead of " << ToString(expected.GetColorSpace()); + EXPECT_EQ(actual.GetWhitePointType(), expected.GetWhitePointType()) + << "different white point: " << ToString(actual.GetWhitePointType()) + << " instead of " << ToString(expected.GetWhitePointType()); + EXPECT_EQ(actual.HasPrimaries(), expected.HasPrimaries()); + if (actual.HasPrimaries()) { + EXPECT_EQ(actual.GetPrimariesType(), expected.GetPrimariesType()) + << "different primaries: " << ToString(actual.GetPrimariesType()) + << " instead of " << ToString(expected.GetPrimariesType()); + } - VerifyPixelRoundTrip(c); -} + static const auto tf_to_string = + [](const jxl::cms::CustomTransferFunction& tf) { + if (tf.have_gamma) { + return "g" + ToString(tf.GetGamma()); + } + return ToString(tf.transfer_function); + }; + EXPECT_TRUE(actual.Tf().IsSame(expected.Tf())) + << "different transfer function: " << tf_to_string(actual.Tf()) + << " instead of " << tf_to_string(expected.Tf()); -testing::Matcher<CIExy> CIExyIs(const double x, const double y) { - static constexpr double kMaxError = 1e-4; - return testing::AllOf( - testing::Field(&CIExy::x, testing::DoubleNear(x, kMaxError)), - testing::Field(&CIExy::y, testing::DoubleNear(y, kMaxError))); + VerifyPixelRoundTrip(actual); } -testing::Matcher<PrimariesCIExy> PrimariesAre( - const testing::Matcher<CIExy>& r, const testing::Matcher<CIExy>& g, - const testing::Matcher<CIExy>& b) { - return testing::AllOf(testing::Field(&PrimariesCIExy::r, r), - testing::Field(&PrimariesCIExy::g, g), - testing::Field(&PrimariesCIExy::b, b)); -} +#define EXPECT_CIEXY_NEAR(A, E, T) \ + { \ + CIExy _actual = (A); \ + CIExy _expected = (E); \ + double _tolerance = (T); \ + EXPECT_NEAR(_actual.x, _expected.x, _tolerance) << "x is different"; \ + EXPECT_NEAR(_actual.y, _expected.y, _tolerance) << "y is different"; \ + } + +#define EXPECT_PRIMARIES_NEAR(A, E, T) \ + { \ + PrimariesCIExy _actual = (A); \ + PrimariesCIExy _expected = (E); \ + double _tolerance = (T); \ + EXPECT_NEAR(_actual.r.x, _expected.r.x, _tolerance) << "r.x is different"; \ + EXPECT_NEAR(_actual.r.y, _expected.r.y, _tolerance) << "r.y is different"; \ + EXPECT_NEAR(_actual.g.x, _expected.g.x, _tolerance) << "g.x is different"; \ + EXPECT_NEAR(_actual.g.y, _expected.g.y, _tolerance) << "g.y is different"; \ + EXPECT_NEAR(_actual.b.x, _expected.b.x, _tolerance) << "b.x is different"; \ + EXPECT_NEAR(_actual.b.y, _expected.b.y, _tolerance) << "b.y is different"; \ + } TEST_F(ColorManagementTest, sRGBChromaticity) { const ColorEncoding sRGB = ColorEncoding::SRGB(); - EXPECT_THAT(sRGB.GetWhitePoint(), CIExyIs(0.3127, 0.3290)); - EXPECT_THAT(sRGB.GetPrimaries(), - PrimariesAre(CIExyIs(0.64, 0.33), CIExyIs(0.30, 0.60), - CIExyIs(0.15, 0.06))); + EXPECT_CIEXY_NEAR(sRGB.GetWhitePoint(), CIExy(0.3127, 0.3290), 1e-4); + PrimariesCIExy srgb_primaries = {{0.64, 0.33}, {0.30, 0.60}, {0.15, 0.06}}; + EXPECT_PRIMARIES_NEAR(sRGB.GetPrimaries(), srgb_primaries, 1e-4); } TEST_F(ColorManagementTest, D2700Chromaticity) { std::vector<uint8_t> icc_data = jxl::test::ReadTestData("jxl/color_management/sRGB-D2700.icc"); IccBytes icc; - Bytes(icc_data).AppendTo(&icc); + Bytes(icc_data).AppendTo(icc); ColorEncoding sRGB_D2700; ASSERT_TRUE(sRGB_D2700.SetICC(std::move(icc), JxlGetDefaultCms())); - EXPECT_THAT(sRGB_D2700.GetWhitePoint(), CIExyIs(0.45986, 0.41060)); + EXPECT_CIEXY_NEAR(sRGB_D2700.GetWhitePoint(), CIExy(0.45986, 0.41060), 1e-4); // The illuminant-relative chromaticities of this profile's primaries are the // same as for sRGB. It is the PCS-relative chromaticities that would be // different. - EXPECT_THAT(sRGB_D2700.GetPrimaries(), - PrimariesAre(CIExyIs(0.64, 0.33), CIExyIs(0.30, 0.60), - CIExyIs(0.15, 0.06))); + PrimariesCIExy srgb_primaries = {{0.64, 0.33}, {0.30, 0.60}, {0.15, 0.06}}; + EXPECT_PRIMARIES_NEAR(sRGB_D2700.GetPrimaries(), srgb_primaries, 1e-4); } TEST_F(ColorManagementTest, D2700ToSRGB) { std::vector<uint8_t> icc_data = jxl::test::ReadTestData("jxl/color_management/sRGB-D2700.icc"); IccBytes icc; - Bytes(icc_data).AppendTo(&icc); + Bytes(icc_data).AppendTo(icc); ColorEncoding sRGB_D2700; ASSERT_TRUE(sRGB_D2700.SetICC(std::move(icc), JxlGetDefaultCms())); ColorSpaceTransform transform(*JxlGetDefaultCms()); ASSERT_TRUE(transform.Init(sRGB_D2700, ColorEncoding::SRGB(), kDefaultIntensityTarget, 1, 1)); - const float sRGB_D2700_values[3] = {0.863, 0.737, 0.490}; - float sRGB_values[3]; - ASSERT_TRUE(transform.Run(0, sRGB_D2700_values, sRGB_values)); - EXPECT_THAT(sRGB_values, - ElementsAre(FloatNear(0.914, 1e-3), FloatNear(0.745, 1e-3), - FloatNear(0.601, 1e-3))); + Color sRGB_D2700_values{0.863, 0.737, 0.490}; + Color sRGB_values; + ASSERT_TRUE( + transform.Run(0, sRGB_D2700_values.data(), sRGB_values.data(), 1)); + Color sRGB_expected{0.914, 0.745, 0.601}; + EXPECT_ARRAY_NEAR(sRGB_values, sRGB_expected, 1e-3); } TEST_F(ColorManagementTest, P3HlgTo2020Hlg) { @@ -287,12 +275,12 @@ TEST_F(ColorManagementTest, P3HlgTo2020Hlg) { ColorSpaceTransform transform(*JxlGetDefaultCms()); ASSERT_TRUE(transform.Init(p3_hlg, rec2020_hlg, 1000, 1, 1)); - const float p3_hlg_values[3] = {0., 0.75, 0.}; - float rec2020_hlg_values[3]; - ASSERT_TRUE(transform.Run(0, p3_hlg_values, rec2020_hlg_values)); - EXPECT_THAT(rec2020_hlg_values, - ElementsAre(FloatNear(0.3973, 1e-4), FloatNear(0.7382, 1e-4), - FloatNear(0.1183, 1e-4))); + Color p3_hlg_values{0., 0.75, 0.}; + Color rec2020_hlg_values; + ASSERT_TRUE( + transform.Run(0, p3_hlg_values.data(), rec2020_hlg_values.data(), 1)); + Color rec2020_hlg_expected{0.3973, 0.7382, 0.1183}; + EXPECT_ARRAY_NEAR(rec2020_hlg_values, rec2020_hlg_expected, 1e-4); } TEST_F(ColorManagementTest, HlgOotf) { @@ -307,38 +295,34 @@ TEST_F(ColorManagementTest, HlgOotf) { ASSERT_TRUE( transform_to_1000.Init(p3_hlg, ColorEncoding::LinearSRGB(), 1000, 1, 1)); // HDR reference white: https://www.itu.int/pub/R-REP-BT.2408-4-2021 - float p3_hlg_values[3] = {0.75, 0.75, 0.75}; - float linear_srgb_values[3]; - ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values, linear_srgb_values)); + Color p3_hlg_values{0.75, 0.75, 0.75}; + Color linear_srgb_values; + ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values.data(), + linear_srgb_values.data(), 1)); // On a 1000-nit display, HDR reference white should be 203 cd/m² which is // 0.203 times the maximum. - EXPECT_THAT(linear_srgb_values, - ElementsAre(FloatNear(0.203, 1e-3), FloatNear(0.203, 1e-3), - FloatNear(0.203, 1e-3))); + EXPECT_ARRAY_NEAR(linear_srgb_values, (Color{0.203, 0.203, 0.203}), 1e-3); ColorSpaceTransform transform_to_400(*JxlGetDefaultCms()); ASSERT_TRUE( transform_to_400.Init(p3_hlg, ColorEncoding::LinearSRGB(), 400, 1, 1)); - ASSERT_TRUE(transform_to_400.Run(0, p3_hlg_values, linear_srgb_values)); + ASSERT_TRUE(transform_to_400.Run(0, p3_hlg_values.data(), + linear_srgb_values.data(), 1)); // On a 400-nit display, it should be 100 cd/m². - EXPECT_THAT(linear_srgb_values, - ElementsAre(FloatNear(0.250, 1e-3), FloatNear(0.250, 1e-3), - FloatNear(0.250, 1e-3))); + EXPECT_ARRAY_NEAR(linear_srgb_values, (Color{0.250, 0.250, 0.250}), 1e-3); p3_hlg_values[2] = 0.50; - ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values, linear_srgb_values)); - EXPECT_THAT(linear_srgb_values, - ElementsAre(FloatNear(0.201, 1e-3), FloatNear(0.201, 1e-3), - FloatNear(0.050, 1e-3))); + ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values.data(), + linear_srgb_values.data(), 1)); + EXPECT_ARRAY_NEAR(linear_srgb_values, (Color{0.201, 0.201, 0.050}), 1e-3); ColorSpaceTransform transform_from_400(*JxlGetDefaultCms()); ASSERT_TRUE( transform_from_400.Init(ColorEncoding::LinearSRGB(), p3_hlg, 400, 1, 1)); linear_srgb_values[0] = linear_srgb_values[1] = linear_srgb_values[2] = 0.250; - ASSERT_TRUE(transform_from_400.Run(0, linear_srgb_values, p3_hlg_values)); - EXPECT_THAT(p3_hlg_values, - ElementsAre(FloatNear(0.75, 1e-3), FloatNear(0.75, 1e-3), - FloatNear(0.75, 1e-3))); + ASSERT_TRUE(transform_from_400.Run(0, linear_srgb_values.data(), + p3_hlg_values.data(), 1)); + EXPECT_ARRAY_NEAR(p3_hlg_values, (Color{0.75, 0.75, 0.75}), 1e-3); ColorEncoding grayscale_hlg; grayscale_hlg.SetColorSpace(ColorSpace::kGray); @@ -352,8 +336,8 @@ TEST_F(ColorManagementTest, HlgOotf) { const float grayscale_hlg_value = 0.75; float linear_grayscale_value; ASSERT_TRUE(grayscale_transform.Run(0, &grayscale_hlg_value, - &linear_grayscale_value)); - EXPECT_THAT(linear_grayscale_value, FloatNear(0.203, 1e-3)); + &linear_grayscale_value, 1)); + EXPECT_NEAR(linear_grayscale_value, 0.203, 1e-3); } TEST_F(ColorManagementTest, XYBProfile) { @@ -373,7 +357,7 @@ TEST_F(ColorManagementTest, XYBProfile) { ImageMetadata metadata; metadata.color_encoding = c_native; ImageBundle ib(&metadata); - Image3F native(kNumColors, 1); + JXL_ASSIGN_OR_DIE(Image3F native, Image3F::Create(kNumColors, 1)); float mul = 1.0f / (kGridDim - 1); for (size_t ir = 0, x = 0; ir < kGridDim; ++ir) { for (size_t ig = 0; ig < kGridDim; ++ig) { @@ -386,10 +370,10 @@ TEST_F(ColorManagementTest, XYBProfile) { } ib.SetFromImage(std::move(native), c_native); const Image3F& in = *ib.color(); - Image3F opsin(kNumColors, 1); - ToXYB(ib, nullptr, &opsin, cms, nullptr); + JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(kNumColors, 1)); + JXL_CHECK(ToXYB(ib, nullptr, &opsin, cms, nullptr)); - Image3F opsin2(kNumColors, 1); + JXL_ASSIGN_OR_DIE(Image3F opsin2, Image3F::Create(kNumColors, 1)); CopyImageTo(opsin, &opsin2); ScaleXYB(&opsin2); @@ -401,9 +385,9 @@ TEST_F(ColorManagementTest, XYBProfile) { } float* dst = xform.BufDst(0); - ASSERT_TRUE(xform.Run(0, src, dst)); + ASSERT_TRUE(xform.Run(0, src, dst, kNumColors)); - Image3F out(kNumColors, 1); + JXL_ASSIGN_OR_DIE(Image3F out, Image3F::Create(kNumColors, 1)); for (size_t i = 0; i < kNumColors; ++i) { for (size_t c = 0; c < 3; ++c) { out.PlaneRow(c, 0)[i] = dst[3 * i + c]; diff --git a/third_party/jpeg-xl/lib/jxl/common.h b/third_party/jpeg-xl/lib/jxl/common.h index d619711c9f..d593244433 100644 --- a/third_party/jpeg-xl/lib/jxl/common.h +++ b/third_party/jpeg-xl/lib/jxl/common.h @@ -33,6 +33,38 @@ constexpr size_t kMaxNumPasses = 11; // Maximum number of reference frames. constexpr size_t kMaxNumReferenceFrames = 4; +enum class SpeedTier { + // Try multiple combinations of Glacier flags for modular mode. Otherwise + // like kGlacier. + kTectonicPlate = -1, + // Learn a global tree in Modular mode. + kGlacier = 0, + // Turns on FindBestQuantizationHQ loop. Equivalent to "guetzli" mode. + kTortoise = 1, + // Turns on FindBestQuantization butteraugli loop. + kKitten = 2, + // Turns on dots, patches, and spline detection by default, as well as full + // context clustering. Default. + kSquirrel = 3, + // Turns on error diffusion and full AC strategy heuristics. Equivalent to + // "fast" mode. + kWombat = 4, + // Turns on gaborish by default, non-default cmap, initial quant field. + kHare = 5, + // Turns on simple heuristics for AC strategy, quant field, and clustering; + // also enables coefficient reordering. + kCheetah = 6, + // Turns off most encoder features. Does context clustering. + // Modular: uses fixed tree with Weighted predictor. + kFalcon = 7, + // Currently fastest possible setting for VarDCT. + // Modular: uses fixed tree with Gradient predictor. + kThunder = 8, + // VarDCT: same as kThunder. + // Modular: no tree, Gradient predictor, fast histograms + kLightning = 9 +}; + } // namespace jxl #endif // LIB_JXL_COMMON_H_ diff --git a/third_party/jpeg-xl/lib/jxl/compressed_dc.cc b/third_party/jpeg-xl/lib/jxl/compressed_dc.cc index b21b1da18b..250be9e9a6 100644 --- a/third_party/jpeg-xl/lib/jxl/compressed_dc.cc +++ b/third_party/jpeg-xl/lib/jxl/compressed_dc.cc @@ -10,9 +10,6 @@ #include <string.h> #include <algorithm> -#include <array> -#include <memory> -#include <utility> #include <vector> #undef HWY_TARGET_INCLUDE @@ -21,17 +18,9 @@ #include <hwy/foreach_target.h> #include <hwy/highway.h> -#include "lib/jxl/ac_strategy.h" -#include "lib/jxl/ans_params.h" -#include "lib/jxl/base/bits.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/chroma_from_luma.h" -#include "lib/jxl/dec_ans.h" -#include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/dec_cache.h" -#include "lib/jxl/entropy_coder.h" #include "lib/jxl/image.h" HWY_BEFORE_NAMESPACE(); namespace jxl { @@ -131,21 +120,21 @@ JXL_INLINE void ComputePixel( Store(out, d, out_rows[2] + x); } -void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, - ThreadPool* pool) { +Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, + ThreadPool* pool) { const size_t xsize = dc->xsize(); const size_t ysize = dc->ysize(); - if (ysize <= 2 || xsize <= 2) return; + if (ysize <= 2 || xsize <= 2) return true; // TODO(veluca): use tile-based processing? // TODO(veluca): decide if changes to the y channel should be propagated to // the x and b channels through color correlation. JXL_ASSERT(w1 + w2 < 0.25f); - Image3F smoothed(xsize, ysize); + JXL_ASSIGN_OR_RETURN(Image3F smoothed, Image3F::Create(xsize, ysize)); // Fill in borders that the loop below will not. First and last are unused. for (size_t c = 0; c < 3; c++) { - for (size_t y : {size_t(0), ysize - 1}) { + for (size_t y : {static_cast<size_t>(0), ysize - 1}) { memcpy(smoothed.PlaneRow(c, y), dc->PlaneRow(c, y), xsize * sizeof(float)); } @@ -171,7 +160,7 @@ void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, smoothed.PlaneRow(1, y), smoothed.PlaneRow(2, y), }; - for (size_t x : {size_t(0), xsize - 1}) { + for (size_t x : {static_cast<size_t>(0), xsize - 1}) { for (size_t c = 0; c < 3; c++) { rows_out[c][x] = rows[c][x]; } @@ -197,12 +186,13 @@ void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, JXL_CHECK(RunOnPool(pool, 1, ysize - 1, ThreadPool::NoInit, process_row, "DCSmoothingRow")); dc->Swap(smoothed); + return true; } // DC dequantization. void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in, const float* dc_factors, float mul, const float* cfl_factors, - YCbCrChromaSubsampling chroma_subsampling, + const YCbCrChromaSubsampling& chroma_subsampling, const BlockCtxMap& bctx) { const HWY_FULL(float) df; const Rebind<pixel_type, HWY_FULL(float)> di; // assumes pixel_type <= float @@ -265,7 +255,9 @@ void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in, const int32_t* quant_row_b = in.channel[2].plane.Row(y >> chroma_subsampling.VShift(2)); for (size_t x = 0; x < r.xsize(); x++) { - int bucket_x = 0, bucket_y = 0, bucket_b = 0; + int bucket_x = 0; + int bucket_y = 0; + int bucket_b = 0; for (int t : bctx.dc_thresholds[0]) { if (quant_row_x[x >> chroma_subsampling.HShift(0)] > t) bucket_x++; } @@ -296,17 +288,17 @@ namespace jxl { HWY_EXPORT(DequantDC); HWY_EXPORT(AdaptiveDCSmoothing); -void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, - ThreadPool* pool) { +Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, + ThreadPool* pool) { return HWY_DYNAMIC_DISPATCH(AdaptiveDCSmoothing)(dc_factors, dc, pool); } void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in, const float* dc_factors, float mul, const float* cfl_factors, - YCbCrChromaSubsampling chroma_subsampling, + const YCbCrChromaSubsampling& chroma_subsampling, const BlockCtxMap& bctx) { - return HWY_DYNAMIC_DISPATCH(DequantDC)(r, dc, quant_dc, in, dc_factors, mul, - cfl_factors, chroma_subsampling, bctx); + HWY_DYNAMIC_DISPATCH(DequantDC) + (r, dc, quant_dc, in, dc_factors, mul, cfl_factors, chroma_subsampling, bctx); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/compressed_dc.h b/third_party/jpeg-xl/lib/jxl/compressed_dc.h index b06e5931f0..30259ebd56 100644 --- a/third_party/jpeg-xl/lib/jxl/compressed_dc.h +++ b/third_party/jpeg-xl/lib/jxl/compressed_dc.h @@ -21,12 +21,12 @@ namespace jxl { // Smooth DC in already-smooth areas, to counteract banding. -void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, - ThreadPool* pool); +Status AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc, + ThreadPool* pool); void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in, const float* dc_factors, float mul, const float* cfl_factors, - YCbCrChromaSubsampling chroma_subsampling, + const YCbCrChromaSubsampling& chroma_subsampling, const BlockCtxMap& bctx); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/convolve-inl.h b/third_party/jpeg-xl/lib/jxl/convolve-inl.h index cd79153a3a..949fd8ad67 100644 --- a/third_party/jpeg-xl/lib/jxl/convolve-inl.h +++ b/third_party/jpeg-xl/lib/jxl/convolve-inl.h @@ -118,7 +118,7 @@ class Neighbors { // Returns indices for SetTableIndices such that TableLookupLanes on the // rightmost unaligned vector (rightmost sample in its most-significant lane) // returns the mirrored values, with the mirror outside the last valid sample. -static inline const int32_t* MirrorLanes(const size_t mod) { +inline const int32_t* MirrorLanes(const size_t mod) { const HWY_CAPPED(float, 16) d; constexpr size_t kN = MaxLanes(d); @@ -181,7 +181,7 @@ class ConvolveT { JXL_CHECK(SameSize(rect, *out)); JXL_CHECK(rect.xsize() >= MinWidth()); - static_assert(int64_t(kRadius) <= 3, + static_assert(static_cast<int64_t>(kRadius) <= 3, "Must handle [0, kRadius) and >= kRadius"); switch (rect.xsize() % Lanes(Simd())) { case 0: @@ -273,15 +273,17 @@ class ConvolveT { const Weights& weights, ThreadPool* pool, Image* out) { const int64_t ysize = rect.ysize(); - RunBorderRows<kSizeModN>(in, rect, 0, std::min(int64_t(kRadius), ysize), + RunBorderRows<kSizeModN>(in, rect, 0, + std::min(static_cast<int64_t>(kRadius), ysize), weights, out); - if (ysize > 2 * int64_t(kRadius)) { - RunInteriorRows<kSizeModN>(in, rect, int64_t(kRadius), - ysize - int64_t(kRadius), weights, pool, out); + if (ysize > 2 * static_cast<int64_t>(kRadius)) { + RunInteriorRows<kSizeModN>(in, rect, static_cast<int64_t>(kRadius), + ysize - static_cast<int64_t>(kRadius), weights, + pool, out); } - if (ysize > int64_t(kRadius)) { - RunBorderRows<kSizeModN>(in, rect, ysize - int64_t(kRadius), ysize, - weights, out); + if (ysize > static_cast<int64_t>(kRadius)) { + RunBorderRows<kSizeModN>(in, rect, ysize - static_cast<int64_t>(kRadius), + ysize, weights, out); } } }; diff --git a/third_party/jpeg-xl/lib/jxl/convolve_separable5.cc b/third_party/jpeg-xl/lib/jxl/convolve_separable5.cc index db533606a1..ae618b9990 100644 --- a/third_party/jpeg-xl/lib/jxl/convolve_separable5.cc +++ b/third_party/jpeg-xl/lib/jxl/convolve_separable5.cc @@ -185,7 +185,8 @@ class Separable5Strategy { const V l1 = LoadU(d, row + x - 1); const V l2 = LoadU(d, row + x - 2); - V r1, r2; + V r1; + V r2; #if HWY_TARGET == HWY_SCALAR r1 = LoadU(d, row + Mirror(x + 1, xsize)); r2 = LoadU(d, row + Mirror(x + 2, xsize)); @@ -236,10 +237,11 @@ void Separable5(const ImageF& in, const Rect& rect, ImageF* out) { using Conv = ConvolveT<Separable5Strategy>; if (rect.xsize() >= Conv::MinWidth()) { - return Conv::Run(in, rect, weights, pool, out); + Conv::Run(in, rect, weights, pool, out); + return; } - return SlowSeparable5(in, rect, weights, pool, out, Rect(*out)); + SlowSeparable5(in, rect, weights, pool, out, Rect(*out)); } // NOLINTNEXTLINE(google-readability-namespace-comments) @@ -254,7 +256,7 @@ HWY_EXPORT(Separable5); void Separable5(const ImageF& in, const Rect& rect, const WeightsSeparable5& weights, ThreadPool* pool, ImageF* out) { - return HWY_DYNAMIC_DISPATCH(Separable5)(in, rect, weights, pool, out); + HWY_DYNAMIC_DISPATCH(Separable5)(in, rect, weights, pool, out); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc b/third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc index 06b59dfb60..618ad03a86 100644 --- a/third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc +++ b/third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc @@ -93,7 +93,9 @@ class Symmetric3Strategy { const V mc = LoadU(d, row_m + x); const V bc = LoadU(d, row_b + x); - V tr, mr, br; + V tr; + V mr; + V br; #if HWY_TARGET == HWY_SCALAR tr = tc; // Single-lane => mirrored right neighbor = center value. mr = mc; @@ -169,10 +171,11 @@ void Symmetric3(const ImageF& in, const Rect& rect, ImageF* out) { using Conv = ConvolveT<Symmetric3Strategy>; if (rect.xsize() >= Conv::MinWidth()) { - return Conv::Run(in, rect, weights, pool, out); + Conv::Run(in, rect, weights, pool, out); + return; } - return SlowSymmetric3(in, rect, weights, pool, out); + SlowSymmetric3(in, rect, weights, pool, out); } // NOLINTNEXTLINE(google-readability-namespace-comments) @@ -187,7 +190,7 @@ HWY_EXPORT(Symmetric3); void Symmetric3(const ImageF& in, const Rect& rect, const WeightsSymmetric3& weights, ThreadPool* pool, ImageF* out) { - return HWY_DYNAMIC_DISPATCH(Symmetric3)(in, rect, weights, pool, out); + HWY_DYNAMIC_DISPATCH(Symmetric3)(in, rect, weights, pool, out); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc b/third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc index 2e203fd08f..7ed384894e 100644 --- a/third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc +++ b/third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc @@ -175,14 +175,13 @@ HWY_EXPORT(Symmetric5); void Symmetric5(const ImageF& in, const Rect& in_rect, const WeightsSymmetric5& weights, ThreadPool* pool, ImageF* JXL_RESTRICT out, const Rect& out_rect) { - return HWY_DYNAMIC_DISPATCH(Symmetric5)(in, in_rect, weights, pool, out, - out_rect); + HWY_DYNAMIC_DISPATCH(Symmetric5)(in, in_rect, weights, pool, out, out_rect); } void Symmetric5(const ImageF& in, const Rect& rect, const WeightsSymmetric5& weights, ThreadPool* pool, ImageF* JXL_RESTRICT out) { - return Symmetric5(in, rect, weights, pool, out, Rect(*out)); + Symmetric5(in, rect, weights, pool, out, Rect(*out)); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/convolve_test.cc b/third_party/jpeg-xl/lib/jxl/convolve_test.cc index 6a8dc9c400..09cbdc12a6 100644 --- a/third_party/jpeg-xl/lib/jxl/convolve_test.cc +++ b/third_party/jpeg-xl/lib/jxl/convolve_test.cc @@ -5,6 +5,7 @@ #include "lib/jxl/convolve.h" +#include <jxl/types.h> #include <time.h> #undef HWY_TARGET_INCLUDE @@ -68,11 +69,11 @@ void VerifySymmetric3(const size_t xsize, const size_t ysize, ThreadPool* pool, Rng* rng) { const Rect rect(0, 0, xsize, ysize); - ImageF in(xsize, ysize); + JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize)); GenerateImage(*rng, &in, 0.0f, 1.0f); - ImageF out_expected(xsize, ysize); - ImageF out_actual(xsize, ysize); + JXL_ASSIGN_OR_DIE(ImageF out_expected, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF out_actual, ImageF::Create(xsize, ysize)); const WeightsSymmetric3& weights = WeightsSymmetric3Lowpass(); Symmetric3(in, rect, weights, pool, &out_expected); @@ -86,7 +87,7 @@ std::vector<Rect> GenerateTestRectangles(size_t xsize, size_t ysize) { for (size_t tl : {0, 1, 13}) { for (size_t br : {0, 1, 13}) { if (xsize > tl + br && ysize > tl + br) { - out.push_back(Rect(tl, tl, xsize - tl - br, ysize - tl - br)); + out.emplace_back(tl, tl, xsize - tl - br, ysize - tl - br); } } } @@ -96,7 +97,7 @@ std::vector<Rect> GenerateTestRectangles(size_t xsize, size_t ysize) { // Ensures Symmetric and Separable give the same result. void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool, Rng* rng) { - ImageF in(xsize, ysize); + JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize)); GenerateImage(*rng, &in, 0.0f, 1.0f); for (const Rect& in_rect : GenerateTestRectangles(xsize, ysize)) { @@ -105,8 +106,8 @@ void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool, in_rect.xsize(), in_rect.ysize(), in_rect.x0(), in_rect.y0()); { Rect out_rect = in_rect; - ImageF out_expected(xsize, ysize); - ImageF out_actual(xsize, ysize); + JXL_ASSIGN_OR_DIE(ImageF out_expected, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF out_actual, ImageF::Create(xsize, ysize)); FillImage(-1.0f, &out_expected); FillImage(-1.0f, &out_actual); @@ -120,8 +121,10 @@ void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool, } { Rect out_rect(0, 0, in_rect.xsize(), in_rect.ysize()); - ImageF out_expected(out_rect.xsize(), out_rect.ysize()); - ImageF out_actual(out_rect.xsize(), out_rect.ysize()); + JXL_ASSIGN_OR_DIE(ImageF out_expected, + ImageF::Create(out_rect.xsize(), out_rect.ysize())); + JXL_ASSIGN_OR_DIE(ImageF out_actual, + ImageF::Create(out_rect.xsize(), out_rect.ysize())); SlowSeparable5(in, in_rect, WeightsSeparable5Lowpass(), pool, &out_expected, out_rect); @@ -138,11 +141,11 @@ void VerifySeparable5(const size_t xsize, const size_t ysize, ThreadPool* pool, Rng* rng) { const Rect rect(0, 0, xsize, ysize); - ImageF in(xsize, ysize); + JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize)); GenerateImage(*rng, &in, 0.0f, 1.0f); - ImageF out_expected(xsize, ysize); - ImageF out_actual(xsize, ysize); + JXL_ASSIGN_OR_DIE(ImageF out_expected, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF out_actual, ImageF::Create(xsize, ysize)); const WeightsSeparable5& weights = WeightsSeparable5Lowpass(); SlowSeparable5(in, rect, weights, pool, &out_expected, rect); @@ -197,10 +200,10 @@ void BenchmarkConv(const char* caption, const Conv& conv, hwy::Result results[kNumInputs]; const size_t kDim = 160; // in+out fit in L2 - ImageF in(kDim, kDim); + JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(kDim, kDim)); ZeroFillImage(&in); in.Row(kDim / 2)[kDim / 2] = unpredictable1; - ImageF out(kDim, kDim); + JXL_ASSIGN_OR_DIE(ImageF out, ImageF::Create(kDim, kDim)); hwy::Params p; p.verbose = false; @@ -239,7 +242,7 @@ struct ConvSeparable5 { }; void BenchmarkAll() { -#if 0 // disabled to avoid test timeouts, run manually on demand +#if JXL_FALSE // disabled to avoid test timeouts, run manually on demand const hwy::FuncInput unpredictable1 = time(nullptr) != 1234; BenchmarkConv("Symmetric3", ConvSymmetric3(), unpredictable1); BenchmarkConv("Separable5", ConvSeparable5(), unpredictable1); diff --git a/third_party/jpeg-xl/lib/jxl/dct-inl.h b/third_party/jpeg-xl/lib/jxl/dct-inl.h index cb6c54bc46..05cfbde553 100644 --- a/third_party/jpeg-xl/lib/jxl/dct-inl.h +++ b/third_party/jpeg-xl/lib/jxl/dct-inl.h @@ -154,12 +154,12 @@ struct DCT1DImpl; template <size_t SZ> struct DCT1DImpl<1, SZ> { - JXL_INLINE void operator()(float* JXL_RESTRICT mem, float*) {} + JXL_INLINE void operator()(float* JXL_RESTRICT mem, float* /* tmp */) {} }; template <size_t SZ> struct DCT1DImpl<2, SZ> { - JXL_INLINE void operator()(float* JXL_RESTRICT mem, float*) { + JXL_INLINE void operator()(float* JXL_RESTRICT mem, float* /* tmp */) { auto in1 = Load(FV<SZ>(), mem); auto in2 = Load(FV<SZ>(), mem + SZ); Store(Add(in1, in2), FV<SZ>(), mem); @@ -186,7 +186,7 @@ struct IDCT1DImpl; template <size_t SZ> struct IDCT1DImpl<1, SZ> { JXL_INLINE void operator()(const float* from, size_t from_stride, float* to, - size_t to_stride, float* JXL_RESTRICT) { + size_t to_stride, float* JXL_RESTRICT /* tmp */) { StoreU(LoadU(FV<SZ>(), from), FV<SZ>(), to); } }; @@ -194,7 +194,7 @@ struct IDCT1DImpl<1, SZ> { template <size_t SZ> struct IDCT1DImpl<2, SZ> { JXL_INLINE void operator()(const float* from, size_t from_stride, float* to, - size_t to_stride, float* JXL_RESTRICT) { + size_t to_stride, float* JXL_RESTRICT /* tmp */) { JXL_DASSERT(from_stride >= SZ); JXL_DASSERT(to_stride >= SZ); auto in1 = LoadU(FV<SZ>(), from); diff --git a/third_party/jpeg-xl/lib/jxl/dct_block-inl.h b/third_party/jpeg-xl/lib/jxl/dct_block-inl.h index 50646a737f..6db5f0e7c8 100644 --- a/third_party/jpeg-xl/lib/jxl/dct_block-inl.h +++ b/third_party/jpeg-xl/lib/jxl/dct_block-inl.h @@ -44,7 +44,7 @@ class DCTFrom { DCTFrom(const float* data, size_t stride) : stride_(stride), data_(data) {} template <typename D> - HWY_INLINE Vec<D> LoadPart(D, const size_t row, size_t i) const { + HWY_INLINE Vec<D> LoadPart(D /* tag */, const size_t row, size_t i) const { JXL_DASSERT(Lanes(D()) <= stride_); // Since these functions are used also for DC, no alignment at all is // guaranteed in the case of floating blocks. @@ -74,7 +74,7 @@ class DCTTo { DCTTo(float* data, size_t stride) : stride_(stride), data_(data) {} template <typename D> - HWY_INLINE void StorePart(D, const Vec<D>& v, const size_t row, + HWY_INLINE void StorePart(D /* tag */, const Vec<D>& v, const size_t row, size_t i) const { JXL_DASSERT(Lanes(D()) <= stride_); // Since these functions are used also for DC, no alignment at all is diff --git a/third_party/jpeg-xl/lib/jxl/dct_util.h b/third_party/jpeg-xl/lib/jxl/dct_util.h index 2f29449677..90a02658af 100644 --- a/third_party/jpeg-xl/lib/jxl/dct_util.h +++ b/third_party/jpeg-xl/lib/jxl/dct_util.h @@ -8,8 +8,9 @@ #include <stddef.h> -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/data_parallel.h" +#include <memory> + +#include "lib/jxl/base/common.h" #include "lib/jxl/base/status.h" #include "lib/jxl/image.h" #include "lib/jxl/image_ops.h" @@ -50,12 +51,16 @@ template <typename T> class ACImageT final : public ACImage { public: ACImageT() = default; - ACImageT(size_t xsize, size_t ysize) { + + static StatusOr<std::unique_ptr<ACImageT>> Make(size_t xsize, size_t ysize) { static_assert( std::is_same<T, int16_t>::value || std::is_same<T, int32_t>::value, "ACImage must be either 32- or 16- bit"); - img_ = Image3<T>(xsize, ysize); + std::unique_ptr<ACImageT> result = jxl::make_unique<ACImageT>(); + JXL_ASSIGN_OR_RETURN(result->img_, Image3<T>::Create(xsize, ysize)); + return result; } + ACType Type() const override { return sizeof(T) == 2 ? ACType::k16 : ACType::k32; } diff --git a/third_party/jpeg-xl/lib/jxl/dec_ans.cc b/third_party/jpeg-xl/lib/jxl/dec_ans.cc index 29d41c8062..8b7b54ce91 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_ans.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_ans.cc @@ -154,7 +154,7 @@ Status ReadHistogram(int precision_bits, std::vector<int32_t>* counts, (*counts)[i] = prev; numsame--; } else { - int code = logcounts[i]; + unsigned int code = logcounts[i]; // omit_pos may not be negative at this point (checked before). if (i == static_cast<size_t>(omit_pos)) { continue; @@ -164,7 +164,7 @@ Status ReadHistogram(int precision_bits, std::vector<int32_t>* counts, (*counts)[i] = 1; } else { int bitcount = GetPopulationCountPrecision(code - 1, shift); - (*counts)[i] = (1 << (code - 1)) + + (*counts)[i] = (1u << (code - 1)) + (input->ReadBits(bitcount) << (code - 1 - bitcount)); } } @@ -260,7 +260,8 @@ Status DecodeUintConfig(size_t log_alpha_size, HybridUintConfig* uint_config, BitReader* br) { br->Refill(); size_t split_exponent = br->ReadBits(CeilLog2Nonzero(log_alpha_size + 1)); - size_t msb_in_token = 0, lsb_in_token = 0; + size_t msb_in_token = 0; + size_t lsb_in_token = 0; if (split_exponent != log_alpha_size) { // otherwise, msb/lsb don't matter. size_t nbits = CeilLog2Nonzero(split_exponent + 1); @@ -284,9 +285,8 @@ Status DecodeUintConfigs(size_t log_alpha_size, std::vector<HybridUintConfig>* uint_config, BitReader* br) { // TODO(veluca): RLE? - for (size_t i = 0; i < uint_config->size(); i++) { - JXL_RETURN_IF_ERROR( - DecodeUintConfig(log_alpha_size, &(*uint_config)[i], br)); + for (auto& cfg : *uint_config) { + JXL_RETURN_IF_ERROR(DecodeUintConfig(log_alpha_size, &cfg, br)); } return true; } @@ -345,7 +345,7 @@ Status DecodeHistograms(BitReader* br, size_t num_contexts, ANSCode* code, 4, "Decoded context map of size %" PRIuS " and %" PRIuS " histograms", num_contexts, num_histograms); code->lz77.nonserialized_distance_context = context_map->back(); - code->use_prefix_code = br->ReadFixedBits<1>(); + code->use_prefix_code = static_cast<bool>(br->ReadFixedBits<1>()); if (code->use_prefix_code) { code->log_alpha_size = PREFIX_MAX_BITS; } else { diff --git a/third_party/jpeg-xl/lib/jxl/dec_ans.h b/third_party/jpeg-xl/lib/jxl/dec_ans.h index 57faad25a7..cbff1deebe 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_ans.h +++ b/third_party/jpeg-xl/lib/jxl/dec_ans.h @@ -9,6 +9,7 @@ // Library to decode the ANS population counts from the bit-stream and build a // decoding table from them. +#include <jxl/types.h> #include <stddef.h> #include <stdint.h> @@ -133,6 +134,11 @@ static constexpr int8_t kSpecialDistances[kNumSpecialDistances][2] = { {8, 0}, {4, 7}, {-4, 7}, {7, 4}, {-7, 4}, {8, 1}, {8, 2}, {6, 6}, {-6, 6}, {8, 3}, {5, 7}, {-5, 7}, {7, 5}, {-7, 5}, {8, 4}, {6, 7}, {-6, 7}, {7, 6}, {-7, 6}, {8, 5}, {7, 7}, {-7, 7}, {8, 6}, {8, 7}}; +static JXL_INLINE int SpecialDistance(size_t index, int multiplier) { + int dist = kSpecialDistances[index][0] + + static_cast<int>(multiplier) * kSpecialDistances[index][1]; + return (dist > 1) ? dist : 1; +} struct ANSCode { CacheAlignedUniquePtr alias_tables; @@ -179,10 +185,7 @@ class ANSSymbolReader { num_special_distances_ = distance_multiplier == 0 ? 0 : kNumSpecialDistances; for (size_t i = 0; i < num_special_distances_; i++) { - int dist = kSpecialDistances[i][0]; - dist += static_cast<int>(distance_multiplier) * kSpecialDistances[i][1]; - if (dist < 1) dist = 1; - special_distances_[i] = dist; + special_distances_[i] = SpecialDistance(i, distance_multiplier); } } @@ -196,7 +199,7 @@ class ANSSymbolReader { AliasTable::Lookup(table, res, log_entry_size_, entry_size_minus_1_); state_ = symbol.freq * (state_ >> ANS_LOG_TAB_SIZE) + symbol.offset; -#if 1 +#if JXL_TRUE // Branchless version is about equally fast on SKX. const uint32_t new_state = (state_ << 16u) | static_cast<uint32_t>(br->PeekFixedBits<16>()); diff --git a/third_party/jpeg-xl/lib/jxl/dec_cache.cc b/third_party/jpeg-xl/lib/jxl/dec_cache.cc index 8d12bce02e..2a89420018 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_cache.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_cache.cc @@ -5,6 +5,7 @@ #include "lib/jxl/dec_cache.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/blending.h" #include "lib/jxl/common.h" // JXL_HIGH_PRECISION #include "lib/jxl/render_pipeline/stage_blending.h" @@ -247,6 +248,7 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, } linear = false; } + (void)linear; if (main_output.callback.IsPresent() || main_output.buffer) { builder.AddStage(GetWriteToOutputStage(main_output, width, height, @@ -257,7 +259,8 @@ Status PassesDecoderState::PreparePipeline(const FrameHeader& frame_header, decoded, output_encoding_info.color_encoding)); } } - render_pipeline = std::move(builder).Finalize(shared->frame_dim); + JXL_ASSIGN_OR_RETURN(render_pipeline, + std::move(builder).Finalize(shared->frame_dim)); return render_pipeline->IsInitialized(); } diff --git a/third_party/jpeg-xl/lib/jxl/dec_cache.h b/third_party/jpeg-xl/lib/jxl/dec_cache.h index d4cc7a1957..d031074532 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_cache.h +++ b/third_party/jpeg-xl/lib/jxl/dec_cache.h @@ -52,7 +52,8 @@ struct PixelCallback { const bool has_init = init != nullptr; const bool has_run = run != nullptr; const bool has_destroy = destroy != nullptr; - JXL_ASSERT(has_init == has_run && has_run == has_destroy); + const bool healthy = (has_init == has_run) && (has_run == has_destroy); + JXL_ASSERT(healthy); #endif } @@ -128,7 +129,7 @@ struct PassesDecoderState { std::atomic<uint32_t> used_acs{0}; // Storage for coefficients if in "accumulate" mode. - std::unique_ptr<ACImage> coefficients = make_unique<ACImageT<int32_t>>(0, 0); + std::unique_ptr<ACImage> coefficients = make_unique<ACImageT<int32_t>>(); // Rendering pipeline. std::unique_ptr<RenderPipeline> render_pipeline; @@ -166,8 +167,10 @@ struct PassesDecoderState { upsampler8x = GetUpsamplingStage(shared->metadata->transform_data, 0, 3); if (frame_header.loop_filter.epf_iters > 0) { - sigma = ImageF(shared->frame_dim.xsize_blocks + 2 * kSigmaPadding, - shared->frame_dim.ysize_blocks + 2 * kSigmaPadding); + JXL_ASSIGN_OR_RETURN( + sigma, + ImageF::Create(shared->frame_dim.xsize_blocks + 2 * kSigmaPadding, + shared->frame_dim.ysize_blocks + 2 * kSigmaPadding)); } return true; } @@ -193,14 +196,16 @@ struct PassesDecoderState { // Temp images required for decoding a single group. Reduces memory allocations // for large images because we only initialize min(#threads, #groups) instances. struct GroupDecCache { - void InitOnce(size_t num_passes, size_t used_acs) { + Status InitOnce(size_t num_passes, size_t used_acs) { for (size_t i = 0; i < num_passes; i++) { if (num_nzeroes[i].xsize() == 0) { // Allocate enough for a whole group - partial groups on the // right/bottom border just use a subset. The valid size is passed via // Rect. - num_nzeroes[i] = Image3I(kGroupDimInBlocks, kGroupDimInBlocks); + JXL_ASSIGN_OR_RETURN( + num_nzeroes[i], + Image3I::Create(kGroupDimInBlocks, kGroupDimInBlocks)); } } size_t max_block_area = 0; @@ -227,13 +232,17 @@ struct GroupDecCache { scratch_space = dec_group_block + max_block_area_ * 3; dec_group_qblock = int32_memory_.get(); dec_group_qblock16 = int16_memory_.get(); + return true; } - void InitDCBufferOnce() { + Status InitDCBufferOnce() { if (dc_buffer.xsize() == 0) { - dc_buffer = ImageF(kGroupDimInBlocks + kRenderPipelineXOffset * 2, - kGroupDimInBlocks + 4); + JXL_ASSIGN_OR_RETURN( + dc_buffer, + ImageF::Create(kGroupDimInBlocks + kRenderPipelineXOffset * 2, + kGroupDimInBlocks + 4)); } + return true; } // Scratch space used by DecGroupImpl(). diff --git a/third_party/jpeg-xl/lib/jxl/dec_context_map.cc b/third_party/jpeg-xl/lib/jxl/dec_context_map.cc index 2c936722da..baff87fa49 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_context_map.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_context_map.cc @@ -6,6 +6,7 @@ #include "lib/jxl/dec_context_map.h" #include <algorithm> +#include <cstdint> #include <vector> #include "lib/jxl/ans_params.h" @@ -41,18 +42,18 @@ Status VerifyContextMap(const std::vector<uint8_t>& context_map, Status DecodeContextMap(std::vector<uint8_t>* context_map, size_t* num_htrees, BitReader* input) { - bool is_simple = input->ReadFixedBits<1>(); + bool is_simple = static_cast<bool>(input->ReadFixedBits<1>()); if (is_simple) { int bits_per_entry = input->ReadFixedBits<2>(); if (bits_per_entry != 0) { - for (size_t i = 0; i < context_map->size(); i++) { - (*context_map)[i] = input->ReadBits(bits_per_entry); + for (uint8_t& entry : *context_map) { + entry = input->ReadBits(bits_per_entry); } } else { std::fill(context_map->begin(), context_map->end(), 0); } } else { - bool use_mtf = input->ReadFixedBits<1>(); + bool use_mtf = static_cast<bool>(input->ReadFixedBits<1>()); ANSCode code; std::vector<uint8_t> sink_ctx_map; // Usage of LZ77 is disallowed if decoding only two symbols. This doesn't diff --git a/third_party/jpeg-xl/lib/jxl/dec_external_image.cc b/third_party/jpeg-xl/lib/jxl/dec_external_image.cc index 06cd573378..51e12fcc81 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_external_image.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_external_image.cc @@ -9,11 +9,11 @@ #include <string.h> #include <algorithm> -#include <array> -#include <functional> #include <utility> #include <vector> +#include "lib/jxl/base/status.h" + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/dec_external_image.cc" #include <hwy/foreach_target.h> @@ -113,7 +113,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image, const size_t ysize = image.ysize(); if (undo_orientation == Orientation::kFlipHorizontal) { - out = Plane<T>(xsize, ysize); + JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(xsize, ysize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -126,7 +126,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kRotate180) { - out = Plane<T>(xsize, ysize); + JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(xsize, ysize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -139,7 +139,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kFlipVertical) { - out = Plane<T>(xsize, ysize); + JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(xsize, ysize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -152,7 +152,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kTranspose) { - out = Plane<T>(ysize, xsize); + JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -164,7 +164,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kRotate90) { - out = Plane<T>(ysize, xsize); + JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -176,7 +176,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kAntiTranspose) { - out = Plane<T>(ysize, xsize); + JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -188,7 +188,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kRotate270) { - out = Plane<T>(ysize, xsize); + JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -247,7 +247,8 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], JXL_DASSERT(in_channels[0] != nullptr); JXL_CHECK(float_out ? bits_per_sample == 16 || bits_per_sample == 32 : bits_per_sample > 0 && bits_per_sample <= 16); - if (!!out_image == out_callback.IsPresent()) { + const bool has_out_image = (out_image != nullptr); + if (has_out_image == out_callback.IsPresent()) { return JXL_FAILURE( "Must provide either an out_image or an out_callback, but not both."); } @@ -309,7 +310,7 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], ImageF ones; for (size_t c = 0; c < num_channels; ++c) { if (!channels[c]) { - ones = ImageF(xsize, 1); + JXL_ASSIGN_OR_RETURN(ones, ImageF::Create(xsize, 1)); FillImage(1.0f, &ones); break; } @@ -322,9 +323,12 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast<uint32_t>(ysize), [&](size_t num_threads) { - f16_cache = - Plane<hwy::float16_t>(xsize, num_channels * num_threads); - return InitOutCallback(num_threads); + StatusOr<Plane<hwy::float16_t>> f16_cache_or = + Plane<hwy::float16_t>::Create(xsize, + num_channels * num_threads); + if (!f16_cache_or.ok()) return false; + f16_cache = std::move(f16_cache_or).value(); + return !!InitOutCallback(num_threads); }, [&](const uint32_t task, const size_t thread) { const int64_t y = task; @@ -398,8 +402,11 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast<uint32_t>(ysize), [&](size_t num_threads) { - u32_cache = Plane<uint32_t>(xsize, num_channels * num_threads); - return InitOutCallback(num_threads); + StatusOr<Plane<uint32_t>> u32_cache_or = + Plane<uint32_t>::Create(xsize, num_channels * num_threads); + if (!u32_cache_or.ok()) return false; + u32_cache = std::move(u32_cache_or).value(); + return !!InitOutCallback(num_threads); }, [&](const uint32_t task, const size_t thread) { const int64_t y = task; @@ -453,7 +460,8 @@ Status ConvertToExternal(const jxl::ImageBundle& ib, size_t bits_per_sample, // Undo premultiplied alpha. Image3F unpremul; if (ib.AlphaIsPremultiplied() && ib.HasAlpha() && unpremul_alpha) { - unpremul = Image3F(color->xsize(), color->ysize()); + JXL_ASSIGN_OR_RETURN(unpremul, + Image3F::Create(color->xsize(), color->ysize())); CopyImageTo(*color, &unpremul); for (size_t y = 0; y < unpremul.ysize(); y++) { UnpremultiplyAlpha(unpremul.PlaneRow(0, y), unpremul.PlaneRow(1, y), diff --git a/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc b/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc index c87a4d5f36..720a278fc0 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_external_image_gbench.cc @@ -5,6 +5,7 @@ #include "benchmark/benchmark.h" #include "lib/jxl/dec_external_image.h" +#include "lib/jxl/image.h" #include "lib/jxl/image_ops.h" namespace jxl { @@ -20,10 +21,10 @@ void BM_DecExternalImage_ConvertImageRGBA(benchmark::State& state) { ImageMetadata im; im.SetAlphaBits(8); ImageBundle ib(&im); - Image3F color(xsize, ysize); + JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(xsize, ysize)); ZeroFillImage(&color); ib.SetFromImage(std::move(color), ColorEncoding::SRGB()); - ImageF alpha(xsize, ysize); + JXL_ASSIGN_OR_DIE(ImageF alpha, ImageF::Create(xsize, ysize)); ZeroFillImage(&alpha); ib.SetAlpha(std::move(alpha)); diff --git a/third_party/jpeg-xl/lib/jxl/dec_frame.cc b/third_party/jpeg-xl/lib/jxl/dec_frame.cc index 918dbe7c37..a2a82ad1fb 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_frame.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_frame.cc @@ -331,21 +331,23 @@ Status FrameDecoder::ProcessDCGroup(size_t dc_group_id, BitReader* br) { } else if (lf.epf_iters > 0) { FillImage(kInvSigmaNum / lf.epf_sigma_for_modular, &dec_state_->sigma); } - decoded_dc_groups_[dc_group_id] = uint8_t{true}; + decoded_dc_groups_[dc_group_id] = JXL_TRUE; return true; } -void FrameDecoder::FinalizeDC() { +Status FrameDecoder::FinalizeDC() { // Do Adaptive DC smoothing if enabled. This *must* happen between all the // ProcessDCGroup and ProcessACGroup. if (frame_header_.encoding == FrameEncoding::kVarDCT && !(frame_header_.flags & FrameHeader::kSkipAdaptiveDCSmoothing) && !(frame_header_.flags & FrameHeader::kUseDcFrame)) { - AdaptiveDCSmoothing(dec_state_->shared->quantizer.MulDC(), - &dec_state_->shared_storage.dc_storage, pool_); + JXL_RETURN_IF_ERROR( + AdaptiveDCSmoothing(dec_state_->shared->quantizer.MulDC(), + &dec_state_->shared_storage.dc_storage, pool_)); } finalized_dc_ = true; + return true; } Status FrameDecoder::AllocateOutput() { @@ -410,9 +412,11 @@ Status FrameDecoder::ProcessACGlobal(BitReader* br) { size_t xs = store ? kGroupDim * kGroupDim : 0; size_t ys = store ? frame_dim_.num_groups : 0; if (use_16_bit) { - dec_state_->coefficients = make_unique<ACImageT<int16_t>>(xs, ys); + JXL_ASSIGN_OR_RETURN(dec_state_->coefficients, + ACImageT<int16_t>::Make(xs, ys)); } else { - dec_state_->coefficients = make_unique<ACImageT<int32_t>>(xs, ys); + JXL_ASSIGN_OR_RETURN(dec_state_->coefficients, + ACImageT<int32_t>::Make(xs, ys)); } if (store) { dec_state_->coefficients->ZeroFill(); @@ -482,8 +486,8 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id, bool should_run_pipeline = true; if (frame_header_.encoding == FrameEncoding::kVarDCT) { - group_dec_caches_[thread].InitOnce(frame_header_.passes.num_passes, - dec_state_->used_acs); + JXL_RETURN_IF_ERROR(group_dec_caches_[thread].InitOnce( + frame_header_.passes.num_passes, dec_state_->used_acs)); JXL_RETURN_IF_ERROR(DecodeGroup(frame_header_, br, num_passes, ac_group_id, dec_state_, &group_dec_caches_[thread], thread, render_pipeline_input, decoded_, @@ -498,10 +502,16 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id, size_t pass1 = force_draw ? frame_header_.passes.num_passes : pass0 + num_passes; for (size_t i = pass0; i < pass1; ++i) { - int minShift, maxShift; + int minShift; + int maxShift; frame_header_.passes.GetDownsamplingBracket(i, minShift, maxShift); bool modular_pass_ready = true; + JXL_DEBUG_V(2, "Decoding modular in group %d pass %d", + static_cast<int>(ac_group_id), static_cast<int>(i)); if (i < pass0 + num_passes) { + JXL_DEBUG_V(2, "Bit reader position: %" PRIuS " / %" PRIuS, + br[i - pass0]->TotalBitsConsumed(), + br[i - pass0]->TotalBytes() * kBitsPerByte); JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup( frame_header_, mrect, br[i - pass0], minShift, maxShift, ModularStreamId::ModularAC(ac_group_id, i), @@ -546,7 +556,7 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id, if (!modular_frame_decoder_.UsesFullImage() && !decoded_->IsJPEG()) { if (should_run_pipeline && modular_ready) { - render_pipeline_input.Done(); + JXL_RETURN_IF_ERROR(render_pipeline_input.Done()); } else if (force_draw) { return JXL_FAILURE("Modular group decoding failed."); } @@ -555,11 +565,11 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id, } void FrameDecoder::MarkSections(const SectionInfo* sections, size_t num, - SectionStatus* section_status) { + const SectionStatus* section_status) { num_sections_done_ += num; for (size_t i = 0; i < num; i++) { if (section_status[i] != SectionStatus::kDone) { - processed_section_[sections[i].id] = false; + processed_section_[sections[i].id] = JXL_FALSE; num_sections_done_--; } } @@ -583,8 +593,8 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num, if (single_section) { JXL_ASSERT(num == 1); JXL_ASSERT(sections[0].id == 0); - if (processed_section_[0] == false) { - processed_section_[0] = true; + if (processed_section_[0] == JXL_FALSE) { + processed_section_[0] = JXL_TRUE; ac_group_sec[0].resize(1); dc_global_sec = ac_global_sec = dc_group_sec[0] = ac_group_sec[0][0] = 0; desired_num_ac_passes[0] = 1; @@ -614,7 +624,7 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num, } ac_group_sec[acg][acp] = i; } - processed_section_[sections[i].id] = true; + processed_section_[sections[i].id] = JXL_TRUE; } // Count number of new passes per group. for (size_t g = 0; g < ac_group_sec.size(); g++) { @@ -645,9 +655,11 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num, pool_, 0, dc_group_sec.size(), ThreadPool::NoInit, [this, &dc_group_sec, &num, §ions, §ion_status, &has_error]( size_t i, size_t thread) { + if (has_error) return; if (dc_group_sec[i] != num) { if (!ProcessDCGroup(i, sections[dc_group_sec[i]].br)) { has_error = true; + return; } else { section_status[dc_group_sec[i]] = SectionStatus::kDone; } @@ -657,8 +669,7 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num, } if (has_error) return JXL_FAILURE("Error in DC group"); - if (*std::min_element(decoded_dc_groups_.begin(), decoded_dc_groups_.end()) && - !finalized_dc_) { + if (!HasDcGroupToDecode() && !finalized_dc_) { PassesDecoderState::PipelineOptions pipeline_options; pipeline_options.use_slow_render_pipeline = use_slow_rendering_pipeline_; pipeline_options.coalescing = coalescing_; @@ -666,7 +677,7 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num, pipeline_options.render_noise = true; JXL_RETURN_IF_ERROR( dec_state_->PreparePipeline(frame_header_, decoded_, pipeline_options)); - FinalizeDC(); + JXL_RETURN_IF_ERROR(FinalizeDC()); JXL_RETURN_IF_ERROR(AllocateOutput()); if (progressive_detail_ >= JxlProgressiveDetail::kDC) { MarkSections(sections, num, section_status); @@ -776,21 +787,22 @@ Status FrameDecoder::Flush() { decoded_passes_per_ac_group_.size()); }, [this, &has_error](const uint32_t g, size_t thread) { + if (has_error) return; if (decoded_passes_per_ac_group_[g] == frame_header_.passes.num_passes) { // This group was drawn already, nothing to do. return; } BitReader* JXL_RESTRICT readers[kMaxNumPasses] = {}; - bool ok = ProcessACGroup( - g, readers, /*num_passes=*/0, GetStorageLocation(thread, g), - /*force_draw=*/true, /*dc_only=*/!decoded_ac_global_); - if (!ok) has_error = true; + if (!ProcessACGroup( + g, readers, /*num_passes=*/0, GetStorageLocation(thread, g), + /*force_draw=*/true, /*dc_only=*/!decoded_ac_global_)) { + has_error = true; + return; + } }, "ForceDrawGroup")); - if (has_error) { - return JXL_FAILURE("Drawing groups failed"); - } + if (has_error) return JXL_FAILURE("Drawing groups failed"); } // undo global modular transforms and copy int pixel buffers to float ones @@ -815,10 +827,8 @@ int FrameDecoder::SavedAs(const FrameHeader& header) { bool FrameDecoder::HasEverything() const { if (!decoded_dc_global_) return false; if (!decoded_ac_global_) return false; - for (auto& have_dc_group : decoded_dc_groups_) { - if (!have_dc_group) return false; - } - for (auto& nb_passes : decoded_passes_per_ac_group_) { + if (HasDcGroupToDecode()) return false; + for (const auto& nb_passes : decoded_passes_per_ac_group_) { if (nb_passes < frame_header_.passes.num_passes) return false; } return true; @@ -840,9 +850,9 @@ int FrameDecoder::References() const { result |= (1 << frame_header_.blending_info.source); } const auto& extra = frame_header_.extra_channel_blending_info; - for (size_t i = 0; i < extra.size(); ++i) { - if (cropped || extra[i].mode != BlendMode::kReplace) { - result |= (1 << extra[i].source); + for (const auto& ecbi : extra) { + if (cropped || ecbi.mode != BlendMode::kReplace) { + result |= (1 << ecbi.source); } } } diff --git a/third_party/jpeg-xl/lib/jxl/dec_frame.h b/third_party/jpeg-xl/lib/jxl/dec_frame.h index 09bdbc9675..663f1a8b33 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_frame.h +++ b/third_party/jpeg-xl/lib/jxl/dec_frame.h @@ -242,14 +242,14 @@ class FrameDecoder { private: Status ProcessDCGlobal(BitReader* br); Status ProcessDCGroup(size_t dc_group_id, BitReader* br); - void FinalizeDC(); + Status FinalizeDC(); Status AllocateOutput(); Status ProcessACGlobal(BitReader* br); Status ProcessACGroup(size_t ac_group_id, BitReader* JXL_RESTRICT* br, size_t num_passes, size_t thread, bool force_draw, bool dc_only); void MarkSections(const SectionInfo* sections, size_t num, - SectionStatus* section_status); + const SectionStatus* section_status); // Allocates storage for parallel decoding using up to `num_threads` threads // of up to `num_tasks` tasks. The value of `thread` passed to @@ -262,9 +262,10 @@ class FrameDecoder { group_dec_caches_.resize(storage_size); } use_task_id_ = num_threads > num_tasks; - bool use_group_ids = (modular_frame_decoder_.UsesFullImage() && - (frame_header_.encoding == FrameEncoding::kVarDCT || - (frame_header_.flags & FrameHeader::kNoise))); + bool use_noise = (frame_header_.flags & FrameHeader::kNoise) != 0; + bool use_group_ids = + (modular_frame_decoder_.UsesFullImage() && + (frame_header_.encoding == FrameEncoding::kVarDCT || use_noise)); if (dec_state_->render_pipeline) { JXL_RETURN_IF_ERROR(dec_state_->render_pipeline->PrepareForThreads( storage_size, use_group_ids)); @@ -272,7 +273,7 @@ class FrameDecoder { return true; } - size_t GetStorageLocation(size_t thread, size_t task) { + size_t GetStorageLocation(size_t thread, size_t task) const { if (use_task_id_) return task; return thread; } @@ -292,6 +293,11 @@ class FrameDecoder { return stride; } + bool HasDcGroupToDecode() const { + return std::any_of(decoded_dc_groups_.cbegin(), decoded_dc_groups_.cend(), + [](uint8_t ready) { return ready == 0; }); + } + PassesDecoderState* dec_state_; ThreadPool* pool_; std::vector<TocEntry> toc_; diff --git a/third_party/jpeg-xl/lib/jxl/dec_group.cc b/third_party/jpeg-xl/lib/jxl/dec_group.cc index 186318e63d..7dc4772eba 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_group.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_group.cc @@ -27,13 +27,10 @@ #include "lib/jxl/base/status.h" #include "lib/jxl/coeff_order.h" #include "lib/jxl/common.h" // kMaxNumPasses -#include "lib/jxl/convolve.h" -#include "lib/jxl/dct_scales.h" #include "lib/jxl/dec_cache.h" #include "lib/jxl/dec_transforms-inl.h" #include "lib/jxl/dec_xyb.h" #include "lib/jxl/entropy_coder.h" -#include "lib/jxl/epf.h" #include "lib/jxl/quant_weights.h" #include "lib/jxl/quantizer-inl.h" #include "lib/jxl/quantizer.h" @@ -70,6 +67,11 @@ namespace jxl { namespace HWY_NAMESPACE { // These templates are not found via ADL. +using hwy::HWY_NAMESPACE::AllFalse; +using hwy::HWY_NAMESPACE::Gt; +using hwy::HWY_NAMESPACE::Le; +using hwy::HWY_NAMESPACE::MaskFromVec; +using hwy::HWY_NAMESPACE::Or; using hwy::HWY_NAMESPACE::Rebind; using hwy::HWY_NAMESPACE::ShiftRight; @@ -77,9 +79,11 @@ using D = HWY_FULL(float); using DU = HWY_FULL(uint32_t); using DI = HWY_FULL(int32_t); using DI16 = Rebind<int16_t, DI>; +using DI16_FULL = HWY_CAPPED(int16_t, kDCTBlockSize); constexpr D d; constexpr DI di; constexpr DI16 di16; +constexpr DI16_FULL di16_full; // TODO(veluca): consider SIMDfying. void Transpose8x8InPlace(int32_t* JXL_RESTRICT block) { @@ -181,6 +185,9 @@ Status DecodeGroupImpl(const FrameHeader& frame_header, const YCbCrChromaSubsampling& cs = frame_header.chroma_subsampling; + const auto kJpegDctMin = Set(di16_full, -4095); + const auto kJpegDctMax = Set(di16_full, 4095); + size_t idct_stride[3]; for (size_t c = 0; c < 3; c++) { idct_stride[c] = render_pipeline_input.GetBuffer(c).first->PixelsPerRow(); @@ -355,7 +362,7 @@ Status DecodeGroupImpl(const FrameHeader& frame_header, int16_t* JXL_RESTRICT jpeg_pos = jpeg_row[c] + sbx[c] * kDCTBlockSize; // JPEG XL is transposed, JPEG is not. - auto transposed_dct = qblock[c].ptr32; + auto* transposed_dct = qblock[c].ptr32; Transpose8x8InPlace(transposed_dct); // No CfL - no need to store the y block converted to integers. if (!cs.Is444() || @@ -391,6 +398,16 @@ Status DecodeGroupImpl(const FrameHeader& frame_header, } jpeg_pos[0] = Clamp1<float>(dc_rows[c][sbx[c]] - dcoff[c], -2047, 2047); + auto overflow = MaskFromVec(Set(di16_full, 0)); + auto underflow = MaskFromVec(Set(di16_full, 0)); + for (int i = 0; i < 64; i += Lanes(di16_full)) { + auto in = LoadU(di16_full, jpeg_pos + i); + overflow = Or(overflow, Gt(in, kJpegDctMax)); + underflow = Or(underflow, Lt(in, kJpegDctMin)); + } + if (!AllFalse(di16_full, Or(overflow, underflow))) { + return JXL_FAILURE("JPEG DCT coefficients out of range"); + } } } else { HWY_ALIGN float* const block = group_dec_cache->dec_group_block; @@ -683,7 +700,7 @@ Status DecodeGroup(const FrameHeader& frame_header, } if (draw == kDraw && num_passes == 0 && first_pass == 0) { - group_dec_cache->InitDCBufferOnce(); + JXL_RETURN_IF_ERROR(group_dec_cache->InitDCBufferOnce()); const YCbCrChromaSubsampling& cs = frame_header.chroma_subsampling; for (size_t c : {0, 1, 2}) { size_t hs = cs.HShift(c); @@ -726,7 +743,7 @@ Status DecodeGroup(const FrameHeader& frame_header, y++) { for (ssize_t iy = 0; iy < 5; iy++) { input_rows[0][iy] = group_dec_cache->dc_buffer.Row( - Mirror(ssize_t(y) + iy - 2, + Mirror(static_cast<ssize_t>(y) + iy - 2, dec_state->shared->dc->Plane(c).ysize() >> vs) + 2 - src_rect.y0()); } @@ -736,9 +753,9 @@ Status DecodeGroup(const FrameHeader& frame_header, kRenderPipelineXOffset; } // Arguments set to 0/nullptr are not used. - dec_state->upsampler8x->ProcessRow(input_rows, output_rows, - /*xextra=*/0, src_rect.xsize(), 0, 0, - thread); + JXL_RETURN_IF_ERROR(dec_state->upsampler8x->ProcessRow( + input_rows, output_rows, + /*xextra=*/0, src_rect.xsize(), 0, 0, thread)); } } return true; @@ -780,9 +797,9 @@ Status DecodeGroupForRoundtrip(const FrameHeader& frame_header, ImageBundle* JXL_RESTRICT decoded, AuxOut* aux_out) { GetBlockFromEncoder get_block(ac, group_idx, frame_header.passes.shift); - group_dec_cache->InitOnce( + JXL_RETURN_IF_ERROR(group_dec_cache->InitOnce( /*num_passes=*/0, - /*used_acs=*/(1u << AcStrategy::kNumValidStrategies) - 1); + /*used_acs=*/(1u << AcStrategy::kNumValidStrategies) - 1)); return HWY_DYNAMIC_DISPATCH(DecodeGroupImpl)( frame_header, &get_block, group_dec_cache, dec_state, thread, group_idx, diff --git a/third_party/jpeg-xl/lib/jxl/dec_huffman.cc b/third_party/jpeg-xl/lib/jxl/dec_huffman.cc index 05b275773a..849b1a5f64 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_huffman.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_huffman.cc @@ -5,6 +5,7 @@ #include "lib/jxl/dec_huffman.h" +#include <jxl/types.h> #include <string.h> /* for memset */ #include <vector> @@ -22,9 +23,9 @@ static const uint8_t kCodeLengthCodeOrder[kCodeLengthCodes] = { static const uint8_t kDefaultCodeLength = 8; static const uint8_t kCodeLengthRepeatCode = 16; -int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths, - int num_symbols, uint8_t* code_lengths, - BitReader* br) { +JXL_BOOL ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths, + int num_symbols, uint8_t* code_lengths, + BitReader* br) { int symbol = 0; uint8_t prev_code_len = kDefaultCodeLength; int repeat = 0; @@ -38,7 +39,7 @@ int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths, } if (!BuildHuffmanTable(table, 5, code_length_code_lengths, kCodeLengthCodes, &counts[0])) { - return 0; + return JXL_FALSE; } while (symbol < num_symbols && space > 0) { @@ -47,7 +48,7 @@ int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths, br->Refill(); p += br->PeekFixedBits<5>(); br->Consume(p->bits); - code_len = (uint8_t)p->value; + code_len = static_cast<uint8_t>(p->value); if (code_len < kCodeLengthRepeatCode) { repeat = 0; code_lengths[symbol++] = code_len; @@ -72,12 +73,13 @@ int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths, repeat -= 2; repeat <<= extra_bits; } - repeat += (int)br->ReadBits(extra_bits) + 3; + repeat += static_cast<int>(br->ReadBits(extra_bits) + 3); repeat_delta = repeat - old_repeat; if (symbol + repeat_delta > num_symbols) { return 0; } - memset(&code_lengths[symbol], repeat_code_len, (size_t)repeat_delta); + memset(&code_lengths[symbol], repeat_code_len, + static_cast<size_t>(repeat_delta)); symbol += repeat_delta; if (repeat_code_len != 0) { space -= repeat_delta << (15 - repeat_code_len); @@ -85,10 +87,10 @@ int ReadHuffmanCodeLengths(const uint8_t* code_length_code_lengths, } } if (space != 0) { - return 0; + return JXL_FALSE; } - memset(&code_lengths[symbol], 0, (size_t)(num_symbols - symbol)); - return true; + memset(&code_lengths[symbol], 0, static_cast<size_t>(num_symbols - symbol)); + return JXL_TRUE; } static JXL_INLINE bool ReadSimpleCode(size_t alphabet_size, BitReader* br, @@ -176,7 +178,7 @@ static JXL_INLINE bool ReadSimpleCode(size_t alphabet_size, BitReader* br, const uint32_t goal_size = 1u << kHuffmanTableBits; while (table_size != goal_size) { memcpy(&table[table_size], &table[0], - (size_t)table_size * sizeof(table[0])); + static_cast<size_t>(table_size) * sizeof(table[0])); table_size <<= 1; } @@ -212,16 +214,17 @@ bool HuffmanDecodingData::ReadFromBitStream(size_t alphabet_size, br->Refill(); p += br->PeekFixedBits<4>(); br->Consume(p->bits); - v = (uint8_t)p->value; + v = static_cast<uint8_t>(p->value); code_length_code_lengths[code_len_idx] = v; if (v != 0) { space -= (32u >> v); ++num_codes; } } - bool ok = (num_codes == 1 || space == 0) && - ReadHuffmanCodeLengths(code_length_code_lengths, alphabet_size, - &code_lengths[0], br); + bool ok = + (num_codes == 1 || space == 0) && + FROM_JXL_BOOL(ReadHuffmanCodeLengths( + code_length_code_lengths, alphabet_size, code_lengths.data(), br)); if (!ok) return false; uint16_t counts[16] = {0}; @@ -230,7 +233,7 @@ bool HuffmanDecodingData::ReadFromBitStream(size_t alphabet_size, } table_.resize(alphabet_size + 376); uint32_t table_size = - BuildHuffmanTable(table_.data(), kHuffmanTableBits, &code_lengths[0], + BuildHuffmanTable(table_.data(), kHuffmanTableBits, code_lengths.data(), alphabet_size, &counts[0]); table_.resize(table_size); return (table_size > 0); diff --git a/third_party/jpeg-xl/lib/jxl/dec_modular.cc b/third_party/jpeg-xl/lib/jxl/dec_modular.cc index 4fcba489e2..49561e6ec2 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_modular.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_modular.cc @@ -8,7 +8,6 @@ #include <stdint.h> #include <atomic> -#include <sstream> #include <vector> #include "lib/jxl/frame_header.h" @@ -18,10 +17,8 @@ #include <hwy/foreach_target.h> #include <hwy/highway.h> -#include "lib/jxl/alpha.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/printf_macros.h" -#include "lib/jxl/base/span.h" #include "lib/jxl/base/status.h" #include "lib/jxl/compressed_dc.h" #include "lib/jxl/epf.h" @@ -189,7 +186,7 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader, } do_color = decode_color; size_t nb_extra = metadata.extra_channel_info.size(); - bool has_tree = reader->ReadBits(1); + bool has_tree = static_cast<bool>(reader->ReadBits(1)); if (!allow_truncated_group || reader->TotalBitsConsumed() < reader->TotalBytes() * kBitsPerByte) { if (has_tree) { @@ -216,8 +213,10 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader, } } - Image gi(frame_dim.xsize, frame_dim.ysize, metadata.bit_depth.bits_per_sample, - nb_chans + nb_extra); + JXL_ASSIGN_OR_RETURN( + Image gi, + Image::Create(frame_dim.xsize, frame_dim.ysize, + metadata.bit_depth.bits_per_sample, nb_chans + nb_extra)); all_same_shift = true; if (frame_header.color_transform == ColorTransform::kYCbCr) { @@ -228,7 +227,7 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader, DivCeil(frame_dim.xsize, 1 << gi.channel[c].hshift); size_t ysize_shifted = DivCeil(frame_dim.ysize, 1 << gi.channel[c].vshift); - gi.channel[c].shrink(xsize_shifted, ysize_shifted); + JXL_RETURN_IF_ERROR(gi.channel[c].shrink(xsize_shifted, ysize_shifted)); if (gi.channel[c].hshift != gi.channel[0].hshift || gi.channel[c].vshift != gi.channel[0].vshift) all_same_shift = false; @@ -237,8 +236,9 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader, for (size_t ec = 0, c = nb_chans; ec < nb_extra; ec++, c++) { size_t ecups = frame_header.extra_channel_upsampling[ec]; - gi.channel[c].shrink(DivCeil(frame_dim.xsize_upsampled, ecups), - DivCeil(frame_dim.ysize_upsampled, ecups)); + JXL_RETURN_IF_ERROR( + gi.channel[c].shrink(DivCeil(frame_dim.xsize_upsampled, ecups), + DivCeil(frame_dim.ysize_upsampled, ecups))); gi.channel[c].hshift = gi.channel[c].vshift = CeilLog2Nonzero(ecups) - CeilLog2Nonzero(frame_header.upsampling); if (gi.channel[c].hshift != gi.channel[0].hshift || @@ -306,7 +306,8 @@ Status ModularFrameDecoder::DecodeGroup( stream.kind == ModularStreamId::kModularAC); const size_t xsize = rect.xsize(); const size_t ysize = rect.ysize(); - Image gi(xsize, ysize, full_image.bitdepth, 0); + JXL_ASSIGN_OR_RETURN(Image gi, + Image::Create(xsize, ysize, full_image.bitdepth, 0)); // start at the first bigger-than-groupsize non-metachannel size_t c = full_image.nb_meta_channels; for (; c < full_image.channel.size(); c++) { @@ -328,7 +329,7 @@ Status ModularFrameDecoder::DecodeGroup( memset(row_out, 0, r.xsize() * sizeof(*row_out)); } } else { - Channel gc(r.xsize(), r.ysize()); + JXL_ASSIGN_OR_RETURN(Channel gc, Channel::Create(r.xsize(), r.ysize())); if (zerofill) ZeroFillImage(&gc.plane); gc.hshift = fc.hshift; gc.vshift = fc.vshift; @@ -398,7 +399,8 @@ Status ModularFrameDecoder::DecodeVarDCTDC(const FrameHeader& frame_header, // 3 comes from XybToRgb that cubes the values, and "magic" is // the sum of all other contributions. 2**18 is known to lead // to NaN on input found by fuzzing (see commit message). - Image image(r.xsize(), r.ysize(), full_image.bitdepth, 3); + JXL_ASSIGN_OR_RETURN( + Image image, Image::Create(r.xsize(), r.ysize(), full_image.bitdepth, 3)); size_t stream_id = ModularStreamId::VarDCTDC(group_id).ID(frame_dim); reader->Refill(); size_t extra_precision = reader->ReadFixedBits<2>(); @@ -408,12 +410,13 @@ Status ModularFrameDecoder::DecodeVarDCTDC(const FrameHeader& frame_header, Channel& ch = image.channel[c < 2 ? c ^ 1 : c]; ch.w >>= frame_header.chroma_subsampling.HShift(c); ch.h >>= frame_header.chroma_subsampling.VShift(c); - ch.shrink(); + JXL_RETURN_IF_ERROR(ch.shrink()); } if (!ModularGenericDecompress( reader, image, /*header=*/nullptr, stream_id, &options, /*undo_transforms=*/true, &tree, &code, &context_map)) { - return JXL_FAILURE("Failed to decode VarDCT DC group"); + return JXL_FAILURE("Failed to decode VarDCT DC group (DC group id %d)", + static_cast<int>(group_id)); } DequantDC(r, &dec_state->shared_storage.dc_storage, &dec_state->shared_storage.quant_dc, image, @@ -433,12 +436,15 @@ Status ModularFrameDecoder::DecodeAcMetadata(const FrameHeader& frame_header, size_t count = reader->ReadBits(CeilLog2Nonzero(upper_bound)) + 1; size_t stream_id = ModularStreamId::ACMetadata(group_id).ID(frame_dim); // YToX, YToB, ACS + QF, EPF - Image image(r.xsize(), r.ysize(), full_image.bitdepth, 4); + JXL_ASSIGN_OR_RETURN( + Image image, Image::Create(r.xsize(), r.ysize(), full_image.bitdepth, 4)); static_assert(kColorTileDimInBlocks == 8, "Color tile size changed"); Rect cr(r.x0() >> 3, r.y0() >> 3, (r.xsize() + 7) >> 3, (r.ysize() + 7) >> 3); - image.channel[0] = Channel(cr.xsize(), cr.ysize(), 3, 3); - image.channel[1] = Channel(cr.xsize(), cr.ysize(), 3, 3); - image.channel[2] = Channel(count, 2, 0, 0); + JXL_ASSIGN_OR_RETURN(image.channel[0], + Channel::Create(cr.xsize(), cr.ysize(), 3, 3)); + JXL_ASSIGN_OR_RETURN(image.channel[1], + Channel::Create(cr.xsize(), cr.ysize(), 3, 3)); + JXL_ASSIGN_OR_RETURN(image.channel[2], Channel::Create(count, 2, 0, 0)); ModularOptions options; if (!ModularGenericDecompress( reader, image, /*header=*/nullptr, stream_id, &options, @@ -513,7 +519,7 @@ Status ModularFrameDecoder::DecodeAcMetadata(const FrameHeader& frame_header, Status ModularFrameDecoder::ModularImageToDecodedRect( const FrameHeader& frame_header, Image& gi, PassesDecoderState* dec_state, jxl::ThreadPool* pool, RenderPipelineInput& render_pipeline_input, - Rect modular_rect) { + Rect modular_rect) const { const auto* metadata = frame_header.nonserialized_metadata; JXL_CHECK(gi.transform.empty()); @@ -686,7 +692,12 @@ Status ModularFrameDecoder::FinalizeDecoding(const FrameHeader& frame_header, jxl::ThreadPool* pool, bool inplace) { if (!use_full_image) return true; - Image gi = (inplace ? std::move(full_image) : full_image.clone()); + Image gi; + if (inplace) { + gi = std::move(full_image); + } else { + JXL_ASSIGN_OR_RETURN(gi, Image::Clone(full_image)); + } size_t xsize = gi.w; size_t ysize = gi.h; @@ -714,6 +725,7 @@ Status ModularFrameDecoder::FinalizeDecoding(const FrameHeader& frame_header, use_group_ids); }, [&](const uint32_t group, size_t thread_id) { + if (has_error) return; RenderPipelineInput input = dec_state->render_pipeline->GetInputBuffers(group, thread_id); if (!ModularImageToDecodedRect( @@ -722,12 +734,13 @@ Status ModularFrameDecoder::FinalizeDecoding(const FrameHeader& frame_header, has_error = true; return; } - input.Done(); + if (!input.Done()) { + has_error = true; + return; + } }, "ModularToRect")); - if (has_error) { - return JXL_FAILURE("Error producing input to render pipeline"); - } + if (has_error) return JXL_FAILURE("Error producing input to render pipeline"); return true; } @@ -743,7 +756,8 @@ Status ModularFrameDecoder::DecodeQuantTable( // be negative. return JXL_FAILURE("Invalid qtable_den: value too small"); } - Image image(required_size_x, required_size_y, 8, 3); + JXL_ASSIGN_OR_RETURN(Image image, + Image::Create(required_size_x, required_size_y, 8, 3)); ModularOptions options; if (modular_frame_decoder) { JXL_RETURN_IF_ERROR(ModularGenericDecompress( diff --git a/third_party/jpeg-xl/lib/jxl/dec_modular.h b/third_party/jpeg-xl/lib/jxl/dec_modular.h index 58a6562740..23caad0c16 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_modular.h +++ b/third_party/jpeg-xl/lib/jxl/dec_modular.h @@ -123,7 +123,7 @@ class ModularFrameDecoder { PassesDecoderState* dec_state, jxl::ThreadPool* pool, RenderPipelineInput& render_pipeline_input, - Rect modular_rect); + Rect modular_rect) const; Image full_image; std::vector<Transform> global_transform; diff --git a/third_party/jpeg-xl/lib/jxl/dec_noise.cc b/third_party/jpeg-xl/lib/jxl/dec_noise.cc index ae46b1062f..24f0136490 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_noise.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_noise.cc @@ -108,9 +108,8 @@ void Random3Planes(size_t visible_frame_index, size_t nonvisible_frame_index, size_t x0, size_t y0, const std::pair<ImageF*, Rect>& plane0, const std::pair<ImageF*, Rect>& plane1, const std::pair<ImageF*, Rect>& plane2) { - return HWY_DYNAMIC_DISPATCH(Random3Planes)(visible_frame_index, - nonvisible_frame_index, x0, y0, - plane0, plane1, plane2); + HWY_DYNAMIC_DISPATCH(Random3Planes) + (visible_frame_index, nonvisible_frame_index, x0, y0, plane0, plane1, plane2); } void DecodeFloatParam(float precision, float* val, BitReader* br) { diff --git a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc index 0ae2223252..120a977ca7 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc @@ -10,26 +10,16 @@ #include <sys/types.h> #include <algorithm> -#include <string> -#include <tuple> #include <utility> #include <vector> -#include "lib/jxl/ans_params.h" -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/override.h" #include "lib/jxl/base/printf_macros.h" #include "lib/jxl/base/status.h" #include "lib/jxl/blending.h" -#include "lib/jxl/chroma_from_luma.h" #include "lib/jxl/common.h" // kMaxNumReferenceFrames #include "lib/jxl/dec_ans.h" -#include "lib/jxl/dec_frame.h" -#include "lib/jxl/entropy_coder.h" -#include "lib/jxl/frame_header.h" #include "lib/jxl/image.h" #include "lib/jxl/image_bundle.h" -#include "lib/jxl/image_ops.h" #include "lib/jxl/pack_signed.h" #include "lib/jxl/patch_dictionary_internal.h" @@ -138,7 +128,8 @@ Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize, } for (size_t j = 0; j < num_ec + 1; j++) { uint32_t blend_mode = read_num(kPatchBlendModeContext); - if (blend_mode >= uint32_t(PatchBlendMode::kNumBlendModes)) { + if (blend_mode >= + static_cast<uint32_t>(PatchBlendMode::kNumBlendModes)) { return JXL_FAILURE("Invalid patch blend mode: %u", blend_mode); } PatchBlending info; @@ -157,21 +148,22 @@ Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize, return JXL_FAILURE( "Invalid alpha channel for blending: %u out of %u\n", info.alpha_channel, - (uint32_t)shared_->metadata->m.extra_channel_info.size()); + static_cast<uint32_t>( + shared_->metadata->m.extra_channel_info.size())); } } else { info.alpha_channel = 0; } if (UsesClamp(info.mode)) { - info.clamp = read_num(kPatchClampContext); + info.clamp = static_cast<bool>(read_num(kPatchClampContext)); } else { info.clamp = false; } blendings_.push_back(info); } - positions_.push_back(std::move(pos)); + positions_.emplace_back(pos); } - ref_positions_.emplace_back(std::move(ref_pos)); + ref_positions_.emplace_back(ref_pos); } positions_.shrink_to_fit(); @@ -185,8 +177,8 @@ Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize, int PatchDictionary::GetReferences() const { int result = 0; - for (size_t i = 0; i < ref_positions_.size(); ++i) { - result |= (1 << static_cast<int>(ref_positions_[i].ref)); + for (const auto& ref_pos : ref_positions_) { + result |= (1 << static_cast<int>(ref_pos.ref)); } return result; } @@ -263,11 +255,11 @@ void PatchDictionary::ComputePatchTree() { node.start = sorted_patches_y0_.size(); for (ssize_t i = static_cast<ssize_t>(right_start) - 1; i >= static_cast<ssize_t>(left_end); --i) { - sorted_patches_y1_.push_back({intervals[i].y1, intervals[i].idx}); + sorted_patches_y1_.emplace_back(intervals[i].y1, intervals[i].idx); } sort_by_y0(left_end, right_start); for (size_t i = left_end; i < right_start; ++i) { - sorted_patches_y0_.push_back({intervals[i].y0, intervals[i].idx}); + sorted_patches_y0_.emplace_back(intervals[i].y0, intervals[i].idx); } // Create the left and right nodes (if not empty). node.left_child = node.right_child = -1; @@ -294,7 +286,7 @@ std::vector<size_t> PatchDictionary::GetPatchesForRow(size_t y) const { if (y < num_patches_.size() && num_patches_[y] > 0) { result.reserve(num_patches_[y]); for (ssize_t tree_idx = 0; tree_idx != -1;) { - JXL_DASSERT(tree_idx < (ssize_t)patch_tree_.size()); + JXL_DASSERT(tree_idx < static_cast<ssize_t>(patch_tree_.size())); const auto& node = patch_tree_[tree_idx]; if (y <= node.y_center) { for (size_t i = 0; i < node.num; ++i) { @@ -322,8 +314,8 @@ std::vector<size_t> PatchDictionary::GetPatchesForRow(size_t y) const { // Adds patches to a segment of `xsize` pixels, starting at `inout`, assumed // to be located at position (x0, y) in the frame. -void PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0, - size_t xsize) const { +Status PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0, + size_t xsize) const { size_t num_ec = shared_->metadata->m.num_extra_channels; std::vector<const float*> fg_ptrs(3 + num_ec); for (size_t pos_idx : GetPatchesForRow(y)) { @@ -352,10 +344,11 @@ void PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0, ref_pos.y0 + iy) + ref_pos.x0 + x0 - bx; } - PerformBlending(inout, fg_ptrs.data(), inout, patch_x0 - x0, - patch_x1 - patch_x0, blendings_[blending_idx], - blendings_.data() + blending_idx + 1, - shared_->metadata->m.extra_channel_info); + JXL_RETURN_IF_ERROR(PerformBlending( + inout, fg_ptrs.data(), inout, patch_x0 - x0, patch_x1 - patch_x0, + blendings_[blending_idx], blendings_.data() + blending_idx + 1, + shared_->metadata->m.extra_channel_info)); } + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h index aac6111ae6..72dca8f057 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h +++ b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.h @@ -12,12 +12,10 @@ #include <string.h> #include <sys/types.h> -#include <tuple> #include <vector> #include "lib/jxl/base/status.h" #include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/image.h" namespace jxl { @@ -109,7 +107,8 @@ class PatchDictionary { // Adds patches to a segment of `xsize` pixels, starting at `inout`, assumed // to be located at position (x0, y) in the frame. - void AddOneRow(float* const* inout, size_t y, size_t x0, size_t xsize) const; + Status AddOneRow(float* const* inout, size_t y, size_t x0, + size_t xsize) const; // Returns dependencies of this patch dictionary on reference frame ids as a // bit mask: bits 0-3 indicate reference frame 0-3. diff --git a/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.cc b/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.cc index 2d40740262..6451c41f6d 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.cc @@ -21,21 +21,21 @@ void TransformToPixels(AcStrategy::Type strategy, float* JXL_RESTRICT coefficients, float* JXL_RESTRICT pixels, size_t pixels_stride, float* scratch_space) { - return HWY_DYNAMIC_DISPATCH(TransformToPixels)(strategy, coefficients, pixels, - pixels_stride, scratch_space); + HWY_DYNAMIC_DISPATCH(TransformToPixels) + (strategy, coefficients, pixels, pixels_stride, scratch_space); } HWY_EXPORT(LowestFrequenciesFromDC); void LowestFrequenciesFromDC(const jxl::AcStrategy::Type strategy, const float* dc, size_t dc_stride, float* llf, float* JXL_RESTRICT scratch) { - return HWY_DYNAMIC_DISPATCH(LowestFrequenciesFromDC)(strategy, dc, dc_stride, - llf, scratch); + HWY_DYNAMIC_DISPATCH(LowestFrequenciesFromDC) + (strategy, dc, dc_stride, llf, scratch); } HWY_EXPORT(AFVIDCT4x4); void AFVIDCT4x4(const float* JXL_RESTRICT coeffs, float* JXL_RESTRICT pixels) { - return HWY_DYNAMIC_DISPATCH(AFVIDCT4x4)(coeffs, pixels); + HWY_DYNAMIC_DISPATCH(AFVIDCT4x4)(coeffs, pixels); } #endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.h b/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.h index f68481fda9..2cbf6bd5f6 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.h +++ b/third_party/jpeg-xl/lib/jxl/dec_transforms_testonly.h @@ -22,8 +22,8 @@ void TransformToPixels(AcStrategy::Type strategy, float* JXL_RESTRICT scratch_space); // Equivalent of the above for DC image. -void LowestFrequenciesFromDC(const jxl::AcStrategy::Type strategy, - const float* dc, size_t dc_stride, float* llf, +void LowestFrequenciesFromDC(jxl::AcStrategy::Type strategy, const float* dc, + size_t dc_stride, float* llf, float* JXL_RESTRICT scratch); void AFVIDCT4x4(const float* JXL_RESTRICT coeffs, float* JXL_RESTRICT pixels); diff --git a/third_party/jpeg-xl/lib/jxl/dec_xyb-inl.h b/third_party/jpeg-xl/lib/jxl/dec_xyb-inl.h index 495693b257..09a8013960 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_xyb-inl.h +++ b/third_party/jpeg-xl/lib/jxl/dec_xyb-inl.h @@ -83,7 +83,7 @@ HWY_INLINE HWY_MAYBE_UNUSED void XybToRgb(D d, const V opsin_x, const V opsin_y, *linear_b = MulAdd(LoadDup128(d, &inverse_matrix[8 * 4]), mixed_b, *linear_b); } -static inline HWY_MAYBE_UNUSED bool HasFastXYBTosRGB8() { +inline HWY_MAYBE_UNUSED bool HasFastXYBTosRGB8() { #if HWY_TARGET == HWY_NEON return true; #else @@ -91,9 +91,9 @@ static inline HWY_MAYBE_UNUSED bool HasFastXYBTosRGB8() { #endif } -static inline HWY_MAYBE_UNUSED void FastXYBTosRGB8(const float* input[4], - uint8_t* output, - bool is_rgba, size_t xsize) { +inline HWY_MAYBE_UNUSED void FastXYBTosRGB8(const float* input[4], + uint8_t* output, bool is_rgba, + size_t xsize) { // This function is very NEON-specific. As such, it uses intrinsics directly. #if HWY_TARGET == HWY_NEON // WARNING: doing fixed point arithmetic correctly is very complicated. diff --git a/third_party/jpeg-xl/lib/jxl/dec_xyb.cc b/third_party/jpeg-xl/lib/jxl/dec_xyb.cc index 7010f0d813..a719b3eb8c 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_xyb.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_xyb.cc @@ -160,20 +160,19 @@ namespace jxl { HWY_EXPORT(OpsinToLinearInplace); void OpsinToLinearInplace(Image3F* JXL_RESTRICT inout, ThreadPool* pool, const OpsinParams& opsin_params) { - return HWY_DYNAMIC_DISPATCH(OpsinToLinearInplace)(inout, pool, opsin_params); + HWY_DYNAMIC_DISPATCH(OpsinToLinearInplace)(inout, pool, opsin_params); } HWY_EXPORT(OpsinToLinear); void OpsinToLinear(const Image3F& opsin, const Rect& rect, ThreadPool* pool, Image3F* JXL_RESTRICT linear, const OpsinParams& opsin_params) { - return HWY_DYNAMIC_DISPATCH(OpsinToLinear)(opsin, rect, pool, linear, - opsin_params); + HWY_DYNAMIC_DISPATCH(OpsinToLinear)(opsin, rect, pool, linear, opsin_params); } HWY_EXPORT(YcbcrToRgb); void YcbcrToRgb(const Image3F& ycbcr, Image3F* rgb, const Rect& rect) { - return HWY_DYNAMIC_DISPATCH(YcbcrToRgb)(ycbcr, rgb, rect); + HWY_DYNAMIC_DISPATCH(YcbcrToRgb)(ycbcr, rgb, rect); } HWY_EXPORT(HasFastXYBTosRGB8); @@ -182,7 +181,7 @@ bool HasFastXYBTosRGB8() { return HWY_DYNAMIC_DISPATCH(HasFastXYBTosRGB8)(); } HWY_EXPORT(FastXYBTosRGB8); void FastXYBTosRGB8(const float* input[4], uint8_t* output, bool is_rgba, size_t xsize) { - return HWY_DYNAMIC_DISPATCH(FastXYBTosRGB8)(input, output, is_rgba, xsize); + HWY_DYNAMIC_DISPATCH(FastXYBTosRGB8)(input, output, is_rgba, xsize); } void OpsinParams::Init(float intensity_target) { @@ -218,7 +217,7 @@ Status OutputEncodingInfo::SetFromMetadata(const CodecMetadata& metadata) { orig_intensity_target = metadata.m.IntensityTarget(); desired_intensity_target = orig_intensity_target; const auto& im = metadata.transform_data.opsin_inverse_matrix; - memcpy(orig_inverse_matrix, im.inverse_matrix, sizeof(orig_inverse_matrix)); + orig_inverse_matrix = im.inverse_matrix; default_transform = im.all_default; xyb_encoded = metadata.m.xyb_encoded; std::copy(std::begin(im.opsin_biases), std::end(im.opsin_biases), @@ -258,38 +257,38 @@ Status OutputEncodingInfo::SetColorEncoding(const ColorEncoding& c_desired) { // Compute the opsin inverse matrix and luminances based on primaries and // white point. - float inverse_matrix[9]; + Matrix3x3 inverse_matrix; bool inverse_matrix_is_default = default_transform; - memcpy(inverse_matrix, orig_inverse_matrix, sizeof(inverse_matrix)); - constexpr float kSRGBLuminances[3] = {0.2126, 0.7152, 0.0722}; - memcpy(luminances, kSRGBLuminances, sizeof(luminances)); + inverse_matrix = orig_inverse_matrix; + constexpr Vector3 kSRGBLuminances{0.2126, 0.7152, 0.0722}; + luminances = kSRGBLuminances; if ((c_desired.GetPrimariesType() != Primaries::kSRGB || c_desired.GetWhitePointType() != WhitePoint::kD65) && !c_desired.IsGray()) { - float srgb_to_xyzd50[9]; + Matrix3x3 srgb_to_xyzd50; const auto& srgb = ColorEncoding::SRGB(/*is_gray=*/false); PrimariesCIExy p = srgb.GetPrimaries(); CIExy w = srgb.GetWhitePoint(); JXL_CHECK(PrimariesToXYZD50(p.r.x, p.r.y, p.g.x, p.g.y, p.b.x, p.b.y, w.x, w.y, srgb_to_xyzd50)); - float original_to_xyz[3][3]; + Matrix3x3 original_to_xyz; p = c_desired.GetPrimaries(); w = c_desired.GetWhitePoint(); if (!PrimariesToXYZ(p.r.x, p.r.y, p.g.x, p.g.y, p.b.x, p.b.y, w.x, w.y, - &original_to_xyz[0][0])) { + original_to_xyz)) { return JXL_FAILURE("PrimariesToXYZ failed"); } - memcpy(luminances, original_to_xyz[1], sizeof luminances); + luminances = original_to_xyz[1]; if (xyb_encoded) { - float adapt_to_d50[9]; + Matrix3x3 adapt_to_d50; if (!AdaptToXYZD50(c_desired.GetWhitePoint().x, c_desired.GetWhitePoint().y, adapt_to_d50)) { return JXL_FAILURE("AdaptToXYZD50 failed"); } - float xyzd50_to_original[9]; - Mul3x3Matrix(adapt_to_d50, &original_to_xyz[0][0], xyzd50_to_original); + Matrix3x3 xyzd50_to_original; + Mul3x3Matrix(adapt_to_d50, original_to_xyz, xyzd50_to_original); JXL_RETURN_IF_ERROR(Inv3x3Matrix(xyzd50_to_original)); - float srgb_to_original[9]; + Matrix3x3 srgb_to_original; Mul3x3Matrix(xyzd50_to_original, srgb_to_xyzd50, srgb_to_original); Mul3x3Matrix(srgb_to_original, orig_inverse_matrix, inverse_matrix); inverse_matrix_is_default = false; @@ -297,12 +296,8 @@ Status OutputEncodingInfo::SetColorEncoding(const ColorEncoding& c_desired) { } if (c_desired.IsGray()) { - float tmp_inv_matrix[9]; - memcpy(tmp_inv_matrix, inverse_matrix, sizeof(inverse_matrix)); - float srgb_to_luma[9]; - memcpy(&srgb_to_luma[0], luminances, sizeof(luminances)); - memcpy(&srgb_to_luma[3], luminances, sizeof(luminances)); - memcpy(&srgb_to_luma[6], luminances, sizeof(luminances)); + Matrix3x3 tmp_inv_matrix = inverse_matrix; + Matrix3x3 srgb_to_luma{luminances, luminances, luminances}; Mul3x3Matrix(srgb_to_luma, tmp_inv_matrix, inverse_matrix); } diff --git a/third_party/jpeg-xl/lib/jxl/dec_xyb.h b/third_party/jpeg-xl/lib/jxl/dec_xyb.h index ddfd555632..65317f2f54 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_xyb.h +++ b/third_party/jpeg-xl/lib/jxl/dec_xyb.h @@ -39,7 +39,7 @@ struct OutputEncodingInfo { // Used for the HLG OOTF and PQ tone mapping. float orig_intensity_target; // Opsin inverse matrix taken from the metadata. - float orig_inverse_matrix[9]; + Matrix3x3 orig_inverse_matrix; bool default_transform; bool xyb_encoded; // @@ -60,7 +60,7 @@ struct OutputEncodingInfo { // Luminances of color_encoding's primaries, used for the HLG inverse OOTF and // for PQ tone mapping. // Default to sRGB's. - float luminances[3]; + Vector3 luminances; // Used for the HLG inverse OOTF and PQ tone mapping. float desired_intensity_target; bool cms_set = false; diff --git a/third_party/jpeg-xl/lib/jxl/decode.cc b/third_party/jpeg-xl/lib/jxl/decode.cc index b674d1ba88..605c2d6fd6 100644 --- a/third_party/jpeg-xl/lib/jxl/decode.cc +++ b/third_party/jpeg-xl/lib/jxl/decode.cc @@ -721,7 +721,7 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) { dec->events_wanted = dec->orig_events_wanted; dec->basic_info_size_hint = InitialBasicInfoSizeHint(); - dec->have_container = 0; + dec->have_container = false; dec->box_count = 0; dec->downsampling_target = 8; dec->image_out_buffer_set = false; @@ -733,7 +733,7 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) { dec->image_out_size = 0; dec->image_out_bit_depth.type = JXL_BIT_DEPTH_FROM_PIXEL_FORMAT; dec->extra_channel_output.clear(); - dec->next_in = 0; + dec->next_in = nullptr; dec->avail_in = 0; dec->input_closed = false; @@ -839,9 +839,9 @@ void JxlDecoderSkipFrames(JxlDecoder* dec, size_t amount) { internal_index, dec->frame_saved_as, dec->frame_references); dec->frame_required.resize(internal_index + 1, 0); - for (size_t i = 0; i < deps.size(); i++) { - JXL_ASSERT(deps[i] < dec->frame_required.size()); - dec->frame_required[deps[i]] = 1; + for (size_t idx : deps) { + JXL_ASSERT(idx < dec->frame_required.size()); + dec->frame_required[idx] = 1; } } } @@ -894,7 +894,7 @@ JxlDecoderStatus JxlDecoderSetKeepOrientation(JxlDecoder* dec, if (dec->stage != DecoderStage::kInited) { return JXL_API_ERROR("Must set keep_orientation option before starting"); } - dec->keep_orientation = !!skip_reorientation; + dec->keep_orientation = FROM_JXL_BOOL(skip_reorientation); return JXL_DEC_SUCCESS; } @@ -903,7 +903,7 @@ JxlDecoderStatus JxlDecoderSetUnpremultiplyAlpha(JxlDecoder* dec, if (dec->stage != DecoderStage::kInited) { return JXL_API_ERROR("Must set unpremul_alpha option before starting"); } - dec->unpremul_alpha = !!unpremul_alpha; + dec->unpremul_alpha = FROM_JXL_BOOL(unpremul_alpha); return JXL_DEC_SUCCESS; } @@ -912,7 +912,7 @@ JxlDecoderStatus JxlDecoderSetRenderSpotcolors(JxlDecoder* dec, if (dec->stage != DecoderStage::kInited) { return JXL_API_ERROR("Must set render_spotcolors option before starting"); } - dec->render_spotcolors = !!render_spotcolors; + dec->render_spotcolors = FROM_JXL_BOOL(render_spotcolors); return JXL_DEC_SUCCESS; } @@ -920,7 +920,7 @@ JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec, JXL_BOOL coalescing) { if (dec->stage != DecoderStage::kInited) { return JXL_API_ERROR("Must set coalescing option before starting"); } - dec->coalescing = !!coalescing; + dec->coalescing = FROM_JXL_BOOL(coalescing); return JXL_DEC_SUCCESS; } @@ -1076,7 +1076,7 @@ JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec) { return JXL_DEC_ERROR; } IccBytes icc; - Bytes(decoded_icc).AppendTo(&icc); + Bytes(decoded_icc).AppendTo(icc); dec->metadata.m.color_encoding.SetICCRaw(std::move(icc)); } @@ -1118,7 +1118,7 @@ JxlDecoderStatus JxlDecoderProcessSections(JxlDecoder* dec) { if (OutOfBounds(pos, size, span.size())) { break; } - auto br = new jxl::BitReader(jxl::Bytes(span.data() + pos, size)); + auto* br = new jxl::BitReader(jxl::Bytes(span.data() + pos, size)); section_info.emplace_back(jxl::FrameDecoder::SectionInfo{br, id, i}); section_status.emplace_back(); pos += size; @@ -1263,9 +1263,9 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) { frame_dim.ysize_upsampled_padded)) { return JXL_INPUT_ERROR("frame is too large"); } - bool output_needed = - (dec->preview_frame ? (dec->events_wanted & JXL_DEC_PREVIEW_IMAGE) - : (dec->events_wanted & JXL_DEC_FULL_IMAGE)); + int output_type = + dec->preview_frame ? JXL_DEC_PREVIEW_IMAGE : JXL_DEC_FULL_IMAGE; + bool output_needed = ((dec->events_wanted & output_type) != 0); if (output_needed) { JXL_API_RETURN_IF_ERROR(dec->frame_dec->InitFrameOutput()); } @@ -1376,7 +1376,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) { } else { dec->frame_prog_detail = JxlProgressiveDetail::kFrames; } - dec->dc_frame_progression_done = 0; + dec->dc_frame_progression_done = false; dec->next_section = 0; dec->section_processed.clear(); @@ -1413,7 +1413,8 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) { } if (dec->image_out_buffer_set) { - size_t xsize, ysize; + size_t xsize; + size_t ysize; GetCurrentDimensions(dec, xsize, ysize); size_t bits_per_sample = GetBitDepth( dec->image_out_bit_depth, dec->metadata.m, dec->image_out_format); @@ -1765,7 +1766,8 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) { return JXL_DEC_SUCCESS; } - uint64_t box_size, header_size; + uint64_t box_size; + uint64_t header_size; JxlDecoderStatus status = ParseBoxHeader(dec->next_in, dec->avail_in, 0, dec->file_pos, dec->box_type, &box_size, &header_size); @@ -2042,7 +2044,7 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) { dec->got_signature = true; if (sig == JXL_SIG_CONTAINER) { - dec->have_container = 1; + dec->have_container = true; } else { dec->last_codestream_seen = true; } @@ -2083,16 +2085,16 @@ JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec, const jxl::ImageMetadata& meta = dec->metadata.m; - info->have_container = dec->have_container; + info->have_container = TO_JXL_BOOL(dec->have_container); info->xsize = dec->metadata.size.xsize(); info->ysize = dec->metadata.size.ysize(); - info->uses_original_profile = !meta.xyb_encoded; + info->uses_original_profile = TO_JXL_BOOL(!meta.xyb_encoded); info->bits_per_sample = meta.bit_depth.bits_per_sample; info->exponent_bits_per_sample = meta.bit_depth.exponent_bits_per_sample; - info->have_preview = meta.have_preview; - info->have_animation = meta.have_animation; + info->have_preview = TO_JXL_BOOL(meta.have_preview); + info->have_animation = TO_JXL_BOOL(meta.have_animation); info->orientation = static_cast<JxlOrientation>(meta.orientation); if (!dec->keep_orientation) { @@ -2107,14 +2109,15 @@ JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec, info->intensity_target = dec->desired_intensity_target; } info->min_nits = meta.tone_mapping.min_nits; - info->relative_to_max_display = meta.tone_mapping.relative_to_max_display; + info->relative_to_max_display = + TO_JXL_BOOL(meta.tone_mapping.relative_to_max_display); info->linear_below = meta.tone_mapping.linear_below; const jxl::ExtraChannelInfo* alpha = meta.Find(jxl::ExtraChannel::kAlpha); if (alpha != nullptr) { info->alpha_bits = alpha->bit_depth.bits_per_sample; info->alpha_exponent_bits = alpha->bit_depth.exponent_bits_per_sample; - info->alpha_premultiplied = alpha->alpha_associated; + info->alpha_premultiplied = TO_JXL_BOOL(alpha->alpha_associated); } else { info->alpha_bits = 0; info->alpha_exponent_bits = 0; @@ -2136,7 +2139,8 @@ JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec, info->animation.tps_denominator = dec->metadata.m.animation.tps_denominator; info->animation.num_loops = dec->metadata.m.animation.num_loops; - info->animation.have_timecodes = dec->metadata.m.animation.have_timecodes; + info->animation.have_timecodes = + TO_JXL_BOOL(dec->metadata.m.animation.have_timecodes); } if (meta.have_intrinsic_size) { @@ -2170,7 +2174,7 @@ JxlDecoderStatus JxlDecoderGetExtraChannelInfo(const JxlDecoder* dec, : 0; info->dim_shift = channel.dim_shift; info->name_length = channel.name.size(); - info->alpha_premultiplied = channel.alpha_associated; + info->alpha_premultiplied = TO_JXL_BOOL(channel.alpha_associated); info->spot_color[0] = channel.spot_color[0]; info->spot_color[1] = channel.spot_color[1]; info->spot_color[2] = channel.spot_color[2]; @@ -2348,29 +2352,41 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetCms(JxlDecoder* dec, return JXL_DEC_SUCCESS; } -JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize( - const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) { +static JxlDecoderStatus GetMinSize(const JxlDecoder* dec, + const JxlPixelFormat* format, + size_t num_channels, size_t* min_size, + bool preview) { size_t bits; JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits); if (status != JXL_DEC_SUCCESS) return status; - if (format->num_channels < 3 && - !dec->image_metadata.color_encoding.IsGray()) { - return JXL_API_ERROR("Number of channels is too low for color output"); + size_t xsize; + size_t ysize; + if (preview) { + xsize = dec->metadata.oriented_preview_xsize(dec->keep_orientation); + ysize = dec->metadata.oriented_preview_ysize(dec->keep_orientation); + } else { + GetCurrentDimensions(dec, xsize, ysize); } - - size_t xsize = dec->metadata.oriented_preview_xsize(dec->keep_orientation); - size_t ysize = dec->metadata.oriented_preview_ysize(dec->keep_orientation); - + if (num_channels == 0) num_channels = format->num_channels; size_t row_size = - jxl::DivCeil(xsize * format->num_channels * bits, jxl::kBitsPerByte); + jxl::DivCeil(xsize * num_channels * bits, jxl::kBitsPerByte); size_t last_row_size = row_size; if (format->align > 1) { row_size = jxl::DivCeil(row_size, format->align) * format->align; } - *size = row_size * (ysize - 1) + last_row_size; + *min_size = row_size * (ysize - 1) + last_row_size; return JXL_DEC_SUCCESS; } +JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize( + const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) { + if (format->num_channels < 3 && + !dec->image_metadata.color_encoding.IsGray()) { + return JXL_API_ERROR("Number of channels is too low for color output"); + } + return GetMinSize(dec, format, 0, size, true); +} + JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer( JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size) { if (!dec->got_basic_info || !dec->metadata.m.have_preview || @@ -2401,23 +2417,12 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreviewOutBuffer( JXL_EXPORT JxlDecoderStatus JxlDecoderImageOutBufferSize( const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size) { - size_t bits; - JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits); - if (status != JXL_DEC_SUCCESS) return status; if (format->num_channels < 3 && !dec->image_metadata.color_encoding.IsGray()) { return JXL_API_ERROR("Number of channels is too low for color output"); } - size_t xsize, ysize; - GetCurrentDimensions(dec, xsize, ysize); - size_t row_size = - jxl::DivCeil(xsize * format->num_channels * bits, jxl::kBitsPerByte); - if (format->align > 1) { - row_size = jxl::DivCeil(row_size, format->align) * format->align; - } - *size = row_size * ysize; - return JXL_DEC_SUCCESS; + return GetMinSize(dec, format, 0, size, false); } JxlDecoderStatus JxlDecoderSetImageOutBuffer(JxlDecoder* dec, @@ -2463,22 +2468,7 @@ JxlDecoderStatus JxlDecoderExtraChannelBufferSize(const JxlDecoder* dec, return JXL_API_ERROR("Invalid extra channel index"); } - size_t num_channels = 1; // Do not use format's num_channels - - size_t bits; - JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits); - if (status != JXL_DEC_SUCCESS) return status; - - size_t xsize, ysize; - GetCurrentDimensions(dec, xsize, ysize); - size_t row_size = - jxl::DivCeil(xsize * num_channels * bits, jxl::kBitsPerByte); - if (format->align > 1) { - row_size = jxl::DivCeil(row_size, format->align) * format->align; - } - *size = row_size * ysize; - - return JXL_DEC_SUCCESS; + return GetMinSize(dec, format, 1, size, false); } JxlDecoderStatus JxlDecoderSetExtraChannelBuffer(JxlDecoder* dec, @@ -2577,8 +2567,9 @@ JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec, } } header->name_length = dec->frame_header->name.size(); - header->is_last = dec->frame_header->is_last; - size_t xsize, ysize; + header->is_last = TO_JXL_BOOL(dec->frame_header->is_last); + size_t xsize; + size_t ysize; GetCurrentDimensions(dec, xsize, ysize); header->layer_info.xsize = xsize; header->layer_info.ysize = ysize; @@ -2620,7 +2611,7 @@ JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec, header->layer_info.blend_info.alpha = dec->frame_header->blending_info.alpha_channel; header->layer_info.blend_info.clamp = - dec->frame_header->blending_info.clamp; + TO_JXL_BOOL(dec->frame_header->blending_info.clamp); header->layer_info.save_as_reference = dec->frame_header->save_as_reference; } return JXL_DEC_SUCCESS; @@ -2643,7 +2634,7 @@ JxlDecoderStatus JxlDecoderGetExtraChannelBlendInfo(const JxlDecoder* dec, blend_info->alpha = dec->frame_header->extra_channel_blending_info[index].alpha_channel; blend_info->clamp = - dec->frame_header->extra_channel_blending_info[index].clamp; + TO_JXL_BOOL(dec->frame_header->extra_channel_blending_info[index].clamp); return JXL_DEC_SUCCESS; } @@ -2717,7 +2708,7 @@ JxlDecoderStatus JxlDecoderSetOutputColorProfile( "setting output color profile from icc_data not yet implemented."); } JXL_API_RETURN_IF_ERROR( - (int)output_encoding.MaybeSetColorEncoding(std::move(c_dst))); + static_cast<int>(output_encoding.MaybeSetColorEncoding(c_dst))); return JXL_DEC_SUCCESS; } @@ -2769,7 +2760,7 @@ JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec, JXL_BOOL decompress) { // TODO(lode): return error if libbrotli is not compiled in the jxl decoding // library - dec->decompress_boxes = decompress; + dec->decompress_boxes = FROM_JXL_BOOL(decompress); return JXL_DEC_SUCCESS; } @@ -2798,6 +2789,17 @@ JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec, return JXL_DEC_SUCCESS; } +JxlDecoderStatus JxlDecoderGetBoxSizeContents(const JxlDecoder* dec, + uint64_t* size) { + if (!dec->box_event) { + return JXL_API_ERROR("can only get box info after JXL_DEC_BOX event"); + } + if (size) { + *size = dec->box_contents_size; + } + return JXL_DEC_SUCCESS; +} + JxlDecoderStatus JxlDecoderSetProgressiveDetail(JxlDecoder* dec, JxlProgressiveDetail detail) { if (detail != kDC && detail != kLastPasses && detail != kPasses) { diff --git a/third_party/jpeg-xl/lib/jxl/decode_test.cc b/third_party/jpeg-xl/lib/jxl/decode_test.cc index caee6dbc56..33176cfd66 100644 --- a/third_party/jpeg-xl/lib/jxl/decode_test.cc +++ b/third_party/jpeg-xl/lib/jxl/decode_test.cc @@ -47,7 +47,6 @@ #include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/dec_external_image.h" #include "lib/jxl/enc_aux_out.h" -#include "lib/jxl/enc_butteraugli_comparator.h" #include "lib/jxl/enc_external_image.h" #include "lib/jxl/enc_fields.h" #include "lib/jxl/enc_frame.h" @@ -113,23 +112,23 @@ enum CodeStreamBoxFormat { }; // Unknown boxes for testing -static const char* unk1_box_type = "unk1"; -static const char* unk1_box_contents = "abcdefghijklmnopqrstuvwxyz"; -static const size_t unk1_box_size = strlen(unk1_box_contents); -static const char* unk2_box_type = "unk2"; -static const char* unk2_box_contents = "0123456789"; -static const size_t unk2_box_size = strlen(unk2_box_contents); -static const char* unk3_box_type = "unk3"; -static const char* unk3_box_contents = "ABCDEF123456"; -static const size_t unk3_box_size = strlen(unk3_box_contents); +const char* unk1_box_type = "unk1"; +const char* unk1_box_contents = "abcdefghijklmnopqrstuvwxyz"; +const size_t unk1_box_size = strlen(unk1_box_contents); +const char* unk2_box_type = "unk2"; +const char* unk2_box_contents = "0123456789"; +const size_t unk2_box_size = strlen(unk2_box_contents); +const char* unk3_box_type = "unk3"; +const char* unk3_box_contents = "ABCDEF123456"; +const size_t unk3_box_size = strlen(unk3_box_contents); // Box with brob-compressed exif, including header -static const uint8_t* box_brob_exif = reinterpret_cast<const uint8_t*>( +const uint8_t* box_brob_exif = reinterpret_cast<const uint8_t*>( "\0\0\0@brobExif\241\350\2\300\177\244v\2525\304\360\27=?\267{" "\33\37\314\332\214QX17PT\"\256\0\0\202s\214\313t\333\310\320k\20\276\30" "\204\277l$\326c#\1\b"); size_t box_brob_exif_size = 64; // The uncompressed Exif data from the brob box -static const uint8_t* exif_uncompressed = reinterpret_cast<const uint8_t*>( +const uint8_t* exif_uncompressed = reinterpret_cast<const uint8_t*>( "\0\0\0\0MM\0*" "\0\0\0\b\0\5\1\22\0\3\0\0\0\1\0\5\0\0\1\32\0\5\0\0\0\1\0\0\0J\1\33\0\5\0\0" "\0\1\0\0\0R\1(" @@ -193,7 +192,7 @@ void AppendTestBox(const char* type, const char* contents, size_t contents_size, bytes->push_back(type[2]); bytes->push_back(type[3]); const uint8_t* contents_u = reinterpret_cast<const uint8_t*>(contents); - Bytes(contents_u, contents_size).AppendTo(bytes); + Bytes(contents_u, contents_size).AppendTo(*bytes); } enum PreviewMode { @@ -214,13 +213,15 @@ void GeneratePreview(PreviewMode preview_mode, ImageBundle* ib) { } } }; - Image3F preview(ib->xsize() * 7, ib->ysize() * 7); + JXL_ASSIGN_OR_DIE(Image3F preview, + Image3F::Create(ib->xsize() * 7, ib->ysize() * 7)); for (size_t c = 0; c < 3; ++c) { upsample7(ib->color()->Plane(c), &preview.Plane(c)); } std::vector<ImageF> extra_channels; for (size_t i = 0; i < ib->extra_channels().size(); ++i) { - ImageF ec(ib->xsize() * 7, ib->ysize() * 7); + JXL_ASSIGN_OR_DIE(ImageF ec, + ImageF::Create(ib->xsize() * 7, ib->ysize() * 7)); upsample7(ib->extra_channels()[i], &ec); extra_channels.emplace_back(std::move(ec)); } @@ -258,7 +259,8 @@ std::vector<uint8_t> CreateTestJXLCodestream( const TestCodestreamParams& params) { // Compress the pixels with JPEG XL. bool grayscale = (num_channels <= 2); - bool include_alpha = !(num_channels & 1) && params.jpeg_codestream == nullptr; + bool have_alpha = ((num_channels & 1) == 0); + bool include_alpha = have_alpha && params.jpeg_codestream == nullptr; size_t bitdepth = params.jpeg_codestream == nullptr ? 16 : 8; CodecInOut io; io.SetSize(xsize, ysize); @@ -296,7 +298,8 @@ std::vector<uint8_t> CreateTestJXLCodestream( if (jxl::extras::CanDecode(jxl::extras::Codec::kJPG)) { std::vector<uint8_t> jpeg_bytes; extras::PackedPixelFile ppf; - extras::PackedFrame frame(xsize, ysize, format); + JXL_ASSIGN_OR_DIE(extras::PackedFrame frame, + extras::PackedFrame::Create(xsize, ysize, format)); JXL_ASSERT(frame.color.pixels_size == pixels.size()); memcpy(frame.color.pixels(0, 0, 0), pixels.data(), pixels.size()); ppf.frames.emplace_back(std::move(frame)); @@ -307,9 +310,9 @@ std::vector<uint8_t> CreateTestJXLCodestream( auto encoder = extras::GetJPEGEncoder(); encoder->SetOption("quality", "70"); extras::EncodedImage encoded; - EXPECT_TRUE(encoder->Encode(ppf, &encoded)); + EXPECT_TRUE(encoder->Encode(ppf, &encoded, nullptr)); jpeg_bytes = encoded.bitstreams[0]; - Bytes(jpeg_bytes).AppendTo(params.jpeg_codestream); + Bytes(jpeg_bytes).AppendTo(*params.jpeg_codestream); EXPECT_TRUE(jxl::jpeg::DecodeImageJPG( jxl::Bytes(jpeg_bytes.data(), jpeg_bytes.size()), &io)); EXPECT_TRUE( @@ -321,7 +324,7 @@ std::vector<uint8_t> CreateTestJXLCodestream( } } if (params.preview_mode) { - io.preview_frame = io.Main().Copy(); + JXL_ASSIGN_OR_DIE(io.preview_frame, io.Main().Copy()); GeneratePreview(params.preview_mode, &io.preview_frame); io.metadata.m.have_preview = true; EXPECT_TRUE(io.metadata.m.preview_size.Set(io.preview_frame.xsize(), @@ -358,11 +361,11 @@ std::vector<uint8_t> CreateTestJXLCodestream( compressed.data() + compressed.size()); std::vector<uint8_t> c; - Bytes(header).AppendTo(&c); + Bytes(header).AppendTo(c); if (params.jpeg_codestream != nullptr) { jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false, &c); - Bytes(jpeg_data).AppendTo(&c); + Bytes(jpeg_data).AppendTo(c); } uint32_t jxlp_index = 0; if (add_container == kCSBF_Multi_First_Empty) { @@ -381,7 +384,7 @@ std::vector<uint8_t> CreateTestJXLCodestream( c.push_back('l'); c.push_back('p'); AppendU32BE(jxlp_index++, &c); - Bytes(compressed0).AppendTo(&c); + Bytes(compressed0).AppendTo(c); // A few non-codestream boxes in between AppendTestBox(unk1_box_type, unk1_box_contents, unk1_box_size, false, &c); AppendTestBox(unk2_box_type, unk2_box_contents, unk2_box_size, false, &c); @@ -399,7 +402,7 @@ std::vector<uint8_t> CreateTestJXLCodestream( c.push_back('l'); c.push_back('p'); AppendU32BE(jxlp_index++, &c); - Bytes(compressed1).AppendTo(&c); + Bytes(compressed1).AppendTo(c); // Third (last) codestream part AppendU32BE(add_container == kCSBF_Multi_Zero_Terminated ? 0 @@ -414,7 +417,7 @@ std::vector<uint8_t> CreateTestJXLCodestream( } else { AppendU32BE(jxlp_index++, &c); } - Bytes(compressed2).AppendTo(&c); + Bytes(compressed2).AppendTo(c); if (add_container == kCSBF_Multi_Last_Empty_Other) { // Empty placeholder codestream part AppendU32BE(12, &c); @@ -437,14 +440,14 @@ std::vector<uint8_t> CreateTestJXLCodestream( compressed.swap(c); } else { std::vector<uint8_t> c; - Bytes(header).AppendTo(&c); + Bytes(header).AppendTo(c); if (params.jpeg_codestream != nullptr) { jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false, &c); - Bytes(jpeg_data).AppendTo(&c); + Bytes(jpeg_data).AppendTo(c); } if (add_container == kCSBF_Brob_Exif) { - Bytes(box_brob_exif, box_brob_exif_size).AppendTo(&c); + Bytes(box_brob_exif, box_brob_exif_size).AppendTo(c); } AppendU32BE(add_container == kCSBF_Single_Zero_Terminated ? 0 @@ -454,7 +457,7 @@ std::vector<uint8_t> CreateTestJXLCodestream( c.push_back('x'); c.push_back('l'); c.push_back('c'); - Bytes(compressed).AppendTo(&c); + Bytes(compressed).AppendTo(c); if (add_container == kCSBF_Single_Other) { AppendTestBox(unk1_box_type, unk1_box_contents, unk1_box_size, false, &c); @@ -609,7 +612,7 @@ std::vector<uint8_t> DecodeWithAPI(Span<const uint8_t> compressed, bool use_callback, bool set_buffer_early, bool use_resizable_runner, bool require_boxes, bool expect_success) { - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); std::vector<uint8_t> pixels = DecodeWithAPI(dec, compressed, format, use_callback, set_buffer_early, use_resizable_runner, require_boxes, expect_success); @@ -622,6 +625,11 @@ std::vector<uint8_t> DecodeWithAPI(Span<const uint8_t> compressed, //////////////////////////////////////////////////////////////////////////////// +using jxl::Image3F; +using jxl::ImageF; +using jxl::test::BoolToCStr; +using jxl::test::ButteraugliDistance; + TEST(DecodeTest, JxlSignatureCheckTest) { std::vector<std::pair<int, std::vector<uint8_t>>> tests = { // No JPEGXL header starts with 'a'. @@ -728,22 +736,22 @@ std::vector<uint8_t> GetTestHeader(size_t xsize, size_t ysize, const std::vector<uint8_t> codestream_box_header = {0, 0, 0, 0xff, 'j', 'x', 'l', 'c'}; - for (size_t i = 0; i < signature_box.size(); i++) { - writer.Write(8, signature_box[i]); + for (uint8_t c : signature_box) { + writer.Write(8, c); } - for (size_t i = 0; i < filetype_box.size(); i++) { - writer.Write(8, filetype_box[i]); + for (uint8_t c : filetype_box) { + writer.Write(8, c); } if (insert_extra_box) { - for (size_t i = 0; i < extra_box_header.size(); i++) { - writer.Write(8, extra_box_header[i]); + for (uint8_t c : extra_box_header) { + writer.Write(8, c); } for (size_t i = 0; i < 255 - 8; i++) { writer.Write(8, 0); } } - for (size_t i = 0; i < codestream_box_header.size(); i++) { - writer.Write(8, codestream_box_header[i]); + for (uint8_t c : codestream_box_header) { + writer.Write(8, c); } } @@ -794,7 +802,7 @@ TEST(DecodeTest, BasicInfoTest) { size_t bits_per_sample[2] = {8, 23}; size_t orientation[2] = {3, 5}; size_t alpha_bits[2] = {0, 8}; - JXL_BOOL have_container[2] = {0, 1}; + bool have_container[2] = {false, true}; bool xyb_encoded = false; std::vector<std::vector<uint8_t>> test_samples; @@ -823,14 +831,15 @@ TEST(DecodeTest, BasicInfoTest) { JxlDecoderStatus status = JxlDecoderProcessInput(dec); JxlBasicInfo info; - bool have_basic_info = !JxlDecoderGetBasicInfo(dec, &info); + JxlDecoderStatus bi_status = JxlDecoderGetBasicInfo(dec, &info); + bool have_basic_info = (bi_status == JXL_DEC_SUCCESS); if (size == data.size()) { EXPECT_EQ(JXL_DEC_BASIC_INFO, status); // All header bytes given so the decoder must have the basic info. EXPECT_EQ(true, have_basic_info); - EXPECT_EQ(have_container[i], info.have_container); + EXPECT_EQ(have_container[i], FROM_JXL_BOOL(info.have_container)); EXPECT_EQ(alpha_bits[i], info.alpha_bits); // Orientations 5..8 swap the dimensions if (orientation[i] >= 5) { @@ -1291,7 +1300,7 @@ class DecodeTestParam : public ::testing::TestWithParam<PixelTestConfig> {}; TEST_P(DecodeTestParam, PixelTest) { PixelTestConfig config = GetParam(); - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); if (config.keep_orientation) { EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetKeepOrientation(dec, JXL_TRUE)); @@ -1352,7 +1361,7 @@ TEST_P(DecodeTestParam, PixelTest) { color_encoding, 16, format_orig, nullptr, &io.Main())); - for (size_t i = 0; i < pixels.size(); i++) pixels[i] = 0; + for (uint8_t& pixel : pixels) pixel = 0; EXPECT_TRUE(ConvertToExternal( io.Main(), 16, /*float_out=*/false, orig_channels, JXL_BIG_ENDIAN, @@ -1420,7 +1429,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() { c.add_intrinsic_size = intrinsic_size; c.xsize = xsize; c.ysize = ysize; - c.add_container = (CodeStreamBoxFormat)box; + c.add_container = box; c.output_channels = ch.output_channels; c.data_type = format.data_type; c.endianness = format.endianness; @@ -1435,7 +1444,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() { // Test output formats and methods. for (ChannelInfo ch : ch_info) { - for (int use_callback = 0; use_callback <= 1; use_callback++) { + for (bool use_callback : {false, true}) { for (size_t upsampling : {1, 2, 4, 8}) { for (OutputFormat fmt : out_formats) { make_test(ch, 301, 33, jxl::kNoPreview, @@ -1451,8 +1460,8 @@ std::vector<PixelTestConfig> GeneratePixelTests() { // Test codestream formats. for (size_t box = 1; box < kCSBF_NUM_ENTRIES; ++box) { make_test(ch_info[0], 77, 33, jxl::kNoPreview, - /*add_intrinsic_size=*/false, (CodeStreamBoxFormat)box, - JXL_ORIENT_IDENTITY, + /*add_intrinsic_size=*/false, + static_cast<CodeStreamBoxFormat>(box), JXL_ORIENT_IDENTITY, /*keep_orientation=*/false, out_formats[0], /*use_callback=*/false, /*set_buffer_early=*/false, /*resizable_runner=*/false, 1); @@ -1460,7 +1469,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() { // Test previews. for (int preview_mode = 0; preview_mode < jxl::kNumPreviewModes; preview_mode++) { - make_test(ch_info[0], 77, 33, (jxl::PreviewMode)preview_mode, + make_test(ch_info[0], 77, 33, static_cast<jxl::PreviewMode>(preview_mode), /*add_intrinsic_size=*/false, CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY, /*keep_orientation=*/false, out_formats[0], @@ -1468,8 +1477,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() { /*resizable_runner=*/false, 1); } // Test intrinsic sizes. - for (int add_intrinsic_size = 0; add_intrinsic_size <= 1; - add_intrinsic_size++) { + for (bool add_intrinsic_size : {false, true}) { make_test(ch_info[0], 55, 34, jxl::kNoPreview, add_intrinsic_size, CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY, /*keep_orientation=*/false, out_formats[0], @@ -1496,8 +1504,8 @@ std::vector<PixelTestConfig> GeneratePixelTests() { // Test orientations. for (int orientation = 2; orientation <= 8; ++orientation) { - for (int keep_orientation = 0; keep_orientation <= 1; keep_orientation++) { - for (int use_callback = 0; use_callback <= 1; use_callback++) { + for (bool keep_orientation : {false, true}) { + for (bool use_callback : {false, true}) { for (ChannelInfo ch : ch_info) { for (OutputFormat fmt : out_formats) { make_test(ch, 280, 12, jxl::kNoPreview, @@ -1549,7 +1557,7 @@ std::ostream& operator<<(std::ostream& os, const PixelTestConfig& c) { } if (c.add_container != CodeStreamBoxFormat::kCSBF_None) { os << "Box"; - os << (size_t)c.add_container; + os << static_cast<size_t>(c.add_container); } if (c.preview_mode == jxl::kSmallPreview) os << "Preview"; if (c.preview_mode == jxl::kBigPreview) os << "BigPreview"; @@ -1575,9 +1583,10 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeTest, DecodeTestParam, PixelTestDescription); TEST(DecodeTest, PixelTestWithICCProfileLossless) { - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; size_t num_pixels = xsize * ysize; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -1642,9 +1651,10 @@ TEST(DecodeTest, PixelTestWithICCProfileLossless) { } TEST(DecodeTest, PixelTestWithICCProfileLossy) { - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; size_t num_pixels = xsize * ysize; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -1678,7 +1688,7 @@ TEST(DecodeTest, PixelTestWithICCProfileLossy) { jxl::ColorEncoding color_encoding1; jxl::IccBytes icc; - jxl::Bytes(icc_data).AppendTo(&icc); + jxl::Bytes(icc_data).AppendTo(icc); EXPECT_TRUE(color_encoding1.SetICC(std::move(icc), JxlGetDefaultCms())); jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size()); jxl::CodecInOut io1; @@ -1688,10 +1698,10 @@ TEST(DecodeTest, PixelTestWithICCProfileLossy) { /*pool=*/nullptr, &io1.Main())); jxl::ButteraugliParams ba; - EXPECT_THAT( + EXPECT_SLIGHTLY_BELOW( ButteraugliDistance(io0.frames, io1.frames, ba, *JxlGetDefaultCms(), /*distmap=*/nullptr, nullptr), - IsSlightlyBelow(0.56f)); + 0.56f); JxlDecoderDestroy(dec); } @@ -1753,7 +1763,8 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P( DecodeAllEncodingsTestInstantiation, DecodeAllEncodingsTest, ::testing::ValuesIn(jxl::test::AllEncodings())); TEST_P(DecodeAllEncodingsTest, PreserveOriginalProfileTest) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE; @@ -1796,7 +1807,8 @@ namespace { void SetPreferredColorProfileTest( const jxl::test::ColorEncodingDescriptor& from, bool icc_dst, bool use_cms) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE; jxl::ColorEncoding c_in = jxl::test::ColorEncodingFromDescriptor(from); if (c_in.GetRenderingIntent() != jxl::RenderingIntent::kRelative) return; @@ -1970,6 +1982,7 @@ void DecodeImageWithColorEncoding(const std::vector<uint8_t>& compressed, EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec)); + // TODO(eustas): why unused? std::string color_space_in = GetOrigProfile(dec); if (with_cms) { JxlDecoderSetCms(dec, *JxlGetDefaultCms()); @@ -2009,7 +2022,8 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P( TEST_P(DecodeAllEncodingsWithCMSTest, DecodeWithCMS) { auto all_encodings = jxl::test::AllEncodings(); uint32_t num_channels = 3; - size_t xsize = 177, ysize = 123; + size_t xsize = 177; + size_t ysize = 123; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); jxl::TestCodestreamParams params; @@ -2045,9 +2059,10 @@ TEST_P(DecodeAllEncodingsWithCMSTest, DecodeWithCMS) { // and to RGBA8 TEST(DecodeTest, PixelTestOpaqueSrgbLossy) { for (unsigned channels = 3; channels <= 4; channels++) { - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; size_t num_pixels = xsize * ysize; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); @@ -2082,10 +2097,10 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossy) { /*pool=*/nullptr, &io1.Main())); jxl::ButteraugliParams ba; - EXPECT_THAT( + EXPECT_SLIGHTLY_BELOW( ButteraugliDistance(io0.frames, io1.frames, ba, *JxlGetDefaultCms(), /*distmap=*/nullptr, nullptr), - IsSlightlyBelow(0.65f)); + 0.65f); JxlDecoderDestroy(dec); } @@ -2094,9 +2109,10 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossy) { // Opaque image with noise enabled, decoded to RGB8 and RGBA8. TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) { for (unsigned channels = 3; channels <= 4; channels++) { - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); - size_t xsize = 512, ysize = 300; + size_t xsize = 512; + size_t ysize = 300; size_t num_pixels = xsize * ysize; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); @@ -2132,26 +2148,28 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) { /*pool=*/nullptr, &io1.Main())); jxl::ButteraugliParams ba; - EXPECT_THAT( + EXPECT_SLIGHTLY_BELOW( ButteraugliDistance(io0.frames, io1.frames, ba, *JxlGetDefaultCms(), /*distmap=*/nullptr, nullptr), - IsSlightlyBelow(1.3f)); + 1.3f); JxlDecoderDestroy(dec); } } TEST(DecodeTest, ProcessEmptyInputWithBoxes) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); jxl::CompressParams cparams; uint32_t channels = 3; JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); jxl::TestCodestreamParams params; - params.box_format = (CodeStreamBoxFormat)i; - printf("Testing empty input with box format %d\n", (int)params.box_format); + params.box_format = static_cast<CodeStreamBoxFormat>(i); + printf("Testing empty input with box format %d\n", + static_cast<int>(params.box_format)); std::vector<uint8_t> compressed = jxl::CreateTestJXLCodestream( jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params); const int events = @@ -2175,14 +2193,15 @@ TEST(DecodeTest, ProcessEmptyInputWithBoxes) { } TEST(DecodeTest, ExtraBytesAfterCompressedStream) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; size_t num_pixels = xsize * ysize; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); jxl::CompressParams cparams; for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { - CodeStreamBoxFormat box_format = (CodeStreamBoxFormat)i; + CodeStreamBoxFormat box_format = static_cast<CodeStreamBoxFormat>(i); if (box_format == kCSBF_Multi_Other_Zero_Terminated) continue; - printf("Testing with box format %d\n", (int)box_format); + printf("Testing with box format %d\n", static_cast<int>(box_format)); size_t last_unknown_box_size = 0; if (box_format == kCSBF_Single_Other) { last_unknown_box_size = unk1_box_size + 8; @@ -2201,7 +2220,7 @@ TEST(DecodeTest, ExtraBytesAfterCompressedStream) { compressed.push_back(0); compressed.push_back(1); compressed.push_back(2); - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); uint32_t channels = 3; JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( @@ -2217,14 +2236,15 @@ TEST(DecodeTest, ExtraBytesAfterCompressedStream) { } TEST(DecodeTest, ExtraBytesAfterCompressedStreamRequireBoxes) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; size_t num_pixels = xsize * ysize; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); jxl::CompressParams cparams; for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { - CodeStreamBoxFormat box_format = (CodeStreamBoxFormat)i; + CodeStreamBoxFormat box_format = static_cast<CodeStreamBoxFormat>(i); if (box_format == kCSBF_Multi_Other_Zero_Terminated) continue; - printf("Testing with box format %d\n", (int)box_format); + printf("Testing with box format %d\n", static_cast<int>(box_format)); bool expect_success = (box_format == kCSBF_None || box_format == kCSBF_Single_Zero_Terminated || box_format == kCSBF_Multi_Zero_Terminated); @@ -2236,7 +2256,7 @@ TEST(DecodeTest, ExtraBytesAfterCompressedStreamRequireBoxes) { compressed.push_back(0); compressed.push_back(1); compressed.push_back(2); - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); uint32_t channels = 3; JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( @@ -2251,35 +2271,38 @@ TEST(DecodeTest, ExtraBytesAfterCompressedStreamRequireBoxes) { } TEST(DecodeTest, ConcatenatedCompressedStreams) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; size_t num_pixels = xsize * ysize; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); jxl::CompressParams cparams; for (int i = 0; i < kCSBF_NUM_ENTRIES; ++i) { - CodeStreamBoxFormat first_box_format = (CodeStreamBoxFormat)i; + CodeStreamBoxFormat first_box_format = static_cast<CodeStreamBoxFormat>(i); if (first_box_format == kCSBF_Multi_Other_Zero_Terminated) continue; jxl::TestCodestreamParams params1; params1.box_format = first_box_format; std::vector<uint8_t> compressed1 = jxl::CreateTestJXLCodestream( jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params1); for (int j = 0; j < kCSBF_NUM_ENTRIES; ++j) { - CodeStreamBoxFormat second_box_format = (CodeStreamBoxFormat)j; + CodeStreamBoxFormat second_box_format = + static_cast<CodeStreamBoxFormat>(j); if (second_box_format == kCSBF_Multi_Other_Zero_Terminated) continue; - printf("Testing with box format pair %d, %d\n", (int)first_box_format, - (int)second_box_format); + printf("Testing with box format pair %d, %d\n", + static_cast<int>(first_box_format), + static_cast<int>(second_box_format)); jxl::TestCodestreamParams params2; params2.box_format = second_box_format; std::vector<uint8_t> compressed2 = jxl::CreateTestJXLCodestream( jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, 3, params2); std::vector<uint8_t> concat; - jxl::Bytes(compressed1).AppendTo(&concat); - jxl::Bytes(compressed2).AppendTo(&concat); + jxl::Bytes(compressed1).AppendTo(concat); + jxl::Bytes(compressed2).AppendTo(concat); uint32_t channels = 3; JxlPixelFormat format = {channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0}; size_t remaining = concat.size(); for (int part = 0; part < 2; ++part) { printf(" Decoding part %d\n", part + 1); - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); size_t pos = concat.size() - remaining; bool expect_success = (part == 0 || second_box_format == kCSBF_None || @@ -2300,7 +2323,8 @@ TEST(DecodeTest, ConcatenatedCompressedStreams) { } void TestPartialStream(bool reconstructible_jpeg) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; uint32_t channels = 4; if (reconstructible_jpeg) { channels = 3; @@ -2325,7 +2349,7 @@ void TestPartialStream(bool reconstructible_jpeg) { std::vector<std::vector<uint8_t>> codestreams(kCSBF_NUM_ENTRIES); std::vector<std::vector<uint8_t>> jpeg_codestreams(kCSBF_NUM_ENTRIES); for (size_t i = 0; i < kCSBF_NUM_ENTRIES; ++i) { - params.box_format = (CodeStreamBoxFormat)i; + params.box_format = static_cast<CodeStreamBoxFormat>(i); if (reconstructible_jpeg) { params.jpeg_codestream = &jpeg_codestreams[i]; } @@ -2338,10 +2362,10 @@ void TestPartialStream(bool reconstructible_jpeg) { // box parsing. std::vector<size_t> increments = {1, 3, 17, 23, 120, 700, 1050}; - for (size_t index = 0; index < increments.size(); index++) { + for (size_t increment : increments) { for (size_t i = 0; i < kCSBF_NUM_ENTRIES; ++i) { - if (reconstructible_jpeg && - (CodeStreamBoxFormat)i == CodeStreamBoxFormat::kCSBF_None) { + if (reconstructible_jpeg && static_cast<CodeStreamBoxFormat>(i) == + CodeStreamBoxFormat::kCSBF_None) { continue; } const std::vector<uint8_t>& data = codestreams[i]; @@ -2376,7 +2400,6 @@ void TestPartialStream(bool reconstructible_jpeg) { break; } - size_t increment = increments[index]; // End of the file reached, should be the final test. if (total_size + increment > data.size()) { increment = data.size() - total_size; @@ -2467,7 +2490,7 @@ TEST(DecodeTest, DCNotGettableTest) { "\363\6\22\bp\0\200\237\34\231W2d\255$\1", 68); - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_BASIC_INFO)); @@ -2487,7 +2510,8 @@ TEST(DecodeTest, DCNotGettableTest) { } TEST(DecodeTest, PreviewTest) { - size_t xsize = 77, ysize = 120; + size_t xsize = 77; + size_t ysize = 120; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; for (jxl::PreviewMode mode : {jxl::kSmallPreview, jxl::kBigPreview}) { @@ -2499,7 +2523,7 @@ TEST(DecodeTest, PreviewTest) { JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = compressed.size(); @@ -2561,7 +2585,8 @@ TEST(DecodeTest, PreviewTest) { } TEST(DecodeTest, AlignTest) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -2575,22 +2600,27 @@ TEST(DecodeTest, AlignTest) { size_t align = 17; JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, align}; // On purpose not using jxl::RoundUpTo to test it independently. - size_t expected_line_bytes = (1 * 3 * xsize + align - 1) / align * align; + size_t expected_line_size_last = 1 * 3 * xsize; + size_t expected_line_size = + ((expected_line_size_last + align - 1) / align) * align; + size_t expected_pixels_size = + expected_line_size * (ysize - 1) + expected_line_size_last; - for (int use_callback = 0; use_callback <= 1; ++use_callback) { + for (bool use_callback : {false, true}) { std::vector<uint8_t> pixels2 = jxl::DecodeWithAPI( jxl::Bytes(compressed.data(), compressed.size()), format, use_callback, /*set_buffer_early=*/false, /*use_resizable_runner=*/false, /*require_boxes=*/false, /*expect_success=*/true); - EXPECT_EQ(expected_line_bytes * ysize, pixels2.size()); + EXPECT_EQ(expected_pixels_size, pixels2.size()); EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize, ysize, format_orig, format)); } } TEST(DecodeTest, AnimationTest) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; static const size_t num_frames = 2; std::vector<uint8_t> frames[2]; frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); @@ -2631,12 +2661,12 @@ TEST(DecodeTest, AnimationTest) { // Decode and test the animation frames - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = compressed.size(); void* runner = JxlThreadParallelRunnerCreate( - NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner)); @@ -2690,7 +2720,8 @@ TEST(DecodeTest, AnimationTest) { } TEST(DecodeTest, AnimationTestStreaming) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; static const size_t num_frames = 2; std::vector<uint8_t> frames[2]; frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); @@ -2733,7 +2764,7 @@ TEST(DecodeTest, AnimationTestStreaming) { const size_t step_size = 16; - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = 0; size_t frame_headers_seen = 0; @@ -2741,7 +2772,7 @@ TEST(DecodeTest, AnimationTestStreaming) { bool seen_basic_info = false; void* runner = JxlThreadParallelRunnerCreate( - NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner)); @@ -2805,7 +2836,7 @@ TEST(DecodeTest, AnimationTestStreaming) { frames_seen++; EXPECT_EQ(frame_headers_seen, frames_seen); } else { - fprintf(stderr, "Unexpected status: %d\n", (int)status); + fprintf(stderr, "Unexpected status: %d\n", static_cast<int>(status)); FAIL(); } } @@ -2822,7 +2853,8 @@ TEST(DecodeTest, AnimationTestStreaming) { } TEST(DecodeTest, ExtraChannelTest) { - size_t xsize = 55, ysize = 257; + size_t xsize = 55; + size_t ysize = 257; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -2836,7 +2868,7 @@ TEST(DecodeTest, ExtraChannelTest) { size_t align = 17; JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, align}; - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents( dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE)); @@ -2899,7 +2931,8 @@ TEST(DecodeTest, ExtraChannelTest) { } TEST(DecodeTest, SkipCurrentFrameTest) { - size_t xsize = 90, ysize = 120; + size_t xsize = 90; + size_t ysize = 120; constexpr size_t num_frames = 7; std::vector<uint8_t> frames[num_frames]; for (size_t i = 0; i < num_frames; i++) { @@ -2945,7 +2978,7 @@ TEST(DecodeTest, SkipCurrentFrameTest) { cparams.custom_progressive_mode = &progressive_mode; EXPECT_TRUE(jxl::test::EncodeFile(cparams, &io, &compressed)); - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = compressed.size(); @@ -2964,7 +2997,7 @@ TEST(DecodeTest, SkipCurrentFrameTest) { EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); for (size_t i = 0; i < num_frames; ++i) { - printf("Decoding frame %d\n", (int)i); + printf("Decoding frame %d\n", static_cast<int>(i)); EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderSkipCurrentFrame(dec)); std::vector<uint8_t> pixels(buffer_size); EXPECT_EQ(JXL_DEC_FRAME, JxlDecoderProcessInput(dec)); @@ -3010,7 +3043,8 @@ TEST(DecodeTest, SkipCurrentFrameTest) { } TEST(DecodeTest, SkipFrameTest) { - size_t xsize = 90, ysize = 120; + size_t xsize = 90; + size_t ysize = 120; constexpr size_t num_frames = 16; std::vector<uint8_t> frames[num_frames]; for (size_t i = 0; i < num_frames; i++) { @@ -3056,12 +3090,12 @@ TEST(DecodeTest, SkipFrameTest) { // Decode and test the animation frames - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = compressed.size(); void* runner = JxlThreadParallelRunnerCreate( - NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner)); @@ -3146,7 +3180,8 @@ TEST(DecodeTest, SkipFrameTest) { } TEST(DecodeTest, SkipFrameWithBlendingTest) { - size_t xsize = 90, ysize = 120; + size_t xsize = 90; + size_t ysize = 120; constexpr size_t num_frames = 16; std::vector<uint8_t> frames[num_frames]; JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -3213,23 +3248,22 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) { // Independently decode all frames without any skipping, to create the // expected blended frames, for the actual tests below to compare with. { - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = compressed.size(); void* runner = JxlThreadParallelRunnerCreate( - NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner( dec, JxlThreadParallelRunner, runner)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); - for (size_t i = 0; i < num_frames; ++i) { + for (auto& frame : frames) { EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); - frames[i].resize(xsize * ysize * 6); - EXPECT_EQ(JXL_DEC_SUCCESS, - JxlDecoderSetImageOutBuffer(dec, &format, frames[i].data(), - frames[i].size())); + frame.resize(xsize * ysize * 6); + EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer( + dec, &format, frame.data(), frame.size())); EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec)); } @@ -3240,12 +3274,12 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) { JxlDecoderDestroy(dec); } - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = compressed.size(); void* runner = JxlThreadParallelRunnerCreate( - NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(dec, JxlThreadParallelRunner, runner)); @@ -3360,7 +3394,8 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) { } TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { - size_t xsize = 90, ysize = 120; + size_t xsize = 90; + size_t ysize = 120; constexpr size_t num_frames = 16; std::vector<uint8_t> frames[num_frames + 5]; JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -3376,7 +3411,10 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { std::vector<uint32_t> frame_durations_c; std::vector<uint32_t> frame_durations_nc; - std::vector<uint32_t> frame_xsize, frame_ysize, frame_x0, frame_y0; + std::vector<uint32_t> frame_xsize; + std::vector<uint32_t> frame_ysize; + std::vector<uint32_t> frame_x0; + std::vector<uint32_t> frame_y0; for (size_t i = 0; i < num_frames; ++i) { size_t cropxsize = 1 + xsize * 2 / (i + 1); @@ -3444,12 +3482,12 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { // Independently decode all frames without any skipping, to create the // expected blended frames, for the actual tests below to compare with. { - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = compressed.size(); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing)); void* runner = JxlThreadParallelRunnerCreate( - NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner( dec, JxlThreadParallelRunner, runner)); EXPECT_EQ(JXL_DEC_SUCCESS, @@ -3479,13 +3517,13 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { JxlDecoderDestroy(dec); } - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = compressed.size(); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing)); void* runner = JxlThreadParallelRunnerCreate( - NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner( dec, JxlThreadParallelRunner, runner)); @@ -3648,7 +3686,8 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { TEST(DecodeTest, OrientedCroppedFrameTest) { const auto test = [](bool keep_orientation, uint32_t orientation, uint32_t resampling) { - size_t xsize = 90, ysize = 120; + size_t xsize = 90; + size_t ysize = 120; JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; size_t oxsize = (!keep_orientation && orientation > 4 ? ysize : xsize); size_t oysize = (!keep_orientation && orientation > 4 ? xsize : ysize); @@ -3698,14 +3737,14 @@ TEST(DecodeTest, OrientedCroppedFrameTest) { // Independently decode all frames without any skipping, to create the // expected blended frames, for the actual tests below to compare with. { - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); const uint8_t* next_in = compressed.data(); size_t avail_in = compressed.size(); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetKeepOrientation(dec, keep_orientation)); void* runner = JxlThreadParallelRunnerCreate( - NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads()); + nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads()); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner( dec, JxlThreadParallelRunner, runner)); EXPECT_EQ(JXL_DEC_SUCCESS, @@ -3747,10 +3786,10 @@ TEST(DecodeTest, OrientedCroppedFrameTest) { for (int y = 0; y < static_cast<int>(oysize); y++) { if (y < y0 || y >= y0 + h) continue; // pointers do whole 16-bit RGBA pixels at a time - uint64_t* row_merged = static_cast<uint64_t*>( - (void*)(frames[4].data() + y * oxsize * 8)); - uint64_t* row_layer = static_cast<uint64_t*>( - (void*)(frames[i].data() + (y - y0) * w * 8)); + uint64_t* row_merged = reinterpret_cast<uint64_t*>( + frames[4].data() + y * oxsize * 8); + uint64_t* row_layer = reinterpret_cast<uint64_t*>( + frames[i].data() + (y - y0) * w * 8); for (int x = 0; x < static_cast<int>(oxsize); x++) { if (x < x0 || x >= x0 + w) continue; row_merged[x] = row_layer[x - x0]; @@ -3825,18 +3864,18 @@ void AnalyzeCodestream(const std::vector<uint8_t>& data, codestream.insert(codestream.end(), in + pos + 8, in + pos + box_size); codestream_end = true; } else if (memcmp(in + pos + 4, "jxlp", 4) == 0) { - codestream_end = (LoadBE32(in + pos + 8) & 0x80000000); + codestream_end = ((LoadBE32(in + pos + 8) & 0x80000000) != 0); if (codestream.empty()) { streampos->codestream_start = pos + 12; } else if (box_size > 12 || !codestream_end) { - breakpoints.push_back({codestream.size(), 12}); + breakpoints.emplace_back(codestream.size(), 12); } codestream.insert(codestream.end(), in + pos + 12, in + pos + box_size); } else if (memcmp(in + pos + 4, "jbrd", 4) == 0) { EXPECT_TRUE(codestream.empty()); streampos->jbrd_end = pos + box_size; } else if (!codestream.empty() && !codestream_end) { - breakpoints.push_back({codestream.size(), box_size}); + breakpoints.emplace_back(codestream.size(), box_size); } pos += box_size; } @@ -3931,7 +3970,7 @@ void VerifyProgression(size_t xsize, size_t ysize, uint32_t num_channels, double prev_dist = 1.0; for (;;) { JxlDecoderStatus status = JxlDecoderProcessInput(dec); - printf("bp: %d status: 0x%x\n", bp, (int)status); + printf("bp: %d status: 0x%x\n", bp, static_cast<int>(status)); if (status == JXL_DEC_BASIC_INFO) { JxlBasicInfo info; EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info)); @@ -3977,7 +4016,7 @@ void VerifyProgression(size_t xsize, size_t ysize, uint32_t num_channels, avail_in = breakpoints[bp].file_pos - (next_in - data.data()); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in)); } else { - printf("Unexpected status: 0x%x\n", (int)status); + printf("Unexpected status: 0x%x\n", static_cast<int>(status)); FAIL(); // unexpected returned status } } @@ -3985,7 +4024,8 @@ void VerifyProgression(size_t xsize, size_t ysize, uint32_t num_channels, } TEST(DecodeTest, ProgressionTest) { - size_t xsize = 508, ysize = 470; + size_t xsize = 508; + size_t ysize = 470; uint32_t num_channels = 3; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4020,7 +4060,8 @@ TEST(DecodeTest, ProgressionTest) { } TEST(DecodeTest, ProgressionTestLosslessAlpha) { - size_t xsize = 508, ysize = 470; + size_t xsize = 508; + size_t ysize = 470; uint32_t num_channels = 4; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4063,7 +4104,8 @@ void VerifyFilePosition(size_t expected_pos, const std::vector<uint8_t>& data, } TEST(DecodeTest, InputHandlingTestOneShot) { - size_t xsize = 508, ysize = 470; + size_t xsize = 508; + size_t ysize = 470; uint32_t num_channels = 3; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4072,7 +4114,7 @@ TEST(DecodeTest, InputHandlingTestOneShot) { jxl::TestCodestreamParams params; params.cparams.progressive_dc = 1; params.preview_mode = jxl::kSmallPreview; - params.box_format = (CodeStreamBoxFormat)i; + params.box_format = static_cast<CodeStreamBoxFormat>(i); std::vector<uint8_t> data = jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, num_channels, params); @@ -4167,7 +4209,7 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(InputHandlingTestJPEGOneshot)) { params.cparams.color_transform = jxl::ColorTransform::kNone; params.jpeg_codestream = &jpeg_codestream; params.preview_mode = jxl::kSmallPreview; - params.box_format = (CodeStreamBoxFormat)i; + params.box_format = static_cast<CodeStreamBoxFormat>(i); std::vector<uint8_t> data = jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, channels, params); @@ -4246,7 +4288,8 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(InputHandlingTestJPEGOneshot)) { } TEST(DecodeTest, InputHandlingTestStreaming) { - size_t xsize = 508, ysize = 470; + size_t xsize = 508; + size_t ysize = 470; uint32_t num_channels = 3; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4255,7 +4298,7 @@ TEST(DecodeTest, InputHandlingTestStreaming) { fflush(stdout); jxl::TestCodestreamParams params; params.cparams.progressive_dc = 1; - params.box_format = (CodeStreamBoxFormat)i; + params.box_format = static_cast<CodeStreamBoxFormat>(i); params.preview_mode = jxl::kSmallPreview; std::vector<uint8_t> data = jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), @@ -4334,7 +4377,7 @@ TEST(DecodeTest, InputHandlingTestStreaming) { ASSERT_LT(box_index, streampos.box_start.size()); EXPECT_EQ(file_pos, streampos.box_start[box_index++]); } else { - printf("Unexpected status: 0x%x\n", (int)status); + printf("Unexpected status: 0x%x\n", static_cast<int>(status)); FAIL(); } } @@ -4346,7 +4389,8 @@ TEST(DecodeTest, InputHandlingTestStreaming) { TEST(DecodeTest, FlushTest) { // Size large enough for multiple groups, required to have progressive // stages - size_t xsize = 333, ysize = 300; + size_t xsize = 333; + size_t ysize = 300; uint32_t num_channels = 3; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4421,7 +4465,8 @@ TEST(DecodeTest, FlushTest) { TEST(DecodeTest, FlushTestImageOutCallback) { // Size large enough for multiple groups, required to have progressive // stages - size_t xsize = 333, ysize = 300; + size_t xsize = 333; + size_t ysize = 300; uint32_t num_channels = 3; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4507,7 +4552,8 @@ TEST(DecodeTest, FlushTestImageOutCallback) { TEST(DecodeTest, FlushTestLossyProgressiveAlpha) { // Size large enough for multiple groups, required to have progressive // stages - size_t xsize = 333, ysize = 300; + size_t xsize = 333; + size_t ysize = 300; uint32_t num_channels = 4; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4577,7 +4623,8 @@ TEST(DecodeTest, FlushTestLossyProgressiveAlpha) { JxlDecoderDestroy(dec); } TEST(DecodeTest, FlushTestLossyProgressiveAlphaUpsampling) { - size_t xsize = 533, ysize = 401; + size_t xsize = 533; + size_t ysize = 401; uint32_t num_channels = 4; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4651,7 +4698,8 @@ TEST(DecodeTest, FlushTestLossyProgressiveAlphaUpsampling) { TEST(DecodeTest, FlushTestLosslessProgressiveAlpha) { // Size large enough for multiple groups, required to have progressive // stages - size_t xsize = 333, ysize = 300; + size_t xsize = 333; + size_t ysize = 300; uint32_t num_channels = 4; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4731,17 +4779,19 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeProgressiveTestInstantiation, ::testing::Range(0, 8)); TEST_P(DecodeProgressiveTest, ProgressiveEventTest) { const int params = GetParam(); - int single_group = params & 1; - int lossless = (params >> 1) & 1; + bool single_group = ((params & 1) != 0); + bool lossless = (((params >> 1) & 1) != 0); uint32_t num_channels = 3 + ((params >> 2) & 1); + bool has_alpha = ((num_channels & 1) == 0); std::set<JxlProgressiveDetail> progressive_details = {kDC, kLastPasses, kPasses}; for (auto prog_detail : progressive_details) { // Only few combinations are expected to support outputting // intermediate flushes for complete DC and complete passes. // The test can be updated if more cases are expected to support it. - bool expect_flush = (num_channels & 1) && !lossless; - size_t xsize, ysize; + bool expect_flush = !has_alpha && !lossless; + size_t xsize; + size_t ysize; if (single_group) { // An image smaller than 256x256 ensures it contains only 1 group. xsize = 99; @@ -4774,12 +4824,13 @@ TEST_P(DecodeProgressiveTest, ProgressiveEventTest) { jxl::CreateTestJXLCodestream(jxl::Bytes(pixels.data(), pixels.size()), xsize, ysize, num_channels, params); - for (size_t increment : {(size_t)1, data.size()}) { + for (size_t increment : {static_cast<size_t>(1), data.size()}) { printf( - "Testing with single_group=%d, lossless=%d, " + "Testing with single_group=%s, lossless=%s, " "num_channels=%d, prog_detail=%d, increment=%d\n", - single_group, lossless, (int)num_channels, (int)prog_detail, - (int)increment); + BoolToCStr(single_group), BoolToCStr(lossless), + static_cast<int>(num_channels), static_cast<int>(prog_detail), + static_cast<int>(increment)); std::vector<std::vector<uint8_t>> passes(kNumPasses + 1); for (int i = 0; i <= kNumPasses; ++i) { passes[i].resize(pixels.size()); @@ -4963,13 +5014,13 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) { ASSERT_TRUE( EncodeJPEGData(*orig_io.Main().jpeg_data.get(), &jpeg_data, cparams)); std::vector<uint8_t> container; - jxl::Bytes(jxl::kContainerHeader).AppendTo(&container); + jxl::Bytes(jxl::kContainerHeader).AppendTo(container); jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_data.size(), false, &container); - jxl::Bytes(jpeg_data).AppendTo(&container); + jxl::Bytes(jpeg_data).AppendTo(container); jxl::AppendBoxHeader(jxl::MakeBoxType("jxlc"), 0, true, &container); jxl::PaddedBytes codestream = std::move(writer).TakeBytes(); - jxl::Bytes(codestream).AppendTo(&container); + jxl::Bytes(codestream).AppendTo(container); VerifyJPEGReconstruction(jxl::Bytes(container), jxl::Bytes(orig)); } @@ -4982,7 +5033,8 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionMetadataTest)) { } TEST(DecodeTest, ContinueFinalNonEssentialBoxTest) { - size_t xsize = 80, ysize = 90; + size_t xsize = 80; + size_t ysize = 90; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); jxl::TestCodestreamParams params; params.box_format = kCSBF_Multi_Other_Terminated; @@ -5038,7 +5090,7 @@ TEST(DecodeTest, ContinueFinalNonEssentialBoxTest) { } namespace { -bool BoxTypeEquals(const std::string& type_string, JxlBoxType type) { +bool BoxTypeEquals(const std::string& type_string, const JxlBoxType type) { return type_string.size() == 4 && type_string[0] == type[0] && type_string[1] == type[1] && type_string[2] == type[2] && type_string[3] == type[3]; @@ -5054,28 +5106,38 @@ TEST(DecodeTest, ExtentedBoxSizeTest) { JxlBoxType type; uint64_t box_size; + uint64_t contents_size; EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, orig.data(), orig.size())); EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); EXPECT_TRUE(BoxTypeEquals("JXL ", type)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size)); EXPECT_EQ(12, box_size); + EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size)); + EXPECT_EQ(contents_size + 8, box_size); EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); EXPECT_TRUE(BoxTypeEquals("ftyp", type)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size)); EXPECT_EQ(20, box_size); + EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size)); + EXPECT_EQ(contents_size + 8, box_size); EXPECT_EQ(JXL_DEC_BOX, JxlDecoderProcessInput(dec)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxType(dec, type, JXL_FALSE)); EXPECT_TRUE(BoxTypeEquals("jxlc", type)); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeRaw(dec, &box_size)); EXPECT_EQ(72, box_size); + EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBoxSizeContents(dec, &contents_size)); + // This is an extended box, hence the difference between `box_size` and + // `contents_size` is 16. + EXPECT_EQ(contents_size + 8 + 8, box_size); JxlDecoderDestroy(dec); } TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) { - size_t xsize = 1, ysize = 1; + size_t xsize = 1; + size_t ysize = 1; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); jxl::TestCodestreamParams params; params.box_format = kCSBF_Multi_Other_Terminated; @@ -5096,6 +5158,7 @@ TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) { JxlBoxType type; uint64_t box_size; + uint64_t contents_size; std::vector<uint8_t> contents(50); size_t expected_release_size = 0; @@ -5113,6 +5176,9 @@ TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) { EXPECT_TRUE(BoxTypeEquals(expected_box_types[i], type)); if (expected_box_sizes[i]) { EXPECT_EQ(expected_box_sizes[i], box_size); + EXPECT_EQ(JXL_DEC_SUCCESS, + JxlDecoderGetBoxSizeContents(dec, &contents_size)); + EXPECT_EQ(contents_size + 8, box_size); } if (expected_release_size > 0) { @@ -5147,7 +5213,8 @@ TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) { } TEST(DecodeTest, JXL_BOXES_TEST(ExifBrobBoxTest)) { - size_t xsize = 1, ysize = 1; + size_t xsize = 1; + size_t ysize = 1; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); jxl::TestCodestreamParams params; // Lossless to verify pixels exactly after roundtrip. @@ -5328,7 +5395,8 @@ TEST(DecodeTest, JXL_BOXES_TEST(ExifBrobBoxTest)) { } TEST(DecodeTest, JXL_BOXES_TEST(PartialCodestreamBoxTest)) { - size_t xsize = 23, ysize = 81; + size_t xsize = 23; + size_t ysize = 81; std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; // Lossless to verify pixels exactly after roundtrip. @@ -5482,10 +5550,11 @@ TEST(DecodeTest, JXL_BOXES_TEST(PartialCodestreamBoxTest)) { TEST(DecodeTest, SpotColorTest) { jxl::CodecInOut io; - size_t xsize = 55, ysize = 257; + size_t xsize = 55; + size_t ysize = 257; io.metadata.m.color_encoding = jxl::ColorEncoding::LinearSRGB(); - jxl::Image3F main(xsize, ysize); - jxl::ImageF spot(xsize, ysize); + JXL_ASSIGN_OR_DIE(Image3F main, Image3F::Create(xsize, ysize)); + JXL_ASSIGN_OR_DIE(ImageF spot, ImageF::Create(xsize, ysize)); jxl::ZeroFillImage(&main); jxl::ZeroFillImage(&spot); @@ -5508,7 +5577,7 @@ TEST(DecodeTest, SpotColorTest) { info.spot_color[3] = 0.5f; io.metadata.m.extra_channel_info.push_back(info); - std::vector<jxl::ImageF> ec; + std::vector<ImageF> ec; ec.push_back(std::move(spot)); io.frames[0].SetExtraChannels(std::move(ec)); @@ -5524,7 +5593,7 @@ TEST(DecodeTest, SpotColorTest) { for (size_t render_spot = 0; render_spot < 2; render_spot++) { JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0}; - JxlDecoder* dec = JxlDecoderCreate(NULL); + JxlDecoder* dec = JxlDecoderCreate(nullptr); EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSubscribeEvents( @@ -5545,7 +5614,8 @@ TEST(DecodeTest, SpotColorTest) { JxlExtraChannelInfo extra_info; EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetExtraChannelInfo(dec, 0, &extra_info)); - EXPECT_EQ((unsigned int)jxl::ExtraChannel::kSpotColor, extra_info.type); + EXPECT_EQ(static_cast<unsigned int>(jxl::ExtraChannel::kSpotColor), + extra_info.type); EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec)); size_t buffer_size; diff --git a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc index d3b5ad3269..4aafd7a5e5 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.cc @@ -18,20 +18,15 @@ #include <hwy/highway.h> #include "lib/jxl/ac_strategy.h" -#include "lib/jxl/ans_params.h" #include "lib/jxl/base/bits.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/fast_math-inl.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/coeff_order_fwd.h" -#include "lib/jxl/convolve.h" -#include "lib/jxl/dct_scales.h" #include "lib/jxl/dec_transforms-inl.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_debug_image.h" #include "lib/jxl/enc_params.h" #include "lib/jxl/enc_transforms-inl.h" -#include "lib/jxl/entropy_coder.h" #include "lib/jxl/simd_util.h" // Some of the floating point constants in this file and in other @@ -215,10 +210,10 @@ const uint8_t* TypeMask(const uint8_t& raw_strategy) { return kMask[raw_strategy]; } -void DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize, - size_t ysize, const char* tag, AuxOut* aux_out, - const CompressParams& cparams) { - Image3F color_acs(xsize, ysize); +Status DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize, + size_t ysize, const char* tag, AuxOut* aux_out, + const CompressParams& cparams) { + JXL_ASSIGN_OR_RETURN(Image3F color_acs, Image3F::Create(xsize, ysize)); for (size_t y = 0; y < ysize; y++) { float* JXL_RESTRICT rows[3] = { color_acs.PlaneRow(0, y), @@ -269,7 +264,7 @@ void DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize, } } } - DumpImage(cparams, tag, color_acs); + return DumpImage(cparams, tag, color_acs); } } // namespace @@ -353,7 +348,9 @@ bool MultiBlockTransformCrossesVerticalBoundary( float EstimateEntropy(const AcStrategy& acs, float entropy_mul, size_t x, size_t y, const ACSConfig& config, const float* JXL_RESTRICT cmap_factors, float* block, - float* scratch_space, uint32_t* quantized) { + float* full_scratch_space, uint32_t* quantized) { + float* mem = full_scratch_space; + float* scratch_space = full_scratch_space + AcStrategy::kMaxCoeffArea; const size_t size = (1 << acs.log2_covered_blocks()) * kDCTBlockSize; // Apply transform. @@ -403,8 +400,6 @@ float EstimateEntropy(const AcStrategy& acs, float entropy_mul, size_t x, float entropy = 0.0f; const HWY_CAPPED(float, 8) df8; - auto mem_alloc = hwy::AllocateAligned<float>(AcStrategy::kMaxCoeffArea); - float* mem = mem_alloc.get(); auto loss = Zero(df8); for (size_t c = 0; c < 3; c++) { const float* inv_matrix = config.dequant->InvMatrix(acs.RawStrategy(), c); @@ -792,6 +787,7 @@ void FindBestFirstLevelDivisionForSquare( void ProcessRectACS(const CompressParams& cparams, const ACSConfig& config, const Rect& rect, const ColorCorrelationMap& cmap, + float* JXL_RESTRICT block, uint32_t* JXL_RESTRICT quantized, AcStrategyImage* ac_strategy) { // Main philosophy here: // 1. First find best 8x8 transform for each area. @@ -804,15 +800,7 @@ void ProcessRectACS(const CompressParams& cparams, const ACSConfig& config, // integral transforms cross these boundaries leads to // additional complications. const float butteraugli_target = cparams.butteraugli_distance; - const size_t dct_scratch_size = - 3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim; - // TODO(veluca): reuse allocations - auto mem = hwy::AllocateAligned<float>(5 * AcStrategy::kMaxCoeffArea + - dct_scratch_size); - auto qmem = hwy::AllocateAligned<uint32_t>(AcStrategy::kMaxCoeffArea); - uint32_t* JXL_RESTRICT quantized = qmem.get(); - float* JXL_RESTRICT block = mem.get(); - float* JXL_RESTRICT scratch_space = mem.get() + 3 * AcStrategy::kMaxCoeffArea; + float* JXL_RESTRICT scratch_space = block + 3 * AcStrategy::kMaxCoeffArea; size_t bx = rect.x0(); size_t by = rect.y0(); JXL_ASSERT(rect.xsize() <= 8); @@ -1085,26 +1073,37 @@ void AcStrategyHeuristics::Init(const Image3F& src, const Rect& rect_in, static const float kPow1 = 0.33677806662454718; static const float kPow2 = 0.50990926717963703; static const float kPow3 = 0.36702940662370243; - config.info_loss_multiplier *= pow(ratio, kPow1); - config.zeros_mul *= pow(ratio, kPow2); - config.cost_delta *= pow(ratio, kPow3); + config.info_loss_multiplier *= std::pow(ratio, kPow1); + config.zeros_mul *= std::pow(ratio, kPow2); + config.cost_delta *= std::pow(ratio, kPow3); +} + +void AcStrategyHeuristics::PrepareForThreads(std::size_t num_threads) { + const size_t dct_scratch_size = + 3 * (MaxVectorSize() / sizeof(float)) * AcStrategy::kMaxBlockDim; + mem_per_thread = 6 * AcStrategy::kMaxCoeffArea + dct_scratch_size; + mem = hwy::AllocateAligned<float>(num_threads * mem_per_thread); + qmem_per_thread = AcStrategy::kMaxCoeffArea; + qmem = hwy::AllocateAligned<uint32_t>(num_threads * qmem_per_thread); } void AcStrategyHeuristics::ProcessRect(const Rect& rect, const ColorCorrelationMap& cmap, - AcStrategyImage* ac_strategy) { + AcStrategyImage* ac_strategy, + size_t thread) { // In Falcon mode, use DCT8 everywhere and uniform quantization. if (cparams.speed_tier >= SpeedTier::kCheetah) { ac_strategy->FillDCT8(rect); return; } HWY_DYNAMIC_DISPATCH(ProcessRectACS) - (cparams, config, rect, cmap, ac_strategy); + (cparams, config, rect, cmap, mem.get() + thread * mem_per_thread, + qmem.get() + thread * qmem_per_thread, ac_strategy); } -void AcStrategyHeuristics::Finalize(const FrameDimensions& frame_dim, - const AcStrategyImage& ac_strategy, - AuxOut* aux_out) { +Status AcStrategyHeuristics::Finalize(const FrameDimensions& frame_dim, + const AcStrategyImage& ac_strategy, + AuxOut* aux_out) { // Accounting and debug output. if (aux_out != nullptr) { aux_out->num_small_blocks = @@ -1141,9 +1140,11 @@ void AcStrategyHeuristics::Finalize(const FrameDimensions& frame_dim, // if (JXL_DEBUG_AC_STRATEGY && WantDebugOutput(aux_out)) { if (JXL_DEBUG_AC_STRATEGY && WantDebugOutput(cparams)) { - DumpAcStrategy(ac_strategy, frame_dim.xsize, frame_dim.ysize, "ac_strategy", - aux_out, cparams); + JXL_RETURN_IF_ERROR(DumpAcStrategy(ac_strategy, frame_dim.xsize, + frame_dim.ysize, "ac_strategy", aux_out, + cparams)); } + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h index 9f6d92a6f7..31eb73c6a0 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h +++ b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h @@ -58,16 +58,22 @@ struct ACSConfig { }; struct AcStrategyHeuristics { - AcStrategyHeuristics(const CompressParams& cparams) : cparams(cparams) {} + explicit AcStrategyHeuristics(const CompressParams& cparams) + : cparams(cparams), mem_per_thread(0), qmem_per_thread(0) {} void Init(const Image3F& src, const Rect& rect_in, const ImageF& quant_field, const ImageF& mask, const ImageF& mask1x1, DequantMatrices* matrices); + void PrepareForThreads(std::size_t num_threads); void ProcessRect(const Rect& rect, const ColorCorrelationMap& cmap, - AcStrategyImage* ac_strategy); - void Finalize(const FrameDimensions& frame_dim, - const AcStrategyImage& ac_strategy, AuxOut* aux_out); + AcStrategyImage* ac_strategy, size_t thread); + Status Finalize(const FrameDimensions& frame_dim, + const AcStrategyImage& ac_strategy, AuxOut* aux_out); const CompressParams& cparams; - ACSConfig config; + ACSConfig config = {}; + size_t mem_per_thread; + hwy::AlignedFreeUniquePtr<float[]> mem; + size_t qmem_per_thread; + hwy::AlignedFreeUniquePtr<uint32_t[]> qmem; }; } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc index ae4cd3bd3b..4c2ddba95b 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.cc @@ -9,6 +9,7 @@ #include <stdlib.h> #include <algorithm> +#include <atomic> #include <cmath> #include <string> #include <vector> @@ -26,8 +27,6 @@ #include "lib/jxl/base/status.h" #include "lib/jxl/butteraugli/butteraugli.h" #include "lib/jxl/cms/opsin_params.h" -#include "lib/jxl/coeff_order_fwd.h" -#include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/convolve.h" #include "lib/jxl/dec_cache.h" #include "lib/jxl/dec_group.h" @@ -102,12 +101,12 @@ V ComputeMask(const D d, const V out_val) { } // mul and mul2 represent a scaling difference between jxl and butteraugli. -static const float kSGmul = 226.77216153508914f; -static const float kSGmul2 = 1.0f / 73.377132366608819f; -static const float kLog2 = 0.693147181f; +const float kSGmul = 226.77216153508914f; +const float kSGmul2 = 1.0f / 73.377132366608819f; +const float kLog2 = 0.693147181f; // Includes correction factor for std::log -> log2. -static const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2; -static const float kSGVOffset = 7.7825991679894591f; +const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2; +const float kSGVOffset = 7.7825991679894591f; template <bool invert, typename D, typename V> V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) { @@ -131,7 +130,7 @@ V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) { } template <bool invert = false> -static float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) { +float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) { using DScalar = HWY_CAPPED(float, 1); auto vscalar = Load(DScalar(), &v); return GetLane( @@ -396,12 +395,16 @@ void FuzzyErosion(const float butteraugli_target, const Rect& from_rect, } struct AdaptiveQuantizationImpl { - void PrepareBuffers(size_t num_threads) { - diff_buffer = ImageF(kEncTileDim + 8, num_threads); + Status PrepareBuffers(size_t num_threads) { + JXL_ASSIGN_OR_RETURN(diff_buffer, + ImageF::Create(kEncTileDim + 8, num_threads)); for (size_t i = pre_erosion.size(); i < num_threads; i++) { - pre_erosion.emplace_back(kEncTileDimInBlocks * 2 + 2, - kEncTileDimInBlocks * 2 + 2); + JXL_ASSIGN_OR_RETURN(ImageF tmp, + ImageF::Create(kEncTileDimInBlocks * 2 + 2, + kEncTileDimInBlocks * 2 + 2)); + pre_erosion.emplace_back(std::move(tmp)); } + return true; } void ComputeTile(float butteraugli_target, float scale, const Image3F& xyb, @@ -568,8 +571,7 @@ struct AdaptiveQuantizationImpl { ImageF diff_buffer; }; -static void Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1, - const Rect& rect) { +Status Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1, const Rect& rect) { // Blur the mask1x1 to obtain the masking image. // Before blurring it contains an image of absolute value of the // Laplacian of the intensity channel. @@ -595,30 +597,30 @@ static void Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1, {HWY_REP4(normalize_mul * kFilterMask1x1[1])}, {HWY_REP4(normalize_mul * kFilterMask1x1[4])}, {HWY_REP4(normalize_mul * kFilterMask1x1[3])}}; - ImageF temp(rect.xsize(), rect.ysize()); + JXL_ASSIGN_OR_RETURN(ImageF temp, ImageF::Create(rect.xsize(), rect.ysize())); Symmetric5(*mask1x1, rect, weights, pool, &temp); *mask1x1 = std::move(temp); + return true; } -ImageF AdaptiveQuantizationMap(const float butteraugli_target, - const Image3F& xyb, const Rect& rect, - float scale, ThreadPool* pool, ImageF* mask, - ImageF* mask1x1) { +StatusOr<ImageF> AdaptiveQuantizationMap(const float butteraugli_target, + const Image3F& xyb, const Rect& rect, + float scale, ThreadPool* pool, + ImageF* mask, ImageF* mask1x1) { JXL_DASSERT(rect.xsize() % kBlockDim == 0); JXL_DASSERT(rect.ysize() % kBlockDim == 0); AdaptiveQuantizationImpl impl; const size_t xsize_blocks = rect.xsize() / kBlockDim; const size_t ysize_blocks = rect.ysize() / kBlockDim; - impl.aq_map = ImageF(xsize_blocks, ysize_blocks); - *mask = ImageF(xsize_blocks, ysize_blocks); - *mask1x1 = ImageF(xyb.xsize(), xyb.ysize()); + JXL_ASSIGN_OR_RETURN(impl.aq_map, ImageF::Create(xsize_blocks, ysize_blocks)); + JXL_ASSIGN_OR_RETURN(*mask, ImageF::Create(xsize_blocks, ysize_blocks)); + JXL_ASSIGN_OR_RETURN(*mask1x1, ImageF::Create(xyb.xsize(), xyb.ysize())); JXL_CHECK(RunOnPool( pool, 0, DivCeil(xsize_blocks, kEncTileDimInBlocks) * DivCeil(ysize_blocks, kEncTileDimInBlocks), [&](const size_t num_threads) { - impl.PrepareBuffers(num_threads); - return true; + return !!impl.PrepareBuffers(num_threads); }, [&](const uint32_t tid, const size_t thread) { size_t n_enc_tiles = DivCeil(xsize_blocks, kEncTileDimInBlocks); @@ -634,7 +636,7 @@ ImageF AdaptiveQuantizationMap(const float butteraugli_target, }, "AQ DiffPrecompute")); - Blur1x1Masking(pool, mask1x1, rect); + JXL_RETURN_IF_ERROR(Blur1x1Masking(pool, mask1x1, rect)); return std::move(impl).aq_map; } @@ -654,24 +656,28 @@ namespace { // If true, prints the quantization maps at each iteration. constexpr bool FLAGS_dump_quant_state = false; -void DumpHeatmap(const CompressParams& cparams, const AuxOut* aux_out, - const std::string& label, const ImageF& image, - float good_threshold, float bad_threshold) { +Status DumpHeatmap(const CompressParams& cparams, const AuxOut* aux_out, + const std::string& label, const ImageF& image, + float good_threshold, float bad_threshold) { if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) { - Image3F heatmap = CreateHeatMapImage(image, good_threshold, bad_threshold); + JXL_ASSIGN_OR_RETURN( + Image3F heatmap, + CreateHeatMapImage(image, good_threshold, bad_threshold)); char filename[200]; snprintf(filename, sizeof(filename), "%s%05d", label.c_str(), aux_out->num_butteraugli_iters); - DumpImage(cparams, filename, heatmap); + JXL_RETURN_IF_ERROR(DumpImage(cparams, filename, heatmap)); } + return true; } -void DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out, - float ba_target, const ImageF& quant_field, - const ImageF& tile_heatmap, const ImageF& bt_diffmap) { +Status DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out, + float ba_target, const ImageF& quant_field, + const ImageF& tile_heatmap, const ImageF& bt_diffmap) { if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) { - if (!WantDebugOutput(cparams)) return; - ImageF inv_qmap(quant_field.xsize(), quant_field.ysize()); + if (!WantDebugOutput(cparams)) return true; + JXL_ASSIGN_OR_RETURN(ImageF inv_qmap, ImageF::Create(quant_field.xsize(), + quant_field.ysize())); for (size_t y = 0; y < quant_field.ysize(); ++y) { const float* JXL_RESTRICT row_q = quant_field.ConstRow(y); float* JXL_RESTRICT row_inv_q = inv_qmap.Row(y); @@ -679,21 +685,24 @@ void DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out, row_inv_q[x] = 1.0f / row_q[x]; // never zero } } - DumpHeatmap(cparams, aux_out, "quant_heatmap", inv_qmap, 4.0f * ba_target, - 6.0f * ba_target); - DumpHeatmap(cparams, aux_out, "tile_heatmap", tile_heatmap, ba_target, - 1.5f * ba_target); + JXL_RETURN_IF_ERROR(DumpHeatmap(cparams, aux_out, "quant_heatmap", inv_qmap, + 4.0f * ba_target, 6.0f * ba_target)); + JXL_RETURN_IF_ERROR(DumpHeatmap(cparams, aux_out, "tile_heatmap", + tile_heatmap, ba_target, 1.5f * ba_target)); // matches heat maps produced by the command line tool. - DumpHeatmap(cparams, aux_out, "bt_diffmap", bt_diffmap, - ButteraugliFuzzyInverse(1.5), ButteraugliFuzzyInverse(0.5)); + JXL_RETURN_IF_ERROR(DumpHeatmap(cparams, aux_out, "bt_diffmap", bt_diffmap, + ButteraugliFuzzyInverse(1.5), + ButteraugliFuzzyInverse(0.5))); } + return true; } -ImageF TileDistMap(const ImageF& distmap, int tile_size, int margin, - const AcStrategyImage& ac_strategy) { +StatusOr<ImageF> TileDistMap(const ImageF& distmap, int tile_size, int margin, + const AcStrategyImage& ac_strategy) { const int tile_xsize = (distmap.xsize() + tile_size - 1) / tile_size; const int tile_ysize = (distmap.ysize() + tile_size - 1) / tile_size; - ImageF tile_distmap(tile_xsize, tile_ysize); + JXL_ASSIGN_OR_RETURN(ImageF tile_distmap, + ImageF::Create(tile_xsize, tile_ysize)); size_t distmap_stride = tile_distmap.PixelsPerRow(); for (int tile_y = 0; tile_y < tile_ysize; ++tile_y) { AcStrategyRow ac_strategy_row = ac_strategy.ConstRow(tile_y); @@ -754,14 +763,16 @@ ImageF TileDistMap(const ImageF& distmap, int tile_size, int margin, return tile_distmap; } -static const float kDcQuantPow = 0.83f; -static const float kDcQuant = 1.095924047623553f; -static const float kAcQuant = 0.7381485255235064f; +const float kDcQuantPow = 0.83f; +const float kDcQuant = 1.095924047623553f; +const float kAcQuant = 0.7381485255235064f; // Computes the decoded image for a given set of compression parameters. -ImageBundle RoundtripImage(const FrameHeader& frame_header, - const Image3F& opsin, PassesEncoderState* enc_state, - const JxlCmsInterface& cms, ThreadPool* pool) { +StatusOr<ImageBundle> RoundtripImage(const FrameHeader& frame_header, + const Image3F& opsin, + PassesEncoderState* enc_state, + const JxlCmsInterface& cms, + ThreadPool* pool) { std::unique_ptr<PassesDecoderState> dec_state = jxl::make_unique<PassesDecoderState>(); JXL_CHECK(dec_state->output_encoding_info.SetFromMetadata( @@ -775,7 +786,8 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header, size_t num_special_frames = enc_state->special_frames.size(); size_t num_passes = enc_state->progressive_splitter.GetNumPasses(); - ModularFrameEncoder modular_frame_encoder(frame_header, enc_state->cparams); + ModularFrameEncoder modular_frame_encoder(frame_header, enc_state->cparams, + false); JXL_CHECK(InitializePassesEncoder(frame_header, opsin, Rect(opsin), cms, pool, enc_state, &modular_frame_encoder, nullptr)); @@ -784,7 +796,9 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header, ImageBundle decoded(&enc_state->shared.metadata->m); decoded.origin = frame_header.frame_origin; - decoded.SetFromImage(Image3F(opsin.xsize(), opsin.ysize()), + JXL_ASSIGN_OR_RETURN(Image3F tmp, + Image3F::Create(opsin.xsize(), opsin.ysize())); + decoded.SetFromImage(std::move(tmp), dec_state->output_encoding_info.color_encoding); PassesDecoderState::PipelineOptions options; @@ -806,8 +820,10 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header, group_dec_caches = hwy::MakeUniqueAlignedArray<GroupDecCache>(num_threads); return true; }; + std::atomic<bool> has_error{false}; const auto process_group = [&](const uint32_t group_index, const size_t thread) { + if (has_error) return; if (frame_header.loop_filter.epf_iters > 0) { ComputeSigma(frame_header.loop_filter, dec_state->shared->frame_dim.BlockGroupRect(group_index), @@ -822,10 +838,14 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header, std::pair<ImageF*, Rect> ri = input.GetBuffer(3 + c); FillPlane(0.0f, ri.first, ri.second); } - input.Done(); + if (!input.Done()) { + has_error = true; + return; + } }; JXL_CHECK(RunOnPool(pool, 0, num_groups, allocate_storage, process_group, "AQ loop")); + if (has_error) return JXL_FAILURE("AQ loop failure"); // Ensure we don't create any new special frames. enc_state->special_frames.resize(num_special_frames); @@ -835,18 +855,18 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header, constexpr int kMaxButteraugliIters = 4; -void FindBestQuantization(const FrameHeader& frame_header, - const Image3F& linear, const Image3F& opsin, - ImageF& quant_field, PassesEncoderState* enc_state, - const JxlCmsInterface& cms, ThreadPool* pool, - AuxOut* aux_out) { +Status FindBestQuantization(const FrameHeader& frame_header, + const Image3F& linear, const Image3F& opsin, + ImageF& quant_field, PassesEncoderState* enc_state, + const JxlCmsInterface& cms, ThreadPool* pool, + AuxOut* aux_out) { const CompressParams& cparams = enc_state->cparams; if (cparams.resampling > 1 && cparams.original_butteraugli_distance <= 4.0 * cparams.resampling) { // For downsampled opsin image, the butteraugli based adaptive quantization // loop would only make the size bigger without improving the distance much, // so in this case we enable it only for very high butteraugli targets. - return; + return true; } Quantizer& quantizer = enc_state->shared.quantizer; ImageI& raw_quant_field = enc_state->shared.raw_quant_field; @@ -863,10 +883,13 @@ void FindBestQuantization(const FrameHeader& frame_header, AdjustQuantField(enc_state->shared.ac_strategy, Rect(quant_field), original_butteraugli, &quant_field); ImageF tile_distmap; - ImageF initial_quant_field(quant_field.xsize(), quant_field.ysize()); + JXL_ASSIGN_OR_RETURN( + ImageF initial_quant_field, + ImageF::Create(quant_field.xsize(), quant_field.ysize())); CopyImageTo(quant_field, &initial_quant_field); - float initial_qf_min, initial_qf_max; + float initial_qf_min; + float initial_qf_max; ImageMinMax(initial_quant_field, &initial_qf_min, &initial_qf_max); float initial_qf_ratio = initial_qf_max / initial_qf_min; float qf_max_deviation_low = std::sqrt(250 / initial_qf_ratio); @@ -893,8 +916,9 @@ void FindBestQuantization(const FrameHeader& frame_header, } } quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field); - ImageBundle dec_linear = - RoundtripImage(frame_header, opsin, enc_state, cms, pool); + JXL_ASSIGN_OR_RETURN( + ImageBundle dec_linear, + RoundtripImage(frame_header, opsin, enc_state, cms, pool)); float score; ImageF diffmap; JXL_CHECK(comparator.CompareWith(dec_linear, &diffmap, &score)); @@ -902,16 +926,19 @@ void FindBestQuantization(const FrameHeader& frame_header, score = -score; ScaleImage(-1.0f, &diffmap); } - tile_distmap = TileDistMap(diffmap, 8 * cparams.resampling, 0, - enc_state->shared.ac_strategy); + JXL_ASSIGN_OR_RETURN(tile_distmap, + TileDistMap(diffmap, 8 * cparams.resampling, 0, + enc_state->shared.ac_strategy)); if (JXL_DEBUG_ADAPTIVE_QUANTIZATION && WantDebugOutput(cparams)) { - DumpImage(cparams, ("dec" + ToString(i)).c_str(), *dec_linear.color()); - DumpHeatmaps(cparams, aux_out, butteraugli_target, quant_field, - tile_distmap, diffmap); + JXL_RETURN_IF_ERROR(DumpImage(cparams, ("dec" + ToString(i)).c_str(), + *dec_linear.color())); + JXL_RETURN_IF_ERROR(DumpHeatmaps(cparams, aux_out, butteraugli_target, + quant_field, tile_distmap, diffmap)); } if (aux_out != nullptr) ++aux_out->num_butteraugli_iters; if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) { - float minval, maxval; + float minval; + float maxval; ImageMinMax(quant_field, &minval, &maxval); printf("\nButteraugli iter: %d/%d\n", i, kMaxButteraugliIters); printf("Butteraugli distance: %f (target = %f)\n", score, @@ -967,8 +994,10 @@ void FindBestQuantization(const FrameHeader& frame_header, if (diff > 1.0f) { float old = row_q[x]; row_q[x] *= diff; - int qf_old = old * quantizer.InvGlobalScale() + 0.5; - int qf_new = row_q[x] * quantizer.InvGlobalScale() + 0.5; + int qf_old = + static_cast<int>(std::lround(old * quantizer.InvGlobalScale())); + int qf_new = static_cast<int>( + std::lround(row_q[x] * quantizer.InvGlobalScale())); if (qf_old == qf_new) { row_q[x] = old + quantizer.Scale(); } @@ -988,8 +1017,10 @@ void FindBestQuantization(const FrameHeader& frame_header, } else { float old = row_q[x]; row_q[x] *= diff; - int qf_old = old * quantizer.InvGlobalScale() + 0.5; - int qf_new = row_q[x] * quantizer.InvGlobalScale() + 0.5; + int qf_old = + static_cast<int>(std::lround(old * quantizer.InvGlobalScale())); + int qf_new = static_cast<int>( + std::lround(row_q[x] * quantizer.InvGlobalScale())); if (qf_old == qf_new) { row_q[x] = old + quantizer.Scale(); } @@ -1001,13 +1032,14 @@ void FindBestQuantization(const FrameHeader& frame_header, } } quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field); + return true; } -void FindBestQuantizationMaxError(const FrameHeader& frame_header, - const Image3F& opsin, ImageF& quant_field, - PassesEncoderState* enc_state, - const JxlCmsInterface& cms, ThreadPool* pool, - AuxOut* aux_out) { +Status FindBestQuantizationMaxError(const FrameHeader& frame_header, + const Image3F& opsin, ImageF& quant_field, + PassesEncoderState* enc_state, + const JxlCmsInterface& cms, + ThreadPool* pool, AuxOut* aux_out) { // TODO(szabadka): Make this work for non-opsin color spaces. const CompressParams& cparams = enc_state->cparams; Quantizer& quantizer = enc_state->shared.quantizer; @@ -1026,12 +1058,15 @@ void FindBestQuantizationMaxError(const FrameHeader& frame_header, for (int i = 0; i < kMaxButteraugliIters + 1; ++i) { quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field); if (JXL_DEBUG_ADAPTIVE_QUANTIZATION && aux_out) { - DumpXybImage(cparams, ("ops" + ToString(i)).c_str(), opsin); + JXL_RETURN_IF_ERROR( + DumpXybImage(cparams, ("ops" + ToString(i)).c_str(), opsin)); } - ImageBundle decoded = - RoundtripImage(frame_header, opsin, enc_state, cms, pool); + JXL_ASSIGN_OR_RETURN( + ImageBundle decoded, + RoundtripImage(frame_header, opsin, enc_state, cms, pool)); if (JXL_DEBUG_ADAPTIVE_QUANTIZATION && aux_out) { - DumpXybImage(cparams, ("dec" + ToString(i)).c_str(), *decoded.color()); + JXL_RETURN_IF_ERROR(DumpXybImage(cparams, ("dec" + ToString(i)).c_str(), + *decoded.color())); } for (size_t by = 0; by < enc_state->shared.frame_dim.ysize_blocks; by++) { AcStrategyRow ac_strategy_row = @@ -1073,6 +1108,7 @@ void FindBestQuantizationMaxError(const FrameHeader& frame_header, } } quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field); + return true; } } // namespace @@ -1142,28 +1178,31 @@ float InitialQuantDC(float butteraugli_target) { return std::min(kDcQuant / butteraugli_target_dc, 50.f); } -ImageF InitialQuantField(const float butteraugli_target, const Image3F& opsin, - const Rect& rect, ThreadPool* pool, float rescale, - ImageF* mask, ImageF* mask1x1) { +StatusOr<ImageF> InitialQuantField(const float butteraugli_target, + const Image3F& opsin, const Rect& rect, + ThreadPool* pool, float rescale, + ImageF* mask, ImageF* mask1x1) { const float quant_ac = kAcQuant / butteraugli_target; return HWY_DYNAMIC_DISPATCH(AdaptiveQuantizationMap)( butteraugli_target, opsin, rect, quant_ac * rescale, pool, mask, mask1x1); } -void FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear, - const Image3F& opsin, ImageF& quant_field, - PassesEncoderState* enc_state, - const JxlCmsInterface& cms, ThreadPool* pool, - AuxOut* aux_out, double rescale) { +Status FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear, + const Image3F& opsin, ImageF& quant_field, + PassesEncoderState* enc_state, + const JxlCmsInterface& cms, ThreadPool* pool, + AuxOut* aux_out, double rescale) { const CompressParams& cparams = enc_state->cparams; if (cparams.max_error_mode) { - FindBestQuantizationMaxError(frame_header, opsin, quant_field, enc_state, - cms, pool, aux_out); + JXL_RETURN_IF_ERROR(FindBestQuantizationMaxError( + frame_header, opsin, quant_field, enc_state, cms, pool, aux_out)); } else if (linear && cparams.speed_tier <= SpeedTier::kKitten) { // Normal encoding to a butteraugli score. - FindBestQuantization(frame_header, *linear, opsin, quant_field, enc_state, - cms, pool, aux_out); + JXL_RETURN_IF_ERROR(FindBestQuantization(frame_header, *linear, opsin, + quant_field, enc_state, cms, pool, + aux_out)); } + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.h b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.h index 6aa8b10df6..26ed3f26ca 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.h +++ b/third_party/jpeg-xl/lib/jxl/enc_adaptive_quantization.h @@ -31,10 +31,11 @@ struct AuxOut; // of the input image, while a value less than 1.0 indicates that less // fine-grained quantization should be enough. Returns a mask, too, which // can later be used to make better decisions about ac strategy. -ImageF InitialQuantField(float butteraugli_target, const Image3F& opsin, - const Rect& rect, ThreadPool* pool, float rescale, - ImageF* initial_quant_mask, - ImageF* initial_quant_mask1x1); +StatusOr<ImageF> InitialQuantField(float butteraugli_target, + const Image3F& opsin, const Rect& rect, + ThreadPool* pool, float rescale, + ImageF* initial_quant_mask, + ImageF* initial_quant_mask1x1); float InitialQuantDC(float butteraugli_target); @@ -45,11 +46,11 @@ void AdjustQuantField(const AcStrategyImage& ac_strategy, const Rect& rect, // quant_field. Also computes the dequant_map corresponding to the given // dequant_float_map and chosen quantization levels. // `linear` is only used in Kitten mode or slower. -void FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear, - const Image3F& opsin, ImageF& quant_field, - PassesEncoderState* enc_state, - const JxlCmsInterface& cms, ThreadPool* pool, - AuxOut* aux_out, double rescale = 1.0); +Status FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear, + const Image3F& opsin, ImageF& quant_field, + PassesEncoderState* enc_state, + const JxlCmsInterface& cms, ThreadPool* pool, + AuxOut* aux_out, double rescale = 1.0); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_ans.cc b/third_party/jpeg-xl/lib/jxl/enc_ans.cc index 3efa62d8e1..5e59790b1e 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ans.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_ans.cc @@ -5,11 +5,13 @@ #include "lib/jxl/enc_ans.h" +#include <jxl/types.h> #include <stdint.h> #include <algorithm> #include <array> #include <cmath> +#include <cstdint> #include <limits> #include <numeric> #include <type_traits> @@ -20,12 +22,15 @@ #include "lib/jxl/ans_common.h" #include "lib/jxl/base/bits.h" #include "lib/jxl/base/fast_math-inl.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/dec_ans.h" +#include "lib/jxl/enc_ans_params.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_cluster.h" #include "lib/jxl/enc_context_map.h" #include "lib/jxl/enc_fields.h" #include "lib/jxl/enc_huffman.h" +#include "lib/jxl/enc_params.h" #include "lib/jxl/fields.h" namespace jxl { @@ -37,7 +42,7 @@ constexpr #endif bool ans_fuzzer_friendly_ = false; -static const int kMaxNumSymbolsForSmallCode = 4; +const int kMaxNumSymbolsForSmallCode = 4; void ANSBuildInfoTable(const ANSHistBin* counts, const AliasTable::Entry* table, size_t alphabet_size, size_t log_alpha_size, @@ -99,16 +104,16 @@ float EstimateDataBitsFlat(const ANSHistBin* histogram, size_t len) { // Static Huffman code for encoding logcounts. The last symbol is used as RLE // sequence. -static const uint8_t kLogCountBitLengths[ANS_LOG_TAB_SIZE + 2] = { +const uint8_t kLogCountBitLengths[ANS_LOG_TAB_SIZE + 2] = { 5, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 6, 7, 7, }; -static const uint8_t kLogCountSymbols[ANS_LOG_TAB_SIZE + 2] = { +const uint8_t kLogCountSymbols[ANS_LOG_TAB_SIZE + 2] = { 17, 11, 15, 3, 9, 7, 4, 2, 5, 6, 0, 33, 1, 65, }; // Returns the difference between largest count that can be represented and is // smaller than "count" and smallest representable count larger than "count". -static int SmallestIncrement(uint32_t count, uint32_t shift) { +int SmallestIncrement(uint32_t count, uint32_t shift) { int bits = count == 0 ? -1 : FloorLog2Nonzero(count); int drop_bits = bits - GetPopulationCountPrecision(bits, shift); return drop_bits < 0 ? 1 : (1 << drop_bits); @@ -148,10 +153,11 @@ bool RebalanceHistogram(const float* targets, int max_symbol, int table_size, int inc = SmallestIncrement(counts[n], shift); counts[n] -= counts[n] & (inc - 1); // TODO(robryk): Should we rescale targets[n]? - const float target = - minimize_error_of_sum ? (sum_nonrounded - sum) : targets[n]; + const int target = minimize_error_of_sum + ? (static_cast<int>(sum_nonrounded) - sum) + : static_cast<int>(targets[n]); if (counts[n] == 0 || - (target > counts[n] + inc / 2 && counts[n] + inc < table_size)) { + (target >= counts[n] + inc / 2 && counts[n] + inc < table_size)) { counts[n] += inc; } sum += counts[n]; @@ -203,11 +209,11 @@ Status NormalizeCounts(ANSHistBin* counts, int* omit_pos, const int length, for (size_t n = 0; n < targets.size(); ++n) { targets[n] = norm * counts[n]; } - if (!RebalanceHistogram<false>(&targets[0], max_symbol, table_size, shift, + if (!RebalanceHistogram<false>(targets.data(), max_symbol, table_size, shift, omit_pos, counts)) { // Use an alternative rebalancing mechanism if the one above failed // to create a histogram that is positive wherever the original one was. - if (!RebalanceHistogram<true>(&targets[0], max_symbol, table_size, shift, + if (!RebalanceHistogram<true>(targets.data(), max_symbol, table_size, shift, omit_pos, counts)) { return JXL_FAILURE("Logic error: couldn't rebalance a histogram"); } @@ -482,8 +488,8 @@ size_t BuildAndStoreANSEncodingData( std::vector<ANSHistBin> counts(histogram, histogram + alphabet_size); if (!counts.empty()) { size_t sum = 0; - for (size_t i = 0; i < counts.size(); i++) { - sum += counts[i]; + for (int count : counts) { + sum += count; } if (sum == 0) { counts[0] = ANS_TAB_SIZE; @@ -538,8 +544,8 @@ template <typename Writer> void EncodeUintConfigs(const std::vector<HybridUintConfig>& uint_config, Writer* writer, size_t log_alpha_size) { // TODO(veluca): RLE? - for (size_t i = 0; i < uint_config.size(); i++) { - EncodeUintConfig(uint_config[i], writer, log_alpha_size); + for (const auto& cfg : uint_config) { + EncodeUintConfig(cfg, writer, log_alpha_size); } } template void EncodeUintConfigs(const std::vector<HybridUintConfig>&, @@ -553,8 +559,7 @@ void ChooseUintConfigs(const HistogramParams& params, std::vector<Histogram>* clustered_histograms, EntropyEncodingData* codes, size_t* log_alpha_size) { codes->uint_config.resize(clustered_histograms->size()); - if (params.streaming_mode || - params.uint_method == HistogramParams::HybridUintMethod::kNone) { + if (params.uint_method == HistogramParams::HybridUintMethod::kNone) { return; } if (params.uint_method == HistogramParams::HybridUintMethod::k000) { @@ -570,6 +575,12 @@ void ChooseUintConfigs(const HistogramParams& params, return; } + // If the uint config is adaptive, just stick with the default in streaming + // mode. + if (params.streaming_mode) { + return; + } + // Brute-force method that tries a few options. std::vector<HybridUintConfig> configs; if (params.uint_method == HistogramParams::HybridUintMethod::kBest) { @@ -619,12 +630,11 @@ void ChooseUintConfigs(const HistogramParams& params, std::fill(is_valid.begin(), is_valid.end(), true); std::fill(extra_bits.begin(), extra_bits.end(), 0); - for (size_t i = 0; i < clustered_histograms->size(); i++) { - (*clustered_histograms)[i].Clear(); + for (auto& histo : *clustered_histograms) { + histo.Clear(); } - for (size_t i = 0; i < tokens.size(); ++i) { - for (size_t j = 0; j < tokens[i].size(); ++j) { - const Token token = tokens[i][j]; + for (const auto& stream : tokens) { + for (const auto& token : stream) { // TODO(veluca): do not ignore lz77 commands. if (token.is_lz77_length) continue; size_t histo = context_map[token.context]; @@ -632,7 +642,7 @@ void ChooseUintConfigs(const HistogramParams& params, cfg.Encode(token.value, &tok, &nbits, &bits); if (tok >= max_alpha || (codes->lz77.enabled && tok >= codes->lz77.min_symbol)) { - is_valid[histo] = false; + is_valid[histo] = JXL_FALSE; continue; } extra_bits[histo] += nbits; @@ -654,13 +664,12 @@ void ChooseUintConfigs(const HistogramParams& params, } // Rebuild histograms. - for (size_t i = 0; i < clustered_histograms->size(); i++) { - (*clustered_histograms)[i].Clear(); + for (auto& histo : *clustered_histograms) { + histo.Clear(); } *log_alpha_size = 4; - for (size_t i = 0; i < tokens.size(); ++i) { - for (size_t j = 0; j < tokens[i].size(); ++j) { - const Token token = tokens[i][j]; + for (const auto& stream : tokens) { + for (const auto& token : stream) { uint32_t tok, nbits, bits; size_t histo = context_map[token.context]; (token.is_lz77_length ? codes->lz77.length_uint_config @@ -771,7 +780,7 @@ class HistogramBuilder { } SizeWriter size_writer; // Used if writer == nullptr to estimate costs. cost += 1; - if (writer) writer->Write(1, codes->use_prefix_code); + if (writer) writer->Write(1, TO_JXL_BOOL(codes->use_prefix_code)); if (codes->use_prefix_code) { log_alpha_size = PREFIX_MAX_BITS; @@ -785,8 +794,8 @@ class HistogramBuilder { EncodeUintConfigs(codes->uint_config, writer, log_alpha_size); } if (codes->use_prefix_code) { - for (size_t c = 0; c < clustered_histograms.size(); ++c) { - size_t alphabet_size = clustered_histograms[c].alphabet_size(); + for (const auto& histo : clustered_histograms) { + size_t alphabet_size = histo.alphabet_size(); if (writer) { StoreVarLenUint16(alphabet_size - 1, writer); } else { @@ -832,9 +841,8 @@ class SymbolCostEstimator { HistogramBuilder builder(num_contexts); // Build histograms for estimating lz77 savings. HybridUintConfig uint_config; - for (size_t i = 0; i < tokens.size(); ++i) { - for (size_t j = 0; j < tokens[i].size(); ++j) { - const Token token = tokens[i][j]; + for (const auto& stream : tokens) { + for (const auto& token : stream) { uint32_t tok, nbits, bits; (token.is_lz77_length ? lz77.length_uint_config : uint_config) .Encode(token.value, &tok, &nbits, &bits); @@ -1025,12 +1033,7 @@ struct HashChain { // Count down, so if due to small distance multiplier multiple distances // map to the same code, the smallest code will be used in the end. for (int i = kNumSpecialDistances - 1; i >= 0; --i) { - int xi = kSpecialDistances[i][0]; - int yi = kSpecialDistances[i][1]; - int distance = yi * distance_multiplier + xi; - // Ensure that we map distance 1 to the lowest symbols. - if (distance < 1) distance = 1; - special_dist_table_[distance] = i; + special_dist_table_[SpecialDistance(i, distance_multiplier)] = i; } num_special_distances_ = kNumSpecialDistances; } @@ -1041,9 +1044,9 @@ struct HashChain { if (pos + 2 < size_) { // TODO(lode): take the MSB's of the uint32_t values into account as well, // given that the hash code itself is less than 32 bits. - result ^= (uint32_t)(data_[pos + 0] << 0u); - result ^= (uint32_t)(data_[pos + 1] << hash_shift_); - result ^= (uint32_t)(data_[pos + 2] << (hash_shift_ * 2)); + result ^= static_cast<uint32_t>(data_[pos + 0] << 0u); + result ^= static_cast<uint32_t>(data_[pos + 1] << hash_shift_); + result ^= static_cast<uint32_t>(data_[pos + 2] << (hash_shift_ * 2)); } else { // No need to compute hash of last 2 bytes, the length 2 is too short. return 0; @@ -1071,7 +1074,7 @@ struct HashChain { uint32_t hashval = GetHash(pos); uint32_t wpos = pos & window_mask_; - val[wpos] = (int)hashval; + val[wpos] = static_cast<int>(hashval); if (head[hashval] != -1) chain[wpos] = head[hashval]; head[hashval] = wpos; @@ -1142,7 +1145,10 @@ struct HashChain { } else { if (hashpos == chain[hashpos]) break; hashpos = chain[hashpos]; - if (val[hashpos] != (int)hashval) break; // outdated hash value + if (val[hashpos] != static_cast<int>(hashval)) { + // outdated hash value + break; + } } } } @@ -1274,7 +1280,8 @@ void ApplyLZ77_LZ77(const HistogramParams& params, size_t num_contexts, HashChain chain(in.data(), in.size(), window_size, min_length, max_length, distance_multiplier); - size_t len, dist_symbol; + size_t len; + size_t dist_symbol; const size_t max_lazy_match_len = 256; // 0 to disable lazy matching @@ -1507,7 +1514,7 @@ void EncodeHistograms(const std::vector<uint8_t>& context_map, } EncodeContextMap(context_map, codes.encoding_info.size(), writer, layer, aux_out); - writer->Write(1, codes.use_prefix_code); + writer->Write(1, TO_JXL_BOOL(codes.use_prefix_code)); size_t log_alpha_size = 8; if (codes.use_prefix_code) { log_alpha_size = PREFIX_MAX_BITS; @@ -1583,10 +1590,9 @@ size_t BuildAndEncodeHistograms(const HistogramParams& params, if (ans_fuzzer_friendly_) { uint_config = HybridUintConfig(10, 0, 0); } - for (size_t i = 0; i < tokens.size(); ++i) { + for (const auto& stream : tokens) { if (codes->lz77.enabled) { - for (size_t j = 0; j < tokens[i].size(); ++j) { - const Token& token = tokens[i][j]; + for (const auto& token : stream) { total_tokens++; uint32_t tok, nbits, bits; (token.is_lz77_length ? codes->lz77.length_uint_config : uint_config) @@ -1595,16 +1601,14 @@ size_t BuildAndEncodeHistograms(const HistogramParams& params, builder.VisitSymbol(tok, token.context); } } else if (num_contexts == 1) { - for (size_t j = 0; j < tokens[i].size(); ++j) { - const Token& token = tokens[i][j]; + for (const auto& token : stream) { total_tokens++; uint32_t tok, nbits, bits; uint_config.Encode(token.value, &tok, &nbits, &bits); builder.VisitSymbol(tok, /*token.context=*/0); } } else { - for (size_t j = 0; j < tokens[i].size(); ++j) { - const Token& token = tokens[i][j]; + for (const auto& token : stream) { total_tokens++; uint32_t tok, nbits, bits; uint_config.Encode(token.value, &tok, &nbits, &bits); @@ -1654,10 +1658,10 @@ size_t BuildAndEncodeHistograms(const HistogramParams& params, codes->encoded_histograms.emplace_back(); BitWriter* histo_writer = &codes->encoded_histograms.back(); BitWriter::Allotment allotment(histo_writer, 256 + alphabet_size * 24); - BuildAndStoreANSEncodingData(params.ans_histogram_strategy, counts.data(), - alphabet_size, log_alpha_size, - codes->use_prefix_code, - &codes->encoding_info.back()[0], histo_writer); + BuildAndStoreANSEncodingData( + params.ans_histogram_strategy, counts.data(), alphabet_size, + log_alpha_size, codes->use_prefix_code, + codes->encoding_info.back().data(), histo_writer); allotment.ReclaimAndCharge(histo_writer, 0, nullptr); } @@ -1680,9 +1684,8 @@ size_t WriteTokens(const std::vector<Token>& tokens, size_t context_offset, BitWriter* writer) { size_t num_extra_bits = 0; if (codes.use_prefix_code) { - for (size_t i = 0; i < tokens.size(); i++) { + for (const auto& token : tokens) { uint32_t tok, nbits, bits; - const Token& token = tokens[i]; size_t histo = context_map[context_offset + token.context]; (token.is_lz77_length ? codes.lz77.length_uint_config : codes.uint_config[histo]) @@ -1693,7 +1696,8 @@ size_t WriteTokens(const std::vector<Token>& tokens, // codes.encoding_info[histo][tok].bits); // writer->Write(nbits, bits); uint64_t data = codes.encoding_info[histo][tok].bits; - data |= bits << codes.encoding_info[histo][tok].depth; + data |= static_cast<uint64_t>(bits) + << codes.encoding_info[histo][tok].depth; writer->Write(codes.encoding_info[histo][tok].depth + nbits, data); num_extra_bits += nbits; } @@ -1765,7 +1769,8 @@ void WriteTokens(const std::vector<Token>& tokens, const EntropyEncodingData& codes, const std::vector<uint8_t>& context_map, size_t context_offset, BitWriter* writer, size_t layer, AuxOut* aux_out) { - BitWriter::Allotment allotment(writer, 32 * tokens.size() + 32 * 1024 * 4); + // Theoretically, we could have 15 prefix code bits + 31 extra bits. + BitWriter::Allotment allotment(writer, 46 * tokens.size() + 32 * 1024 * 4); size_t num_extra_bits = WriteTokens(tokens, codes, context_map, context_offset, writer); allotment.ReclaimAndCharge(writer, layer, aux_out); @@ -1779,4 +1784,51 @@ void SetANSFuzzerFriendly(bool ans_fuzzer_friendly) { ans_fuzzer_friendly_ = ans_fuzzer_friendly; #endif } + +HistogramParams HistogramParams::ForModular( + const CompressParams& cparams, + const std::vector<uint8_t>& extra_dc_precision, bool streaming_mode) { + HistogramParams params; + params.streaming_mode = streaming_mode; + if (cparams.speed_tier > SpeedTier::kKitten) { + params.clustering = HistogramParams::ClusteringType::kFast; + params.ans_histogram_strategy = + cparams.speed_tier > SpeedTier::kThunder + ? HistogramParams::ANSHistogramStrategy::kFast + : HistogramParams::ANSHistogramStrategy::kApproximate; + params.lz77_method = + cparams.decoding_speed_tier >= 3 && cparams.modular_mode + ? (cparams.speed_tier >= SpeedTier::kFalcon + ? HistogramParams::LZ77Method::kRLE + : HistogramParams::LZ77Method::kLZ77) + : HistogramParams::LZ77Method::kNone; + // Near-lossless DC, as well as modular mode, require choosing hybrid uint + // more carefully. + if ((!extra_dc_precision.empty() && extra_dc_precision[0] != 0) || + (cparams.modular_mode && cparams.speed_tier < SpeedTier::kCheetah)) { + params.uint_method = HistogramParams::HybridUintMethod::kFast; + } else { + params.uint_method = HistogramParams::HybridUintMethod::kNone; + } + } else if (cparams.speed_tier <= SpeedTier::kTortoise) { + params.lz77_method = HistogramParams::LZ77Method::kOptimal; + } else { + params.lz77_method = HistogramParams::LZ77Method::kLZ77; + } + if (cparams.decoding_speed_tier >= 1) { + params.max_histograms = 12; + } + if (cparams.decoding_speed_tier >= 1 && cparams.responsive) { + params.lz77_method = cparams.speed_tier >= SpeedTier::kCheetah + ? HistogramParams::LZ77Method::kRLE + : cparams.speed_tier >= SpeedTier::kKitten + ? HistogramParams::LZ77Method::kLZ77 + : HistogramParams::LZ77Method::kOptimal; + } + if (cparams.decoding_speed_tier >= 2 && cparams.responsive) { + params.uint_method = HistogramParams::HybridUintMethod::k000; + params.force_huffman = true; + } + return params; +} } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_ans.h b/third_party/jpeg-xl/lib/jxl/enc_ans.h index 445a5f0c9a..ae4d955a56 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ans.h +++ b/third_party/jpeg-xl/lib/jxl/enc_ans.h @@ -84,7 +84,7 @@ struct EntropyEncodingData { // Integer to be encoded by an entropy coder, either ANS or Huffman. struct Token { - Token() {} + Token() = default; Token(uint32_t c, uint32_t value) : is_lz77_length(false), context(c), value(value) {} uint32_t is_lz77_length : 1; diff --git a/third_party/jpeg-xl/lib/jxl/enc_ans_params.h b/third_party/jpeg-xl/lib/jxl/enc_ans_params.h index 86664f593e..23921097b4 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ans_params.h +++ b/third_party/jpeg-xl/lib/jxl/enc_ans_params.h @@ -11,10 +11,15 @@ #include <stdint.h> #include <stdlib.h> -#include "lib/jxl/enc_params.h" +#include <vector> + +#include "lib/jxl/common.h" namespace jxl { +// Forward declaration to break include cycle. +struct CompressParams; + // RebalanceHistogram requires a signed type. using ANSHistBin = int32_t; @@ -65,6 +70,10 @@ struct HistogramParams { } } + static HistogramParams ForModular( + const CompressParams& cparams, + const std::vector<uint8_t>& extra_dc_precision, bool streaming_mode); + ClusteringType clustering = ClusteringType::kBest; HybridUintMethod uint_method = HybridUintMethod::kBest; LZ77Method lz77_method = LZ77Method::kRLE; diff --git a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc index ed8a42d299..e80771248e 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.cc @@ -17,16 +17,10 @@ #include "lib/jxl/ac_strategy.h" #include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/chroma_from_luma.h" -#include "lib/jxl/enc_adaptive_quantization.h" #include "lib/jxl/enc_params.h" #include "lib/jxl/image.h" -#include "lib/jxl/image_bundle.h" #include "lib/jxl/image_ops.h" -#include "lib/jxl/quant_weights.h" -#include "lib/jxl/quantizer.h" HWY_BEFORE_NAMESPACE(); namespace jxl { @@ -40,11 +34,12 @@ using hwy::HWY_NAMESPACE::Mul; using hwy::HWY_NAMESPACE::MulAdd; using hwy::HWY_NAMESPACE::Sqrt; -void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header, - const Image3F& opsin, const Rect& opsin_rect, - const ImageF& quant_field, const AcStrategyImage& ac_strategy, - ImageB* epf_sharpness, const Rect& rect, - ArControlFieldHeuristics::TempImages* temp_image) { +Status ProcessTile(const CompressParams& cparams, + const FrameHeader& frame_header, const Image3F& opsin, + const Rect& opsin_rect, const ImageF& quant_field, + const AcStrategyImage& ac_strategy, ImageB* epf_sharpness, + const Rect& rect, + ArControlFieldHeuristics::TempImages* temp_image) { JXL_ASSERT(opsin_rect.x0() % 8 == 0); JXL_ASSERT(opsin_rect.y0() % 8 == 0); JXL_ASSERT(opsin_rect.xsize() % 8 == 0); @@ -54,7 +49,7 @@ void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header, cparams.speed_tier > SpeedTier::kWombat || frame_header.loop_filter.epf_iters == 0) { FillPlane(static_cast<uint8_t>(4), epf_sharpness, rect); - return; + return true; } // Likely better to have a higher X weight, like: @@ -70,7 +65,7 @@ void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header, size_t by1 = by0 + rect.ysize(); size_t bx0 = opsin_rect.x0() / 8 + rect.x0(); size_t bx1 = bx0 + rect.xsize(); - temp_image->InitOnce(); + JXL_RETURN_IF_ERROR(temp_image->InitOnce()); ImageF& laplacian_sqrsum = temp_image->laplacian_sqrsum; // Calculate the L2 of the 3x3 Laplacian in an integral transform // (for example 32x32 dct). This relates to transforms ability @@ -295,6 +290,7 @@ void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header, } } } + return true; } } // namespace @@ -307,14 +303,14 @@ HWY_AFTER_NAMESPACE(); namespace jxl { HWY_EXPORT(ProcessTile); -void ArControlFieldHeuristics::RunRect( +Status ArControlFieldHeuristics::RunRect( const CompressParams& cparams, const FrameHeader& frame_header, const Rect& block_rect, const Image3F& opsin, const Rect& opsin_rect, const ImageF& quant_field, const AcStrategyImage& ac_strategy, ImageB* epf_sharpness, size_t thread) { - HWY_DYNAMIC_DISPATCH(ProcessTile) - (cparams, frame_header, opsin, opsin_rect, quant_field, ac_strategy, - epf_sharpness, block_rect, &temp_images[thread]); + return HWY_DYNAMIC_DISPATCH(ProcessTile)( + cparams, frame_header, opsin, opsin_rect, quant_field, ac_strategy, + epf_sharpness, block_rect, &temp_images[thread]); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h index fe602c16e3..f3c5a97a1b 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h +++ b/third_party/jpeg-xl/lib/jxl/enc_ar_control_field.h @@ -21,11 +21,15 @@ struct PassesEncoderState; struct ArControlFieldHeuristics { struct TempImages { - void InitOnce() { - if (laplacian_sqrsum.xsize() != 0) return; - laplacian_sqrsum = ImageF(kEncTileDim + 4, kEncTileDim + 4); - sqrsum_00 = ImageF(kEncTileDim / 4, kEncTileDim / 4); - sqrsum_22 = ImageF(kEncTileDim / 4 + 1, kEncTileDim / 4 + 1); + Status InitOnce() { + if (laplacian_sqrsum.xsize() != 0) return true; + JXL_ASSIGN_OR_RETURN(laplacian_sqrsum, + ImageF::Create(kEncTileDim + 4, kEncTileDim + 4)); + JXL_ASSIGN_OR_RETURN(sqrsum_00, + ImageF::Create(kEncTileDim / 4, kEncTileDim / 4)); + JXL_ASSIGN_OR_RETURN( + sqrsum_22, ImageF::Create(kEncTileDim / 4 + 1, kEncTileDim / 4 + 1)); + return true; } ImageF laplacian_sqrsum; @@ -37,11 +41,11 @@ struct ArControlFieldHeuristics { temp_images.resize(num_threads); } - void RunRect(const CompressParams& cparams, const FrameHeader& frame_header, - const Rect& block_rect, const Image3F& opsin, - const Rect& opsin_rect, const ImageF& quant_field, - const AcStrategyImage& ac_strategy, ImageB* epf_sharpness, - size_t thread); + Status RunRect(const CompressParams& cparams, const FrameHeader& frame_header, + const Rect& block_rect, const Image3F& opsin, + const Rect& opsin_rect, const ImageF& quant_field, + const AcStrategyImage& ac_strategy, ImageB* epf_sharpness, + size_t thread); std::vector<TempImages> temp_images; }; diff --git a/third_party/jpeg-xl/lib/jxl/enc_aux_out.cc b/third_party/jpeg-xl/lib/jxl/enc_aux_out.cc index 12c8619e91..d529b381f8 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_aux_out.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_aux_out.cc @@ -5,16 +5,10 @@ #include "lib/jxl/enc_aux_out.h" -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - #include <inttypes.h> #include <stddef.h> #include <stdint.h> -#include <algorithm> -#include <numeric> // accumulate #include <sstream> #include "lib/jxl/base/printf_macros.h" @@ -61,13 +55,11 @@ const char* LayerName(size_t layer) { void AuxOut::LayerTotals::Print(size_t num_inputs) const { if (JXL_DEBUG_V_LEVEL > 0) { - printf("%10" PRId64, static_cast<int64_t>(total_bits)); + printf("%10" PRIuS, total_bits); if (histogram_bits != 0) { - printf(" [c/i:%6.2f | hst:%8" PRId64 " | ex:%8" PRId64 - " | h+c+e:%12.3f", - num_clustered_histograms * 1.0 / num_inputs, - static_cast<int64_t>(histogram_bits >> 3), - static_cast<int64_t>(extra_bits >> 3), + printf(" [c/i:%6.2f | hst:%8" PRIuS " | ex:%8" PRIuS " | h+c+e:%12.3f", + num_clustered_histograms * 1.0 / num_inputs, histogram_bits >> 3, + extra_bits >> 3, (histogram_bits + clustered_entropy + extra_bits) / 8.0); printf("]"); } @@ -99,8 +91,8 @@ void AuxOut::Print(size_t num_inputs) const { if (num_inputs == 0) return; LayerTotals all_layers; - for (size_t i = 0; i < layers.size(); ++i) { - all_layers.Assimilate(layers[i]); + for (const auto& layer : layers) { + all_layers.Assimilate(layer); } printf("Average butteraugli iters: %10.2f\n", diff --git a/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc b/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc index a9a86dca3b..6e6e6a353c 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc @@ -5,6 +5,7 @@ #include "lib/jxl/enc_bit_writer.h" +#include <jxl/types.h> #include <string.h> // memcpy #include "lib/jxl/base/byte_order.h" @@ -43,10 +44,11 @@ void BitWriter::Allotment::FinishedHistogram(BitWriter* JXL_RESTRICT writer) { void BitWriter::Allotment::ReclaimAndCharge(BitWriter* JXL_RESTRICT writer, size_t layer, AuxOut* JXL_RESTRICT aux_out) { - size_t used_bits = 0, unused_bits = 0; + size_t used_bits = 0; + size_t unused_bits = 0; PrivateReclaim(writer, &used_bits, &unused_bits); -#if 0 +#if JXL_FALSE printf("Layer %s bits: max %" PRIuS " used %" PRIuS " unused %" PRIuS "\n", LayerName(layer), MaxBits(), used_bits, unused_bits); #endif @@ -77,7 +79,7 @@ void BitWriter::Allotment::PrivateReclaim(BitWriter* JXL_RESTRICT writer, writer->storage_.resize(writer->storage_.size() - unused_bytes); writer->current_allotment_ = parent_; // Ensure we don't also charge the parent for these bits. - auto parent = parent_; + auto* parent = parent_; while (parent != nullptr) { parent->prev_bits_written_ += *used_bits; parent = parent->parent_; diff --git a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc index 019d6125a2..b20fa751c1 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.cc @@ -5,9 +5,7 @@ #include "lib/jxl/enc_butteraugli_comparator.h" -#include <algorithm> -#include <vector> - +#include "lib/jxl/base/status.h" #include "lib/jxl/enc_image_bundle.h" namespace jxl { @@ -24,9 +22,8 @@ Status JxlButteraugliComparator::SetReferenceImage(const ImageBundle& ref) { /*pool=*/nullptr, &store, &ref_linear_srgb)) { return false; } - - comparator_.reset( - new ButteraugliComparator(ref_linear_srgb->color(), params_)); + JXL_ASSIGN_OR_RETURN(comparator_, ButteraugliComparator::Make( + ref_linear_srgb->color(), params_)); xsize_ = ref.xsize(); ysize_ = ref.ysize(); return true; @@ -34,7 +31,8 @@ Status JxlButteraugliComparator::SetReferenceImage(const ImageBundle& ref) { Status JxlButteraugliComparator::SetLinearReferenceImage( const Image3F& linear) { - comparator_.reset(new ButteraugliComparator(linear, params_)); + JXL_ASSIGN_OR_RETURN(comparator_, + ButteraugliComparator::Make(linear, params_)); xsize_ = linear.xsize(); ysize_ = linear.ysize(); return true; @@ -58,8 +56,9 @@ Status JxlButteraugliComparator::CompareWith(const ImageBundle& actual, return false; } - ImageF temp_diffmap(xsize_, ysize_); - comparator_->Diffmap(actual_linear_srgb->color(), temp_diffmap); + JXL_ASSIGN_OR_RETURN(ImageF temp_diffmap, ImageF::Create(xsize_, ysize_)); + JXL_RETURN_IF_ERROR( + comparator_->Diffmap(actual_linear_srgb->color(), temp_diffmap)); if (score != nullptr) { *score = ButteraugliScoreFromDiffmap(temp_diffmap, ¶ms_); @@ -79,29 +78,4 @@ float JxlButteraugliComparator::BadQualityScore() const { return ButteraugliFuzzyInverse(0.5); } -float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1, - const ButteraugliParams& params, - const JxlCmsInterface& cms, ImageF* distmap, - ThreadPool* pool, bool ignore_alpha) { - JxlButteraugliComparator comparator(params, cms); - return ComputeScore(rgb0, rgb1, &comparator, cms, distmap, pool, - ignore_alpha); -} - -float ButteraugliDistance(const std::vector<ImageBundle>& frames0, - const std::vector<ImageBundle>& frames1, - const ButteraugliParams& params, - const JxlCmsInterface& cms, ImageF* distmap, - ThreadPool* pool) { - JxlButteraugliComparator comparator(params, cms); - JXL_ASSERT(frames0.size() == frames1.size()); - float max_dist = 0.0f; - for (size_t i = 0; i < frames0.size(); ++i) { - max_dist = std::max( - max_dist, - ComputeScore(frames0[i], frames1[i], &comparator, cms, distmap, pool)); - } - return max_dist; -} - } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.h b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.h index 641d7732d5..4f70e21a1b 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.h +++ b/third_party/jpeg-xl/lib/jxl/enc_butteraugli_comparator.h @@ -10,9 +10,7 @@ #include <stddef.h> #include <memory> -#include <vector> -#include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/status.h" #include "lib/jxl/butteraugli/butteraugli.h" #include "lib/jxl/enc_comparator.h" @@ -43,20 +41,6 @@ class JxlButteraugliComparator : public Comparator { size_t ysize_ = 0; }; -// Returns the butteraugli distance between rgb0 and rgb1. -// If distmap is not null, it must be the same size as rgb0 and rgb1. -float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1, - const ButteraugliParams& params, - const JxlCmsInterface& cms, ImageF* distmap = nullptr, - ThreadPool* pool = nullptr, - bool ignore_alpha = false); - -float ButteraugliDistance(const std::vector<ImageBundle>& frames0, - const std::vector<ImageBundle>& frames1, - const ButteraugliParams& params, - const JxlCmsInterface& cms, ImageF* distmap = nullptr, - ThreadPool* pool = nullptr); - } // namespace jxl #endif // LIB_JXL_ENC_BUTTERAUGLI_COMPARATOR_H_ diff --git a/third_party/jpeg-xl/lib/jxl/enc_cache.cc b/third_party/jpeg-xl/lib/jxl/enc_cache.cc index ff62c57e4d..200ec83b65 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_cache.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_cache.cc @@ -8,15 +8,14 @@ #include <stddef.h> #include <stdint.h> -#include <type_traits> +#include <memory> -#include "lib/jxl/ac_strategy.h" #include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/span.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/compressed_dc.h" -#include "lib/jxl/dct_scales.h" #include "lib/jxl/dct_util.h" #include "lib/jxl/dec_frame.h" #include "lib/jxl/enc_aux_out.h" @@ -50,8 +49,11 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, for (size_t i = enc_state->coeffs.size(); i < frame_header.passes.num_passes; i++) { // Allocate enough coefficients for each group on every row. - enc_state->coeffs.emplace_back(make_unique<ACImageT<int32_t>>( - kGroupDim * kGroupDim, shared.frame_dim.num_groups)); + JXL_ASSIGN_OR_RETURN( + std::unique_ptr<ACImageT<int32_t>> coeffs, + ACImageT<int32_t>::Make(kGroupDim * kGroupDim, + shared.frame_dim.num_groups)); + enc_state->coeffs.emplace_back(std::move(coeffs)); } } while (enc_state->coeffs.size() > frame_header.passes.num_passes) { @@ -65,7 +67,9 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, shared.quantizer.RecomputeFromGlobalScale(); } - Image3F dc(shared.frame_dim.xsize_blocks, shared.frame_dim.ysize_blocks); + JXL_ASSIGN_OR_RETURN(Image3F dc, + Image3F::Create(shared.frame_dim.xsize_blocks, + shared.frame_dim.ysize_blocks)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, shared.frame_dim.num_groups, ThreadPool::NoInit, [&](size_t group_idx, size_t _) { @@ -90,9 +94,9 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, // and kModular for the smallest DC (first in the bitstream) if (cparams.progressive_dc == 0) { cparams.modular_mode = true; - cparams.speed_tier = - SpeedTier(std::max(static_cast<int>(SpeedTier::kTortoise), - static_cast<int>(cparams.speed_tier) - 1)); + cparams.speed_tier = static_cast<SpeedTier>( + std::max(static_cast<int>(SpeedTier::kTortoise), + static_cast<int>(cparams.speed_tier) - 1)); cparams.butteraugli_distance = std::max(kMinButteraugliDistance, enc_state->cparams.butteraugli_distance * 0.02f); @@ -120,7 +124,8 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, std::vector<ImageF> extra_channels; extra_channels.reserve(ib.metadata()->extra_channel_info.size()); for (size_t i = 0; i < ib.metadata()->extra_channel_info.size(); i++) { - extra_channels.emplace_back(ib.xsize(), ib.ysize()); + JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(ib.xsize(), ib.ysize())); + extra_channels.emplace_back(std::move(ch)); // Must initialize the image with data to not affect blending with // uninitialized memory. // TODO(lode): dc_level must copy and use the real extra channels @@ -168,32 +173,41 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, // outputs multiple frames, this assumption could be wrong. const Image3F& dc_frame = dec_state->shared->dc_frames[frame_header.dc_level]; - shared.dc_storage = Image3F(dc_frame.xsize(), dc_frame.ysize()); + JXL_ASSIGN_OR_RETURN(shared.dc_storage, + Image3F::Create(dc_frame.xsize(), dc_frame.ysize())); CopyImageTo(dc_frame, &shared.dc_storage); ZeroFillImage(&shared.quant_dc); shared.dc = &shared.dc_storage; JXL_CHECK(encoded_size == 0); } else { + std::atomic<bool> has_error{false}; auto compute_dc_coeffs = [&](int group_index, int /* thread */) { + if (has_error) return; const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index); int modular_group_index = group_index; if (enc_state->streaming_mode) { JXL_ASSERT(group_index == 0); modular_group_index = enc_state->dc_group_index; } - modular_frame_encoder->AddVarDCTDC( - frame_header, dc, r, modular_group_index, - enc_state->cparams.speed_tier < SpeedTier::kFalcon, enc_state, - /*jpeg_transcode=*/false); + if (!modular_frame_encoder->AddVarDCTDC( + frame_header, dc, r, modular_group_index, + enc_state->cparams.speed_tier < SpeedTier::kFalcon, enc_state, + /*jpeg_transcode=*/false)) { + has_error = true; + return; + } }; JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups, ThreadPool::NoInit, compute_dc_coeffs, "Compute DC coeffs")); + if (has_error) return JXL_FAILURE("Compute DC coeffs failed"); // TODO(veluca): this is only useful in tests and if inspection is enabled. if (!(frame_header.flags & FrameHeader::kSkipAdaptiveDCSmoothing)) { - AdaptiveDCSmoothing(shared.quantizer.MulDC(), &shared.dc_storage, pool); + JXL_RETURN_IF_ERROR(AdaptiveDCSmoothing(shared.quantizer.MulDC(), + &shared.dc_storage, pool)); } } + std::atomic<bool> has_error{false}; auto compute_ac_meta = [&](int group_index, int /* thread */) { const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index); int modular_group_index = group_index; @@ -201,13 +215,17 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, JXL_ASSERT(group_index == 0); modular_group_index = enc_state->dc_group_index; } - modular_frame_encoder->AddACMetadata(r, modular_group_index, - /*jpeg_transcode=*/false, enc_state); + if (!modular_frame_encoder->AddACMetadata(r, modular_group_index, + /*jpeg_transcode=*/false, + enc_state)) { + has_error = true; + return; + } }; JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups, ThreadPool::NoInit, compute_ac_meta, "Compute AC Metadata")); - + if (has_error) return JXL_FAILURE("Compute AC Metadata failed"); return true; } diff --git a/third_party/jpeg-xl/lib/jxl/enc_cache.h b/third_party/jpeg-xl/lib/jxl/enc_cache.h index 6efcc081c1..43ee7bc9c8 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_cache.h +++ b/third_party/jpeg-xl/lib/jxl/enc_cache.h @@ -55,7 +55,7 @@ struct PassesEncoderState { }; std::vector<PassData> passes; - std::vector<uint8_t> histogram_idx; + std::vector<size_t> histogram_idx; // Block sizes seen so far. uint32_t used_acs = 0; diff --git a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc index 9a894d89cc..4039da2858 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc @@ -9,7 +9,6 @@ #include <stdlib.h> #include <algorithm> -#include <array> #include <cmath> #undef HWY_TARGET_INCLUDE @@ -18,18 +17,13 @@ #include <hwy/foreach_target.h> #include <hwy/highway.h> -#include "lib/jxl/base/bits.h" #include "lib/jxl/base/common.h" -#include "lib/jxl/base/span.h" #include "lib/jxl/base/status.h" #include "lib/jxl/cms/opsin_params.h" #include "lib/jxl/dec_transforms-inl.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_params.h" #include "lib/jxl/enc_transforms-inl.h" -#include "lib/jxl/entropy_coder.h" -#include "lib/jxl/image_ops.h" -#include "lib/jxl/modular/encoding/encoding.h" #include "lib/jxl/quantizer.h" #include "lib/jxl/simd_util.h" HWY_BEFORE_NAMESPACE(); @@ -149,7 +143,8 @@ int32_t FindBestMultiplier(const float* values_m, const float* values_s, // Derivatives are approximate due to the high amount of noise in the exact // derivatives. for (size_t i = 0; i < 20; i++) { - float dfpeps, dfmeps; + float dfpeps; + float dfmeps; float df = fn.Compute(x, eps, &dfpeps, &dfmeps); float ddf = (dfpeps - dfmeps) / (2 * eps); float kExperimentalInsignificantStabilizer = 0.85; @@ -175,12 +170,13 @@ int32_t FindBestMultiplier(const float* values_m, const float* values_s, return std::max(-128.0f, std::min(127.0f, roundf(x))); } -void InitDCStorage(size_t num_blocks, ImageF* dc_values) { +Status InitDCStorage(size_t num_blocks, ImageF* dc_values) { // First row: Y channel // Second row: X channel // Third row: Y channel // Fourth row: B channel - *dc_values = ImageF(RoundUpTo(num_blocks, Lanes(df)), 4); + JXL_ASSIGN_OR_RETURN(*dc_values, + ImageF::Create(RoundUpTo(num_blocks, Lanes(df)), 4)); JXL_ASSERT(dc_values->xsize() != 0); // Zero-fill the last lanes @@ -190,6 +186,7 @@ void InitDCStorage(size_t num_blocks, ImageF* dc_values) { dc_values->Row(y)[x] = 0; } } + return true; } void ComputeTile(const Image3F& opsin, const Rect& opsin_rect, @@ -352,11 +349,11 @@ namespace jxl { HWY_EXPORT(InitDCStorage); HWY_EXPORT(ComputeTile); -void CfLHeuristics::Init(const Rect& rect) { +Status CfLHeuristics::Init(const Rect& rect) { size_t xsize_blocks = rect.xsize() / kBlockDim; size_t ysize_blocks = rect.ysize() / kBlockDim; - HWY_DYNAMIC_DISPATCH(InitDCStorage) - (xsize_blocks * ysize_blocks, &dc_values); + return HWY_DYNAMIC_DISPATCH(InitDCStorage)(xsize_blocks * ysize_blocks, + &dc_values); } void CfLHeuristics::ComputeTile(const Rect& r, const Image3F& opsin, diff --git a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h index 04743842bf..c6481a0ec9 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h +++ b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.h @@ -29,7 +29,7 @@ void ColorCorrelationMapEncodeDC(const ColorCorrelationMap& map, AuxOut* aux_out); struct CfLHeuristics { - void Init(const Rect& rect); + Status Init(const Rect& rect); void PrepareForThreads(size_t num_threads) { mem = hwy::AllocateAligned<float>(num_threads * ItemsPerThread()); diff --git a/third_party/jpeg-xl/lib/jxl/enc_cluster.cc b/third_party/jpeg-xl/lib/jxl/enc_cluster.cc index df1b31ddf7..f7284331d9 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_cluster.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_cluster.cc @@ -236,7 +236,7 @@ void HistogramReindex(std::vector<Histogram>* out, size_t prev_histograms, // Clusters similar histograms in 'in' together, the selected histograms are // placed in 'out', and for each index in 'in', *histogram_symbols will // indicate which of the 'out' histograms is the best approximation. -void ClusterHistograms(const HistogramParams params, +void ClusterHistograms(const HistogramParams& params, const std::vector<Histogram>& in, size_t max_histograms, std::vector<Histogram>* out, std::vector<uint32_t>* histogram_symbols) { @@ -252,9 +252,9 @@ void ClusterHistograms(const HistogramParams params, if (prev_histograms == 0 && params.clustering == HistogramParams::ClusteringType::kBest) { - for (size_t i = 0; i < out->size(); i++) { - (*out)[i].entropy_ = - ANSPopulationCost((*out)[i].data_.data(), (*out)[i].data_.size()); + for (auto& histo : *out) { + histo.entropy_ = + ANSPopulationCost(histo.data_.data(), histo.data_.size()); } uint32_t next_version = 2; std::vector<uint32_t> version(out->size(), 1); @@ -308,9 +308,9 @@ void ClusterHistograms(const HistogramParams params, (*out)[first].AddHistogram((*out)[second]); (*out)[first].entropy_ = ANSPopulationCost((*out)[first].data_.data(), (*out)[first].data_.size()); - for (size_t i = 0; i < renumbering.size(); i++) { - if (renumbering[i] == second) { - renumbering[i] = first; + for (uint32_t& item : renumbering) { + if (item == second) { + item = first; } } version[second] = 0; @@ -338,9 +338,8 @@ void ClusterHistograms(const HistogramParams params, reverse_renumbering[i] = num_alive - 1; } out->resize(num_alive); - for (size_t i = 0; i < histogram_symbols->size(); i++) { - (*histogram_symbols)[i] = - reverse_renumbering[renumbering[(*histogram_symbols)[i]]]; + for (uint32_t& item : *histogram_symbols) { + item = reverse_renumbering[renumbering[item]]; } } diff --git a/third_party/jpeg-xl/lib/jxl/enc_cluster.h b/third_party/jpeg-xl/lib/jxl/enc_cluster.h index 923aaaccfe..7ddc003cf2 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_cluster.h +++ b/third_party/jpeg-xl/lib/jxl/enc_cluster.h @@ -15,6 +15,7 @@ #include <vector> #include "lib/jxl/ans_params.h" +#include "lib/jxl/base/common.h" #include "lib/jxl/enc_ans_params.h" namespace jxl { @@ -61,8 +62,9 @@ struct Histogram { static constexpr size_t kRounding = 8; }; -void ClusterHistograms(HistogramParams params, const std::vector<Histogram>& in, - size_t max_histograms, std::vector<Histogram>* out, +void ClusterHistograms(const HistogramParams& params, + const std::vector<Histogram>& in, size_t max_histograms, + std::vector<Histogram>* out, std::vector<uint32_t>* histogram_symbols); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc b/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc index 5be012aa92..49129f5d9e 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_coeff_order.cc @@ -6,21 +6,16 @@ #include <stdint.h> #include <algorithm> +#include <cmath> #include <hwy/aligned_allocator.h> #include <vector> -#include "lib/jxl/ans_params.h" -#include "lib/jxl/base/span.h" #include "lib/jxl/coeff_order.h" #include "lib/jxl/coeff_order_fwd.h" -#include "lib/jxl/dec_ans.h" -#include "lib/jxl/dec_bit_reader.h" +#include "lib/jxl/dct_util.h" #include "lib/jxl/enc_ans.h" #include "lib/jxl/enc_bit_writer.h" -#include "lib/jxl/entropy_coder.h" #include "lib/jxl/lehmer_code.h" -#include "lib/jxl/modular/encoding/encoding.h" -#include "lib/jxl/modular/modular_image.h" namespace jxl { @@ -240,7 +235,7 @@ void EncodePermutation(const coeff_order_t* JXL_RESTRICT order, size_t skip, size_t size, BitWriter* writer, int layer, AuxOut* aux_out) { std::vector<std::vector<Token>> tokens(1); - TokenizePermutation(order, skip, size, &tokens[0]); + TokenizePermutation(order, skip, size, tokens.data()); std::vector<uint8_t> context_map; EntropyEncodingData codes; BuildAndEncodeHistograms(HistogramParams(), kPermutationContexts, tokens, @@ -280,7 +275,7 @@ void EncodeCoeffOrders(uint16_t used_orders, if (natural_order_lut.size() < size) natural_order_lut.resize(size); acs.ComputeNaturalCoeffOrderLut(natural_order_lut.data()); for (size_t c = 0; c < 3; c++) { - EncodeCoeffOrder(&order[CoeffOrderOffset(ord, c)], acs, &tokens[0], + EncodeCoeffOrder(&order[CoeffOrderOffset(ord, c)], acs, tokens.data(), mem.get(), natural_order_lut); } } diff --git a/third_party/jpeg-xl/lib/jxl/enc_comparator.cc b/third_party/jpeg-xl/lib/jxl/enc_comparator.cc index 268122af06..3ef6a49b8f 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_comparator.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_comparator.cc @@ -66,9 +66,10 @@ float ComputeScoreImpl(const ImageBundle& rgb0, const ImageBundle& rgb1, } // namespace -float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, - Comparator* comparator, const JxlCmsInterface& cms, - ImageF* diffmap, ThreadPool* pool, bool ignore_alpha) { +Status ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, + Comparator* comparator, const JxlCmsInterface& cms, + float* score, ImageF* diffmap, ThreadPool* pool, + bool ignore_alpha) { // Convert to linear sRGB (unless already in that space) ImageMetadata metadata0 = *rgb0.metadata(); ImageBundle store0(&metadata0); @@ -83,25 +84,28 @@ float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, // No alpha: skip blending, only need a single call to Butteraugli. if (ignore_alpha || (!rgb0.HasAlpha() && !rgb1.HasAlpha())) { - return ComputeScoreImpl(*linear_srgb0, *linear_srgb1, comparator, diffmap); + *score = + ComputeScoreImpl(*linear_srgb0, *linear_srgb1, comparator, diffmap); + return true; } // Blend on black and white backgrounds const float black = 0.0f; - ImageBundle blended_black0 = linear_srgb0->Copy(); - ImageBundle blended_black1 = linear_srgb1->Copy(); + JXL_ASSIGN_OR_RETURN(ImageBundle blended_black0, linear_srgb0->Copy()); + JXL_ASSIGN_OR_RETURN(ImageBundle blended_black1, linear_srgb1->Copy()); AlphaBlend(black, &blended_black0); AlphaBlend(black, &blended_black1); const float white = 1.0f; - ImageBundle blended_white0 = linear_srgb0->Copy(); - ImageBundle blended_white1 = linear_srgb1->Copy(); + JXL_ASSIGN_OR_RETURN(ImageBundle blended_white0, linear_srgb0->Copy()); + JXL_ASSIGN_OR_RETURN(ImageBundle blended_white1, linear_srgb1->Copy()); AlphaBlend(white, &blended_white0); AlphaBlend(white, &blended_white1); - ImageF diffmap_black, diffmap_white; + ImageF diffmap_black; + ImageF diffmap_white; const float dist_black = ComputeScoreImpl(blended_black0, blended_black1, comparator, &diffmap_black); const float dist_white = ComputeScoreImpl(blended_white0, blended_white1, @@ -111,7 +115,7 @@ float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, if (diffmap != nullptr) { const size_t xsize = rgb0.xsize(); const size_t ysize = rgb0.ysize(); - *diffmap = ImageF(xsize, ysize); + JXL_ASSIGN_OR_RETURN(*diffmap, ImageF::Create(xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { const float* JXL_RESTRICT row_black = diffmap_black.ConstRow(y); const float* JXL_RESTRICT row_white = diffmap_white.ConstRow(y); @@ -121,7 +125,8 @@ float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, } } } - return std::max(dist_black, dist_white); + *score = std::max(dist_black, dist_white); + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_comparator.h b/third_party/jpeg-xl/lib/jxl/enc_comparator.h index c545ea6111..ee62ab6f28 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_comparator.h +++ b/third_party/jpeg-xl/lib/jxl/enc_comparator.h @@ -43,10 +43,10 @@ class Comparator { // Computes the score given images in any RGB color model, optionally with // alpha channel. -float ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, - Comparator* comparator, const JxlCmsInterface& cms, - ImageF* diffmap = nullptr, ThreadPool* pool = nullptr, - bool ignore_alpha = false); +Status ComputeScore(const ImageBundle& rgb0, const ImageBundle& rgb1, + Comparator* comparator, const JxlCmsInterface& cms, + float* score, ImageF* diffmap = nullptr, + ThreadPool* pool = nullptr, bool ignore_alpha = false); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_context_map.cc b/third_party/jpeg-xl/lib/jxl/enc_context_map.cc index 6968a6fbae..36efc4e649 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_context_map.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_context_map.cc @@ -7,6 +7,7 @@ #include "lib/jxl/enc_context_map.h" +#include <jxl/types.h> #include <stdint.h> #include <algorithm> @@ -18,6 +19,7 @@ #include "lib/jxl/enc_ans.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/entropy_coder.h" +#include "lib/jxl/fields.h" #include "lib/jxl/pack_signed.h" namespace jxl { @@ -69,16 +71,18 @@ void EncodeContextMap(const std::vector<uint8_t>& context_map, } std::vector<uint8_t> transformed_symbols = MoveToFrontTransform(context_map); - std::vector<std::vector<Token>> tokens(1), mtf_tokens(1); - for (size_t i = 0; i < context_map.size(); i++) { - tokens[0].emplace_back(0, context_map[i]); + std::vector<std::vector<Token>> tokens(1); + std::vector<std::vector<Token>> mtf_tokens(1); + for (const uint8_t& ctx : context_map) { + tokens[0].emplace_back(0, ctx); } - for (size_t i = 0; i < transformed_symbols.size(); i++) { - mtf_tokens[0].emplace_back(0, transformed_symbols[i]); + for (const uint8_t& sym : transformed_symbols) { + mtf_tokens[0].emplace_back(0, sym); } HistogramParams params; params.uint_method = HistogramParams::HybridUintMethod::kContextMap; - size_t ans_cost, mtf_cost; + size_t ans_cost; + size_t mtf_cost; { EntropyEncodingData codes; std::vector<uint8_t> sink_context_map; @@ -104,14 +108,14 @@ void EncodeContextMap(const std::vector<uint8_t>& context_map, BitWriter::Allotment allotment(writer, 3 + entry_bits * context_map.size()); writer->Write(1, 1); writer->Write(2, entry_bits); - for (size_t i = 0; i < context_map.size(); i++) { - writer->Write(entry_bits, context_map[i]); + for (uint8_t entry : context_map) { + writer->Write(entry_bits, entry); } allotment.ReclaimAndCharge(writer, layer, aux_out); } else { BitWriter::Allotment allotment(writer, 2 + tokens[0].size() * 24); writer->Write(1, 0); - writer->Write(1, use_mtf); // Use/don't use MTF. + writer->Write(1, TO_JXL_BOOL(use_mtf)); // Use/don't use MTF. EntropyEncodingData codes; std::vector<uint8_t> sink_context_map; BuildAndEncodeHistograms(params, 1, tokens, &codes, &sink_context_map, @@ -123,9 +127,9 @@ void EncodeContextMap(const std::vector<uint8_t>& context_map, void EncodeBlockCtxMap(const BlockCtxMap& block_ctx_map, BitWriter* writer, AuxOut* aux_out) { - auto& dct = block_ctx_map.dc_thresholds; - auto& qft = block_ctx_map.qf_thresholds; - auto& ctx_map = block_ctx_map.ctx_map; + const auto& dct = block_ctx_map.dc_thresholds; + const auto& qft = block_ctx_map.qf_thresholds; + const auto& ctx_map = block_ctx_map.ctx_map; BitWriter::Allotment allotment( writer, (dct[0].size() + dct[1].size() + dct[2].size() + qft.size()) * 34 + 1 + diff --git a/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc b/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc index 261570e690..d67ab7db46 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_debug_image.cc @@ -18,32 +18,29 @@ namespace jxl { namespace { template <typename From> -Plane<float> ConvertToFloat(const Plane<From>& from) { +StatusOr<Image3F> ConvertToFloat(const Image3<From>& from) { float factor = 1.0f / std::numeric_limits<From>::max(); if (std::is_same<From, double>::value || std::is_same<From, float>::value) { factor = 1.0f; } - Plane<float> to(from.xsize(), from.ysize()); - for (size_t y = 0; y < from.ysize(); ++y) { - const From* const JXL_RESTRICT row_from = from.Row(y); - float* const JXL_RESTRICT row_to = to.Row(y); - for (size_t x = 0; x < from.xsize(); ++x) { - row_to[x] = row_from[x] * factor; + JXL_ASSIGN_OR_RETURN(Image3F to, Image3F::Create(from.xsize(), from.ysize())); + for (size_t c = 0; c < 3; ++c) { + for (size_t y = 0; y < from.ysize(); ++y) { + const From* const JXL_RESTRICT row_from = from.ConstPlaneRow(c, y); + float* const JXL_RESTRICT row_to = to.PlaneRow(c, y); + for (size_t x = 0; x < from.xsize(); ++x) { + row_to[x] = row_from[x] * factor; + } } } return to; } -template <typename From> -Image3F ConvertToFloat(const Image3<From>& from) { - return Image3F(ConvertToFloat(from.Plane(0)), ConvertToFloat(from.Plane(1)), - ConvertToFloat(from.Plane(2))); -} template <typename T> -void DumpImageT(const CompressParams& cparams, const char* label, - const ColorEncoding& color_encoding, const Image3<T>& image) { - if (!cparams.debug_image) return; - Image3F float_image = ConvertToFloat(image); +Status DumpImageT(const CompressParams& cparams, const char* label, + const ColorEncoding& color_encoding, const Image3<T>& image) { + if (!cparams.debug_image) return true; + JXL_ASSIGN_OR_RETURN(Image3F float_image, ConvertToFloat(image)); JxlColorEncoding color = color_encoding.ToExternal(); size_t num_pixels = 3 * image.xsize() * image.ysize(); std::vector<uint16_t> pixels(num_pixels); @@ -53,18 +50,20 @@ void DumpImageT(const CompressParams& cparams, const char* label, } JXL_CHECK(ConvertChannelsToExternal( channels, 3, 16, false, JXL_BIG_ENDIAN, 6 * image.xsize(), nullptr, - &pixels[0], 2 * num_pixels, PixelCallback(), Orientation::kIdentity)); + pixels.data(), 2 * num_pixels, PixelCallback(), Orientation::kIdentity)); (*cparams.debug_image)(cparams.debug_image_opaque, label, image.xsize(), - image.ysize(), &color, &pixels[0]); + image.ysize(), &color, pixels.data()); + return true; } template <typename T> -void DumpPlaneNormalizedT(const CompressParams& cparams, const char* label, - const Plane<T>& image) { +Status DumpPlaneNormalizedT(const CompressParams& cparams, const char* label, + const Plane<T>& image) { T min; T max; ImageMinMax(image, &min, &max); - Image3B normalized(image.xsize(), image.ysize()); + JXL_ASSIGN_OR_RETURN(Image3B normalized, + Image3B::Create(image.xsize(), image.ysize())); for (size_t c = 0; c < 3; ++c) { float mul = min == max ? 0 : (255.0f / (max - min)); for (size_t y = 0; y < image.ysize(); ++y) { @@ -75,41 +74,42 @@ void DumpPlaneNormalizedT(const CompressParams& cparams, const char* label, } } } - DumpImageT(cparams, label, ColorEncoding::SRGB(), normalized); + return DumpImageT(cparams, label, ColorEncoding::SRGB(), normalized); } } // namespace -void DumpImage(const CompressParams& cparams, const char* label, - const Image3<float>& image) { - DumpImageT(cparams, label, ColorEncoding::SRGB(), image); +Status DumpImage(const CompressParams& cparams, const char* label, + const Image3<float>& image) { + return DumpImageT(cparams, label, ColorEncoding::SRGB(), image); } -void DumpImage(const CompressParams& cparams, const char* label, - const Image3<uint8_t>& image) { - DumpImageT(cparams, label, ColorEncoding::SRGB(), image); +Status DumpImage(const CompressParams& cparams, const char* label, + const Image3<uint8_t>& image) { + return DumpImageT(cparams, label, ColorEncoding::SRGB(), image); } -void DumpXybImage(const CompressParams& cparams, const char* label, - const Image3F& image) { - if (!cparams.debug_image) return; +Status DumpXybImage(const CompressParams& cparams, const char* label, + const Image3F& image) { + if (!cparams.debug_image) return true; - Image3F linear(image.xsize(), image.ysize()); + JXL_ASSIGN_OR_RETURN(Image3F linear, + Image3F::Create(image.xsize(), image.ysize())); OpsinParams opsin_params; opsin_params.Init(kDefaultIntensityTarget); OpsinToLinear(image, Rect(linear), nullptr, &linear, opsin_params); - DumpImageT(cparams, label, ColorEncoding::LinearSRGB(), linear); + return DumpImageT(cparams, label, ColorEncoding::LinearSRGB(), linear); } -void DumpPlaneNormalized(const CompressParams& cparams, const char* label, - const Plane<float>& image) { - DumpPlaneNormalizedT(cparams, label, image); +Status DumpPlaneNormalized(const CompressParams& cparams, const char* label, + const Plane<float>& image) { + return DumpPlaneNormalizedT(cparams, label, image); } -void DumpPlaneNormalized(const CompressParams& cparams, const char* label, - const Plane<uint8_t>& image) { - DumpPlaneNormalizedT(cparams, label, image); +Status DumpPlaneNormalized(const CompressParams& cparams, const char* label, + const Plane<uint8_t>& image) { + return DumpPlaneNormalizedT(cparams, label, image); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_debug_image.h b/third_party/jpeg-xl/lib/jxl/enc_debug_image.h index 33799a5f7f..428293f8bc 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_debug_image.h +++ b/third_party/jpeg-xl/lib/jxl/enc_debug_image.h @@ -16,16 +16,16 @@ namespace jxl { -void DumpImage(const CompressParams& cparams, const char* label, - const Image3<float>& image); -void DumpImage(const CompressParams& cparams, const char* label, - const Image3<uint8_t>& image); -void DumpXybImage(const CompressParams& cparams, const char* label, - const Image3<float>& image); -void DumpPlaneNormalized(const CompressParams& cparams, const char* label, - const Plane<float>& image); -void DumpPlaneNormalized(const CompressParams& cparams, const char* label, - const Plane<uint8_t>& image); +Status DumpImage(const CompressParams& cparams, const char* label, + const Image3<float>& image); +Status DumpImage(const CompressParams& cparams, const char* label, + const Image3<uint8_t>& image); +Status DumpXybImage(const CompressParams& cparams, const char* label, + const Image3<float>& image); +Status DumpPlaneNormalized(const CompressParams& cparams, const char* label, + const Plane<float>& image); +Status DumpPlaneNormalized(const CompressParams& cparams, const char* label, + const Plane<uint8_t>& image); // Used to skip image creation if they won't be written to debug directory. static inline bool WantDebugOutput(const CompressParams& cparams) { diff --git a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc index 4ee8808766..94e6fefb61 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.cc @@ -26,7 +26,6 @@ #include "lib/jxl/base/status.h" #include "lib/jxl/convolve.h" #include "lib/jxl/enc_linalg.h" -#include "lib/jxl/enc_optimize.h" #include "lib/jxl/image.h" #include "lib/jxl/image_ops.h" @@ -44,14 +43,16 @@ using hwy::HWY_NAMESPACE::Add; using hwy::HWY_NAMESPACE::Mul; using hwy::HWY_NAMESPACE::Sub; -ImageF SumOfSquareDifferences(const Image3F& forig, const Image3F& smooth, - ThreadPool* pool) { +StatusOr<ImageF> SumOfSquareDifferences(const Image3F& forig, + const Image3F& smooth, + ThreadPool* pool) { const HWY_FULL(float) d; const auto color_coef0 = Set(d, 0.0f); const auto color_coef1 = Set(d, 10.0f); const auto color_coef2 = Set(d, 0.0f); - ImageF sum_of_squares(forig.xsize(), forig.ysize()); + JXL_ASSIGN_OR_RETURN(ImageF sum_of_squares, + ImageF::Create(forig.xsize(), forig.ysize())); JXL_CHECK(RunOnPool( pool, 0, forig.ysize(), ThreadPool::NoInit, [&](const uint32_t task, size_t thread) { @@ -142,11 +143,12 @@ const WeightsSeparable5& WeightsSeparable5Gaussian3() { return weights; } -ImageF ComputeEnergyImage(const Image3F& orig, Image3F* smooth, - ThreadPool* pool) { +StatusOr<ImageF> ComputeEnergyImage(const Image3F& orig, Image3F* smooth, + ThreadPool* pool) { // Prepare guidance images for dot selection. - Image3F forig(orig.xsize(), orig.ysize()); - *smooth = Image3F(orig.xsize(), orig.ysize()); + JXL_ASSIGN_OR_RETURN(Image3F forig, + Image3F::Create(orig.xsize(), orig.ysize())); + JXL_ASSIGN_OR_RETURN(*smooth, Image3F::Create(orig.xsize(), orig.ysize())); Rect rect(orig); const auto& weights1 = WeightsSeparable5Gaussian0_65(); @@ -176,7 +178,7 @@ const size_t kMaxCCSize = 1000; // Extracts a connected component from a Binary image where seed is part // of the component -bool ExtractComponent(ImageF* img, std::vector<Pixel>* pixels, +bool ExtractComponent(const Rect& rect, ImageF* img, std::vector<Pixel>* pixels, const Pixel& seed, double threshold) { static const std::vector<Pixel> neighbors{{1, -1}, {1, 0}, {1, 1}, {0, -1}, {0, 1}, {-1, -1}, {-1, 1}, {1, 0}}; @@ -188,9 +190,9 @@ bool ExtractComponent(ImageF* img, std::vector<Pixel>* pixels, if (pixels->size() > kMaxCCSize) return false; for (const Pixel& delta : neighbors) { Pixel child = current + delta; - if (child.x >= 0 && static_cast<size_t>(child.x) < img->xsize() && - child.y >= 0 && static_cast<size_t>(child.y) < img->ysize()) { - float* value = &img->Row(child.y)[child.x]; + if (child.x >= 0 && static_cast<size_t>(child.x) < rect.xsize() && + child.y >= 0 && static_cast<size_t>(child.y) < rect.ysize()) { + float* value = &rect.Row(img, child.y)[child.x]; if (*value > threshold) { *value = 0.0; q.push_back(child); @@ -221,7 +223,7 @@ struct ConnectedComponent { float score; Pixel mode; - void CompStats(const ImageF& energy, int extra) { + void CompStats(const ImageF& energy, const Rect& rect, int extra) { maxEnergy = 0.0; meanEnergy = 0.0; varEnergy = 0.0; @@ -234,12 +236,12 @@ struct ConnectedComponent { for (int sy = -extra; sy < (static_cast<int>(bounds.ysize()) + extra); sy++) { int y = sy + static_cast<int>(bounds.y0()); - if (y < 0 || static_cast<size_t>(y) >= energy.ysize()) continue; - const float* JXL_RESTRICT erow = energy.ConstRow(y); + if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue; + const float* JXL_RESTRICT erow = rect.ConstRow(energy, y); for (int sx = -extra; sx < (static_cast<int>(bounds.xsize()) + extra); sx++) { int x = sx + static_cast<int>(bounds.x0()); - if (x < 0 || static_cast<size_t>(x) >= energy.xsize()) continue; + if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue; if (erow[x] > maxEnergy) { maxEnergy = erow[x]; mode.x = x; @@ -266,7 +268,10 @@ struct ConnectedComponent { Rect BoundingRectangle(const std::vector<Pixel>& pixels) { JXL_ASSERT(!pixels.empty()); - int low_x, high_x, low_y, high_y; + int low_x; + int high_x; + int low_y; + int high_y; low_x = high_x = pixels[0].x; low_y = high_y = pixels[0].y; for (const Pixel& p : pixels) { @@ -278,22 +283,25 @@ Rect BoundingRectangle(const std::vector<Pixel>& pixels) { return Rect(low_x, low_y, high_x - low_x + 1, high_y - low_y + 1); } -std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low, - double t_high, uint32_t maxWindow, - double minScore) { +StatusOr<std::vector<ConnectedComponent>> FindCC(const ImageF& energy, + const Rect& rect, double t_low, + double t_high, + uint32_t maxWindow, + double minScore) { const int kExtraRect = 4; - ImageF img(energy.xsize(), energy.ysize()); + JXL_ASSIGN_OR_RETURN(ImageF img, + ImageF::Create(energy.xsize(), energy.ysize())); CopyImageTo(energy, &img); std::vector<ConnectedComponent> ans; - for (size_t y = 0; y < img.ysize(); y++) { - float* JXL_RESTRICT row = img.Row(y); - for (size_t x = 0; x < img.xsize(); x++) { + for (size_t y = 0; y < rect.ysize(); y++) { + float* JXL_RESTRICT row = rect.Row(&img, y); + for (size_t x = 0; x < rect.xsize(); x++) { if (row[x] > t_high) { std::vector<Pixel> pixels; row[x] = 0.0; bool success = ExtractComponent( - &img, &pixels, Pixel{static_cast<int>(x), static_cast<int>(y)}, - t_low); + rect, &img, &pixels, + Pixel{static_cast<int>(x), static_cast<int>(y)}, t_low); if (!success) continue; #if JXL_DEBUG_DOT_DETECT for (size_t i = 0; i < pixels.size(); i++) { @@ -304,7 +312,7 @@ std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low, Rect bounds = BoundingRectangle(pixels); if (bounds.xsize() < maxWindow && bounds.ysize() < maxWindow) { ConnectedComponent cc{bounds, std::move(pixels)}; - cc.CompStats(energy, kExtraRect); + cc.CompStats(energy, rect, kExtraRect); if (cc.score < minScore) continue; JXL_DEBUG(JXL_DEBUG_DOT_DETECT, "cc mode: (%d,%d), max: %f, bgMean: %f bgVar: " @@ -323,12 +331,14 @@ std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low, // TODO(sggonzalez): Adapt this function for the different color spaces or // remove it if the color space with the best performance does not need it void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc, - const Image3F& img, const Image3F& background) { + const Rect& rect, const Image3F& img, + const Image3F& background) { const int rectBounds = 2; const double kIntensityR = 0.0; // 0.015; const double kSigmaR = 0.0; // 0.01; const double kZeroEpsilon = 0.1; // Tolerance to consider a value negative - double ct = cos(ellipse->angle), st = sin(ellipse->angle); + double ct = cos(ellipse->angle); + double st = sin(ellipse->angle); const std::array<double, 3> channelGains{{1.0, 1.0, 1.0}}; int N = 0; ellipse->l1_loss = 0.0; @@ -342,15 +352,15 @@ void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc, for (int sy = -rectBounds; sy < (static_cast<int>(cc.bounds.ysize()) + rectBounds); sy++) { int y = sy + cc.bounds.y0(); - if (y < 0 || static_cast<size_t>(y) >= img.ysize()) continue; - const float* JXL_RESTRICT row = img.ConstPlaneRow(c, y); + if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue; + const float* JXL_RESTRICT row = rect.ConstPlaneRow(img, c, y); // bgrow is only used if kOptimizeBackground is false. // NOLINTNEXTLINE(clang-analyzer-deadcode.DeadStores) - const float* JXL_RESTRICT bgrow = background.ConstPlaneRow(c, y); + const float* JXL_RESTRICT bgrow = rect.ConstPlaneRow(background, c, y); for (int sx = -rectBounds; sx < (static_cast<int>(cc.bounds.xsize()) + rectBounds); sx++) { int x = sx + cc.bounds.x0(); - if (x < 0 || static_cast<size_t>(x) >= img.xsize()) continue; + if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue; double target = row[x]; double dotDelta = DotGaussianModel( x - ellipse->x, y - ellipse->y, ct, st, ellipse->sigma_x, @@ -385,9 +395,8 @@ void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc, ellipse->ridge_loss = ellipse->l2_loss + ridgeTerm; } -GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, - const ImageF& energy, const Image3F& img, - const Image3F& background) { +GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, const Rect& rect, + const Image3F& img, const Image3F& background) { constexpr bool leastSqIntensity = true; constexpr double kEpsilon = 1e-6; GaussianEllipse ans; @@ -405,18 +414,18 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, "%" PRIuS " %" PRIuS " %" PRIuS " %" PRIuS "\n", cc.bounds.x0(), cc.bounds.y0(), cc.bounds.xsize(), cc.bounds.ysize()); for (int c = 0; c < 3; c++) { - color[c] = img.ConstPlaneRow(c, cc.mode.y)[cc.mode.x] - - background.ConstPlaneRow(c, cc.mode.y)[cc.mode.x]; + color[c] = rect.ConstPlaneRow(img, c, cc.mode.y)[cc.mode.x] - + rect.ConstPlaneRow(background, c, cc.mode.y)[cc.mode.x]; } double sign = (color[1] > 0) ? 1 : -1; for (int sy = -kRectBounds; sy <= kRectBounds; sy++) { int y = sy + cc.mode.y; - if (y < 0 || static_cast<size_t>(y) >= energy.ysize()) continue; - const float* JXL_RESTRICT row = img.ConstPlaneRow(1, y); - const float* JXL_RESTRICT bgrow = background.ConstPlaneRow(1, y); + if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue; + const float* JXL_RESTRICT row = rect.ConstPlaneRow(img, 1, y); + const float* JXL_RESTRICT bgrow = rect.ConstPlaneRow(background, 1, y); for (int sx = -kRectBounds; sx <= kRectBounds; sx++) { int x = sx + cc.mode.x; - if (x < 0 || static_cast<size_t>(x) >= energy.xsize()) continue; + if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue; double w = std::max(kEpsilon, sign * (row[x] - bgrow[x])); sum += w; @@ -426,7 +435,7 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, m2[1] += w * x * y; m2[2] += w * y * y; for (int c = 0; c < 3; c++) { - bgColor[c] += background.ConstPlaneRow(c, y)[x]; + bgColor[c] += rect.ConstPlaneRow(background, c, y)[x]; } N++; } @@ -450,14 +459,16 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, ans.intensity[j] = kScaleMult[j] * color[j]; } - ImageD Sigma(2, 2), D(1, 2), U(2, 2); - Sigma.Row(0)[0] = m2[0] - m1[0] * m1[0]; - Sigma.Row(1)[1] = m2[2] - m1[1] * m1[1]; - Sigma.Row(0)[1] = Sigma.Row(1)[0] = m2[1] - m1[0] * m1[1]; - ConvertToDiagonal(Sigma, &D, &U); - const double* JXL_RESTRICT d = D.ConstRow(0); - const double* JXL_RESTRICT u = U.ConstRow(1); - int p1 = 0, p2 = 1; + Matrix2x2 Sigma; + Vector2 d; + Matrix2x2 U; + Sigma[0][0] = m2[0] - m1[0] * m1[0]; + Sigma[1][1] = m2[2] - m1[1] * m1[1]; + Sigma[0][1] = Sigma[1][0] = m2[1] - m1[0] * m1[1]; + ConvertToDiagonal(Sigma, d, U); + Vector2& u = U[1]; + int p1 = 0; + int p2 = 1; if (d[0] < d[1]) std::swap(p1, p2); ans.sigma_x = kSigmaMult * d[p1]; ans.sigma_y = kSigmaMult * d[p2]; @@ -466,7 +477,8 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, ans.bgColor = bgColor; if (leastSqIntensity) { GaussianEllipse* ellipse = &ans; - double ct = cos(ans.angle), st = sin(ans.angle); + double ct = cos(ans.angle); + double st = sin(ans.angle); // Estimate intensity with least squares (fixed background) for (int c = 0; c < 3; c++) { double gg = 0.0; @@ -474,11 +486,11 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, int yc = static_cast<int>(cc.mode.y); int xc = static_cast<int>(cc.mode.x); for (int y = yc - kRectBounds; y <= yc + kRectBounds; y++) { - if (y < 0 || static_cast<size_t>(y) >= img.ysize()) continue; - const float* JXL_RESTRICT row = img.ConstPlaneRow(c, y); - const float* JXL_RESTRICT bgrow = background.ConstPlaneRow(c, y); + if (y < 0 || static_cast<size_t>(y) >= rect.ysize()) continue; + const float* JXL_RESTRICT row = rect.ConstPlaneRow(img, c, y); + const float* JXL_RESTRICT bgrow = rect.ConstPlaneRow(background, c, y); for (int x = xc - kRectBounds; x <= xc + kRectBounds; x++) { - if (x < 0 || static_cast<size_t>(x) >= img.xsize()) continue; + if (x < 0 || static_cast<size_t>(x) >= rect.xsize()) continue; double target = row[x] - bgrow[x]; double gaussian = DotGaussianModel(x - ellipse->x, y - ellipse->y, ct, st, @@ -490,13 +502,13 @@ GaussianEllipse FitGaussianFast(const ConnectedComponent& cc, ans.intensity[c] = gd / (gg + 1e-6); // Regularized least squares } } - ComputeDotLosses(&ans, cc, img, background); + ComputeDotLosses(&ans, cc, rect, img, background); return ans; } -GaussianEllipse FitGaussian(const ConnectedComponent& cc, const ImageF& energy, +GaussianEllipse FitGaussian(const ConnectedComponent& cc, const Rect& rect, const Image3F& img, const Image3F& background) { - auto ellipse = FitGaussianFast(cc, energy, img, background); + auto ellipse = FitGaussianFast(cc, rect, img, background); if (ellipse.sigma_x < ellipse.sigma_y) { std::swap(ellipse.sigma_x, ellipse.sigma_y); ellipse.angle += kPi / 2.0; @@ -522,14 +534,16 @@ GaussianEllipse FitGaussian(const ConnectedComponent& cc, const ImageF& energy, } // namespace -std::vector<PatchInfo> DetectGaussianEllipses( - const Image3F& opsin, const GaussianDetectParams& params, +StatusOr<std::vector<PatchInfo>> DetectGaussianEllipses( + const Image3F& opsin, const Rect& rect, const GaussianDetectParams& params, const EllipseQuantParams& qParams, ThreadPool* pool) { std::vector<PatchInfo> dots; - Image3F smooth(opsin.xsize(), opsin.ysize()); - ImageF energy = ComputeEnergyImage(opsin, &smooth, pool); - std::vector<ConnectedComponent> components = FindCC( - energy, params.t_low, params.t_high, params.maxWinSize, params.minScore); + JXL_ASSIGN_OR_RETURN(Image3F smooth, + Image3F::Create(opsin.xsize(), opsin.ysize())); + JXL_ASSIGN_OR_RETURN(ImageF energy, ComputeEnergyImage(opsin, &smooth, pool)); + JXL_ASSIGN_OR_RETURN(std::vector<ConnectedComponent> components, + FindCC(energy, rect, params.t_low, params.t_high, + params.maxWinSize, params.minScore)); size_t numCC = std::min(params.maxCC, (components.size() * params.percCC) / 100); if (components.size() > numCC) { @@ -541,11 +555,11 @@ std::vector<PatchInfo> DetectGaussianEllipses( components.erase(components.begin() + numCC, components.end()); } for (const auto& cc : components) { - GaussianEllipse ellipse = FitGaussian(cc, energy, opsin, smooth); + GaussianEllipse ellipse = FitGaussian(cc, rect, opsin, smooth); if (ellipse.x < 0.0 || - std::ceil(ellipse.x) >= static_cast<double>(opsin.xsize()) || + std::ceil(ellipse.x) >= static_cast<double>(rect.xsize()) || ellipse.y < 0.0 || - std::ceil(ellipse.y) >= static_cast<double>(opsin.ysize())) { + std::ceil(ellipse.y) >= static_cast<double>(rect.ysize())) { continue; } if (ellipse.neg_pixels > params.maxNegPixels) continue; @@ -573,8 +587,8 @@ std::vector<PatchInfo> DetectGaussianEllipses( for (size_t x = 0; x < patch.xsize; x++) { for (size_t c = 0; c < 3; c++) { patch.fpixels[c][y * patch.xsize + x] = - opsin.ConstPlaneRow(c, y0 + y)[x0 + x] - - smooth.ConstPlaneRow(c, y0 + y)[x0 + x]; + rect.ConstPlaneRow(opsin, c, y0 + y)[x0 + x] - + rect.ConstPlaneRow(smooth, c, y0 + y)[x0 + x]; } } } diff --git a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.h b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.h index c3071d9a2f..59def59f8f 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_detect_dots.h +++ b/third_party/jpeg-xl/lib/jxl/enc_detect_dots.h @@ -14,7 +14,6 @@ #include <vector> #include "lib/jxl/base/data_parallel.h" -#include "lib/jxl/dec_patch_dictionary.h" #include "lib/jxl/enc_patch_dictionary.h" #include "lib/jxl/image.h" @@ -58,8 +57,8 @@ struct EllipseQuantParams { }; // Detects dots in XYB image. -std::vector<PatchInfo> DetectGaussianEllipses( - const Image3F& opsin, const GaussianDetectParams& params, +StatusOr<std::vector<PatchInfo>> DetectGaussianEllipses( + const Image3F& opsin, const Rect& rect, const GaussianDetectParams& params, const EllipseQuantParams& qParams, ThreadPool* pool); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.cc b/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.cc index a5b1af63b2..7d76ba9002 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.cc @@ -9,17 +9,12 @@ #include <string.h> #include <array> -#include <utility> #include "lib/jxl/base/override.h" #include "lib/jxl/base/status.h" #include "lib/jxl/chroma_from_luma.h" -#include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/dec_xyb.h" -#include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_detect_dots.h" #include "lib/jxl/enc_params.h" -#include "lib/jxl/enc_xyb.h" #include "lib/jxl/image.h" namespace jxl { @@ -39,10 +34,9 @@ const std::array<double, 3> kEllipseMaxIntensity{{0.05, 1.0, 0.4}}; const std::array<size_t, 3> kEllipseIntensityQ{{10, 36, 10}}; } // namespace -std::vector<PatchInfo> FindDotDictionary(const CompressParams& cparams, - const Image3F& opsin, - const ColorCorrelationMap& cmap, - ThreadPool* pool) { +StatusOr<std::vector<PatchInfo>> FindDotDictionary( + const CompressParams& cparams, const Image3F& opsin, const Rect& rect, + const ColorCorrelationMap& cmap, ThreadPool* pool) { if (ApplyOverride(cparams.dots, cparams.butteraugli_distance >= kMinButteraugliForDots)) { GaussianDetectParams ellipse_params; @@ -58,14 +52,15 @@ std::vector<PatchInfo> FindDotDictionary(const CompressParams& cparams, ellipse_params.maxCC = 100; ellipse_params.percCC = 100; EllipseQuantParams qParams{ - opsin.xsize(), opsin.ysize(), kEllipsePosQ, + rect.xsize(), rect.ysize(), kEllipsePosQ, kEllipseMinSigma, kEllipseMaxSigma, kEllipseSigmaQ, kEllipseAngleQ, kEllipseMinIntensity, kEllipseMaxIntensity, kEllipseIntensityQ, kEllipsePosQ <= 5, cmap.YtoXRatio(0), cmap.YtoBRatio(0)}; - return DetectGaussianEllipses(opsin, ellipse_params, qParams, pool); + return DetectGaussianEllipses(opsin, rect, ellipse_params, qParams, pool); } - return {}; + std::vector<PatchInfo> nothing; + return nothing; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.h b/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.h index 2ba4393f30..d348fb73b8 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.h +++ b/third_party/jpeg-xl/lib/jxl/enc_dot_dictionary.h @@ -15,19 +15,15 @@ #include "lib/jxl/base/status.h" #include "lib/jxl/chroma_from_luma.h" -#include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/dec_patch_dictionary.h" -#include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_params.h" #include "lib/jxl/enc_patch_dictionary.h" #include "lib/jxl/image.h" namespace jxl { -std::vector<PatchInfo> FindDotDictionary(const CompressParams& cparams, - const Image3F& opsin, - const ColorCorrelationMap& cmap, - ThreadPool* pool); +StatusOr<std::vector<PatchInfo>> FindDotDictionary( + const CompressParams& cparams, const Image3F& opsin, const Rect& rect, + const ColorCorrelationMap& cmap, ThreadPool* pool); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.cc b/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.cc index 07601a2221..b71bb07bc9 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.cc @@ -90,8 +90,8 @@ int32_t NumNonZeroExceptLLF(const size_t cx, const size_t cy, } // We want area - sum_zero, add because neg_sum_zero is already negated. - const int32_t nzeros = - int32_t(cx * cy * kDCTBlockSize) + GetLane(SumOfLanes(di, neg_sum_zero)); + const int32_t nzeros = static_cast<int32_t>(cx * cy * kDCTBlockSize) + + GetLane(SumOfLanes(di, neg_sum_zero)); const int32_t shifted_nzeros = static_cast<int32_t>( (nzeros + covered_blocks - 1) >> log2_covered_blocks); @@ -139,8 +139,8 @@ int32_t NumNonZero8x8ExceptDC(const int32_t* JXL_RESTRICT block, } // We want 64 - sum_zero, add because neg_sum_zero is already negated. - const int32_t nzeros = - int32_t(kDCTBlockSize) + GetLane(SumOfLanes(di, neg_sum_zero)); + const int32_t nzeros = static_cast<int32_t>(kDCTBlockSize) + + GetLane(SumOfLanes(di, neg_sum_zero)); *nzeros_pos = nzeros; @@ -157,7 +157,7 @@ void TokenizeCoefficients(const coeff_order_t* JXL_RESTRICT orders, const Rect& rect, const int32_t* JXL_RESTRICT* JXL_RESTRICT ac_rows, const AcStrategyImage& ac_strategy, - YCbCrChromaSubsampling cs, + const YCbCrChromaSubsampling& cs, Image3I* JXL_RESTRICT tmp_num_nzeroes, std::vector<Token>* JXL_RESTRICT output, const ImageB& qdc, const ImageI& qf, @@ -236,7 +236,7 @@ void TokenizeCoefficients(const coeff_order_t* JXL_RESTRICT orders, log2_covered_blocks, prev); uint32_t u_coeff = PackSigned(coeff); output->emplace_back(ctx, u_coeff); - prev = coeff != 0; + prev = (coeff != 0) ? 1 : 0; nzeros -= prev; } JXL_DASSERT(nzeros == 0); @@ -258,14 +258,14 @@ void TokenizeCoefficients(const coeff_order_t* JXL_RESTRICT orders, const Rect& rect, const int32_t* JXL_RESTRICT* JXL_RESTRICT ac_rows, const AcStrategyImage& ac_strategy, - YCbCrChromaSubsampling cs, + const YCbCrChromaSubsampling& cs, Image3I* JXL_RESTRICT tmp_num_nzeroes, std::vector<Token>* JXL_RESTRICT output, const ImageB& qdc, const ImageI& qf, const BlockCtxMap& block_ctx_map) { - return HWY_DYNAMIC_DISPATCH(TokenizeCoefficients)( - orders, rect, ac_rows, ac_strategy, cs, tmp_num_nzeroes, output, qdc, qf, - block_ctx_map); + HWY_DYNAMIC_DISPATCH(TokenizeCoefficients) + (orders, rect, ac_rows, ac_strategy, cs, tmp_num_nzeroes, output, qdc, qf, + block_ctx_map); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.h b/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.h index 7dfc71c726..6df3e8e770 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.h +++ b/third_party/jpeg-xl/lib/jxl/enc_entropy_coder.h @@ -35,7 +35,7 @@ void TokenizeCoefficients(const coeff_order_t* JXL_RESTRICT orders, const Rect& rect, const int32_t* JXL_RESTRICT* JXL_RESTRICT ac_rows, const AcStrategyImage& ac_strategy, - YCbCrChromaSubsampling cs, + const YCbCrChromaSubsampling& cs, Image3I* JXL_RESTRICT tmp_num_nzeroes, std::vector<Token>* JXL_RESTRICT output, const ImageB& qdc, const ImageI& qf, diff --git a/third_party/jpeg-xl/lib/jxl/enc_external_image.cc b/third_party/jpeg-xl/lib/jxl/enc_external_image.cc index 680323e79a..90e4937e7b 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_external_image.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_external_image.cc @@ -8,14 +8,9 @@ #include <jxl/types.h> #include <string.h> -#include <algorithm> -#include <array> #include <atomic> -#include <functional> #include <utility> -#include <vector> -#include "lib/jxl/alpha.h" #include "lib/jxl/base/byte_order.h" #include "lib/jxl/base/common.h" #include "lib/jxl/base/float.h" @@ -114,7 +109,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize, color_channels, format.num_channels); } - Image3F color(xsize, ysize); + JXL_ASSIGN_OR_RETURN(Image3F color, Image3F::Create(xsize, ysize)); for (size_t c = 0; c < color_channels; ++c) { JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck( data, xsize, ysize, stride, bits_per_sample, format, c, pool, @@ -129,7 +124,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize, // Passing an interleaved image with an alpha channel to an image that doesn't // have alpha channel just discards the passed alpha channel. if (has_alpha && ib->HasAlpha()) { - ImageF alpha(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize)); JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck( data, xsize, ysize, stride, bits_per_sample, format, format.num_channels - 1, pool, &alpha)); @@ -137,7 +132,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize, } else if (!has_alpha && ib->HasAlpha()) { // if alpha is not passed, but it is expected, then assume // it is all-opaque - ImageF alpha(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize)); FillImage(1.0f, &alpha); ib->SetAlpha(std::move(alpha)); } @@ -184,7 +179,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize, color_channels, format.num_channels); } - Image3F color(xsize, ysize); + JXL_ASSIGN_OR_RETURN(Image3F color, Image3F::Create(xsize, ysize)); for (size_t c = 0; c < color_channels; ++c) { JXL_RETURN_IF_ERROR(ConvertFromExternal(bytes.data(), bytes.size(), xsize, ysize, bits_per_sample, format, c, @@ -199,7 +194,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize, // Passing an interleaved image with an alpha channel to an image that doesn't // have alpha channel just discards the passed alpha channel. if (has_alpha && ib->HasAlpha()) { - ImageF alpha(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize)); JXL_RETURN_IF_ERROR(ConvertFromExternal( bytes.data(), bytes.size(), xsize, ysize, bits_per_sample, format, format.num_channels - 1, pool, &alpha)); @@ -207,7 +202,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize, } else if (!has_alpha && ib->HasAlpha()) { // if alpha is not passed, but it is expected, then assume // it is all-opaque - ImageF alpha(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize)); FillImage(1.0f, &alpha); ib->SetAlpha(std::move(alpha)); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc b/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc index b32d2478e0..58d0d00eaa 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc @@ -331,6 +331,8 @@ struct PrefixCode { uint8_t* min_limit, uint8_t* max_limit, uint8_t* nbits) { + assert(precision < 15); + assert(n <= kMaxNumSymbols); std::vector<T> dynp(((1U << precision) + 1) * (n + 1), infty); auto d = [&](size_t sym, size_t off) -> T& { return dynp[sym * ((1 << precision) + 1) + off]; @@ -428,10 +430,11 @@ struct PrefixCode { } // Invalid code, used to construct arrays. - PrefixCode() {} + PrefixCode() = default; template <typename BitDepth> - PrefixCode(BitDepth, uint64_t* raw_counts, uint64_t* lz77_counts) { + PrefixCode(BitDepth /* bitdepth */, uint64_t* raw_counts, + uint64_t* lz77_counts) { // "merge" together all the lz77 counts in a single symbol for the level 1 // table (containing just the raw symbols, up to length 7). uint64_t level1_counts[kNumRawSymbols + 1]; @@ -1317,7 +1320,7 @@ struct Mask32 { SIMDVec32 IfThenElse(const SIMDVec32& if_true, const SIMDVec32& if_false); size_t CountPrefix() const { return CtzNonZero(~static_cast<uint64_t>( - (uint8_t)_mm256_movemask_ps(_mm256_castsi256_ps(mask)))); + static_cast<uint8_t>(_mm256_movemask_ps(_mm256_castsi256_ps(mask))))); } }; @@ -1414,8 +1417,8 @@ struct Mask16 { return Mask16{_mm256_and_si256(mask, oth.mask)}; } size_t CountPrefix() const { - return CtzNonZero( - ~static_cast<uint64_t>((uint32_t)_mm256_movemask_epi8(mask))) / + return CtzNonZero(~static_cast<uint64_t>( + static_cast<uint32_t>(_mm256_movemask_epi8(mask)))) / 2; } }; @@ -3151,9 +3154,9 @@ void StoreYCoCg(SIMDVec16 r, SIMDVec16 g, SIMDVec16 b, int16_t* y, int16_t* co, SIMDVec16 tmp = b.Add(co_v.SignedShiftRight<1>()); SIMDVec16 cg_v = g.Sub(tmp); SIMDVec16 y_v = tmp.Add(cg_v.SignedShiftRight<1>()); - y_v.Store((uint16_t*)y); - co_v.Store((uint16_t*)co); - cg_v.Store((uint16_t*)cg); + y_v.Store(reinterpret_cast<uint16_t*>(y)); + co_v.Store(reinterpret_cast<uint16_t*>(co)); + cg_v.Store(reinterpret_cast<uint16_t*>(cg)); } void StoreYCoCg(SIMDVec16 r, SIMDVec16 g, SIMDVec16 b, int32_t* y, int32_t* co, @@ -3169,12 +3172,12 @@ void StoreYCoCg(SIMDVec16 r, SIMDVec16 g, SIMDVec16 b, int32_t* y, int32_t* co, SIMDVec32 tmp_hi = b_up.hi.Add(co_hi_v.SignedShiftRight<1>()); SIMDVec32 cg_hi_v = g_up.hi.Sub(tmp_hi); SIMDVec32 y_hi_v = tmp_hi.Add(cg_hi_v.SignedShiftRight<1>()); - y_lo_v.Store((uint32_t*)y); - co_lo_v.Store((uint32_t*)co); - cg_lo_v.Store((uint32_t*)cg); - y_hi_v.Store((uint32_t*)y + SIMDVec32::kLanes); - co_hi_v.Store((uint32_t*)co + SIMDVec32::kLanes); - cg_hi_v.Store((uint32_t*)cg + SIMDVec32::kLanes); + y_lo_v.Store(reinterpret_cast<uint32_t*>(y)); + co_lo_v.Store(reinterpret_cast<uint32_t*>(co)); + cg_lo_v.Store(reinterpret_cast<uint32_t*>(cg)); + y_hi_v.Store(reinterpret_cast<uint32_t*>(y) + SIMDVec32::kLanes); + co_hi_v.Store(reinterpret_cast<uint32_t*>(co) + SIMDVec32::kLanes); + cg_hi_v.Store(reinterpret_cast<uint32_t*>(cg) + SIMDVec32::kLanes); } #endif @@ -3573,8 +3576,7 @@ void PrepareDCGlobalPalette(bool is_single_group, size_t width, size_t height, int16_t p[4][32 + 1024] = {}; uint8_t prgba[4]; size_t i = 0; - size_t have_zero = 0; - if (palette[pcolors - 1] == 0) have_zero = 1; + size_t have_zero = 1; for (; i < pcolors; i++) { memcpy(prgba, &palette[i], 4); p[0][16 + i + have_zero] = prgba[0]; @@ -3735,10 +3737,13 @@ JxlFastLosslessFrameState* LLPrepare(JxlChunkedFrameInputSource input, const void* buffer = input.get_color_channel_data_at(input.opaque, x0, y0, xs, ys, &stride); auto rgba = reinterpret_cast<const unsigned char*>(buffer); - int y_begin = std::max<int>(0, ys - 2 * effort) / 2; - int y_count = std::min<int>(num_rows, y0 + ys - y_begin - 1); + int y_begin_group = + std::max<ssize_t>( + 0, static_cast<ssize_t>(ys) - static_cast<ssize_t>(num_rows)) / + 2; + int y_count = std::min<int>(num_rows, ys - y_begin_group); int x_max = xs / kChunkSize * kChunkSize; - CollectSamples(rgba, 0, y_begin, x_max, stride, y_count, raw_counts, + CollectSamples(rgba, 0, y_begin_group, x_max, stride, y_count, raw_counts, lz77_counts, onegroup, !collided, bitdepth, nb_chans, big_endian, lookup.data()); input.release_buffer(input.opaque, buffer); @@ -4020,7 +4025,7 @@ namespace default_implementation { #else // FJXL_ENABLE_NEON namespace default_implementation { -#include "lib/jxl/enc_fast_lossless.cc" +#include "lib/jxl/enc_fast_lossless.cc" // NOLINT } #if FJXL_ENABLE_AVX2 @@ -4039,7 +4044,7 @@ namespace default_implementation { namespace AVX2 { #define FJXL_AVX2 -#include "lib/jxl/enc_fast_lossless.cc" +#include "lib/jxl/enc_fast_lossless.cc" // NOLINT #undef FJXL_AVX2 } // namespace AVX2 @@ -4174,18 +4179,20 @@ void JxlFastLosslessProcessFrame( __builtin_cpu_supports("avx512vbmi") && __builtin_cpu_supports("avx512bw") && __builtin_cpu_supports("avx512f") && __builtin_cpu_supports("avx512vl")) { - return AVX512::JxlFastLosslessProcessFrameImpl( - frame_state, is_last, runner_opaque, runner, output_processor); + AVX512::JxlFastLosslessProcessFrameImpl(frame_state, is_last, runner_opaque, + runner, output_processor); + return; } #endif #if FJXL_ENABLE_AVX2 if (__builtin_cpu_supports("avx2")) { - return AVX2::JxlFastLosslessProcessFrameImpl( - frame_state, is_last, runner_opaque, runner, output_processor); + AVX2::JxlFastLosslessProcessFrameImpl(frame_state, is_last, runner_opaque, + runner, output_processor); + return; } #endif - return default_implementation::JxlFastLosslessProcessFrameImpl( + default_implementation::JxlFastLosslessProcessFrameImpl( frame_state, is_last, runner_opaque, runner, output_processor); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_fields.cc b/third_party/jpeg-xl/lib/jxl/enc_fields.cc index dc0cbb7913..fa513297fd 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_fields.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_fields.cc @@ -74,7 +74,8 @@ class WriteVisitor : public VisitorBase { Status Bundle::Write(const Fields& fields, BitWriter* writer, size_t layer, AuxOut* aux_out) { - size_t extension_bits, total_bits; + size_t extension_bits; + size_t total_bits; JXL_RETURN_IF_ERROR(Bundle::CanEncode(fields, &extension_bits, &total_bits)); BitWriter::Allotment allotment(writer, total_bits); @@ -173,7 +174,8 @@ Status F16Coder::Write(float value, BitWriter* JXL_RESTRICT writer) { return true; } - uint32_t biased_exp16, mantissa16; + uint32_t biased_exp16; + uint32_t mantissa16; // exp = [-24, -15] => subnormal if (JXL_UNLIKELY(exp < -14)) { diff --git a/third_party/jpeg-xl/lib/jxl/enc_frame.cc b/third_party/jpeg-xl/lib/jxl/enc_frame.cc index aae59c49a6..8587e1aed2 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_frame.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_frame.cc @@ -12,13 +12,13 @@ #include <array> #include <atomic> #include <cmath> -#include <limits> +#include <memory> #include <numeric> +#include <utility> #include <vector> #include "lib/jxl/ac_context.h" #include "lib/jxl/ac_strategy.h" -#include "lib/jxl/ans_params.h" #include "lib/jxl/base/bits.h" #include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" @@ -31,7 +31,6 @@ #include "lib/jxl/coeff_order_fwd.h" #include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/common.h" // kMaxNumPasses -#include "lib/jxl/compressed_dc.h" #include "lib/jxl/dct_util.h" #include "lib/jxl/dec_external_image.h" #include "lib/jxl/enc_ac_strategy.h" @@ -47,7 +46,6 @@ #include "lib/jxl/enc_entropy_coder.h" #include "lib/jxl/enc_external_image.h" #include "lib/jxl/enc_fields.h" -#include "lib/jxl/enc_gaborish.h" #include "lib/jxl/enc_group.h" #include "lib/jxl/enc_heuristics.h" #include "lib/jxl/enc_modular.h" @@ -285,7 +283,8 @@ Status LoopFilterFromParams(const CompressParams& cparams, bool streaming_mode, if (frame_header->encoding == FrameEncoding::kModular && !cparams.IsLossless()) { // TODO(veluca): this formula is nonsense. - loop_filter->epf_sigma_for_modular = cparams.butteraugli_distance; + loop_filter->epf_sigma_for_modular = + std::max(cparams.butteraugli_distance, 1.0f); } if (frame_header->encoding == FrameEncoding::kModular && cparams.lossy_palette) { @@ -539,7 +538,7 @@ struct PixelStatsForChromacityAdjustment { float dx = 0; float db = 0; float exposed_blue = 0; - float CalcPlane(const ImageF* JXL_RESTRICT plane, const Rect& rect) const { + static float CalcPlane(const ImageF* JXL_RESTRICT plane, const Rect& rect) { float xmax = 0; float ymax = 0; for (size_t ty = 1; ty < rect.ysize(); ++ty) { @@ -583,7 +582,7 @@ struct PixelStatsForChromacityAdjustment { dx = CalcPlane(&opsin->Plane(0), rect); CalcExposedBlue(&opsin->Plane(1), &opsin->Plane(2), rect); } - int HowMuchIsXChannelPixelized() { + int HowMuchIsXChannelPixelized() const { if (dx >= 0.03) { return 2; } @@ -592,7 +591,7 @@ struct PixelStatsForChromacityAdjustment { } return 0; } - int HowMuchIsBChannelPixelized() { + int HowMuchIsBChannelPixelized() const { int add = exposed_blue >= 0.13 ? 1 : 0; if (db > 0.38) { return 2 + add; @@ -682,12 +681,12 @@ void ComputeNoiseParams(const CompressParams& cparams, bool streaming_mode, } } -void DownsampleColorChannels(const CompressParams& cparams, - const FrameHeader& frame_header, - bool color_is_jpeg, Image3F* opsin) { +Status DownsampleColorChannels(const CompressParams& cparams, + const FrameHeader& frame_header, + bool color_is_jpeg, Image3F* opsin) { if (color_is_jpeg || frame_header.upsampling == 1 || cparams.already_downsampled) { - return; + return true; } if (frame_header.encoding == FrameEncoding::kVarDCT && frame_header.upsampling == 2) { @@ -698,16 +697,18 @@ void DownsampleColorChannels(const CompressParams& cparams, // TODO(lode): DownsampleImage2_Iterative is currently too slow to // be used for squirrel, make it faster, and / or enable it only for // kitten. - DownsampleImage2_Iterative(opsin); + JXL_RETURN_IF_ERROR(DownsampleImage2_Iterative(opsin)); } else { - DownsampleImage2_Sharper(opsin); + JXL_RETURN_IF_ERROR(DownsampleImage2_Sharper(opsin)); } } else { - DownsampleImage(opsin, frame_header.upsampling); + JXL_ASSIGN_OR_RETURN(*opsin, + DownsampleImage(*opsin, frame_header.upsampling)); } if (frame_header.encoding == FrameEncoding::kVarDCT) { PadImageToBlockMultipleInPlace(opsin); } + return true; } template <typename V, typename R> @@ -741,14 +742,17 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, const size_t ysize_blocks = frame_dim.ysize_blocks; // no-op chroma from luma - shared.cmap = ColorCorrelationMap(xsize, ysize, false); + JXL_ASSIGN_OR_RETURN(shared.cmap, + ColorCorrelationMap::Create(xsize, ysize, false)); shared.ac_strategy.FillDCT8(); - FillImage(uint8_t(0), &shared.epf_sharpness); + FillImage(static_cast<uint8_t>(0), &shared.epf_sharpness); enc_state->coeffs.clear(); while (enc_state->coeffs.size() < enc_state->passes.size()) { - enc_state->coeffs.emplace_back(make_unique<ACImageT<int32_t>>( - kGroupDim * kGroupDim, frame_dim.num_groups)); + JXL_ASSIGN_OR_RETURN( + std::unique_ptr<ACImageT<int32_t>> coeffs, + ACImageT<int32_t>::Make(kGroupDim * kGroupDim, frame_dim.num_groups)); + enc_state->coeffs.emplace_back(std::move(coeffs)); } // convert JPEG quantization table to a Quantizer object @@ -779,7 +783,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, 1.0f / dcquantization[2]}; qe[AcStrategy::Type::DCT] = QuantEncoding::RAW(qt); - DequantMatricesSetCustom(&shared.matrices, qe, enc_modular); + JXL_RETURN_IF_ERROR( + DequantMatricesSetCustom(&shared.matrices, qe, enc_modular)); // Ensure that InvGlobalScale() is 1. shared.quantizer = Quantizer(&shared.matrices, 1, kGlobalScaleDenom); @@ -844,7 +849,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, kScale * row_s[x * kDCTBlockSize + coeffpos] + (kOffset - kBase * kScale) * scaled_m; if (std::abs(scaled_m) > 1e-8f) { - float from, to; + float from; + float to; if (scaled_m > 0) { from = (scaled_s - kZeroThresh) / scaled_m; to = (scaled_s + kZeroThresh) / scaled_m; @@ -889,7 +895,7 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, } } - Image3F dc = Image3F(xsize_blocks, ysize_blocks); + JXL_ASSIGN_OR_RETURN(Image3F dc, Image3F::Create(xsize_blocks, ysize_blocks)); if (!frame_header.chroma_subsampling.Is444()) { ZeroFillImage(&dc); for (auto& coeff : enc_state->coeffs) { @@ -944,7 +950,7 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, idc = inputjpeg[base] + 1024 / qt[c * 64]; } dc_counts[c][std::min(static_cast<uint32_t>(idc + 1024), - uint32_t(2047))]++; + static_cast<uint32_t>(2047))]++; total_dc[c]++; fdc[bx >> hshift] = idc * dcquantization_r[c]; if (c == 1 || !enc_state->cparams.force_cfl_jpeg_recompression || @@ -1024,18 +1030,27 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data, *std::max_element(ctx_map.begin(), ctx_map.end()) + 1; // disable DC frame for now + std::atomic<bool> has_error{false}; auto compute_dc_coeffs = [&](const uint32_t group_index, size_t /* thread */) { + if (has_error) return; const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index); - enc_modular->AddVarDCTDC(frame_header, dc, r, group_index, - /*nl_dc=*/false, enc_state, - /*jpeg_transcode=*/true); - enc_modular->AddACMetadata(r, group_index, /*jpeg_transcode=*/true, - enc_state); + if (!enc_modular->AddVarDCTDC(frame_header, dc, r, group_index, + /*nl_dc=*/false, enc_state, + /*jpeg_transcode=*/true)) { + has_error = true; + return; + } + if (!enc_modular->AddACMetadata(r, group_index, /*jpeg_transcode=*/true, + enc_state)) { + has_error = true; + return; + } }; JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups, ThreadPool::NoInit, compute_dc_coeffs, "Compute DC coeffs")); + if (has_error) return JXL_FAILURE("Compute DC coeffs failed"); return true; } @@ -1077,10 +1092,12 @@ void ComputeAllCoeffOrders(PassesEncoderState& enc_state, // Working area for TokenizeCoefficients (per-group!) struct EncCache { // Allocates memory when first called. - void InitOnce() { + Status InitOnce() { if (num_nzeroes.xsize() == 0) { - num_nzeroes = Image3I(kGroupDimInBlocks, kGroupDimInBlocks); + JXL_ASSIGN_OR_RETURN( + num_nzeroes, Image3I::Create(kGroupDimInBlocks, kGroupDimInBlocks)); } + return true; } // TokenizeCoefficients Image3I num_nzeroes; @@ -1095,8 +1112,10 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header, group_caches.resize(num_threads); return true; }; + std::atomic<bool> has_error{false}; const auto tokenize_group = [&](const uint32_t group_index, const size_t thread) { + if (has_error) return; // Tokenize coefficients. const Rect rect = shared.frame_dim.BlockGroupRect(group_index); for (size_t idx_pass = 0; idx_pass < enc_state->passes.size(); idx_pass++) { @@ -1107,7 +1126,10 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header, enc_state->coeffs[idx_pass]->PlaneRow(2, group_index, 0).ptr32, }; // Ensure group cache is initialized. - group_caches[thread].InitOnce(); + if (!group_caches[thread].InitOnce()) { + has_error = true; + return; + } TokenizeCoefficients( &shared.coeff_orders[idx_pass * shared.coeff_order_size], rect, ac_rows, shared.ac_strategy, frame_header.chroma_subsampling, @@ -1116,8 +1138,11 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header, shared.raw_quant_field, shared.block_ctx_map); } }; - return RunOnPool(pool, 0, shared.frame_dim.num_groups, tokenize_group_init, - tokenize_group, "TokenizeGroup"); + JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_groups, + tokenize_group_init, tokenize_group, + "TokenizeGroup")); + if (has_error) return JXL_FAILURE("TokenizeGroup failed"); + return true; } Status EncodeGlobalDCInfo(const PassesSharedState& shared, BitWriter* writer, @@ -1208,10 +1233,13 @@ Status EncodeGroups(const FrameHeader& frame_header, const size_t num_groups = frame_dim.num_groups; const size_t num_passes = enc_state->progressive_splitter.GetNumPasses(); const size_t global_ac_index = frame_dim.num_dc_groups + 1; - const bool is_small_image = frame_dim.num_groups == 1 && num_passes == 1; - - group_codes->resize( - NumTocEntries(num_groups, frame_dim.num_dc_groups, num_passes)); + const bool is_small_image = + !enc_state->streaming_mode && num_groups == 1 && num_passes == 1; + const size_t num_toc_entries = + is_small_image ? 1 + : AcGroupIndex(0, 0, num_groups, frame_dim.num_dc_groups) + + num_groups * num_passes; + group_codes->resize(num_toc_entries); const auto get_output = [&](const size_t index) { return &(*group_codes)[is_small_image ? 0 : index]; @@ -1308,35 +1336,48 @@ Status EncodeGroups(const FrameHeader& frame_header, enc_state, get_output(global_ac_index), enc_modular, aux_out)); } - std::atomic<int> num_errors{0}; + std::atomic<bool> has_error{false}; const auto process_group = [&](const uint32_t group_index, const size_t thread) { + if (has_error) return; AuxOut* my_aux_out = aux_outs[thread].get(); + size_t ac_group_id = + enc_state->streaming_mode + ? enc_modular->ComputeStreamingAbsoluteAcGroupId( + enc_state->dc_group_index, group_index, shared.frame_dim) + : group_index; + for (size_t i = 0; i < num_passes; i++) { + JXL_DEBUG_V(2, "Encoding AC group %u [abs %" PRIuS "] pass %" PRIuS, + group_index, ac_group_id, i); if (frame_header.encoding == FrameEncoding::kVarDCT) { if (!EncodeGroupTokenizedCoefficients( group_index, i, enc_state->histogram_idx[group_index], *enc_state, ac_group_code(i, group_index), my_aux_out)) { - num_errors.fetch_add(1, std::memory_order_relaxed); + has_error = true; return; } } // Write all modular encoded data (color?, alpha, depth, extra channels) if (!enc_modular->EncodeStream( ac_group_code(i, group_index), my_aux_out, kLayerModularAcGroup, - ModularStreamId::ModularAC(group_index, i))) { - num_errors.fetch_add(1, std::memory_order_relaxed); + ModularStreamId::ModularAC(ac_group_id, i))) { + has_error = true; return; } + JXL_DEBUG_V(2, + "AC group %u [abs %" PRIuS "] pass %" PRIuS + " encoded size is %" PRIuS " bits", + group_index, ac_group_id, i, + ac_group_code(i, group_index)->BitsWritten()); } }; JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, num_groups, resize_aux_outs, process_group, "EncodeGroupCoefficients")); - + if (has_error) return JXL_FAILURE("EncodeGroupCoefficients failed"); // Resizing aux_outs to 0 also Assimilates the array. static_cast<void>(resize_aux_outs(0)); - JXL_RETURN_IF_ERROR(num_errors.load(std::memory_order_relaxed) == 0); for (BitWriter& bw : *group_codes) { BitWriter::Allotment allotment(&bw, 8); @@ -1360,29 +1401,39 @@ Status ComputeEncodingData( PassesSharedState& shared = enc_state.shared; shared.metadata = metadata; if (enc_state.streaming_mode) { - shared.frame_dim.Set(xsize, ysize, /*group_size_shift=*/1, - /*maxhshift=*/0, /*maxvshift=*/0, - /*modular_mode=*/false, /*upsampling=*/1); + shared.frame_dim.Set( + xsize, ysize, frame_header.group_size_shift, + /*max_hshift=*/0, /*max_vshift=*/0, + mutable_frame_header.encoding == FrameEncoding::kModular, + /*upsampling=*/1); } else { shared.frame_dim = frame_header.ToFrameDimensions(); } shared.image_features.patches.SetPassesSharedState(&shared); const FrameDimensions& frame_dim = shared.frame_dim; - shared.ac_strategy = - AcStrategyImage(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared.raw_quant_field = - ImageI(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared.epf_sharpness = ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared.cmap = ColorCorrelationMap(frame_dim.xsize, frame_dim.ysize); + JXL_ASSIGN_OR_RETURN( + shared.ac_strategy, + AcStrategyImage::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + shared.raw_quant_field, + ImageI::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + shared.epf_sharpness, + ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared.cmap, ColorCorrelationMap::Create( + frame_dim.xsize, frame_dim.ysize)); shared.coeff_order_size = kCoeffOrderMaxSize; if (frame_header.encoding == FrameEncoding::kVarDCT) { shared.coeff_orders.resize(frame_header.passes.num_passes * kCoeffOrderMaxSize); } - shared.quant_dc = ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared.dc_storage = Image3F(frame_dim.xsize_blocks, frame_dim.ysize_blocks); + JXL_ASSIGN_OR_RETURN(shared.quant_dc, ImageB::Create(frame_dim.xsize_blocks, + frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + shared.dc_storage, + Image3F::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); shared.dc = &shared.dc_storage; const size_t num_extra_channels = metadata->m.num_extra_channels; @@ -1397,16 +1448,19 @@ Status ComputeEncodingData( // computing inverse Gaborish and adaptive quantization map. int max_border = enc_state.streaming_mode ? kBlockDim : 0; Rect frame_rect(0, 0, frame_data.xsize, frame_data.ysize); - Rect patch_rect = Rect(x0, y0, xsize, ysize).Extend(max_border, frame_rect); + Rect frame_area_rect = Rect(x0, y0, xsize, ysize); + Rect patch_rect = frame_area_rect.Extend(max_border, frame_rect); JXL_ASSERT(patch_rect.IsInside(frame_rect)); // Allocating a large enough image avoids a copy when padding. - Image3F color(RoundUpToBlockDim(patch_rect.xsize()), - RoundUpToBlockDim(patch_rect.ysize())); + JXL_ASSIGN_OR_RETURN(Image3F color, + Image3F::Create(RoundUpToBlockDim(patch_rect.xsize()), + RoundUpToBlockDim(patch_rect.ysize()))); color.ShrinkTo(patch_rect.xsize(), patch_rect.ysize()); std::vector<ImageF> extra_channels(num_extra_channels); for (auto& extra_channel : extra_channels) { - extra_channel = jxl::ImageF(patch_rect.xsize(), patch_rect.ysize()); + JXL_ASSIGN_OR_RETURN( + extra_channel, ImageF::Create(patch_rect.xsize(), patch_rect.ysize())); } ImageF* alpha = alpha_eci ? &extra_channels[alpha_idx] : nullptr; ImageF* black = black_eci ? &extra_channels[black_idx] : nullptr; @@ -1432,7 +1486,9 @@ Status ComputeEncodingData( frame_info.ib_needs_color_transform) { if (frame_header.encoding == FrameEncoding::kVarDCT && cparams.speed_tier <= SpeedTier::kKitten) { - linear_storage = Image3F(patch_rect.xsize(), patch_rect.ysize()); + JXL_ASSIGN_OR_RETURN( + linear_storage, + Image3F::Create(patch_rect.xsize(), patch_rect.ysize())); linear = &linear_storage; } ToXYB(c_enc, metadata->m.IntensityTarget(), black, pool, &color, cms, @@ -1446,7 +1502,7 @@ Status ComputeEncodingData( bool lossless = cparams.IsLossless(); if (alpha && !alpha_eci->alpha_associated && frame_header.frame_type == FrameType::kRegularFrame && - !ApplyOverride(cparams.keep_invisible, lossless) && + !ApplyOverride(cparams.keep_invisible, true) && cparams.ec_resampling == cparams.resampling) { // simplify invisible pixels SimplifyInvisible(&color, *alpha, lossless); @@ -1467,15 +1523,17 @@ Status ComputeEncodingData( &mutable_frame_header); } - ComputeNoiseParams(cparams, enc_state.streaming_mode, !!jpeg_data, color, + bool has_jpeg_data = (jpeg_data != nullptr); + ComputeNoiseParams(cparams, enc_state.streaming_mode, has_jpeg_data, color, frame_dim, &mutable_frame_header, &shared.image_features.noise_params); - DownsampleColorChannels(cparams, frame_header, !!jpeg_data, &color); + JXL_RETURN_IF_ERROR( + DownsampleColorChannels(cparams, frame_header, has_jpeg_data, &color)); if (cparams.ec_resampling != 1 && !cparams.already_downsampled) { for (ImageF& ec : extra_channels) { - DownsampleImage(&ec, cparams.ec_resampling); + JXL_ASSIGN_OR_RETURN(ec, DownsampleImage(ec, cparams.ec_resampling)); } } @@ -1505,15 +1563,21 @@ Status ComputeEncodingData( TokenizeAllCoefficients(frame_header, pool, &enc_state)); } + if (cparams.modular_mode || !extra_channels.empty()) { + JXL_RETURN_IF_ERROR(enc_modular.ComputeEncodingData( + frame_header, metadata->m, &color, extra_channels, group_rect, + frame_dim, frame_area_rect, &enc_state, cms, pool, aux_out, + /*do_color=*/cparams.modular_mode)); + } + if (!enc_state.streaming_mode) { - if (cparams.modular_mode || !extra_channels.empty()) { - JXL_RETURN_IF_ERROR(enc_modular.ComputeEncodingData( - frame_header, metadata->m, &color, extra_channels, &enc_state, cms, - pool, aux_out, /*do_color=*/cparams.modular_mode)); + if (cparams.speed_tier < SpeedTier::kTortoise || + !cparams.ModularPartIsLossless() || cparams.responsive || + !cparams.custom_fixed_tree.empty()) { + // Use local trees if doing lossless modular, unless at very slow speeds. + JXL_RETURN_IF_ERROR(enc_modular.ComputeTree(pool)); + JXL_RETURN_IF_ERROR(enc_modular.ComputeTokens(pool)); } - JXL_RETURN_IF_ERROR(enc_modular.ComputeTree(pool)); - JXL_RETURN_IF_ERROR(enc_modular.ComputeTokens(pool)); - mutable_frame_header.UpdateFlag(shared.image_features.patches.HasAny(), FrameHeader::kPatches); mutable_frame_header.UpdateFlag(shared.image_features.splines.HasAny(), @@ -1526,6 +1590,7 @@ Status ComputeEncodingData( const size_t group_index = enc_state.dc_group_index; enc_modular.ClearStreamData(ModularStreamId::VarDCTDC(group_index)); enc_modular.ClearStreamData(ModularStreamId::ACMetadata(group_index)); + enc_modular.ClearModularStreamData(); } return true; } @@ -1614,49 +1679,58 @@ bool CanDoStreamingEncoding(const CompressParams& cparams, const FrameInfo& frame_info, const CodecMetadata& metadata, const JxlEncoderChunkedFrameAdapter& frame_data) { - if (frame_data.IsJPEG()) { - return false; - } - if (cparams.noise == Override::kOn || cparams.patches == Override::kOn) { + if (cparams.buffering == 0) { return false; } - if (cparams.progressive_dc != 0 || frame_info.dc_level != 0) { - return false; + if (cparams.buffering == -1) { + if (cparams.speed_tier < SpeedTier::kTortoise) return false; + if (cparams.speed_tier < SpeedTier::kSquirrel && + cparams.butteraugli_distance > 0.5f) { + return false; + } + if (cparams.speed_tier == SpeedTier::kSquirrel && + cparams.butteraugli_distance >= 3.f) { + return false; + } } - if (cparams.resampling != 1 || cparams.ec_resampling != 1) { + + // TODO(veluca): handle different values of `buffering`. + if (frame_data.xsize <= 2048 && frame_data.ysize <= 2048) { return false; } - if (cparams.max_error_mode) { + if (frame_data.IsJPEG()) { return false; } - if (cparams.color_transform != ColorTransform::kXYB) { + if (cparams.noise == Override::kOn || cparams.patches == Override::kOn) { return false; } - if (cparams.modular_mode) { + if (cparams.progressive_dc != 0 || frame_info.dc_level != 0) { return false; } - if (metadata.m.num_extra_channels > 0) { + if (cparams.resampling != 1 || cparams.ec_resampling != 1) { return false; } - if (cparams.buffering == 0) { + if (cparams.max_error_mode) { return false; } - if (cparams.buffering == 1 && frame_data.xsize <= 2048 && - frame_data.ysize <= 2048) { - return false; + if (!cparams.ModularPartIsLossless() || cparams.responsive > 0) { + if (metadata.m.num_extra_channels > 0 || cparams.modular_mode) { + return false; + } } - if (frame_data.xsize <= 256 && frame_data.ysize <= 256) { + ColorTransform ok_color_transform = + cparams.modular_mode ? ColorTransform::kNone : ColorTransform::kXYB; + if (cparams.color_transform != ok_color_transform) { return false; } return true; } void ComputePermutationForStreaming(size_t xsize, size_t ysize, - size_t num_passes, + size_t group_size, size_t num_passes, std::vector<coeff_order_t>& permutation, std::vector<size_t>& dc_group_order) { // This is only valid in VarDCT mode, otherwise there can be group shift. - const size_t group_size = 256; const size_t dc_group_size = group_size * kBlockDim; const size_t group_xsize = DivCeil(xsize, group_size); const size_t group_ysize = DivCeil(ysize, group_size); @@ -1794,7 +1868,7 @@ void RemoveUnusedHistograms(std::vector<uint8_t>& context_map, for (uint8_t histo_idx : inv_remap) { new_codes.encoding_info.emplace_back( std::move(codes.encoding_info[histo_idx])); - new_codes.uint_config.emplace_back(std::move(codes.uint_config[histo_idx])); + new_codes.uint_config.emplace_back(codes.uint_config[histo_idx]); new_codes.encoded_histograms.emplace_back( std::move(codes.encoded_histograms[histo_idx])); } @@ -1864,14 +1938,13 @@ Status EncodeFrameStreaming(const CompressParams& cparams, frame_info, jpeg_data.get(), true, &frame_header)); const size_t num_passes = enc_state.progressive_splitter.GetNumPasses(); - ModularFrameEncoder enc_modular(frame_header, cparams); + ModularFrameEncoder enc_modular(frame_header, cparams, true); std::vector<coeff_order_t> permutation; std::vector<size_t> dc_group_order; - ComputePermutationForStreaming(frame_data.xsize, frame_data.ysize, num_passes, - permutation, dc_group_order); + size_t group_size = frame_header.ToFrameDimensions().group_dim; + ComputePermutationForStreaming(frame_data.xsize, frame_data.ysize, group_size, + num_passes, permutation, dc_group_order); enc_state.shared.num_histograms = dc_group_order.size(); - // This is only valid in VarDCT mode, otherwise there can be group shift. - size_t group_size = 256; size_t dc_group_size = group_size * kBlockDim; size_t dc_group_xsize = DivCeil(frame_data.xsize, dc_group_size); size_t min_dc_global_size = 0; @@ -1898,8 +1971,7 @@ Status EncodeFrameStreaming(const CompressParams& cparams, enc_state.streaming_mode = true; enc_state.initialize_global_state = (i == 0); enc_state.dc_group_index = dc_ix; - enc_state.histogram_idx = - std::vector<uint8_t>(group_xsize * group_ysize, i); + enc_state.histogram_idx = std::vector<size_t>(group_xsize * group_ysize, i); std::vector<BitWriter> group_codes; JXL_RETURN_IF_ERROR(ComputeEncodingData( cparams, frame_info, metadata, frame_data, jpeg_data.get(), x0, y0, @@ -1931,9 +2003,13 @@ Status EncodeFrameStreaming(const CompressParams& cparams, JXL_RETURN_IF_ERROR( OutputGroups(std::move(group_codes), &group_sizes, output_processor)); } - JXL_RETURN_IF_ERROR(OutputAcGlobal(enc_state, - frame_header.ToFrameDimensions(), - &group_sizes, output_processor, aux_out)); + if (frame_header.encoding == FrameEncoding::kVarDCT) { + JXL_RETURN_IF_ERROR( + OutputAcGlobal(enc_state, frame_header.ToFrameDimensions(), + &group_sizes, output_processor, aux_out)); + } else { + group_sizes.push_back(0); + } JXL_ASSERT(group_sizes.size() == permutation.size()); size_t end_pos = output_processor->CurrentPosition(); output_processor->Seek(start_pos); @@ -1975,7 +2051,7 @@ Status EncodeFrameOneShot(const CompressParams& cparams, frame_info, jpeg_data.get(), false, &frame_header)); const size_t num_passes = enc_state.progressive_splitter.GetNumPasses(); - ModularFrameEncoder enc_modular(frame_header, cparams); + ModularFrameEncoder enc_modular(frame_header, cparams, false); JXL_RETURN_IF_ERROR(ComputeEncodingData( cparams, frame_info, metadata, frame_data, jpeg_data.get(), 0, 0, frame_data.xsize, frame_data.ysize, cms, pool, frame_header, enc_modular, @@ -2008,15 +2084,21 @@ Status EncodeFrame(const CompressParams& cparams_orig, JxlEncoderOutputProcessorWrapper* output_processor, AuxOut* aux_out) { CompressParams cparams = cparams_orig; - if (cparams.speed_tier == SpeedTier::kGlacier && !cparams.IsLossless()) { - cparams.speed_tier = SpeedTier::kTortoise; + if (cparams.speed_tier == SpeedTier::kTectonicPlate && + !cparams.IsLossless()) { + cparams.speed_tier = SpeedTier::kGlacier; + } + // Lightning mode is handled externally, so switch to Thunder mode to handle + // potentially weird cases. + if (cparams.speed_tier == SpeedTier::kLightning) { + cparams.speed_tier = SpeedTier::kThunder; } - if (cparams.speed_tier == SpeedTier::kGlacier) { + if (cparams.speed_tier == SpeedTier::kTectonicPlate) { std::vector<CompressParams> all_params; std::vector<size_t> size; CompressParams cparams_attempt = cparams_orig; - cparams_attempt.speed_tier = SpeedTier::kTortoise; + cparams_attempt.speed_tier = SpeedTier::kGlacier; cparams_attempt.options.max_properties = 4; for (float x : {0.0f, 80.f}) { @@ -2027,8 +2109,9 @@ Status EncodeFrame(const CompressParams& cparams_orig, // modular headers. for (int K : {0, 1 << 10, 70000}) { cparams_attempt.palette_colors = K; - for (int tree_mode : {-1, (int)ModularOptions::TreeMode::kNoWP, - (int)ModularOptions::TreeMode::kDefault}) { + for (int tree_mode : + {-1, static_cast<int>(ModularOptions::TreeMode::kNoWP), + static_cast<int>(ModularOptions::TreeMode::kDefault)}) { if (tree_mode == -1) { // LZ77 only cparams_attempt.options.nb_repeats = 0; @@ -2054,11 +2137,12 @@ Status EncodeFrame(const CompressParams& cparams_orig, size.resize(all_params.size()); - std::atomic<int> num_errors{0}; + std::atomic<bool> has_error{false}; JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, all_params.size(), ThreadPool::NoInit, [&](size_t task, size_t) { + if (has_error) return; std::vector<uint8_t> output(64); uint8_t* next_out = output.data(); size_t avail_out = output.size(); @@ -2066,13 +2150,13 @@ Status EncodeFrame(const CompressParams& cparams_orig, local_output.SetAvailOut(&next_out, &avail_out); if (!EncodeFrame(all_params[task], frame_info, metadata, frame_data, cms, nullptr, &local_output, aux_out)) { - num_errors.fetch_add(1, std::memory_order_relaxed); + has_error = true; return; } size[task] = local_output.CurrentPosition(); }, - "Compress kGlacier")); - JXL_RETURN_IF_ERROR(num_errors.load(std::memory_order_relaxed) == 0); + "Compress kTectonicPlate")); + if (has_error) return JXL_FAILURE("Compress kTectonicPlate failed"); size_t best_idx = 0; for (size_t i = 1; i < all_params.size(); i++) { @@ -2156,7 +2240,7 @@ Status EncodeFrame(const CompressParams& cparams_orig, size_t stride = ib.xsize() * num_channels * 4; color.resize(ib.ysize() * stride); JXL_RETURN_IF_ERROR(ConvertToExternal( - ib, /*bites_per_sample=*/32, /*float_out=*/true, num_channels, + ib, /*bits_per_sample=*/32, /*float_out=*/true, num_channels, JXL_NATIVE_ENDIAN, stride, pool, color.data(), color.size(), /*out_callback=*/{}, Orientation::kIdentity)); JxlPixelFormat format{num_channels, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}; @@ -2169,7 +2253,7 @@ Status EncodeFrame(const CompressParams& cparams_orig, const ImageF* channel = &ib.extra_channels()[ec]; JXL_RETURN_IF_ERROR(ConvertChannelsToExternal( &channel, 1, - /*bites_per_sample=*/32, + /*bits_per_sample=*/32, /*float_out=*/true, JXL_NATIVE_ENDIAN, ec_stride, pool, ec_data.data(), ec_data.size(), /*out_callback=*/{}, Orientation::kIdentity)); frame_data.SetFromBuffer(1 + ec, ec_data.data(), ec_data.size(), ec_format); diff --git a/third_party/jpeg-xl/lib/jxl/enc_frame.h b/third_party/jpeg-xl/lib/jxl/enc_frame.h index c6db64ee4e..a5b74cc648 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_frame.h +++ b/third_party/jpeg-xl/lib/jxl/enc_frame.h @@ -57,7 +57,7 @@ struct FrameInfo { // Corresponds to BlendingInfo::source from the FrameHeader. size_t source = 1; // Corresponds to BlendingInfo::clamp from the FrameHeader. - size_t clamp = 1; + bool clamp = true; // Corresponds to BlendingInfo::alpha_channel from the FrameHeader, or set to // -1 to automatically choose it as the index of the first extra channel of // type alpha. diff --git a/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc b/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc index 3f2ee32afd..7467a4d669 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc @@ -15,8 +15,8 @@ namespace jxl { -void GaborishInverse(Image3F* in_out, const Rect& rect, float mul[3], - ThreadPool* pool) { +Status GaborishInverse(Image3F* in_out, const Rect& rect, const float mul[3], + ThreadPool* pool) { WeightsSymmetric5 weights[3]; // Only an approximation. One or even two 3x3, and rank-1 (separable) 5x5 // are insufficient. The numbers here have been obtained by butteraugli @@ -47,7 +47,9 @@ void GaborishInverse(Image3F* in_out, const Rect& rect, float mul[3], // Note that we cannot *allocate* a plane, as doing so might cause Image3F to // have planes of different stride. Instead, we copy one plane in a temporary // image and reuse the existing planes of the in/out image. - ImageF temp(in_out->Plane(2).xsize(), in_out->Plane(2).ysize()); + ImageF temp; + JXL_ASSIGN_OR_RETURN( + temp, ImageF::Create(in_out->Plane(2).xsize(), in_out->Plane(2).ysize())); CopyImageTo(in_out->Plane(2), &temp); Rect xrect = rect.Extend(3, Rect(*in_out)); Symmetric5(in_out->Plane(0), xrect, weights[0], pool, &in_out->Plane(2), @@ -59,6 +61,7 @@ void GaborishInverse(Image3F* in_out, const Rect& rect, float mul[3], in_out->Plane(0).Swap(in_out->Plane(1)); // 2 1 0 in_out->Plane(0).Swap(in_out->Plane(2)); + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_gaborish.h b/third_party/jpeg-xl/lib/jxl/enc_gaborish.h index ece4959f36..041edcec96 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_gaborish.h +++ b/third_party/jpeg-xl/lib/jxl/enc_gaborish.h @@ -9,6 +9,7 @@ // Linear smoothing (3x3 convolution) for deblocking without too much blur. #include "lib/jxl/base/data_parallel.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/image.h" namespace jxl { @@ -16,8 +17,8 @@ namespace jxl { // Used in encoder to reduce the impact of the decoder's smoothing. // This is not exact. Works in-place to reduce memory use. // The input is typically in XYB space. -void GaborishInverse(Image3F* in_out, const Rect& rect, float mul[3], - ThreadPool* pool); +Status GaborishInverse(Image3F* in_out, const Rect& rect, const float mul[3], + ThreadPool* pool); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc b/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc index 426f08ecb0..0d173c5eb8 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc @@ -5,6 +5,8 @@ #include "lib/jxl/enc_gaborish.h" +#include <jxl/types.h> + #include <hwy/base.h> #include "lib/jxl/base/compiler_specific.h" @@ -40,7 +42,7 @@ void ConvolveGaborish(const ImageF& in, float weight1, float weight2, } void TestRoundTrip(const Image3F& in, float max_l1) { - Image3F fwd(in.xsize(), in.ysize()); + JXL_ASSIGN_OR_DIE(Image3F fwd, Image3F::Create(in.xsize(), in.ysize())); ThreadPool* null_pool = nullptr; ConvolveGaborish(in.Plane(0), 0, 0, null_pool, &fwd.Plane(0)); ConvolveGaborish(in.Plane(1), 0, 0, null_pool, &fwd.Plane(1)); @@ -51,20 +53,20 @@ void TestRoundTrip(const Image3F& in, float max_l1) { w, w, }; - GaborishInverse(&fwd, Rect(fwd), weights, null_pool); + JXL_CHECK(GaborishInverse(&fwd, Rect(fwd), weights, null_pool)); JXL_ASSERT_OK(VerifyRelativeError(in, fwd, max_l1, 1E-4f, _)); } TEST(GaborishTest, TestZero) { - Image3F in(20, 20); + JXL_ASSIGN_OR_DIE(Image3F in, Image3F::Create(20, 20)); ZeroFillImage(&in); TestRoundTrip(in, 0.0f); } // Disabled: large difference. -#if 0 +#if JXL_FALSE TEST(GaborishTest, TestDirac) { - Image3F in(20, 20); + JXL_ASSIGN_OR_DIE(Image3F in, Image3F::Create(20, 20)); ZeroFillImage(&in); in.PlaneRow(1, 10)[10] = 10.0f; TestRoundTrip(in, 0.26f); @@ -72,7 +74,7 @@ TEST(GaborishTest, TestDirac) { #endif TEST(GaborishTest, TestFlat) { - Image3F in(20, 20); + JXL_ASSIGN_OR_DIE(Image3F in, Image3F::Create(20, 20)); FillImage(1.0f, &in); TestRoundTrip(in, 1E-5f); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_group.cc b/third_party/jpeg-xl/lib/jxl/enc_group.cc index 09bab534c9..1967fdaba9 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_group.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_group.cc @@ -286,10 +286,11 @@ void AdjustQuantBlockAC(const Quantizer& quantizer, size_t c, { // Reduce quant in highly active areas. int32_t div = (xsize * ysize); - int32_t activity = (hfNonZeros[0] + div / 2) / div; + int32_t activity = (static_cast<int32_t>(hfNonZeros[0]) + div / 2) / div; int32_t orig_qp_limit = std::max(4, *quant / 2); for (int i = 1; i < 4; ++i) { - activity = std::min<int32_t>(activity, (hfNonZeros[i] + div / 2) / div); + activity = std::min( + activity, (static_cast<int32_t>(hfNonZeros[i]) + div / 2) / div); } if (activity >= 15) { activity = 15; @@ -316,7 +317,7 @@ void QuantizeRoundtripYBlockAC(PassesEncoderState* enc_state, const size_t size, float* JXL_RESTRICT inout, int32_t* JXL_RESTRICT quantized) { float thres_y[4] = {0.58f, 0.64f, 0.64f, 0.64f}; - { + if (enc_state->cparams.speed_tier <= SpeedTier::kHare) { int32_t max_quant = 0; int quant_orig = *quant; float val[3] = {enc_state->x_qm_multiplier, 1.0f, @@ -337,6 +338,11 @@ void QuantizeRoundtripYBlockAC(PassesEncoderState* enc_state, const size_t size, max_quant = std::max(*quant, max_quant); } *quant = max_quant; + } else { + thres_y[0] = 0.56; + thres_y[1] = 0.62; + thres_y[2] = 0.62; + thres_y[3] = 0.62; } QuantizeBlockAC(quantizer, error_diffusion, 1, 1.0f, quant_kind, xsize, ysize, @@ -507,8 +513,8 @@ namespace jxl { HWY_EXPORT(ComputeCoefficients); void ComputeCoefficients(size_t group_idx, PassesEncoderState* enc_state, const Image3F& opsin, const Rect& rect, Image3F* dc) { - return HWY_DYNAMIC_DISPATCH(ComputeCoefficients)(group_idx, enc_state, opsin, - rect, dc); + HWY_DYNAMIC_DISPATCH(ComputeCoefficients) + (group_idx, enc_state, opsin, rect, dc); } Status EncodeGroupTokenizedCoefficients(size_t group_idx, size_t pass_idx, diff --git a/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc b/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc index 9d6bf11184..685558ac7c 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc @@ -149,8 +149,10 @@ void FindBestBlockEntropyModel(const CompressParams& cparams, const ImageI& rqf, std::vector<uint8_t> remap((qft.size() + 1) * kNumOrders); std::iota(remap.begin(), remap.end(), 0); std::vector<uint8_t> clusters(remap); - size_t nb_clusters = Clamp1((int)(tot / size_for_ctx_model / 2), 2, 9); - size_t nb_clusters_chroma = Clamp1((int)(tot / size_for_ctx_model / 3), 1, 5); + size_t nb_clusters = + Clamp1(static_cast<int>(tot / size_for_ctx_model / 2), 2, 9); + size_t nb_clusters_chroma = + Clamp1(static_cast<int>(tot / size_for_ctx_model / 3), 1, 5); // This is O(n^2 log n), but n is small. while (clusters.size() > nb_clusters) { std::sort(clusters.begin(), clusters.end(), @@ -181,8 +183,8 @@ void FindBestBlockEntropyModel(const CompressParams& cparams, const ImageI& rqf, // for chroma, only use up to nb_clusters_chroma separate block contexts // (those for the biggest clusters) for (size_t i = remap.size(); i < remap.size() * 3; i++) { - ctx_map[i] = num + Clamp1((int)remap[i % remap.size()], 0, - (int)nb_clusters_chroma - 1); + ctx_map[i] = num + Clamp1(static_cast<int>(remap[i % remap.size()]), 0, + static_cast<int>(nb_clusters_chroma) - 1); } block_ctx_map->num_ctxs = *std::max_element(ctx_map.begin(), ctx_map.end()) + 1; @@ -190,9 +192,9 @@ void FindBestBlockEntropyModel(const CompressParams& cparams, const ImageI& rqf, namespace { -void FindBestDequantMatrices(const CompressParams& cparams, - ModularFrameEncoder* modular_frame_encoder, - DequantMatrices* dequant_matrices) { +Status FindBestDequantMatrices(const CompressParams& cparams, + ModularFrameEncoder* modular_frame_encoder, + DequantMatrices* dequant_matrices) { // TODO(veluca): quant matrices for no-gaborish. // TODO(veluca): heuristics for in-bitstream quant tables. *dequant_matrices = DequantMatrices(); @@ -204,13 +206,14 @@ void FindBestDequantMatrices(const CompressParams& cparams, DctQuantWeightParams dct_params(weights); std::vector<QuantEncoding> encodings(DequantMatrices::kNum, QuantEncoding::DCT(dct_params)); - DequantMatricesSetCustom(dequant_matrices, encodings, - modular_frame_encoder); + JXL_RETURN_IF_ERROR(DequantMatricesSetCustom(dequant_matrices, encodings, + modular_frame_encoder)); float dc_weights[3] = {1.0f / cparams.max_error[0], 1.0f / cparams.max_error[1], 1.0f / cparams.max_error[2]}; DequantMatricesSetCustomDC(dequant_matrices, dc_weights); } + return true; } void StoreMin2(const float v, float& min1, float& min2) { @@ -226,9 +229,9 @@ void StoreMin2(const float v, float& min1, float& min2) { void CreateMask(const ImageF& image, ImageF& mask) { for (size_t y = 0; y < image.ysize(); y++) { - auto* row_n = y > 0 ? image.Row(y - 1) : image.Row(y); - auto* row_in = image.Row(y); - auto* row_s = y + 1 < image.ysize() ? image.Row(y + 1) : image.Row(y); + const auto* row_n = y > 0 ? image.Row(y - 1) : image.Row(y); + const auto* row_in = image.Row(y); + const auto* row_s = y + 1 < image.ysize() ? image.Row(y + 1) : image.Row(y); auto* row_out = mask.Row(y); for (size_t x = 0; x < image.xsize(); x++) { // Center, west, east, north, south values and their absolute difference @@ -258,7 +261,7 @@ void CreateMask(const ImageF& image, ImageF& mask) { // by the decoder. Ringing is slightly reduced by clamping the values of the // resulting pixels within certain bounds of a small region in the original // image. -void DownsampleImage2_Sharper(const ImageF& input, ImageF* output) { +Status DownsampleImage2_Sharper(const ImageF& input, ImageF* output) { const int64_t kernelx = 12; const int64_t kernely = 12; @@ -315,11 +318,12 @@ void DownsampleImage2_Sharper(const ImageF& input, ImageF* output) { int64_t xsize = input.xsize(); int64_t ysize = input.ysize(); - ImageF box_downsample(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF box_downsample, ImageF::Create(xsize, ysize)); CopyImageTo(input, &box_downsample); - DownsampleImage(&box_downsample, 2); + JXL_ASSIGN_OR_RETURN(box_downsample, DownsampleImage(box_downsample, 2)); - ImageF mask(box_downsample.xsize(), box_downsample.ysize()); + JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(box_downsample.xsize(), + box_downsample.ysize())); CreateMask(box_downsample, mask); for (size_t y = 0; y < output->ysize(); y++) { @@ -379,50 +383,54 @@ void DownsampleImage2_Sharper(const ImageF& input, ImageF* output) { } } } + return true; } } // namespace -void DownsampleImage2_Sharper(Image3F* opsin) { +Status DownsampleImage2_Sharper(Image3F* opsin) { // Allocate extra space to avoid a reallocation when padding. - Image3F downsampled(DivCeil(opsin->xsize(), 2) + kBlockDim, - DivCeil(opsin->ysize(), 2) + kBlockDim); + JXL_ASSIGN_OR_RETURN(Image3F downsampled, + Image3F::Create(DivCeil(opsin->xsize(), 2) + kBlockDim, + DivCeil(opsin->ysize(), 2) + kBlockDim)); downsampled.ShrinkTo(downsampled.xsize() - kBlockDim, downsampled.ysize() - kBlockDim); for (size_t c = 0; c < 3; c++) { - DownsampleImage2_Sharper(opsin->Plane(c), &downsampled.Plane(c)); + JXL_RETURN_IF_ERROR( + DownsampleImage2_Sharper(opsin->Plane(c), &downsampled.Plane(c))); } *opsin = std::move(downsampled); + return true; } namespace { // The default upsampling kernels used by Upsampler in the decoder. -static const constexpr int64_t kSize = 5; +const constexpr int64_t kSize = 5; -static const float kernel00[25] = { +const float kernel00[25] = { -0.01716200f, -0.03452303f, -0.04022174f, -0.02921014f, -0.00624645f, -0.03452303f, 0.14111091f, 0.28896755f, 0.00278718f, -0.01610267f, -0.04022174f, 0.28896755f, 0.56661550f, 0.03777607f, -0.01986694f, -0.02921014f, 0.00278718f, 0.03777607f, -0.03144731f, -0.01185068f, -0.00624645f, -0.01610267f, -0.01986694f, -0.01185068f, -0.00213539f, }; -static const float kernel01[25] = { +const float kernel01[25] = { -0.00624645f, -0.01610267f, -0.01986694f, -0.01185068f, -0.00213539f, -0.02921014f, 0.00278718f, 0.03777607f, -0.03144731f, -0.01185068f, -0.04022174f, 0.28896755f, 0.56661550f, 0.03777607f, -0.01986694f, -0.03452303f, 0.14111091f, 0.28896755f, 0.00278718f, -0.01610267f, -0.01716200f, -0.03452303f, -0.04022174f, -0.02921014f, -0.00624645f, }; -static const float kernel10[25] = { +const float kernel10[25] = { -0.00624645f, -0.02921014f, -0.04022174f, -0.03452303f, -0.01716200f, -0.01610267f, 0.00278718f, 0.28896755f, 0.14111091f, -0.03452303f, -0.01986694f, 0.03777607f, 0.56661550f, 0.28896755f, -0.04022174f, -0.01185068f, -0.03144731f, 0.03777607f, 0.00278718f, -0.02921014f, -0.00213539f, -0.01185068f, -0.01986694f, -0.01610267f, -0.00624645f, }; -static const float kernel11[25] = { +const float kernel11[25] = { -0.00213539f, -0.01185068f, -0.01986694f, -0.01610267f, -0.00624645f, -0.01185068f, -0.03144731f, 0.03777607f, 0.00278718f, -0.02921014f, -0.01986694f, 0.03777607f, 0.56661550f, 0.28896755f, -0.04022174f, @@ -435,14 +443,14 @@ static const float kernel11[25] = { // TODO(lode): use Upsampler instead. However, it requires pre-initialization // and padding on the left side of the image which requires refactoring the // other code using this. -static void UpsampleImage(const ImageF& input, ImageF* output) { +void UpsampleImage(const ImageF& input, ImageF* output) { int64_t xsize = input.xsize(); int64_t ysize = input.ysize(); int64_t xsize2 = output->xsize(); int64_t ysize2 = output->ysize(); for (int64_t y = 0; y < ysize2; y++) { for (int64_t x = 0; x < xsize2; x++) { - auto kernel = kernel00; + const auto* kernel = kernel00; if ((x & 1) && (y & 1)) { kernel = kernel11; } else if (x & 1) { @@ -492,7 +500,7 @@ static void UpsampleImage(const ImageF& input, ImageF* output) { // Returns the derivative of Upsampler, with respect to input pixel x2, y2, to // output pixel x, y (ignoring the clamping). float UpsamplerDeriv(int64_t x2, int64_t y2, int64_t x, int64_t y) { - auto kernel = kernel00; + const auto* kernel = kernel00; if ((x & 1) && (y & 1)) { kernel = kernel11; } else if (x & 1) { @@ -597,11 +605,9 @@ void ReduceRinging(const ImageF& initial, const ImageF& mask, ImageF& down) { float max = initial.Row(y)[x]; for (int64_t yi = -1; yi < 2; yi++) { for (int64_t xi = -1; xi < 2; xi++) { - int64_t x2 = (int64_t)x + xi; - int64_t y2 = (int64_t)y + yi; - if (x2 < 0 || y2 < 0 || x2 >= (int64_t)xsize2 || - y2 >= (int64_t)ysize2) - continue; + int64_t x2 = static_cast<int64_t>(x) + xi; + int64_t y2 = static_cast<int64_t>(y) + yi; + if (x2 < 0 || y2 < 0 || x2 >= xsize2 || y2 >= ysize2) continue; min = std::min<float>(min, initial.Row(y2)[x2]); max = std::max<float>(max, initial.Row(y2)[x2]); } @@ -625,32 +631,35 @@ void ReduceRinging(const ImageF& initial, const ImageF& mask, ImageF& down) { } // TODO(lode): move this to a separate file enc_downsample.cc -void DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) { +Status DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) { int64_t xsize = orig.xsize(); int64_t ysize = orig.ysize(); int64_t xsize2 = DivCeil(orig.xsize(), 2); int64_t ysize2 = DivCeil(orig.ysize(), 2); - ImageF box_downsample(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF box_downsample, ImageF::Create(xsize, ysize)); CopyImageTo(orig, &box_downsample); - DownsampleImage(&box_downsample, 2); - ImageF mask(box_downsample.xsize(), box_downsample.ysize()); + JXL_ASSIGN_OR_RETURN(box_downsample, DownsampleImage(box_downsample, 2)); + JXL_ASSIGN_OR_RETURN(ImageF mask, ImageF::Create(box_downsample.xsize(), + box_downsample.ysize())); CreateMask(box_downsample, mask); output->ShrinkTo(xsize2, ysize2); // Initial result image using the sharper downsampling. // Allocate extra space to avoid a reallocation when padding. - ImageF initial(DivCeil(orig.xsize(), 2) + kBlockDim, - DivCeil(orig.ysize(), 2) + kBlockDim); + JXL_ASSIGN_OR_RETURN(ImageF initial, + ImageF::Create(DivCeil(orig.xsize(), 2) + kBlockDim, + DivCeil(orig.ysize(), 2) + kBlockDim)); initial.ShrinkTo(initial.xsize() - kBlockDim, initial.ysize() - kBlockDim); - DownsampleImage2_Sharper(orig, &initial); + JXL_RETURN_IF_ERROR(DownsampleImage2_Sharper(orig, &initial)); - ImageF down(initial.xsize(), initial.ysize()); + JXL_ASSIGN_OR_RETURN(ImageF down, + ImageF::Create(initial.xsize(), initial.ysize())); CopyImageTo(initial, &down); - ImageF up(xsize, ysize); - ImageF corr(xsize, ysize); - ImageF corr2(xsize2, ysize2); + JXL_ASSIGN_OR_RETURN(ImageF up, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF corr, ImageF::Create(xsize, ysize)); + JXL_ASSIGN_OR_RETURN(ImageF corr2, ImageF::Create(xsize2, ysize2)); // In the weights map, relatively higher values will allow less ringing but // also less sharpness. With all constant values, it optimizes equally @@ -659,25 +668,25 @@ void DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) { // TODO(lode): Make use of the weights field for anti-ringing and clamping, // the values are all set to 1 for now, but it is intended to be used for // reducing ringing based on the mask, and taking clamping into account. - ImageF weights(xsize, ysize); + JXL_ASSIGN_OR_RETURN(ImageF weights, ImageF::Create(xsize, ysize)); for (size_t y = 0; y < weights.ysize(); y++) { auto* row = weights.Row(y); for (size_t x = 0; x < weights.xsize(); x++) { row[x] = 1; } } - ImageF weights2(xsize2, ysize2); + JXL_ASSIGN_OR_RETURN(ImageF weights2, ImageF::Create(xsize2, ysize2)); AntiUpsample(weights, &weights2); const size_t num_it = 3; for (size_t it = 0; it < num_it; ++it) { UpsampleImage(down, &up); - corr = LinComb<float>(1, orig, -1, up); + JXL_ASSIGN_OR_RETURN(corr, LinComb<float>(1, orig, -1, up)); ElwiseMul(corr, weights, &corr); AntiUpsample(corr, &corr2); ElwiseDiv(corr2, weights2, &corr2); - down = LinComb<float>(1, down, 1, corr2); + JXL_ASSIGN_OR_RETURN(down, LinComb<float>(1, down, 1, corr2)); } ReduceRinging(initial, mask, down); @@ -690,32 +699,40 @@ void DownsampleImage2_Iterative(const ImageF& orig, ImageF* output) { output->Row(y)[x] = v; } } + return true; } } // namespace -void DownsampleImage2_Iterative(Image3F* opsin) { +Status DownsampleImage2_Iterative(Image3F* opsin) { // Allocate extra space to avoid a reallocation when padding. - Image3F downsampled(DivCeil(opsin->xsize(), 2) + kBlockDim, - DivCeil(opsin->ysize(), 2) + kBlockDim); + JXL_ASSIGN_OR_RETURN(Image3F downsampled, + Image3F::Create(DivCeil(opsin->xsize(), 2) + kBlockDim, + DivCeil(opsin->ysize(), 2) + kBlockDim)); downsampled.ShrinkTo(downsampled.xsize() - kBlockDim, downsampled.ysize() - kBlockDim); - Image3F rgb(opsin->xsize(), opsin->ysize()); + JXL_ASSIGN_OR_RETURN(Image3F rgb, + Image3F::Create(opsin->xsize(), opsin->ysize())); OpsinParams opsin_params; // TODO(user): use the ones that are actually used opsin_params.Init(kDefaultIntensityTarget); OpsinToLinear(*opsin, Rect(rgb), nullptr, &rgb, opsin_params); - ImageF mask(opsin->xsize(), opsin->ysize()); + JXL_ASSIGN_OR_RETURN(ImageF mask, + ImageF::Create(opsin->xsize(), opsin->ysize())); ButteraugliParams butter_params; - ButteraugliComparator butter(rgb, butter_params); - butter.Mask(&mask); - ImageF mask_fuzzy(opsin->xsize(), opsin->ysize()); + JXL_ASSIGN_OR_RETURN(std::unique_ptr<ButteraugliComparator> butter, + ButteraugliComparator::Make(rgb, butter_params)); + JXL_RETURN_IF_ERROR(butter->Mask(&mask)); + JXL_ASSIGN_OR_RETURN(ImageF mask_fuzzy, + ImageF::Create(opsin->xsize(), opsin->ysize())); for (size_t c = 0; c < 3; c++) { - DownsampleImage2_Iterative(opsin->Plane(c), &downsampled.Plane(c)); + JXL_RETURN_IF_ERROR( + DownsampleImage2_Iterative(opsin->Plane(c), &downsampled.Plane(c))); } *opsin = std::move(downsampled); + return true; } Status LossyFrameHeuristics(const FrameHeader& frame_header, @@ -739,10 +756,11 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, BlockCtxMap& block_ctx_map = shared.block_ctx_map; // Find and subtract splines. + if (cparams.custom_splines.HasAny()) { + image_features.splines = cparams.custom_splines; + } if (!streaming_mode && cparams.speed_tier <= SpeedTier::kSquirrel) { - if (cparams.custom_splines.HasAny()) { - image_features.splines = cparams.custom_splines; - } else { + if (!cparams.custom_splines.HasAny()) { image_features.splines = FindSplines(*opsin); } JXL_RETURN_IF_ERROR(image_features.splines.InitializeDrawCache( @@ -754,7 +772,8 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, if (!streaming_mode && ApplyOverride(cparams.patches, cparams.speed_tier <= SpeedTier::kSquirrel)) { - FindBestPatchDictionary(*opsin, enc_state, cms, pool, aux_out); + JXL_RETURN_IF_ERROR( + FindBestPatchDictionary(*opsin, enc_state, cms, pool, aux_out)); PatchDictionaryEncoder::SubtractFrom(image_features.patches, opsin); } @@ -791,10 +810,12 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, // on simple heuristics in FindBestAcStrategy, or set a constant for Falcon // mode. if (cparams.speed_tier > SpeedTier::kHare) { - initial_quant_field = - ImageF(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - initial_quant_masking = - ImageF(frame_dim.xsize_blocks, frame_dim.ysize_blocks); + JXL_ASSIGN_OR_RETURN( + initial_quant_field, + ImageF::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + initial_quant_masking, + ImageF::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); float q = 0.79 / cparams.butteraugli_distance; FillImage(q, &initial_quant_field); FillImage(1.0f / (q + 0.001f), &initial_quant_masking); @@ -805,9 +826,11 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, if (!frame_header.loop_filter.gab) { butteraugli_distance_for_iqf *= 0.73f; } - initial_quant_field = InitialQuantField( - butteraugli_distance_for_iqf, *opsin, rect, pool, 1.0f, - &initial_quant_masking, &initial_quant_masking1x1); + JXL_ASSIGN_OR_RETURN( + initial_quant_field, + InitialQuantField(butteraugli_distance_for_iqf, *opsin, rect, pool, + 1.0f, &initial_quant_masking, + &initial_quant_masking1x1)); float q = 0.39 / cparams.butteraugli_distance; quantizer.ComputeGlobalScaleAndQuant(quant_dc, q, 0); } @@ -822,18 +845,21 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, 0.99406123118127299f, 0.99719338015886894f, }; - GaborishInverse(opsin, rect, weight, pool); + JXL_RETURN_IF_ERROR(GaborishInverse(opsin, rect, weight, pool)); } if (initialize_global_state) { - FindBestDequantMatrices(cparams, modular_frame_encoder, &matrices); + JXL_RETURN_IF_ERROR( + FindBestDequantMatrices(cparams, modular_frame_encoder, &matrices)); } - cfl_heuristics.Init(rect); + JXL_RETURN_IF_ERROR(cfl_heuristics.Init(rect)); acs_heuristics.Init(*opsin, rect, initial_quant_field, initial_quant_masking, initial_quant_masking1x1, &matrices); + std::atomic<bool> has_error{false}; auto process_tile = [&](const uint32_t tid, const size_t thread) { + if (has_error) return; size_t n_enc_tiles = DivCeil(frame_dim.xsize_blocks, kEncTileDimInBlocks); size_t tx = tid % n_enc_tiles; size_t ty = tid / n_enc_tiles; @@ -856,13 +882,16 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, } // Choose block sizes. - acs_heuristics.ProcessRect(r, cmap, &ac_strategy); + acs_heuristics.ProcessRect(r, cmap, &ac_strategy, thread); // Choose amount of post-processing smoothing. // TODO(veluca): should this go *after* AdjustQuantField? - ar_heuristics.RunRect(cparams, frame_header, r, *opsin, rect, - initial_quant_field, ac_strategy, &epf_sharpness, - thread); + if (!ar_heuristics.RunRect(cparams, frame_header, r, *opsin, rect, + initial_quant_field, ac_strategy, &epf_sharpness, + thread)) { + has_error = true; + return; + } // Always set the initial quant field, so we can compute the CfL map with // more accuracy. The initial quant field might change in slower modes, but @@ -884,18 +913,21 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, DivCeil(frame_dim.xsize_blocks, kEncTileDimInBlocks) * DivCeil(frame_dim.ysize_blocks, kEncTileDimInBlocks), [&](const size_t num_threads) { + acs_heuristics.PrepareForThreads(num_threads); ar_heuristics.PrepareForThreads(num_threads); cfl_heuristics.PrepareForThreads(num_threads); return true; }, process_tile, "Enc Heuristics")); + if (has_error) return JXL_FAILURE("Enc Heuristics failed"); - acs_heuristics.Finalize(frame_dim, ac_strategy, aux_out); + JXL_RETURN_IF_ERROR(acs_heuristics.Finalize(frame_dim, ac_strategy, aux_out)); // Refine quantization levels. if (!streaming_mode) { - FindBestQuantizer(frame_header, original_pixels, *opsin, - initial_quant_field, enc_state, cms, pool, aux_out); + JXL_RETURN_IF_ERROR(FindBestQuantizer(frame_header, original_pixels, *opsin, + initial_quant_field, enc_state, cms, + pool, aux_out)); } // Choose a context model that depends on the amount of quantization for AC. diff --git a/third_party/jpeg-xl/lib/jxl/enc_heuristics.h b/third_party/jpeg-xl/lib/jxl/enc_heuristics.h index 14cb596387..0dd93e4288 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_heuristics.h +++ b/third_party/jpeg-xl/lib/jxl/enc_heuristics.h @@ -38,8 +38,8 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, void FindBestBlockEntropyModel(PassesEncoderState& enc_state); -void DownsampleImage2_Iterative(Image3F* output); -void DownsampleImage2_Sharper(Image3F* opsin); +Status DownsampleImage2_Iterative(Image3F* opsin); +Status DownsampleImage2_Sharper(Image3F* opsin); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_huffman.cc b/third_party/jpeg-xl/lib/jxl/enc_huffman.cc index 3eab2c218a..6af92f6ed5 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_huffman.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_huffman.cc @@ -70,6 +70,7 @@ void StoreHuffmanTreeToBitMask(const size_t huffman_tree_size, for (size_t i = 0; i < huffman_tree_size; ++i) { size_t ix = huffman_tree[i]; writer->Write(code_length_bitdepth[ix], code_length_bitdepth_symbols[ix]); + JXL_ASSERT(ix <= 17); // Extra bits switch (ix) { case 16: @@ -78,6 +79,9 @@ void StoreHuffmanTreeToBitMask(const size_t huffman_tree_size, case 17: writer->Write(3, huffman_tree_extra_bits[i]); break; + default: + // no-op + break; } } } diff --git a/third_party/jpeg-xl/lib/jxl/enc_huffman_tree.cc b/third_party/jpeg-xl/lib/jxl/enc_huffman_tree.cc index 5c40dea770..82ae4bccc7 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_huffman_tree.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_huffman_tree.cc @@ -85,7 +85,8 @@ void CreateHuffmanTree(const uint32_t* data, const size_t length, size_t i = 0; // Points to the next leaf node. size_t j = n + 1; // Points to the next non-leaf node. for (size_t k = n - 1; k != 0; --k) { - size_t left, right; + size_t left; + size_t right; if (tree[i].total_count <= tree[j].total_count) { left = i; ++i; @@ -112,7 +113,7 @@ void CreateHuffmanTree(const uint32_t* data, const size_t length, tree.push_back(sentinel); } JXL_DASSERT(tree.size() == 2 * n + 1); - SetDepth(tree[2 * n - 1], &tree[0], depth, 0); + SetDepth(tree[2 * n - 1], tree.data(), depth, 0); // We need to pack the Huffman tree in tree_limit bits. // If this was not successful, add fake entities to the lowest values diff --git a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc index 8e92fe3452..a29fb3f299 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc @@ -13,6 +13,7 @@ #include <vector> #include "lib/jxl/base/byte_order.h" +#include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/enc_ans.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/fields.h" @@ -36,7 +37,8 @@ void Unshuffle(uint8_t* data, size_t size, size_t width) { size_t height = (size + width - 1) / width; // amount of rows of input PaddedBytes result(size); // i = input index, j output index - size_t s = 0, j = 0; + size_t s = 0; + size_t j = 0; for (size_t i = 0; i < size; i++) { result[j] = data[i]; j += height; @@ -71,7 +73,7 @@ Status PredictAndShuffle(size_t stride, size_t width, int order, size_t num, return true; } -static inline void EncodeVarInt(uint64_t value, PaddedBytes* data) { +inline void EncodeVarInt(uint64_t value, PaddedBytes* data) { size_t pos = data->size(); data->resize(data->size() + 9); size_t output_size = data->size(); @@ -83,13 +85,13 @@ static inline void EncodeVarInt(uint64_t value, PaddedBytes* data) { // TODO(eustas): should it be `<` ? JXL_CHECK(pos <= output_size); // |128: Set the next byte flag - output[pos++] = ((uint8_t)(value & 127)) | 128; + output[pos++] = (static_cast<uint8_t>(value & 127)) | 128; // Remove the seven bits we just wrote value >>= 7; } // TODO(eustas): should it be `<` ? JXL_CHECK(pos <= output_size); - output[pos++] = ((uint8_t)value) & 127; + output[pos++] = static_cast<uint8_t>(value & 127); data->resize(pos); } @@ -235,7 +237,9 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) { // allowed for tagged elements to overlap, e.g. the curve for R, G and B could // all point to the same one. Tag tag; - size_t tagstart = 0, tagsize = 0, clutstart = 0; + size_t tagstart = 0; + size_t tagsize = 0; + size_t clutstart = 0; // Should always check tag_sane before doing math with tagsize. const auto tag_sane = [&tagsize]() { @@ -292,7 +296,9 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) { commands_add.push_back(kCommandTypeStartFirst + 5); pos += 8; commands_add.push_back(kCommandPredict); - int order = 1, width = 2, stride = width; + int order = 1; + int width = 2; + int stride = width; commands_add.push_back((order << 2) | (width - 1)); EncodeVarInt(num, &commands_add); JXL_RETURN_IF_ERROR(PredictAndShuffle(stride, width, order, num, icc, @@ -310,7 +316,9 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) { pos += 12; last1 = pos; commands_add.push_back(kCommandPredict); - int order = 1, width = 2, stride = width; + int order = 1; + int width = 2; + int stride = width; commands_add.push_back((order << 2) | (width - 1)); EncodeVarInt(num, &commands_add); JXL_RETURN_IF_ERROR(PredictAndShuffle(stride, width, order, num, icc, @@ -352,7 +360,9 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) { if (commands_add.empty() && data_add.empty() && tag == kGbd_Tag && tag_sane() && pos == tagstart + 8 && pos + tagsize - 8 <= size && pos > 16) { - size_t width = 4, order = 0, stride = width; + size_t width = 4; + size_t order = 0; + size_t stride = width; size_t num = tagsize - 8; uint8_t flags = (order << 2) | (width - 1) | (stride == width ? 0 : 16); commands_add.push_back(kCommandPredict); diff --git a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h index 224c2e5316..a99e11b19c 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h +++ b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.h @@ -15,7 +15,6 @@ #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/enc_bit_writer.h" namespace jxl { @@ -27,9 +26,6 @@ class PaddedBytes; Status WriteICC(const std::vector<uint8_t>& icc, BitWriter* JXL_RESTRICT writer, size_t layer, AuxOut* JXL_RESTRICT aux_out); -// Exposed only for testing -Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result); - } // namespace jxl #endif // LIB_JXL_ENC_ICC_CODEC_H_ diff --git a/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc b/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc index 1b41361320..044f763363 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_image_bundle.cc @@ -8,13 +8,10 @@ #include <jxl/cms_interface.h> #include <atomic> -#include <limits> #include <utility> -#include "lib/jxl/alpha.h" -#include "lib/jxl/base/byte_order.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/fields.h" #include "lib/jxl/image_bundle.h" namespace jxl { @@ -30,11 +27,11 @@ Status ApplyColorTransform(const ColorEncoding& c_current, JXL_CHECK(c_current.IsGray() == c_desired.IsGray()); bool is_gray = c_current.IsGray(); if (out->xsize() < rect.xsize() || out->ysize() < rect.ysize()) { - *out = Image3F(rect.xsize(), rect.ysize()); + JXL_ASSIGN_OR_RETURN(*out, Image3F::Create(rect.xsize(), rect.ysize())); } else { out->ShrinkTo(rect.xsize(), rect.ysize()); } - std::atomic<bool> ok{true}; + std::atomic<bool> has_error{false}; JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, rect.ysize(), [&](const size_t num_threads) { @@ -42,6 +39,7 @@ Status ApplyColorTransform(const ColorEncoding& c_current, rect.xsize(), num_threads); }, [&](const uint32_t y, const size_t thread) { + if (has_error) return; float* mutable_src_buf = c_transform.BufSrc(thread); const float* src_buf = mutable_src_buf; // Interleave input. @@ -49,7 +47,7 @@ Status ApplyColorTransform(const ColorEncoding& c_current, src_buf = rect.ConstPlaneRow(color, 0, y); } else if (c_current.IsCMYK()) { if (!black) { - ok.store(false); + has_error = true; return; } const float* JXL_RESTRICT row_in0 = rect.ConstPlaneRow(color, 0, y); @@ -74,8 +72,8 @@ Status ApplyColorTransform(const ColorEncoding& c_current, } } float* JXL_RESTRICT dst_buf = c_transform.BufDst(thread); - if (!c_transform.Run(thread, src_buf, dst_buf)) { - ok.store(false); + if (!c_transform.Run(thread, src_buf, dst_buf, rect.xsize())) { + has_error = true; return; } float* JXL_RESTRICT row_out0 = out->PlaneRow(0, y); @@ -97,7 +95,8 @@ Status ApplyColorTransform(const ColorEncoding& c_current, } }, "Colorspace transform")); - return ok.load(); + if (has_error) return JXL_FAILURE("Colorspace transform failed"); + return true; } namespace { @@ -133,7 +132,8 @@ Status TransformIfNeeded(const ImageBundle& in, const ColorEncoding& c_desired, } // TODO(janwas): avoid copying via createExternal+copyBackToIO // instead of copy+createExternal+copyBackToIO - Image3F color(in.color().xsize(), in.color().ysize()); + JXL_ASSIGN_OR_RETURN(Image3F color, + Image3F::Create(in.color().xsize(), in.color().ysize())); CopyImageTo(in.color(), &color); store->SetFromImage(std::move(color), in.c_current()); @@ -141,7 +141,8 @@ Status TransformIfNeeded(const ImageBundle& in, const ColorEncoding& c_desired, if (in.HasExtraChannels()) { std::vector<ImageF> extra_channels; for (const ImageF& extra_channel : in.extra_channels()) { - ImageF ec(extra_channel.xsize(), extra_channel.ysize()); + JXL_ASSIGN_OR_RETURN(ImageF ec, ImageF::Create(extra_channel.xsize(), + extra_channel.ysize())); CopyImageTo(extra_channel, &ec); extra_channels.emplace_back(std::move(ec)); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_linalg.cc b/third_party/jpeg-xl/lib/jxl/enc_linalg.cc index fe2090a909..452c17f4e8 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_linalg.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_linalg.cc @@ -7,46 +7,43 @@ #include <cmath> -#include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/status.h" namespace jxl { -void ConvertToDiagonal(const ImageD& A, ImageD* const JXL_RESTRICT diag, - ImageD* const JXL_RESTRICT U) { +void ConvertToDiagonal(const Matrix2x2& A, Vector2& diag, Matrix2x2& U) { #if JXL_ENABLE_ASSERT - JXL_ASSERT(A.xsize() == 2); - JXL_ASSERT(A.ysize() == 2); - JXL_ASSERT(std::abs(A.Row(0)[1] - A.Row(1)[0]) < 1e-15); + // Check A is symmetric. + JXL_ASSERT(std::abs(A[0][1] - A[1][0]) < 1e-15); #endif - if (std::abs(A.ConstRow(0)[1]) < 1e-15) { + if (std::abs(A[0][1]) < 1e-15) { // Already diagonal. - diag->Row(0)[0] = A.ConstRow(0)[0]; - diag->Row(0)[1] = A.ConstRow(1)[1]; - U->Row(0)[0] = U->Row(1)[1] = 1.0; - U->Row(0)[1] = U->Row(1)[0] = 0.0; + diag[0] = A[0][0]; + diag[1] = A[1][1]; + U[0][0] = U[1][1] = 1.0; + U[0][1] = U[1][0] = 0.0; return; } - double b = -(A.Row(0)[0] + A.Row(1)[1]); - double c = A.Row(0)[0] * A.Row(1)[1] - A.Row(0)[1] * A.Row(0)[1]; + double b = -(A[0][0] + A[1][1]); + double c = A[0][0] * A[1][1] - A[0][1] * A[0][1]; double d = b * b - 4.0 * c; double sqd = std::sqrt(d); double l1 = (-b - sqd) * 0.5; double l2 = (-b + sqd) * 0.5; - double v1[2] = {A.Row(0)[0] - l1, A.Row(1)[0]}; + Vector2 v1 = {A[0][0] - l1, A[1][0]}; double v1n = 1.0 / std::hypot(v1[0], v1[1]); v1[0] = v1[0] * v1n; v1[1] = v1[1] * v1n; - diag->Row(0)[0] = l1; - diag->Row(0)[1] = l2; + diag[0] = l1; + diag[1] = l2; - U->Row(0)[0] = v1[1]; - U->Row(0)[1] = -v1[0]; - U->Row(1)[0] = v1[0]; - U->Row(1)[1] = v1[1]; + U[0][0] = v1[1]; + U[0][1] = -v1[0]; + U[1][0] = v1[0]; + U[1][1] = v1[1]; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_linalg.h b/third_party/jpeg-xl/lib/jxl/enc_linalg.h index 791770d5d4..b9a36c7ca1 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_linalg.h +++ b/third_party/jpeg-xl/lib/jxl/enc_linalg.h @@ -8,16 +8,16 @@ // Linear algebra. -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/image.h" +#include <array> namespace jxl { -using ImageD = Plane<double>; +typedef std::array<double, 2> Vector2; +// NB: matrix2x2[row][column] +typedef std::array<Vector2, 2> Matrix2x2; // A is symmetric, U is orthogonal, and A = U * Diagonal(diag) * Transpose(U). -void ConvertToDiagonal(const ImageD& A, ImageD* JXL_RESTRICT diag, - ImageD* JXL_RESTRICT U); +void ConvertToDiagonal(const Matrix2x2& A, Vector2& diag, Matrix2x2& U); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc b/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc index 967b9a3afb..c02f009ca7 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_linalg_test.cc @@ -5,110 +5,78 @@ #include "lib/jxl/enc_linalg.h" -#include "lib/jxl/image_test_utils.h" +#include "lib/jxl/base/random.h" #include "lib/jxl/testing.h" namespace jxl { namespace { -ImageD Identity(const size_t N) { - ImageD out(N, N); - for (size_t i = 0; i < N; ++i) { - double* JXL_RESTRICT row = out.Row(i); - std::fill(row, row + N, 0); - row[i] = 1.0; - } - return out; -} +Matrix2x2 Diagonal(const Vector2& d) { return {{{d[0], 0.0}, {0.0, d[1]}}}; } -ImageD Diagonal(const ImageD& d) { - JXL_ASSERT(d.ysize() == 1); - ImageD out(d.xsize(), d.xsize()); - const double* JXL_RESTRICT row_diag = d.Row(0); - for (size_t k = 0; k < d.xsize(); ++k) { - double* JXL_RESTRICT row_out = out.Row(k); - std::fill(row_out, row_out + d.xsize(), 0.0); - row_out[k] = row_diag[k]; - } - return out; -} +Matrix2x2 Identity() { return Diagonal({1.0, 1.0}); } -ImageD MatMul(const ImageD& A, const ImageD& B) { - JXL_ASSERT(A.ysize() == B.xsize()); - ImageD out(A.xsize(), B.ysize()); - for (size_t y = 0; y < B.ysize(); ++y) { - const double* const JXL_RESTRICT row_b = B.Row(y); - double* const JXL_RESTRICT row_out = out.Row(y); - for (size_t x = 0; x < A.xsize(); ++x) { - row_out[x] = 0.0; - for (size_t k = 0; k < B.xsize(); ++k) { - row_out[x] += A.Row(k)[x] * row_b[k]; - } +Matrix2x2 MatMul(const Matrix2x2& A, const Matrix2x2& B) { + Matrix2x2 out; + for (size_t y = 0; y < 2; ++y) { + for (size_t x = 0; x < 2; ++x) { + out[y][x] = A[0][x] * B[y][0] + A[1][x] * B[y][1]; } } return out; } -ImageD Transpose(const ImageD& A) { - ImageD out(A.ysize(), A.xsize()); - for (size_t x = 0; x < A.xsize(); ++x) { - double* const JXL_RESTRICT row_out = out.Row(x); - for (size_t y = 0; y < A.ysize(); ++y) { - row_out[y] = A.Row(y)[x]; - } - } - return out; +Matrix2x2 Transpose(const Matrix2x2& A) { + return {{{A[0][0], A[1][0]}, {A[0][1], A[1][1]}}}; } -ImageD RandomSymmetricMatrix(const size_t N, Rng& rng, const double vmin, - const double vmax) { - ImageD A(N, N); - GenerateImage(rng, &A, vmin, vmax); - for (size_t i = 0; i < N; ++i) { - for (size_t j = 0; j < i; ++j) { - A.Row(j)[i] = A.Row(i)[j]; - } - } +Matrix2x2 RandomSymmetricMatrix(Rng& rng, const double vmin, + const double vmax) { + Matrix2x2 A; + A[0][0] = rng.UniformF(vmin, vmax); + A[0][1] = A[1][0] = rng.UniformF(vmin, vmax); + A[1][1] = rng.UniformF(vmin, vmax); return A; } -void VerifyMatrixEqual(const ImageD& A, const ImageD& B, const double eps) { - ASSERT_EQ(A.xsize(), B.xsize()); - ASSERT_EQ(A.ysize(), B.ysize()); - for (size_t y = 0; y < A.ysize(); ++y) { - for (size_t x = 0; x < A.xsize(); ++x) { - ASSERT_NEAR(A.Row(y)[x], B.Row(y)[x], eps); +void VerifyMatrixEqual(const Matrix2x2& A, const Matrix2x2& B, + const double eps) { + for (size_t y = 0; y < 2; ++y) { + for (size_t x = 0; x < 2; ++x) { + ASSERT_NEAR(A[y][x], B[y][x], eps); } } } -void VerifyOrthogonal(const ImageD& A, const double eps) { - VerifyMatrixEqual(Identity(A.xsize()), MatMul(Transpose(A), A), eps); +void VerifyOrthogonal(const Matrix2x2& A, const double eps) { + VerifyMatrixEqual(Identity(), MatMul(Transpose(A), A), eps); } TEST(LinAlgTest, ConvertToDiagonal) { { - ImageD I = Identity(2); - ImageD U(2, 2), d(2, 1); - ConvertToDiagonal(I, &d, &U); + Matrix2x2 I = Identity(); + Matrix2x2 U; + Vector2 d; + ConvertToDiagonal(I, d, U); VerifyMatrixEqual(I, U, 1e-15); for (size_t k = 0; k < 2; ++k) { - ASSERT_NEAR(d.Row(0)[k], 1.0, 1e-15); + ASSERT_NEAR(d[k], 1.0, 1e-15); } } { - ImageD A = Identity(2); - A.Row(0)[1] = A.Row(1)[0] = 2.0; - ImageD U(2, 2), d(2, 1); - ConvertToDiagonal(A, &d, &U); + Matrix2x2 A = Identity(); + A[0][1] = A[1][0] = 2.0; + Matrix2x2 U; + Vector2 d; + ConvertToDiagonal(A, d, U); VerifyOrthogonal(U, 1e-12); VerifyMatrixEqual(A, MatMul(U, MatMul(Diagonal(d), Transpose(U))), 1e-12); } Rng rng(0); for (size_t i = 0; i < 100; ++i) { - ImageD A = RandomSymmetricMatrix(2, rng, -1.0, 1.0); - ImageD U(2, 2), d(2, 1); - ConvertToDiagonal(A, &d, &U); + Matrix2x2 A = RandomSymmetricMatrix(rng, -1.0, 1.0); + Matrix2x2 U; + Vector2 d; + ConvertToDiagonal(A, d, U); VerifyOrthogonal(U, 1e-12); VerifyMatrixEqual(A, MatMul(U, MatMul(Diagonal(d), Transpose(U))), 1e-12); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_modular.cc b/third_party/jpeg-xl/lib/jxl/enc_modular.cc index b8366953b7..dbd62d4a01 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_modular.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_modular.cc @@ -10,8 +10,8 @@ #include <array> #include <atomic> +#include <cstdint> #include <limits> -#include <queue> #include <utility> #include <vector> @@ -28,9 +28,9 @@ #include "lib/jxl/enc_params.h" #include "lib/jxl/enc_patch_dictionary.h" #include "lib/jxl/enc_quant_weights.h" +#include "lib/jxl/frame_dimensions.h" #include "lib/jxl/frame_header.h" #include "lib/jxl/modular/encoding/context_predict.h" -#include "lib/jxl/modular/encoding/enc_debug_tree.h" #include "lib/jxl/modular/encoding/enc_encoding.h" #include "lib/jxl/modular/encoding/encoding.h" #include "lib/jxl/modular/encoding/ma_common.h" @@ -38,7 +38,7 @@ #include "lib/jxl/modular/options.h" #include "lib/jxl/modular/transform/enc_transform.h" #include "lib/jxl/pack_signed.h" -#include "lib/jxl/toc.h" +#include "modular/options.h" namespace jxl { @@ -48,15 +48,15 @@ namespace { // Squeeze default quantization factors // these quantization factors are for -Q 50 (other qualities simply scale the // factors; things are rounded down and obviously cannot get below 1) -static const float squeeze_quality_factor = +const float squeeze_quality_factor = 0.35; // for easy tweaking of the quality range (decrease this number for // higher quality) -static const float squeeze_luma_factor = +const float squeeze_luma_factor = 1.1; // for easy tweaking of the balance between luma (or anything // non-chroma) and chroma (decrease this number for higher quality // luma) -static const float squeeze_quality_factor_xyb = 2.4f; -static const float squeeze_xyb_qtable[3][16] = { +const float squeeze_quality_factor_xyb = 2.4f; +const float squeeze_xyb_qtable[3][16] = { {163.84, 81.92, 40.96, 20.48, 10.24, 5.12, 2.56, 1.28, 0.64, 0.32, 0.16, 0.08, 0.04, 0.02, 0.01, 0.005}, // Y {1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0.5, 0.5, 0.5, 0.5, @@ -65,12 +65,12 @@ static const float squeeze_xyb_qtable[3][16] = { 0.5}, // B-Y }; -static const float squeeze_luma_qtable[16] = { - 163.84, 81.92, 40.96, 20.48, 10.24, 5.12, 2.56, 1.28, - 0.64, 0.32, 0.16, 0.08, 0.04, 0.02, 0.01, 0.005}; +const float squeeze_luma_qtable[16] = {163.84, 81.92, 40.96, 20.48, 10.24, 5.12, + 2.56, 1.28, 0.64, 0.32, 0.16, 0.08, + 0.04, 0.02, 0.01, 0.005}; // for 8-bit input, the range of YCoCg chroma is -255..255 so basically this // does 4:2:0 subsampling (two most fine grained layers get quantized away) -static const float squeeze_chroma_qtable[16] = { +const float squeeze_chroma_qtable[16] = { 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1, 0.5, 0.5, 0.5, 0.5, 0.5}; // Merges the trees in `trees` using nodes that decide on stream_id, as defined @@ -139,10 +139,12 @@ Status float_to_int(const float* const row_in, pixel_type* const row_out, } if (bits == 32 && fp) { JXL_ASSERT(exp_bits == 8); - memcpy((void*)row_out, (const void*)row_in, 4 * xsize); + memcpy(static_cast<void*>(row_out), static_cast<const void*>(row_in), + 4 * xsize); return true; } + JXL_ASSERT(bits > 0); int exp_bias = (1 << (exp_bits - 1)) - 1; int max_exp = (1 << exp_bits) - 1; uint32_t sign = (1u << (bits - 1)); @@ -186,14 +188,144 @@ Status float_to_int(const float* const row_in, pixel_type* const row_out, f = (signbit ? sign : 0); f |= (exp << mant_bits); f |= mantissa; - row_out[x] = (pixel_type)f; + row_out[x] = static_cast<pixel_type>(f); } return true; } + +float EstimateWPCost(const Image& img, size_t i) { + size_t extra_bits = 0; + float histo_cost = 0; + HybridUintConfig config; + int32_t cutoffs[] = {-500, -392, -255, -191, -127, -95, -63, -47, -31, + -23, -15, -11, -7, -4, -3, -1, 0, 1, + 3, 5, 7, 11, 15, 23, 31, 47, 63, + 95, 127, 191, 255, 392, 500}; + constexpr size_t nc = sizeof(cutoffs) / sizeof(*cutoffs) + 1; + Histogram histo[nc] = {}; + weighted::Header wp_header; + PredictorMode(i, &wp_header); + for (const Channel& ch : img.channel) { + const intptr_t onerow = ch.plane.PixelsPerRow(); + weighted::State wp_state(wp_header, ch.w, ch.h); + Properties properties(1); + for (size_t y = 0; y < ch.h; y++) { + const pixel_type* JXL_RESTRICT r = ch.Row(y); + for (size_t x = 0; x < ch.w; x++) { + size_t offset = 0; + pixel_type_w left = (x ? r[x - 1] : y ? *(r + x - onerow) : 0); + pixel_type_w top = (y ? *(r + x - onerow) : left); + pixel_type_w topleft = (x && y ? *(r + x - 1 - onerow) : left); + pixel_type_w topright = + (x + 1 < ch.w && y ? *(r + x + 1 - onerow) : top); + pixel_type_w toptop = (y > 1 ? *(r + x - onerow - onerow) : top); + pixel_type guess = wp_state.Predict</*compute_properties=*/true>( + x, y, ch.w, top, left, topright, topleft, toptop, &properties, + offset); + size_t ctx = 0; + for (int c : cutoffs) { + ctx += (c >= properties[0]) ? 1 : 0; + } + pixel_type res = r[x] - guess; + uint32_t token; + uint32_t nbits; + uint32_t bits; + config.Encode(PackSigned(res), &token, &nbits, &bits); + histo[ctx].Add(token); + extra_bits += nbits; + wp_state.UpdateErrors(r[x], x, y, ch.w); + } + } + for (auto& h : histo) { + histo_cost += h.ShannonEntropy(); + h.Clear(); + } + } + return histo_cost + extra_bits; +} + +float EstimateCost(const Image& img) { + // TODO(veluca): consider SIMDfication of this code. + size_t extra_bits = 0; + float histo_cost = 0; + HybridUintConfig config; + uint32_t cutoffs[] = {0, 1, 3, 5, 7, 11, 15, 23, 31, + 47, 63, 95, 127, 191, 255, 392, 500}; + constexpr size_t nc = sizeof(cutoffs) / sizeof(*cutoffs) + 1; + Histogram histo[nc] = {}; + for (const Channel& ch : img.channel) { + const intptr_t onerow = ch.plane.PixelsPerRow(); + for (size_t y = 0; y < ch.h; y++) { + const pixel_type* JXL_RESTRICT r = ch.Row(y); + for (size_t x = 0; x < ch.w; x++) { + pixel_type_w left = (x ? r[x - 1] : y ? *(r + x - onerow) : 0); + pixel_type_w top = (y ? *(r + x - onerow) : left); + pixel_type_w topleft = (x && y ? *(r + x - 1 - onerow) : left); + size_t maxdiff = std::max(std::max(left, top), topleft) - + std::min(std::min(left, top), topleft); + size_t ctx = 0; + for (uint32_t c : cutoffs) { + ctx += (c > maxdiff) ? 1 : 0; + } + pixel_type res = r[x] - ClampedGradient(top, left, topleft); + uint32_t token; + uint32_t nbits; + uint32_t bits; + config.Encode(PackSigned(res), &token, &nbits, &bits); + histo[ctx].Add(token); + extra_bits += nbits; + } + } + for (auto& h : histo) { + histo_cost += h.ShannonEntropy(); + h.Clear(); + } + } + return histo_cost + extra_bits; +} + +bool do_transform(Image& image, const Transform& tr, + const weighted::Header& wp_header, + jxl::ThreadPool* pool = nullptr, bool force_jxlart = false) { + Transform t = tr; + bool did_it = true; + if (force_jxlart) { + if (!t.MetaApply(image)) return false; + } else { + did_it = TransformForward(t, image, wp_header, pool); + } + if (did_it) image.transform.push_back(t); + return did_it; +} + +bool maybe_do_transform(Image& image, const Transform& tr, + const CompressParams& cparams, + const weighted::Header& wp_header, + jxl::ThreadPool* pool = nullptr, + bool force_jxlart = false) { + if (force_jxlart || cparams.speed_tier >= SpeedTier::kSquirrel) { + return do_transform(image, tr, wp_header, pool, force_jxlart); + } + float cost_before = EstimateCost(image); + bool did_it = do_transform(image, tr, wp_header, pool); + if (did_it) { + float cost_after = EstimateCost(image); + JXL_DEBUG_V(7, "Cost before: %f cost after: %f", cost_before, cost_after); + if (cost_after > cost_before) { + Transform t = image.transform.back(); + JXL_RETURN_IF_ERROR(t.Inverse(image, wp_header, pool)); + image.transform.pop_back(); + did_it = false; + } + } + return did_it; +} + } // namespace ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header, - const CompressParams& cparams_orig) + const CompressParams& cparams_orig, + bool streaming_mode) : frame_dim_(frame_header.ToFrameDimensions()), cparams_(cparams_orig) { size_t num_streams = ModularStreamId::Num(frame_dim_, frame_header.passes.num_passes); @@ -253,10 +385,16 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header, // Same, but for the non-Squeeze case. prop_order = {0, 1, 15, 9, 10, 11, 12, 13, 14, 2, 3, 4, 5, 6, 7, 8}; // if few groups, don't use group as a property - if (num_streams < 30 && cparams_.speed_tier > SpeedTier::kTortoise) { + if (num_streams < 30 && cparams_.speed_tier > SpeedTier::kTortoise && + cparams_orig.ModularPartIsLossless()) { prop_order.erase(prop_order.begin() + 1); } } + int max_properties = std::min<int>( + cparams_.options.max_properties, + static_cast<int>( + frame_header.nonserialized_metadata->m.num_extra_channels) + + (frame_header.encoding == FrameEncoding::kModular ? 2 : -1)); switch (cparams_.speed_tier) { case SpeedTier::kHare: cparams_.options.splitting_heuristics_properties.assign( @@ -278,6 +416,7 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header, prop_order.begin(), prop_order.begin() + 10); cparams_.options.max_property_values = 96; break; + case SpeedTier::kGlacier: case SpeedTier::kTortoise: cparams_.options.splitting_heuristics_properties = prop_order; cparams_.options.max_property_values = 256; @@ -290,24 +429,36 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header, } if (cparams_.speed_tier > SpeedTier::kTortoise) { // Gradient in previous channels. - for (int i = 0; i < cparams_.options.max_properties; i++) { + for (int i = 0; i < max_properties; i++) { cparams_.options.splitting_heuristics_properties.push_back( kNumNonrefProperties + i * 4 + 3); } } else { // All the extra properties in Tortoise mode. - for (int i = 0; i < cparams_.options.max_properties * 4; i++) { + for (int i = 0; i < max_properties * 4; i++) { cparams_.options.splitting_heuristics_properties.push_back( kNumNonrefProperties + i); } } } - if (cparams_.options.predictor == static_cast<Predictor>(-1)) { + if ((cparams_.options.predictor == Predictor::Average0 || + cparams_.options.predictor == Predictor::Average1 || + cparams_.options.predictor == Predictor::Average2 || + cparams_.options.predictor == Predictor::Average3 || + cparams_.options.predictor == Predictor::Average4 || + cparams_.options.predictor == Predictor::Weighted) && + !cparams_.ModularPartIsLossless()) { + // Lossy + Average/Weighted predictors does not work, so switch to default + // predictors. + cparams_.options.predictor = kUndefinedPredictor; + } + + if (cparams_.options.predictor == kUndefinedPredictor) { // no explicit predictor(s) given, set a good default - if ((cparams_.speed_tier <= SpeedTier::kTortoise || + if ((cparams_.speed_tier <= SpeedTier::kGlacier || cparams_.modular_mode == false) && - cparams_.IsLossless() && cparams_.responsive == false) { + cparams_.IsLossless() && cparams_.responsive == JXL_FALSE) { // TODO(veluca): allow all predictors that don't break residual // multipliers in lossy mode. cparams_.options.predictor = Predictor::Variable; @@ -354,48 +505,54 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header, // TODO(veluca): figure out how to use different predictor sets per channel. stream_options_.resize(num_streams, cparams_.options); -} -bool do_transform(Image& image, const Transform& tr, - const weighted::Header& wp_header, - jxl::ThreadPool* pool = nullptr, bool force_jxlart = false) { - Transform t = tr; - bool did_it = true; - if (force_jxlart) { - if (!t.MetaApply(image)) return false; - } else { - did_it = TransformForward(t, image, wp_header, pool); + stream_options_[0] = cparams_.options; + if (cparams_.speed_tier == SpeedTier::kFalcon) { + stream_options_[0].tree_kind = ModularOptions::TreeKind::kWPFixedDC; + } else if (cparams_.speed_tier == SpeedTier::kThunder) { + stream_options_[0].tree_kind = ModularOptions::TreeKind::kGradientFixedDC; } - if (did_it) image.transform.push_back(t); - return did_it; + stream_options_[0].histogram_params = + HistogramParams::ForModular(cparams_, {}, streaming_mode); } Status ModularFrameEncoder::ComputeEncodingData( const FrameHeader& frame_header, const ImageMetadata& metadata, Image3F* JXL_RESTRICT color, const std::vector<ImageF>& extra_channels, - PassesEncoderState* JXL_RESTRICT enc_state, const JxlCmsInterface& cms, - ThreadPool* pool, AuxOut* aux_out, bool do_color) { + const Rect& group_rect, const FrameDimensions& patch_dim, + const Rect& frame_area_rect, PassesEncoderState* JXL_RESTRICT enc_state, + const JxlCmsInterface& cms, ThreadPool* pool, AuxOut* aux_out, + bool do_color) { JXL_DEBUG_V(6, "Computing modular encoding data for frame %s", frame_header.DebugString().c_str()); - if (do_color && frame_header.loop_filter.gab) { + bool groupwise = enc_state->streaming_mode; + + if (do_color && frame_header.loop_filter.gab && !groupwise) { float w = 0.9908511000000001f; float weights[3] = {w, w, w}; - GaborishInverse(color, Rect(*color), weights, pool); + JXL_RETURN_IF_ERROR(GaborishInverse(color, Rect(*color), weights, pool)); } if (do_color && metadata.bit_depth.bits_per_sample <= 16 && cparams_.speed_tier < SpeedTier::kCheetah && - cparams_.decoding_speed_tier < 2) { - FindBestPatchDictionary(*color, enc_state, cms, nullptr, aux_out, - cparams_.color_transform == ColorTransform::kXYB); + cparams_.decoding_speed_tier < 2 && !groupwise) { + JXL_RETURN_IF_ERROR(FindBestPatchDictionary( + *color, enc_state, cms, nullptr, aux_out, + cparams_.color_transform == ColorTransform::kXYB)); PatchDictionaryEncoder::SubtractFrom( enc_state->shared.image_features.patches, color); } + if (cparams_.custom_splines.HasAny()) { + PassesSharedState& shared = enc_state->shared; + ImageFeatures& image_features = shared.image_features; + image_features.splines = cparams_.custom_splines; + } + // Convert ImageBundle to modular Image object - const size_t xsize = frame_dim_.xsize; - const size_t ysize = frame_dim_.ysize; + const size_t xsize = patch_dim.xsize; + const size_t ysize = patch_dim.ysize; int nb_chans = 3; if (metadata.color_encoding.IsGray() && @@ -423,7 +580,9 @@ Status ModularFrameEncoder::ComputeEncodingData( int max_bitdepth = do_color ? metadata.bit_depth.bits_per_sample + (fp ? 0 : 1) : 0; Image& gi = stream_images_[0]; - gi = Image(xsize, ysize, metadata.bit_depth.bits_per_sample, nb_chans); + JXL_ASSIGN_OR_RETURN( + gi, Image::Create(xsize, ysize, metadata.bit_depth.bits_per_sample, + nb_chans)); int c = 0; if (cparams_.color_transform == ColorTransform::kXYB && cparams_.modular_mode == true) { @@ -478,17 +637,21 @@ Status ModularFrameEncoder::ComputeEncodingData( gi.channel[c_out].vshift = frame_header.chroma_subsampling.VShift(c); size_t xsize_shifted = DivCeil(xsize, 1 << gi.channel[c_out].hshift); size_t ysize_shifted = DivCeil(ysize, 1 << gi.channel[c_out].vshift); - gi.channel[c_out].shrink(xsize_shifted, ysize_shifted); + JXL_RETURN_IF_ERROR( + gi.channel[c_out].shrink(xsize_shifted, ysize_shifted)); std::atomic<bool> has_error{false}; JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, ysize_shifted, ThreadPool::NoInit, [&](const int task, const int thread) { + if (has_error) return; const size_t y = task; - const float* const JXL_RESTRICT row_in = color->PlaneRow(c, y); + const float* const JXL_RESTRICT row_in = + color->PlaneRow(c, y + group_rect.y0()) + group_rect.x0(); pixel_type* const JXL_RESTRICT row_out = gi.channel[c_out].Row(y); if (!float_to_int(row_in, row_out, xsize_shifted, bits, exp_bits, fp, factor)) { has_error = true; + return; }; }, "float2int")); @@ -505,8 +668,9 @@ Status ModularFrameEncoder::ComputeEncodingData( for (size_t ec = 0; ec < extra_channels.size(); ec++, c++) { const ExtraChannelInfo& eci = metadata.extra_channel_info[ec]; size_t ecups = frame_header.extra_channel_upsampling[ec]; - gi.channel[c].shrink(DivCeil(frame_dim_.xsize_upsampled, ecups), - DivCeil(frame_dim_.ysize_upsampled, ecups)); + JXL_RETURN_IF_ERROR( + gi.channel[c].shrink(DivCeil(patch_dim.xsize_upsampled, ecups), + DivCeil(patch_dim.ysize_upsampled, ecups))); gi.channel[c].hshift = gi.channel[c].vshift = CeilLog2Nonzero(ecups) - CeilLog2Nonzero(frame_header.upsampling); @@ -519,12 +683,15 @@ Status ModularFrameEncoder::ComputeEncodingData( JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, gi.channel[c].plane.ysize(), ThreadPool::NoInit, [&](const int task, const int thread) { + if (has_error) return; const size_t y = task; - const float* const JXL_RESTRICT row_in = extra_channels[ec].Row(y); + const float* const JXL_RESTRICT row_in = + extra_channels[ec].Row(y + group_rect.y0()) + group_rect.x0(); pixel_type* const JXL_RESTRICT row_out = gi.channel[c].Row(y); if (!float_to_int(row_in, row_out, gi.channel[c].plane.xsize(), bits, exp_bits, fp, factor)) { has_error = true; + return; }; }, "float2int")); @@ -533,11 +700,12 @@ Status ModularFrameEncoder::ComputeEncodingData( JXL_ASSERT(c == nb_chans); int level_max_bitdepth = (cparams_.level == 5 ? 16 : 32); - if (max_bitdepth > level_max_bitdepth) + if (max_bitdepth > level_max_bitdepth) { return JXL_FAILURE( "Bitdepth too high for level %i (need %i bits, have only %i in this " "level)", cparams_.level, max_bitdepth, level_max_bitdepth); + } // Set options and apply transformations if (!cparams_.ModularPartIsLossless()) { @@ -553,14 +721,14 @@ Status ModularFrameEncoder::ComputeEncodingData( } // Global palette - if (cparams_.palette_colors != 0 || cparams_.lossy_palette) { + if ((cparams_.palette_colors != 0 || cparams_.lossy_palette) && !groupwise) { // all-channel palette (e.g. RGBA) if (gi.channel.size() - gi.nb_meta_channels > 1) { Transform maybe_palette(TransformId::kPalette); maybe_palette.begin_c = gi.nb_meta_channels; maybe_palette.num_c = gi.channel.size() - gi.nb_meta_channels; - maybe_palette.nb_colors = - std::min((int)(xsize * ysize / 2), std::abs(cparams_.palette_colors)); + maybe_palette.nb_colors = std::min(static_cast<int>(xsize * ysize / 2), + std::abs(cparams_.palette_colors)); maybe_palette.ordered_palette = cparams_.palette_colors >= 0; maybe_palette.lossy_palette = (cparams_.lossy_palette && maybe_palette.num_c == 3); @@ -569,8 +737,8 @@ Status ModularFrameEncoder::ComputeEncodingData( } // TODO(veluca): use a custom weighted header if using the weighted // predictor. - do_transform(gi, maybe_palette, weighted::Header(), pool, - cparams_.options.zero_tokens); + maybe_do_transform(gi, maybe_palette, cparams_, weighted::Header(), pool, + cparams_.options.zero_tokens); } // all-minus-one-channel palette (RGB with separate alpha, or CMY with // separate K) @@ -578,20 +746,20 @@ Status ModularFrameEncoder::ComputeEncodingData( Transform maybe_palette_3(TransformId::kPalette); maybe_palette_3.begin_c = gi.nb_meta_channels; maybe_palette_3.num_c = gi.channel.size() - gi.nb_meta_channels - 1; - maybe_palette_3.nb_colors = - std::min((int)(xsize * ysize / 3), std::abs(cparams_.palette_colors)); + maybe_palette_3.nb_colors = std::min(static_cast<int>(xsize * ysize / 3), + std::abs(cparams_.palette_colors)); maybe_palette_3.ordered_palette = cparams_.palette_colors >= 0; maybe_palette_3.lossy_palette = cparams_.lossy_palette; if (maybe_palette_3.lossy_palette) { maybe_palette_3.predictor = delta_pred_; } - do_transform(gi, maybe_palette_3, weighted::Header(), pool, - cparams_.options.zero_tokens); + maybe_do_transform(gi, maybe_palette_3, cparams_, weighted::Header(), + pool, cparams_.options.zero_tokens); } } // Global channel palette - if (cparams_.channel_colors_pre_transform_percent > 0 && + if (!groupwise && cparams_.channel_colors_pre_transform_percent > 0 && !cparams_.lossy_palette && (cparams_.speed_tier <= SpeedTier::kThunder || (do_color && metadata.bit_depth.bits_per_sample > 8))) { @@ -600,9 +768,10 @@ Status ModularFrameEncoder::ComputeEncodingData( int orig_bitdepth = max_bitdepth; max_bitdepth = 0; for (size_t i = 0; i < nb_channels; i++) { - int32_t min, max; + int32_t min; + int32_t max; compute_minmax(gi.channel[gi.nb_meta_channels + i], &min, &max); - int64_t colors = (int64_t)max - min + 1; + int64_t colors = static_cast<int64_t>(max) - min + 1; JXL_DEBUG_V(10, "Channel %" PRIuS ": range=%i..%i", i, min, max); Transform maybe_palette_1(TransformId::kPalette); maybe_palette_1.begin_c = i + gi.nb_meta_channels; @@ -612,9 +781,11 @@ Status ModularFrameEncoder::ComputeEncodingData( // (but only if the channel palette is less than 6% the size of the // image itself) maybe_palette_1.nb_colors = std::min( - (int)(xsize * ysize / 16), - (int)(cparams_.channel_colors_pre_transform_percent / 100. * colors)); - if (do_transform(gi, maybe_palette_1, weighted::Header(), pool)) { + static_cast<int>(xsize * ysize / 16), + static_cast<int>(cparams_.channel_colors_pre_transform_percent / + 100. * colors)); + if (maybe_do_transform(gi, maybe_palette_1, cparams_, weighted::Header(), + pool)) { // effective bit depth is lower, adjust quantization accordingly compute_minmax(gi.channel[gi.nb_meta_channels + i], &min, &max); if (max < maxval) maxval = max; @@ -646,8 +817,28 @@ Status ModularFrameEncoder::ComputeEncodingData( } } + if (cparams_.move_to_front_from_channel > 0) { + for (size_t tgt = 0; + tgt + cparams_.move_to_front_from_channel < gi.channel.size(); tgt++) { + size_t pos = cparams_.move_to_front_from_channel; + while (pos > 0) { + Transform move(TransformId::kRCT); + if (pos == 1) { + move.begin_c = tgt; + move.rct_type = 28; // RGB -> GRB + pos -= 1; + } else { + move.begin_c = tgt + pos - 2; + move.rct_type = 14; // RGB -> BRG + pos -= 2; + } + do_transform(gi, move, weighted::Header(), pool); + } + } + } + // don't do squeeze if we don't have some spare bits - if (cparams_.responsive && !gi.channel.empty() && + if (!groupwise && cparams_.responsive && !gi.channel.empty() && max_bitdepth + 2 < level_max_bitdepth) { Transform t(TransformId::kSqueeze); do_transform(gi, t, weighted::Header(), pool); @@ -674,8 +865,8 @@ Status ModularFrameEncoder::ComputeEncodingData( bitdepth_correction = maxval / 255.f; } std::vector<float> quantizers; - float dist = cparams_.butteraugli_distance; for (size_t i = 0; i < 3; i++) { + float dist = cparams_.butteraugli_distance; quantizers.push_back(quantizer * dist * bitdepth_correction); } for (size_t i = 0; i < extra_channels.size(); i++) { @@ -683,6 +874,7 @@ Status ModularFrameEncoder::ComputeEncodingData( metadata.extra_channel_info[i].bit_depth.bits_per_sample; pixel_type ec_maxval = ec_bitdepth < 32 ? (1u << ec_bitdepth) - 1 : 0; bitdepth_correction = ec_maxval / 255.f; + float dist = 0; if (i < cparams_.ec_distance.size()) dist = cparams_.ec_distance[i]; if (dist < 0) dist = cparams_.butteraugli_distance; quantizers.push_back(quantizer * dist * bitdepth_correction); @@ -722,57 +914,57 @@ Status ModularFrameEncoder::ComputeEncodingData( } // Fill other groups. - struct GroupParams { - Rect rect; - int minShift; - int maxShift; - ModularStreamId id; - }; - std::vector<GroupParams> stream_params; - - stream_options_[0] = cparams_.options; - // DC - for (size_t group_id = 0; group_id < frame_dim_.num_dc_groups; group_id++) { - const size_t gx = group_id % frame_dim_.xsize_dc_groups; - const size_t gy = group_id / frame_dim_.xsize_dc_groups; - const Rect rect(gx * frame_dim_.dc_group_dim, gy * frame_dim_.dc_group_dim, - frame_dim_.dc_group_dim, frame_dim_.dc_group_dim); + for (size_t group_id = 0; group_id < patch_dim.num_dc_groups; group_id++) { + const size_t rgx = group_id % patch_dim.xsize_dc_groups; + const size_t rgy = group_id / patch_dim.xsize_dc_groups; + const Rect rect(rgx * patch_dim.dc_group_dim, rgy * patch_dim.dc_group_dim, + patch_dim.dc_group_dim, patch_dim.dc_group_dim); + size_t gx = rgx + frame_area_rect.x0() / 2048; + size_t gy = rgy + frame_area_rect.y0() / 2048; + size_t real_group_id = gy * frame_dim_.xsize_dc_groups + gx; // minShift==3 because (frame_dim.dc_group_dim >> 3) == frame_dim.group_dim // maxShift==1000 is infinity - stream_params.push_back( - GroupParams{rect, 3, 1000, ModularStreamId::ModularDC(group_id)}); + stream_params_.push_back( + GroupParams{rect, 3, 1000, ModularStreamId::ModularDC(real_group_id)}); } // AC global -> nothing. // AC - for (size_t group_id = 0; group_id < frame_dim_.num_groups; group_id++) { - const size_t gx = group_id % frame_dim_.xsize_groups; - const size_t gy = group_id / frame_dim_.xsize_groups; - const Rect mrect(gx * frame_dim_.group_dim, gy * frame_dim_.group_dim, - frame_dim_.group_dim, frame_dim_.group_dim); + for (size_t group_id = 0; group_id < patch_dim.num_groups; group_id++) { + const size_t rgx = group_id % patch_dim.xsize_groups; + const size_t rgy = group_id / patch_dim.xsize_groups; + const Rect mrect(rgx * patch_dim.group_dim, rgy * patch_dim.group_dim, + patch_dim.group_dim, patch_dim.group_dim); + size_t gx = rgx + frame_area_rect.x0() / (frame_dim_.group_dim); + size_t gy = rgy + frame_area_rect.y0() / (frame_dim_.group_dim); + size_t real_group_id = gy * frame_dim_.xsize_groups + gx; for (size_t i = 0; i < enc_state->progressive_splitter.GetNumPasses(); i++) { - int maxShift, minShift; + int maxShift; + int minShift; frame_header.passes.GetDownsamplingBracket(i, minShift, maxShift); - stream_params.push_back(GroupParams{ - mrect, minShift, maxShift, ModularStreamId::ModularAC(group_id, i)}); + stream_params_.push_back( + GroupParams{mrect, minShift, maxShift, + ModularStreamId::ModularAC(real_group_id, i)}); } } // if there's only one group, everything ends up in GlobalModular // in that case, also try RCTs/WP params for the one group - if (stream_params.size() == 2) { - stream_params.push_back(GroupParams{Rect(0, 0, xsize, ysize), 0, 1000, - ModularStreamId::Global()}); + if (stream_params_.size() == 2) { + stream_params_.push_back(GroupParams{Rect(0, 0, xsize, ysize), 0, 1000, + ModularStreamId::Global()}); } gi_channel_.resize(stream_images_.size()); JXL_RETURN_IF_ERROR(RunOnPool( - pool, 0, stream_params.size(), ThreadPool::NoInit, + pool, 0, stream_params_.size(), ThreadPool::NoInit, [&](const uint32_t i, size_t /* thread */) { - stream_options_[stream_params[i].id.ID(frame_dim_)] = cparams_.options; + size_t stream = stream_params_[i].id.ID(frame_dim_); + stream_options_[stream] = stream_options_[0]; JXL_CHECK(PrepareStreamParams( - stream_params[i].rect, cparams_, stream_params[i].minShift, - stream_params[i].maxShift, stream_params[i].id, do_color)); + stream_params_[i].rect, cparams_, stream_params_[i].minShift, + stream_params_[i].maxShift, stream_params_[i].id, do_color, + groupwise)); }, "ChooseParams")); { @@ -821,7 +1013,7 @@ Status ModularFrameEncoder::ComputeTree(ThreadPool* pool) { StaticPropRange range; range[0] = {{i, i + 1}}; range[1] = {{stream_id, stream_id + 1}}; - multiplier_info.push_back({range, (uint32_t)q}); + multiplier_info.push_back({range, static_cast<uint32_t>(q)}); } else { // Previous channel in the same group had the same quantization // factor. Don't provide two different ranges, as that creates @@ -922,11 +1114,10 @@ Status ModularFrameEncoder::ComputeTree(ThreadPool* pool) { StaticPropRange range; range[0] = {{0, max_c}}; range[1] = {{start, stop}}; - auto local_multiplier_info = multiplier_info; tree_samples.PreQuantizeProperties( - range, local_multiplier_info, group_pixel_count, - channel_pixel_count, pixel_samples, diff_samples, + range, multiplier_info, group_pixel_count, channel_pixel_count, + pixel_samples, diff_samples, stream_options_[start].max_property_values); for (size_t i = start; i < stop; i++) { JXL_CHECK(ModularGenericCompress( @@ -937,7 +1128,7 @@ Status ModularFrameEncoder::ComputeTree(ThreadPool* pool) { // TODO(veluca): parallelize more. trees[chunk] = LearnTree(std::move(tree_samples), total_pixels, - stream_options_[start], local_multiplier_info, range); + stream_options_[start], multiplier_info, range); }, "LearnTrees")); if (invalid_force_wp.test_and_set(std::memory_order_acq_rel)) { @@ -966,7 +1157,7 @@ Status ModularFrameEncoder::ComputeTree(ThreadPool* pool) { tree_tokens_.resize(1); tree_tokens_[0].clear(); Tree decoded_tree; - TokenizeTree(tree_, &tree_tokens_[0], &decoded_tree); + TokenizeTree(tree_, tree_tokens_.data(), &decoded_tree); JXL_ASSERT(tree_.size() == decoded_tree.size()); tree_ = std::move(decoded_tree); @@ -1019,46 +1210,8 @@ Status ModularFrameEncoder::EncodeGlobalInfo(bool streaming_mode, allotment.ReclaimAndCharge(writer, kLayerModularTree, aux_out); // Write tree - HistogramParams params; - if (cparams_.speed_tier > SpeedTier::kKitten) { - params.clustering = HistogramParams::ClusteringType::kFast; - params.ans_histogram_strategy = - cparams_.speed_tier > SpeedTier::kThunder - ? HistogramParams::ANSHistogramStrategy::kFast - : HistogramParams::ANSHistogramStrategy::kApproximate; - params.lz77_method = - cparams_.decoding_speed_tier >= 3 && cparams_.modular_mode - ? (cparams_.speed_tier >= SpeedTier::kFalcon - ? HistogramParams::LZ77Method::kRLE - : HistogramParams::LZ77Method::kLZ77) - : HistogramParams::LZ77Method::kNone; - // Near-lossless DC, as well as modular mode, require choosing hybrid uint - // more carefully. - if ((!extra_dc_precision.empty() && extra_dc_precision[0] != 0) || - (cparams_.modular_mode && cparams_.speed_tier < SpeedTier::kCheetah)) { - params.uint_method = HistogramParams::HybridUintMethod::kFast; - } else { - params.uint_method = HistogramParams::HybridUintMethod::kNone; - } - } else if (cparams_.speed_tier <= SpeedTier::kTortoise) { - params.lz77_method = HistogramParams::LZ77Method::kOptimal; - } else { - params.lz77_method = HistogramParams::LZ77Method::kLZ77; - } - if (cparams_.decoding_speed_tier >= 1) { - params.max_histograms = 12; - } - if (cparams_.decoding_speed_tier >= 1 && cparams_.responsive) { - params.lz77_method = cparams_.speed_tier >= SpeedTier::kCheetah - ? HistogramParams::LZ77Method::kRLE - : cparams_.speed_tier >= SpeedTier::kKitten - ? HistogramParams::LZ77Method::kLZ77 - : HistogramParams::LZ77Method::kOptimal; - } - if (cparams_.decoding_speed_tier >= 2 && cparams_.responsive) { - params.uint_method = HistogramParams::HybridUintMethod::k000; - params.force_huffman = true; - } + HistogramParams params = + HistogramParams::ForModular(cparams_, extra_dc_precision, streaming_mode); { EntropyEncodingData tree_code; std::vector<uint8_t> tree_context_map; @@ -1082,6 +1235,7 @@ Status ModularFrameEncoder::EncodeStream(BitWriter* writer, AuxOut* aux_out, const ModularStreamId& stream) { size_t stream_id = stream.ID(frame_dim_); if (stream_images_[stream_id].channel.empty()) { + JXL_DEBUG_V(10, "Modular stream %" PRIuS " is empty.", stream_id); return true; // Image with no channels, header never gets decoded. } if (tokens_.empty()) { @@ -1103,113 +1257,44 @@ void ModularFrameEncoder::ClearStreamData(const ModularStreamId& stream) { std::swap(stream_images_[stream_id], empty_image); } -namespace { -float EstimateWPCost(const Image& img, size_t i) { - size_t extra_bits = 0; - float histo_cost = 0; - HybridUintConfig config; - int32_t cutoffs[] = {-500, -392, -255, -191, -127, -95, -63, -47, -31, - -23, -15, -11, -7, -4, -3, -1, 0, 1, - 3, 5, 7, 11, 15, 23, 31, 47, 63, - 95, 127, 191, 255, 392, 500}; - constexpr size_t nc = sizeof(cutoffs) / sizeof(*cutoffs) + 1; - Histogram histo[nc] = {}; - weighted::Header wp_header; - PredictorMode(i, &wp_header); - for (const Channel& ch : img.channel) { - const intptr_t onerow = ch.plane.PixelsPerRow(); - weighted::State wp_state(wp_header, ch.w, ch.h); - Properties properties(1); - for (size_t y = 0; y < ch.h; y++) { - const pixel_type* JXL_RESTRICT r = ch.Row(y); - for (size_t x = 0; x < ch.w; x++) { - size_t offset = 0; - pixel_type_w left = (x ? r[x - 1] : y ? *(r + x - onerow) : 0); - pixel_type_w top = (y ? *(r + x - onerow) : left); - pixel_type_w topleft = (x && y ? *(r + x - 1 - onerow) : left); - pixel_type_w topright = - (x + 1 < ch.w && y ? *(r + x + 1 - onerow) : top); - pixel_type_w toptop = (y > 1 ? *(r + x - onerow - onerow) : top); - pixel_type guess = wp_state.Predict</*compute_properties=*/true>( - x, y, ch.w, top, left, topright, topleft, toptop, &properties, - offset); - size_t ctx = 0; - for (int c : cutoffs) { - ctx += c >= properties[0]; - } - pixel_type res = r[x] - guess; - uint32_t token, nbits, bits; - config.Encode(PackSigned(res), &token, &nbits, &bits); - histo[ctx].Add(token); - extra_bits += nbits; - wp_state.UpdateErrors(r[x], x, y, ch.w); - } - } - for (size_t h = 0; h < nc; h++) { - histo_cost += histo[h].ShannonEntropy(); - histo[h].Clear(); - } +void ModularFrameEncoder::ClearModularStreamData() { + for (const auto& group : stream_params_) { + ClearStreamData(group.id); } - return histo_cost + extra_bits; + stream_params_.clear(); } -float EstimateCost(const Image& img) { - // TODO(veluca): consider SIMDfication of this code. - size_t extra_bits = 0; - float histo_cost = 0; - HybridUintConfig config; - uint32_t cutoffs[] = {0, 1, 3, 5, 7, 11, 15, 23, 31, - 47, 63, 95, 127, 191, 255, 392, 500}; - constexpr size_t nc = sizeof(cutoffs) / sizeof(*cutoffs) + 1; - Histogram histo[nc] = {}; - for (const Channel& ch : img.channel) { - const intptr_t onerow = ch.plane.PixelsPerRow(); - for (size_t y = 0; y < ch.h; y++) { - const pixel_type* JXL_RESTRICT r = ch.Row(y); - for (size_t x = 0; x < ch.w; x++) { - pixel_type_w left = (x ? r[x - 1] : y ? *(r + x - onerow) : 0); - pixel_type_w top = (y ? *(r + x - onerow) : left); - pixel_type_w topleft = (x && y ? *(r + x - 1 - onerow) : left); - size_t maxdiff = std::max(std::max(left, top), topleft) - - std::min(std::min(left, top), topleft); - size_t ctx = 0; - for (uint32_t c : cutoffs) { - ctx += c > maxdiff; - } - pixel_type res = r[x] - ClampedGradient(top, left, topleft); - uint32_t token, nbits, bits; - config.Encode(PackSigned(res), &token, &nbits, &bits); - histo[ctx].Add(token); - extra_bits += nbits; - } - } - for (size_t h = 0; h < nc; h++) { - histo_cost += histo[h].ShannonEntropy(); - histo[h].Clear(); - } - } - return histo_cost + extra_bits; +size_t ModularFrameEncoder::ComputeStreamingAbsoluteAcGroupId( + size_t dc_group_id, size_t ac_group_id, + const FrameDimensions& patch_dim) const { + size_t dc_group_x = dc_group_id % frame_dim_.xsize_dc_groups; + size_t dc_group_y = dc_group_id / frame_dim_.xsize_dc_groups; + size_t ac_group_x = ac_group_id % patch_dim.xsize_groups; + size_t ac_group_y = ac_group_id / patch_dim.xsize_groups; + return (dc_group_x * 8 + ac_group_x) + + (dc_group_y * 8 + ac_group_y) * frame_dim_.xsize_groups; } -} // namespace - Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, const CompressParams& cparams_, int minShift, int maxShift, const ModularStreamId& stream, - bool do_color) { + bool do_color, bool groupwise) { size_t stream_id = stream.ID(frame_dim_); Image& full_image = stream_images_[0]; const size_t xsize = rect.xsize(); const size_t ysize = rect.ysize(); Image& gi = stream_images_[stream_id]; if (stream_id > 0) { - gi = Image(xsize, ysize, full_image.bitdepth, 0); + JXL_ASSIGN_OR_RETURN(gi, + Image::Create(xsize, ysize, full_image.bitdepth, 0)); // start at the first bigger-than-frame_dim.group_dim non-metachannel size_t c = full_image.nb_meta_channels; - for (; c < full_image.channel.size(); c++) { - Channel& fc = full_image.channel[c]; - if (fc.w > frame_dim_.group_dim || fc.h > frame_dim_.group_dim) break; + if (!groupwise) { + for (; c < full_image.channel.size(); c++) { + Channel& fc = full_image.channel[c]; + if (fc.w > frame_dim_.group_dim || fc.h > frame_dim_.group_dim) break; + } } for (; c < full_image.channel.size(); c++) { Channel& fc = full_image.channel[c]; @@ -1220,7 +1305,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, rect.xsize() >> fc.hshift, rect.ysize() >> fc.vshift, fc.w, fc.h); if (r.xsize() == 0 || r.ysize() == 0) continue; gi_channel_[stream_id].push_back(c); - Channel gc(r.xsize(), r.ysize()); + JXL_ASSIGN_OR_RETURN(Channel gc, Channel::Create(r.xsize(), r.ysize())); gc.hshift = fc.hshift; gc.vshift = fc.vshift; for (size_t y = 0; y < r.ysize(); ++y) { @@ -1245,7 +1330,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, maybe_palette.num_c = gi.channel.size() - gi.nb_meta_channels; maybe_palette.nb_colors = std::abs(cparams_.palette_colors); maybe_palette.ordered_palette = cparams_.palette_colors >= 0; - do_transform(gi, maybe_palette, weighted::Header()); + maybe_do_transform(gi, maybe_palette, cparams_, weighted::Header()); } // all-minus-one-channel palette (RGB with separate alpha, or CMY with // separate K) @@ -1259,7 +1344,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, if (maybe_palette_3.lossy_palette) { maybe_palette_3.predictor = Predictor::Weighted; } - do_transform(gi, maybe_palette_3, weighted::Header()); + maybe_do_transform(gi, maybe_palette_3, cparams_, weighted::Header()); } } @@ -1271,9 +1356,10 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, // single channel palette (like FLIF's ChannelCompact) size_t nb_channels = gi.channel.size() - gi.nb_meta_channels; for (size_t i = 0; i < nb_channels; i++) { - int32_t min, max; + int32_t min; + int32_t max; compute_minmax(gi.channel[gi.nb_meta_channels + i], &min, &max); - int64_t colors = (int64_t)max - min + 1; + int64_t colors = static_cast<int64_t>(max) - min + 1; JXL_DEBUG_V(10, "Channel %" PRIuS ": range=%i..%i", i, min, max); Transform maybe_palette_1(TransformId::kPalette); maybe_palette_1.begin_c = i + gi.nb_meta_channels; @@ -1282,10 +1368,10 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, // actually occur, it is probably worth it to do a compaction // (but only if the channel palette is less than 80% the size of the // image itself) - maybe_palette_1.nb_colors = - std::min((int)(xsize * ysize * 0.8), - (int)(cparams_.channel_colors_percent / 100. * colors)); - do_transform(gi, maybe_palette_1, weighted::Header()); + maybe_palette_1.nb_colors = std::min( + static_cast<int>(xsize * ysize * 0.8), + static_cast<int>(cparams_.channel_colors_percent / 100. * colors)); + maybe_do_transform(gi, maybe_palette_1, cparams_, weighted::Header()); } } } @@ -1295,7 +1381,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, if (cparams_.color_transform == ColorTransform::kNone && cparams_.IsLossless() && cparams_.colorspace < 0 && gi.channel.size() - gi.nb_meta_channels >= 3 && - cparams_.responsive == false && do_color && + cparams_.responsive == JXL_FALSE && do_color && cparams_.speed_tier <= SpeedTier::kHare) { Transform sg(TransformId::kRCT); sg.begin_c = gi.nb_meta_channels; @@ -1319,6 +1405,7 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, case SpeedTier::kKitten: nb_rcts_to_try = 9; break; + case SpeedTier::kTectonicPlate: case SpeedTier::kGlacier: case SpeedTier::kTortoise: nb_rcts_to_try = 19; @@ -1403,11 +1490,11 @@ int QuantizeGradient(const int32_t* qrow, size_t onerow, size_t c, size_t x, return residual + pred.guess; } -void ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header, - const Image3F& dc, const Rect& r, - size_t group_index, bool nl_dc, - PassesEncoderState* enc_state, - bool jpeg_transcode) { +Status ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header, + const Image3F& dc, const Rect& r, + size_t group_index, bool nl_dc, + PassesEncoderState* enc_state, + bool jpeg_transcode) { extra_dc_precision[group_index] = nl_dc ? 1 : 0; float mul = 1 << extra_dc_precision[group_index]; @@ -1430,8 +1517,11 @@ void ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header, stream_options_[stream_id].tree_kind = ModularOptions::TreeKind::kGradientFixedDC; } + stream_options_[stream_id].histogram_params = + stream_options_[0].histogram_params; - stream_images_[stream_id] = Image(r.xsize(), r.ysize(), 8, 3); + JXL_ASSIGN_OR_RETURN(stream_images_[stream_id], + Image::Create(r.xsize(), r.ysize(), 8, 3)); if (nl_dc && stream_options_[stream_id].tree_kind == ModularOptions::TreeKind::kGradientFixedDC) { JXL_ASSERT(frame_header.chroma_subsampling.Is444()); @@ -1531,7 +1621,7 @@ void ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header, Channel& ch = stream_images_[stream_id].channel[c < 2 ? c ^ 1 : c]; ch.w = xs; ch.h = ys; - ch.shrink(); + JXL_RETURN_IF_ERROR(ch.shrink()); for (size_t y = 0; y < ys; y++) { int32_t* quant_row = ch.plane.Row(y); const float* row = rect.ConstPlaneRow(dc, c, y); @@ -1546,14 +1636,17 @@ void ModularFrameEncoder::AddVarDCTDC(const FrameHeader& frame_header, stream_images_[stream_id], enc_state->shared.quantizer.MulDC(), 1.0 / mul, enc_state->shared.cmap.DCFactors(), frame_header.chroma_subsampling, enc_state->shared.block_ctx_map); + return true; } -void ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index, - bool jpeg_transcode, - PassesEncoderState* enc_state) { +Status ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index, + bool jpeg_transcode, + PassesEncoderState* enc_state) { size_t stream_id = ModularStreamId::ACMetadata(group_index).ID(frame_dim_); stream_options_[stream_id].max_chan_size = 0xFFFFFF; - stream_options_[stream_id].wp_tree_mode = ModularOptions::TreeMode::kNoWP; + if (stream_options_[stream_id].predictor != Predictor::Weighted) { + stream_options_[stream_id].wp_tree_mode = ModularOptions::TreeMode::kNoWP; + } if (jpeg_transcode) { stream_options_[stream_id].tree_kind = ModularOptions::TreeKind::kJpegTranscodeACMeta; @@ -1569,14 +1662,19 @@ void ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index, cparams_.force_cfl_jpeg_recompression) { stream_options_[stream_id].tree_kind = ModularOptions::TreeKind::kLearn; } + stream_options_[stream_id].histogram_params = + stream_options_[0].histogram_params; // YToX, YToB, ACS + QF, EPF Image& image = stream_images_[stream_id]; - image = Image(r.xsize(), r.ysize(), 8, 4); + JXL_ASSIGN_OR_RETURN(image, Image::Create(r.xsize(), r.ysize(), 8, 4)); static_assert(kColorTileDimInBlocks == 8, "Color tile size changed"); Rect cr(r.x0() >> 3, r.y0() >> 3, (r.xsize() + 7) >> 3, (r.ysize() + 7) >> 3); - image.channel[0] = Channel(cr.xsize(), cr.ysize(), 3, 3); - image.channel[1] = Channel(cr.xsize(), cr.ysize(), 3, 3); - image.channel[2] = Channel(r.xsize() * r.ysize(), 2, 0, 0); + JXL_ASSIGN_OR_RETURN(image.channel[0], + Channel::Create(cr.xsize(), cr.ysize(), 3, 3)); + JXL_ASSIGN_OR_RETURN(image.channel[1], + Channel::Create(cr.xsize(), cr.ysize(), 3, 3)); + JXL_ASSIGN_OR_RETURN(image.channel[2], + Channel::Create(r.xsize() * r.ysize(), 2, 0, 0)); ConvertPlaneAndClamp(cr, enc_state->shared.cmap.ytox_map, Rect(image.channel[0].plane), &image.channel[0].plane); ConvertPlaneAndClamp(cr, enc_state->shared.cmap.ytob_map, @@ -1599,9 +1697,10 @@ void ModularFrameEncoder::AddACMetadata(const Rect& r, size_t group_index, } image.channel[2].w = num; ac_metadata_size[group_index] = num; + return true; } -void ModularFrameEncoder::EncodeQuantTable( +Status ModularFrameEncoder::EncodeQuantTable( size_t size_x, size_t size_y, BitWriter* writer, const QuantEncoding& encoding, size_t idx, ModularFrameEncoder* modular_frame_encoder) { @@ -1611,9 +1710,9 @@ void ModularFrameEncoder::EncodeQuantTable( if (modular_frame_encoder) { JXL_CHECK(modular_frame_encoder->EncodeStream( writer, nullptr, 0, ModularStreamId::QuantTable(idx))); - return; + return true; } - Image image(size_x, size_y, 8, 3); + JXL_ASSIGN_OR_RETURN(Image image, Image::Create(size_x, size_y, 8, 3)); for (size_t c = 0; c < 3; c++) { for (size_t y = 0; y < size_y; y++) { int32_t* JXL_RESTRICT row = image.channel[c].Row(y); @@ -1624,16 +1723,17 @@ void ModularFrameEncoder::EncodeQuantTable( } ModularOptions cfopts; JXL_CHECK(ModularGenericCompress(image, cfopts, writer)); + return true; } -void ModularFrameEncoder::AddQuantTable(size_t size_x, size_t size_y, - const QuantEncoding& encoding, - size_t idx) { +Status ModularFrameEncoder::AddQuantTable(size_t size_x, size_t size_y, + const QuantEncoding& encoding, + size_t idx) { size_t stream_id = ModularStreamId::QuantTable(idx).ID(frame_dim_); JXL_ASSERT(encoding.qraw.qtable != nullptr); JXL_ASSERT(size_x * size_y * 3 == encoding.qraw.qtable->size()); Image& image = stream_images_[stream_id]; - image = Image(size_x, size_y, 8, 3); + JXL_ASSIGN_OR_RETURN(image, Image::Create(size_x, size_y, 8, 3)); for (size_t c = 0; c < 3; c++) { for (size_t y = 0; y < size_y; y++) { int32_t* JXL_RESTRICT row = image.channel[c].Row(y); @@ -1642,5 +1742,6 @@ void ModularFrameEncoder::AddQuantTable(size_t size_x, size_t size_y, } } } + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_modular.h b/third_party/jpeg-xl/lib/jxl/enc_modular.h index 2158a781af..8e2015b226 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_modular.h +++ b/third_party/jpeg-xl/lib/jxl/enc_modular.h @@ -6,18 +6,29 @@ #ifndef LIB_JXL_ENC_MODULAR_H_ #define LIB_JXL_ENC_MODULAR_H_ +#include <jxl/cms_interface.h> + +#include <cstddef> #include <cstdint> +#include <vector> +#include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/status.h" #include "lib/jxl/dec_modular.h" +#include "lib/jxl/enc_ans.h" #include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_cache.h" #include "lib/jxl/enc_params.h" +#include "lib/jxl/frame_dimensions.h" #include "lib/jxl/frame_header.h" #include "lib/jxl/image.h" -#include "lib/jxl/image_bundle.h" +#include "lib/jxl/image_metadata.h" +#include "lib/jxl/modular/encoding/dec_ma.h" #include "lib/jxl/modular/encoding/encoding.h" #include "lib/jxl/modular/modular_image.h" +#include "lib/jxl/modular/options.h" +#include "lib/jxl/quant_weights.h" namespace jxl { @@ -26,14 +37,14 @@ struct AuxOut; class ModularFrameEncoder { public: ModularFrameEncoder(const FrameHeader& frame_header, - const CompressParams& cparams_orig); - Status ComputeEncodingData(const FrameHeader& frame_header, - const ImageMetadata& metadata, - Image3F* JXL_RESTRICT color, - const std::vector<ImageF>& extra_channels, - PassesEncoderState* JXL_RESTRICT enc_state, - const JxlCmsInterface& cms, ThreadPool* pool, - AuxOut* aux_out, bool do_color); + const CompressParams& cparams_orig, bool streaming_mode); + Status ComputeEncodingData( + const FrameHeader& frame_header, const ImageMetadata& metadata, + Image3F* JXL_RESTRICT color, const std::vector<ImageF>& extra_channels, + const Rect& group_rect, const FrameDimensions& patch_dim, + const Rect& frame_area_rect, PassesEncoderState* JXL_RESTRICT enc_state, + const JxlCmsInterface& cms, ThreadPool* pool, AuxOut* aux_out, + bool do_color); Status ComputeTree(ThreadPool* pool); Status ComputeTokens(ThreadPool* pool); // Encodes global info (tree + histograms) in the `writer`. @@ -43,28 +54,35 @@ class ModularFrameEncoder { // assigning bits to the provided `layer`. Status EncodeStream(BitWriter* writer, AuxOut* aux_out, size_t layer, const ModularStreamId& stream); + void ClearStreamData(const ModularStreamId& stream); + void ClearModularStreamData(); + size_t ComputeStreamingAbsoluteAcGroupId( + size_t dc_group_id, size_t ac_group_id, + const FrameDimensions& patch_dim) const; + // Creates a modular image for a given DC group of VarDCT mode. `dc` is the // input DC image, not quantized; the group is specified by `group_index`, and // `nl_dc` decides whether to apply a near-lossless processing to the DC or // not. - void AddVarDCTDC(const FrameHeader& frame_header, const Image3F& dc, - const Rect& r, size_t group_index, bool nl_dc, - PassesEncoderState* enc_state, bool jpeg_transcode); + Status AddVarDCTDC(const FrameHeader& frame_header, const Image3F& dc, + const Rect& r, size_t group_index, bool nl_dc, + PassesEncoderState* enc_state, bool jpeg_transcode); // Creates a modular image for the AC metadata of the given group // (`group_index`). - void AddACMetadata(const Rect& r, size_t group_index, bool jpeg_transcode, - PassesEncoderState* enc_state); + Status AddACMetadata(const Rect& r, size_t group_index, bool jpeg_transcode, + PassesEncoderState* enc_state); // Encodes a RAW quantization table in `writer`. If `modular_frame_encoder` is // null, the quantization table in `encoding` is used, with dimensions `size_x // x size_y`. Otherwise, the table with ID `idx` is encoded from the given // `modular_frame_encoder`. - static void EncodeQuantTable(size_t size_x, size_t size_y, BitWriter* writer, - const QuantEncoding& encoding, size_t idx, - ModularFrameEncoder* modular_frame_encoder); + static Status EncodeQuantTable(size_t size_x, size_t size_y, + BitWriter* writer, + const QuantEncoding& encoding, size_t idx, + ModularFrameEncoder* modular_frame_encoder); // Stores a quantization table for future usage with `EncodeQuantTable`. - void AddQuantTable(size_t size_x, size_t size_y, - const QuantEncoding& encoding, size_t idx); + Status AddQuantTable(size_t size_x, size_t size_y, + const QuantEncoding& encoding, size_t idx); std::vector<size_t> ac_metadata_size; std::vector<uint8_t> extra_dc_precision; @@ -72,7 +90,8 @@ class ModularFrameEncoder { private: Status PrepareStreamParams(const Rect& rect, const CompressParams& cparams, int minShift, int maxShift, - const ModularStreamId& stream, bool do_color); + const ModularStreamId& stream, bool do_color, + bool groupwise); std::vector<Image> stream_images_; std::vector<ModularOptions> stream_options_; std::vector<uint32_t> quants_; @@ -89,6 +108,14 @@ class ModularFrameEncoder { std::vector<std::vector<uint32_t>> gi_channel_; std::vector<size_t> image_widths_; Predictor delta_pred_ = Predictor::Average4; + + struct GroupParams { + Rect rect; + int minShift; + int maxShift; + ModularStreamId id; + }; + std::vector<GroupParams> stream_params_; }; } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_noise.cc b/third_party/jpeg-xl/lib/jxl/enc_noise.cc index a12a9e6dc4..80b90eed2c 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_noise.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_noise.cc @@ -114,7 +114,7 @@ class NoiseHistogram { private: template <typename T> T ClampX(const T x) const { - return std::min(std::max(T(0), x), T(kBins - 1)); + return std::min(std::max(static_cast<T>(0), x), static_cast<T>(kBins - 1)); } size_t Index(const float x) const { return ClampX(static_cast<int>(x)); } @@ -321,7 +321,7 @@ std::vector<NoiseLevel> GetNoiseLevel( void EncodeFloatParam(float val, float precision, BitWriter* writer) { JXL_ASSERT(val >= 0); - const int absval_quant = static_cast<int>(val * precision + 0.5f); + const int absval_quant = static_cast<int>(std::lround(val * precision)); JXL_ASSERT(absval_quant < (1 << 10)); writer->Write(10, absval_quant); } diff --git a/third_party/jpeg-xl/lib/jxl/enc_optimize_test.cc b/third_party/jpeg-xl/lib/jxl/enc_optimize_test.cc index cc65bf1a0c..9184765be2 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_optimize_test.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_optimize_test.cc @@ -12,7 +12,7 @@ namespace optimize { namespace { // The maximum number of iterations for the test. -static const size_t kMaxTestIter = 100000; +const size_t kMaxTestIter = 100000; // F(w) = (w - w_min)^2. struct SimpleQuadraticFunction { diff --git a/third_party/jpeg-xl/lib/jxl/enc_params.h b/third_party/jpeg-xl/lib/jxl/enc_params.h index 89fd2c924f..162c59d04c 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_params.h +++ b/third_party/jpeg-xl/lib/jxl/enc_params.h @@ -15,6 +15,7 @@ #include <vector> #include "lib/jxl/base/override.h" +#include "lib/jxl/common.h" #include "lib/jxl/enc_progressive_split.h" #include "lib/jxl/frame_dimensions.h" #include "lib/jxl/frame_header.h" @@ -24,36 +25,6 @@ namespace jxl { -enum class SpeedTier { - // Try multiple combinations of Tortoise flags for modular mode. Otherwise - // like kTortoise. - kGlacier = 0, - // Turns on FindBestQuantizationHQ loop. Equivalent to "guetzli" mode. - kTortoise = 1, - // Turns on FindBestQuantization butteraugli loop. - kKitten = 2, - // Turns on dots, patches, and spline detection by default, as well as full - // context clustering. Default. - kSquirrel = 3, - // Turns on error diffusion and full AC strategy heuristics. Equivalent to - // "fast" mode. - kWombat = 4, - // Turns on gaborish by default, non-default cmap, initial quant field. - kHare = 5, - // Turns on simple heuristics for AC strategy, quant field, and clustering; - // also enables coefficient reordering. - kCheetah = 6, - // Turns off most encoder features. Does context clustering. - // Modular: uses fixed tree with Weighted predictor. - kFalcon = 7, - // Currently fastest possible setting for VarDCT. - // Modular: uses fixed tree with Gradient predictor. - kThunder = 8, - // VarDCT: same as kThunder. - // Modular: no tree, Gradient predictor, fast histograms - kLightning = 9 -}; - // NOLINTNEXTLINE(clang-analyzer-optin.performance.Padding) struct CompressParams { float butteraugli_distance = 1.0f; @@ -106,7 +77,7 @@ struct CompressParams { int progressive_dc = -1; // If on: preserve color of invisible pixels (if off: don't care) - // Default: on for lossless, off for lossy + // Default: on Override keep_invisible = Override::kDefault; JxlCmsInterface cms; @@ -137,6 +108,7 @@ struct CompressParams { ModularOptions options; int responsive = -1; int colorspace = -1; + int move_to_front_from_channel = -1; // Use Global channel palette if #colors < this percentage of range float channel_colors_pre_transform_percent = 95.f; // Use Local channel palette if #colors < this percentage of range @@ -159,9 +131,6 @@ struct CompressParams { if (f > 0) return false; if (f < 0 && butteraugli_distance != 0) return false; } - // if no explicit ec_distance given, and using vardct, then the modular part - // is empty or not lossless - if (!modular_mode && ec_distance.empty()) return false; // all modular channels are encoded at distance 0 return true; } @@ -194,7 +163,7 @@ struct CompressParams { int level = -1; // See JXL_ENC_FRAME_SETTING_BUFFERING option value. - int buffering = 0; + int buffering = -1; // See JXL_ENC_FRAME_SETTING_USE_FULL_IMAGE_HEURISTICS option value. bool use_full_image_heuristics = true; diff --git a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc index 0abd177809..f19ba0dd9e 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc @@ -5,24 +5,22 @@ #include "lib/jxl/enc_patch_dictionary.h" +#include <jxl/types.h> #include <stdint.h> #include <stdlib.h> #include <sys/types.h> #include <algorithm> #include <atomic> -#include <string> -#include <tuple> #include <utility> #include <vector> -#include "lib/jxl/ans_params.h" #include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/override.h" +#include "lib/jxl/base/printf_macros.h" #include "lib/jxl/base/random.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/chroma_from_luma.h" #include "lib/jxl/dec_cache.h" #include "lib/jxl/dec_frame.h" #include "lib/jxl/enc_ans.h" @@ -31,7 +29,6 @@ #include "lib/jxl/enc_debug_image.h" #include "lib/jxl/enc_dot_dictionary.h" #include "lib/jxl/enc_frame.h" -#include "lib/jxl/entropy_coder.h" #include "lib/jxl/frame_header.h" #include "lib/jxl/image.h" #include "lib/jxl/image_bundle.h" @@ -100,7 +97,7 @@ void PatchDictionaryEncoder::Encode(const PatchDictionary& pdic, add_num(kPatchAlphaChannelContext, info.alpha_channel); } if (UsesClamp(info.mode)) { - add_num(kPatchClampContext, info.clamp); + add_num(kPatchClampContext, TO_JXL_BOOL(info.clamp)); } } } @@ -159,7 +156,7 @@ void PatchDictionaryEncoder::SubtractFrom(const PatchDictionary& pdic, // Nothing to do. } else { JXL_UNREACHABLE("Blending mode %u not yet implemented", - (uint32_t)mode); + static_cast<uint32_t>(mode)); } } } @@ -208,11 +205,12 @@ struct PatchColorspaceInfo { } }; -std::vector<PatchInfo> FindTextLikePatches( +StatusOr<std::vector<PatchInfo>> FindTextLikePatches( const CompressParams& cparams, const Image3F& opsin, const PassesEncoderState* JXL_RESTRICT state, ThreadPool* pool, AuxOut* aux_out, bool is_xyb) { - if (state->cparams.patches == Override::kOff) return {}; + std::vector<PatchInfo> info; + if (state->cparams.patches == Override::kOff) return info; const auto& frame_dim = state->shared.frame_dim; PatchColorspaceInfo pci(is_xyb); @@ -222,7 +220,8 @@ std::vector<PatchInfo> FindTextLikePatches( std::pair<uint32_t, uint32_t> p2, const float* JXL_RESTRICT rows[3], size_t stride, float threshold) { - float v1[3], v2[3]; + float v1[3]; + float v2[3]; for (size_t c = 0; c < 3; c++) { v1[c] = rows[c][p1.second * stride + p1.first]; v2[c] = rows[c][p2.second * stride + p2.first]; @@ -258,8 +257,9 @@ std::vector<PatchInfo> FindTextLikePatches( // Look for kPatchSide size squares, naturally aligned, that all have the same // pixel values. - ImageB is_screenshot_like(DivCeil(frame_dim.xsize, kPatchSide), - DivCeil(frame_dim.ysize, kPatchSide)); + JXL_ASSIGN_OR_RETURN(ImageB is_screenshot_like, + ImageB::Create(DivCeil(frame_dim.xsize, kPatchSide), + DivCeil(frame_dim.ysize, kPatchSide))); ZeroFillImage(&is_screenshot_like); uint8_t* JXL_RESTRICT screenshot_row = is_screenshot_like.Row(0); const size_t screenshot_stride = is_screenshot_like.PixelsPerRow(); @@ -302,19 +302,22 @@ std::vector<PatchInfo> FindTextLikePatches( // TODO(veluca): also parallelize the rest of this function. if (WantDebugOutput(cparams)) { - DumpPlaneNormalized(cparams, "screenshot_like", is_screenshot_like); + JXL_RETURN_IF_ERROR( + DumpPlaneNormalized(cparams, "screenshot_like", is_screenshot_like)); } constexpr int kSearchRadius = 1; if (!ApplyOverride(state->cparams.patches, has_screenshot_areas)) { - return {}; + return info; } // Search for "similar enough" pixels near the screenshot-like areas. - ImageB is_background(frame_dim.xsize, frame_dim.ysize); + JXL_ASSIGN_OR_RETURN(ImageB is_background, + ImageB::Create(frame_dim.xsize, frame_dim.ysize)); ZeroFillImage(&is_background); - Image3F background(frame_dim.xsize, frame_dim.ysize); + JXL_ASSIGN_OR_RETURN(Image3F background, + Image3F::Create(frame_dim.xsize, frame_dim.ysize)); ZeroFillImage(&background); constexpr size_t kDistanceLimit = 50; float* JXL_RESTRICT background_rows[3] = { @@ -383,13 +386,14 @@ std::vector<PatchInfo> FindTextLikePatches( Rng rng(0); bool paint_ccs = false; if (WantDebugOutput(cparams)) { - DumpPlaneNormalized(cparams, "is_background", is_background); + JXL_RETURN_IF_ERROR( + DumpPlaneNormalized(cparams, "is_background", is_background)); if (is_xyb) { - DumpXybImage(cparams, "background", background); + JXL_RETURN_IF_ERROR(DumpXybImage(cparams, "background", background)); } else { - DumpImage(cparams, "background", background); + JXL_RETURN_IF_ERROR(DumpImage(cparams, "background", background)); } - ccs = ImageF(frame_dim.xsize, frame_dim.ysize); + JXL_ASSIGN_OR_RETURN(ccs, ImageF::Create(frame_dim.xsize, frame_dim.ysize)); ZeroFillImage(&ccs); paint_ccs = true; } @@ -407,11 +411,10 @@ std::vector<PatchInfo> FindTextLikePatches( constexpr int kMinPeak = 2; constexpr int kHasSimilarRadius = 2; - std::vector<PatchInfo> info; - // Find small CC outside the "similar enough" areas, compute bounding boxes, // and run heuristics to exclude some patches. - ImageB visited(frame_dim.xsize, frame_dim.ysize); + JXL_ASSIGN_OR_RETURN(ImageB visited, + ImageB::Create(frame_dim.xsize, frame_dim.ysize)); ZeroFillImage(&visited); uint8_t* JXL_RESTRICT visited_row = visited.Row(0); const size_t visited_stride = visited.PixelsPerRow(); @@ -525,10 +528,10 @@ std::vector<PatchInfo> FindTextLikePatches( if (paint_ccs) { JXL_ASSERT(WantDebugOutput(cparams)); - DumpPlaneNormalized(cparams, "ccs", ccs); + JXL_RETURN_IF_ERROR(DumpPlaneNormalized(cparams, "ccs", ccs)); } if (info.empty()) { - return {}; + return info; } // Remove duplicates. @@ -560,19 +563,22 @@ std::vector<PatchInfo> FindTextLikePatches( // don't use patches if all patches are smaller than this constexpr size_t kMinMaxPatchSize = 20; - if (max_patch_size < kMinMaxPatchSize) return {}; + if (max_patch_size < kMinMaxPatchSize) { + info.clear(); + } return info; } } // namespace -void FindBestPatchDictionary(const Image3F& opsin, - PassesEncoderState* JXL_RESTRICT state, - const JxlCmsInterface& cms, ThreadPool* pool, - AuxOut* aux_out, bool is_xyb) { - std::vector<PatchInfo> info = - FindTextLikePatches(state->cparams, opsin, state, pool, aux_out, is_xyb); +Status FindBestPatchDictionary(const Image3F& opsin, + PassesEncoderState* JXL_RESTRICT state, + const JxlCmsInterface& cms, ThreadPool* pool, + AuxOut* aux_out, bool is_xyb) { + JXL_ASSIGN_OR_RETURN( + std::vector<PatchInfo> info, + FindTextLikePatches(state->cparams, opsin, state, pool, aux_out, is_xyb)); // TODO(veluca): this doesn't work if both dots and patches are enabled. // For now, since dots and patches are not likely to occur in the same kind of @@ -582,10 +588,13 @@ void FindBestPatchDictionary(const Image3F& opsin, state->cparams.dots, state->cparams.speed_tier <= SpeedTier::kSquirrel && state->cparams.butteraugli_distance >= kMinButteraugliForDots)) { - info = FindDotDictionary(state->cparams, opsin, state->shared.cmap, pool); + Rect rect(0, 0, state->shared.frame_dim.xsize, + state->shared.frame_dim.ysize); + JXL_ASSIGN_OR_RETURN(info, FindDotDictionary(state->cparams, opsin, rect, + state->shared.cmap, pool)); } - if (info.empty()) return; + if (info.empty()) return true; std::sort( info.begin(), info.end(), [&](const PatchInfo& a, const PatchInfo& b) { @@ -616,7 +625,7 @@ void FindBestPatchDictionary(const Image3F& opsin, ref_xsize = ref_xsize * kBinPackingSlackness + 1; ref_ysize = ref_ysize * kBinPackingSlackness + 1; - ImageB occupied(ref_xsize, ref_ysize); + JXL_ASSIGN_OR_RETURN(ImageB occupied, ImageB::Create(ref_xsize, ref_ysize)); ZeroFillImage(&occupied); uint8_t* JXL_RESTRICT occupied_rows = occupied.Row(0); size_t occupied_stride = occupied.PixelsPerRow(); @@ -667,7 +676,7 @@ void FindBestPatchDictionary(const Image3F& opsin, ref_positions[patch] = {x0, y0}; for (size_t y = y0; y < y0 + ysize; y++) { for (size_t x = x0; x < x0 + xsize; x++) { - occupied_rows[y * occupied_stride + x] = true; + occupied_rows[y * occupied_stride + x] = JXL_TRUE; } } max_y = std::max(max_y, y0 + ysize); @@ -680,7 +689,8 @@ void FindBestPatchDictionary(const Image3F& opsin, ref_ysize = max_y; - Image3F reference_frame(ref_xsize, ref_ysize); + JXL_ASSIGN_OR_RETURN(Image3F reference_frame, + Image3F::Create(ref_xsize, ref_ysize)); // TODO(veluca): figure out a better way to fill the image. ZeroFillImage(&reference_frame); std::vector<PatchPosition> positions; @@ -710,6 +720,8 @@ void FindBestPatchDictionary(const Image3F& opsin, } } for (const auto& pos : info[i].second) { + JXL_DEBUG_V(4, "Patch %" PRIuS "x%" PRIuS " at position %u,%u", + ref_pos.xsize, ref_pos.ysize, pos.first, pos.second); positions.emplace_back( PatchPosition{pos.first, pos.second, pref_positions.size()}); // Add blending for color channels, ignore other channels. @@ -718,15 +730,16 @@ void FindBestPatchDictionary(const Image3F& opsin, blendings.push_back({PatchBlendMode::kNone, 0, false}); } } - pref_positions.emplace_back(std::move(ref_pos)); + pref_positions.emplace_back(ref_pos); } CompressParams cparams = state->cparams; // Recursive application of patches could create very weird issues. cparams.patches = Override::kOff; - RoundtripPatchFrame(&reference_frame, state, kPatchFrameReferenceId, cparams, - cms, pool, aux_out, /*subtract=*/true); + JXL_RETURN_IF_ERROR(RoundtripPatchFrame(&reference_frame, state, + kPatchFrameReferenceId, cparams, cms, + pool, aux_out, /*subtract=*/true)); // TODO(veluca): this assumes that applying patches is commutative, which is // not true for all blending modes. This code only produces kAdd patches, so @@ -734,12 +747,13 @@ void FindBestPatchDictionary(const Image3F& opsin, PatchDictionaryEncoder::SetPositions( &state->shared.image_features.patches, std::move(positions), std::move(pref_positions), std::move(blendings)); + return true; } -void RoundtripPatchFrame(Image3F* reference_frame, - PassesEncoderState* JXL_RESTRICT state, int idx, - CompressParams& cparams, const JxlCmsInterface& cms, - ThreadPool* pool, AuxOut* aux_out, bool subtract) { +Status RoundtripPatchFrame(Image3F* reference_frame, + PassesEncoderState* JXL_RESTRICT state, int idx, + CompressParams& cparams, const JxlCmsInterface& cms, + ThreadPool* pool, AuxOut* aux_out, bool subtract) { FrameInfo patch_frame_info; cparams.resampling = 1; cparams.ec_resampling = 1; @@ -768,7 +782,8 @@ void RoundtripPatchFrame(Image3F* reference_frame, std::vector<ImageF> extra_channels; extra_channels.reserve(ib.metadata()->extra_channel_info.size()); for (size_t i = 0; i < ib.metadata()->extra_channel_info.size(); i++) { - extra_channels.emplace_back(ib.xsize(), ib.ysize()); + JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(ib.xsize(), ib.ysize())); + extra_channels.emplace_back(std::move(ch)); // Must initialize the image with data to not affect blending with // uninitialized memory. // TODO(lode): patches must copy and use the real extra channels instead. @@ -814,6 +829,7 @@ void RoundtripPatchFrame(Image3F* reference_frame, } else { state->shared.reference_frames[idx].frame = std::move(ib); } + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.h b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.h index e17bfe4f04..ac236d7f17 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.h +++ b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.h @@ -12,13 +12,10 @@ #include <string.h> #include <sys/types.h> -#include <tuple> #include <vector> #include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/chroma_from_luma.h" -#include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/dec_patch_dictionary.h" #include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_cache.h" @@ -92,15 +89,15 @@ class PatchDictionaryEncoder { static void SubtractFrom(const PatchDictionary& pdic, Image3F* opsin); }; -void FindBestPatchDictionary(const Image3F& opsin, - PassesEncoderState* JXL_RESTRICT state, - const JxlCmsInterface& cms, ThreadPool* pool, - AuxOut* aux_out, bool is_xyb = true); +Status FindBestPatchDictionary(const Image3F& opsin, + PassesEncoderState* JXL_RESTRICT state, + const JxlCmsInterface& cms, ThreadPool* pool, + AuxOut* aux_out, bool is_xyb = true); -void RoundtripPatchFrame(Image3F* reference_frame, - PassesEncoderState* JXL_RESTRICT state, int idx, - CompressParams& cparams, const JxlCmsInterface& cms, - ThreadPool* pool, AuxOut* aux_out, bool subtract); +Status RoundtripPatchFrame(Image3F* reference_frame, + PassesEncoderState* JXL_RESTRICT state, int idx, + CompressParams& cparams, const JxlCmsInterface& cms, + ThreadPool* pool, AuxOut* aux_out, bool subtract); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_photon_noise_test.cc b/third_party/jpeg-xl/lib/jxl/enc_photon_noise_test.cc index be11b465ad..2e10dd5457 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_photon_noise_test.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_photon_noise_test.cc @@ -5,46 +5,37 @@ #include "lib/jxl/enc_photon_noise.h" -#include "lib/jxl/test_utils.h" +#include "lib/jxl/base/common.h" +#include "lib/jxl/noise.h" #include "lib/jxl/testing.h" namespace jxl { namespace { -using ::testing::FloatNear; -using ::testing::Pointwise; - -MATCHER(AreApproximatelyEqual, "") { - constexpr float kTolerance = 1e-6; - const float actual = std::get<0>(arg); - const float expected = std::get<1>(arg); - return testing::ExplainMatchResult(FloatNear(expected, kTolerance), actual, - result_listener); -} - TEST(EncPhotonNoiseTest, LUTs) { - EXPECT_THAT( + const double kTolerance = 1e-6; + EXPECT_ARRAY_NEAR( SimulatePhotonNoise(/*xsize=*/6000, /*ysize=*/4000, /*iso=*/100).lut, - Pointwise(AreApproximatelyEqual(), - {0.00259652, 0.0139648, 0.00681551, 0.00632582, 0.00694917, - 0.00803922, 0.00934574, 0.0107607})); - EXPECT_THAT( + (NoiseParams::Lut{0.00259652, 0.0139648, 0.00681551, 0.00632582, + 0.00694917, 0.00803922, 0.00934574, 0.0107607}), + kTolerance); + EXPECT_ARRAY_NEAR( SimulatePhotonNoise(/*xsize=*/6000, /*ysize=*/4000, /*iso=*/800).lut, - Pointwise(AreApproximatelyEqual(), - {0.02077220, 0.0420923, 0.01820690, 0.01439020, 0.01293670, - 0.01254030, 0.01277390, 0.0134161})); - EXPECT_THAT( + (NoiseParams::Lut{0.02077220, 0.0420923, 0.01820690, 0.01439020, + 0.01293670, 0.01254030, 0.01277390, 0.0134161}), + kTolerance); + EXPECT_ARRAY_NEAR( SimulatePhotonNoise(/*xsize=*/6000, /*ysize=*/4000, /*iso=*/6400).lut, - Pointwise(AreApproximatelyEqual(), - {0.1661770, 0.1691120, 0.05309080, 0.03963960, 0.03357410, - 0.03001650, 0.02776740, 0.0263478})); + (NoiseParams::Lut{0.1661770, 0.1691120, 0.05309080, 0.03963960, + 0.03357410, 0.03001650, 0.02776740, 0.0263478}), + kTolerance); // Lower when measured on a per-pixel basis as there are fewer of them. - EXPECT_THAT( + EXPECT_ARRAY_NEAR( SimulatePhotonNoise(/*xsize=*/4000, /*ysize=*/3000, /*iso=*/6400).lut, - Pointwise(AreApproximatelyEqual(), - {0.0830886, 0.1008720, 0.0367748, 0.0280305, 0.0240236, - 0.0218040, 0.0205771, 0.0200058})); + (NoiseParams::Lut{0.0830886, 0.1008720, 0.0367748, 0.0280305, 0.0240236, + 0.0218040, 0.0205771, 0.0200058}), + kTolerance); } } // namespace diff --git a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc index 236ddaacfd..35e49d5993 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc @@ -5,24 +5,18 @@ #include "lib/jxl/enc_quant_weights.h" +#include <jxl/types.h> #include <stdlib.h> -#include <algorithm> #include <cmath> -#include <limits> -#include <utility> -#include "lib/jxl/base/bits.h" #include "lib/jxl/base/common.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/dct_scales.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_modular.h" #include "lib/jxl/fields.h" -#include "lib/jxl/image.h" #include "lib/jxl/modular/encoding/encoding.h" -#include "lib/jxl/modular/options.h" namespace jxl { @@ -95,8 +89,8 @@ Status EncodeQuant(const QuantEncoding& encoding, size_t idx, size_t size_x, break; } case QuantEncoding::kQuantModeRAW: { - ModularFrameEncoder::EncodeQuantTable(size_x, size_y, writer, encoding, - idx, modular_frame_encoder); + JXL_RETURN_IF_ERROR(ModularFrameEncoder::EncodeQuantTable( + size_x, size_y, writer, encoding, idx, modular_frame_encoder)); break; } case QuantEncoding::kQuantModeAFV: { @@ -130,7 +124,7 @@ Status DequantMatricesEncode(const DequantMatrices& matrices, BitWriter* writer, } // TODO(janwas): better bound BitWriter::Allotment allotment(writer, 512 * 1024); - writer->Write(1, all_default); + writer->Write(1, TO_JXL_BOOL(all_default)); if (!all_default) { for (size_t i = 0; i < encodings.size(); i++) { JXL_RETURN_IF_ERROR(EncodeQuant( @@ -153,7 +147,7 @@ Status DequantMatricesEncodeDC(const DequantMatrices& matrices, } } BitWriter::Allotment allotment(writer, 1 + sizeof(float) * kBitsPerByte * 3); - writer->Write(1, all_default); + writer->Write(1, TO_JXL_BOOL(all_default)); if (!all_default) { for (size_t c = 0; c < 3; c++) { JXL_RETURN_IF_ERROR(F16Coder::Write(dc_quant[c] * 128.0f, writer)); @@ -195,19 +189,20 @@ void DequantMatricesRoundtrip(DequantMatrices* matrices) { JXL_CHECK(br.Close()); } -void DequantMatricesSetCustom(DequantMatrices* matrices, - const std::vector<QuantEncoding>& encodings, - ModularFrameEncoder* encoder) { +Status DequantMatricesSetCustom(DequantMatrices* matrices, + const std::vector<QuantEncoding>& encodings, + ModularFrameEncoder* encoder) { JXL_ASSERT(encodings.size() == DequantMatrices::kNum); matrices->SetEncodings(encodings); for (size_t i = 0; i < encodings.size(); i++) { if (encodings[i].mode == QuantEncodingInternal::kQuantModeRAW) { - encoder->AddQuantTable(DequantMatrices::required_size_x[i] * kBlockDim, - DequantMatrices::required_size_y[i] * kBlockDim, - encodings[i], i); + JXL_RETURN_IF_ERROR(encoder->AddQuantTable( + DequantMatrices::required_size_x[i] * kBlockDim, + DequantMatrices::required_size_y[i] * kBlockDim, encodings[i], i)); } } DequantMatricesRoundtrip(matrices); + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h index a47dfd4988..82d8278b72 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h +++ b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.h @@ -27,9 +27,9 @@ void DequantMatricesSetCustomDC(DequantMatrices* matrices, const float* dc); void DequantMatricesScaleDC(DequantMatrices* matrices, float scale); -void DequantMatricesSetCustom(DequantMatrices* matrices, - const std::vector<QuantEncoding>& encodings, - ModularFrameEncoder* encoder); +Status DequantMatricesSetCustom(DequantMatrices* matrices, + const std::vector<QuantEncoding>& encodings, + ModularFrameEncoder* encoder); // Roundtrip encode/decode the matrices to ensure same values as decoder. void DequantMatricesRoundtrip(DequantMatrices* matrices); diff --git a/third_party/jpeg-xl/lib/jxl/enc_splines.cc b/third_party/jpeg-xl/lib/jxl/enc_splines.cc index de6c9670ea..fa15648ca5 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_splines.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_splines.cc @@ -73,13 +73,13 @@ void EncodeSplines(const Splines& splines, BitWriter* writer, splines.QuantizedSplines(); std::vector<std::vector<Token>> tokens(1); tokens[0].emplace_back(kNumSplinesContext, quantized_splines.size() - 1); - EncodeAllStartingPoints(splines.StartingPoints(), &tokens[0]); + EncodeAllStartingPoints(splines.StartingPoints(), tokens.data()); tokens[0].emplace_back(kQuantizationAdjustmentContext, PackSigned(splines.GetQuantizationAdjustment())); for (const QuantizedSpline& spline : quantized_splines) { - QuantizedSplineEncoder::Tokenize(spline, &tokens[0]); + QuantizedSplineEncoder::Tokenize(spline, tokens.data()); } EntropyEncodingData codes; diff --git a/third_party/jpeg-xl/lib/jxl/enc_transforms.cc b/third_party/jpeg-xl/lib/jxl/enc_transforms.cc index 8978ba1dcb..d116183404 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_transforms.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_transforms.cc @@ -21,20 +21,19 @@ void TransformFromPixels(const AcStrategy::Type strategy, const float* JXL_RESTRICT pixels, size_t pixels_stride, float* JXL_RESTRICT coefficients, float* scratch_space) { - return HWY_DYNAMIC_DISPATCH(TransformFromPixels)( - strategy, pixels, pixels_stride, coefficients, scratch_space); + HWY_DYNAMIC_DISPATCH(TransformFromPixels) + (strategy, pixels, pixels_stride, coefficients, scratch_space); } HWY_EXPORT(DCFromLowestFrequencies); void DCFromLowestFrequencies(AcStrategy::Type strategy, const float* block, float* dc, size_t dc_stride) { - return HWY_DYNAMIC_DISPATCH(DCFromLowestFrequencies)(strategy, block, dc, - dc_stride); + HWY_DYNAMIC_DISPATCH(DCFromLowestFrequencies)(strategy, block, dc, dc_stride); } HWY_EXPORT(AFVDCT4x4); void AFVDCT4x4(const float* JXL_RESTRICT pixels, float* JXL_RESTRICT coeffs) { - return HWY_DYNAMIC_DISPATCH(AFVDCT4x4)(pixels, coeffs); + HWY_DYNAMIC_DISPATCH(AFVDCT4x4)(pixels, coeffs); } #endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jxl/enc_transforms.h b/third_party/jpeg-xl/lib/jxl/enc_transforms.h index 039ccc3893..f0ce95659d 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_transforms.h +++ b/third_party/jpeg-xl/lib/jxl/enc_transforms.h @@ -9,14 +9,13 @@ // Facade for (non-inlined) integral transforms. #include <stddef.h> -#include <stdint.h> #include "lib/jxl/ac_strategy.h" #include "lib/jxl/base/compiler_specific.h" namespace jxl { -void TransformFromPixels(const AcStrategy::Type strategy, +void TransformFromPixels(AcStrategy::Type strategy, const float* JXL_RESTRICT pixels, size_t pixels_stride, float* JXL_RESTRICT coefficients, float* JXL_RESTRICT scratch_space); diff --git a/third_party/jpeg-xl/lib/jxl/enc_xyb.cc b/third_party/jpeg-xl/lib/jxl/enc_xyb.cc index e538e8c91d..9fc68d8474 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_xyb.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_xyb.cc @@ -21,9 +21,7 @@ #include "lib/jxl/cms/opsin_params.h" #include "lib/jxl/cms/transfer_functions-inl.h" #include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_image_bundle.h" -#include "lib/jxl/fields.h" #include "lib/jxl/image_bundle.h" #include "lib/jxl/image_ops.h" @@ -44,7 +42,7 @@ JXL_INLINE void OpsinAbsorbance(const V r, const V g, const V b, const float* JXL_RESTRICT premul_absorb, V* JXL_RESTRICT mixed0, V* JXL_RESTRICT mixed1, V* JXL_RESTRICT mixed2) { - const float* bias = &jxl::cms::kOpsinAbsorbanceBias[0]; + const float* bias = jxl::cms::kOpsinAbsorbanceBias.data(); const HWY_FULL(float) d; const size_t N = Lanes(d); const auto m0 = Load(d, premul_absorb + 0 * N); @@ -77,7 +75,9 @@ void LinearRGBToXYB(const V r, const V g, const V b, const float* JXL_RESTRICT premul_absorb, float* JXL_RESTRICT valx, float* JXL_RESTRICT valy, float* JXL_RESTRICT valz) { - V mixed0, mixed1, mixed2; + V mixed0; + V mixed1; + V mixed2; OpsinAbsorbance(r, g, b, premul_absorb, &mixed0, &mixed1, &mixed2); // mixed* should be non-negative even for wide-gamut, so clamp to zero. @@ -198,9 +198,11 @@ void ComputePremulAbsorb(float intensity_target, float* premul_absorb) { const HWY_FULL(float) d; const size_t N = Lanes(d); const float mul = intensity_target / 255.0f; - for (size_t i = 0; i < 9; ++i) { - const auto absorb = Set(d, jxl::cms::kOpsinAbsorbanceMatrix[i] * mul); - Store(absorb, d, premul_absorb + i * N); + for (size_t j = 0; j < 3; ++j) { + for (size_t i = 0; i < 3; ++i) { + const auto absorb = Set(d, jxl::cms::kOpsinAbsorbanceMatrix[j][i] * mul); + Store(absorb, d, premul_absorb + (j * 3 + i) * N); + } } for (size_t i = 0; i < 3; ++i) { const auto neg_bias_cbrt = @@ -209,15 +211,16 @@ void ComputePremulAbsorb(float intensity_target, float* premul_absorb) { } } -Image3F TransformToLinearRGB(const Image3F& in, - const ColorEncoding& color_encoding, - float intensity_target, const JxlCmsInterface& cms, - ThreadPool* pool) { +StatusOr<Image3F> TransformToLinearRGB(const Image3F& in, + const ColorEncoding& color_encoding, + float intensity_target, + const JxlCmsInterface& cms, + ThreadPool* pool) { ColorSpaceTransform c_transform(cms); bool is_gray = color_encoding.IsGray(); const ColorEncoding& c_desired = ColorEncoding::LinearSRGB(is_gray); - Image3F out(in.xsize(), in.ysize()); - std::atomic<bool> ok{true}; + JXL_ASSIGN_OR_RETURN(Image3F out, Image3F::Create(in.xsize(), in.ysize())); + std::atomic<bool> has_error{false}; JXL_CHECK(RunOnPool( pool, 0, in.ysize(), [&](const size_t num_threads) { @@ -225,6 +228,7 @@ Image3F TransformToLinearRGB(const Image3F& in, in.xsize(), num_threads); }, [&](const uint32_t y, const size_t thread) { + if (has_error) return; float* mutable_src_buf = c_transform.BufSrc(thread); const float* src_buf = mutable_src_buf; // Interleave input. @@ -241,8 +245,8 @@ Image3F TransformToLinearRGB(const Image3F& in, } } float* JXL_RESTRICT dst_buf = c_transform.BufDst(thread); - if (!c_transform.Run(thread, src_buf, dst_buf)) { - ok.store(false); + if (!c_transform.Run(thread, src_buf, dst_buf, in.xsize())) { + has_error = true; return; } float* JXL_RESTRICT row_out0 = out.PlaneRow(0, y); @@ -264,7 +268,7 @@ Image3F TransformToLinearRGB(const Image3F& in, } }, "Colorspace transform")); - JXL_CHECK(ok.load()); + JXL_CHECK(!has_error); return out; } @@ -394,12 +398,13 @@ void ToXYB(const ColorEncoding& c_current, float intensity_target, (c_current, intensity_target, black, pool, image, cms, linear); } -void ToXYB(const ImageBundle& in, ThreadPool* pool, Image3F* JXL_RESTRICT xyb, - const JxlCmsInterface& cms, Image3F* JXL_RESTRICT linear) { - *xyb = Image3F(in.xsize(), in.ysize()); +Status ToXYB(const ImageBundle& in, ThreadPool* pool, Image3F* JXL_RESTRICT xyb, + const JxlCmsInterface& cms, Image3F* JXL_RESTRICT linear) { + JXL_ASSIGN_OR_RETURN(*xyb, Image3F::Create(in.xsize(), in.ysize())); CopyImageTo(in.color(), xyb); ToXYB(in.c_current(), in.metadata()->IntensityTarget(), in.HasBlack() ? &in.black() : nullptr, pool, xyb, cms, linear); + return true; } HWY_EXPORT(LinearRGBRowToXYB); diff --git a/third_party/jpeg-xl/lib/jxl/enc_xyb.h b/third_party/jpeg-xl/lib/jxl/enc_xyb.h index 6a2e7c4123..741d447b92 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_xyb.h +++ b/third_party/jpeg-xl/lib/jxl/enc_xyb.h @@ -27,8 +27,9 @@ void ToXYB(const ColorEncoding& c_current, float intensity_target, const ImageF* black, ThreadPool* pool, Image3F* JXL_RESTRICT image, const JxlCmsInterface& cms, Image3F* JXL_RESTRICT linear); -void ToXYB(const ImageBundle& in, ThreadPool* pool, Image3F* JXL_RESTRICT xyb, - const JxlCmsInterface& cms, Image3F* JXL_RESTRICT linear = nullptr); +Status ToXYB(const ImageBundle& in, ThreadPool* pool, Image3F* JXL_RESTRICT xyb, + const JxlCmsInterface& cms, + Image3F* JXL_RESTRICT linear = nullptr); void LinearRGBRowToXYB(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1, float* JXL_RESTRICT row2, diff --git a/third_party/jpeg-xl/lib/jxl/encode.cc b/third_party/jpeg-xl/lib/jxl/encode.cc index 76f2148d62..4dbbeba4e7 100644 --- a/third_party/jpeg-xl/lib/jxl/encode.cc +++ b/third_party/jpeg-xl/lib/jxl/encode.cc @@ -595,21 +595,21 @@ JxlEncoderStatus VerifyInputBitDepth(JxlBitDepth bit_depth, return JxlErrorOrStatus::Success(); } -static inline bool EncodeVarInt(uint64_t value, size_t output_size, - size_t* output_pos, uint8_t* output) { +inline bool EncodeVarInt(uint64_t value, size_t output_size, size_t* output_pos, + uint8_t* output) { // While more than 7 bits of data are left, // store 7 bits and set the next byte flag while (value > 127) { // TODO(eustas): should it be `>=` ? if (*output_pos > output_size) return false; // |128: Set the next byte flag - output[(*output_pos)++] = ((uint8_t)(value & 127)) | 128; + output[(*output_pos)++] = (static_cast<uint8_t>(value & 127)) | 128; // Remove the seven bits we just wrote value >>= 7; } // TODO(eustas): should it be `>=` ? if (*output_pos > output_size) return false; - output[(*output_pos)++] = ((uint8_t)value) & 127; + output[(*output_pos)++] = static_cast<uint8_t>(value & 127); return true; } @@ -854,7 +854,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() { timecode = 0; } - const bool last_frame = frames_closed && !num_queued_frames; + const bool last_frame = frames_closed && (num_queued_frames == 0); uint32_t max_bits_per_sample = metadata.m.bit_depth.bits_per_sample; for (const auto& info : metadata.m.extra_channel_info) { @@ -901,7 +901,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() { return JXL_API_ERROR( this, JXL_ENC_ERR_API_USAGE, "Cannot use save_as_reference values >=3 (found: %d)", - (int)save_as_reference); + static_cast<int>(save_as_reference)); } jxl::FrameInfo frame_info; @@ -909,8 +909,8 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() { frame_info.save_as_reference = save_as_reference; frame_info.source = input_frame->option_values.header.layer_info.blend_info.source; - frame_info.clamp = - input_frame->option_values.header.layer_info.blend_info.clamp; + frame_info.clamp = FROM_JXL_BOOL( + input_frame->option_values.header.layer_info.blend_info.clamp); frame_info.alpha_channel = input_frame->option_values.header.layer_info.blend_info.alpha; frame_info.extra_channel_blending_info.resize( @@ -989,7 +989,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() { #endif jxl::WriteBoxHeader(jxl::MakeBoxType("jxlc"), frame_codestream_size, /*unbounded=*/false, use_large_box, - &box_header[0]); + box_header.data()); JXL_ASSERT(n == box_header_size); } else { #if JXL_ENABLE_ASSERT @@ -997,7 +997,7 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() { #endif jxl::WriteBoxHeader( jxl::MakeBoxType("jxlp"), frame_codestream_size + 4, - /*unbounded=*/false, use_large_box, &box_header[0]); + /*unbounded=*/false, use_large_box, box_header.data()); JXL_ASSERT(n == box_header_size - 4); WriteJxlpBoxCounter(jxlp_counter++, last_frame, &box_header[box_header_size - 4]); @@ -1061,15 +1061,17 @@ JxlEncoderStatus JxlEncoderSetColorEncoding(JxlEncoder* enc, } if (enc->metadata.m.color_encoding.GetColorSpace() == jxl::ColorSpace::kGray) { - if (enc->basic_info.num_color_channels != 1) + if (enc->basic_info.num_color_channels != 1) { return JXL_API_ERROR( enc, JXL_ENC_ERR_API_USAGE, "Cannot use grayscale color encoding with num_color_channels != 1"); + } } else { - if (enc->basic_info.num_color_channels != 3) + if (enc->basic_info.num_color_channels != 3) { return JXL_API_ERROR( enc, JXL_ENC_ERR_API_USAGE, "Cannot use RGB color encoding with num_color_channels != 3"); + } } enc->color_encoding_set = true; if (!enc->intensity_target_set) { @@ -1103,15 +1105,17 @@ JxlEncoderStatus JxlEncoderSetICCProfile(JxlEncoder* enc, } if (enc->metadata.m.color_encoding.GetColorSpace() == jxl::ColorSpace::kGray) { - if (enc->basic_info.num_color_channels != 1) + if (enc->basic_info.num_color_channels != 1) { return JXL_API_ERROR( enc, JXL_ENC_ERR_BAD_INPUT, "Cannot use grayscale ICC profile with num_color_channels != 1"); + } } else { - if (enc->basic_info.num_color_channels != 3) + if (enc->basic_info.num_color_channels != 3) { return JXL_API_ERROR( enc, JXL_ENC_ERR_BAD_INPUT, "Cannot use RGB ICC profile with num_color_channels != 3"); + } // TODO(jon): also check that a kBlack extra channel is provided in the CMYK // case } @@ -1210,7 +1214,8 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc, enc->metadata.m.bit_depth.floating_point_sample = (info->exponent_bits_per_sample != 0u); enc->metadata.m.modular_16_bit_buffer_sufficient = - (!info->uses_original_profile || info->bits_per_sample <= 12) && + (!FROM_JXL_BOOL(info->uses_original_profile) || + info->bits_per_sample <= 12) && info->alpha_bits <= 12; if ((info->intrinsic_xsize > 0 || info->intrinsic_ysize > 0) && (info->intrinsic_xsize != info->xsize || @@ -1248,7 +1253,7 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc, } } - enc->metadata.m.xyb_encoded = !info->uses_original_profile; + enc->metadata.m.xyb_encoded = !FROM_JXL_BOOL(info->uses_original_profile); if (info->orientation > 0 && info->orientation <= 8) { enc->metadata.m.orientation = info->orientation; } else { @@ -1271,12 +1276,12 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc, } enc->metadata.m.tone_mapping.min_nits = info->min_nits; enc->metadata.m.tone_mapping.relative_to_max_display = - info->relative_to_max_display; + FROM_JXL_BOOL(info->relative_to_max_display); enc->metadata.m.tone_mapping.linear_below = info->linear_below; enc->basic_info = *info; enc->basic_info_set = true; - enc->metadata.m.have_animation = info->have_animation; + enc->metadata.m.have_animation = FROM_JXL_BOOL(info->have_animation); if (info->have_animation) { if (info->animation.tps_denominator < 1) { return JXL_API_ERROR( @@ -1290,7 +1295,8 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc, enc->metadata.m.animation.tps_numerator = info->animation.tps_numerator; enc->metadata.m.animation.tps_denominator = info->animation.tps_denominator; enc->metadata.m.animation.num_loops = info->animation.num_loops; - enc->metadata.m.animation.have_timecodes = info->animation.have_timecodes; + enc->metadata.m.animation.have_timecodes = + FROM_JXL_BOOL(info->animation.have_timecodes); } std::string level_message; int required_level = VerifyLevelSettings(enc, &level_message); @@ -1326,14 +1332,16 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetUpsamplingMode(JxlEncoder* enc, const int64_t mode) { // for convenience, allow calling this with factor 1 and just make it a no-op if (factor == 1) return JxlErrorOrStatus::Success(); - if (factor != 2 && factor != 4 && factor != 8) + if (factor != 2 && factor != 4 && factor != 8) { return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE, "Invalid upsampling factor"); + } if (mode < -1) return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE, "Invalid upsampling mode"); - if (mode > 1) + if (mode > 1) { return JXL_API_ERROR(enc, JXL_ENC_ERR_NOT_SUPPORTED, "Unsupported upsampling mode"); + } const size_t count = (factor == 2 ? 15 : (factor == 4 ? 55 : 210)); auto& td = enc->metadata.transform_data; @@ -1436,7 +1444,7 @@ JxlEncoderFrameSettings* JxlEncoderFrameSettingsCreate( } opts->values.cparams.level = enc->codestream_level; opts->values.cparams.ec_distance.resize(enc->metadata.m.num_extra_channels, - -1); + 0); JxlEncoderFrameSettings* ret = opts.get(); enc->encoder_options.emplace_back(std::move(opts)); @@ -1451,7 +1459,7 @@ JxlEncoderStatus JxlEncoderSetFrameLossless( frame_settings->enc, JXL_ENC_ERR_API_USAGE, "Set uses_original_profile=true for lossless encoding"); } - frame_settings->values.lossless = lossless; + frame_settings->values.lossless = FROM_JXL_BOOL(lossless); return JxlErrorOrStatus::Success(); } @@ -1489,7 +1497,7 @@ JxlEncoderStatus JxlEncoderSetExtraChannelDistance( // This can only happen if JxlEncoderFrameSettingsCreate() was called before // JxlEncoderSetBasicInfo(). frame_settings->values.cparams.ec_distance.resize( - frame_settings->enc->metadata.m.num_extra_channels, -1); + frame_settings->enc->metadata.m.num_extra_channels, 0); } frame_settings->values.cparams.ec_distance[index] = distance; @@ -1537,14 +1545,14 @@ JxlEncoderStatus JxlEncoderFrameSettingsSetOption( switch (option) { case JXL_ENC_FRAME_SETTING_EFFORT: if (frame_settings->enc->allow_expert_options) { - if (value < 1 || value > 10) { + if (value < 1 || value > 11) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED, - "Encode effort has to be in [1..10]"); + "Encode effort has to be in [1..11]"); } } else { - if (value < 1 || value > 9) { + if (value < 1 || value > 10) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED, - "Encode effort has to be in [1..9]"); + "Encode effort has to be in [1..10]"); } } frame_settings->values.cparams.speed_tier = @@ -1747,9 +1755,9 @@ JxlEncoderStatus JxlEncoderFrameSettingsSetOption( frame_settings->values.cparams.jpeg_compress_boxes = value; break; case JXL_ENC_FRAME_SETTING_BUFFERING: - if (value < 0 || value > 3) { + if (value < -1 || value > 3) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED, - "Buffering has to be in [0..3]"); + "Buffering has to be in [-1..3]"); } frame_settings->values.cparams.buffering = value; break; @@ -1934,7 +1942,7 @@ JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc, return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE, "this setting can only be set at the beginning"); } - enc->use_container = static_cast<bool>(use_container); + enc->use_container = FROM_JXL_BOOL(use_container); return JxlErrorOrStatus::Success(); } @@ -1944,7 +1952,7 @@ JxlEncoderStatus JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE, "this setting can only be set at the beginning"); } - enc->store_jpeg_metadata = static_cast<bool>(store_jpeg_metadata); + enc->store_jpeg_metadata = FROM_JXL_BOOL(store_jpeg_metadata); return JxlErrorOrStatus::Success(); } @@ -2034,7 +2042,7 @@ JxlEncoderStatus JxlEncoderAddJPEGFrame( JxlEncoderInitBasicInfo(&basic_info); basic_info.xsize = io.Main().jpeg_data->width; basic_info.ysize = io.Main().jpeg_data->height; - basic_info.uses_original_profile = true; + basic_info.uses_original_profile = JXL_TRUE; if (JxlEncoderSetBasicInfo(frame_settings->enc, &basic_info) != JXL_ENC_SUCCESS) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_GENERIC, @@ -2042,7 +2050,8 @@ JxlEncoderStatus JxlEncoderAddJPEGFrame( } } - size_t xsize, ysize; + size_t xsize; + size_t ysize; if (GetCurrentDimensions(frame_settings, xsize, ysize) != JXL_ENC_SUCCESS) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_GENERIC, "bad dimensions"); @@ -2074,21 +2083,23 @@ JxlEncoderStatus JxlEncoderAddJPEGFrame( std::vector<uint8_t> exif(exif_size); memcpy(exif.data() + 4, io.blobs.exif.data(), io.blobs.exif.size()); JxlEncoderUseBoxes(frame_settings->enc); - JxlEncoderAddBox(frame_settings->enc, "Exif", exif.data(), exif_size, - frame_settings->values.cparams.jpeg_compress_boxes); + JxlEncoderAddBox( + frame_settings->enc, "Exif", exif.data(), exif_size, + TO_JXL_BOOL(frame_settings->values.cparams.jpeg_compress_boxes)); } if (!io.blobs.xmp.empty() && frame_settings->values.cparams.jpeg_keep_xmp) { JxlEncoderUseBoxes(frame_settings->enc); - JxlEncoderAddBox(frame_settings->enc, "xml ", io.blobs.xmp.data(), - io.blobs.xmp.size(), - frame_settings->values.cparams.jpeg_compress_boxes); + JxlEncoderAddBox( + frame_settings->enc, "xml ", io.blobs.xmp.data(), io.blobs.xmp.size(), + TO_JXL_BOOL(frame_settings->values.cparams.jpeg_compress_boxes)); } if (!io.blobs.jumbf.empty() && frame_settings->values.cparams.jpeg_keep_jumbf) { JxlEncoderUseBoxes(frame_settings->enc); - JxlEncoderAddBox(frame_settings->enc, "jumb", io.blobs.jumbf.data(), - io.blobs.jumbf.size(), - frame_settings->values.cparams.jpeg_compress_boxes); + JxlEncoderAddBox( + frame_settings->enc, "jumb", io.blobs.jumbf.data(), + io.blobs.jumbf.size(), + TO_JXL_BOOL(frame_settings->values.cparams.jpeg_compress_boxes)); } if (frame_settings->enc->store_jpeg_metadata) { if (!frame_settings->values.cparams.jpeg_keep_exif || @@ -2259,10 +2270,12 @@ JxlEncoderStatus JxlEncoderAddImageFrameInternal( pool, 0, count, jxl::ThreadPool::NoInit, [&](size_t i, size_t) { fun(opaque, i); }, "Encode fast lossless")); }; - auto frame_state = JxlFastLosslessPrepareFrame( + JXL_BOOL oneshot = TO_JXL_BOOL(!frame_data.StreamingInput()); + auto* frame_state = JxlFastLosslessPrepareFrame( frame_data.GetInputSource(), xsize, ysize, num_channels, - frame_settings->enc->metadata.m.bit_depth.bits_per_sample, big_endian, - /*effort=*/2, /*oneshot=*/!frame_data.StreamingInput()); + frame_settings->enc->metadata.m.bit_depth.bits_per_sample, + TO_JXL_BOOL(big_endian), + /*effort=*/2, oneshot); if (!streaming) { JxlFastLosslessProcessFrame(frame_state, /*is_last=*/false, frame_settings->enc->thread_pool.get(), @@ -2325,7 +2338,8 @@ JxlEncoderStatus JxlEncoderAddImageFrameInternal( JxlEncoderStatus JxlEncoderAddImageFrame( const JxlEncoderFrameSettings* frame_settings, const JxlPixelFormat* pixel_format, const void* buffer, size_t size) { - size_t xsize, ysize; + size_t xsize; + size_t ysize; if (GetCurrentDimensions(frame_settings, xsize, ysize) != JXL_ENC_SUCCESS) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_GENERIC, "bad dimensions"); @@ -2345,7 +2359,8 @@ JxlEncoderStatus JxlEncoderAddImageFrame( JxlEncoderStatus JxlEncoderAddChunkedFrame( const JxlEncoderFrameSettings* frame_settings, JXL_BOOL is_last_frame, JxlChunkedFrameInputSource chunked_frame_input) { - size_t xsize, ysize; + size_t xsize; + size_t ysize; if (GetCurrentDimensions(frame_settings, xsize, ysize) != JXL_ENC_SUCCESS) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_GENERIC, "bad dimensions"); @@ -2417,7 +2432,7 @@ JxlEncoderStatus JxlEncoderAddBox(JxlEncoder* enc, const JxlBoxType type, box->type = jxl::MakeBoxType(type); box->contents.assign(contents, contents + size); - box->compress_box = !!compress_box; + box->compress_box = FROM_JXL_BOOL(compress_box); QueueBox(enc, box); return JxlErrorOrStatus::Success(); } @@ -2450,7 +2465,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer( return JXL_API_ERROR_NOSET("Invalid input bit depth"); } const uint8_t* uint8_buffer = reinterpret_cast<const uint8_t*>(buffer); - auto queued_frame = frame_settings->enc->input_queue.back().frame.get(); + auto* queued_frame = frame_settings->enc->input_queue.back().frame.get(); if (!queued_frame->frame_data.SetFromBuffer(1 + index, uint8_buffer, size, ec_format)) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_API_USAGE, @@ -2588,12 +2603,14 @@ JxlEncoderStatus JxlEncoderSetFrameBitDepth( void JxlColorEncodingSetToSRGB(JxlColorEncoding* color_encoding, JXL_BOOL is_gray) { - *color_encoding = jxl::ColorEncoding::SRGB(is_gray).ToExternal(); + *color_encoding = + jxl::ColorEncoding::SRGB(FROM_JXL_BOOL(is_gray)).ToExternal(); } void JxlColorEncodingSetToLinearSRGB(JxlColorEncoding* color_encoding, JXL_BOOL is_gray) { - *color_encoding = jxl::ColorEncoding::LinearSRGB(is_gray).ToExternal(); + *color_encoding = + jxl::ColorEncoding::LinearSRGB(FROM_JXL_BOOL(is_gray)).ToExternal(); } void JxlEncoderAllowExpertOptions(JxlEncoder* enc) { @@ -2611,9 +2628,7 @@ JXL_EXPORT JxlEncoderStats* JxlEncoderStatsCreate() { return new JxlEncoderStats(); } -JXL_EXPORT void JxlEncoderStatsDestroy(JxlEncoderStats* stats) { - if (stats) delete stats; -} +JXL_EXPORT void JxlEncoderStatsDestroy(JxlEncoderStats* stats) { delete stats; } JXL_EXPORT void JxlEncoderCollectStats(JxlEncoderFrameSettings* frame_settings, JxlEncoderStats* stats) { diff --git a/third_party/jpeg-xl/lib/jxl/encode_internal.h b/third_party/jpeg-xl/lib/jxl/encode_internal.h index e89993f253..001df5fed5 100644 --- a/third_party/jpeg-xl/lib/jxl/encode_internal.h +++ b/third_party/jpeg-xl/lib/jxl/encode_internal.h @@ -580,12 +580,9 @@ jxl::Status AppendData(JxlEncoderOutputProcessorWrapper& output_processor, // Internal use only struct, can only be initialized correctly by // JxlEncoderCreate. struct JxlEncoderStruct { - JxlEncoderError error = JxlEncoderError::JXL_ENC_ERR_OK; JxlMemoryManager memory_manager; jxl::MemoryManagerUniquePtr<jxl::ThreadPool> thread_pool{ nullptr, jxl::MemoryManagerDeleteHelper(&memory_manager)}; - JxlCmsInterface cms; - bool cms_set; std::vector<jxl::MemoryManagerUniquePtr<JxlEncoderFrameSettings>> encoder_options; @@ -603,6 +600,9 @@ struct JxlEncoderStruct { size_t codestream_bytes_written_end_of_frame; jxl::JxlEncoderFrameIndexBox frame_index_box; + JxlCmsInterface cms; + bool cms_set; + // Force using the container even if not needed bool use_container; // User declared they will add metadata boxes @@ -611,23 +611,26 @@ struct JxlEncoderStruct { // TODO(lode): move level into jxl::CompressParams since some C++ // implementation decisions should be based on it: level 10 allows more // features to be used. - int32_t codestream_level; bool store_jpeg_metadata; + int32_t codestream_level; jxl::CodecMetadata metadata; std::vector<uint8_t> jpeg_metadata; - // Wrote any output at all, so wrote the data before the first user added - // frame or box, such as signature, basic info, ICC profile or jpeg - // reconstruction box. - bool wrote_bytes; jxl::CompressParams last_used_cparams; JxlBasicInfo basic_info; + JxlEncoderError error = JxlEncoderError::JXL_ENC_ERR_OK; + // Encoder wrote a jxlp (partial codestream) box, so any next codestream // parts must also be written in jxlp boxes, a single jxlc box cannot be // used. The counter is used for the 4-byte jxlp box index header. size_t jxlp_counter; + // Wrote any output at all, so wrote the data before the first user added + // frame or box, such as signature, basic info, ICC profile or jpeg + // reconstruction box. + bool wrote_bytes; + bool frames_closed; bool boxes_closed; bool basic_info_set; diff --git a/third_party/jpeg-xl/lib/jxl/encode_test.cc b/third_party/jpeg-xl/lib/jxl/encode_test.cc index 2c17fcab21..3e519cc45d 100644 --- a/third_party/jpeg-xl/lib/jxl/encode_test.cc +++ b/third_party/jpeg-xl/lib/jxl/encode_test.cc @@ -33,6 +33,7 @@ #include "lib/extras/packed_image.h" #include "lib/jxl/base/byte_order.h" #include "lib/jxl/base/c_callback_support.h" +#include "lib/jxl/base/override.h" #include "lib/jxl/base/span.h" #include "lib/jxl/base/status.h" #include "lib/jxl/common.h" // JXL_HIGH_PRECISION @@ -79,16 +80,16 @@ TEST(EncodeTest, AddFrameAfterCloseInputTest) { jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format); basic_info.xsize = xsize; basic_info.ysize = ysize; - basic_info.uses_original_profile = false; + basic_info.uses_original_profile = 0; EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10)); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info)); JxlColorEncoding color_encoding; - JxlColorEncodingSetToSRGB(&color_encoding, - /*is_gray=*/pixel_format.num_channels < 3); + JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3); + JxlColorEncodingSetToSRGB(&color_encoding, is_gray); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc.get(), &color_encoding)); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderAddImageFrame(frame_settings, &pixel_format, pixels.data(), pixels.size())); @@ -104,7 +105,7 @@ TEST(EncodeTest, AddJPEGAfterCloseTest) { const std::vector<uint8_t> orig = jxl::test::ReadTestData(jpeg_path); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderAddJPEGFrame(frame_settings, orig.data(), orig.size())); @@ -123,12 +124,12 @@ TEST(EncodeTest, AddFrameBeforeBasicInfoTest) { jxl::test::SomeTestImageToCodecInOut(pixels, 4, xsize, ysize); JxlColorEncoding color_encoding; - JxlColorEncodingSetToSRGB(&color_encoding, - /*is_gray=*/pixel_format.num_channels < 3); + JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3); + JxlColorEncodingSetToSRGB(&color_encoding, is_gray); EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderSetColorEncoding(enc.get(), &color_encoding)); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderAddImageFrame(frame_settings, &pixel_format, pixels.data(), pixels.size())); @@ -188,17 +189,17 @@ void VerifyFrameEncoding(size_t xsize, size_t ysize, JxlEncoder* enc, basic_info.xsize = xsize; basic_info.ysize = ysize; if (frame_settings->values.lossless || lossy_use_original_profile) { - basic_info.uses_original_profile = true; + basic_info.uses_original_profile = JXL_TRUE; } else { - basic_info.uses_original_profile = false; + basic_info.uses_original_profile = JXL_FALSE; } // 16-bit alpha means this requires level 10 EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc, 10)); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info)); JxlColorEncoding color_encoding; - JxlColorEncodingSetToSRGB(&color_encoding, true); + JxlColorEncodingSetToSRGB(&color_encoding, JXL_TRUE); EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderSetColorEncoding(enc, &color_encoding)); - JxlColorEncodingSetToSRGB(&color_encoding, false); + JxlColorEncodingSetToSRGB(&color_encoding, JXL_FALSE); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc, &color_encoding)); pixel_format.num_channels = 1; EXPECT_EQ(JXL_ENC_ERROR, @@ -230,18 +231,20 @@ void VerifyFrameEncoding(size_t xsize, size_t ysize, JxlEncoder* enc, EXPECT_TRUE(jxl::test::DecodeFile( {}, jxl::Bytes(compressed.data(), compressed.size()), &decoded_io)); - EXPECT_LE( - ComputeDistance2(input_io.Main(), decoded_io.Main(), *JxlGetDefaultCms()), + static constexpr double kMaxButteraugli = #if JXL_HIGH_PRECISION - 1.84); + 1.84; #else - 8.7); + 8.7; #endif + EXPECT_LE( + ComputeDistance2(input_io.Main(), decoded_io.Main(), *JxlGetDefaultCms()), + kMaxButteraugli); } void VerifyFrameEncoding(JxlEncoder* enc, const JxlEncoderFrameSettings* frame_settings) { - VerifyFrameEncoding(63, 129, enc, frame_settings, 2700, + VerifyFrameEncoding(63, 129, enc, frame_settings, 27000, /*lossy_use_original_profile=*/false); } @@ -256,7 +259,7 @@ TEST(EncodeTest, EncoderResetTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); VerifyFrameEncoding(50, 200, enc.get(), - JxlEncoderFrameSettingsCreate(enc.get(), nullptr), 4300, + JxlEncoderFrameSettingsCreate(enc.get(), nullptr), 4550, false); // Encoder should become reusable for a new image from scratch after using // reset. @@ -293,7 +296,7 @@ TEST(EncodeTest, CmsTest) { JxlEncoderSetCms(enc.get(), cms); JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate(enc.get(), nullptr); - JxlEncoderSetFrameLossless(frame_settings, false); + JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE); ASSERT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 8)); @@ -306,7 +309,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 5)); @@ -318,7 +321,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); const size_t nb_options = 23; const JxlEncoderFrameSettingId options[nb_options] = { JXL_ENC_FRAME_SETTING_EFFORT, @@ -346,7 +349,7 @@ TEST(EncodeTest, frame_settingsTest) { JXL_ENC_FRAME_SETTING_JPEG_KEEP_JUMBF}; const int too_low[nb_options] = {0, -2, -2, 3, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, - -2, -1, -2, -1, -2, -2, -2}; + -2, -1, -2, -2, -2, -2, -2}; const int too_high[nb_options] = {11, 12, 5, 16, 6, 2, 4, -3, -3, 3, 70914, 3, 42, 4, 16, 12, 2, 2, 2, 4, 2, 2, 2}; @@ -367,14 +370,14 @@ TEST(EncodeTest, frame_settingsTest) { EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( frame_settings, options[i], in_range[i])); } - // Effort 10 should only work when expert options are allowed + // Effort 11 should only work when expert options are allowed EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderFrameSettingsSetOption( - frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 10)); + frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 11)); JxlEncoderAllowExpertOptions(enc.get()); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( - frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 10)); + frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 11)); // Non-existing option EXPECT_EQ(JXL_ENC_ERROR, @@ -438,14 +441,14 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_PHOTON_NOISE, 50.0f)); - VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 2500, false); + VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3700, false); } { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE)); VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3000, false); @@ -456,16 +459,16 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetFrameDistance(frame_settings, 0.5)); - VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3030, false); + VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3130, false); EXPECT_EQ(0.5, enc->last_used_cparams.butteraugli_distance); } { JxlEncoderPtr enc = JxlEncoderMake(nullptr); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); // Disallowed negative distance EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderSetFrameDistance(frame_settings, -1)); } @@ -474,7 +477,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_DECODING_SPEED, 2)); @@ -486,7 +489,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_GROUP_ORDER, 100)); @@ -506,7 +509,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_RESPONSIVE, 0)); @@ -519,7 +522,7 @@ TEST(EncodeTest, frame_settingsTest) { EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC, 2)); - VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 2830, + VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 3430, /*lossy_use_original_profile=*/false); EXPECT_EQ(false, enc->last_used_cparams.responsive); EXPECT_EQ(jxl::Override::kOn, enc->last_used_cparams.progressive_mode); @@ -530,7 +533,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ( JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetFloatOption( @@ -543,7 +546,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetFloatOption( frame_settings, @@ -571,7 +574,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ( JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( @@ -603,7 +606,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_JPEG_RECON_CFL, 0)); @@ -615,7 +618,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_JPEG_RECON_CFL, 1)); @@ -629,14 +632,14 @@ TEST(EncodeTest, LossyEncoderUseOriginalProfileTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); ASSERT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); VerifyFrameEncoding(63, 129, enc.get(), frame_settings, 7897, true); } { JxlEncoderPtr enc = JxlEncoderMake(nullptr); ASSERT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC, 2)); @@ -646,7 +649,7 @@ TEST(EncodeTest, LossyEncoderUseOriginalProfileTest) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); ASSERT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); ASSERT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption( frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 8)); @@ -787,7 +790,7 @@ TEST(EncodeTest, SingleFrameBoundedJXLCTest) { EXPECT_NE(nullptr, enc.get()); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderUseContainer(enc.get(), true)); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); size_t xsize = 71; size_t ysize = 23; @@ -798,12 +801,12 @@ TEST(EncodeTest, SingleFrameBoundedJXLCTest) { jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format); basic_info.xsize = xsize; basic_info.ysize = ysize; - basic_info.uses_original_profile = false; + basic_info.uses_original_profile = JXL_FALSE; EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10)); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info)); JxlColorEncoding color_encoding; JxlColorEncodingSetToSRGB(&color_encoding, - /*is_gray=*/false); + /*is_gray=*/JXL_FALSE); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc.get(), &color_encoding)); EXPECT_EQ(JXL_ENC_SUCCESS, @@ -866,17 +869,17 @@ TEST(EncodeTest, CodestreamLevelTest) { jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format); basic_info.xsize = xsize; basic_info.ysize = ysize; - basic_info.uses_original_profile = false; + basic_info.uses_original_profile = 0; JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10)); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info)); JxlColorEncoding color_encoding; - JxlColorEncodingSetToSRGB(&color_encoding, - /*is_gray=*/pixel_format.num_channels < 3); + JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3); + JxlColorEncodingSetToSRGB(&color_encoding, is_gray); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc.get(), &color_encoding)); EXPECT_EQ(JXL_ENC_SUCCESS, @@ -915,7 +918,7 @@ TEST(EncodeTest, CodestreamLevelVerificationTest) { jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format); basic_info.xsize = 64; basic_info.ysize = 64; - basic_info.uses_original_profile = false; + basic_info.uses_original_profile = JXL_FALSE; JxlEncoderPtr enc = JxlEncoderMake(nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info)); @@ -943,7 +946,7 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderStoreJPEGMetadata(enc.get(), JXL_TRUE)); EXPECT_EQ(JXL_ENC_SUCCESS, @@ -983,7 +986,7 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(ProgressiveJPEGReconstructionTest)) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); frame_settings->values.cparams.progressive_mode = jxl::Override::kOn; @@ -1043,7 +1046,7 @@ TEST(EncodeTest, BasicInfoTest) { EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); size_t xsize = 1; size_t ysize = 1; JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -1052,8 +1055,8 @@ TEST(EncodeTest, BasicInfoTest) { jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format); basic_info.xsize = xsize; basic_info.ysize = ysize; - basic_info.uses_original_profile = false; - basic_info.have_animation = true; + basic_info.uses_original_profile = 0; + basic_info.have_animation = 1; basic_info.intensity_target = 123.4; basic_info.min_nits = 5.0; basic_info.linear_below = 12.7; @@ -1067,7 +1070,7 @@ TEST(EncodeTest, BasicInfoTest) { EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10)); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info)); JxlColorEncoding color_encoding; - JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false); + JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc.get(), &color_encoding)); @@ -1155,7 +1158,7 @@ TEST(EncodeTest, AnimationHeaderTest) { EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); size_t xsize = 1; size_t ysize = 1; JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -1164,14 +1167,14 @@ TEST(EncodeTest, AnimationHeaderTest) { jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format); basic_info.xsize = xsize; basic_info.ysize = ysize; - basic_info.have_animation = true; + basic_info.have_animation = JXL_TRUE; basic_info.animation.tps_numerator = 1000; basic_info.animation.tps_denominator = 1; basic_info.animation.have_timecodes = JXL_TRUE; EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10)); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info)); JxlColorEncoding color_encoding; - JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false); + JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc.get(), &color_encoding)); @@ -1258,7 +1261,7 @@ TEST(EncodeTest, CroppedFrameTest) { EXPECT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); size_t xsize = 300; size_t ysize = 300; JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -1273,7 +1276,7 @@ TEST(EncodeTest, CroppedFrameTest) { EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10)); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info)); JxlColorEncoding color_encoding; - JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false); + JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc.get(), &color_encoding)); @@ -1358,7 +1361,7 @@ TEST_P(EncodeBoxTest, JXL_BOXES_TEST(BoxTest)) { EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderUseBoxes(enc.get())); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); size_t xsize = 50; size_t ysize = 17; JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -1367,12 +1370,12 @@ TEST_P(EncodeBoxTest, JXL_BOXES_TEST(BoxTest)) { jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format); basic_info.xsize = xsize; basic_info.ysize = ysize; - basic_info.uses_original_profile = false; + basic_info.uses_original_profile = JXL_FALSE; EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10)); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info)); JxlColorEncoding color_encoding; JxlColorEncodingSetToSRGB(&color_encoding, - /*is_gray=*/false); + /*is_gray=*/JXL_FALSE); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc.get(), &color_encoding)); @@ -1386,7 +1389,8 @@ TEST_P(EncodeBoxTest, JXL_BOXES_TEST(BoxTest)) { const uint8_t* exif_data = reinterpret_cast<const uint8_t*>(exif_test_string); // Skip the 4 zeroes for strlen const size_t exif_size = 4 + strlen(exif_test_string + 4); - JxlEncoderAddBox(enc.get(), "Exif", exif_data, exif_size, compress_box); + JxlEncoderAddBox(enc.get(), "Exif", exif_data, exif_size, + TO_JXL_BOOL(compress_box)); // Write to output ProcessEncoder(enc.get(), compressed, next_out, avail_out); @@ -1405,7 +1409,8 @@ TEST_P(EncodeBoxTest, JXL_BOXES_TEST(BoxTest)) { constexpr const char* xml_test_string = "<some random xml data>"; const uint8_t* xml_data = reinterpret_cast<const uint8_t*>(xml_test_string); size_t xml_size = strlen(xml_test_string); - JxlEncoderAddBox(enc.get(), "XML ", xml_data, xml_size, compress_box); + JxlEncoderAddBox(enc.get(), "XML ", xml_data, xml_size, + TO_JXL_BOOL(compress_box)); // Indicate this is the last box JxlEncoderCloseBoxes(enc.get()); @@ -1495,7 +1500,7 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGFrameTest)) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 1); if (!skip_basic_info) { @@ -1503,13 +1508,13 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGFrameTest)) { JxlEncoderInitBasicInfo(&basic_info); basic_info.xsize = orig_io.xsize(); basic_info.ysize = orig_io.ysize(); - basic_info.uses_original_profile = true; + basic_info.uses_original_profile = JXL_TRUE; EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info)); } if (!skip_color_encoding) { JxlColorEncoding color_encoding; - JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false); + JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc.get(), &color_encoding)); } @@ -1696,14 +1701,16 @@ class JxlChunkedFrameInputSourceAdapter { struct StreamingTestParam { size_t bitmask; - bool use_container() const { return bitmask & 0x1; } - bool return_large_buffers() const { return bitmask & 0x2; } - bool multiple_frames() const { return bitmask & 0x4; } - bool fast_lossless() const { return bitmask & 0x8; } - bool can_seek() const { return bitmask & 0x10; } - bool with_extra_channels() const { return bitmask & 0x20; } - bool color_includes_alpha() const { return bitmask & 0x40; } - bool onegroup() const { return bitmask & 0x80; } + bool use_container() const { return static_cast<bool>(bitmask & 0x1); } + bool return_large_buffers() const { return static_cast<bool>(bitmask & 0x2); } + bool multiple_frames() const { return static_cast<bool>(bitmask & 0x4); } + bool fast_lossless() const { return static_cast<bool>(bitmask & 0x8); } + bool can_seek() const { return static_cast<bool>(bitmask & 0x10); } + bool with_extra_channels() const { return static_cast<bool>(bitmask & 0x20); } + bool color_includes_alpha() const { + return static_cast<bool>(bitmask & 0x40); + } + bool onegroup() const { return static_cast<bool>(bitmask & 0x80); } bool is_lossless() const { return fast_lossless(); } @@ -1773,8 +1780,9 @@ class EncoderStreamingTest : public testing::TestWithParam<StreamingTestParam> { bool include_alpha, bool is_lossless) { basic_info.xsize = xsize; basic_info.ysize = ysize; - basic_info.num_extra_channels = number_extra_channels + include_alpha; - basic_info.uses_original_profile = is_lossless; + basic_info.num_extra_channels = + number_extra_channels + (include_alpha ? 1 : 0); + basic_info.uses_original_profile = TO_JXL_BOOL(is_lossless); } static void SetupEncoder(JxlEncoderFrameSettings* frame_settings, @@ -1791,7 +1799,7 @@ class EncoderStreamingTest : public testing::TestWithParam<StreamingTestParam> { frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, 1)); } JxlColorEncoding color_encoding; - JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false); + JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/JXL_FALSE); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc, &color_encoding)); EXPECT_EQ(JXL_ENC_SUCCESS, @@ -1825,7 +1833,7 @@ class EncoderStreamingTest : public testing::TestWithParam<StreamingTestParam> { // Copy pixel data here because it is only guaranteed to be available // during the call to JxlEncoderAddImageFrame(). std::vector<uint8_t> pixels(frame.pixels_size); - memcpy(&pixels[0], frame.pixels(), pixels.size()); + memcpy(pixels.data(), frame.pixels(), pixels.size()); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderAddImageFrame(frame_settings, &frame.format, pixels.data(), pixels.size())); @@ -1834,7 +1842,7 @@ class EncoderStreamingTest : public testing::TestWithParam<StreamingTestParam> { // Copy pixel data here because it is only guaranteed to be available // during the call to JxlEncoderSetExtraChannelBuffer(). std::vector<uint8_t> ec_pixels(ec_frame.pixels_size); - memcpy(&ec_pixels[0], ec_frame.pixels(), ec_pixels.size()); + memcpy(ec_pixels.data(), ec_frame.pixels(), ec_pixels.size()); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetExtraChannelBuffer( frame_settings, &ec_frame.format, ec_pixels.data(), ec_pixels.size(), i)); @@ -1885,7 +1893,7 @@ TEST_P(EncoderStreamingTest, OutputCallback) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); ASSERT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); SetupEncoder(frame_settings, p, basic_info, number_extra_channels, false); SetupInputNonStreaming(frame_settings, p, number_extra_channels, frame, ec_frame); @@ -1900,7 +1908,7 @@ TEST_P(EncoderStreamingTest, OutputCallback) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); ASSERT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); SetupEncoder(frame_settings, p, basic_info, number_extra_channels, true); SetupInputNonStreaming(frame_settings, p, number_extra_channels, frame, ec_frame); @@ -1938,7 +1946,7 @@ TEST_P(EncoderStreamingTest, ChunkedFrame) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); ASSERT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); SetupEncoder(frame_settings, p, basic_info, number_extra_channels, false); SetupInputNonStreaming(frame_settings, p, number_extra_channels, frame, ec_frame); @@ -1952,7 +1960,7 @@ TEST_P(EncoderStreamingTest, ChunkedFrame) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); ASSERT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); SetupEncoder(frame_settings, p, basic_info, number_extra_channels, true); SetupInputStreaming(frame_settings, p, number_extra_channels, frame, ec_frame); @@ -1988,7 +1996,7 @@ TEST_P(EncoderStreamingTest, ChunkedAndOutputCallback) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); ASSERT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); SetupEncoder(frame_settings, p, basic_info, number_extra_channels, false); SetupInputNonStreaming(frame_settings, p, number_extra_channels, frame, ec_frame); @@ -2003,7 +2011,7 @@ TEST_P(EncoderStreamingTest, ChunkedAndOutputCallback) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); ASSERT_NE(nullptr, enc.get()); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); SetupEncoder(frame_settings, p, basic_info, number_extra_channels, true); JxlStreamingAdapter streaming_adapter = JxlStreamingAdapter(enc.get(), p.return_large_buffers(), p.can_seek()); @@ -2049,7 +2057,7 @@ TEST(EncoderTest, CMYK) { JxlEncoderStruct* enc = enc_ptr.get(); ASSERT_NE(nullptr, enc); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc, NULL); + JxlEncoderFrameSettingsCreate(enc, nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info)); JxlExtraChannelInfo channel_info; diff --git a/third_party/jpeg-xl/lib/jxl/entropy_coder.cc b/third_party/jpeg-xl/lib/jxl/entropy_coder.cc index a90ed0257a..5dc101b36f 100644 --- a/third_party/jpeg-xl/lib/jxl/entropy_coder.cc +++ b/third_party/jpeg-xl/lib/jxl/entropy_coder.cc @@ -33,7 +33,7 @@ Status DecodeBlockCtxMap(BitReader* br, BlockCtxMap* block_ctx_map) { auto& dct = block_ctx_map->dc_thresholds; auto& qft = block_ctx_map->qf_thresholds; auto& ctx_map = block_ctx_map->ctx_map; - bool is_default = br->ReadFixedBits<1>(); + bool is_default = static_cast<bool>(br->ReadFixedBits<1>()); if (is_default) { *block_ctx_map = BlockCtxMap(); return true; diff --git a/third_party/jpeg-xl/lib/jxl/fast_dct-inl.h b/third_party/jpeg-xl/lib/jxl/fast_dct-inl.h index de1f845901..e315200b0c 100644 --- a/third_party/jpeg-xl/lib/jxl/fast_dct-inl.h +++ b/third_party/jpeg-xl/lib/jxl/fast_dct-inl.h @@ -191,7 +191,7 @@ HWY_NOINLINE void TestFastIDCT() { } } printf("max error: %f mantissa bits: %d\n", max_error, - 14 - (int)integer_bits); + 14 - static_cast<int>(integer_bits)); #endif } diff --git a/third_party/jpeg-xl/lib/jxl/fast_dct.cc b/third_party/jpeg-xl/lib/jxl/fast_dct.cc deleted file mode 100644 index d796018fd0..0000000000 --- a/third_party/jpeg-xl/lib/jxl/fast_dct.cc +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) the JPEG XL 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. - -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jxl/fast_dct.cc" -#include <hwy/foreach_target.h> -#include <hwy/highway.h> - -#include "lib/jxl/base/random.h" -#include "lib/jxl/dct-inl.h" -#include "lib/jxl/fast_dct-inl.h" -HWY_BEFORE_NAMESPACE(); -namespace jxl { -namespace HWY_NAMESPACE { -namespace { -void BenchmarkFloatIDCT32x32() { TestFloatIDCT<32, 32>(); } -void BenchmarkFastIDCT32x32() { TestFastIDCT<32, 32>(); } -} // namespace -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE -} // namespace jxl -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jxl { -HWY_EXPORT(BenchmarkFloatIDCT32x32); -HWY_EXPORT(BenchmarkFastIDCT32x32); -void BenchmarkFloatIDCT32x32() { - HWY_DYNAMIC_DISPATCH(BenchmarkFloatIDCT32x32)(); -} -void BenchmarkFastIDCT32x32() { - HWY_DYNAMIC_DISPATCH(BenchmarkFastIDCT32x32)(); -} -} // namespace jxl -#endif diff --git a/third_party/jpeg-xl/lib/jxl/fast_dct.h b/third_party/jpeg-xl/lib/jxl/fast_dct.h deleted file mode 100644 index 641933d8a0..0000000000 --- a/third_party/jpeg-xl/lib/jxl/fast_dct.h +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright (c) the JPEG XL 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. - -namespace jxl { -void BenchmarkFloatIDCT32x32(); -void BenchmarkFastIDCT32x32(); -} // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/fast_dct_test.cc b/third_party/jpeg-xl/lib/jxl/fast_dct_test.cc index a55b67afb2..417e202988 100644 --- a/third_party/jpeg-xl/lib/jxl/fast_dct_test.cc +++ b/third_party/jpeg-xl/lib/jxl/fast_dct_test.cc @@ -12,7 +12,6 @@ #include "lib/jxl/base/random.h" #include "lib/jxl/dct-inl.h" #include "lib/jxl/fast_dct-inl.h" -#include "lib/jxl/fast_dct.h" #include "lib/jxl/testing.h" #include "lib/jxl/transpose-inl.h" @@ -21,9 +20,13 @@ #include <hwy/tests/hwy_gtest.h> HWY_BEFORE_NAMESPACE(); namespace jxl { + namespace HWY_NAMESPACE { namespace { +void BenchmarkFloatIDCT32x32() { TestFloatIDCT<32, 32>(); } +void BenchmarkFastIDCT32x32() { TestFastIDCT<32, 32>(); } + template <size_t N, size_t M> HWY_NOINLINE void TestFastTranspose() { #if HWY_TARGET == HWY_NEON @@ -370,8 +373,8 @@ HWY_EXPORT_AND_TEST_P(FastDCTTargetTest, TestFloatIDCT256x256); HWY_EXPORT_AND_TEST_P(FastDCTTargetTest, TestFastIDCT256x256); */ -TEST(FastDCTTest, TestWrapperFloat) { BenchmarkFloatIDCT32x32(); } -TEST(FastDCTTest, TestWrapperFast) { BenchmarkFastIDCT32x32(); } +HWY_EXPORT_AND_TEST_P(FastDCTTargetTest, BenchmarkFloatIDCT32x32); +HWY_EXPORT_AND_TEST_P(FastDCTTargetTest, BenchmarkFastIDCT32x32); } // namespace jxl #endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jxl/fast_math_test.cc b/third_party/jpeg-xl/lib/jxl/fast_math_test.cc index 868e1b72f4..b242dbe575 100644 --- a/third_party/jpeg-xl/lib/jxl/fast_math_test.cc +++ b/third_party/jpeg-xl/lib/jxl/fast_math_test.cc @@ -167,7 +167,8 @@ HWY_NOINLINE void TestFastXYB() { for (int cr = 0; cr < n; cr += kChunk) { for (int cg = 0; cg < n; cg += kChunk) { for (int cb = 0; cb < n; cb += kChunk) { - Image3F chunk(kChunk * kChunk, kChunk); + JXL_ASSIGN_OR_DIE(Image3F chunk, + Image3F::Create(kChunk * kChunk, kChunk)); for (int ir = 0; ir < kChunk; ir++) { for (int ig = 0; ig < kChunk; ig++) { for (int ib = 0; ib < kChunk; ib++) { @@ -181,9 +182,10 @@ HWY_NOINLINE void TestFastXYB() { } } ib.SetFromImage(std::move(chunk), ColorEncoding::SRGB()); - Image3F xyb(kChunk * kChunk, kChunk); + JXL_ASSIGN_OR_DIE(Image3F xyb, + Image3F::Create(kChunk * kChunk, kChunk)); std::vector<uint8_t> roundtrip(kChunk * kChunk * kChunk * 3); - ToXYB(ib, nullptr, &xyb, *JxlGetDefaultCms()); + JXL_CHECK(ToXYB(ib, nullptr, &xyb, *JxlGetDefaultCms())); for (int y = 0; y < kChunk; y++) { const float* xyba[4] = {xyb.PlaneRow(0, y), xyb.PlaneRow(1, y), xyb.PlaneRow(2, y), nullptr}; diff --git a/third_party/jpeg-xl/lib/jxl/fields.cc b/third_party/jpeg-xl/lib/jxl/fields.cc index 746d7e4d30..6bb5eae25d 100644 --- a/third_party/jpeg-xl/lib/jxl/fields.cc +++ b/third_party/jpeg-xl/lib/jxl/fields.cc @@ -109,7 +109,7 @@ struct SetDefaultVisitor : public VisitorBase { class AllDefaultVisitor : public VisitorBase { public: - explicit AllDefaultVisitor() : VisitorBase() {} + explicit AllDefaultVisitor() = default; Status Bits(const size_t bits, const uint32_t default_value, uint32_t* JXL_RESTRICT value) override { @@ -148,7 +148,7 @@ class AllDefaultVisitor : public VisitorBase { class ReadVisitor : public VisitorBase { public: - explicit ReadVisitor(BitReader* reader) : VisitorBase(), reader_(reader) {} + explicit ReadVisitor(BitReader* reader) : reader_(reader) {} Status Bits(const size_t bits, const uint32_t /*default_value*/, uint32_t* JXL_RESTRICT value) override { @@ -267,7 +267,8 @@ class ReadVisitor : public VisitorBase { uint64_t total_extension_bits_ = 0; size_t pos_after_ext_size_ = 0; // 0 iff extensions == 0. - friend Status jxl::CheckHasEnoughBits(Visitor*, size_t); + friend Status jxl::CheckHasEnoughBits(Visitor* /* visitor */, + size_t /* bits */); }; class MaxBitsVisitor : public VisitorBase { @@ -321,7 +322,7 @@ class MaxBitsVisitor : public VisitorBase { class CanEncodeVisitor : public VisitorBase { public: - explicit CanEncodeVisitor() : VisitorBase() {} + explicit CanEncodeVisitor() = default; Status Bits(const size_t bits, const uint32_t /*default_value*/, uint32_t* JXL_RESTRICT value) override { diff --git a/third_party/jpeg-xl/lib/jxl/fields.h b/third_party/jpeg-xl/lib/jxl/fields.h index d05fe4517e..a8d8d8671a 100644 --- a/third_party/jpeg-xl/lib/jxl/fields.h +++ b/third_party/jpeg-xl/lib/jxl/fields.h @@ -298,7 +298,7 @@ class ExtensionStates { class VisitorBase : public Visitor { public: - explicit VisitorBase() {} + explicit VisitorBase() = default; ~VisitorBase() override { JXL_ASSERT(depth_ == 0); } // This is the only call site of Fields::VisitFields. diff --git a/third_party/jpeg-xl/lib/jxl/fields_test.cc b/third_party/jpeg-xl/lib/jxl/fields_test.cc index b178a6bd6a..0584458d07 100644 --- a/third_party/jpeg-xl/lib/jxl/fields_test.cc +++ b/third_party/jpeg-xl/lib/jxl/fields_test.cc @@ -200,7 +200,8 @@ TEST(FieldsTest, TestRoundtripSize) { SizeHeader size; ASSERT_TRUE(size.Set(123 + 77 * i, 7 + i)); - size_t extension_bits = 999, total_bits = 999; // Initialize as garbage. + size_t extension_bits = 999; + size_t total_bits = 999; // Initialize as garbage. ASSERT_TRUE(Bundle::CanEncode(size, &extension_bits, &total_bits)); EXPECT_EQ(0u, extension_bits); @@ -230,7 +231,8 @@ TEST(FieldsTest, TestCropRect) { f.frame_origin.y0 = i; f.frame_size.xsize = 1000 + i; f.frame_size.ysize = 1000 + i; - size_t extension_bits = 0, total_bits = 0; + size_t extension_bits = 0; + size_t total_bits = 0; ASSERT_TRUE(Bundle::CanEncode(f, &extension_bits, &total_bits)); EXPECT_EQ(0u, extension_bits); EXPECT_GE(total_bits, 9u); @@ -241,7 +243,8 @@ TEST(FieldsTest, TestPreview) { for (uint32_t i = 1; i < 4360; ++i) { PreviewHeader p; ASSERT_TRUE(p.Set(i, i)); - size_t extension_bits = 0, total_bits = 0; + size_t extension_bits = 0; + size_t total_bits = 0; ASSERT_TRUE(Bundle::CanEncode(p, &extension_bits, &total_bits)); EXPECT_EQ(0u, extension_bits); EXPECT_GE(total_bits, 6u); @@ -254,7 +257,8 @@ TEST(FieldsTest, TestRoundtripFrame) { FrameHeader h(&metadata); h.extensions = 0x800; - size_t extension_bits = 999, total_bits = 999; // Initialize as garbage. + size_t extension_bits = 999; + size_t total_bits = 999; // Initialize as garbage. ASSERT_TRUE(Bundle::CanEncode(h, &extension_bits, &total_bits)); EXPECT_EQ(0u, extension_bits); BitWriter writer; @@ -277,7 +281,8 @@ TEST(FieldsTest, TestRoundtripFrame) { TEST(FieldsTest, TestOutOfRange) { SizeHeader h; ASSERT_TRUE(h.Set(0xFFFFFFFFull, 0xFFFFFFFFull)); - size_t extension_bits = 999, total_bits = 999; // Initialize as garbage. + size_t extension_bits = 999; + size_t total_bits = 999; // Initialize as garbage. ASSERT_FALSE(Bundle::CanEncode(h, &extension_bits, &total_bits)); } #endif @@ -315,12 +320,12 @@ struct NewBundle : public Fields { visitor->U32(Bits(7), Bits(12), Bits(16), Bits(32), 0, &old_large)); JXL_QUIET_RETURN_IF_ERROR(visitor->BeginExtensions(&extensions)); - if (visitor->Conditional(extensions & 1)) { + if (visitor->Conditional((extensions & 1) != 0)) { JXL_QUIET_RETURN_IF_ERROR( visitor->U32(Val(2), Bits(2), Bits(3), Bits(4), 2, &new_small)); JXL_QUIET_RETURN_IF_ERROR(visitor->F16(-2.0f, &new_f)); } - if (visitor->Conditional(extensions & 2)) { + if (visitor->Conditional((extensions & 2) != 0)) { JXL_QUIET_RETURN_IF_ERROR( visitor->U32(Bits(9), Bits(12), Bits(16), Bits(32), 0, &new_large)); } @@ -349,7 +354,8 @@ TEST(FieldsTest, TestNewDecoderOldData) { const size_t kMaxOutBytes = 999; BitWriter writer; // Make sure values are initialized by code under test. - size_t extension_bits = 12345, total_bits = 12345; + size_t extension_bits = 12345; + size_t total_bits = 12345; ASSERT_TRUE(Bundle::CanEncode(old_bundle, &extension_bits, &total_bits)); ASSERT_LE(total_bits, kMaxOutBytes * kBitsPerByte); EXPECT_EQ(0u, extension_bits); @@ -393,7 +399,8 @@ TEST(FieldsTest, TestOldDecoderNewData) { constexpr size_t kMaxOutBytes = 999; BitWriter writer; // Make sure values are initialized by code under test. - size_t extension_bits = 12345, total_bits = 12345; + size_t extension_bits = 12345; + size_t total_bits = 12345; ASSERT_TRUE(Bundle::CanEncode(new_bundle, &extension_bits, &total_bits)); EXPECT_NE(0u, extension_bits); AuxOut aux_out; diff --git a/third_party/jpeg-xl/lib/jxl/frame_header.cc b/third_party/jpeg-xl/lib/jxl/frame_header.cc index a9e79ff1b8..6648e6d8cc 100644 --- a/third_party/jpeg-xl/lib/jxl/frame_header.cc +++ b/third_party/jpeg-xl/lib/jxl/frame_header.cc @@ -397,16 +397,18 @@ Status FrameHeader::VisitFields(Visitor* JXL_RESTRICT visitor) { } else if (visitor->Conditional(frame_type == FrameType::kReferenceOnly)) { JXL_QUIET_RETURN_IF_ERROR( visitor->Bool(true, &save_before_color_transform)); + size_t xsize = custom_size_or_origin ? frame_size.xsize + : nonserialized_metadata->xsize(); + size_t ysize = custom_size_or_origin ? frame_size.ysize + : nonserialized_metadata->ysize(); if (!save_before_color_transform && - (frame_size.xsize < nonserialized_metadata->xsize() || - frame_size.ysize < nonserialized_metadata->ysize() || - frame_origin.x0 != 0 || frame_origin.y0 != 0)) { + (xsize < nonserialized_metadata->xsize() || + ysize < nonserialized_metadata->ysize() || frame_origin.x0 != 0 || + frame_origin.y0 != 0)) { return JXL_FAILURE( "non-patch reference frame with invalid crop: %" PRIuS "x%" PRIuS "%+d%+d", - static_cast<size_t>(frame_size.xsize), - static_cast<size_t>(frame_size.ysize), - static_cast<int>(frame_origin.x0), + xsize, ysize, static_cast<int>(frame_origin.x0), static_cast<int>(frame_origin.y0)); } } diff --git a/third_party/jpeg-xl/lib/jxl/frame_header.h b/third_party/jpeg-xl/lib/jxl/frame_header.h index b246bf813e..30c62d5f27 100644 --- a/third_party/jpeg-xl/lib/jxl/frame_header.h +++ b/third_party/jpeg-xl/lib/jxl/frame_header.h @@ -82,8 +82,8 @@ struct YCbCrChromaSubsampling : public Fields { Status VisitFields(Visitor* JXL_RESTRICT visitor) override { // TODO(veluca): consider allowing 4x downsamples - for (size_t i = 0; i < 3; i++) { - JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(2, 0, &channel_mode_[i])); + for (uint32_t& ch : channel_mode_) { + JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(2, 0, &ch)); } Recompute(); return true; @@ -152,9 +152,9 @@ struct YCbCrChromaSubsampling : public Fields { void Recompute() { maxhs_ = 0; maxvs_ = 0; - for (size_t i = 0; i < 3; i++) { - maxhs_ = std::max(maxhs_, kHShift[channel_mode_[i]]); - maxvs_ = std::max(maxvs_, kVShift[channel_mode_[i]]); + for (uint32_t ch : channel_mode_) { + maxhs_ = std::max(maxhs_, kHShift[ch]); + maxvs_ = std::max(maxvs_, kVShift[ch]); } } static const uint8_t kHShift[4]; diff --git a/third_party/jpeg-xl/lib/jxl/gradient_test.cc b/third_party/jpeg-xl/lib/jxl/gradient_test.cc index 055a419f5b..d2c83619fc 100644 --- a/third_party/jpeg-xl/lib/jxl/gradient_test.cc +++ b/third_party/jpeg-xl/lib/jxl/gradient_test.cc @@ -9,7 +9,6 @@ #include <stdint.h> #include <algorithm> -#include <array> #include <cmath> #include <utility> #include <vector> @@ -45,10 +44,10 @@ double PointLineDist(double x0, double y0, double x1, double y1, double x, // angle in which the change direction happens. Image3F GenerateTestGradient(uint32_t color0, uint32_t color1, double angle, size_t xsize, size_t ysize) { - Image3F image(xsize, ysize); + JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(xsize, ysize)); - double x0 = xsize / 2; - double y0 = ysize / 2; + double x0 = xsize / 2.0; + double y0 = ysize / 2.0; double x1 = x0 + std::sin(angle / 360.0 * 2.0 * kPi); double y1 = y0 + std::cos(angle / 360.0 * 2.0 * kPi); @@ -78,63 +77,60 @@ Image3F GenerateTestGradient(uint32_t color0, uint32_t color1, double angle, // delta and right delta (top/bottom for vertical direction). // The radius over which the derivative is computed is only 1 pixel and it only // checks two angles (hor and ver), but this approximation works well enough. -static ImageF Gradient2(const ImageF& image) { +Image3F Gradient2(const Image3F& image) { size_t xsize = image.xsize(); size_t ysize = image.ysize(); - ImageF image2(image.xsize(), image.ysize()); - for (size_t y = 1; y + 1 < ysize; y++) { - const auto* JXL_RESTRICT row0 = image.Row(y - 1); - const auto* JXL_RESTRICT row1 = image.Row(y); - const auto* JXL_RESTRICT row2 = image.Row(y + 1); - auto* row_out = image2.Row(y); - for (size_t x = 1; x + 1 < xsize; x++) { - float ddx = (row1[x] - row1[x - 1]) - (row1[x + 1] - row1[x]); - float ddy = (row1[x] - row0[x]) - (row2[x] - row1[x]); - row_out[x] = std::max(fabsf(ddx), fabsf(ddy)); - } - } - // Copy to the borders - if (ysize > 2) { - auto* JXL_RESTRICT row0 = image2.Row(0); - const auto* JXL_RESTRICT row1 = image2.Row(1); - const auto* JXL_RESTRICT row2 = image2.Row(ysize - 2); - auto* JXL_RESTRICT row3 = image2.Row(ysize - 1); - for (size_t x = 1; x + 1 < xsize; x++) { - row0[x] = row1[x]; - row3[x] = row2[x]; - } - } else { - const auto* row0_in = image.Row(0); - const auto* row1_in = image.Row(ysize - 1); - auto* row0_out = image2.Row(0); - auto* row1_out = image2.Row(ysize - 1); - for (size_t x = 1; x + 1 < xsize; x++) { - // Image too narrow, take first derivative instead - row0_out[x] = row1_out[x] = fabsf(row0_in[x] - row1_in[x]); + JXL_ASSIGN_OR_DIE(Image3F image2, Image3F::Create(xsize, ysize)); + for (size_t c = 0; c < 3; ++c) { + for (size_t y = 1; y + 1 < ysize; y++) { + const auto* JXL_RESTRICT row0 = image.ConstPlaneRow(c, y - 1); + const auto* JXL_RESTRICT row1 = image.ConstPlaneRow(c, y); + const auto* JXL_RESTRICT row2 = image.ConstPlaneRow(c, y + 1); + auto* row_out = image2.PlaneRow(c, y); + for (size_t x = 1; x + 1 < xsize; x++) { + float ddx = (row1[x] - row1[x - 1]) - (row1[x + 1] - row1[x]); + float ddy = (row1[x] - row0[x]) - (row2[x] - row1[x]); + row_out[x] = std::max(fabsf(ddx), fabsf(ddy)); + } } - } - if (xsize > 2) { - for (size_t y = 0; y < ysize; y++) { - auto* row = image2.Row(y); - row[0] = row[1]; - row[xsize - 1] = row[xsize - 2]; + // Copy to the borders + if (ysize > 2) { + auto* JXL_RESTRICT row0 = image2.PlaneRow(c, 0); + const auto* JXL_RESTRICT row1 = image2.PlaneRow(c, 1); + const auto* JXL_RESTRICT row2 = image2.PlaneRow(c, ysize - 2); + auto* JXL_RESTRICT row3 = image2.PlaneRow(c, ysize - 1); + for (size_t x = 1; x + 1 < xsize; x++) { + row0[x] = row1[x]; + row3[x] = row2[x]; + } + } else { + const auto* row0_in = image.ConstPlaneRow(c, 0); + const auto* row1_in = image.ConstPlaneRow(c, ysize - 1); + auto* row0_out = image2.PlaneRow(c, 0); + auto* row1_out = image2.PlaneRow(c, ysize - 1); + for (size_t x = 1; x + 1 < xsize; x++) { + // Image too narrow, take first derivative instead + row0_out[x] = row1_out[x] = fabsf(row0_in[x] - row1_in[x]); + } } - } else { - for (size_t y = 0; y < ysize; y++) { - const auto* JXL_RESTRICT row_in = image.Row(y); - auto* row_out = image2.Row(y); - // Image too narrow, take first derivative instead - row_out[0] = row_out[xsize - 1] = fabsf(row_in[0] - row_in[xsize - 1]); + if (xsize > 2) { + for (size_t y = 0; y < ysize; y++) { + auto* row = image2.PlaneRow(c, y); + row[0] = row[1]; + row[xsize - 1] = row[xsize - 2]; + } + } else { + for (size_t y = 0; y < ysize; y++) { + const auto* JXL_RESTRICT row_in = image.ConstPlaneRow(c, y); + auto* row_out = image2.PlaneRow(c, y); + // Image too narrow, take first derivative instead + row_out[0] = row_out[xsize - 1] = fabsf(row_in[0] - row_in[xsize - 1]); + } } } return image2; } -static Image3F Gradient2(const Image3F& image) { - return Image3F(Gradient2(image.Plane(0)), Gradient2(image.Plane(1)), - Gradient2(image.Plane(2))); -} - /* Tests if roundtrip with jxl on a gradient image doesn't cause banding. Only tests if use_gradient is true. Set to false for debugging to see the @@ -173,17 +169,19 @@ void TestGradient(ThreadPool* pool, uint32_t color0, uint32_t color1, // butteraugli_distance). Image3F gradient2 = Gradient2(*io2.Main().color()); - std::array<float, 3> image_max; - Image3Max(gradient2, &image_max); - // TODO(jyrki): These values used to work with 0.2, 0.2, 0.2. - EXPECT_LE(image_max[0], 3.15); - EXPECT_LE(image_max[1], 1.72); - EXPECT_LE(image_max[2], 5.05); + float image_min; + float image_max; + ImageMinMax(gradient2.Plane(0), &image_min, &image_max); + EXPECT_LE(image_max, 3.15); + ImageMinMax(gradient2.Plane(1), &image_min, &image_max); + EXPECT_LE(image_max, 1.72); + ImageMinMax(gradient2.Plane(2), &image_min, &image_max); + EXPECT_LE(image_max, 5.05); } } -static constexpr bool fast_mode = true; +constexpr bool fast_mode = true; TEST(GradientTest, SteepGradient) { test::ThreadPoolForTests pool(8); diff --git a/third_party/jpeg-xl/lib/jxl/headers.cc b/third_party/jpeg-xl/lib/jxl/headers.cc index db88147687..52e7cf5db3 100644 --- a/third_party/jpeg-xl/lib/jxl/headers.cc +++ b/third_party/jpeg-xl/lib/jxl/headers.cc @@ -17,7 +17,7 @@ struct Rational { // Returns floor(multiplicand * rational). constexpr uint32_t MulTruncate(uint32_t multiplicand) const { - return uint64_t(multiplicand) * num / den; + return static_cast<uint64_t>(multiplicand) * num / den; } uint32_t num; diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec.cc b/third_party/jpeg-xl/lib/jxl/icc_codec.cc index a1f118ebfb..8501c684ac 100644 --- a/third_party/jpeg-xl/lib/jxl/icc_codec.cc +++ b/third_party/jpeg-xl/lib/jxl/icc_codec.cc @@ -33,7 +33,8 @@ void Shuffle(uint8_t* data, size_t size, size_t width) { size_t height = (size + width - 1) / width; // amount of rows of output PaddedBytes result(size); // i = output index, j input index - size_t s = 0, j = 0; + size_t s = 0; + size_t j = 0; for (size_t i = 0; i < size; i++) { result[i] = data[j]; j += height; @@ -55,7 +56,8 @@ uint64_t DecodeVarInt(const uint8_t* input, size_t inputSize, size_t* pos) { size_t i; uint64_t ret = 0; for (i = 0; *pos + i < inputSize && i < 10; ++i) { - ret |= uint64_t(input[*pos + i] & 127) << uint64_t(7 * i); + ret |= static_cast<uint64_t>(input[*pos + i] & 127) + << static_cast<uint64_t>(7 * i); // If the next-byte flag is not set, stop if ((input[*pos + i] & 128) == 0) break; } diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec.h b/third_party/jpeg-xl/lib/jxl/icc_codec.h index 87e523a575..8b880c7d3b 100644 --- a/third_party/jpeg-xl/lib/jxl/icc_codec.h +++ b/third_party/jpeg-xl/lib/jxl/icc_codec.h @@ -39,12 +39,6 @@ struct ICCReader { PaddedBytes decompressed_; }; -// Exposed only for testing -Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result); - -// Exposed only for testing -Status UnpredictICC(const uint8_t* enc, size_t size, PaddedBytes* result); - } // namespace jxl #endif // LIB_JXL_ICC_CODEC_H_ diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc b/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc index d420567b6f..1cb4542687 100644 --- a/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc +++ b/third_party/jpeg-xl/lib/jxl/icc_codec_common.cc @@ -17,7 +17,7 @@ namespace jxl { namespace { -static uint8_t ByteKind1(uint8_t b) { +uint8_t ByteKind1(uint8_t b) { if ('a' <= b && b <= 'z') return 0; if ('A' <= b && b <= 'Z') return 0; if ('0' <= b && b <= '9') return 1; @@ -30,7 +30,7 @@ static uint8_t ByteKind1(uint8_t b) { return 7; } -static uint8_t ByteKind2(uint8_t b) { +uint8_t ByteKind2(uint8_t b) { if ('a' <= b && b <= 'z') return 0; if ('A' <= b && b <= 'Z') return 0; if ('0' <= b && b <= '9') return 1; @@ -105,7 +105,7 @@ const uint8_t kIccInitialHeaderPrediction[kICCHeaderSize] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; -const Span<const uint8_t> ICCInitialHeaderPrediction() { +Span<const uint8_t> ICCInitialHeaderPrediction() { return Bytes(kIccInitialHeaderPrediction); } diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec_common.h b/third_party/jpeg-xl/lib/jxl/icc_codec_common.h index 702a0e7b1f..0922d74145 100644 --- a/third_party/jpeg-xl/lib/jxl/icc_codec_common.h +++ b/third_party/jpeg-xl/lib/jxl/icc_codec_common.h @@ -95,7 +95,7 @@ void AppendKeyword(const Tag& keyword, PaddedBytes* data); Status CheckOutOfBounds(uint64_t a, uint64_t b, uint64_t size); Status CheckIs32Bit(uint64_t v); -const Span<const uint8_t> ICCInitialHeaderPrediction(); +Span<const uint8_t> ICCInitialHeaderPrediction(); void ICCPredictHeader(const uint8_t* icc, size_t size, uint8_t* header, size_t pos); uint8_t LinearPredictICCValue(const uint8_t* data, size_t start, size_t i, diff --git a/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc b/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc index 743aa9a30e..175b4768a0 100644 --- a/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc +++ b/third_party/jpeg-xl/lib/jxl/icc_codec_test.cc @@ -37,12 +37,12 @@ void TestProfile(const IccBytes& icc) { void TestProfile(const std::string& icc) { IccBytes data; - Bytes(icc).AppendTo(&data); + Bytes(icc).AppendTo(data); TestProfile(data); } // Valid profile from one of the images output by the decoder. -static const unsigned char kTestProfile[] = { +const unsigned char kTestProfile[] = { 0x00, 0x00, 0x03, 0x80, 0x6c, 0x63, 0x6d, 0x73, 0x04, 0x30, 0x00, 0x00, 0x6d, 0x6e, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5a, 0x20, 0x07, 0xe3, 0x00, 0x04, 0x00, 0x1d, 0x00, 0x0f, 0x00, 0x32, 0x00, 0x2e, @@ -139,7 +139,7 @@ TEST(IccCodecTest, Icc) { { IccBytes profile; - Bytes(kTestProfile, sizeof(kTestProfile)).AppendTo(&profile); + Bytes(kTestProfile, sizeof(kTestProfile)).AppendTo(profile); TestProfile(profile); } diff --git a/third_party/jpeg-xl/lib/jxl/image.cc b/third_party/jpeg-xl/lib/jxl/image.cc index 382c957799..7468dad130 100644 --- a/third_party/jpeg-xl/lib/jxl/image.cc +++ b/third_party/jpeg-xl/lib/jxl/image.cc @@ -5,123 +5,93 @@ #include "lib/jxl/image.h" -#include <algorithm> // swap +#include <algorithm> // fill, swap +#include <cstddef> +#include <cstdint> -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jxl/image.cc" -#include <hwy/foreach_target.h> -#include <hwy/highway.h> +#include "lib/jxl/base/status.h" +#include "lib/jxl/cache_aligned.h" +#include "lib/jxl/simd_util.h" +#if defined(MEMORY_SANITIZER) #include "lib/jxl/base/common.h" -#include "lib/jxl/frame_dimensions.h" -#include "lib/jxl/image_ops.h" #include "lib/jxl/sanitizers.h" +#endif -HWY_BEFORE_NAMESPACE(); namespace jxl { +namespace detail { -namespace HWY_NAMESPACE { -size_t GetVectorSize() { return HWY_LANES(uint8_t); } -// NOLINTNEXTLINE(google-readability-namespace-comments) -} // namespace HWY_NAMESPACE - -} // namespace jxl -HWY_AFTER_NAMESPACE(); - -#if HWY_ONCE -namespace jxl { namespace { -HWY_EXPORT(GetVectorSize); // Local function. +// Initializes the minimum bytes required to suppress MSAN warnings from +// legitimate vector loads/stores on the right border, where some lanes are +// uninitialized and assumed to be unused. +void InitializePadding(PlaneBase& plane, const size_t sizeof_t) { +#if defined(MEMORY_SANITIZER) + size_t xsize = plane.xsize(); + size_t ysize = plane.ysize(); + if (xsize == 0 || ysize == 0) return; -// Returns distance [bytes] between the start of two consecutive rows, a -// multiple of vector/cache line size but NOT CacheAligned::kAlias - see below. -size_t BytesPerRow(const size_t xsize, const size_t sizeof_t) { - const size_t vec_size = VectorSize(); - size_t valid_bytes = xsize * sizeof_t; + const size_t vec_size = MaxVectorSize(); + if (vec_size == 0) return; // Scalar mode: no padding needed - // Allow unaligned accesses starting at the last valid value - this may raise - // msan errors unless the user calls InitializePaddingForUnalignedAccesses. - // Skip for the scalar case because no extra lanes will be loaded. - if (vec_size != 0) { - valid_bytes += vec_size - sizeof_t; - } + const size_t valid_size = xsize * sizeof_t; + const size_t initialize_size = RoundUpTo(valid_size, vec_size); + if (valid_size == initialize_size) return; - // Round up to vector and cache line size. - const size_t align = std::max(vec_size, CacheAligned::kAlignment); - size_t bytes_per_row = RoundUpTo(valid_bytes, align); - - // During the lengthy window before writes are committed to memory, CPUs - // guard against read after write hazards by checking the address, but - // only the lower 11 bits. We avoid a false dependency between writes to - // consecutive rows by ensuring their sizes are not multiples of 2 KiB. - // Avoid2K prevents the same problem for the planes of an Image3. - if (bytes_per_row % CacheAligned::kAlias == 0) { - bytes_per_row += align; + for (size_t y = 0; y < ysize; ++y) { + uint8_t* JXL_RESTRICT row = plane.bytes() + y * plane.bytes_per_row(); +#if defined(__clang__) && \ + ((!defined(__apple_build_version__) && __clang_major__ <= 6) || \ + (defined(__apple_build_version__) && \ + __apple_build_version__ <= 10001145)) + // There's a bug in MSAN in clang-6 when handling AVX2 operations. This + // workaround allows tests to pass on MSAN, although it is slower and + // prevents MSAN warnings from uninitialized images. + std::fill(row, msan::kSanitizerSentinelByte, initialize_size); +#else + memset(row + valid_size, msan::kSanitizerSentinelByte, + initialize_size - valid_size); +#endif // clang6 } - - JXL_ASSERT(bytes_per_row % align == 0); - return bytes_per_row; +#endif // MEMORY_SANITIZER } } // namespace -size_t VectorSize() { - static size_t bytes = HWY_DYNAMIC_DISPATCH(GetVectorSize)(); - return bytes; -} - PlaneBase::PlaneBase(const size_t xsize, const size_t ysize, const size_t sizeof_t) : xsize_(static_cast<uint32_t>(xsize)), ysize_(static_cast<uint32_t>(ysize)), orig_xsize_(static_cast<uint32_t>(xsize)), - orig_ysize_(static_cast<uint32_t>(ysize)) { + orig_ysize_(static_cast<uint32_t>(ysize)), + bytes_per_row_(BytesPerRow(xsize_, sizeof_t)), + bytes_(nullptr), + sizeof_t_(sizeof_t) { + // TODO(eustas): turn to error instead of abort. JXL_CHECK(xsize == xsize_); JXL_CHECK(ysize == ysize_); JXL_ASSERT(sizeof_t == 1 || sizeof_t == 2 || sizeof_t == 4 || sizeof_t == 8); +} + +Status PlaneBase::Allocate() { + JXL_CHECK(!bytes_.get()); - bytes_per_row_ = 0; // Dimensions can be zero, e.g. for lazily-allocated images. Only allocate // if nonzero, because "zero" bytes still have padding/bookkeeping overhead. - if (xsize != 0 && ysize != 0) { - bytes_per_row_ = BytesPerRow(xsize, sizeof_t); - bytes_ = AllocateArray(bytes_per_row_ * ysize); - JXL_CHECK(bytes_.get()); - InitializePadding(sizeof_t, Padding::kRoundUp); + if (xsize_ == 0 || ysize_ == 0) { + return true; } -} - -void PlaneBase::InitializePadding(const size_t sizeof_t, Padding padding) { -#if defined(MEMORY_SANITIZER) || HWY_IDE - if (xsize_ == 0 || ysize_ == 0) return; - - const size_t vec_size = VectorSize(); - if (vec_size == 0) return; // Scalar mode: no padding needed - - const size_t valid_size = xsize_ * sizeof_t; - const size_t initialize_size = padding == Padding::kRoundUp - ? RoundUpTo(valid_size, vec_size) - : valid_size + vec_size - sizeof_t; - if (valid_size == initialize_size) return; - for (size_t y = 0; y < ysize_; ++y) { - uint8_t* JXL_RESTRICT row = static_cast<uint8_t*>(VoidRow(y)); -#if defined(__clang__) && \ - ((!defined(__apple_build_version__) && __clang_major__ <= 6) || \ - (defined(__apple_build_version__) && \ - __apple_build_version__ <= 10001145)) - // There's a bug in msan in clang-6 when handling AVX2 operations. This - // workaround allows tests to pass on msan, although it is slower and - // prevents msan warnings from uninitialized images. - std::fill(row, msan::kSanitizerSentinelByte, initialize_size); -#else - memset(row + valid_size, msan::kSanitizerSentinelByte, - initialize_size - valid_size); -#endif // clang6 + bytes_ = AllocateArray(bytes_per_row_ * ysize_); + if (!bytes_.get()) { + // TODO(eustas): use specialized OOM error code + return JXL_FAILURE("Failed to allocate memory for image surface"); } -#endif // MEMORY_SANITIZER + InitializePadding(*this, sizeof_t_); + + return true; } void PlaneBase::Swap(PlaneBase& other) { @@ -133,73 +103,5 @@ void PlaneBase::Swap(PlaneBase& other) { std::swap(bytes_, other.bytes_); } -void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in, - size_t block_dim) { - const size_t xsize_orig = in->xsize(); - const size_t ysize_orig = in->ysize(); - const size_t xsize = RoundUpTo(xsize_orig, block_dim); - const size_t ysize = RoundUpTo(ysize_orig, block_dim); - // Expands image size to the originally-allocated size. - in->ShrinkTo(xsize, ysize); - for (size_t c = 0; c < 3; c++) { - for (size_t y = 0; y < ysize_orig; y++) { - float* JXL_RESTRICT row = in->PlaneRow(c, y); - for (size_t x = xsize_orig; x < xsize; x++) { - row[x] = row[xsize_orig - 1]; - } - } - const float* JXL_RESTRICT row_src = in->ConstPlaneRow(c, ysize_orig - 1); - for (size_t y = ysize_orig; y < ysize; y++) { - memcpy(in->PlaneRow(c, y), row_src, xsize * sizeof(float)); - } - } -} - -static void DownsampleImage(const ImageF& input, size_t factor, - ImageF* output) { - JXL_ASSERT(factor != 1); - output->ShrinkTo(DivCeil(input.xsize(), factor), - DivCeil(input.ysize(), factor)); - size_t in_stride = input.PixelsPerRow(); - for (size_t y = 0; y < output->ysize(); y++) { - float* row_out = output->Row(y); - const float* row_in = input.Row(factor * y); - for (size_t x = 0; x < output->xsize(); x++) { - size_t cnt = 0; - float sum = 0; - for (size_t iy = 0; iy < factor && iy + factor * y < input.ysize(); - iy++) { - for (size_t ix = 0; ix < factor && ix + factor * x < input.xsize(); - ix++) { - sum += row_in[iy * in_stride + x * factor + ix]; - cnt++; - } - } - row_out[x] = sum / cnt; - } - } -} - -void DownsampleImage(ImageF* image, size_t factor) { - // Allocate extra space to avoid a reallocation when padding. - ImageF downsampled(DivCeil(image->xsize(), factor) + kBlockDim, - DivCeil(image->ysize(), factor) + kBlockDim); - DownsampleImage(*image, factor, &downsampled); - *image = std::move(downsampled); -} - -void DownsampleImage(Image3F* opsin, size_t factor) { - JXL_ASSERT(factor != 1); - // Allocate extra space to avoid a reallocation when padding. - Image3F downsampled(DivCeil(opsin->xsize(), factor) + kBlockDim, - DivCeil(opsin->ysize(), factor) + kBlockDim); - downsampled.ShrinkTo(downsampled.xsize() - kBlockDim, - downsampled.ysize() - kBlockDim); - for (size_t c = 0; c < 3; c++) { - DownsampleImage(opsin->Plane(c), factor, &downsampled.Plane(c)); - } - *opsin = std::move(downsampled); -} - +} // namespace detail } // namespace jxl -#endif // HWY_ONCE diff --git a/third_party/jpeg-xl/lib/jxl/image.h b/third_party/jpeg-xl/lib/jxl/image.h index 98c387bb77..be97b929e3 100644 --- a/third_party/jpeg-xl/lib/jxl/image.h +++ b/third_party/jpeg-xl/lib/jxl/image.h @@ -28,8 +28,8 @@ namespace jxl { -// Helper function to create rows that are multiples of SIMD vector size. -size_t VectorSize(); +// DO NOT use PlaneBase outside of image.{h|cc} +namespace detail { // Type-independent parts of Plane<> - reduces code duplication and facilitates // moving member function implementations to cc file. @@ -40,8 +40,8 @@ struct PlaneBase { orig_xsize_(0), orig_ysize_(0), bytes_per_row_(0), - bytes_(nullptr) {} - PlaneBase(size_t xsize, size_t ysize, size_t sizeof_t); + bytes_(nullptr), + sizeof_t_(0) {} // Copy construction/assignment is forbidden to avoid inadvertent copies, // which can be very expensive. Use CopyImageTo() instead. @@ -88,13 +88,16 @@ struct PlaneBase { } protected: + PlaneBase(size_t xsize, size_t ysize, size_t sizeof_t); + Status Allocate(); + // Returns pointer to the start of a row. JXL_INLINE void* VoidRow(const size_t y) const { #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ defined(THREAD_SANITIZER) if (y >= ysize_) { - JXL_ABORT("Row(%" PRIu64 ") in (%u x %u) image\n", (uint64_t)y, xsize_, - ysize_); + JXL_ABORT("Row(%" PRIu64 ") in (%u x %u) image\n", + static_cast<uint64_t>(y), xsize_, ysize_); } #endif @@ -102,21 +105,6 @@ struct PlaneBase { return JXL_ASSUME_ALIGNED(row, 64); } - enum class Padding { - // Allow Load(d, row + x) for x = 0; x < xsize(); x += Lanes(d). Default. - kRoundUp, - // Allow LoadU(d, row + x) for x = xsize() - 1. This requires an extra - // vector to be initialized. If done by default, this would suppress - // legitimate msan warnings. We therefore require users to explicitly call - // InitializePadding before using unaligned loads (e.g. convolution). - kUnaligned - }; - - // Initializes the minimum bytes required to suppress msan warnings from - // legitimate (according to Padding mode) vector loads/stores on the right - // border, where some lanes are uninitialized and assumed to be unused. - void InitializePadding(size_t sizeof_t, Padding padding); - // (Members are non-const to enable assignment during move-assignment.) uint32_t xsize_; // In valid pixels, not including any padding. uint32_t ysize_; @@ -124,8 +112,11 @@ struct PlaneBase { uint32_t orig_ysize_; size_t bytes_per_row_; // Includes padding. CacheAlignedUniquePtr bytes_; + size_t sizeof_t_; }; +} // namespace detail + // Single channel, aligned rows separated by padding. T must be POD. // // 'Single channel' (one 2D array per channel) simplifies vectorization @@ -148,17 +139,17 @@ struct PlaneBase { // provides convenient accessors for xsize/ysize, which shortens function // argument lists. Supports move-construction so it can be stored in containers. template <typename ComponentType> -class Plane : public PlaneBase { +class Plane : public detail::PlaneBase { public: using T = ComponentType; static constexpr size_t kNumPlanes = 1; Plane() = default; - Plane(const size_t xsize, const size_t ysize) - : PlaneBase(xsize, ysize, sizeof(T)) {} - void InitializePaddingForUnalignedAccesses() { - InitializePadding(sizeof(T), Padding::kUnaligned); + static StatusOr<Plane> Create(const size_t xsize, const size_t ysize) { + Plane plane(xsize, ysize, sizeof(T)); + JXL_RETURN_IF_ERROR(plane.Allocate()); + return plane; } JXL_INLINE T* Row(const size_t y) { return static_cast<T*>(VoidRow(y)); } @@ -179,6 +170,10 @@ class Plane : public PlaneBase { JXL_INLINE intptr_t PixelsPerRow() const { return static_cast<intptr_t>(bytes_per_row_ / sizeof(T)); } + + private: + Plane(size_t xsize, size_t ysize, size_t sizeof_t) + : detail::PlaneBase(xsize, ysize, sizeof_t) {} }; using ImageSB = Plane<int8_t>; @@ -189,12 +184,6 @@ using ImageI = Plane<int32_t>; using ImageF = Plane<float>; using ImageD = Plane<double>; -// Also works for Image3 and mixed argument types. -template <class Image1, class Image2> -bool SameSize(const Image1& image1, const Image2& image2) { - return image1.xsize() == image2.xsize() && image1.ysize() == image2.ysize(); -} - template <typename T> class Image3; @@ -339,7 +328,8 @@ class RectT { template <typename U> RectT<U> As() const { - return RectT<U>(U(x0_), U(y0_), U(xsize_), U(ysize_)); + return RectT<U>(static_cast<U>(x0_), static_cast<U>(y0_), + static_cast<U>(xsize_), static_cast<U>(ysize_)); } private: @@ -394,24 +384,12 @@ class Image3 { Image3() : planes_{PlaneT(), PlaneT(), PlaneT()} {} - Image3(const size_t xsize, const size_t ysize) - : planes_{PlaneT(xsize, ysize), PlaneT(xsize, ysize), - PlaneT(xsize, ysize)} {} - Image3(Image3&& other) noexcept { for (size_t i = 0; i < kNumPlanes; i++) { planes_[i] = std::move(other.planes_[i]); } } - Image3(PlaneT&& plane0, PlaneT&& plane1, PlaneT&& plane2) { - JXL_CHECK(SameSize(plane0, plane1)); - JXL_CHECK(SameSize(plane0, plane2)); - planes_[0] = std::move(plane0); - planes_[1] = std::move(plane1); - planes_[2] = std::move(plane2); - } - // Copy construction/assignment is forbidden to avoid inadvertent copies, // which can be very expensive. Use CopyImageTo instead. Image3(const Image3& other) = delete; @@ -424,6 +402,17 @@ class Image3 { return *this; } + static StatusOr<Image3> Create(const size_t xsize, const size_t ysize) { + StatusOr<PlaneT> plane0 = PlaneT::Create(xsize, ysize); + JXL_RETURN_IF_ERROR(plane0.status()); + StatusOr<PlaneT> plane1 = PlaneT::Create(xsize, ysize); + JXL_RETURN_IF_ERROR(plane1.status()); + StatusOr<PlaneT> plane2 = PlaneT::Create(xsize, ysize); + JXL_RETURN_IF_ERROR(plane2.status()); + return Image3(std::move(plane0).value(), std::move(plane1).value(), + std::move(plane2).value()); + } + // Returns row pointer; usage: PlaneRow(idx_plane, y)[x] = val. JXL_INLINE T* PlaneRow(const size_t c, const size_t y) { // Custom implementation instead of calling planes_[c].Row ensures only a @@ -481,6 +470,12 @@ class Image3 { JXL_INLINE intptr_t PixelsPerRow() const { return planes_[0].PixelsPerRow(); } private: + Image3(PlaneT&& plane0, PlaneT&& plane1, PlaneT&& plane2) { + planes_[0] = std::move(plane0); + planes_[1] = std::move(plane1); + planes_[2] = std::move(plane2); + } + void PlaneRowBoundsCheck(const size_t c, const size_t y) const { #if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ defined(THREAD_SANITIZER) @@ -493,7 +488,6 @@ class Image3 { #endif } - private: PlaneT planes_[kNumPlanes]; }; diff --git a/third_party/jpeg-xl/lib/jxl/image_bundle.h b/third_party/jpeg-xl/lib/jxl/image_bundle.h index 2eea496d5e..c8a76a9f59 100644 --- a/third_party/jpeg-xl/lib/jxl/image_bundle.h +++ b/third_party/jpeg-xl/lib/jxl/image_bundle.h @@ -42,14 +42,16 @@ class ImageBundle { ImageBundle(ImageBundle&&) = default; ImageBundle& operator=(ImageBundle&&) = default; - ImageBundle Copy() const { + StatusOr<ImageBundle> Copy() const { ImageBundle copy(metadata_); - copy.color_ = Image3F(color_.xsize(), color_.ysize()); + JXL_ASSIGN_OR_RETURN(copy.color_, + Image3F::Create(color_.xsize(), color_.ysize())); CopyImageTo(color_, ©.color_); copy.c_current_ = c_current_; copy.extra_channels_.reserve(extra_channels_.size()); for (const ImageF& plane : extra_channels_) { - ImageF ec(plane.xsize(), plane.ysize()); + JXL_ASSIGN_OR_RETURN(ImageF ec, + ImageF::Create(plane.xsize(), plane.ysize())); CopyImageTo(plane, &ec); copy.extra_channels_.emplace_back(std::move(ec)); } diff --git a/third_party/jpeg-xl/lib/jxl/image_metadata.cc b/third_party/jpeg-xl/lib/jxl/image_metadata.cc index 4cca910753..91d27ff44e 100644 --- a/third_party/jpeg-xl/lib/jxl/image_metadata.cc +++ b/third_party/jpeg-xl/lib/jxl/image_metadata.cc @@ -10,6 +10,7 @@ #include "lib/jxl/alpha.h" #include "lib/jxl/base/byte_order.h" +#include "lib/jxl/base/matrix_ops.h" #include "lib/jxl/cms/opsin_params.h" #include "lib/jxl/fields.h" #include "lib/jxl/frame_header.h" @@ -245,8 +246,8 @@ Status ExtraChannelInfo::VisitFields(Visitor* JXL_RESTRICT visitor) { } if (type == ExtraChannel::kUnknown || - (int(ExtraChannel::kReserved0) <= int(type) && - int(type) <= int(ExtraChannel::kReserved7))) { + (static_cast<int>(ExtraChannel::kReserved0) <= static_cast<int>(type) && + static_cast<int>(type) <= static_cast<int>(ExtraChannel::kReserved7))) { return JXL_FAILURE("Unknown extra channel (bits %u, shift %u, name '%s')\n", bit_depth.bits_per_sample, dim_shift, name.c_str()); } @@ -354,10 +355,13 @@ Status OpsinInverseMatrix::VisitFields(Visitor* JXL_RESTRICT visitor) { visitor->SetDefault(this); return true; } - for (int i = 0; i < 9; ++i) { - JXL_QUIET_RETURN_IF_ERROR( - visitor->F16(jxl::cms::DefaultInverseOpsinAbsorbanceMatrix()[i], - &inverse_matrix[i])); + const Matrix3x3& default_inverse = + jxl::cms::DefaultInverseOpsinAbsorbanceMatrix(); + for (int j = 0; j < 3; ++j) { + for (int i = 0; i < 3; ++i) { + JXL_QUIET_RETURN_IF_ERROR( + visitor->F16(default_inverse[j][i], &inverse_matrix[j][i])); + } } for (int i = 0; i < 3; ++i) { JXL_QUIET_RETURN_IF_ERROR(visitor->F16( diff --git a/third_party/jpeg-xl/lib/jxl/image_metadata.h b/third_party/jpeg-xl/lib/jxl/image_metadata.h index be603a49f3..2b647cfa30 100644 --- a/third_party/jpeg-xl/lib/jxl/image_metadata.h +++ b/third_party/jpeg-xl/lib/jxl/image_metadata.h @@ -138,7 +138,7 @@ struct OpsinInverseMatrix : public Fields { mutable bool all_default; - float inverse_matrix[9]; + Matrix3x3 inverse_matrix; float opsin_biases[3]; float quant_biases[4]; }; diff --git a/third_party/jpeg-xl/lib/jxl/image_ops.cc b/third_party/jpeg-xl/lib/jxl/image_ops.cc new file mode 100644 index 0000000000..36d19df938 --- /dev/null +++ b/third_party/jpeg-xl/lib/jxl/image_ops.cc @@ -0,0 +1,91 @@ +// Copyright (c) the JPEG XL 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. + +#include "lib/jxl/image_ops.h" + +#include <cstddef> +#include <cstring> + +#include "lib/jxl/base/common.h" +#include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/base/status.h" +#include "lib/jxl/frame_dimensions.h" +#include "lib/jxl/image.h" + +namespace jxl { + +void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in, + size_t block_dim) { + const size_t xsize_orig = in->xsize(); + const size_t ysize_orig = in->ysize(); + const size_t xsize = RoundUpTo(xsize_orig, block_dim); + const size_t ysize = RoundUpTo(ysize_orig, block_dim); + // Expands image size to the originally-allocated size. + in->ShrinkTo(xsize, ysize); + for (size_t c = 0; c < 3; c++) { + for (size_t y = 0; y < ysize_orig; y++) { + float* JXL_RESTRICT row = in->PlaneRow(c, y); + for (size_t x = xsize_orig; x < xsize; x++) { + row[x] = row[xsize_orig - 1]; + } + } + const float* JXL_RESTRICT row_src = in->ConstPlaneRow(c, ysize_orig - 1); + for (size_t y = ysize_orig; y < ysize; y++) { + memcpy(in->PlaneRow(c, y), row_src, xsize * sizeof(float)); + } + } +} + +static void DoDownsampleImage(const ImageF& input, size_t factor, + ImageF* output) { + JXL_ASSERT(factor != 1); + output->ShrinkTo(DivCeil(input.xsize(), factor), + DivCeil(input.ysize(), factor)); + size_t in_stride = input.PixelsPerRow(); + for (size_t y = 0; y < output->ysize(); y++) { + float* row_out = output->Row(y); + const float* row_in = input.Row(factor * y); + for (size_t x = 0; x < output->xsize(); x++) { + size_t cnt = 0; + float sum = 0; + for (size_t iy = 0; iy < factor && iy + factor * y < input.ysize(); + iy++) { + for (size_t ix = 0; ix < factor && ix + factor * x < input.xsize(); + ix++) { + sum += row_in[iy * in_stride + x * factor + ix]; + cnt++; + } + } + row_out[x] = sum / cnt; + } + } +} + +StatusOr<ImageF> DownsampleImage(const ImageF& image, size_t factor) { + ImageF downsampled; + // Allocate extra space to avoid a reallocation when padding. + JXL_ASSIGN_OR_RETURN( + downsampled, ImageF::Create(DivCeil(image.xsize(), factor) + kBlockDim, + DivCeil(image.ysize(), factor) + kBlockDim)); + DoDownsampleImage(image, factor, &downsampled); + return downsampled; +} + +StatusOr<Image3F> DownsampleImage(const Image3F& opsin, size_t factor) { + JXL_ASSERT(factor != 1); + // Allocate extra space to avoid a reallocation when padding. + Image3F downsampled; + JXL_ASSIGN_OR_RETURN( + downsampled, Image3F::Create(DivCeil(opsin.xsize(), factor) + kBlockDim, + DivCeil(opsin.ysize(), factor) + kBlockDim)); + downsampled.ShrinkTo(downsampled.xsize() - kBlockDim, + downsampled.ysize() - kBlockDim); + for (size_t c = 0; c < 3; c++) { + DoDownsampleImage(opsin.Plane(c), factor, &downsampled.Plane(c)); + } + return downsampled; +} + +} // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/image_ops.h b/third_party/jpeg-xl/lib/jxl/image_ops.h index b2ce23f13d..84cf7dad76 100644 --- a/third_party/jpeg-xl/lib/jxl/image_ops.h +++ b/third_party/jpeg-xl/lib/jxl/image_ops.h @@ -9,16 +9,23 @@ // Operations on images. #include <algorithm> -#include <array> +#include <cstddef> +#include <cstdint> #include <limits> -#include <vector> +#include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/status.h" #include "lib/jxl/frame_dimensions.h" #include "lib/jxl/image.h" namespace jxl { +// Works for mixed image-like argument types. +template <class Image1, class Image2> +bool SameSize(const Image1& image1, const Image2& image2) { + return image1.xsize() == image2.xsize() && image1.ysize() == image2.ysize(); +} + template <typename T> void CopyImageTo(const Plane<T>& from, Plane<T>* JXL_RESTRICT to) { JXL_ASSERT(SameSize(from, *to)); @@ -102,76 +109,15 @@ void CopyImageToWithPadding(const Rect& from_rect, const T& from, to); } -template <class ImageIn, class ImageOut> -void Subtract(const ImageIn& image1, const ImageIn& image2, ImageOut* out) { - using T = typename ImageIn::T; - const size_t xsize = image1.xsize(); - const size_t ysize = image1.ysize(); - JXL_CHECK(xsize == image2.xsize()); - JXL_CHECK(ysize == image2.ysize()); - - for (size_t y = 0; y < ysize; ++y) { - const T* const JXL_RESTRICT row1 = image1.Row(y); - const T* const JXL_RESTRICT row2 = image2.Row(y); - T* const JXL_RESTRICT row_out = out->Row(y); - for (size_t x = 0; x < xsize; ++x) { - row_out[x] = row1[x] - row2[x]; - } - } -} - -// In-place. -template <typename Tin, typename Tout> -void SubtractFrom(const Plane<Tin>& what, Plane<Tout>* to) { - const size_t xsize = what.xsize(); - const size_t ysize = what.ysize(); - for (size_t y = 0; y < ysize; ++y) { - const Tin* JXL_RESTRICT row_what = what.ConstRow(y); - Tout* JXL_RESTRICT row_to = to->Row(y); - for (size_t x = 0; x < xsize; ++x) { - row_to[x] -= row_what[x]; - } - } -} - -// In-place. -template <typename Tin, typename Tout> -void AddTo(const Plane<Tin>& what, Plane<Tout>* to) { - const size_t xsize = what.xsize(); - const size_t ysize = what.ysize(); - for (size_t y = 0; y < ysize; ++y) { - const Tin* JXL_RESTRICT row_what = what.ConstRow(y); - Tout* JXL_RESTRICT row_to = to->Row(y); - for (size_t x = 0; x < xsize; ++x) { - row_to[x] += row_what[x]; - } - } -} - -template <typename Tin, typename Tout> -void AddTo(Rect rectFrom, const Plane<Tin>& what, Rect rectTo, - Plane<Tout>* to) { - JXL_ASSERT(SameSize(rectFrom, rectTo)); - const size_t xsize = rectTo.xsize(); - const size_t ysize = rectTo.ysize(); - for (size_t y = 0; y < ysize; ++y) { - const Tin* JXL_RESTRICT row_what = rectFrom.ConstRow(what, y); - Tout* JXL_RESTRICT row_to = rectTo.Row(to, y); - for (size_t x = 0; x < xsize; ++x) { - row_to[x] += row_what[x]; - } - } -} - // Returns linear combination of two grayscale images. template <typename T> -Plane<T> LinComb(const T lambda1, const Plane<T>& image1, const T lambda2, - const Plane<T>& image2) { +StatusOr<Plane<T>> LinComb(const T lambda1, const Plane<T>& image1, + const T lambda2, const Plane<T>& image2) { const size_t xsize = image1.xsize(); const size_t ysize = image1.ysize(); JXL_CHECK(xsize == image2.xsize()); JXL_CHECK(ysize == image2.ysize()); - Plane<T> out(xsize, ysize); + JXL_ASSIGN_OR_RETURN(Plane<T> out, Plane<T>::Create(xsize, ysize)); for (size_t y = 0; y < ysize; ++y) { const T* const JXL_RESTRICT row1 = image1.Row(y); const T* const JXL_RESTRICT row2 = image2.Row(y); @@ -291,35 +237,6 @@ struct WrapRowUnchanged { } }; -// Sets "thickness" pixels on each border to "value". This is faster than -// initializing the entire image and overwriting valid/interior pixels. -template <typename T> -void SetBorder(const size_t thickness, const T value, Plane<T>* image) { - const size_t xsize = image->xsize(); - const size_t ysize = image->ysize(); - // Top: fill entire row - for (size_t y = 0; y < std::min(thickness, ysize); ++y) { - T* const JXL_RESTRICT row = image->Row(y); - std::fill(row, row + xsize, value); - } - - // Bottom: fill entire row - for (size_t y = ysize - thickness; y < ysize; ++y) { - T* const JXL_RESTRICT row = image->Row(y); - std::fill(row, row + xsize, value); - } - - // Left/right: fill the 'columns' on either side, but only if the image is - // big enough that they don't already belong to the top/bottom rows. - if (ysize >= 2 * thickness) { - for (size_t y = thickness; y < ysize - thickness; ++y) { - T* const JXL_RESTRICT row = image->Row(y); - std::fill(row, row + thickness, value); - std::fill(row + xsize - thickness, row + xsize, value); - } - } -} - // Computes the minimum and maximum pixel value. template <typename T> void ImageMinMax(const Plane<T>& image, T* const JXL_RESTRICT min, @@ -335,48 +252,6 @@ void ImageMinMax(const Plane<T>& image, T* const JXL_RESTRICT min, } } -template <typename T> -Plane<T> ImageFromPacked(const std::vector<T>& packed, const size_t xsize, - const size_t ysize) { - Plane<T> out(xsize, ysize); - for (size_t y = 0; y < ysize; ++y) { - T* const JXL_RESTRICT row = out.Row(y); - const T* const JXL_RESTRICT packed_row = &packed[y * xsize]; - memcpy(row, packed_row, xsize * sizeof(T)); - } - return out; -} - -template <typename T> -void Image3Max(const Image3<T>& image, std::array<T, 3>* out_max) { - for (size_t c = 0; c < 3; ++c) { - T max = std::numeric_limits<T>::min(); - for (size_t y = 0; y < image.ysize(); ++y) { - const T* JXL_RESTRICT row = image.ConstPlaneRow(c, y); - for (size_t x = 0; x < image.xsize(); ++x) { - max = std::max(max, row[x]); - } - } - (*out_max)[c] = max; - } -} - -template <typename T> -std::vector<T> PackedFromImage(const Plane<T>& image, const Rect& rect) { - const size_t xsize = rect.xsize(); - const size_t ysize = rect.ysize(); - std::vector<T> packed(xsize * ysize); - for (size_t y = 0; y < rect.ysize(); ++y) { - memcpy(&packed[y * xsize], rect.ConstRow(image, y), xsize * sizeof(T)); - } - return packed; -} - -template <typename T> -std::vector<T> PackedFromImage(const Plane<T>& image) { - return PackedFromImage(image, Rect(image)); -} - // Initializes all planes to the same "value". template <typename T> void FillImage(const T value, Image3<T>* image) { @@ -391,28 +266,6 @@ void FillImage(const T value, Image3<T>* image) { } template <typename T> -void FillPlane(const T value, Plane<T>* image) { - for (size_t y = 0; y < image->ysize(); ++y) { - T* JXL_RESTRICT row = image->Row(y); - for (size_t x = 0; x < image->xsize(); ++x) { - row[x] = value; - } - } -} - -template <typename T> -void FillImage(const T value, Image3<T>* image, Rect rect) { - for (size_t c = 0; c < 3; ++c) { - for (size_t y = 0; y < rect.ysize(); ++y) { - T* JXL_RESTRICT row = rect.PlaneRow(image, c, y); - for (size_t x = 0; x < rect.xsize(); ++x) { - row[x] = value; - } - } - } -} - -template <typename T> void FillPlane(const T value, Plane<T>* image, Rect rect) { for (size_t y = 0; y < rect.ysize(); ++y) { T* JXL_RESTRICT row = rect.Row(image, y); @@ -432,22 +285,14 @@ void ZeroFillImage(Image3<T>* image) { } } -template <typename T> -void ZeroFillPlane(Plane<T>* image, Rect rect) { - for (size_t y = 0; y < rect.ysize(); ++y) { - T* JXL_RESTRICT row = rect.Row(image, y); - memset(row, 0, rect.xsize() * sizeof(T)); - } -} - // Same as above, but operates in-place. Assumes that the `in` image was // allocated large enough. void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in, size_t block_dim = kBlockDim); // Downsamples an image by a given factor. -void DownsampleImage(Image3F* opsin, size_t factor); -void DownsampleImage(ImageF* image, size_t factor); +StatusOr<Image3F> DownsampleImage(const Image3F& opsin, size_t factor); +StatusOr<ImageF> DownsampleImage(const ImageF& image, size_t factor); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/image_ops_test.cc b/third_party/jpeg-xl/lib/jxl/image_ops_test.cc index dfcb2292c5..6b308baab4 100644 --- a/third_party/jpeg-xl/lib/jxl/image_ops_test.cc +++ b/third_party/jpeg-xl/lib/jxl/image_ops_test.cc @@ -8,43 +8,19 @@ #include <stdint.h> #include <stdlib.h> -#include <utility> +#include <cstring> +#include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/printf_macros.h" +#include "lib/jxl/base/random.h" +#include "lib/jxl/base/status.h" +#include "lib/jxl/cache_aligned.h" #include "lib/jxl/image.h" -#include "lib/jxl/image_test_utils.h" #include "lib/jxl/testing.h" namespace jxl { namespace { -template <typename T> -void TestPacked(const size_t xsize, const size_t ysize) { - Plane<T> image1(xsize, ysize); - RandomFillImage(&image1); - const std::vector<T>& packed = PackedFromImage(image1); - const Plane<T>& image2 = ImageFromPacked(packed, xsize, ysize); - JXL_EXPECT_OK(SamePixels(image1, image2, _)); -} - -TEST(ImageTest, TestPacked) { - TestPacked<uint8_t>(1, 1); - TestPacked<uint8_t>(7, 1); - TestPacked<uint8_t>(1, 7); - - TestPacked<int16_t>(1, 1); - TestPacked<int16_t>(7, 1); - TestPacked<int16_t>(1, 7); - - TestPacked<uint16_t>(1, 1); - TestPacked<uint16_t>(7, 1); - TestPacked<uint16_t>(1, 7); - - TestPacked<float>(1, 1); - TestPacked<float>(7, 1); - TestPacked<float>(1, 7); -} - // Ensure entire payload is readable/writable for various size/offset combos. TEST(ImageTest, TestAllocator) { Rng rng(0); @@ -70,18 +46,18 @@ TEST(ImageTest, TestAllocator) { template <typename T> void TestFillImpl(Image3<T>* img, const char* layout) { - FillImage(T(1), img); + FillImage(static_cast<T>(1), img); for (size_t y = 0; y < img->ysize(); ++y) { for (size_t c = 0; c < 3; ++c) { T* JXL_RESTRICT row = img->PlaneRow(c, y); for (size_t x = 0; x < img->xsize(); ++x) { - if (row[x] != T(1)) { + if (row[x] != static_cast<T>(1)) { printf("Not 1 at c=%" PRIuS " %" PRIuS ", %" PRIuS " (%" PRIuS " x %" PRIuS ") (%s)\n", c, x, y, img->xsize(), img->ysize(), layout); abort(); } - row[x] = T(2); + row[x] = static_cast<T>(2); } } } @@ -92,13 +68,13 @@ void TestFillImpl(Image3<T>* img, const char* layout) { for (size_t y = 0; y < img->ysize(); ++y) { T* JXL_RESTRICT row = img->PlaneRow(c, y); for (size_t x = 0; x < img->xsize(); ++x) { - if (row[x] != T(0)) { + if (row[x] != static_cast<T>(0)) { printf("Not 0 at c=%" PRIuS " %" PRIuS ", %" PRIuS " (%" PRIuS " x %" PRIuS ") (%s)\n", c, x, y, img->xsize(), img->ysize(), layout); abort(); } - row[x] = T(3); + row[x] = static_cast<T>(3); } } } @@ -108,12 +84,8 @@ template <typename T> void TestFillT() { for (uint32_t xsize : {0, 1, 15, 16, 31, 32}) { for (uint32_t ysize : {0, 1, 15, 16, 31, 32}) { - Image3<T> image(xsize, ysize); + JXL_ASSIGN_OR_DIE(Image3<T> image, Image3<T>::Create(xsize, ysize)); TestFillImpl(&image, "size ctor"); - - Image3<T> planar(Plane<T>(xsize, ysize), Plane<T>(xsize, ysize), - Plane<T>(xsize, ysize)); - TestFillImpl(&planar, "planar"); } } } @@ -127,7 +99,7 @@ TEST(ImageTest, TestFill) { } TEST(ImageTest, CopyImageToWithPaddingTest) { - Plane<uint32_t> src(100, 61); + JXL_ASSIGN_OR_DIE(Plane<uint32_t> src, Plane<uint32_t>::Create(100, 61)); for (size_t y = 0; y < src.ysize(); y++) { for (size_t x = 0; x < src.xsize(); x++) { src.Row(y)[x] = x * 1000 + y; @@ -136,7 +108,7 @@ TEST(ImageTest, CopyImageToWithPaddingTest) { Rect src_rect(10, 20, 30, 40); EXPECT_TRUE(src_rect.IsInside(src)); - Plane<uint32_t> dst(60, 50); + JXL_ASSIGN_OR_DIE(Plane<uint32_t> dst, Plane<uint32_t>::Create(60, 50)); FillImage(0u, &dst); Rect dst_rect(20, 5, 30, 40); EXPECT_TRUE(dst_rect.IsInside(dst)); diff --git a/third_party/jpeg-xl/lib/jxl/image_test_utils.h b/third_party/jpeg-xl/lib/jxl/image_test_utils.h index e7d72285e6..7bb146866e 100644 --- a/third_party/jpeg-xl/lib/jxl/image_test_utils.h +++ b/third_party/jpeg-xl/lib/jxl/image_test_utils.h @@ -6,10 +6,6 @@ #ifndef LIB_JXL_IMAGE_TEST_UTILS_H_ #define LIB_JXL_IMAGE_TEST_UTILS_H_ -#ifndef __STDC_FORMAT_MACROS -#define __STDC_FORMAT_MACROS -#endif - #include <inttypes.h> #include <stddef.h> #include <stdint.h> @@ -37,7 +33,8 @@ bool SamePixels(const Plane<T>& image1, const Plane<T>& image2, for (size_t x = rect.x0(); x < rect.xsize(); ++x) { if (row1[x] != row2[x]) { failures << "pixel mismatch" << x << ", " << y << ": " - << double(row1[x]) << " != " << double(row2[x]) << "\n"; + << static_cast<double>(row1[x]) + << " != " << static_cast<double>(row2[x]) << "\n"; if (++mismatches > 4) { return false; } @@ -66,7 +63,7 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual, const double threshold_l1, const double threshold_relative, std::stringstream& failures, const intptr_t border = 0, - const size_t c = 0) { + const int c = 0) { JXL_CHECK(SameSize(expected, actual)); const intptr_t xsize = expected.xsize(); const intptr_t ysize = expected.ysize(); @@ -89,7 +86,8 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual, max_l1 = std::max(max_l1, l1); } } else { - const double relative = l1 / std::abs(double(row_expected[x])); + const double relative = + l1 / std::abs(static_cast<double>(row_expected[x])); if (l1 > threshold_l1 && relative > threshold_relative) { // Fails both tolerances => will exit below, update max_*. any_bad = true; @@ -104,13 +102,11 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual, } // Never had a valid relative value, don't print it. if (max_relative < 0) { - fprintf(stderr, "c=%" PRIu64 ": max +/- %E exceeds +/- %.2E\n", - static_cast<uint64_t>(c), max_l1, threshold_l1); + fprintf(stderr, "c=%d: max +/- %E exceeds +/- %.2E\n", c, max_l1, + threshold_l1); } else { - fprintf(stderr, - "c=%" PRIu64 ": max +/- %E, x %E exceeds +/- %.2E, x %.2E\n", - static_cast<uint64_t>(c), max_l1, max_relative, threshold_l1, - threshold_relative); + fprintf(stderr, "c=%d: max +/- %E, x %E exceeds +/- %.2E, x %.2E\n", c, + max_l1, max_relative, threshold_l1, threshold_relative); } // Dump the expected image and actual image if the region is small enough. const intptr_t kMaxTestDumpSize = 16; @@ -134,7 +130,8 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual, bool bad = l1 > threshold_l1; if (row_expected[x] > 1E-10) { - const double relative = l1 / std::abs(double(row_expected[x])); + const double relative = + l1 / std::abs(static_cast<double>(row_expected[x])); bad &= relative > threshold_relative; } if (bad) { @@ -157,7 +154,8 @@ bool VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual, bool bad = l1 > threshold_l1; if (row_expected[x] > 1E-10) { - const double relative = l1 / std::abs(double(row_expected[x])); + const double relative = + l1 / std::abs(static_cast<double>(row_expected[x])); bad &= relative > threshold_relative; } if (bad) { @@ -179,9 +177,9 @@ bool VerifyRelativeError(const Image3<T>& expected, const Image3<T>& actual, std::stringstream& failures, const intptr_t border = 0) { for (size_t c = 0; c < 3; ++c) { - bool ok = - VerifyRelativeError(expected.Plane(c), actual.Plane(c), threshold_l1, - threshold_relative, failures, border, c); + bool ok = VerifyRelativeError(expected.Plane(c), actual.Plane(c), + threshold_l1, threshold_relative, failures, + border, static_cast<int>(c)); if (!ok) { return false; } @@ -216,8 +214,8 @@ template <typename T> typename std::enable_if<std::is_integral<T>::value>::type RandomFillImage( Plane<T>* image) { Rng rng(129); - GenerateImage(rng, image, int64_t(0), - int64_t(std::numeric_limits<T>::max()) + 1); + GenerateImage(rng, image, static_cast<int64_t>(0), + static_cast<int64_t>(std::numeric_limits<T>::max()) + 1); } JXL_INLINE void RandomFillImage(Plane<float>* image) { @@ -236,8 +234,8 @@ template <typename T> typename std::enable_if<std::is_integral<T>::value>::type RandomFillImage( Image3<T>* image) { Rng rng(129); - GenerateImage(rng, image, int64_t(0), - int64_t(std::numeric_limits<T>::max()) + 1); + GenerateImage(rng, image, static_cast<int64_t>(0), + static_cast<int64_t>(std::numeric_limits<T>::max()) + 1); } JXL_INLINE void RandomFillImage(Image3F* image) { diff --git a/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h b/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h index fcb01d7396..60ea69ddbd 100644 --- a/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h +++ b/third_party/jpeg-xl/lib/jxl/inverse_mtf-inl.h @@ -82,7 +82,7 @@ HWY_AFTER_NAMESPACE(); namespace jxl { inline void InverseMoveToFrontTransform(uint8_t* v, int v_len) { - return HWY_STATIC_DISPATCH(InverseMoveToFrontTransform)(v, v_len); + HWY_STATIC_DISPATCH(InverseMoveToFrontTransform)(v, v_len); } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc b/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc index 64560d9ab0..31bb2dda23 100644 --- a/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc +++ b/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc @@ -18,6 +18,7 @@ #include "lib/jxl/base/bits.h" #include "lib/jxl/base/byte_order.h" #include "lib/jxl/base/common.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/frame_dimensions.h" #include "lib/jxl/image_bundle.h" #include "lib/jxl/jpeg/dec_jpeg_serialization_state.h" @@ -42,7 +43,7 @@ const size_t kJpegBitWriterChunkSize = 16384; // Returns non-zero if and only if x has a zero byte, i.e. one of // x & 0xff, x & 0xff00, ..., x & 0xff00000000000000 is zero. -static JXL_INLINE uint64_t HasZeroByte(uint64_t x) { +JXL_INLINE uint64_t HasZeroByte(uint64_t x) { return (x - 0x0101010101010101ULL) & ~x & 0x8080808080808080ULL; } @@ -57,7 +58,7 @@ void JpegBitWriterInit(JpegBitWriter* bw, bw->data = bw->chunk.buffer->data(); } -static JXL_NOINLINE void SwapBuffer(JpegBitWriter* bw) { +JXL_NOINLINE void SwapBuffer(JpegBitWriter* bw) { bw->chunk.len = bw->pos; bw->output->emplace_back(std::move(bw->chunk)); bw->chunk = OutputChunk(kJpegBitWriterChunkSize); @@ -65,7 +66,7 @@ static JXL_NOINLINE void SwapBuffer(JpegBitWriter* bw) { bw->pos = 0; } -static JXL_INLINE void Reserve(JpegBitWriter* bw, size_t n_bytes) { +JXL_INLINE void Reserve(JpegBitWriter* bw, size_t n_bytes) { if (JXL_UNLIKELY((bw->pos + n_bytes) > kJpegBitWriterChunkSize)) { SwapBuffer(bw); } @@ -77,14 +78,14 @@ static JXL_INLINE void Reserve(JpegBitWriter* bw, size_t n_bytes) { * This method is "careless" - caller must make sure that there is enough * space in the output buffer. Emits up to 2 bytes to buffer. */ -static JXL_INLINE void EmitByte(JpegBitWriter* bw, int byte) { +JXL_INLINE void EmitByte(JpegBitWriter* bw, int byte) { bw->data[bw->pos] = byte; bw->data[bw->pos + 1] = 0; bw->pos += (byte != 0xFF ? 1 : 2); } -static JXL_INLINE void DischargeBitBuffer(JpegBitWriter* bw, int nbits, - uint64_t bits) { +JXL_INLINE void DischargeBitBuffer(JpegBitWriter* bw, int nbits, + uint64_t bits) { // At this point we are ready to emit the put_buffer to the output. // The JPEG format requires that after every 0xff byte in the entropy // coded section, there is a zero byte, therefore we first check if any of @@ -111,7 +112,7 @@ static JXL_INLINE void DischargeBitBuffer(JpegBitWriter* bw, int nbits, bw->put_buffer = bits << bw->put_bits; } -static JXL_INLINE void WriteBits(JpegBitWriter* bw, int nbits, uint64_t bits) { +JXL_INLINE void WriteBits(JpegBitWriter* bw, int nbits, uint64_t bits) { JXL_DASSERT(nbits > 0); bw->put_bits -= nbits; if (JXL_UNLIKELY(bw->put_bits < 0)) { @@ -142,12 +143,14 @@ bool JumpToByteBoundary(JpegBitWriter* bw, const uint8_t** pad_bits, } else { pad_pattern = 0; const uint8_t* src = *pad_bits; - // TODO(eustas): bitwise reading looks insanely ineffective... + // TODO(eustas): bitwise reading looks insanely ineffective! while (n_bits--) { pad_pattern <<= 1; if (src >= pad_bits_end) return false; - // TODO(eustas): DCHECK *src == {0, 1} - pad_pattern |= !!*(src++); + uint8_t bit = *src; + src++; + JXL_ASSERT(bit <= 1); + pad_pattern |= bit; } *pad_bits = src; } @@ -187,21 +190,20 @@ void DCTCodingStateInit(DCTCodingState* s) { s->refinement_bits_.reserve(64); } -static JXL_INLINE void WriteSymbol(int symbol, HuffmanCodeTable* table, - JpegBitWriter* bw) { +JXL_INLINE void WriteSymbol(int symbol, HuffmanCodeTable* table, + JpegBitWriter* bw) { WriteBits(bw, table->depth[symbol], table->code[symbol]); } -static JXL_INLINE void WriteSymbolBits(int symbol, HuffmanCodeTable* table, - JpegBitWriter* bw, int nbits, - uint64_t bits) { +JXL_INLINE void WriteSymbolBits(int symbol, HuffmanCodeTable* table, + JpegBitWriter* bw, int nbits, uint64_t bits) { WriteBits(bw, nbits + table->depth[symbol], bits | (table->code[symbol] << nbits)); } // Emit all buffered data to the bit stream using the given Huffman code and // bit writer. -static JXL_INLINE void Flush(DCTCodingState* s, JpegBitWriter* bw) { +JXL_INLINE void Flush(DCTCodingState* s, JpegBitWriter* bw) { if (s->eob_run_ > 0) { Reserve(bw, 16); int nbits = FloorLog2Nonzero<uint32_t>(s->eob_run_); @@ -233,11 +235,9 @@ static JXL_INLINE void Flush(DCTCodingState* s, JpegBitWriter* bw) { // Buffer some more data at the end-of-band (the last non-zero or newly // non-zero coefficient within the [Ss, Se] spectral band). -static JXL_INLINE void BufferEndOfBand(DCTCodingState* s, - HuffmanCodeTable* ac_huff, - const int* new_bits_array, - size_t new_bits_count, - JpegBitWriter* bw) { +JXL_INLINE void BufferEndOfBand(DCTCodingState* s, HuffmanCodeTable* ac_huff, + const int* new_bits_array, + size_t new_bits_count, JpegBitWriter* bw) { if (s->eob_run_ == 0) { s->cur_ac_huff_ = ac_huff; } @@ -530,7 +530,7 @@ bool EncodeDCTBlockSequential(const coeff_t* coeffs, HuffmanCodeTable* dc_huff, int dc_nbits = (temp2 == 0) ? 0 : (FloorLog2Nonzero<uint32_t>(temp2) + 1); WriteSymbol(dc_nbits, dc_huff, bw); -#if false +#if JXL_FALSE // If the input is corrupt, this could be triggered. Checking is // costly though, so it makes more sense to avoid this branch. // (producing a corrupt JPEG when the input is corrupt, instead @@ -543,7 +543,8 @@ bool EncodeDCTBlockSequential(const coeff_t* coeffs, HuffmanCodeTable* dc_huff, int16_t r = 0; for (size_t i = 1; i < 64; i++) { - if ((temp = coeffs[kJPEGNaturalOrder[i]]) == 0) { + temp = coeffs[kJPEGNaturalOrder[i]]; + if (temp == 0) { r++; } else { temp2 = temp >> (8 * sizeof(coeff_t) - 1); @@ -611,7 +612,8 @@ bool EncodeDCTBlockProgressive(const coeff_t* coeffs, HuffmanCodeTable* dc_huff, } int r = 0; for (int k = Ss; k <= Se; ++k) { - if ((temp = coeffs[kJPEGNaturalOrder[k]]) == 0) { + temp = coeffs[kJPEGNaturalOrder[k]]; + if (temp == 0) { r++; continue; } @@ -884,8 +886,8 @@ SerializationStatus JXL_NOINLINE DoEncodeScan(const JPEGData& jpg, return SerializationStatus::DONE; } -static SerializationStatus JXL_INLINE EncodeScan(const JPEGData& jpg, - SerializationState* state) { +SerializationStatus JXL_INLINE EncodeScan(const JPEGData& jpg, + SerializationState* state) { const JPEGScanInfo& scan_info = jpg.scan_info[state->scan_index]; const bool is_progressive = state->is_progressive; const int Al = is_progressive ? scan_info.Al : 0; diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc index 97342553e5..d311908415 100644 --- a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc +++ b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc @@ -8,7 +8,7 @@ #include <brotli/encode.h> #include "lib/jxl/codec_in_out.h" -#include "lib/jxl/enc_fields.h" +#include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/image_bundle.h" #include "lib/jxl/jpeg/enc_jpeg_data_reader.h" #include "lib/jxl/luminance.h" @@ -76,7 +76,8 @@ bool GetMarkerPayload(const uint8_t* data, size_t size, ByteSpan* payload) { Status DetectBlobs(jpeg::JPEGData& jpeg_data) { JXL_DASSERT(jpeg_data.app_data.size() == jpeg_data.app_marker_type.size()); - bool have_exif = false, have_xmp = false; + bool have_exif = false; + bool have_xmp = false; for (size_t i = 0; i < jpeg_data.app_data.size(); i++) { auto& marker = jpeg_data.app_data[i]; if (marker.empty() || marker[0] != kApp1) { @@ -165,7 +166,7 @@ Status ParseChunkedMarker(const jpeg::JPEGData& src, uint8_t marker_type, if (!presence[index]) { return JXL_FAILURE("Missing chunk."); } - chunks[index].AppendTo(output); + chunks[index].AppendTo(*output); } return true; @@ -173,7 +174,7 @@ Status ParseChunkedMarker(const jpeg::JPEGData& src, uint8_t marker_type, Status SetBlobsFromJpegData(const jpeg::JPEGData& jpeg_data, Blobs* blobs) { for (size_t i = 0; i < jpeg_data.app_data.size(); i++) { - auto& marker = jpeg_data.app_data[i]; + const auto& marker = jpeg_data.app_data[i]; if (marker.empty() || marker[0] != kApp1) { continue; } @@ -210,7 +211,7 @@ Status SetBlobsFromJpegData(const jpeg::JPEGData& jpeg_data, Blobs* blobs) { return true; } -static inline bool IsJPG(const Span<const uint8_t> bytes) { +inline bool IsJPG(const Span<const uint8_t> bytes) { return bytes.size() >= 2 && bytes[0] == 0xFF && bytes[1] == 0xD8; } @@ -239,14 +240,16 @@ Status SetChromaSubsamplingFromJpegData(const JPEGData& jpg, return JXL_FAILURE("Cannot recompress JPEGs with neither 1 nor 3 channels"); } if (nbcomp == 3) { - uint8_t hsample[3], vsample[3]; + uint8_t hsample[3]; + uint8_t vsample[3]; for (size_t i = 0; i < nbcomp; i++) { hsample[i] = jpg.components[i].h_samp_factor; vsample[i] = jpg.components[i].v_samp_factor; } JXL_RETURN_IF_ERROR(cs->Set(hsample, vsample)); } else if (nbcomp == 1) { - uint8_t hsample[3], vsample[3]; + uint8_t hsample[3]; + uint8_t vsample[3]; for (size_t i = 0; i < 3; i++) { hsample[i] = jpg.components[0].h_samp_factor; vsample[i] = jpg.components[0].v_samp_factor; @@ -330,7 +333,7 @@ Status EncodeJPEGData(JPEGData& jpeg_data, std::vector<uint8_t>* bytes, { PaddedBytes serialized_jpeg_data = std::move(writer).TakeBytes(); bytes->reserve(serialized_jpeg_data.size() + brotli_capacity); - Bytes(serialized_jpeg_data).AppendTo(bytes); + Bytes(serialized_jpeg_data).AppendTo(*bytes); } BrotliEncoderState* brotli_enc = @@ -394,8 +397,9 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, CodecInOut* io) { io->metadata.m.SetIntensityTarget(kDefaultIntensityTarget); io->metadata.m.SetUintSamples(BITS_IN_JSAMPLE); - io->SetFromImage(Image3F(jpeg_data->width, jpeg_data->height), - io->metadata.m.color_encoding); + JXL_ASSIGN_OR_RETURN(Image3F tmp, + Image3F::Create(jpeg_data->width, jpeg_data->height)); + io->SetFromImage(std::move(tmp), io->metadata.m.color_encoding); SetIntensityTarget(&io->metadata.m); return true; } diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.cc b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.cc index ce64dae47b..d1e8476db6 100644 --- a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.cc +++ b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.cc @@ -23,7 +23,7 @@ namespace jxl { namespace jpeg { namespace { -static const int kBrunsliMaxSampling = 15; +const int kBrunsliMaxSampling = 15; // Macros for commonly used error conditions. @@ -234,7 +234,7 @@ bool ProcessDHT(const uint8_t* data, const size_t len, JpegReadMode mode, JPEGHuffmanCode huff; huff.slot_id = ReadUint8(data, pos); int huffman_index = huff.slot_id; - int is_ac_table = (huff.slot_id & 0x10) != 0; + bool is_ac_table = ((huff.slot_id & 0x10) != 0); HuffmanTableEntry* huff_lut; if (is_ac_table) { huffman_index -= 0x10; @@ -292,7 +292,7 @@ bool ProcessDHT(const uint8_t* data, const size_t len, JpegReadMode mode, } huff.is_last = (*pos == start_pos + marker_len); if (mode == JpegReadMode::kReadAll) { - BuildJpegHuffmanTable(&huff.counts[0], &huff.values[0], huff_lut); + BuildJpegHuffmanTable(huff.counts.data(), huff.values.data(), huff_lut); } jpg->huffman_code.push_back(huff); } @@ -419,7 +419,7 @@ struct BitReaderState { if (bits_left_ <= 16) { while (bits_left_ <= 56) { val_ <<= 8; - val_ |= (uint64_t)GetNextByte(); + val_ |= static_cast<uint64_t>(GetNextByte()); bits_left_ += 8; } } diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.h b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.h index 3fad820e9d..4389b2ffe3 100644 --- a/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.h +++ b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data_reader.h @@ -27,7 +27,7 @@ enum class JpegReadMode { // If mode is kReadHeader, it fills in only the image dimensions in *jpg. // Returns false if the data is not valid JPEG, or if it contains an unsupported // JPEG feature. -bool ReadJpeg(const uint8_t* data, const size_t len, JpegReadMode mode, +bool ReadJpeg(const uint8_t* data, size_t len, JpegReadMode mode, JPEGData* jpg); } // namespace jpeg diff --git a/third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc b/third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc index 6744e6935a..aeb9914cca 100644 --- a/third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc +++ b/third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc @@ -5,6 +5,8 @@ #include "lib/jxl/jpeg/jpeg_data.h" +#include <jxl/types.h> + #include "lib/jxl/base/printf_macros.h" #include "lib/jxl/base/status.h" #include "lib/jxl/common.h" // kMaxNumPasses, JPEGXL_ENABLE_TRANSCODE_JPEG @@ -214,7 +216,7 @@ Status JPEGData::VisitFields(Visitor* visitor) { huffman_code.resize(num_huff); } for (JPEGHuffmanCode& hc : huffman_code) { - bool is_ac = hc.slot_id >> 4; + bool is_ac = ((hc.slot_id >> 4) != 0); uint32_t id = hc.slot_id & 0xF; JXL_RETURN_IF_ERROR(visitor->Bool(false, &is_ac)); JXL_RETURN_IF_ERROR(visitor->Bits(2, 0, &id)); @@ -240,7 +242,8 @@ Status JPEGData::VisitFields(Visitor* visitor) { JXL_RETURN_IF_ERROR(visitor->U32(Bits(2), BitsOffset(2, 4), BitsOffset(4, 8), BitsOffset(8, 1), 0, &hc.values[i])); - value_slots[hc.values[i] >> 6] |= (uint64_t)1 << (hc.values[i] & 0x3F); + value_slots[hc.values[i] >> 6] |= static_cast<uint64_t>(1) + << (hc.values[i] & 0x3F); } if (hc.values[num_symbols - 1] != kJpegHuffmanAlphabetSize) { return JXL_FAILURE("Missing EOI symbol"); @@ -361,13 +364,13 @@ Status JPEGData::VisitFields(Visitor* visitor) { for (uint32_t i = 0; i < nbit; i++) { bool bbit = false; JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit)); - padding_bits.push_back(bbit); + padding_bits.push_back(TO_JXL_BOOL(bbit)); } } else { for (uint8_t& bit : padding_bits) { - bool bbit = bit; + bool bbit = FROM_JXL_BOOL(bit); JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit)); - bit = bbit; + bit = TO_JXL_BOOL(bbit); } } } diff --git a/third_party/jpeg-xl/lib/jxl/jxl_test.cc b/third_party/jpeg-xl/lib/jxl/jxl_test.cc index a91dbd0672..b0933d5d50 100644 --- a/third_party/jpeg-xl/lib/jxl/jxl_test.cc +++ b/third_party/jpeg-xl/lib/jxl/jxl_test.cc @@ -33,7 +33,6 @@ #include "lib/jxl/codec_in_out.h" #include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/common.h" // JXL_HIGH_PRECISION -#include "lib/jxl/enc_butteraugli_comparator.h" #include "lib/jxl/enc_params.h" #include "lib/jxl/fake_parallel_runner_testonly.h" #include "lib/jxl/image.h" @@ -72,7 +71,7 @@ TEST(JxlTest, RoundtripSinglePixelWithAlpha) { TestImage t; t.SetDimensions(1, 1).SetChannels(4).AddFrame().ZeroFill(); PackedPixelFile ppf_out; - EXPECT_EQ(Roundtrip(t.ppf(), {}, {}, nullptr, &ppf_out), 59); + EXPECT_EQ(Roundtrip(t.ppf(), {}, {}, nullptr, &ppf_out), 58); } // Changing serialized signature causes Decode to fail. @@ -87,7 +86,7 @@ TEST(JxlTest, RoundtripMarker) { compressed[i] ^= 0xFF; PackedPixelFile ppf_out; EXPECT_FALSE(extras::DecodeImageJXL(compressed.data(), compressed.size(), - {}, /*decodec_bytes=*/nullptr, + {}, /* decoded_bytes */ nullptr, &ppf_out)); } } @@ -121,7 +120,7 @@ TEST(JxlTest, RoundtripSmallD1) { { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 916, 40); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.888)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.888); } // With a lower intensity target than the default, the bitrate should be @@ -131,7 +130,7 @@ TEST(JxlTest, RoundtripSmallD1) { { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 745, 20); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.3)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.3); EXPECT_EQ(ppf_out.info.intensity_target, t.ppf().info.intensity_target); } } @@ -147,8 +146,8 @@ TEST(JxlTest, RoundtripResample2) { cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 3); // kFalcon PackedPixelFile ppf_out; - EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 18500, 200); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(90)); + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 17300, 500); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 90); } TEST(JxlTest, RoundtripResample2Slow) { @@ -165,7 +164,7 @@ TEST(JxlTest, RoundtripResample2Slow) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 3888, 200); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(250)); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 250); } TEST(JxlTest, RoundtripResample2MT) { @@ -180,8 +179,8 @@ TEST(JxlTest, RoundtripResample2MT) { cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 3); // kFalcon PackedPixelFile ppf_out; - EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 223310, 2000); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(340)); + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 203300, 2000); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 340); } // Roundtrip the image using a parallel runner that executes single-threaded but @@ -237,7 +236,7 @@ TEST(JxlTest, RoundtripResample4) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 5758, 100); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(22)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 22); } TEST(JxlTest, RoundtripResample8) { @@ -252,7 +251,7 @@ TEST(JxlTest, RoundtripResample8) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 2036, 50); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(50)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 50); } TEST(JxlTest, RoundtripUnalignedD2) { @@ -270,7 +269,7 @@ TEST(JxlTest, RoundtripUnalignedD2) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 506, 30); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.72)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.72); } TEST(JxlTest, RoundtripMultiGroup) { @@ -289,8 +288,8 @@ TEST(JxlTest, RoundtripMultiGroup) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), expected_size, 700); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), - IsSlightlyBelow(expected_distance)); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), + expected_distance); }; auto run_kitten = std::async(std::launch::async, test, SpeedTier::kKitten, @@ -339,10 +338,11 @@ TEST(JxlTest, RoundtripRGBToGrayscale) { ASSERT_TRUE(io.frames[0].TransformTo(srgb_gamma, *JxlGetDefaultCms())); io.metadata.m.color_encoding = io2.Main().c_current(); io.Main().OverrideProfile(io2.Main().c_current()); - EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr, &pool), - IsSlightlyBelow(1.36)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr, &pool), + 1.4); } TEST(JxlTest, RoundtripLargeFast) { @@ -355,8 +355,57 @@ TEST(JxlTest, RoundtripLargeFast) { cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 7); // kSquirrel PackedPixelFile ppf_out; - EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 492867, 5000); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(78)); + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 503000, 12000); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 78); +} + +TEST(JxlTest, JXL_X86_64_TEST(RoundtripLargeEmptyModular)) { + ThreadPoolForTests pool(8); + TestImage t; + t.SetDimensions(4096, 4096).SetDataType(JXL_TYPE_UINT8).SetChannels(4); + TestImage::Frame frame = t.AddFrame(); + frame.ZeroFill(); + for (size_t c = 0; c < 4; ++c) { + for (size_t y = 0; y < 1024; y += (c + 1)) { + for (size_t x = 0; x < 1024; x += ((y % 4) + 3)) { + frame.SetValue(y, x, c, 0.88); + } + } + } + + JXLCompressParams cparams; + cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 1); + cparams.AddOption(JXL_ENC_FRAME_SETTING_MODULAR, 1); + cparams.AddOption(JXL_ENC_FRAME_SETTING_RESPONSIVE, 1); + cparams.AddOption(JXL_ENC_FRAME_SETTING_DECODING_SPEED, 2); + + PackedPixelFile ppf_out; + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 3474795, + 100000); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), +#if JXL_HIGH_PRECISION + 2050 +#else + 12100 +#endif + ); +} + +TEST(JxlTest, RoundtripOutputColorSpace) { + ThreadPoolForTests pool(8); + const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png"); + TestImage t; + t.DecodeFromBytes(orig).ClearMetadata(); + + JXLCompressParams cparams; + cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 7); // kSquirrel + + JXLDecompressParams dparams; + dparams.color_space = "RGB_D65_DCI_Rel_709"; + PackedPixelFile ppf_out; + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, &pool, &ppf_out), 503000, + 12000); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 78); } TEST(JxlTest, RoundtripDotsForceEpf) { @@ -373,7 +422,7 @@ TEST(JxlTest, RoundtripDotsForceEpf) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 41355, 300); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(18)); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 18); } // Checks for differing size/distance in two consecutive runs of distance 2, @@ -453,7 +502,7 @@ TEST(JxlTest, RoundtripSmallNL) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 916, 45); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.82)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.82); } TEST(JxlTest, RoundtripNoGaborishNoAR) { @@ -469,7 +518,7 @@ TEST(JxlTest, RoundtripNoGaborishNoAR) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 41142, 400); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.8)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.8); } TEST(JxlTest, RoundtripSmallNoGaborish) { @@ -487,7 +536,7 @@ TEST(JxlTest, RoundtripSmallNoGaborish) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 1006, 20); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.1)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.1); } TEST(JxlTest, RoundtripSmallPatchesAlpha) { @@ -513,7 +562,7 @@ TEST(JxlTest, RoundtripSmallPatchesAlpha) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 597, 100); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.018f)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.018f); } TEST(JxlTest, RoundtripSmallPatches) { @@ -538,17 +587,17 @@ TEST(JxlTest, RoundtripSmallPatches) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 486, 100); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.018f)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.018f); } // TODO(szabadka) Add encoder and decoder API functions that accept frame // buffers in arbitrary unsigned and floating point formats, and then roundtrip // test the lossless codepath to make sure the exact binary representations // are preserved. -#if 0 +#if JXL_FALSE TEST(JxlTest, RoundtripImageBundleOriginalBits) { // Image does not matter, only io.metadata.m and io2.metadata.m are tested. - Image3F image(1, 1); + JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(1, 1)); ZeroFillImage(&image); CodecInOut io; io.metadata.m.color_encoding = ColorEncoding::LinearSRGB(); @@ -639,10 +688,11 @@ TEST(JxlTest, RoundtripGrayscale) { EXPECT_TRUE(io2.Main().IsGray()); EXPECT_LE(compressed.size(), 7000u); - EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(1.6)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 1.6); } // Test with larger butteraugli distance and other settings enabled so @@ -658,10 +708,11 @@ TEST(JxlTest, RoundtripGrayscale) { EXPECT_TRUE(io2.Main().IsGray()); EXPECT_LE(compressed.size(), 1300u); - EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(6.7)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 6.7); } { @@ -678,10 +729,11 @@ TEST(JxlTest, RoundtripGrayscale) { EXPECT_FALSE(io2.Main().IsGray()); EXPECT_LE(compressed.size(), 7000u); - EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(1.6)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 1.6); } } @@ -706,7 +758,7 @@ TEST(JxlTest, RoundtripAlpha) { std::vector<uint8_t> compressed; EXPECT_TRUE(test::EncodeFile(cparams, &io, &compressed)); - EXPECT_LE(compressed.size(), 10077u); + EXPECT_LE(compressed.size(), 20000u); for (bool use_image_callback : {false, true}) { for (bool unpremul_alpha : {false, true}) { @@ -715,10 +767,11 @@ TEST(JxlTest, RoundtripAlpha) { dparams.use_image_callback = use_image_callback; dparams.unpremultiply_alpha = unpremul_alpha; EXPECT_TRUE(test::DecodeFile(dparams, Bytes(compressed), &io2)); - EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, - ButteraugliParams(), *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(1.15)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 1.15); } } } @@ -781,7 +834,8 @@ bool UnpremultiplyAlpha(CodecInOut& io) { TEST(JxlTest, RoundtripAlphaPremultiplied) { const std::vector<uint8_t> orig = ReadTestData("external/wesaturate/500px/tmshre_riaphotographs_alpha.png"); - CodecInOut io, io_nopremul; + CodecInOut io; + CodecInOut io_nopremul; ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_nopremul)); @@ -803,7 +857,7 @@ TEST(JxlTest, RoundtripAlphaPremultiplied) { std::vector<uint8_t> compressed; EXPECT_TRUE(test::EncodeFile(cparams, &io, &compressed)); - EXPECT_LE(compressed.size(), 10000u); + EXPECT_LE(compressed.size(), 18000u); for (bool use_image_callback : {false, true}) { for (bool unpremul_alpha : {false, true}) { @@ -826,19 +880,19 @@ TEST(JxlTest, RoundtripAlphaPremultiplied) { EXPECT_EQ(unpremul_alpha, !io2.Main().AlphaIsPremultiplied()); if (!unpremul_alpha) { - EXPECT_THAT( + EXPECT_SLIGHTLY_BELOW( ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), *JxlGetDefaultCms(), /*distmap=*/nullptr), - IsSlightlyBelow(1.111)); + 1.111); EXPECT_TRUE(UnpremultiplyAlpha(io2)); EXPECT_FALSE(io2.Main().AlphaIsPremultiplied()); } - EXPECT_THAT( + EXPECT_SLIGHTLY_BELOW( ButteraugliDistance(io_nopremul.frames, io2.frames, ButteraugliParams(), *JxlGetDefaultCms(), /*distmap=*/nullptr), - IsSlightlyBelow(1.55)); + 1.0); } } } @@ -854,13 +908,14 @@ TEST(JxlTest, RoundtripAlphaResampling) { ASSERT_TRUE(t.ppf().info.alpha_bits > 0); JXLCompressParams cparams; + cparams.alpha_distance = 1.0; cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 5); // kHare cparams.AddOption(JXL_ENC_FRAME_SETTING_RESAMPLING, 2); cparams.AddOption(JXL_ENC_FRAME_SETTING_EXTRA_CHANNEL_RESAMPLING, 2); PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 13507, 130); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(5.2)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 5.2); } TEST(JxlTest, RoundtripAlphaResamplingOnlyAlpha) { @@ -873,12 +928,13 @@ TEST(JxlTest, RoundtripAlphaResamplingOnlyAlpha) { ASSERT_TRUE(t.ppf().info.alpha_bits > 0); JXLCompressParams cparams; + cparams.alpha_distance = 1.0; cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 3); // kFalcon cparams.AddOption(JXL_ENC_FRAME_SETTING_EXTRA_CHANNEL_RESAMPLING, 2); PackedPixelFile ppf_out; - EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 33571, 400); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.49)); + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 32000, 1000); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.52); } TEST(JxlTest, RoundtripAlphaNonMultipleOf8) { @@ -893,13 +949,14 @@ TEST(JxlTest, RoundtripAlphaNonMultipleOf8) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), {}, {}, pool, &ppf_out), 107, 10); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.95)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.006); } TEST(JxlTest, RoundtripAlpha16) { ThreadPoolForTests pool(4); // The image is wider than 512 pixels to ensure multiple groups are tested. - size_t xsize = 1200, ysize = 160; + size_t xsize = 1200; + size_t ysize = 160; TestImage t; t.SetDimensions(xsize, ysize).SetChannels(4).SetAllBitDepths(16); TestImage::Frame frame = t.AddFrame(); @@ -923,12 +980,13 @@ TEST(JxlTest, RoundtripAlpha16) { JXLCompressParams cparams; cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 6); // kWombat cparams.distance = 0.5; + cparams.alpha_distance = 0.5; PackedPixelFile ppf_out; // TODO(szabadka) Investigate big size difference on i686 // This still keeps happening (2023-04-18). EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 3666, 120); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.65)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.65); } namespace { @@ -990,8 +1048,8 @@ TEST(JxlTest, JXL_SLOW_TEST(RoundtripLossless8LightningGradient)) { PackedPixelFile ppf_out; // Lax comparison because different SIMD will cause different compression. - EXPECT_THAT(Roundtrip(t.ppf(), cparams, dparams, &pool, &ppf_out), - IsSlightlyBelow(286848u)); + EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), cparams, dparams, &pool, &ppf_out), + 286848u); EXPECT_EQ(ComputeDistance2(t.ppf(), ppf_out), 0.0); } @@ -1035,7 +1093,8 @@ TEST(JxlTest, RoundtripLossless8Alpha) { TEST(JxlTest, RoundtripLossless16Alpha) { ThreadPool* pool = nullptr; - size_t xsize = 1200, ysize = 160; + size_t xsize = 1200; + size_t ysize = 160; TestImage t; t.SetDimensions(xsize, ysize).SetChannels(4).SetAllBitDepths(16); TestImage::Frame frame = t.AddFrame(); @@ -1062,7 +1121,7 @@ TEST(JxlTest, RoundtripLossless16Alpha) { PackedPixelFile ppf_out; // TODO(szabadka) Investigate big size difference on i686 - EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), 4884, 100); + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), 4665, 100); EXPECT_EQ(ComputeDistance2(t.ppf(), ppf_out), 0.0); EXPECT_EQ(ppf_out.info.alpha_bits, 16); EXPECT_TRUE(test::SameAlpha(t.ppf(), ppf_out)); @@ -1070,7 +1129,8 @@ TEST(JxlTest, RoundtripLossless16Alpha) { TEST(JxlTest, RoundtripLossless16AlphaNotMisdetectedAs8Bit) { ThreadPool* pool = nullptr; - size_t xsize = 128, ysize = 128; + size_t xsize = 128; + size_t ysize = 128; TestImage t; t.SetDimensions(xsize, ysize).SetChannels(4).SetAllBitDepths(16); TestImage::Frame frame = t.AddFrame(); @@ -1098,7 +1158,7 @@ TEST(JxlTest, RoundtripLossless16AlphaNotMisdetectedAs8Bit) { dparams.accepted_formats.push_back(t.ppf().frames[0].color.format); PackedPixelFile ppf_out; - EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), 591, 50); + EXPECT_NEAR(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), 280, 50); EXPECT_EQ(ComputeDistance2(t.ppf(), ppf_out), 0.0); EXPECT_EQ(ppf_out.info.bits_per_sample, 16); EXPECT_EQ(ppf_out.info.alpha_bits, 16); @@ -1123,7 +1183,7 @@ TEST(JxlTest, RoundtripDots) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 280333, 4000); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(0.35)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 0.35); } TEST(JxlTest, RoundtripNoise) { @@ -1143,7 +1203,7 @@ TEST(JxlTest, RoundtripNoise) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 41009, 750); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.42)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.42); } TEST(JxlTest, RoundtripLossless8Gray) { @@ -1182,17 +1242,17 @@ TEST(JxlTest, RoundtripAnimation) { dparams.accepted_formats.push_back(t.ppf().frames[0].color.format); PackedPixelFile ppf_out; - EXPECT_THAT(Roundtrip(t.ppf(), {}, dparams, pool, &ppf_out), - IsSlightlyBelow(2888)); + EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), {}, dparams, pool, &ppf_out), 3350); t.CoalesceGIFAnimationWithAlpha(); ASSERT_EQ(ppf_out.frames.size(), t.ppf().frames.size()); - EXPECT_LE(ButteraugliDistance(t.ppf(), ppf_out), + static constexpr double kMaxButteraugli = #if JXL_HIGH_PRECISION - 1.55); + 1.55; #else - 1.75); + 1.75; #endif + EXPECT_LE(ButteraugliDistance(t.ppf(), ppf_out), kMaxButteraugli); } TEST(JxlTest, RoundtripLosslessAnimation) { @@ -1212,8 +1272,8 @@ TEST(JxlTest, RoundtripLosslessAnimation) { dparams.accepted_formats.push_back(t.ppf().frames[0].color.format); PackedPixelFile ppf_out; - EXPECT_THAT(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), - IsSlightlyBelow(958)); + EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), + 958); t.CoalesceGIFAnimationWithAlpha(); ASSERT_EQ(ppf_out.frames.size(), t.ppf().frames.size()); @@ -1240,11 +1300,11 @@ TEST(JxlTest, RoundtripAnimationPatches) { PackedPixelFile ppf_out; // 40k with no patches, 27k with patch frames encoded multiple times. - EXPECT_THAT(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), - IsSlightlyBelow(19300)); + EXPECT_SLIGHTLY_BELOW(Roundtrip(t.ppf(), cparams, dparams, pool, &ppf_out), + 19300); EXPECT_EQ(ppf_out.frames.size(), t.ppf().frames.size()); // >10 with broken patches; not all patches are detected on borders. - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.9)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.9); } size_t RoundtripJpeg(const std::vector<uint8_t>& jpeg_in, ThreadPool* pool) { @@ -1293,7 +1353,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444)) { const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png.im_q85_444.jpg"); // JPEG size is 696,659 bytes. - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 568940u, 20); + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 568891u, 20); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels)) { @@ -1306,7 +1366,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels)) { PackedPixelFile ppf_out; RoundtripJpegToPixels(orig, {}, &pool, &ppf_out); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(12)); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 12); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420)) { @@ -1319,7 +1379,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420)) { PackedPixelFile ppf_out; RoundtripJpegToPixels(orig, {}, &pool, &ppf_out); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(11)); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 11); } TEST(JxlTest, @@ -1336,7 +1396,7 @@ TEST(JxlTest, PackedPixelFile ppf_out; RoundtripJpegToPixels(orig, dparams, &pool, &ppf_out); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(4410)); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 4410); } TEST(JxlTest, @@ -1350,7 +1410,7 @@ TEST(JxlTest, PackedPixelFile ppf_out; RoundtripJpegToPixels(orig, {}, &pool, &ppf_out); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(4)); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 4); } TEST(JxlTest, @@ -1364,7 +1424,7 @@ TEST(JxlTest, PackedPixelFile ppf_out; RoundtripJpegToPixels(orig, {}, &pool, &ppf_out); - EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(10)); + EXPECT_SLIGHTLY_BELOW(ComputeDistance2(t.ppf(), ppf_out), 10); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionGray)) { @@ -1380,7 +1440,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420)) { const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png.im_q85_420.jpg"); // JPEG size is 546,797 bytes. - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 455560u, 10); + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 455510u, 20); } TEST(JxlTest, @@ -1389,7 +1449,7 @@ TEST(JxlTest, const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png.im_q85_luma_subsample.jpg"); // JPEG size is 400,724 bytes. - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 325354u, 15); + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 325310u, 20); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444_12)) { @@ -1398,7 +1458,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444_12)) { const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png.im_q85_444_1x2.jpg"); // JPEG size is 703,874 bytes. - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 569679u, 10); + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 569630u, 20); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression422)) { @@ -1406,7 +1466,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression422)) { const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png.im_q85_422.jpg"); // JPEG size is 522,057 bytes. - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 499282u, 10); + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 499236u, 20); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression440)) { @@ -1414,7 +1474,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression440)) { const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png.im_q85_440.jpg"); // JPEG size is 603,623 bytes. - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 501151u, 10); + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 501101u, 20); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression_asymmetric)) { @@ -1424,7 +1484,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression_asymmetric)) { const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png.im_q85_asymmetric.jpg"); // JPEG size is 604,601 bytes. - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 500602u, 10); + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 500548u, 20); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420Progr)) { @@ -1432,7 +1492,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420Progr)) { const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png.im_q85_420_progr.jpg"); // JPEG size is 522,057 bytes. - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 455499u, 10); + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 455454u, 20); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionMetadata)) { @@ -1440,7 +1500,8 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionMetadata)) { const std::vector<uint8_t> orig = ReadTestData("jxl/jpeg_reconstruction/1x1_exif_xmp.jpg"); // JPEG size is 4290 bytes - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 1400u, 30); + // 1370 on 386, so higher margin. + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 1334u, 100); } TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionRestarts)) { @@ -1448,7 +1509,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionRestarts)) { const std::vector<uint8_t> orig = ReadTestData("jxl/jpeg_reconstruction/bicycles_restarts.jpg"); // JPEG size is 87478 bytes - EXPECT_NEAR(RoundtripJpeg(orig, &pool), 76125u, 30); + EXPECT_NEAR(RoundtripJpeg(orig, &pool), 76054u, 30); } TEST(JxlTest, @@ -1475,7 +1536,7 @@ TEST(JxlTest, RoundtripProgressive) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 70544, 750); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.4)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.4); } TEST(JxlTest, RoundtripProgressiveLevel2Slow) { @@ -1492,7 +1553,7 @@ TEST(JxlTest, RoundtripProgressiveLevel2Slow) { PackedPixelFile ppf_out; EXPECT_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 76666, 1000); - EXPECT_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.17)); + EXPECT_SLIGHTLY_BELOW(ButteraugliDistance(t.ppf(), ppf_out), 1.17); } TEST(JxlTest, RoundtripUnsignedCustomBitdepthLossless) { @@ -1509,9 +1570,9 @@ TEST(JxlTest, RoundtripUnsignedCustomBitdepthLossless) { t.SetAllBitDepths(bitdepth).SetEndianness(endianness); TestImage::Frame frame = t.AddFrame(); frame.RandomFill(); + t.ppf().input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; JXLCompressParams cparams = CompressParamsForLossless(); - cparams.input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; JXLDecompressParams dparams; dparams.accepted_formats.push_back(t.ppf().frames[0].color.format); @@ -1543,7 +1604,6 @@ TEST(JxlTest, LosslessPNMRoundtrip) { JXLCompressParams cparams = CompressParamsForLossless(); cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 1); // kLightning - cparams.input_bitdepth.type = JXL_BIT_DEPTH_FROM_CODESTREAM; JXLDecompressParams dparams; dparams.accepted_formats.push_back(t.ppf().frames[0].color.format); @@ -1592,10 +1652,13 @@ struct StreamingTestParam { size_t xsize; size_t ysize; bool is_grey; + bool has_alpha; int effort; bool progressive; - size_t num_channels() const { return is_grey ? 1 : 3; } + size_t num_channels() const { + return (is_grey ? 1 : 3) + (has_alpha ? 1 : 0); + } float max_psnr() const { return is_grey ? 90 : 50; } @@ -1603,11 +1666,13 @@ struct StreamingTestParam { std::vector<StreamingTestParam> params; for (int e : {1, 3, 4, 7}) { for (bool g : {false, true}) { - params.push_back(StreamingTestParam{357, 517, g, e, false}); - params.push_back(StreamingTestParam{2247, 2357, g, e, false}); + params.push_back(StreamingTestParam{357, 517, g, false, e, false}); + params.push_back(StreamingTestParam{2247, 2357, g, false, e, false}); } } - params.push_back(StreamingTestParam{2247, 2357, false, 1, true}); + params.push_back(StreamingTestParam{2247, 2357, false, false, 1, true}); + params.push_back(StreamingTestParam{2247, 2157, false, false, 5, false}); + params.push_back(StreamingTestParam{2247, 2157, false, true, 5, false}); return params; } }; @@ -1651,15 +1716,66 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P( JxlStreamingTest, JxlStreamingTest, testing::ValuesIn(StreamingTestParam::All())); +struct StreamingEncodingTestParam { + std::string file; + int effort; + float distance; + int group_size; + float palette_percent; + + static std::vector<StreamingEncodingTestParam> All() { + std::vector<StreamingEncodingTestParam> params; + for (const auto* file : + {"jxl/flower/flower.png", "jxl/flower/flower_alpha.png"}) { + for (int effort : {1, 3, 5, 6}) { + if (effort != 1) { + params.push_back( + StreamingEncodingTestParam{file, effort, 1.0, 1, -1}); + params.push_back( + StreamingEncodingTestParam{file, effort, 4.0, 1, -1}); + } + for (auto group_size : {-1, 0}) { + for (float palette_percent : {-1, 50, 100}) { + params.push_back(StreamingEncodingTestParam{ + file, effort, 0.0, group_size, palette_percent}); + } + } + } + } + return params; + } +}; + +std::ostream& operator<<(std::ostream& out, + const StreamingEncodingTestParam& p) { + out << p.file << "-"; + out << "e" << p.effort; + if (p.distance == 0) { + out << "Lossless"; + out << "G" << p.group_size << "P" << p.palette_percent; + } else { + out << "D" << p.distance; + } + return out; +} + +class JxlStreamingEncodingTest + : public ::testing::TestWithParam<StreamingEncodingTestParam> {}; + // This is broken on mingw32, so we only enable it for x86_64 now. -TEST(JxlTest, JXL_X86_64_TEST(StreamingSamePixels)) { - const std::vector<uint8_t> orig = ReadTestData("jxl/flower/flower.png"); +TEST_P(JxlStreamingEncodingTest, JXL_X86_64_TEST(StreamingSamePixels)) { + const auto param = GetParam(); + const std::vector<uint8_t> orig = ReadTestData(param.file); jxl::test::TestImage image; image.DecodeFromBytes(orig); + JXLCompressParams cparams; - cparams.distance = 1.0; - cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, 6); + cparams.distance = param.distance; + cparams.AddOption(JXL_ENC_FRAME_SETTING_EFFORT, param.effort); + cparams.AddOption(JXL_ENC_FRAME_SETTING_MODULAR_GROUP_SIZE, param.group_size); + cparams.AddFloatOption(JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GROUP_PERCENT, + param.palette_percent); cparams.AddOption(JXL_ENC_FRAME_SETTING_USE_FULL_IMAGE_HEURISTICS, 0); ThreadPoolForTests pool(8); @@ -1673,5 +1789,9 @@ TEST(JxlTest, JXL_X86_64_TEST(StreamingSamePixels)) { EXPECT_TRUE(jxl::test::SamePixels(ppf_out, ppf_out_streaming)); } +JXL_GTEST_INSTANTIATE_TEST_SUITE_P( + JxlStreamingTest, JxlStreamingEncodingTest, + testing::ValuesIn(StreamingEncodingTestParam::All())); + } // namespace } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/libjxl.pc.in b/third_party/jpeg-xl/lib/jxl/libjxl.pc.in index 58b6941305..f5a9c43650 100644 --- a/third_party/jpeg-xl/lib/jxl/libjxl.pc.in +++ b/third_party/jpeg-xl/lib/jxl/libjxl.pc.in @@ -7,7 +7,7 @@ Name: libjxl Description: Loads and saves JPEG XL files Version: @JPEGXL_LIBRARY_VERSION@ @JPEGXL_REQUIRES_TYPE@: @JPEGXL_LIBRARY_REQUIRES@ -Libs: -L${libdir} -ljxl -Libs.private: -lm +Libs: -L${libdir} -ljxl @JPEGXL_PUBLIC_LIBS@ +Libs.private: @JPEGXL_PRIVATE_LIBS@ Cflags: -I${includedir} Cflags.private: -DJXL_STATIC_DEFINE diff --git a/third_party/jpeg-xl/lib/jxl/libjxl_cms.pc.in b/third_party/jpeg-xl/lib/jxl/libjxl_cms.pc.in index 9aaa3f4dbe..deab7367ac 100644 --- a/third_party/jpeg-xl/lib/jxl/libjxl_cms.pc.in +++ b/third_party/jpeg-xl/lib/jxl/libjxl_cms.pc.in @@ -7,7 +7,7 @@ Name: libjxl_cms Description: CMS support library for libjxl Version: @JPEGXL_LIBRARY_VERSION@ @JPEGXL_REQUIRES_TYPE@: @JPEGXL_CMS_LIBRARY_REQUIRES@ -Libs: -L${libdir} -ljxl_cms -Libs.private: -lm +Libs: -L${libdir} -ljxl_cms @JPEGXL_CMS_PUBLIC_LIBS@ +Libs.private: @JPEGXL_CMS_PRIVATE_LIBS@ Cflags: -I${includedir} Cflags.private: -DJXL_CMS_STATIC_DEFINE diff --git a/third_party/jpeg-xl/lib/jxl/memory_manager_internal.h b/third_party/jpeg-xl/lib/jxl/memory_manager_internal.h index c728d62e35..e578e91f8e 100644 --- a/third_party/jpeg-xl/lib/jxl/memory_manager_internal.h +++ b/third_party/jpeg-xl/lib/jxl/memory_manager_internal.h @@ -36,11 +36,13 @@ static JXL_INLINE Status MemoryManagerInit( } else { memset(self, 0, sizeof(*self)); } - if (!self->alloc != !self->free) { + bool is_default_alloc = (self->alloc == nullptr); + bool is_default_free = (self->free == nullptr); + if (is_default_alloc != is_default_free) { return false; } - if (!self->alloc) self->alloc = jxl::MemoryManagerDefaultAlloc; - if (!self->free) self->free = jxl::MemoryManagerDefaultFree; + if (is_default_alloc) self->alloc = jxl::MemoryManagerDefaultAlloc; + if (is_default_free) self->free = jxl::MemoryManagerDefaultFree; return true; } @@ -52,7 +54,7 @@ static JXL_INLINE void* MemoryManagerAlloc( static JXL_INLINE void MemoryManagerFree(const JxlMemoryManager* memory_manager, void* address) { - return memory_manager->free(memory_manager->opaque, address); + memory_manager->free(memory_manager->opaque, address); } // Helper class to be used as a deleter in a unique_ptr<T> call. diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h b/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h index 4c3a33a52a..7bec5128fc 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h @@ -10,6 +10,7 @@ #include <vector> #include "lib/jxl/fields.h" +#include "lib/jxl/image_ops.h" #include "lib/jxl/modular/modular_image.h" #include "lib/jxl/modular/options.h" @@ -78,14 +79,14 @@ struct State { 294337, 289262, 284359, 279620, 275036, 270600, 266305, 262144}; constexpr static pixel_type_w AddBits(pixel_type_w x) { - return uint64_t(x) << kPredExtraBits; + return static_cast<uint64_t>(x) << kPredExtraBits; } State(Header header, size_t xsize, size_t ysize) : header(header) { // Extra margin to avoid out-of-bounds writes. // All have space for two rows of data. - for (size_t i = 0; i < 4; i++) { - pred_errors[i].resize((xsize + 2) * 2); + for (auto &pred_error : pred_errors) { + pred_error.resize((xsize + 2) * 2); } error.resize((xsize + 2) * 2); } @@ -538,8 +539,9 @@ JXL_INLINE PredictionResult Predict( } if (mode & kAllPredictions) { for (size_t i = 0; i < kNumModularPredictors; i++) { - predictions[i] = PredictOne((Predictor)i, left, top, toptop, topleft, - topright, leftleft, toprightright, wp_pred); + predictions[i] = + PredictOne(static_cast<Predictor>(i), left, top, toptop, topleft, + topright, leftleft, toprightright, wp_pred); } } result.guess += PredictOne(predictor, left, top, toptop, topleft, topright, diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc index ee7177bcd6..b53b9a9103 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc @@ -5,6 +5,8 @@ #include "lib/jxl/modular/encoding/dec_ma.h" +#include <limits> + #include "lib/jxl/base/printf_macros.h" #include "lib/jxl/dec_ans.h" #include "lib/jxl/modular/encoding/ma_common.h" diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_debug_tree.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_debug_tree.cc index bd27f28458..f863823629 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_debug_tree.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_debug_tree.cc @@ -95,7 +95,7 @@ std::string PropertyName(size_t i) { case 15: return "WGH"; default: - return "ch[" + ToString(15 - (int)i) + "]"; + return "ch[" + ToString(15 - static_cast<int>(i)) + "]"; } } diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc index fc2e69e4a6..84d8137d21 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc @@ -6,35 +6,24 @@ #include <stdint.h> #include <stdlib.h> -#include <cinttypes> #include <limits> -#include <numeric> #include <queue> -#include <set> -#include <unordered_map> -#include <unordered_set> #include "lib/jxl/base/common.h" #include "lib/jxl/base/printf_macros.h" #include "lib/jxl/base/status.h" -#include "lib/jxl/dec_ans.h" -#include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/enc_ans.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_bit_writer.h" #include "lib/jxl/enc_fields.h" -#include "lib/jxl/entropy_coder.h" #include "lib/jxl/fields.h" #include "lib/jxl/image_ops.h" #include "lib/jxl/modular/encoding/context_predict.h" -#include "lib/jxl/modular/encoding/enc_debug_tree.h" #include "lib/jxl/modular/encoding/enc_ma.h" #include "lib/jxl/modular/encoding/encoding.h" #include "lib/jxl/modular/encoding/ma_common.h" #include "lib/jxl/modular/options.h" -#include "lib/jxl/modular/transform/transform.h" #include "lib/jxl/pack_signed.h" -#include "lib/jxl/toc.h" namespace jxl { @@ -61,7 +50,7 @@ inline std::array<uint8_t, 3> PredictorColor(Predictor p) { return {{255, 0, 255}}; case Predictor::Weighted: return {{0, 255, 255}}; - // TODO + // TODO(jon) default: return {{255, 255, 255}}; }; @@ -101,17 +90,17 @@ Tree MakeFixedTree(int property, const std::vector<int32_t> &cutoffs, } // namespace -void GatherTreeData(const Image &image, pixel_type chan, size_t group_id, - const weighted::Header &wp_header, - const ModularOptions &options, TreeSamples &tree_samples, - size_t *total_pixels) { +Status GatherTreeData(const Image &image, pixel_type chan, size_t group_id, + const weighted::Header &wp_header, + const ModularOptions &options, TreeSamples &tree_samples, + size_t *total_pixels) { const Channel &channel = image.channel[chan]; JXL_DEBUG_V(7, "Learning %" PRIuS "x%" PRIuS " channel %d", channel.w, channel.h, chan); std::array<pixel_type, kNumStaticProperties> static_props = { - {chan, (int)group_id}}; + {chan, static_cast<int>(group_id)}}; Properties properties(kNumNonrefProperties + kExtraPropsPerChannel * options.max_properties); double pixel_fraction = std::min(1.0f, options.nb_repeats); @@ -137,7 +126,9 @@ void GatherTreeData(const Image &image, pixel_type chan, size_t group_id, }; const intptr_t onerow = channel.plane.PixelsPerRow(); - Channel references(properties.size() - kNumNonrefProperties, channel.w); + JXL_ASSIGN_OR_RETURN( + Channel references, + Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); weighted::State wp_state(wp_header, channel.w, channel.h); tree_samples.PrepareForSamples(pixel_fraction * channel.h * channel.w + 64); const bool multiple_predictors = tree_samples.NumPredictors() != 1; @@ -198,6 +189,7 @@ void GatherTreeData(const Image &image, pixel_type chan, size_t group_id, } } } + return true; } Tree PredefinedTree(ModularOptions::TreeKind tree_kind, size_t total_pixels) { @@ -316,7 +308,9 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan, JXL_ASSERT(channel.w != 0 && channel.h != 0); Image3F predictor_img; - if (kWantDebug) predictor_img = Image3F(channel.w, channel.h); + if (kWantDebug) { + JXL_ASSIGN_OR_RETURN(predictor_img, Image3F::Create(channel.w, channel.h)); + } JXL_DEBUG_V(6, "Encoding %" PRIuS "x%" PRIuS @@ -325,8 +319,9 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan, channel.w, channel.h, chan, channel.hshift, channel.vshift); std::array<pixel_type, kNumStaticProperties> static_props = { - {chan, (int)group_id}}; - bool use_wp, is_wp_only; + {chan, static_cast<int>(group_id)}}; + bool use_wp; + bool is_wp_only; bool is_gradient_only; size_t num_props; FlatTree tree = FilterTree(global_tree, static_props, &num_props, &use_wp, @@ -439,7 +434,8 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan, FillImage(static_cast<float>(PredictorColor(tree[0].predictor)[c]), &predictor_img.Plane(c)); } - uint32_t mul_shift = FloorLog2Nonzero((uint32_t)tree[0].multiplier); + uint32_t mul_shift = + FloorLog2Nonzero(static_cast<uint32_t>(tree[0].multiplier)); const intptr_t onerow = channel.plane.PixelsPerRow(); for (size_t y = 0; y < channel.h; y++) { const pixel_type *JXL_RESTRICT r = channel.Row(y); @@ -454,7 +450,9 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan, } else if (!use_wp && !skip_encoder_fast_path) { const intptr_t onerow = channel.plane.PixelsPerRow(); - Channel references(properties.size() - kNumNonrefProperties, channel.w); + JXL_ASSIGN_OR_RETURN( + Channel references, + Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); for (size_t y = 0; y < channel.h; y++) { const pixel_type *JXL_RESTRICT p = channel.Row(y); PrecomputeReferences(channel, y, image, chan, &references); @@ -481,7 +479,9 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan, } } else { const intptr_t onerow = channel.plane.PixelsPerRow(); - Channel references(properties.size() - kNumNonrefProperties, channel.w); + JXL_ASSIGN_OR_RETURN( + Channel references, + Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); weighted::State wp_state(wp_header, channel.w, channel.h); for (size_t y = 0; y < channel.h; y++) { const pixel_type *JXL_RESTRICT p = channel.Row(y); @@ -556,8 +556,20 @@ Status ModularEncode(const Image &image, const ModularOptions &options, TreeSamples tree_samples_storage; size_t total_pixels_storage = 0; if (!total_pixels) total_pixels = &total_pixels_storage; + if (*total_pixels == 0) { + for (size_t i = 0; i < nb_channels; i++) { + if (i >= image.nb_meta_channels && + (image.channel[i].w > options.max_chan_size || + image.channel[i].h > options.max_chan_size)) { + break; + } + *total_pixels += image.channel[i].w * image.channel[i].h; + } + *total_pixels = std::max<size_t>(*total_pixels, 1); + } // If there's no tree, compute one (or gather data to). - if (tree == nullptr) { + if (tree == nullptr && + options.tree_kind == ModularOptions::TreeKind::kLearn) { bool gather_data = tree_samples != nullptr; if (tree_samples == nullptr) { JXL_RETURN_IF_ERROR(tree_samples_storage.SetPredictor( @@ -586,9 +598,9 @@ Status ModularEncode(const Image &image, const ModularOptions &options, image.channel[i].h > options.max_chan_size)) { break; } - GatherTreeData(image, i, group_id, header->wp_header, options, - gather_data ? *tree_samples : tree_samples_storage, - total_pixels); + JXL_RETURN_IF_ERROR(GatherTreeData( + image, i, group_id, header->wp_header, options, + gather_data ? *tree_samples : tree_samples_storage, total_pixels)); } if (gather_data) return true; } @@ -609,10 +621,10 @@ Status ModularEncode(const Image &image, const ModularOptions &options, ? LearnTree(std::move(tree_samples_storage), *total_pixels, options) : PredefinedTree(options.tree_kind, *total_pixels); tree = &tree_storage; - tokens = &tokens_storage[0]; + tokens = tokens_storage.data(); Tree decoded_tree; - TokenizeTree(*tree, &tree_tokens[0], &decoded_tree); + TokenizeTree(*tree, tree_tokens.data(), &decoded_tree); JXL_ASSERT(tree->size() == decoded_tree.size()); tree_storage = std::move(decoded_tree); @@ -622,9 +634,9 @@ Status ModularEncode(const Image &image, const ModularOptions &options, } */ // Write tree - BuildAndEncodeHistograms(HistogramParams(), kNumTreeContexts, tree_tokens, - &code, &context_map, writer, kLayerModularTree, - aux_out); + BuildAndEncodeHistograms(options.histogram_params, kNumTreeContexts, + tree_tokens, &code, &context_map, writer, + kLayerModularTree, aux_out); WriteTokens(tree_tokens[0], code, context_map, 0, writer, kLayerModularTree, aux_out); } @@ -669,7 +681,7 @@ Status ModularEncode(const Image &image, const ModularOptions &options, if (!header->use_global_tree) { EntropyEncodingData code; std::vector<uint8_t> context_map; - HistogramParams histo_params; + HistogramParams histo_params = options.histogram_params; histo_params.image_widths.push_back(image_width); BuildAndEncodeHistograms(histo_params, (tree->size() + 1) / 2, tokens_storage, &code, &context_map, writer, layer, @@ -691,7 +703,7 @@ Status ModularGenericCompress(Image &image, const ModularOptions &opts, if (image.w == 0 || image.h == 0) return true; ModularOptions options = opts; // Make a copy to modify it. - if (options.predictor == static_cast<Predictor>(-1)) { + if (options.predictor == kUndefinedPredictor) { options.predictor = Predictor::Gradient; } diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc index ef72b2477b..de629ad038 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_ma.cc @@ -109,8 +109,8 @@ IntersectionType BoxIntersects(StaticPropRange needle, StaticPropRange haystack, void SplitTreeSamples(TreeSamples &tree_samples, size_t begin, size_t pos, size_t end, size_t prop) { auto cmp = [&](size_t a, size_t b) { - return int32_t(tree_samples.Property(prop, a)) - - int32_t(tree_samples.Property(prop, b)); + return static_cast<int32_t>(tree_samples.Property(prop, a)) - + static_cast<int32_t>(tree_samples.Property(prop, b)); }; Rng rng(0); while (end > begin + 1) { @@ -243,7 +243,8 @@ void FindBestSplit(TreeSamples &tree_samples, float threshold, // properties. We do this even if the current node is not a leaf, to // minimize the number of nodes in the resulting tree. for (size_t i = 0; i < mul_info.size(); i++) { - uint32_t axis, val; + uint32_t axis; + uint32_t val; IntersectionType t = BoxIntersects(static_prop_range, mul_info[i].range, axis, val); if (t == IntersectionType::kNone) continue; @@ -696,7 +697,11 @@ void TreeSamples::Swap(size_t a, size_t b) { } void TreeSamples::ThreeShuffle(size_t a, size_t b, size_t c) { - if (b == c) return Swap(a, b); + if (b == c) { + Swap(a, b); + return; + } + for (auto &r : residuals) { auto tmp = r[a]; r[a] = r[c]; @@ -966,7 +971,7 @@ void CollectPixelSamples(const Image &image, const ModularOptions &options, const pixel_type *row = image.channel[channel_ids[i]].Row(y); pixel_samples.push_back(row[x]); size_t xp = x == 0 ? 1 : x - 1; - diff_samples.push_back((int64_t)row[x] - row[xp]); + diff_samples.push_back(static_cast<int64_t>(row[x]) - row[xp]); } } diff --git a/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc index a6abdcfc91..bb690b74ba 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc @@ -14,6 +14,8 @@ #include "lib/jxl/base/scope_guard.h" #include "lib/jxl/dec_ans.h" #include "lib/jxl/dec_bit_reader.h" +#include "lib/jxl/frame_dimensions.h" +#include "lib/jxl/image_ops.h" #include "lib/jxl/modular/encoding/context_predict.h" #include "lib/jxl/modular/options.h" #include "lib/jxl/pack_signed.h" @@ -141,7 +143,7 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader, Channel &channel = image->channel[chan]; std::array<pixel_type, kNumStaticProperties> static_props = { - {chan, (int)group_id}}; + {chan, static_cast<int>(group_id)}}; // TODO(veluca): filter the tree according to static_props. // zero pixel channel? could happen @@ -376,7 +378,9 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader, MATreeLookup tree_lookup(tree); Properties properties = Properties(num_props); const intptr_t onerow = channel.plane.PixelsPerRow(); - Channel references(properties.size() - kNumNonrefProperties, channel.w); + JXL_ASSIGN_OR_RETURN( + Channel references, + Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); for (size_t y = 0; y < channel.h; y++) { pixel_type *JXL_RESTRICT p = channel.Row(y); PrecomputeReferences(channel, y, *image, chan, &references); @@ -422,7 +426,9 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader, MATreeLookup tree_lookup(tree); Properties properties = Properties(num_props); const intptr_t onerow = channel.plane.PixelsPerRow(); - Channel references(properties.size() - kNumNonrefProperties, channel.w); + JXL_ASSIGN_OR_RETURN( + Channel references, + Channel::Create(properties.size() - kNumNonrefProperties, channel.w)); weighted::State wp_state(wp_header, channel.w, channel.h); for (size_t y = 0; y < channel.h; y++) { pixel_type *JXL_RESTRICT p = channel.Row(y); diff --git a/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc b/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc index 746d7c87fd..32a5531080 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc @@ -8,6 +8,7 @@ #include <sstream> #include "lib/jxl/base/status.h" +#include "lib/jxl/image_ops.h" #include "lib/jxl/modular/transform/transform.h" namespace jxl { @@ -28,9 +29,18 @@ void Image::undo_transforms(const weighted::Header &wp_header, } } -Image::Image(size_t iw, size_t ih, int bitdepth, int nb_chans) - : w(iw), h(ih), bitdepth(bitdepth), nb_meta_channels(0), error(false) { - for (int i = 0; i < nb_chans; i++) channel.emplace_back(Channel(iw, ih)); +Image::Image(size_t iw, size_t ih, int bitdepth) + : w(iw), h(ih), bitdepth(bitdepth), nb_meta_channels(0), error(false) {} + +StatusOr<Image> Image::Create(size_t iw, size_t ih, int bitdepth, + int nb_chans) { + Image result(iw, ih, bitdepth); + for (int i = 0; i < nb_chans; i++) { + StatusOr<Channel> channel_or = Channel::Create(iw, ih); + JXL_RETURN_IF_ERROR(channel_or.status()); + result.channel.emplace_back(std::move(channel_or).value()); + } + return result; } Image::Image() : w(0), h(0), bitdepth(8), nb_meta_channels(0), error(true) {} @@ -46,17 +56,18 @@ Image &Image::operator=(Image &&other) noexcept { return *this; } -Image Image::clone() { - Image c(w, h, bitdepth, 0); - c.nb_meta_channels = nb_meta_channels; - c.error = error; - c.transform = transform; - for (Channel &ch : channel) { - Channel a(ch.w, ch.h, ch.hshift, ch.vshift); +StatusOr<Image> Image::Clone(const Image &that) { + Image clone(that.w, that.h, that.bitdepth); + clone.nb_meta_channels = that.nb_meta_channels; + clone.error = that.error; + clone.transform = that.transform; + for (const Channel &ch : that.channel) { + JXL_ASSIGN_OR_RETURN(Channel a, + Channel::Create(ch.w, ch.h, ch.hshift, ch.vshift)); CopyImageTo(ch.plane, &a.plane); - c.channel.push_back(std::move(a)); + clone.channel.push_back(std::move(a)); } - return c; + return clone; } #if JXL_DEBUG_V_LEVEL >= 1 diff --git a/third_party/jpeg-xl/lib/jxl/modular/modular_image.h b/third_party/jpeg-xl/lib/jxl/modular/modular_image.h index 56e80d823a..eb95b1cb6c 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/modular_image.h +++ b/third_party/jpeg-xl/lib/jxl/modular/modular_image.h @@ -18,7 +18,6 @@ #include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/status.h" #include "lib/jxl/image.h" -#include "lib/jxl/image_ops.h" namespace jxl { @@ -36,12 +35,16 @@ class Channel { jxl::Plane<pixel_type> plane; size_t w, h; int hshift, vshift; // w ~= image.w >> hshift; h ~= image.h >> vshift - Channel(size_t iw, size_t ih, int hsh = 0, int vsh = 0) - : plane(iw, ih), w(iw), h(ih), hshift(hsh), vshift(vsh) {} - Channel(const Channel& other) = delete; Channel& operator=(const Channel& other) = delete; + static StatusOr<Channel> Create(size_t iw, size_t ih, int hsh = 0, + int vsh = 0) { + JXL_ASSIGN_OR_RETURN(Plane<pixel_type> plane, + Plane<pixel_type>::Create(iw, ih)); + return Channel(std::move(plane), iw, ih, hsh, vsh); + } + // Move assignment Channel& operator=(Channel&& other) noexcept { w = other.w; @@ -55,21 +58,25 @@ class Channel { // Move constructor Channel(Channel&& other) noexcept = default; - void shrink() { - if (plane.xsize() == w && plane.ysize() == h) return; - jxl::Plane<pixel_type> resizedplane(w, h); - plane = std::move(resizedplane); + Status shrink() { + if (plane.xsize() == w && plane.ysize() == h) return true; + JXL_ASSIGN_OR_RETURN(plane, Plane<pixel_type>::Create(w, h)); + return true; } - void shrink(int nw, int nh) { + Status shrink(int nw, int nh) { w = nw; h = nh; - shrink(); + return shrink(); } JXL_INLINE pixel_type* Row(const size_t y) { return plane.Row(y); } JXL_INLINE const pixel_type* Row(const size_t y) const { return plane.Row(y); } + + private: + Channel(jxl::Plane<pixel_type>&& p, size_t iw, size_t ih, int hsh, int vsh) + : plane(std::move(p)), w(iw), h(ih), hshift(hsh), vshift(vsh) {} }; class Transform; @@ -88,7 +95,6 @@ class Image { size_t nb_meta_channels; // first few channels might contain palette(s) bool error; // true if a fatal error occurred, false otherwise - Image(size_t iw, size_t ih, int bitdepth, int nb_chans); Image(); Image(const Image& other) = delete; @@ -97,6 +103,9 @@ class Image { Image& operator=(Image&& other) noexcept; Image(Image&& other) noexcept = default; + static StatusOr<Image> Create(size_t iw, size_t ih, int bitdepth, + int nb_chans); + bool empty() const { for (const auto& ch : channel) { if (ch.w && ch.h) return false; @@ -104,12 +113,15 @@ class Image { return true; } - Image clone(); + static StatusOr<Image> Clone(const Image& that); void undo_transforms(const weighted::Header& wp_header, jxl::ThreadPool* pool = nullptr); std::string DebugString() const; + + private: + Image(size_t iw, size_t ih, int bitdepth); }; } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/modular/options.h b/third_party/jpeg-xl/lib/jxl/modular/options.h index ce6596b912..6613b513de 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/options.h +++ b/third_party/jpeg-xl/lib/jxl/modular/options.h @@ -11,6 +11,8 @@ #include <array> #include <vector> +#include "lib/jxl/enc_ans_params.h" + namespace jxl { using PropertyVal = int32_t; @@ -37,6 +39,8 @@ enum class Predictor : uint32_t { 15, // Find the best decision tree for predictors/predictor per row }; +constexpr Predictor kUndefinedPredictor = static_cast<Predictor>(~0u); + constexpr size_t kNumModularPredictors = static_cast<size_t>(Predictor::Average4) + 1; constexpr size_t kNumModularEncoderPredictors = @@ -80,7 +84,7 @@ struct ModularOptions { size_t max_property_values = 32; // Predictor to use for each channel. - Predictor predictor = static_cast<Predictor>(-1); + Predictor predictor = kUndefinedPredictor; int wp_mode = 0; @@ -108,6 +112,8 @@ struct ModularOptions { }; TreeKind tree_kind = TreeKind::kLearn; + HistogramParams histogram_params; + // Ignore the image and just pretend all tokens are zeroes bool zero_tokens = false; }; diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc index f5172aa126..24c64f5aad 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_palette.cc @@ -10,8 +10,8 @@ #include <set> #include "lib/jxl/base/common.h" -#include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/status.h" +#include "lib/jxl/image_ops.h" #include "lib/jxl/modular/encoding/context_predict.h" #include "lib/jxl/modular/modular_image.h" #include "lib/jxl/modular/transform/enc_transform.h" @@ -34,7 +34,8 @@ float ColorDistance(const std::vector<float> &JXL_RESTRICT a, if (a.size() >= 3) { ave3 = (a[0] + b[0] + a[1] + b[1] + a[2] + b[2]) * (1.21f / 3.0f); } - float sum_a = 0, sum_b = 0; + float sum_a = 0; + float sum_b = 0; for (size_t c = 0; c < a.size(); ++c) { const float difference = static_cast<float>(a[c]) - static_cast<float>(b[c]); @@ -132,7 +133,8 @@ struct PaletteIterationData { delta_frequency.first[1], delta_frequency.first[2]}; float delta_distance = - sqrt(palette_internal::ColorDistance({0, 0, 0}, current_delta)) + 1; + std::sqrt(palette_internal::ColorDistance({0, 0, 0}, current_delta)) + + 1; delta_frequency.second *= delta_distance * delta_distance_multiplier; } @@ -174,7 +176,8 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, // Channel palette special case if (nb_colors == 0) return false; std::vector<pixel_type> lookup; - pixel_type minval, maxval; + pixel_type minval; + pixel_type maxval; compute_minmax(input.channel[begin_c], &minval, &maxval); size_t lookup_table_size = static_cast<int64_t>(maxval) - static_cast<int64_t>(minval) + 1; @@ -189,12 +192,12 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, const bool new_color = chpalette.insert(p[x]).second; if (new_color) { idx++; - if (idx > (int)nb_colors) return false; + if (idx > static_cast<int>(nb_colors)) return false; } } } JXL_DEBUG_V(6, "Channel %i uses only %i colors.", begin_c, idx); - Channel pch(idx, 1); + JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(idx, 1)); pch.hshift = -1; pch.vshift = -1; nb_colors = idx; @@ -206,9 +209,12 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, for (size_t y = 0; y < h; y++) { pixel_type *p = input.channel[begin_c].Row(y); for (size_t x = 0; x < w; x++) { - for (idx = 0; p[x] != p_palette[idx] && idx < (int)nb_colors; idx++) { + for (idx = 0; + p[x] != p_palette[idx] && idx < static_cast<int>(nb_colors); + idx++) { + // no-op } - JXL_DASSERT(idx < (int)nb_colors); + JXL_DASSERT(idx < static_cast<int>(nb_colors)); p[x] = idx; } } @@ -226,12 +232,12 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, if (lookup[p[x] - minval] == 0) { lookup[p[x] - minval] = 1; idx++; - if (idx > (int)nb_colors) return false; + if (idx > static_cast<int>(nb_colors)) return false; } } } JXL_DEBUG_V(6, "Channel %i uses only %i colors.", begin_c, idx); - Channel pch(idx, 1); + JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(idx, 1)); pch.hshift = -1; pch.vshift = -1; nb_colors = idx; @@ -256,7 +262,8 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, Image quantized_input; if (lossy) { - quantized_input = Image(w, h, input.bitdepth, nb); + JXL_ASSIGN_OR_RETURN(quantized_input, + Image::Create(w, h, input.bitdepth, nb)); for (size_t c = 0; c < nb; c++) { CopyImageTo(input.channel[begin_c + c].plane, &quantized_input.channel[c].plane); @@ -337,7 +344,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, JXL_DEBUG_V(6, "Channels %i-%i can be represented using a %i-color palette.", begin_c, end_c, nb_colors); - Channel pch(nb_colors, nb); + JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(nb_colors, nb)); pch.hshift = -1; pch.vshift = -1; pixel_type *JXL_RESTRICT p_palette = pch.Row(0); @@ -361,7 +368,8 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, std::sort(candidate_palette_imageorder.begin(), candidate_palette_imageorder.end(), [](std::vector<pixel_type> ap, std::vector<pixel_type> bp) { - float ay, by; + float ay; + float by; ay = (0.299f * ap[0] + 0.587f * ap[1] + 0.114f * ap[2] + 0.1f); if (ap.size() > 3) ay *= 1.f + ap[3]; by = (0.299f * bp[0] + 0.587f * bp[1] + 0.114f * bp[2] + 0.1f); @@ -420,7 +428,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, for (int diffusion_index = 0; diffusion_index < 2; ++diffusion_index) { for (size_t c = 0; c < nb; c++) { color_with_error[c] = - p_in[c][x] + palette_iteration_data.final_run * + p_in[c][x] + (palette_iteration_data.final_run ? 1 : 0) * kDiffusionMultiplier[diffusion_index] * error_row[0][c][x + 2]; color[c] = Clamp1(lroundf(color_with_error[c]), 0l, @@ -503,7 +511,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, float local_error = color_with_error[c] - best_val[c]; len_error += local_error * local_error; } - len_error = sqrt(len_error); + len_error = std::sqrt(len_error); float modulate = 1.0; int len_limit = 38 << std::max(0, bit_depth - 8); if (len_error > len_limit) { diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc index 489f72a90d..0d924c0ace 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/enc_squeeze.cc @@ -14,15 +14,20 @@ namespace jxl { -void FwdHSqueeze(Image &input, int c, int rc) { +#define AVERAGE(X, Y) (((X) + (Y) + (((X) > (Y)) ? 1 : 0)) >> 1) + +Status FwdHSqueeze(Image &input, int c, int rc) { const Channel &chin = input.channel[c]; JXL_DEBUG_V(4, "Doing horizontal squeeze of channel %i to new channel %i", c, rc); - Channel chout((chin.w + 1) / 2, chin.h, chin.hshift + 1, chin.vshift); - Channel chout_residual(chin.w - chout.w, chout.h, chin.hshift + 1, - chin.vshift); + JXL_ASSIGN_OR_RETURN( + Channel chout, + Channel::Create((chin.w + 1) / 2, chin.h, chin.hshift + 1, chin.vshift)); + JXL_ASSIGN_OR_RETURN( + Channel chout_residual, + Channel::Create(chin.w - chout.w, chout.h, chin.hshift + 1, chin.vshift)); for (size_t y = 0; y < chout.h; y++) { const pixel_type *JXL_RESTRICT p_in = chin.Row(y); @@ -31,18 +36,19 @@ void FwdHSqueeze(Image &input, int c, int rc) { for (size_t x = 0; x < chout_residual.w; x++) { pixel_type A = p_in[x * 2]; pixel_type B = p_in[x * 2 + 1]; - pixel_type avg = (A + B + (A > B)) >> 1; + pixel_type avg = AVERAGE(A, B); p_out[x] = avg; pixel_type diff = A - B; pixel_type next_avg = avg; if (x + 1 < chout_residual.w) { - next_avg = (p_in[x * 2 + 2] + p_in[x * 2 + 3] + - (p_in[x * 2 + 2] > p_in[x * 2 + 3])) >> - 1; // which will be chout.value(y,x+1) - } else if (chin.w & 1) + pixel_type C = p_in[x * 2 + 2]; + pixel_type D = p_in[x * 2 + 3]; + next_avg = AVERAGE(C, D); // which will be chout.value(y,x+1) + } else if (chin.w & 1) { next_avg = p_in[x * 2 + 2]; + } pixel_type left = (x > 0 ? p_in[x * 2 - 1] : avg); pixel_type tendency = SmoothTendency(left, avg, next_avg); @@ -55,17 +61,21 @@ void FwdHSqueeze(Image &input, int c, int rc) { } input.channel[c] = std::move(chout); input.channel.insert(input.channel.begin() + rc, std::move(chout_residual)); + return true; } -void FwdVSqueeze(Image &input, int c, int rc) { +Status FwdVSqueeze(Image &input, int c, int rc) { const Channel &chin = input.channel[c]; JXL_DEBUG_V(4, "Doing vertical squeeze of channel %i to new channel %i", c, rc); - Channel chout(chin.w, (chin.h + 1) / 2, chin.hshift, chin.vshift + 1); - Channel chout_residual(chin.w, chin.h - chout.h, chin.hshift, - chin.vshift + 1); + JXL_ASSIGN_OR_RETURN( + Channel chout, + Channel::Create(chin.w, (chin.h + 1) / 2, chin.hshift, chin.vshift + 1)); + JXL_ASSIGN_OR_RETURN( + Channel chout_residual, + Channel::Create(chin.w, chin.h - chout.h, chin.hshift, chin.vshift + 1)); intptr_t onerow_in = chin.plane.PixelsPerRow(); for (size_t y = 0; y < chout_residual.h; y++) { const pixel_type *JXL_RESTRICT p_in = chin.Row(y * 2); @@ -74,16 +84,16 @@ void FwdVSqueeze(Image &input, int c, int rc) { for (size_t x = 0; x < chout.w; x++) { pixel_type A = p_in[x]; pixel_type B = p_in[x + onerow_in]; - pixel_type avg = (A + B + (A > B)) >> 1; + pixel_type avg = AVERAGE(A, B); p_out[x] = avg; pixel_type diff = A - B; pixel_type next_avg = avg; if (y + 1 < chout_residual.h) { - next_avg = (p_in[x + 2 * onerow_in] + p_in[x + 3 * onerow_in] + - (p_in[x + 2 * onerow_in] > p_in[x + 3 * onerow_in])) >> - 1; // which will be chout.value(y+1,x) + pixel_type C = p_in[x + 2 * onerow_in]; + pixel_type D = p_in[x + 3 * onerow_in]; + next_avg = AVERAGE(C, D); // which will be chout.value(y+1,x) } else if (chin.h & 1) { next_avg = p_in[x + 2 * onerow_in]; } @@ -104,6 +114,7 @@ void FwdVSqueeze(Image &input, int c, int rc) { } input.channel[c] = std::move(chout); input.channel.insert(input.channel.begin() + rc, std::move(chout_residual)); + return true; } Status FwdSqueeze(Image &input, std::vector<SqueezeParams> parameters, @@ -128,9 +139,9 @@ Status FwdSqueeze(Image &input, std::vector<SqueezeParams> parameters, } for (uint32_t c = beginc; c <= endc; c++) { if (horizontal) { - FwdHSqueeze(input, c, offset + c - beginc); + JXL_RETURN_IF_ERROR(FwdHSqueeze(input, c, offset + c - beginc)); } else { - FwdVSqueeze(input, c, offset + c - beginc); + JXL_RETURN_IF_ERROR(FwdVSqueeze(input, c, offset + c - beginc)); } } } diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc index bffbacf160..1ab499ccf6 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.cc @@ -23,9 +23,11 @@ Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors, size_t h = input.channel[c0].h; if (nb < 1) return JXL_FAILURE("Corrupted transforms"); for (int i = 1; i < nb; i++) { - input.channel.insert( - input.channel.begin() + c0 + 1, - Channel(w, h, input.channel[c0].hshift, input.channel[c0].vshift)); + StatusOr<Channel> channel_or = Channel::Create( + w, h, input.channel[c0].hshift, input.channel[c0].vshift); + JXL_RETURN_IF_ERROR(channel_or.status()); + input.channel.insert(input.channel.begin() + c0 + 1, + std::move(channel_or).value()); } const Channel &palette = input.channel[0]; const pixel_type *JXL_RESTRICT p_palette = input.channel[0].Row(0); @@ -44,7 +46,8 @@ Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors, const size_t y = task; pixel_type *p = input.channel[c0].Row(y); for (size_t x = 0; x < w; x++) { - const int index = Clamp1<int>(p[x], 0, (pixel_type)palette.w - 1); + const int index = + Clamp1<int>(p[x], 0, static_cast<pixel_type>(palette.w) - 1); p[x] = palette_internal::GetPaletteValue( p_palette, index, /*c=*/0, /*palette_size=*/palette.w, @@ -75,8 +78,10 @@ Status InvPalette(Image &input, uint32_t begin_c, uint32_t nb_colors, } } else { // Parallelized per channel. - ImageI indices = std::move(input.channel[c0].plane); - input.channel[c0].plane = ImageI(indices.xsize(), indices.ysize()); + ImageI indices; + ImageI &plane = input.channel[c0].plane; + JXL_ASSIGN_OR_RETURN(indices, ImageI::Create(plane.xsize(), plane.ysize())); + plane.Swap(indices); if (predictor == Predictor::Weighted) { JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, nb, ThreadPool::NoInit, @@ -167,7 +172,7 @@ Status MetaPalette(Image &input, uint32_t begin_c, uint32_t end_c, } input.channel.erase(input.channel.begin() + begin_c + 1, input.channel.begin() + end_c + 1); - Channel pch(nb_colors + nb_deltas, nb); + JXL_ASSIGN_OR_RETURN(Channel pch, Channel::Create(nb_colors + nb_deltas, nb)); pch.hshift = -1; pch.vshift = -1; input.channel.insert(input.channel.begin(), std::move(pch)); diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h index 279ef04568..e0405a2162 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/palette.h @@ -101,6 +101,7 @@ GetPaletteValue(const pixel_type *const palette, int index, const size_t c, // index >= kLargeCube ** 3 ? switch (c) { case 0: + default: break; case 1: index /= kLargeCube; diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc index e9892ea48f..580829741a 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc @@ -113,7 +113,9 @@ Status InvHSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) { } // Note: chin.w >= chin_residual.w and at most 1 different. - Channel chout(chin.w + chin_residual.w, chin.h, chin.hshift - 1, chin.vshift); + JXL_ASSIGN_OR_RETURN(Channel chout, + Channel::Create(chin.w + chin_residual.w, chin.h, + chin.hshift - 1, chin.vshift)); JXL_DEBUG_V(4, "Undoing horizontal squeeze of channel %i using residuals in " "channel %i (going from width %" PRIuS " to %" PRIuS ")", @@ -222,7 +224,9 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) { } // Note: chin.h >= chin_residual.h and at most 1 different. - Channel chout(chin.w, chin.h + chin_residual.h, chin.hshift, chin.vshift - 1); + JXL_ASSIGN_OR_RETURN(Channel chout, + Channel::Create(chin.w, chin.h + chin_residual.h, + chin.hshift, chin.vshift - 1)); JXL_DEBUG_V( 4, "Undoing vertical squeeze of channel %i using residuals in channel " @@ -238,7 +242,8 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) { static constexpr const int kColsPerThread = 64; const auto unsqueeze_slice = [&](const uint32_t task, size_t /* thread */) { const size_t x0 = task * kColsPerThread; - const size_t x1 = std::min((size_t)(task + 1) * kColsPerThread, chin.w); + const size_t x1 = + std::min(static_cast<size_t>(task + 1) * kColsPerThread, chin.w); const size_t w = x1 - x0; // We only iterate up to std::min(chin_residual.h, chin.h) which is // always chin_residual.h. @@ -289,7 +294,7 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) { return true; } -Status InvSqueeze(Image &input, std::vector<SqueezeParams> parameters, +Status InvSqueeze(Image &input, const std::vector<SqueezeParams> ¶meters, ThreadPool *pool) { for (int i = parameters.size() - 1; i >= 0; i--) { JXL_RETURN_IF_ERROR( @@ -340,7 +345,7 @@ HWY_AFTER_NAMESPACE(); namespace jxl { HWY_EXPORT(InvSqueeze); -Status InvSqueeze(Image &input, std::vector<SqueezeParams> parameters, +Status InvSqueeze(Image &input, const std::vector<SqueezeParams> ¶meters, ThreadPool *pool) { return HWY_DYNAMIC_DISPATCH(InvSqueeze)(input, parameters, pool); } @@ -459,8 +464,8 @@ Status MetaSqueeze(Image &image, std::vector<SqueezeParams> *parameters) { if (image.channel[c].vshift >= 0) image.channel[c].vshift++; h = h - (h + 1) / 2; } - image.channel[c].shrink(); - Channel placeholder(w, h); + JXL_RETURN_IF_ERROR(image.channel[c].shrink()); + JXL_ASSIGN_OR_RETURN(Channel placeholder, Channel::Create(w, h)); placeholder.hshift = image.channel[c].hshift; placeholder.vshift = image.channel[c].vshift; diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h index 305a0ca3ec..bbd16c59c0 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.h @@ -81,7 +81,7 @@ Status CheckMetaSqueezeParams(const SqueezeParams ¶meter, int num_channels); Status MetaSqueeze(Image &image, std::vector<SqueezeParams> *parameters); -Status InvSqueeze(Image &input, std::vector<SqueezeParams> parameters, +Status InvSqueeze(Image &input, const std::vector<SqueezeParams> ¶meters, ThreadPool *pool); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h b/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h index d5d3259f7a..b68861706f 100644 --- a/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h +++ b/third_party/jpeg-xl/lib/jxl/modular/transform/transform.h @@ -77,11 +77,13 @@ class Transform : public Fields { Transform() : Transform(TransformId::kInvalid) {} Status VisitFields(Visitor *JXL_RESTRICT visitor) override { - JXL_QUIET_RETURN_IF_ERROR(visitor->U32( - Val((uint32_t)TransformId::kRCT), Val((uint32_t)TransformId::kPalette), - Val((uint32_t)TransformId::kSqueeze), - Val((uint32_t)TransformId::kInvalid), (uint32_t)TransformId::kRCT, - reinterpret_cast<uint32_t *>(&id))); + JXL_QUIET_RETURN_IF_ERROR( + visitor->U32(Val(static_cast<uint32_t>(TransformId::kRCT)), + Val(static_cast<uint32_t>(TransformId::kPalette)), + Val(static_cast<uint32_t>(TransformId::kSqueeze)), + Val(static_cast<uint32_t>(TransformId::kInvalid)), + static_cast<uint32_t>(TransformId::kRCT), + reinterpret_cast<uint32_t *>(&id))); if (id == TransformId::kInvalid) { return JXL_FAILURE("Invalid transform ID"); } @@ -109,7 +111,7 @@ class Transform : public Fields { visitor->U32(Val(0), BitsOffset(8, 1), BitsOffset(10, 257), BitsOffset(16, 1281), 0, &nb_deltas)); JXL_QUIET_RETURN_IF_ERROR( - visitor->Bits(4, (uint32_t)Predictor::Zero, + visitor->Bits(4, static_cast<uint32_t>(Predictor::Zero), reinterpret_cast<uint32_t *>(&predictor))); if (predictor >= Predictor::Best) { return JXL_FAILURE("Invalid predictor"); diff --git a/third_party/jpeg-xl/lib/jxl/modular_test.cc b/third_party/jpeg-xl/lib/jxl/modular_test.cc index 689063ce95..bd1a947493 100644 --- a/third_party/jpeg-xl/lib/jxl/modular_test.cc +++ b/third_party/jpeg-xl/lib/jxl/modular_test.cc @@ -7,6 +7,7 @@ #include <jxl/encode.h> #include <jxl/types.h> +#include <cmath> #include <cstddef> #include <cstdint> #include <sstream> @@ -29,7 +30,6 @@ #include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_bit_writer.h" -#include "lib/jxl/enc_butteraugli_comparator.h" #include "lib/jxl/enc_fields.h" #include "lib/jxl/enc_params.h" #include "lib/jxl/enc_toc.h" @@ -54,6 +54,7 @@ namespace jxl { namespace { +using test::ButteraugliDistance; using test::ReadTestData; using test::Roundtrip; using test::TestImage; @@ -130,10 +131,11 @@ TEST(ModularTest, RoundtripLossyDeltaPalette) { size_t compressed_size; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size)); EXPECT_LE(compressed_size, 6800u); - EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(1.5)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 1.5); } TEST(ModularTest, RoundtripLossyDeltaPaletteWP) { const std::vector<uint8_t> orig = @@ -153,10 +155,11 @@ TEST(ModularTest, RoundtripLossyDeltaPaletteWP) { size_t compressed_size; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size)); EXPECT_LE(compressed_size, 7000u); - EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(10.1)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 10.1); } TEST(ModularTest, RoundtripLossy) { @@ -175,10 +178,11 @@ TEST(ModularTest, RoundtripLossy) { size_t compressed_size; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size)); EXPECT_LE(compressed_size, 30000u); - EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(2.3)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 2.3); } TEST(ModularTest, RoundtripLossy16) { @@ -201,15 +205,17 @@ TEST(ModularTest, RoundtripLossy16) { size_t compressed_size; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io_out, _, &compressed_size)); EXPECT_LE(compressed_size, 300u); - EXPECT_THAT(ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(1.6)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io_out.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 1.6); } TEST(ModularTest, RoundtripExtraProperties) { constexpr size_t kSize = 250; - Image image(kSize, kSize, /*bitdepth=*/8, 3); + JXL_ASSIGN_OR_DIE(Image image, + Image::Create(kSize, kSize, /*bitdepth=*/8, 3)); ModularOptions options; options.max_properties = 4; options.predictor = Predictor::Zero; @@ -224,10 +230,12 @@ TEST(ModularTest, RoundtripExtraProperties) { BitWriter writer; ASSERT_TRUE(ModularGenericCompress(image, options, &writer)); writer.ZeroPadToByte(); - Image decoded(kSize, kSize, /*bitdepth=*/8, image.channel.size()); + JXL_ASSIGN_OR_DIE(Image decoded, Image::Create(kSize, kSize, /*bitdepth=*/8, + image.channel.size())); for (size_t i = 0; i < image.channel.size(); i++) { const Channel& ch = image.channel[i]; - decoded.channel[i] = Channel(ch.w, ch.h, ch.hshift, ch.vshift); + JXL_ASSIGN_OR_DIE(decoded.channel[i], + Channel::Create(ch.w, ch.h, ch.hshift, ch.vshift)); } Status status = true; { @@ -302,7 +310,7 @@ TEST_P(ModularTestParam, RoundtripLossless) { double factor = ((1lu << bitdepth) - 1lu); double ifactor = 1.0 / factor; - Image3F noise_added(xsize, ysize); + JXL_ASSIGN_OR_DIE(Image3F noise_added, Image3F::Create(xsize, ysize)); for (size_t c = 0; c < 3; c++) { for (size_t y = 0; y < ysize; y++) { @@ -313,7 +321,7 @@ TEST_P(ModularTestParam, RoundtripLossless) { float f = in[x] + generator.UniformF(0.0f, 1.f / 255.f); if (f > 1.f) f = 1.f; // quantize to the bitdepth we're testing - unsigned int u = f * factor + 0.5; + unsigned int u = static_cast<unsigned int>(std::lround(f * factor)); out[x] = u * ifactor; } } @@ -330,7 +338,7 @@ TEST_P(ModularTestParam, RoundtripLossless) { CodecInOut io2; size_t compressed_size; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, &compressed_size)); - EXPECT_LE(compressed_size, bitdepth * xsize * ysize / 3); + EXPECT_LE(compressed_size, bitdepth * xsize * ysize / 3.0 * 1.1); EXPECT_LE(0, ComputeDistance2(io.Main(), io2.Main(), *JxlGetDefaultCms())); size_t different = 0; for (size_t c = 0; c < 3; c++) { @@ -350,7 +358,8 @@ TEST_P(ModularTestParam, RoundtripLossless) { TEST(ModularTest, RoundtripLosslessCustomFloat) { CodecInOut io; - size_t xsize = 100, ysize = 300; + size_t xsize = 100; + size_t ysize = 300; io.SetSize(xsize, ysize); io.metadata.m.bit_depth.bits_per_sample = 18; io.metadata.m.bit_depth.exponent_bits_per_sample = 6; @@ -359,7 +368,7 @@ TEST(ModularTest, RoundtripLosslessCustomFloat) { ColorEncoding color_encoding; color_encoding.Tf().SetTransferFunction(TransferFunction::kLinear); color_encoding.SetColorSpace(ColorSpace::kRGB); - Image3F testimage(xsize, ysize); + JXL_ASSIGN_OR_DIE(Image3F testimage, Image3F::Create(xsize, ysize)); float factor = 1.f / (1 << 14); for (size_t c = 0; c < 3; c++) { for (size_t y = 0; y < ysize; y++) { @@ -442,7 +451,7 @@ TEST(ModularTest, PredictorIntegerOverflow) { WriteHeaders(&writer, xsize, ysize); std::vector<BitWriter> group_codes(1); { - BitWriter* bw = &group_codes[0]; + BitWriter* bw = group_codes.data(); BitWriter::Allotment allotment(bw, 1 << 20); WriteHistograms(bw); GroupHeader header; @@ -466,7 +475,7 @@ TEST(ModularTest, PredictorIntegerOverflow) { nullptr, &ppf)); ASSERT_EQ(1, ppf.frames.size()); const auto& img = ppf.frames[0].color; - const auto pixels = reinterpret_cast<const float*>(img.pixels()); + const auto* pixels = reinterpret_cast<const float*>(img.pixels()); EXPECT_EQ(-1.0f, pixels[0]); } @@ -478,7 +487,7 @@ TEST(ModularTest, UnsqueezeIntegerOverflow) { WriteHeaders(&writer, xsize, ysize); std::vector<BitWriter> group_codes(1); { - BitWriter* bw = &group_codes[0]; + BitWriter* bw = group_codes.data(); BitWriter::Allotment allotment(bw, 1 << 20); WriteHistograms(bw); GroupHeader header; @@ -514,7 +523,7 @@ TEST(ModularTest, UnsqueezeIntegerOverflow) { nullptr, &ppf)); ASSERT_EQ(1, ppf.frames.size()); const auto& img = ppf.frames[0].color; - const auto pixels = reinterpret_cast<const float*>(img.pixels()); + const float* pixels = reinterpret_cast<const float*>(img.pixels()); for (size_t x = 0; x < xsize; ++x) { EXPECT_NEAR(-0.5f, pixels[x], 1e-10); EXPECT_NEAR(0.5f, pixels[xsize + x], 1e-10); diff --git a/third_party/jpeg-xl/lib/jxl/noise.h b/third_party/jpeg-xl/lib/jxl/noise.h index 585fab0d42..c588c59d33 100644 --- a/third_party/jpeg-xl/lib/jxl/noise.h +++ b/third_party/jpeg-xl/lib/jxl/noise.h @@ -11,6 +11,7 @@ #include <stddef.h> #include <algorithm> +#include <array> #include <cmath> #include <utility> @@ -23,7 +24,9 @@ const float kNoisePrecision = 1 << 10; struct NoiseParams { // LUT index is an intensity of pixel / mean intensity of patch static constexpr size_t kNumNoisePoints = 8; - float lut[kNumNoisePoints]; + using Lut = std::array<float, kNumNoisePoints>; + + Lut lut; void Clear() { for (float& i : lut) i = 0.f; @@ -39,7 +42,7 @@ struct NoiseParams { static inline std::pair<int, float> IndexAndFrac(float x) { constexpr size_t kScaleNumerator = NoiseParams::kNumNoisePoints - 2; // TODO(user): instead of 1, this should be a proper Y range. - constexpr float kScale = kScaleNumerator / 1; + constexpr float kScale = kScaleNumerator / 1.0f; float scaled_x = std::max(0.f, x * kScale); float floor_x; float frac_x = std::modf(scaled_x, &floor_x); diff --git a/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc b/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc index f7842c32e4..b8ea839b9e 100644 --- a/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc +++ b/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc @@ -27,7 +27,7 @@ namespace { void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b, float* JXL_RESTRICT xyb_x, float* JXL_RESTRICT xyb_y, float* JXL_RESTRICT xyb_b) { - Image3F linear(1, 1); + JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(1, 1)); linear.PlaneRow(0, 0)[0] = rgb_r; linear.PlaneRow(1, 0)[0] = rgb_g; linear.PlaneRow(2, 0)[0] = rgb_b; @@ -37,7 +37,7 @@ void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b, metadata.color_encoding = ColorEncoding::LinearSRGB(); ImageBundle ib(&metadata); ib.SetFromImage(std::move(linear), metadata.color_encoding); - Image3F opsin(1, 1); + JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(1, 1)); (void)ToXYB(ib, /*pool=*/nullptr, &opsin, *JxlGetDefaultCms()); *xyb_x = opsin.PlaneRow(0, 0)[0]; @@ -50,11 +50,11 @@ void LinearSrgbToOpsin(float rgb_r, float rgb_g, float rgb_b, void OpsinToLinearSrgb(float xyb_x, float xyb_y, float xyb_b, float* JXL_RESTRICT rgb_r, float* JXL_RESTRICT rgb_g, float* JXL_RESTRICT rgb_b) { - Image3F opsin(1, 1); + JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(1, 1)); opsin.PlaneRow(0, 0)[0] = xyb_x; opsin.PlaneRow(1, 0)[0] = xyb_y; opsin.PlaneRow(2, 0)[0] = xyb_b; - Image3F linear(1, 1); + JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(1, 1)); OpsinParams opsin_params; opsin_params.Init(/*intensity_target=*/255.0f); OpsinToLinear(opsin, Rect(opsin), nullptr, &linear, opsin_params); @@ -64,9 +64,13 @@ void OpsinToLinearSrgb(float xyb_x, float xyb_y, float xyb_b, } void OpsinRoundtripTestRGB(float r, float g, float b) { - float xyb_x, xyb_y, xyb_b; + float xyb_x; + float xyb_y; + float xyb_b; LinearSrgbToOpsin(r, g, b, &xyb_x, &xyb_y, &xyb_b); - float r2, g2, b2; + float r2; + float g2; + float b2; OpsinToLinearSrgb(xyb_x, xyb_y, xyb_b, &r2, &g2, &b2); EXPECT_NEAR(r, r2, 1e-3); EXPECT_NEAR(g, g2, 1e-3); @@ -74,13 +78,13 @@ void OpsinRoundtripTestRGB(float r, float g, float b) { } TEST(OpsinImageTest, VerifyOpsinAbsorbanceInverseMatrix) { - float matrix[9]; // writable copy - for (int i = 0; i < 9; i++) { - matrix[i] = GetOpsinAbsorbanceInverseMatrix()[i]; - } + Matrix3x3 matrix; // writable copy + matrix = GetOpsinAbsorbanceInverseMatrix(); EXPECT_TRUE(Inv3x3Matrix(matrix)); - for (int i = 0; i < 9; i++) { - EXPECT_NEAR(matrix[i], jxl::cms::kOpsinAbsorbanceMatrix[i], 1e-6); + for (int j = 0; j < 3; j++) { + for (int i = 0; i < 3; i++) { + EXPECT_NEAR(matrix[j][i], jxl::cms::kOpsinAbsorbanceMatrix[j][i], 1e-6); + } } } @@ -105,7 +109,9 @@ TEST(OpsinImageTest, OpsinRoundtrip) { TEST(OpsinImageTest, VerifyZero) { // Test that black color (zero energy) is 0,0,0 in xyb. - float x, y, b; + float x; + float y; + float b; LinearSrgbToOpsin(0, 0, 0, &x, &y, &b); EXPECT_NEAR(0, x, 1e-9); EXPECT_NEAR(0, y, 1e-7); @@ -115,7 +121,9 @@ TEST(OpsinImageTest, VerifyZero) { TEST(OpsinImageTest, VerifyGray) { // Test that grayscale colors have a fixed y/b ratio and x==0. for (size_t i = 1; i < 255; i++) { - float x, y, b; + float x; + float y; + float b; LinearSrgbToOpsin(i / 255., i / 255., i / 255., &x, &y, &b); EXPECT_NEAR(0, x, 1e-6); EXPECT_NEAR(jxl::cms::kYToBRatio, b / y, 3e-5); diff --git a/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc b/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc index b8c151fbea..b8f9aa13df 100644 --- a/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc +++ b/third_party/jpeg-xl/lib/jxl/opsin_inverse_test.cc @@ -21,17 +21,17 @@ namespace jxl { namespace { TEST(OpsinInverseTest, LinearInverseInverts) { - Image3F linear(128, 128); + JXL_ASSIGN_OR_DIE(Image3F linear, Image3F::Create(128, 128)); RandomFillImage(&linear, 0.0f, 1.0f); CodecInOut io; io.metadata.m.SetFloat32Samples(); io.metadata.m.color_encoding = ColorEncoding::LinearSRGB(); - Image3F linear2(128, 128); + JXL_ASSIGN_OR_DIE(Image3F linear2, Image3F::Create(128, 128)); CopyImageTo(linear, &linear2); io.SetFromImage(std::move(linear2), io.metadata.m.color_encoding); ThreadPool* null_pool = nullptr; - Image3F opsin(io.xsize(), io.ysize()); + JXL_ASSIGN_OR_DIE(Image3F opsin, Image3F::Create(io.xsize(), io.ysize())); (void)ToXYB(io.Main(), null_pool, &opsin, *JxlGetDefaultCms()); OpsinParams opsin_params; @@ -42,16 +42,16 @@ TEST(OpsinInverseTest, LinearInverseInverts) { } TEST(OpsinInverseTest, YcbCrInverts) { - Image3F rgb(128, 128); + JXL_ASSIGN_OR_DIE(Image3F rgb, Image3F::Create(128, 128)); RandomFillImage(&rgb, 0.0f, 1.0f); ThreadPool* null_pool = nullptr; - Image3F ycbcr(rgb.xsize(), rgb.ysize()); + JXL_ASSIGN_OR_DIE(Image3F ycbcr, Image3F::Create(rgb.xsize(), rgb.ysize())); EXPECT_TRUE(RgbToYcbcr(rgb.Plane(0), rgb.Plane(1), rgb.Plane(2), &ycbcr.Plane(1), &ycbcr.Plane(0), &ycbcr.Plane(2), null_pool)); - Image3F rgb2(rgb.xsize(), rgb.ysize()); + JXL_ASSIGN_OR_DIE(Image3F rgb2, Image3F::Create(rgb.xsize(), rgb.ysize())); YcbcrToRgb(ycbcr, &rgb2, Rect(rgb)); JXL_ASSERT_OK(VerifyRelativeError(rgb, rgb2, 4E-5, 4E-7, _)); diff --git a/third_party/jpeg-xl/lib/jxl/opsin_params.cc b/third_party/jpeg-xl/lib/jxl/opsin_params.cc index e1fdda5322..8aae4e3597 100644 --- a/third_party/jpeg-xl/lib/jxl/opsin_params.cc +++ b/third_party/jpeg-xl/lib/jxl/opsin_params.cc @@ -9,24 +9,19 @@ #define INVERSE_OPSIN_FROM_SPEC 1 -#if not(INVERSE_OPSIN_FROM_SPEC) #include "lib/jxl/base/matrix_ops.h" -#endif namespace jxl { -const float* GetOpsinAbsorbanceInverseMatrix() { +const Matrix3x3& GetOpsinAbsorbanceInverseMatrix() { #if INVERSE_OPSIN_FROM_SPEC return jxl::cms::DefaultInverseOpsinAbsorbanceMatrix(); #else // INVERSE_OPSIN_FROM_SPEC // Compute the inverse opsin matrix from the forward matrix. Less precise // than taking the values from the specification, but must be used if the // forward transform is changed and the spec will require updating. - static const float* const kInverse = [] { - static float inverse[9]; - for (int i = 0; i < 9; i++) { - inverse[i] = kOpsinAbsorbanceMatrix[i]; - } + static const Matrix3x3 const kInverse = [] { + static Matrix3x3 inverse = kOpsinAbsorbanceMatrix; Inv3x3Matrix(inverse); return inverse; }(); @@ -34,12 +29,15 @@ const float* GetOpsinAbsorbanceInverseMatrix() { #endif // INVERSE_OPSIN_FROM_SPEC } -void InitSIMDInverseMatrix(const float* JXL_RESTRICT inverse, +void InitSIMDInverseMatrix(const Matrix3x3& inverse, float* JXL_RESTRICT simd_inverse, float intensity_target) { - for (size_t i = 0; i < 9; ++i) { - simd_inverse[4 * i] = simd_inverse[4 * i + 1] = simd_inverse[4 * i + 2] = - simd_inverse[4 * i + 3] = inverse[i] * (255.0f / intensity_target); + for (size_t j = 0; j < 3; ++j) { + for (size_t i = 0; i < 3; ++i) { + size_t idx = (j * 3 + i) * 4; + simd_inverse[idx] = simd_inverse[idx + 1] = simd_inverse[idx + 2] = + simd_inverse[idx + 3] = inverse[j][i] * (255.0f / intensity_target); + } } } diff --git a/third_party/jpeg-xl/lib/jxl/opsin_params.h b/third_party/jpeg-xl/lib/jxl/opsin_params.h index fc285ac208..4ed2124fd7 100644 --- a/third_party/jpeg-xl/lib/jxl/opsin_params.h +++ b/third_party/jpeg-xl/lib/jxl/opsin_params.h @@ -9,14 +9,15 @@ // Constants that define the XYB color space. #include "lib/jxl/base/compiler_specific.h" +#include "lib/jxl/base/matrix_ops.h" namespace jxl { // Returns 3x3 row-major matrix inverse of kOpsinAbsorbanceMatrix. // opsin_image_test verifies this is actually the inverse. -const float* GetOpsinAbsorbanceInverseMatrix(); +const Matrix3x3& GetOpsinAbsorbanceInverseMatrix(); -void InitSIMDInverseMatrix(const float* JXL_RESTRICT inverse, +void InitSIMDInverseMatrix(const Matrix3x3& inverse, float* JXL_RESTRICT simd_inverse, float intensity_target); diff --git a/third_party/jpeg-xl/lib/jxl/padded_bytes.h b/third_party/jpeg-xl/lib/jxl/padded_bytes.h index 0d696475fa..38167ed408 100644 --- a/third_party/jpeg-xl/lib/jxl/padded_bytes.h +++ b/third_party/jpeg-xl/lib/jxl/padded_bytes.h @@ -71,7 +71,7 @@ class PaddedBytes { return *this; } - void swap(PaddedBytes& other) { + void swap(PaddedBytes& other) noexcept { std::swap(size_, other.size_); std::swap(capacity_, other.capacity_); std::swap(data_, other.data_); diff --git a/third_party/jpeg-xl/lib/jxl/passes_state.cc b/third_party/jpeg-xl/lib/jxl/passes_state.cc index 12cc6a0c93..5da731b48e 100644 --- a/third_party/jpeg-xl/lib/jxl/passes_state.cc +++ b/third_party/jpeg-xl/lib/jxl/passes_state.cc @@ -5,6 +5,7 @@ #include "lib/jxl/passes_state.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/chroma_from_luma.h" #include "lib/jxl/coeff_order.h" #include "lib/jxl/frame_dimensions.h" @@ -21,13 +22,17 @@ Status InitializePassesSharedState(const FrameHeader& frame_header, const FrameDimensions& frame_dim = shared->frame_dim; - shared->ac_strategy = - AcStrategyImage(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared->raw_quant_field = - ImageI(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared->epf_sharpness = - ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks); - shared->cmap = ColorCorrelationMap(frame_dim.xsize, frame_dim.ysize); + JXL_ASSIGN_OR_RETURN( + shared->ac_strategy, + AcStrategyImage::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + shared->raw_quant_field, + ImageI::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN( + shared->epf_sharpness, + ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); + JXL_ASSIGN_OR_RETURN(shared->cmap, ColorCorrelationMap::Create( + frame_dim.xsize, frame_dim.ysize)); // In the decoder, we allocate coeff orders afterwards, when we know how many // we will actually need. @@ -40,9 +45,11 @@ Status InitializePassesSharedState(const FrameHeader& frame_header, kCoeffOrderMaxSize); } - shared->quant_dc = ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks); + JXL_ASSIGN_OR_RETURN( + shared->quant_dc, + ImageB::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); - bool use_dc_frame = !!(frame_header.flags & FrameHeader::kUseDcFrame); + bool use_dc_frame = ((frame_header.flags & FrameHeader::kUseDcFrame) != 0u); if (!encoder && use_dc_frame) { if (frame_header.dc_level == 4) { return JXL_FAILURE("Invalid DC level for kUseDcFrame: %u", @@ -58,8 +65,9 @@ Status InitializePassesSharedState(const FrameHeader& frame_header, } ZeroFillImage(&shared->quant_dc); } else { - shared->dc_storage = - Image3F(frame_dim.xsize_blocks, frame_dim.ysize_blocks); + JXL_ASSIGN_OR_RETURN( + shared->dc_storage, + Image3F::Create(frame_dim.xsize_blocks, frame_dim.ysize_blocks)); shared->dc = &shared->dc_storage; } diff --git a/third_party/jpeg-xl/lib/jxl/passes_test.cc b/third_party/jpeg-xl/lib/jxl/passes_test.cc index a47134cd00..cb9164706f 100644 --- a/third_party/jpeg-xl/lib/jxl/passes_test.cc +++ b/third_party/jpeg-xl/lib/jxl/passes_test.cc @@ -17,7 +17,6 @@ #include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/override.h" #include "lib/jxl/base/span.h" -#include "lib/jxl/enc_butteraugli_comparator.h" #include "lib/jxl/enc_params.h" #include "lib/jxl/image.h" #include "lib/jxl/image_bundle.h" @@ -27,6 +26,7 @@ namespace jxl { +using test::ButteraugliDistance; using test::ReadTestData; using test::Roundtrip; using test::ThreadPoolForTests; @@ -47,10 +47,11 @@ TEST(PassesTest, RoundtripSmallPasses) { CodecInOut io2; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _)); - EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(0.8222)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 0.8222); } TEST(PassesTest, RoundtripUnalignedPasses) { @@ -67,10 +68,11 @@ TEST(PassesTest, RoundtripUnalignedPasses) { CodecInOut io2; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _)); - EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(1.72)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 1.72); } TEST(PassesTest, RoundtripMultiGroupPasses) { @@ -91,10 +93,11 @@ TEST(PassesTest, RoundtripMultiGroupPasses) { CodecInOut io2; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _, /* compressed_size */ nullptr, &pool)); - EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr, &pool), - IsSlightlyBelow(target_distance + threshold)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr, &pool), + target_distance + threshold); }; auto run1 = std::async(std::launch::async, test, 1.0f, 0.15f); @@ -256,7 +259,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) { ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_orig, &pool)); Rect rect(0, 0, io_orig.xsize(), 128); // need 2 DC groups for the DC frame to actually be progressive. - Image3F large(4242, rect.ysize()); + JXL_ASSIGN_OR_DIE(Image3F large, Image3F::Create(4242, rect.ysize())); ZeroFillImage(&large); CopyImageTo(rect, *io_orig.Main().color(), rect, &large); CodecInOut io; @@ -268,7 +271,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) { CompressParams cparams; cparams.speed_tier = SpeedTier::kSquirrel; cparams.progressive_dc = 1; - cparams.responsive = true; + cparams.responsive = JXL_TRUE; cparams.qprogressive_mode = Override::kOn; cparams.butteraugli_distance = 1.0; ASSERT_TRUE(test::EncodeFile(cparams, &io, &compressed, &pool)); @@ -300,7 +303,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) { ASSERT_TRUE(SetFromBytes(Bytes(orig), &io_orig, &pool)); Rect rect(0, 0, io_orig.xsize(), 128); // need 2 DC groups for the DC frame to actually be progressive. - Image3F large(4242, rect.ysize()); + JXL_ASSIGN_OR_DIE(Image3F large, Image3F::Create(4242, rect.ysize())); ZeroFillImage(&large); CopyImageTo(rect, *io_orig.Main().color(), rect, &large); CodecInOut io; @@ -311,7 +314,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) { CompressParams cparams; cparams.speed_tier = SpeedTier::kSquirrel; cparams.progressive_dc = 1; - cparams.responsive = true; + cparams.responsive = JXL_TRUE; cparams.qprogressive_mode = Override::kOn; cparams.butteraugli_distance = 1.0; ASSERT_TRUE(test::EncodeFile(cparams, &io, &compressed, &pool)); @@ -375,10 +378,11 @@ TEST(PassesTest, RoundtripSmallNoGaborishPasses) { CodecInOut io2; JXL_EXPECT_OK(Roundtrip(&io, cparams, {}, &io2, _)); - EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), - *JxlGetDefaultCms(), - /*distmap=*/nullptr), - IsSlightlyBelow(1.2)); + EXPECT_SLIGHTLY_BELOW( + ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), + *JxlGetDefaultCms(), + /*distmap=*/nullptr), + 1.2); } } // namespace diff --git a/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc b/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc index 60f7c32229..fd04b7fc2e 100644 --- a/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc +++ b/third_party/jpeg-xl/lib/jxl/patch_dictionary_test.cc @@ -12,7 +12,6 @@ #include "lib/extras/codec.h" #include "lib/jxl/base/override.h" #include "lib/jxl/base/span.h" -#include "lib/jxl/enc_butteraugli_comparator.h" #include "lib/jxl/enc_params.h" #include "lib/jxl/image_test_utils.h" #include "lib/jxl/test_utils.h" @@ -21,6 +20,7 @@ namespace jxl { namespace { +using test::ButteraugliDistance; using test::ReadTestData; using test::Roundtrip; diff --git a/third_party/jpeg-xl/lib/jxl/preview_test.cc b/third_party/jpeg-xl/lib/jxl/preview_test.cc index b7fe855d4d..c482db9fd8 100644 --- a/third_party/jpeg-xl/lib/jxl/preview_test.cc +++ b/third_party/jpeg-xl/lib/jxl/preview_test.cc @@ -13,7 +13,6 @@ #include "lib/extras/codec.h" #include "lib/jxl/base/span.h" #include "lib/jxl/codec_in_out.h" -#include "lib/jxl/enc_butteraugli_comparator.h" #include "lib/jxl/enc_params.h" #include "lib/jxl/headers.h" #include "lib/jxl/image_bundle.h" @@ -22,6 +21,7 @@ namespace jxl { namespace { +using test::ButteraugliDistance; using test::ReadTestData; using test::Roundtrip; @@ -32,7 +32,7 @@ TEST(PreviewTest, RoundtripGivenPreview) { ASSERT_TRUE(SetFromBytes(Bytes(orig), &io)); io.ShrinkTo(io.xsize() / 8, io.ysize() / 8); // Same as main image - io.preview_frame = io.Main().Copy(); + JXL_ASSIGN_OR_DIE(io.preview_frame, io.Main().Copy()); const size_t preview_xsize = 15; const size_t preview_ysize = 27; io.preview_frame.ShrinkTo(preview_xsize, preview_ysize); diff --git a/third_party/jpeg-xl/lib/jxl/quant_weights.cc b/third_party/jpeg-xl/lib/jxl/quant_weights.cc index 70b3b9e451..00563c5cbd 100644 --- a/third_party/jpeg-xl/lib/jxl/quant_weights.cc +++ b/third_party/jpeg-xl/lib/jxl/quant_weights.cc @@ -160,8 +160,8 @@ Status ComputeQuantTable(const QuantEncoding& encoding, float* JXL_RESTRICT inv_table, size_t table_num, DequantMatrices::QuantTable kind, size_t* pos) { constexpr size_t N = kBlockDim; - size_t wrows = 8 * DequantMatrices::required_size_x[kind], - wcols = 8 * DequantMatrices::required_size_y[kind]; + size_t wrows = 8 * DequantMatrices::required_size_x[kind]; + size_t wcols = 8 * DequantMatrices::required_size_y[kind]; size_t num = wrows * wcols; std::vector<float> weights(3 * num); @@ -361,7 +361,7 @@ namespace { HWY_EXPORT(ComputeQuantTable); -static constexpr const float kAlmostZero = 1e-8f; +constexpr const float kAlmostZero = 1e-8f; Status DecodeDctParams(BitReader* br, DctQuantWeightParams* params) { params->num_distance_bands = @@ -474,16 +474,16 @@ Status Decode(BitReader* br, QuantEncoding* encoding, size_t required_size_x, default: return JXL_FAILURE("Invalid quantization table encoding"); } - encoding->mode = QuantEncoding::Mode(mode); + encoding->mode = static_cast<QuantEncoding::Mode>(mode); return true; } } // namespace // These definitions are needed before C++17. -constexpr size_t DequantMatrices::required_size_[]; -constexpr size_t DequantMatrices::required_size_x[]; -constexpr size_t DequantMatrices::required_size_y[]; +constexpr const std::array<int, 17> DequantMatrices::required_size_x; +constexpr const std::array<int, 17> DequantMatrices::required_size_y; +constexpr const size_t DequantMatrices::kSumRequiredXy; constexpr DequantMatrices::QuantTable DequantMatrices::kQuantTable[]; Status DequantMatrices::Decode(BitReader* br, @@ -502,7 +502,7 @@ Status DequantMatrices::Decode(BitReader* br, } Status DequantMatrices::DecodeDC(BitReader* br) { - bool all_default = br->ReadBits(1); + bool all_default = static_cast<bool>(br->ReadBits(1)); if (!br->AllReadsWithinBounds()) return JXL_FAILURE("EOS during DecodeDC"); if (!all_default) { for (size_t c = 0; c < 3; c++) { @@ -1162,11 +1162,12 @@ const QuantEncoding* DequantMatrices::Library() { } DequantMatrices::DequantMatrices() { - encodings_.resize(size_t(QuantTable::kNum), QuantEncoding::Library(0)); + encodings_.resize(static_cast<size_t>(QuantTable::kNum), + QuantEncoding::Library(0)); size_t pos = 0; size_t offsets[kNum * 3]; - for (size_t i = 0; i < size_t(QuantTable::kNum); i++) { - size_t num = required_size_[i] * kDCTBlockSize; + for (size_t i = 0; i < static_cast<size_t>(QuantTable::kNum); i++) { + size_t num = required_size_x[i] * required_size_y[i] * kDCTBlockSize; for (size_t c = 0; c < 3; c++) { offsets[3 * i + c] = pos + c * num; } @@ -1191,7 +1192,7 @@ Status DequantMatrices::EnsureComputed(uint32_t acs_mask) { size_t offsets[kNum * 3 + 1]; size_t pos = 0; for (size_t i = 0; i < kNum; i++) { - size_t num = required_size_[i] * kDCTBlockSize; + size_t num = required_size_x[i] * required_size_y[i] * kDCTBlockSize; for (size_t c = 0; c < 3; c++) { offsets[3 * i + c] = pos + c * num; } diff --git a/third_party/jpeg-xl/lib/jxl/quant_weights.h b/third_party/jpeg-xl/lib/jxl/quant_weights.h index 3004176aba..0fa23ffddb 100644 --- a/third_party/jpeg-xl/lib/jxl/quant_weights.h +++ b/third_party/jpeg-xl/lib/jxl/quant_weights.h @@ -15,6 +15,7 @@ #include <vector> #include "lib/jxl/ac_strategy.h" +#include "lib/jxl/base/common.h" #include "lib/jxl/base/compiler_specific.h" #include "lib/jxl/base/span.h" #include "lib/jxl/base/status.h" @@ -23,12 +24,6 @@ namespace jxl { -template <typename T, size_t N> -constexpr T ArraySum(T (&a)[N], size_t i = N - 1) { - static_assert(N > 0, "Trying to compute the sum of an empty array"); - return i == 0 ? a[0] : a[i] + ArraySum(a, i - 1); -} - static constexpr size_t kMaxQuantTableSize = AcStrategy::kMaxCoeffArea; static constexpr size_t kNumPredefinedTables = 1; static constexpr size_t kCeilLog2NumPredefinedTables = 0; @@ -410,25 +405,23 @@ class DequantMatrices { const std::vector<QuantEncoding>& encodings() const { return encodings_; } - static constexpr size_t required_size_x[] = {1, 1, 1, 1, 2, 4, 1, 1, 2, - 1, 1, 8, 4, 16, 8, 32, 16}; - static_assert(kNum == sizeof(required_size_x) / sizeof(*required_size_x), + static constexpr auto required_size_x = + to_array<int>({1, 1, 1, 1, 2, 4, 1, 1, 2, 1, 1, 8, 4, 16, 8, 32, 16}); + static_assert(kNum == required_size_x.size(), "Update this array when adding or removing quant tables."); - static constexpr size_t required_size_y[] = {1, 1, 1, 1, 2, 4, 2, 4, 4, - 1, 1, 8, 8, 16, 16, 32, 32}; - static_assert(kNum == sizeof(required_size_y) / sizeof(*required_size_y), + static constexpr auto required_size_y = + to_array<int>({1, 1, 1, 1, 2, 4, 2, 4, 4, 1, 1, 8, 8, 16, 16, 32, 32}); + static_assert(kNum == required_size_y.size(), "Update this array when adding or removing quant tables."); + // MUST be equal `sum(dot(required_size_x, required_size_y))`. + static constexpr size_t kSumRequiredXy = 2056; + Status EnsureComputed(uint32_t acs_mask); private: - static constexpr size_t required_size_[] = { - 1, 1, 1, 1, 4, 16, 2, 4, 8, 1, 1, 64, 32, 256, 128, 1024, 512}; - static_assert(kNum == sizeof(required_size_) / sizeof(*required_size_), - "Update this array when adding or removing quant tables."); - static constexpr size_t kTotalTableSize = - ArraySum(required_size_) * kDCTBlockSize * 3; + static constexpr size_t kTotalTableSize = kSumRequiredXy * kDCTBlockSize * 3; uint32_t computed_mask_ = 0; // kTotalTableSize entries followed by kTotalTableSize for inv_table diff --git a/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc b/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc index 2dd513804c..e92cbf2151 100644 --- a/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc +++ b/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc @@ -23,6 +23,18 @@ namespace jxl { namespace { +// This should have been static assert; not compiling though with C++<17. +TEST(QuantWeightsTest, Invariant) { + size_t sum = 0; + ASSERT_EQ(DequantMatrices::required_size_x.size(), + DequantMatrices::required_size_y.size()); + for (size_t i = 0; i < DequantMatrices::required_size_x.size(); ++i) { + sum += DequantMatrices::required_size_x[i] * + DequantMatrices::required_size_y[i]; + } + ASSERT_EQ(DequantMatrices::kSumRequiredXy, sum); +} + template <typename T> void CheckSimilar(T a, T b) { EXPECT_EQ(a, b); @@ -50,8 +62,8 @@ void RoundtripMatrices(const std::vector<QuantEncoding>& encodings) { DequantMatrices mat; CodecMetadata metadata; FrameHeader frame_header(&metadata); - ModularFrameEncoder encoder(frame_header, CompressParams{}); - DequantMatricesSetCustom(&mat, encodings, &encoder); + ModularFrameEncoder encoder(frame_header, CompressParams{}, false); + JXL_CHECK(DequantMatricesSetCustom(&mat, encodings, &encoder)); const std::vector<QuantEncoding>& encodings_dec = mat.encodings(); for (size_t i = 0; i < encodings.size(); i++) { const QuantEncoding& e = encodings[i]; @@ -172,8 +184,8 @@ TEST_P(QuantWeightsTargetTest, DCTUniform) { DequantMatrices dequant_matrices; CodecMetadata metadata; FrameHeader frame_header(&metadata); - ModularFrameEncoder encoder(frame_header, CompressParams{}); - DequantMatricesSetCustom(&dequant_matrices, encodings, &encoder); + ModularFrameEncoder encoder(frame_header, CompressParams{}, false); + JXL_CHECK(DequantMatricesSetCustom(&dequant_matrices, encodings, &encoder)); JXL_CHECK(dequant_matrices.EnsureComputed(~0u)); const float dc_quant[3] = {1.0f / kUniformQuant, 1.0f / kUniformQuant, diff --git a/third_party/jpeg-xl/lib/jxl/quantizer.h b/third_party/jpeg-xl/lib/jxl/quantizer.h index 4e34ac78e8..602d12bdfa 100644 --- a/third_party/jpeg-xl/lib/jxl/quantizer.h +++ b/third_party/jpeg-xl/lib/jxl/quantizer.h @@ -73,7 +73,7 @@ class Quantizer { } float ScaleGlobalScale(const float scale) { - int new_global_scale = static_cast<int>(global_scale_ * scale + 0.5f); + int new_global_scale = static_cast<int>(std::lround(global_scale_ * scale)); float scale_out = new_global_scale * 1.0f / global_scale_; global_scale_ = new_global_scale; RecomputeFromGlobalScale(); diff --git a/third_party/jpeg-xl/lib/jxl/quantizer_test.cc b/third_party/jpeg-xl/lib/jxl/quantizer_test.cc index aff19f42c1..eeaac9ba53 100644 --- a/third_party/jpeg-xl/lib/jxl/quantizer_test.cc +++ b/third_party/jpeg-xl/lib/jxl/quantizer_test.cc @@ -5,10 +5,8 @@ #include "lib/jxl/quantizer.h" -#include "lib/jxl/base/span.h" #include "lib/jxl/dec_bit_reader.h" #include "lib/jxl/enc_fields.h" -#include "lib/jxl/image_ops.h" #include "lib/jxl/image_test_utils.h" #include "lib/jxl/testing.h" @@ -24,7 +22,8 @@ TEST(QuantizerTest, QuantizerParams) { for (uint32_t i = 1; i < 10000; ++i) { QuantizerParams p; p.global_scale = i; - size_t extension_bits = 0, total_bits = 0; + size_t extension_bits = 0; + size_t total_bits = 0; EXPECT_TRUE(Bundle::CanEncode(p, &extension_bits, &total_bits)); EXPECT_EQ(0u, extension_bits); EXPECT_GE(total_bits, 4u); @@ -36,7 +35,7 @@ TEST(QuantizerTest, BitStreamRoundtripSameQuant) { const int qysize = 8; DequantMatrices dequant; Quantizer quantizer1(&dequant); - ImageI raw_quant_field(qxsize, qysize); + JXL_ASSIGN_OR_DIE(ImageI raw_quant_field, ImageI::Create(qxsize, qysize)); quantizer1.SetQuant(0.17f, 0.17f, &raw_quant_field); BitWriter writer; QuantizerParams params = quantizer1.GetParams(); @@ -57,10 +56,10 @@ TEST(QuantizerTest, BitStreamRoundtripRandomQuant) { const int qysize = 8; DequantMatrices dequant; Quantizer quantizer1(&dequant); - ImageI raw_quant_field(qxsize, qysize); + JXL_ASSIGN_OR_DIE(ImageI raw_quant_field, ImageI::Create(qxsize, qysize)); quantizer1.SetQuant(0.17f, 0.17f, &raw_quant_field); float quant_dc = 0.17f; - ImageF qf(qxsize, qysize); + JXL_ASSIGN_OR_DIE(ImageF qf, ImageF::Create(qxsize, qysize)); RandomFillImage(&qf, 0.0f, 1.0f); quantizer1.SetQuantField(quant_dc, qf, &raw_quant_field); BitWriter writer; diff --git a/third_party/jpeg-xl/lib/jxl/rational_polynomial_test.cc b/third_party/jpeg-xl/lib/jxl/rational_polynomial_test.cc index bc31cdd092..e5211cda7a 100644 --- a/third_party/jpeg-xl/lib/jxl/rational_polynomial_test.cc +++ b/third_party/jpeg-xl/lib/jxl/rational_polynomial_test.cc @@ -158,8 +158,8 @@ void TestExp() { const T q[4 * (2 + 1)] = {HWY_REP4(9.6259895571622622E-01), HWY_REP4(-4.7272457588933831E-01), HWY_REP4(7.4802088567547664E-02)}; - const T err = - RunApproximation(-1, 1, p, q, EvalPoly(), [](T x) { return T(exp(x)); }); + const T err = RunApproximation(-1, 1, p, q, EvalPoly(), + [](T x) { return static_cast<T>(exp(x)); }); EXPECT_LT(err, 1E-4); } @@ -174,8 +174,8 @@ void TestNegExp() { HWY_REP4(5.9579108238812878E-02), HWY_REP4(3.4542074345478582E-02), HWY_REP4(8.7263562483501714E-03), HWY_REP4(1.4095109143061216E-03)}; - const T err = - RunApproximation(0, 10, p, q, EvalPoly(), [](T x) { return T(exp(-x)); }); + const T err = RunApproximation(0, 10, p, q, EvalPoly(), + [](T x) { return static_cast<T>(exp(-x)); }); EXPECT_LT(err, sizeof(T) == 8 ? 2E-5 : 3E-5); } @@ -191,7 +191,7 @@ void TestSin() { HWY_REP4(3.1546157932479282E-03), HWY_REP4(-1.6692542019380155E-04)}; const T err = RunApproximation(0, Pi<T>(1) * 2, p, q, EvalPoly(), - [](T x) { return T(sin(x)); }); + [](T x) { return static_cast<T>(sin(x)); }); EXPECT_LT(err, sizeof(T) == 8 ? 5E-4 : 7E-4); } diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc index 9aefdd007d..27718e6413 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc @@ -6,10 +6,9 @@ #include "lib/jxl/render_pipeline/low_memory_render_pipeline.h" #include <algorithm> -#include <queue> -#include <tuple> #include "lib/jxl/base/arch_macros.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/image_ops.h" namespace jxl { @@ -174,7 +173,7 @@ size_t LowMemoryRenderPipeline::GroupInputYSize(size_t c) const { channel_shifts_[0][c].second; } -void LowMemoryRenderPipeline::EnsureBordersStorage() { +Status LowMemoryRenderPipeline::EnsureBordersStorage() { const auto& shifts = channel_shifts_[0]; if (borders_horizontal_.size() < shifts.size()) { borders_horizontal_.resize(shifts.size()); @@ -194,16 +193,20 @@ void LowMemoryRenderPipeline::EnsureBordersStorage() { 1 << shifts[c].second); Rect horizontal = Rect(0, 0, downsampled_xsize, bordery * num_yborders); if (!SameSize(horizontal, borders_horizontal_[c])) { - borders_horizontal_[c] = ImageF(horizontal.xsize(), horizontal.ysize()); + JXL_ASSIGN_OR_RETURN( + borders_horizontal_[c], + ImageF::Create(horizontal.xsize(), horizontal.ysize())); } Rect vertical = Rect(0, 0, borderx * num_xborders, downsampled_ysize); if (!SameSize(vertical, borders_vertical_[c])) { - borders_vertical_[c] = ImageF(vertical.xsize(), vertical.ysize()); + JXL_ASSIGN_OR_RETURN(borders_vertical_[c], + ImageF::Create(vertical.xsize(), vertical.ysize())); } } + return true; } -void LowMemoryRenderPipeline::Init() { +Status LowMemoryRenderPipeline::Init() { group_border_ = {0, 0}; base_color_shift_ = CeilLog2Nonzero(frame_dimensions_.xsize_upsampled_padded / frame_dimensions_.xsize_padded); @@ -255,7 +258,7 @@ void LowMemoryRenderPipeline::Init() { group_data_x_border_ = RoundUpTo(max_border.first, kGroupXAlign); group_data_y_border_ = max_border.second; - EnsureBordersStorage(); + JXL_RETURN_IF_ERROR(EnsureBordersStorage()); group_border_assigner_.Init(frame_dimensions_); for (first_trailing_stage_ = stages_.size(); first_trailing_stage_ > 0; @@ -282,7 +285,7 @@ void LowMemoryRenderPipeline::Init() { DivCeil(frame_dimensions_.ysize_upsampled, 1 << channel_shifts_[i][c].second)); } - stages_[i]->SetInputSizes(input_sizes); + JXL_RETURN_IF_ERROR(stages_[i]->SetInputSizes(input_sizes)); if (stages_[i]->SwitchToImageDimensions()) { // We don't allow kInOut after switching to image dimensions. JXL_ASSERT(i >= first_trailing_stage_); @@ -300,7 +303,7 @@ void LowMemoryRenderPipeline::Init() { for (size_t c = 0; c < shifts.size(); c++) { input_sizes[c] = {full_image_xsize_, full_image_ysize_}; } - stages_[i]->SetInputSizes(input_sizes); + JXL_RETURN_IF_ERROR(stages_[i]->SetInputSizes(input_sizes)); } anyc_.resize(stages_.size()); @@ -355,10 +358,11 @@ void LowMemoryRenderPipeline::Init() { } } } + return true; } -void LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num, - bool use_group_ids) { +Status LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num, + bool use_group_ids) { const auto& shifts = channel_shifts_[0]; use_group_ids_ = use_group_ids; size_t num_buffers = use_group_ids_ ? frame_dimensions_.num_groups : num; @@ -366,8 +370,10 @@ void LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num, group_data_.emplace_back(); group_data_[t].resize(shifts.size()); for (size_t c = 0; c < shifts.size(); c++) { - group_data_[t][c] = ImageF(GroupInputXSize(c) + group_data_x_border_ * 2, - GroupInputYSize(c) + group_data_y_border_ * 2); + JXL_ASSIGN_OR_RETURN( + group_data_[t][c], + ImageF::Create(GroupInputXSize(c) + group_data_x_border_ * 2, + GroupInputYSize(c) + group_data_y_border_ * 2)); } } // TODO(veluca): avoid reallocating buffers if not needed. @@ -390,7 +396,9 @@ void LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num, 2 * next_y_border + (1 << stages_[i]->settings_.shift_y); stage_buffer_ysize = 1 << CeilLog2Nonzero(stage_buffer_ysize); next_y_border = stages_[i]->settings_.border_y; - stage_data_[t][c][i] = ImageF(stage_buffer_xsize, stage_buffer_ysize); + JXL_ASSIGN_OR_RETURN( + stage_data_[t][c][i], + ImageF::Create(stage_buffer_xsize, stage_buffer_ysize)); } } } @@ -412,9 +420,11 @@ void LowMemoryRenderPipeline::PrepareForThreadsInternal(size_t num, std::max(left_padding, std::max(middle_padding, right_padding)); out_of_frame_data_.resize(num); for (size_t t = 0; t < num; t++) { - out_of_frame_data_[t] = ImageF(out_of_frame_xsize, shifts.size()); + JXL_ASSIGN_OR_RETURN(out_of_frame_data_[t], + ImageF::Create(out_of_frame_xsize, shifts.size())); } } + return true; } std::vector<std::pair<ImageF*, Rect>> LowMemoryRenderPipeline::PrepareBuffers( @@ -520,7 +530,8 @@ class Rows { .Translate(-group_data_x_border, -group_data_y_border) .ShiftLeft(base_color_shift) .CeilShiftRight(group_data_shift[c]) - .Translate(group_data_x_border - ssize_t(kRenderPipelineXOffset), + .Translate(group_data_x_border - + static_cast<ssize_t>(kRenderPipelineXOffset), group_data_y_border); rows_[0][c].base_ptr = channel_group_data_rect.Row(&input_data[c], 0); rows_[0][c].stride = input_data[c].PixelsPerRow(); @@ -533,7 +544,8 @@ class Rows { JXL_INLINE float* GetBuffer(int stage, int y, size_t c) const { JXL_DASSERT(stage >= -1); const RowInfo& info = rows_[stage + 1][c]; - return info.base_ptr + ssize_t(info.stride) * (y & info.ymod_minus_1); + return info.base_ptr + + static_cast<ssize_t>(info.stride) * (y & info.ymod_minus_1); } private: @@ -551,10 +563,10 @@ class Rows { } // namespace -void LowMemoryRenderPipeline::RenderRect(size_t thread_id, - std::vector<ImageF>& input_data, - Rect data_max_color_channel_rect, - Rect image_max_color_channel_rect) { +Status LowMemoryRenderPipeline::RenderRect(size_t thread_id, + std::vector<ImageF>& input_data, + Rect data_max_color_channel_rect, + Rect image_max_color_channel_rect) { // For each stage, the rect corresponding to the image area currently being // processed, in the coordinates of that stage (i.e. with the scaling factor // that that stage has). @@ -599,7 +611,7 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id, // is no point in proceeding. Note: this uses the assumption that if there is // a stage with observable effects (i.e. a kInput stage), it only appears // after the stage that switches to image dimensions. - if (full_image_x1 <= full_image_x0) return; + if (full_image_x1 <= full_image_x0) return true; // Data structures to hold information about input/output rows and their // buffers. @@ -643,7 +655,7 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id, } // If we already have rows from a previous iteration, we can just shift // the rows by 1 and insert the new one. - if (input_rows[i][c].size() == 2 * size_t(bordery) + 1) { + if (input_rows[i][c].size() == 2 * static_cast<size_t>(bordery) + 1) { for (ssize_t iy = 0; iy < 2 * bordery; iy++) { input_rows[i][c][iy] = input_rows[i][c][iy + 1]; } @@ -674,7 +686,7 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id, virtual_ypadding_for_output_.end()); for (int vy = -num_extra_rows; - vy < int(image_area_rect.ysize()) + num_extra_rows; vy++) { + vy < static_cast<int>(image_area_rect.ysize()) + num_extra_rows; vy++) { for (size_t i = 0; i < first_trailing_stage_; i++) { int stage_vy = vy - num_extra_rows + virtual_ypadding_for_output_[i]; @@ -688,9 +700,10 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id, int y = stage_vy >> channel_shifts_[i][anyc_[i]].second; - ssize_t image_y = ssize_t(group_rect[i].y0()) + y; + ssize_t image_y = static_cast<ssize_t>(group_rect[i].y0()) + y; // Do not produce rows in out-of-bounds areas. - if (image_y < 0 || image_y >= ssize_t(image_rect_[i].ysize())) { + if (image_y < 0 || + image_y >= static_cast<ssize_t>(image_rect_[i].ysize())) { continue; } @@ -698,9 +711,9 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id, prepare_io_rows(y, i); // Produce output rows. - stages_[i]->ProcessRow(input_rows[i], output_rows, - xpadding_for_output_[i], group_rect[i].xsize(), - group_rect[i].x0(), image_y, thread_id); + JXL_RETURN_IF_ERROR(stages_[i]->ProcessRow( + input_rows[i], output_rows, xpadding_for_output_[i], + group_rect[i].xsize(), group_rect[i].x0(), image_y, thread_id)); } // Process trailing stages, i.e. the final set of non-kInOut stages; they @@ -719,7 +732,7 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id, // Check that we are not outside of the bounds for the current rendering // rect. Not doing so might result in overwriting some rows that have been // written (or will be written) by other threads. - if (y < 0 || y >= ssize_t(image_area_rect.ysize())) { + if (y < 0 || y >= static_cast<ssize_t>(image_area_rect.ysize())) { continue; } @@ -728,7 +741,8 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id, // (and may be necessary for correctness, as some stages assume coordinates // are within bounds). ssize_t full_image_y = frame_y0 + image_area_rect.y0() + y; - if (full_image_y < 0 || full_image_y >= ssize_t(full_image_ysize)) { + if (full_image_y < 0 || + full_image_y >= static_cast<ssize_t>(full_image_ysize)) { continue; } @@ -739,15 +753,16 @@ void LowMemoryRenderPipeline::RenderRect(size_t thread_id, i < first_image_dim_stage_ ? full_image_x0 - frame_x0 : full_image_x0; size_t y = i < first_image_dim_stage_ ? full_image_y - frame_y0 : full_image_y; - stages_[i]->ProcessRow(input_rows[first_trailing_stage_], output_rows, - /*xextra=*/0, full_image_x1 - full_image_x0, x0, y, - thread_id); + JXL_RETURN_IF_ERROR(stages_[i]->ProcessRow( + input_rows[first_trailing_stage_], output_rows, + /*xextra=*/0, full_image_x1 - full_image_x0, x0, y, thread_id)); } } + return true; } -void LowMemoryRenderPipeline::RenderPadding(size_t thread_id, Rect rect) { - if (rect.xsize() == 0) return; +Status LowMemoryRenderPipeline::RenderPadding(size_t thread_id, Rect rect) { + if (rect.xsize() == 0) return true; size_t numc = channel_shifts_[0].size(); RenderPipelineStage::RowInfo input_rows(numc, std::vector<float*>(1)); RenderPipelineStage::RowInfo output_rows; @@ -760,15 +775,16 @@ void LowMemoryRenderPipeline::RenderPadding(size_t thread_id, Rect rect) { stages_[first_image_dim_stage_ - 1]->ProcessPaddingRow( input_rows, rect.xsize(), rect.x0(), rect.y0() + y); for (size_t i = first_image_dim_stage_; i < stages_.size(); i++) { - stages_[i]->ProcessRow(input_rows, output_rows, - /*xextra=*/0, rect.xsize(), rect.x0(), - rect.y0() + y, thread_id); + JXL_RETURN_IF_ERROR(stages_[i]->ProcessRow( + input_rows, output_rows, + /*xextra=*/0, rect.xsize(), rect.x0(), rect.y0() + y, thread_id)); } } + return true; } -void LowMemoryRenderPipeline::ProcessBuffers(size_t group_id, - size_t thread_id) { +Status LowMemoryRenderPipeline::ProcessBuffers(size_t group_id, + size_t thread_id) { std::vector<ImageF>& input_data = group_data_[use_group_ids_ ? group_id : thread_id]; @@ -804,38 +820,43 @@ void LowMemoryRenderPipeline::ProcessBuffers(size_t group_id, if (group_id == 0 && (image_rect.xsize() == 0 || image_rect.ysize() == 0)) { // If this frame does not intersect with the full image, we have to // initialize the whole image area with RenderPadding. - RenderPadding(thread_id, - Rect(0, 0, full_image_xsize_, full_image_ysize_)); + JXL_RETURN_IF_ERROR(RenderPadding( + thread_id, Rect(0, 0, full_image_xsize_, full_image_ysize_))); } // Render padding for groups that intersect with the full image. The case // where no groups intersect was handled above. if (group_rect.xsize() > 0 && group_rect.ysize() > 0) { if (gx == 0 && gy == 0) { - RenderPadding(thread_id, Rect(0, 0, x0, y0)); + JXL_RETURN_IF_ERROR(RenderPadding(thread_id, Rect(0, 0, x0, y0))); } if (gy == 0) { - RenderPadding(thread_id, Rect(x0, 0, x1 - x0, y0)); + JXL_RETURN_IF_ERROR(RenderPadding(thread_id, Rect(x0, 0, x1 - x0, y0))); } if (gx == 0) { - RenderPadding(thread_id, Rect(0, y0, x0, y1 - y0)); + JXL_RETURN_IF_ERROR(RenderPadding(thread_id, Rect(0, y0, x0, y1 - y0))); } if (gx == 0 && gy + 1 == frame_dimensions_.ysize_groups) { - RenderPadding(thread_id, Rect(0, y1, x0, full_image_ysize_ - y1)); + JXL_RETURN_IF_ERROR( + RenderPadding(thread_id, Rect(0, y1, x0, full_image_ysize_ - y1))); } if (gy + 1 == frame_dimensions_.ysize_groups) { - RenderPadding(thread_id, Rect(x0, y1, x1 - x0, full_image_ysize_ - y1)); + JXL_RETURN_IF_ERROR(RenderPadding( + thread_id, Rect(x0, y1, x1 - x0, full_image_ysize_ - y1))); } if (gy == 0 && gx + 1 == frame_dimensions_.xsize_groups) { - RenderPadding(thread_id, Rect(x1, 0, full_image_xsize_ - x1, y0)); + JXL_RETURN_IF_ERROR( + RenderPadding(thread_id, Rect(x1, 0, full_image_xsize_ - x1, y0))); } if (gx + 1 == frame_dimensions_.xsize_groups) { - RenderPadding(thread_id, Rect(x1, y0, full_image_xsize_ - x1, y1 - y0)); + JXL_RETURN_IF_ERROR(RenderPadding( + thread_id, Rect(x1, y0, full_image_xsize_ - x1, y1 - y0))); } if (gy + 1 == frame_dimensions_.ysize_groups && gx + 1 == frame_dimensions_.xsize_groups) { - RenderPadding(thread_id, Rect(x1, y1, full_image_xsize_ - x1, - full_image_ysize_ - y1)); + JXL_RETURN_IF_ERROR(RenderPadding( + thread_id, + Rect(x1, y1, full_image_xsize_ - x1, full_image_ysize_ - y1))); } } } @@ -857,8 +878,10 @@ void LowMemoryRenderPipeline::ProcessBuffers(size_t group_id, gy * frame_dimensions_.group_dim, image_max_color_channel_rect.xsize(), image_max_color_channel_rect.ysize()); - RenderRect(thread_id, input_data, data_max_color_channel_rect, - image_max_color_channel_rect); + JXL_RETURN_IF_ERROR(RenderRect(thread_id, input_data, + data_max_color_channel_rect, + image_max_color_channel_rect)); } + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h index b386f7c078..f0b21d3dca 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.h @@ -20,21 +20,21 @@ class LowMemoryRenderPipeline final : public RenderPipeline { std::vector<std::pair<ImageF*, Rect>> PrepareBuffers( size_t group_id, size_t thread_id) override; - void PrepareForThreadsInternal(size_t num, bool use_group_ids) override; + Status PrepareForThreadsInternal(size_t num, bool use_group_ids) override; - void ProcessBuffers(size_t group_id, size_t thread_id) override; + Status ProcessBuffers(size_t group_id, size_t thread_id) override; void ClearDone(size_t i) override { group_border_assigner_.ClearDone(i); } - void Init() override; + Status Init() override; - void EnsureBordersStorage(); + Status EnsureBordersStorage(); size_t GroupInputXSize(size_t c) const; size_t GroupInputYSize(size_t c) const; - void RenderRect(size_t thread_id, std::vector<ImageF>& input_data, - Rect data_max_color_channel_rect, - Rect image_max_color_channel_rect); - void RenderPadding(size_t thread_id, Rect rect); + Status RenderRect(size_t thread_id, std::vector<ImageF>& input_data, + Rect data_max_color_channel_rect, + Rect image_max_color_channel_rect); + Status RenderPadding(size_t thread_id, Rect rect); void SaveBorders(size_t group_id, size_t c, const ImageF& in); void LoadBorders(size_t group_id, size_t c, const Rect& r, ImageF* out); diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc index 68b6ef613f..14bd363110 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc @@ -5,8 +5,7 @@ #include "lib/jxl/render_pipeline/render_pipeline.h" -#include <algorithm> - +#include "lib/jxl/base/status.h" #include "lib/jxl/render_pipeline/low_memory_render_pipeline.h" #include "lib/jxl/render_pipeline/simple_render_pipeline.h" #include "lib/jxl/sanitizers.h" @@ -18,7 +17,7 @@ void RenderPipeline::Builder::AddStage( stages_.push_back(std::move(stage)); } -std::unique_ptr<RenderPipeline> RenderPipeline::Builder::Finalize( +StatusOr<std::unique_ptr<RenderPipeline>> RenderPipeline::Builder::Finalize( FrameDimensions frame_dimensions) && { #if JXL_ENABLE_ASSERT // Check that the last stage is not an kInOut stage for any channel, and that @@ -88,7 +87,7 @@ std::unique_ptr<RenderPipeline> RenderPipeline::Builder::Finalize( } } res->stages_ = std::move(stages_); - res->Init(); + JXL_RETURN_IF_ERROR(res->Init()); return res; } @@ -103,7 +102,7 @@ RenderPipelineInput RenderPipeline::GetInputBuffers(size_t group_id, return ret; } -void RenderPipeline::InputReady( +Status RenderPipeline::InputReady( size_t group_id, size_t thread_id, const std::vector<std::pair<ImageF*, Rect>>& buffers) { JXL_DASSERT(group_id < group_completed_passes_.size()); @@ -113,20 +112,22 @@ void RenderPipeline::InputReady( JXL_CHECK_PLANE_INITIALIZED(*buffers[i].first, buffers[i].second, i); } - ProcessBuffers(group_id, thread_id); + JXL_RETURN_IF_ERROR(ProcessBuffers(group_id, thread_id)); + return true; } Status RenderPipeline::PrepareForThreads(size_t num, bool use_group_ids) { for (const auto& stage : stages_) { JXL_RETURN_IF_ERROR(stage->PrepareForThreads(num)); } - PrepareForThreadsInternal(num, use_group_ids); + JXL_RETURN_IF_ERROR(PrepareForThreadsInternal(num, use_group_ids)); return true; } -void RenderPipelineInput::Done() { +Status RenderPipelineInput::Done() { JXL_ASSERT(pipeline_); - pipeline_->InputReady(group_id_, thread_id_, buffers_); + JXL_RETURN_IF_ERROR(pipeline_->InputReady(group_id_, thread_id_, buffers_)); + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h index bf3ad4975e..c61420be4b 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.h @@ -32,7 +32,7 @@ class RenderPipelineInput { } RenderPipelineInput() = default; - void Done(); + Status Done(); const std::pair<ImageF*, Rect>& GetBuffer(size_t c) const { JXL_ASSERT(c < buffers_.size()); @@ -63,7 +63,7 @@ class RenderPipeline { // Finalizes setup of the pipeline. Shifts for all channels should be 0 at // this point. - std::unique_ptr<RenderPipeline> Finalize( + StatusOr<std::unique_ptr<RenderPipeline>> Finalize( FrameDimensions frame_dimensions) &&; private: @@ -118,20 +118,20 @@ class RenderPipeline { friend class RenderPipelineInput; private: - void InputReady(size_t group_id, size_t thread_id, - const std::vector<std::pair<ImageF*, Rect>>& buffers); + Status InputReady(size_t group_id, size_t thread_id, + const std::vector<std::pair<ImageF*, Rect>>& buffers); virtual std::vector<std::pair<ImageF*, Rect>> PrepareBuffers( size_t group_id, size_t thread_id) = 0; - virtual void ProcessBuffers(size_t group_id, size_t thread_id) = 0; + virtual Status ProcessBuffers(size_t group_id, size_t thread_id) = 0; // Note that this method may be called multiple times with different (or // equal) `num`. - virtual void PrepareForThreadsInternal(size_t num, bool use_group_ids) = 0; + virtual Status PrepareForThreadsInternal(size_t num, bool use_group_ids) = 0; // Called once frame dimensions and stages are known. - virtual void Init() {} + virtual Status Init() { return true; } }; } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_stage.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_stage.h index d1a0074161..d054027ba7 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_stage.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_stage.h @@ -9,6 +9,7 @@ #include <stdint.h> #include "lib/jxl/base/arch_macros.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/frame_header.h" namespace jxl { @@ -99,9 +100,10 @@ class RenderPipelineStage { // `GroupBorderAssigner::kPaddingXRound`. If `settings_.temp_buffer_size` is // nonzero, `temp` will point to an HWY-aligned buffer of at least that number // of floats; concurrent calls will have different buffers. - virtual void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const = 0; + virtual Status ProcessRow(const RowInfo& input_rows, + const RowInfo& output_rows, size_t xextra, + size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const = 0; // How each channel will be processed. Channels are numbered starting from // color channels (always 3) and followed by all other channels. @@ -114,8 +116,10 @@ class RenderPipelineStage { // Informs the stage about the total size of each channel. Few stages will // actually need to use this information. - virtual void SetInputSizes( - const std::vector<std::pair<size_t, size_t>>& input_sizes) {} + virtual Status SetInputSizes( + const std::vector<std::pair<size_t, size_t>>& input_sizes) { + return true; + } virtual Status PrepareForThreads(size_t num_threads) { return true; } diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc index 51b9f273f8..e9cb913983 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline_test.cc @@ -111,7 +111,7 @@ TEST(RenderPipelineTest, Build) { frame_dimensions.Set(/*xsize=*/1024, /*ysize=*/1024, /*group_size_shift=*/0, /*max_hshift=*/0, /*max_vshift=*/0, /*modular_mode=*/false, /*upsampling=*/1); - std::move(builder).Finalize(frame_dimensions); + std::move(builder).Finalize(frame_dimensions).value(); } TEST(RenderPipelineTest, CallAllGroups) { @@ -124,14 +124,14 @@ TEST(RenderPipelineTest, CallAllGroups) { frame_dimensions.Set(/*xsize=*/1024, /*ysize=*/1024, /*group_size_shift=*/0, /*max_hshift=*/0, /*max_vshift=*/0, /*modular_mode=*/false, /*upsampling=*/1); - auto pipeline = std::move(builder).Finalize(frame_dimensions); + auto pipeline = std::move(builder).Finalize(frame_dimensions).value(); ASSERT_TRUE(pipeline->PrepareForThreads(1, /*use_group_ids=*/false)); for (size_t i = 0; i < frame_dimensions.num_groups; i++) { auto input_buffers = pipeline->GetInputBuffers(i, 0); FillPlane(0.0f, input_buffers.GetBuffer(0).first, input_buffers.GetBuffer(0).second); - input_buffers.Done(); + JXL_CHECK(input_buffers.Done()); } EXPECT_EQ(pipeline->PassesWithAllInput(), 1); @@ -146,7 +146,7 @@ TEST(RenderPipelineTest, BuildFast) { frame_dimensions.Set(/*xsize=*/1024, /*ysize=*/1024, /*group_size_shift=*/0, /*max_hshift=*/0, /*max_vshift=*/0, /*modular_mode=*/false, /*upsampling=*/1); - std::move(builder).Finalize(frame_dimensions); + std::move(builder).Finalize(frame_dimensions).value(); } TEST(RenderPipelineTest, CallAllGroupsFast) { @@ -159,14 +159,14 @@ TEST(RenderPipelineTest, CallAllGroupsFast) { frame_dimensions.Set(/*xsize=*/1024, /*ysize=*/1024, /*group_size_shift=*/0, /*max_hshift=*/0, /*max_vshift=*/0, /*modular_mode=*/false, /*upsampling=*/1); - auto pipeline = std::move(builder).Finalize(frame_dimensions); + auto pipeline = std::move(builder).Finalize(frame_dimensions).value(); ASSERT_TRUE(pipeline->PrepareForThreads(1, /*use_group_ids=*/false)); for (size_t i = 0; i < frame_dimensions.num_groups; i++) { auto input_buffers = pipeline->GetInputBuffers(i, 0); FillPlane(0.0f, input_buffers.GetBuffer(0).first, input_buffers.GetBuffer(0).second); - input_buffers.Done(); + JXL_CHECK(input_buffers.Done()); } EXPECT_EQ(pipeline->PassesWithAllInput(), 1); @@ -208,7 +208,7 @@ TEST_P(RenderPipelineTestParam, PipelineTest) { io.ShrinkTo(config.xsize, config.ysize); if (config.add_spot_color) { - jxl::ImageF spot(config.xsize, config.ysize); + JXL_ASSIGN_OR_DIE(ImageF spot, ImageF::Create(config.xsize, config.ysize)); jxl::ZeroFillImage(&spot); for (size_t y = 0; y < config.ysize; y++) { @@ -227,7 +227,7 @@ TEST_P(RenderPipelineTestParam, PipelineTest) { info.spot_color[3] = 0.5f; io.metadata.m.extra_channel_info.push_back(info); - std::vector<jxl::ImageF> ec; + std::vector<ImageF> ec; ec.push_back(std::move(spot)); io.frames[0].SetExtraChannels(std::move(ec)); } @@ -267,11 +267,11 @@ Splines CreateTestSplines() { const ColorCorrelationMap cmap; std::vector<Spline::Point> control_points{{9, 54}, {118, 159}, {97, 3}, {10, 40}, {150, 25}, {120, 300}}; - const Spline spline{ - control_points, - /*color_dct=*/ - {{0.03125f, 0.00625f, 0.003125f}, {1.f, 0.321875f}, {1.f, 0.24375f}}, - /*sigma_dct=*/{0.3125f, 0.f, 0.f, 0.0625f}}; + const Spline spline{control_points, + /*color_dct=*/ + {Dct32{0.03125f, 0.00625f, 0.003125f}, + Dct32{1.f, 0.321875f}, Dct32{1.f, 0.24375f}}, + /*sigma_dct=*/{0.3125f, 0.f, 0.f, 0.0625f}}; std::vector<Spline> spline_data = {spline}; std::vector<QuantizedSpline> quantized_splines; std::vector<Spline::Point> starting_points; @@ -522,7 +522,7 @@ std::ostream& operator<<(std::ostream& os, filename = c.input_path.substr(pos + 1); } std::replace_if( - filename.begin(), filename.end(), [](char c) { return !isalnum(c); }, + filename.begin(), filename.end(), [](char c) { return isalnum(c) == 0; }, '_'); os << filename << "_" << (c.jpeg_transcode ? "JPEG_" : "") << c.xsize << "x" << c.ysize << "_" << c.cparams_descr; diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc index 4495288860..7f5a8ef00f 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc @@ -7,27 +7,31 @@ #include <hwy/base.h> +#include "lib/jxl/base/status.h" #include "lib/jxl/image_ops.h" #include "lib/jxl/render_pipeline/render_pipeline_stage.h" #include "lib/jxl/sanitizers.h" namespace jxl { -void SimpleRenderPipeline::PrepareForThreadsInternal(size_t num, - bool use_group_ids) { +Status SimpleRenderPipeline::PrepareForThreadsInternal(size_t num, + bool use_group_ids) { if (!channel_data_.empty()) { - return; + return true; } auto ch_size = [](size_t frame_size, size_t shift) { return DivCeil(frame_size, 1 << shift) + kRenderPipelineXOffset * 2; }; for (size_t c = 0; c < channel_shifts_[0].size(); c++) { - channel_data_.push_back(ImageF( - ch_size(frame_dimensions_.xsize_upsampled, channel_shifts_[0][c].first), - ch_size(frame_dimensions_.ysize_upsampled, - channel_shifts_[0][c].second))); + JXL_ASSIGN_OR_RETURN( + ImageF ch, ImageF::Create(ch_size(frame_dimensions_.xsize_upsampled, + channel_shifts_[0][c].first), + ch_size(frame_dimensions_.ysize_upsampled, + channel_shifts_[0][c].second))); + channel_data_.push_back(std::move(ch)); msan::PoisonImage(channel_data_.back()); } + return true; } Rect SimpleRenderPipeline::MakeChannelRect(size_t group_id, size_t channel) { @@ -60,14 +64,14 @@ std::vector<std::pair<ImageF*, Rect>> SimpleRenderPipeline::PrepareBuffers( return ret; } -void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { +Status SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { for (size_t c = 0; c < channel_data_.size(); c++) { Rect r = MakeChannelRect(group_id, c); (void)r; JXL_CHECK_PLANE_INITIALIZED(channel_data_[c], r, c); } - if (PassesWithAllInput() <= processed_passes_) return; + if (PassesWithAllInput() <= processed_passes_) return true; processed_passes_++; for (size_t stage_id = 0; stage_id < stages_.size(); stage_id++) { @@ -89,11 +93,13 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { } // Ensure that the newly allocated channels are large enough to avoid // problems with padding. - new_channels[c] = - ImageF(frame_dimensions_.xsize_upsampled_padded + - kRenderPipelineXOffset * 2 + hwy::kMaxVectorSize * 8, - frame_dimensions_.ysize_upsampled_padded + - kRenderPipelineXOffset * 2); + JXL_ASSIGN_OR_RETURN( + new_channels[c], + ImageF::Create(frame_dimensions_.xsize_upsampled_padded + + kRenderPipelineXOffset * 2 + + hwy::kMaxVectorSize * 8, + frame_dimensions_.ysize_upsampled_padded + + kRenderPipelineXOffset * 2)); new_channels[c].ShrinkTo( (input_sizes[c].first << stage->settings_.shift_x) + kRenderPipelineXOffset * 2, @@ -116,7 +122,8 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { for (size_t y = 0; y < input_sizes[c].second; y++) { float* row = get_row(c, y); for (size_t ix = 0; ix < stage->settings_.border_x; ix++) { - *(row - ix - 1) = row[Mirror(-ssize_t(ix) - 1, input_sizes[c].first)]; + *(row - ix - 1) = + row[Mirror(-static_cast<ssize_t>(ix) - 1, input_sizes[c].first)]; } for (size_t ix = 0; ix < stage->settings_.border_x; ix++) { *(row + ix + input_sizes[c].first) = @@ -126,7 +133,8 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { // Vertical mirroring. for (int y = 0; y < static_cast<int>(stage->settings_.border_y); y++) { memcpy(get_row(c, -y - 1) - stage->settings_.border_x, - get_row(c, Mirror(-ssize_t(y) - 1, input_sizes[c].second)) - + get_row(c, Mirror(-static_cast<ssize_t>(y) - 1, + input_sizes[c].second)) - stage->settings_.border_x, sizeof(float) * (input_sizes[c].first + 2 * stage->settings_.border_x)); @@ -160,7 +168,7 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { // Run the pipeline. { - stage->SetInputSizes(input_sizes); + JXL_RETURN_IF_ERROR(stage->SetInputSizes(input_sizes)); int border_y = stage->settings_.border_y; for (size_t y = 0; y < ysize; y++) { // Prepare input rows. @@ -183,8 +191,9 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { (y << stage->settings_.shift_y) + iy + kRenderPipelineXOffset); } } - stage->ProcessRow(input_rows, output_rows, /*xextra=*/0, xsize, - /*xpos=*/0, y, thread_id); + JXL_RETURN_IF_ERROR(stage->ProcessRow(input_rows, output_rows, + /*xextra=*/0, xsize, + /*xpos=*/0, y, thread_id)); } } @@ -210,7 +219,8 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { } if (stage->SwitchToImageDimensions()) { - size_t image_xsize, image_ysize; + size_t image_xsize; + size_t image_ysize; FrameOrigin frame_origin; stage->GetImageDimensions(&image_xsize, &image_ysize, &frame_origin); frame_dimensions_.Set(image_xsize, image_ysize, 0, 0, 0, false, 1); @@ -218,8 +228,11 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { channel_data_.clear(); channel_data_.reserve(old_channels.size()); for (size_t c = 0; c < old_channels.size(); c++) { - channel_data_.emplace_back(2 * kRenderPipelineXOffset + image_xsize, - 2 * kRenderPipelineXOffset + image_ysize); + JXL_ASSIGN_OR_RETURN( + ImageF ch, + ImageF::Create(2 * kRenderPipelineXOffset + image_xsize, + 2 * kRenderPipelineXOffset + image_ysize)); + channel_data_.emplace_back(std::move(ch)); } for (size_t y = 0; y < image_ysize; ++y) { for (size_t c = 0; c < channel_data_.size(); c++) { @@ -262,5 +275,6 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) { } } } + return true; } } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h index 10f4505912..1240b9fa46 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.h @@ -19,9 +19,9 @@ class SimpleRenderPipeline : public RenderPipeline { std::vector<std::pair<ImageF*, Rect>> PrepareBuffers( size_t group_id, size_t thread_id) override; - void ProcessBuffers(size_t group_id, size_t thread_id) override; + Status ProcessBuffers(size_t group_id, size_t thread_id) override; - void PrepareForThreadsInternal(size_t num, bool use_group_ids) override; + Status PrepareForThreadsInternal(size_t num, bool use_group_ids) override; // Full frame buffers. Both X and Y dimensions are padded by // kRenderPipelineXOffset. diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc index b68105f4c9..ef3899d1b3 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc @@ -109,7 +109,7 @@ class BlendingStage : public RenderPipelineStage { } } }; - make_blending(info_, &blending_info_[0]); + make_blending(info_, blending_info_.data()); for (size_t i = 0; i < ec_info.size(); i++) { make_blending(ec_info[i], &blending_info_[1 + i]); } @@ -117,9 +117,9 @@ class BlendingStage : public RenderPipelineStage { Status IsInitialized() const override { return initialized_; } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { JXL_ASSERT(initialized_); const FrameOrigin& frame_origin = frame_header_.frame_origin; ssize_t bg_xpos = frame_origin.x0 + static_cast<ssize_t>(xpos); @@ -128,7 +128,8 @@ class BlendingStage : public RenderPipelineStage { if (bg_xpos + static_cast<ssize_t>(xsize) <= 0 || frame_origin.x0 >= static_cast<ssize_t>(image_xsize_) || bg_ypos < 0 || bg_ypos >= static_cast<ssize_t>(image_ysize_)) { - return; + // TODO(eustas): or fail? + return true; } if (bg_xpos < 0) { offset -= bg_xpos; @@ -160,9 +161,9 @@ class BlendingStage : public RenderPipelineStage { : zeroes_.data(); } } - PerformBlending(bg_row_ptrs_.data(), fg_row_ptrs_.data(), - fg_row_ptrs_.data(), 0, xsize, blending_info_[0], - blending_info_.data() + 1, *extra_channel_info_); + return PerformBlending(bg_row_ptrs_.data(), fg_row_ptrs_.data(), + fg_row_ptrs_.data(), 0, xsize, blending_info_[0], + blending_info_.data() + 1, *extra_channel_info_); } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc index 936fbd3a44..2bc88ada67 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc @@ -27,9 +27,9 @@ class HorizontalChromaUpsamplingStage : public RenderPipelineStage { /*shift=*/1, /*border=*/1)), c_(channel) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { HWY_FULL(float) df; xextra = RoundUpTo(xextra, Lanes(df)); auto threefour = Set(df, 0.75f); @@ -45,6 +45,7 @@ class HorizontalChromaUpsamplingStage : public RenderPipelineStage { auto right = MulAdd(onefour, next, current); StoreInterleaved(df, left, right, row_out + x * 2); } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { @@ -65,9 +66,9 @@ class VerticalChromaUpsamplingStage : public RenderPipelineStage { /*shift=*/1, /*border=*/1)), c_(channel) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { HWY_FULL(float) df; xextra = RoundUpTo(xextra, Lanes(df)); auto threefour = Set(df, 0.75f); @@ -86,6 +87,7 @@ class VerticalChromaUpsamplingStage : public RenderPipelineStage { Store(MulAdd(it, onefour, im_scaled), df, row_out0 + x); Store(MulAdd(ib, onefour, im_scaled), df, row_out1 + x); } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_cms.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_cms.cc index 2465146b47..3202a03e44 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_cms.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_cms.cc @@ -7,10 +7,8 @@ #include <memory> -#include "jxl/cms_interface.h" -#include "jxl/color_encoding.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/color_encoding_internal.h" -#include "lib/jxl/common.h" #include "lib/jxl/dec_xyb.h" #undef HWY_TARGET_INCLUDE @@ -19,7 +17,6 @@ #include <hwy/highway.h> #include "lib/jxl/dec_xyb-inl.h" -#include "lib/jxl/sanitizers.h" HWY_BEFORE_NAMESPACE(); namespace jxl { @@ -44,10 +41,10 @@ class CmsStage : public RenderPipelineStage { not_mixing_color_and_grey; } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { - JXL_ASSERT(xsize == xsize_); + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { + JXL_ASSERT(xsize <= xsize_); // TODO(firsching): handle grey case seperately // interleave float* JXL_RESTRICT row0 = GetInputRow(input_rows, 0, 0); @@ -62,16 +59,15 @@ class CmsStage : public RenderPipelineStage { } const float* buf_src = mutable_buf_src; float* JXL_RESTRICT buf_dst = color_space_transform->BufDst(thread_id); - if (!color_space_transform->Run(thread_id, buf_src, buf_dst)) { - // TODO(firsching): somehow mark failing here? - return; - } + JXL_RETURN_IF_ERROR( + color_space_transform->Run(thread_id, buf_src, buf_dst, xsize)); // de-interleave for (size_t x = 0; x < xsize; x++) { row0[x] = buf_dst[3 * x + 0]; row1[x] = buf_dst[3 * x + 1]; row2[x] = buf_dst[3 * x + 2]; } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { return c < 3 ? RenderPipelineChannelMode::kInPlace @@ -86,7 +82,7 @@ class CmsStage : public RenderPipelineStage { std::unique_ptr<jxl::ColorSpaceTransform> color_space_transform; ColorEncoding c_src_; - void SetInputSizes( + Status SetInputSizes( const std::vector<std::pair<size_t, size_t>>& input_sizes) override { #if JXL_ENABLE_ASSERT JXL_ASSERT(input_sizes.size() >= 3); @@ -96,6 +92,7 @@ class CmsStage : public RenderPipelineStage { } #endif xsize_ = input_sizes[0].first; + return true; } Status PrepareForThreads(size_t num_threads) override { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc index 5d1a379ede..d3030b02cb 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc @@ -8,7 +8,6 @@ #include "lib/jxl/base/common.h" #include "lib/jxl/common.h" // JXL_HIGH_PRECISION #include "lib/jxl/epf.h" -#include "lib/jxl/sanitizers.h" #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_epf.cc" @@ -67,9 +66,9 @@ class EPF0Stage : public RenderPipelineStage { *B = MulAdd(weight, cb, *B); } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { DF df; using V = decltype(Zero(df)); @@ -163,6 +162,7 @@ class EPF0Stage : public RenderPipelineStage { StoreU(Mul(Y, inv_w), df, GetOutputRow(output_rows, 1, 0) + x); StoreU(Mul(B, inv_w), df, GetOutputRow(output_rows, 2, 0) + x); } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { @@ -207,9 +207,9 @@ class EPF1Stage : public RenderPipelineStage { *B = MulAdd(weight, cb, *B); } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { DF df; xextra = RoundUpTo(xextra, Lanes(df)); const float* JXL_RESTRICT row_sigma = @@ -343,6 +343,7 @@ class EPF1Stage : public RenderPipelineStage { Store(Mul(Y, inv_w), df, GetOutputRow(output_rows, 1, 0) + x); Store(Mul(B, inv_w), df, GetOutputRow(output_rows, 2, 0) + x); } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { @@ -392,9 +393,9 @@ class EPF2Stage : public RenderPipelineStage { *B = MulAdd(weight, cb, *B); } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { DF df; xextra = RoundUpTo(xextra, Lanes(df)); const float* JXL_RESTRICT row_sigma = @@ -465,6 +466,7 @@ class EPF2Stage : public RenderPipelineStage { Store(Mul(Y, inv_w), df, GetOutputRow(output_rows, 1, 0) + x); Store(Mul(B, inv_w), df, GetOutputRow(output_rows, 2, 0) + x); } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc index 6b1f646cd5..922c7da366 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc @@ -68,7 +68,7 @@ struct OpPq { }; struct OpHlg { - explicit OpHlg(const float luminances[3], const float intensity_target) + explicit OpHlg(const Vector3& luminances, const float intensity_target) : hlg_ootf_(HlgOOTF::ToSceneLight(/*display_luminance=*/intensity_target, luminances)) {} @@ -105,9 +105,9 @@ class FromLinearStage : public RenderPipelineStage { : RenderPipelineStage(RenderPipelineStage::Settings()), op_(std::move(op)) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { const HWY_FULL(float) d; const size_t xsize_v = RoundUpTo(xsize, Lanes(d)); float* JXL_RESTRICT row0 = GetInputRow(input_rows, 0, 0); @@ -119,7 +119,8 @@ class FromLinearStage : public RenderPipelineStage { msan::UnpoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize)); msan::UnpoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize)); msan::UnpoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize)); - for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) { + for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra); + x += Lanes(d)) { auto r = LoadU(d, row0 + x); auto g = LoadU(d, row1 + x); auto b = LoadU(d, row2 + x); @@ -131,6 +132,7 @@ class FromLinearStage : public RenderPipelineStage { msan::PoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize)); msan::PoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize)); msan::PoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize)); + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc index 0917db3f9a..6fcd5e14df 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc @@ -44,9 +44,9 @@ class GaborishStage : public RenderPipelineStage { } } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { const HWY_FULL(float) d; for (size_t c = 0; c < 3; c++) { float* JXL_RESTRICT row_t = GetInputRow(input_rows, c, -1); @@ -66,7 +66,7 @@ class GaborishStage : public RenderPipelineStage { // Since GetInputRow(input_rows, c, {-1, 0, 1}) is aligned, rounding // xextra up to Lanes(d) doesn't access anything problematic. for (ssize_t x = -RoundUpTo(xextra, Lanes(d)); - x < (ssize_t)(xsize + xextra); x += Lanes(d)) { + x < static_cast<ssize_t>(xsize + xextra); x += Lanes(d)) { const auto t = LoadMaybeU(d, row_t + x); const auto tl = LoadU(d, row_t + x - 1); const auto tr = LoadU(d, row_t + x + 1); @@ -83,6 +83,7 @@ class GaborishStage : public RenderPipelineStage { Store(pixels, d, row_out + x); } } + return true; } #undef LoadMaybeU diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc index 5cf8a6ed51..eca679b948 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc @@ -5,6 +5,8 @@ #include "lib/jxl/render_pipeline/stage_noise.h" +#include "lib/jxl/noise.h" + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_noise.cc" #include <hwy/foreach_target.h> @@ -61,9 +63,10 @@ class StrengthEvalLut { #endif { #if HWY_TARGET != HWY_SCALAR - uint32_t lut[8]; - memcpy(lut, noise_params.lut, sizeof(lut)); - for (size_t i = 0; i < 8; i++) { + uint32_t lut[NoiseParams::kNumNoisePoints]; + memcpy(lut, noise_params.lut.data(), + NoiseParams::kNumNoisePoints * sizeof(uint32_t)); + for (size_t i = 0; i < NoiseParams::kNumNoisePoints; i++) { low16_lut[2 * i] = (lut[i] >> 0) & 0xFF; low16_lut[2 * i + 1] = (lut[i] >> 8) & 0xFF; high16_lut[2 * i] = (lut[i] >> 16) & 0xFF; @@ -161,10 +164,10 @@ class AddNoiseStage : public RenderPipelineStage { cmap_(cmap), first_c_(first_c) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { - if (!noise_params_.HasAny()) return; + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { + if (!noise_params_.HasAny()) return true; const StrengthEvalLut noise_model(noise_params_); D d; const auto half = Set(d, 0.5f); @@ -212,6 +215,7 @@ class AddNoiseStage : public RenderPipelineStage { msan::PoisonMemory(row_x + xsize, (xsize_v - xsize) * sizeof(float)); msan::PoisonMemory(row_y + xsize, (xsize_v - xsize) * sizeof(float)); msan::PoisonMemory(row_b + xsize, (xsize_v - xsize) * sizeof(float)); + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { @@ -241,9 +245,9 @@ class ConvolveNoiseStage : public RenderPipelineStage { /*shift=*/0, /*border=*/2)), first_c_(first_c) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { const HWY_FULL(float) d; for (size_t c = first_c_; c < first_c_ + 3; c++) { float* JXL_RESTRICT rows[5]; @@ -252,7 +256,7 @@ class ConvolveNoiseStage : public RenderPipelineStage { } float* JXL_RESTRICT row_out = GetOutputRow(output_rows, c, 0); for (ssize_t x = -RoundUpTo(xextra, Lanes(d)); - x < (ssize_t)(xsize + xextra); x += Lanes(d)) { + x < static_cast<ssize_t>(xsize + xextra); x += Lanes(d)) { const auto p00 = LoadU(d, rows[2] + x); auto others = Zero(d); // TODO(eustas): sum loaded values to reduce the calculation chain @@ -271,6 +275,7 @@ class ConvolveNoiseStage : public RenderPipelineStage { StoreU(pixels, d, row_out + x); } } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc index c5a75b09f7..e0a66167d4 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc @@ -14,16 +14,17 @@ class PatchDictionaryStage : public RenderPipelineStage { patches_(*patches), num_channels_(num_channels) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { JXL_ASSERT(xpos == 0 || xpos >= xextra); size_t x0 = xpos ? xpos - xextra : 0; std::vector<float*> row_ptrs(num_channels_); for (size_t i = 0; i < num_channels_; i++) { row_ptrs[i] = GetInputRow(input_rows, i, 0) + x0 - xpos; } - patches_.AddOneRow(row_ptrs.data(), ypos, x0, xsize + xextra + xpos - x0); + return patches_.AddOneRow(row_ptrs.data(), ypos, x0, + xsize + xextra + xpos - x0); } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc index 4a0529ce2c..92a13090a7 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc @@ -20,13 +20,14 @@ class SplineStage : public RenderPipelineStage { : RenderPipelineStage(RenderPipelineStage::Settings()), splines_(*splines) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { float* row_x = GetInputRow(input_rows, 0, 0); float* row_y = GetInputRow(input_rows, 1, 0); float* row_b = GetInputRow(input_rows, 2, 0); splines_.AddToRow(row_x, row_y, row_b, Rect(xpos, ypos, xsize, 1)); + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc index a43cb4e1ab..18588e2012 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc @@ -15,19 +15,20 @@ class SpotColorStage : public RenderPipelineStage { JXL_ASSERT(spot_c_ >= 3); } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { // TODO(veluca): add SIMD. float scale = spot_color_[3]; for (size_t c = 0; c < 3; c++) { float* JXL_RESTRICT p = GetInputRow(input_rows, c, 0); const float* JXL_RESTRICT s = GetInputRow(input_rows, spot_c_, 0); - for (ssize_t x = -xextra; x < ssize_t(xsize + xextra); x++) { + for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra); x++) { float mix = scale * s[x]; p[x] = mix * spot_color_[c] + (1.0f - mix) * p[x]; } } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc index 85eca2f039..c2c5ac484b 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc @@ -63,7 +63,7 @@ struct OpPq { }; struct OpHlg { - explicit OpHlg(const float luminances[3], const float intensity_target) + explicit OpHlg(const Vector3& luminances, const float intensity_target) : hlg_ootf_(HlgOOTF::FromSceneLight( /*display_luminance=*/intensity_target, luminances)) {} @@ -113,9 +113,9 @@ class ToLinearStage : public RenderPipelineStage { explicit ToLinearStage() : RenderPipelineStage(RenderPipelineStage::Settings()), valid_(false) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { const HWY_FULL(float) d; const size_t xsize_v = RoundUpTo(xsize, Lanes(d)); float* JXL_RESTRICT row0 = GetInputRow(input_rows, 0, 0); @@ -127,7 +127,8 @@ class ToLinearStage : public RenderPipelineStage { msan::UnpoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize)); msan::UnpoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize)); msan::UnpoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize)); - for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) { + for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra); + x += Lanes(d)) { auto r = LoadU(d, row0 + x); auto g = LoadU(d, row1 + x); auto b = LoadU(d, row2 + x); @@ -139,6 +140,7 @@ class ToLinearStage : public RenderPipelineStage { msan::PoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize)); msan::PoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize)); msan::PoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize)); + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc index 2a272e15dc..e8cd90b244 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc @@ -56,10 +56,10 @@ class ToneMappingStage : public RenderPipelineStage { bool IsNeeded() const { return tone_mapper_ || hlg_ootf_; } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { - if (!(tone_mapper_ || hlg_ootf_)) return; + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { + if (!(tone_mapper_ || hlg_ootf_)) return true; const HWY_FULL(float) d; const size_t xsize_v = RoundUpTo(xsize, Lanes(d)); @@ -72,7 +72,8 @@ class ToneMappingStage : public RenderPipelineStage { msan::UnpoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize)); msan::UnpoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize)); msan::UnpoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize)); - for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) { + for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra); + x += Lanes(d)) { auto r = LoadU(d, row0 + x); auto g = LoadU(d, row1 + x); auto b = LoadU(d, row2 + x); @@ -100,6 +101,7 @@ class ToneMappingStage : public RenderPipelineStage { msan::PoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize)); msan::PoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize)); msan::PoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize)); + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc index ade37d59a6..897b20c4c6 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc @@ -46,9 +46,9 @@ class UpsamplingStage : public RenderPipelineStage { } } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { static HWY_FULL(float) df; size_t shift = settings_.shift_x; size_t N = 1 << shift; @@ -74,6 +74,7 @@ class UpsamplingStage : public RenderPipelineStage { msan::PoisonMemory(dst_row + xsize * N, sizeof(float) * (xsize_v - xsize) * N); } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc index 847972acc8..c5a91e8efd 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc @@ -9,7 +9,9 @@ #include "lib/jxl/alpha.h" #include "lib/jxl/base/common.h" +#include "lib/jxl/base/status.h" #include "lib/jxl/dec_cache.h" +#include "lib/jxl/image.h" #include "lib/jxl/image_bundle.h" #include "lib/jxl/sanitizers.h" @@ -150,13 +152,13 @@ class WriteToOutputStage : public RenderPipelineStage { } } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { JXL_DASSERT(xextra == 0); JXL_DASSERT(main_.run_opaque_ || main_.buffer_); - if (ypos >= height_) return; - if (xpos >= width_) return; + if (ypos >= height_) return true; + if (xpos >= width_) return true; if (flip_y_) { ypos = height_ - 1u - ypos; } @@ -184,6 +186,7 @@ class WriteToOutputStage : public RenderPipelineStage { OutputBuffers(extra, thread_id, ypos, xstart, len, line_buffers); } } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { @@ -202,7 +205,7 @@ class WriteToOutputStage : public RenderPipelineStage { private: struct Output { - Output(const ImageOutput& image_out) + explicit Output(const ImageOutput& image_out) : pixel_callback_(image_out.callback), buffer_(image_out.buffer), buffer_size_(image_out.buffer_size), @@ -406,8 +409,8 @@ class WriteToOutputStage : public RenderPipelineStage { sizeof(output[0]) * out.num_channels_ * padding); } - void StoreFloat16Row(const Output& out, const float* input[4], size_t len, - uint16_t* output) const { + static void StoreFloat16Row(const Output& out, const float* input[4], + size_t len, uint16_t* output) { const HWY_FULL(float) d; const Rebind<uint16_t, decltype(d)> du; const Rebind<hwy::float16_t, decltype(d)> df16; @@ -452,8 +455,8 @@ class WriteToOutputStage : public RenderPipelineStage { sizeof(output[0]) * out.num_channels_ * padding); } - void StoreFloatRow(const Output& out, const float* input[4], size_t len, - float* output) const { + static void StoreFloatRow(const Output& out, const float* input[4], + size_t len, float* output) { const HWY_FULL(float) d; if (out.num_channels_ == 1) { memcpy(output, input[0], len * sizeof(output[0])); @@ -559,7 +562,7 @@ class WriteToImageBundleStage : public RenderPipelineStage { image_bundle_(image_bundle), color_encoding_(std::move(color_encoding)) {} - void SetInputSizes( + Status SetInputSizes( const std::vector<std::pair<size_t, size_t>>& input_sizes) override { #if JXL_ENABLE_ASSERT JXL_ASSERT(input_sizes.size() >= 3); @@ -569,19 +572,22 @@ class WriteToImageBundleStage : public RenderPipelineStage { } #endif // TODO(eustas): what should we do in the case of "want only ECs"? - image_bundle_->SetFromImage( - Image3F(input_sizes[0].first, input_sizes[0].second), color_encoding_); + JXL_ASSIGN_OR_RETURN(Image3F tmp, Image3F::Create(input_sizes[0].first, + input_sizes[0].second)); + image_bundle_->SetFromImage(std::move(tmp), color_encoding_); // TODO(veluca): consider not reallocating ECs if not needed. image_bundle_->extra_channels().clear(); for (size_t c = 3; c < input_sizes.size(); c++) { - image_bundle_->extra_channels().emplace_back(input_sizes[c].first, - input_sizes[c].second); + JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(input_sizes[c].first, + input_sizes[c].second)); + image_bundle_->extra_channels().emplace_back(std::move(ch)); } + return true; } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { for (size_t c = 0; c < 3; c++) { memcpy(image_bundle_->color()->PlaneRow(c, ypos) + xpos - xextra, GetInputRow(input_rows, c, 0) - xextra, @@ -594,6 +600,7 @@ class WriteToImageBundleStage : public RenderPipelineStage { GetInputRow(input_rows, 3 + ec, 0) - xextra, sizeof(float) * (xsize + 2 * xextra)); } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { @@ -612,7 +619,7 @@ class WriteToImage3FStage : public RenderPipelineStage { explicit WriteToImage3FStage(Image3F* image) : RenderPipelineStage(RenderPipelineStage::Settings()), image_(image) {} - void SetInputSizes( + Status SetInputSizes( const std::vector<std::pair<size_t, size_t>>& input_sizes) override { #if JXL_ENABLE_ASSERT JXL_ASSERT(input_sizes.size() >= 3); @@ -621,17 +628,20 @@ class WriteToImage3FStage : public RenderPipelineStage { JXL_ASSERT(input_sizes[c].second == input_sizes[0].second); } #endif - *image_ = Image3F(input_sizes[0].first, input_sizes[0].second); + JXL_ASSIGN_OR_RETURN( + *image_, Image3F::Create(input_sizes[0].first, input_sizes[0].second)); + return true; } - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { for (size_t c = 0; c < 3; c++) { memcpy(image_->PlaneRow(c, ypos) + xpos - xextra, GetInputRow(input_rows, c, 0) - xextra, sizeof(float) * (xsize + 2 * xextra)); } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc index 56e86e6095..a20e686de6 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc @@ -28,9 +28,9 @@ class XYBStage : public RenderPipelineStage { output_is_xyb_(output_encoding_info.color_encoding.GetColorSpace() == ColorSpace::kXYB) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { const HWY_FULL(float) d; JXL_ASSERT(xextra == 0); const size_t xsize_v = RoundUpTo(xsize, Lanes(d)); @@ -52,7 +52,8 @@ class XYBStage : public RenderPipelineStage { const auto offset_x = Set(d, jxl::cms::kScaledXYBOffset[0]); const auto offset_y = Set(d, jxl::cms::kScaledXYBOffset[1]); const auto offset_bmy = Set(d, jxl::cms::kScaledXYBOffset[2]); - for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) { + for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra); + x += Lanes(d)) { const auto in_x = LoadU(d, row0 + x); const auto in_y = LoadU(d, row1 + x); const auto in_b = LoadU(d, row2 + x); @@ -64,7 +65,8 @@ class XYBStage : public RenderPipelineStage { StoreU(out_b, d, row2 + x); } } else { - for (ssize_t x = -xextra; x < (ssize_t)(xsize + xextra); x += Lanes(d)) { + for (ssize_t x = -xextra; x < static_cast<ssize_t>(xsize + xextra); + x += Lanes(d)) { const auto in_opsin_x = LoadU(d, row0 + x); const auto in_opsin_y = LoadU(d, row1 + x); const auto in_opsin_b = LoadU(d, row2 + x); @@ -81,6 +83,7 @@ class XYBStage : public RenderPipelineStage { msan::PoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize)); msan::PoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize)); msan::PoisonMemory(row2 + xsize, sizeof(float) * (xsize_v - xsize)); + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { @@ -130,10 +133,10 @@ class FastXYBStage : public RenderPipelineStage { has_alpha_(has_alpha), alpha_c_(alpha_c) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { - if (ypos >= height_) return; + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { + if (ypos >= height_) return true; JXL_ASSERT(xextra == 0); const float* xyba[4] = { GetInputRow(input_rows, 0, 0), GetInputRow(input_rows, 1, 0), @@ -142,6 +145,7 @@ class FastXYBStage : public RenderPipelineStage { uint8_t* out_buf = rgb_ + stride_ * ypos + (rgba_ ? 4 : 3) * xpos; FastXYBTosRGB8(xyba, out_buf, rgba_, xsize + xpos <= width_ ? xsize : width_ - xpos); + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc index 30ad327221..f21a00c728 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc @@ -22,9 +22,9 @@ class kYCbCrStage : public RenderPipelineStage { public: kYCbCrStage() : RenderPipelineStage(RenderPipelineStage::Settings()) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { const HWY_FULL(float) df; // Full-range BT.601 as defined by JFIF Clause 7: @@ -51,6 +51,7 @@ class kYCbCrStage : public RenderPipelineStage { StoreU(g_vec, df, row1 + x); StoreU(b_vec, df, row2 + x); } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/render_pipeline/test_render_pipeline_stages.h b/third_party/jpeg-xl/lib/jxl/render_pipeline/test_render_pipeline_stages.h index 789a52f8b2..c2c25c46c3 100644 --- a/third_party/jpeg-xl/lib/jxl/render_pipeline/test_render_pipeline_stages.h +++ b/third_party/jpeg-xl/lib/jxl/render_pipeline/test_render_pipeline_stages.h @@ -7,10 +7,7 @@ #include <stdint.h> #include <stdio.h> -#include <algorithm> -#include <utility> -#include <vector> - +#include "lib/jxl/base/status.h" #include "lib/jxl/render_pipeline/render_pipeline_stage.h" namespace jxl { @@ -20,13 +17,13 @@ class UpsampleXSlowStage : public RenderPipelineStage { UpsampleXSlowStage() : RenderPipelineStage(RenderPipelineStage::Settings::ShiftX(1, 1)) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { for (size_t c = 0; c < input_rows.size(); c++) { const float* row = GetInputRow(input_rows, c, 0); float* row_out = GetOutputRow(output_rows, c, 0); - for (int64_t x = -xextra; x < (int64_t)(xsize + xextra); x++) { + for (int64_t x = -xextra; x < static_cast<int64_t>(xsize + xextra); x++) { float xp = *(row + x - 1); float xc = *(row + x); float xn = *(row + x + 1); @@ -36,6 +33,7 @@ class UpsampleXSlowStage : public RenderPipelineStage { *(row_out + 2 * x + 1) = xout1; } } + return true; } const char* GetName() const override { return "TEST::UpsampleXSlowStage"; } @@ -50,16 +48,16 @@ class UpsampleYSlowStage : public RenderPipelineStage { UpsampleYSlowStage() : RenderPipelineStage(RenderPipelineStage::Settings::ShiftY(1, 1)) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { for (size_t c = 0; c < input_rows.size(); c++) { const float* rowp = GetInputRow(input_rows, c, -1); const float* rowc = GetInputRow(input_rows, c, 0); const float* rown = GetInputRow(input_rows, c, 1); float* row_out0 = GetOutputRow(output_rows, c, 0); float* row_out1 = GetOutputRow(output_rows, c, 1); - for (int64_t x = -xextra; x < (int64_t)(xsize + xextra); x++) { + for (int64_t x = -xextra; x < static_cast<int64_t>(xsize + xextra); x++) { float xp = *(rowp + x); float xc = *(rowc + x); float xn = *(rown + x); @@ -69,6 +67,7 @@ class UpsampleYSlowStage : public RenderPipelineStage { *(row_out1 + x) = yout1; } } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { @@ -82,14 +81,15 @@ class Check0FinalStage : public RenderPipelineStage { public: Check0FinalStage() : RenderPipelineStage(RenderPipelineStage::Settings()) {} - void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, - size_t xextra, size_t xsize, size_t xpos, size_t ypos, - size_t thread_id) const final { + Status ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows, + size_t xextra, size_t xsize, size_t xpos, size_t ypos, + size_t thread_id) const final { for (size_t c = 0; c < input_rows.size(); c++) { for (size_t x = 0; x < xsize; x++) { JXL_CHECK(fabsf(GetInputRow(input_rows, c, 0)[x]) < 1e-8); } } + return true; } RenderPipelineChannelMode GetChannelMode(size_t c) const final { diff --git a/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc b/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc index c00fda0de1..a4a87bebb7 100644 --- a/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc +++ b/third_party/jpeg-xl/lib/jxl/roundtrip_test.cc @@ -26,7 +26,6 @@ #include "lib/jxl/butteraugli/butteraugli.h" #include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/dec_bit_reader.h" -#include "lib/jxl/enc_butteraugli_comparator.h" #include "lib/jxl/enc_external_image.h" #include "lib/jxl/encode_internal.h" #include "lib/jxl/image.h" @@ -37,6 +36,9 @@ namespace { +using jxl::ImageF; +using jxl::test::ButteraugliDistance; + // Converts a test image to a CodecInOut. // icc_profile can be empty to automatically deduce profile from the pixel // format, or filled in to force this ICC profile @@ -94,7 +96,7 @@ jxl::CodecInOut ConvertTestImage(const std::vector<uint8_t>& buf, jxl::ColorEncoding color_encoding; if (!icc_profile.empty()) { jxl::IccBytes icc_profile_copy; - icc_profile.AppendTo(&icc_profile_copy); + icc_profile.AppendTo(icc_profile_copy); EXPECT_TRUE( color_encoding.SetICC(std::move(icc_profile_copy), JxlGetDefaultCms())); } else if (pixel_format.data_type == JXL_TYPE_FLOAT) { @@ -118,12 +120,12 @@ float ConvertTestPixel<float>(const float val) { template <> uint16_t ConvertTestPixel<uint16_t>(const float val) { - return (uint16_t)(val * UINT16_MAX); + return static_cast<uint16_t>(val * UINT16_MAX); } template <> uint8_t ConvertTestPixel<uint8_t>(const float val) { - return (uint8_t)(val * UINT8_MAX); + return static_cast<uint8_t>(val * UINT8_MAX); } // Returns a test image. @@ -146,6 +148,7 @@ std::vector<uint8_t> GetTestImage(const size_t xsize, const size_t ysize, val = static_cast<float>(x + y) / static_cast<float>(xsize + ysize); break; case 3: + default: val = static_cast<float>(x * y) / static_cast<float>(xsize * ysize); break; } @@ -233,7 +236,7 @@ void VerifyRoundtripCompression( } } if (alpha_in_extra_channels_vector && !has_interleaved_alpha) { - jxl::ImageF alpha_channel(xsize, ysize); + JXL_ASSIGN_OR_DIE(ImageF alpha_channel, ImageF::Create(xsize, ysize)); EXPECT_TRUE(jxl::ConvertFromExternal( extra_channel_bytes.data(), extra_channel_bytes.size(), xsize, ysize, basic_info.bits_per_sample, extra_channel_pixel_format, 0, @@ -298,17 +301,18 @@ void VerifyRoundtripCompression( frame_settings, JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED, already_downsampled)); } - EXPECT_EQ(JXL_ENC_SUCCESS, - JxlEncoderAddImageFrame(frame_settings, &input_pixel_format, - (void*)original_bytes.data(), - original_bytes.size())); + EXPECT_EQ( + JXL_ENC_SUCCESS, + JxlEncoderAddImageFrame(frame_settings, &input_pixel_format, + static_cast<const void*>(original_bytes.data()), + original_bytes.size())); EXPECT_EQ(frame_settings->enc->input_queue.empty(), false); for (size_t index = 0; index < channel_infos.size(); index++) { EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetExtraChannelBuffer( frame_settings, &extra_channel_pixel_format, - (void*)extra_channel_bytes.data(), extra_channel_bytes.size(), - index + has_interleaved_alpha)); + static_cast<const void*>(extra_channel_bytes.data()), + extra_channel_bytes.size(), index + has_interleaved_alpha)); } JxlEncoderCloseInput(enc); std::vector<uint8_t> compressed; @@ -412,10 +416,10 @@ void VerifyRoundtripCompression( if (already_downsampled) { jxl::Image3F* color = decoded_io.Main().color(); - jxl::DownsampleImage(color, resampling); + JXL_ASSIGN_OR_DIE(*color, jxl::DownsampleImage(*color, resampling)); if (decoded_io.Main().HasAlpha()) { - jxl::ImageF* alpha = decoded_io.Main().alpha(); - jxl::DownsampleImage(alpha, resampling); + ImageF* alpha = decoded_io.Main().alpha(); + JXL_ASSIGN_OR_DIE(*alpha, jxl::DownsampleImage(*alpha, resampling)); } decoded_io.SetSize(color->xsize(), color->ysize()); } @@ -463,8 +467,8 @@ TEST(RoundtripTest, FloatFrameRoundtripTest) { {JXL_CHANNEL_CFA, "my cfa channel"}, {JXL_CHANNEL_OPTIONAL, "optional channel"}}, {{JXL_CHANNEL_DEPTH, "very deep"}}}; - for (int use_container = 0; use_container < 2; use_container++) { - for (int lossless = 0; lossless < 2; lossless++) { + for (bool use_container : {false, true}) { + for (bool lossless : {false, true}) { for (uint32_t num_channels = 1; num_channels < 5; num_channels++) { for (auto& extra_channels : extra_channels_cases) { uint32_t has_alpha = static_cast<uint32_t>(num_channels % 2 == 0); @@ -475,8 +479,8 @@ TEST(RoundtripTest, FloatFrameRoundtripTest) { JxlPixelFormat pixel_format = JxlPixelFormat{ num_channels, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}; VerifyRoundtripCompression<float>( - 63, 129, pixel_format, pixel_format, (bool)lossless, - (bool)use_container, 1, false, extra_channels); + 63, 129, pixel_format, pixel_format, lossless, use_container, 1, + false, extra_channels); } } } @@ -499,8 +503,8 @@ TEST(RoundtripTest, Uint16FrameRoundtripTest) { JxlPixelFormat pixel_format = JxlPixelFormat{ num_channels, JXL_TYPE_UINT16, JXL_NATIVE_ENDIAN, 0}; VerifyRoundtripCompression<uint16_t>( - 63, 129, pixel_format, pixel_format, (bool)lossless, - (bool)use_container, 1, false, extra_channels); + 63, 129, pixel_format, pixel_format, static_cast<bool>(lossless), + static_cast<bool>(use_container), 1, false, extra_channels); } } } @@ -523,8 +527,8 @@ TEST(RoundtripTest, Uint8FrameRoundtripTest) { JxlPixelFormat pixel_format = JxlPixelFormat{ num_channels, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0}; VerifyRoundtripCompression<uint8_t>( - 63, 129, pixel_format, pixel_format, (bool)lossless, - (bool)use_container, 1, false, extra_channels); + 63, 129, pixel_format, pixel_format, static_cast<bool>(lossless), + static_cast<bool>(use_container), 1, false, extra_channels); } } } @@ -540,7 +544,7 @@ TEST(RoundtripTest, TestNonlinearSrgbAsXybEncoded) { JxlPixelFormat{num_channels, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0}; VerifyRoundtripCompression<uint8_t>( 63, 129, pixel_format_in, pixel_format_out, - /*lossless=*/false, (bool)use_container, 1, false, {}); + /*lossless=*/false, static_cast<bool>(use_container), 1, false, {}); } } } @@ -586,25 +590,25 @@ TEST(RoundtripTest, ExtraBoxesTest) { jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format); basic_info.xsize = xsize; basic_info.ysize = ysize; - basic_info.uses_original_profile = false; + basic_info.uses_original_profile = JXL_FALSE; EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc, 10)); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info)); JxlColorEncoding color_encoding; + JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3); if (pixel_format.data_type == JXL_TYPE_FLOAT) { - JxlColorEncodingSetToLinearSRGB(&color_encoding, - /*is_gray=*/pixel_format.num_channels < 3); + JxlColorEncodingSetToLinearSRGB(&color_encoding, is_gray); } else { - JxlColorEncodingSetToSRGB(&color_encoding, - /*is_gray=*/pixel_format.num_channels < 3); + JxlColorEncodingSetToSRGB(&color_encoding, is_gray); } EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc, &color_encoding)); JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate(enc, nullptr); - JxlEncoderSetFrameLossless(frame_settings, false); - EXPECT_EQ(JXL_ENC_SUCCESS, - JxlEncoderAddImageFrame(frame_settings, &pixel_format, - (void*)original_bytes.data(), - original_bytes.size())); + JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE); + EXPECT_EQ( + JXL_ENC_SUCCESS, + JxlEncoderAddImageFrame(frame_settings, &pixel_format, + static_cast<const void*>(original_bytes.data()), + original_bytes.size())); JxlEncoderCloseInput(enc); std::vector<uint8_t> compressed; @@ -703,19 +707,17 @@ TEST(RoundtripTest, MultiFrameTest) { EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info)); JxlColorEncoding color_encoding; + JXL_BOOL is_gray = TO_JXL_BOOL(pixel_format.num_channels < 3); if (pixel_format.data_type == JXL_TYPE_FLOAT) { - JxlColorEncodingSetToLinearSRGB( - &color_encoding, - /*is_gray=*/pixel_format.num_channels < 3); + JxlColorEncodingSetToLinearSRGB(&color_encoding, is_gray); } else { - JxlColorEncodingSetToSRGB(&color_encoding, - /*is_gray=*/pixel_format.num_channels < 3); + JxlColorEncodingSetToSRGB(&color_encoding, is_gray); } EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetColorEncoding(enc, &color_encoding)); JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate(enc, nullptr); - JxlEncoderSetFrameLossless(frame_settings, false); + JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE); if (index_frames == 1) { EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderFrameSettingsSetOption(frame_settings, @@ -733,9 +735,10 @@ TEST(RoundtripTest, MultiFrameTest) { JxlEncoderSetFrameHeader(frame_settings, &frame_header); EXPECT_EQ( JXL_ENC_SUCCESS, - JxlEncoderAddImageFrame( - frame_settings, &pixel_format, - (void*)(original_bytes.data() + oneframesize * i), oneframesize)); + JxlEncoderAddImageFrame(frame_settings, &pixel_format, + static_cast<const void*>( + original_bytes.data() + oneframesize * i), + oneframesize)); } JxlEncoderCloseInput(enc); @@ -877,10 +880,11 @@ TEST(RoundtripTest, TestICCProfile) { JxlEncoderSetICCProfile(enc, icc.data(), icc.size())); JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate(enc, nullptr); - EXPECT_EQ(JXL_ENC_SUCCESS, - JxlEncoderAddImageFrame(frame_settings, &format, - (void*)original_bytes.data(), - original_bytes.size())); + EXPECT_EQ( + JXL_ENC_SUCCESS, + JxlEncoderAddImageFrame(frame_settings, &format, + static_cast<const void*>(original_bytes.data()), + original_bytes.size())); JxlEncoderCloseInput(enc); std::vector<uint8_t> compressed; @@ -946,7 +950,7 @@ TEST(RoundtripTest, JXL_TRANSCODE_JPEG_TEST(TestJPEGReconstruction)) { JxlEncoderPtr enc = JxlEncoderMake(nullptr); JxlEncoderFrameSettings* frame_settings = - JxlEncoderFrameSettingsCreate(enc.get(), NULL); + JxlEncoderFrameSettingsCreate(enc.get(), nullptr); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderUseContainer(enc.get(), JXL_TRUE)); EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderStoreJPEGMetadata(enc.get(), JXL_TRUE)); diff --git a/third_party/jpeg-xl/lib/jxl/sanitizers.h b/third_party/jpeg-xl/lib/jxl/sanitizers.h index adeaea67ed..bb133e9203 100644 --- a/third_party/jpeg-xl/lib/jxl/sanitizers.h +++ b/third_party/jpeg-xl/lib/jxl/sanitizers.h @@ -43,28 +43,18 @@ static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const volatile void* m, __msan_unpoison(m, size); } -static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonCStr(const char* c) { - do { - UnpoisonMemory(c, 1); - } while (*c++); -} - static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized( const volatile void* m, size_t size) { __msan_check_mem_is_initialized(m, size); } // Mark all the bytes of an image (including padding) as poisoned bytes. -static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const PlaneBase& im) { +template <typename T> +static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Plane<T>& im) { PoisonMemory(im.bytes(), im.bytes_per_row() * im.ysize()); } -template <typename T> -static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Image3<T>& im) { - PoisonImage(im.Plane(0)); - PoisonImage(im.Plane(1)); - PoisonImage(im.Plane(2)); -} +namespace { // Print the uninitialized regions of an image. template <typename T> @@ -210,6 +200,8 @@ static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized( } } +} // namespace + #define JXL_CHECK_IMAGE_INITIALIZED(im, r) \ ::jxl::msan::CheckImageInitialized(im, r, "im=" #im ", r=" #r); @@ -221,13 +213,13 @@ static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized( // In non-msan mode these functions don't use volatile since it is not needed // for the empty functions. -static JXL_INLINE JXL_MAYBE_UNUSED void PoisonMemory(const void*, size_t) {} -static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const void*, size_t) {} -static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonCStr(const char*) {} -static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized(const void*, - size_t) {} +static JXL_INLINE JXL_MAYBE_UNUSED void PoisonMemory(const void* m, + size_t size) {} +static JXL_INLINE JXL_MAYBE_UNUSED void UnpoisonMemory(const void* m, + size_t size) {} +static JXL_INLINE JXL_MAYBE_UNUSED void MemoryIsInitialized(const void* m, + size_t size) {} -static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const PlaneBase& im) {} template <typename T> static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Plane<T>& im) {} diff --git a/third_party/jpeg-xl/lib/jxl/simd_util.cc b/third_party/jpeg-xl/lib/jxl/simd_util.cc index a3971ff900..6515daaa2b 100644 --- a/third_party/jpeg-xl/lib/jxl/simd_util.cc +++ b/third_party/jpeg-xl/lib/jxl/simd_util.cc @@ -5,11 +5,18 @@ #include "lib/jxl/simd_util.h" +#include <algorithm> +#include <cstddef> + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/simd_util.cc" #include <hwy/foreach_target.h> #include <hwy/highway.h> +#include "lib/jxl/base/common.h" +#include "lib/jxl/base/status.h" +#include "lib/jxl/cache_aligned.h" + HWY_BEFORE_NAMESPACE(); namespace jxl { namespace HWY_NAMESPACE { @@ -36,5 +43,37 @@ size_t MaxVectorSize() { return HWY_DYNAMIC_DISPATCH(MaxVectorSize)(); } +size_t BytesPerRow(const size_t xsize, const size_t sizeof_t) { + // Special case: we don't allow any ops -> don't need extra padding/ + if (xsize == 0) { + return 0; + } + + const size_t vec_size = MaxVectorSize(); + size_t valid_bytes = xsize * sizeof_t; + + // Allow unaligned accesses starting at the last valid value. + // Skip for the scalar case because no extra lanes will be loaded. + if (vec_size != 0) { + valid_bytes += vec_size - sizeof_t; + } + + // Round up to vector and cache line size. + const size_t align = std::max(vec_size, CacheAligned::kAlignment); + size_t bytes_per_row = RoundUpTo(valid_bytes, align); + + // During the lengthy window before writes are committed to memory, CPUs + // guard against read after write hazards by checking the address, but + // only the lower 11 bits. We avoid a false dependency between writes to + // consecutive rows by ensuring their sizes are not multiples of 2 KiB. + // Avoid2K prevents the same problem for the planes of an Image3. + if (bytes_per_row % CacheAligned::kAlias == 0) { + bytes_per_row += align; + } + + JXL_ASSERT(bytes_per_row % align == 0); + return bytes_per_row; +} + } // namespace jxl #endif diff --git a/third_party/jpeg-xl/lib/jxl/simd_util.h b/third_party/jpeg-xl/lib/jxl/simd_util.h index 84938a931a..62b68c989c 100644 --- a/third_party/jpeg-xl/lib/jxl/simd_util.h +++ b/third_party/jpeg-xl/lib/jxl/simd_util.h @@ -12,6 +12,10 @@ namespace jxl { // Maximal vector size in bytes. size_t MaxVectorSize(); +// Returns distance [bytes] between the start of two consecutive rows, a +// multiple of vector/cache line size but NOT CacheAligned::kAlias - see below. +size_t BytesPerRow(size_t xsize, size_t sizeof_t); + } // namespace jxl #endif // LIB_JXL_SIMD_UTIL_H_ diff --git a/third_party/jpeg-xl/lib/jxl/speed_tier_test.cc b/third_party/jpeg-xl/lib/jxl/speed_tier_test.cc index 7874bdc158..381367b54d 100644 --- a/third_party/jpeg-xl/lib/jxl/speed_tier_test.cc +++ b/third_party/jpeg-xl/lib/jxl/speed_tier_test.cc @@ -105,7 +105,7 @@ TEST_P(SpeedTierTest, Roundtrip) { { extras::PackedPixelFile ppf_out; test::Roundtrip(t.ppf(), cparams, dparams, nullptr, &ppf_out); - EXPECT_LE(test::ButteraugliDistance(t.ppf(), ppf_out), 1.6); + EXPECT_LE(test::ButteraugliDistance(t.ppf(), ppf_out), 2.0); } if (params.shrink8) { cparams.distance = 0.0f; diff --git a/third_party/jpeg-xl/lib/jxl/splines.cc b/third_party/jpeg-xl/lib/jxl/splines.cc index acbaf38428..1d4fc69e3e 100644 --- a/third_party/jpeg-xl/lib/jxl/splines.cc +++ b/third_party/jpeg-xl/lib/jxl/splines.cc @@ -40,7 +40,7 @@ using hwy::HWY_NAMESPACE::Sub; // Given a set of DCT coefficients, this returns the result of performing cosine // interpolation on the original samples. -float ContinuousIDCT(const float dct[32], const float t) { +float ContinuousIDCT(const Dct32& dct, const float t) { // We compute here the DCT-3 of the `dct` vector, rescaled by a factor of // sqrt(32). This is such that an input vector vector {x, 0, ..., 0} produces // a constant result of x. dct[0] was scaled in Dequantize() to allow uniform @@ -60,7 +60,7 @@ float ContinuousIDCT(const float dct[32], const float t) { for (int i = 0; i < 32; i += Lanes(df)) { auto cos_arg = Mul(LoadU(df, kMultipliers + i), tandhalf); auto cos = FastCosf(df, cos_arg); - auto local_res = Mul(LoadU(df, dct + i), cos); + auto local_res = Mul(LoadU(df, dct.data() + i), cos); result = MulAdd(Set(df, kSqrt2), local_res, result); } return GetLane(SumOfLanes(df, result)); @@ -140,8 +140,9 @@ void ComputeSegments(const Spline::Point& center, const float intensity, segment.inv_sigma = 1.0f / sigma; segment.sigma_over_4_times_intensity = .25f * sigma * intensity; segment.maximum_distance = maximum_distance; - ssize_t y0 = center.y - maximum_distance + .5f; - ssize_t y1 = center.y + maximum_distance + 1.5f; // one-past-the-end + ssize_t y0 = std::llround(center.y - maximum_distance); + ssize_t y1 = + std::llround(center.y + maximum_distance) + 1; // one-past-the-end for (ssize_t y = std::max<ssize_t>(y0, 0); y < y1; y++) { segments_by_y.emplace_back(y, segments.size()); } @@ -227,7 +228,7 @@ float InvAdjustedQuant(const int32_t adjustment) { } // X, Y, B, sigma. -static constexpr float kChannelWeight[] = {0.0042f, 0.075f, 0.07f, .3333f}; +constexpr float kChannelWeight[] = {0.0042f, 0.075f, 0.07f, .3333f}; Status DecodeAllStartingPoints(std::vector<Spline::Point>* const points, BitReader* const br, ANSSymbolReader* reader, @@ -366,7 +367,8 @@ QuantizedSpline::QuantizedSpline(const Spline& original, const Spline::Point& starting_point = original.control_points.front(); int previous_x = static_cast<int>(std::roundf(starting_point.x)); int previous_y = static_cast<int>(std::roundf(starting_point.y)); - int previous_delta_x = 0, previous_delta_y = 0; + int previous_delta_x = 0; + int previous_delta_y = 0; for (auto it = original.control_points.begin() + 1; it != original.control_points.end(); ++it) { const int new_x = static_cast<int>(std::roundf(it->x)); @@ -426,9 +428,10 @@ Status QuantizedSpline::Dequantize(const Spline::Point& starting_point, JXL_RETURN_IF_ERROR(ValidateSplinePointPos(px, py)); int current_x = static_cast<int>(px); int current_y = static_cast<int>(py); - result.control_points.push_back(Spline::Point{static_cast<float>(current_x), - static_cast<float>(current_y)}); - int current_delta_x = 0, current_delta_y = 0; + result.control_points.emplace_back(static_cast<float>(current_x), + static_cast<float>(current_y)); + int current_delta_x = 0; + int current_delta_y = 0; uint64_t manhattan_distance = 0; for (const auto& point : control_points_) { current_delta_x += point.first; @@ -443,8 +446,8 @@ Status QuantizedSpline::Dequantize(const Spline::Point& starting_point, current_x += current_delta_x; current_y += current_delta_y; JXL_RETURN_IF_ERROR(ValidateSplinePointPos(current_x, current_y)); - result.control_points.push_back(Spline::Point{ - static_cast<float>(current_x), static_cast<float>(current_y)}); + result.control_points.emplace_back(static_cast<float>(current_x), + static_cast<float>(current_y)); } const auto inv_quant = InvAdjustedQuant(quantization_adjustment); @@ -605,15 +608,15 @@ Status Splines::Decode(jxl::BitReader* br, const size_t num_pixels) { void Splines::AddTo(Image3F* const opsin, const Rect& opsin_rect, const Rect& image_rect) const { - return Apply</*add=*/true>(opsin, opsin_rect, image_rect); + Apply</*add=*/true>(opsin, opsin_rect, image_rect); } void Splines::AddToRow(float* JXL_RESTRICT row_x, float* JXL_RESTRICT row_y, float* JXL_RESTRICT row_b, const Rect& image_row) const { - return ApplyToRow</*add=*/true>(row_x, row_y, row_b, image_row); + ApplyToRow</*add=*/true>(row_x, row_y, row_b, image_row); } void Splines::SubtractFrom(Image3F* const opsin) const { - return Apply</*add=*/false>(opsin, Rect(*opsin), Rect(*opsin)); + Apply</*add=*/false>(opsin, Rect(*opsin), Rect(*opsin)); } Status Splines::InitializeDrawCache(const size_t image_xsize, @@ -646,8 +649,9 @@ Status Splines::InitializeDrawCache(const size_t image_xsize, } // TODO(firsching) Change this into a JXL_FAILURE for level 5 codestreams. if (total_estimated_area_reached > - std::min((8 * image_xsize * image_ysize + (uint64_t(1) << 25)), - (uint64_t(1) << 30))) { + std::min( + (8 * image_xsize * image_ysize + (static_cast<uint64_t>(1) << 25)), + (static_cast<uint64_t>(1) << 30))) { JXL_WARNING( "Large total_estimated_area_reached, expect slower decoding: %" PRIu64, total_estimated_area_reached); diff --git a/third_party/jpeg-xl/lib/jxl/splines.h b/third_party/jpeg-xl/lib/jxl/splines.h index acdd0857d0..af51ec937e 100644 --- a/third_party/jpeg-xl/lib/jxl/splines.h +++ b/third_party/jpeg-xl/lib/jxl/splines.h @@ -6,6 +6,7 @@ #ifndef LIB_JXL_SPLINES_H_ #define LIB_JXL_SPLINES_H_ +#include <array> #include <cmath> #include <cstddef> #include <cstdint> @@ -24,6 +25,8 @@ class BitReader; static constexpr float kDesiredRenderingDistance = 1.f; +typedef std::array<float, 32> Dct32; + enum SplineEntropyContexts : size_t { kQuantizationAdjustmentContext = 0, kStartingPositionContext, @@ -45,10 +48,10 @@ struct Spline { }; std::vector<Point> control_points; // X, Y, B. - float color_dct[3][32]; + std::array<Dct32, 3> color_dct; // Splines are draws by normalized Gaussian splatting. This controls the // Gaussian's parameter along the spline. - float sigma_dct[32]; + Dct32 sigma_dct; }; class QuantizedSplineEncoder; diff --git a/third_party/jpeg-xl/lib/jxl/splines_gbench.cc b/third_party/jpeg-xl/lib/jxl/splines_gbench.cc index 78ff6d41c0..903c5b6328 100644 --- a/third_party/jpeg-xl/lib/jxl/splines_gbench.cc +++ b/third_party/jpeg-xl/lib/jxl/splines_gbench.cc @@ -3,7 +3,10 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. +#include <array> + #include "benchmark/benchmark.h" +#include "lib/jxl/image_ops.h" #include "lib/jxl/splines.h" namespace jxl { @@ -17,12 +20,14 @@ const float kYToB = cmap->YtoBRatio(0); void BM_Splines(benchmark::State& state) { const size_t n = state.range(); - std::vector<Spline> spline_data = { - {/*control_points=*/{ - {9, 54}, {118, 159}, {97, 3}, {10, 40}, {150, 25}, {120, 300}}, - /*color_dct=*/ - {{0.03125f, 0.00625f, 0.003125f}, {1.f, 0.321875f}, {1.f, 0.24375f}}, - /*sigma_dct=*/{0.3125f, 0.f, 0.f, 0.0625f}}}; + Spline spline1{ + /*control_points=*/{ + {9, 54}, {118, 159}, {97, 3}, {10, 40}, {150, 25}, {120, 300}}, + /*color_dct=*/ + {Dct32{0.03125f, 0.00625f, 0.003125f}, Dct32{1.f, 0.321875f}, + Dct32{1.f, 0.24375f}}, + /*sigma_dct=*/{0.3125f, 0.f, 0.f, 0.0625f}}; + std::vector<Spline> spline_data = {spline1}; std::vector<QuantizedSpline> quantized_splines; std::vector<Spline::Point> starting_points; for (const Spline& spline : spline_data) { @@ -33,7 +38,7 @@ void BM_Splines(benchmark::State& state) { Splines splines(kQuantizationAdjustment, std::move(quantized_splines), std::move(starting_points)); - Image3F drawing_area(320, 320); + JXL_ASSIGN_OR_DIE(Image3F drawing_area, Image3F::Create(320, 320)); ZeroFillImage(&drawing_area); for (auto _ : state) { for (size_t i = 0; i < n; ++i) { diff --git a/third_party/jpeg-xl/lib/jxl/splines_test.cc b/third_party/jpeg-xl/lib/jxl/splines_test.cc index d812545a37..83cc524234 100644 --- a/third_party/jpeg-xl/lib/jxl/splines_test.cc +++ b/third_party/jpeg-xl/lib/jxl/splines_test.cc @@ -23,7 +23,6 @@ #include "lib/jxl/chroma_from_luma.h" #include "lib/jxl/enc_aux_out.h" #include "lib/jxl/enc_bit_writer.h" -#include "lib/jxl/enc_params.h" #include "lib/jxl/enc_splines.h" #include "lib/jxl/image.h" #include "lib/jxl/image_ops.h" @@ -45,10 +44,6 @@ std::ostream& operator<<(std::ostream& os, const Spline& spline) { namespace { using test::ReadTestData; -using ::testing::AllOf; -using ::testing::Field; -using ::testing::FloatNear; -using ::testing::Pointwise; constexpr int kQuantizationAdjustment = 0; const ColorCorrelationMap* const cmap = new ColorCorrelationMap; @@ -73,127 +68,75 @@ std::vector<Spline> DequantizeSplines(const Splines& splines) { return dequantized; } -MATCHER(ControlPointIs, "") { - const Spline::Point& actual = std::get<0>(arg); - const Spline::Point& expected = std::get<1>(arg); - return testing::ExplainMatchResult( - AllOf(Field(&Spline::Point::x, FloatNear(expected.x, kTolerance)), - Field(&Spline::Point::y, FloatNear(expected.y, kTolerance))), - actual, result_listener); -} - -MATCHER(ControlPointsMatch, "") { - const Spline& actual = std::get<0>(arg); - const Spline& expected = std::get<1>(arg); - return testing::ExplainMatchResult( - Field(&Spline::control_points, - Pointwise(ControlPointIs(), expected.control_points)), - actual, result_listener); -} - -MATCHER(SplinesMatch, "") { - const Spline& actual = std::get<0>(arg); - const Spline& expected = std::get<1>(arg); - if (!testing::ExplainMatchResult(ControlPointsMatch(), arg, - result_listener)) { - return false; - } - for (int i = 0; i < 3; ++i) { - size_t color_dct_size = - sizeof(expected.color_dct[i]) / sizeof(expected.color_dct[i][0]); - for (size_t j = 0; j < color_dct_size; j++) { - testing::StringMatchResultListener color_dct_listener; - if (!testing::ExplainMatchResult( - FloatNear(expected.color_dct[i][j], kTolerance), - actual.color_dct[i][j], &color_dct_listener)) { - *result_listener << ", where color_dct[" << i << "][" << j - << "] don't match, " << color_dct_listener.str(); - return false; - } - } - } - size_t sigma_dct_size = - sizeof(expected.sigma_dct) / sizeof(expected.sigma_dct[0]); - for (size_t i = 0; i < sigma_dct_size; i++) { - testing::StringMatchResultListener sigma_listener; - if (!testing::ExplainMatchResult( - FloatNear(expected.sigma_dct[i], kTolerance), actual.sigma_dct[i], - &sigma_listener)) { - *result_listener << ", where sigma_dct[" << i << "] don't match, " - << sigma_listener.str(); - return false; - } - } - return true; -} - } // namespace TEST(SplinesTest, Serialization) { - std::vector<Spline> spline_data = { - {/*control_points=*/{ - {109, 54}, {218, 159}, {80, 3}, {110, 274}, {94, 185}, {17, 277}}, - /*color_dct=*/ - {{36.3, 39.7, 23.2, 67.5, 4.4, 71.5, 62.3, 32.3, 92.2, 10.1, 10.8, - 9.2, 6.1, 10.5, 79.1, 7, 24.6, 90.8, 5.5, 84, 43.8, 49, - 33.5, 78.9, 54.5, 77.9, 62.1, 51.4, 36.4, 14.3, 83.7, 35.4}, - {9.4, 53.4, 9.5, 74.9, 72.7, 26.7, 7.9, 0.9, 84.9, 23.2, 26.5, - 31.1, 91, 11.7, 74.1, 39.3, 23.7, 82.5, 4.8, 2.7, 61.2, 96.4, - 13.7, 66.7, 62.9, 82.4, 5.9, 98.7, 21.5, 7.9, 51.7, 63.1}, - {48, 39.3, 6.9, 26.3, 33.3, 6.2, 1.7, 98.9, 59.9, 59.6, 95, - 61.3, 82.7, 53, 6.1, 30.4, 34.7, 96.9, 93.4, 17, 38.8, 80.8, - 63, 18.6, 43.6, 32.3, 61, 20.2, 24.3, 28.3, 69.1, 62.4}}, - /*sigma_dct=*/{32.7, 21.5, 44.4, 1.8, 45.8, 90.6, 29.3, 59.2, - 23.7, 85.2, 84.8, 27.2, 42.1, 84.1, 50.6, 17.6, - 93.7, 4.9, 2.6, 69.8, 94.9, 52, 24.3, 18.8, - 12.1, 95.7, 28.5, 81.4, 89.9, 31.4, 74.8, 52}}, - {/*control_points=*/{{172, 309}, - {196, 277}, - {42, 238}, - {114, 350}, - {307, 290}, - {316, 269}, - {124, 66}, - {233, 267}}, - /*color_dct=*/ - {{15, 28.9, 22, 6.6, 41.8, 83, 8.6, 56.8, 68.9, 9.7, 5.4, - 19.8, 70.8, 90, 52.5, 65.2, 7.8, 23.5, 26.4, 72.2, 64.7, 87.1, - 1.3, 67.5, 46, 68.4, 65.4, 35.5, 29.1, 13, 41.6, 23.9}, - {47.7, 79.4, 62.7, 29.1, 96.8, 18.5, 17.6, 15.2, 80.5, 56, 96.2, - 59.9, 26.7, 96.1, 92.3, 42.1, 35.8, 54, 23.2, 55, 76, 35.8, - 58.4, 88.7, 2.4, 78.1, 95.6, 27.5, 6.6, 78.5, 24.1, 69.8}, - {43.8, 96.5, 0.9, 95.1, 49.1, 71.2, 25.1, 33.6, 75.2, 95, 82.1, - 19.7, 10.5, 44.9, 50, 93.3, 83.5, 99.5, 64.6, 54, 3.5, 99.7, - 45.3, 82.1, 22.4, 37.9, 60, 32.2, 12.6, 4.6, 65.5, 96.4}}, - /*sigma_dct=*/{72.5, 2.6, 41.7, 2.2, 39.7, 79.1, 69.6, 19.9, - 92.3, 71.5, 41.9, 62.1, 30, 49.4, 70.3, 45.3, - 62.5, 47.2, 46.7, 41.2, 90.8, 46.8, 91.2, 55, - 8.1, 69.6, 25.4, 84.7, 61.7, 27.6, 3.7, 46.9}}, - {/*control_points=*/{{100, 186}, - {257, 97}, - {170, 49}, - {25, 169}, - {309, 104}, - {232, 237}, - {385, 101}, - {122, 168}, - {26, 300}, - {390, 88}}, - /*color_dct=*/ - {{16.9, 64.8, 4.2, 10.6, 23.5, 17, 79.3, 5.7, 60.4, 16.6, 94.9, - 63.7, 87.6, 10.5, 3.8, 61.1, 22.9, 81.9, 80.4, 40.5, 45.9, 25.4, - 39.8, 30, 50.2, 90.4, 27.9, 93.7, 65.1, 48.2, 22.3, 43.9}, - {24.9, 66, 3.5, 90.2, 97.1, 15.8, 35.6, 0.6, 68, 39.6, 24.4, - 85.9, 57.7, 77.6, 47.5, 67.9, 4.3, 5.4, 91.2, 58.5, 0.1, 52.2, - 3.5, 47.8, 63.2, 43.5, 85.8, 35.8, 50.2, 35.9, 19.2, 48.2}, - {82.8, 44.9, 76.4, 39.5, 94.1, 14.3, 89.8, 10, 10.5, 74.5, 56.3, - 65.8, 7.8, 23.3, 52.8, 99.3, 56.8, 46, 76.7, 13.5, 67, 22.4, - 29.9, 43.3, 70.3, 26, 74.3, 53.9, 62, 19.1, 49.3, 46.7}}, - /*sigma_dct=*/{83.5, 1.7, 25.1, 18.7, 46.5, 75.3, 28, 62.3, - 50.3, 23.3, 85.6, 96, 45.8, 33.1, 33.4, 52.9, - 26.3, 58.5, 19.6, 70, 92.6, 22.5, 57, 21.6, - 76.8, 87.5, 22.9, 66.3, 35.7, 35.6, 56.8, 67.2}}, - }; + Spline spline1{ + /*control_points=*/{ + {109, 54}, {218, 159}, {80, 3}, {110, 274}, {94, 185}, {17, 277}}, + /*color_dct=*/ + {Dct32{36.3, 39.7, 23.2, 67.5, 4.4, 71.5, 62.3, 32.3, 92.2, 10.1, 10.8, + 9.2, 6.1, 10.5, 79.1, 7, 24.6, 90.8, 5.5, 84, 43.8, 49, + 33.5, 78.9, 54.5, 77.9, 62.1, 51.4, 36.4, 14.3, 83.7, 35.4}, + Dct32{9.4, 53.4, 9.5, 74.9, 72.7, 26.7, 7.9, 0.9, 84.9, 23.2, 26.5, + 31.1, 91, 11.7, 74.1, 39.3, 23.7, 82.5, 4.8, 2.7, 61.2, 96.4, + 13.7, 66.7, 62.9, 82.4, 5.9, 98.7, 21.5, 7.9, 51.7, 63.1}, + Dct32{48, 39.3, 6.9, 26.3, 33.3, 6.2, 1.7, 98.9, 59.9, 59.6, 95, + 61.3, 82.7, 53, 6.1, 30.4, 34.7, 96.9, 93.4, 17, 38.8, 80.8, + 63, 18.6, 43.6, 32.3, 61, 20.2, 24.3, 28.3, 69.1, 62.4}}, + /*sigma_dct=*/{32.7, 21.5, 44.4, 1.8, 45.8, 90.6, 29.3, 59.2, + 23.7, 85.2, 84.8, 27.2, 42.1, 84.1, 50.6, 17.6, + 93.7, 4.9, 2.6, 69.8, 94.9, 52, 24.3, 18.8, + 12.1, 95.7, 28.5, 81.4, 89.9, 31.4, 74.8, 52}}; + Spline spline2{ + /*control_points=*/{{172, 309}, + {196, 277}, + {42, 238}, + {114, 350}, + {307, 290}, + {316, 269}, + {124, 66}, + {233, 267}}, + /*color_dct=*/ + {Dct32{15, 28.9, 22, 6.6, 41.8, 83, 8.6, 56.8, 68.9, 9.7, 5.4, + 19.8, 70.8, 90, 52.5, 65.2, 7.8, 23.5, 26.4, 72.2, 64.7, 87.1, + 1.3, 67.5, 46, 68.4, 65.4, 35.5, 29.1, 13, 41.6, 23.9}, + Dct32{47.7, 79.4, 62.7, 29.1, 96.8, 18.5, 17.6, 15.2, 80.5, 56, 96.2, + 59.9, 26.7, 96.1, 92.3, 42.1, 35.8, 54, 23.2, 55, 76, 35.8, + 58.4, 88.7, 2.4, 78.1, 95.6, 27.5, 6.6, 78.5, 24.1, 69.8}, + Dct32{43.8, 96.5, 0.9, 95.1, 49.1, 71.2, 25.1, 33.6, 75.2, 95, 82.1, + 19.7, 10.5, 44.9, 50, 93.3, 83.5, 99.5, 64.6, 54, 3.5, 99.7, + 45.3, 82.1, 22.4, 37.9, 60, 32.2, 12.6, 4.6, 65.5, 96.4}}, + /*sigma_dct=*/{72.5, 2.6, 41.7, 2.2, 39.7, 79.1, 69.6, 19.9, + 92.3, 71.5, 41.9, 62.1, 30, 49.4, 70.3, 45.3, + 62.5, 47.2, 46.7, 41.2, 90.8, 46.8, 91.2, 55, + 8.1, 69.6, 25.4, 84.7, 61.7, 27.6, 3.7, 46.9}}; + Spline spline3{ + /*control_points=*/{{100, 186}, + {257, 97}, + {170, 49}, + {25, 169}, + {309, 104}, + {232, 237}, + {385, 101}, + {122, 168}, + {26, 300}, + {390, 88}}, + /*color_dct=*/ + {Dct32{16.9, 64.8, 4.2, 10.6, 23.5, 17, 79.3, 5.7, 60.4, 16.6, 94.9, + 63.7, 87.6, 10.5, 3.8, 61.1, 22.9, 81.9, 80.4, 40.5, 45.9, 25.4, + 39.8, 30, 50.2, 90.4, 27.9, 93.7, 65.1, 48.2, 22.3, 43.9}, + Dct32{24.9, 66, 3.5, 90.2, 97.1, 15.8, 35.6, 0.6, 68, 39.6, 24.4, + 85.9, 57.7, 77.6, 47.5, 67.9, 4.3, 5.4, 91.2, 58.5, 0.1, 52.2, + 3.5, 47.8, 63.2, 43.5, 85.8, 35.8, 50.2, 35.9, 19.2, 48.2}, + Dct32{82.8, 44.9, 76.4, 39.5, 94.1, 14.3, 89.8, 10, 10.5, 74.5, 56.3, + 65.8, 7.8, 23.3, 52.8, 99.3, 56.8, 46, 76.7, 13.5, 67, 22.4, + 29.9, 43.3, 70.3, 26, 74.3, 53.9, 62, 19.1, 49.3, 46.7}}, + /*sigma_dct=*/{83.5, 1.7, 25.1, 18.7, 46.5, 75.3, 28, 62.3, + 50.3, 23.3, 85.6, 96, 45.8, 33.1, 33.4, 52.9, + 26.3, 58.5, 19.6, 70, 92.6, 22.5, 57, 21.6, + 76.8, 87.5, 22.9, 66.3, 35.7, 35.6, 56.8, 67.2}}; + std::vector<Spline> spline_data{spline1, spline2, spline3}; std::vector<QuantizedSpline> quantized_splines; std::vector<Spline::Point> starting_points; @@ -206,8 +149,20 @@ TEST(SplinesTest, Serialization) { Splines splines(kQuantizationAdjustment, std::move(quantized_splines), std::move(starting_points)); const std::vector<Spline> quantized_spline_data = DequantizeSplines(splines); - EXPECT_THAT(quantized_spline_data, - Pointwise(ControlPointsMatch(), spline_data)); + EXPECT_EQ(quantized_spline_data.size(), spline_data.size()); + for (size_t i = 0; i < quantized_spline_data.size(); ++i) { + const Spline& actual = quantized_spline_data[i]; + const Spline& expected = spline_data[i]; + const auto& actual_points = actual.control_points; + const auto& expected_points = expected.control_points; + EXPECT_EQ(actual_points.size(), expected_points.size()); + for (size_t j = 0; j < actual_points.size(); ++j) { + EXPECT_NEAR(actual_points[j].x, expected_points[j].x, kTolerance) + << "spline " << i << " point " << j; + EXPECT_NEAR(actual_points[j].y, expected_points[j].y, kTolerance) + << "spline " << i << " point " << j; + } + } BitWriter writer; EncodeSplines(splines, &writer, kLayerSplines, HistogramParams(), nullptr); @@ -225,8 +180,28 @@ TEST(SplinesTest, Serialization) { const std::vector<Spline> decoded_spline_data = DequantizeSplines(decoded_splines); - EXPECT_THAT(decoded_spline_data, - Pointwise(SplinesMatch(), quantized_spline_data)); + + EXPECT_EQ(decoded_spline_data.size(), quantized_spline_data.size()); + for (size_t i = 0; i < decoded_spline_data.size(); ++i) { + const Spline& actual = decoded_spline_data[i]; + const Spline& expected = quantized_spline_data[i]; + const auto& actual_points = actual.control_points; + const auto& expected_points = expected.control_points; + EXPECT_EQ(actual_points.size(), expected_points.size()); + for (size_t j = 0; j < actual_points.size(); ++j) { + EXPECT_NEAR(actual_points[j].x, expected_points[j].x, kTolerance) + << "spline " << i << " point " << j; + EXPECT_NEAR(actual_points[j].y, expected_points[j].y, kTolerance) + << "spline " << i << " point " << j; + } + + const auto& actual_color_dct = actual.color_dct; + const auto& expected_color_dct = expected.color_dct; + for (size_t j = 0; j < actual_color_dct.size(); ++j) { + EXPECT_ARRAY_NEAR(actual_color_dct[j], expected_color_dct[j], kTolerance); + } + EXPECT_ARRAY_NEAR(actual.sigma_dct, expected.sigma_dct, kTolerance); + } } #ifdef JXL_CRASH_ON_ERROR @@ -240,10 +215,10 @@ TEST(SplinesTest, TooManySplinesTest) { std::vector<QuantizedSpline> quantized_splines; std::vector<Spline::Point> starting_points; for (size_t i = 0; i < kNumSplines; i++) { - Spline spline = { + Spline spline{ /*control_points=*/{{1.f + i, 2}, {10.f + i, 25}, {30.f + i, 300}}, /*color_dct=*/ - {{1.f, 0.2f, 0.1f}, {35.7f, 10.3f}, {35.7f, 7.8f}}, + {Dct32{1.f, 0.2f, 0.1f}, Dct32{35.7f, 10.3f}, Dct32{35.7f, 7.8f}}, /*sigma_dct=*/{10.f, 0.f, 0.f, 2.f}}; quantized_splines.emplace_back(spline, kQuantizationAdjustment, kYToX, kYToB); @@ -271,10 +246,11 @@ TEST(SplinesTest, DuplicatePoints) { std::vector<Spline::Point> control_points{ {9, 54}, {118, 159}, {97, 3}, // Repeated. {97, 3}, {10, 40}, {150, 25}, {120, 300}}; - Spline spline{control_points, - /*color_dct=*/ - {{1.f, 0.2f, 0.1f}, {35.7f, 10.3f}, {35.7f, 7.8f}}, - /*sigma_dct=*/{10.f, 0.f, 0.f, 2.f}}; + Spline spline{ + control_points, + /*color_dct=*/ + {Dct32{1.f, 0.2f, 0.1f}, Dct32{35.7f, 10.3f}, Dct32{35.7f, 7.8f}}, + /*sigma_dct=*/{10.f, 0.f, 0.f, 2.f}}; std::vector<Spline> spline_data{spline}; std::vector<QuantizedSpline> quantized_splines; std::vector<Spline::Point> starting_points; @@ -286,7 +262,7 @@ TEST(SplinesTest, DuplicatePoints) { Splines splines(kQuantizationAdjustment, std::move(quantized_splines), std::move(starting_points)); - Image3F image(320, 320); + JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(320, 320)); ZeroFillImage(&image); EXPECT_FALSE( splines.InitializeDrawCache(image.xsize(), image.ysize(), *cmap)); @@ -304,10 +280,10 @@ TEST(SplinesTest, Drawing) { const Spline spline{ control_points, /*color_dct=*/ - {{0.4989345073699951171875000f, 0.4997999966144561767578125f}, - {0.4772970676422119140625000f, 0.f, 0.5250000357627868652343750f}, - {-0.0176776945590972900390625f, 0.4900000095367431640625000f, - 0.5250000357627868652343750f}}, + {Dct32{0.4989345073699951171875000f, 0.4997999966144561767578125f}, + Dct32{0.4772970676422119140625000f, 0.f, 0.5250000357627868652343750f}, + Dct32{-0.0176776945590972900390625f, 0.4900000095367431640625000f, + 0.5250000357627868652343750f}}, /*sigma_dct=*/ {0.9427147507667541503906250f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.6665999889373779296875000f}}; @@ -322,13 +298,13 @@ TEST(SplinesTest, Drawing) { Splines splines(kQuantizationAdjustment, std::move(quantized_splines), std::move(starting_points)); - Image3F image(320, 320); + JXL_ASSIGN_OR_DIE(Image3F image, Image3F::Create(320, 320)); ZeroFillImage(&image); ASSERT_TRUE(splines.InitializeDrawCache(image.xsize(), image.ysize(), *cmap)); splines.AddTo(&image, Rect(image), Rect(image)); CodecInOut io_actual; - Image3F image2(320, 320); + JXL_ASSIGN_OR_DIE(Image3F image2, Image3F::Create(320, 320)); CopyImageTo(image, &image2); io_actual.SetFromImage(std::move(image2), ColorEncoding::SRGB()); ASSERT_TRUE(io_actual.frames[0].TransformTo(io_expected.Main().c_current(), diff --git a/third_party/jpeg-xl/lib/jxl/test_image.cc b/third_party/jpeg-xl/lib/jxl/test_image.cc index 098e9c25a1..42f028d53a 100644 --- a/third_party/jpeg-xl/lib/jxl/test_image.cc +++ b/third_party/jpeg-xl/lib/jxl/test_image.cc @@ -323,7 +323,7 @@ TestImage& TestImage::SetColorEncoding(const std::string& description) { } TestImage& TestImage::CoalesceGIFAnimationWithAlpha() { - extras::PackedFrame canvas = ppf_.frames[0].Copy(); + JXL_ASSIGN_OR_DIE(extras::PackedFrame canvas, ppf_.frames[0].Copy()); JXL_CHECK(canvas.color.format.num_channels == 3); JXL_CHECK(canvas.color.format.data_type == JXL_TYPE_UINT8); JXL_CHECK(canvas.extra_channels.size() == 1); @@ -331,7 +331,7 @@ TestImage& TestImage::CoalesceGIFAnimationWithAlpha() { const extras::PackedFrame& frame = ppf_.frames[i]; JXL_CHECK(frame.extra_channels.size() == 1); const JxlLayerInfo& layer_info = frame.frame_info.layer_info; - extras::PackedFrame rendered = canvas.Copy(); + JXL_ASSIGN_OR_DIE(extras::PackedFrame rendered, canvas.Copy()); uint8_t* pixels_rendered = reinterpret_cast<uint8_t*>(rendered.color.pixels()); const uint8_t* pixels_frame = @@ -353,7 +353,7 @@ TestImage& TestImage::CoalesceGIFAnimationWithAlpha() { } } if (layer_info.save_as_reference != 0) { - canvas = rendered.Copy(); + JXL_ASSIGN_OR_DIE(canvas, rendered.Copy()); } ppf_.frames[i] = std::move(rendered); } @@ -393,10 +393,14 @@ void TestImage::Frame::SetValue(size_t y, size_t x, size_t c, float val) { TestImage::Frame TestImage::AddFrame() { size_t index = ppf_.frames.size(); - extras::PackedFrame frame(ppf_.info.xsize, ppf_.info.ysize, format_); + JXL_ASSIGN_OR_DIE( + extras::PackedFrame frame, + extras::PackedFrame::Create(ppf_.info.xsize, ppf_.info.ysize, format_)); for (size_t i = 0; i < ppf_.extra_channels_info.size(); ++i) { JxlPixelFormat ec_format = {1, format_.data_type, format_.endianness, 0}; - extras::PackedImage image(ppf_.info.xsize, ppf_.info.ysize, ec_format); + JXL_ASSIGN_OR_DIE(extras::PackedImage image, + extras::PackedImage::Create(ppf_.info.xsize, + ppf_.info.ysize, ec_format)); frame.extra_channels.emplace_back(std::move(image)); } ppf_.frames.emplace_back(std::move(frame)); @@ -404,10 +408,12 @@ TestImage::Frame TestImage::AddFrame() { } TestImage::Frame TestImage::AddPreview(size_t xsize, size_t ysize) { - extras::PackedFrame frame(xsize, ysize, format_); + JXL_ASSIGN_OR_DIE(extras::PackedFrame frame, + extras::PackedFrame::Create(xsize, ysize, format_)); for (size_t i = 0; i < ppf_.extra_channels_info.size(); ++i) { JxlPixelFormat ec_format = {1, format_.data_type, format_.endianness, 0}; - extras::PackedImage image(xsize, ysize, ec_format); + JXL_ASSIGN_OR_DIE(extras::PackedImage image, + extras::PackedImage::Create(xsize, ysize, ec_format)); frame.extra_channels.emplace_back(std::move(image)); } ppf_.preview_frame = make_unique<extras::PackedFrame>(std::move(frame)); diff --git a/third_party/jpeg-xl/lib/jxl/test_utils.cc b/third_party/jpeg-xl/lib/jxl/test_utils.cc index 451f2a0a03..308efb9a13 100644 --- a/third_party/jpeg-xl/lib/jxl/test_utils.cc +++ b/third_party/jpeg-xl/lib/jxl/test_utils.cc @@ -228,7 +228,7 @@ bool Roundtrip(const CodecInOut* io, const CompressParams& cparams, } size_t Roundtrip(const extras::PackedPixelFile& ppf_in, - extras::JXLCompressParams cparams, + const extras::JXLCompressParams& cparams, extras::JXLDecompressParams dparams, ThreadPool* pool, extras::PackedPixelFile* ppf_out) { DefaultAcceptedFormats(dparams); @@ -303,12 +303,12 @@ bool Near(double expected, double value, double max_dist) { float LoadLEFloat16(const uint8_t* p) { uint16_t bits16 = LoadLE16(p); - return LoadFloat16(bits16); + return detail::LoadFloat16(bits16); } float LoadBEFloat16(const uint8_t* p) { uint16_t bits16 = LoadBE16(p); - return LoadFloat16(bits16); + return detail::LoadFloat16(bits16); } size_t GetPrecision(JxlDataType data_type) { @@ -385,7 +385,10 @@ std::vector<double> ConvertToRGBA32(const uint8_t* pixels, size_t xsize, for (size_t x = 0; x < xsize; ++x) { size_t j = (y * xsize + x) * 4; size_t i = y * stride + x * num_channels * 2; - double r, g, b, a; + double r; + double g; + double b; + double a; if (endianness == JXL_BIG_ENDIAN) { r = (pixels[i + 0] << 8) + pixels[i + 1]; g = gray ? r : (pixels[i + 2] << 8) + pixels[i + 3]; @@ -413,7 +416,10 @@ std::vector<double> ConvertToRGBA32(const uint8_t* pixels, size_t xsize, for (size_t x = 0; x < xsize; ++x) { size_t j = (y * xsize + x) * 4; size_t i = y * stride + x * num_channels * 4; - double r, g, b, a; + double r; + double g; + double b; + double a; if (endianness == JXL_BIG_ENDIAN) { r = LoadBEFloat(pixels + i); g = gray ? r : LoadBEFloat(pixels + i + 4); @@ -437,7 +443,10 @@ std::vector<double> ConvertToRGBA32(const uint8_t* pixels, size_t xsize, for (size_t x = 0; x < xsize; ++x) { size_t j = (y * xsize + x) * 4; size_t i = y * stride + x * num_channels * 2; - double r, g, b, a; + double r; + double g; + double b; + double a; if (endianness == JXL_BIG_ENDIAN) { r = LoadBEFloat16(pixels + i); g = gray ? r : LoadBEFloat16(pixels + i + 2); @@ -470,8 +479,8 @@ size_t ComparePixels(const uint8_t* a, const uint8_t* b, size_t xsize, std::vector<double> b_full = ConvertToRGBA32(b, xsize, ysize, format_b); bool gray_a = format_a.num_channels < 3; bool gray_b = format_b.num_channels < 3; - bool alpha_a = !(format_a.num_channels & 1); - bool alpha_b = !(format_b.num_channels & 1); + bool alpha_a = ((format_a.num_channels & 1) == 0); + bool alpha_b = ((format_b.num_channels & 1) == 0); size_t bits_a = GetPrecision(format_a.data_type); size_t bits_b = GetPrecision(format_b.data_type); size_t bits = std::min(bits_a, bits_b); @@ -558,6 +567,34 @@ float ButteraugliDistance(const extras::PackedPixelFile& a, /*distmap=*/nullptr, pool); } +float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1, + const ButteraugliParams& params, + const JxlCmsInterface& cms, ImageF* distmap, + ThreadPool* pool, bool ignore_alpha) { + JxlButteraugliComparator comparator(params, cms); + float distance; + JXL_CHECK(ComputeScore(rgb0, rgb1, &comparator, cms, &distance, distmap, pool, + ignore_alpha)); + return distance; +} + +float ButteraugliDistance(const std::vector<ImageBundle>& frames0, + const std::vector<ImageBundle>& frames1, + const ButteraugliParams& params, + const JxlCmsInterface& cms, ImageF* distmap, + ThreadPool* pool) { + JxlButteraugliComparator comparator(params, cms); + JXL_ASSERT(frames0.size() == frames1.size()); + float max_dist = 0.0f; + for (size_t i = 0; i < frames0.size(); ++i) { + float frame_score; + JXL_CHECK(ComputeScore(frames0[i], frames1[i], &comparator, cms, + &frame_score, distmap, pool)); + max_dist = std::max(max_dist, frame_score); + } + return max_dist; +} + float Butteraugli3Norm(const extras::PackedPixelFile& a, const extras::PackedPixelFile& b, ThreadPool* pool) { CodecInOut io0; @@ -683,7 +720,7 @@ Status ReadICC(BitReader* JXL_RESTRICT reader, PaddedBytes icc_buffer; JXL_RETURN_IF_ERROR(icc_reader.Init(reader, output_limit)); JXL_RETURN_IF_ERROR(icc_reader.Process(reader, &icc_buffer)); - Bytes(icc_buffer).AppendTo(icc); + Bytes(icc_buffer).AppendTo(*icc); return true; } @@ -787,7 +824,7 @@ Status EncodeFile(const CompressParams& params, const CodecInOut* io, } PaddedBytes output = std::move(writer).TakeBytes(); - Bytes(output).AppendTo(compressed); + Bytes(output).AppendTo(*compressed); return true; } diff --git a/third_party/jpeg-xl/lib/jxl/test_utils.h b/third_party/jpeg-xl/lib/jxl/test_utils.h index 6734380bf5..15057cc92d 100644 --- a/third_party/jpeg-xl/lib/jxl/test_utils.h +++ b/third_party/jpeg-xl/lib/jxl/test_utils.h @@ -18,13 +18,13 @@ #include <ostream> #include <vector> -#include "lib/extras/dec/decode.h" #include "lib/extras/dec/jxl.h" #include "lib/extras/enc/jxl.h" #include "lib/extras/packed_image.h" #include "lib/jxl/base/data_parallel.h" #include "lib/jxl/base/span.h" #include "lib/jxl/base/status.h" +#include "lib/jxl/butteraugli/butteraugli.h" #include "lib/jxl/codec_in_out.h" #include "lib/jxl/color_encoding_internal.h" #include "lib/jxl/enc_params.h" @@ -63,9 +63,8 @@ void SetThreadParallelRunner(Params params, ThreadPool* pool) { } } -Status DecodeFile(extras::JXLDecompressParams dparams, - const Span<const uint8_t> file, CodecInOut* JXL_RESTRICT io, - ThreadPool* pool = nullptr); +Status DecodeFile(extras::JXLDecompressParams dparams, Span<const uint8_t> file, + CodecInOut* JXL_RESTRICT io, ThreadPool* pool = nullptr); bool Roundtrip(const CodecInOut* io, const CompressParams& cparams, extras::JXLDecompressParams dparams, @@ -74,7 +73,7 @@ bool Roundtrip(const CodecInOut* io, const CompressParams& cparams, // Returns compressed size [bytes]. size_t Roundtrip(const extras::PackedPixelFile& ppf_in, - extras::JXLCompressParams cparams, + const extras::JXLCompressParams& cparams, extras::JXLDecompressParams dparams, ThreadPool* pool, extras::PackedPixelFile* ppf_out); @@ -141,6 +140,18 @@ float ButteraugliDistance(const extras::PackedPixelFile& a, const extras::PackedPixelFile& b, ThreadPool* pool = nullptr); +float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1, + const ButteraugliParams& params, + const JxlCmsInterface& cms, ImageF* distmap = nullptr, + ThreadPool* pool = nullptr, + bool ignore_alpha = false); + +float ButteraugliDistance(const std::vector<ImageBundle>& frames0, + const std::vector<ImageBundle>& frames1, + const ButteraugliParams& params, + const JxlCmsInterface& cms, ImageF* distmap = nullptr, + ThreadPool* pool = nullptr); + float Butteraugli3Norm(const extras::PackedPixelFile& a, const extras::PackedPixelFile& b, ThreadPool* pool = nullptr); @@ -169,6 +180,7 @@ class ThreadPoolForTests { } ThreadPoolForTests(const ThreadPoolForTests&) = delete; ThreadPoolForTests& operator&(const ThreadPoolForTests&) = delete; + // TODO(eustas): avoid unary `&` overload? ThreadPool* operator&() { return pool_.get(); } private: @@ -188,6 +200,8 @@ Status ReadICC(BitReader* JXL_RESTRICT reader, Status EncodeFile(const CompressParams& params, const CodecInOut* io, std::vector<uint8_t>* compressed, ThreadPool* pool = nullptr); +constexpr const char* BoolToCStr(bool b) { return b ? "true" : "false"; } + } // namespace test bool operator==(const jxl::Bytes& a, const jxl::Bytes& b); diff --git a/third_party/jpeg-xl/lib/jxl/testing.h b/third_party/jpeg-xl/lib/jxl/testing.h index 5344399c4c..1fac352a78 100644 --- a/third_party/jpeg-xl/lib/jxl/testing.h +++ b/third_party/jpeg-xl/lib/jxl/testing.h @@ -6,15 +6,7 @@ #ifndef LIB_JXL_TESTING_H_ #define LIB_JXL_TESTING_H_ -// GTest/GMock specific macros / wrappers. - -// gmock unconditionally redefines those macros (to wrong values). -// Lets include it only here and mitigate the problem. -#pragma push_macro("PRIdS") -#pragma push_macro("PRIuS") -#include "gmock/gmock.h" -#pragma pop_macro("PRIuS") -#pragma pop_macro("PRIdS") +// GTest specific macros / wrappers. #include "gtest/gtest.h" // JPEGXL_ENABLE_BOXES, JPEGXL_ENABLE_TRANSCODE_JPEG @@ -60,9 +52,26 @@ // Ensures that we don't make our test bounds too lax, effectively disabling the // tests. -MATCHER_P(IsSlightlyBelow, max, "") { - return max * 0.75 <= arg && arg <= max * 1.0; -} +#define EXPECT_SLIGHTLY_BELOW(A, E) \ + { \ + double _actual = (A); \ + double _expected = (E); \ + EXPECT_LE(_actual, _expected); \ + EXPECT_GE(_actual, 0.75 * _expected); \ + } + +#define EXPECT_ARRAY_NEAR(A, E, T) \ + { \ + const auto _actual = (A); \ + const auto _expected = (E); \ + const auto _tolerance = (T); \ + size_t _n = _expected.size(); \ + ASSERT_EQ(_actual.size(), _n); \ + for (size_t _i = 0; _i < _n; ++_i) { \ + EXPECT_NEAR(_actual[_i], _expected[_i], _tolerance) \ + << "@" << _i << ": " << _actual[_i] << " !~= " << _expected[_i]; \ + } \ + } #define JXL_EXPECT_OK(F) \ { \ diff --git a/third_party/jpeg-xl/lib/jxl/toc.h b/third_party/jpeg-xl/lib/jxl/toc.h index 00006440b7..f5b9c65763 100644 --- a/third_party/jpeg-xl/lib/jxl/toc.h +++ b/third_party/jpeg-xl/lib/jxl/toc.h @@ -24,7 +24,7 @@ namespace jxl { constexpr U32Enc kTocDist(Bits(10), BitsOffset(14, 1024), BitsOffset(22, 17408), BitsOffset(30, 4211712)); -size_t MaxBits(const size_t num_sizes); +size_t MaxBits(size_t num_sizes); // TODO(veluca): move these to FrameDimensions. static JXL_INLINE size_t AcGroupIndex(size_t pass, size_t group, diff --git a/third_party/jpeg-xl/lib/jxl/transpose-inl.h b/third_party/jpeg-xl/lib/jxl/transpose-inl.h index 4674420737..efe7d90569 100644 --- a/third_party/jpeg-xl/lib/jxl/transpose-inl.h +++ b/third_party/jpeg-xl/lib/jxl/transpose-inl.h @@ -45,9 +45,9 @@ struct TransposeSimdTag {}; // TODO(veluca): it's not super useful to have this in the SIMD namespace. template <size_t ROWS_or_0, size_t COLS_or_0, class From, class To> -JXL_INLINE_TRANSPOSE void GenericTransposeBlock(TransposeSimdTag<false>, - const From& from, const To& to, - size_t ROWSp, size_t COLSp) { +JXL_INLINE_TRANSPOSE void GenericTransposeBlock( + TransposeSimdTag<false> /* tag */, const From& from, const To& to, + size_t ROWSp, size_t COLSp) { size_t ROWS = ROWS_or_0 == 0 ? ROWSp : ROWS_or_0; size_t COLS = COLS_or_0 == 0 ? COLSp : COLS_or_0; for (size_t n = 0; n < ROWS; ++n) { @@ -64,9 +64,9 @@ constexpr bool TransposeUseSimd(size_t ROWS, size_t COLS) { } template <size_t ROWS_or_0, size_t COLS_or_0, class From, class To> -JXL_INLINE_TRANSPOSE void GenericTransposeBlock(TransposeSimdTag<true>, - const From& from, const To& to, - size_t ROWSp, size_t COLSp) { +JXL_INLINE_TRANSPOSE void GenericTransposeBlock( + TransposeSimdTag<true> /* tag */, const From& from, const To& to, + size_t ROWSp, size_t COLSp) { size_t ROWS = ROWS_or_0 == 0 ? ROWSp : ROWS_or_0; size_t COLS = COLS_or_0 == 0 ? COLSp : COLS_or_0; static_assert(MaxLanes(BlockDesc<8>()) == 8, "Invalid descriptor size"); @@ -128,9 +128,9 @@ constexpr bool TransposeUseSimd(size_t ROWS, size_t COLS) { } template <size_t ROWS_or_0, size_t COLS_or_0, class From, class To> -JXL_INLINE_TRANSPOSE void GenericTransposeBlock(TransposeSimdTag<true>, - const From& from, const To& to, - size_t ROWSp, size_t COLSp) { +JXL_INLINE_TRANSPOSE void GenericTransposeBlock( + TransposeSimdTag<true> /* tag */, const From& from, const To& to, + size_t ROWSp, size_t COLSp) { size_t ROWS = ROWS_or_0 == 0 ? ROWSp : ROWS_or_0; size_t COLS = COLS_or_0 == 0 ? COLSp : COLS_or_0; static_assert(MaxLanes(BlockDesc<4>()) == 4, "Invalid descriptor size"); diff --git a/third_party/jpeg-xl/lib/jxl/version.h.in b/third_party/jpeg-xl/lib/jxl/version.h.in index d077abec79..ad1eb24409 100644 --- a/third_party/jpeg-xl/lib/jxl/version.h.in +++ b/third_party/jpeg-xl/lib/jxl/version.h.in @@ -29,7 +29,7 @@ * #endif * @endcode */ -#define JPEGXL_COMPUTE_NUMERIC_VERSION(major,minor,patch) ((major<<24) | (minor<<16) | (patch<<8) | 0) +#define JPEGXL_COMPUTE_NUMERIC_VERSION(major,minor,patch) (((major)<<24) | ((minor)<<16) | ((patch)<<8) | 0) /* Numeric representation of the version */ #define JPEGXL_NUMERIC_VERSION JPEGXL_COMPUTE_NUMERIC_VERSION(JPEGXL_MAJOR_VERSION,JPEGXL_MINOR_VERSION,JPEGXL_PATCH_VERSION) diff --git a/third_party/jpeg-xl/lib/jxl_cms.cmake b/third_party/jpeg-xl/lib/jxl_cms.cmake index 47757c8f3b..04980066c1 100644 --- a/third_party/jpeg-xl/lib/jxl_cms.cmake +++ b/third_party/jpeg-xl/lib/jxl_cms.cmake @@ -62,8 +62,10 @@ install(TARGETS jxl_cms if (BUILD_SHARED_LIBS) set(JPEGXL_REQUIRES_TYPE "Requires.private") + set(JPEGXL_CMS_PRIVATE_LIBS "-lm ${PKGCONFIG_CXX_LIB}") else() set(JPEGXL_REQUIRES_TYPE "Requires") + set(JPEGXL_CMS_PRIVATE_LIBS "-lm ${PKGCONFIG_CXX_LIB}") endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/jxl/libjxl_cms.pc.in" diff --git a/third_party/jpeg-xl/lib/jxl_lists.bzl b/third_party/jpeg-xl/lib/jxl_lists.bzl index bbbc444b36..6c98ca15ed 100644 --- a/third_party/jpeg-xl/lib/jxl_lists.bzl +++ b/third_party/jpeg-xl/lib/jxl_lists.bzl @@ -188,8 +188,6 @@ libjxl_dec_sources = [ "jxl/epf.cc", "jxl/epf.h", "jxl/fast_dct-inl.h", - "jxl/fast_dct.cc", - "jxl/fast_dct.h", "jxl/fast_dct128-inl.h", "jxl/fast_dct16-inl.h", "jxl/fast_dct256-inl.h", @@ -216,6 +214,7 @@ libjxl_dec_sources = [ "jxl/image_bundle.h", "jxl/image_metadata.cc", "jxl/image_metadata.h", + "jxl/image_ops.cc", "jxl/image_ops.h", "jxl/inverse_mtf-inl.h", "jxl/lehmer_code.h", diff --git a/third_party/jpeg-xl/lib/jxl_lists.cmake b/third_party/jpeg-xl/lib/jxl_lists.cmake index e6bf4e5e22..d1a56f9ca8 100644 --- a/third_party/jpeg-xl/lib/jxl_lists.cmake +++ b/third_party/jpeg-xl/lib/jxl_lists.cmake @@ -185,8 +185,6 @@ set(JPEGXL_INTERNAL_DEC_SOURCES jxl/epf.cc jxl/epf.h jxl/fast_dct-inl.h - jxl/fast_dct.cc - jxl/fast_dct.h jxl/fast_dct128-inl.h jxl/fast_dct16-inl.h jxl/fast_dct256-inl.h @@ -213,6 +211,7 @@ set(JPEGXL_INTERNAL_DEC_SOURCES jxl/image_bundle.h jxl/image_metadata.cc jxl/image_metadata.h + jxl/image_ops.cc jxl/image_ops.h jxl/inverse_mtf-inl.h jxl/lehmer_code.h diff --git a/third_party/jpeg-xl/lib/jxl_tests.cmake b/third_party/jpeg-xl/lib/jxl_tests.cmake index eec4149fba..64f807cb82 100644 --- a/third_party/jpeg-xl/lib/jxl_tests.cmake +++ b/third_party/jpeg-xl/lib/jxl_tests.cmake @@ -76,7 +76,6 @@ foreach (TESTFILE IN LISTS JPEGXL_INTERNAL_TESTS) ${JPEGXL_COVERAGE_FLAGS} ) target_link_libraries(${TESTNAME} - gmock GTest::GTest GTest::Main jxl_testlib-internal diff --git a/third_party/jpeg-xl/lib/jxl_vars.bzl b/third_party/jpeg-xl/lib/jxl_vars.bzl index 2c8bccfdf2..fb00f6e34c 100644 --- a/third_party/jpeg-xl/lib/jxl_vars.bzl +++ b/third_party/jpeg-xl/lib/jxl_vars.bzl @@ -8,17 +8,18 @@ libjxl_root_package = "libjxl" libjxl_deps_brotli = ["@brotli//:brotlidec", "@brotli//:brotlienc"] -libjxl_deps_gif = ["@gif//:gif"] +libjxl_deps_exr = ["@openexr//:OpenEXR"] +libjxl_deps_gif = ["@giflib//:giflib"] libjxl_deps_gtest = ["@googletest//:gtest_main"] libjxl_deps_hwy = ["@highway//:hwy"] libjxl_deps_hwy_nanobenchmark = ["@highway//:nanobenchmark"] libjxl_deps_hwy_test_util = ["@highway//:hwy_test_util"] libjxl_deps_jpeg = ["@libjpeg_turbo//:jpeg"] -libjxl_deps_exr = ["@openexr//:OpenEXR"] libjxl_deps_png = ["@libpng//:png"] libjxl_deps_runfiles = ["@bazel_tools//tools/cpp/runfiles"] libjxl_deps_skcms = ["@skcms//:skcms"] libjxl_deps_testdata = ["//:testdata"] +libjxl_deps_webp = ["@libwebp//:webp"] libjxl_test_shards = { "jpegli/decode_api_test": 10, diff --git a/third_party/jpeg-xl/lib/nothing.cc b/third_party/jpeg-xl/lib/nothing.cc new file mode 100644 index 0000000000..e65f587da6 --- /dev/null +++ b/third_party/jpeg-xl/lib/nothing.cc @@ -0,0 +1,7 @@ +/* Copyright (c) the JPEG XL 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. + */ + +// Nothing at all. Just a compilation unit. diff --git a/third_party/jpeg-xl/lib/threads/thread_parallel_runner.cc b/third_party/jpeg-xl/lib/threads/thread_parallel_runner.cc index d12947ce55..558c9dbe42 100644 --- a/third_party/jpeg-xl/lib/threads/thread_parallel_runner.cc +++ b/third_party/jpeg-xl/lib/threads/thread_parallel_runner.cc @@ -41,11 +41,13 @@ bool ThreadMemoryManagerInit(JxlMemoryManager* self, } else { memset(self, 0, sizeof(*self)); } - if (!self->alloc != !self->free) { + bool is_default_alloc = (self->alloc == nullptr); + bool is_default_free = (self->free == nullptr); + if (is_default_alloc != is_default_free) { return false; } - if (!self->alloc) self->alloc = ThreadMemoryManagerDefaultAlloc; - if (!self->free) self->free = ThreadMemoryManagerDefaultFree; + if (is_default_alloc) self->alloc = ThreadMemoryManagerDefaultAlloc; + if (is_default_free) self->free = ThreadMemoryManagerDefaultFree; return true; } @@ -57,7 +59,7 @@ void* ThreadMemoryManagerAlloc(const JxlMemoryManager* memory_manager, void ThreadMemoryManagerFree(const JxlMemoryManager* memory_manager, void* address) { - return memory_manager->free(memory_manager->opaque, address); + memory_manager->free(memory_manager->opaque, address); } } // namespace diff --git a/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.cc b/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.cc index 5f73d94897..7ffcb34428 100644 --- a/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.cc +++ b/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.cc @@ -6,6 +6,7 @@ #include "lib/threads/thread_parallel_runner_internal.h" #include <jxl/parallel_runner.h> +#include <jxl/types.h> #include <algorithm> #include <atomic> @@ -120,9 +121,9 @@ void ThreadParallelRunner::RunRange(ThreadParallelRunner* self, // because it avoids user-specified parameters. for (;;) { -#if 0 - // dynamic - const uint32_t my_size = std::max(num_tasks / (num_worker_threads * 4), 1); +#if JXL_FALSE + // dynamic + const uint32_t my_size = std::max(num_tasks / (num_worker_threads * 4), 1); #else // guided const uint32_t num_reserved = diff --git a/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.h b/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.h index 199a5f2a8b..5b28384bf0 100644 --- a/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.h +++ b/third_party/jpeg-xl/lib/threads/thread_parallel_runner_internal.h @@ -132,8 +132,8 @@ class ThreadParallelRunner { // Attempts to reserve and perform some work from the global range of tasks, // which is encoded within "command". Returns after all tasks are reserved. - static void RunRange(ThreadParallelRunner* self, const WorkerCommand command, - const int thread); + static void RunRange(ThreadParallelRunner* self, WorkerCommand command, + int thread); static void ThreadFunc(ThreadParallelRunner* self, int thread); diff --git a/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader-jxl.c b/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader-jxl.c index bafa57b167..6a0de0486b 100644 --- a/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader-jxl.c +++ b/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader-jxl.c @@ -331,9 +331,8 @@ static gboolean load_increment(gpointer context, const guchar *buf, guint size, GError **error) { GdkPixbufJxlAnimation *decoder_state = context; if (decoder_state->done == TRUE) { - g_set_error(error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, - "JXL decoder load_increment called after end of file"); - return FALSE; + g_warning_once("Trailing data found at end of JXL file"); + return TRUE; } JxlDecoderStatus status; @@ -491,9 +490,8 @@ static gboolean load_increment(gpointer context, const guchar *buf, guint size, decoder_state->frames->len - 1) .data; decoder_state->pixel_format.align = gdk_pixbuf_get_rowstride(output); - guchar *dst = gdk_pixbuf_get_pixels(output); - size_t num_pixels = decoder_state->xsize * decoder_state->ysize; - size_t size = num_pixels * decoder_state->pixel_format.num_channels; + guint size; + guchar *dst = gdk_pixbuf_get_pixels_with_length(output, &size); if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer( decoder_state->decoder, &decoder_state->pixel_format, dst, size)) { diff --git a/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader_test.cc b/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader_test.cc index 5e5642d491..a2e5dc9703 100644 --- a/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader_test.cc +++ b/third_party/jpeg-xl/plugins/gdk-pixbuf/pixbufloader_test.cc @@ -16,12 +16,13 @@ int main(int argc, char* argv[]) { const char* loaders_cache = argv[1]; const char* filename = argv[2]; - setenv("GDK_PIXBUF_MODULE_FILE", loaders_cache, true); + const int kDoOverwrite = 1; + setenv("GDK_PIXBUF_MODULE_FILE", loaders_cache, kDoOverwrite); // XDG_DATA_HOME is the path where we look for the mime cache. // XDG_DATA_DIRS directories are used in addition to XDG_DATA_HOME. - setenv("XDG_DATA_HOME", ".", true); - setenv("XDG_DATA_DIRS", "", true); + setenv("XDG_DATA_HOME", ".", kDoOverwrite); + setenv("XDG_DATA_DIRS", "", kDoOverwrite); if (!gdk_init_check(nullptr, nullptr)) { fprintf(stderr, "This test requires a DISPLAY\n"); diff --git a/third_party/jpeg-xl/plugins/gimp/common.cc b/third_party/jpeg-xl/plugins/gimp/common.cc index 1a884570cb..a40340aaca 100644 --- a/third_party/jpeg-xl/plugins/gimp/common.cc +++ b/third_party/jpeg-xl/plugins/gimp/common.cc @@ -15,13 +15,10 @@ JpegXlGimpProgress::JpegXlGimpProgress(const char *message) { } void JpegXlGimpProgress::update() { - gimp_progress_update((float)++cur_progress / (float)max_progress); - return; + gimp_progress_update(static_cast<float>(++cur_progress) / + static_cast<float>(max_progress)); } -void JpegXlGimpProgress::finished() { - gimp_progress_update(1.0); - return; -} +void JpegXlGimpProgress::finished() { gimp_progress_update(1.0); } } // namespace jxl diff --git a/third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc b/third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc index 4796c1708c..07acd524d2 100644 --- a/third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc +++ b/third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc @@ -39,11 +39,14 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) { GimpColorProfile *profile_icc = nullptr; GimpColorProfile *profile_int = nullptr; bool is_linear = false; - unsigned long xsize = 0, ysize = 0; - long crop_x0 = 0, crop_y0 = 0; + unsigned long xsize = 0; + unsigned long ysize = 0; + long crop_x0 = 0; + long crop_y0 = 0; size_t layer_idx = 0; uint32_t frame_duration = 0; - double tps_denom = 1.f, tps_numer = 1.f; + double tps_denom = 1.f; + double tps_numer = 1.f; gint32 layer; @@ -356,13 +359,13 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) { const GString *blend_null_flag = g_string_new(""); const GString *blend_replace_flag = g_string_new(" (replace)"); const GString *blend_combine_flag = g_string_new(" (combine)"); - GString *blend; + const GString *blend; if (blend_mode == JXL_BLEND_REPLACE) { - blend = (GString *)blend_replace_flag; + blend = blend_replace_flag; } else if (blend_mode == JXL_BLEND_BLEND) { - blend = (GString *)blend_combine_flag; + blend = blend_combine_flag; } else { - blend = (GString *)blend_null_flag; + blend = blend_null_flag; } char *temp_frame_name = nullptr; bool must_free_frame_name = false; @@ -433,8 +436,10 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) { " Warning: JxlDecoderGetFrameHeader: Unhandled blend mode: %d\n", blend_mode); } - if ((frame_name_len = frame_header.name_length) > 0) { - frame_name = (char *)realloc(frame_name, frame_name_len); + frame_name_len = frame_header.name_length; + if (frame_name_len > 0) { + frame_name = + reinterpret_cast<char *>(realloc(frame_name, frame_name_len)); if (JXL_DEC_SUCCESS != JxlDecoderGetFrameName(dec.get(), frame_name, frame_name_len)) { g_printerr(LOAD_PROC "Error: JxlDecoderGetFrameName failed"); diff --git a/third_party/jpeg-xl/plugins/gimp/file-jxl-save.cc b/third_party/jpeg-xl/plugins/gimp/file-jxl-save.cc index 45aaa1f8df..284a9f2771 100644 --- a/third_party/jpeg-xl/plugins/gimp/file-jxl-save.cc +++ b/third_party/jpeg-xl/plugins/gimp/file-jxl-save.cc @@ -7,6 +7,7 @@ #include <jxl/encode.h> #include <jxl/encode_cxx.h> +#include <jxl/types.h> #include <cmath> #include <utility> @@ -229,7 +230,7 @@ bool JpegXlSaveGui::GuiOnChangeAdvancedMode(GtkWidget* toggle, gtk_widget_set_sensitive(self->frame_advanced, jxl_save_opts.advanced_mode); if (!jxl_save_opts.advanced_mode) { - jxl_save_opts.basic_info.uses_original_profile = false; + jxl_save_opts.basic_info.uses_original_profile = JXL_FALSE; gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(self->toggle_no_xyb), false); jxl_save_opts.use_container = true; @@ -290,19 +291,20 @@ bool JpegXlSaveGui::SaveDialog() { "\n\td\u00A0=\u00A03\tFair" "\n\td\u00A0=\u00A06\tPoor"; - entry_distance = (GtkAdjustment*)gimp_scale_entry_new( - GTK_TABLE(table), 0, 0, "Distance", SCALE_WIDTH, 0, - jxl_save_opts.distance, 0.0, 15.0, 0.001, 1.0, 3, true, 0.0, 0.0, - distance_help, SAVE_PROC); - gimp_scale_entry_set_logarithmic((GtkObject*)entry_distance, true); + entry_distance = reinterpret_cast<GtkAdjustment*>( + gimp_scale_entry_new(GTK_TABLE(table), 0, 0, "Distance", SCALE_WIDTH, 0, + jxl_save_opts.distance, 0.0, 15.0, 0.001, 1.0, 3, + true, 0.0, 0.0, distance_help, SAVE_PROC)); + gimp_scale_entry_set_logarithmic(reinterpret_cast<GtkObject*>(entry_distance), + true); // Quality Slider static gchar quality_help[] = "JPEG-style Quality is remapped to distance. " "Values roughly match libjpeg quality settings."; - entry_quality = (GtkAdjustment*)gimp_scale_entry_new( + entry_quality = reinterpret_cast<GtkAdjustment*>(gimp_scale_entry_new( GTK_TABLE(table), 0, 1, "Quality", SCALE_WIDTH, 0, jxl_save_opts.quality, - 8.26, 100.0, 1.0, 10.0, 2, true, 0.0, 0.0, quality_help, SAVE_PROC); + 8.26, 100.0, 1.0, 10.0, 2, true, 0.0, 0.0, quality_help, SAVE_PROC)); // Distance and Quality Signals handle_entry_distance = g_signal_connect( @@ -322,10 +324,10 @@ bool JpegXlSaveGui::SaveDialog() { "the encoder uses less effort to hit distance targets. " "As\u00A0a\u00A0result, image quality may be decreased. " "Default\u00A0=\u00A03."; - entry_effort = (GtkAdjustment*)gimp_scale_entry_new( - GTK_TABLE(table), 0, 3, "Speed", SCALE_WIDTH, 0, - 10 - jxl_save_opts.encoding_effort, 1, 9, 1, 2, 0, true, 0.0, 0.0, - effort_help, SAVE_PROC); + entry_effort = reinterpret_cast<GtkAdjustment*>( + gimp_scale_entry_new(GTK_TABLE(table), 0, 3, "Speed", SCALE_WIDTH, 0, + 10 - jxl_save_opts.encoding_effort, 1, 9, 1, 2, 0, + true, 0.0, 0.0, effort_help, SAVE_PROC)); // effort signal g_signal_connect(entry_effort, "value-changed", G_CALLBACK(GuiOnChangeEffort), @@ -415,10 +417,10 @@ bool JpegXlSaveGui::SaveDialog() { gtk_container_add(GTK_CONTAINER(vbox), table); gtk_widget_show(table); - entry_faster = (GtkAdjustment*)gimp_scale_entry_new( - GTK_TABLE(table), 0, 0, "Faster Decoding", SCALE_WIDTH, 0, - jxl_save_opts.faster_decoding, 0, 4, 1, 1, 0, true, 0.0, 0.0, faster_help, - SAVE_PROC); + entry_faster = reinterpret_cast<GtkAdjustment*>( + gimp_scale_entry_new(GTK_TABLE(table), 0, 0, "Faster Decoding", + SCALE_WIDTH, 0, jxl_save_opts.faster_decoding, 0, 4, + 1, 1, 0, true, 0.0, 0.0, faster_help, SAVE_PROC)); // Faster Decoding Signals g_signal_connect(entry_faster, "value-changed", @@ -472,7 +474,6 @@ JpegXlSaveOpts::JpegXlSaveOpts() { pixel_format.align = 0; JxlEncoderInitBasicInfo(&basic_info); - return; } // JpegXlSaveOpts constructor bool JpegXlSaveOpts::SetModel(bool is_linear_) { @@ -568,7 +569,8 @@ bool JpegXlSaveOpts::SetNumChannels(int channels) { pixel_format.num_channels = 2; basic_info.num_color_channels = 1; basic_info.num_extra_channels = 1; - basic_info.alpha_bits = int(std::fmin(16, basic_info.bits_per_sample)); + basic_info.alpha_bits = + static_cast<int>(std::fmin(16, basic_info.bits_per_sample)); basic_info.alpha_exponent_bits = 0; break; case 3: @@ -582,7 +584,8 @@ bool JpegXlSaveOpts::SetNumChannels(int channels) { pixel_format.num_channels = 4; basic_info.num_color_channels = 3; basic_info.num_extra_channels = 1; - basic_info.alpha_bits = int(std::fmin(16, basic_info.bits_per_sample)); + basic_info.alpha_bits = + static_cast<int>(std::fmin(16, basic_info.bits_per_sample)); basic_info.alpha_exponent_bits = 0; break; default: @@ -698,7 +701,7 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id, // treat layers as animation frames, for now if (nlayers > 1) { - jxl_save_opts.basic_info.have_animation = true; + jxl_save_opts.basic_info.have_animation = JXL_TRUE; jxl_save_opts.basic_info.animation.tps_numerator = 100; } @@ -738,12 +741,12 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id, jxl_save_opts.icc_attached = true; } else { g_printerr(SAVE_PROC " Warning: JxlEncoderSetICCProfile failed.\n"); - jxl_save_opts.basic_info.uses_original_profile = false; + jxl_save_opts.basic_info.uses_original_profile = JXL_FALSE; jxl_save_opts.lossless = false; } } else { g_printerr(SAVE_PROC " Warning: Using internal profile.\n"); - jxl_save_opts.basic_info.uses_original_profile = false; + jxl_save_opts.basic_info.uses_original_profile = JXL_FALSE; jxl_save_opts.lossless = false; } @@ -751,9 +754,11 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id, JxlColorEncoding color_encoding = {}; if (jxl_save_opts.is_linear) { - JxlColorEncodingSetToLinearSRGB(&color_encoding, jxl_save_opts.is_gray); + JxlColorEncodingSetToLinearSRGB(&color_encoding, + TO_JXL_BOOL(jxl_save_opts.is_gray)); } else { - JxlColorEncodingSetToSRGB(&color_encoding, jxl_save_opts.is_gray); + JxlColorEncodingSetToSRGB(&color_encoding, + TO_JXL_BOOL(jxl_save_opts.is_gray)); } if (JXL_ENC_SUCCESS != @@ -777,15 +782,15 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id, // lossless mode doesn't work well with floating point jxl_save_opts.distance = 0.01; jxl_save_opts.lossless = false; - JxlEncoderSetFrameLossless(frame_settings, false); + JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE); JxlEncoderSetFrameDistance(frame_settings, 0.01); } else { JxlEncoderSetFrameDistance(frame_settings, 0); - JxlEncoderSetFrameLossless(frame_settings, true); + JxlEncoderSetFrameLossless(frame_settings, JXL_TRUE); } } else { jxl_save_opts.lossless = false; - JxlEncoderSetFrameLossless(frame_settings, false); + JxlEncoderSetFrameLossless(frame_settings, JXL_FALSE); JxlEncoderSetFrameDistance(frame_settings, jxl_save_opts.distance); } diff --git a/third_party/jpeg-xl/plugins/mime/README.md b/third_party/jpeg-xl/plugins/mime/README.md index 4d398c7b90..6954a91e48 100644 --- a/third_party/jpeg-xl/plugins/mime/README.md +++ b/third_party/jpeg-xl/plugins/mime/README.md @@ -1,6 +1,6 @@ ## :warning: Not needed anymore -As `image/jxl` is now supported by [shared-mine-info 2.2](https://gitlab.freedesktop.org/xdg/shared-mime-info/-/releases/2.2), it should not be necessary anymore to install this plugin. +As `image/jxl` is now supported by [shared-mime-info 2.2](https://gitlab.freedesktop.org/xdg/shared-mime-info/-/releases/2.2), it should not be necessary anymore to install this plugin. You can test if your system correctly understand the MIME type of JPEG XL image by obtaining a JPEG XL image, e.g. with ```bash |