Bug 1740415 - Update libjxl to 9e8c576 r=tnikkel

Differential Revision: https://phabricator.services.mozilla.com/D130862
This commit is contained in:
Kagami Sascha Rosylight 2021-11-10 13:32:14 +00:00
Родитель a5f66c95e1
Коммит 75108aa7bb
88 изменённых файлов: 2275 добавлений и 553 удалений

Просмотреть файл

@ -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/

2
third_party/jpeg-xl/AUTHORS поставляемый
Просмотреть файл

@ -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

22
third_party/jpeg-xl/CHANGELOG.md поставляемый
Просмотреть файл

@ -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:

9
third_party/jpeg-xl/CMakeLists.txt поставляемый
Просмотреть файл

@ -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")

25
third_party/jpeg-xl/bash_test.sh поставляемый
Просмотреть файл

@ -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.

2
third_party/jpeg-xl/ci.sh поставляемый
Просмотреть файл

@ -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());

1
third_party/jpeg-xl/lib/extras/codec_pgx.cc поставляемый
Просмотреть файл

@ -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"

1
third_party/jpeg-xl/lib/extras/codec_png.cc поставляемый
Просмотреть файл

@ -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"

1
third_party/jpeg-xl/lib/extras/codec_pnm.cc поставляемый
Просмотреть файл

@ -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"

1
third_party/jpeg-xl/lib/extras/codec_psd.cc поставляемый
Просмотреть файл

@ -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"

110
third_party/jpeg-xl/lib/extras/tone_mapping.cc поставляемый
Просмотреть файл

@ -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

12
third_party/jpeg-xl/lib/extras/tone_mapping.h поставляемый
Просмотреть файл

@ -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_

8
third_party/jpeg-xl/lib/gbench_main.cc поставляемый Normal file
Просмотреть файл

@ -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();

37
third_party/jpeg-xl/lib/include/jxl/decode.h поставляемый
Просмотреть файл

@ -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.

569
third_party/jpeg-xl/lib/include/jxl/encode.h поставляемый
Просмотреть файл

@ -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.
*/ */

1
third_party/jpeg-xl/lib/jxl.cmake поставляемый
Просмотреть файл

@ -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

1
third_party/jpeg-xl/lib/jxl/aux_out.cc поставляемый
Просмотреть файл

@ -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 {

12
third_party/jpeg-xl/lib/jxl/aux_out.h поставляемый
Просмотреть файл

@ -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

34
third_party/jpeg-xl/lib/jxl/base/printf_macros.h поставляемый Normal file
Просмотреть файл

@ -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_

1
third_party/jpeg-xl/lib/jxl/blending.cc поставляемый
Просмотреть файл

@ -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"

21
third_party/jpeg-xl/lib/jxl/common.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"

42
third_party/jpeg-xl/lib/jxl/dct-inl.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

4
third_party/jpeg-xl/lib/jxl/dct_test.cc поставляемый
Просмотреть файл

@ -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>

1
third_party/jpeg-xl/lib/jxl/dec_ans.cc поставляемый
Просмотреть файл

@ -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"

15
third_party/jpeg-xl/lib/jxl/dec_cache.cc поставляемый
Просмотреть файл

@ -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"

9
third_party/jpeg-xl/lib/jxl/dec_file.cc поставляемый
Просмотреть файл

@ -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.");

11
third_party/jpeg-xl/lib/jxl/dec_frame.cc поставляемый
Просмотреть файл

@ -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() &&

1
third_party/jpeg-xl/lib/jxl/dec_group.cc поставляемый
Просмотреть файл

@ -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"

12
third_party/jpeg-xl/lib/jxl/dec_modular.cc поставляемый
Просмотреть файл

@ -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 =

46
third_party/jpeg-xl/lib/jxl/dec_reconstruct_gbench.cc поставляемый Normal file
Просмотреть файл

@ -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:

397
third_party/jpeg-xl/lib/jxl/decode.cc поставляемый
Просмотреть файл

@ -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.

203
third_party/jpeg-xl/lib/jxl/decode_test.cc поставляемый
Просмотреть файл

@ -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()));
}

92
third_party/jpeg-xl/lib/jxl/decode_to_jpeg.cc поставляемый
Просмотреть файл

@ -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

62
third_party/jpeg-xl/lib/jxl/decode_to_jpeg.h поставляемый
Просмотреть файл

@ -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;
} }

4
third_party/jpeg-xl/lib/jxl/enc_ans.cc поставляемый
Просмотреть файл

@ -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"

1
third_party/jpeg-xl/lib/jxl/enc_modular.cc поставляемый
Просмотреть файл

@ -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: {

209
third_party/jpeg-xl/lib/jxl/encode.cc поставляемый
Просмотреть файл

@ -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),

13
third_party/jpeg-xl/lib/jxl/encode_internal.h поставляемый
Просмотреть файл

@ -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.

198
third_party/jpeg-xl/lib/jxl/encode_test.cc поставляемый
Просмотреть файл

@ -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

79
third_party/jpeg-xl/lib/jxl/fake_parallel_runner_testonly.h поставляемый Normal file
Просмотреть файл

@ -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_

1
third_party/jpeg-xl/lib/jxl/fields.cc поставляемый
Просмотреть файл

@ -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
third_party/jpeg-xl/lib/jxl/fields.h поставляемый
Просмотреть файл

@ -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;

1
third_party/jpeg-xl/lib/jxl/frame_header.cc поставляемый
Просмотреть файл

@ -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"

1
third_party/jpeg-xl/lib/jxl/headers.cc поставляемый
Просмотреть файл

@ -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"

14
third_party/jpeg-xl/lib/jxl/image.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
} }

1
third_party/jpeg-xl/lib/jxl/image_bundle.cc поставляемый
Просмотреть файл

@ -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"

31
third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc поставляемый
Просмотреть файл

@ -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();

41
third_party/jpeg-xl/lib/jxl/jxl_test.cc поставляемый
Просмотреть файл

@ -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

36
third_party/jpeg-xl/lib/jxl/sanitizers.h поставляемый
Просмотреть файл

@ -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.

1
third_party/jpeg-xl/lib/jxl/splines.cc поставляемый
Просмотреть файл

@ -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"

1
third_party/jpeg-xl/lib/jxl/splines_test.cc поставляемый
Просмотреть файл

@ -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"

4
third_party/jpeg-xl/lib/jxl_benchmark.cmake поставляемый
Просмотреть файл

@ -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

1
third_party/jpeg-xl/lib/jxl_tests.cmake поставляемый
Просмотреть файл

@ -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

3
third_party/jpeg-xl/lib/lib.gni поставляемый
Просмотреть файл

@ -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);
} }

81
third_party/jpeg-xl/lib/profiler/tsc_timer.h поставляемый
Просмотреть файл

@ -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) {