diff --git a/media/libjxl/moz.build b/media/libjxl/moz.build index a34f7627a631..5c00cce7b3c4 100644 --- a/media/libjxl/moz.build +++ b/media/libjxl/moz.build @@ -50,6 +50,7 @@ SOURCES += [ "/third_party/jpeg-xl/lib/jxl/image.cc", "/third_party/jpeg-xl/lib/jxl/image_bundle.cc", "/third_party/jpeg-xl/lib/jxl/image_metadata.cc", + "/third_party/jpeg-xl/lib/jxl/image_ops.cc", "/third_party/jpeg-xl/lib/jxl/loop_filter.cc", "/third_party/jpeg-xl/lib/jxl/luminance.cc", "/third_party/jpeg-xl/lib/jxl/memory_manager_internal.cc", diff --git a/media/libjxl/moz.yaml b/media/libjxl/moz.yaml index e860ac63a49c..d1fd1fb43581 100644 --- a/media/libjxl/moz.yaml +++ b/media/libjxl/moz.yaml @@ -10,9 +10,9 @@ origin: url: https://github.com/libjxl/libjxl - release: 07203da045f6b41f9b3b5b86023fd70b075137f6 (2024-01-29T17:41:05Z). + release: ab47708dcf002fae0164555b370aa36487df0f5d (2024-02-19T19:38:30Z). - revision: 07203da045f6b41f9b3b5b86023fd70b075137f6 + revision: ab47708dcf002fae0164555b370aa36487df0f5d license: Apache-2.0 diff --git a/third_party/jpeg-xl/AUTHORS b/third_party/jpeg-xl/AUTHORS index 04d436b8fe1a..ed6d72db6611 100644 --- a/third_party/jpeg-xl/AUTHORS +++ b/third_party/jpeg-xl/AUTHORS @@ -32,6 +32,7 @@ Zoltan Szabadka # Individuals: a-shvedov Aditya Patadia +Ahmad Amsyar Asyadiq Bin Syaiful Bahri <27284123+Ahmad-Amsyar@users.noreply.github.com> Alex Xu (Hello71) Alexander Sago Alistair Barrow @@ -74,6 +75,7 @@ Misaki Kasumi Moonchild Straver Nicholas Hayes <0xC0000054@users.noreply.github.com> Nigel Tao +oupson Petr Diblík Pieter Wuille roland-rollo @@ -93,4 +95,3 @@ xiota Yonatan Nebenzhal Ziemowit Zabawa 源文雨 <41315874+fumiama@users.noreply.github.com> -oupson diff --git a/third_party/jpeg-xl/CHANGELOG.md b/third_party/jpeg-xl/CHANGELOG.md index add193bf677d..e2affabd10cf 100644 --- a/third_party/jpeg-xl/CHANGELOG.md +++ b/third_party/jpeg-xl/CHANGELOG.md @@ -8,11 +8,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased ### Added - + - decoder API: added `JxlDecoderGetBoxSizeContents` for getting the size of the + content of a box without the headers. ### Removed ### Changed / clarified +## [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 +69,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 85f3c265c287..d6891ef79d66 100644 --- a/third_party/jpeg-xl/CMakeLists.txt +++ b/third_party/jpeg-xl/CMakeLists.txt @@ -161,7 +161,7 @@ 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 diff --git a/third_party/jpeg-xl/debian/libjxl-gdk-pixbuf.install b/third_party/jpeg-xl/debian/libjxl-gdk-pixbuf.install index 12d2ab250f97..91a4d49d2109 100644 --- a/third_party/jpeg-xl/debian/libjxl-gdk-pixbuf.install +++ b/third_party/jpeg-xl/debian/libjxl-gdk-pixbuf.install @@ -1,3 +1,2 @@ usr/lib/*/gdk-pixbuf-*/*/loaders/* -usr/share/mime/packages/image-jxl.xml usr/share/thumbnailers/jxl.thumbnailer diff --git a/third_party/jpeg-xl/deps.sh b/third_party/jpeg-xl/deps.sh index 70b3a15f0776..6c51a5cd4a21 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 aee5fd43ee27..8cdb09217e79 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/lib/CMakeLists.txt b/third_party/jpeg-xl/lib/CMakeLists.txt index 938f15dcbb0f..1e6f4abf7411 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/dec/apng.cc b/third_party/jpeg-xl/lib/extras/dec/apng.cc index f77dab77d1e7..31b4820a0a3e 100644 --- a/third_party/jpeg-xl/lib/extras/dec/apng.cc +++ b/third_party/jpeg-xl/lib/extras/dec/apng.cc @@ -813,6 +813,8 @@ Status DecodeImageAPNG(const Span 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 +832,7 @@ Status DecodeImageAPNG(const Span 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 { 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 5c6d7b84a07a..b1edd9f6eabf 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(value.data()); std::vector 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(value.data()); std::vector 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(value.data()); std::vector 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(value.data()); std::vector 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/jpegli.cc b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc index ffa1b79c25f5..b771274d45c6 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jpegli.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jpegli.cc @@ -188,7 +188,11 @@ Status DecodeJpeg(const std::vector& 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 = @@ -227,7 +231,7 @@ Status DecodeJpeg(const std::vector& 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.two_pass_quantize = static_cast(dparams.two_pass_quant); cinfo.dither_mode = (J_DITHER_MODE)dparams.dither_mode; } diff --git a/third_party/jpeg-xl/lib/extras/dec/jpg.cc b/third_party/jpeg-xl/lib/extras/dec/jpg.cc index 3c8a4bccfec5..0cec8320d05d 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jpg.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jpg.cc @@ -242,7 +242,11 @@ Status DecodeImageJPG(const Span 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 @@ -278,7 +282,7 @@ Status DecodeImageJPG(const Span 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.two_pass_quantize = static_cast(dparams->two_pass_quant); cinfo.dither_mode = (J_DITHER_MODE)dparams->dither_mode; } diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.cc b/third_party/jpeg-xl/lib/extras/dec/jxl.cc index f3e62c970af0..faa26a282259 100644 --- a/third_party/jpeg-xl/lib/extras/dec/jxl.cc +++ b/third_party/jpeg-xl/lib/extras/dec/jxl.cc @@ -351,24 +351,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 != diff --git a/third_party/jpeg-xl/lib/extras/dec/jxl.h b/third_party/jpeg-xl/lib/extras/dec/jxl.h index cbada1f6dd08..5f4ed7f68380 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::max(); diff --git a/third_party/jpeg-xl/lib/extras/enc/apng.cc b/third_party/jpeg-xl/lib/extras/enc/apng.cc index 413a9c8081a7..f2f8754b8010 100644 --- a/third_party/jpeg-xl/lib/extras/enc/apng.cc +++ b/third_party/jpeg-xl/lib/extras/enc/apng.cc @@ -344,11 +344,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); } diff --git a/third_party/jpeg-xl/lib/extras/enc/jpegli.cc b/third_party/jpeg-xl/lib/extras/enc/jpegli.cc index 7e1aa426df3e..e6db5cdecf28 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 { @@ -71,7 +72,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())); @@ -374,11 +375,11 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings, unsigned char* output_buffer = nullptr; unsigned long output_size = 0; std::vector row_bytes; - size_t rowlen = RoundUpTo(ppf.info.xsize, VectorSize()); + size_t rowlen = RoundUpTo(ppf.info.xsize, MaxVectorSize()); hwy::AlignedFreeUniquePtr xyb_tmp = hwy::AllocateAligned(6 * rowlen); hwy::AlignedFreeUniquePtr premul_absorb = - hwy::AllocateAligned(VectorSize() * 12); + hwy::AllocateAligned(MaxVectorSize() * 12); ComputePremulAbsorb(255.0f, premul_absorb.get()); jpeg_compress_struct cinfo; diff --git a/third_party/jpeg-xl/lib/extras/enc/jpg.cc b/third_party/jpeg-xl/lib/extras/enc/jpg.cc index c34dc6c13f0c..623ab3c155b3 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jpg.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jpg.cc @@ -284,7 +284,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(params.optimize_coding); if (cinfo.input_components == 3) { JXL_RETURN_IF_ERROR( SetChromaSubsampling(params.chroma_subsampling, &cinfo)); diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.cc b/third_party/jpeg-xl/lib/extras/enc/jxl.cc index 00adbb7ddaa2..fc4b1b8b6234 100644 --- a/third_party/jpeg-xl/lib/extras/enc/jxl.cc +++ b/third_party/jpeg-xl/lib/extras/enc/jxl.cc @@ -167,6 +167,16 @@ bool EncodeImageJXL(const JXLCompressParams& params, const PackedPixelFile& ppf, 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); @@ -248,7 +258,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"); diff --git a/third_party/jpeg-xl/lib/extras/enc/jxl.h b/third_party/jpeg-xl/lib/extras/enc/jxl.h index b8ca5bda2fca..2298e121f848 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 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. diff --git a/third_party/jpeg-xl/lib/extras/mmap.cc b/third_party/jpeg-xl/lib/extras/mmap.cc index 7852831ebd8f..3eeed72aa90e 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 #include #include @@ -97,6 +98,8 @@ struct MemoryMappedFileImpl { return f; } + ~MemoryMappedFileImpl() { UnmapViewOfFile(ptr); } + const uint8_t* data() const { return reinterpret_cast(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 edd5f1be7502..dd33171189d7 100644 --- a/third_party/jpeg-xl/lib/extras/packed_image.h +++ b/third_party/jpeg-xl/lib/extras/packed_image.h @@ -244,9 +244,21 @@ class PackedPixelFile { std::vector extra_channels_info; // Color information of the decoded pixels. - // If the icc is empty, the JxlColorEncoding should be used instead. - std::vector 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 icc; // The icc profile of the original image. std::vector orig_icc; 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 56f3b044a488..ef05c24c3d50 100644 --- a/third_party/jpeg-xl/lib/extras/packed_image_convert.cc +++ b/third_party/jpeg-xl/lib/extras/packed_image_convert.cc @@ -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])); @@ -113,7 +114,7 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf, 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())) { @@ -274,6 +275,9 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, // 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 @@ -304,7 +308,7 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io, 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_gbench.cc b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc index 34cbdde781e0..6a1c7e3495ab 100644 --- a/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc +++ b/third_party/jpeg-xl/lib/extras/tone_mapping_gbench.cc @@ -10,7 +10,7 @@ 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 +25,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 c164eaccb511..25c700867ac5 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 fb714842336f..e60eb0335541 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 928117e8dd82..e6325dcb3029 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 eaee70fa61b6..599b8336f3fc 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,12 +1378,28 @@ 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 @@ -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 2a87821f065a..8a321f07c767 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; @@ -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. + * 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 JxlPixelFormat: + * 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 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 e71e0aa92660..ea66685dbc0e 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 a65015d8618d..c82b0bc23b81 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 c9359dc870c8..5ed440636f35 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 6166fe7d36bc..933f373ce960 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 a47216f878aa..9a97fa2b3d7e 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 #include #include @@ -32,7 +31,7 @@ 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) /** Data type for the sample values per channel per pixel. @@ -40,7 +39,7 @@ extern "C" { 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 +91,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/libjpeg_test_util.cc b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc index de2303756e6a..7b0a21fe3b14 100644 --- a/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc +++ b/third_party/jpeg-xl/lib/jpegli/libjpeg_test_util.cc @@ -113,7 +113,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/jxl.cmake b/third_party/jpeg-xl/lib/jxl.cmake index 8c7e711f52f7..86fa37151d0b 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 3de477f71c74..e7a72f5a3324 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 #include -#include // iota -#include #include #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::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 ecdcbbbd32a0..bef9b2da3fc4 100644 --- a/third_party/jpeg-xl/lib/jxl/ac_strategy.h +++ b/third_party/jpeg-xl/lib/jxl/ac_strategy.h @@ -191,7 +191,8 @@ class AcStrategyRow { class AcStrategyImage { public: AcStrategyImage() = default; - AcStrategyImage(size_t xsize, size_t ysize); + static StatusOr Create(size_t xsize, size_t ysize); + AcStrategyImage(AcStrategyImage&&) = default; AcStrategyImage& operator=(AcStrategyImage&&) = default; diff --git a/third_party/jpeg-xl/lib/jxl/base/common.h b/third_party/jpeg-xl/lib/jxl/base/common.h index b7fe6ab0bcd9..79a69f118751 100644 --- a/third_party/jpeg-xl/lib/jxl/base/common.h +++ b/third_party/jpeg-xl/lib/jxl/base/common.h @@ -90,6 +90,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/status.h b/third_party/jpeg-xl/lib/jxl/base/status.h index b33bd64fc30d..2e88ba68aecc 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 #include +#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/blending.cc b/third_party/jpeg-xl/lib/jxl/blending.cc index ccb168ee4575..7575ec6e4af2 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& 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& 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 3f23297f1df4..94bead8dd68f 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& 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& 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 c34ab5c7caa9..9345d31a54b7 100644 --- a/third_party/jpeg-xl/lib/jxl/blending_test.cc +++ b/third_party/jpeg-xl/lib/jxl/blending_test.cc @@ -40,8 +40,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/butteraugli/butteraugli.cc b/third_party/jpeg-xl/lib/jxl/butteraugli/butteraugli.cc index 66dde9afb128..a3bc45885f4c 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 #include -#include #include -#include +#include #include +#include "lib/jxl/image.h" + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/butteraugli/butteraugli.cc" #include @@ -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 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 { @@ -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) { @@ -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 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> ButteraugliComparator::Make( + const Image3F& rgb0, const ButteraugliParams& params) { + size_t xsize = rgb0.xsize(); + size_t ysize = rgb0.ysize(); + std::unique_ptr result = + std::unique_ptr( + 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> 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(kMax, xsize); size_t yscaled = std::max(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(rgb0, rgb1, params, diffmap); } - ButteraugliComparator butteraugli(rgb0, params); - butteraugli.Diffmap(rgb1, diffmap); + JXL_ASSIGN_OR_RETURN(std::unique_ptr 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 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 29130e876861..e0bfd354e16c 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 #include +#include #include -#include #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> 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 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 c2ccf5617598..39df4bd473e4 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(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/chroma_from_luma.cc b/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc index 63d21cbb4b5b..69585c44cf99 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::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 cb3b71076214..1f2353d9af26 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 #include -#include +#include -#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 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/jxl_cms.cc b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc index dd00b8b81ffd..b0fe1eb53285 100644 --- a/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc +++ b/third_party/jpeg-xl/lib/jxl/cms/jxl_cms.cc @@ -971,14 +971,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(rendering_intent32); + c_enc.rendering_intent = + static_cast(candidate_rendering_intent); if (profile.has_CICP && ApplyCICP(profile.CICP.color_primaries, 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 ca50c9960e85..2946cbb611aa 100644 --- a/third_party/jpeg-xl/lib/jxl/color_management_test.cc +++ b/third_party/jpeg-xl/lib/jxl/color_management_test.cc @@ -49,9 +49,9 @@ using ::testing::ElementsAre; using ::testing::FloatNear; // Small enough to be fast. If changed, must update Generate*. -static constexpr size_t kWidth = 16; +constexpr size_t kWidth = 16; -static constexpr size_t kNumThreads = 1; // only have a single row. +constexpr size_t kNumThreads = 1; // only have a single row. MATCHER_P(HasSameFieldsAs, expected, "") { if (arg.GetRenderingIntent() != expected.GetRenderingIntent()) { @@ -106,15 +106,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 +124,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); @@ -373,7 +373,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 +386,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); @@ -403,7 +403,7 @@ TEST_F(ColorManagementTest, XYBProfile) { float* dst = xform.BufDst(0); ASSERT_TRUE(xform.Run(0, src, dst)); - 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 d619711c9f20..d5932444330e 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 b21b1da18b81..b3332d2fee50 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 #include -#include -#include -#include #include #undef HWY_TARGET_INCLUDE @@ -21,17 +18,9 @@ #include #include -#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,18 +120,18 @@ 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}) { @@ -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 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,14 +288,14 @@ 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); diff --git a/third_party/jpeg-xl/lib/jxl/compressed_dc.h b/third_party/jpeg-xl/lib/jxl/compressed_dc.h index b06e5931f09a..30259ebd563f 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_test.cc b/third_party/jpeg-xl/lib/jxl/convolve_test.cc index 6a8dc9c400b8..8bd4851f5d47 100644 --- a/third_party/jpeg-xl/lib/jxl/convolve_test.cc +++ b/third_party/jpeg-xl/lib/jxl/convolve_test.cc @@ -68,11 +68,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); @@ -96,7 +96,7 @@ std::vector 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 +105,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 +120,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 +140,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 +199,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; diff --git a/third_party/jpeg-xl/lib/jxl/dct_util.h b/third_party/jpeg-xl/lib/jxl/dct_util.h index 2f2944967732..90a02658af72 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 -#include "lib/jxl/base/compiler_specific.h" -#include "lib/jxl/base/data_parallel.h" +#include + +#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 class ACImageT final : public ACImage { public: ACImageT() = default; - ACImageT(size_t xsize, size_t ysize) { + + static StatusOr> Make(size_t xsize, size_t ysize) { static_assert( std::is_same::value || std::is_same::value, "ACImage must be either 32- or 16- bit"); - img_ = Image3(xsize, ysize); + std::unique_ptr result = jxl::make_unique(); + JXL_ASSIGN_OR_RETURN(result->img_, Image3::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_cache.cc b/third_party/jpeg-xl/lib/jxl/dec_cache.cc index 8d12bce02ea8..d413241ed66a 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" @@ -257,7 +258,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 d4cc7a19571e..d0310745322f 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 used_acs{0}; // Storage for coefficients if in "accumulate" mode. - std::unique_ptr coefficients = make_unique>(0, 0); + std::unique_ptr coefficients = make_unique>(); // Rendering pipeline. std::unique_ptr 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_external_image.cc b/third_party/jpeg-xl/lib/jxl/dec_external_image.cc index 06cd5733782e..59ebf7435d2c 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 #include -#include -#include #include #include +#include "lib/jxl/base/status.h" + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/dec_external_image.cc" #include @@ -113,7 +113,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, const size_t ysize = image.ysize(); if (undo_orientation == Orientation::kFlipHorizontal) { - out = Plane(xsize, ysize); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(xsize, ysize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -126,7 +126,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kRotate180) { - out = Plane(xsize, ysize); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(xsize, ysize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -139,7 +139,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kFlipVertical) { - out = Plane(xsize, ysize); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(xsize, ysize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -152,7 +152,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kTranspose) { - out = Plane(ysize, xsize); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -164,7 +164,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kRotate90) { - out = Plane(ysize, xsize); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -176,7 +176,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kAntiTranspose) { - out = Plane(ysize, xsize); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -188,7 +188,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane& image, }, "UndoOrientation")); } else if (undo_orientation == Orientation::kRotate270) { - out = Plane(ysize, xsize); + JXL_ASSIGN_OR_RETURN(out, Plane::Create(ysize, xsize)); JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), ThreadPool::NoInit, [&](const uint32_t task, size_t /*thread*/) { @@ -309,7 +309,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 +322,12 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), [&](size_t num_threads) { - f16_cache = - Plane(xsize, num_channels * num_threads); - return InitOutCallback(num_threads); + StatusOr> f16_cache_or = + Plane::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 +401,11 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[], JXL_RETURN_IF_ERROR(RunOnPool( pool, 0, static_cast(ysize), [&](size_t num_threads) { - u32_cache = Plane(xsize, num_channels * num_threads); - return InitOutCallback(num_threads); + StatusOr> u32_cache_or = + Plane::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 +459,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 c87a4d5f367a..1a8abb11aef8 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 @@ -20,10 +20,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 918dbe7c37ee..4b27b9181265 100644 --- a/third_party/jpeg-xl/lib/jxl/dec_frame.cc +++ b/third_party/jpeg-xl/lib/jxl/dec_frame.cc @@ -335,17 +335,19 @@ Status FrameDecoder::ProcessDCGroup(size_t dc_group_id, BitReader* br) { 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>(xs, ys); + JXL_ASSIGN_OR_RETURN(dec_state_->coefficients, + ACImageT::Make(xs, ys)); } else { - dec_state_->coefficients = make_unique>(xs, ys); + JXL_ASSIGN_OR_RETURN(dec_state_->coefficients, + ACImageT::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,9 +502,12 @@ 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", (int)ac_group_id, + (int)i); if (i < pass0 + num_passes) { JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup( frame_header_, mrect, br[i - pass0], minShift, maxShift, @@ -546,7 +553,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,7 +562,7 @@ 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) { @@ -645,9 +652,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; } @@ -666,7 +675,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 +785,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 +825,10 @@ 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_) { + for (const auto& have_dc_group : decoded_dc_groups_) { if (!have_dc_group) return false; } - for (auto& nb_passes : decoded_passes_per_ac_group_) { + for (const auto& nb_passes : decoded_passes_per_ac_group_) { if (nb_passes < frame_header_.passes.num_passes) return false; } return true; diff --git a/third_party/jpeg-xl/lib/jxl/dec_frame.h b/third_party/jpeg-xl/lib/jxl/dec_frame.h index 09bdbc9675af..35964d418886 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 @@ -272,7 +272,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; } diff --git a/third_party/jpeg-xl/lib/jxl/dec_group.cc b/third_party/jpeg-xl/lib/jxl/dec_group.cc index 186318e63d31..bc5ecea71912 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; +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(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); @@ -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_modular.cc b/third_party/jpeg-xl/lib/jxl/dec_modular.cc index 4fcba489e28c..15d9190537e3 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 #include -#include #include #include "lib/jxl/frame_header.h" @@ -18,10 +17,8 @@ #include #include -#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" @@ -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)", + (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, @@ -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_patch_dictionary.cc b/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.cc index 0ae222325211..81920ed2b4ff 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 #include -#include -#include #include #include -#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" @@ -322,8 +312,8 @@ std::vector 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 fg_ptrs(3 + num_ec); for (size_t pos_idx : GetPatchesForRow(y)) { @@ -352,10 +342,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 aac6111ae69c..72dca8f057a3 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 #include -#include #include #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/decode.cc b/third_party/jpeg-xl/lib/jxl/decode.cc index b674d1ba885c..3f473ce16f92 100644 --- a/third_party/jpeg-xl/lib/jxl/decode.cc +++ b/third_party/jpeg-xl/lib/jxl/decode.cc @@ -2348,29 +2348,40 @@ 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, 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 +2412,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 +2463,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, @@ -2798,6 +2783,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 caee6dbc5680..9ef220345507 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* box_brob_exif = reinterpret_cast( "\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* exif_uncompressed = reinterpret_cast( "\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(" @@ -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 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)); } @@ -321,7 +322,7 @@ std::vector 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(), @@ -622,6 +623,10 @@ std::vector DecodeWithAPI(Span compressed, //////////////////////////////////////////////////////////////////////////////// +using jxl::Image3F; +using jxl::ImageF; +using jxl::test::ButteraugliDistance; + TEST(DecodeTest, JxlSignatureCheckTest) { std::vector>> tests = { // No JPEGXL header starts with 'a'. @@ -1420,7 +1425,7 @@ std::vector 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; @@ -1577,7 +1582,8 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeTest, DecodeTestParam, TEST(DecodeTest, PixelTestWithICCProfileLossless) { JxlDecoder* dec = JxlDecoderCreate(NULL); - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; size_t num_pixels = xsize * ysize; std::vector pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -1644,7 +1650,8 @@ TEST(DecodeTest, PixelTestWithICCProfileLossless) { TEST(DecodeTest, PixelTestWithICCProfileLossy) { JxlDecoder* dec = JxlDecoderCreate(NULL); - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; size_t num_pixels = xsize * ysize; std::vector pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -1753,7 +1760,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 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 +1804,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 +1979,7 @@ void DecodeImageWithColorEncoding(const std::vector& 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 +2019,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); jxl::TestCodestreamParams params; @@ -2047,7 +2058,8 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossy) { for (unsigned channels = 3; channels <= 4; channels++) { JxlDecoder* dec = JxlDecoderCreate(NULL); - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; size_t num_pixels = xsize * ysize; std::vector pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); @@ -2096,7 +2108,8 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) { for (unsigned channels = 3; channels <= 4; channels++) { JxlDecoder* dec = JxlDecoderCreate(NULL); - size_t xsize = 512, ysize = 300; + size_t xsize = 512; + size_t ysize = 300; size_t num_pixels = xsize * ysize; std::vector pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); @@ -2142,7 +2155,8 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) { } TEST(DecodeTest, ProcessEmptyInputWithBoxes) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; std::vector pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); jxl::CompressParams cparams; uint32_t channels = 3; @@ -2175,7 +2189,8 @@ 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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); jxl::CompressParams cparams; @@ -2217,7 +2232,8 @@ 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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); jxl::CompressParams cparams; @@ -2251,7 +2267,8 @@ 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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); jxl::CompressParams cparams; @@ -2300,7 +2317,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; @@ -2487,7 +2505,8 @@ TEST(DecodeTest, DCNotGettableTest) { } TEST(DecodeTest, PreviewTest) { - size_t xsize = 77, ysize = 120; + size_t xsize = 77; + size_t ysize = 120; std::vector 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}) { @@ -2561,7 +2580,8 @@ TEST(DecodeTest, PreviewTest) { } TEST(DecodeTest, AlignTest) { - size_t xsize = 123, ysize = 77; + size_t xsize = 123; + size_t ysize = 77; std::vector pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -2575,7 +2595,11 @@ 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) { std::vector pixels2 = jxl::DecodeWithAPI( @@ -2583,14 +2607,15 @@ TEST(DecodeTest, AlignTest) { /*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 frames[2]; frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); @@ -2690,7 +2715,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 frames[2]; frames[0] = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0); @@ -2822,7 +2848,8 @@ TEST(DecodeTest, AnimationTestStreaming) { } TEST(DecodeTest, ExtraChannelTest) { - size_t xsize = 55, ysize = 257; + size_t xsize = 55; + size_t ysize = 257; std::vector pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -2899,7 +2926,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 frames[num_frames]; for (size_t i = 0; i < num_frames; i++) { @@ -3010,7 +3038,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 frames[num_frames]; for (size_t i = 0; i < num_frames; i++) { @@ -3146,7 +3175,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 frames[num_frames]; JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -3360,7 +3390,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 frames[num_frames + 5]; JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0}; @@ -3376,7 +3407,10 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) { std::vector frame_durations_c; std::vector frame_durations_nc; - std::vector frame_xsize, frame_ysize, frame_x0, frame_y0; + std::vector frame_xsize; + std::vector frame_ysize; + std::vector frame_x0; + std::vector frame_y0; for (size_t i = 0; i < num_frames; ++i) { size_t cropxsize = 1 + xsize * 2 / (i + 1); @@ -3648,7 +3682,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); @@ -3985,7 +4020,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4020,7 +4056,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4063,7 +4100,8 @@ void VerifyFilePosition(size_t expected_pos, const std::vector& 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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4246,7 +4284,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4346,7 +4385,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4421,7 +4461,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4507,7 +4548,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4577,7 +4619,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4651,7 +4694,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0); @@ -4741,7 +4785,8 @@ TEST_P(DecodeProgressiveTest, ProgressiveEventTest) { // 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; + size_t xsize; + size_t ysize; if (single_group) { // An image smaller than 256x256 ensures it contains only 1 group. xsize = 99; @@ -4982,7 +5027,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); jxl::TestCodestreamParams params; params.box_format = kCSBF_Multi_Other_Terminated; @@ -5038,7 +5084,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 +5100,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); jxl::TestCodestreamParams params; params.box_format = kCSBF_Multi_Other_Terminated; @@ -5096,6 +5152,7 @@ TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) { JxlBoxType type; uint64_t box_size; + uint64_t contents_size; std::vector contents(50); size_t expected_release_size = 0; @@ -5113,6 +5170,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 +5207,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 pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0); jxl::TestCodestreamParams params; // Lossless to verify pixels exactly after roundtrip. @@ -5328,7 +5389,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 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 +5544,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 +5571,7 @@ TEST(DecodeTest, SpotColorTest) { info.spot_color[3] = 0.5f; io.metadata.m.extra_channel_info.push_back(info); - std::vector ec; + std::vector ec; ec.push_back(std::move(spot)); io.frames[0].SetExtraChannels(std::move(ec)); 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 d3b5ad3269a7..a939eae6280d 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 #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 @@ -1102,9 +1097,9 @@ void AcStrategyHeuristics::ProcessRect(const Rect& rect, (cparams, config, rect, cmap, 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 +1136,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 9f6d92a6f796..a767d18ef11f 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h +++ b/third_party/jpeg-xl/lib/jxl/enc_ac_strategy.h @@ -58,14 +58,15 @@ struct ACSConfig { }; struct AcStrategyHeuristics { - AcStrategyHeuristics(const CompressParams& cparams) : cparams(cparams) {} + explicit AcStrategyHeuristics(const CompressParams& cparams) + : cparams(cparams) {} void Init(const Image3F& src, const Rect& rect_in, const ImageF& quant_field, const ImageF& mask, const ImageF& mask1x1, DequantMatrices* matrices); void ProcessRect(const Rect& rect, const ColorCorrelationMap& cmap, AcStrategyImage* ac_strategy); - void Finalize(const FrameDimensions& frame_dim, - const AcStrategyImage& ac_strategy, AuxOut* aux_out); + Status Finalize(const FrameDimensions& frame_dim, + const AcStrategyImage& ac_strategy, AuxOut* aux_out); const CompressParams& cparams; ACSConfig config; }; 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 ae4cd3bd3bd4..11cb662a156a 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 #include +#include #include #include #include @@ -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 V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) { @@ -131,7 +130,7 @@ V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) { } template -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 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 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 RoundtripImage(const FrameHeader& frame_header, + const Image3F& opsin, + PassesEncoderState* enc_state, + const JxlCmsInterface& cms, + ThreadPool* pool) { std::unique_ptr dec_state = jxl::make_unique(); 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(num_threads); return true; }; + std::atomic 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 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, @@ -1001,13 +1028,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 +1054,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 +1104,7 @@ void FindBestQuantizationMaxError(const FrameHeader& frame_header, } } quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field); + return true; } } // namespace @@ -1142,28 +1174,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 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 6aa8b10df663..26ed3f26ca39 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 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 3efa62d8e19e..1f4df437af32 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_ans.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_ans.cc @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -20,12 +21,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 { @@ -553,8 +557,7 @@ void ChooseUintConfigs(const HistogramParams& params, std::vector* 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 +573,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 configs; if (params.uint_method == HistogramParams::HybridUintMethod::kBest) { @@ -1765,7 +1774,8 @@ void WriteTokens(const std::vector& tokens, const EntropyEncodingData& codes, const std::vector& 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 +1789,51 @@ void SetANSFuzzerFriendly(bool ans_fuzzer_friendly) { ans_fuzzer_friendly_ = ans_fuzzer_friendly; #endif } + +HistogramParams HistogramParams::ForModular( + const CompressParams& cparams, + const std::vector& 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_params.h b/third_party/jpeg-xl/lib/jxl/enc_ans_params.h index 86664f593e4b..23921097b48d 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 #include -#include "lib/jxl/enc_params.h" +#include + +#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& 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 ed8a42d29998..e80771248e57 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(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 fe602c16e3ea..f3c5a97a1b15 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 temp_images; }; 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 019d6125a2d9..b20fa751c186 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 -#include - +#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& frames0, - const std::vector& 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 641d7732d51f..4f70e21a1b15 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 #include -#include -#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& frames0, - const std::vector& 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 ff62c57e4d37..5b23959c8601 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 #include -#include +#include -#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>( - kGroupDim * kGroupDim, shared.frame_dim.num_groups)); + JXL_ASSIGN_OR_RETURN( + std::unique_ptr> coeffs, + ACImageT::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 _) { @@ -120,7 +124,8 @@ Status InitializePassesEncoder(const FrameHeader& frame_header, std::vector 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 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 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_chroma_from_luma.cc b/third_party/jpeg-xl/lib/jxl/enc_chroma_from_luma.cc index 9a894d89ccc7..4039da28583c 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 #include -#include #include #undef HWY_TARGET_INCLUDE @@ -18,18 +17,13 @@ #include #include -#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 04743842bf22..c6481a0ec979 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(num_threads * ItemsPerThread()); diff --git a/third_party/jpeg-xl/lib/jxl/enc_cluster.h b/third_party/jpeg-xl/lib/jxl/enc_cluster.h index 923aaaccfe2a..a9e31b551719 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 #include "lib/jxl/ans_params.h" +#include "lib/jxl/base/common.h" #include "lib/jxl/enc_ans_params.h" namespace jxl { diff --git a/third_party/jpeg-xl/lib/jxl/enc_comparator.cc b/third_party/jpeg-xl/lib/jxl/enc_comparator.cc index 268122af06e2..3ef6a49b8f69 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 c545ea611100..ee62ab6f2844 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 6968a6fbae85..d4909dc9195a 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_context_map.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_context_map.cc @@ -18,6 +18,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 { 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 261570e69019..d67ab7db46e7 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 -Plane ConvertToFloat(const Plane& from) { +StatusOr ConvertToFloat(const Image3& from) { float factor = 1.0f / std::numeric_limits::max(); if (std::is_same::value || std::is_same::value) { factor = 1.0f; } - Plane 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 -Image3F ConvertToFloat(const Image3& from) { - return Image3F(ConvertToFloat(from.Plane(0)), ConvertToFloat(from.Plane(1)), - ConvertToFloat(from.Plane(2))); -} template -void DumpImageT(const CompressParams& cparams, const char* label, - const ColorEncoding& color_encoding, const Image3& 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& 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 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 -void DumpPlaneNormalizedT(const CompressParams& cparams, const char* label, - const Plane& image) { +Status DumpPlaneNormalizedT(const CompressParams& cparams, const char* label, + const Plane& 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& image) { - DumpImageT(cparams, label, ColorEncoding::SRGB(), image); +Status DumpImage(const CompressParams& cparams, const char* label, + const Image3& image) { + return DumpImageT(cparams, label, ColorEncoding::SRGB(), image); } -void DumpImage(const CompressParams& cparams, const char* label, - const Image3& image) { - DumpImageT(cparams, label, ColorEncoding::SRGB(), image); +Status DumpImage(const CompressParams& cparams, const char* label, + const Image3& 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& image) { - DumpPlaneNormalizedT(cparams, label, image); +Status DumpPlaneNormalized(const CompressParams& cparams, const char* label, + const Plane& image) { + return DumpPlaneNormalizedT(cparams, label, image); } -void DumpPlaneNormalized(const CompressParams& cparams, const char* label, - const Plane& image) { - DumpPlaneNormalizedT(cparams, label, image); +Status DumpPlaneNormalized(const CompressParams& cparams, const char* label, + const Plane& 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 33799a5f7f39..428293f8bc0a 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& image); -void DumpImage(const CompressParams& cparams, const char* label, - const Image3& image); -void DumpXybImage(const CompressParams& cparams, const char* label, - const Image3& image); -void DumpPlaneNormalized(const CompressParams& cparams, const char* label, - const Plane& image); -void DumpPlaneNormalized(const CompressParams& cparams, const char* label, - const Plane& image); +Status DumpImage(const CompressParams& cparams, const char* label, + const Image3& image); +Status DumpImage(const CompressParams& cparams, const char* label, + const Image3& image); +Status DumpXybImage(const CompressParams& cparams, const char* label, + const Image3& image); +Status DumpPlaneNormalized(const CompressParams& cparams, const char* label, + const Plane& image); +Status DumpPlaneNormalized(const CompressParams& cparams, const char* label, + const Plane& 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 4ee8808766d0..4e9d9c155987 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 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 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(); @@ -266,7 +268,10 @@ struct ConnectedComponent { Rect BoundingRectangle(const std::vector& 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,11 +283,13 @@ Rect BoundingRectangle(const std::vector& pixels) { return Rect(low_x, low_y, high_x - low_x + 1, high_y - low_y + 1); } -std::vector FindCC(const ImageF& energy, double t_low, - double t_high, uint32_t maxWindow, - double minScore) { +StatusOr> FindCC(const ImageF& energy, + 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 ans; for (size_t y = 0; y < img.ysize(); y++) { @@ -328,7 +335,8 @@ void ComputeDotLosses(GaussianEllipse* ellipse, const ConnectedComponent& cc, 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 channelGains{{1.0, 1.0, 1.0}}; int N = 0; ellipse->l1_loss = 0.0; @@ -450,14 +458,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 +476,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; @@ -522,14 +533,16 @@ GaussianEllipse FitGaussian(const ConnectedComponent& cc, const ImageF& energy, } // namespace -std::vector DetectGaussianEllipses( +StatusOr> DetectGaussianEllipses( const Image3F& opsin, const GaussianDetectParams& params, const EllipseQuantParams& qParams, ThreadPool* pool) { std::vector dots; - Image3F smooth(opsin.xsize(), opsin.ysize()); - ImageF energy = ComputeEnergyImage(opsin, &smooth, pool); - std::vector 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 components, + FindCC(energy, 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) { 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 c3071d9a2f79..2e693c0b2948 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 #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,7 +57,7 @@ struct EllipseQuantParams { }; // Detects dots in XYB image. -std::vector DetectGaussianEllipses( +StatusOr> DetectGaussianEllipses( const Image3F& opsin, const GaussianDetectParams& params, const EllipseQuantParams& qParams, ThreadPool* pool); 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 a5b1af63b20e..d87419b1df3f 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 #include -#include #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 kEllipseMaxIntensity{{0.05, 1.0, 0.4}}; const std::array kEllipseIntensityQ{{10, 36, 10}}; } // namespace -std::vector FindDotDictionary(const CompressParams& cparams, - const Image3F& opsin, - const ColorCorrelationMap& cmap, - ThreadPool* pool) { +StatusOr> FindDotDictionary( + const CompressParams& cparams, const Image3F& opsin, + const ColorCorrelationMap& cmap, ThreadPool* pool) { if (ApplyOverride(cparams.dots, cparams.butteraugli_distance >= kMinButteraugliForDots)) { GaussianDetectParams ellipse_params; @@ -66,6 +60,7 @@ std::vector FindDotDictionary(const CompressParams& cparams, return DetectGaussianEllipses(opsin, ellipse_params, qParams, pool); } - return {}; + std::vector 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 2ba4393f30f1..cf45894a06f7 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 FindDotDictionary(const CompressParams& cparams, - const Image3F& opsin, - const ColorCorrelationMap& cmap, - ThreadPool* pool); +StatusOr> FindDotDictionary( + const CompressParams& cparams, const Image3F& opsin, + const ColorCorrelationMap& cmap, ThreadPool* pool); } // namespace jxl 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 680323e79a4e..90e4937e7be3 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 #include -#include -#include #include -#include #include -#include -#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 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 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 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 b32d2478e0d5..4b8f748bad2e 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_fast_lossless.cc @@ -3735,10 +3735,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(buffer); - int y_begin = std::max(0, ys - 2 * effort) / 2; - int y_count = std::min(num_rows, y0 + ys - y_begin - 1); + int y_begin_group = + std::max( + 0, static_cast(ys) - static_cast(num_rows)) / + 2; + int y_count = std::min(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); diff --git a/third_party/jpeg-xl/lib/jxl/enc_frame.cc b/third_party/jpeg-xl/lib/jxl/enc_frame.cc index aae59c49a614..0f5dfe14f923 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 #include #include -#include +#include #include +#include #include #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 @@ -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); enc_state->coeffs.clear(); while (enc_state->coeffs.size() < enc_state->passes.size()) { - enc_state->coeffs.emplace_back(make_unique>( - kGroupDim * kGroupDim, frame_dim.num_groups)); + JXL_ASSIGN_OR_RETURN( + std::unique_ptr> coeffs, + ACImageT::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) { @@ -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 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 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, @@ -1308,35 +1333,43 @@ Status EncodeGroups(const FrameHeader& frame_header, enc_state, get_output(global_ac_index), enc_modular, aux_out)); } - std::atomic num_errors{0}; + std::atomic 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_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(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 +1393,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 +1440,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 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 +1478,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 +1494,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 +1515,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 +1555,21 @@ Status ComputeEncodingData( TokenizeAllCoefficients(frame_header, pool, &enc_state)); } - 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)); - } - JXL_RETURN_IF_ERROR(enc_modular.ComputeTree(pool)); - JXL_RETURN_IF_ERROR(enc_modular.ComputeTokens(pool)); + 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.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)); + } mutable_frame_header.UpdateFlag(shared.image_features.patches.HasAny(), FrameHeader::kPatches); mutable_frame_header.UpdateFlag(shared.image_features.splines.HasAny(), @@ -1526,6 +1582,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,6 +1671,25 @@ bool CanDoStreamingEncoding(const CompressParams& cparams, const FrameInfo& frame_info, const CodecMetadata& metadata, const JxlEncoderChunkedFrameAdapter& frame_data) { + if (cparams.buffering == 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; + } + } + + // TODO(veluca): handle different values of `buffering`. + if (frame_data.xsize <= 2048 && frame_data.ysize <= 2048) { + return false; + } if (frame_data.IsJPEG()) { return false; } @@ -1629,34 +1705,24 @@ bool CanDoStreamingEncoding(const CompressParams& cparams, if (cparams.max_error_mode) { return false; } - if (cparams.color_transform != ColorTransform::kXYB) { - return false; + if (!cparams.ModularPartIsLossless() || cparams.responsive > 0) { + if (metadata.m.num_extra_channels > 0 || cparams.modular_mode) { + return false; + } } - if (cparams.modular_mode) { - return false; - } - if (metadata.m.num_extra_channels > 0) { - return false; - } - if (cparams.buffering == 0) { - return false; - } - if (cparams.buffering == 1 && frame_data.xsize <= 2048 && - frame_data.ysize <= 2048) { - 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& permutation, std::vector& 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); @@ -1864,14 +1930,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 permutation; std::vector 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; @@ -1931,9 +1996,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 +2044,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 +2077,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; } - if (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::kTectonicPlate) { std::vector all_params; std::vector 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}) { @@ -2054,11 +2129,12 @@ Status EncodeFrame(const CompressParams& cparams_orig, size.resize(all_params.size()); - std::atomic num_errors{0}; + std::atomic 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 output(64); uint8_t* next_out = output.data(); size_t avail_out = output.size(); @@ -2066,13 +2142,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 +2232,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 +2245,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_gaborish.cc b/third_party/jpeg-xl/lib/jxl/enc_gaborish.cc index 3f2ee32afdcb..7467a4d66990 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 ece4959f3606..041edcec96c6 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 426f08ecb09d..0e71bf985693 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_gaborish_test.cc @@ -40,7 +40,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,12 +51,12 @@ 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); } @@ -64,7 +64,7 @@ TEST(GaborishTest, TestZero) { // Disabled: large difference. #if 0 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 +72,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 09bab534c9ff..9e50290af076 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_group.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_group.cc @@ -316,7 +316,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 +337,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, diff --git a/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc b/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc index 9d6bf11184bc..10efc99dde81 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_heuristics.cc @@ -190,9 +190,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 +204,14 @@ void FindBestDequantMatrices(const CompressParams& cparams, DctQuantWeightParams dct_params(weights); std::vector 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 +227,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 +259,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 +316,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 +381,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 +441,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 +498,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) { @@ -599,9 +605,7 @@ void ReduceRinging(const ImageF& initial, const ImageF& mask, ImageF& down) { 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; + if (x2 < 0 || y2 < 0 || x2 >= xsize2 || y2 >= ysize2) continue; min = std::min(min, initial.Row(y2)[x2]); max = std::max(max, initial.Row(y2)[x2]); } @@ -625,32 +629,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 +666,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(1, orig, -1, up); + JXL_ASSIGN_OR_RETURN(corr, LinComb(1, orig, -1, up)); ElwiseMul(corr, weights, &corr); AntiUpsample(corr, &corr2); ElwiseDiv(corr2, weights2, &corr2); - down = LinComb(1, down, 1, corr2); + JXL_ASSIGN_OR_RETURN(down, LinComb(1, down, 1, corr2)); } ReduceRinging(initial, mask, down); @@ -690,32 +697,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 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 +754,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 +770,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 +808,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 +824,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 +843,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 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; @@ -860,9 +884,12 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, // 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 @@ -889,13 +916,15 @@ Status LossyFrameHeuristics(const FrameHeader& frame_header, 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 14cb59638707..0dd93e4288ff 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_icc_codec.cc b/third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc index 8e92fe345260..06fd8fa5f22d 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 #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" 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 1b4136132054..9cd5aecfc25e 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 #include -#include #include -#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 ok{true}; + std::atomic 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); @@ -75,7 +73,7 @@ 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); + 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 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 fe2090a9093f..452c17f4e82c 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 -#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 791770d5d490..b9a36c7ca10f 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 namespace jxl { -using ImageD = Plane; +typedef std::array Vector2; +// NB: matrix2x2[row][column] +typedef std::array 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 967b9a3afbb4..c02f009ca7dd 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 b8366953b769..2969d194acb4 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 #include +#include #include -#include #include #include @@ -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 @@ -193,7 +193,8 @@ Status float_to_int(const float* const row_in, pixel_type* const row_out, } // 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,7 +254,8 @@ 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); } } @@ -278,6 +280,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; @@ -303,6 +306,18 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header, } } + 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 = static_cast(-1); + } + if (cparams_.options.predictor == static_cast(-1)) { // no explicit predictor(s) given, set a good default if ((cparams_.speed_tier <= SpeedTier::kTortoise || @@ -354,6 +369,15 @@ 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); + + 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; + } + stream_options_[0].histogram_params = + HistogramParams::ForModular(cparams_, {}, streaming_mode); } bool do_transform(Image& image, const Transform& tr, @@ -373,29 +397,40 @@ bool do_transform(Image& image, const Transform& tr, Status ModularFrameEncoder::ComputeEncodingData( const FrameHeader& frame_header, const ImageMetadata& metadata, Image3F* JXL_RESTRICT color, const std::vector& 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 +458,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 +515,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 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 +546,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 +561,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 +578,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,7 +599,7 @@ 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); @@ -591,7 +637,7 @@ Status ModularFrameEncoder::ComputeEncodingData( } // 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,7 +646,8 @@ 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; JXL_DEBUG_V(10, "Channel %" PRIuS ": range=%i..%i", i, min, max); @@ -646,8 +693,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 +741,8 @@ Status ModularFrameEncoder::ComputeEncodingData( bitdepth_correction = maxval / 255.f; } std::vector 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 +750,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 +790,56 @@ Status ModularFrameEncoder::ComputeEncodingData( } // Fill other groups. - struct GroupParams { - Rect rect; - int minShift; - int maxShift; - ModularStreamId id; - }; - std::vector 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)); }, "ChooseParams")); { @@ -966,7 +1033,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 +1086,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 tree_context_map; @@ -1103,6 +1132,24 @@ void ModularFrameEncoder::ClearStreamData(const ModularStreamId& stream) { std::swap(stream_images_[stream_id], empty_image); } +void ModularFrameEncoder::ClearModularStreamData() { + for (const auto& group : stream_params_) { + ClearStreamData(group.id); + } + stream_params_.clear(); +} + +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 { float EstimateWPCost(const Image& img, size_t i) { size_t extra_bits = 0; @@ -1138,7 +1185,9 @@ float EstimateWPCost(const Image& img, size_t i) { ctx += c >= properties[0]; } pixel_type res = r[x] - guess; - uint32_t token, nbits, bits; + uint32_t token; + uint32_t nbits; + uint32_t bits; config.Encode(PackSigned(res), &token, &nbits, &bits); histo[ctx].Add(token); extra_bits += nbits; @@ -1177,7 +1226,9 @@ float EstimateCost(const Image& img) { ctx += c > maxdiff; } pixel_type res = r[x] - ClampedGradient(top, left, topleft); - uint32_t token, nbits, bits; + uint32_t token; + uint32_t nbits; + uint32_t bits; config.Encode(PackSigned(res), &token, &nbits, &bits); histo[ctx].Add(token); extra_bits += nbits; @@ -1204,7 +1255,8 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect, 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++) { @@ -1220,7 +1272,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) { @@ -1271,7 +1323,8 @@ 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; JXL_DEBUG_V(10, "Channel %" PRIuS ": range=%i..%i", i, min, max); @@ -1319,6 +1372,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 +1457,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 +1484,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 +1588,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 +1603,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 +1629,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 +1664,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 +1677,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 +1690,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 +1709,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 2158a781af1f..b4e83a615bdf 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 +#include +#include +#include +#include + +#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& 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& 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 ac_metadata_size; std::vector extra_dc_precision; @@ -89,6 +107,14 @@ class ModularFrameEncoder { std::vector> gi_channel_; std::vector image_widths_; Predictor delta_pred_ = Predictor::Average4; + + struct GroupParams { + Rect rect; + int minShift; + int maxShift; + ModularStreamId id; + }; + std::vector stream_params_; }; } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/enc_params.h b/third_party/jpeg-xl/lib/jxl/enc_params.h index 89fd2c924ff3..162c59d04cb0 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 #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 0abd1778090e..3079b88a940e 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_patch_dictionary.cc @@ -11,18 +11,14 @@ #include #include -#include -#include #include #include -#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/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 +27,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" @@ -208,11 +203,12 @@ struct PatchColorspaceInfo { } }; -std::vector FindTextLikePatches( +StatusOr> 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 info; + if (state->cparams.patches == Override::kOff) return info; const auto& frame_dim = state->shared.frame_dim; PatchColorspaceInfo pci(is_xyb); @@ -222,7 +218,8 @@ std::vector FindTextLikePatches( std::pair 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 +255,9 @@ std::vector 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 +300,22 @@ std::vector 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 +384,14 @@ std::vector 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 +409,10 @@ std::vector FindTextLikePatches( constexpr int kMinPeak = 2; constexpr int kHasSimilarRadius = 2; - std::vector 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 +526,10 @@ std::vector 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 +561,22 @@ std::vector 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 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 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 +586,11 @@ 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); + JXL_ASSIGN_OR_RETURN(info, FindDotDictionary(state->cparams, opsin, + 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 +621,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(); @@ -680,7 +685,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 positions; @@ -725,8 +731,9 @@ void FindBestPatchDictionary(const Image3F& opsin, // 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 +741,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 +776,8 @@ void RoundtripPatchFrame(Image3F* reference_frame, std::vector 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 +823,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 e17bfe4f0499..ac236d7f17e4 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 #include -#include #include #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_quant_weights.cc b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc index 236ddaacfd84..f76b4f20c04d 100644 --- a/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc +++ b/third_party/jpeg-xl/lib/jxl/enc_quant_weights.cc @@ -7,22 +7,15 @@ #include -#include #include -#include -#include -#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 +88,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: { @@ -195,19 +188,20 @@ void DequantMatricesRoundtrip(DequantMatrices* matrices) { JXL_CHECK(br.Close()); } -void DequantMatricesSetCustom(DequantMatrices* matrices, - const std::vector& encodings, - ModularFrameEncoder* encoder) { +Status DequantMatricesSetCustom(DequantMatrices* matrices, + const std::vector& 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 a47dfd498873..82d8278b7246 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& encodings, - ModularFrameEncoder* encoder); +Status DequantMatricesSetCustom(DequantMatrices* matrices, + const std::vector& 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_xyb.cc b/third_party/jpeg-xl/lib/jxl/enc_xyb.cc index e538e8c91ddb..5cfd7dc3e467 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. @@ -209,15 +209,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 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 ok{true}; + JXL_ASSIGN_OR_RETURN(Image3F out, Image3F::Create(in.xsize(), in.ysize())); + std::atomic has_error{false}; JXL_CHECK(RunOnPool( pool, 0, in.ysize(), [&](const size_t num_threads) { @@ -225,6 +226,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. @@ -242,7 +244,7 @@ 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); + has_error = true; return; } float* JXL_RESTRICT row_out0 = out.PlaneRow(0, y); @@ -264,7 +266,7 @@ Image3F TransformToLinearRGB(const Image3F& in, } }, "Colorspace transform")); - JXL_CHECK(ok.load()); + JXL_CHECK(!has_error); return out; } @@ -394,12 +396,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 6a2e7c4123dc..741d447b92f2 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 76f2148d6283..12be5acdcd28 100644 --- a/third_party/jpeg-xl/lib/jxl/encode.cc +++ b/third_party/jpeg-xl/lib/jxl/encode.cc @@ -1436,7 +1436,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)); @@ -1489,7 +1489,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,15 +1537,15 @@ JxlEncoderStatus JxlEncoderFrameSettingsSetOption( switch (option) { case JXL_ENC_FRAME_SETTING_EFFORT: if (frame_settings->enc->allow_expert_options) { + if (value < 1 || value > 11) { + return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED, + "Encode effort has to be in [1..11]"); + } + } else { if (value < 1 || value > 10) { return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED, "Encode effort has to be in [1..10]"); } - } else { - if (value < 1 || value > 9) { - return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_NOT_SUPPORTED, - "Encode effort has to be in [1..9]"); - } } frame_settings->values.cparams.speed_tier = static_cast(10 - value); @@ -1747,9 +1747,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; diff --git a/third_party/jpeg-xl/lib/jxl/encode_test.cc b/third_party/jpeg-xl/lib/jxl/encode_test.cc index 2c17fcab21fa..b9b13752cd8a 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 @@ -241,7 +242,7 @@ void VerifyFrameEncoding(size_t xsize, size_t ysize, JxlEncoder* enc, 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 +257,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. @@ -346,7 +347,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 +368,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,7 +439,7 @@ 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); } { @@ -458,7 +459,7 @@ TEST(EncodeTest, frame_settingsTest) { JxlEncoderFrameSettings* frame_settings = JxlEncoderFrameSettingsCreate(enc.get(), NULL); 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); } @@ -519,7 +520,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); 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 868e1b72f464..b242dbe5758d 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 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/frame_header.cc b/third_party/jpeg-xl/lib/jxl/frame_header.cc index a9e79ff1b858..6648e6d8cccf 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(frame_size.xsize), - static_cast(frame_size.ysize), - static_cast(frame_origin.x0), + xsize, ysize, static_cast(frame_origin.x0), static_cast(frame_origin.y0)); } } diff --git a/third_party/jpeg-xl/lib/jxl/gradient_test.cc b/third_party/jpeg-xl/lib/jxl/gradient_test.cc index 055a419f5b97..91c996eeaa77 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 #include -#include #include #include #include @@ -45,7 +44,7 @@ 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; @@ -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)); + 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)); + } } - } - // 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]; + // 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 { - 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]); - } - } - 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]; - } - } 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 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/image.cc b/third_party/jpeg-xl/lib/jxl/image.cc index 382c95779948..7468dad130d8 100644 --- a/third_party/jpeg-xl/lib/jxl/image.cc +++ b/third_party/jpeg-xl/lib/jxl/image.cc @@ -5,116 +5,49 @@ #include "lib/jxl/image.h" -#include // swap +#include // fill, swap +#include +#include -#undef HWY_TARGET_INCLUDE -#define HWY_TARGET_INCLUDE "lib/jxl/image.cc" -#include -#include +#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; - - // 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; - } - - // 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 - -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(xsize)), - ysize_(static_cast(ysize)), - orig_xsize_(static_cast(xsize)), - orig_ysize_(static_cast(ysize)) { - JXL_CHECK(xsize == xsize_); - JXL_CHECK(ysize == ysize_); - - JXL_ASSERT(sizeof_t == 1 || sizeof_t == 2 || sizeof_t == 4 || sizeof_t == 8); - - 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); - } -} - -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(); + const size_t vec_size = MaxVectorSize(); 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; + const size_t valid_size = xsize * sizeof_t; + const size_t initialize_size = RoundUpTo(valid_size, vec_size); if (valid_size == initialize_size) return; - for (size_t y = 0; y < ysize_; ++y) { - uint8_t* JXL_RESTRICT row = static_cast(VoidRow(y)); + 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. + // 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, @@ -124,6 +57,43 @@ void PlaneBase::InitializePadding(const size_t sizeof_t, Padding padding) { #endif // MEMORY_SANITIZER } +} // namespace + +PlaneBase::PlaneBase(const size_t xsize, const size_t ysize, + const size_t sizeof_t) + : xsize_(static_cast(xsize)), + ysize_(static_cast(ysize)), + orig_xsize_(static_cast(xsize)), + orig_ysize_(static_cast(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()); + + // 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) { + return true; + } + + 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"); + } + InitializePadding(*this, sizeof_t_); + + return true; +} + void PlaneBase::Swap(PlaneBase& other) { std::swap(xsize_, other.xsize_); std::swap(ysize_, other.ysize_); @@ -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 98c387bb771b..edb0aab77eec 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,6 +88,9 @@ 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) || \ @@ -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 -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 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(VoidRow(y)); } @@ -179,6 +170,10 @@ class Plane : public PlaneBase { JXL_INLINE intptr_t PixelsPerRow() const { return static_cast(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; @@ -189,12 +184,6 @@ using ImageI = Plane; using ImageF = Plane; using ImageD = Plane; -// Also works for Image3 and mixed argument types. -template -bool SameSize(const Image1& image1, const Image2& image2) { - return image1.xsize() == image2.xsize() && image1.ysize() == image2.ysize(); -} - template class Image3; @@ -394,24 +383,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 +401,17 @@ class Image3 { return *this; } + static StatusOr Create(const size_t xsize, const size_t ysize) { + StatusOr plane0 = PlaneT::Create(xsize, ysize); + JXL_RETURN_IF_ERROR(plane0.status()); + StatusOr plane1 = PlaneT::Create(xsize, ysize); + JXL_RETURN_IF_ERROR(plane1.status()); + StatusOr 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 +469,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 +487,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 2eea496d5ede..c8a76a9f5984 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 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_ops.cc b/third_party/jpeg-xl/lib/jxl/image_ops.cc new file mode 100644 index 000000000000..36d19df9389c --- /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 +#include + +#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 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 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 b2ce23f13d85..84cf7dad7649 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 -#include +#include +#include #include -#include +#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 +bool SameSize(const Image1& image1, const Image2& image2) { + return image1.xsize() == image2.xsize() && image1.ysize() == image2.ysize(); +} + template void CopyImageTo(const Plane& from, Plane* JXL_RESTRICT to) { JXL_ASSERT(SameSize(from, *to)); @@ -102,76 +109,15 @@ void CopyImageToWithPadding(const Rect& from_rect, const T& from, to); } -template -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 -void SubtractFrom(const Plane& what, Plane* 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 -void AddTo(const Plane& what, Plane* 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 -void AddTo(Rect rectFrom, const Plane& what, Rect rectTo, - Plane* 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 -Plane LinComb(const T lambda1, const Plane& image1, const T lambda2, - const Plane& image2) { +StatusOr> LinComb(const T lambda1, const Plane& image1, + const T lambda2, const Plane& image2) { const size_t xsize = image1.xsize(); const size_t ysize = image1.ysize(); JXL_CHECK(xsize == image2.xsize()); JXL_CHECK(ysize == image2.ysize()); - Plane out(xsize, ysize); + JXL_ASSIGN_OR_RETURN(Plane out, Plane::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 -void SetBorder(const size_t thickness, const T value, Plane* 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 void ImageMinMax(const Plane& image, T* const JXL_RESTRICT min, @@ -335,48 +252,6 @@ void ImageMinMax(const Plane& image, T* const JXL_RESTRICT min, } } -template -Plane ImageFromPacked(const std::vector& packed, const size_t xsize, - const size_t ysize) { - Plane 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 -void Image3Max(const Image3& image, std::array* out_max) { - for (size_t c = 0; c < 3; ++c) { - T max = std::numeric_limits::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 -std::vector PackedFromImage(const Plane& image, const Rect& rect) { - const size_t xsize = rect.xsize(); - const size_t ysize = rect.ysize(); - std::vector 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 -std::vector PackedFromImage(const Plane& image) { - return PackedFromImage(image, Rect(image)); -} - // Initializes all planes to the same "value". template void FillImage(const T value, Image3* image) { @@ -390,28 +265,6 @@ void FillImage(const T value, Image3* image) { } } -template -void FillPlane(const T value, Plane* 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 -void FillImage(const T value, Image3* 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 void FillPlane(const T value, Plane* image, Rect rect) { for (size_t y = 0; y < rect.ysize(); ++y) { @@ -432,22 +285,14 @@ void ZeroFillImage(Image3* image) { } } -template -void ZeroFillPlane(Plane* 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 DownsampleImage(const Image3F& opsin, size_t factor); +StatusOr 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 dfcb2292c5b7..bbca30fdeb0c 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 #include -#include +#include +#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 -void TestPacked(const size_t xsize, const size_t ysize) { - Plane image1(xsize, ysize); - RandomFillImage(&image1); - const std::vector& packed = PackedFromImage(image1); - const Plane& image2 = ImageFromPacked(packed, xsize, ysize); - JXL_EXPECT_OK(SamePixels(image1, image2, _)); -} - -TEST(ImageTest, TestPacked) { - TestPacked(1, 1); - TestPacked(7, 1); - TestPacked(1, 7); - - TestPacked(1, 1); - TestPacked(7, 1); - TestPacked(1, 7); - - TestPacked(1, 1); - TestPacked(7, 1); - TestPacked(1, 7); - - TestPacked(1, 1); - TestPacked(7, 1); - TestPacked(1, 7); -} - // Ensure entire payload is readable/writable for various size/offset combos. TEST(ImageTest, TestAllocator) { Rng rng(0); @@ -108,12 +84,8 @@ template void TestFillT() { for (uint32_t xsize : {0, 1, 15, 16, 31, 32}) { for (uint32_t ysize : {0, 1, 15, 16, 31, 32}) { - Image3 image(xsize, ysize); + JXL_ASSIGN_OR_DIE(Image3 image, Image3::Create(xsize, ysize)); TestFillImpl(&image, "size ctor"); - - Image3 planar(Plane(xsize, ysize), Plane(xsize, ysize), - Plane(xsize, ysize)); - TestFillImpl(&planar, "planar"); } } } @@ -127,7 +99,7 @@ TEST(ImageTest, TestFill) { } TEST(ImageTest, CopyImageToWithPaddingTest) { - Plane src(100, 61); + JXL_ASSIGN_OR_DIE(Plane src, Plane::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 dst(60, 50); + JXL_ASSIGN_OR_DIE(Plane dst, Plane::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/jpeg/enc_jpeg_data.cc b/third_party/jpeg-xl/lib/jxl/jpeg/enc_jpeg_data.cc index 97342553e541..954cb2d5c3b1 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 #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) { @@ -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 bytes) { +inline bool IsJPG(const Span 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; @@ -394,8 +397,9 @@ Status DecodeImageJPG(const Span 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/jxl_test.cc b/third_party/jpeg-xl/lib/jxl/jxl_test.cc index a91dbd06724a..1c7ff6e9214a 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. @@ -147,7 +146,7 @@ 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_NEAR(Roundtrip(t.ppf(), cparams, {}, pool, &ppf_out), 17300, 500); EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(90)); } @@ -180,7 +179,7 @@ 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_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 203300, 2000); EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(340)); } @@ -342,7 +341,7 @@ TEST(JxlTest, RoundtripRGBToGrayscale) { EXPECT_THAT(ButteraugliDistance(io.frames, io2.frames, ButteraugliParams(), *JxlGetDefaultCms(), /*distmap=*/nullptr, &pool), - IsSlightlyBelow(1.36)); + IsSlightlyBelow(1.4)); } TEST(JxlTest, RoundtripLargeFast) { @@ -355,7 +354,7 @@ 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_NEAR(Roundtrip(t.ppf(), cparams, {}, &pool, &ppf_out), 503000, 12000); EXPECT_THAT(ComputeDistance2(t.ppf(), ppf_out), IsSlightlyBelow(78)); } @@ -548,7 +547,7 @@ TEST(JxlTest, RoundtripSmallPatches) { #if 0 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(); @@ -706,7 +705,7 @@ TEST(JxlTest, RoundtripAlpha) { std::vector 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}) { @@ -781,7 +780,8 @@ bool UnpremultiplyAlpha(CodecInOut& io) { TEST(JxlTest, RoundtripAlphaPremultiplied) { const std::vector 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 +803,7 @@ TEST(JxlTest, RoundtripAlphaPremultiplied) { std::vector 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}) { @@ -838,7 +838,7 @@ TEST(JxlTest, RoundtripAlphaPremultiplied) { ButteraugliDistance(io_nopremul.frames, io2.frames, ButteraugliParams(), *JxlGetDefaultCms(), /*distmap=*/nullptr), - IsSlightlyBelow(1.55)); + IsSlightlyBelow(1.0)); } } } @@ -854,6 +854,7 @@ 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); @@ -873,12 +874,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_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(1.52)); } TEST(JxlTest, RoundtripAlphaNonMultipleOf8) { @@ -893,13 +895,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_THAT(ButteraugliDistance(t.ppf(), ppf_out), IsSlightlyBelow(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,6 +926,7 @@ 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 @@ -1035,7 +1039,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 +1067,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 +1075,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 +1104,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); @@ -1183,7 +1189,7 @@ TEST(JxlTest, RoundtripAnimation) { PackedPixelFile ppf_out; EXPECT_THAT(Roundtrip(t.ppf(), {}, dparams, pool, &ppf_out), - IsSlightlyBelow(2888)); + IsSlightlyBelow(3350)); t.CoalesceGIFAnimationWithAlpha(); ASSERT_EQ(ppf_out.frames.size(), t.ppf().frames.size()); @@ -1293,7 +1299,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444)) { const std::vector 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)) { @@ -1380,7 +1386,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420)) { const std::vector 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 +1395,7 @@ TEST(JxlTest, const std::vector 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 +1404,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444_12)) { const std::vector 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 +1412,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression422)) { const std::vector 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 +1420,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression440)) { const std::vector 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 +1430,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression_asymmetric)) { const std::vector 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 +1438,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420Progr)) { const std::vector 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 +1446,8 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionMetadata)) { const std::vector 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 +1455,7 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionRestarts)) { const std::vector 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, @@ -1651,15 +1658,65 @@ JXL_GTEST_INSTANTIATE_TEST_SUITE_P( JxlStreamingTest, JxlStreamingTest, testing::ValuesIn(StreamingTestParam::All())); -// This is broken on mingw32, so we only enable it for x86_64 now. -TEST(JxlTest, JXL_X86_64_TEST(StreamingSamePixels)) { - const std::vector orig = ReadTestData("jxl/flower/flower.png"); +struct StreamingEncodingTestParam { + std::string file; + int effort; + float distance; + int group_size; + float palette_percent; + static std::vector All() { + std::vector 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, 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 {}; + +// This is broken on mingw32, so we only enable it for x86_64 now. +TEST_P(JxlStreamingEncodingTest, JXL_X86_64_TEST(StreamingSamePixels)) { + const auto param = GetParam(); + + const std::vector 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 +1730,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 58b6941305aa..f5a9c43650f2 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 9aaa3f4dbe78..deab7367ac00 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/modular/encoding/context_predict.h b/third_party/jpeg-xl/lib/jxl/modular/encoding/context_predict.h index 4c3a33a52a0d..567bf50152c6 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 #include "lib/jxl/fields.h" +#include "lib/jxl/image_ops.h" #include "lib/jxl/modular/modular_image.h" #include "lib/jxl/modular/options.h" 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 ee7177bcd6f6..b53b9a910328 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 + #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_encoding.cc b/third_party/jpeg-xl/lib/jxl/modular/encoding/enc_encoding.cc index fc2e69e4a6c2..abe5579a7c7a 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 #include -#include #include -#include #include -#include -#include -#include #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 { @@ -101,10 +90,10 @@ Tree MakeFixedTree(int property, const std::vector &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, @@ -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 @@ -326,7 +320,8 @@ Status EncodeModularChannelMAANS(const Image &image, pixel_type chan, std::array static_props = { {chan, (int)group_id}}; - bool use_wp, is_wp_only; + 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, @@ -454,7 +449,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 +478,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 +555,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(*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 +597,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 +620,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 +633,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 +680,7 @@ Status ModularEncode(const Image &image, const ModularOptions &options, if (!header->use_global_tree) { EntropyEncodingData code; std::vector 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, 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 a6abdcfc91c2..304dd6813905 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" @@ -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 746d7c87fda9..32a5531080c4 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 #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::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_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::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 56e80d823a62..eb95b1cb6c76 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 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 Create(size_t iw, size_t ih, int hsh = 0, + int vsh = 0) { + JXL_ASSIGN_OR_RETURN(Plane plane, + Plane::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 resizedplane(w, h); - plane = std::move(resizedplane); + Status shrink() { + if (plane.xsize() == w && plane.ysize() == h) return true; + JXL_ASSIGN_OR_RETURN(plane, Plane::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&& 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 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 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 ce6596b912ba..95afd8a1bf8c 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 #include +#include "lib/jxl/enc_ans_params.h" + namespace jxl { using PropertyVal = int32_t; @@ -108,6 +110,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 f5172aa12686..a3427253b270 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 #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 &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(a[c]) - static_cast(b[c]); @@ -174,7 +175,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 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(maxval) - static_cast(minval) + 1; @@ -194,7 +196,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, } } 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; @@ -231,7 +233,7 @@ Status FwdPaletteIteration(Image &input, uint32_t begin_c, uint32_t end_c, } } 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 +258,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 +340,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 +364,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 ap, std::vector 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); 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 489f72a90d87..5741eaaf46d6 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,18 @@ namespace jxl { -void FwdHSqueeze(Image &input, int c, int rc) { +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); @@ -55,17 +58,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); @@ -104,6 +111,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 parameters, @@ -128,9 +136,9 @@ Status FwdSqueeze(Image &input, std::vector 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 bffbacf16063..7af525f05cd9 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_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); @@ -75,8 +77,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 +171,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/squeeze.cc b/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc index e9892ea48f1b..86ad6895324f 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 " @@ -289,7 +293,7 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) { return true; } -Status InvSqueeze(Image &input, std::vector parameters, +Status InvSqueeze(Image &input, const std::vector ¶meters, ThreadPool *pool) { for (int i = parameters.size() - 1; i >= 0; i--) { JXL_RETURN_IF_ERROR( @@ -340,7 +344,7 @@ HWY_AFTER_NAMESPACE(); namespace jxl { HWY_EXPORT(InvSqueeze); -Status InvSqueeze(Image &input, std::vector parameters, +Status InvSqueeze(Image &input, const std::vector ¶meters, ThreadPool *pool) { return HWY_DYNAMIC_DISPATCH(InvSqueeze)(input, parameters, pool); } @@ -459,8 +463,8 @@ Status MetaSqueeze(Image &image, std::vector *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 305a0ca3ecf4..bbd16c59c083 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 *parameters); -Status InvSqueeze(Image &input, std::vector parameters, +Status InvSqueeze(Image &input, const std::vector ¶meters, ThreadPool *pool); } // namespace jxl diff --git a/third_party/jpeg-xl/lib/jxl/modular_test.cc b/third_party/jpeg-xl/lib/jxl/modular_test.cc index 689063ce9532..103e4d1483b3 100644 --- a/third_party/jpeg-xl/lib/jxl/modular_test.cc +++ b/third_party/jpeg-xl/lib/jxl/modular_test.cc @@ -29,7 +29,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 +53,7 @@ namespace jxl { namespace { +using test::ButteraugliDistance; using test::ReadTestData; using test::Roundtrip; using test::TestImage; @@ -209,7 +209,8 @@ TEST(ModularTest, RoundtripLossy16) { 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 +225,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 +305,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++) { @@ -330,7 +333,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 * 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 +353,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 +363,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 +446,7 @@ TEST(ModularTest, PredictorIntegerOverflow) { WriteHeaders(&writer, xsize, ysize); std::vector 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 +470,7 @@ TEST(ModularTest, PredictorIntegerOverflow) { nullptr, &ppf)); ASSERT_EQ(1, ppf.frames.size()); const auto& img = ppf.frames[0].color; - const auto pixels = reinterpret_cast(img.pixels()); + const auto* pixels = reinterpret_cast(img.pixels()); EXPECT_EQ(-1.0f, pixels[0]); } @@ -478,7 +482,7 @@ TEST(ModularTest, UnsqueezeIntegerOverflow) { WriteHeaders(&writer, xsize, ysize); std::vector 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 +518,7 @@ TEST(ModularTest, UnsqueezeIntegerOverflow) { nullptr, &ppf)); ASSERT_EQ(1, ppf.frames.size()); const auto& img = ppf.frames[0].color; - const auto pixels = reinterpret_cast(img.pixels()); + const float* pixels = reinterpret_cast(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/opsin_image_test.cc b/third_party/jpeg-xl/lib/jxl/opsin_image_test.cc index f7842c32e424..bc5463a7d904 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); @@ -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 b8c151fbea8e..b8f9aa13df81 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/passes_state.cc b/third_party/jpeg-xl/lib/jxl/passes_state.cc index 12cc6a0c93fe..5da731b48e8e 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 a47134cd003b..bbfe4529eda0 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; @@ -256,7 +256,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; @@ -300,7 +300,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; 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 60f7c32229b5..fd04b7fc2e82 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 b7fe855d4d5c..c482db9fd8cb 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_test.cc b/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc index 2dd513804c5c..e229648d4399 100644 --- a/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc +++ b/third_party/jpeg-xl/lib/jxl/quant_weights_test.cc @@ -50,8 +50,8 @@ void RoundtripMatrices(const std::vector& 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& encodings_dec = mat.encodings(); for (size_t i = 0; i < encodings.size(); i++) { const QuantEncoding& e = encodings[i]; @@ -172,8 +172,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_test.cc b/third_party/jpeg-xl/lib/jxl/quantizer_test.cc index aff19f42c13b..eeaac9ba5369 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/render_pipeline/low_memory_render_pipeline.cc b/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc index 9aefdd007da7..a8a25d0296ec 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 -#include -#include #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> LowMemoryRenderPipeline::PrepareBuffers( @@ -551,10 +561,10 @@ class Rows { } // namespace -void LowMemoryRenderPipeline::RenderRect(size_t thread_id, - std::vector& input_data, - Rect data_max_color_channel_rect, - Rect image_max_color_channel_rect) { +Status LowMemoryRenderPipeline::RenderRect(size_t thread_id, + std::vector& 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 +609,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. @@ -698,9 +708,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 @@ -739,15 +749,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(1)); RenderPipelineStage::RowInfo output_rows; @@ -760,15 +771,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& input_data = group_data_[use_group_ids_ ? group_id : thread_id]; @@ -804,38 +816,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 +874,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 b386f7c078a2..f0b21d3dca76 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> 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& 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& 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 68b6ef613fe9..14bd36311040 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 - +#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::Builder::Finalize( +StatusOr> 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::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>& 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 bf3ad4975e6e..c61420be4bbe 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& 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 Finalize( + StatusOr> 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>& buffers); + Status InputReady(size_t group_id, size_t thread_id, + const std::vector>& buffers); virtual std::vector> 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 d1a0074161b2..d054027ba757 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 #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>& input_sizes) {} + virtual Status SetInputSizes( + const std::vector>& 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 51b9f273f8f8..25902978296b 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 ec; + std::vector ec; ec.push_back(std::move(spot)); io.frames[0].SetExtraChannels(std::move(ec)); } 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 4495288860cb..669e70273b08 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 +#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> 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, @@ -160,7 +166,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 +189,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 +217,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 +226,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 +273,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 10f4505912a6..1240b9fa46d3 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> 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 b68105f4c9c9..ef3899d1b39a 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(xpos); @@ -128,7 +128,8 @@ class BlendingStage : public RenderPipelineStage { if (bg_xpos + static_cast(xsize) <= 0 || frame_origin.x0 >= static_cast(image_xsize_) || bg_ypos < 0 || bg_ypos >= static_cast(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 936fbd3a4427..2bc88ada6778 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 2465146b4767..388cad95059d 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 -#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 #include "lib/jxl/dec_xyb-inl.h" -#include "lib/jxl/sanitizers.h" HWY_BEFORE_NAMESPACE(); namespace jxl { @@ -44,9 +41,9 @@ 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 { + 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 @@ -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)); // 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 color_space_transform; ColorEncoding c_src_; - void SetInputSizes( + Status SetInputSizes( const std::vector>& 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 5d1a379ede5e..d3030b02cb18 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 6b1f646cd538..d2135a61c72c 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 @@ -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); @@ -131,6 +131,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 0917db3f9aa2..27f9491e2df1 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); @@ -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 5cf8a6ed516f..f4cb4edacec4 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 @@ -161,10 +161,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 +212,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 +242,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]; @@ -271,6 +272,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 c5a75b09f737..e0a66167d429 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 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 4a0529ce2cd5..92a13090a74d 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 a43cb4e1ab0c..4d3d3414a406 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,9 +15,9 @@ 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++) { @@ -28,6 +28,7 @@ class SpotColorStage : public RenderPipelineStage { 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 85eca2f03935..e792a0d56e57 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 @@ -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); @@ -139,6 +139,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 2a272e15dcb9..c9161dcb21f5 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)); @@ -100,6 +100,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 ade37d59a677..897b20c4c6d5 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 847972acc8a1..c5a91e8efd6d 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 du; const Rebind 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>& 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>& 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 56e86e60951e..7d2d62868b01 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)); @@ -81,6 +81,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 +131,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 +143,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 30ad327221ad..f21a00c728f0 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 789a52f8b299..d24fa9603246 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 #include -#include -#include -#include - +#include "lib/jxl/base/status.h" #include "lib/jxl/render_pipeline/render_pipeline_stage.h" namespace jxl { @@ -20,9 +17,9 @@ 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); @@ -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,9 +48,9 @@ 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); @@ -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 c00fda0de12c..dcd7d2abf9c2 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 @@ -233,7 +235,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, @@ -412,10 +414,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()); } diff --git a/third_party/jpeg-xl/lib/jxl/sanitizers.h b/third_party/jpeg-xl/lib/jxl/sanitizers.h index adeaea67edd4..bb133e9203f2 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 +static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Plane& im) { PoisonMemory(im.bytes(), im.bytes_per_row() * im.ysize()); } -template -static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Image3& im) { - PoisonImage(im.Plane(0)); - PoisonImage(im.Plane(1)); - PoisonImage(im.Plane(2)); -} +namespace { // Print the uninitialized regions of an image. template @@ -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 static JXL_INLINE JXL_MAYBE_UNUSED void PoisonImage(const Plane& im) {} diff --git a/third_party/jpeg-xl/lib/jxl/simd_util.cc b/third_party/jpeg-xl/lib/jxl/simd_util.cc index a3971ff90012..6515daaa2b5e 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 +#include + #undef HWY_TARGET_INCLUDE #define HWY_TARGET_INCLUDE "lib/jxl/simd_util.cc" #include #include +#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 84938a931a30..62b68c989c2c 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 7874bdc1581a..381367b54d4f 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_gbench.cc b/third_party/jpeg-xl/lib/jxl/splines_gbench.cc index 78ff6d41c000..0461be40b826 100644 --- a/third_party/jpeg-xl/lib/jxl/splines_gbench.cc +++ b/third_party/jpeg-xl/lib/jxl/splines_gbench.cc @@ -4,6 +4,7 @@ // license that can be found in the LICENSE file. #include "benchmark/benchmark.h" +#include "lib/jxl/image_ops.h" #include "lib/jxl/splines.h" namespace jxl { @@ -33,7 +34,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 d812545a3724..6a0f157e6e9c 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" @@ -286,7 +285,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)); @@ -322,13 +321,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_utils.cc b/third_party/jpeg-xl/lib/jxl/test_utils.cc index 451f2a0a03a6..cf4b96c43565 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); @@ -385,7 +385,10 @@ std::vector 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 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 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); @@ -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& frames0, + const std::vector& 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; diff --git a/third_party/jpeg-xl/lib/jxl/test_utils.h b/third_party/jpeg-xl/lib/jxl/test_utils.h index 6734380bf575..f702221ca588 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 #include -#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 file, CodecInOut* JXL_RESTRICT io, - ThreadPool* pool = nullptr); +Status DecodeFile(extras::JXLDecompressParams dparams, Span 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& frames0, + const std::vector& 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 `&` overload? ThreadPool* operator&() { return pool_.get(); } private: diff --git a/third_party/jpeg-xl/lib/jxl_cms.cmake b/third_party/jpeg-xl/lib/jxl_cms.cmake index 47757c8f3b94..04980066c126 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 bbbc444b36f4..2c63f7825eb4 100644 --- a/third_party/jpeg-xl/lib/jxl_lists.bzl +++ b/third_party/jpeg-xl/lib/jxl_lists.bzl @@ -216,6 +216,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 e6bf4e5e22b9..1de9cd6302e6 100644 --- a/third_party/jpeg-xl/lib/jxl_lists.cmake +++ b/third_party/jpeg-xl/lib/jxl_lists.cmake @@ -213,6 +213,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/nothing.cc b/third_party/jpeg-xl/lib/nothing.cc new file mode 100644 index 000000000000..e65f587da6b3 --- /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/plugins/CMakeLists.txt b/third_party/jpeg-xl/plugins/CMakeLists.txt index bff1bff29dff..6e09e5aae99b 100644 --- a/third_party/jpeg-xl/plugins/CMakeLists.txt +++ b/third_party/jpeg-xl/plugins/CMakeLists.txt @@ -15,7 +15,4 @@ if(JPEGXL_ENABLE_PLUGIN_GIMP210) add_subdirectory(gimp) endif() -option(JPEGXL_ENABLE_PLUGIN_MIME "Enable image/jxl declaration for shared-mime-info" ON) -if(JPEGXL_ENABLE_PLUGIN_MIME) - add_subdirectory(mime) -endif() + 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 bafa57b16743..6a0de0486b1a 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/mime/CMakeLists.txt b/third_party/jpeg-xl/plugins/mime/CMakeLists.txt deleted file mode 100644 index 6f2a0f919c17..000000000000 --- a/third_party/jpeg-xl/plugins/mime/CMakeLists.txt +++ /dev/null @@ -1,6 +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. - -install(FILES image-jxl.xml DESTINATION share/mime/packages/) diff --git a/third_party/jpeg-xl/plugins/mime/README.md b/third_party/jpeg-xl/plugins/mime/README.md index 4d398c7b90c5..6954a91e4825 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 diff --git a/third_party/jpeg-xl/third_party/CMakeLists.txt b/third_party/jpeg-xl/third_party/CMakeLists.txt index ea22103eb886..409c6a3754f5 100644 --- a/third_party/jpeg-xl/third_party/CMakeLists.txt +++ b/third_party/jpeg-xl/third_party/CMakeLists.txt @@ -31,7 +31,7 @@ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/highway/CMakeLists.txt" AND configure_file("${CMAKE_CURRENT_SOURCE_DIR}/highway/LICENSE" ${PROJECT_BINARY_DIR}/LICENSE.highway COPYONLY) else() - find_package(HWY 1.0.7) + find_package(HWY 1.1.0) if (NOT HWY_FOUND) message(FATAL_ERROR "Highway library (hwy) not found. Install libhwy-dev or download it "