зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1740415 - Update libjxl to 9e8c576 r=tnikkel
Differential Revision: https://phabricator.services.mozilla.com/D130862
This commit is contained in:
Родитель
a5f66c95e1
Коммит
75108aa7bb
|
@ -20,12 +20,12 @@ origin:
|
||||||
|
|
||||||
# Human-readable identifier for this version/release
|
# Human-readable identifier for this version/release
|
||||||
# Generally "version NNN", "tag SSS", "bookmark SSS"
|
# Generally "version NNN", "tag SSS", "bookmark SSS"
|
||||||
release: commit 0eff04c3a04e72e78d35f0965f17f54a98d61830 (2021-10-20T17:45:50Z).
|
release: commit 9e8c5766ba32c008ddcaf11f0fac591aa7964f7b (2021-11-10T07:51:06Z).
|
||||||
|
|
||||||
# Revision to pull in
|
# Revision to pull in
|
||||||
# Must be a long or short commit SHA (long preferred)
|
# Must be a long or short commit SHA (long preferred)
|
||||||
# NOTE(krosylight): Update highway together when updating this!
|
# NOTE(krosylight): Update highway together when updating this!
|
||||||
revision: 0eff04c3a04e72e78d35f0965f17f54a98d61830
|
revision: 9e8c5766ba32c008ddcaf11f0fac591aa7964f7b
|
||||||
|
|
||||||
# The package's license, where possible using the mnemonic from
|
# The package's license, where possible using the mnemonic from
|
||||||
# https://spdx.org/licenses/
|
# https://spdx.org/licenses/
|
||||||
|
@ -52,3 +52,4 @@ vendoring:
|
||||||
- doc/
|
- doc/
|
||||||
- third_party/testdata/
|
- third_party/testdata/
|
||||||
- tools/
|
- tools/
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ Alexander Sago <cagelight@gmail.com>
|
||||||
Dirk Lemstra <dirk@lemstra.org>
|
Dirk Lemstra <dirk@lemstra.org>
|
||||||
Jon Sneyers <jon@cloudinary.com>
|
Jon Sneyers <jon@cloudinary.com>
|
||||||
Lovell Fuller
|
Lovell Fuller
|
||||||
|
Kleis Auke Wolthuizen <github@kleisauke.nl>
|
||||||
Marcin Konicki <ahwayakchih@gmail.com>
|
Marcin Konicki <ahwayakchih@gmail.com>
|
||||||
Petr Diblík
|
Petr Diblík
|
||||||
Pieter Wuille
|
Pieter Wuille
|
||||||
|
@ -30,3 +31,4 @@ Misaki Kasumi <misakikasumi@outlook.com>
|
||||||
Samuel Leong <wvvwvvvvwvvw@gmail.com>
|
Samuel Leong <wvvwvvvvwvvw@gmail.com>
|
||||||
Alex Xu (Hello71) <alex_y_xu@yahoo.ca>
|
Alex Xu (Hello71) <alex_y_xu@yahoo.ca>
|
||||||
Vincent Torri <vincent.torri@gmail.com>
|
Vincent Torri <vincent.torri@gmail.com>
|
||||||
|
Artem Selishchev
|
||||||
|
|
|
@ -5,6 +5,28 @@ All notable changes to this project will be documented in this file.
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||||
|
|
||||||
|
## Unreleased
|
||||||
|
### Added
|
||||||
|
- decoder API: Ability to decode the content of metadata boxes:
|
||||||
|
`JXL_DEC_BOX`, `JXL_DEC_BOX_NEED_MORE_OUTPUT`, `JxlDecoderSetBoxBuffer`,
|
||||||
|
`JxlDecoderGetBoxType`, `JxlDecoderGetBoxSizeRaw` and
|
||||||
|
`JxlDecoderSetDecompressBoxes`
|
||||||
|
- decoder API: ability to mark the input is finished: `JxlDecoderCloseInput`
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- decoder API: `JxlDecoderCloseInput` is required when using JXL_DEC_BOX, and
|
||||||
|
also encouraged, but not required for backwards compatiblity, for any other
|
||||||
|
usage of the decoder.
|
||||||
|
|
||||||
|
## [0.6.1] - 2021-10-29
|
||||||
|
### Changed
|
||||||
|
- Security: Fix OOB read in splines rendering (#735 -
|
||||||
|
[CVE-2021-22563](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22563))
|
||||||
|
- Security: Fix OOB copy (read/write) in out-of-order/multi-threaded decoding
|
||||||
|
(#708 - [CVE-2021-22564](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-22564))
|
||||||
|
- Fix segfault in `djxl` tool with `--allow_partial_files` flag (#781).
|
||||||
|
- Fix border in extra channels when using upsampling (#796)
|
||||||
|
|
||||||
## [0.6] - 2021-10-04
|
## [0.6] - 2021-10-04
|
||||||
### Added
|
### Added
|
||||||
- API: New functions to decode extra channels:
|
- API: New functions to decode extra channels:
|
||||||
|
|
|
@ -355,7 +355,12 @@ if(JPEGXL_ENABLE_MANPAGES)
|
||||||
find_program(ASCIIDOC a2x)
|
find_program(ASCIIDOC a2x)
|
||||||
if(NOT "${ASCIIDOC}" STREQUAL "ASCIIDOC-NOTFOUND")
|
if(NOT "${ASCIIDOC}" STREQUAL "ASCIIDOC-NOTFOUND")
|
||||||
file(STRINGS "${ASCIIDOC}" ASCIIDOC_SHEBANG LIMIT_COUNT 1)
|
file(STRINGS "${ASCIIDOC}" ASCIIDOC_SHEBANG LIMIT_COUNT 1)
|
||||||
if(ASCIIDOC_SHEBANG MATCHES "python2")
|
if(ASCIIDOC_SHEBANG MATCHES "/sh")
|
||||||
|
set(ASCIIDOC_PY_FOUND ON)
|
||||||
|
# Run the program directly and set ASCIIDOC as empty.
|
||||||
|
set(ASCIIDOC_PY "${ASCIIDOC}")
|
||||||
|
set(ASCIIDOC "")
|
||||||
|
elseif(ASCIIDOC_SHEBANG MATCHES "python2")
|
||||||
find_package(Python2 COMPONENTS Interpreter)
|
find_package(Python2 COMPONENTS Interpreter)
|
||||||
set(ASCIIDOC_PY_FOUND "${Python2_Interpreter_FOUND}")
|
set(ASCIIDOC_PY_FOUND "${Python2_Interpreter_FOUND}")
|
||||||
set(ASCIIDOC_PY Python2::Interpreter)
|
set(ASCIIDOC_PY Python2::Interpreter)
|
||||||
|
@ -386,7 +391,7 @@ if (ASCIIDOC_PY_FOUND)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT "${PAGE}.1"
|
OUTPUT "${PAGE}.1"
|
||||||
COMMAND "${ASCIIDOC_PY}"
|
COMMAND "${ASCIIDOC_PY}"
|
||||||
ARGS "${ASCIIDOC}"
|
ARGS ${ASCIIDOC}
|
||||||
--format manpage --destination-dir="${CMAKE_CURRENT_BINARY_DIR}"
|
--format manpage --destination-dir="${CMAKE_CURRENT_BINARY_DIR}"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt"
|
"${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt"
|
||||||
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt")
|
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt")
|
||||||
|
|
|
@ -86,13 +86,34 @@ test_copyright() {
|
||||||
|
|
||||||
# Check that we don't use "%zu" or "%zd" in format string for size_t.
|
# Check that we don't use "%zu" or "%zd" in format string for size_t.
|
||||||
test_printf_size_t() {
|
test_printf_size_t() {
|
||||||
|
local ret=0
|
||||||
if grep -n -E '%[0-9]*z[udx]' \
|
if grep -n -E '%[0-9]*z[udx]' \
|
||||||
$(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$'); then
|
$(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$'); then
|
||||||
echo "Don't use '%zu' or '%zd' in a format string, instead use " \
|
echo "Don't use '%zu' or '%zd' in a format string, instead use " \
|
||||||
"'%\" PRIuS \"' or '%\" PRIdS \"'." >&2
|
"'%\" PRIuS \"' or '%\" PRIdS \"'." >&2
|
||||||
return 1
|
ret=1
|
||||||
fi
|
fi
|
||||||
return 0
|
|
||||||
|
local f
|
||||||
|
for f in $(git ls-files | grep -E "\.cc$" | xargs grep 'PRI[udx]S' |
|
||||||
|
cut -f 1 -d : | uniq); do
|
||||||
|
if ! grep -F printf_macros.h "$f" >/dev/null; then
|
||||||
|
echo "$f: Add lib/jxl/base/printf_macros.h for PRI.S, or use other " \
|
||||||
|
"types for code outside lib/jxl library." >&2
|
||||||
|
ret=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for f in $(git ls-files | grep -E "\.h$" | grep -v -F printf_macros.h |
|
||||||
|
xargs grep -n 'PRI[udx]S'); do
|
||||||
|
# Having PRIuS / PRIdS in a header file means that printf_macros.h may
|
||||||
|
# be included before a system header, in particular before gtest headers.
|
||||||
|
# those may re-define PRIuS unconditionally causing a compile error.
|
||||||
|
echo "$f: Don't use PRI.S in header files. Sorry."
|
||||||
|
ret=1
|
||||||
|
done
|
||||||
|
|
||||||
|
return ${ret}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Check that "dec_" code doesn't depend on "enc_" headers.
|
# Check that "dec_" code doesn't depend on "enc_" headers.
|
||||||
|
|
|
@ -468,6 +468,8 @@ cmake_build_and_test() {
|
||||||
if [[ "${PACK_TEST:-}" == "1" ]]; then
|
if [[ "${PACK_TEST:-}" == "1" ]]; then
|
||||||
(cd "${BUILD_DIR}"
|
(cd "${BUILD_DIR}"
|
||||||
${FIND_BIN} -name '*.cmake' -a '!' -path '*CMakeFiles*'
|
${FIND_BIN} -name '*.cmake' -a '!' -path '*CMakeFiles*'
|
||||||
|
# gtest / gmock / gtest_main shared libs
|
||||||
|
${FIND_BIN} lib/ -name 'libg*.so*'
|
||||||
${FIND_BIN} -type d -name tests -a '!' -path '*CMakeFiles*'
|
${FIND_BIN} -type d -name tests -a '!' -path '*CMakeFiles*'
|
||||||
) | tar -C "${BUILD_DIR}" -cf "${BUILD_DIR}/tests.tar.xz" -T - \
|
) | tar -C "${BUILD_DIR}" -cf "${BUILD_DIR}/tests.tar.xz" -T - \
|
||||||
--use-compress-program="xz --threads=$(nproc --all || echo 1) -6"
|
--use-compress-program="xz --threads=$(nproc --all || echo 1) -6"
|
||||||
|
|
|
@ -36,6 +36,7 @@ bool DecodeJpegXlExif(const uint8_t* jxl, size_t size,
|
||||||
}
|
}
|
||||||
|
|
||||||
JxlDecoderSetInput(dec.get(), jxl, size);
|
JxlDecoderSetInput(dec.get(), jxl, size);
|
||||||
|
JxlDecoderCloseInput(dec.get());
|
||||||
|
|
||||||
const constexpr size_t kChunkSize = 65536;
|
const constexpr size_t kChunkSize = 65536;
|
||||||
size_t output_pos = 0;
|
size_t output_pos = 0;
|
||||||
|
|
|
@ -52,6 +52,7 @@ bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size,
|
||||||
JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
|
JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
|
||||||
|
|
||||||
JxlDecoderSetInput(dec.get(), jxl, size);
|
JxlDecoderSetInput(dec.get(), jxl, size);
|
||||||
|
JxlDecoderCloseInput(dec.get());
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
|
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "lib/jxl/base/byte_order.h"
|
#include "lib/jxl/base/byte_order.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/file_io.h"
|
#include "lib/jxl/base/file_io.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/color_management.h"
|
#include "lib/jxl/color_management.h"
|
||||||
#include "lib/jxl/dec_external_image.h"
|
#include "lib/jxl/dec_external_image.h"
|
||||||
#include "lib/jxl/enc_external_image.h"
|
#include "lib/jxl/enc_external_image.h"
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "lib/jxl/base/byte_order.h"
|
#include "lib/jxl/base/byte_order.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/file_io.h"
|
#include "lib/jxl/base/file_io.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/color_management.h"
|
#include "lib/jxl/color_management.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
#include "lib/jxl/dec_external_image.h"
|
#include "lib/jxl/dec_external_image.h"
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "lib/jxl/base/byte_order.h"
|
#include "lib/jxl/base/byte_order.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/file_io.h"
|
#include "lib/jxl/base/file_io.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/color_management.h"
|
#include "lib/jxl/color_management.h"
|
||||||
#include "lib/jxl/dec_external_image.h"
|
#include "lib/jxl/dec_external_image.h"
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "lib/jxl/base/byte_order.h"
|
#include "lib/jxl/base/byte_order.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/file_io.h"
|
#include "lib/jxl/base/file_io.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/color_management.h"
|
#include "lib/jxl/color_management.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
#include "lib/jxl/fields.h" // AllDefault
|
#include "lib/jxl/fields.h" // AllDefault
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "lib/extras/codec_pgx.h"
|
#include "lib/extras/codec_pgx.h"
|
||||||
#include "lib/extras/codec_pnm.h"
|
#include "lib/extras/codec_pnm.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/random.h"
|
#include "lib/jxl/base/random.h"
|
||||||
#include "lib/jxl/base/thread_pool_internal.h"
|
#include "lib/jxl/base/thread_pool_internal.h"
|
||||||
#include "lib/jxl/color_management.h"
|
#include "lib/jxl/color_management.h"
|
||||||
|
|
|
@ -98,30 +98,13 @@ Status ToneMapFrame(const std::pair<float, float> display_nits,
|
||||||
TF_PQ().DisplayFromEncoded(df, e4)));
|
TF_PQ().DisplayFromEncoded(df, e4)));
|
||||||
|
|
||||||
const V ratio = new_luminance / luminance;
|
const V ratio = new_luminance / luminance;
|
||||||
const V multiplier = ratio *
|
const V normalizer =
|
||||||
Set(df, ib->metadata()->IntensityTarget()) *
|
Set(df, ib->metadata()->IntensityTarget()) * inv_max_display_nits;
|
||||||
inv_max_display_nits;
|
|
||||||
|
|
||||||
red *= multiplier;
|
|
||||||
green *= multiplier;
|
|
||||||
blue *= multiplier;
|
|
||||||
|
|
||||||
const V gray = new_luminance * inv_max_display_nits;
|
|
||||||
|
|
||||||
// Desaturate out-of-gamut pixels.
|
|
||||||
V gray_mix = Zero(df);
|
|
||||||
for (const V val : {red, green, blue}) {
|
|
||||||
const V inv_val_minus_gray = Set(df, 1) / (val - gray);
|
|
||||||
const V bound1 = val * inv_val_minus_gray;
|
|
||||||
const V bound2 = bound1 - inv_val_minus_gray;
|
|
||||||
const V min_bound = Min(bound1, bound2);
|
|
||||||
const V max_bound = Max(bound1, bound2);
|
|
||||||
gray_mix = Clamp(gray_mix, min_bound, max_bound);
|
|
||||||
}
|
|
||||||
gray_mix = Clamp(gray_mix, Zero(df), Set(df, 1));
|
|
||||||
for (V* const val : {&red, &green, &blue}) {
|
for (V* const val : {&red, &green, &blue}) {
|
||||||
*val = IfThenElse(luminance < Set(df, 1e-6), gray,
|
*val = IfThenElse(luminance <= Set(df, 1e-6f), new_luminance,
|
||||||
MulAdd(gray_mix, gray - *val, *val));
|
*val * ratio) *
|
||||||
|
normalizer;
|
||||||
}
|
}
|
||||||
|
|
||||||
Store(red, df, row_r + x);
|
Store(red, df, row_r + x);
|
||||||
|
@ -134,6 +117,77 @@ Status ToneMapFrame(const std::pair<float, float> display_nits,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status GamutMapFrame(ImageBundle* const ib, float preserve_saturation,
|
||||||
|
ThreadPool* const pool) {
|
||||||
|
HWY_FULL(float) df;
|
||||||
|
using V = decltype(Zero(df));
|
||||||
|
|
||||||
|
ColorEncoding linear_rec2020;
|
||||||
|
linear_rec2020.SetColorSpace(ColorSpace::kRGB);
|
||||||
|
linear_rec2020.primaries = Primaries::k2100;
|
||||||
|
linear_rec2020.white_point = WhitePoint::kD65;
|
||||||
|
linear_rec2020.tf.SetTransferFunction(TransferFunction::kLinear);
|
||||||
|
JXL_RETURN_IF_ERROR(linear_rec2020.CreateICC());
|
||||||
|
JXL_RETURN_IF_ERROR(ib->TransformTo(linear_rec2020, pool));
|
||||||
|
|
||||||
|
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||||
|
pool, 0, ib->ysize(), ThreadPool::SkipInit(),
|
||||||
|
[&](const int y, const int thread) {
|
||||||
|
float* const JXL_RESTRICT row_r = ib->color()->PlaneRow(0, y);
|
||||||
|
float* const JXL_RESTRICT row_g = ib->color()->PlaneRow(1, y);
|
||||||
|
float* const JXL_RESTRICT row_b = ib->color()->PlaneRow(2, y);
|
||||||
|
for (size_t x = 0; x < ib->xsize(); x += Lanes(df)) {
|
||||||
|
V red = Load(df, row_r + x);
|
||||||
|
V green = Load(df, row_g + x);
|
||||||
|
V blue = Load(df, row_b + x);
|
||||||
|
const V luminance =
|
||||||
|
MulAdd(Set(df, 0.2627f), red,
|
||||||
|
MulAdd(Set(df, 0.6780f), green, Set(df, 0.0593f) * blue));
|
||||||
|
|
||||||
|
// Desaturate out-of-gamut pixels. This is done by mixing each pixel
|
||||||
|
// with just enough gray of the target luminance to make all
|
||||||
|
// components non-negative.
|
||||||
|
// - For saturation preservation, if a component is still larger than
|
||||||
|
// 1 then the pixel is normalized to have a maximum component of 1.
|
||||||
|
// That will reduce its luminance.
|
||||||
|
// - For luminance preservation, getting all components below 1 is
|
||||||
|
// done by mixing in yet more gray. That will desaturate it further.
|
||||||
|
V gray_mix_saturation = Zero(df);
|
||||||
|
V gray_mix_luminance = Zero(df);
|
||||||
|
for (const V val : {red, green, blue}) {
|
||||||
|
const V inv_val_minus_gray = Set(df, 1) / (val - luminance);
|
||||||
|
gray_mix_saturation =
|
||||||
|
IfThenElse(val >= luminance, gray_mix_saturation,
|
||||||
|
Max(gray_mix_saturation, val * inv_val_minus_gray));
|
||||||
|
gray_mix_luminance =
|
||||||
|
Max(gray_mix_luminance,
|
||||||
|
IfThenElse(val <= luminance, gray_mix_saturation,
|
||||||
|
(val - Set(df, 1)) * inv_val_minus_gray));
|
||||||
|
}
|
||||||
|
const V gray_mix =
|
||||||
|
Clamp(Set(df, preserve_saturation) *
|
||||||
|
(gray_mix_saturation - gray_mix_luminance) +
|
||||||
|
gray_mix_luminance,
|
||||||
|
Zero(df), Set(df, 1));
|
||||||
|
for (V* const val : {&red, &green, &blue}) {
|
||||||
|
*val = MulAdd(gray_mix, luminance - *val, *val);
|
||||||
|
}
|
||||||
|
const V normalizer =
|
||||||
|
Set(df, 1) / Max(Set(df, 1), Max(red, Max(green, blue)));
|
||||||
|
for (V* const val : {&red, &green, &blue}) {
|
||||||
|
*val = *val * normalizer;
|
||||||
|
}
|
||||||
|
|
||||||
|
Store(red, df, row_r + x);
|
||||||
|
Store(green, df, row_g + x);
|
||||||
|
Store(blue, df, row_b + x);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"GamutMap"));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// NOLINTNEXTLINE(google-readability-namespace-comments)
|
// NOLINTNEXTLINE(google-readability-namespace-comments)
|
||||||
} // namespace HWY_NAMESPACE
|
} // namespace HWY_NAMESPACE
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
@ -144,7 +198,8 @@ namespace jxl {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
HWY_EXPORT(ToneMapFrame);
|
HWY_EXPORT(ToneMapFrame);
|
||||||
}
|
HWY_EXPORT(GamutMapFrame);
|
||||||
|
} // namespace
|
||||||
|
|
||||||
Status ToneMapTo(const std::pair<float, float> display_nits,
|
Status ToneMapTo(const std::pair<float, float> display_nits,
|
||||||
CodecInOut* const io, ThreadPool* const pool) {
|
CodecInOut* const io, ThreadPool* const pool) {
|
||||||
|
@ -156,5 +211,14 @@ Status ToneMapTo(const std::pair<float, float> display_nits,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status GamutMap(CodecInOut* const io, float preserve_saturation,
|
||||||
|
ThreadPool* const pool) {
|
||||||
|
const auto gamut_map_frame = HWY_DYNAMIC_DISPATCH(GamutMapFrame);
|
||||||
|
for (ImageBundle& ib : io->frames) {
|
||||||
|
JXL_RETURN_IF_ERROR(gamut_map_frame(&ib, preserve_saturation, pool));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -10,9 +10,21 @@
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
||||||
|
// Important: after calling this, the result will contain many out-of-gamut
|
||||||
|
// colors. It is very strongly recommended to call GamutMap afterwards to
|
||||||
|
// rectify this.
|
||||||
Status ToneMapTo(std::pair<float, float> display_nits, CodecInOut* io,
|
Status ToneMapTo(std::pair<float, float> display_nits, CodecInOut* io,
|
||||||
ThreadPool* pool = nullptr);
|
ThreadPool* pool = nullptr);
|
||||||
|
|
||||||
|
// `preserve_saturation` indicates to what extent to favor saturation over
|
||||||
|
// luminance when mapping out-of-gamut colors to Rec. 2020. 0 preserves
|
||||||
|
// luminance at the complete expense of saturation, while 1 gives the most
|
||||||
|
// saturated color with the same hue that Rec. 2020 can represent even if it
|
||||||
|
// means lowering the luminance. Values in between correspond to linear mixtures
|
||||||
|
// of those two extremes.
|
||||||
|
Status GamutMap(CodecInOut* io, float preserve_saturation,
|
||||||
|
ThreadPool* pool = nullptr);
|
||||||
|
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
|
||||||
#endif // LIB_EXTRAS_TONE_MAPPING_H_
|
#endif // LIB_EXTRAS_TONE_MAPPING_H_
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
BENCHMARK_MAIN();
|
|
@ -405,6 +405,19 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec,
|
||||||
JXL_EXPORT JxlDecoderStatus
|
JXL_EXPORT JxlDecoderStatus
|
||||||
JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL keep_orientation);
|
JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL keep_orientation);
|
||||||
|
|
||||||
|
/** 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 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 JXL_DEC_SUCCESS if no error, JXL_DEC_ERROR otherwise.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JxlDecoderStatus
|
||||||
|
JxlDecoderSetRenderSpotcolors(JxlDecoder* dec, JXL_BOOL render_spotcolors);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decodes JPEG XL file using the available bytes. Requires input has been
|
* Decodes JPEG XL file using the available bytes. Requires input has been
|
||||||
* set with JxlDecoderSetInput. After JxlDecoderProcessInput, input can
|
* set with JxlDecoderSetInput. After JxlDecoderProcessInput, input can
|
||||||
|
@ -454,11 +467,14 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec);
|
||||||
* Sets input data for JxlDecoderProcessInput. The data is owned by the caller
|
* Sets input data for JxlDecoderProcessInput. The data is owned by the caller
|
||||||
* and may be used by the decoder until JxlDecoderReleaseInput is called or
|
* and may be used by the decoder until JxlDecoderReleaseInput is called or
|
||||||
* the decoder is destroyed or reset so must be kept alive until then.
|
* the decoder is destroyed or reset so must be kept alive until then.
|
||||||
|
* Cannot be called if JxlDecoderSetInput was already called and
|
||||||
|
* JxlDecoderReleaseInput was not yet called, and cannot be called after
|
||||||
|
* JxlDecoderCloseInput indicating the end of input was called.
|
||||||
* @param dec decoder object
|
* @param dec decoder object
|
||||||
* @param data pointer to next bytes to read from
|
* @param data pointer to next bytes to read from
|
||||||
* @param size amount of bytes available starting from data
|
* @param size amount of bytes available starting from data
|
||||||
* @return JXL_DEC_ERROR if input was already set without releasing,
|
* @return JXL_DEC_ERROR if input was already set without releasing or
|
||||||
* JXL_DEC_SUCCESS otherwise.
|
* JxlDecoderCloseInput was already called, JXL_DEC_SUCCESS otherwise.
|
||||||
*/
|
*/
|
||||||
JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec,
|
JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec,
|
||||||
const uint8_t* data,
|
const uint8_t* data,
|
||||||
|
@ -483,6 +499,23 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec,
|
||||||
*/
|
*/
|
||||||
JXL_EXPORT size_t JxlDecoderReleaseInput(JxlDecoder* dec);
|
JXL_EXPORT size_t JxlDecoderReleaseInput(JxlDecoder* dec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Marks the input as finished, indicates that no more JxlDecoderSetInput 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 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 JXL_DEC_BOX for getting metadata
|
||||||
|
* box contents. This function does not replace JxlDecoderReleaseInput, that
|
||||||
|
* function should still be called if its return value is needed.
|
||||||
|
* JxlDecoderCloseInput should be called as soon as all known input bytes are
|
||||||
|
* set (e.g. at the beginning when not streaming but setting all input at once),
|
||||||
|
* before the final JxlDecoderProcessInput calls.
|
||||||
|
* @param dec decoder object
|
||||||
|
*/
|
||||||
|
JXL_EXPORT void JxlDecoderCloseInput(JxlDecoder* dec);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Outputs the basic image information, such as image dimensions, bit depth and
|
* Outputs the basic image information, such as image dimensions, bit depth and
|
||||||
* all other JxlBasicInfo fields, if available.
|
* all other JxlBasicInfo fields, if available.
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#ifndef JXL_ENCODE_H_
|
#ifndef JXL_ENCODE_H_
|
||||||
#define JXL_ENCODE_H_
|
#define JXL_ENCODE_H_
|
||||||
|
|
||||||
|
#include "jxl/codestream_header.h"
|
||||||
#include "jxl/decode.h"
|
#include "jxl/decode.h"
|
||||||
#include "jxl/jxl_export.h"
|
#include "jxl/jxl_export.h"
|
||||||
#include "jxl/memory_manager.h"
|
#include "jxl/memory_manager.h"
|
||||||
|
@ -73,42 +74,163 @@ typedef enum {
|
||||||
} JxlEncoderStatus;
|
} JxlEncoderStatus;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Id of options to set to JxlEncoderOptions.
|
* Id of per-frame options to set to JxlEncoderOptions with
|
||||||
|
* JxlEncoderOptionsSetInteger.
|
||||||
|
* NOTE: this enum includes most but not all encoder options. The image quality
|
||||||
|
* is a frame option that can be set with JxlEncoderOptionsSetDistance instead.
|
||||||
|
* Options that apply globally, rather than per-frame, are set with their own
|
||||||
|
* functions and do not use the per-frame JxlEncoderOptions.
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
/** Sets encoder effort/speed level without affecting decoding speed. Valid
|
||||||
|
* values are, from faster to slower speed: 1:lightning 2:thunder 3:falcon
|
||||||
|
* 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise.
|
||||||
|
* Default: squirrel (7).
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_EFFORT = 0,
|
||||||
|
|
||||||
|
/** Sets the decoding speed tier for the provided options. Minimum is 0
|
||||||
|
* (slowest to decode, best quality/density), and maximum is 4 (fastest to
|
||||||
|
* decode, at the cost of some quality/density). Default is 0.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_DECODING_SPEED = 1,
|
||||||
|
|
||||||
/** Sets resampling option. If enabled, the image is downsampled before
|
/** Sets resampling option. If enabled, the image is downsampled before
|
||||||
* compression, and upsampled to original size in the decoder. Integer option,
|
* compression, and upsampled to original size in the decoder. Integer option,
|
||||||
* use 0 for the default behavior (resampling only applied for low quality),
|
* use -1 for the default behavior (resampling only applied for low quality),
|
||||||
* 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for 4x4
|
* 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for 4x4
|
||||||
* downsampling, 8 for 8x8 downsampling. The default value is 0.
|
* downsampling, 8 for 8x8 downsampling.
|
||||||
*/
|
*/
|
||||||
JXL_ENC_OPTION_RESAMPLING = 0,
|
JXL_ENC_OPTION_RESAMPLING = 2,
|
||||||
|
|
||||||
/** Similar to JXL_ENC_OPTION_RESAMPLING, but for extra channels. Integer
|
/** Similar to JXL_ENC_OPTION_RESAMPLING, but for extra channels. Integer
|
||||||
* option, use 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for 4x4
|
* option, use -1 for the default behavior (depends on encoder
|
||||||
* downsampling, 8 for 8x8 downsampling. The default value is 1.
|
* implementation), 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for
|
||||||
|
* 4x4 downsampling, 8 for 8x8 downsampling.
|
||||||
*/
|
*/
|
||||||
JXL_ENC_OPTION_EXTRA_CHANNEL_RESAMPLING = 1,
|
JXL_ENC_OPTION_EXTRA_CHANNEL_RESAMPLING = 3,
|
||||||
|
|
||||||
/** Enables or disables noise generation. Integer option, use -1 for the
|
/** Adds noise to the image emulating photographic film noise, the higher the
|
||||||
* encoder default, 0 to disable, 1 to enable. The
|
* given number, the grainier the image will be. As an example, a value of 100
|
||||||
|
* gives low noise whereas a value of 3200 gives a lot of noise. The default
|
||||||
|
* value is 0.
|
||||||
*/
|
*/
|
||||||
JXL_ENC_OPTION_NOISE = 2,
|
JXL_ENC_OPTION_PHOTON_NOISE = 4,
|
||||||
|
|
||||||
/** Enables or disables dots generation. Integer option, use -1 for the
|
/** Enables adaptive noise generation. This setting is not recommended for
|
||||||
|
* use, please use JXL_ENC_OPTION_PHOTON_NOISE instead. Use -1 for the default
|
||||||
|
* (encoder chooses), 0 to disable, 1 to enable.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_NOISE = 5,
|
||||||
|
|
||||||
|
/** Enables or disables dots generation. Use -1 for the default (encoder
|
||||||
|
* chooses), 0 to disable, 1 to enable.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_DOTS = 6,
|
||||||
|
|
||||||
|
/** Enables or disables patches generation. Use -1 for the default (encoder
|
||||||
|
* chooses), 0 to disable, 1 to enable.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_PATCHES = 7,
|
||||||
|
|
||||||
|
/** Edge preserving filter level, -1 to 3. Use -1 for the default (encoder
|
||||||
|
* chooses), 0 to 3 to set a strength.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_EPF = 8,
|
||||||
|
|
||||||
|
/** Enables or disables the gaborish filter. Use -1 for the default (encoder
|
||||||
|
* chooses), 0 to disable, 1 to enable.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_GABORISH = 9,
|
||||||
|
|
||||||
|
/** Enables modular encoding. Use -1 for default (encoder
|
||||||
|
* chooses), 0 to enforce VarDCT mode (e.g. for photographic images), 1 to
|
||||||
|
* enforce modular mode (e.g. for lossless images).
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_MODULAR = 10,
|
||||||
|
|
||||||
|
/** Enables or disables preserving color of invisible pixels. Use -1 for the
|
||||||
|
* default (1 if lossless, 0 if lossy), 0 to disable, 1 to enable.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_KEEP_INVISIBLE = 11,
|
||||||
|
|
||||||
|
/** Determines the order in which 256x256 regions are stored in the codestream
|
||||||
|
* for progressive rendering. Use -1 for the encoder
|
||||||
|
* default, 0 for scanline order, 1 for center-first order.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_GROUP_ORDER = 12,
|
||||||
|
|
||||||
|
/** Determines the horizontal position of center for the center-first group
|
||||||
|
* order. Use -1 to automatically use the middle of the image, 0..xsize to
|
||||||
|
* specifically set it.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_GROUP_ORDER_CENTER_X = 13,
|
||||||
|
|
||||||
|
/** Determines the center for the center-first group order. Use -1 to
|
||||||
|
* automatically use the middle of the image, 0..ysize to specifically set it.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_GROUP_ORDER_CENTER_Y = 14,
|
||||||
|
|
||||||
|
/** Enables or disables progressive encoding for modular mode. Use -1 for the
|
||||||
* encoder default, 0 to disable, 1 to enable.
|
* encoder default, 0 to disable, 1 to enable.
|
||||||
*/
|
*/
|
||||||
JXL_ENC_OPTION_DOTS = 3,
|
JXL_ENC_OPTION_RESPONSIVE = 15,
|
||||||
|
|
||||||
/** Enables or disables patches generation. Integer option, use -1 for the
|
/** Set the progressive mode for the AC coefficients of VarDCT, using spectral
|
||||||
* encoder default, 0 to disable, 1 to enable.
|
* progression from the DCT coefficients. Use -1 for the encoder default, 0 to
|
||||||
|
* disable, 1 to enable.
|
||||||
*/
|
*/
|
||||||
JXL_ENC_OPTION_PATCHES = 4,
|
JXL_ENC_OPTION_PROGRESSIVE_AC = 16,
|
||||||
|
|
||||||
/** Enables or disables the gaborish filter. Integer option, use -1 for the
|
/** Set the progressive mode for the AC coefficients of VarDCT, using
|
||||||
* encoder default, 0 to disable, 1 to enable.
|
* quantization of the least significant bits. Use -1 for the encoder default,
|
||||||
|
* 0 to disable, 1 to enable.
|
||||||
*/
|
*/
|
||||||
JXL_ENC_OPTION_GABORISH = 5,
|
JXL_ENC_OPTION_QPROGRESSIVE_AC = 17,
|
||||||
|
|
||||||
|
/** Set the progressive mode using lower-resolution DC images for VarDCT. Use
|
||||||
|
* -1 for the encoder default, 0 to disable, 1 to have an extra 64x64 lower
|
||||||
|
* resolution pass, 2 to have a 512x512 and 64x64 lower resolution pass.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_PROGRESSIVE_DC = 18,
|
||||||
|
|
||||||
|
/** Use Global channel palette if the amount of colors is smaller than this
|
||||||
|
* percentage of range. Use 0-100 to set an explicit percentage, -1 to use the
|
||||||
|
* encoder default. Used for modular encoding.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_CHANNEL_COLORS_PRE_TRANSFORM_PERCENT = 19,
|
||||||
|
|
||||||
|
/** Use Local channel palette if the amount of colors is smaller than this
|
||||||
|
* percentage of range. Use 0-100 to set an explicit percentage, -1 to use the
|
||||||
|
* encoder default. Used for modular encoding.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_CHANNEL_COLORS_PERCENT = 20,
|
||||||
|
|
||||||
|
/** Use color palette if amount of colors is smaller than or equal to this
|
||||||
|
* amount, or -1 to use the encoder default. Used for modular encoding.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_PALETTE_COLORS = 21,
|
||||||
|
|
||||||
|
/** Enables or disables delta palette. Use -1 for the default (encoder
|
||||||
|
* chooses), 0 to disable, 1 to enable. Used in modular mode.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_LOSSY_PALETTE = 22,
|
||||||
|
|
||||||
|
/** Color space for modular encoding: 0=RGB, 1=YCoCg, 2-37=RCT, -1=default:
|
||||||
|
* try several, depending on speed.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_MODULAR_COLOR_SPACE = 23,
|
||||||
|
|
||||||
|
/** Group size for modular encoding: -1=default, 0=128, 1=256, 2=512, 3=1024.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_MODULAR_GROUP_SIZE = 24,
|
||||||
|
|
||||||
|
/** Predictor for modular encoding. -1 = default, 0=zero, 1=left, 2=top,
|
||||||
|
* 3=avg0, 4=select, 5=gradient, 6=weighted, 7=topright, 8=topleft,
|
||||||
|
* 9=leftleft, 10=avg1, 11=avg2, 12=avg3, 13=toptop predictive average 14=mix
|
||||||
|
* 5 and 6, 15=mix everything.
|
||||||
|
*/
|
||||||
|
JXL_ENC_OPTION_MODULAR_PREDICTOR = 25,
|
||||||
|
|
||||||
/** Enum value not to be used as an option. This value is added to force the
|
/** Enum value not to be used as an option. This value is added to force the
|
||||||
* C compiler to have the enum to take a known size.
|
* C compiler to have the enum to take a known size.
|
||||||
|
@ -214,12 +336,21 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddJPEGFrame(
|
||||||
* Sets the buffer to read pixels from for the next image to encode. Must call
|
* Sets the buffer to read pixels from for the next image to encode. Must call
|
||||||
* JxlEncoderSetBasicInfo before JxlEncoderAddImageFrame.
|
* JxlEncoderSetBasicInfo before JxlEncoderAddImageFrame.
|
||||||
*
|
*
|
||||||
* Currently only some pixel formats are supported:
|
* Currently only some data types for pixel formats are supported:
|
||||||
* - JXL_TYPE_UINT8
|
* - JXL_TYPE_UINT8
|
||||||
* - JXL_TYPE_UINT16
|
* - JXL_TYPE_UINT16
|
||||||
* - JXL_TYPE_FLOAT16, with nominal range 0..1
|
* - JXL_TYPE_FLOAT16, with nominal range 0..1
|
||||||
* - JXL_TYPE_FLOAT, with nominal range 0..1
|
* - JXL_TYPE_FLOAT, with nominal range 0..1
|
||||||
*
|
*
|
||||||
|
* We support interleaved channels as described by the JxlPixelFormat:
|
||||||
|
* - single-channel data, e.g. grayscale
|
||||||
|
* - single-channel + alpha
|
||||||
|
* - trichromatic, e.g. RGB
|
||||||
|
* - trichromatic + alpha
|
||||||
|
*
|
||||||
|
* Extra channels not handled here need to be set by @ref
|
||||||
|
* JxlEncoderSetExtraChannelBuffer.
|
||||||
|
*
|
||||||
* The color profile of the pixels depends on the value of uses_original_profile
|
* The color profile of the pixels depends on the value of uses_original_profile
|
||||||
* in the JxlBasicInfo. If true, the pixels are assumed to be encoded in the
|
* in the JxlBasicInfo. If true, the pixels are assumed to be encoded in the
|
||||||
* original profile that is set with JxlEncoderSetColorEncoding or
|
* original profile that is set with JxlEncoderSetColorEncoding or
|
||||||
|
@ -240,7 +371,152 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddImageFrame(
|
||||||
const void* buffer, size_t size);
|
const void* buffer, size_t size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declares that this encoder will not encode anything further.
|
* 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
|
||||||
|
* JxlEncoderSetExtraChannelBuffer.
|
||||||
|
*
|
||||||
|
* TODO(firsching): mention what data types in pixel formats are supported.
|
||||||
|
*
|
||||||
|
* It is required to call this function for every extra channel, except for the
|
||||||
|
* alpha channel if that was already set through @ref JxlEncoderAddImageFrame.
|
||||||
|
*
|
||||||
|
* @param options set of encoder options to use when encoding the extra channel.
|
||||||
|
* @param pixel_format format for pixels. Object owned by the caller and its
|
||||||
|
* contents are copied internally. The num_channels value is ignored, since the
|
||||||
|
* number of channels for an extra channel is always assumed to be one.
|
||||||
|
* @param buffer buffer type to input the pixel data from. Owned by the caller
|
||||||
|
* and its contents are copied internally.
|
||||||
|
* @param size size of buffer in bytes.
|
||||||
|
* @param index index of the extra channel to use.
|
||||||
|
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer(
|
||||||
|
const JxlEncoderOptions* options, 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 JxlEncoderUseContainer must
|
||||||
|
* be enabled before using this function.
|
||||||
|
*
|
||||||
|
* Background information about the container format and boxes follows here:
|
||||||
|
*
|
||||||
|
* For users of libjxl, boxes allow inserting application-specific data and
|
||||||
|
* metadata (Exif, XML, JUMBF and user defined boxes).
|
||||||
|
*
|
||||||
|
* The box format follows ISO BMFF and shares features and box types with other
|
||||||
|
* image and video formats, including the Exif, XML and JUMBF boxes. The box
|
||||||
|
* format for JPEG XL is specified in ISO/IEC 18181-2.
|
||||||
|
*
|
||||||
|
* Boxes in general don't contain other boxes inside, except a JUMBF superbox.
|
||||||
|
* Boxes follow each other sequentially and are byte-aligned. If the container
|
||||||
|
* format is used, the JXL stream exists out of 3 or more concatenated boxes.
|
||||||
|
* It is also possible to use a direct codestream without boxes, but in that
|
||||||
|
* case metadata cannot be added.
|
||||||
|
*
|
||||||
|
* Each box generally has the following byte structure in the file:
|
||||||
|
* - 4 bytes: box size including box header (Big endian. If set to 0, an
|
||||||
|
* 8-byte 64-bit size follows instead).
|
||||||
|
* - 4 bytes: type, e.g. "JXL " for the signature box, "jxlc" for a codestream
|
||||||
|
* box.
|
||||||
|
* - N bytes: box contents.
|
||||||
|
*
|
||||||
|
* Only the box contents are provided to the contents argument of this function,
|
||||||
|
* the encoder encodes the size header itself.
|
||||||
|
*
|
||||||
|
* Box types are given by 4 characters. A list of known types follows:
|
||||||
|
* - "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
|
||||||
|
* - "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
|
||||||
|
* profile, and all the pixel data of all the image frames.
|
||||||
|
* - "jxlp": a codestream box in case it is split across multiple boxes. The
|
||||||
|
* encoder will automatically do this if necessary. The contents are the same
|
||||||
|
* as in case of a jxlc box, when concatenated.
|
||||||
|
* - "Exif": a box with EXIF metadata, can be added by libjxl users, or is
|
||||||
|
* automatically added when needed for JPEG reconstruction. The contents of
|
||||||
|
* this box must be prepended by a 4-byte tiff header offset, which may
|
||||||
|
* be 4 zero bytes.
|
||||||
|
* - "XML ": a box with XMP or IPTC metadata, can be added by libjxl users, or
|
||||||
|
* is automatically added when needed for JPEG reconstruction
|
||||||
|
* - "jumb": a JUMBF superbox, which can contain boxes with different types of
|
||||||
|
* metadata inside. This box type can be added by the encoder transparently,
|
||||||
|
* and other libraries to create and handle JUMBF content exist.
|
||||||
|
* - "brob": a Brotli-compressed box, which otherwise represents an existing
|
||||||
|
* type of box such as Exif or XML. The encoder creates these when enabled and
|
||||||
|
* users of libjxl don't need to create them directly. Some box types are not
|
||||||
|
* allowed to be compressed: any of the signature, jxl* and jbrd boxes.
|
||||||
|
* - "jxli": frame index box, can list the keyframes in case of a JXL animation,
|
||||||
|
* allowing the decoder to jump to individual frames more efficiently. This
|
||||||
|
* box type is specified, but not currently supported by the encoder or
|
||||||
|
* decoder.
|
||||||
|
* - "jbrd": JPEG reconstruction box, contains the information required to
|
||||||
|
* byte-for-byte losslessly recontruct a JPEG-1 image. The JPEG coefficients
|
||||||
|
* (pixel content) themselves are encoded in the JXL codestream (jxlc or jxlp)
|
||||||
|
* itself. Exif and XMP metadata will be encoded in Exif and XMP boxes. The
|
||||||
|
* jbrd box itself contains information such as the app markers of the JPEG-1
|
||||||
|
* file and everything else required to fit the information together into the
|
||||||
|
* exact original JPEG file. This box is added automatically by the encoder
|
||||||
|
* when needed, and only when JPEG reconstruction is used.
|
||||||
|
* - other: other application-specific boxes can be added. Their typename should
|
||||||
|
* not begin with "jxl" or "JXL" or conflict with other existing typenames.
|
||||||
|
*
|
||||||
|
* Most boxes are automatically added by the encoder and should not be added
|
||||||
|
* with JxlEncoderAddBox. Boxes that one may wish to add with JxlEncoderAddBox
|
||||||
|
* are: Exif and XML (but not when using JPEG reconstruction since if the
|
||||||
|
* JPEG has those, these boxes are already added automatically), jumb, and
|
||||||
|
* application-specific boxes.
|
||||||
|
*
|
||||||
|
* Adding metadata boxes increases the filesize. When adding Exif metadata, the
|
||||||
|
* data must be in sync with what is encoded in the JPEG XL codestream,
|
||||||
|
* specifically the image orientation. While this is not recommended in
|
||||||
|
* practice, in case of conflicting metadata, the JPEG XL codestream takes
|
||||||
|
* precedence.
|
||||||
|
*
|
||||||
|
* It is possible to create a codestream without boxes, then what would be in
|
||||||
|
* the jxlc box is written directly to the output
|
||||||
|
*
|
||||||
|
* It is possible to split the codestream across multiple boxes, in that case
|
||||||
|
* multiple boxes of type jxlp are used. This is handled by the encoder when
|
||||||
|
* needed.
|
||||||
|
*
|
||||||
|
* For now metadata boxes can only be added before or after the codestream with
|
||||||
|
* all frames, so using JxlEncoderAddBox is only possible before the first
|
||||||
|
* JxlEncoderAddImageFrame call, and/or after the last JxlEncoderAddImageFrame
|
||||||
|
* call and JxlEncoderCloseInput. Support for adding boxes in-between the
|
||||||
|
* codestream, and/or in-between image frames may be added later, and would
|
||||||
|
* cause the encoder to use jxlp boxes for the codestream.
|
||||||
|
*
|
||||||
|
* @param enc encoder object.
|
||||||
|
* @param type the box type, e.g. "Exif" for EXIF metadata, "XML " for XMP or
|
||||||
|
* IPTC metadata, "jumb" for JUMBF metadata.
|
||||||
|
* @param contents the full contents of the box, for example EXIF
|
||||||
|
* data. For an "Exif" box, the EXIF data must be prepended by a 4-byte tiff
|
||||||
|
* header offset, which may be 4 zero-bytes. The ISO BMFF box header must not
|
||||||
|
* be included, only the contents.
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JxlEncoderStatus JxlEncoderAddBox(JxlEncoder* enc, JxlBoxType type,
|
||||||
|
const uint8_t* contents,
|
||||||
|
size_t size,
|
||||||
|
JXL_BOOL compress_box);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declares that this encoder will not encode any further frames. Further
|
||||||
|
* metadata boxes may still be added.
|
||||||
*
|
*
|
||||||
* Must be called between JxlEncoderAddImageFrame/JPEGFrame of the last frame
|
* Must be called between JxlEncoderAddImageFrame/JPEGFrame of the last frame
|
||||||
* and the next call to JxlEncoderProcessOutput, or JxlEncoderProcessOutput
|
* and the next call to JxlEncoderProcessOutput, or JxlEncoderProcessOutput
|
||||||
|
@ -295,6 +571,10 @@ JXL_EXPORT void JxlEncoderInitBasicInfo(JxlBasicInfo* info);
|
||||||
/**
|
/**
|
||||||
* Sets the global metadata of the image encoded by this encoder.
|
* 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.
|
||||||
|
*
|
||||||
* @param enc encoder object.
|
* @param enc encoder object.
|
||||||
* @param info global image metadata. Object owned by the caller and its
|
* @param info global image metadata. Object owned by the caller and its
|
||||||
* contents are copied internally.
|
* contents are copied internally.
|
||||||
|
@ -305,103 +585,50 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
|
||||||
const JxlBasicInfo* info);
|
const JxlBasicInfo* info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the encoder to store JPEG reconstruction metadata in the JPEG XL
|
* Initializes a JxlExtraChannelInfo struct to default values.
|
||||||
* container.
|
* 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.
|
||||||
*
|
*
|
||||||
* The encoder must be configured to use the JPEG XL container format using @ref
|
* @param type type of the extra channel.
|
||||||
* JxlEncoderUseContainer for this to have any effect.
|
* @param info global extra channel metadata. Object owned by the caller and its
|
||||||
*
|
* contents are copied internally.
|
||||||
* If this is set to true and a single JPEG frame is added, it will be
|
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
|
||||||
* possible to losslessly reconstruct the JPEG codestream.
|
|
||||||
*
|
|
||||||
* @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.
|
|
||||||
*/
|
*/
|
||||||
JXL_EXPORT JxlEncoderStatus
|
JXL_EXPORT void JxlEncoderInitExtraChannelInfo(JxlExtraChannelType type,
|
||||||
JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata);
|
JxlExtraChannelInfo* info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configure the encoder to use the JPEG XL container format.
|
* Sets information for the extra channel at the given index. The index
|
||||||
|
* must be smaller than num_extra_channels in the associated JxlBasicInfo.
|
||||||
*
|
*
|
||||||
* Using the JPEG XL container format allows to store metadata such as JPEG
|
* @param enc encoder object
|
||||||
* reconstruction (@ref JxlEncoderStoreJPEGMetadata) or other metadata like
|
* @param index index of the extra channel to set.
|
||||||
* EXIF; but it adds a few bytes to the encoded file for container headers even
|
* @param info global extra channel metadata. Object owned by the caller and its
|
||||||
* if there is no extra metadata.
|
* contents are copied internally.
|
||||||
*
|
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
|
||||||
* @param enc encoder object.
|
|
||||||
* @param use_container true if the encoder should output the JPEG XL container
|
|
||||||
* format.
|
|
||||||
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
|
||||||
* otherwise.
|
|
||||||
*/
|
*/
|
||||||
JXL_EXPORT JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc,
|
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelInfo(
|
||||||
JXL_BOOL use_container);
|
JxlEncoder* enc, size_t index, const JxlExtraChannelInfo* info);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets lossless/lossy mode for the provided options. Default is lossy.
|
* 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.
|
||||||
*
|
*
|
||||||
* @param options set of encoder options to update with the new mode
|
* @param enc encoder object
|
||||||
* @param lossless whether the options should be lossless
|
* @param index index of the extra channel to set.
|
||||||
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
* @param name buffer with the name of the extra channel.
|
||||||
* otherwise.
|
* @param size size of the name buffer in bytes.
|
||||||
|
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
|
||||||
*/
|
*/
|
||||||
JXL_EXPORT JxlEncoderStatus
|
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelName(JxlEncoder* enc,
|
||||||
JxlEncoderOptionsSetLossless(JxlEncoderOptions* options, JXL_BOOL lossless);
|
size_t index,
|
||||||
|
const char* name,
|
||||||
|
size_t size);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the decoding speed tier for the provided options. Minimum is 0 (highest
|
* Sets a frame-specific option of integer type to the encoder options.
|
||||||
* quality), and maximum is 4 (lowest quality). Default is 0.
|
* The JxlEncoderOptionId argument determines which option is set.
|
||||||
*
|
|
||||||
* @param options set of encoder options to update with the new decoding speed
|
|
||||||
* tier.
|
|
||||||
* @param tier the decoding speed tier to set.
|
|
||||||
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
|
||||||
* otherwise.
|
|
||||||
*/
|
|
||||||
JXL_EXPORT JxlEncoderStatus
|
|
||||||
JxlEncoderOptionsSetDecodingSpeed(JxlEncoderOptions* options, int tier);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets encoder effort/speed level without affecting decoding speed. Valid
|
|
||||||
* values are, from faster to slower speed: 1:lightning 2:thunder 3:falcon
|
|
||||||
* 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise.
|
|
||||||
* Default: squirrel (7).
|
|
||||||
*
|
|
||||||
* @param options set of encoder options to update with the new mode.
|
|
||||||
* @param effort the effort value to set.
|
|
||||||
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
|
||||||
* otherwise.
|
|
||||||
*/
|
|
||||||
JXL_EXPORT JxlEncoderStatus
|
|
||||||
JxlEncoderOptionsSetEffort(JxlEncoderOptions* options, int effort);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the distance level for lossy compression: target max butteraugli
|
|
||||||
* distance, lower = higher quality. Range: 0 .. 15.
|
|
||||||
* 0.0 = mathematically lossless (however, use JxlEncoderOptionsSetLossless to
|
|
||||||
* use true lossless).
|
|
||||||
* 1.0 = visually lossless.
|
|
||||||
* Recommended range: 0.5 .. 3.0.
|
|
||||||
* Default value: 1.0.
|
|
||||||
* If JxlEncoderOptionsSetLossless is used, this value is unused and implied
|
|
||||||
* to be 0.
|
|
||||||
*
|
|
||||||
* @param options set of encoder options to update with the new mode.
|
|
||||||
* @param distance the distance value to set.
|
|
||||||
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
|
||||||
* otherwise.
|
|
||||||
*/
|
|
||||||
JXL_EXPORT JxlEncoderStatus
|
|
||||||
JxlEncoderOptionsSetDistance(JxlEncoderOptions* options, float distance);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets an option of integer type to the encoder option. The JxlEncoderOptionId
|
|
||||||
* argument determines which option is set.
|
|
||||||
*
|
|
||||||
* TODO(lode): also use the option enum for the container, lossless, speed,
|
|
||||||
* effort and distance options.
|
|
||||||
*
|
*
|
||||||
* @param options set of encoder options to update with the new mode.
|
* @param options set of encoder options to update with the new mode.
|
||||||
* @param option ID of the option to set.
|
* @param option ID of the option to set.
|
||||||
|
@ -412,9 +639,139 @@ JxlEncoderOptionsSetDistance(JxlEncoderOptions* options, float distance);
|
||||||
* JxlEncoderOptions object is still valid and is the same as before this
|
* JxlEncoderOptions object is still valid and is the same as before this
|
||||||
* function was called.
|
* function was called.
|
||||||
*/
|
*/
|
||||||
JXL_EXPORT JxlEncoderStatus JxlEncoderOptionsSetAsInteger(
|
JXL_EXPORT JxlEncoderStatus JxlEncoderOptionsSetInteger(
|
||||||
JxlEncoderOptions* options, JxlEncoderOptionId option, int32_t value);
|
JxlEncoderOptions* options, JxlEncoderOptionId option, int32_t value);
|
||||||
|
|
||||||
|
/** Indicates the encoder should use the box-based JPEG XL container format
|
||||||
|
* (BMFF) instead of outputting the codestream bytes directly. Both with and
|
||||||
|
* without container are valid JPEG XL files, but the container is necessary
|
||||||
|
* when metadata, level 10 features or JPEG reconstruction is used.
|
||||||
|
*
|
||||||
|
* If enabled, the encoder always uses the container format, even if not
|
||||||
|
* necessary. If disabled, the encoder will still use the container format if
|
||||||
|
* required (such as for JPEG metadata @ref JxlEncoderStoreJPEGMetadata).
|
||||||
|
*
|
||||||
|
* This setting must be explicitely enabled before using @ref JxlEncoderAddBox.
|
||||||
|
*
|
||||||
|
* By default this setting is disabled.
|
||||||
|
*
|
||||||
|
* This setting can only be set at the beginning, before encoding starts.
|
||||||
|
*
|
||||||
|
* @param enc encoder object.
|
||||||
|
* @param use_container true if the encoder should always output the JPEG XL
|
||||||
|
* container format.
|
||||||
|
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc,
|
||||||
|
JXL_BOOL use_container);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure the encoder to store JPEG reconstruction metadata in the JPEG XL
|
||||||
|
* container.
|
||||||
|
*
|
||||||
|
* If this is set to true and a single JPEG frame is added, it will be
|
||||||
|
* possible to losslessly reconstruct the JPEG codestream.
|
||||||
|
*
|
||||||
|
* This setting can only be set at the beginning, before encoding starts.
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JxlEncoderStatus
|
||||||
|
JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata);
|
||||||
|
|
||||||
|
/** Sets the feature level of the JPEG XL codestream. Valid values are 5 and
|
||||||
|
* 10.
|
||||||
|
*
|
||||||
|
* Level 5: for end-user image delivery, this level is the most widely
|
||||||
|
* supported level by image decoders and the recommended level to use unless a
|
||||||
|
* level 10 feature is absolutely necessary. Supports a maximum resolution
|
||||||
|
* 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
|
||||||
|
* 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
|
||||||
|
* 5 limitations, allows CMYK color and up to 32 bits per color channel, but
|
||||||
|
* may be less widely supported.
|
||||||
|
*
|
||||||
|
* The default value is 5. To use level 10 features, the setting must be
|
||||||
|
* explicitly set to 10, the encoder will not automatically enable it. If
|
||||||
|
* incompatible parameters such as too high image resolution for the current
|
||||||
|
* level are set, the encoder will return an error. For internal coding tools,
|
||||||
|
* the encoder will only use those compatible with the level setting.
|
||||||
|
*
|
||||||
|
* This setting can only be set at the beginning, before encoding starts.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JxlEncoderStatus JxlEncoderSetCodestreamLevel(JxlEncoder* enc,
|
||||||
|
int level);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enables lossless encoding.
|
||||||
|
*
|
||||||
|
* This is not an option like the others on itself, but rather while enabled it
|
||||||
|
* overrides a set of existing options (such as distance and modular mode) that
|
||||||
|
* enables bit-for-bit lossless encoding.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param options set of encoder options to update with the new mode
|
||||||
|
* @param lossless whether to override options for lossless mode
|
||||||
|
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JxlEncoderStatus
|
||||||
|
JxlEncoderOptionsSetLossless(JxlEncoderOptions* options, JXL_BOOL lossless);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options set of encoder options to update with the new mode.
|
||||||
|
* @param effort the effort value to set.
|
||||||
|
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* DEPRECATED: use JxlEncoderOptionsSetInteger(options, JXL_ENC_OPTION_EFFORT,
|
||||||
|
* effort)) instead.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JXL_DEPRECATED JxlEncoderStatus
|
||||||
|
JxlEncoderOptionsSetEffort(JxlEncoderOptions* options, int effort);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param options set of encoder options to update with the new decoding speed
|
||||||
|
* tier.
|
||||||
|
* @param tier the decoding speed tier to set.
|
||||||
|
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
||||||
|
* otherwise.
|
||||||
|
*
|
||||||
|
* DEPRECATED: use JxlEncoderOptionsSetInteger(options,
|
||||||
|
* JXL_ENC_OPTION_DECODING_SPEED, tier)) instead.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JXL_DEPRECATED JxlEncoderStatus
|
||||||
|
JxlEncoderOptionsSetDecodingSpeed(JxlEncoderOptions* options, int tier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the distance level for lossy compression: target max butteraugli
|
||||||
|
* distance, lower = higher quality. Range: 0 .. 15.
|
||||||
|
* 0.0 = mathematically lossless (however, use JxlEncoderOptionsSetLossless
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param options set of encoder options to update with the new mode.
|
||||||
|
* @param distance the distance value to set.
|
||||||
|
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JxlEncoderStatus
|
||||||
|
JxlEncoderOptionsSetDistance(JxlEncoderOptions* options, float distance);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new set of encoder options, with all values initially copied from
|
* Create a new set of encoder options, with all values initially copied from
|
||||||
* the @p source options, or set to default if @p source is NULL.
|
* the @p source options, or set to default if @p source is NULL.
|
||||||
|
|
|
@ -27,8 +27,8 @@ extern "C" {
|
||||||
*
|
*
|
||||||
* @param opaque custom memory manager handle provided by the caller.
|
* @param opaque custom memory manager handle provided by the caller.
|
||||||
* @param size in bytes of the requested memory region.
|
* @param size in bytes of the requested memory region.
|
||||||
* @returns @c NULL if the memory can not be allocated,
|
* @return @c NULL if the memory can not be allocated,
|
||||||
* @returns pointer to the memory otherwise.
|
* @return pointer to the memory otherwise.
|
||||||
*/
|
*/
|
||||||
typedef void* (*jpegxl_alloc_func)(void* opaque, size_t size);
|
typedef void* (*jpegxl_alloc_func)(void* opaque, size_t size);
|
||||||
|
|
||||||
|
|
|
@ -70,8 +70,8 @@ typedef int JxlParallelRetCode;
|
||||||
* JxlParallelRunner() must be passed here.
|
* JxlParallelRunner() must be passed here.
|
||||||
* @param num_threads the maximum number of threads. This value must be
|
* @param num_threads the maximum number of threads. This value must be
|
||||||
* positive.
|
* positive.
|
||||||
* @returns 0 if the initialization process was successful.
|
* @return 0 if the initialization process was successful.
|
||||||
* @returns an error code if there was an error, which should be returned by
|
* @return an error code if there was an error, which should be returned by
|
||||||
* JxlParallelRunner().
|
* JxlParallelRunner().
|
||||||
*/
|
*/
|
||||||
typedef JxlParallelRetCode (*JxlParallelRunInit)(void* jpegxl_opaque,
|
typedef JxlParallelRetCode (*JxlParallelRunInit)(void* jpegxl_opaque,
|
||||||
|
@ -110,9 +110,9 @@ typedef void (*JxlParallelRunFunction)(void* jpegxl_opaque, uint32_t value,
|
||||||
* or encoding instance may call the provided JxlParallelRunner multiple
|
* or encoding instance may call the provided JxlParallelRunner multiple
|
||||||
* times for different parts of the decoding or encoding process.
|
* times for different parts of the decoding or encoding process.
|
||||||
*
|
*
|
||||||
* @returns 0 if the @p init call succeeded (returned 0) and no other error
|
* @return 0 if the @p init call succeeded (returned 0) and no other error
|
||||||
* occurred in the runner code.
|
* occurred in the runner code.
|
||||||
* @returns JXL_PARALLEL_RET_RUNNER_ERROR if an error occurred in the runner
|
* @return JXL_PARALLEL_RET_RUNNER_ERROR if an error occurred in the runner
|
||||||
* code, for example, setting up the threads.
|
* code, for example, setting up the threads.
|
||||||
* @return the return value of @p init() if non-zero.
|
* @return the return value of @p init() if non-zero.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -35,6 +35,7 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
||||||
jxl/base/override.h
|
jxl/base/override.h
|
||||||
jxl/base/padded_bytes.cc
|
jxl/base/padded_bytes.cc
|
||||||
jxl/base/padded_bytes.h
|
jxl/base/padded_bytes.h
|
||||||
|
jxl/base/printf_macros.h
|
||||||
jxl/base/profiler.h
|
jxl/base/profiler.h
|
||||||
jxl/base/random.cc
|
jxl/base/random.cc
|
||||||
jxl/base/random.h
|
jxl/base/random.h
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include <numeric> // accumulate
|
#include <numeric> // accumulate
|
||||||
|
|
||||||
#include "lib/jxl/aux_out_fwd.h"
|
#include "lib/jxl/aux_out_fwd.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/enc_bit_writer.h"
|
#include "lib/jxl/enc_bit_writer.h"
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
// Optional output information for debugging and analyzing size usage.
|
// Optional output information for debugging and analyzing size usage.
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -103,7 +104,7 @@ static inline const char* LayerName(size_t layer) {
|
||||||
case kLayerExtraChannels:
|
case kLayerExtraChannels:
|
||||||
return "extra channels";
|
return "extra channels";
|
||||||
default:
|
default:
|
||||||
JXL_ABORT("Invalid layer %" PRIuS "\n", layer);
|
JXL_ABORT("Invalid layer %d\n", static_cast<int>(layer));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,12 +120,13 @@ struct AuxOut {
|
||||||
clustered_entropy += victim.clustered_entropy;
|
clustered_entropy += victim.clustered_entropy;
|
||||||
}
|
}
|
||||||
void Print(size_t num_inputs) const {
|
void Print(size_t num_inputs) const {
|
||||||
printf("%10" PRIdS, total_bits);
|
printf("%10" PRId64, static_cast<int64_t>(total_bits));
|
||||||
if (histogram_bits != 0) {
|
if (histogram_bits != 0) {
|
||||||
printf(" [c/i:%6.2f | hst:%8" PRIdS " | ex:%8" PRIdS
|
printf(" [c/i:%6.2f | hst:%8" PRId64 " | ex:%8" PRId64
|
||||||
" | h+c+e:%12.3f",
|
" | h+c+e:%12.3f",
|
||||||
num_clustered_histograms * 1.0 / num_inputs, histogram_bits >> 3,
|
num_clustered_histograms * 1.0 / num_inputs,
|
||||||
extra_bits >> 3,
|
static_cast<int64_t>(histogram_bits >> 3),
|
||||||
|
static_cast<int64_t>(extra_bits >> 3),
|
||||||
(histogram_bits + clustered_entropy + extra_bits) / 8.0);
|
(histogram_bits + clustered_entropy + extra_bits) / 8.0);
|
||||||
printf("]");
|
printf("]");
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,8 +20,8 @@
|
||||||
#include <hwy/base.h> // kMaxVectorSize
|
#include <hwy/base.h> // kMaxVectorSize
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/common.h"
|
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
|
@ -55,6 +55,8 @@
|
||||||
#define JXL_NORETURN __declspec(noreturn)
|
#define JXL_NORETURN __declspec(noreturn)
|
||||||
#elif JXL_COMPILER_GCC || JXL_COMPILER_CLANG
|
#elif JXL_COMPILER_GCC || JXL_COMPILER_CLANG
|
||||||
#define JXL_NORETURN __attribute__((noreturn))
|
#define JXL_NORETURN __attribute__((noreturn))
|
||||||
|
#else
|
||||||
|
#define JXL_NORETURN
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if JXL_COMPILER_MSVC
|
#if JXL_COMPILER_MSVC
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef LIB_JXL_BASE_PRINTF_MACROS_H_
|
||||||
|
#define LIB_JXL_BASE_PRINTF_MACROS_H_
|
||||||
|
|
||||||
|
// Format string macros. These should be included after any other system
|
||||||
|
// library since those may unconditionally define these, depending on the
|
||||||
|
// platform.
|
||||||
|
|
||||||
|
// PRIuS and PRIdS macros to print size_t and ssize_t respectively.
|
||||||
|
#if !defined(PRIdS)
|
||||||
|
#if defined(_WIN64)
|
||||||
|
#define PRIdS "lld"
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#define PRIdS "d"
|
||||||
|
#else
|
||||||
|
#define PRIdS "zd"
|
||||||
|
#endif
|
||||||
|
#endif // PRIdS
|
||||||
|
|
||||||
|
#if !defined(PRIuS)
|
||||||
|
#if defined(_WIN64)
|
||||||
|
#define PRIuS "llu"
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
#define PRIuS "u"
|
||||||
|
#else
|
||||||
|
#define PRIuS "zu"
|
||||||
|
#endif
|
||||||
|
#endif // PRIuS
|
||||||
|
|
||||||
|
#endif // LIB_JXL_BASE_PRINTF_MACROS_H_
|
|
@ -6,6 +6,7 @@
|
||||||
#include "lib/jxl/blending.h"
|
#include "lib/jxl/blending.h"
|
||||||
|
|
||||||
#include "lib/jxl/alpha.h"
|
#include "lib/jxl/alpha.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/image_ops.h"
|
#include "lib/jxl/image_ops.h"
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
|
@ -83,7 +83,6 @@ JxlDecoderStatus JxlBoxContentDecoder::Process(const uint8_t* next_in,
|
||||||
size_t can_read = avail_in;
|
size_t can_read = avail_in;
|
||||||
if (!box_until_eof_) can_read = std::min<size_t>(can_read, remaining_);
|
if (!box_until_eof_) can_read = std::min<size_t>(can_read, remaining_);
|
||||||
size_t to_write = std::min<size_t>(can_read, *avail_out);
|
size_t to_write = std::min<size_t>(can_read, *avail_out);
|
||||||
|
|
||||||
memcpy(*next_out, next_in, to_write);
|
memcpy(*next_out, next_in, to_write);
|
||||||
|
|
||||||
*next_out += to_write;
|
*next_out += to_write;
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#define HWY_TARGET_INCLUDE "lib/jxl/butteraugli/butteraugli.cc"
|
#define HWY_TARGET_INCLUDE "lib/jxl/butteraugli/butteraugli.cc"
|
||||||
#include <hwy/foreach_target.h>
|
#include <hwy/foreach_target.h>
|
||||||
|
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/profiler.h"
|
#include "lib/jxl/base/profiler.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/convolve.h"
|
#include "lib/jxl/convolve.h"
|
||||||
|
@ -1136,7 +1137,7 @@ void Mask(const ImageF& mask0, const ImageF& mask1,
|
||||||
FuzzyErosion(blurred1, &diff1);
|
FuzzyErosion(blurred1, &diff1);
|
||||||
for (size_t y = 0; y < ysize; ++y) {
|
for (size_t y = 0; y < ysize; ++y) {
|
||||||
for (size_t x = 0; x < xsize; ++x) {
|
for (size_t x = 0; x < xsize; ++x) {
|
||||||
mask->Row(y)[x] = diff1.Row(y)[x];
|
mask->Row(y)[x] = diff0.Row(y)[x];
|
||||||
if (diff_ac != nullptr) {
|
if (diff_ac != nullptr) {
|
||||||
static const float kMaskToErrorMul = 10.0;
|
static const float kMaskToErrorMul = 10.0;
|
||||||
float diff = blurred0.Row(y)[x] - blurred1.Row(y)[x];
|
float diff = blurred0.Row(y)[x] - blurred1.Row(y)[x];
|
||||||
|
@ -1796,9 +1797,9 @@ bool ButteraugliDiffmap(const Image3F& rgb0, const Image3F& rgb1,
|
||||||
for (size_t y = 0; y < yscaled; ++y) {
|
for (size_t y = 0; y < yscaled; ++y) {
|
||||||
for (size_t x = 0; x < xscaled; ++x) {
|
for (size_t x = 0; x < xscaled; ++x) {
|
||||||
size_t x2 =
|
size_t x2 =
|
||||||
std::min<size_t>(xsize - 1, std::max<size_t>(0, x - xborder));
|
std::min<size_t>(xsize - 1, x > xborder ? x - xborder : 0);
|
||||||
size_t y2 =
|
size_t y2 =
|
||||||
std::min<size_t>(ysize - 1, std::max<size_t>(0, y - yborder));
|
std::min<size_t>(ysize - 1, y > yborder ? y - yborder : 0);
|
||||||
scaled0.PlaneRow(i, y)[x] = rgb0.PlaneRow(i, y2)[x2];
|
scaled0.PlaneRow(i, y)[x] = rgb0.PlaneRow(i, y2)[x2];
|
||||||
scaled1.PlaneRow(i, y)[x] = rgb1.PlaneRow(i, y2)[x2];
|
scaled1.PlaneRow(i, y)[x] = rgb1.PlaneRow(i, y2)[x2];
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/random.h"
|
#include "lib/jxl/base/random.h"
|
||||||
#include "lib/jxl/base/span.h"
|
#include "lib/jxl/base/span.h"
|
||||||
#include "lib/jxl/coeff_order_fwd.h"
|
#include "lib/jxl/coeff_order_fwd.h"
|
||||||
|
|
|
@ -27,27 +27,6 @@
|
||||||
#define JPEGXL_ENABLE_TRANSCODE_JPEG 1
|
#define JPEGXL_ENABLE_TRANSCODE_JPEG 1
|
||||||
#endif // JPEGXL_ENABLE_TRANSCODE_JPEG
|
#endif // JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||||
|
|
||||||
// PRIuS and PRIdS macros to print size_t and ssize_t respectively.
|
|
||||||
#if !defined(PRIdS)
|
|
||||||
#if defined(_WIN64)
|
|
||||||
#define PRIdS "lld"
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#define PRIdS "d"
|
|
||||||
#else
|
|
||||||
#define PRIdS "zd"
|
|
||||||
#endif
|
|
||||||
#endif // PRIdS
|
|
||||||
|
|
||||||
#if !defined(PRIuS)
|
|
||||||
#if defined(_WIN64)
|
|
||||||
#define PRIuS "llu"
|
|
||||||
#elif defined(_WIN32)
|
|
||||||
#define PRIuS "u"
|
|
||||||
#else
|
|
||||||
#define PRIuS "zu"
|
|
||||||
#endif
|
|
||||||
#endif // PRIuS
|
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
// Some enums and typedefs used by more than one header file.
|
// Some enums and typedefs used by more than one header file.
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/data_parallel.h"
|
#include "lib/jxl/base/data_parallel.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/thread_pool_internal.h"
|
#include "lib/jxl/base/thread_pool_internal.h"
|
||||||
#include "lib/jxl/image_ops.h"
|
#include "lib/jxl/image_ops.h"
|
||||||
#include "lib/jxl/image_test_utils.h"
|
#include "lib/jxl/image_test_utils.h"
|
||||||
|
|
|
@ -273,42 +273,8 @@ struct IDCT1D<N, M, typename std::enable_if<(M > MaxLanes(FV<0>()))>::type> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Computes the in-place NxN transposed-scaled-DCT (tsDCT) of block.
|
// Computes the maybe-transposed, scaled DCT of a block, that needs to be
|
||||||
// Requires that block is HWY_ALIGN'ed.
|
// HWY_ALIGN'ed.
|
||||||
//
|
|
||||||
// See also DCTSlow, ComputeDCT
|
|
||||||
template <size_t N>
|
|
||||||
struct ComputeTransposedScaledDCT {
|
|
||||||
// scratch_space must be aligned, and should have space for N*N floats.
|
|
||||||
template <class From>
|
|
||||||
HWY_MAYBE_UNUSED void operator()(const From& from, float* JXL_RESTRICT to,
|
|
||||||
float* JXL_RESTRICT scratch_space) {
|
|
||||||
float* JXL_RESTRICT block = scratch_space;
|
|
||||||
DCT1D<N, N>()(from, DCTTo(to, N));
|
|
||||||
Transpose<N, N>::Run(DCTFrom(to, N), DCTTo(block, N));
|
|
||||||
DCT1D<N, N>()(DCTFrom(block, N), DCTTo(to, N));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Computes the in-place NxN transposed-scaled-iDCT (tsIDCT)of block.
|
|
||||||
// Requires that block is HWY_ALIGN'ed.
|
|
||||||
//
|
|
||||||
// See also IDCTSlow, ComputeIDCT.
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
struct ComputeTransposedScaledIDCT {
|
|
||||||
// scratch_space must be aligned, and should have space for N*N floats.
|
|
||||||
template <class To>
|
|
||||||
HWY_MAYBE_UNUSED void operator()(float* JXL_RESTRICT from, const To& to,
|
|
||||||
float* JXL_RESTRICT scratch_space) {
|
|
||||||
float* JXL_RESTRICT block = scratch_space;
|
|
||||||
IDCT1D<N, N>()(DCTFrom(from, N), DCTTo(block, N));
|
|
||||||
Transpose<N, N>::Run(DCTFrom(block, N), DCTTo(from, N));
|
|
||||||
IDCT1D<N, N>()(DCTFrom(from, N), to);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
// Computes the non-transposed, scaled DCT of a block, that needs to be
|
|
||||||
// HWY_ALIGN'ed. Used for rectangular blocks.
|
|
||||||
template <size_t ROWS, size_t COLS>
|
template <size_t ROWS, size_t COLS>
|
||||||
struct ComputeScaledDCT {
|
struct ComputeScaledDCT {
|
||||||
// scratch_space must be aligned, and should have space for ROWS*COLS
|
// scratch_space must be aligned, and should have space for ROWS*COLS
|
||||||
|
@ -329,8 +295,8 @@ struct ComputeScaledDCT {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Computes the non-transposed, scaled DCT of a block, that needs to be
|
// Computes the maybe-transposed, scaled IDCT of a block, that needs to be
|
||||||
// HWY_ALIGN'ed. Used for rectangular blocks.
|
// HWY_ALIGN'ed.
|
||||||
template <size_t ROWS, size_t COLS>
|
template <size_t ROWS, size_t COLS>
|
||||||
struct ComputeScaledIDCT {
|
struct ComputeScaledIDCT {
|
||||||
// scratch_space must be aligned, and should have space for ROWS*COLS
|
// scratch_space must be aligned, and should have space for ROWS*COLS
|
||||||
|
|
|
@ -35,7 +35,7 @@ template <size_t N>
|
||||||
void ComputeDCT(float block[N * N]) {
|
void ComputeDCT(float block[N * N]) {
|
||||||
HWY_ALIGN float tmp_block[N * N];
|
HWY_ALIGN float tmp_block[N * N];
|
||||||
HWY_ALIGN float scratch_space[N * N];
|
HWY_ALIGN float scratch_space[N * N];
|
||||||
ComputeTransposedScaledDCT<N>()(DCTFrom(block, N), tmp_block, scratch_space);
|
ComputeScaledDCT<N, N>()(DCTFrom(block, N), tmp_block, scratch_space);
|
||||||
|
|
||||||
// Untranspose.
|
// Untranspose.
|
||||||
Transpose<N, N>::Run(DCTFrom(tmp_block, N), DCTTo(block, N));
|
Transpose<N, N>::Run(DCTFrom(tmp_block, N), DCTTo(block, N));
|
||||||
|
@ -50,7 +50,7 @@ void ComputeIDCT(float block[N * N]) {
|
||||||
// Untranspose.
|
// Untranspose.
|
||||||
Transpose<N, N>::Run(DCTFrom(block, N), DCTTo(tmp_block, N));
|
Transpose<N, N>::Run(DCTFrom(block, N), DCTTo(tmp_block, N));
|
||||||
|
|
||||||
ComputeTransposedScaledIDCT<N>()(tmp_block, DCTTo(block, N), scratch_space);
|
ComputeScaledIDCT<N, N>()(tmp_block, DCTTo(block, N), scratch_space);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t N>
|
template <size_t N>
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "lib/jxl/ans_common.h"
|
#include "lib/jxl/ans_common.h"
|
||||||
#include "lib/jxl/ans_params.h"
|
#include "lib/jxl/ans_params.h"
|
||||||
#include "lib/jxl/base/bits.h"
|
#include "lib/jxl/base/bits.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/profiler.h"
|
#include "lib/jxl/base/profiler.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
|
|
|
@ -87,18 +87,20 @@ void LoadBorders(const Rect& block_rect, size_t hshift, size_t vshift,
|
||||||
// Limits of the area to copy from, in image coordinates.
|
// Limits of the area to copy from, in image coordinates.
|
||||||
JXL_DASSERT(r.x0() == 0 || r.x0() >= borderx);
|
JXL_DASSERT(r.x0() == 0 || r.x0() >= borderx);
|
||||||
size_t x0src = DivCeil(r.x0() == 0 ? r.x0() : r.x0() - borderx, 1 << hshift);
|
size_t x0src = DivCeil(r.x0() == 0 ? r.x0() : r.x0() - borderx, 1 << hshift);
|
||||||
|
// r may be such that r.x1 (namely x0() + xsize()) is within borderx of the
|
||||||
|
// right side of the image, so we use min() here.
|
||||||
size_t x1src =
|
size_t x1src =
|
||||||
DivCeil(r.x0() + r.xsize() +
|
DivCeil(std::min(r.x0() + r.xsize() + borderx, frame_dim.xsize_padded),
|
||||||
(r.x0() + r.xsize() == frame_dim.xsize_padded ? 0 : borderx),
|
|
||||||
1 << hshift);
|
1 << hshift);
|
||||||
JXL_DASSERT(r.y0() == 0 || r.y0() >= bordery);
|
JXL_DASSERT(r.y0() == 0 || r.y0() >= bordery);
|
||||||
size_t y0src = DivCeil(r.y0() == 0 ? r.y0() : r.y0() - bordery, 1 << vshift);
|
size_t y0src = DivCeil(r.y0() == 0 ? r.y0() : r.y0() - bordery, 1 << vshift);
|
||||||
|
// Similar to x1, y1 might be closer than bordery from the bottom.
|
||||||
size_t y1src =
|
size_t y1src =
|
||||||
DivCeil(r.y0() + r.ysize() +
|
DivCeil(std::min(r.y0() + r.ysize() + bordery, frame_dim.ysize_padded),
|
||||||
(r.y0() + r.ysize() == frame_dim.ysize_padded ? 0 : bordery),
|
|
||||||
1 << vshift);
|
1 << vshift);
|
||||||
// Copy other groups' borders from the border storage.
|
// Copy other groups' borders from the border storage.
|
||||||
if (y0src < y0) {
|
if (y0src < y0) {
|
||||||
|
JXL_DASSERT(gy > 0);
|
||||||
CopyImageTo(
|
CopyImageTo(
|
||||||
Rect(x0src, (gy * 2 - 1) * bordery_write, x1src - x0src, bordery_write),
|
Rect(x0src, (gy * 2 - 1) * bordery_write, x1src - x0src, bordery_write),
|
||||||
border_storage_h,
|
border_storage_h,
|
||||||
|
@ -107,6 +109,8 @@ void LoadBorders(const Rect& block_rect, size_t hshift, size_t vshift,
|
||||||
plane_out);
|
plane_out);
|
||||||
}
|
}
|
||||||
if (y1src > y1) {
|
if (y1src > y1) {
|
||||||
|
// When copying the bottom border we must not be on the bottom groups.
|
||||||
|
JXL_DASSERT(gy + 1 < frame_dim.ysize_groups);
|
||||||
CopyImageTo(
|
CopyImageTo(
|
||||||
Rect(x0src, (gy * 2 + 2) * bordery_write, x1src - x0src, bordery_write),
|
Rect(x0src, (gy * 2 + 2) * bordery_write, x1src - x0src, bordery_write),
|
||||||
border_storage_h,
|
border_storage_h,
|
||||||
|
@ -115,6 +119,7 @@ void LoadBorders(const Rect& block_rect, size_t hshift, size_t vshift,
|
||||||
plane_out);
|
plane_out);
|
||||||
}
|
}
|
||||||
if (x0src < x0) {
|
if (x0src < x0) {
|
||||||
|
JXL_DASSERT(gx > 0);
|
||||||
CopyImageTo(
|
CopyImageTo(
|
||||||
Rect((gx * 2 - 1) * borderx_write, y0src, borderx_write, y1src - y0src),
|
Rect((gx * 2 - 1) * borderx_write, y0src, borderx_write, y1src - y0src),
|
||||||
border_storage_v,
|
border_storage_v,
|
||||||
|
@ -123,6 +128,8 @@ void LoadBorders(const Rect& block_rect, size_t hshift, size_t vshift,
|
||||||
plane_out);
|
plane_out);
|
||||||
}
|
}
|
||||||
if (x1src > x1) {
|
if (x1src > x1) {
|
||||||
|
// When copying the right border we must not be on the rightmost groups.
|
||||||
|
JXL_DASSERT(gx + 1 < frame_dim.xsize_groups);
|
||||||
CopyImageTo(
|
CopyImageTo(
|
||||||
Rect((gx * 2 + 2) * borderx_write, y0src, borderx_write, y1src - y0src),
|
Rect((gx * 2 + 2) * borderx_write, y0src, borderx_write, y1src - y0src),
|
||||||
border_storage_v,
|
border_storage_v,
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#include "lib/jxl/base/byte_order.h"
|
#include "lib/jxl/base/byte_order.h"
|
||||||
#include "lib/jxl/base/cache_aligned.h"
|
#include "lib/jxl/base/cache_aligned.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/color_management.h"
|
#include "lib/jxl/color_management.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
#include "lib/jxl/sanitizers.h"
|
#include "lib/jxl/sanitizers.h"
|
||||||
|
|
|
@ -147,6 +147,7 @@ Status DecodeFile(const DecompressParams& dparams,
|
||||||
io->frames.back().jpeg_data = std::move(jpeg_data);
|
io->frames.back().jpeg_data = std::move(jpeg_data);
|
||||||
}
|
}
|
||||||
// Skip frames that are not displayed.
|
// Skip frames that are not displayed.
|
||||||
|
bool found_displayed_frame = true;
|
||||||
do {
|
do {
|
||||||
dec_ok =
|
dec_ok =
|
||||||
DecodeFrame(dparams, &dec_state, pool, &reader, &io->frames.back(),
|
DecodeFrame(dparams, &dec_state, pool, &reader, &io->frames.back(),
|
||||||
|
@ -155,13 +156,19 @@ Status DecodeFile(const DecompressParams& dparams,
|
||||||
JXL_RETURN_IF_ERROR(dec_ok);
|
JXL_RETURN_IF_ERROR(dec_ok);
|
||||||
} else if (!dec_ok) {
|
} else if (!dec_ok) {
|
||||||
io->frames.pop_back();
|
io->frames.pop_back();
|
||||||
|
found_displayed_frame = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (dec_state.shared->frame_header.frame_type !=
|
} while (dec_state.shared->frame_header.frame_type !=
|
||||||
FrameType::kRegularFrame &&
|
FrameType::kRegularFrame &&
|
||||||
dec_state.shared->frame_header.frame_type !=
|
dec_state.shared->frame_header.frame_type !=
|
||||||
FrameType::kSkipProgressive);
|
FrameType::kSkipProgressive);
|
||||||
io->dec_pixels += io->frames.back().xsize() * io->frames.back().ysize();
|
if (found_displayed_frame) {
|
||||||
|
// if found_displayed_frame is true io->frames shouldn't be empty
|
||||||
|
// because we added a frame before the loop.
|
||||||
|
JXL_ASSERT(!io->frames.empty());
|
||||||
|
io->dec_pixels += io->frames.back().xsize() * io->frames.back().ysize();
|
||||||
|
}
|
||||||
} while (!dec_state.shared->frame_header.is_last && dec_ok);
|
} while (!dec_state.shared->frame_header.is_last && dec_ok);
|
||||||
|
|
||||||
if (io->frames.empty()) return JXL_FAILURE("Not enough data.");
|
if (io->frames.empty()) return JXL_FAILURE("Not enough data.");
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "lib/jxl/base/bits.h"
|
#include "lib/jxl/base/bits.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/data_parallel.h"
|
#include "lib/jxl/base/data_parallel.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/profiler.h"
|
#include "lib/jxl/base/profiler.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/chroma_from_luma.h"
|
#include "lib/jxl/chroma_from_luma.h"
|
||||||
|
@ -541,11 +542,13 @@ Status FrameDecoder::ProcessACGlobal(BitReader* br) {
|
||||||
size_t num_components = jpeg_data->components.size();
|
size_t num_components = jpeg_data->components.size();
|
||||||
bool is_gray = (num_components == 1);
|
bool is_gray = (num_components == 1);
|
||||||
auto jpeg_c_map = JpegOrder(frame_header_.color_transform, is_gray);
|
auto jpeg_c_map = JpegOrder(frame_header_.color_transform, is_gray);
|
||||||
|
size_t qt_set = 0;
|
||||||
for (size_t c = 0; c < num_components; c++) {
|
for (size_t c = 0; c < num_components; c++) {
|
||||||
// TODO(eustas): why 1-st quant table for gray?
|
// TODO(eustas): why 1-st quant table for gray?
|
||||||
size_t quant_c = is_gray ? 1 : c;
|
size_t quant_c = is_gray ? 1 : c;
|
||||||
size_t qpos = jpeg_data->components[jpeg_c_map[c]].quant_idx;
|
size_t qpos = jpeg_data->components[jpeg_c_map[c]].quant_idx;
|
||||||
JXL_CHECK(qpos != jpeg_data->quant.size());
|
JXL_CHECK(qpos != jpeg_data->quant.size());
|
||||||
|
qt_set |= 1 << qpos;
|
||||||
for (size_t x = 0; x < 8; x++) {
|
for (size_t x = 0; x < 8; x++) {
|
||||||
for (size_t y = 0; y < 8; y++) {
|
for (size_t y = 0; y < 8; y++) {
|
||||||
jpeg_data->quant[qpos].values[x * 8 + y] =
|
jpeg_data->quant[qpos].values[x * 8 + y] =
|
||||||
|
@ -553,6 +556,14 @@ Status FrameDecoder::ProcessACGlobal(BitReader* br) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (size_t i = 0; i < jpeg_data->quant.size(); i++) {
|
||||||
|
if (qt_set & (1 << i)) continue;
|
||||||
|
if (i == 0) return JXL_FAILURE("First quant table unused.");
|
||||||
|
// Unused quant table is set to copy of previous quant table
|
||||||
|
for (size_t j = 0; j < 64; j++) {
|
||||||
|
jpeg_data->quant[i].values[j] = jpeg_data->quant[i - 1].values[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Set memory buffer for pre-color-transform frame, if needed.
|
// Set memory buffer for pre-color-transform frame, if needed.
|
||||||
if (frame_header_.needs_color_transform() &&
|
if (frame_header_.needs_color_transform() &&
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "lib/jxl/ac_strategy.h"
|
#include "lib/jxl/ac_strategy.h"
|
||||||
#include "lib/jxl/aux_out.h"
|
#include "lib/jxl/aux_out.h"
|
||||||
#include "lib/jxl/base/bits.h"
|
#include "lib/jxl/base/bits.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/profiler.h"
|
#include "lib/jxl/base/profiler.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/coeff_order.h"
|
#include "lib/jxl/coeff_order.h"
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
|
|
||||||
#include "lib/jxl/alpha.h"
|
#include "lib/jxl/alpha.h"
|
||||||
#include "lib/jxl/base/compiler_specific.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/span.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/compressed_dc.h"
|
#include "lib/jxl/compressed_dc.h"
|
||||||
|
@ -158,17 +159,18 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader,
|
||||||
if (is_gray && frame_header.color_transform == ColorTransform::kNone) {
|
if (is_gray && frame_header.color_transform == ColorTransform::kNone) {
|
||||||
nb_chans = 1;
|
nb_chans = 1;
|
||||||
}
|
}
|
||||||
|
do_color = decode_color;
|
||||||
|
if (!do_color) nb_chans = 0;
|
||||||
|
size_t nb_extra = metadata.extra_channel_info.size();
|
||||||
bool has_tree = reader->ReadBits(1);
|
bool has_tree = reader->ReadBits(1);
|
||||||
if (has_tree) {
|
if (has_tree) {
|
||||||
size_t tree_size_limit =
|
size_t tree_size_limit = std::min(
|
||||||
1024 + frame_dim.xsize * frame_dim.ysize * nb_chans / 16;
|
static_cast<size_t>(1 << 22),
|
||||||
|
1024 + frame_dim.xsize * frame_dim.ysize * (nb_chans + nb_extra) / 16);
|
||||||
JXL_RETURN_IF_ERROR(DecodeTree(reader, &tree, tree_size_limit));
|
JXL_RETURN_IF_ERROR(DecodeTree(reader, &tree, tree_size_limit));
|
||||||
JXL_RETURN_IF_ERROR(
|
JXL_RETURN_IF_ERROR(
|
||||||
DecodeHistograms(reader, (tree.size() + 1) / 2, &code, &context_map));
|
DecodeHistograms(reader, (tree.size() + 1) / 2, &code, &context_map));
|
||||||
}
|
}
|
||||||
do_color = decode_color;
|
|
||||||
if (!do_color) nb_chans = 0;
|
|
||||||
size_t nb_extra = metadata.extra_channel_info.size();
|
|
||||||
|
|
||||||
bool fp = metadata.bit_depth.floating_point_sample;
|
bool fp = metadata.bit_depth.floating_point_sample;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include "lib/jxl/ans_params.h"
|
#include "lib/jxl/ans_params.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/override.h"
|
#include "lib/jxl/base/override.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/blending.h"
|
#include "lib/jxl/blending.h"
|
||||||
#include "lib/jxl/chroma_from_luma.h"
|
#include "lib/jxl/chroma_from_luma.h"
|
||||||
|
|
|
@ -140,7 +140,7 @@ Status UndoXYBInPlace(Image3F* idct, const Rect& rect,
|
||||||
DoUndoXYBInPlace(idct, rect, Op709(), output_encoding_info);
|
DoUndoXYBInPlace(idct, rect, Op709(), output_encoding_info);
|
||||||
} else if (output_encoding_info.color_encoding.tf.IsGamma() ||
|
} else if (output_encoding_info.color_encoding.tf.IsGamma() ||
|
||||||
output_encoding_info.color_encoding.tf.IsDCI()) {
|
output_encoding_info.color_encoding.tf.IsDCI()) {
|
||||||
OpGamma op = {output_encoding_info.inverse_gamma};
|
OpGamma op{output_encoding_info.inverse_gamma};
|
||||||
DoUndoXYBInPlace(idct, rect, op, output_encoding_info);
|
DoUndoXYBInPlace(idct, rect, op, output_encoding_info);
|
||||||
} else {
|
} else {
|
||||||
// This is a programming error.
|
// This is a programming error.
|
||||||
|
@ -418,10 +418,13 @@ HWY_EXPORT(DoYCbCrUpsampling);
|
||||||
void UndoXYB(const Image3F& src, Image3F* dst,
|
void UndoXYB(const Image3F& src, Image3F* dst,
|
||||||
const OutputEncodingInfo& output_info, ThreadPool* pool) {
|
const OutputEncodingInfo& output_info, ThreadPool* pool) {
|
||||||
CopyImageTo(src, dst);
|
CopyImageTo(src, dst);
|
||||||
pool->Run(0, src.ysize(), ThreadPool::SkipInit(), [&](int y, int /*thread*/) {
|
RunOnPool(
|
||||||
JXL_CHECK(HWY_DYNAMIC_DISPATCH(UndoXYBInPlace)(dst, Rect(*dst).Line(y),
|
pool, 0, src.ysize(), ThreadPool::SkipInit(),
|
||||||
output_info));
|
[&](int y, int /*thread*/) {
|
||||||
});
|
JXL_CHECK(HWY_DYNAMIC_DISPATCH(UndoXYBInPlace)(dst, Rect(*dst).Line(y),
|
||||||
|
output_info));
|
||||||
|
},
|
||||||
|
"UndoXYB");
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -441,7 +444,9 @@ class EnsurePaddingInPlaceRowByRow {
|
||||||
size_t image_ysize, size_t xpadding, size_t ypadding, ssize_t* y0,
|
size_t image_ysize, size_t xpadding, size_t ypadding, ssize_t* y0,
|
||||||
ssize_t* y1) {
|
ssize_t* y1) {
|
||||||
// coordinates relative to rect.
|
// coordinates relative to rect.
|
||||||
JXL_DASSERT(SameSize(rect, image_rect));
|
JXL_ASSERT(SameSize(rect, image_rect));
|
||||||
|
JXL_ASSERT(image_rect.x0() + image_rect.xsize() <= image_xsize);
|
||||||
|
JXL_ASSERT(image_rect.y0() + image_rect.ysize() <= image_ysize);
|
||||||
*y0 = -std::min(image_rect.y0(), ypadding);
|
*y0 = -std::min(image_rect.y0(), ypadding);
|
||||||
*y1 = rect.ysize() + std::min(ypadding, image_ysize - image_rect.ysize() -
|
*y1 = rect.ysize() + std::min(ypadding, image_ysize - image_rect.ysize() -
|
||||||
image_rect.y0());
|
image_rect.y0());
|
||||||
|
@ -455,7 +460,7 @@ class EnsurePaddingInPlaceRowByRow {
|
||||||
strategy_ = kSlow;
|
strategy_ = kSlow;
|
||||||
}
|
}
|
||||||
y0_ = rect.y0();
|
y0_ = rect.y0();
|
||||||
JXL_DASSERT(rect.x0() >= xpadding);
|
JXL_ASSERT(rect.x0() >= xpadding);
|
||||||
x0_ = x1_ = rect.x0() - xpadding;
|
x0_ = x1_ = rect.x0() - xpadding;
|
||||||
// If close to the left border - do mirroring.
|
// If close to the left border - do mirroring.
|
||||||
if (image_rect.x0() < xpadding) x1_ = rect.x0() - image_rect.x0();
|
if (image_rect.x0() < xpadding) x1_ = rect.x0() - image_rect.x0();
|
||||||
|
@ -464,8 +469,11 @@ class EnsurePaddingInPlaceRowByRow {
|
||||||
if (image_rect.x0() + image_rect.xsize() + xpadding > image_xsize) {
|
if (image_rect.x0() + image_rect.xsize() + xpadding > image_xsize) {
|
||||||
x2_ = rect.x0() + image_xsize - image_rect.x0();
|
x2_ = rect.x0() + image_xsize - image_rect.x0();
|
||||||
}
|
}
|
||||||
JXL_DASSERT(image_xsize == (x2_ - x1_) ||
|
JXL_ASSERT(x0_ <= x1_);
|
||||||
(x1_ - x0_ <= x2_ - x1_ && x3_ - x2_ <= x2_ - x1_));
|
JXL_ASSERT(x1_ <= x2_);
|
||||||
|
JXL_ASSERT(x2_ <= x3_);
|
||||||
|
JXL_ASSERT(image_xsize == (x2_ - x1_) ||
|
||||||
|
(x1_ - x0_ <= x2_ - x1_ && x3_ - x2_ <= x2_ - x1_));
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -793,7 +801,9 @@ Status FinalizeImageRect(
|
||||||
}
|
}
|
||||||
ssize_t ensure_padding_y0, ensure_padding_y1;
|
ssize_t ensure_padding_y0, ensure_padding_y1;
|
||||||
EnsurePaddingInPlaceRowByRow ensure_padding;
|
EnsurePaddingInPlaceRowByRow ensure_padding;
|
||||||
Rect ec_image_rect = ScaleRectForEC(frame_rect, frame_header, ec);
|
// frame_rect can go up to frame_dim.xsize_padded, in VarDCT mode.
|
||||||
|
Rect ec_image_rect = ScaleRectForEC(
|
||||||
|
frame_rect.Crop(frame_dim.xsize, frame_dim.ysize), frame_header, ec);
|
||||||
size_t ecxs = DivCeil(frame_dim.xsize_upsampled,
|
size_t ecxs = DivCeil(frame_dim.xsize_upsampled,
|
||||||
frame_header.extra_channel_upsampling[ec]);
|
frame_header.extra_channel_upsampling[ec]);
|
||||||
size_t ecys = DivCeil(frame_dim.ysize_upsampled,
|
size_t ecys = DivCeil(frame_dim.ysize_upsampled,
|
||||||
|
@ -836,8 +846,10 @@ Status FinalizeImageRect(
|
||||||
extra_channels[ec].second.ysize() + rect_for_if_storage.ysize() -
|
extra_channels[ec].second.ysize() + rect_for_if_storage.ysize() -
|
||||||
rect_for_upsampling.ysize());
|
rect_for_upsampling.ysize());
|
||||||
extra_channels_for_patches.emplace_back(extra_channels[ec].first, r);
|
extra_channels_for_patches.emplace_back(extra_channels[ec].first, r);
|
||||||
|
// frame_rect can go up to frame_dim.xsize_padded, in VarDCT mode.
|
||||||
ec_padding[ec].Init(extra_channels[ec].first, extra_channels[ec].second,
|
ec_padding[ec].Init(extra_channels[ec].first, extra_channels[ec].second,
|
||||||
frame_rect, frame_dim.xsize, frame_dim.ysize, 2, 2,
|
frame_rect.Crop(frame_dim.xsize, frame_dim.ysize),
|
||||||
|
frame_dim.xsize, frame_dim.ysize, 2, 2,
|
||||||
&ensure_padding_upsampling_ec_y0,
|
&ensure_padding_upsampling_ec_y0,
|
||||||
&ensure_padding_upsampling_ec_y1);
|
&ensure_padding_upsampling_ec_y1);
|
||||||
}
|
}
|
||||||
|
@ -1048,7 +1060,8 @@ Status FinalizeImageRect(
|
||||||
dec_state->rgb_output_is_rgba, alpha,
|
dec_state->rgb_output_is_rgba, alpha,
|
||||||
alpha_rect.Lines(available_y, num_ys),
|
alpha_rect.Lines(available_y, num_ys),
|
||||||
upsampled_frame_rect.Lines(available_y, num_ys)
|
upsampled_frame_rect.Lines(available_y, num_ys)
|
||||||
.Crop(Rect(0, 0, frame_dim.xsize, frame_dim.ysize)),
|
.Crop(Rect(0, 0, frame_dim.xsize_upsampled,
|
||||||
|
frame_dim.ysize_upsampled)),
|
||||||
dec_state->rgb_output, dec_state->rgb_stride);
|
dec_state->rgb_output, dec_state->rgb_stride);
|
||||||
}
|
}
|
||||||
if (dec_state->pixel_callback != nullptr) {
|
if (dec_state->pixel_callback != nullptr) {
|
||||||
|
@ -1157,7 +1170,9 @@ Status FinalizeFrameDecoding(ImageBundle* decoded,
|
||||||
std::vector<std::pair<ImageF*, Rect>> ec_rects;
|
std::vector<std::pair<ImageF*, Rect>> ec_rects;
|
||||||
ec_rects.reserve(decoded->extra_channels().size());
|
ec_rects.reserve(decoded->extra_channels().size());
|
||||||
for (size_t i = 0; i < decoded->extra_channels().size(); i++) {
|
for (size_t i = 0; i < decoded->extra_channels().size(); i++) {
|
||||||
Rect r = ScaleRectForEC(rects_to_process[rect_id], frame_header, i);
|
Rect r = ScaleRectForEC(
|
||||||
|
rects_to_process[rect_id].Crop(frame_dim.xsize, frame_dim.ysize),
|
||||||
|
frame_header, i);
|
||||||
if (frame_header.extra_channel_upsampling[i] != 1) {
|
if (frame_header.extra_channel_upsampling[i] != 1) {
|
||||||
Rect ec_input_rect(kBlockDim, 2, r.xsize(), r.ysize());
|
Rect ec_input_rect(kBlockDim, 2, r.xsize(), r.ysize());
|
||||||
auto eti =
|
auto eti =
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
#include "lib/jxl/dec_reconstruct.h"
|
||||||
|
#include "lib/jxl/image_ops.h"
|
||||||
|
|
||||||
|
namespace jxl {
|
||||||
|
|
||||||
|
static void BM_UndoXYB(benchmark::State& state, TransferFunction tf) {
|
||||||
|
const size_t xsize = state.range();
|
||||||
|
const size_t ysize = xsize;
|
||||||
|
Image3F src(xsize, ysize);
|
||||||
|
Image3F dst(xsize, ysize);
|
||||||
|
FillImage(1.f, &src);
|
||||||
|
|
||||||
|
OutputEncodingInfo output_info = {};
|
||||||
|
CodecMetadata metadata = {};
|
||||||
|
JXL_CHECK(output_info.Set(metadata, ColorEncoding::LinearSRGB(false)));
|
||||||
|
output_info.inverse_gamma = 1.;
|
||||||
|
// Set the TransferFunction since the code executed depends on this parameter.
|
||||||
|
output_info.color_encoding.tf.SetTransferFunction(tf);
|
||||||
|
|
||||||
|
ThreadPool* null_pool = nullptr;
|
||||||
|
for (auto _ : state) {
|
||||||
|
UndoXYB(src, &dst, output_info, null_pool);
|
||||||
|
}
|
||||||
|
state.SetItemsProcessed(xsize * ysize * state.iterations());
|
||||||
|
}
|
||||||
|
|
||||||
|
BENCHMARK_CAPTURE(BM_UndoXYB, Linear, TransferFunction::kLinear)
|
||||||
|
->RangeMultiplier(4)
|
||||||
|
->Range(512, 2048);
|
||||||
|
BENCHMARK_CAPTURE(BM_UndoXYB, SRGB, TransferFunction::kSRGB)
|
||||||
|
->RangeMultiplier(4)
|
||||||
|
->Range(512, 2048);
|
||||||
|
BENCHMARK_CAPTURE(BM_UndoXYB, PQ, TransferFunction::kPQ)
|
||||||
|
->RangeMultiplier(4)
|
||||||
|
->Range(512, 2048);
|
||||||
|
BENCHMARK_CAPTURE(BM_UndoXYB, DCI, TransferFunction::kDCI)
|
||||||
|
->RangeMultiplier(4)
|
||||||
|
->Range(512, 2048);
|
||||||
|
|
||||||
|
} // namespace jxl
|
|
@ -23,24 +23,6 @@ namespace jxl {
|
||||||
namespace HWY_NAMESPACE {
|
namespace HWY_NAMESPACE {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <size_t ROWS, size_t COLS>
|
|
||||||
struct DoDCT {
|
|
||||||
template <typename From>
|
|
||||||
void operator()(const From& from, float* JXL_RESTRICT to,
|
|
||||||
float* JXL_RESTRICT scratch_space) {
|
|
||||||
ComputeScaledDCT<ROWS, COLS>()(from, to, scratch_space);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
struct DoDCT<N, N> {
|
|
||||||
template <typename From>
|
|
||||||
void operator()(const From& from, float* JXL_RESTRICT to,
|
|
||||||
float* JXL_RESTRICT scratch_space) {
|
|
||||||
ComputeTransposedScaledDCT<N>()(from, to, scratch_space);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Computes the lowest-frequency LF_ROWSxLF_COLS-sized square in output, which
|
// Computes the lowest-frequency LF_ROWSxLF_COLS-sized square in output, which
|
||||||
// is a DCT_ROWS*DCT_COLS-sized DCT block, by doing a ROWS*COLS DCT on the
|
// is a DCT_ROWS*DCT_COLS-sized DCT block, by doing a ROWS*COLS DCT on the
|
||||||
// input block.
|
// input block.
|
||||||
|
@ -56,7 +38,8 @@ JXL_INLINE void ReinterpretingDCT(const float* input, const size_t input_stride,
|
||||||
|
|
||||||
// ROWS, COLS <= 8, so we can put scratch space on the stack.
|
// ROWS, COLS <= 8, so we can put scratch space on the stack.
|
||||||
HWY_ALIGN float scratch_space[ROWS * COLS];
|
HWY_ALIGN float scratch_space[ROWS * COLS];
|
||||||
DoDCT<ROWS, COLS>()(DCTFrom(input, input_stride), block, scratch_space);
|
ComputeScaledDCT<ROWS, COLS>()(DCTFrom(input, input_stride), block,
|
||||||
|
scratch_space);
|
||||||
if (ROWS < COLS) {
|
if (ROWS < COLS) {
|
||||||
for (size_t y = 0; y < LF_ROWS; y++) {
|
for (size_t y = 0; y < LF_ROWS; y++) {
|
||||||
for (size_t x = 0; x < LF_COLS; x++) {
|
for (size_t x = 0; x < LF_COLS; x++) {
|
||||||
|
@ -447,7 +430,7 @@ void AFVTransformToPixels(const float* JXL_RESTRICT coefficients,
|
||||||
block[iy * 4 + ix] = coefficients[iy * 2 * 8 + ix * 2 + 1];
|
block[iy * 4 + ix] = coefficients[iy * 2 * 8 + ix * 2 + 1];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ComputeTransposedScaledIDCT<4>()(
|
ComputeScaledIDCT<4, 4>()(
|
||||||
block,
|
block,
|
||||||
DCTTo(pixels + afv_y * 4 * pixels_stride + (afv_x == 1 ? 0 : 4),
|
DCTTo(pixels + afv_y * 4 * pixels_stride + (afv_x == 1 ? 0 : 4),
|
||||||
pixels_stride),
|
pixels_stride),
|
||||||
|
@ -575,7 +558,7 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
|
||||||
block[iy * 4 + ix] = coefficients[(y + iy * 2) * 8 + x + ix * 2];
|
block[iy * 4 + ix] = coefficients[(y + iy * 2) * 8 + x + ix * 2];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ComputeTransposedScaledIDCT<4>()(
|
ComputeScaledIDCT<4, 4>()(
|
||||||
block,
|
block,
|
||||||
DCTTo(pixels + y * 4 * pixels_stride + x * 4, pixels_stride),
|
DCTTo(pixels + y * 4 * pixels_stride + x * 4, pixels_stride),
|
||||||
scratch_space);
|
scratch_space);
|
||||||
|
@ -599,8 +582,8 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT16X16: {
|
case Type::DCT16X16: {
|
||||||
PROFILER_ZONE("IDCT 16");
|
PROFILER_ZONE("IDCT 16");
|
||||||
ComputeTransposedScaledIDCT<16>()(
|
ComputeScaledIDCT<16, 16>()(coefficients, DCTTo(pixels, pixels_stride),
|
||||||
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DCT16X8: {
|
case Type::DCT16X8: {
|
||||||
|
@ -641,14 +624,14 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT32X32: {
|
case Type::DCT32X32: {
|
||||||
PROFILER_ZONE("IDCT 32");
|
PROFILER_ZONE("IDCT 32");
|
||||||
ComputeTransposedScaledIDCT<32>()(
|
ComputeScaledIDCT<32, 32>()(coefficients, DCTTo(pixels, pixels_stride),
|
||||||
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DCT: {
|
case Type::DCT: {
|
||||||
PROFILER_ZONE("IDCT 8");
|
PROFILER_ZONE("IDCT 8");
|
||||||
ComputeTransposedScaledIDCT<8>()(
|
ComputeScaledIDCT<8, 8>()(coefficients, DCTTo(pixels, pixels_stride),
|
||||||
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::AFV0: {
|
case Type::AFV0: {
|
||||||
|
@ -685,8 +668,8 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT64X64: {
|
case Type::DCT64X64: {
|
||||||
PROFILER_ZONE("IDCT 64");
|
PROFILER_ZONE("IDCT 64");
|
||||||
ComputeTransposedScaledIDCT<64>()(
|
ComputeScaledIDCT<64, 64>()(coefficients, DCTTo(pixels, pixels_stride),
|
||||||
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DCT128X64: {
|
case Type::DCT128X64: {
|
||||||
|
@ -703,8 +686,8 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT128X128: {
|
case Type::DCT128X128: {
|
||||||
PROFILER_ZONE("IDCT 128");
|
PROFILER_ZONE("IDCT 128");
|
||||||
ComputeTransposedScaledIDCT<128>()(
|
ComputeScaledIDCT<128, 128>()(coefficients, DCTTo(pixels, pixels_stride),
|
||||||
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DCT256X128: {
|
case Type::DCT256X128: {
|
||||||
|
@ -721,8 +704,8 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT256X256: {
|
case Type::DCT256X256: {
|
||||||
PROFILER_ZONE("IDCT 256");
|
PROFILER_ZONE("IDCT 256");
|
||||||
ComputeTransposedScaledIDCT<256>()(
|
ComputeScaledIDCT<256, 256>()(coefficients, DCTTo(pixels, pixels_stride),
|
||||||
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::kNumValidStrategies:
|
case Type::kNumValidStrategies:
|
||||||
|
|
|
@ -192,12 +192,20 @@ enum class FrameStage : uint32_t {
|
||||||
enum class BoxStage : uint32_t {
|
enum class BoxStage : uint32_t {
|
||||||
kHeader, // Parsing box header of the next box, or start of non-container
|
kHeader, // Parsing box header of the next box, or start of non-container
|
||||||
// stream
|
// stream
|
||||||
|
kFtyp, // The ftyp box
|
||||||
kSkip, // Box whose contents are skipped
|
kSkip, // Box whose contents are skipped
|
||||||
kCodestream, // Handling codestream box contents, or non-container stream
|
kCodestream, // Handling codestream box contents, or non-container stream
|
||||||
kPartialCodestream, // Handling the extra header of partial codestream box
|
kPartialCodestream, // Handling the extra header of partial codestream box
|
||||||
kJpegRecon, // Handling jpeg reconstruction box
|
kJpegRecon, // Handling jpeg reconstruction box
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class JpegReconStage : uint32_t {
|
||||||
|
kNone, // Not outputting
|
||||||
|
kSettingMetadata, // Ready to output, must set metadata to the jpeg_data
|
||||||
|
kOutputting, // Currently outputting the JPEG bytes
|
||||||
|
kFinished, // JPEG reconstruction fully handled
|
||||||
|
};
|
||||||
|
|
||||||
// Manages the sections for the FrameDecoder based on input bytes received.
|
// Manages the sections for the FrameDecoder based on input bytes received.
|
||||||
struct Sections {
|
struct Sections {
|
||||||
// sections_begin = position in the frame where the sections begin, after
|
// sections_begin = position in the frame where the sections begin, after
|
||||||
|
@ -444,6 +452,7 @@ struct JxlDecoderStruct {
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
bool keep_orientation;
|
bool keep_orientation;
|
||||||
|
bool render_spotcolors;
|
||||||
|
|
||||||
// Bitfield, for which informative events (JXL_DEC_BASIC_INFO, etc...) the
|
// Bitfield, for which informative events (JXL_DEC_BASIC_INFO, etc...) the
|
||||||
// decoder returns a status. By default, do not return for any of the events,
|
// decoder returns a status. By default, do not return for any of the events,
|
||||||
|
@ -455,6 +464,7 @@ struct JxlDecoderStruct {
|
||||||
// Fields for reading the basic info from the header.
|
// Fields for reading the basic info from the header.
|
||||||
size_t basic_info_size_hint;
|
size_t basic_info_size_hint;
|
||||||
bool have_container;
|
bool have_container;
|
||||||
|
size_t box_count;
|
||||||
|
|
||||||
// Whether the preview out buffer was set. It is possible for the buffer to
|
// Whether the preview out buffer was set. It is possible for the buffer to
|
||||||
// be nullptr and buffer_set to be true, indicating it was deliberately
|
// be nullptr and buffer_set to be true, indicating it was deliberately
|
||||||
|
@ -551,12 +561,33 @@ struct JxlDecoderStruct {
|
||||||
|
|
||||||
jxl::JxlToJpegDecoder jpeg_decoder;
|
jxl::JxlToJpegDecoder jpeg_decoder;
|
||||||
jxl::JxlBoxContentDecoder box_content_decoder;
|
jxl::JxlBoxContentDecoder box_content_decoder;
|
||||||
|
// Decodes Exif or XMP metadata for JPEG reconstruction
|
||||||
|
jxl::JxlBoxContentDecoder metadata_decoder;
|
||||||
|
std::vector<uint8_t> exif_metadata;
|
||||||
|
std::vector<uint8_t> xmp_metadata;
|
||||||
|
// must store JPEG reconstruction metadata from the current box
|
||||||
|
// 0 = not stored, 1 = currently storing, 2 = finished
|
||||||
|
int store_exif;
|
||||||
|
int store_xmp;
|
||||||
|
size_t recon_out_buffer_pos;
|
||||||
|
size_t recon_exif_size; // Expected exif size as read from the jbrd box
|
||||||
|
size_t recon_xmp_size; // Expected exif size as read from the jbrd box
|
||||||
|
JpegReconStage recon_output_jpeg;
|
||||||
|
|
||||||
|
bool JbrdNeedMoreBoxes() const {
|
||||||
|
// jbrd box wants exif but exif box not yet seen
|
||||||
|
if (store_exif < 2 && recon_exif_size > 0) return true;
|
||||||
|
// jbrd box wants xmp but xmp box not yet seen
|
||||||
|
if (store_xmp < 2 && recon_xmp_size > 0) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Statistics which CodecInOut can keep
|
// Statistics which CodecInOut can keep
|
||||||
uint64_t dec_pixels;
|
uint64_t dec_pixels;
|
||||||
|
|
||||||
const uint8_t* next_in;
|
const uint8_t* next_in;
|
||||||
size_t avail_in;
|
size_t avail_in;
|
||||||
|
bool input_closed;
|
||||||
|
|
||||||
void AdvanceInput(size_t size) {
|
void AdvanceInput(size_t size) {
|
||||||
next_in += size;
|
next_in += size;
|
||||||
|
@ -601,10 +632,19 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
|
||||||
dec->box_out_buffer_size = 0;
|
dec->box_out_buffer_size = 0;
|
||||||
dec->box_out_buffer_begin = 0;
|
dec->box_out_buffer_begin = 0;
|
||||||
dec->box_out_buffer_pos = 0;
|
dec->box_out_buffer_pos = 0;
|
||||||
|
dec->exif_metadata.clear();
|
||||||
|
dec->xmp_metadata.clear();
|
||||||
|
dec->store_exif = 0;
|
||||||
|
dec->store_xmp = 0;
|
||||||
|
dec->recon_out_buffer_pos = 0;
|
||||||
|
dec->recon_exif_size = 0;
|
||||||
|
dec->recon_xmp_size = 0;
|
||||||
|
dec->recon_output_jpeg = JpegReconStage::kNone;
|
||||||
|
|
||||||
dec->events_wanted = 0;
|
dec->events_wanted = 0;
|
||||||
dec->basic_info_size_hint = InitialBasicInfoSizeHint();
|
dec->basic_info_size_hint = InitialBasicInfoSizeHint();
|
||||||
dec->have_container = 0;
|
dec->have_container = 0;
|
||||||
|
dec->box_count = 0;
|
||||||
dec->preview_out_buffer_set = false;
|
dec->preview_out_buffer_set = false;
|
||||||
dec->image_out_buffer_set = false;
|
dec->image_out_buffer_set = false;
|
||||||
dec->preview_out_buffer = nullptr;
|
dec->preview_out_buffer = nullptr;
|
||||||
|
@ -617,6 +657,7 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
|
||||||
dec->dec_pixels = 0;
|
dec->dec_pixels = 0;
|
||||||
dec->next_in = 0;
|
dec->next_in = 0;
|
||||||
dec->avail_in = 0;
|
dec->avail_in = 0;
|
||||||
|
dec->input_closed = false;
|
||||||
|
|
||||||
dec->passes_state.reset(nullptr);
|
dec->passes_state.reset(nullptr);
|
||||||
dec->frame_dec.reset(nullptr);
|
dec->frame_dec.reset(nullptr);
|
||||||
|
@ -645,6 +686,7 @@ void JxlDecoderReset(JxlDecoder* dec) {
|
||||||
|
|
||||||
dec->thread_pool.reset();
|
dec->thread_pool.reset();
|
||||||
dec->keep_orientation = false;
|
dec->keep_orientation = false;
|
||||||
|
dec->render_spotcolors = true;
|
||||||
dec->orig_events_wanted = 0;
|
dec->orig_events_wanted = 0;
|
||||||
dec->frame_references.clear();
|
dec->frame_references.clear();
|
||||||
dec->frame_saved_as.clear();
|
dec->frame_saved_as.clear();
|
||||||
|
@ -746,6 +788,15 @@ JxlDecoderStatus JxlDecoderSetKeepOrientation(JxlDecoder* dec,
|
||||||
return JXL_DEC_SUCCESS;
|
return JXL_DEC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JxlDecoderStatus JxlDecoderSetRenderSpotcolors(JxlDecoder* dec,
|
||||||
|
JXL_BOOL render_spotcolors) {
|
||||||
|
if (dec->stage != DecoderStage::kInited) {
|
||||||
|
return JXL_API_ERROR("Must set render_spotcolors option before starting");
|
||||||
|
}
|
||||||
|
dec->render_spotcolors = !!render_spotcolors;
|
||||||
|
return JXL_DEC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -1119,6 +1170,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
||||||
auto reader = GetBitReader(compressed);
|
auto reader = GetBitReader(compressed);
|
||||||
jxl::DecompressParams dparams;
|
jxl::DecompressParams dparams;
|
||||||
dparams.preview = want_preview ? jxl::Override::kOn : jxl::Override::kOff;
|
dparams.preview = want_preview ? jxl::Override::kOn : jxl::Override::kOff;
|
||||||
|
dparams.render_spotcolors = dec->render_spotcolors;
|
||||||
jxl::ImageBundle ib(&dec->metadata.m);
|
jxl::ImageBundle ib(&dec->metadata.m);
|
||||||
PassesDecoderState preview_dec_state;
|
PassesDecoderState preview_dec_state;
|
||||||
JXL_API_RETURN_IF_ERROR(preview_dec_state.output_encoding_info.Set(
|
JXL_API_RETURN_IF_ERROR(preview_dec_state.output_encoding_info.Set(
|
||||||
|
@ -1159,6 +1211,15 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dec->frame_stage == FrameStage::kHeader) {
|
if (dec->frame_stage == FrameStage::kHeader) {
|
||||||
|
if (dec->recon_output_jpeg == JpegReconStage::kSettingMetadata ||
|
||||||
|
dec->recon_output_jpeg == JpegReconStage::kOutputting) {
|
||||||
|
// The image bundle contains the JPEG reconstruction frame, but the
|
||||||
|
// decoder is still waiting to decode an EXIF or XMP box. It's not
|
||||||
|
// implemented to decode additional frames during this, and a JPEG
|
||||||
|
// reconstruction image should have only one frame.
|
||||||
|
return JXL_API_ERROR(
|
||||||
|
"cannot decode a next frame after JPEG reconstruction frame");
|
||||||
|
}
|
||||||
size_t pos = dec->frame_start - dec->codestream_pos;
|
size_t pos = dec->frame_start - dec->codestream_pos;
|
||||||
if (pos >= size) {
|
if (pos >= size) {
|
||||||
return JXL_DEC_NEED_MORE_INPUT;
|
return JXL_DEC_NEED_MORE_INPUT;
|
||||||
|
@ -1257,6 +1318,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
||||||
|
|
||||||
dec->frame_dec.reset(new FrameDecoder(
|
dec->frame_dec.reset(new FrameDecoder(
|
||||||
dec->passes_state.get(), dec->metadata, dec->thread_pool.get()));
|
dec->passes_state.get(), dec->metadata, dec->thread_pool.get()));
|
||||||
|
dec->frame_dec->SetRenderSpotcolors(dec->render_spotcolors);
|
||||||
|
|
||||||
// If JPEG reconstruction is wanted and possible, set the jpeg_data of
|
// If JPEG reconstruction is wanted and possible, set the jpeg_data of
|
||||||
// the ImageBundle.
|
// the ImageBundle.
|
||||||
|
@ -1381,10 +1443,17 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
||||||
if (!dec->frame_dec->FinalizeFrame()) {
|
if (!dec->frame_dec->FinalizeFrame()) {
|
||||||
return JXL_API_ERROR("decoding frame failed");
|
return JXL_API_ERROR("decoding frame failed");
|
||||||
}
|
}
|
||||||
|
// Copy exif/xmp metadata from their boxes into the jpeg_data, if
|
||||||
|
// JPEG reconstruction is requested.
|
||||||
|
if (dec->jpeg_decoder.IsOutputSet() && dec->ib->jpeg_data != nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
dec->frame_dec_in_progress = false;
|
dec->frame_dec_in_progress = false;
|
||||||
dec->frame_stage = FrameStage::kFullOutput;
|
dec->frame_stage = FrameStage::kFullOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool output_jpeg_reconstruction = false;
|
||||||
|
|
||||||
if (dec->frame_stage == FrameStage::kFullOutput) {
|
if (dec->frame_stage == FrameStage::kFullOutput) {
|
||||||
if (dec->is_last_of_still) {
|
if (dec->is_last_of_still) {
|
||||||
if (dec->events_wanted & JXL_DEC_FULL_IMAGE) {
|
if (dec->events_wanted & JXL_DEC_FULL_IMAGE) {
|
||||||
|
@ -1400,9 +1469,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
||||||
// If no output buffer was set, we merely return the JXL_DEC_FULL_IMAGE
|
// If no output buffer was set, we merely return the JXL_DEC_FULL_IMAGE
|
||||||
// status without outputting pixels.
|
// status without outputting pixels.
|
||||||
if (dec->jpeg_decoder.IsOutputSet() && dec->ib->jpeg_data != nullptr) {
|
if (dec->jpeg_decoder.IsOutputSet() && dec->ib->jpeg_data != nullptr) {
|
||||||
JxlDecoderStatus status =
|
output_jpeg_reconstruction = true;
|
||||||
dec->jpeg_decoder.WriteOutput(*dec->ib->jpeg_data);
|
|
||||||
if (status != JXL_DEC_SUCCESS) return status;
|
|
||||||
} else if (return_full_image && dec->image_out_buffer_set) {
|
} else if (return_full_image && dec->image_out_buffer_set) {
|
||||||
if (!dec->frame_dec->HasRGBBuffer()) {
|
if (!dec->frame_dec->HasRGBBuffer()) {
|
||||||
// Copy pixels if desired.
|
// Copy pixels if desired.
|
||||||
|
@ -1433,13 +1500,19 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The pixels have been output or are not needed, do not keep them in
|
|
||||||
// memory here.
|
|
||||||
dec->ib.reset();
|
|
||||||
dec->frame_stage = FrameStage::kHeader;
|
dec->frame_stage = FrameStage::kHeader;
|
||||||
dec->frame_start += dec->frame_size;
|
dec->frame_start += dec->frame_size;
|
||||||
if (return_full_image && !dec->skipping_frame) {
|
|
||||||
|
if (output_jpeg_reconstruction) {
|
||||||
|
dec->recon_output_jpeg = JpegReconStage::kSettingMetadata;
|
||||||
return JXL_DEC_FULL_IMAGE;
|
return JXL_DEC_FULL_IMAGE;
|
||||||
|
} else {
|
||||||
|
// The pixels have been output or are not needed, do not keep them in
|
||||||
|
// memory here.
|
||||||
|
dec->ib.reset();
|
||||||
|
if (return_full_image && !dec->skipping_frame) {
|
||||||
|
return JXL_DEC_FULL_IMAGE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1453,7 +1526,12 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
||||||
|
|
||||||
JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec, const uint8_t* data,
|
JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec, const uint8_t* data,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
if (dec->next_in) return JXL_DEC_ERROR;
|
if (dec->next_in) {
|
||||||
|
return JXL_API_ERROR("already set input, use JxlDecoderReleaseInput first");
|
||||||
|
}
|
||||||
|
if (dec->input_closed) {
|
||||||
|
return JXL_API_ERROR("input already closed");
|
||||||
|
}
|
||||||
|
|
||||||
dec->next_in = data;
|
dec->next_in = data;
|
||||||
dec->avail_in = size;
|
dec->avail_in = size;
|
||||||
|
@ -1467,8 +1545,19 @@ size_t JxlDecoderReleaseInput(JxlDecoder* dec) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void JxlDecoderCloseInput(JxlDecoder* dec) { dec->input_closed = true; }
|
||||||
|
|
||||||
JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec, uint8_t* data,
|
JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec, uint8_t* data,
|
||||||
size_t size) {
|
size_t size) {
|
||||||
|
// JPEG reconstruction buffer can only set and updated before or during the
|
||||||
|
// first frame, the reconstruction box refers to the first frame and in
|
||||||
|
// theory multi-frame images should not be used with a jbrd box.
|
||||||
|
if (dec->internal_frames > 1) {
|
||||||
|
return JXL_API_ERROR("JPEG reconstruction only works for the first frame");
|
||||||
|
}
|
||||||
|
if (dec->jpeg_decoder.IsOutputSet()) {
|
||||||
|
return JXL_API_ERROR("Already set JPEG buffer");
|
||||||
|
}
|
||||||
return dec->jpeg_decoder.SetOutputBuffer(data, size);
|
return dec->jpeg_decoder.SetOutputBuffer(data, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1521,29 +1610,8 @@ static JxlDecoderStatus ParseBoxHeader(const uint8_t* in, size_t size,
|
||||||
return JXL_DEC_SUCCESS;
|
return JXL_DEC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This includes handling the codestream if it is not a box-based jxl file.
|
||||||
JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||||
if (dec->stage == DecoderStage::kInited) {
|
|
||||||
dec->stage = DecoderStage::kStarted;
|
|
||||||
}
|
|
||||||
if (dec->stage == DecoderStage::kError) {
|
|
||||||
return JXL_API_ERROR(
|
|
||||||
"Cannot keep using decoder after it encountered an error, use "
|
|
||||||
"JxlDecoderReset to reset it");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dec->got_signature) {
|
|
||||||
JxlSignature sig = JxlSignatureCheck(dec->next_in, dec->avail_in);
|
|
||||||
if (sig == JXL_SIG_INVALID) return JXL_API_ERROR("invalid signature");
|
|
||||||
if (sig == JXL_SIG_NOT_ENOUGH_BYTES) return JXL_DEC_NEED_MORE_INPUT;
|
|
||||||
|
|
||||||
dec->got_signature = true;
|
|
||||||
|
|
||||||
if (sig == JXL_SIG_CONTAINER) {
|
|
||||||
dec->have_container = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Box handling loop
|
// Box handling loop
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (dec->box_stage != BoxStage::kHeader) {
|
if (dec->box_stage != BoxStage::kHeader) {
|
||||||
|
@ -1567,6 +1635,77 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
return box_result;
|
return box_result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (dec->store_exif == 1 || dec->store_xmp == 1) {
|
||||||
|
std::vector<uint8_t>& metadata =
|
||||||
|
(dec->store_exif == 1) ? dec->exif_metadata : dec->xmp_metadata;
|
||||||
|
for (;;) {
|
||||||
|
if (metadata.empty()) metadata.resize(64);
|
||||||
|
uint8_t* orig_next_out = metadata.data() + dec->recon_out_buffer_pos;
|
||||||
|
uint8_t* next_out = orig_next_out;
|
||||||
|
size_t avail_out = metadata.size() - dec->recon_out_buffer_pos;
|
||||||
|
JxlDecoderStatus box_result = dec->metadata_decoder.Process(
|
||||||
|
dec->next_in, dec->avail_in,
|
||||||
|
dec->file_pos - dec->box_contents_begin, &next_out, &avail_out);
|
||||||
|
size_t produced = next_out - orig_next_out;
|
||||||
|
dec->recon_out_buffer_pos += produced;
|
||||||
|
if (box_result == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
|
||||||
|
metadata.resize(metadata.size() * 2);
|
||||||
|
} else if (box_result == JXL_DEC_NEED_MORE_INPUT) {
|
||||||
|
break; // box stage handling below will handle this instead
|
||||||
|
} else if (box_result == JXL_DEC_SUCCESS) {
|
||||||
|
size_t needed_size = (dec->store_exif == 1) ? dec->recon_exif_size
|
||||||
|
: dec->recon_xmp_size;
|
||||||
|
if (dec->box_contents_unbounded &&
|
||||||
|
dec->recon_out_buffer_pos < needed_size) {
|
||||||
|
// Unbounded box, but we know the expected size due to the jbrd
|
||||||
|
// box's data. Treat this as the JXL_DEC_NEED_MORE_INPUT case.
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
metadata.resize(dec->recon_out_buffer_pos);
|
||||||
|
if (dec->store_exif == 1) dec->store_exif = 2;
|
||||||
|
if (dec->store_xmp == 1) dec->store_xmp = 2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// error
|
||||||
|
return box_result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dec->recon_output_jpeg == JpegReconStage::kSettingMetadata &&
|
||||||
|
!dec->JbrdNeedMoreBoxes()) {
|
||||||
|
using namespace jxl;
|
||||||
|
jpeg::JPEGData* jpeg_data = dec->ib->jpeg_data.get();
|
||||||
|
if (dec->recon_exif_size) {
|
||||||
|
JxlDecoderStatus status = JxlToJpegDecoder::SetExif(
|
||||||
|
dec->exif_metadata.data(), dec->exif_metadata.size(), jpeg_data);
|
||||||
|
if (status != JXL_DEC_SUCCESS) return status;
|
||||||
|
}
|
||||||
|
if (dec->recon_xmp_size) {
|
||||||
|
JxlDecoderStatus status = JxlToJpegDecoder::SetXmp(
|
||||||
|
dec->xmp_metadata.data(), dec->xmp_metadata.size(), jpeg_data);
|
||||||
|
if (status != JXL_DEC_SUCCESS) return status;
|
||||||
|
}
|
||||||
|
dec->recon_output_jpeg = JpegReconStage::kOutputting;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dec->recon_output_jpeg == JpegReconStage::kOutputting &&
|
||||||
|
!dec->JbrdNeedMoreBoxes()) {
|
||||||
|
using namespace jxl;
|
||||||
|
JxlDecoderStatus status =
|
||||||
|
dec->jpeg_decoder.WriteOutput(*dec->ib->jpeg_data);
|
||||||
|
if (status != JXL_DEC_SUCCESS) return status;
|
||||||
|
dec->recon_output_jpeg = JpegReconStage::kFinished;
|
||||||
|
dec->ib.reset();
|
||||||
|
if (dec->events_wanted & JXL_DEC_FULL_IMAGE) {
|
||||||
|
// Return the full image event here now, this may be delayed if this
|
||||||
|
// could only be done after decoding an exif or xmp box after the
|
||||||
|
// codestream.
|
||||||
|
return JXL_DEC_FULL_IMAGE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dec->box_stage == BoxStage::kHeader) {
|
if (dec->box_stage == BoxStage::kHeader) {
|
||||||
|
@ -1577,12 +1716,27 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (dec->avail_in == 0) {
|
if (dec->avail_in == 0) {
|
||||||
if (dec->stage == DecoderStage::kFinished) {
|
if (dec->stage != DecoderStage::kFinished) {
|
||||||
// All codestream boxes done, return success. However, if the user
|
// Not yet seen (all) codestream boxes.
|
||||||
// still has more input, which could be a next metadata box, it's
|
return JXL_DEC_NEED_MORE_INPUT;
|
||||||
// still possible to continue next JxlDecoderProcessInput calls.
|
}
|
||||||
|
if (dec->JbrdNeedMoreBoxes()) {
|
||||||
|
return JXL_DEC_NEED_MORE_INPUT;
|
||||||
|
}
|
||||||
|
if (dec->input_closed) {
|
||||||
return JXL_DEC_SUCCESS;
|
return JXL_DEC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
if (!(dec->events_wanted & JXL_DEC_BOX)) {
|
||||||
|
// All codestream and jbrd metadata boxes finished, and no individual
|
||||||
|
// boxes requested by user, so no need to request any more input.
|
||||||
|
// This returns success for backwards compatibility, when
|
||||||
|
// JxlDecoderCloseInput and JXL_DEC_BOX did not exist, as well
|
||||||
|
// as for efficiency.
|
||||||
|
return JXL_DEC_SUCCESS;
|
||||||
|
}
|
||||||
|
// Even though we are exactly at a box end, there still may be more
|
||||||
|
// boxes. The user may call JxlDecoderCloseInput to indicate the input
|
||||||
|
// is finished and get success instead.
|
||||||
return JXL_DEC_NEED_MORE_INPUT;
|
return JXL_DEC_NEED_MORE_INPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1597,7 +1751,6 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (memcmp(dec->box_type, "brob", 4) == 0) {
|
if (memcmp(dec->box_type, "brob", 4) == 0) {
|
||||||
if (dec->avail_in < header_size + 4) {
|
if (dec->avail_in < header_size + 4) {
|
||||||
return JXL_DEC_NEED_MORE_INPUT;
|
return JXL_DEC_NEED_MORE_INPUT;
|
||||||
|
@ -1609,6 +1762,17 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
sizeof(dec->box_decoded_type));
|
sizeof(dec->box_decoded_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Box order validity checks
|
||||||
|
// The signature box at box_count == 1 is not checked here since that's
|
||||||
|
// already done at the beginning.
|
||||||
|
dec->box_count++;
|
||||||
|
if (dec->box_count == 2 && memcmp(dec->box_type, "ftyp", 4) != 0) {
|
||||||
|
return JXL_API_ERROR("the second box must be the ftyp box");
|
||||||
|
}
|
||||||
|
if (memcmp(dec->box_type, "ftyp", 4) == 0 && dec->box_count != 2) {
|
||||||
|
return JXL_API_ERROR("the ftyp box must come second");
|
||||||
|
}
|
||||||
|
|
||||||
dec->AdvanceInput(header_size);
|
dec->AdvanceInput(header_size);
|
||||||
|
|
||||||
dec->box_contents_unbounded = (box_size == 0);
|
dec->box_contents_unbounded = (box_size == 0);
|
||||||
|
@ -1620,18 +1784,44 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
dec->box_contents_unbounded ? 0 : (box_size - header_size);
|
dec->box_contents_unbounded ? 0 : (box_size - header_size);
|
||||||
dec->box_size = box_size;
|
dec->box_size = box_size;
|
||||||
|
|
||||||
|
if (dec->orig_events_wanted & JXL_DEC_JPEG_RECONSTRUCTION) {
|
||||||
|
// Initiate storing of Exif or XMP data for JPEG reconstruction
|
||||||
|
if (dec->store_exif == 0 &&
|
||||||
|
memcmp(dec->box_decoded_type, "Exif", 4) == 0) {
|
||||||
|
dec->store_exif = 1;
|
||||||
|
dec->recon_out_buffer_pos = 0;
|
||||||
|
}
|
||||||
|
if (dec->store_xmp == 0 &&
|
||||||
|
memcmp(dec->box_decoded_type, "xml ", 4) == 0) {
|
||||||
|
dec->store_xmp = 1;
|
||||||
|
dec->recon_out_buffer_pos = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (dec->events_wanted & JXL_DEC_BOX) {
|
if (dec->events_wanted & JXL_DEC_BOX) {
|
||||||
bool decompress =
|
bool decompress =
|
||||||
dec->decompress_boxes && memcmp(dec->box_type, "brob", 4) == 0;
|
dec->decompress_boxes && memcmp(dec->box_type, "brob", 4) == 0;
|
||||||
dec->box_content_decoder.StartBox(
|
dec->box_content_decoder.StartBox(
|
||||||
decompress, dec->box_contents_unbounded, dec->box_contents_size);
|
decompress, dec->box_contents_unbounded, dec->box_contents_size);
|
||||||
}
|
}
|
||||||
|
if (dec->store_exif == 1 || dec->store_xmp == 1) {
|
||||||
|
bool brob = memcmp(dec->box_type, "brob", 4) == 0;
|
||||||
|
dec->metadata_decoder.StartBox(brob, dec->box_contents_unbounded,
|
||||||
|
dec->box_contents_size);
|
||||||
|
}
|
||||||
|
|
||||||
if (memcmp(dec->box_type, "jxlc", 4) == 0) {
|
if (memcmp(dec->box_type, "ftyp", 4) == 0) {
|
||||||
|
dec->box_stage = BoxStage::kFtyp;
|
||||||
|
} else if (memcmp(dec->box_type, "jxlc", 4) == 0) {
|
||||||
dec->box_stage = BoxStage::kCodestream;
|
dec->box_stage = BoxStage::kCodestream;
|
||||||
} else if (memcmp(dec->box_type, "jxlp", 4) == 0) {
|
} else if (memcmp(dec->box_type, "jxlp", 4) == 0) {
|
||||||
dec->box_stage = BoxStage::kPartialCodestream;
|
dec->box_stage = BoxStage::kPartialCodestream;
|
||||||
} else if (memcmp(dec->box_type, "jbrd", 4) == 0) {
|
} else if ((dec->orig_events_wanted & JXL_DEC_JPEG_RECONSTRUCTION) &&
|
||||||
|
memcmp(dec->box_type, "jbrd", 4) == 0) {
|
||||||
|
if (!(dec->events_wanted & JXL_DEC_JPEG_RECONSTRUCTION)) {
|
||||||
|
return JXL_API_ERROR(
|
||||||
|
"multiple JPEG reconstruction boxes not supported");
|
||||||
|
}
|
||||||
dec->box_stage = BoxStage::kJpegRecon;
|
dec->box_stage = BoxStage::kJpegRecon;
|
||||||
} else {
|
} else {
|
||||||
dec->box_stage = BoxStage::kSkip;
|
dec->box_stage = BoxStage::kSkip;
|
||||||
|
@ -1642,6 +1832,16 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
dec->box_out_buffer_set_current_box = false;
|
dec->box_out_buffer_set_current_box = false;
|
||||||
return JXL_DEC_BOX;
|
return JXL_DEC_BOX;
|
||||||
}
|
}
|
||||||
|
} else if (dec->box_stage == BoxStage::kFtyp) {
|
||||||
|
if (dec->box_contents_size < 12) {
|
||||||
|
return JXL_API_ERROR("file type box too small");
|
||||||
|
}
|
||||||
|
if (dec->avail_in < 4) return JXL_DEC_NEED_MORE_INPUT;
|
||||||
|
if (memcmp(dec->next_in, "jxl ", 4) != 0) {
|
||||||
|
return JXL_API_ERROR("file type box major brand must be \"jxl \"");
|
||||||
|
}
|
||||||
|
dec->AdvanceInput(4);
|
||||||
|
dec->box_stage = BoxStage::kSkip;
|
||||||
} else if (dec->box_stage == BoxStage::kPartialCodestream) {
|
} else if (dec->box_stage == BoxStage::kPartialCodestream) {
|
||||||
if (dec->last_codestream_seen) {
|
if (dec->last_codestream_seen) {
|
||||||
return JXL_API_ERROR("cannot have codestream after last codestream");
|
return JXL_API_ERROR("cannot have codestream after last codestream");
|
||||||
|
@ -1681,19 +1881,29 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
|
|
||||||
JxlDecoderStatus status =
|
JxlDecoderStatus status =
|
||||||
jxl::JxlDecoderProcessCodestream(dec, codestream, avail_codestream);
|
jxl::JxlDecoderProcessCodestream(dec, codestream, avail_codestream);
|
||||||
if (!have_copy && status == JXL_DEC_NEED_MORE_INPUT) {
|
if (status == JXL_DEC_FULL_IMAGE) {
|
||||||
dec->codestream_copy.insert(dec->codestream_copy.end(), dec->next_in,
|
if (dec->recon_output_jpeg != JpegReconStage::kNone) {
|
||||||
dec->next_in + avail_codestream);
|
continue;
|
||||||
dec->AdvanceInput(avail_codestream);
|
}
|
||||||
}
|
}
|
||||||
|
if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||||
|
if (!have_copy) {
|
||||||
|
dec->codestream_copy.insert(dec->codestream_copy.end(), dec->next_in,
|
||||||
|
dec->next_in + avail_codestream);
|
||||||
|
dec->AdvanceInput(avail_codestream);
|
||||||
|
}
|
||||||
|
|
||||||
if (status == JXL_DEC_NEED_MORE_INPUT &&
|
if (dec->file_pos == dec->box_contents_end) {
|
||||||
dec->file_pos == dec->box_contents_end) {
|
dec->box_stage = BoxStage::kHeader;
|
||||||
dec->box_stage = BoxStage::kHeader;
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == JXL_DEC_SUCCESS) {
|
if (status == JXL_DEC_SUCCESS) {
|
||||||
|
if (dec->JbrdNeedMoreBoxes()) {
|
||||||
|
dec->box_stage = BoxStage::kSkip;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (dec->box_contents_unbounded) {
|
if (dec->box_contents_unbounded) {
|
||||||
// Last box reached and codestream done, nothing more to do.
|
// Last box reached and codestream done, nothing more to do.
|
||||||
dec->AdvanceInput(dec->avail_in);
|
dec->AdvanceInput(dec->avail_in);
|
||||||
|
@ -1704,7 +1914,7 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
dec->box_stage = BoxStage::kSkip;
|
dec->box_stage = BoxStage::kSkip;
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// Codestreaam decoded, and no box output requested, skip all further
|
// Codestream decoded, and no box output requested, skip all further
|
||||||
// input and return success.
|
// input and return success.
|
||||||
dec->AdvanceInput(dec->avail_in);
|
dec->AdvanceInput(dec->avail_in);
|
||||||
break;
|
break;
|
||||||
|
@ -1724,6 +1934,30 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
size_t consumed = next_in - dec->next_in;
|
size_t consumed = next_in - dec->next_in;
|
||||||
dec->AdvanceInput(consumed);
|
dec->AdvanceInput(consumed);
|
||||||
if (recon_result == JXL_DEC_JPEG_RECONSTRUCTION) {
|
if (recon_result == JXL_DEC_JPEG_RECONSTRUCTION) {
|
||||||
|
jxl::jpeg::JPEGData* jpeg_data = dec->jpeg_decoder.GetJpegData();
|
||||||
|
size_t num_exif = jxl::JxlToJpegDecoder::NumExifMarkers(*jpeg_data);
|
||||||
|
size_t num_xmp = jxl::JxlToJpegDecoder::NumXmpMarkers(*jpeg_data);
|
||||||
|
if (num_exif) {
|
||||||
|
if (num_exif > 1) {
|
||||||
|
return JXL_API_ERROR(
|
||||||
|
"multiple exif markers for JPEG reconstruction not supported");
|
||||||
|
}
|
||||||
|
if (JXL_DEC_SUCCESS != jxl::JxlToJpegDecoder::ExifBoxContentSize(
|
||||||
|
*jpeg_data, &dec->recon_exif_size)) {
|
||||||
|
return JXL_API_ERROR("invalid jbrd exif size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (num_xmp) {
|
||||||
|
if (num_xmp > 1) {
|
||||||
|
return JXL_API_ERROR(
|
||||||
|
"multiple XMP markers for JPEG reconstruction not supported");
|
||||||
|
}
|
||||||
|
if (JXL_DEC_SUCCESS != jxl::JxlToJpegDecoder::XmlBoxContentSize(
|
||||||
|
*jpeg_data, &dec->recon_xmp_size)) {
|
||||||
|
return JXL_API_ERROR("invalid jbrd XMP size");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dec->box_stage = BoxStage::kHeader;
|
dec->box_stage = BoxStage::kHeader;
|
||||||
// If successful JPEG reconstruction, return the success if the user
|
// If successful JPEG reconstruction, return the success if the user
|
||||||
// cares about it, otherwise continue.
|
// cares about it, otherwise continue.
|
||||||
|
@ -1737,9 +1971,19 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
}
|
}
|
||||||
} else if (dec->box_stage == BoxStage::kSkip) {
|
} else if (dec->box_stage == BoxStage::kSkip) {
|
||||||
if (dec->box_contents_unbounded) {
|
if (dec->box_contents_unbounded) {
|
||||||
// Nothing further to do, an unbounded box is the last box,
|
if (dec->input_closed) {
|
||||||
// can end early.
|
return JXL_DEC_SUCCESS;
|
||||||
break;
|
}
|
||||||
|
if (!(dec->box_out_buffer_set)) {
|
||||||
|
// An unbounded box is always the last box. Not requesting box data,
|
||||||
|
// so return success even if JxlDecoderCloseInput was not called for
|
||||||
|
// backwards compatibility as well as efficiency since this box is
|
||||||
|
// being skipped.
|
||||||
|
return JXL_DEC_SUCCESS;
|
||||||
|
}
|
||||||
|
// Arbitrarily more bytes may follow, only JxlDecoderCloseInput can
|
||||||
|
// mark the end.
|
||||||
|
return JXL_DEC_NEED_MORE_INPUT;
|
||||||
}
|
}
|
||||||
// Amount of remaining bytes in the box that is being skipped.
|
// Amount of remaining bytes in the box that is being skipped.
|
||||||
size_t remaining = dec->box_contents_end - dec->file_pos;
|
size_t remaining = dec->box_contents_end - dec->file_pos;
|
||||||
|
@ -1760,11 +2004,58 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dec->stage != DecoderStage::kFinished) {
|
return JXL_DEC_SUCCESS;
|
||||||
return JXL_API_ERROR("codestream never finished");
|
}
|
||||||
|
|
||||||
|
JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||||
|
if (dec->stage == DecoderStage::kInited) {
|
||||||
|
dec->stage = DecoderStage::kStarted;
|
||||||
|
}
|
||||||
|
if (dec->stage == DecoderStage::kError) {
|
||||||
|
return JXL_API_ERROR(
|
||||||
|
"Cannot keep using decoder after it encountered an error, use "
|
||||||
|
"JxlDecoderReset to reset it");
|
||||||
}
|
}
|
||||||
|
|
||||||
return JXL_DEC_SUCCESS;
|
if (!dec->got_signature) {
|
||||||
|
JxlSignature sig = JxlSignatureCheck(dec->next_in, dec->avail_in);
|
||||||
|
if (sig == JXL_SIG_INVALID) return JXL_API_ERROR("invalid signature");
|
||||||
|
if (sig == JXL_SIG_NOT_ENOUGH_BYTES) {
|
||||||
|
if (dec->input_closed) {
|
||||||
|
return JXL_API_ERROR("file too small for signature");
|
||||||
|
}
|
||||||
|
return JXL_DEC_NEED_MORE_INPUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
dec->got_signature = true;
|
||||||
|
|
||||||
|
if (sig == JXL_SIG_CONTAINER) {
|
||||||
|
dec->have_container = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlDecoderStatus status = HandleBoxes(dec);
|
||||||
|
|
||||||
|
if (status == JXL_DEC_NEED_MORE_INPUT && dec->input_closed) {
|
||||||
|
return JXL_API_ERROR("missing input");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Even if the box handling returns success, certain types of
|
||||||
|
// data may be missing.
|
||||||
|
if (status == JXL_DEC_SUCCESS) {
|
||||||
|
if (dec->stage != DecoderStage::kFinished) {
|
||||||
|
// TODO(lode): consider not returning this error if only subscribed to
|
||||||
|
// the JXL_DEC_BOX event and so finishing the image frames is not
|
||||||
|
// required.
|
||||||
|
return JXL_API_ERROR("codestream never finished");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dec->JbrdNeedMoreBoxes()) {
|
||||||
|
return JXL_API_ERROR("missing metadata boxes for jpeg reconstruction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
// To ensure ABI forward-compatibility, this struct has a constant size.
|
// To ensure ABI forward-compatibility, this struct has a constant size.
|
||||||
|
|
|
@ -1474,6 +1474,7 @@ struct PixelTestConfig {
|
||||||
// Exif orientation, 1-8
|
// Exif orientation, 1-8
|
||||||
JxlOrientation orientation;
|
JxlOrientation orientation;
|
||||||
bool keep_orientation;
|
bool keep_orientation;
|
||||||
|
size_t upsampling;
|
||||||
};
|
};
|
||||||
|
|
||||||
class DecodeTestParam : public ::testing::TestWithParam<PixelTestConfig> {};
|
class DecodeTestParam : public ::testing::TestWithParam<PixelTestConfig> {};
|
||||||
|
@ -1495,6 +1496,8 @@ TEST_P(DecodeTestParam, PixelTest) {
|
||||||
0};
|
0};
|
||||||
jxl::CompressParams cparams;
|
jxl::CompressParams cparams;
|
||||||
cparams.SetLossless(); // Lossless to verify pixels exactly after roundtrip.
|
cparams.SetLossless(); // Lossless to verify pixels exactly after roundtrip.
|
||||||
|
cparams.resampling = config.upsampling;
|
||||||
|
cparams.ec_resampling = config.upsampling;
|
||||||
jxl::PaddedBytes compressed = jxl::CreateTestJXLCodestream(
|
jxl::PaddedBytes compressed = jxl::CreateTestJXLCodestream(
|
||||||
jxl::Span<const uint8_t>(pixels.data(), pixels.size()), config.xsize,
|
jxl::Span<const uint8_t>(pixels.data(), pixels.size()), config.xsize,
|
||||||
config.ysize, orig_channels, cparams, config.add_container,
|
config.ysize, orig_channels, cparams, config.add_container,
|
||||||
|
@ -1540,9 +1543,19 @@ TEST_P(DecodeTestParam, PixelTest) {
|
||||||
xsize * 2 * orig_channels, nullptr, pixels.data(), pixels.size(),
|
xsize * 2 * orig_channels, nullptr, pixels.data(), pixels.size(),
|
||||||
nullptr, nullptr, static_cast<jxl::Orientation>(config.orientation)));
|
nullptr, nullptr, static_cast<jxl::Orientation>(config.orientation)));
|
||||||
}
|
}
|
||||||
|
if (config.upsampling == 1) {
|
||||||
EXPECT_EQ(0u, ComparePixels(pixels.data(), pixels2.data(), xsize, ysize,
|
EXPECT_EQ(0u, ComparePixels(pixels.data(), pixels2.data(), xsize, ysize,
|
||||||
format_orig, format));
|
format_orig, format));
|
||||||
|
} else {
|
||||||
|
// resampling is of course not lossless, so as a rough check:
|
||||||
|
// count pixels that are more than off-by-25 in the 8-bit value of one of
|
||||||
|
// the channels
|
||||||
|
EXPECT_LE(
|
||||||
|
ComparePixels(
|
||||||
|
pixels.data(), pixels2.data(), xsize, ysize, format_orig, format,
|
||||||
|
50.0 * (config.data_type == JXL_TYPE_UINT8 ? 1.0 : 256.0)),
|
||||||
|
300u);
|
||||||
|
}
|
||||||
|
|
||||||
JxlDecoderDestroy(dec);
|
JxlDecoderDestroy(dec);
|
||||||
}
|
}
|
||||||
|
@ -1581,7 +1594,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
|
||||||
CodeStreamBoxFormat box, JxlOrientation orientation,
|
CodeStreamBoxFormat box, JxlOrientation orientation,
|
||||||
bool keep_orientation, OutputFormat format,
|
bool keep_orientation, OutputFormat format,
|
||||||
bool use_callback, bool set_buffer_early,
|
bool use_callback, bool set_buffer_early,
|
||||||
bool resizable_runner) {
|
bool resizable_runner, size_t upsampling) {
|
||||||
PixelTestConfig c;
|
PixelTestConfig c;
|
||||||
c.grayscale = ch.grayscale;
|
c.grayscale = ch.grayscale;
|
||||||
c.include_alpha = ch.include_alpha;
|
c.include_alpha = ch.include_alpha;
|
||||||
|
@ -1597,17 +1610,21 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
|
||||||
c.use_resizable_runner = resizable_runner;
|
c.use_resizable_runner = resizable_runner;
|
||||||
c.orientation = orientation;
|
c.orientation = orientation;
|
||||||
c.keep_orientation = keep_orientation;
|
c.keep_orientation = keep_orientation;
|
||||||
|
c.upsampling = upsampling;
|
||||||
all_tests.push_back(c);
|
all_tests.push_back(c);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Test output formats and methods.
|
// Test output formats and methods.
|
||||||
for (ChannelInfo ch : ch_info) {
|
for (ChannelInfo ch : ch_info) {
|
||||||
for (int use_callback = 0; use_callback <= 1; use_callback++) {
|
for (int use_callback = 0; use_callback <= 1; use_callback++) {
|
||||||
for (OutputFormat fmt : out_formats) {
|
for (size_t upsampling : {1, 2, 4, 8}) {
|
||||||
make_test(ch, 301, 33, /*add_preview=*/false,
|
for (OutputFormat fmt : out_formats) {
|
||||||
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
|
make_test(ch, 301, 33, /*add_preview=*/false,
|
||||||
/*keep_orientation=*/false, fmt, use_callback,
|
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
|
||||||
/*set_buffer_early=*/false, /*resizable_runner=*/false);
|
/*keep_orientation=*/false, fmt, use_callback,
|
||||||
|
/*set_buffer_early=*/false, /*resizable_runner=*/false,
|
||||||
|
upsampling);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1617,21 +1634,21 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
|
||||||
(CodeStreamBoxFormat)box, JXL_ORIENT_IDENTITY,
|
(CodeStreamBoxFormat)box, JXL_ORIENT_IDENTITY,
|
||||||
/*keep_orientation=*/false, out_formats[0],
|
/*keep_orientation=*/false, out_formats[0],
|
||||||
/*use_callback=*/false,
|
/*use_callback=*/false,
|
||||||
/*set_buffer_early=*/false, /*resizable_runner=*/false);
|
/*set_buffer_early=*/false, /*resizable_runner=*/false, 1);
|
||||||
}
|
}
|
||||||
// Test previews.
|
// Test previews.
|
||||||
for (int add_preview = 0; add_preview <= 1; add_preview++) {
|
for (int add_preview = 0; add_preview <= 1; add_preview++) {
|
||||||
make_test(ch_info[0], 77, 33, add_preview, CodeStreamBoxFormat::kCSBF_None,
|
make_test(ch_info[0], 77, 33, add_preview, CodeStreamBoxFormat::kCSBF_None,
|
||||||
JXL_ORIENT_IDENTITY, /*keep_orientation=*/false, out_formats[0],
|
JXL_ORIENT_IDENTITY, /*keep_orientation=*/false, out_formats[0],
|
||||||
/*use_callback=*/false, /*set_buffer_early=*/false,
|
/*use_callback=*/false, /*set_buffer_early=*/false,
|
||||||
/*resizable_runner=*/false);
|
/*resizable_runner=*/false, 1);
|
||||||
}
|
}
|
||||||
// Test setting buffers early.
|
// Test setting buffers early.
|
||||||
make_test(ch_info[0], 300, 33, /*add_preview=*/false,
|
make_test(ch_info[0], 300, 33, /*add_preview=*/false,
|
||||||
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
|
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
|
||||||
/*keep_orientation=*/false, out_formats[0],
|
/*keep_orientation=*/false, out_formats[0],
|
||||||
/*use_callback=*/false, /*set_buffer_early=*/true,
|
/*use_callback=*/false, /*set_buffer_early=*/true,
|
||||||
/*resizable_runner=*/false);
|
/*resizable_runner=*/false, 1);
|
||||||
|
|
||||||
// Test using the resizable runner
|
// Test using the resizable runner
|
||||||
for (size_t i = 0; i < 4; i++) {
|
for (size_t i = 0; i < 4; i++) {
|
||||||
|
@ -1639,7 +1656,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
|
||||||
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
|
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
|
||||||
/*keep_orientation=*/false, out_formats[0],
|
/*keep_orientation=*/false, out_formats[0],
|
||||||
/*use_callback=*/false, /*set_buffer_early=*/false,
|
/*use_callback=*/false, /*set_buffer_early=*/false,
|
||||||
/*resizable_runner=*/true);
|
/*resizable_runner=*/true, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test orientations.
|
// Test orientations.
|
||||||
|
@ -1649,13 +1666,13 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
|
||||||
static_cast<JxlOrientation>(orientation),
|
static_cast<JxlOrientation>(orientation),
|
||||||
/*keep_orientation=*/false, out_formats[0],
|
/*keep_orientation=*/false, out_formats[0],
|
||||||
/*use_callback=*/false, /*set_buffer_early=*/true,
|
/*use_callback=*/false, /*set_buffer_early=*/true,
|
||||||
/*resizable_runner=*/false);
|
/*resizable_runner=*/false, 1);
|
||||||
make_test(ch_info[0], 280, 12, /*add_preview=*/false,
|
make_test(ch_info[0], 280, 12, /*add_preview=*/false,
|
||||||
CodeStreamBoxFormat::kCSBF_None,
|
CodeStreamBoxFormat::kCSBF_None,
|
||||||
static_cast<JxlOrientation>(orientation),
|
static_cast<JxlOrientation>(orientation),
|
||||||
/*keep_orientation=*/true, out_formats[0],
|
/*keep_orientation=*/true, out_formats[0],
|
||||||
/*use_callback=*/false, /*set_buffer_early=*/true,
|
/*use_callback=*/false, /*set_buffer_early=*/true,
|
||||||
/*resizable_runner=*/false);
|
/*resizable_runner=*/false, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return all_tests;
|
return all_tests;
|
||||||
|
@ -1706,6 +1723,7 @@ std::ostream& operator<<(std::ostream& os, const PixelTestConfig& c) {
|
||||||
if (c.use_resizable_runner) os << "ResizableRunner";
|
if (c.use_resizable_runner) os << "ResizableRunner";
|
||||||
if (c.orientation != 1) os << "O" << c.orientation;
|
if (c.orientation != 1) os << "O" << c.orientation;
|
||||||
if (c.keep_orientation) os << "Keep";
|
if (c.keep_orientation) os << "Keep";
|
||||||
|
if (c.upsampling > 1) os << "x" << c.upsampling;
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3315,6 +3333,14 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
|
||||||
VerifyJPEGReconstruction(container, orig);
|
VerifyJPEGReconstruction(container, orig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionMetadataTest)) {
|
||||||
|
const std::string jpeg_path = "jxl/jpeg_reconstruction/1x1_exif_xmp.jpg";
|
||||||
|
const std::string jxl_path = "jxl/jpeg_reconstruction/1x1_exif_xmp.jxl";
|
||||||
|
const jxl::PaddedBytes jpeg = jxl::ReadTestData(jpeg_path);
|
||||||
|
const jxl::PaddedBytes jxl = jxl::ReadTestData(jxl_path);
|
||||||
|
VerifyJPEGReconstruction(jxl, jpeg);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(DecodeTest, ContinueFinalNonEssentialBoxTest) {
|
TEST(DecodeTest, ContinueFinalNonEssentialBoxTest) {
|
||||||
size_t xsize = 80, ysize = 90;
|
size_t xsize = 80, ysize = 90;
|
||||||
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
|
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
|
||||||
|
@ -3426,6 +3452,10 @@ TEST(DecodeTest, BoxTest) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Even though all input is given, the decoder cannot assume there aren't
|
||||||
|
// more boxes if the input was not closed.
|
||||||
|
EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec));
|
||||||
|
JxlDecoderCloseInput(dec);
|
||||||
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
|
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
|
||||||
|
|
||||||
JxlDecoderDestroy(dec);
|
JxlDecoderDestroy(dec);
|
||||||
|
@ -3448,6 +3478,7 @@ TEST(DecodeTest, ExifBrobBoxTest) {
|
||||||
if (!streaming) {
|
if (!streaming) {
|
||||||
EXPECT_EQ(JXL_DEC_SUCCESS,
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
|
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
|
||||||
|
JxlDecoderCloseInput(dec);
|
||||||
}
|
}
|
||||||
// for streaming input case
|
// for streaming input case
|
||||||
const uint8_t* next_in = compressed.data();
|
const uint8_t* next_in = compressed.data();
|
||||||
|
@ -3476,6 +3507,7 @@ TEST(DecodeTest, ExifBrobBoxTest) {
|
||||||
total_in += amount;
|
total_in += amount;
|
||||||
EXPECT_EQ(JXL_DEC_SUCCESS,
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
JxlDecoderSetInput(dec, next_in, avail_in));
|
JxlDecoderSetInput(dec, next_in, avail_in));
|
||||||
|
if (total_in == compressed.size()) JxlDecoderCloseInput(dec);
|
||||||
} else {
|
} else {
|
||||||
FAIL();
|
FAIL();
|
||||||
break;
|
break;
|
||||||
|
@ -3528,6 +3560,7 @@ TEST(DecodeTest, ExifBrobBoxTest) {
|
||||||
if (!streaming) {
|
if (!streaming) {
|
||||||
EXPECT_EQ(JXL_DEC_SUCCESS,
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
|
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
|
||||||
|
JxlDecoderCloseInput(dec);
|
||||||
}
|
}
|
||||||
// for streaming input case
|
// for streaming input case
|
||||||
const uint8_t* next_in = compressed.data();
|
const uint8_t* next_in = compressed.data();
|
||||||
|
@ -3558,6 +3591,7 @@ TEST(DecodeTest, ExifBrobBoxTest) {
|
||||||
total_in += amount;
|
total_in += amount;
|
||||||
EXPECT_EQ(JXL_DEC_SUCCESS,
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
JxlDecoderSetInput(dec, next_in, avail_in));
|
JxlDecoderSetInput(dec, next_in, avail_in));
|
||||||
|
if (total_in == compressed.size()) JxlDecoderCloseInput(dec);
|
||||||
} else {
|
} else {
|
||||||
FAIL();
|
FAIL();
|
||||||
break;
|
break;
|
||||||
|
@ -3628,6 +3662,7 @@ TEST(DecodeTest, PartialCodestreamBoxTest) {
|
||||||
dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_BOX));
|
dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_BOX));
|
||||||
EXPECT_EQ(JXL_DEC_SUCCESS,
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
|
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
|
||||||
|
JxlDecoderCloseInput(dec);
|
||||||
|
|
||||||
size_t num_jxlp = 0;
|
size_t num_jxlp = 0;
|
||||||
|
|
||||||
|
@ -3707,6 +3742,7 @@ TEST(DecodeTest, PartialCodestreamBoxTest) {
|
||||||
EXPECT_EQ(JXL_DEC_SUCCESS,
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
JxlDecoderSetInput(dec, extracted_codestream.data(),
|
JxlDecoderSetInput(dec, extracted_codestream.data(),
|
||||||
extracted_codestream.size()));
|
extracted_codestream.size()));
|
||||||
|
JxlDecoderCloseInput(dec);
|
||||||
|
|
||||||
size_t num_boxes = 0;
|
size_t num_boxes = 0;
|
||||||
|
|
||||||
|
@ -3756,3 +3792,140 @@ TEST(DecodeTest, PartialCodestreamBoxTest) {
|
||||||
JxlDecoderDestroy(dec);
|
JxlDecoderDestroy(dec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(DecodeTest, SpotColorTest) {
|
||||||
|
jxl::ThreadPool* pool = nullptr;
|
||||||
|
jxl::CodecInOut io;
|
||||||
|
size_t xsize = 55, ysize = 257;
|
||||||
|
io.metadata.m.color_encoding = jxl::ColorEncoding::LinearSRGB();
|
||||||
|
jxl::Image3F main(xsize, ysize);
|
||||||
|
jxl::ImageF spot(xsize, ysize);
|
||||||
|
jxl::ZeroFillImage(&main);
|
||||||
|
jxl::ZeroFillImage(&spot);
|
||||||
|
|
||||||
|
for (size_t y = 0; y < ysize; y++) {
|
||||||
|
float* JXL_RESTRICT rowm = main.PlaneRow(1, y);
|
||||||
|
float* JXL_RESTRICT rows = spot.Row(y);
|
||||||
|
for (size_t x = 0; x < xsize; x++) {
|
||||||
|
rowm[x] = (x + y) * (1.f / 255.f);
|
||||||
|
rows[x] = ((x ^ y) & 255) * (1.f / 255.f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
io.SetFromImage(std::move(main), jxl::ColorEncoding::LinearSRGB());
|
||||||
|
jxl::ExtraChannelInfo info;
|
||||||
|
info.bit_depth.bits_per_sample = 8;
|
||||||
|
info.dim_shift = 0;
|
||||||
|
info.type = jxl::ExtraChannel::kSpotColor;
|
||||||
|
info.spot_color[0] = 0.5f;
|
||||||
|
info.spot_color[1] = 0.2f;
|
||||||
|
info.spot_color[2] = 1.f;
|
||||||
|
info.spot_color[3] = 0.5f;
|
||||||
|
|
||||||
|
io.metadata.m.extra_channel_info.push_back(info);
|
||||||
|
std::vector<jxl::ImageF> ec;
|
||||||
|
ec.push_back(std::move(spot));
|
||||||
|
io.frames[0].SetExtraChannels(std::move(ec));
|
||||||
|
|
||||||
|
jxl::CompressParams cparams;
|
||||||
|
cparams.speed_tier = jxl::SpeedTier::kLightning;
|
||||||
|
cparams.modular_mode = true;
|
||||||
|
cparams.color_transform = jxl::ColorTransform::kNone;
|
||||||
|
cparams.quality_pair = {100, 100};
|
||||||
|
|
||||||
|
jxl::PaddedBytes compressed;
|
||||||
|
std::unique_ptr<jxl::PassesEncoderState> enc_state =
|
||||||
|
jxl::make_unique<jxl::PassesEncoderState>();
|
||||||
|
EXPECT_TRUE(jxl::EncodeFile(cparams, &io, enc_state.get(), &compressed,
|
||||||
|
nullptr, pool));
|
||||||
|
|
||||||
|
for (size_t render_spot = 0; render_spot < 2; render_spot++) {
|
||||||
|
JxlPixelFormat format = {3, JXL_TYPE_UINT8, JXL_LITTLE_ENDIAN, 0};
|
||||||
|
|
||||||
|
JxlDecoder* dec = JxlDecoderCreate(NULL);
|
||||||
|
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
|
JxlDecoderSubscribeEvents(
|
||||||
|
dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE));
|
||||||
|
if (!render_spot) {
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetRenderSpotcolors(dec, JXL_FALSE));
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
|
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
|
||||||
|
EXPECT_EQ(JXL_DEC_BASIC_INFO, JxlDecoderProcessInput(dec));
|
||||||
|
JxlBasicInfo binfo;
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderGetBasicInfo(dec, &binfo));
|
||||||
|
EXPECT_EQ(1u, binfo.num_extra_channels);
|
||||||
|
EXPECT_EQ(xsize, binfo.xsize);
|
||||||
|
EXPECT_EQ(ysize, binfo.ysize);
|
||||||
|
|
||||||
|
JxlExtraChannelInfo extra_info;
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
|
JxlDecoderGetExtraChannelInfo(dec, 0, &extra_info));
|
||||||
|
EXPECT_EQ((unsigned int)jxl::ExtraChannel::kSpotColor, extra_info.type);
|
||||||
|
|
||||||
|
EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
|
||||||
|
size_t buffer_size;
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
|
JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
|
||||||
|
size_t extra_size;
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
|
JxlDecoderExtraChannelBufferSize(dec, &format, &extra_size, 0));
|
||||||
|
|
||||||
|
std::vector<uint8_t> image(buffer_size);
|
||||||
|
std::vector<uint8_t> extra(extra_size);
|
||||||
|
size_t bytes_per_pixel =
|
||||||
|
format.num_channels * GetDataBits(format.data_type) / jxl::kBitsPerByte;
|
||||||
|
size_t stride = bytes_per_pixel * binfo.xsize;
|
||||||
|
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetImageOutBuffer(
|
||||||
|
dec, &format, image.data(), image.size()));
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
|
JxlDecoderSetExtraChannelBuffer(dec, &format, extra.data(),
|
||||||
|
extra.size(), 0));
|
||||||
|
|
||||||
|
EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
|
||||||
|
|
||||||
|
// After the full image was output, JxlDecoderProcessInput should return
|
||||||
|
// success to indicate all is done.
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
|
||||||
|
JxlDecoderDestroy(dec);
|
||||||
|
|
||||||
|
for (size_t y = 0; y < ysize; y++) {
|
||||||
|
uint8_t* JXL_RESTRICT rowm = image.data() + stride * y;
|
||||||
|
uint8_t* JXL_RESTRICT rows = extra.data() + xsize * y;
|
||||||
|
for (size_t x = 0; x < xsize; x++) {
|
||||||
|
if (!render_spot) {
|
||||||
|
// if spot color isn't rendered, main image should be as we made it
|
||||||
|
// (red and blue are all zeroes)
|
||||||
|
|
||||||
|
EXPECT_EQ(rowm[x * 3 + 0], 0);
|
||||||
|
EXPECT_EQ(rowm[x * 3 + 1], (x + y > 255 ? 255 : x + y));
|
||||||
|
EXPECT_EQ(rowm[x * 3 + 2], 0);
|
||||||
|
}
|
||||||
|
if (render_spot) {
|
||||||
|
// if spot color is rendered, expect red and blue to look like the
|
||||||
|
// spot color channel
|
||||||
|
EXPECT_LT(abs(rowm[x * 3 + 0] - (rows[x] * 0.25f)), 1);
|
||||||
|
EXPECT_LT(abs(rowm[x * 3 + 2] - (rows[x] * 0.5f)), 1);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(rows[x], ((x ^ y) & 255));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(DecodeTest, CloseInput) {
|
||||||
|
std::vector<uint8_t> partial_file = {0xff};
|
||||||
|
|
||||||
|
JxlDecoderPtr dec = JxlDecoderMake(nullptr);
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS,
|
||||||
|
JxlDecoderSubscribeEvents(dec.get(),
|
||||||
|
JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE));
|
||||||
|
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec.get(), partial_file.data(),
|
||||||
|
partial_file.size()));
|
||||||
|
EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec.get()));
|
||||||
|
EXPECT_EQ(JXL_DEC_NEED_MORE_INPUT, JxlDecoderProcessInput(dec.get()));
|
||||||
|
JxlDecoderCloseInput(dec.get());
|
||||||
|
EXPECT_EQ(JXL_DEC_ERROR, JxlDecoderProcessInput(dec.get()));
|
||||||
|
}
|
||||||
|
|
|
@ -72,6 +72,98 @@ JxlDecoderStatus JxlToJpegDecoder::Process(const uint8_t** next_in,
|
||||||
return JXL_DEC_NEED_MORE_INPUT;
|
return JXL_DEC_NEED_MORE_INPUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t JxlToJpegDecoder::NumExifMarkers(const jpeg::JPEGData& jpeg_data) {
|
||||||
|
size_t num = 0;
|
||||||
|
for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) {
|
||||||
|
if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kExif) {
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t JxlToJpegDecoder::NumXmpMarkers(const jpeg::JPEGData& jpeg_data) {
|
||||||
|
size_t num = 0;
|
||||||
|
for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) {
|
||||||
|
if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kXMP) {
|
||||||
|
num++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return num;
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlDecoderStatus JxlToJpegDecoder::ExifBoxContentSize(
|
||||||
|
const jpeg::JPEGData& jpeg_data, size_t* size) {
|
||||||
|
for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) {
|
||||||
|
if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kExif) {
|
||||||
|
if (jpeg_data.app_data[i].size() < 3 + sizeof(jpeg::kExifTag)) {
|
||||||
|
// too small for app marker header
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
// The first 4 bytes are the TIFF header from the box contents, and are
|
||||||
|
// not included in the JPEG
|
||||||
|
*size = jpeg_data.app_data[i].size() + 4 - 3 - sizeof(jpeg::kExifTag);
|
||||||
|
return JXL_DEC_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlDecoderStatus JxlToJpegDecoder::XmlBoxContentSize(
|
||||||
|
const jpeg::JPEGData& jpeg_data, size_t* size) {
|
||||||
|
for (size_t i = 0; i < jpeg_data.app_data.size(); ++i) {
|
||||||
|
if (jpeg_data.app_marker_type[i] == jxl::jpeg::AppMarkerType::kXMP) {
|
||||||
|
if (jpeg_data.app_data[i].size() < 3 + sizeof(jpeg::kXMPTag)) {
|
||||||
|
// too small for app marker header
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
*size = jpeg_data.app_data[i].size() - 3 - sizeof(jpeg::kXMPTag);
|
||||||
|
return JXL_DEC_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlDecoderStatus JxlToJpegDecoder::SetExif(const uint8_t* data, size_t size,
|
||||||
|
jpeg::JPEGData* jpeg_data) {
|
||||||
|
for (size_t i = 0; i < jpeg_data->app_data.size(); ++i) {
|
||||||
|
if (jpeg_data->app_marker_type[i] == jxl::jpeg::AppMarkerType::kExif) {
|
||||||
|
if (jpeg_data->app_data[i].size() !=
|
||||||
|
size + 3 + sizeof(jpeg::kExifTag) - 4)
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
// The first 9 bytes are used for JPEG marker header.
|
||||||
|
jpeg_data->app_data[i][0] = 0xE1;
|
||||||
|
// The second and third byte are already filled in correctly
|
||||||
|
memcpy(jpeg_data->app_data[i].data() + 3, jpeg::kExifTag,
|
||||||
|
sizeof(jpeg::kExifTag));
|
||||||
|
// The first 4 bytes are the TIFF header from the box contents, and are
|
||||||
|
// not included in the JPEG
|
||||||
|
memcpy(jpeg_data->app_data[i].data() + 3 + sizeof(jpeg::kExifTag),
|
||||||
|
data + 4, size - 4);
|
||||||
|
return JXL_DEC_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
JxlDecoderStatus JxlToJpegDecoder::SetXmp(const uint8_t* data, size_t size,
|
||||||
|
jpeg::JPEGData* jpeg_data) {
|
||||||
|
for (size_t i = 0; i < jpeg_data->app_data.size(); ++i) {
|
||||||
|
if (jpeg_data->app_marker_type[i] == jxl::jpeg::AppMarkerType::kXMP) {
|
||||||
|
if (jpeg_data->app_data[i].size() != size + 3 + sizeof(jpeg::kXMPTag))
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
// The first 9 bytes are used for JPEG marker header.
|
||||||
|
jpeg_data->app_data[i][0] = 0xE1;
|
||||||
|
// The second and third byte are already filled in correctly
|
||||||
|
memcpy(jpeg_data->app_data[i].data() + 3, jpeg::kXMPTag,
|
||||||
|
sizeof(jpeg::kXMPTag));
|
||||||
|
memcpy(jpeg_data->app_data[i].data() + 3 + sizeof(jpeg::kXMPTag), data,
|
||||||
|
size);
|
||||||
|
return JXL_DEC_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
#endif // JPEGXL_ENABLE_TRANSCODE_JPEG
|
#endif // JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||||
|
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
|
|
@ -37,12 +37,6 @@ class JxlToJpegDecoder {
|
||||||
// Returns whether the decoder is parsing a boxa JPEG box was parsed.
|
// Returns whether the decoder is parsing a boxa JPEG box was parsed.
|
||||||
bool IsParsingBox() const { return inside_box_; }
|
bool IsParsingBox() const { return inside_box_; }
|
||||||
|
|
||||||
const jpeg::JPEGData* JpegData() const { return jpeg_data_.get(); }
|
|
||||||
|
|
||||||
// Return the parsed jpeg::JPEGData object and removes it from the
|
|
||||||
// JxlToJpegDecoder.
|
|
||||||
jpeg::JPEGData* ReleaseJpegData() { return jpeg_data_.release(); }
|
|
||||||
|
|
||||||
// Sets the output buffer used when producing JPEG output.
|
// Sets the output buffer used when producing JPEG output.
|
||||||
JxlDecoderStatus SetOutputBuffer(uint8_t* data, size_t size) {
|
JxlDecoderStatus SetOutputBuffer(uint8_t* data, size_t size) {
|
||||||
if (next_out_) return JXL_DEC_ERROR;
|
if (next_out_) return JXL_DEC_ERROR;
|
||||||
|
@ -74,8 +68,39 @@ class JxlToJpegDecoder {
|
||||||
// Uses box_size_, inside_box_ and box_until_eof_ to calculate how much to
|
// Uses box_size_, inside_box_ and box_until_eof_ to calculate how much to
|
||||||
// consume. Potentially stores unparsed data in buffer_.
|
// consume. Potentially stores unparsed data in buffer_.
|
||||||
// Potentially populates jpeg_data_. Potentially updates inside_box_.
|
// Potentially populates jpeg_data_. Potentially updates inside_box_.
|
||||||
|
// Returns JXL_DEC_JPEG_RECONSTRUCTION when finished, JXL_DEC_NEED_MORE_INPUT
|
||||||
|
// if more input is needed, JXL_DEC_ERROR on parsing error.
|
||||||
JxlDecoderStatus Process(const uint8_t** next_in, size_t* avail_in);
|
JxlDecoderStatus Process(const uint8_t** next_in, size_t* avail_in);
|
||||||
|
|
||||||
|
// Returns non-owned copy of the JPEGData, only after Process finished and
|
||||||
|
// the JPEGData was not yet moved to an image bundle with
|
||||||
|
// SetImageBundleJpegData.
|
||||||
|
jpeg::JPEGData* GetJpegData() { return jpeg_data_.get(); }
|
||||||
|
|
||||||
|
// Returns how many exif or xmp app markers are present in the JPEG data. A
|
||||||
|
// return value higher than 1 would require multiple exif boxes or multiple
|
||||||
|
// xmp boxes in the container format, and this is not supported by the API and
|
||||||
|
// considered an error. May only be called after Process returned success.
|
||||||
|
static size_t NumExifMarkers(const jpeg::JPEGData& jpeg_data);
|
||||||
|
static size_t NumXmpMarkers(const jpeg::JPEGData& jpeg_data);
|
||||||
|
|
||||||
|
// Returns box content size for metadata, using the known data from the app
|
||||||
|
// markers.
|
||||||
|
static JxlDecoderStatus ExifBoxContentSize(const jpeg::JPEGData& jpeg_data,
|
||||||
|
size_t* size);
|
||||||
|
static JxlDecoderStatus XmlBoxContentSize(const jpeg::JPEGData& jpeg_data,
|
||||||
|
size_t* size);
|
||||||
|
|
||||||
|
// Returns JXL_DEC_ERROR if there is no exif/XMP marker or the data size
|
||||||
|
// does not match, or this function is called before Process returned
|
||||||
|
// success, JXL_DEC_SUCCESS otherwise. As input, provide the full box contents
|
||||||
|
// but not the box header. In case of exif, this includes the 4-byte TIFF
|
||||||
|
// header, even though it won't be copied into the JPEG.
|
||||||
|
static JxlDecoderStatus SetExif(const uint8_t* data, size_t size,
|
||||||
|
jpeg::JPEGData* jpeg_data);
|
||||||
|
static JxlDecoderStatus SetXmp(const uint8_t* data, size_t size,
|
||||||
|
jpeg::JPEGData* jpeg_data);
|
||||||
|
|
||||||
// Sets the JpegData of the ImageBundle passed if there is anything to set.
|
// Sets the JpegData of the ImageBundle passed if there is anything to set.
|
||||||
// Releases the JpegData from this decoder if set.
|
// Releases the JpegData from this decoder if set.
|
||||||
Status SetImageBundleJpegData(ImageBundle* ib) {
|
Status SetImageBundleJpegData(ImageBundle* ib) {
|
||||||
|
@ -145,9 +170,6 @@ class JxlToJpegDecoder {
|
||||||
bool IsOutputSet() const { return false; }
|
bool IsOutputSet() const { return false; }
|
||||||
bool IsParsingBox() const { return false; }
|
bool IsParsingBox() const { return false; }
|
||||||
|
|
||||||
const jpeg::JPEGData* JpegData() const { return nullptr; }
|
|
||||||
jpeg::JPEGData* ReleaseJpegData() { return nullptr; }
|
|
||||||
|
|
||||||
JxlDecoderStatus SetOutputBuffer(uint8_t* /* data */, size_t /* size */) {
|
JxlDecoderStatus SetOutputBuffer(uint8_t* /* data */, size_t /* size */) {
|
||||||
return JXL_DEC_ERROR;
|
return JXL_DEC_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -158,9 +180,31 @@ class JxlToJpegDecoder {
|
||||||
JxlDecoderStatus Process(const uint8_t** next_in, size_t* avail_in) {
|
JxlDecoderStatus Process(const uint8_t** next_in, size_t* avail_in) {
|
||||||
return JXL_DEC_ERROR;
|
return JXL_DEC_ERROR;
|
||||||
}
|
}
|
||||||
|
jpeg::JPEGData* GetJpegData() { return nullptr; }
|
||||||
|
|
||||||
Status SetImageBundleJpegData(ImageBundle* /* ib */) { return true; }
|
Status SetImageBundleJpegData(ImageBundle* /* ib */) { return true; }
|
||||||
|
|
||||||
|
static size_t NumExifMarkers(const jpeg::JPEGData& /*jpeg_data*/) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
static size_t NumXmpMarkers(const jpeg::JPEGData& /*jpeg_data*/) { return 0; }
|
||||||
|
static size_t ExifBoxContentSize(const jpeg::JPEGData& /*jpeg_data*/,
|
||||||
|
size_t* /*size*/) {
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
static size_t XmlBoxContentSize(const jpeg::JPEGData& /*jpeg_data*/,
|
||||||
|
size_t* /*size*/) {
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
static JxlDecoderStatus SetExif(const uint8_t* /*data*/, size_t /*size*/,
|
||||||
|
jpeg::JPEGData* /*jpeg_data*/) {
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
static JxlDecoderStatus SetXmp(const uint8_t* /*data*/, size_t /*size*/,
|
||||||
|
jpeg::JPEGData* /*jpeg_data*/) {
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
JxlDecoderStatus WriteOutput(const jpeg::JPEGData& /* jpeg_data */) {
|
JxlDecoderStatus WriteOutput(const jpeg::JPEGData& /* jpeg_data */) {
|
||||||
return JXL_DEC_SUCCESS;
|
return JXL_DEC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
|
@ -541,6 +541,7 @@ void ChooseUintConfigs(const HistogramParams& params,
|
||||||
std::vector<Histogram>* clustered_histograms,
|
std::vector<Histogram>* clustered_histograms,
|
||||||
EntropyEncodingData* codes, size_t* log_alpha_size) {
|
EntropyEncodingData* codes, size_t* log_alpha_size) {
|
||||||
codes->uint_config.resize(clustered_histograms->size());
|
codes->uint_config.resize(clustered_histograms->size());
|
||||||
|
|
||||||
if (params.uint_method == HistogramParams::HybridUintMethod::kNone) return;
|
if (params.uint_method == HistogramParams::HybridUintMethod::kNone) return;
|
||||||
if (params.uint_method == HistogramParams::HybridUintMethod::kContextMap) {
|
if (params.uint_method == HistogramParams::HybridUintMethod::kContextMap) {
|
||||||
codes->uint_config.clear();
|
codes->uint_config.clear();
|
||||||
|
@ -622,6 +623,9 @@ void ChooseUintConfigs(const HistogramParams& params,
|
||||||
for (size_t i = 0; i < clustered_histograms->size(); i++) {
|
for (size_t i = 0; i < clustered_histograms->size(); i++) {
|
||||||
if (!is_valid[i]) continue;
|
if (!is_valid[i]) continue;
|
||||||
float cost = (*clustered_histograms)[i].PopulationCost() + extra_bits[i];
|
float cost = (*clustered_histograms)[i].PopulationCost() + extra_bits[i];
|
||||||
|
// add signaling cost of the hybriduintconfig itself
|
||||||
|
cost += CeilLog2Nonzero(cfg.split_exponent + 1);
|
||||||
|
cost += CeilLog2Nonzero(cfg.split_exponent - cfg.msb_in_token + 1);
|
||||||
if (cost < costs[i]) {
|
if (cost < costs[i]) {
|
||||||
codes->uint_config[i] = cfg;
|
codes->uint_config[i] = cfg;
|
||||||
costs[i] = cost;
|
costs[i] = cost;
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
|
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/data_parallel.h"
|
#include "lib/jxl/base/data_parallel.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/field_encodings.h"
|
#include "lib/jxl/field_encodings.h"
|
||||||
#include "lib/jxl/linalg.h"
|
#include "lib/jxl/linalg.h"
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/data_parallel.h"
|
#include "lib/jxl/base/data_parallel.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/profiler.h"
|
#include "lib/jxl/base/profiler.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/codec_in_out.h"
|
#include "lib/jxl/codec_in_out.h"
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "lib/jxl/aux_out.h"
|
#include "lib/jxl/aux_out.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/padded_bytes.h"
|
#include "lib/jxl/base/padded_bytes.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/compressed_dc.h"
|
#include "lib/jxl/compressed_dc.h"
|
||||||
#include "lib/jxl/dec_ans.h"
|
#include "lib/jxl/dec_ans.h"
|
||||||
|
|
|
@ -23,24 +23,6 @@ namespace jxl {
|
||||||
namespace HWY_NAMESPACE {
|
namespace HWY_NAMESPACE {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
template <size_t ROWS, size_t COLS>
|
|
||||||
struct DoIDCT {
|
|
||||||
template <typename To>
|
|
||||||
void operator()(float* JXL_RESTRICT from, const To& to,
|
|
||||||
float* JXL_RESTRICT scratch_space) {
|
|
||||||
ComputeScaledIDCT<ROWS, COLS>()(from, to, scratch_space);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <size_t N>
|
|
||||||
struct DoIDCT<N, N> {
|
|
||||||
template <typename To>
|
|
||||||
void operator()(float* JXL_RESTRICT from, const To& to,
|
|
||||||
float* JXL_RESTRICT scratch_space) const {
|
|
||||||
ComputeTransposedScaledIDCT<N>()(from, to, scratch_space);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Inverse of ReinterpretingDCT.
|
// Inverse of ReinterpretingDCT.
|
||||||
template <size_t DCT_ROWS, size_t DCT_COLS, size_t LF_ROWS, size_t LF_COLS,
|
template <size_t DCT_ROWS, size_t DCT_COLS, size_t LF_ROWS, size_t LF_COLS,
|
||||||
size_t ROWS, size_t COLS>
|
size_t ROWS, size_t COLS>
|
||||||
|
@ -68,7 +50,8 @@ HWY_INLINE void ReinterpretingIDCT(const float* input,
|
||||||
|
|
||||||
// ROWS, COLS <= 8, so we can put scratch space on the stack.
|
// ROWS, COLS <= 8, so we can put scratch space on the stack.
|
||||||
HWY_ALIGN float scratch_space[ROWS * COLS];
|
HWY_ALIGN float scratch_space[ROWS * COLS];
|
||||||
DoIDCT<ROWS, COLS>()(block, DCTTo(output, output_stride), scratch_space);
|
ComputeScaledIDCT<ROWS, COLS>()(block, DCTTo(output, output_stride),
|
||||||
|
scratch_space);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <size_t S>
|
template <size_t S>
|
||||||
|
@ -435,7 +418,7 @@ void AFVTransformFromPixels(const float* JXL_RESTRICT pixels,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 4x4 DCT of the block with same y and different x.
|
// 4x4 DCT of the block with same y and different x.
|
||||||
ComputeTransposedScaledDCT<4>()(
|
ComputeScaledDCT<4, 4>()(
|
||||||
DCTFrom(pixels + afv_y * 4 * pixels_stride + (afv_x == 1 ? 0 : 4),
|
DCTFrom(pixels + afv_y * 4 * pixels_stride + (afv_x == 1 ? 0 : 4),
|
||||||
pixels_stride),
|
pixels_stride),
|
||||||
block, scratch_space);
|
block, scratch_space);
|
||||||
|
@ -545,7 +528,7 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
|
||||||
for (size_t y = 0; y < 2; y++) {
|
for (size_t y = 0; y < 2; y++) {
|
||||||
for (size_t x = 0; x < 2; x++) {
|
for (size_t x = 0; x < 2; x++) {
|
||||||
HWY_ALIGN float block[4 * 4];
|
HWY_ALIGN float block[4 * 4];
|
||||||
ComputeTransposedScaledDCT<4>()(
|
ComputeScaledDCT<4, 4>()(
|
||||||
DCTFrom(pixels + y * 4 * pixels_stride + x * 4, pixels_stride),
|
DCTFrom(pixels + y * 4 * pixels_stride + x * 4, pixels_stride),
|
||||||
block, scratch_space);
|
block, scratch_space);
|
||||||
for (size_t iy = 0; iy < 4; iy++) {
|
for (size_t iy = 0; iy < 4; iy++) {
|
||||||
|
@ -574,8 +557,8 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT16X16: {
|
case Type::DCT16X16: {
|
||||||
PROFILER_ZONE("DCT 16");
|
PROFILER_ZONE("DCT 16");
|
||||||
ComputeTransposedScaledDCT<16>()(DCTFrom(pixels, pixels_stride),
|
ComputeScaledDCT<16, 16>()(DCTFrom(pixels, pixels_stride), coefficients,
|
||||||
coefficients, scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DCT16X8: {
|
case Type::DCT16X8: {
|
||||||
|
@ -616,14 +599,14 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT32X32: {
|
case Type::DCT32X32: {
|
||||||
PROFILER_ZONE("DCT 32");
|
PROFILER_ZONE("DCT 32");
|
||||||
ComputeTransposedScaledDCT<32>()(DCTFrom(pixels, pixels_stride),
|
ComputeScaledDCT<32, 32>()(DCTFrom(pixels, pixels_stride), coefficients,
|
||||||
coefficients, scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DCT: {
|
case Type::DCT: {
|
||||||
PROFILER_ZONE("DCT 8");
|
PROFILER_ZONE("DCT 8");
|
||||||
ComputeTransposedScaledDCT<8>()(DCTFrom(pixels, pixels_stride),
|
ComputeScaledDCT<8, 8>()(DCTFrom(pixels, pixels_stride), coefficients,
|
||||||
coefficients, scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::AFV0: {
|
case Type::AFV0: {
|
||||||
|
@ -648,8 +631,8 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT64X64: {
|
case Type::DCT64X64: {
|
||||||
PROFILER_ZONE("DCT 64x64");
|
PROFILER_ZONE("DCT 64x64");
|
||||||
ComputeTransposedScaledDCT<64>()(DCTFrom(pixels, pixels_stride),
|
ComputeScaledDCT<64, 64>()(DCTFrom(pixels, pixels_stride), coefficients,
|
||||||
coefficients, scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DCT64X32: {
|
case Type::DCT64X32: {
|
||||||
|
@ -666,8 +649,8 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT128X128: {
|
case Type::DCT128X128: {
|
||||||
PROFILER_ZONE("DCT 128x128");
|
PROFILER_ZONE("DCT 128x128");
|
||||||
ComputeTransposedScaledDCT<128>()(DCTFrom(pixels, pixels_stride),
|
ComputeScaledDCT<128, 128>()(DCTFrom(pixels, pixels_stride), coefficients,
|
||||||
coefficients, scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DCT128X64: {
|
case Type::DCT128X64: {
|
||||||
|
@ -684,8 +667,8 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
|
||||||
}
|
}
|
||||||
case Type::DCT256X256: {
|
case Type::DCT256X256: {
|
||||||
PROFILER_ZONE("DCT 256x256");
|
PROFILER_ZONE("DCT 256x256");
|
||||||
ComputeTransposedScaledDCT<256>()(DCTFrom(pixels, pixels_stride),
|
ComputeScaledDCT<256, 256>()(DCTFrom(pixels, pixels_stride), coefficients,
|
||||||
coefficients, scratch_space);
|
scratch_space);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case Type::DCT256X128: {
|
case Type::DCT256X128: {
|
||||||
|
|
|
@ -48,10 +48,18 @@ JxlEncoderStatus JxlEncoderStruct::RefillOutputByteQueue() {
|
||||||
jxl::BitWriter writer;
|
jxl::BitWriter writer;
|
||||||
|
|
||||||
if (!wrote_bytes) {
|
if (!wrote_bytes) {
|
||||||
if (use_container) {
|
if (MustUseContainer()) {
|
||||||
|
// Add "JXL " and ftyp box.
|
||||||
output_byte_queue.insert(
|
output_byte_queue.insert(
|
||||||
output_byte_queue.end(), jxl::kContainerHeader,
|
output_byte_queue.end(), jxl::kContainerHeader,
|
||||||
jxl::kContainerHeader + sizeof(jxl::kContainerHeader));
|
jxl::kContainerHeader + sizeof(jxl::kContainerHeader));
|
||||||
|
if (codestream_level != 5) {
|
||||||
|
// Add jxll box.
|
||||||
|
output_byte_queue.insert(
|
||||||
|
output_byte_queue.end(), jxl::kLevelBoxHeader,
|
||||||
|
jxl::kLevelBoxHeader + sizeof(jxl::kLevelBoxHeader));
|
||||||
|
output_byte_queue.push_back(codestream_level);
|
||||||
|
}
|
||||||
if (store_jpeg_metadata && jpeg_metadata.size() > 0) {
|
if (store_jpeg_metadata && jpeg_metadata.size() > 0) {
|
||||||
jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_metadata.size(),
|
jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_metadata.size(),
|
||||||
false, &output_byte_queue);
|
false, &output_byte_queue);
|
||||||
|
@ -100,7 +108,7 @@ JxlEncoderStatus JxlEncoderStruct::RefillOutputByteQueue() {
|
||||||
|
|
||||||
jxl::PaddedBytes bytes = std::move(writer).TakeBytes();
|
jxl::PaddedBytes bytes = std::move(writer).TakeBytes();
|
||||||
|
|
||||||
if (use_container && !wrote_bytes) {
|
if (MustUseContainer() && !wrote_bytes) {
|
||||||
if (input_closed && input_frame_queue.empty()) {
|
if (input_closed && input_frame_queue.empty()) {
|
||||||
jxl::AppendBoxHeader(jxl::MakeBoxType("jxlc"), bytes.size(),
|
jxl::AppendBoxHeader(jxl::MakeBoxType("jxlc"), bytes.size(),
|
||||||
/*unbounded=*/false, &output_byte_queue);
|
/*unbounded=*/false, &output_byte_queue);
|
||||||
|
@ -251,11 +259,7 @@ JxlEncoderStatus JxlEncoderOptionsSetLossless(JxlEncoderOptions* options,
|
||||||
|
|
||||||
JxlEncoderStatus JxlEncoderOptionsSetEffort(JxlEncoderOptions* options,
|
JxlEncoderStatus JxlEncoderOptionsSetEffort(JxlEncoderOptions* options,
|
||||||
const int effort) {
|
const int effort) {
|
||||||
if (effort < 1 || effort > 9) {
|
return JxlEncoderOptionsSetInteger(options, JXL_ENC_OPTION_EFFORT, effort);
|
||||||
return JXL_ENC_ERROR;
|
|
||||||
}
|
|
||||||
options->values.cparams.speed_tier = static_cast<jxl::SpeedTier>(10 - effort);
|
|
||||||
return JXL_ENC_SUCCESS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JxlEncoderStatus JxlEncoderOptionsSetDistance(JxlEncoderOptions* options,
|
JxlEncoderStatus JxlEncoderOptionsSetDistance(JxlEncoderOptions* options,
|
||||||
|
@ -264,40 +268,193 @@ JxlEncoderStatus JxlEncoderOptionsSetDistance(JxlEncoderOptions* options,
|
||||||
return JXL_ENC_ERROR;
|
return JXL_ENC_ERROR;
|
||||||
}
|
}
|
||||||
options->values.cparams.butteraugli_distance = distance;
|
options->values.cparams.butteraugli_distance = distance;
|
||||||
|
float jpeg_quality;
|
||||||
|
// Formula to translate butteraugli distance roughly into JPEG 0-100 quality.
|
||||||
|
// This is the inverse of the formula in cjxl.cc to translate JPEG quality
|
||||||
|
// into butteraugli distance.
|
||||||
|
if (distance < 6.56f) {
|
||||||
|
jpeg_quality = -5.456783f * std::log(0.0256f * distance - 0.16384f);
|
||||||
|
} else {
|
||||||
|
jpeg_quality = -11.11111f * distance + 101.11111f;
|
||||||
|
}
|
||||||
|
// Translate JPEG quality into the quality_pair setting for modular encoding.
|
||||||
|
// This is the formula also used in cjxl.cc to convert the command line JPEG
|
||||||
|
// quality parameter to the quality_pair setting.
|
||||||
|
// TODO(lode): combine the distance -> quality_pair conversion into a single
|
||||||
|
// formula, possibly altering it to a more suitable heuristic.
|
||||||
|
float quality;
|
||||||
|
if (jpeg_quality < 7) {
|
||||||
|
quality = std::min<float>(35 + (jpeg_quality - 7) * 3.0f, 100.0f);
|
||||||
|
} else {
|
||||||
|
quality = std::min<float>(35 + (jpeg_quality - 7) * 65.f / 93.f, 100.0f);
|
||||||
|
}
|
||||||
|
options->values.cparams.quality_pair.first =
|
||||||
|
options->values.cparams.quality_pair.second = quality;
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
JxlEncoderStatus JxlEncoderOptionsSetAsInteger(JxlEncoderOptions* options,
|
JxlEncoderStatus JxlEncoderOptionsSetDecodingSpeed(JxlEncoderOptions* options,
|
||||||
JxlEncoderOptionId option,
|
int tier) {
|
||||||
int32_t value) {
|
return JxlEncoderOptionsSetInteger(options, JXL_ENC_OPTION_DECODING_SPEED,
|
||||||
|
tier);
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlEncoderStatus JxlEncoderOptionsSetInteger(JxlEncoderOptions* options,
|
||||||
|
JxlEncoderOptionId option,
|
||||||
|
int32_t value) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
|
case JXL_ENC_OPTION_EFFORT:
|
||||||
|
if (value < 1 || value > 9) {
|
||||||
|
return JXL_ENC_ERROR;
|
||||||
|
}
|
||||||
|
options->values.cparams.speed_tier =
|
||||||
|
static_cast<jxl::SpeedTier>(10 - value);
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_DECODING_SPEED:
|
||||||
|
if (value < 0 || value > 4) {
|
||||||
|
return JXL_ENC_ERROR;
|
||||||
|
}
|
||||||
|
options->values.cparams.decoding_speed_tier = value;
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
case JXL_ENC_OPTION_RESAMPLING:
|
case JXL_ENC_OPTION_RESAMPLING:
|
||||||
if (value != 0 && value != 1 && value != 2 && value != 4 && value != 8) {
|
if (value != -1 && value != 1 && value != 2 && value != 4 && value != 8) {
|
||||||
return JXL_ENC_ERROR;
|
return JXL_ENC_ERROR;
|
||||||
}
|
}
|
||||||
options->values.cparams.resampling = value;
|
options->values.cparams.resampling = value;
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
case JXL_ENC_OPTION_EXTRA_CHANNEL_RESAMPLING:
|
case JXL_ENC_OPTION_EXTRA_CHANNEL_RESAMPLING:
|
||||||
if (value != 0 && value != 1 && value != 2 && value != 4 && value != 8) {
|
if (value != -1 && value != 1 && value != 2 && value != 4 && value != 8) {
|
||||||
return JXL_ENC_ERROR;
|
return JXL_ENC_ERROR;
|
||||||
}
|
}
|
||||||
// The implementation doesn't support the default choice between 1x1 and
|
// The implementation doesn't support the default choice between 1x1 and
|
||||||
// 2x2 for extra channels, so 1x1 is set as the default.
|
// 2x2 for extra channels, so 1x1 is set as the default.
|
||||||
if (value == 0) value = 1;
|
if (value == -1) value = 1;
|
||||||
options->values.cparams.ec_resampling = value;
|
options->values.cparams.ec_resampling = value;
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_PHOTON_NOISE:
|
||||||
|
if (value < 0) return JXL_ENC_ERROR;
|
||||||
|
// TODO(lode): add encoder setting to set the 8 floating point values of
|
||||||
|
// the noise synthesis parameters per frame for more fine grained control.
|
||||||
|
options->values.cparams.photon_noise_iso = static_cast<float>(value);
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
case JXL_ENC_OPTION_NOISE:
|
case JXL_ENC_OPTION_NOISE:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
options->values.cparams.noise = static_cast<jxl::Override>(value);
|
options->values.cparams.noise = static_cast<jxl::Override>(value);
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
case JXL_ENC_OPTION_DOTS:
|
case JXL_ENC_OPTION_DOTS:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
options->values.cparams.dots = static_cast<jxl::Override>(value);
|
options->values.cparams.dots = static_cast<jxl::Override>(value);
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
case JXL_ENC_OPTION_PATCHES:
|
case JXL_ENC_OPTION_PATCHES:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
options->values.cparams.patches = static_cast<jxl::Override>(value);
|
options->values.cparams.patches = static_cast<jxl::Override>(value);
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_EPF:
|
||||||
|
if (value < -1 || value > 3) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.epf = static_cast<int>(value);
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
case JXL_ENC_OPTION_GABORISH:
|
case JXL_ENC_OPTION_GABORISH:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
options->values.cparams.gaborish = static_cast<jxl::Override>(value);
|
options->values.cparams.gaborish = static_cast<jxl::Override>(value);
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_MODULAR:
|
||||||
|
if (value == 1) {
|
||||||
|
options->values.cparams.modular_mode = true;
|
||||||
|
} else if (value == -1 || value == 0) {
|
||||||
|
options->values.cparams.modular_mode = false;
|
||||||
|
} else {
|
||||||
|
return JXL_ENC_ERROR;
|
||||||
|
}
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_KEEP_INVISIBLE:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.keep_invisible =
|
||||||
|
static_cast<jxl::Override>(value);
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_GROUP_ORDER:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.centerfirst = (value == 1);
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_GROUP_ORDER_CENTER_X:
|
||||||
|
if (value < -1) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.center_x = static_cast<size_t>(value);
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_GROUP_ORDER_CENTER_Y:
|
||||||
|
if (value < -1) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.center_y = static_cast<size_t>(value);
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_RESPONSIVE:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.responsive = value;
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_PROGRESSIVE_AC:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.progressive_mode = value;
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_QPROGRESSIVE_AC:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.qprogressive_mode = value;
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_PROGRESSIVE_DC:
|
||||||
|
if (value < -1 || value > 2) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.progressive_dc = value;
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_CHANNEL_COLORS_PRE_TRANSFORM_PERCENT:
|
||||||
|
if (value < -1 || value > 100) return JXL_ENC_ERROR;
|
||||||
|
if (value == -1) {
|
||||||
|
options->values.cparams.channel_colors_pre_transform_percent = 95.0f;
|
||||||
|
} else {
|
||||||
|
options->values.cparams.channel_colors_pre_transform_percent =
|
||||||
|
static_cast<float>(value);
|
||||||
|
}
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_CHANNEL_COLORS_PERCENT:
|
||||||
|
if (value < -1 || value > 100) return JXL_ENC_ERROR;
|
||||||
|
if (value == -1) {
|
||||||
|
options->values.cparams.channel_colors_percent = 80.0f;
|
||||||
|
} else {
|
||||||
|
options->values.cparams.channel_colors_percent =
|
||||||
|
static_cast<float>(value);
|
||||||
|
}
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_PALETTE_COLORS:
|
||||||
|
if (value < -1 || value > 70913) return JXL_ENC_ERROR;
|
||||||
|
if (value == -1) {
|
||||||
|
options->values.cparams.palette_colors = 1 << 10;
|
||||||
|
} else {
|
||||||
|
options->values.cparams.palette_colors = value;
|
||||||
|
}
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_LOSSY_PALETTE:
|
||||||
|
if (value < -1 || value > 1) return JXL_ENC_ERROR;
|
||||||
|
// TODO(lode): the defaults of some palette settings depend on others.
|
||||||
|
// See the logic in cjxl. Similar for other settings. This should be
|
||||||
|
// handled in the encoder during JxlEncoderProcessOutput (or,
|
||||||
|
// alternatively, in the cjxl binary like now)
|
||||||
|
options->values.cparams.lossy_palette = (value == 1);
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_MODULAR_COLOR_SPACE:
|
||||||
|
// TODO(lode): also add color transform option (xyb, none, ycbcr)
|
||||||
|
if (value < -1 || value > 37) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.colorspace = value;
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_MODULAR_GROUP_SIZE:
|
||||||
|
if (value < -1 || value > 3) return JXL_ENC_ERROR;
|
||||||
|
// TODO(lode): the default behavior of this parameter for cjxl is
|
||||||
|
// to choose 1 or 2 depending on the situation. This behavior needs to be
|
||||||
|
// implemented either in the C++ library by allowing to set this to -1, or
|
||||||
|
// kept in cjxl and set it to 1 or 2 using this API.
|
||||||
|
if (value == -1) {
|
||||||
|
options->values.cparams.modular_group_size_shift = 1;
|
||||||
|
} else {
|
||||||
|
options->values.cparams.modular_group_size_shift = value;
|
||||||
|
}
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_OPTION_MODULAR_PREDICTOR:
|
||||||
|
if (value < -1 || value > 15) return JXL_ENC_ERROR;
|
||||||
|
options->values.cparams.options.predictor =
|
||||||
|
static_cast<jxl::Predictor>(value);
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
default:
|
default:
|
||||||
return JXL_ENC_ERROR;
|
return JXL_ENC_ERROR;
|
||||||
}
|
}
|
||||||
|
@ -329,6 +486,8 @@ void JxlEncoderReset(JxlEncoder* enc) {
|
||||||
enc->input_closed = false;
|
enc->input_closed = false;
|
||||||
enc->basic_info_set = false;
|
enc->basic_info_set = false;
|
||||||
enc->color_encoding_set = false;
|
enc->color_encoding_set = false;
|
||||||
|
enc->use_container = false;
|
||||||
|
enc->codestream_level = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
void JxlEncoderDestroy(JxlEncoder* enc) {
|
void JxlEncoderDestroy(JxlEncoder* enc) {
|
||||||
|
@ -341,16 +500,31 @@ void JxlEncoderDestroy(JxlEncoder* enc) {
|
||||||
|
|
||||||
JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc,
|
JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc,
|
||||||
JXL_BOOL use_container) {
|
JXL_BOOL use_container) {
|
||||||
|
if (enc->wrote_bytes) {
|
||||||
|
return JXL_API_ERROR("this setting can only be set at the beginning");
|
||||||
|
}
|
||||||
enc->use_container = static_cast<bool>(use_container);
|
enc->use_container = static_cast<bool>(use_container);
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
JxlEncoderStatus JxlEncoderStoreJPEGMetadata(JxlEncoder* enc,
|
JxlEncoderStatus JxlEncoderStoreJPEGMetadata(JxlEncoder* enc,
|
||||||
JXL_BOOL store_jpeg_metadata) {
|
JXL_BOOL store_jpeg_metadata) {
|
||||||
|
if (enc->wrote_bytes) {
|
||||||
|
return JXL_API_ERROR("this setting can only be set at the beginning");
|
||||||
|
}
|
||||||
enc->store_jpeg_metadata = static_cast<bool>(store_jpeg_metadata);
|
enc->store_jpeg_metadata = static_cast<bool>(store_jpeg_metadata);
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JxlEncoderStatus JxlEncoderSetCodestreamLevel(JxlEncoder* enc, int level) {
|
||||||
|
if (level != 5 && level != 10) return JXL_API_ERROR("invalid level");
|
||||||
|
if (enc->wrote_bytes) {
|
||||||
|
return JXL_API_ERROR("this setting can only be set at the beginning");
|
||||||
|
}
|
||||||
|
enc->codestream_level = level;
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
JxlEncoderStatus JxlEncoderSetParallelRunner(JxlEncoder* enc,
|
JxlEncoderStatus JxlEncoderSetParallelRunner(JxlEncoder* enc,
|
||||||
JxlParallelRunner parallel_runner,
|
JxlParallelRunner parallel_runner,
|
||||||
void* parallel_runner_opaque) {
|
void* parallel_runner_opaque) {
|
||||||
|
@ -507,15 +681,6 @@ JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc, uint8_t** next_out,
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
JxlEncoderStatus JxlEncoderOptionsSetDecodingSpeed(JxlEncoderOptions* options,
|
|
||||||
int tier) {
|
|
||||||
if (tier < 0 || tier > 4) {
|
|
||||||
return JXL_ENC_ERROR;
|
|
||||||
}
|
|
||||||
options->values.cparams.decoding_speed_tier = tier;
|
|
||||||
return JXL_ENC_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JxlColorEncodingSetToSRGB(JxlColorEncoding* color_encoding,
|
void JxlColorEncodingSetToSRGB(JxlColorEncoding* color_encoding,
|
||||||
JXL_BOOL is_gray) {
|
JXL_BOOL is_gray) {
|
||||||
ConvertInternalToExternalColorEncoding(jxl::ColorEncoding::SRGB(is_gray),
|
ConvertInternalToExternalColorEncoding(jxl::ColorEncoding::SRGB(is_gray),
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
||||||
|
// Options per-frame, this is not used for codestream-wide settings or global
|
||||||
|
// encoder settings.
|
||||||
typedef struct JxlEncoderOptionsValuesStruct {
|
typedef struct JxlEncoderOptionsValuesStruct {
|
||||||
// lossless is a separate setting from cparams because it is a combination
|
// lossless is a separate setting from cparams because it is a combination
|
||||||
// setting that overrides multiple settings inside of cparams.
|
// setting that overrides multiple settings inside of cparams.
|
||||||
|
@ -45,6 +47,8 @@ constexpr unsigned char kContainerHeader[] = {
|
||||||
0xa, 0, 0, 0, 0x14, 'f', 't', 'y', 'p', 'j', 'x',
|
0xa, 0, 0, 0, 0x14, 'f', 't', 'y', 'p', 'j', 'x',
|
||||||
'l', ' ', 0, 0, 0, 0, 'j', 'x', 'l', ' '};
|
'l', ' ', 0, 0, 0, 0, 'j', 'x', 'l', ' '};
|
||||||
|
|
||||||
|
constexpr unsigned char kLevelBoxHeader[] = {0, 0, 0, 0x9, 'j', 'x', 'l', 'l'};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
template <typename T>
|
template <typename T>
|
||||||
uint8_t* Extend(T* vec, size_t size) {
|
uint8_t* Extend(T* vec, size_t size) {
|
||||||
|
@ -91,6 +95,11 @@ struct JxlEncoderStruct {
|
||||||
std::vector<uint8_t> output_byte_queue;
|
std::vector<uint8_t> output_byte_queue;
|
||||||
|
|
||||||
bool use_container = false;
|
bool use_container = false;
|
||||||
|
|
||||||
|
// TODO(lode): move level into jxl::CompressParams since some C++
|
||||||
|
// implementation decisions should be based on it: level 10 allows more
|
||||||
|
// features to be used.
|
||||||
|
uint32_t codestream_level = 5;
|
||||||
bool store_jpeg_metadata = false;
|
bool store_jpeg_metadata = false;
|
||||||
jxl::CodecMetadata metadata;
|
jxl::CodecMetadata metadata;
|
||||||
std::vector<uint8_t> jpeg_metadata;
|
std::vector<uint8_t> jpeg_metadata;
|
||||||
|
@ -106,6 +115,10 @@ struct JxlEncoderStruct {
|
||||||
// bytes to the output_byte_queue.
|
// bytes to the output_byte_queue.
|
||||||
JxlEncoderStatus RefillOutputByteQueue();
|
JxlEncoderStatus RefillOutputByteQueue();
|
||||||
|
|
||||||
|
bool MustUseContainer() const {
|
||||||
|
return use_container || codestream_level != 5 || store_jpeg_metadata;
|
||||||
|
}
|
||||||
|
|
||||||
// Appends the bytes of a JXL box header with the provided type and size to
|
// Appends the bytes of a JXL box header with the provided type and size to
|
||||||
// the end of the output_byte_queue. If unbounded is true, the size won't be
|
// the end of the output_byte_queue. If unbounded is true, the size won't be
|
||||||
// added to the header and the box will be assumed to continue until EOF.
|
// added to the header and the box will be assumed to continue until EOF.
|
||||||
|
|
|
@ -56,9 +56,6 @@ TEST(EncodeTest, AddJPEGAfterCloseTest) {
|
||||||
const std::string jpeg_path =
|
const std::string jpeg_path =
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
||||||
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
||||||
jxl::CodecInOut orig_io;
|
|
||||||
ASSERT_TRUE(
|
|
||||||
SetFromBytes(jxl::Span<const uint8_t>(orig), &orig_io, /*pool=*/nullptr));
|
|
||||||
|
|
||||||
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
|
|
||||||
|
@ -234,7 +231,8 @@ TEST(EncodeTest, OptionsTest) {
|
||||||
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
EXPECT_NE(nullptr, enc.get());
|
EXPECT_NE(nullptr, enc.get());
|
||||||
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderOptionsSetEffort(options, 5));
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderOptionsSetInteger(options, JXL_ENC_OPTION_EFFORT, 5));
|
||||||
VerifyFrameEncoding(enc.get(), options);
|
VerifyFrameEncoding(enc.get(), options);
|
||||||
EXPECT_EQ(jxl::SpeedTier::kHare, enc->last_used_cparams.speed_tier);
|
EXPECT_EQ(jxl::SpeedTier::kHare, enc->last_used_cparams.speed_tier);
|
||||||
}
|
}
|
||||||
|
@ -244,9 +242,11 @@ TEST(EncodeTest, OptionsTest) {
|
||||||
EXPECT_NE(nullptr, enc.get());
|
EXPECT_NE(nullptr, enc.get());
|
||||||
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
// Lower than currently supported values
|
// Lower than currently supported values
|
||||||
EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderOptionsSetEffort(options, 0));
|
EXPECT_EQ(JXL_ENC_ERROR,
|
||||||
|
JxlEncoderOptionsSetInteger(options, JXL_ENC_OPTION_EFFORT, 0));
|
||||||
// Higher than currently supported values
|
// Higher than currently supported values
|
||||||
EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderOptionsSetEffort(options, 10));
|
EXPECT_EQ(JXL_ENC_ERROR,
|
||||||
|
JxlEncoderOptionsSetInteger(options, JXL_ENC_OPTION_EFFORT, 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -278,10 +278,99 @@ TEST(EncodeTest, OptionsTest) {
|
||||||
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
EXPECT_NE(nullptr, enc.get());
|
EXPECT_NE(nullptr, enc.get());
|
||||||
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderOptionsSetDecodingSpeed(options, 2));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_DECODING_SPEED, 2));
|
||||||
VerifyFrameEncoding(enc.get(), options);
|
VerifyFrameEncoding(enc.get(), options);
|
||||||
EXPECT_EQ(2u, enc->last_used_cparams.decoding_speed_tier);
|
EXPECT_EQ(2u, enc->last_used_cparams.decoding_speed_tier);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
|
EXPECT_NE(nullptr, enc.get());
|
||||||
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
|
EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_GROUP_ORDER, 100));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_GROUP_ORDER, 1));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_GROUP_ORDER_CENTER_X, 5));
|
||||||
|
VerifyFrameEncoding(enc.get(), options);
|
||||||
|
EXPECT_EQ(true, enc->last_used_cparams.centerfirst);
|
||||||
|
EXPECT_EQ(5, enc->last_used_cparams.center_x);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
|
EXPECT_NE(nullptr, enc.get());
|
||||||
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_RESPONSIVE, 0));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_PROGRESSIVE_AC, 1));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderOptionsSetInteger(options,
|
||||||
|
JXL_ENC_OPTION_QPROGRESSIVE_AC, -1));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_PROGRESSIVE_DC, 2));
|
||||||
|
VerifyFrameEncoding(enc.get(), options);
|
||||||
|
EXPECT_EQ(false, enc->last_used_cparams.responsive);
|
||||||
|
EXPECT_EQ(true, enc->last_used_cparams.progressive_mode);
|
||||||
|
EXPECT_EQ(2, enc->last_used_cparams.progressive_dc);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
|
EXPECT_NE(nullptr, enc.get());
|
||||||
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_PHOTON_NOISE, 1777));
|
||||||
|
VerifyFrameEncoding(enc.get(), options);
|
||||||
|
EXPECT_EQ(1777.0f, enc->last_used_cparams.photon_noise_iso);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
|
EXPECT_NE(nullptr, enc.get());
|
||||||
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
|
EXPECT_EQ(
|
||||||
|
JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_CHANNEL_COLORS_PRE_TRANSFORM_PERCENT, 55));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_CHANNEL_COLORS_PERCENT, 25));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_PALETTE_COLORS, 70000));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_LOSSY_PALETTE, 1));
|
||||||
|
VerifyFrameEncoding(enc.get(), options);
|
||||||
|
EXPECT_EQ(55.0f,
|
||||||
|
enc->last_used_cparams.channel_colors_pre_transform_percent);
|
||||||
|
EXPECT_EQ(25.0f, enc->last_used_cparams.channel_colors_percent);
|
||||||
|
EXPECT_EQ(70000, enc->last_used_cparams.palette_colors);
|
||||||
|
EXPECT_EQ(true, enc->last_used_cparams.lossy_palette);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
|
EXPECT_NE(nullptr, enc.get());
|
||||||
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_MODULAR_COLOR_SPACE, 30));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_MODULAR_GROUP_SIZE, 2));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderOptionsSetInteger(
|
||||||
|
options, JXL_ENC_OPTION_MODULAR_PREDICTOR, 14));
|
||||||
|
VerifyFrameEncoding(enc.get(), options);
|
||||||
|
EXPECT_EQ(30, enc->last_used_cparams.colorspace);
|
||||||
|
EXPECT_EQ(2, enc->last_used_cparams.modular_group_size_shift);
|
||||||
|
EXPECT_EQ(jxl::Predictor::Best, enc->last_used_cparams.options.predictor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -466,18 +555,68 @@ TEST(EncodeTest, SingleFrameBoundedJXLCTest) {
|
||||||
EXPECT_EQ(true, container.boxes[0].data_size_given);
|
EXPECT_EQ(true, container.boxes[0].data_size_given);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(EncodeTest, CodestreamLevelTest) {
|
||||||
|
size_t xsize = 64;
|
||||||
|
size_t ysize = 64;
|
||||||
|
JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
|
||||||
|
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
|
||||||
|
|
||||||
|
jxl::CodecInOut input_io =
|
||||||
|
jxl::test::SomeTestImageToCodecInOut(pixels, 4, xsize, ysize);
|
||||||
|
|
||||||
|
JxlBasicInfo basic_info;
|
||||||
|
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
|
||||||
|
basic_info.xsize = xsize;
|
||||||
|
basic_info.ysize = ysize;
|
||||||
|
basic_info.uses_original_profile = false;
|
||||||
|
|
||||||
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
|
||||||
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
|
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
|
JxlColorEncoding color_encoding;
|
||||||
|
JxlColorEncodingSetToSRGB(&color_encoding,
|
||||||
|
/*is_gray=*/pixel_format.num_channels < 3);
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderSetColorEncoding(enc.get(), &color_encoding));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
|
JxlEncoderAddImageFrame(options, &pixel_format, pixels.data(),
|
||||||
|
pixels.size()));
|
||||||
|
JxlEncoderCloseInput(enc.get());
|
||||||
|
|
||||||
|
std::vector<uint8_t> compressed = std::vector<uint8_t>(64);
|
||||||
|
uint8_t* next_out = compressed.data();
|
||||||
|
size_t avail_out = compressed.size() - (next_out - compressed.data());
|
||||||
|
JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT;
|
||||||
|
while (process_result == JXL_ENC_NEED_MORE_OUTPUT) {
|
||||||
|
process_result = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out);
|
||||||
|
if (process_result == JXL_ENC_NEED_MORE_OUTPUT) {
|
||||||
|
size_t offset = next_out - compressed.data();
|
||||||
|
compressed.resize(compressed.size() * 2);
|
||||||
|
next_out = compressed.data() + offset;
|
||||||
|
avail_out = compressed.size() - offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
compressed.resize(next_out - compressed.data());
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, process_result);
|
||||||
|
|
||||||
|
Container container = {};
|
||||||
|
jxl::Span<const uint8_t> encoded_span =
|
||||||
|
jxl::Span<const uint8_t>(compressed.data(), compressed.size());
|
||||||
|
EXPECT_TRUE(container.Decode(&encoded_span));
|
||||||
|
EXPECT_EQ(0u, encoded_span.size());
|
||||||
|
EXPECT_EQ(0, memcmp("jxll", container.boxes[0].type, 4));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
|
TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
|
||||||
const std::string jpeg_path =
|
const std::string jpeg_path =
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
||||||
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
||||||
jxl::CodecInOut orig_io;
|
|
||||||
ASSERT_TRUE(
|
|
||||||
SetFromBytes(jxl::Span<const uint8_t>(orig), &orig_io, /*pool=*/nullptr));
|
|
||||||
|
|
||||||
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
|
||||||
|
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderUseContainer(enc.get(), JXL_TRUE));
|
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderStoreJPEGMetadata(enc.get(), JXL_TRUE));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderStoreJPEGMetadata(enc.get(), JXL_TRUE));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS,
|
EXPECT_EQ(JXL_ENC_SUCCESS,
|
||||||
JxlEncoderAddJPEGFrame(options, orig.data(), orig.size()));
|
JxlEncoderAddJPEGFrame(options, orig.data(), orig.size()));
|
||||||
|
@ -528,6 +667,42 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
|
||||||
EXPECT_EQ(0, memcmp(decoded_jpeg_bytes.data(), orig.data(), orig.size()));
|
EXPECT_EQ(0, memcmp(decoded_jpeg_bytes.data(), orig.data(), orig.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This test is commented out until JxlEncoderAddBox is implemented, and is a
|
||||||
|
// prototype of JxlEncoderAddBox usage, not a finished test implementation.
|
||||||
|
#if 0
|
||||||
|
TEST(EncodeTest, BoxTest) {
|
||||||
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
|
EXPECT_NE(nullptr, enc.get());
|
||||||
|
|
||||||
|
// TODO(lode): create a test image, initialize encoder and options, prepare
|
||||||
|
// next_out and avail_out, and handle status and output buffer after the
|
||||||
|
// JxlEncoderProcessOutput calls below.
|
||||||
|
|
||||||
|
// Add an early metadata box
|
||||||
|
JxlEncoderAddBox("Exif", exif_data, exif_size);
|
||||||
|
|
||||||
|
// Write to output
|
||||||
|
status = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out);
|
||||||
|
|
||||||
|
// Add image frame
|
||||||
|
EXPECT_EQ(JXL_ENC_ERROR,
|
||||||
|
JxlEncoderAddImageFrame(options, &pixel_format, pixels.data(),
|
||||||
|
pixels.size()));
|
||||||
|
// Indicate this is the last frame
|
||||||
|
JxlEncoderCloseInput(enc.get());
|
||||||
|
|
||||||
|
// Write to output
|
||||||
|
status = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out);
|
||||||
|
|
||||||
|
// Add a late metadata box
|
||||||
|
JxlEncoderAddBox("XML ", xml_data, xml_size);
|
||||||
|
|
||||||
|
// Write to output
|
||||||
|
status = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JPEGXL_ENABLE_JPEG // Loading .jpg files requires libjpeg support.
|
||||||
TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGFrameTest)) {
|
TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGFrameTest)) {
|
||||||
for (int skip_basic_info = 0; skip_basic_info < 2; skip_basic_info++) {
|
for (int skip_basic_info = 0; skip_basic_info < 2; skip_basic_info++) {
|
||||||
for (int skip_color_encoding = 0; skip_color_encoding < 2;
|
for (int skip_color_encoding = 0; skip_color_encoding < 2;
|
||||||
|
@ -592,3 +767,4 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGFrameTest)) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#endif // JPEGXL_ENABLE_JPEG
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef LIB_JXL_FAKE_PARALLEL_RUNNER_TESTONLY_H_
|
||||||
|
#define LIB_JXL_FAKE_PARALLEL_RUNNER_TESTONLY_H_
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "jxl/parallel_runner.h"
|
||||||
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
|
#include "lib/jxl/base/random.h"
|
||||||
|
|
||||||
|
namespace jxl {
|
||||||
|
|
||||||
|
// A parallel runner implementation that runs all the jobs in a single thread
|
||||||
|
// (the caller thread) but runs them pretending to use multiple threads and
|
||||||
|
// potentially out of order. This is useful for testing conditions that only
|
||||||
|
// occur under heavy load where the order of operations is different.
|
||||||
|
class FakeParallelRunner {
|
||||||
|
public:
|
||||||
|
FakeParallelRunner(uint32_t order_seed, uint32_t num_threads)
|
||||||
|
: order_seed_(order_seed), rng_(order_seed), num_threads_(num_threads) {
|
||||||
|
if (num_threads_ < 1) num_threads_ = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlParallelRetCode Run(void* jxl_opaque, JxlParallelRunInit init,
|
||||||
|
JxlParallelRunFunction func, uint32_t start,
|
||||||
|
uint32_t end) {
|
||||||
|
JxlParallelRetCode ret = init(jxl_opaque, num_threads_);
|
||||||
|
if (ret != 0) return ret;
|
||||||
|
|
||||||
|
if (order_seed_ == 0) {
|
||||||
|
for (uint32_t i = start; i < end; i++) {
|
||||||
|
func(jxl_opaque, i, i % num_threads_);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::vector<uint32_t> order(end - start);
|
||||||
|
for (uint32_t i = start; i < end; i++) {
|
||||||
|
order[i - start] = i;
|
||||||
|
}
|
||||||
|
rng_.Shuffle(order.data(), order.size());
|
||||||
|
for (uint32_t i = start; i < end; i++) {
|
||||||
|
func(jxl_opaque, order[i - start], i % num_threads_);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Seed for the RNG for defining the execution order. A value of 0 means
|
||||||
|
// sequential order from start to end.
|
||||||
|
uint32_t order_seed_;
|
||||||
|
|
||||||
|
// The PRNG object, initialized with the order_seed_. Only used if the seed is
|
||||||
|
// not 0.
|
||||||
|
Rng rng_;
|
||||||
|
|
||||||
|
// Number of fake threads. All the tasks are run on the same thread, but using
|
||||||
|
// different thread_id values based on this num_threads.
|
||||||
|
uint32_t num_threads_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace jxl
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
// Function to pass as the parallel runner.
|
||||||
|
JXL_INLINE JxlParallelRetCode JxlFakeParallelRunner(
|
||||||
|
void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init,
|
||||||
|
JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range) {
|
||||||
|
return static_cast<jxl::FakeParallelRunner*>(runner_opaque)
|
||||||
|
->Run(jpegxl_opaque, init, func, start_range, end_range);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // LIB_JXL_FAKE_PARALLEL_RUNNER_TESTONLY_H_
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
#include "hwy/base.h"
|
#include "hwy/base.h"
|
||||||
#include "lib/jxl/base/bits.h"
|
#include "lib/jxl/base/bits.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
// Forward/backward-compatible 'bundles' with auto-serialized 'fields'.
|
// Forward/backward-compatible 'bundles' with auto-serialized 'fields'.
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -40,7 +41,8 @@ class BitsCoder {
|
||||||
size_t* JXL_RESTRICT encoded_bits) {
|
size_t* JXL_RESTRICT encoded_bits) {
|
||||||
*encoded_bits = bits;
|
*encoded_bits = bits;
|
||||||
if (value >= (1ULL << bits)) {
|
if (value >= (1ULL << bits)) {
|
||||||
return JXL_FAILURE("Value %u too large for %" PRIuS " bits", value, bits);
|
return JXL_FAILURE("Value %u too large for %" PRIu64 " bits", value,
|
||||||
|
static_cast<uint64_t>(bits));
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -53,8 +55,8 @@ class BitsCoder {
|
||||||
static Status Write(const size_t bits, const uint32_t value,
|
static Status Write(const size_t bits, const uint32_t value,
|
||||||
BitWriter* JXL_RESTRICT writer) {
|
BitWriter* JXL_RESTRICT writer) {
|
||||||
if (value >= (1ULL << bits)) {
|
if (value >= (1ULL << bits)) {
|
||||||
return JXL_FAILURE("Value %d too large to encode in %" PRIuS " bits",
|
return JXL_FAILURE("Value %d too large to encode in %" PRIu64 " bits",
|
||||||
value, bits);
|
value, static_cast<uint64_t>(bits));
|
||||||
}
|
}
|
||||||
writer->Write(bits, value);
|
writer->Write(bits, value);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "lib/jxl/frame_header.h"
|
#include "lib/jxl/frame_header.h"
|
||||||
|
|
||||||
#include "lib/jxl/aux_out.h"
|
#include "lib/jxl/aux_out.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/fields.h"
|
#include "lib/jxl/fields.h"
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "lib/extras/time.h"
|
#include "lib/extras/time.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/convolve.h"
|
#include "lib/jxl/convolve.h"
|
||||||
#include "lib/jxl/image_ops.h"
|
#include "lib/jxl/image_ops.h"
|
||||||
#include "lib/jxl/image_test_utils.h"
|
#include "lib/jxl/image_test_utils.h"
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "lib/jxl/headers.h"
|
#include "lib/jxl/headers.h"
|
||||||
|
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
#include "lib/jxl/fields.h"
|
#include "lib/jxl/fields.h"
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
// SIMD/multicore-friendly planar image representation with row accessors.
|
// SIMD/multicore-friendly planar image representation with row accessors.
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -84,7 +85,7 @@ struct PlaneBase {
|
||||||
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
|
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
|
||||||
defined(THREAD_SANITIZER)
|
defined(THREAD_SANITIZER)
|
||||||
if (y >= ysize_) {
|
if (y >= ysize_) {
|
||||||
JXL_ABORT("Row(%" PRIuS ") in (%u x %u) image\n", y, xsize_, ysize_);
|
JXL_ABORT("Row(%" PRIu64 ") in (%u x %u) image\n", y, xsize_, ysize_);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -223,6 +224,12 @@ class Rect {
|
||||||
return Rect(x0_, y0_, xsize_, ysize_, image.xsize(), image.ysize());
|
return Rect(x0_, y0_, xsize_, ysize_, image.xsize(), image.ysize());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Construct a subrect that resides in the [0, ysize) x [0, xsize) region of
|
||||||
|
// the current rect.
|
||||||
|
Rect Crop(size_t area_xsize, size_t area_ysize) const {
|
||||||
|
return Rect(x0_, y0_, xsize_, ysize_, area_xsize, area_ysize);
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a rect that only contains `num` lines with offset `y` from `y0()`.
|
// Returns a rect that only contains `num` lines with offset `y` from `y0()`.
|
||||||
Rect Lines(size_t y, size_t num) const {
|
Rect Lines(size_t y, size_t num) const {
|
||||||
JXL_DASSERT(y + num <= ysize_);
|
JXL_DASSERT(y + num <= ysize_);
|
||||||
|
@ -416,9 +423,10 @@ class Image3 {
|
||||||
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
|
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
|
||||||
defined(THREAD_SANITIZER)
|
defined(THREAD_SANITIZER)
|
||||||
if (c >= kNumPlanes || y >= ysize()) {
|
if (c >= kNumPlanes || y >= ysize()) {
|
||||||
JXL_ABORT("PlaneRow(%" PRIuS ", %" PRIuS ") in (%" PRIuS " x %" PRIuS
|
JXL_ABORT("PlaneRow(%" PRIu64 ", %" PRIu64 ") in (%" PRIu64 " x %" PRIu64
|
||||||
") image\n",
|
") image\n",
|
||||||
c, y, xsize(), ysize());
|
static_cast<uint64_t>(c), static_cast<uint64_t>(y),
|
||||||
|
static_cast<uint64_t>(xsize()), static_cast<uint64_t>(ysize()));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include "lib/jxl/alpha.h"
|
#include "lib/jxl/alpha.h"
|
||||||
#include "lib/jxl/base/byte_order.h"
|
#include "lib/jxl/base/byte_order.h"
|
||||||
#include "lib/jxl/base/padded_bytes.h"
|
#include "lib/jxl/base/padded_bytes.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/profiler.h"
|
#include "lib/jxl/base/profiler.h"
|
||||||
#include "lib/jxl/codec_in_out.h"
|
#include "lib/jxl/codec_in_out.h"
|
||||||
#include "lib/jxl/color_management.h"
|
#include "lib/jxl/color_management.h"
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/image.h"
|
#include "lib/jxl/image.h"
|
||||||
#include "lib/jxl/image_test_utils.h"
|
#include "lib/jxl/image_test_utils.h"
|
||||||
|
|
||||||
|
|
|
@ -121,12 +121,13 @@ void VerifyRelativeError(const Plane<T>& expected, const Plane<T>& actual,
|
||||||
if (any_bad) {
|
if (any_bad) {
|
||||||
// Never had a valid relative value, don't print it.
|
// Never had a valid relative value, don't print it.
|
||||||
if (max_relative < 0) {
|
if (max_relative < 0) {
|
||||||
fprintf(stderr, "c=%" PRIuS ": max +/- %E exceeds +/- %.2E\n", c, max_l1,
|
fprintf(stderr, "c=%" PRIu64 ": max +/- %E exceeds +/- %.2E\n",
|
||||||
threshold_l1);
|
static_cast<uint64_t>(c), max_l1, threshold_l1);
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"c=%" PRIuS ": max +/- %E, x %E exceeds +/- %.2E, x %.2E\n", c,
|
"c=%" PRIu64 ": max +/- %E, x %E exceeds +/- %.2E, x %.2E\n",
|
||||||
max_l1, max_relative, threshold_l1, threshold_relative);
|
static_cast<uint64_t>(c), max_l1, max_relative, threshold_l1,
|
||||||
|
threshold_relative);
|
||||||
}
|
}
|
||||||
// Dump the expected image and actual image if the region is small enough.
|
// Dump the expected image and actual image if the region is small enough.
|
||||||
const intptr_t kMaxTestDumpSize = 16;
|
const intptr_t kMaxTestDumpSize = 16;
|
||||||
|
@ -230,7 +231,7 @@ typename std::enable_if<std::is_integral<T>::value>::type RandomFillImage(
|
||||||
int64_t(std::numeric_limits<T>::max()) + 1);
|
int64_t(std::numeric_limits<T>::max()) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RandomFillImage(Plane<float>* image) {
|
JXL_INLINE void RandomFillImage(Plane<float>* image) {
|
||||||
Rng rng(129);
|
Rng rng(129);
|
||||||
GenerateImage(rng, image, 0.0f, std::numeric_limits<float>::max());
|
GenerateImage(rng, image, 0.0f, std::numeric_limits<float>::max());
|
||||||
}
|
}
|
||||||
|
@ -250,7 +251,7 @@ typename std::enable_if<std::is_integral<T>::value>::type RandomFillImage(
|
||||||
int64_t(std::numeric_limits<T>::max()) + 1);
|
int64_t(std::numeric_limits<T>::max()) + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RandomFillImage(Image3F* image) {
|
JXL_INLINE void RandomFillImage(Image3F* image) {
|
||||||
Rng rng(129);
|
Rng rng(129);
|
||||||
GenerateImage(rng, image, 0.0f, std::numeric_limits<float>::max());
|
GenerateImage(rng, image, 0.0f, std::numeric_limits<float>::max());
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
#include "lib/jxl/jpeg/enc_jpeg_huffman_decode.h"
|
#include "lib/jxl/jpeg/enc_jpeg_huffman_decode.h"
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "lib/jxl/jpeg/jpeg_data.h"
|
#include "lib/jxl/jpeg/jpeg_data.h"
|
||||||
|
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
@ -143,15 +144,14 @@ Status JPEGData::VisitFields(Visitor* visitor) {
|
||||||
}
|
}
|
||||||
|
|
||||||
JPEGComponentType component_type =
|
JPEGComponentType component_type =
|
||||||
components.size() == 1 && components[0].id == 1
|
components.size() == 1 && components[0].id == 1 ? JPEGComponentType::kGray
|
||||||
? JPEGComponentType::kGray
|
: components.size() == 3 && components[0].id == 1 &&
|
||||||
: components.size() == 3 && components[0].id == 1 &&
|
components[1].id == 2 && components[2].id == 3
|
||||||
components[1].id == 2 && components[2].id == 3
|
? JPEGComponentType::kYCbCr
|
||||||
? JPEGComponentType::kYCbCr
|
: components.size() == 3 && components[0].id == 'R' &&
|
||||||
: components.size() == 3 && components[0].id == 'R' &&
|
components[1].id == 'G' && components[2].id == 'B'
|
||||||
components[1].id == 'G' && components[2].id == 'B'
|
? JPEGComponentType::kRGB
|
||||||
? JPEGComponentType::kRGB
|
: JPEGComponentType::kCustom;
|
||||||
: JPEGComponentType::kCustom;
|
|
||||||
JXL_RETURN_IF_ERROR(
|
JXL_RETURN_IF_ERROR(
|
||||||
visitor->Bits(2, JPEGComponentType::kYCbCr,
|
visitor->Bits(2, JPEGComponentType::kYCbCr,
|
||||||
reinterpret_cast<uint32_t*>(&component_type)));
|
reinterpret_cast<uint32_t*>(&component_type)));
|
||||||
|
@ -195,10 +195,15 @@ Status JPEGData::VisitFields(Visitor* visitor) {
|
||||||
}
|
}
|
||||||
used_tables |= 1U << components[i].quant_idx;
|
used_tables |= 1U << components[i].quant_idx;
|
||||||
}
|
}
|
||||||
if (used_tables + 1 != 1U << quant.size()) {
|
for (size_t i = 0; i < quant.size(); i++) {
|
||||||
return JXL_FAILURE("Not all quant tables are used (%" PRIuS
|
if (used_tables & (1 << i)) continue;
|
||||||
" tables, %" PRIx64 " used table mask)",
|
if (i == 0) return JXL_FAILURE("First quant table unused.");
|
||||||
quant.size(), static_cast<uint64_t>(used_tables));
|
// Unused quant table has to be set to copy of previous quant table
|
||||||
|
for (size_t j = 0; j < 64; j++) {
|
||||||
|
if (quant[i].values[j] != quant[i - 1].values[j]) {
|
||||||
|
return JXL_FAILURE("Non-trivial unused quant table");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t num_huff = huffman_code.size();
|
uint32_t num_huff = huffman_code.size();
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "lib/jxl/base/data_parallel.h"
|
#include "lib/jxl/base/data_parallel.h"
|
||||||
#include "lib/jxl/base/override.h"
|
#include "lib/jxl/base/override.h"
|
||||||
#include "lib/jxl/base/padded_bytes.h"
|
#include "lib/jxl/base/padded_bytes.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/thread_pool_internal.h"
|
#include "lib/jxl/base/thread_pool_internal.h"
|
||||||
#include "lib/jxl/codec_in_out.h"
|
#include "lib/jxl/codec_in_out.h"
|
||||||
#include "lib/jxl/codec_y4m_testonly.h"
|
#include "lib/jxl/codec_y4m_testonly.h"
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
#include "lib/jxl/enc_cache.h"
|
#include "lib/jxl/enc_cache.h"
|
||||||
#include "lib/jxl/enc_file.h"
|
#include "lib/jxl/enc_file.h"
|
||||||
#include "lib/jxl/enc_params.h"
|
#include "lib/jxl/enc_params.h"
|
||||||
|
#include "lib/jxl/fake_parallel_runner_testonly.h"
|
||||||
#include "lib/jxl/image.h"
|
#include "lib/jxl/image.h"
|
||||||
#include "lib/jxl/image_bundle.h"
|
#include "lib/jxl/image_bundle.h"
|
||||||
#include "lib/jxl/image_ops.h"
|
#include "lib/jxl/image_ops.h"
|
||||||
|
@ -196,7 +198,7 @@ TEST(JxlTest, RoundtripOtherTransforms) {
|
||||||
EXPECT_LE(compressed_size, 23000u);
|
EXPECT_LE(compressed_size, 23000u);
|
||||||
EXPECT_THAT(ButteraugliDistance(*io, *io2, cparams.ba_params,
|
EXPECT_THAT(ButteraugliDistance(*io, *io2, cparams.ba_params,
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(4.0));
|
IsSlightlyBelow(3.0));
|
||||||
|
|
||||||
// Check the consistency when performing another roundtrip.
|
// Check the consistency when performing another roundtrip.
|
||||||
std::unique_ptr<CodecInOut> io3 = jxl::make_unique<CodecInOut>();
|
std::unique_ptr<CodecInOut> io3 = jxl::make_unique<CodecInOut>();
|
||||||
|
@ -205,7 +207,7 @@ TEST(JxlTest, RoundtripOtherTransforms) {
|
||||||
EXPECT_LE(compressed_size2, 23000u);
|
EXPECT_LE(compressed_size2, 23000u);
|
||||||
EXPECT_THAT(ButteraugliDistance(*io, *io3, cparams.ba_params,
|
EXPECT_THAT(ButteraugliDistance(*io, *io3, cparams.ba_params,
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(4.0));
|
IsSlightlyBelow(3.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripResample2) {
|
TEST(JxlTest, RoundtripResample2) {
|
||||||
|
@ -222,7 +224,7 @@ TEST(JxlTest, RoundtripResample2) {
|
||||||
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 17000u);
|
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 17000u);
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(10));
|
IsSlightlyBelow(8));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripResample2MT) {
|
TEST(JxlTest, RoundtripResample2MT) {
|
||||||
|
@ -248,6 +250,31 @@ TEST(JxlTest, RoundtripResample2MT) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Roundtrip the image using a parallel runner that executes single-threaded but
|
||||||
|
// in random order.
|
||||||
|
TEST(JxlTest, RoundtripOutOfOrderProcessing) {
|
||||||
|
FakeParallelRunner fake_pool(/*order_seed=*/123, /*num_threads=*/8);
|
||||||
|
ThreadPool pool(&JxlFakeParallelRunner, &fake_pool);
|
||||||
|
const PaddedBytes orig =
|
||||||
|
ReadTestData("imagecompression.info/flower_foveon.png");
|
||||||
|
CodecInOut io;
|
||||||
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
// Image size is selected so that the block border needed is larger than the
|
||||||
|
// amount of pixels available on the next block.
|
||||||
|
io.ShrinkTo(513, 515);
|
||||||
|
|
||||||
|
CompressParams cparams;
|
||||||
|
// Force epf so we end up needing a lot of border.
|
||||||
|
cparams.epf = 3;
|
||||||
|
|
||||||
|
DecompressParams dparams;
|
||||||
|
CodecInOut io2;
|
||||||
|
Roundtrip(&io, cparams, dparams, &pool, &io2);
|
||||||
|
|
||||||
|
EXPECT_GE(1.5, ButteraugliDistance(io, io2, cparams.ba_params,
|
||||||
|
/*distmap=*/nullptr, &pool));
|
||||||
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripResample4) {
|
TEST(JxlTest, RoundtripResample4) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
|
@ -262,7 +289,7 @@ TEST(JxlTest, RoundtripResample4) {
|
||||||
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 6000u);
|
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 6000u);
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(28));
|
IsSlightlyBelow(22));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripResample8) {
|
TEST(JxlTest, RoundtripResample8) {
|
||||||
|
@ -279,7 +306,7 @@ TEST(JxlTest, RoundtripResample8) {
|
||||||
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 2100u);
|
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 2100u);
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(80));
|
IsSlightlyBelow(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripUnalignedD2) {
|
TEST(JxlTest, RoundtripUnalignedD2) {
|
||||||
|
@ -706,7 +733,7 @@ TEST(JxlTest, RoundtripGrayscale) {
|
||||||
EXPECT_LE(compressed.size(), 1300u);
|
EXPECT_LE(compressed.size(), 1300u);
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(7.0));
|
IsSlightlyBelow(6.0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1202,7 +1229,7 @@ TEST(JxlTest, RoundtripYCbCr420) {
|
||||||
// we're comparing an original PNG with a YCbCr 4:2:0 version
|
// we're comparing an original PNG with a YCbCr 4:2:0 version
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io3, cparams.ba_params,
|
EXPECT_THAT(ButteraugliDistance(io, io3, cparams.ba_params,
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(2.8));
|
IsSlightlyBelow(3.0));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripDots) {
|
TEST(JxlTest, RoundtripDots) {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "lib/jxl/base/os_macros.h"
|
#include "lib/jxl/base/os_macros.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/modular/encoding/context_predict.h"
|
#include "lib/jxl/modular/encoding/context_predict.h"
|
||||||
#include "lib/jxl/modular/encoding/dec_ma.h"
|
#include "lib/jxl/modular/encoding/dec_ma.h"
|
||||||
|
@ -22,9 +23,7 @@
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
||||||
namespace {
|
const char *PredictorName(Predictor p) {
|
||||||
|
|
||||||
inline const char *PredictorName(Predictor p) {
|
|
||||||
switch (p) {
|
switch (p) {
|
||||||
case Predictor::Zero:
|
case Predictor::Zero:
|
||||||
return "Zero";
|
return "Zero";
|
||||||
|
@ -59,7 +58,7 @@ inline const char *PredictorName(Predictor p) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string PropertyName(size_t i) {
|
std::string PropertyName(size_t i) {
|
||||||
static_assert(kNumNonrefProperties == 16, "Update this function");
|
static_assert(kNumNonrefProperties == 16, "Update this function");
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -99,8 +98,6 @@ inline std::string PropertyName(size_t i) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void PrintTree(const Tree &tree, const std::string &path) {
|
void PrintTree(const Tree &tree, const std::string &path) {
|
||||||
FILE *f = fopen((path + ".dot").c_str(), "w");
|
FILE *f = fopen((path + ".dot").c_str(), "w");
|
||||||
fprintf(f, "graph{\n");
|
fprintf(f, "graph{\n");
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "lib/jxl/modular/encoding/dec_ma.h"
|
#include "lib/jxl/modular/encoding/dec_ma.h"
|
||||||
|
@ -16,6 +17,9 @@
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
||||||
|
const char *PredictorName(Predictor p);
|
||||||
|
std::string PropertyName(size_t i);
|
||||||
|
|
||||||
void PrintTree(const Tree &tree, const std::string &path);
|
void PrintTree(const Tree &tree, const std::string &path);
|
||||||
|
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
#include "lib/jxl/dec_ans.h"
|
#include "lib/jxl/dec_ans.h"
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/modular/encoding/context_predict.h"
|
#include "lib/jxl/modular/encoding/context_predict.h"
|
||||||
#include "lib/jxl/modular/options.h"
|
#include "lib/jxl/modular/options.h"
|
||||||
|
|
||||||
|
@ -446,7 +447,7 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header,
|
||||||
max_tree_size += pixels;
|
max_tree_size += pixels;
|
||||||
if (max_tree_size < pixels) return JXL_FAILURE("Tree size overflow");
|
if (max_tree_size < pixels) return JXL_FAILURE("Tree size overflow");
|
||||||
}
|
}
|
||||||
|
max_tree_size = std::min(static_cast<size_t>(1 << 20), max_tree_size);
|
||||||
JXL_RETURN_IF_ERROR(DecodeTree(br, &tree_storage, max_tree_size));
|
JXL_RETURN_IF_ERROR(DecodeTree(br, &tree_storage, max_tree_size));
|
||||||
JXL_RETURN_IF_ERROR(DecodeHistograms(br, (tree_storage.size() + 1) / 2,
|
JXL_RETURN_IF_ERROR(DecodeHistograms(br, (tree_storage.size() + 1) / 2,
|
||||||
&code_storage, &context_map_storage));
|
&code_storage, &context_map_storage));
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace jxl {
|
||||||
|
|
||||||
void Image::undo_transforms(const weighted::Header &wp_header,
|
void Image::undo_transforms(const weighted::Header &wp_header,
|
||||||
jxl::ThreadPool *pool) {
|
jxl::ThreadPool *pool) {
|
||||||
while (transform.size() > 0) {
|
while (!transform.empty()) {
|
||||||
Transform t = transform.back();
|
Transform t = transform.back();
|
||||||
JXL_DEBUG_V(4, "Undoing transform");
|
JXL_DEBUG_V(4, "Undoing transform");
|
||||||
Status result = t.Inverse(*this, wp_header, pool);
|
Status result = t.Inverse(*this, wp_header, pool);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "lib/jxl/base/data_parallel.h"
|
#include "lib/jxl/base/data_parallel.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
#include "lib/jxl/modular/modular_image.h"
|
#include "lib/jxl/modular/modular_image.h"
|
||||||
#include "lib/jxl/modular/transform/transform.h"
|
#include "lib/jxl/modular/transform/transform.h"
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include "lib/jxl/modular/transform/transform.h"
|
#include "lib/jxl/modular/transform/transform.h"
|
||||||
|
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/fields.h"
|
#include "lib/jxl/fields.h"
|
||||||
#include "lib/jxl/modular/modular_image.h"
|
#include "lib/jxl/modular/modular_image.h"
|
||||||
#include "lib/jxl/modular/transform/palette.h"
|
#include "lib/jxl/modular/transform/palette.h"
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "lib/jxl/base/bits.h"
|
#include "lib/jxl/base/bits.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
#include "lib/jxl/dct_scales.h"
|
#include "lib/jxl/dct_scales.h"
|
||||||
|
|
|
@ -567,6 +567,7 @@ TEST(RoundtripTest, TestICCProfile) {
|
||||||
JxlDecoderDestroy(dec);
|
JxlDecoderDestroy(dec);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if JPEGXL_ENABLE_JPEG // Loading .jpg files requires libjpeg support.
|
||||||
TEST(RoundtripTest, JXL_TRANSCODE_JPEG_TEST(TestJPEGReconstruction)) {
|
TEST(RoundtripTest, JXL_TRANSCODE_JPEG_TEST(TestJPEGReconstruction)) {
|
||||||
const std::string jpeg_path =
|
const std::string jpeg_path =
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
||||||
|
@ -613,3 +614,4 @@ TEST(RoundtripTest, JXL_TRANSCODE_JPEG_TEST(TestJPEGReconstruction)) {
|
||||||
ASSERT_EQ(used, orig.size());
|
ASSERT_EQ(used, orig.size());
|
||||||
EXPECT_EQ(0, memcmp(reconstructed_buffer.data(), orig.data(), used));
|
EXPECT_EQ(0, memcmp(reconstructed_buffer.data(), orig.data(), used));
|
||||||
}
|
}
|
||||||
|
#endif // JPEGXL_ENABLE_JPEG
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#ifndef LIB_JXL_SANITIZERS_H_
|
#ifndef LIB_JXL_SANITIZERS_H_
|
||||||
#define LIB_JXL_SANITIZERS_H_
|
#define LIB_JXL_SANITIZERS_H_
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
|
@ -99,8 +100,8 @@ template <typename T>
|
||||||
static JXL_INLINE JXL_MAYBE_UNUSED void PrintImageUninitialized(
|
static JXL_INLINE JXL_MAYBE_UNUSED void PrintImageUninitialized(
|
||||||
const Plane<T>& im) {
|
const Plane<T>& im) {
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"Uninitialized regions for image of size %" PRIuS "x%" PRIuS ":\n",
|
"Uninitialized regions for image of size %" PRIu64 "x%" PRIu64 ":\n",
|
||||||
im.xsize(), im.ysize());
|
static_cast<uint64_t>(im.xsize()), static_cast<uint64_t>(im.ysize()));
|
||||||
|
|
||||||
// A segment of uninitialized pixels in a row, in the format [first, second).
|
// A segment of uninitialized pixels in a row, in the format [first, second).
|
||||||
typedef std::pair<size_t, size_t> PixelSegment;
|
typedef std::pair<size_t, size_t> PixelSegment;
|
||||||
|
@ -138,15 +139,18 @@ static JXL_INLINE JXL_MAYBE_UNUSED void PrintImageUninitialized(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (end_y - start_y_ > 1) {
|
if (end_y - start_y_ > 1) {
|
||||||
fprintf(stderr, " y=[%" PRIdS ", %" PRIuS "):", start_y_, end_y);
|
fprintf(stderr, " y=[%" PRId64 ", %" PRIu64 "):",
|
||||||
|
static_cast<int64_t>(start_y_), static_cast<uint64_t>(end_y));
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, " y=[%" PRIdS "]:", start_y_);
|
fprintf(stderr, " y=[%" PRId64 "]:", static_cast<int64_t>(start_y_));
|
||||||
}
|
}
|
||||||
for (const auto& seg : segments_) {
|
for (const auto& seg : segments_) {
|
||||||
if (seg.first + 1 == seg.second) {
|
if (seg.first + 1 == seg.second) {
|
||||||
fprintf(stderr, " [%" PRIdS "]", seg.first);
|
fprintf(stderr, " [%" PRId64 "]", static_cast<int64_t>(seg.first));
|
||||||
} else {
|
} else {
|
||||||
fprintf(stderr, " [%" PRIdS ", %" PRIuS ")", seg.first, seg.second);
|
fprintf(stderr, " [%" PRId64 ", %" PRIu64 ")",
|
||||||
|
static_cast<int64_t>(seg.first),
|
||||||
|
static_cast<uint64_t>(seg.second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fprintf(stderr, "\n");
|
fprintf(stderr, "\n");
|
||||||
|
@ -203,16 +207,20 @@ static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized(
|
||||||
const auto* row = im.Row(y);
|
const auto* row = im.Row(y);
|
||||||
intptr_t ret = __msan_test_shadow(row + r.x0(), sizeof(*row) * r.xsize());
|
intptr_t ret = __msan_test_shadow(row + r.x0(), sizeof(*row) * r.xsize());
|
||||||
if (ret != -1) {
|
if (ret != -1) {
|
||||||
JXL_DEBUG(1,
|
JXL_DEBUG(
|
||||||
"Checking an image of %" PRIuS " x %" PRIuS ", rect x0=%" PRIuS
|
1,
|
||||||
", y0=%" PRIuS
|
"Checking an image of %" PRIu64 " x %" PRIu64 ", rect x0=%" PRIu64
|
||||||
", "
|
", y0=%" PRIu64
|
||||||
"xsize=%" PRIuS ", ysize=%" PRIuS,
|
", "
|
||||||
im.xsize(), im.ysize(), r.x0(), r.y0(), r.xsize(), r.ysize());
|
"xsize=%" PRIu64 ", ysize=%" PRIu64,
|
||||||
|
static_cast<uint64_t>(im.xsize()), static_cast<uint64_t>(im.ysize()),
|
||||||
|
static_cast<uint64_t>(r.x0()), static_cast<uint64_t>(r.y0()),
|
||||||
|
static_cast<uint64_t>(r.xsize()), static_cast<uint64_t>(r.ysize()));
|
||||||
size_t x = ret / sizeof(*row);
|
size_t x = ret / sizeof(*row);
|
||||||
JXL_DEBUG(
|
JXL_DEBUG(
|
||||||
1, "CheckImageInitialized failed at x=%" PRIuS ", y=%" PRIuS ": %s",
|
1, "CheckImageInitialized failed at x=%" PRIu64 ", y=%" PRIu64 ": %s",
|
||||||
r.x0() + x, y, message ? message : "");
|
static_cast<uint64_t>(r.x0() + x), static_cast<uint64_t>(y),
|
||||||
|
message ? message : "");
|
||||||
PrintImageUninitialized(im);
|
PrintImageUninitialized(im);
|
||||||
}
|
}
|
||||||
// This will report an error if memory is not initialized.
|
// This will report an error if memory is not initialized.
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "lib/jxl/ans_params.h"
|
#include "lib/jxl/ans_params.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/chroma_from_luma.h"
|
#include "lib/jxl/chroma_from_luma.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "lib/extras/codec.h"
|
#include "lib/extras/codec.h"
|
||||||
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
#include "lib/jxl/dec_file.h"
|
#include "lib/jxl/dec_file.h"
|
||||||
#include "lib/jxl/enc_butteraugli_comparator.h"
|
#include "lib/jxl/enc_butteraugli_comparator.h"
|
||||||
#include "lib/jxl/enc_splines.h"
|
#include "lib/jxl/enc_splines.h"
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
set(JPEGXL_INTERNAL_SOURCES_GBENCH
|
set(JPEGXL_INTERNAL_SOURCES_GBENCH
|
||||||
extras/tone_mapping_gbench.cc
|
extras/tone_mapping_gbench.cc
|
||||||
jxl/dec_external_image_gbench.cc
|
jxl/dec_external_image_gbench.cc
|
||||||
|
jxl/dec_reconstruct_gbench.cc
|
||||||
jxl/enc_external_image_gbench.cc
|
jxl/enc_external_image_gbench.cc
|
||||||
jxl/gauss_blur_gbench.cc
|
jxl/gauss_blur_gbench.cc
|
||||||
jxl/splines_gbench.cc
|
jxl/splines_gbench.cc
|
||||||
|
@ -31,7 +32,7 @@ if(benchmark_FOUND)
|
||||||
|
|
||||||
# Compiles all the benchmark files into a single binary. Individual benchmarks
|
# Compiles all the benchmark files into a single binary. Individual benchmarks
|
||||||
# can be run with --benchmark_filter.
|
# can be run with --benchmark_filter.
|
||||||
add_executable(jxl_gbench "${JPEGXL_INTERNAL_SOURCES_GBENCH}")
|
add_executable(jxl_gbench "${JPEGXL_INTERNAL_SOURCES_GBENCH}" gbench_main.cc)
|
||||||
|
|
||||||
target_compile_definitions(jxl_gbench PRIVATE
|
target_compile_definitions(jxl_gbench PRIVATE
|
||||||
-DTEST_DATA_PATH="${PROJECT_SOURCE_DIR}/third_party/testdata")
|
-DTEST_DATA_PATH="${PROJECT_SOURCE_DIR}/third_party/testdata")
|
||||||
|
@ -39,7 +40,6 @@ if(benchmark_FOUND)
|
||||||
jxl_extras-static
|
jxl_extras-static
|
||||||
jxl-static
|
jxl-static
|
||||||
benchmark::benchmark
|
benchmark::benchmark
|
||||||
benchmark::benchmark_main
|
|
||||||
)
|
)
|
||||||
endif() # benchmark_FOUND
|
endif() # benchmark_FOUND
|
||||||
|
|
||||||
|
|
|
@ -70,6 +70,7 @@ set(TESTLIB_FILES
|
||||||
jxl/dct_for_test.h
|
jxl/dct_for_test.h
|
||||||
jxl/dec_transforms_testonly.cc
|
jxl/dec_transforms_testonly.cc
|
||||||
jxl/dec_transforms_testonly.h
|
jxl/dec_transforms_testonly.h
|
||||||
|
jxl/fake_parallel_runner_testonly.h
|
||||||
jxl/image_test_utils.h
|
jxl/image_test_utils.h
|
||||||
jxl/test_utils.h
|
jxl/test_utils.h
|
||||||
jxl/testdata.h
|
jxl/testdata.h
|
||||||
|
|
|
@ -52,6 +52,7 @@ libjxl_dec_sources = [
|
||||||
"jxl/base/override.h",
|
"jxl/base/override.h",
|
||||||
"jxl/base/padded_bytes.cc",
|
"jxl/base/padded_bytes.cc",
|
||||||
"jxl/base/padded_bytes.h",
|
"jxl/base/padded_bytes.h",
|
||||||
|
"jxl/base/printf_macros.h",
|
||||||
"jxl/base/profiler.h",
|
"jxl/base/profiler.h",
|
||||||
"jxl/base/random.cc",
|
"jxl/base/random.cc",
|
||||||
"jxl/base/random.h",
|
"jxl/base/random.h",
|
||||||
|
@ -321,6 +322,7 @@ libjxl_enc_sources = [
|
||||||
libjxl_gbench_sources = [
|
libjxl_gbench_sources = [
|
||||||
"extras/tone_mapping_gbench.cc",
|
"extras/tone_mapping_gbench.cc",
|
||||||
"jxl/dec_external_image_gbench.cc",
|
"jxl/dec_external_image_gbench.cc",
|
||||||
|
"jxl/dec_reconstruct_gbench.cc",
|
||||||
"jxl/enc_external_image_gbench.cc",
|
"jxl/enc_external_image_gbench.cc",
|
||||||
"jxl/gauss_blur_gbench.cc",
|
"jxl/gauss_blur_gbench.cc",
|
||||||
"jxl/splines_gbench.cc",
|
"jxl/splines_gbench.cc",
|
||||||
|
@ -389,6 +391,7 @@ libjxl_testlib_sources = [
|
||||||
"jxl/dct_for_test.h",
|
"jxl/dct_for_test.h",
|
||||||
"jxl/dec_transforms_testonly.cc",
|
"jxl/dec_transforms_testonly.cc",
|
||||||
"jxl/dec_transforms_testonly.h",
|
"jxl/dec_transforms_testonly.h",
|
||||||
|
"jxl/fake_parallel_runner_testonly.h",
|
||||||
"jxl/image_test_utils.h",
|
"jxl/image_test_utils.h",
|
||||||
"jxl/test_utils.h",
|
"jxl/test_utils.h",
|
||||||
"jxl/testdata.h",
|
"jxl/testdata.h",
|
||||||
|
|
|
@ -432,7 +432,7 @@ void ThreadSpecific::ComputeOverhead() {
|
||||||
std::sort(samples, samples + kNumSamples);
|
std::sort(samples, samples + kNumSamples);
|
||||||
self_overhead = samples[kNumSamples / 2];
|
self_overhead = samples[kNumSamples / 2];
|
||||||
#if PROFILER_PRINT_OVERHEAD
|
#if PROFILER_PRINT_OVERHEAD
|
||||||
printf("Overhead: %" PRIuS "\n", self_overhead);
|
printf("Overhead: %" PRIu64 "\n", static_cast<uint64_t>(self_overhead));
|
||||||
#endif
|
#endif
|
||||||
results_->SetSelfOverhead(self_overhead);
|
results_->SetSelfOverhead(self_overhead);
|
||||||
}
|
}
|
||||||
|
@ -468,7 +468,8 @@ void ThreadSpecific::ComputeOverhead() {
|
||||||
std::sort(samples, samples + kNumSamples);
|
std::sort(samples, samples + kNumSamples);
|
||||||
const uint64_t child_overhead = samples[9 * kNumSamples / 10];
|
const uint64_t child_overhead = samples[9 * kNumSamples / 10];
|
||||||
#if PROFILER_PRINT_OVERHEAD
|
#if PROFILER_PRINT_OVERHEAD
|
||||||
printf("Child overhead: %" PRIuS "\n", child_overhead);
|
printf("Child overhead: %" PRIu64 "\n",
|
||||||
|
static_cast<uint64_t>(child_overhead));
|
||||||
#endif
|
#endif
|
||||||
results_->SetChildOverhead(child_overhead);
|
results_->SetChildOverhead(child_overhead);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,20 +10,43 @@
|
||||||
// ensure exactly the desired regions are measured.
|
// ensure exactly the desired regions are measured.
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <time.h> // clock_gettime
|
||||||
|
|
||||||
|
#if defined(_WIN32) || defined(_WIN64)
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif // WIN32_LEAN_AND_MEAN
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX
|
||||||
|
#endif // NOMINMAX
|
||||||
|
#include <windows.h>
|
||||||
|
// Undef macros to avoid collisions
|
||||||
|
#undef LoadFence
|
||||||
|
#undef StoreFence
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__MACH__)
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <mach/mach_time.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__HAIKU__)
|
||||||
|
#include <OS.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <hwy/base.h>
|
#include <hwy/base.h>
|
||||||
#include <hwy/cache_control.h> // LoadFence
|
#include <hwy/cache_control.h> // LoadFence
|
||||||
|
|
||||||
#if HWY_COMPILER_MSVC
|
|
||||||
#include <chrono>
|
|
||||||
#endif // HWY_COMPILER_MSVC
|
|
||||||
|
|
||||||
namespace profiler {
|
namespace profiler {
|
||||||
|
|
||||||
|
// Ticks := platform-specific timer values (CPU cycles on x86). Must be
|
||||||
|
// unsigned to guarantee wraparound on overflow.
|
||||||
|
using Ticks = uint64_t;
|
||||||
|
|
||||||
// TicksBefore/After return absolute timestamps and must be placed immediately
|
// TicksBefore/After return absolute timestamps and must be placed immediately
|
||||||
// before and after the region to measure. The functions are distinct because
|
// before and after the region to measure. We provide separate Before/After
|
||||||
// they use different fences.
|
// functions because they use different fences.
|
||||||
//
|
//
|
||||||
// Background: RDTSC is not 'serializing'; earlier instructions may complete
|
// Background: RDTSC is not 'serializing'; earlier instructions may complete
|
||||||
// after it, and/or later instructions may complete before it. 'Fences' ensure
|
// after it, and/or later instructions may complete before it. 'Fences' ensure
|
||||||
|
@ -59,7 +82,7 @@ namespace profiler {
|
||||||
// Using Before+Before leads to higher variance and overhead than After+After.
|
// Using Before+Before leads to higher variance and overhead than After+After.
|
||||||
// However, After+After includes an LFENCE in the region measurements, which
|
// However, After+After includes an LFENCE in the region measurements, which
|
||||||
// adds a delay dependent on earlier loads. The combination of Before+After
|
// adds a delay dependent on earlier loads. The combination of Before+After
|
||||||
// is faster than Before+Before and more consistent than Stop+Stop because
|
// is faster than Before+Before and more consistent than After+After because
|
||||||
// the first LFENCE already delayed subsequent loads before the measured
|
// the first LFENCE already delayed subsequent loads before the measured
|
||||||
// region. This combination seems not to have been considered in prior work:
|
// region. This combination seems not to have been considered in prior work:
|
||||||
// http://akaros.cs.berkeley.edu/lxr/akaros/kern/arch/x86/rdtsc_test.c
|
// http://akaros.cs.berkeley.edu/lxr/akaros/kern/arch/x86/rdtsc_test.c
|
||||||
|
@ -71,19 +94,18 @@ namespace profiler {
|
||||||
// by several under/over-count errata, so we use the TSC instead.
|
// by several under/over-count errata, so we use the TSC instead.
|
||||||
|
|
||||||
// Returns a 64-bit timestamp in unit of 'ticks'; to convert to seconds,
|
// Returns a 64-bit timestamp in unit of 'ticks'; to convert to seconds,
|
||||||
// divide by InvariantTicksPerSecond. Although 32-bit ticks are faster to read,
|
// divide by InvariantTicksPerSecond.
|
||||||
// they overflow too quickly to measure long regions.
|
static HWY_INLINE HWY_MAYBE_UNUSED Ticks TicksBefore() {
|
||||||
static HWY_INLINE HWY_MAYBE_UNUSED uint64_t TicksBefore() {
|
Ticks t;
|
||||||
uint64_t t;
|
#if HWY_ARCH_PPC && defined(__GLIBC__)
|
||||||
#if HWY_ARCH_PPC
|
|
||||||
asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
|
asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
|
||||||
#elif HWY_ARCH_X86_64 && HWY_COMPILER_MSVC
|
#elif HWY_ARCH_X86 && HWY_COMPILER_MSVC
|
||||||
hwy::LoadFence();
|
hwy::LoadFence();
|
||||||
HWY_FENCE;
|
HWY_FENCE;
|
||||||
t = __rdtsc();
|
t = __rdtsc();
|
||||||
hwy::LoadFence();
|
hwy::LoadFence();
|
||||||
HWY_FENCE;
|
HWY_FENCE;
|
||||||
#elif HWY_ARCH_X86_64 && (HWY_COMPILER_CLANG || HWY_COMPILER_GCC)
|
#elif HWY_ARCH_X86_64
|
||||||
asm volatile(
|
asm volatile(
|
||||||
"lfence\n\t"
|
"lfence\n\t"
|
||||||
"rdtsc\n\t"
|
"rdtsc\n\t"
|
||||||
|
@ -95,30 +117,35 @@ static HWY_INLINE HWY_MAYBE_UNUSED uint64_t TicksBefore() {
|
||||||
// "memory" avoids reordering. rdx = TSC >> 32.
|
// "memory" avoids reordering. rdx = TSC >> 32.
|
||||||
// "cc" = flags modified by SHL.
|
// "cc" = flags modified by SHL.
|
||||||
: "rdx", "memory", "cc");
|
: "rdx", "memory", "cc");
|
||||||
#elif HWY_COMPILER_MSVC
|
#elif HWY_ARCH_RVV
|
||||||
// Use std::chrono in MSVC 32-bit.
|
asm volatile("rdcycle %0" : "=r"(t));
|
||||||
t = std::chrono::time_point_cast<std::chrono::nanoseconds>(
|
#elif defined(_WIN32) || defined(_WIN64)
|
||||||
std::chrono::steady_clock::now())
|
LARGE_INTEGER counter;
|
||||||
.time_since_epoch()
|
(void)QueryPerformanceCounter(&counter);
|
||||||
.count();
|
t = counter.QuadPart;
|
||||||
#else
|
#elif defined(__MACH__)
|
||||||
// Fall back to OS - unsure how to reliably query cntvct_el0 frequency.
|
t = mach_absolute_time();
|
||||||
|
#elif defined(__HAIKU__)
|
||||||
|
t = system_time_nsecs(); // since boot
|
||||||
|
#else // POSIX
|
||||||
timespec ts;
|
timespec ts;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
t = ts.tv_sec * 1000000000LL + ts.tv_nsec;
|
t = static_cast<Ticks>(ts.tv_sec * 1000000000LL + ts.tv_nsec);
|
||||||
#endif
|
#endif
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static HWY_INLINE HWY_MAYBE_UNUSED uint64_t TicksAfter() {
|
static HWY_INLINE HWY_MAYBE_UNUSED Ticks TicksAfter() {
|
||||||
uint64_t t;
|
Ticks t;
|
||||||
#if HWY_ARCH_X86_64 && HWY_COMPILER_MSVC
|
#if HWY_ARCH_PPC && defined(__GLIBC__)
|
||||||
|
asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
|
||||||
|
#elif HWY_ARCH_X86 && HWY_COMPILER_MSVC
|
||||||
HWY_FENCE;
|
HWY_FENCE;
|
||||||
unsigned aux;
|
unsigned aux;
|
||||||
t = __rdtscp(&aux);
|
t = __rdtscp(&aux);
|
||||||
hwy::LoadFence();
|
hwy::LoadFence();
|
||||||
HWY_FENCE;
|
HWY_FENCE;
|
||||||
#elif HWY_ARCH_X86_64 && (HWY_COMPILER_CLANG || HWY_COMPILER_GCC)
|
#elif HWY_ARCH_X86_64
|
||||||
// Use inline asm because __rdtscp generates code to store TSC_AUX (ecx).
|
// Use inline asm because __rdtscp generates code to store TSC_AUX (ecx).
|
||||||
asm volatile(
|
asm volatile(
|
||||||
"rdtscp\n\t"
|
"rdtscp\n\t"
|
||||||
|
|
|
@ -761,8 +761,10 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id,
|
||||||
JxlEncoderOptions* enc_opts;
|
JxlEncoderOptions* enc_opts;
|
||||||
enc_opts = JxlEncoderOptionsCreate(enc.get(), nullptr);
|
enc_opts = JxlEncoderOptionsCreate(enc.get(), nullptr);
|
||||||
|
|
||||||
JxlEncoderOptionsSetEffort(enc_opts, jxl_save_opts.encoding_effort);
|
JxlEncoderOptionsSetInteger(enc_opts, JXL_ENC_OPTION_EFFORT,
|
||||||
JxlEncoderOptionsSetDecodingSpeed(enc_opts, jxl_save_opts.faster_decoding);
|
jxl_save_opts.encoding_effort);
|
||||||
|
JxlEncoderOptionsSetInteger(enc_opts, JXL_ENC_OPTION_DECODING_SPEED,
|
||||||
|
jxl_save_opts.faster_decoding);
|
||||||
|
|
||||||
// lossless mode
|
// lossless mode
|
||||||
if (jxl_save_opts.lossless || jxl_save_opts.distance < 0.01) {
|
if (jxl_save_opts.lossless || jxl_save_opts.distance < 0.01) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче