зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1880922 - Update libjxl to ab47708dcf002fae0164555b370aa36487df0f5d r=saschanaz
Differential Revision: https://phabricator.services.mozilla.com/D202184
This commit is contained in:
Родитель
a7fea0f06a
Коммит
c265adb0a2
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ Zoltan Szabadka <szabadka@google.com>
|
|||
# Individuals:
|
||||
a-shvedov
|
||||
Aditya Patadia <adityapatadia@users.noreply.github.com>
|
||||
Ahmad Amsyar Asyadiq Bin Syaiful Bahri <27284123+Ahmad-Amsyar@users.noreply.github.com>
|
||||
Alex Xu (Hello71) <alex_y_xu@yahoo.ca>
|
||||
Alexander Sago <cagelight@gmail.com>
|
||||
Alistair Barrow
|
||||
|
@ -74,6 +75,7 @@ Misaki Kasumi <misakikasumi@outlook.com>
|
|||
Moonchild Straver <moonchild@palemoon.org>
|
||||
Nicholas Hayes <0xC0000054@users.noreply.github.com>
|
||||
Nigel Tao <nigeltao@golang.org>
|
||||
oupson <oupson1er@gmail.com>
|
||||
Petr Diblík
|
||||
Pieter Wuille
|
||||
roland-rollo
|
||||
|
@ -93,4 +95,3 @@ xiota
|
|||
Yonatan Nebenzhal <yonatan.nebenzhl@gmail.com>
|
||||
Ziemowit Zabawa <ziemek.zabawa@outlook.com>
|
||||
源文雨 <41315874+fumiama@users.noreply.github.com>
|
||||
oupson <oupson1er@gmail.com>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,2 @@
|
|||
usr/lib/*/gdk-pixbuf-*/*/loaders/*
|
||||
usr/share/mime/packages/image-jxl.xml
|
||||
usr/share/thumbnailers/jxl.thumbnailer
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -813,6 +813,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
|||
have_cicp = true;
|
||||
have_color = true;
|
||||
ppf->icc.clear();
|
||||
ppf->primary_color_representation =
|
||||
PackedPixelFile::kColorEncodingIsPrimary;
|
||||
}
|
||||
} else if (!have_cicp && id == kId_iCCP) {
|
||||
if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) {
|
||||
|
@ -830,6 +832,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
|||
&profile, &proflen);
|
||||
if (ok && proflen) {
|
||||
ppf->icc.assign(profile, profile + proflen);
|
||||
ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
|
||||
have_color = true;
|
||||
have_iccp = true;
|
||||
} else {
|
||||
|
|
|
@ -43,20 +43,21 @@ Status ApplyColorHints(const ColorHints& color_hints,
|
|||
} else if (key == "icc") {
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
|
||||
std::vector<uint8_t> icc(data, data + value.size());
|
||||
ppf->icc.swap(icc);
|
||||
ppf->icc = std::move(icc);
|
||||
ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
|
||||
got_color_space = true;
|
||||
} else if (key == "exif") {
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
|
||||
std::vector<uint8_t> blob(data, data + value.size());
|
||||
ppf->metadata.exif.swap(blob);
|
||||
ppf->metadata.exif = std::move(blob);
|
||||
} else if (key == "xmp") {
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
|
||||
std::vector<uint8_t> blob(data, data + value.size());
|
||||
ppf->metadata.xmp.swap(blob);
|
||||
ppf->metadata.xmp = std::move(blob);
|
||||
} else if (key == "jumbf") {
|
||||
const uint8_t* data = reinterpret_cast<const uint8_t*>(value.data());
|
||||
std::vector<uint8_t> blob(data, data + value.size());
|
||||
ppf->metadata.jumbf.swap(blob);
|
||||
ppf->metadata.jumbf = std::move(blob);
|
||||
} else {
|
||||
JXL_WARNING("Ignoring %s hint", key.c_str());
|
||||
}
|
||||
|
|
|
@ -188,7 +188,11 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
|
|||
} else if (dparams.force_grayscale) {
|
||||
cinfo.out_color_space = JCS_GRAYSCALE;
|
||||
}
|
||||
if (!ReadICCProfile(&cinfo, &ppf->icc)) {
|
||||
if (ReadICCProfile(&cinfo, &ppf->icc)) {
|
||||
ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
|
||||
} else {
|
||||
ppf->primary_color_representation =
|
||||
PackedPixelFile::kColorEncodingIsPrimary;
|
||||
ppf->icc.clear();
|
||||
// Default to SRGB
|
||||
ppf->color_encoding.color_space =
|
||||
|
@ -227,7 +231,7 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
|
|||
if (dparams.num_colors > 0) {
|
||||
cinfo.quantize_colors = TRUE;
|
||||
cinfo.desired_number_of_colors = dparams.num_colors;
|
||||
cinfo.two_pass_quantize = dparams.two_pass_quant;
|
||||
cinfo.two_pass_quantize = static_cast<boolean>(dparams.two_pass_quant);
|
||||
cinfo.dither_mode = (J_DITHER_MODE)dparams.dither_mode;
|
||||
}
|
||||
|
||||
|
|
|
@ -242,7 +242,11 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
|
|||
if (nbcomp != 1 && nbcomp != 3) {
|
||||
return failure("unsupported number of components in JPEG");
|
||||
}
|
||||
if (!ReadICCProfile(&cinfo, &ppf->icc)) {
|
||||
if (ReadICCProfile(&cinfo, &ppf->icc)) {
|
||||
ppf->primary_color_representation = PackedPixelFile::kIccIsPrimary;
|
||||
} else {
|
||||
ppf->primary_color_representation =
|
||||
PackedPixelFile::kColorEncodingIsPrimary;
|
||||
ppf->icc.clear();
|
||||
// Default to SRGB
|
||||
// Actually, (cinfo.output_components == nbcomp) will be checked after
|
||||
|
@ -278,7 +282,7 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
|
|||
if (dparams && dparams->num_colors > 0) {
|
||||
cinfo.quantize_colors = TRUE;
|
||||
cinfo.desired_number_of_colors = dparams->num_colors;
|
||||
cinfo.two_pass_quantize = dparams->two_pass_quant;
|
||||
cinfo.two_pass_quantize = static_cast<boolean>(dparams->two_pass_quant);
|
||||
cinfo.dither_mode = (J_DITHER_MODE)dparams->dither_mode;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 !=
|
||||
|
|
|
@ -41,10 +41,6 @@ struct JXLDecompressParams {
|
|||
// Whether truncated input should be treated as an error.
|
||||
bool allow_partial_input = false;
|
||||
|
||||
// Set to true if an ICC profile has to be synthesized for Enum color
|
||||
// encodings
|
||||
bool need_icc = false;
|
||||
|
||||
// How many passes to decode at most. By default, decode everything.
|
||||
uint32_t max_passes = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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<uint8_t> row_bytes;
|
||||
size_t rowlen = RoundUpTo(ppf.info.xsize, VectorSize());
|
||||
size_t rowlen = RoundUpTo(ppf.info.xsize, MaxVectorSize());
|
||||
hwy::AlignedFreeUniquePtr<float[]> xyb_tmp =
|
||||
hwy::AllocateAligned<float>(6 * rowlen);
|
||||
hwy::AlignedFreeUniquePtr<float[]> premul_absorb =
|
||||
hwy::AllocateAligned<float>(VectorSize() * 12);
|
||||
hwy::AllocateAligned<float>(MaxVectorSize() * 12);
|
||||
ComputePremulAbsorb(255.0f, premul_absorb.get());
|
||||
|
||||
jpeg_compress_struct cinfo;
|
||||
|
|
|
@ -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<boolean>(params.optimize_coding);
|
||||
if (cinfo.input_components == 3) {
|
||||
JXL_RETURN_IF_ERROR(
|
||||
SetChromaSubsampling(params.chroma_subsampling, &cinfo));
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -38,7 +38,7 @@ struct JXLCompressParams {
|
|||
std::vector<JXLOption> options;
|
||||
// Target butteraugli distance, 0.0 means lossless.
|
||||
float distance = 1.0f;
|
||||
float alpha_distance = 1.0f;
|
||||
float alpha_distance = 0.0f;
|
||||
// If set to true, forces container mode.
|
||||
bool use_container = false;
|
||||
// Whether to enable/disable byte-exact jpeg reconstruction for jpeg inputs.
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
|
||||
#include "lib/jxl/base/common.h"
|
||||
|
||||
#if __unix__
|
||||
#if defined(__unix__) || defined(__unix) || \
|
||||
defined(__APPLE__) && defined(__MACH__)
|
||||
#include <fcntl.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
@ -97,6 +98,8 @@ struct MemoryMappedFileImpl {
|
|||
return f;
|
||||
}
|
||||
|
||||
~MemoryMappedFileImpl() { UnmapViewOfFile(ptr); }
|
||||
|
||||
const uint8_t* data() const { return reinterpret_cast<const uint8_t*>(ptr); }
|
||||
size_t size() const { return fsize.QuadPart; }
|
||||
|
||||
|
|
|
@ -244,9 +244,21 @@ class PackedPixelFile {
|
|||
std::vector<PackedExtraChannel> extra_channels_info;
|
||||
|
||||
// Color information of the decoded pixels.
|
||||
// If the icc is empty, the JxlColorEncoding should be used instead.
|
||||
std::vector<uint8_t> icc;
|
||||
// `primary_color_representation` indicates whether `color_encoding` or `icc`
|
||||
// is the “authoritative” encoding of the colorspace, as opposed to a fallback
|
||||
// encoding. For example, if `color_encoding` is the primary one, as would
|
||||
// occur when decoding a jxl file with such a representation, then `enc/jxl`
|
||||
// will use it and ignore the ICC profile, whereas `enc/png` will include the
|
||||
// ICC profile for compatibility.
|
||||
// If `icc` is the primary representation, `enc/jxl` will preserve it when
|
||||
// compressing losslessly, but *may* encode it as a color_encoding when
|
||||
// compressing lossily.
|
||||
enum {
|
||||
kColorEncodingIsPrimary,
|
||||
kIccIsPrimary
|
||||
} primary_color_representation = kColorEncodingIsPrimary;
|
||||
JxlColorEncoding color_encoding = {};
|
||||
std::vector<uint8_t> icc;
|
||||
// The icc profile of the original image.
|
||||
std::vector<uint8_t> orig_icc;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
#ifndef JXL_TYPES_H_
|
||||
#define JXL_TYPES_H_
|
||||
|
||||
#include <jxl/jxl_export.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -8,12 +8,9 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric> // iota
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "lib/jxl/base/bits.h"
|
||||
#include "lib/jxl/image_ops.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
|
@ -86,10 +83,12 @@ constexpr size_t AcStrategy::kMaxCoeffBlocks;
|
|||
constexpr size_t AcStrategy::kMaxBlockDim;
|
||||
constexpr size_t AcStrategy::kMaxCoeffArea;
|
||||
|
||||
AcStrategyImage::AcStrategyImage(size_t xsize, size_t ysize)
|
||||
: layers_(xsize, ysize) {
|
||||
row_ = layers_.Row(0);
|
||||
stride_ = layers_.PixelsPerRow();
|
||||
StatusOr<AcStrategyImage> AcStrategyImage::Create(size_t xsize, size_t ysize) {
|
||||
AcStrategyImage img;
|
||||
JXL_ASSIGN_OR_RETURN(img.layers_, ImageB::Create(xsize, ysize));
|
||||
img.row_ = img.layers_.Row(0);
|
||||
img.stride_ = img.layers_.PixelsPerRow();
|
||||
return img;
|
||||
}
|
||||
|
||||
size_t AcStrategyImage::CountBlocks(AcStrategy::Type type) const {
|
||||
|
|
|
@ -191,7 +191,8 @@ class AcStrategyRow {
|
|||
class AcStrategyImage {
|
||||
public:
|
||||
AcStrategyImage() = default;
|
||||
AcStrategyImage(size_t xsize, size_t ysize);
|
||||
static StatusOr<AcStrategyImage> Create(size_t xsize, size_t ysize);
|
||||
|
||||
AcStrategyImage(AcStrategyImage&&) = default;
|
||||
AcStrategyImage& operator=(AcStrategyImage&&) = default;
|
||||
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "lib/jxl/base/common.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/sanitizer_definitions.h"
|
||||
|
||||
|
@ -442,7 +443,7 @@ class JXL_MUST_USE_RESULT StatusOr {
|
|||
|
||||
#define JXL_ASSIGN_OR_RETURN(lhs, statusor) \
|
||||
PRIVATE_JXL_ASSIGN_OR_RETURN_IMPL( \
|
||||
assign_or_return_temporary_variable##__LINE__, lhs, statusor)
|
||||
JXL_JOIN(assign_or_return_temporary_variable, __LINE__), lhs, statusor)
|
||||
|
||||
// NOLINTBEGIN(bugprone-macro-parentheses)
|
||||
#define PRIVATE_JXL_ASSIGN_OR_RETURN_IMPL(name, lhs, statusor) \
|
||||
|
@ -451,6 +452,18 @@ class JXL_MUST_USE_RESULT StatusOr {
|
|||
lhs = std::move(name).value();
|
||||
// NOLINTEND(bugprone-macro-parentheses)
|
||||
|
||||
// NB: do not use outside of tests / tools!!!
|
||||
#define JXL_ASSIGN_OR_DIE(lhs, statusor) \
|
||||
PRIVATE_JXL_ASSIGN_OR_DIE_IMPL( \
|
||||
JXL_JOIN(assign_or_die_temporary_variable, __LINE__), lhs, statusor)
|
||||
|
||||
// NOLINTBEGIN(bugprone-macro-parentheses)
|
||||
#define PRIVATE_JXL_ASSIGN_OR_DIE_IMPL(name, lhs, statusor) \
|
||||
auto name = statusor; \
|
||||
if (!name.ok()) jxl::Abort(); \
|
||||
lhs = std::move(name).value();
|
||||
// NOLINTEND(bugprone-macro-parentheses)
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_JXL_BASE_STATUS_H_
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
#include "lib/jxl/blending.h"
|
||||
|
||||
#include "lib/jxl/alpha.h"
|
||||
#include "lib/jxl/image_ops.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
|
@ -29,11 +28,11 @@ bool NeedsBlending(const FrameHeader& frame_header) {
|
|||
return true;
|
||||
}
|
||||
|
||||
void PerformBlending(const float* const* bg, const float* const* fg,
|
||||
float* const* out, size_t x0, size_t xsize,
|
||||
const PatchBlending& color_blending,
|
||||
const PatchBlending* ec_blending,
|
||||
const std::vector<ExtraChannelInfo>& extra_channel_info) {
|
||||
Status PerformBlending(
|
||||
const float* const* bg, const float* const* fg, float* const* out,
|
||||
size_t x0, size_t xsize, const PatchBlending& color_blending,
|
||||
const PatchBlending* ec_blending,
|
||||
const std::vector<ExtraChannelInfo>& extra_channel_info) {
|
||||
bool has_alpha = false;
|
||||
size_t num_ec = extra_channel_info.size();
|
||||
for (size_t i = 0; i < num_ec; i++) {
|
||||
|
@ -42,7 +41,7 @@ void PerformBlending(const float* const* bg, const float* const* fg,
|
|||
break;
|
||||
}
|
||||
}
|
||||
ImageF tmp(xsize, 3 + num_ec);
|
||||
JXL_ASSIGN_OR_RETURN(ImageF tmp, ImageF::Create(xsize, 3 + num_ec));
|
||||
// Blend extra channels first so that we use the pre-blending alpha.
|
||||
for (size_t i = 0; i < num_ec; i++) {
|
||||
if (ec_blending[i].mode == PatchBlendMode::kAdd) {
|
||||
|
@ -146,6 +145,7 @@ void PerformBlending(const float* const* bg, const float* const* fg,
|
|||
for (size_t i = 0; i < 3 + num_ec; i++) {
|
||||
if (xsize != 0) memcpy(out[i] + x0, tmp.Row(i), xsize * sizeof(**out));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
|
|
|
@ -16,11 +16,11 @@ namespace jxl {
|
|||
|
||||
bool NeedsBlending(const FrameHeader& frame_header);
|
||||
|
||||
void PerformBlending(const float* const* bg, const float* const* fg,
|
||||
float* const* out, size_t x0, size_t xsize,
|
||||
const PatchBlending& color_blending,
|
||||
const PatchBlending* ec_blending,
|
||||
const std::vector<ExtraChannelInfo>& extra_channel_info);
|
||||
Status PerformBlending(const float* const* bg, const float* const* fg,
|
||||
float* const* out, size_t x0, size_t xsize,
|
||||
const PatchBlending& color_blending,
|
||||
const PatchBlending* ec_blending,
|
||||
const std::vector<ExtraChannelInfo>& extra_channel_info);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -28,11 +28,12 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <new>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/image.h"
|
||||
|
||||
#undef HWY_TARGET_INCLUDE
|
||||
#define HWY_TARGET_INCLUDE "lib/jxl/butteraugli/butteraugli.cc"
|
||||
#include <hwy/foreach_target.h>
|
||||
|
@ -222,8 +223,8 @@ void ConvolutionWithTranspose(const ImageF& in,
|
|||
// We retain a special case for 5x5 kernels (even faster than gauss_blur),
|
||||
// optionally use gauss_blur followed by fixup of the borders for large images,
|
||||
// or fall back to the previous truncated FIR followed by a transpose.
|
||||
void Blur(const ImageF& in, float sigma, const ButteraugliParams& params,
|
||||
BlurTemp* temp, ImageF* out) {
|
||||
Status Blur(const ImageF& in, float sigma, const ButteraugliParams& params,
|
||||
BlurTemp* temp, ImageF* out) {
|
||||
std::vector<float> kernel = ComputeKernel(sigma);
|
||||
// Separable5 does an in-place convolution, so this fast path is not safe if
|
||||
// in aliases out.
|
||||
|
@ -241,12 +242,14 @@ void Blur(const ImageF& in, float sigma, const ButteraugliParams& params,
|
|||
{HWY_REP4(w0), HWY_REP4(w1), HWY_REP4(w2)},
|
||||
};
|
||||
Separable5(in, Rect(in), weights, /*pool=*/nullptr, out);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
ImageF* JXL_RESTRICT temp_t = temp->GetTransposed(in);
|
||||
ImageF* temp_t;
|
||||
JXL_RETURN_IF_ERROR(temp->GetTransposed(in, &temp_t));
|
||||
ConvolutionWithTranspose(in, kernel, temp_t);
|
||||
ConvolutionWithTranspose(*temp_t, kernel, out);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allows PaddedMaltaUnit to call either function via overloading.
|
||||
|
@ -386,29 +389,32 @@ void Subtract(const ImageF& a, const ImageF& b, ImageF* c) {
|
|||
}
|
||||
}
|
||||
|
||||
void SeparateLFAndMF(const ButteraugliParams& params, const Image3F& xyb,
|
||||
Image3F* lf, Image3F* mf, BlurTemp* blur_temp) {
|
||||
Status SeparateLFAndMF(const ButteraugliParams& params, const Image3F& xyb,
|
||||
Image3F* lf, Image3F* mf, BlurTemp* blur_temp) {
|
||||
static const double kSigmaLf = 7.15593339443;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
// Extract lf ...
|
||||
Blur(xyb.Plane(i), kSigmaLf, params, blur_temp, &lf->Plane(i));
|
||||
JXL_RETURN_IF_ERROR(
|
||||
Blur(xyb.Plane(i), kSigmaLf, params, blur_temp, &lf->Plane(i)));
|
||||
// ... and keep everything else in mf.
|
||||
Subtract(xyb.Plane(i), lf->Plane(i), &mf->Plane(i));
|
||||
}
|
||||
XybLowFreqToVals(lf);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf,
|
||||
BlurTemp* blur_temp) {
|
||||
Status SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf,
|
||||
BlurTemp* blur_temp) {
|
||||
const HWY_FULL(float) d;
|
||||
static const double kSigmaHf = 3.22489901262;
|
||||
const size_t xsize = mf->xsize();
|
||||
const size_t ysize = mf->ysize();
|
||||
hf[0] = ImageF(xsize, ysize);
|
||||
hf[1] = ImageF(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(hf[0], ImageF::Create(xsize, ysize));
|
||||
JXL_ASSIGN_OR_RETURN(hf[1], ImageF::Create(xsize, ysize));
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
if (i == 2) {
|
||||
Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i));
|
||||
JXL_RETURN_IF_ERROR(
|
||||
Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i)));
|
||||
break;
|
||||
}
|
||||
for (size_t y = 0; y < ysize; ++y) {
|
||||
|
@ -418,7 +424,8 @@ void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf,
|
|||
Store(Load(d, row_mf + x), d, row_hf + x);
|
||||
}
|
||||
}
|
||||
Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i));
|
||||
JXL_RETURN_IF_ERROR(
|
||||
Blur(mf->Plane(i), kSigmaHf, params, blur_temp, &mf->Plane(i)));
|
||||
static const double kRemoveMfRange = 0.29;
|
||||
static const double kAddMfRange = 0.1;
|
||||
if (i == 0) {
|
||||
|
@ -450,16 +457,17 @@ void SeparateMFAndHF(const ButteraugliParams& params, Image3F* mf, ImageF* hf,
|
|||
}
|
||||
// Suppress red-green by intensity change in the high freq channels.
|
||||
SuppressXByY(hf[1], &hf[0]);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf,
|
||||
BlurTemp* blur_temp) {
|
||||
Status SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf,
|
||||
ImageF* uhf, BlurTemp* blur_temp) {
|
||||
const HWY_FULL(float) d;
|
||||
const size_t xsize = hf[0].xsize();
|
||||
const size_t ysize = hf[0].ysize();
|
||||
static const double kSigmaUhf = 1.56416327805;
|
||||
uhf[0] = ImageF(xsize, ysize);
|
||||
uhf[1] = ImageF(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(uhf[0], ImageF::Create(xsize, ysize));
|
||||
JXL_ASSIGN_OR_RETURN(uhf[1], ImageF::Create(xsize, ysize));
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
// Divide hf into hf and uhf.
|
||||
for (size_t y = 0; y < ysize; ++y) {
|
||||
|
@ -469,7 +477,7 @@ void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf,
|
|||
row_uhf[x] = row_hf[x];
|
||||
}
|
||||
}
|
||||
Blur(hf[i], kSigmaUhf, params, blur_temp, &hf[i]);
|
||||
JXL_RETURN_IF_ERROR(Blur(hf[i], kSigmaUhf, params, blur_temp, &hf[i]));
|
||||
static const double kRemoveHfRange = 1.5;
|
||||
static const double kAddHfRange = 0.132;
|
||||
static const double kRemoveUhfRange = 0.04;
|
||||
|
@ -510,6 +518,7 @@ void SeparateHFAndUHF(const ButteraugliParams& params, ImageF* hf, ImageF* uhf,
|
|||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeallocateHFAndUHF(ImageF* hf, ImageF* uhf) {
|
||||
|
@ -519,15 +528,16 @@ void DeallocateHFAndUHF(ImageF* hf, ImageF* uhf) {
|
|||
}
|
||||
}
|
||||
|
||||
static void SeparateFrequencies(size_t xsize, size_t ysize,
|
||||
const ButteraugliParams& params,
|
||||
BlurTemp* blur_temp, const Image3F& xyb,
|
||||
PsychoImage& ps) {
|
||||
ps.lf = Image3F(xyb.xsize(), xyb.ysize());
|
||||
ps.mf = Image3F(xyb.xsize(), xyb.ysize());
|
||||
SeparateLFAndMF(params, xyb, &ps.lf, &ps.mf, blur_temp);
|
||||
SeparateMFAndHF(params, &ps.mf, &ps.hf[0], blur_temp);
|
||||
SeparateHFAndUHF(params, &ps.hf[0], &ps.uhf[0], blur_temp);
|
||||
Status SeparateFrequencies(size_t xsize, size_t ysize,
|
||||
const ButteraugliParams& params, BlurTemp* blur_temp,
|
||||
const Image3F& xyb, PsychoImage& ps) {
|
||||
JXL_ASSIGN_OR_RETURN(ps.lf, Image3F::Create(xyb.xsize(), xyb.ysize()));
|
||||
JXL_ASSIGN_OR_RETURN(ps.mf, Image3F::Create(xyb.xsize(), xyb.ysize()));
|
||||
JXL_RETURN_IF_ERROR(SeparateLFAndMF(params, xyb, &ps.lf, &ps.mf, blur_temp));
|
||||
JXL_RETURN_IF_ERROR(SeparateMFAndHF(params, &ps.mf, &ps.hf[0], blur_temp));
|
||||
JXL_RETURN_IF_ERROR(
|
||||
SeparateHFAndUHF(params, &ps.hf[0], &ps.uhf[0], blur_temp));
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -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<Image3F> SubSample2x(const Image3F& in) {
|
||||
size_t xs = (in.xsize() + 1) / 2;
|
||||
size_t ys = (in.ysize() + 1) / 2;
|
||||
Image3F retval(xs, ys);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F retval, Image3F::Create(xs, ys));
|
||||
for (size_t c = 0; c < 3; ++c) {
|
||||
for (size_t y = 0; y < ys; ++y) {
|
||||
for (size_t x = 0; x < xs; ++x) {
|
||||
|
@ -1724,69 +1746,86 @@ Image3F* ButteraugliComparator::Temp() const {
|
|||
|
||||
void ButteraugliComparator::ReleaseTemp() const { temp_in_use_.clear(); }
|
||||
|
||||
ButteraugliComparator::ButteraugliComparator(const Image3F& rgb0,
|
||||
ButteraugliComparator::ButteraugliComparator(size_t xsize, size_t ysize,
|
||||
const ButteraugliParams& params)
|
||||
: xsize_(rgb0.xsize()),
|
||||
ysize_(rgb0.ysize()),
|
||||
params_(params),
|
||||
temp_(xsize_, ysize_) {
|
||||
if (xsize_ < 8 || ysize_ < 8) {
|
||||
return;
|
||||
: xsize_(xsize), ysize_(ysize), params_(params) {}
|
||||
|
||||
StatusOr<std::unique_ptr<ButteraugliComparator>> ButteraugliComparator::Make(
|
||||
const Image3F& rgb0, const ButteraugliParams& params) {
|
||||
size_t xsize = rgb0.xsize();
|
||||
size_t ysize = rgb0.ysize();
|
||||
std::unique_ptr<ButteraugliComparator> result =
|
||||
std::unique_ptr<ButteraugliComparator>(
|
||||
new ButteraugliComparator(xsize, ysize, params));
|
||||
JXL_ASSIGN_OR_RETURN(result->temp_, Image3F::Create(xsize, ysize));
|
||||
|
||||
if (xsize < 8 || ysize < 8) {
|
||||
return result;
|
||||
}
|
||||
|
||||
Image3F xyb0(xsize_, ysize_);
|
||||
HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)
|
||||
(rgb0, params, Temp(), &blur_temp_, &xyb0);
|
||||
ReleaseTemp();
|
||||
HWY_DYNAMIC_DISPATCH(SeparateFrequencies)
|
||||
(xsize_, ysize_, params_, &blur_temp_, xyb0, pi0_);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F xyb0, Image3F::Create(xsize, ysize));
|
||||
JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)(
|
||||
rgb0, params, result->Temp(), &result->blur_temp_, &xyb0));
|
||||
result->ReleaseTemp();
|
||||
JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(SeparateFrequencies)(
|
||||
xsize, ysize, params, &result->blur_temp_, xyb0, result->pi0_));
|
||||
|
||||
// Awful recursive construction of samples of different resolution.
|
||||
// This is an after-thought and possibly somewhat parallel in
|
||||
// functionality with the PsychoImage multi-resolution approach.
|
||||
sub_.reset(new ButteraugliComparator(SubSample2x(rgb0), params));
|
||||
JXL_ASSIGN_OR_RETURN(Image3F subsampledRgb0, SubSample2x(rgb0));
|
||||
StatusOr<std::unique_ptr<ButteraugliComparator>> sub =
|
||||
ButteraugliComparator::Make(subsampledRgb0, params);
|
||||
if (!sub.ok()) return sub.status();
|
||||
result->sub_ = std::move(sub).value();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void ButteraugliComparator::Mask(ImageF* BUTTERAUGLI_RESTRICT mask) const {
|
||||
HWY_DYNAMIC_DISPATCH(MaskPsychoImage)
|
||||
(pi0_, pi0_, xsize_, ysize_, params_, &blur_temp_, mask, nullptr);
|
||||
Status ButteraugliComparator::Mask(ImageF* BUTTERAUGLI_RESTRICT mask) const {
|
||||
return HWY_DYNAMIC_DISPATCH(MaskPsychoImage)(
|
||||
pi0_, pi0_, xsize_, ysize_, params_, &blur_temp_, mask, nullptr);
|
||||
}
|
||||
|
||||
void ButteraugliComparator::Diffmap(const Image3F& rgb1, ImageF& result) const {
|
||||
Status ButteraugliComparator::Diffmap(const Image3F& rgb1,
|
||||
ImageF& result) const {
|
||||
if (xsize_ < 8 || ysize_ < 8) {
|
||||
ZeroFillImage(&result);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
Image3F xyb1(xsize_, ysize_);
|
||||
HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)
|
||||
(rgb1, params_, Temp(), &blur_temp_, &xyb1);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F xyb1, Image3F::Create(xsize_, ysize_));
|
||||
JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)(
|
||||
rgb1, params_, Temp(), &blur_temp_, &xyb1));
|
||||
ReleaseTemp();
|
||||
DiffmapOpsinDynamicsImage(xyb1, result);
|
||||
JXL_RETURN_IF_ERROR(DiffmapOpsinDynamicsImage(xyb1, result));
|
||||
if (sub_) {
|
||||
if (sub_->xsize_ < 8 || sub_->ysize_ < 8) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
Image3F sub_xyb(sub_->xsize_, sub_->ysize_);
|
||||
HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)
|
||||
(SubSample2x(rgb1), params_, sub_->Temp(), &sub_->blur_temp_, &sub_xyb);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F sub_xyb,
|
||||
Image3F::Create(sub_->xsize_, sub_->ysize_));
|
||||
JXL_ASSIGN_OR_RETURN(Image3F subsampledRgb1, SubSample2x(rgb1));
|
||||
JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(OpsinDynamicsImage)(
|
||||
subsampledRgb1, params_, sub_->Temp(), &sub_->blur_temp_, &sub_xyb));
|
||||
sub_->ReleaseTemp();
|
||||
ImageF subresult;
|
||||
sub_->DiffmapOpsinDynamicsImage(sub_xyb, subresult);
|
||||
JXL_RETURN_IF_ERROR(sub_->DiffmapOpsinDynamicsImage(sub_xyb, subresult));
|
||||
AddSupersampled2x(subresult, 0.5, result);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1,
|
||||
ImageF& result) const {
|
||||
Status ButteraugliComparator::DiffmapOpsinDynamicsImage(const Image3F& xyb1,
|
||||
ImageF& result) const {
|
||||
if (xsize_ < 8 || ysize_ < 8) {
|
||||
ZeroFillImage(&result);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
PsychoImage pi1;
|
||||
HWY_DYNAMIC_DISPATCH(SeparateFrequencies)
|
||||
(xsize_, ysize_, params_, &blur_temp_, xyb1, pi1);
|
||||
result = ImageF(xsize_, ysize_);
|
||||
DiffmapPsychoImage(pi1, result);
|
||||
JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(SeparateFrequencies)(
|
||||
xsize_, ysize_, params_, &blur_temp_, xyb1, pi1));
|
||||
JXL_ASSIGN_OR_RETURN(result, ImageF::Create(xsize_, ysize_));
|
||||
return DiffmapPsychoImage(pi1, result);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -1809,18 +1848,18 @@ void MaltaDiffMapLF(const ImageF& lum0, const ImageF& lum1, const double w_0gt1,
|
|||
|
||||
} // namespace
|
||||
|
||||
void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1,
|
||||
ImageF& diffmap) const {
|
||||
Status ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1,
|
||||
ImageF& diffmap) const {
|
||||
if (xsize_ < 8 || ysize_ < 8) {
|
||||
ZeroFillImage(&diffmap);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
const float hf_asymmetry_ = params_.hf_asymmetry;
|
||||
const float xmul_ = params_.xmul;
|
||||
|
||||
ImageF diffs(xsize_, ysize_);
|
||||
Image3F block_diff_ac(xsize_, ysize_);
|
||||
JXL_ASSIGN_OR_RETURN(ImageF diffs, ImageF::Create(xsize_, ysize_));
|
||||
JXL_ASSIGN_OR_RETURN(Image3F block_diff_ac, Image3F::Create(xsize_, ysize_));
|
||||
ZeroFillImage(&block_diff_ac);
|
||||
MaltaDiffMap(pi0_.uhf[1], pi1.uhf[1], wUhfMalta * hf_asymmetry_,
|
||||
wUhfMalta / hf_asymmetry_, norm1Uhf, &diffs, &block_diff_ac, 1);
|
||||
|
@ -1838,7 +1877,7 @@ void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1,
|
|||
MaltaDiffMapLF(pi0_.mf.Plane(0), pi1.mf.Plane(0), wMfMaltaX, wMfMaltaX,
|
||||
norm1MfX, &diffs, &block_diff_ac, 0);
|
||||
|
||||
Image3F block_diff_dc(xsize_, ysize_);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F block_diff_dc, Image3F::Create(xsize_, ysize_));
|
||||
for (size_t c = 0; c < 3; ++c) {
|
||||
if (c < 2) { // No blue channel error accumulated at HF.
|
||||
HWY_DYNAMIC_DISPATCH(L2DiffAsymmetric)
|
||||
|
@ -1852,12 +1891,13 @@ void ButteraugliComparator::DiffmapPsychoImage(const PsychoImage& pi1,
|
|||
}
|
||||
|
||||
ImageF mask;
|
||||
HWY_DYNAMIC_DISPATCH(MaskPsychoImage)
|
||||
(pi0_, pi1, xsize_, ysize_, params_, &blur_temp_, &mask,
|
||||
&block_diff_ac.Plane(1));
|
||||
JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(MaskPsychoImage)(
|
||||
pi0_, pi1, xsize_, ysize_, params_, &blur_temp_, &mask,
|
||||
&block_diff_ac.Plane(1)));
|
||||
|
||||
HWY_DYNAMIC_DISPATCH(CombineChannelsToDiffmap)
|
||||
(mask, block_diff_dc, block_diff_ac, xmul_, &diffmap);
|
||||
return true;
|
||||
}
|
||||
|
||||
double ButteraugliScoreFromDiffmap(const ImageF& diffmap,
|
||||
|
@ -1872,8 +1912,8 @@ double ButteraugliScoreFromDiffmap(const ImageF& diffmap,
|
|||
return retval;
|
||||
}
|
||||
|
||||
bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
|
||||
double hf_asymmetry, double xmul, ImageF& diffmap) {
|
||||
Status ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
|
||||
double hf_asymmetry, double xmul, ImageF& diffmap) {
|
||||
ButteraugliParams params;
|
||||
params.hf_asymmetry = hf_asymmetry;
|
||||
params.xmul = xmul;
|
||||
|
@ -1893,8 +1933,8 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1,
|
|||
size_t yborder = ysize < kMax ? (kMax - ysize) / 2 : 0;
|
||||
size_t xscaled = std::max<size_t>(kMax, xsize);
|
||||
size_t yscaled = std::max<size_t>(kMax, ysize);
|
||||
Image3F scaled0(xscaled, yscaled);
|
||||
Image3F scaled1(xscaled, yscaled);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F scaled0, Image3F::Create(xscaled, yscaled));
|
||||
JXL_ASSIGN_OR_RETURN(Image3F scaled1, Image3F::Create(xscaled, yscaled));
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
for (size_t y = 0; y < yscaled; ++y) {
|
||||
for (size_t x = 0; x < xscaled; ++x) {
|
||||
|
@ -1907,7 +1947,7 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1,
|
|||
}
|
||||
ImageF diffmap_scaled;
|
||||
const bool ok = ButteraugliDiffmap(scaled0, scaled1, params, diffmap_scaled);
|
||||
diffmap = ImageF(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(diffmap, ImageF::Create(xsize, ysize));
|
||||
for (size_t y = 0; y < ysize; ++y) {
|
||||
for (size_t x = 0; x < xsize; ++x) {
|
||||
diffmap.Row(y)[x] = diffmap_scaled.Row(y + yborder)[x + xborder];
|
||||
|
@ -1916,8 +1956,8 @@ bool ButteraugliDiffmapSmall(const Image3F& rgb0, const Image3F& rgb1,
|
|||
return ok;
|
||||
}
|
||||
|
||||
bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
|
||||
const ButteraugliParams& params, ImageF& diffmap) {
|
||||
Status ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
|
||||
const ButteraugliParams& params, ImageF& diffmap) {
|
||||
const size_t xsize = rgb0.xsize();
|
||||
const size_t ysize = rgb0.ysize();
|
||||
if (xsize < 1 || ysize < 1) {
|
||||
|
@ -1930,8 +1970,9 @@ bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
|
|||
if (xsize < kMax || ysize < kMax) {
|
||||
return ButteraugliDiffmapSmall<kMax>(rgb0, rgb1, params, diffmap);
|
||||
}
|
||||
ButteraugliComparator butteraugli(rgb0, params);
|
||||
butteraugli.Diffmap(rgb1, diffmap);
|
||||
JXL_ASSIGN_OR_RETURN(std::unique_ptr<ButteraugliComparator> butteraugli,
|
||||
ButteraugliComparator::Make(rgb0, params));
|
||||
JXL_RETURN_IF_ERROR(butteraugli->Diffmap(rgb1, diffmap));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1954,9 +1995,9 @@ bool ButteraugliInterface(const Image3F& rgb0, const Image3F& rgb1,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1,
|
||||
const ButteraugliParams& params,
|
||||
ImageF& diffmap, double& diffvalue) {
|
||||
Status ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1,
|
||||
const ButteraugliParams& params,
|
||||
ImageF& diffmap, double& diffvalue) {
|
||||
const size_t xsize = rgb0.xsize();
|
||||
const size_t ysize = rgb0.ysize();
|
||||
if (xsize < 1 || ysize < 1) {
|
||||
|
@ -1973,12 +2014,13 @@ bool ButteraugliInterfaceInPlace(Image3F&& rgb0, Image3F&& rgb1,
|
|||
}
|
||||
ImageF subdiffmap;
|
||||
if (xsize >= 15 && ysize >= 15) {
|
||||
Image3F rgb0_sub = SubSample2x(rgb0);
|
||||
Image3F rgb1_sub = SubSample2x(rgb1);
|
||||
HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)
|
||||
(rgb0_sub, rgb1_sub, params, subdiffmap);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F rgb0_sub, SubSample2x(rgb0));
|
||||
JXL_ASSIGN_OR_RETURN(Image3F rgb1_sub, SubSample2x(rgb1));
|
||||
JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)(
|
||||
rgb0_sub, rgb1_sub, params, subdiffmap));
|
||||
}
|
||||
HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)(rgb0, rgb1, params, diffmap);
|
||||
JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(ButteraugliDiffmapInPlace)(
|
||||
rgb0, rgb1, params, diffmap));
|
||||
if (xsize >= 15 && ysize >= 15) {
|
||||
AddSupersampled2x(subdiffmap, 0.5, diffmap);
|
||||
}
|
||||
|
@ -2066,9 +2108,11 @@ void ScoreToRgb(double score, double good_threshold, double bad_threshold,
|
|||
|
||||
} // namespace
|
||||
|
||||
Image3F CreateHeatMapImage(const ImageF& distmap, double good_threshold,
|
||||
double bad_threshold) {
|
||||
Image3F heatmap(distmap.xsize(), distmap.ysize());
|
||||
StatusOr<Image3F> CreateHeatMapImage(const ImageF& distmap,
|
||||
double good_threshold,
|
||||
double bad_threshold) {
|
||||
JXL_ASSIGN_OR_RETURN(Image3F heatmap,
|
||||
Image3F::Create(distmap.xsize(), distmap.ysize()));
|
||||
for (size_t y = 0; y < distmap.ysize(); ++y) {
|
||||
const float* BUTTERAUGLI_RESTRICT row_distmap = distmap.ConstRow(y);
|
||||
float* BUTTERAUGLI_RESTRICT row_h0 = heatmap.PlaneRow(0, y);
|
||||
|
|
|
@ -14,12 +14,12 @@
|
|||
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/image_ops.h"
|
||||
|
||||
#define BUTTERAUGLI_ENABLE_CHECKS 0
|
||||
#define BUTTERAUGLI_RESTRICT JXL_RESTRICT
|
||||
|
@ -87,9 +87,9 @@ bool ButteraugliInterface(const Image3F &rgb0, const Image3F &rgb1,
|
|||
// Same as ButteraugliInterface, but reuses rgb0 and rgb1 for other purposes
|
||||
// inside the function after they are not needed any more, and it ignores
|
||||
// params.xmul.
|
||||
bool ButteraugliInterfaceInPlace(Image3F &&rgb0, Image3F &&rgb1,
|
||||
const ButteraugliParams ¶ms,
|
||||
ImageF &diffmap, double &diffvalue);
|
||||
Status ButteraugliInterfaceInPlace(Image3F &&rgb0, Image3F &&rgb1,
|
||||
const ButteraugliParams ¶ms,
|
||||
ImageF &diffmap, double &diffvalue);
|
||||
|
||||
// Converts the butteraugli score into fuzzy class values that are continuous
|
||||
// at the class boundary. The class boundary location is based on human
|
||||
|
@ -147,11 +147,13 @@ struct PsychoImage {
|
|||
// Blur needs a transposed image.
|
||||
// Hold it here and only allocate on demand to reduce memory usage.
|
||||
struct BlurTemp {
|
||||
ImageF *GetTransposed(const ImageF &in) {
|
||||
Status GetTransposed(const ImageF &in, ImageF **out) {
|
||||
if (transposed_temp.xsize() == 0) {
|
||||
transposed_temp = ImageF(in.ysize(), in.xsize());
|
||||
JXL_ASSIGN_OR_RETURN(transposed_temp,
|
||||
ImageF::Create(in.ysize(), in.xsize()));
|
||||
}
|
||||
return &transposed_temp;
|
||||
*out = &transposed_temp;
|
||||
return true;
|
||||
}
|
||||
|
||||
ImageF transposed_temp;
|
||||
|
@ -162,22 +164,26 @@ class ButteraugliComparator {
|
|||
// Butteraugli is calibrated at xmul = 1.0. We add a multiplier here so that
|
||||
// we can test the hypothesis that a higher weighing of the X channel would
|
||||
// improve results at higher Butteraugli values.
|
||||
ButteraugliComparator(const Image3F &rgb0, const ButteraugliParams ¶ms);
|
||||
virtual ~ButteraugliComparator() = default;
|
||||
|
||||
static StatusOr<std::unique_ptr<ButteraugliComparator>> Make(
|
||||
const Image3F &rgb0, const ButteraugliParams ¶ms);
|
||||
|
||||
// Computes the butteraugli map between the original image given in the
|
||||
// constructor and the distorted image give here.
|
||||
void Diffmap(const Image3F &rgb1, ImageF &result) const;
|
||||
Status Diffmap(const Image3F &rgb1, ImageF &result) const;
|
||||
|
||||
// Same as above, but OpsinDynamicsImage() was already applied.
|
||||
void DiffmapOpsinDynamicsImage(const Image3F &xyb1, ImageF &result) const;
|
||||
Status DiffmapOpsinDynamicsImage(const Image3F &xyb1, ImageF &result) const;
|
||||
|
||||
// Same as above, but the frequency decomposition was already applied.
|
||||
void DiffmapPsychoImage(const PsychoImage &pi1, ImageF &diffmap) const;
|
||||
Status DiffmapPsychoImage(const PsychoImage &pi1, ImageF &diffmap) const;
|
||||
|
||||
void Mask(ImageF *BUTTERAUGLI_RESTRICT mask) const;
|
||||
Status Mask(ImageF *BUTTERAUGLI_RESTRICT mask) const;
|
||||
|
||||
private:
|
||||
ButteraugliComparator(size_t xsize, size_t ysize,
|
||||
const ButteraugliParams ¶ms);
|
||||
Image3F *Temp() const;
|
||||
void ReleaseTemp() const;
|
||||
|
||||
|
@ -196,18 +202,19 @@ class ButteraugliComparator {
|
|||
};
|
||||
|
||||
// Deprecated.
|
||||
bool ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
|
||||
double hf_asymmetry, double xmul, ImageF &diffmap);
|
||||
Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
|
||||
double hf_asymmetry, double xmul, ImageF &diffmap);
|
||||
|
||||
bool ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
|
||||
const ButteraugliParams ¶ms, ImageF &diffmap);
|
||||
Status ButteraugliDiffmap(const Image3F &rgb0, const Image3F &rgb1,
|
||||
const ButteraugliParams ¶ms, ImageF &diffmap);
|
||||
|
||||
double ButteraugliScoreFromDiffmap(const ImageF &diffmap,
|
||||
const ButteraugliParams *params = nullptr);
|
||||
|
||||
// Generate rgb-representation of the distance between two images.
|
||||
Image3F CreateHeatMapImage(const ImageF &distmap, double good_threshold,
|
||||
double bad_threshold);
|
||||
StatusOr<Image3F> CreateHeatMapImage(const ImageF &distmap,
|
||||
double good_threshold,
|
||||
double bad_threshold);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ using extras::PackedPixelFile;
|
|||
using test::TestImage;
|
||||
|
||||
Image3F SinglePixelImage(float red, float green, float blue) {
|
||||
Image3F img(1, 1);
|
||||
JXL_ASSIGN_OR_DIE(Image3F img, Image3F::Create(1, 1));
|
||||
img.PlaneRow(0, 0)[0] = red;
|
||||
img.PlaneRow(1, 0)[0] = green;
|
||||
img.PlaneRow(2, 0)[0] = blue;
|
||||
|
@ -42,7 +42,7 @@ Image3F GetColorImage(const PackedPixelFile& ppf) {
|
|||
const PackedImage& image = ppf.frames[0].color;
|
||||
const JxlPixelFormat& format = image.format;
|
||||
const uint8_t* pixels = reinterpret_cast<const uint8_t*>(image.pixels());
|
||||
Image3F color(image.xsize, image.ysize);
|
||||
JXL_ASSIGN_OR_DIE(Image3F color, Image3F::Create(image.xsize, image.ysize));
|
||||
for (size_t c = 0; c < format.num_channels; ++c) {
|
||||
JXL_CHECK(ConvertFromExternal(pixels, image.pixels_size, image.xsize,
|
||||
image.ysize, ppf.info.bits_per_sample, format,
|
||||
|
@ -93,7 +93,7 @@ TEST(ButteraugliInPlaceTest, LargeImage) {
|
|||
TestImage img;
|
||||
img.SetDimensions(xsize, ysize).AddFrame().RandomFill(777);
|
||||
Image3F rgb0 = GetColorImage(img.ppf());
|
||||
Image3F rgb1(xsize, ysize);
|
||||
JXL_ASSIGN_OR_DIE(Image3F rgb1, Image3F::Create(xsize, ysize));
|
||||
CopyImageTo(rgb0, &rgb1);
|
||||
AddUniformNoise(&rgb1, 0.02f, 7777);
|
||||
AddEdge(&rgb1, 0.1f, xsize / 2, xsize / 2);
|
||||
|
|
|
@ -5,17 +5,25 @@
|
|||
|
||||
#include "lib/jxl/chroma_from_luma.h"
|
||||
|
||||
#include "lib/jxl/image_ops.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
ColorCorrelationMap::ColorCorrelationMap(size_t xsize, size_t ysize, bool XYB)
|
||||
: ytox_map(DivCeil(xsize, kColorTileDim), DivCeil(ysize, kColorTileDim)),
|
||||
ytob_map(DivCeil(xsize, kColorTileDim), DivCeil(ysize, kColorTileDim)) {
|
||||
ZeroFillImage(&ytox_map);
|
||||
ZeroFillImage(&ytob_map);
|
||||
StatusOr<ColorCorrelationMap> ColorCorrelationMap::Create(size_t xsize,
|
||||
size_t ysize,
|
||||
bool XYB) {
|
||||
ColorCorrelationMap result;
|
||||
size_t xblocks = DivCeil(xsize, kColorTileDim);
|
||||
size_t yblocks = DivCeil(ysize, kColorTileDim);
|
||||
JXL_ASSIGN_OR_RETURN(result.ytox_map, ImageSB::Create(xblocks, yblocks));
|
||||
JXL_ASSIGN_OR_RETURN(result.ytob_map, ImageSB::Create(xblocks, yblocks));
|
||||
ZeroFillImage(&result.ytox_map);
|
||||
ZeroFillImage(&result.ytob_map);
|
||||
if (!XYB) {
|
||||
base_correlation_b_ = 0;
|
||||
result.base_correlation_b_ = 0;
|
||||
}
|
||||
RecomputeDCFactors();
|
||||
result.RecomputeDCFactors();
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
|
|
|
@ -12,19 +12,15 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <vector>
|
||||
#include <limits>
|
||||
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/cms/opsin_params.h"
|
||||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/entropy_coder.h"
|
||||
#include "lib/jxl/field_encodings.h"
|
||||
#include "lib/jxl/fields.h"
|
||||
#include "lib/jxl/frame_dimensions.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/quant_weights.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
|
@ -55,7 +51,8 @@ struct ColorCorrelationMap {
|
|||
// xsize/ysize are in pixels
|
||||
// set XYB=false to do something close to no-op cmap (needed for now since
|
||||
// cmap is mandatory)
|
||||
ColorCorrelationMap(size_t xsize, size_t ysize, bool XYB = true);
|
||||
static StatusOr<ColorCorrelationMap> Create(size_t xsize, size_t ysize,
|
||||
bool XYB = true);
|
||||
|
||||
float YtoXRatio(int32_t x_factor) const {
|
||||
return base_correlation_x_ + x_factor * color_scale_;
|
||||
|
@ -96,7 +93,7 @@ struct ColorCorrelationMap {
|
|||
color_factor_ == kDefaultColorFactor;
|
||||
}
|
||||
|
||||
int32_t RatioJPEG(int32_t factor) const {
|
||||
static int32_t RatioJPEG(int32_t factor) {
|
||||
return factor * (1 << kCFLFixedPointPrecision) / kDefaultColorFactor;
|
||||
}
|
||||
|
||||
|
|
|
@ -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<RenderingIntent>(rendering_intent32);
|
||||
c_enc.rendering_intent =
|
||||
static_cast<RenderingIntent>(candidate_rendering_intent);
|
||||
|
||||
if (profile.has_CICP &&
|
||||
ApplyCICP(profile.CICP.color_primaries,
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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_
|
||||
|
|
|
@ -10,9 +10,6 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#undef HWY_TARGET_INCLUDE
|
||||
|
@ -21,17 +18,9 @@
|
|||
#include <hwy/foreach_target.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/ac_strategy.h"
|
||||
#include "lib/jxl/ans_params.h"
|
||||
#include "lib/jxl/base/bits.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/chroma_from_luma.h"
|
||||
#include "lib/jxl/dec_ans.h"
|
||||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/dec_cache.h"
|
||||
#include "lib/jxl/entropy_coder.h"
|
||||
#include "lib/jxl/image.h"
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace jxl {
|
||||
|
@ -131,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<pixel_type, HWY_FULL(float)> di; // assumes pixel_type <= float
|
||||
|
@ -265,7 +255,9 @@ void DequantDC(const Rect& r, Image3F* dc, ImageB* quant_dc, const Image& in,
|
|||
const int32_t* quant_row_b =
|
||||
in.channel[2].plane.Row(y >> chroma_subsampling.VShift(2));
|
||||
for (size_t x = 0; x < r.xsize(); x++) {
|
||||
int bucket_x = 0, bucket_y = 0, bucket_b = 0;
|
||||
int bucket_x = 0;
|
||||
int bucket_y = 0;
|
||||
int bucket_b = 0;
|
||||
for (int t : bctx.dc_thresholds[0]) {
|
||||
if (quant_row_x[x >> chroma_subsampling.HShift(0)] > t) bucket_x++;
|
||||
}
|
||||
|
@ -296,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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Rect> GenerateTestRectangles(size_t xsize, size_t ysize) {
|
|||
// Ensures Symmetric and Separable give the same result.
|
||||
void VerifySymmetric5(const size_t xsize, const size_t ysize, ThreadPool* pool,
|
||||
Rng* rng) {
|
||||
ImageF in(xsize, ysize);
|
||||
JXL_ASSIGN_OR_DIE(ImageF in, ImageF::Create(xsize, ysize));
|
||||
GenerateImage(*rng, &in, 0.0f, 1.0f);
|
||||
|
||||
for (const Rect& in_rect : GenerateTestRectangles(xsize, ysize)) {
|
||||
|
@ -105,8 +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;
|
||||
|
|
|
@ -8,8 +8,9 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include <memory>
|
||||
|
||||
#include "lib/jxl/base/common.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/image_ops.h"
|
||||
|
@ -50,12 +51,16 @@ template <typename T>
|
|||
class ACImageT final : public ACImage {
|
||||
public:
|
||||
ACImageT() = default;
|
||||
ACImageT(size_t xsize, size_t ysize) {
|
||||
|
||||
static StatusOr<std::unique_ptr<ACImageT>> Make(size_t xsize, size_t ysize) {
|
||||
static_assert(
|
||||
std::is_same<T, int16_t>::value || std::is_same<T, int32_t>::value,
|
||||
"ACImage must be either 32- or 16- bit");
|
||||
img_ = Image3<T>(xsize, ysize);
|
||||
std::unique_ptr<ACImageT> result = jxl::make_unique<ACImageT>();
|
||||
JXL_ASSIGN_OR_RETURN(result->img_, Image3<T>::Create(xsize, ysize));
|
||||
return result;
|
||||
}
|
||||
|
||||
ACType Type() const override {
|
||||
return sizeof(T) == 2 ? ACType::k16 : ACType::k32;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -52,7 +52,8 @@ struct PixelCallback {
|
|||
const bool has_init = init != nullptr;
|
||||
const bool has_run = run != nullptr;
|
||||
const bool has_destroy = destroy != nullptr;
|
||||
JXL_ASSERT(has_init == has_run && has_run == has_destroy);
|
||||
const bool healthy = (has_init == has_run) && (has_run == has_destroy);
|
||||
JXL_ASSERT(healthy);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -128,7 +129,7 @@ struct PassesDecoderState {
|
|||
std::atomic<uint32_t> used_acs{0};
|
||||
|
||||
// Storage for coefficients if in "accumulate" mode.
|
||||
std::unique_ptr<ACImage> coefficients = make_unique<ACImageT<int32_t>>(0, 0);
|
||||
std::unique_ptr<ACImage> coefficients = make_unique<ACImageT<int32_t>>();
|
||||
|
||||
// Rendering pipeline.
|
||||
std::unique_ptr<RenderPipeline> render_pipeline;
|
||||
|
@ -166,8 +167,10 @@ struct PassesDecoderState {
|
|||
|
||||
upsampler8x = GetUpsamplingStage(shared->metadata->transform_data, 0, 3);
|
||||
if (frame_header.loop_filter.epf_iters > 0) {
|
||||
sigma = ImageF(shared->frame_dim.xsize_blocks + 2 * kSigmaPadding,
|
||||
shared->frame_dim.ysize_blocks + 2 * kSigmaPadding);
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
sigma,
|
||||
ImageF::Create(shared->frame_dim.xsize_blocks + 2 * kSigmaPadding,
|
||||
shared->frame_dim.ysize_blocks + 2 * kSigmaPadding));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -193,14 +196,16 @@ struct PassesDecoderState {
|
|||
// Temp images required for decoding a single group. Reduces memory allocations
|
||||
// for large images because we only initialize min(#threads, #groups) instances.
|
||||
struct GroupDecCache {
|
||||
void InitOnce(size_t num_passes, size_t used_acs) {
|
||||
Status InitOnce(size_t num_passes, size_t used_acs) {
|
||||
for (size_t i = 0; i < num_passes; i++) {
|
||||
if (num_nzeroes[i].xsize() == 0) {
|
||||
// Allocate enough for a whole group - partial groups on the
|
||||
// right/bottom border just use a subset. The valid size is passed via
|
||||
// Rect.
|
||||
|
||||
num_nzeroes[i] = Image3I(kGroupDimInBlocks, kGroupDimInBlocks);
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
num_nzeroes[i],
|
||||
Image3I::Create(kGroupDimInBlocks, kGroupDimInBlocks));
|
||||
}
|
||||
}
|
||||
size_t max_block_area = 0;
|
||||
|
@ -227,13 +232,17 @@ struct GroupDecCache {
|
|||
scratch_space = dec_group_block + max_block_area_ * 3;
|
||||
dec_group_qblock = int32_memory_.get();
|
||||
dec_group_qblock16 = int16_memory_.get();
|
||||
return true;
|
||||
}
|
||||
|
||||
void InitDCBufferOnce() {
|
||||
Status InitDCBufferOnce() {
|
||||
if (dc_buffer.xsize() == 0) {
|
||||
dc_buffer = ImageF(kGroupDimInBlocks + kRenderPipelineXOffset * 2,
|
||||
kGroupDimInBlocks + 4);
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
dc_buffer,
|
||||
ImageF::Create(kGroupDimInBlocks + kRenderPipelineXOffset * 2,
|
||||
kGroupDimInBlocks + 4));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Scratch space used by DecGroupImpl().
|
||||
|
|
|
@ -9,11 +9,11 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/base/status.h"
|
||||
|
||||
#undef HWY_TARGET_INCLUDE
|
||||
#define HWY_TARGET_INCLUDE "lib/jxl/dec_external_image.cc"
|
||||
#include <hwy/foreach_target.h>
|
||||
|
@ -113,7 +113,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
|
|||
const size_t ysize = image.ysize();
|
||||
|
||||
if (undo_orientation == Orientation::kFlipHorizontal) {
|
||||
out = Plane<T>(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(xsize, ysize));
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
|
@ -126,7 +126,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
|
|||
},
|
||||
"UndoOrientation"));
|
||||
} else if (undo_orientation == Orientation::kRotate180) {
|
||||
out = Plane<T>(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(xsize, ysize));
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
|
@ -139,7 +139,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
|
|||
},
|
||||
"UndoOrientation"));
|
||||
} else if (undo_orientation == Orientation::kFlipVertical) {
|
||||
out = Plane<T>(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(xsize, ysize));
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
|
@ -152,7 +152,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
|
|||
},
|
||||
"UndoOrientation"));
|
||||
} else if (undo_orientation == Orientation::kTranspose) {
|
||||
out = Plane<T>(ysize, xsize);
|
||||
JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize));
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
|
@ -164,7 +164,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
|
|||
},
|
||||
"UndoOrientation"));
|
||||
} else if (undo_orientation == Orientation::kRotate90) {
|
||||
out = Plane<T>(ysize, xsize);
|
||||
JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize));
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
|
@ -176,7 +176,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
|
|||
},
|
||||
"UndoOrientation"));
|
||||
} else if (undo_orientation == Orientation::kAntiTranspose) {
|
||||
out = Plane<T>(ysize, xsize);
|
||||
JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize));
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
|
@ -188,7 +188,7 @@ Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
|
|||
},
|
||||
"UndoOrientation"));
|
||||
} else if (undo_orientation == Orientation::kRotate270) {
|
||||
out = Plane<T>(ysize, xsize);
|
||||
JXL_ASSIGN_OR_RETURN(out, Plane<T>::Create(ysize, xsize));
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
|
@ -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<uint32_t>(ysize),
|
||||
[&](size_t num_threads) {
|
||||
f16_cache =
|
||||
Plane<hwy::float16_t>(xsize, num_channels * num_threads);
|
||||
return InitOutCallback(num_threads);
|
||||
StatusOr<Plane<hwy::float16_t>> f16_cache_or =
|
||||
Plane<hwy::float16_t>::Create(xsize,
|
||||
num_channels * num_threads);
|
||||
if (!f16_cache_or.ok()) return false;
|
||||
f16_cache = std::move(f16_cache_or).value();
|
||||
return !!InitOutCallback(num_threads);
|
||||
},
|
||||
[&](const uint32_t task, const size_t thread) {
|
||||
const int64_t y = task;
|
||||
|
@ -398,8 +401,11 @@ Status ConvertChannelsToExternal(const ImageF* in_channels[],
|
|||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize),
|
||||
[&](size_t num_threads) {
|
||||
u32_cache = Plane<uint32_t>(xsize, num_channels * num_threads);
|
||||
return InitOutCallback(num_threads);
|
||||
StatusOr<Plane<uint32_t>> u32_cache_or =
|
||||
Plane<uint32_t>::Create(xsize, num_channels * num_threads);
|
||||
if (!u32_cache_or.ok()) return false;
|
||||
u32_cache = std::move(u32_cache_or).value();
|
||||
return !!InitOutCallback(num_threads);
|
||||
},
|
||||
[&](const uint32_t task, const size_t thread) {
|
||||
const int64_t y = task;
|
||||
|
@ -453,7 +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),
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
|
@ -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<ACImageT<int16_t>>(xs, ys);
|
||||
JXL_ASSIGN_OR_RETURN(dec_state_->coefficients,
|
||||
ACImageT<int16_t>::Make(xs, ys));
|
||||
} else {
|
||||
dec_state_->coefficients = make_unique<ACImageT<int32_t>>(xs, ys);
|
||||
JXL_ASSIGN_OR_RETURN(dec_state_->coefficients,
|
||||
ACImageT<int32_t>::Make(xs, ys));
|
||||
}
|
||||
if (store) {
|
||||
dec_state_->coefficients->ZeroFill();
|
||||
|
@ -482,8 +486,8 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
|
|||
bool should_run_pipeline = true;
|
||||
|
||||
if (frame_header_.encoding == FrameEncoding::kVarDCT) {
|
||||
group_dec_caches_[thread].InitOnce(frame_header_.passes.num_passes,
|
||||
dec_state_->used_acs);
|
||||
JXL_RETURN_IF_ERROR(group_dec_caches_[thread].InitOnce(
|
||||
frame_header_.passes.num_passes, dec_state_->used_acs));
|
||||
JXL_RETURN_IF_ERROR(DecodeGroup(frame_header_, br, num_passes, ac_group_id,
|
||||
dec_state_, &group_dec_caches_[thread],
|
||||
thread, render_pipeline_input, decoded_,
|
||||
|
@ -498,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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -27,13 +27,10 @@
|
|||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/coeff_order.h"
|
||||
#include "lib/jxl/common.h" // kMaxNumPasses
|
||||
#include "lib/jxl/convolve.h"
|
||||
#include "lib/jxl/dct_scales.h"
|
||||
#include "lib/jxl/dec_cache.h"
|
||||
#include "lib/jxl/dec_transforms-inl.h"
|
||||
#include "lib/jxl/dec_xyb.h"
|
||||
#include "lib/jxl/entropy_coder.h"
|
||||
#include "lib/jxl/epf.h"
|
||||
#include "lib/jxl/quant_weights.h"
|
||||
#include "lib/jxl/quantizer-inl.h"
|
||||
#include "lib/jxl/quantizer.h"
|
||||
|
@ -70,6 +67,11 @@ namespace jxl {
|
|||
namespace HWY_NAMESPACE {
|
||||
|
||||
// These templates are not found via ADL.
|
||||
using hwy::HWY_NAMESPACE::AllFalse;
|
||||
using hwy::HWY_NAMESPACE::Gt;
|
||||
using hwy::HWY_NAMESPACE::Le;
|
||||
using hwy::HWY_NAMESPACE::MaskFromVec;
|
||||
using hwy::HWY_NAMESPACE::Or;
|
||||
using hwy::HWY_NAMESPACE::Rebind;
|
||||
using hwy::HWY_NAMESPACE::ShiftRight;
|
||||
|
||||
|
@ -77,9 +79,11 @@ using D = HWY_FULL(float);
|
|||
using DU = HWY_FULL(uint32_t);
|
||||
using DI = HWY_FULL(int32_t);
|
||||
using DI16 = Rebind<int16_t, DI>;
|
||||
using DI16_FULL = HWY_CAPPED(int16_t, kDCTBlockSize);
|
||||
constexpr D d;
|
||||
constexpr DI di;
|
||||
constexpr DI16 di16;
|
||||
constexpr DI16_FULL di16_full;
|
||||
|
||||
// TODO(veluca): consider SIMDfying.
|
||||
void Transpose8x8InPlace(int32_t* JXL_RESTRICT block) {
|
||||
|
@ -181,6 +185,9 @@ Status DecodeGroupImpl(const FrameHeader& frame_header,
|
|||
|
||||
const YCbCrChromaSubsampling& cs = frame_header.chroma_subsampling;
|
||||
|
||||
const auto kJpegDctMin = Set(di16_full, -4095);
|
||||
const auto kJpegDctMax = Set(di16_full, 4095);
|
||||
|
||||
size_t idct_stride[3];
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
idct_stride[c] = render_pipeline_input.GetBuffer(c).first->PixelsPerRow();
|
||||
|
@ -355,7 +362,7 @@ Status DecodeGroupImpl(const FrameHeader& frame_header,
|
|||
int16_t* JXL_RESTRICT jpeg_pos =
|
||||
jpeg_row[c] + sbx[c] * kDCTBlockSize;
|
||||
// JPEG XL is transposed, JPEG is not.
|
||||
auto transposed_dct = qblock[c].ptr32;
|
||||
auto* transposed_dct = qblock[c].ptr32;
|
||||
Transpose8x8InPlace(transposed_dct);
|
||||
// No CfL - no need to store the y block converted to integers.
|
||||
if (!cs.Is444() ||
|
||||
|
@ -391,6 +398,16 @@ Status DecodeGroupImpl(const FrameHeader& frame_header,
|
|||
}
|
||||
jpeg_pos[0] =
|
||||
Clamp1<float>(dc_rows[c][sbx[c]] - dcoff[c], -2047, 2047);
|
||||
auto overflow = MaskFromVec(Set(di16_full, 0));
|
||||
auto underflow = MaskFromVec(Set(di16_full, 0));
|
||||
for (int i = 0; i < 64; i += Lanes(di16_full)) {
|
||||
auto in = LoadU(di16_full, jpeg_pos + i);
|
||||
overflow = Or(overflow, Gt(in, kJpegDctMax));
|
||||
underflow = Or(underflow, Lt(in, kJpegDctMin));
|
||||
}
|
||||
if (!AllFalse(di16_full, Or(overflow, underflow))) {
|
||||
return JXL_FAILURE("JPEG DCT coefficients out of range");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
HWY_ALIGN float* const block = group_dec_cache->dec_group_block;
|
||||
|
@ -683,7 +700,7 @@ Status DecodeGroup(const FrameHeader& frame_header,
|
|||
}
|
||||
|
||||
if (draw == kDraw && num_passes == 0 && first_pass == 0) {
|
||||
group_dec_cache->InitDCBufferOnce();
|
||||
JXL_RETURN_IF_ERROR(group_dec_cache->InitDCBufferOnce());
|
||||
const YCbCrChromaSubsampling& cs = frame_header.chroma_subsampling;
|
||||
for (size_t c : {0, 1, 2}) {
|
||||
size_t hs = cs.HShift(c);
|
||||
|
@ -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,
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/frame_header.h"
|
||||
|
@ -18,10 +17,8 @@
|
|||
#include <hwy/foreach_target.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/alpha.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/printf_macros.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/compressed_dc.h"
|
||||
#include "lib/jxl/epf.h"
|
||||
|
@ -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(
|
||||
|
|
|
@ -10,26 +10,16 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/ans_params.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/override.h"
|
||||
#include "lib/jxl/base/printf_macros.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/blending.h"
|
||||
#include "lib/jxl/chroma_from_luma.h"
|
||||
#include "lib/jxl/common.h" // kMaxNumReferenceFrames
|
||||
#include "lib/jxl/dec_ans.h"
|
||||
#include "lib/jxl/dec_frame.h"
|
||||
#include "lib/jxl/entropy_coder.h"
|
||||
#include "lib/jxl/frame_header.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/image_bundle.h"
|
||||
#include "lib/jxl/image_ops.h"
|
||||
#include "lib/jxl/pack_signed.h"
|
||||
#include "lib/jxl/patch_dictionary_internal.h"
|
||||
|
||||
|
@ -322,8 +312,8 @@ std::vector<size_t> PatchDictionary::GetPatchesForRow(size_t y) const {
|
|||
|
||||
// Adds patches to a segment of `xsize` pixels, starting at `inout`, assumed
|
||||
// to be located at position (x0, y) in the frame.
|
||||
void PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0,
|
||||
size_t xsize) const {
|
||||
Status PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0,
|
||||
size_t xsize) const {
|
||||
size_t num_ec = shared_->metadata->m.num_extra_channels;
|
||||
std::vector<const float*> fg_ptrs(3 + num_ec);
|
||||
for (size_t pos_idx : GetPatchesForRow(y)) {
|
||||
|
@ -352,10 +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
|
||||
|
|
|
@ -12,12 +12,10 @@
|
|||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/image.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
|
@ -109,7 +107,8 @@ class PatchDictionary {
|
|||
|
||||
// Adds patches to a segment of `xsize` pixels, starting at `inout`, assumed
|
||||
// to be located at position (x0, y) in the frame.
|
||||
void AddOneRow(float* const* inout, size_t y, size_t x0, size_t xsize) const;
|
||||
Status AddOneRow(float* const* inout, size_t y, size_t x0,
|
||||
size_t xsize) const;
|
||||
|
||||
// Returns dependencies of this patch dictionary on reference frame ids as a
|
||||
// bit mask: bits 0-3 indicate reference frame 0-3.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -47,7 +47,6 @@
|
|||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/dec_external_image.h"
|
||||
#include "lib/jxl/enc_aux_out.h"
|
||||
#include "lib/jxl/enc_butteraugli_comparator.h"
|
||||
#include "lib/jxl/enc_external_image.h"
|
||||
#include "lib/jxl/enc_fields.h"
|
||||
#include "lib/jxl/enc_frame.h"
|
||||
|
@ -113,23 +112,23 @@ enum CodeStreamBoxFormat {
|
|||
};
|
||||
|
||||
// Unknown boxes for testing
|
||||
static const char* unk1_box_type = "unk1";
|
||||
static const char* unk1_box_contents = "abcdefghijklmnopqrstuvwxyz";
|
||||
static const size_t unk1_box_size = strlen(unk1_box_contents);
|
||||
static const char* unk2_box_type = "unk2";
|
||||
static const char* unk2_box_contents = "0123456789";
|
||||
static const size_t unk2_box_size = strlen(unk2_box_contents);
|
||||
static const char* unk3_box_type = "unk3";
|
||||
static const char* unk3_box_contents = "ABCDEF123456";
|
||||
static const size_t unk3_box_size = strlen(unk3_box_contents);
|
||||
const char* unk1_box_type = "unk1";
|
||||
const char* unk1_box_contents = "abcdefghijklmnopqrstuvwxyz";
|
||||
const size_t unk1_box_size = strlen(unk1_box_contents);
|
||||
const char* unk2_box_type = "unk2";
|
||||
const char* unk2_box_contents = "0123456789";
|
||||
const size_t unk2_box_size = strlen(unk2_box_contents);
|
||||
const char* unk3_box_type = "unk3";
|
||||
const char* unk3_box_contents = "ABCDEF123456";
|
||||
const size_t unk3_box_size = strlen(unk3_box_contents);
|
||||
// Box with brob-compressed exif, including header
|
||||
static const uint8_t* box_brob_exif = reinterpret_cast<const uint8_t*>(
|
||||
const uint8_t* box_brob_exif = reinterpret_cast<const uint8_t*>(
|
||||
"\0\0\0@brobExif\241\350\2\300\177\244v\2525\304\360\27=?\267{"
|
||||
"\33\37\314\332\214QX17PT\"\256\0\0\202s\214\313t\333\310\320k\20\276\30"
|
||||
"\204\277l$\326c#\1\b");
|
||||
size_t box_brob_exif_size = 64;
|
||||
// The uncompressed Exif data from the brob box
|
||||
static const uint8_t* exif_uncompressed = reinterpret_cast<const uint8_t*>(
|
||||
const uint8_t* exif_uncompressed = reinterpret_cast<const uint8_t*>(
|
||||
"\0\0\0\0MM\0*"
|
||||
"\0\0\0\b\0\5\1\22\0\3\0\0\0\1\0\5\0\0\1\32\0\5\0\0\0\1\0\0\0J\1\33\0\5\0\0"
|
||||
"\0\1\0\0\0R\1("
|
||||
|
@ -214,13 +213,15 @@ void GeneratePreview(PreviewMode preview_mode, ImageBundle* ib) {
|
|||
}
|
||||
}
|
||||
};
|
||||
Image3F preview(ib->xsize() * 7, ib->ysize() * 7);
|
||||
JXL_ASSIGN_OR_DIE(Image3F preview,
|
||||
Image3F::Create(ib->xsize() * 7, ib->ysize() * 7));
|
||||
for (size_t c = 0; c < 3; ++c) {
|
||||
upsample7(ib->color()->Plane(c), &preview.Plane(c));
|
||||
}
|
||||
std::vector<ImageF> extra_channels;
|
||||
for (size_t i = 0; i < ib->extra_channels().size(); ++i) {
|
||||
ImageF ec(ib->xsize() * 7, ib->ysize() * 7);
|
||||
JXL_ASSIGN_OR_DIE(ImageF ec,
|
||||
ImageF::Create(ib->xsize() * 7, ib->ysize() * 7));
|
||||
upsample7(ib->extra_channels()[i], &ec);
|
||||
extra_channels.emplace_back(std::move(ec));
|
||||
}
|
||||
|
@ -321,7 +322,7 @@ std::vector<uint8_t> CreateTestJXLCodestream(
|
|||
}
|
||||
}
|
||||
if (params.preview_mode) {
|
||||
io.preview_frame = io.Main().Copy();
|
||||
JXL_ASSIGN_OR_DIE(io.preview_frame, io.Main().Copy());
|
||||
GeneratePreview(params.preview_mode, &io.preview_frame);
|
||||
io.metadata.m.have_preview = true;
|
||||
EXPECT_TRUE(io.metadata.m.preview_size.Set(io.preview_frame.xsize(),
|
||||
|
@ -622,6 +623,10 @@ std::vector<uint8_t> DecodeWithAPI(Span<const uint8_t> compressed,
|
|||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
using jxl::Image3F;
|
||||
using jxl::ImageF;
|
||||
using jxl::test::ButteraugliDistance;
|
||||
|
||||
TEST(DecodeTest, JxlSignatureCheckTest) {
|
||||
std::vector<std::pair<int, std::vector<uint8_t>>> tests = {
|
||||
// No JPEGXL header starts with 'a'.
|
||||
|
@ -1420,7 +1425,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
|
|||
c.add_intrinsic_size = intrinsic_size;
|
||||
c.xsize = xsize;
|
||||
c.ysize = ysize;
|
||||
c.add_container = (CodeStreamBoxFormat)box;
|
||||
c.add_container = box;
|
||||
c.output_channels = ch.output_channels;
|
||||
c.data_type = format.data_type;
|
||||
c.endianness = format.endianness;
|
||||
|
@ -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<uint8_t> 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<uint8_t> 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<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
|
||||
JxlPixelFormat format = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
|
||||
int events = JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FULL_IMAGE;
|
||||
|
@ -1796,7 +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<uint8_t>& compressed,
|
|||
EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
|
||||
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &info));
|
||||
EXPECT_EQ(JXL_DEC_COLOR_ENCODING, JxlDecoderProcessInput(dec));
|
||||
// TODO(eustas): why unused?
|
||||
std::string color_space_in = GetOrigProfile(dec);
|
||||
if (with_cms) {
|
||||
JxlDecoderSetCms(dec, *JxlGetDefaultCms());
|
||||
|
@ -2009,7 +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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 3, 0);
|
||||
JxlPixelFormat format_orig = {3, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
|
||||
for (jxl::PreviewMode mode : {jxl::kSmallPreview, jxl::kBigPreview}) {
|
||||
|
@ -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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> frames[num_frames + 5];
|
||||
JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
|
||||
|
@ -3376,7 +3407,10 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
|
|||
|
||||
std::vector<uint32_t> frame_durations_c;
|
||||
std::vector<uint32_t> frame_durations_nc;
|
||||
std::vector<uint32_t> frame_xsize, frame_ysize, frame_x0, frame_y0;
|
||||
std::vector<uint32_t> frame_xsize;
|
||||
std::vector<uint32_t> frame_ysize;
|
||||
std::vector<uint32_t> frame_x0;
|
||||
std::vector<uint32_t> frame_y0;
|
||||
|
||||
for (size_t i = 0; i < num_frames; ++i) {
|
||||
size_t cropxsize = 1 + xsize * 2 / (i + 1);
|
||||
|
@ -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<uint8_t> 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<uint8_t> pixels =
|
||||
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
|
||||
|
@ -4063,7 +4100,8 @@ void VerifyFilePosition(size_t expected_pos, const std::vector<uint8_t>& data,
|
|||
}
|
||||
|
||||
TEST(DecodeTest, InputHandlingTestOneShot) {
|
||||
size_t xsize = 508, ysize = 470;
|
||||
size_t xsize = 508;
|
||||
size_t ysize = 470;
|
||||
uint32_t num_channels = 3;
|
||||
std::vector<uint8_t> pixels =
|
||||
jxl::test::GetSomeTestImage(xsize, ysize, num_channels, 0);
|
||||
|
@ -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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
|
||||
JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
|
||||
// Lossless to verify pixels exactly after roundtrip.
|
||||
|
@ -5482,10 +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<jxl::ImageF> ec;
|
||||
std::vector<ImageF> ec;
|
||||
ec.push_back(std::move(spot));
|
||||
io.frames[0].SetExtraChannels(std::move(ec));
|
||||
|
||||
|
|
|
@ -18,20 +18,15 @@
|
|||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/ac_strategy.h"
|
||||
#include "lib/jxl/ans_params.h"
|
||||
#include "lib/jxl/base/bits.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/fast_math-inl.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/coeff_order_fwd.h"
|
||||
#include "lib/jxl/convolve.h"
|
||||
#include "lib/jxl/dct_scales.h"
|
||||
#include "lib/jxl/dec_transforms-inl.h"
|
||||
#include "lib/jxl/enc_aux_out.h"
|
||||
#include "lib/jxl/enc_debug_image.h"
|
||||
#include "lib/jxl/enc_params.h"
|
||||
#include "lib/jxl/enc_transforms-inl.h"
|
||||
#include "lib/jxl/entropy_coder.h"
|
||||
#include "lib/jxl/simd_util.h"
|
||||
|
||||
// Some of the floating point constants in this file and in other
|
||||
|
@ -215,10 +210,10 @@ const uint8_t* TypeMask(const uint8_t& raw_strategy) {
|
|||
return kMask[raw_strategy];
|
||||
}
|
||||
|
||||
void DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize,
|
||||
size_t ysize, const char* tag, AuxOut* aux_out,
|
||||
const CompressParams& cparams) {
|
||||
Image3F color_acs(xsize, ysize);
|
||||
Status DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize,
|
||||
size_t ysize, const char* tag, AuxOut* aux_out,
|
||||
const CompressParams& cparams) {
|
||||
JXL_ASSIGN_OR_RETURN(Image3F color_acs, Image3F::Create(xsize, ysize));
|
||||
for (size_t y = 0; y < ysize; y++) {
|
||||
float* JXL_RESTRICT rows[3] = {
|
||||
color_acs.PlaneRow(0, y),
|
||||
|
@ -269,7 +264,7 @@ void DumpAcStrategy(const AcStrategyImage& ac_strategy, size_t xsize,
|
|||
}
|
||||
}
|
||||
}
|
||||
DumpImage(cparams, tag, color_acs);
|
||||
return DumpImage(cparams, tag, color_acs);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
@ -26,8 +27,6 @@
|
|||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/butteraugli/butteraugli.h"
|
||||
#include "lib/jxl/cms/opsin_params.h"
|
||||
#include "lib/jxl/coeff_order_fwd.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
#include "lib/jxl/convolve.h"
|
||||
#include "lib/jxl/dec_cache.h"
|
||||
#include "lib/jxl/dec_group.h"
|
||||
|
@ -102,12 +101,12 @@ V ComputeMask(const D d, const V out_val) {
|
|||
}
|
||||
|
||||
// mul and mul2 represent a scaling difference between jxl and butteraugli.
|
||||
static const float kSGmul = 226.77216153508914f;
|
||||
static const float kSGmul2 = 1.0f / 73.377132366608819f;
|
||||
static const float kLog2 = 0.693147181f;
|
||||
const float kSGmul = 226.77216153508914f;
|
||||
const float kSGmul2 = 1.0f / 73.377132366608819f;
|
||||
const float kLog2 = 0.693147181f;
|
||||
// Includes correction factor for std::log -> log2.
|
||||
static const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2;
|
||||
static const float kSGVOffset = 7.7825991679894591f;
|
||||
const float kSGRetMul = kSGmul2 * 18.6580932135f * kLog2;
|
||||
const float kSGVOffset = 7.7825991679894591f;
|
||||
|
||||
template <bool invert, typename D, typename V>
|
||||
V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) {
|
||||
|
@ -131,7 +130,7 @@ V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) {
|
|||
}
|
||||
|
||||
template <bool invert = false>
|
||||
static float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) {
|
||||
float RatioOfDerivativesOfCubicRootToSimpleGamma(float v) {
|
||||
using DScalar = HWY_CAPPED(float, 1);
|
||||
auto vscalar = Load(DScalar(), &v);
|
||||
return GetLane(
|
||||
|
@ -396,12 +395,16 @@ void FuzzyErosion(const float butteraugli_target, const Rect& from_rect,
|
|||
}
|
||||
|
||||
struct AdaptiveQuantizationImpl {
|
||||
void PrepareBuffers(size_t num_threads) {
|
||||
diff_buffer = ImageF(kEncTileDim + 8, num_threads);
|
||||
Status PrepareBuffers(size_t num_threads) {
|
||||
JXL_ASSIGN_OR_RETURN(diff_buffer,
|
||||
ImageF::Create(kEncTileDim + 8, num_threads));
|
||||
for (size_t i = pre_erosion.size(); i < num_threads; i++) {
|
||||
pre_erosion.emplace_back(kEncTileDimInBlocks * 2 + 2,
|
||||
kEncTileDimInBlocks * 2 + 2);
|
||||
JXL_ASSIGN_OR_RETURN(ImageF tmp,
|
||||
ImageF::Create(kEncTileDimInBlocks * 2 + 2,
|
||||
kEncTileDimInBlocks * 2 + 2));
|
||||
pre_erosion.emplace_back(std::move(tmp));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ComputeTile(float butteraugli_target, float scale, const Image3F& xyb,
|
||||
|
@ -568,8 +571,7 @@ struct AdaptiveQuantizationImpl {
|
|||
ImageF diff_buffer;
|
||||
};
|
||||
|
||||
static void Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1,
|
||||
const Rect& rect) {
|
||||
Status Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1, const Rect& rect) {
|
||||
// Blur the mask1x1 to obtain the masking image.
|
||||
// Before blurring it contains an image of absolute value of the
|
||||
// Laplacian of the intensity channel.
|
||||
|
@ -595,30 +597,30 @@ static void Blur1x1Masking(ThreadPool* pool, ImageF* mask1x1,
|
|||
{HWY_REP4(normalize_mul * kFilterMask1x1[1])},
|
||||
{HWY_REP4(normalize_mul * kFilterMask1x1[4])},
|
||||
{HWY_REP4(normalize_mul * kFilterMask1x1[3])}};
|
||||
ImageF temp(rect.xsize(), rect.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(ImageF temp, ImageF::Create(rect.xsize(), rect.ysize()));
|
||||
Symmetric5(*mask1x1, rect, weights, pool, &temp);
|
||||
*mask1x1 = std::move(temp);
|
||||
return true;
|
||||
}
|
||||
|
||||
ImageF AdaptiveQuantizationMap(const float butteraugli_target,
|
||||
const Image3F& xyb, const Rect& rect,
|
||||
float scale, ThreadPool* pool, ImageF* mask,
|
||||
ImageF* mask1x1) {
|
||||
StatusOr<ImageF> AdaptiveQuantizationMap(const float butteraugli_target,
|
||||
const Image3F& xyb, const Rect& rect,
|
||||
float scale, ThreadPool* pool,
|
||||
ImageF* mask, ImageF* mask1x1) {
|
||||
JXL_DASSERT(rect.xsize() % kBlockDim == 0);
|
||||
JXL_DASSERT(rect.ysize() % kBlockDim == 0);
|
||||
AdaptiveQuantizationImpl impl;
|
||||
const size_t xsize_blocks = rect.xsize() / kBlockDim;
|
||||
const size_t ysize_blocks = rect.ysize() / kBlockDim;
|
||||
impl.aq_map = ImageF(xsize_blocks, ysize_blocks);
|
||||
*mask = ImageF(xsize_blocks, ysize_blocks);
|
||||
*mask1x1 = ImageF(xyb.xsize(), xyb.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(impl.aq_map, ImageF::Create(xsize_blocks, ysize_blocks));
|
||||
JXL_ASSIGN_OR_RETURN(*mask, ImageF::Create(xsize_blocks, ysize_blocks));
|
||||
JXL_ASSIGN_OR_RETURN(*mask1x1, ImageF::Create(xyb.xsize(), xyb.ysize()));
|
||||
JXL_CHECK(RunOnPool(
|
||||
pool, 0,
|
||||
DivCeil(xsize_blocks, kEncTileDimInBlocks) *
|
||||
DivCeil(ysize_blocks, kEncTileDimInBlocks),
|
||||
[&](const size_t num_threads) {
|
||||
impl.PrepareBuffers(num_threads);
|
||||
return true;
|
||||
return !!impl.PrepareBuffers(num_threads);
|
||||
},
|
||||
[&](const uint32_t tid, const size_t thread) {
|
||||
size_t n_enc_tiles = DivCeil(xsize_blocks, kEncTileDimInBlocks);
|
||||
|
@ -634,7 +636,7 @@ ImageF AdaptiveQuantizationMap(const float butteraugli_target,
|
|||
},
|
||||
"AQ DiffPrecompute"));
|
||||
|
||||
Blur1x1Masking(pool, mask1x1, rect);
|
||||
JXL_RETURN_IF_ERROR(Blur1x1Masking(pool, mask1x1, rect));
|
||||
return std::move(impl).aq_map;
|
||||
}
|
||||
|
||||
|
@ -654,24 +656,28 @@ namespace {
|
|||
// If true, prints the quantization maps at each iteration.
|
||||
constexpr bool FLAGS_dump_quant_state = false;
|
||||
|
||||
void DumpHeatmap(const CompressParams& cparams, const AuxOut* aux_out,
|
||||
const std::string& label, const ImageF& image,
|
||||
float good_threshold, float bad_threshold) {
|
||||
Status DumpHeatmap(const CompressParams& cparams, const AuxOut* aux_out,
|
||||
const std::string& label, const ImageF& image,
|
||||
float good_threshold, float bad_threshold) {
|
||||
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) {
|
||||
Image3F heatmap = CreateHeatMapImage(image, good_threshold, bad_threshold);
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
Image3F heatmap,
|
||||
CreateHeatMapImage(image, good_threshold, bad_threshold));
|
||||
char filename[200];
|
||||
snprintf(filename, sizeof(filename), "%s%05d", label.c_str(),
|
||||
aux_out->num_butteraugli_iters);
|
||||
DumpImage(cparams, filename, heatmap);
|
||||
JXL_RETURN_IF_ERROR(DumpImage(cparams, filename, heatmap));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out,
|
||||
float ba_target, const ImageF& quant_field,
|
||||
const ImageF& tile_heatmap, const ImageF& bt_diffmap) {
|
||||
Status DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out,
|
||||
float ba_target, const ImageF& quant_field,
|
||||
const ImageF& tile_heatmap, const ImageF& bt_diffmap) {
|
||||
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) {
|
||||
if (!WantDebugOutput(cparams)) return;
|
||||
ImageF inv_qmap(quant_field.xsize(), quant_field.ysize());
|
||||
if (!WantDebugOutput(cparams)) return true;
|
||||
JXL_ASSIGN_OR_RETURN(ImageF inv_qmap, ImageF::Create(quant_field.xsize(),
|
||||
quant_field.ysize()));
|
||||
for (size_t y = 0; y < quant_field.ysize(); ++y) {
|
||||
const float* JXL_RESTRICT row_q = quant_field.ConstRow(y);
|
||||
float* JXL_RESTRICT row_inv_q = inv_qmap.Row(y);
|
||||
|
@ -679,21 +685,24 @@ void DumpHeatmaps(const CompressParams& cparams, const AuxOut* aux_out,
|
|||
row_inv_q[x] = 1.0f / row_q[x]; // never zero
|
||||
}
|
||||
}
|
||||
DumpHeatmap(cparams, aux_out, "quant_heatmap", inv_qmap, 4.0f * ba_target,
|
||||
6.0f * ba_target);
|
||||
DumpHeatmap(cparams, aux_out, "tile_heatmap", tile_heatmap, ba_target,
|
||||
1.5f * ba_target);
|
||||
JXL_RETURN_IF_ERROR(DumpHeatmap(cparams, aux_out, "quant_heatmap", inv_qmap,
|
||||
4.0f * ba_target, 6.0f * ba_target));
|
||||
JXL_RETURN_IF_ERROR(DumpHeatmap(cparams, aux_out, "tile_heatmap",
|
||||
tile_heatmap, ba_target, 1.5f * ba_target));
|
||||
// matches heat maps produced by the command line tool.
|
||||
DumpHeatmap(cparams, aux_out, "bt_diffmap", bt_diffmap,
|
||||
ButteraugliFuzzyInverse(1.5), ButteraugliFuzzyInverse(0.5));
|
||||
JXL_RETURN_IF_ERROR(DumpHeatmap(cparams, aux_out, "bt_diffmap", bt_diffmap,
|
||||
ButteraugliFuzzyInverse(1.5),
|
||||
ButteraugliFuzzyInverse(0.5)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ImageF TileDistMap(const ImageF& distmap, int tile_size, int margin,
|
||||
const AcStrategyImage& ac_strategy) {
|
||||
StatusOr<ImageF> TileDistMap(const ImageF& distmap, int tile_size, int margin,
|
||||
const AcStrategyImage& ac_strategy) {
|
||||
const int tile_xsize = (distmap.xsize() + tile_size - 1) / tile_size;
|
||||
const int tile_ysize = (distmap.ysize() + tile_size - 1) / tile_size;
|
||||
ImageF tile_distmap(tile_xsize, tile_ysize);
|
||||
JXL_ASSIGN_OR_RETURN(ImageF tile_distmap,
|
||||
ImageF::Create(tile_xsize, tile_ysize));
|
||||
size_t distmap_stride = tile_distmap.PixelsPerRow();
|
||||
for (int tile_y = 0; tile_y < tile_ysize; ++tile_y) {
|
||||
AcStrategyRow ac_strategy_row = ac_strategy.ConstRow(tile_y);
|
||||
|
@ -754,14 +763,16 @@ ImageF TileDistMap(const ImageF& distmap, int tile_size, int margin,
|
|||
return tile_distmap;
|
||||
}
|
||||
|
||||
static const float kDcQuantPow = 0.83f;
|
||||
static const float kDcQuant = 1.095924047623553f;
|
||||
static const float kAcQuant = 0.7381485255235064f;
|
||||
const float kDcQuantPow = 0.83f;
|
||||
const float kDcQuant = 1.095924047623553f;
|
||||
const float kAcQuant = 0.7381485255235064f;
|
||||
|
||||
// Computes the decoded image for a given set of compression parameters.
|
||||
ImageBundle RoundtripImage(const FrameHeader& frame_header,
|
||||
const Image3F& opsin, PassesEncoderState* enc_state,
|
||||
const JxlCmsInterface& cms, ThreadPool* pool) {
|
||||
StatusOr<ImageBundle> RoundtripImage(const FrameHeader& frame_header,
|
||||
const Image3F& opsin,
|
||||
PassesEncoderState* enc_state,
|
||||
const JxlCmsInterface& cms,
|
||||
ThreadPool* pool) {
|
||||
std::unique_ptr<PassesDecoderState> dec_state =
|
||||
jxl::make_unique<PassesDecoderState>();
|
||||
JXL_CHECK(dec_state->output_encoding_info.SetFromMetadata(
|
||||
|
@ -775,7 +786,8 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
|
|||
|
||||
size_t num_special_frames = enc_state->special_frames.size();
|
||||
size_t num_passes = enc_state->progressive_splitter.GetNumPasses();
|
||||
ModularFrameEncoder modular_frame_encoder(frame_header, enc_state->cparams);
|
||||
ModularFrameEncoder modular_frame_encoder(frame_header, enc_state->cparams,
|
||||
false);
|
||||
JXL_CHECK(InitializePassesEncoder(frame_header, opsin, Rect(opsin), cms, pool,
|
||||
enc_state, &modular_frame_encoder,
|
||||
nullptr));
|
||||
|
@ -784,7 +796,9 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
|
|||
|
||||
ImageBundle decoded(&enc_state->shared.metadata->m);
|
||||
decoded.origin = frame_header.frame_origin;
|
||||
decoded.SetFromImage(Image3F(opsin.xsize(), opsin.ysize()),
|
||||
JXL_ASSIGN_OR_RETURN(Image3F tmp,
|
||||
Image3F::Create(opsin.xsize(), opsin.ysize()));
|
||||
decoded.SetFromImage(std::move(tmp),
|
||||
dec_state->output_encoding_info.color_encoding);
|
||||
|
||||
PassesDecoderState::PipelineOptions options;
|
||||
|
@ -806,8 +820,10 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
|
|||
group_dec_caches = hwy::MakeUniqueAlignedArray<GroupDecCache>(num_threads);
|
||||
return true;
|
||||
};
|
||||
std::atomic<bool> has_error{false};
|
||||
const auto process_group = [&](const uint32_t group_index,
|
||||
const size_t thread) {
|
||||
if (has_error) return;
|
||||
if (frame_header.loop_filter.epf_iters > 0) {
|
||||
ComputeSigma(frame_header.loop_filter,
|
||||
dec_state->shared->frame_dim.BlockGroupRect(group_index),
|
||||
|
@ -822,10 +838,14 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
|
|||
std::pair<ImageF*, Rect> ri = input.GetBuffer(3 + c);
|
||||
FillPlane(0.0f, ri.first, ri.second);
|
||||
}
|
||||
input.Done();
|
||||
if (!input.Done()) {
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
JXL_CHECK(RunOnPool(pool, 0, num_groups, allocate_storage, process_group,
|
||||
"AQ loop"));
|
||||
if (has_error) return JXL_FAILURE("AQ loop failure");
|
||||
|
||||
// Ensure we don't create any new special frames.
|
||||
enc_state->special_frames.resize(num_special_frames);
|
||||
|
@ -835,18 +855,18 @@ ImageBundle RoundtripImage(const FrameHeader& frame_header,
|
|||
|
||||
constexpr int kMaxButteraugliIters = 4;
|
||||
|
||||
void FindBestQuantization(const FrameHeader& frame_header,
|
||||
const Image3F& linear, const Image3F& opsin,
|
||||
ImageF& quant_field, PassesEncoderState* enc_state,
|
||||
const JxlCmsInterface& cms, ThreadPool* pool,
|
||||
AuxOut* aux_out) {
|
||||
Status FindBestQuantization(const FrameHeader& frame_header,
|
||||
const Image3F& linear, const Image3F& opsin,
|
||||
ImageF& quant_field, PassesEncoderState* enc_state,
|
||||
const JxlCmsInterface& cms, ThreadPool* pool,
|
||||
AuxOut* aux_out) {
|
||||
const CompressParams& cparams = enc_state->cparams;
|
||||
if (cparams.resampling > 1 &&
|
||||
cparams.original_butteraugli_distance <= 4.0 * cparams.resampling) {
|
||||
// For downsampled opsin image, the butteraugli based adaptive quantization
|
||||
// loop would only make the size bigger without improving the distance much,
|
||||
// so in this case we enable it only for very high butteraugli targets.
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
Quantizer& quantizer = enc_state->shared.quantizer;
|
||||
ImageI& raw_quant_field = enc_state->shared.raw_quant_field;
|
||||
|
@ -863,10 +883,13 @@ void FindBestQuantization(const FrameHeader& frame_header,
|
|||
AdjustQuantField(enc_state->shared.ac_strategy, Rect(quant_field),
|
||||
original_butteraugli, &quant_field);
|
||||
ImageF tile_distmap;
|
||||
ImageF initial_quant_field(quant_field.xsize(), quant_field.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
ImageF initial_quant_field,
|
||||
ImageF::Create(quant_field.xsize(), quant_field.ysize()));
|
||||
CopyImageTo(quant_field, &initial_quant_field);
|
||||
|
||||
float initial_qf_min, initial_qf_max;
|
||||
float initial_qf_min;
|
||||
float initial_qf_max;
|
||||
ImageMinMax(initial_quant_field, &initial_qf_min, &initial_qf_max);
|
||||
float initial_qf_ratio = initial_qf_max / initial_qf_min;
|
||||
float qf_max_deviation_low = std::sqrt(250 / initial_qf_ratio);
|
||||
|
@ -893,8 +916,9 @@ void FindBestQuantization(const FrameHeader& frame_header,
|
|||
}
|
||||
}
|
||||
quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field);
|
||||
ImageBundle dec_linear =
|
||||
RoundtripImage(frame_header, opsin, enc_state, cms, pool);
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
ImageBundle dec_linear,
|
||||
RoundtripImage(frame_header, opsin, enc_state, cms, pool));
|
||||
float score;
|
||||
ImageF diffmap;
|
||||
JXL_CHECK(comparator.CompareWith(dec_linear, &diffmap, &score));
|
||||
|
@ -902,16 +926,19 @@ void FindBestQuantization(const FrameHeader& frame_header,
|
|||
score = -score;
|
||||
ScaleImage(-1.0f, &diffmap);
|
||||
}
|
||||
tile_distmap = TileDistMap(diffmap, 8 * cparams.resampling, 0,
|
||||
enc_state->shared.ac_strategy);
|
||||
JXL_ASSIGN_OR_RETURN(tile_distmap,
|
||||
TileDistMap(diffmap, 8 * cparams.resampling, 0,
|
||||
enc_state->shared.ac_strategy));
|
||||
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION && WantDebugOutput(cparams)) {
|
||||
DumpImage(cparams, ("dec" + ToString(i)).c_str(), *dec_linear.color());
|
||||
DumpHeatmaps(cparams, aux_out, butteraugli_target, quant_field,
|
||||
tile_distmap, diffmap);
|
||||
JXL_RETURN_IF_ERROR(DumpImage(cparams, ("dec" + ToString(i)).c_str(),
|
||||
*dec_linear.color()));
|
||||
JXL_RETURN_IF_ERROR(DumpHeatmaps(cparams, aux_out, butteraugli_target,
|
||||
quant_field, tile_distmap, diffmap));
|
||||
}
|
||||
if (aux_out != nullptr) ++aux_out->num_butteraugli_iters;
|
||||
if (JXL_DEBUG_ADAPTIVE_QUANTIZATION) {
|
||||
float minval, maxval;
|
||||
float minval;
|
||||
float maxval;
|
||||
ImageMinMax(quant_field, &minval, &maxval);
|
||||
printf("\nButteraugli iter: %d/%d\n", i, kMaxButteraugliIters);
|
||||
printf("Butteraugli distance: %f (target = %f)\n", score,
|
||||
|
@ -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<ImageF> InitialQuantField(const float butteraugli_target,
|
||||
const Image3F& opsin, const Rect& rect,
|
||||
ThreadPool* pool, float rescale,
|
||||
ImageF* mask, ImageF* mask1x1) {
|
||||
const float quant_ac = kAcQuant / butteraugli_target;
|
||||
return HWY_DYNAMIC_DISPATCH(AdaptiveQuantizationMap)(
|
||||
butteraugli_target, opsin, rect, quant_ac * rescale, pool, mask, mask1x1);
|
||||
}
|
||||
|
||||
void FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear,
|
||||
const Image3F& opsin, ImageF& quant_field,
|
||||
PassesEncoderState* enc_state,
|
||||
const JxlCmsInterface& cms, ThreadPool* pool,
|
||||
AuxOut* aux_out, double rescale) {
|
||||
Status FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear,
|
||||
const Image3F& opsin, ImageF& quant_field,
|
||||
PassesEncoderState* enc_state,
|
||||
const JxlCmsInterface& cms, ThreadPool* pool,
|
||||
AuxOut* aux_out, double rescale) {
|
||||
const CompressParams& cparams = enc_state->cparams;
|
||||
if (cparams.max_error_mode) {
|
||||
FindBestQuantizationMaxError(frame_header, opsin, quant_field, enc_state,
|
||||
cms, pool, aux_out);
|
||||
JXL_RETURN_IF_ERROR(FindBestQuantizationMaxError(
|
||||
frame_header, opsin, quant_field, enc_state, cms, pool, aux_out));
|
||||
} else if (linear && cparams.speed_tier <= SpeedTier::kKitten) {
|
||||
// Normal encoding to a butteraugli score.
|
||||
FindBestQuantization(frame_header, *linear, opsin, quant_field, enc_state,
|
||||
cms, pool, aux_out);
|
||||
JXL_RETURN_IF_ERROR(FindBestQuantization(frame_header, *linear, opsin,
|
||||
quant_field, enc_state, cms, pool,
|
||||
aux_out));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
|
|
|
@ -31,10 +31,11 @@ struct AuxOut;
|
|||
// of the input image, while a value less than 1.0 indicates that less
|
||||
// fine-grained quantization should be enough. Returns a mask, too, which
|
||||
// can later be used to make better decisions about ac strategy.
|
||||
ImageF InitialQuantField(float butteraugli_target, const Image3F& opsin,
|
||||
const Rect& rect, ThreadPool* pool, float rescale,
|
||||
ImageF* initial_quant_mask,
|
||||
ImageF* initial_quant_mask1x1);
|
||||
StatusOr<ImageF> InitialQuantField(float butteraugli_target,
|
||||
const Image3F& opsin, const Rect& rect,
|
||||
ThreadPool* pool, float rescale,
|
||||
ImageF* initial_quant_mask,
|
||||
ImageF* initial_quant_mask1x1);
|
||||
|
||||
float InitialQuantDC(float butteraugli_target);
|
||||
|
||||
|
@ -45,11 +46,11 @@ void AdjustQuantField(const AcStrategyImage& ac_strategy, const Rect& rect,
|
|||
// quant_field. Also computes the dequant_map corresponding to the given
|
||||
// dequant_float_map and chosen quantization levels.
|
||||
// `linear` is only used in Kitten mode or slower.
|
||||
void FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear,
|
||||
const Image3F& opsin, ImageF& quant_field,
|
||||
PassesEncoderState* enc_state,
|
||||
const JxlCmsInterface& cms, ThreadPool* pool,
|
||||
AuxOut* aux_out, double rescale = 1.0);
|
||||
Status FindBestQuantizer(const FrameHeader& frame_header, const Image3F* linear,
|
||||
const Image3F& opsin, ImageF& quant_field,
|
||||
PassesEncoderState* enc_state,
|
||||
const JxlCmsInterface& cms, ThreadPool* pool,
|
||||
AuxOut* aux_out, double rescale = 1.0);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
|
@ -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<Histogram>* clustered_histograms,
|
||||
EntropyEncodingData* codes, size_t* log_alpha_size) {
|
||||
codes->uint_config.resize(clustered_histograms->size());
|
||||
if (params.streaming_mode ||
|
||||
params.uint_method == HistogramParams::HybridUintMethod::kNone) {
|
||||
if (params.uint_method == HistogramParams::HybridUintMethod::kNone) {
|
||||
return;
|
||||
}
|
||||
if (params.uint_method == HistogramParams::HybridUintMethod::k000) {
|
||||
|
@ -570,6 +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<HybridUintConfig> configs;
|
||||
if (params.uint_method == HistogramParams::HybridUintMethod::kBest) {
|
||||
|
@ -1765,7 +1774,8 @@ void WriteTokens(const std::vector<Token>& tokens,
|
|||
const EntropyEncodingData& codes,
|
||||
const std::vector<uint8_t>& context_map, size_t context_offset,
|
||||
BitWriter* writer, size_t layer, AuxOut* aux_out) {
|
||||
BitWriter::Allotment allotment(writer, 32 * tokens.size() + 32 * 1024 * 4);
|
||||
// Theoretically, we could have 15 prefix code bits + 31 extra bits.
|
||||
BitWriter::Allotment allotment(writer, 46 * tokens.size() + 32 * 1024 * 4);
|
||||
size_t num_extra_bits =
|
||||
WriteTokens(tokens, codes, context_map, context_offset, writer);
|
||||
allotment.ReclaimAndCharge(writer, layer, aux_out);
|
||||
|
@ -1779,4 +1789,51 @@ void SetANSFuzzerFriendly(bool ans_fuzzer_friendly) {
|
|||
ans_fuzzer_friendly_ = ans_fuzzer_friendly;
|
||||
#endif
|
||||
}
|
||||
|
||||
HistogramParams HistogramParams::ForModular(
|
||||
const CompressParams& cparams,
|
||||
const std::vector<uint8_t>& extra_dc_precision, bool streaming_mode) {
|
||||
HistogramParams params;
|
||||
params.streaming_mode = streaming_mode;
|
||||
if (cparams.speed_tier > SpeedTier::kKitten) {
|
||||
params.clustering = HistogramParams::ClusteringType::kFast;
|
||||
params.ans_histogram_strategy =
|
||||
cparams.speed_tier > SpeedTier::kThunder
|
||||
? HistogramParams::ANSHistogramStrategy::kFast
|
||||
: HistogramParams::ANSHistogramStrategy::kApproximate;
|
||||
params.lz77_method =
|
||||
cparams.decoding_speed_tier >= 3 && cparams.modular_mode
|
||||
? (cparams.speed_tier >= SpeedTier::kFalcon
|
||||
? HistogramParams::LZ77Method::kRLE
|
||||
: HistogramParams::LZ77Method::kLZ77)
|
||||
: HistogramParams::LZ77Method::kNone;
|
||||
// Near-lossless DC, as well as modular mode, require choosing hybrid uint
|
||||
// more carefully.
|
||||
if ((!extra_dc_precision.empty() && extra_dc_precision[0] != 0) ||
|
||||
(cparams.modular_mode && cparams.speed_tier < SpeedTier::kCheetah)) {
|
||||
params.uint_method = HistogramParams::HybridUintMethod::kFast;
|
||||
} else {
|
||||
params.uint_method = HistogramParams::HybridUintMethod::kNone;
|
||||
}
|
||||
} else if (cparams.speed_tier <= SpeedTier::kTortoise) {
|
||||
params.lz77_method = HistogramParams::LZ77Method::kOptimal;
|
||||
} else {
|
||||
params.lz77_method = HistogramParams::LZ77Method::kLZ77;
|
||||
}
|
||||
if (cparams.decoding_speed_tier >= 1) {
|
||||
params.max_histograms = 12;
|
||||
}
|
||||
if (cparams.decoding_speed_tier >= 1 && cparams.responsive) {
|
||||
params.lz77_method = cparams.speed_tier >= SpeedTier::kCheetah
|
||||
? HistogramParams::LZ77Method::kRLE
|
||||
: cparams.speed_tier >= SpeedTier::kKitten
|
||||
? HistogramParams::LZ77Method::kLZ77
|
||||
: HistogramParams::LZ77Method::kOptimal;
|
||||
}
|
||||
if (cparams.decoding_speed_tier >= 2 && cparams.responsive) {
|
||||
params.uint_method = HistogramParams::HybridUintMethod::k000;
|
||||
params.force_huffman = true;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
} // namespace jxl
|
||||
|
|
|
@ -11,10 +11,15 @@
|
|||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "lib/jxl/enc_params.h"
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/common.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
// Forward declaration to break include cycle.
|
||||
struct CompressParams;
|
||||
|
||||
// RebalanceHistogram requires a signed type.
|
||||
using ANSHistBin = int32_t;
|
||||
|
||||
|
@ -65,6 +70,10 @@ struct HistogramParams {
|
|||
}
|
||||
}
|
||||
|
||||
static HistogramParams ForModular(
|
||||
const CompressParams& cparams,
|
||||
const std::vector<uint8_t>& extra_dc_precision, bool streaming_mode);
|
||||
|
||||
ClusteringType clustering = ClusteringType::kBest;
|
||||
HybridUintMethod uint_method = HybridUintMethod::kBest;
|
||||
LZ77Method lz77_method = LZ77Method::kRLE;
|
||||
|
|
|
@ -17,16 +17,10 @@
|
|||
|
||||
#include "lib/jxl/ac_strategy.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/chroma_from_luma.h"
|
||||
#include "lib/jxl/enc_adaptive_quantization.h"
|
||||
#include "lib/jxl/enc_params.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/image_bundle.h"
|
||||
#include "lib/jxl/image_ops.h"
|
||||
#include "lib/jxl/quant_weights.h"
|
||||
#include "lib/jxl/quantizer.h"
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace jxl {
|
||||
|
@ -40,11 +34,12 @@ using hwy::HWY_NAMESPACE::Mul;
|
|||
using hwy::HWY_NAMESPACE::MulAdd;
|
||||
using hwy::HWY_NAMESPACE::Sqrt;
|
||||
|
||||
void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header,
|
||||
const Image3F& opsin, const Rect& opsin_rect,
|
||||
const ImageF& quant_field, const AcStrategyImage& ac_strategy,
|
||||
ImageB* epf_sharpness, const Rect& rect,
|
||||
ArControlFieldHeuristics::TempImages* temp_image) {
|
||||
Status ProcessTile(const CompressParams& cparams,
|
||||
const FrameHeader& frame_header, const Image3F& opsin,
|
||||
const Rect& opsin_rect, const ImageF& quant_field,
|
||||
const AcStrategyImage& ac_strategy, ImageB* epf_sharpness,
|
||||
const Rect& rect,
|
||||
ArControlFieldHeuristics::TempImages* temp_image) {
|
||||
JXL_ASSERT(opsin_rect.x0() % 8 == 0);
|
||||
JXL_ASSERT(opsin_rect.y0() % 8 == 0);
|
||||
JXL_ASSERT(opsin_rect.xsize() % 8 == 0);
|
||||
|
@ -54,7 +49,7 @@ void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header,
|
|||
cparams.speed_tier > SpeedTier::kWombat ||
|
||||
frame_header.loop_filter.epf_iters == 0) {
|
||||
FillPlane(static_cast<uint8_t>(4), epf_sharpness, rect);
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Likely better to have a higher X weight, like:
|
||||
|
@ -70,7 +65,7 @@ void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header,
|
|||
size_t by1 = by0 + rect.ysize();
|
||||
size_t bx0 = opsin_rect.x0() / 8 + rect.x0();
|
||||
size_t bx1 = bx0 + rect.xsize();
|
||||
temp_image->InitOnce();
|
||||
JXL_RETURN_IF_ERROR(temp_image->InitOnce());
|
||||
ImageF& laplacian_sqrsum = temp_image->laplacian_sqrsum;
|
||||
// Calculate the L2 of the 3x3 Laplacian in an integral transform
|
||||
// (for example 32x32 dct). This relates to transforms ability
|
||||
|
@ -295,6 +290,7 @@ void ProcessTile(const CompressParams& cparams, const FrameHeader& frame_header,
|
|||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -307,14 +303,14 @@ HWY_AFTER_NAMESPACE();
|
|||
namespace jxl {
|
||||
HWY_EXPORT(ProcessTile);
|
||||
|
||||
void ArControlFieldHeuristics::RunRect(
|
||||
Status ArControlFieldHeuristics::RunRect(
|
||||
const CompressParams& cparams, const FrameHeader& frame_header,
|
||||
const Rect& block_rect, const Image3F& opsin, const Rect& opsin_rect,
|
||||
const ImageF& quant_field, const AcStrategyImage& ac_strategy,
|
||||
ImageB* epf_sharpness, size_t thread) {
|
||||
HWY_DYNAMIC_DISPATCH(ProcessTile)
|
||||
(cparams, frame_header, opsin, opsin_rect, quant_field, ac_strategy,
|
||||
epf_sharpness, block_rect, &temp_images[thread]);
|
||||
return HWY_DYNAMIC_DISPATCH(ProcessTile)(
|
||||
cparams, frame_header, opsin, opsin_rect, quant_field, ac_strategy,
|
||||
epf_sharpness, block_rect, &temp_images[thread]);
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
|
|
|
@ -21,11 +21,15 @@ struct PassesEncoderState;
|
|||
|
||||
struct ArControlFieldHeuristics {
|
||||
struct TempImages {
|
||||
void InitOnce() {
|
||||
if (laplacian_sqrsum.xsize() != 0) return;
|
||||
laplacian_sqrsum = ImageF(kEncTileDim + 4, kEncTileDim + 4);
|
||||
sqrsum_00 = ImageF(kEncTileDim / 4, kEncTileDim / 4);
|
||||
sqrsum_22 = ImageF(kEncTileDim / 4 + 1, kEncTileDim / 4 + 1);
|
||||
Status InitOnce() {
|
||||
if (laplacian_sqrsum.xsize() != 0) return true;
|
||||
JXL_ASSIGN_OR_RETURN(laplacian_sqrsum,
|
||||
ImageF::Create(kEncTileDim + 4, kEncTileDim + 4));
|
||||
JXL_ASSIGN_OR_RETURN(sqrsum_00,
|
||||
ImageF::Create(kEncTileDim / 4, kEncTileDim / 4));
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
sqrsum_22, ImageF::Create(kEncTileDim / 4 + 1, kEncTileDim / 4 + 1));
|
||||
return true;
|
||||
}
|
||||
|
||||
ImageF laplacian_sqrsum;
|
||||
|
@ -37,11 +41,11 @@ struct ArControlFieldHeuristics {
|
|||
temp_images.resize(num_threads);
|
||||
}
|
||||
|
||||
void RunRect(const CompressParams& cparams, const FrameHeader& frame_header,
|
||||
const Rect& block_rect, const Image3F& opsin,
|
||||
const Rect& opsin_rect, const ImageF& quant_field,
|
||||
const AcStrategyImage& ac_strategy, ImageB* epf_sharpness,
|
||||
size_t thread);
|
||||
Status RunRect(const CompressParams& cparams, const FrameHeader& frame_header,
|
||||
const Rect& block_rect, const Image3F& opsin,
|
||||
const Rect& opsin_rect, const ImageF& quant_field,
|
||||
const AcStrategyImage& ac_strategy, ImageB* epf_sharpness,
|
||||
size_t thread);
|
||||
|
||||
std::vector<TempImages> temp_images;
|
||||
};
|
||||
|
|
|
@ -5,9 +5,7 @@
|
|||
|
||||
#include "lib/jxl/enc_butteraugli_comparator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/enc_image_bundle.h"
|
||||
|
||||
namespace jxl {
|
||||
|
@ -24,9 +22,8 @@ Status JxlButteraugliComparator::SetReferenceImage(const ImageBundle& ref) {
|
|||
/*pool=*/nullptr, &store, &ref_linear_srgb)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
comparator_.reset(
|
||||
new ButteraugliComparator(ref_linear_srgb->color(), params_));
|
||||
JXL_ASSIGN_OR_RETURN(comparator_, ButteraugliComparator::Make(
|
||||
ref_linear_srgb->color(), params_));
|
||||
xsize_ = ref.xsize();
|
||||
ysize_ = ref.ysize();
|
||||
return true;
|
||||
|
@ -34,7 +31,8 @@ Status JxlButteraugliComparator::SetReferenceImage(const ImageBundle& ref) {
|
|||
|
||||
Status JxlButteraugliComparator::SetLinearReferenceImage(
|
||||
const Image3F& linear) {
|
||||
comparator_.reset(new ButteraugliComparator(linear, params_));
|
||||
JXL_ASSIGN_OR_RETURN(comparator_,
|
||||
ButteraugliComparator::Make(linear, params_));
|
||||
xsize_ = linear.xsize();
|
||||
ysize_ = linear.ysize();
|
||||
return true;
|
||||
|
@ -58,8 +56,9 @@ Status JxlButteraugliComparator::CompareWith(const ImageBundle& actual,
|
|||
return false;
|
||||
}
|
||||
|
||||
ImageF temp_diffmap(xsize_, ysize_);
|
||||
comparator_->Diffmap(actual_linear_srgb->color(), temp_diffmap);
|
||||
JXL_ASSIGN_OR_RETURN(ImageF temp_diffmap, ImageF::Create(xsize_, ysize_));
|
||||
JXL_RETURN_IF_ERROR(
|
||||
comparator_->Diffmap(actual_linear_srgb->color(), temp_diffmap));
|
||||
|
||||
if (score != nullptr) {
|
||||
*score = ButteraugliScoreFromDiffmap(temp_diffmap, ¶ms_);
|
||||
|
@ -79,29 +78,4 @@ float JxlButteraugliComparator::BadQualityScore() const {
|
|||
return ButteraugliFuzzyInverse(0.5);
|
||||
}
|
||||
|
||||
float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1,
|
||||
const ButteraugliParams& params,
|
||||
const JxlCmsInterface& cms, ImageF* distmap,
|
||||
ThreadPool* pool, bool ignore_alpha) {
|
||||
JxlButteraugliComparator comparator(params, cms);
|
||||
return ComputeScore(rgb0, rgb1, &comparator, cms, distmap, pool,
|
||||
ignore_alpha);
|
||||
}
|
||||
|
||||
float ButteraugliDistance(const std::vector<ImageBundle>& frames0,
|
||||
const std::vector<ImageBundle>& frames1,
|
||||
const ButteraugliParams& params,
|
||||
const JxlCmsInterface& cms, ImageF* distmap,
|
||||
ThreadPool* pool) {
|
||||
JxlButteraugliComparator comparator(params, cms);
|
||||
JXL_ASSERT(frames0.size() == frames1.size());
|
||||
float max_dist = 0.0f;
|
||||
for (size_t i = 0; i < frames0.size(); ++i) {
|
||||
max_dist = std::max(
|
||||
max_dist,
|
||||
ComputeScore(frames0[i], frames1[i], &comparator, cms, distmap, pool));
|
||||
}
|
||||
return max_dist;
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
|
|
|
@ -10,9 +10,7 @@
|
|||
#include <stddef.h>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/butteraugli/butteraugli.h"
|
||||
#include "lib/jxl/enc_comparator.h"
|
||||
|
@ -43,20 +41,6 @@ class JxlButteraugliComparator : public Comparator {
|
|||
size_t ysize_ = 0;
|
||||
};
|
||||
|
||||
// Returns the butteraugli distance between rgb0 and rgb1.
|
||||
// If distmap is not null, it must be the same size as rgb0 and rgb1.
|
||||
float ButteraugliDistance(const ImageBundle& rgb0, const ImageBundle& rgb1,
|
||||
const ButteraugliParams& params,
|
||||
const JxlCmsInterface& cms, ImageF* distmap = nullptr,
|
||||
ThreadPool* pool = nullptr,
|
||||
bool ignore_alpha = false);
|
||||
|
||||
float ButteraugliDistance(const std::vector<ImageBundle>& frames0,
|
||||
const std::vector<ImageBundle>& frames1,
|
||||
const ButteraugliParams& params,
|
||||
const JxlCmsInterface& cms, ImageF* distmap = nullptr,
|
||||
ThreadPool* pool = nullptr);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_JXL_ENC_BUTTERAUGLI_COMPARATOR_H_
|
||||
|
|
|
@ -8,15 +8,14 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <type_traits>
|
||||
#include <memory>
|
||||
|
||||
#include "lib/jxl/ac_strategy.h"
|
||||
#include "lib/jxl/base/common.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
#include "lib/jxl/compressed_dc.h"
|
||||
#include "lib/jxl/dct_scales.h"
|
||||
#include "lib/jxl/dct_util.h"
|
||||
#include "lib/jxl/dec_frame.h"
|
||||
#include "lib/jxl/enc_aux_out.h"
|
||||
|
@ -50,8 +49,11 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
|
|||
for (size_t i = enc_state->coeffs.size();
|
||||
i < frame_header.passes.num_passes; i++) {
|
||||
// Allocate enough coefficients for each group on every row.
|
||||
enc_state->coeffs.emplace_back(make_unique<ACImageT<int32_t>>(
|
||||
kGroupDim * kGroupDim, shared.frame_dim.num_groups));
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
std::unique_ptr<ACImageT<int32_t>> coeffs,
|
||||
ACImageT<int32_t>::Make(kGroupDim * kGroupDim,
|
||||
shared.frame_dim.num_groups));
|
||||
enc_state->coeffs.emplace_back(std::move(coeffs));
|
||||
}
|
||||
}
|
||||
while (enc_state->coeffs.size() > frame_header.passes.num_passes) {
|
||||
|
@ -65,7 +67,9 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
|
|||
shared.quantizer.RecomputeFromGlobalScale();
|
||||
}
|
||||
|
||||
Image3F dc(shared.frame_dim.xsize_blocks, shared.frame_dim.ysize_blocks);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F dc,
|
||||
Image3F::Create(shared.frame_dim.xsize_blocks,
|
||||
shared.frame_dim.ysize_blocks));
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, shared.frame_dim.num_groups, ThreadPool::NoInit,
|
||||
[&](size_t group_idx, size_t _) {
|
||||
|
@ -120,7 +124,8 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
|
|||
std::vector<ImageF> extra_channels;
|
||||
extra_channels.reserve(ib.metadata()->extra_channel_info.size());
|
||||
for (size_t i = 0; i < ib.metadata()->extra_channel_info.size(); i++) {
|
||||
extra_channels.emplace_back(ib.xsize(), ib.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(ImageF ch, ImageF::Create(ib.xsize(), ib.ysize()));
|
||||
extra_channels.emplace_back(std::move(ch));
|
||||
// Must initialize the image with data to not affect blending with
|
||||
// uninitialized memory.
|
||||
// TODO(lode): dc_level must copy and use the real extra channels
|
||||
|
@ -168,32 +173,41 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
|
|||
// outputs multiple frames, this assumption could be wrong.
|
||||
const Image3F& dc_frame =
|
||||
dec_state->shared->dc_frames[frame_header.dc_level];
|
||||
shared.dc_storage = Image3F(dc_frame.xsize(), dc_frame.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(shared.dc_storage,
|
||||
Image3F::Create(dc_frame.xsize(), dc_frame.ysize()));
|
||||
CopyImageTo(dc_frame, &shared.dc_storage);
|
||||
ZeroFillImage(&shared.quant_dc);
|
||||
shared.dc = &shared.dc_storage;
|
||||
JXL_CHECK(encoded_size == 0);
|
||||
} else {
|
||||
std::atomic<bool> has_error{false};
|
||||
auto compute_dc_coeffs = [&](int group_index, int /* thread */) {
|
||||
if (has_error) return;
|
||||
const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index);
|
||||
int modular_group_index = group_index;
|
||||
if (enc_state->streaming_mode) {
|
||||
JXL_ASSERT(group_index == 0);
|
||||
modular_group_index = enc_state->dc_group_index;
|
||||
}
|
||||
modular_frame_encoder->AddVarDCTDC(
|
||||
frame_header, dc, r, modular_group_index,
|
||||
enc_state->cparams.speed_tier < SpeedTier::kFalcon, enc_state,
|
||||
/*jpeg_transcode=*/false);
|
||||
if (!modular_frame_encoder->AddVarDCTDC(
|
||||
frame_header, dc, r, modular_group_index,
|
||||
enc_state->cparams.speed_tier < SpeedTier::kFalcon, enc_state,
|
||||
/*jpeg_transcode=*/false)) {
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups,
|
||||
ThreadPool::NoInit, compute_dc_coeffs,
|
||||
"Compute DC coeffs"));
|
||||
if (has_error) return JXL_FAILURE("Compute DC coeffs failed");
|
||||
// TODO(veluca): this is only useful in tests and if inspection is enabled.
|
||||
if (!(frame_header.flags & FrameHeader::kSkipAdaptiveDCSmoothing)) {
|
||||
AdaptiveDCSmoothing(shared.quantizer.MulDC(), &shared.dc_storage, pool);
|
||||
JXL_RETURN_IF_ERROR(AdaptiveDCSmoothing(shared.quantizer.MulDC(),
|
||||
&shared.dc_storage, pool));
|
||||
}
|
||||
}
|
||||
std::atomic<bool> has_error{false};
|
||||
auto compute_ac_meta = [&](int group_index, int /* thread */) {
|
||||
const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index);
|
||||
int modular_group_index = group_index;
|
||||
|
@ -201,13 +215,17 @@ Status InitializePassesEncoder(const FrameHeader& frame_header,
|
|||
JXL_ASSERT(group_index == 0);
|
||||
modular_group_index = enc_state->dc_group_index;
|
||||
}
|
||||
modular_frame_encoder->AddACMetadata(r, modular_group_index,
|
||||
/*jpeg_transcode=*/false, enc_state);
|
||||
if (!modular_frame_encoder->AddACMetadata(r, modular_group_index,
|
||||
/*jpeg_transcode=*/false,
|
||||
enc_state)) {
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups,
|
||||
ThreadPool::NoInit, compute_ac_meta,
|
||||
"Compute AC Metadata"));
|
||||
|
||||
if (has_error) return JXL_FAILURE("Compute AC Metadata failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
|
||||
#undef HWY_TARGET_INCLUDE
|
||||
|
@ -18,18 +17,13 @@
|
|||
#include <hwy/foreach_target.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/base/bits.h"
|
||||
#include "lib/jxl/base/common.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/cms/opsin_params.h"
|
||||
#include "lib/jxl/dec_transforms-inl.h"
|
||||
#include "lib/jxl/enc_aux_out.h"
|
||||
#include "lib/jxl/enc_params.h"
|
||||
#include "lib/jxl/enc_transforms-inl.h"
|
||||
#include "lib/jxl/entropy_coder.h"
|
||||
#include "lib/jxl/image_ops.h"
|
||||
#include "lib/jxl/modular/encoding/encoding.h"
|
||||
#include "lib/jxl/quantizer.h"
|
||||
#include "lib/jxl/simd_util.h"
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
|
@ -149,7 +143,8 @@ int32_t FindBestMultiplier(const float* values_m, const float* values_s,
|
|||
// Derivatives are approximate due to the high amount of noise in the exact
|
||||
// derivatives.
|
||||
for (size_t i = 0; i < 20; i++) {
|
||||
float dfpeps, dfmeps;
|
||||
float dfpeps;
|
||||
float dfmeps;
|
||||
float df = fn.Compute(x, eps, &dfpeps, &dfmeps);
|
||||
float ddf = (dfpeps - dfmeps) / (2 * eps);
|
||||
float kExperimentalInsignificantStabilizer = 0.85;
|
||||
|
@ -175,12 +170,13 @@ int32_t FindBestMultiplier(const float* values_m, const float* values_s,
|
|||
return std::max(-128.0f, std::min(127.0f, roundf(x)));
|
||||
}
|
||||
|
||||
void InitDCStorage(size_t num_blocks, ImageF* dc_values) {
|
||||
Status InitDCStorage(size_t num_blocks, ImageF* dc_values) {
|
||||
// First row: Y channel
|
||||
// Second row: X channel
|
||||
// Third row: Y channel
|
||||
// Fourth row: B channel
|
||||
*dc_values = ImageF(RoundUpTo(num_blocks, Lanes(df)), 4);
|
||||
JXL_ASSIGN_OR_RETURN(*dc_values,
|
||||
ImageF::Create(RoundUpTo(num_blocks, Lanes(df)), 4));
|
||||
|
||||
JXL_ASSERT(dc_values->xsize() != 0);
|
||||
// Zero-fill the last lanes
|
||||
|
@ -190,6 +186,7 @@ void InitDCStorage(size_t num_blocks, ImageF* dc_values) {
|
|||
dc_values->Row(y)[x] = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ComputeTile(const Image3F& opsin, const Rect& opsin_rect,
|
||||
|
@ -352,11 +349,11 @@ namespace jxl {
|
|||
HWY_EXPORT(InitDCStorage);
|
||||
HWY_EXPORT(ComputeTile);
|
||||
|
||||
void CfLHeuristics::Init(const Rect& rect) {
|
||||
Status CfLHeuristics::Init(const Rect& rect) {
|
||||
size_t xsize_blocks = rect.xsize() / kBlockDim;
|
||||
size_t ysize_blocks = rect.ysize() / kBlockDim;
|
||||
HWY_DYNAMIC_DISPATCH(InitDCStorage)
|
||||
(xsize_blocks * ysize_blocks, &dc_values);
|
||||
return HWY_DYNAMIC_DISPATCH(InitDCStorage)(xsize_blocks * ysize_blocks,
|
||||
&dc_values);
|
||||
}
|
||||
|
||||
void CfLHeuristics::ComputeTile(const Rect& r, const Image3F& opsin,
|
||||
|
|
|
@ -29,7 +29,7 @@ void ColorCorrelationMapEncodeDC(const ColorCorrelationMap& map,
|
|||
AuxOut* aux_out);
|
||||
|
||||
struct CfLHeuristics {
|
||||
void Init(const Rect& rect);
|
||||
Status Init(const Rect& rect);
|
||||
|
||||
void PrepareForThreads(size_t num_threads) {
|
||||
mem = hwy::AllocateAligned<float>(num_threads * ItemsPerThread());
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "lib/jxl/ans_params.h"
|
||||
#include "lib/jxl/base/common.h"
|
||||
#include "lib/jxl/enc_ans_params.h"
|
||||
|
||||
namespace jxl {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -18,32 +18,29 @@ namespace jxl {
|
|||
|
||||
namespace {
|
||||
template <typename From>
|
||||
Plane<float> ConvertToFloat(const Plane<From>& from) {
|
||||
StatusOr<Image3F> ConvertToFloat(const Image3<From>& from) {
|
||||
float factor = 1.0f / std::numeric_limits<From>::max();
|
||||
if (std::is_same<From, double>::value || std::is_same<From, float>::value) {
|
||||
factor = 1.0f;
|
||||
}
|
||||
Plane<float> to(from.xsize(), from.ysize());
|
||||
for (size_t y = 0; y < from.ysize(); ++y) {
|
||||
const From* const JXL_RESTRICT row_from = from.Row(y);
|
||||
float* const JXL_RESTRICT row_to = to.Row(y);
|
||||
for (size_t x = 0; x < from.xsize(); ++x) {
|
||||
row_to[x] = row_from[x] * factor;
|
||||
JXL_ASSIGN_OR_RETURN(Image3F to, Image3F::Create(from.xsize(), from.ysize()));
|
||||
for (size_t c = 0; c < 3; ++c) {
|
||||
for (size_t y = 0; y < from.ysize(); ++y) {
|
||||
const From* const JXL_RESTRICT row_from = from.ConstPlaneRow(c, y);
|
||||
float* const JXL_RESTRICT row_to = to.PlaneRow(c, y);
|
||||
for (size_t x = 0; x < from.xsize(); ++x) {
|
||||
row_to[x] = row_from[x] * factor;
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
}
|
||||
template <typename From>
|
||||
Image3F ConvertToFloat(const Image3<From>& from) {
|
||||
return Image3F(ConvertToFloat(from.Plane(0)), ConvertToFloat(from.Plane(1)),
|
||||
ConvertToFloat(from.Plane(2)));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DumpImageT(const CompressParams& cparams, const char* label,
|
||||
const ColorEncoding& color_encoding, const Image3<T>& image) {
|
||||
if (!cparams.debug_image) return;
|
||||
Image3F float_image = ConvertToFloat(image);
|
||||
Status DumpImageT(const CompressParams& cparams, const char* label,
|
||||
const ColorEncoding& color_encoding, const Image3<T>& image) {
|
||||
if (!cparams.debug_image) return true;
|
||||
JXL_ASSIGN_OR_RETURN(Image3F float_image, ConvertToFloat(image));
|
||||
JxlColorEncoding color = color_encoding.ToExternal();
|
||||
size_t num_pixels = 3 * image.xsize() * image.ysize();
|
||||
std::vector<uint16_t> pixels(num_pixels);
|
||||
|
@ -53,18 +50,20 @@ void DumpImageT(const CompressParams& cparams, const char* label,
|
|||
}
|
||||
JXL_CHECK(ConvertChannelsToExternal(
|
||||
channels, 3, 16, false, JXL_BIG_ENDIAN, 6 * image.xsize(), nullptr,
|
||||
&pixels[0], 2 * num_pixels, PixelCallback(), Orientation::kIdentity));
|
||||
pixels.data(), 2 * num_pixels, PixelCallback(), Orientation::kIdentity));
|
||||
(*cparams.debug_image)(cparams.debug_image_opaque, label, image.xsize(),
|
||||
image.ysize(), &color, &pixels[0]);
|
||||
image.ysize(), &color, pixels.data());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DumpPlaneNormalizedT(const CompressParams& cparams, const char* label,
|
||||
const Plane<T>& image) {
|
||||
Status DumpPlaneNormalizedT(const CompressParams& cparams, const char* label,
|
||||
const Plane<T>& image) {
|
||||
T min;
|
||||
T max;
|
||||
ImageMinMax(image, &min, &max);
|
||||
Image3B normalized(image.xsize(), image.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(Image3B normalized,
|
||||
Image3B::Create(image.xsize(), image.ysize()));
|
||||
for (size_t c = 0; c < 3; ++c) {
|
||||
float mul = min == max ? 0 : (255.0f / (max - min));
|
||||
for (size_t y = 0; y < image.ysize(); ++y) {
|
||||
|
@ -75,41 +74,42 @@ void DumpPlaneNormalizedT(const CompressParams& cparams, const char* label,
|
|||
}
|
||||
}
|
||||
}
|
||||
DumpImageT(cparams, label, ColorEncoding::SRGB(), normalized);
|
||||
return DumpImageT(cparams, label, ColorEncoding::SRGB(), normalized);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void DumpImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<float>& image) {
|
||||
DumpImageT(cparams, label, ColorEncoding::SRGB(), image);
|
||||
Status DumpImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<float>& image) {
|
||||
return DumpImageT(cparams, label, ColorEncoding::SRGB(), image);
|
||||
}
|
||||
|
||||
void DumpImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<uint8_t>& image) {
|
||||
DumpImageT(cparams, label, ColorEncoding::SRGB(), image);
|
||||
Status DumpImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<uint8_t>& image) {
|
||||
return DumpImageT(cparams, label, ColorEncoding::SRGB(), image);
|
||||
}
|
||||
|
||||
void DumpXybImage(const CompressParams& cparams, const char* label,
|
||||
const Image3F& image) {
|
||||
if (!cparams.debug_image) return;
|
||||
Status DumpXybImage(const CompressParams& cparams, const char* label,
|
||||
const Image3F& image) {
|
||||
if (!cparams.debug_image) return true;
|
||||
|
||||
Image3F linear(image.xsize(), image.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(Image3F linear,
|
||||
Image3F::Create(image.xsize(), image.ysize()));
|
||||
OpsinParams opsin_params;
|
||||
opsin_params.Init(kDefaultIntensityTarget);
|
||||
OpsinToLinear(image, Rect(linear), nullptr, &linear, opsin_params);
|
||||
|
||||
DumpImageT(cparams, label, ColorEncoding::LinearSRGB(), linear);
|
||||
return DumpImageT(cparams, label, ColorEncoding::LinearSRGB(), linear);
|
||||
}
|
||||
|
||||
void DumpPlaneNormalized(const CompressParams& cparams, const char* label,
|
||||
const Plane<float>& image) {
|
||||
DumpPlaneNormalizedT(cparams, label, image);
|
||||
Status DumpPlaneNormalized(const CompressParams& cparams, const char* label,
|
||||
const Plane<float>& image) {
|
||||
return DumpPlaneNormalizedT(cparams, label, image);
|
||||
}
|
||||
|
||||
void DumpPlaneNormalized(const CompressParams& cparams, const char* label,
|
||||
const Plane<uint8_t>& image) {
|
||||
DumpPlaneNormalizedT(cparams, label, image);
|
||||
Status DumpPlaneNormalized(const CompressParams& cparams, const char* label,
|
||||
const Plane<uint8_t>& image) {
|
||||
return DumpPlaneNormalizedT(cparams, label, image);
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
|
|
|
@ -16,16 +16,16 @@
|
|||
|
||||
namespace jxl {
|
||||
|
||||
void DumpImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<float>& image);
|
||||
void DumpImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<uint8_t>& image);
|
||||
void DumpXybImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<float>& image);
|
||||
void DumpPlaneNormalized(const CompressParams& cparams, const char* label,
|
||||
const Plane<float>& image);
|
||||
void DumpPlaneNormalized(const CompressParams& cparams, const char* label,
|
||||
const Plane<uint8_t>& image);
|
||||
Status DumpImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<float>& image);
|
||||
Status DumpImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<uint8_t>& image);
|
||||
Status DumpXybImage(const CompressParams& cparams, const char* label,
|
||||
const Image3<float>& image);
|
||||
Status DumpPlaneNormalized(const CompressParams& cparams, const char* label,
|
||||
const Plane<float>& image);
|
||||
Status DumpPlaneNormalized(const CompressParams& cparams, const char* label,
|
||||
const Plane<uint8_t>& image);
|
||||
|
||||
// Used to skip image creation if they won't be written to debug directory.
|
||||
static inline bool WantDebugOutput(const CompressParams& cparams) {
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/convolve.h"
|
||||
#include "lib/jxl/enc_linalg.h"
|
||||
#include "lib/jxl/enc_optimize.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/image_ops.h"
|
||||
|
||||
|
@ -44,14 +43,16 @@ using hwy::HWY_NAMESPACE::Add;
|
|||
using hwy::HWY_NAMESPACE::Mul;
|
||||
using hwy::HWY_NAMESPACE::Sub;
|
||||
|
||||
ImageF SumOfSquareDifferences(const Image3F& forig, const Image3F& smooth,
|
||||
ThreadPool* pool) {
|
||||
StatusOr<ImageF> SumOfSquareDifferences(const Image3F& forig,
|
||||
const Image3F& smooth,
|
||||
ThreadPool* pool) {
|
||||
const HWY_FULL(float) d;
|
||||
const auto color_coef0 = Set(d, 0.0f);
|
||||
const auto color_coef1 = Set(d, 10.0f);
|
||||
const auto color_coef2 = Set(d, 0.0f);
|
||||
|
||||
ImageF sum_of_squares(forig.xsize(), forig.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(ImageF sum_of_squares,
|
||||
ImageF::Create(forig.xsize(), forig.ysize()));
|
||||
JXL_CHECK(RunOnPool(
|
||||
pool, 0, forig.ysize(), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t thread) {
|
||||
|
@ -142,11 +143,12 @@ const WeightsSeparable5& WeightsSeparable5Gaussian3() {
|
|||
return weights;
|
||||
}
|
||||
|
||||
ImageF ComputeEnergyImage(const Image3F& orig, Image3F* smooth,
|
||||
ThreadPool* pool) {
|
||||
StatusOr<ImageF> ComputeEnergyImage(const Image3F& orig, Image3F* smooth,
|
||||
ThreadPool* pool) {
|
||||
// Prepare guidance images for dot selection.
|
||||
Image3F forig(orig.xsize(), orig.ysize());
|
||||
*smooth = Image3F(orig.xsize(), orig.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(Image3F forig,
|
||||
Image3F::Create(orig.xsize(), orig.ysize()));
|
||||
JXL_ASSIGN_OR_RETURN(*smooth, Image3F::Create(orig.xsize(), orig.ysize()));
|
||||
Rect rect(orig);
|
||||
|
||||
const auto& weights1 = WeightsSeparable5Gaussian0_65();
|
||||
|
@ -266,7 +268,10 @@ struct ConnectedComponent {
|
|||
|
||||
Rect BoundingRectangle(const std::vector<Pixel>& pixels) {
|
||||
JXL_ASSERT(!pixels.empty());
|
||||
int low_x, high_x, low_y, high_y;
|
||||
int low_x;
|
||||
int high_x;
|
||||
int low_y;
|
||||
int high_y;
|
||||
low_x = high_x = pixels[0].x;
|
||||
low_y = high_y = pixels[0].y;
|
||||
for (const Pixel& p : pixels) {
|
||||
|
@ -278,11 +283,13 @@ Rect BoundingRectangle(const std::vector<Pixel>& pixels) {
|
|||
return Rect(low_x, low_y, high_x - low_x + 1, high_y - low_y + 1);
|
||||
}
|
||||
|
||||
std::vector<ConnectedComponent> FindCC(const ImageF& energy, double t_low,
|
||||
double t_high, uint32_t maxWindow,
|
||||
double minScore) {
|
||||
StatusOr<std::vector<ConnectedComponent>> FindCC(const ImageF& energy,
|
||||
double t_low, double t_high,
|
||||
uint32_t maxWindow,
|
||||
double minScore) {
|
||||
const int kExtraRect = 4;
|
||||
ImageF img(energy.xsize(), energy.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(ImageF img,
|
||||
ImageF::Create(energy.xsize(), energy.ysize()));
|
||||
CopyImageTo(energy, &img);
|
||||
std::vector<ConnectedComponent> ans;
|
||||
for (size_t y = 0; y < img.ysize(); y++) {
|
||||
|
@ -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<double, 3> 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<PatchInfo> DetectGaussianEllipses(
|
||||
StatusOr<std::vector<PatchInfo>> DetectGaussianEllipses(
|
||||
const Image3F& opsin, const GaussianDetectParams& params,
|
||||
const EllipseQuantParams& qParams, ThreadPool* pool) {
|
||||
std::vector<PatchInfo> dots;
|
||||
Image3F smooth(opsin.xsize(), opsin.ysize());
|
||||
ImageF energy = ComputeEnergyImage(opsin, &smooth, pool);
|
||||
std::vector<ConnectedComponent> components = FindCC(
|
||||
energy, params.t_low, params.t_high, params.maxWinSize, params.minScore);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F smooth,
|
||||
Image3F::Create(opsin.xsize(), opsin.ysize()));
|
||||
JXL_ASSIGN_OR_RETURN(ImageF energy, ComputeEnergyImage(opsin, &smooth, pool));
|
||||
JXL_ASSIGN_OR_RETURN(std::vector<ConnectedComponent> components,
|
||||
FindCC(energy, 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) {
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
#include <vector>
|
||||
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/dec_patch_dictionary.h"
|
||||
#include "lib/jxl/enc_patch_dictionary.h"
|
||||
#include "lib/jxl/image.h"
|
||||
|
||||
|
@ -58,7 +57,7 @@ struct EllipseQuantParams {
|
|||
};
|
||||
|
||||
// Detects dots in XYB image.
|
||||
std::vector<PatchInfo> DetectGaussianEllipses(
|
||||
StatusOr<std::vector<PatchInfo>> DetectGaussianEllipses(
|
||||
const Image3F& opsin, const GaussianDetectParams& params,
|
||||
const EllipseQuantParams& qParams, ThreadPool* pool);
|
||||
|
||||
|
|
|
@ -9,17 +9,12 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
#include "lib/jxl/base/override.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/chroma_from_luma.h"
|
||||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/dec_xyb.h"
|
||||
#include "lib/jxl/enc_bit_writer.h"
|
||||
#include "lib/jxl/enc_detect_dots.h"
|
||||
#include "lib/jxl/enc_params.h"
|
||||
#include "lib/jxl/enc_xyb.h"
|
||||
#include "lib/jxl/image.h"
|
||||
|
||||
namespace jxl {
|
||||
|
@ -39,10 +34,9 @@ const std::array<double, 3> kEllipseMaxIntensity{{0.05, 1.0, 0.4}};
|
|||
const std::array<size_t, 3> kEllipseIntensityQ{{10, 36, 10}};
|
||||
} // namespace
|
||||
|
||||
std::vector<PatchInfo> FindDotDictionary(const CompressParams& cparams,
|
||||
const Image3F& opsin,
|
||||
const ColorCorrelationMap& cmap,
|
||||
ThreadPool* pool) {
|
||||
StatusOr<std::vector<PatchInfo>> FindDotDictionary(
|
||||
const CompressParams& cparams, const Image3F& opsin,
|
||||
const ColorCorrelationMap& cmap, ThreadPool* pool) {
|
||||
if (ApplyOverride(cparams.dots,
|
||||
cparams.butteraugli_distance >= kMinButteraugliForDots)) {
|
||||
GaussianDetectParams ellipse_params;
|
||||
|
@ -66,6 +60,7 @@ std::vector<PatchInfo> FindDotDictionary(const CompressParams& cparams,
|
|||
|
||||
return DetectGaussianEllipses(opsin, ellipse_params, qParams, pool);
|
||||
}
|
||||
return {};
|
||||
std::vector<PatchInfo> nothing;
|
||||
return nothing;
|
||||
}
|
||||
} // namespace jxl
|
||||
|
|
|
@ -15,19 +15,15 @@
|
|||
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/chroma_from_luma.h"
|
||||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/dec_patch_dictionary.h"
|
||||
#include "lib/jxl/enc_bit_writer.h"
|
||||
#include "lib/jxl/enc_params.h"
|
||||
#include "lib/jxl/enc_patch_dictionary.h"
|
||||
#include "lib/jxl/image.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
std::vector<PatchInfo> FindDotDictionary(const CompressParams& cparams,
|
||||
const Image3F& opsin,
|
||||
const ColorCorrelationMap& cmap,
|
||||
ThreadPool* pool);
|
||||
StatusOr<std::vector<PatchInfo>> FindDotDictionary(
|
||||
const CompressParams& cparams, const Image3F& opsin,
|
||||
const ColorCorrelationMap& cmap, ThreadPool* pool);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
|
|
|
@ -8,14 +8,9 @@
|
|||
#include <jxl/types.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/alpha.h"
|
||||
#include "lib/jxl/base/byte_order.h"
|
||||
#include "lib/jxl/base/common.h"
|
||||
#include "lib/jxl/base/float.h"
|
||||
|
@ -114,7 +109,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize,
|
|||
color_channels, format.num_channels);
|
||||
}
|
||||
|
||||
Image3F color(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F color, Image3F::Create(xsize, ysize));
|
||||
for (size_t c = 0; c < color_channels; ++c) {
|
||||
JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck(
|
||||
data, xsize, ysize, stride, bits_per_sample, format, c, pool,
|
||||
|
@ -129,7 +124,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize,
|
|||
// Passing an interleaved image with an alpha channel to an image that doesn't
|
||||
// have alpha channel just discards the passed alpha channel.
|
||||
if (has_alpha && ib->HasAlpha()) {
|
||||
ImageF alpha(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize));
|
||||
JXL_RETURN_IF_ERROR(ConvertFromExternalNoSizeCheck(
|
||||
data, xsize, ysize, stride, bits_per_sample, format,
|
||||
format.num_channels - 1, pool, &alpha));
|
||||
|
@ -137,7 +132,7 @@ Status ConvertFromExternalNoSizeCheck(const uint8_t* data, size_t xsize,
|
|||
} else if (!has_alpha && ib->HasAlpha()) {
|
||||
// if alpha is not passed, but it is expected, then assume
|
||||
// it is all-opaque
|
||||
ImageF alpha(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize));
|
||||
FillImage(1.0f, &alpha);
|
||||
ib->SetAlpha(std::move(alpha));
|
||||
}
|
||||
|
@ -184,7 +179,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||
color_channels, format.num_channels);
|
||||
}
|
||||
|
||||
Image3F color(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F color, Image3F::Create(xsize, ysize));
|
||||
for (size_t c = 0; c < color_channels; ++c) {
|
||||
JXL_RETURN_IF_ERROR(ConvertFromExternal(bytes.data(), bytes.size(), xsize,
|
||||
ysize, bits_per_sample, format, c,
|
||||
|
@ -199,7 +194,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||
// Passing an interleaved image with an alpha channel to an image that doesn't
|
||||
// have alpha channel just discards the passed alpha channel.
|
||||
if (has_alpha && ib->HasAlpha()) {
|
||||
ImageF alpha(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize));
|
||||
JXL_RETURN_IF_ERROR(ConvertFromExternal(
|
||||
bytes.data(), bytes.size(), xsize, ysize, bits_per_sample, format,
|
||||
format.num_channels - 1, pool, &alpha));
|
||||
|
@ -207,7 +202,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||
} else if (!has_alpha && ib->HasAlpha()) {
|
||||
// if alpha is not passed, but it is expected, then assume
|
||||
// it is all-opaque
|
||||
ImageF alpha(xsize, ysize);
|
||||
JXL_ASSIGN_OR_RETURN(ImageF alpha, ImageF::Create(xsize, ysize));
|
||||
FillImage(1.0f, &alpha);
|
||||
ib->SetAlpha(std::move(alpha));
|
||||
}
|
||||
|
|
|
@ -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<const unsigned char*>(buffer);
|
||||
int y_begin = std::max<int>(0, ys - 2 * effort) / 2;
|
||||
int y_count = std::min<int>(num_rows, y0 + ys - y_begin - 1);
|
||||
int y_begin_group =
|
||||
std::max<ssize_t>(
|
||||
0, static_cast<ssize_t>(ys) - static_cast<ssize_t>(num_rows)) /
|
||||
2;
|
||||
int y_count = std::min<int>(num_rows, ys - y_begin_group);
|
||||
int x_max = xs / kChunkSize * kChunkSize;
|
||||
CollectSamples(rgba, 0, y_begin, x_max, stride, y_count, raw_counts,
|
||||
CollectSamples(rgba, 0, y_begin_group, x_max, stride, y_count, raw_counts,
|
||||
lz77_counts, onegroup, !collided, bitdepth, nb_chans,
|
||||
big_endian, lookup.data());
|
||||
input.release_buffer(input.opaque, buffer);
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/ac_context.h"
|
||||
#include "lib/jxl/ac_strategy.h"
|
||||
#include "lib/jxl/ans_params.h"
|
||||
#include "lib/jxl/base/bits.h"
|
||||
#include "lib/jxl/base/common.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
|
@ -31,7 +31,6 @@
|
|||
#include "lib/jxl/coeff_order_fwd.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
#include "lib/jxl/common.h" // kMaxNumPasses
|
||||
#include "lib/jxl/compressed_dc.h"
|
||||
#include "lib/jxl/dct_util.h"
|
||||
#include "lib/jxl/dec_external_image.h"
|
||||
#include "lib/jxl/enc_ac_strategy.h"
|
||||
|
@ -47,7 +46,6 @@
|
|||
#include "lib/jxl/enc_entropy_coder.h"
|
||||
#include "lib/jxl/enc_external_image.h"
|
||||
#include "lib/jxl/enc_fields.h"
|
||||
#include "lib/jxl/enc_gaborish.h"
|
||||
#include "lib/jxl/enc_group.h"
|
||||
#include "lib/jxl/enc_heuristics.h"
|
||||
#include "lib/jxl/enc_modular.h"
|
||||
|
@ -285,7 +283,8 @@ Status LoopFilterFromParams(const CompressParams& cparams, bool streaming_mode,
|
|||
if (frame_header->encoding == FrameEncoding::kModular &&
|
||||
!cparams.IsLossless()) {
|
||||
// TODO(veluca): this formula is nonsense.
|
||||
loop_filter->epf_sigma_for_modular = cparams.butteraugli_distance;
|
||||
loop_filter->epf_sigma_for_modular =
|
||||
std::max(cparams.butteraugli_distance, 1.0f);
|
||||
}
|
||||
if (frame_header->encoding == FrameEncoding::kModular &&
|
||||
cparams.lossy_palette) {
|
||||
|
@ -539,7 +538,7 @@ struct PixelStatsForChromacityAdjustment {
|
|||
float dx = 0;
|
||||
float db = 0;
|
||||
float exposed_blue = 0;
|
||||
float CalcPlane(const ImageF* JXL_RESTRICT plane, const Rect& rect) const {
|
||||
static float CalcPlane(const ImageF* JXL_RESTRICT plane, const Rect& rect) {
|
||||
float xmax = 0;
|
||||
float ymax = 0;
|
||||
for (size_t ty = 1; ty < rect.ysize(); ++ty) {
|
||||
|
@ -583,7 +582,7 @@ struct PixelStatsForChromacityAdjustment {
|
|||
dx = CalcPlane(&opsin->Plane(0), rect);
|
||||
CalcExposedBlue(&opsin->Plane(1), &opsin->Plane(2), rect);
|
||||
}
|
||||
int HowMuchIsXChannelPixelized() {
|
||||
int HowMuchIsXChannelPixelized() const {
|
||||
if (dx >= 0.03) {
|
||||
return 2;
|
||||
}
|
||||
|
@ -592,7 +591,7 @@ struct PixelStatsForChromacityAdjustment {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
int HowMuchIsBChannelPixelized() {
|
||||
int HowMuchIsBChannelPixelized() const {
|
||||
int add = exposed_blue >= 0.13 ? 1 : 0;
|
||||
if (db > 0.38) {
|
||||
return 2 + add;
|
||||
|
@ -682,12 +681,12 @@ void ComputeNoiseParams(const CompressParams& cparams, bool streaming_mode,
|
|||
}
|
||||
}
|
||||
|
||||
void DownsampleColorChannels(const CompressParams& cparams,
|
||||
const FrameHeader& frame_header,
|
||||
bool color_is_jpeg, Image3F* opsin) {
|
||||
Status DownsampleColorChannels(const CompressParams& cparams,
|
||||
const FrameHeader& frame_header,
|
||||
bool color_is_jpeg, Image3F* opsin) {
|
||||
if (color_is_jpeg || frame_header.upsampling == 1 ||
|
||||
cparams.already_downsampled) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
if (frame_header.encoding == FrameEncoding::kVarDCT &&
|
||||
frame_header.upsampling == 2) {
|
||||
|
@ -698,16 +697,18 @@ void DownsampleColorChannels(const CompressParams& cparams,
|
|||
// TODO(lode): DownsampleImage2_Iterative is currently too slow to
|
||||
// be used for squirrel, make it faster, and / or enable it only for
|
||||
// kitten.
|
||||
DownsampleImage2_Iterative(opsin);
|
||||
JXL_RETURN_IF_ERROR(DownsampleImage2_Iterative(opsin));
|
||||
} else {
|
||||
DownsampleImage2_Sharper(opsin);
|
||||
JXL_RETURN_IF_ERROR(DownsampleImage2_Sharper(opsin));
|
||||
}
|
||||
} else {
|
||||
DownsampleImage(opsin, frame_header.upsampling);
|
||||
JXL_ASSIGN_OR_RETURN(*opsin,
|
||||
DownsampleImage(*opsin, frame_header.upsampling));
|
||||
}
|
||||
if (frame_header.encoding == FrameEncoding::kVarDCT) {
|
||||
PadImageToBlockMultipleInPlace(opsin);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename V, typename R>
|
||||
|
@ -741,14 +742,17 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
|
|||
const size_t ysize_blocks = frame_dim.ysize_blocks;
|
||||
|
||||
// no-op chroma from luma
|
||||
shared.cmap = ColorCorrelationMap(xsize, ysize, false);
|
||||
JXL_ASSIGN_OR_RETURN(shared.cmap,
|
||||
ColorCorrelationMap::Create(xsize, ysize, false));
|
||||
shared.ac_strategy.FillDCT8();
|
||||
FillImage(uint8_t(0), &shared.epf_sharpness);
|
||||
|
||||
enc_state->coeffs.clear();
|
||||
while (enc_state->coeffs.size() < enc_state->passes.size()) {
|
||||
enc_state->coeffs.emplace_back(make_unique<ACImageT<int32_t>>(
|
||||
kGroupDim * kGroupDim, frame_dim.num_groups));
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
std::unique_ptr<ACImageT<int32_t>> coeffs,
|
||||
ACImageT<int32_t>::Make(kGroupDim * kGroupDim, frame_dim.num_groups));
|
||||
enc_state->coeffs.emplace_back(std::move(coeffs));
|
||||
}
|
||||
|
||||
// convert JPEG quantization table to a Quantizer object
|
||||
|
@ -779,7 +783,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
|
|||
1.0f / dcquantization[2]};
|
||||
|
||||
qe[AcStrategy::Type::DCT] = QuantEncoding::RAW(qt);
|
||||
DequantMatricesSetCustom(&shared.matrices, qe, enc_modular);
|
||||
JXL_RETURN_IF_ERROR(
|
||||
DequantMatricesSetCustom(&shared.matrices, qe, enc_modular));
|
||||
|
||||
// Ensure that InvGlobalScale() is 1.
|
||||
shared.quantizer = Quantizer(&shared.matrices, 1, kGlobalScaleDenom);
|
||||
|
@ -844,7 +849,8 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
|
|||
kScale * row_s[x * kDCTBlockSize + coeffpos] +
|
||||
(kOffset - kBase * kScale) * scaled_m;
|
||||
if (std::abs(scaled_m) > 1e-8f) {
|
||||
float from, to;
|
||||
float from;
|
||||
float to;
|
||||
if (scaled_m > 0) {
|
||||
from = (scaled_s - kZeroThresh) / scaled_m;
|
||||
to = (scaled_s + kZeroThresh) / scaled_m;
|
||||
|
@ -889,7 +895,7 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
|
|||
}
|
||||
}
|
||||
|
||||
Image3F dc = Image3F(xsize_blocks, ysize_blocks);
|
||||
JXL_ASSIGN_OR_RETURN(Image3F dc, Image3F::Create(xsize_blocks, ysize_blocks));
|
||||
if (!frame_header.chroma_subsampling.Is444()) {
|
||||
ZeroFillImage(&dc);
|
||||
for (auto& coeff : enc_state->coeffs) {
|
||||
|
@ -1024,18 +1030,27 @@ Status ComputeJPEGTranscodingData(const jpeg::JPEGData& jpeg_data,
|
|||
*std::max_element(ctx_map.begin(), ctx_map.end()) + 1;
|
||||
|
||||
// disable DC frame for now
|
||||
std::atomic<bool> has_error{false};
|
||||
auto compute_dc_coeffs = [&](const uint32_t group_index,
|
||||
size_t /* thread */) {
|
||||
if (has_error) return;
|
||||
const Rect r = enc_state->shared.frame_dim.DCGroupRect(group_index);
|
||||
enc_modular->AddVarDCTDC(frame_header, dc, r, group_index,
|
||||
/*nl_dc=*/false, enc_state,
|
||||
/*jpeg_transcode=*/true);
|
||||
enc_modular->AddACMetadata(r, group_index, /*jpeg_transcode=*/true,
|
||||
enc_state);
|
||||
if (!enc_modular->AddVarDCTDC(frame_header, dc, r, group_index,
|
||||
/*nl_dc=*/false, enc_state,
|
||||
/*jpeg_transcode=*/true)) {
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
if (!enc_modular->AddACMetadata(r, group_index, /*jpeg_transcode=*/true,
|
||||
enc_state)) {
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
};
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_dc_groups,
|
||||
ThreadPool::NoInit, compute_dc_coeffs,
|
||||
"Compute DC coeffs"));
|
||||
if (has_error) return JXL_FAILURE("Compute DC coeffs failed");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -1077,10 +1092,12 @@ void ComputeAllCoeffOrders(PassesEncoderState& enc_state,
|
|||
// Working area for TokenizeCoefficients (per-group!)
|
||||
struct EncCache {
|
||||
// Allocates memory when first called.
|
||||
void InitOnce() {
|
||||
Status InitOnce() {
|
||||
if (num_nzeroes.xsize() == 0) {
|
||||
num_nzeroes = Image3I(kGroupDimInBlocks, kGroupDimInBlocks);
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
num_nzeroes, Image3I::Create(kGroupDimInBlocks, kGroupDimInBlocks));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// TokenizeCoefficients
|
||||
Image3I num_nzeroes;
|
||||
|
@ -1095,8 +1112,10 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header,
|
|||
group_caches.resize(num_threads);
|
||||
return true;
|
||||
};
|
||||
std::atomic<bool> has_error{false};
|
||||
const auto tokenize_group = [&](const uint32_t group_index,
|
||||
const size_t thread) {
|
||||
if (has_error) return;
|
||||
// Tokenize coefficients.
|
||||
const Rect rect = shared.frame_dim.BlockGroupRect(group_index);
|
||||
for (size_t idx_pass = 0; idx_pass < enc_state->passes.size(); idx_pass++) {
|
||||
|
@ -1107,7 +1126,10 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header,
|
|||
enc_state->coeffs[idx_pass]->PlaneRow(2, group_index, 0).ptr32,
|
||||
};
|
||||
// Ensure group cache is initialized.
|
||||
group_caches[thread].InitOnce();
|
||||
if (!group_caches[thread].InitOnce()) {
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
TokenizeCoefficients(
|
||||
&shared.coeff_orders[idx_pass * shared.coeff_order_size], rect,
|
||||
ac_rows, shared.ac_strategy, frame_header.chroma_subsampling,
|
||||
|
@ -1116,8 +1138,11 @@ Status TokenizeAllCoefficients(const FrameHeader& frame_header,
|
|||
shared.raw_quant_field, shared.block_ctx_map);
|
||||
}
|
||||
};
|
||||
return RunOnPool(pool, 0, shared.frame_dim.num_groups, tokenize_group_init,
|
||||
tokenize_group, "TokenizeGroup");
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, shared.frame_dim.num_groups,
|
||||
tokenize_group_init, tokenize_group,
|
||||
"TokenizeGroup"));
|
||||
if (has_error) return JXL_FAILURE("TokenizeGroup failed");
|
||||
return true;
|
||||
}
|
||||
|
||||
Status EncodeGlobalDCInfo(const PassesSharedState& shared, BitWriter* writer,
|
||||
|
@ -1308,35 +1333,43 @@ Status EncodeGroups(const FrameHeader& frame_header,
|
|||
enc_state, get_output(global_ac_index), enc_modular, aux_out));
|
||||
}
|
||||
|
||||
std::atomic<int> num_errors{0};
|
||||
std::atomic<bool> has_error{false};
|
||||
const auto process_group = [&](const uint32_t group_index,
|
||||
const size_t thread) {
|
||||
if (has_error) return;
|
||||
AuxOut* my_aux_out = aux_outs[thread].get();
|
||||
|
||||
size_t ac_group_id =
|
||||
enc_state->streaming_mode
|
||||
? enc_modular->ComputeStreamingAbsoluteAcGroupId(
|
||||
enc_state->dc_group_index, group_index, shared.frame_dim)
|
||||
: group_index;
|
||||
|
||||
for (size_t i = 0; i < num_passes; i++) {
|
||||
JXL_DEBUG_V(2, "Encoding AC group %u [abs %" PRIuS "] pass %" PRIuS,
|
||||
group_index, ac_group_id, i);
|
||||
if (frame_header.encoding == FrameEncoding::kVarDCT) {
|
||||
if (!EncodeGroupTokenizedCoefficients(
|
||||
group_index, i, enc_state->histogram_idx[group_index],
|
||||
*enc_state, ac_group_code(i, group_index), my_aux_out)) {
|
||||
num_errors.fetch_add(1, std::memory_order_relaxed);
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Write all modular encoded data (color?, alpha, depth, extra channels)
|
||||
if (!enc_modular->EncodeStream(
|
||||
ac_group_code(i, group_index), my_aux_out, kLayerModularAcGroup,
|
||||
ModularStreamId::ModularAC(group_index, i))) {
|
||||
num_errors.fetch_add(1, std::memory_order_relaxed);
|
||||
ModularStreamId::ModularAC(ac_group_id, i))) {
|
||||
has_error = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, num_groups, resize_aux_outs,
|
||||
process_group, "EncodeGroupCoefficients"));
|
||||
|
||||
if (has_error) return JXL_FAILURE("EncodeGroupCoefficients failed");
|
||||
// Resizing aux_outs to 0 also Assimilates the array.
|
||||
static_cast<void>(resize_aux_outs(0));
|
||||
JXL_RETURN_IF_ERROR(num_errors.load(std::memory_order_relaxed) == 0);
|
||||
|
||||
for (BitWriter& bw : *group_codes) {
|
||||
BitWriter::Allotment allotment(&bw, 8);
|
||||
|
@ -1360,29 +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<ImageF> extra_channels(num_extra_channels);
|
||||
for (auto& extra_channel : extra_channels) {
|
||||
extra_channel = jxl::ImageF(patch_rect.xsize(), patch_rect.ysize());
|
||||
JXL_ASSIGN_OR_RETURN(
|
||||
extra_channel, ImageF::Create(patch_rect.xsize(), patch_rect.ysize()));
|
||||
}
|
||||
ImageF* alpha = alpha_eci ? &extra_channels[alpha_idx] : nullptr;
|
||||
ImageF* black = black_eci ? &extra_channels[black_idx] : nullptr;
|
||||
|
@ -1432,7 +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<coeff_order_t>& permutation,
|
||||
std::vector<size_t>& dc_group_order) {
|
||||
// This is only valid in VarDCT mode, otherwise there can be group shift.
|
||||
const size_t group_size = 256;
|
||||
const size_t dc_group_size = group_size * kBlockDim;
|
||||
const size_t group_xsize = DivCeil(xsize, group_size);
|
||||
const size_t group_ysize = DivCeil(ysize, group_size);
|
||||
|
@ -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<coeff_order_t> permutation;
|
||||
std::vector<size_t> dc_group_order;
|
||||
ComputePermutationForStreaming(frame_data.xsize, frame_data.ysize, num_passes,
|
||||
permutation, dc_group_order);
|
||||
size_t group_size = frame_header.ToFrameDimensions().group_dim;
|
||||
ComputePermutationForStreaming(frame_data.xsize, frame_data.ysize, group_size,
|
||||
num_passes, permutation, dc_group_order);
|
||||
enc_state.shared.num_histograms = dc_group_order.size();
|
||||
// This is only valid in VarDCT mode, otherwise there can be group shift.
|
||||
size_t group_size = 256;
|
||||
size_t dc_group_size = group_size * kBlockDim;
|
||||
size_t dc_group_xsize = DivCeil(frame_data.xsize, dc_group_size);
|
||||
size_t min_dc_global_size = 0;
|
||||
|
@ -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<CompressParams> all_params;
|
||||
std::vector<size_t> size;
|
||||
|
||||
CompressParams cparams_attempt = cparams_orig;
|
||||
cparams_attempt.speed_tier = SpeedTier::kTortoise;
|
||||
cparams_attempt.speed_tier = SpeedTier::kGlacier;
|
||||
cparams_attempt.options.max_properties = 4;
|
||||
|
||||
for (float x : {0.0f, 80.f}) {
|
||||
|
@ -2054,11 +2129,12 @@ Status EncodeFrame(const CompressParams& cparams_orig,
|
|||
|
||||
size.resize(all_params.size());
|
||||
|
||||
std::atomic<int> num_errors{0};
|
||||
std::atomic<bool> has_error{false};
|
||||
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, all_params.size(), ThreadPool::NoInit,
|
||||
[&](size_t task, size_t) {
|
||||
if (has_error) return;
|
||||
std::vector<uint8_t> output(64);
|
||||
uint8_t* next_out = output.data();
|
||||
size_t avail_out = output.size();
|
||||
|
@ -2066,13 +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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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<QuantEncoding> encodings(DequantMatrices::kNum,
|
||||
QuantEncoding::DCT(dct_params));
|
||||
DequantMatricesSetCustom(dequant_matrices, encodings,
|
||||
modular_frame_encoder);
|
||||
JXL_RETURN_IF_ERROR(DequantMatricesSetCustom(dequant_matrices, encodings,
|
||||
modular_frame_encoder));
|
||||
float dc_weights[3] = {1.0f / cparams.max_error[0],
|
||||
1.0f / cparams.max_error[1],
|
||||
1.0f / cparams.max_error[2]};
|
||||
DequantMatricesSetCustomDC(dequant_matrices, dc_weights);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void StoreMin2(const float v, float& min1, float& min2) {
|
||||
|
@ -226,9 +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<float>(min, initial.Row(y2)[x2]);
|
||||
max = std::max<float>(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<float>(1, orig, -1, up);
|
||||
JXL_ASSIGN_OR_RETURN(corr, LinComb<float>(1, orig, -1, up));
|
||||
ElwiseMul(corr, weights, &corr);
|
||||
AntiUpsample(corr, &corr2);
|
||||
ElwiseDiv(corr2, weights2, &corr2);
|
||||
|
||||
down = LinComb<float>(1, down, 1, corr2);
|
||||
JXL_ASSIGN_OR_RETURN(down, LinComb<float>(1, down, 1, corr2));
|
||||
}
|
||||
|
||||
ReduceRinging(initial, mask, down);
|
||||
|
@ -690,32 +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<ButteraugliComparator> butter,
|
||||
ButteraugliComparator::Make(rgb, butter_params));
|
||||
JXL_RETURN_IF_ERROR(butter->Mask(&mask));
|
||||
JXL_ASSIGN_OR_RETURN(ImageF mask_fuzzy,
|
||||
ImageF::Create(opsin->xsize(), opsin->ysize()));
|
||||
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
DownsampleImage2_Iterative(opsin->Plane(c), &downsampled.Plane(c));
|
||||
JXL_RETURN_IF_ERROR(
|
||||
DownsampleImage2_Iterative(opsin->Plane(c), &downsampled.Plane(c)));
|
||||
}
|
||||
*opsin = std::move(downsampled);
|
||||
return true;
|
||||
}
|
||||
|
||||
Status LossyFrameHeuristics(const FrameHeader& frame_header,
|
||||
|
@ -739,10 +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<bool> has_error{false};
|
||||
auto process_tile = [&](const uint32_t tid, const size_t thread) {
|
||||
if (has_error) return;
|
||||
size_t n_enc_tiles = DivCeil(frame_dim.xsize_blocks, kEncTileDimInBlocks);
|
||||
size_t tx = tid % n_enc_tiles;
|
||||
size_t ty = tid / n_enc_tiles;
|
||||
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include "lib/jxl/base/byte_order.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
#include "lib/jxl/enc_ans.h"
|
||||
#include "lib/jxl/enc_aux_out.h"
|
||||
#include "lib/jxl/fields.h"
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче