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
# 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
# Must be a long or short commit SHA (long preferred)
# NOTE(krosylight): Update highway together when updating this!
revision: 0eff04c3a04e72e78d35f0965f17f54a98d61830
revision: 9e8c5766ba32c008ddcaf11f0fac591aa7964f7b
# The package's license, where possible using the mnemonic from
# https://spdx.org/licenses/
@ -52,3 +52,4 @@ vendoring:
- doc/
- third_party/testdata/
- tools/

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

@ -20,6 +20,7 @@ Alexander Sago <cagelight@gmail.com>
Dirk Lemstra <dirk@lemstra.org>
Jon Sneyers <jon@cloudinary.com>
Lovell Fuller
Kleis Auke Wolthuizen <github@kleisauke.nl>
Marcin Konicki <ahwayakchih@gmail.com>
Petr Diblík
Pieter Wuille
@ -30,3 +31,4 @@ Misaki Kasumi <misakikasumi@outlook.com>
Samuel Leong <wvvwvvvvwvvw@gmail.com>
Alex Xu (Hello71) <alex_y_xu@yahoo.ca>
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/),
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
### Added
- 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)
if(NOT "${ASCIIDOC}" STREQUAL "ASCIIDOC-NOTFOUND")
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)
set(ASCIIDOC_PY_FOUND "${Python2_Interpreter_FOUND}")
set(ASCIIDOC_PY Python2::Interpreter)
@ -386,7 +391,7 @@ if (ASCIIDOC_PY_FOUND)
add_custom_command(
OUTPUT "${PAGE}.1"
COMMAND "${ASCIIDOC_PY}"
ARGS "${ASCIIDOC}"
ARGS ${ASCIIDOC}
--format manpage --destination-dir="${CMAKE_CURRENT_BINARY_DIR}"
"${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.
test_printf_size_t() {
local ret=0
if grep -n -E '%[0-9]*z[udx]' \
$(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$'); then
echo "Don't use '%zu' or '%zd' in a format string, instead use " \
"'%\" PRIuS \"' or '%\" PRIdS \"'." >&2
return 1
ret=1
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.

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

@ -468,6 +468,8 @@ cmake_build_and_test() {
if [[ "${PACK_TEST:-}" == "1" ]]; then
(cd "${BUILD_DIR}"
${FIND_BIN} -name '*.cmake' -a '!' -path '*CMakeFiles*'
# gtest / gmock / gtest_main shared libs
${FIND_BIN} lib/ -name 'libg*.so*'
${FIND_BIN} -type d -name tests -a '!' -path '*CMakeFiles*'
) | tar -C "${BUILD_DIR}" -cf "${BUILD_DIR}/tests.tar.xz" -T - \
--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);
JxlDecoderCloseInput(dec.get());
const constexpr size_t kChunkSize = 65536;
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};
JxlDecoderSetInput(dec.get(), jxl, size);
JxlDecoderCloseInput(dec.get());
for (;;) {
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/compiler_specific.h"
#include "lib/jxl/base/file_io.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/dec_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/compiler_specific.h"
#include "lib/jxl/base/file_io.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/common.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/compiler_specific.h"
#include "lib/jxl/base/file_io.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/color_management.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/compiler_specific.h"
#include "lib/jxl/base/file_io.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/common.h"
#include "lib/jxl/fields.h" // AllDefault

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

@ -15,6 +15,7 @@
#include "gtest/gtest.h"
#include "lib/extras/codec_pgx.h"
#include "lib/extras/codec_pnm.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/random.h"
#include "lib/jxl/base/thread_pool_internal.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)));
const V ratio = new_luminance / luminance;
const V multiplier = ratio *
Set(df, ib->metadata()->IntensityTarget()) *
inv_max_display_nits;
const V normalizer =
Set(df, ib->metadata()->IntensityTarget()) * 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}) {
*val = IfThenElse(luminance < Set(df, 1e-6), gray,
MulAdd(gray_mix, gray - *val, *val));
*val = IfThenElse(luminance <= Set(df, 1e-6f), new_luminance,
*val * ratio) *
normalizer;
}
Store(red, df, row_r + x);
@ -134,6 +117,77 @@ Status ToneMapFrame(const std::pair<float, float> display_nits,
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)
} // namespace HWY_NAMESPACE
} // namespace jxl
@ -144,7 +198,8 @@ namespace jxl {
namespace {
HWY_EXPORT(ToneMapFrame);
}
HWY_EXPORT(GamutMapFrame);
} // namespace
Status ToneMapTo(const std::pair<float, float> display_nits,
CodecInOut* const io, ThreadPool* const pool) {
@ -156,5 +211,14 @@ Status ToneMapTo(const std::pair<float, float> display_nits,
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
#endif

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

@ -10,9 +10,21 @@
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,
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
#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
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
* 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
* 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.
* 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 data pointer to next bytes to read from
* @param size amount of bytes available starting from data
* @return JXL_DEC_ERROR if input was already set without releasing,
* JXL_DEC_SUCCESS otherwise.
* @return JXL_DEC_ERROR if input was already set without releasing or
* JxlDecoderCloseInput was already called, JXL_DEC_SUCCESS otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec,
const uint8_t* data,
@ -483,6 +499,23 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(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
* all other JxlBasicInfo fields, if available.

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

@ -13,6 +13,7 @@
#ifndef JXL_ENCODE_H_
#define JXL_ENCODE_H_
#include "jxl/codestream_header.h"
#include "jxl/decode.h"
#include "jxl/jxl_export.h"
#include "jxl/memory_manager.h"
@ -73,42 +74,163 @@ typedef enum {
} 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 {
/** 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
* 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
* 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
* option, use 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for 4x4
* downsampling, 8 for 8x8 downsampling. The default value is 1.
* option, use -1 for the default behavior (depends on encoder
* implementation), 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for
* 4x4 downsampling, 8 for 8x8 downsampling.
*/
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
* encoder default, 0 to disable, 1 to enable. The
/** Adds noise to the image emulating photographic film noise, the higher 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.
*/
JXL_ENC_OPTION_DOTS = 3,
JXL_ENC_OPTION_RESPONSIVE = 15,
/** Enables or disables patches generation. Integer option, use -1 for the
* encoder default, 0 to disable, 1 to enable.
/** Set the progressive mode for the AC coefficients of VarDCT, using spectral
* 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
* encoder default, 0 to disable, 1 to enable.
/** Set the progressive mode for the AC coefficients of VarDCT, using
* 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
* 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
* 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_UINT16
* - JXL_TYPE_FLOAT16, 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
* in the JxlBasicInfo. If true, the pixels are assumed to be encoded in the
* original profile that is set with JxlEncoderSetColorEncoding or
@ -240,7 +371,152 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddImageFrame(
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
* 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.
*
* 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 info global image metadata. Object owned by the caller and its
* contents are copied internally.
@ -305,103 +585,50 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
const JxlBasicInfo* info);
/**
* Configure the encoder to store JPEG reconstruction metadata in the JPEG XL
* container.
* Initializes a JxlExtraChannelInfo struct to default values.
* For forwards-compatibility, this function has to be called before values
* are assigned to the struct fields.
* The default values correspond to an 8-bit channel of the provided type.
*
* The encoder must be configured to use the JPEG XL container format using @ref
* JxlEncoderUseContainer for this to have any effect.
*
* If this is set to true and a single JPEG frame is added, it will be
* 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.
* @param type type of the extra channel.
* @param info global extra channel metadata. Object owned by the caller and its
* contents are copied internally.
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus
JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata);
JXL_EXPORT void JxlEncoderInitExtraChannelInfo(JxlExtraChannelType type,
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
* reconstruction (@ref JxlEncoderStoreJPEGMetadata) or other metadata like
* EXIF; but it adds a few bytes to the encoded file for container headers even
* if there is no extra metadata.
*
* @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.
* @param enc encoder object
* @param index index of the extra channel to set.
* @param info global extra channel metadata. Object owned by the caller and its
* contents are copied internally.
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc,
JXL_BOOL use_container);
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelInfo(
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 lossless whether the options should be lossless
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
* otherwise.
* @param enc encoder object
* @param index index of the extra channel to set.
* @param name buffer with the name of the extra channel.
* @param size size of the name buffer in bytes.
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus
JxlEncoderOptionsSetLossless(JxlEncoderOptions* options, JXL_BOOL lossless);
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelName(JxlEncoder* enc,
size_t index,
const char* name,
size_t size);
/**
* Set the decoding speed tier for the provided options. Minimum is 0 (highest
* quality), and maximum is 4 (lowest quality). Default is 0.
*
* @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.
* Sets a frame-specific option of integer type to the encoder options.
* The JxlEncoderOptionId argument determines which option is set.
*
* @param options set of encoder options to update with the new mode.
* @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
* function was called.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderOptionsSetAsInteger(
JXL_EXPORT JxlEncoderStatus JxlEncoderOptionsSetInteger(
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
* 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 size in bytes of the requested memory region.
* @returns @c NULL if the memory can not be allocated,
* @returns pointer to the memory otherwise.
* @return @c NULL if the memory can not be allocated,
* @return pointer to the memory otherwise.
*/
typedef void* (*jpegxl_alloc_func)(void* opaque, size_t size);

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

@ -70,8 +70,8 @@ typedef int JxlParallelRetCode;
* JxlParallelRunner() must be passed here.
* @param num_threads the maximum number of threads. This value must be
* positive.
* @returns 0 if the initialization process was successful.
* @returns an error code if there was an error, which should be returned by
* @return 0 if the initialization process was successful.
* @return an error code if there was an error, which should be returned by
* JxlParallelRunner().
*/
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
* 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.
* @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.
* @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/padded_bytes.cc
jxl/base/padded_bytes.h
jxl/base/printf_macros.h
jxl/base/profiler.h
jxl/base/random.cc
jxl/base/random.h

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

@ -10,6 +10,7 @@
#include <numeric> // accumulate
#include "lib/jxl/aux_out_fwd.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/enc_bit_writer.h"
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.
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@ -103,7 +104,7 @@ static inline const char* LayerName(size_t layer) {
case kLayerExtraChannels:
return "extra channels";
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;
}
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) {
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",
num_clustered_histograms * 1.0 / num_inputs, histogram_bits >> 3,
extra_bits >> 3,
num_clustered_histograms * 1.0 / num_inputs,
static_cast<int64_t>(histogram_bits >> 3),
static_cast<int64_t>(extra_bits >> 3),
(histogram_bits + clustered_entropy + extra_bits) / 8.0);
printf("]");
}

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

@ -20,8 +20,8 @@
#include <hwy/base.h> // kMaxVectorSize
#include <limits>
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/common.h"
namespace jxl {
namespace {

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

@ -55,6 +55,8 @@
#define JXL_NORETURN __declspec(noreturn)
#elif JXL_COMPILER_GCC || JXL_COMPILER_CLANG
#define JXL_NORETURN __attribute__((noreturn))
#else
#define JXL_NORETURN
#endif
#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/alpha.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/image_ops.h"
namespace jxl {

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

@ -83,7 +83,6 @@ JxlDecoderStatus JxlBoxContentDecoder::Process(const uint8_t* next_in,
size_t can_read = avail_in;
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);
memcpy(*next_out, next_in, to_write);
*next_out += to_write;

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

@ -41,6 +41,7 @@
#define HWY_TARGET_INCLUDE "lib/jxl/butteraugli/butteraugli.cc"
#include <hwy/foreach_target.h>
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/profiler.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/convolve.h"
@ -1136,7 +1137,7 @@ void Mask(const ImageF& mask0, const ImageF& mask1,
FuzzyErosion(blurred1, &diff1);
for (size_t y = 0; y < ysize; ++y) {
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) {
static const float kMaskToErrorMul = 10.0;
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 x = 0; x < xscaled; ++x) {
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 =
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];
scaled1.PlaneRow(i, y)[x] = rgb1.PlaneRow(i, y2)[x2];
}

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

@ -13,6 +13,7 @@
#include <vector>
#include "gtest/gtest.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/random.h"
#include "lib/jxl/base/span.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
#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 {
// 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/data_parallel.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/thread_pool_internal.h"
#include "lib/jxl/image_ops.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.
// Requires that block is 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.
// Computes the maybe-transposed, scaled DCT of a block, that needs to be
// HWY_ALIGN'ed.
template <size_t ROWS, size_t COLS>
struct ComputeScaledDCT {
// 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
// HWY_ALIGN'ed. Used for rectangular blocks.
// Computes the maybe-transposed, scaled IDCT of a block, that needs to be
// HWY_ALIGN'ed.
template <size_t ROWS, size_t COLS>
struct ComputeScaledIDCT {
// 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]) {
HWY_ALIGN float tmp_block[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.
Transpose<N, N>::Run(DCTFrom(tmp_block, N), DCTTo(block, N));
@ -50,7 +50,7 @@ void ComputeIDCT(float block[N * N]) {
// Untranspose.
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>

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

@ -12,6 +12,7 @@
#include "lib/jxl/ans_common.h"
#include "lib/jxl/ans_params.h"
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/profiler.h"
#include "lib/jxl/base/status.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.
JXL_DASSERT(r.x0() == 0 || r.x0() >= borderx);
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 =
DivCeil(r.x0() + r.xsize() +
(r.x0() + r.xsize() == frame_dim.xsize_padded ? 0 : borderx),
DivCeil(std::min(r.x0() + r.xsize() + borderx, frame_dim.xsize_padded),
1 << hshift);
JXL_DASSERT(r.y0() == 0 || r.y0() >= bordery);
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 =
DivCeil(r.y0() + r.ysize() +
(r.y0() + r.ysize() == frame_dim.ysize_padded ? 0 : bordery),
DivCeil(std::min(r.y0() + r.ysize() + bordery, frame_dim.ysize_padded),
1 << vshift);
// Copy other groups' borders from the border storage.
if (y0src < y0) {
JXL_DASSERT(gy > 0);
CopyImageTo(
Rect(x0src, (gy * 2 - 1) * bordery_write, x1src - x0src, bordery_write),
border_storage_h,
@ -107,6 +109,8 @@ void LoadBorders(const Rect& block_rect, size_t hshift, size_t vshift,
plane_out);
}
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(
Rect(x0src, (gy * 2 + 2) * bordery_write, x1src - x0src, bordery_write),
border_storage_h,
@ -115,6 +119,7 @@ void LoadBorders(const Rect& block_rect, size_t hshift, size_t vshift,
plane_out);
}
if (x0src < x0) {
JXL_DASSERT(gx > 0);
CopyImageTo(
Rect((gx * 2 - 1) * borderx_write, y0src, borderx_write, y1src - y0src),
border_storage_v,
@ -123,6 +128,8 @@ void LoadBorders(const Rect& block_rect, size_t hshift, size_t vshift,
plane_out);
}
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(
Rect((gx * 2 + 2) * borderx_write, y0src, borderx_write, y1src - y0src),
border_storage_v,

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

@ -22,6 +22,7 @@
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/cache_aligned.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/common.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);
}
// Skip frames that are not displayed.
bool found_displayed_frame = true;
do {
dec_ok =
DecodeFrame(dparams, &dec_state, pool, &reader, &io->frames.back(),
@ -155,13 +156,19 @@ Status DecodeFile(const DecompressParams& dparams,
JXL_RETURN_IF_ERROR(dec_ok);
} else if (!dec_ok) {
io->frames.pop_back();
found_displayed_frame = false;
break;
}
} while (dec_state.shared->frame_header.frame_type !=
FrameType::kRegularFrame &&
dec_state.shared->frame_header.frame_type !=
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);
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/compiler_specific.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/status.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();
bool is_gray = (num_components == 1);
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++) {
// TODO(eustas): why 1-st quant table for gray?
size_t quant_c = is_gray ? 1 : c;
size_t qpos = jpeg_data->components[jpeg_c_map[c]].quant_idx;
JXL_CHECK(qpos != jpeg_data->quant.size());
qt_set |= 1 << qpos;
for (size_t x = 0; x < 8; x++) {
for (size_t y = 0; y < 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.
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/aux_out.h"
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/profiler.h"
#include "lib/jxl/base/status.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/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/compressed_dc.h"
@ -158,17 +159,18 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader,
if (is_gray && frame_header.color_transform == ColorTransform::kNone) {
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);
if (has_tree) {
size_t tree_size_limit =
1024 + frame_dim.xsize * frame_dim.ysize * nb_chans / 16;
size_t tree_size_limit = std::min(
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(
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;

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

@ -18,6 +18,7 @@
#include "lib/jxl/ans_params.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/override.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/blending.h"
#include "lib/jxl/chroma_from_luma.h"

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

@ -140,7 +140,7 @@ Status UndoXYBInPlace(Image3F* idct, const Rect& rect,
DoUndoXYBInPlace(idct, rect, Op709(), output_encoding_info);
} else if (output_encoding_info.color_encoding.tf.IsGamma() ||
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);
} else {
// This is a programming error.
@ -418,10 +418,13 @@ HWY_EXPORT(DoYCbCrUpsampling);
void UndoXYB(const Image3F& src, Image3F* dst,
const OutputEncodingInfo& output_info, ThreadPool* pool) {
CopyImageTo(src, dst);
pool->Run(0, src.ysize(), ThreadPool::SkipInit(), [&](int y, int /*thread*/) {
JXL_CHECK(HWY_DYNAMIC_DISPATCH(UndoXYBInPlace)(dst, Rect(*dst).Line(y),
output_info));
});
RunOnPool(
pool, 0, src.ysize(), ThreadPool::SkipInit(),
[&](int y, int /*thread*/) {
JXL_CHECK(HWY_DYNAMIC_DISPATCH(UndoXYBInPlace)(dst, Rect(*dst).Line(y),
output_info));
},
"UndoXYB");
}
namespace {
@ -441,7 +444,9 @@ class EnsurePaddingInPlaceRowByRow {
size_t image_ysize, size_t xpadding, size_t ypadding, ssize_t* y0,
ssize_t* y1) {
// 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);
*y1 = rect.ysize() + std::min(ypadding, image_ysize - image_rect.ysize() -
image_rect.y0());
@ -455,7 +460,7 @@ class EnsurePaddingInPlaceRowByRow {
strategy_ = kSlow;
}
y0_ = rect.y0();
JXL_DASSERT(rect.x0() >= xpadding);
JXL_ASSERT(rect.x0() >= xpadding);
x0_ = x1_ = rect.x0() - xpadding;
// If close to the left border - do mirroring.
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) {
x2_ = rect.x0() + image_xsize - image_rect.x0();
}
JXL_DASSERT(image_xsize == (x2_ - x1_) ||
(x1_ - x0_ <= x2_ - x1_ && x3_ - x2_ <= x2_ - x1_));
JXL_ASSERT(x0_ <= x1_);
JXL_ASSERT(x1_ <= x2_);
JXL_ASSERT(x2_ <= x3_);
JXL_ASSERT(image_xsize == (x2_ - x1_) ||
(x1_ - x0_ <= x2_ - x1_ && x3_ - x2_ <= x2_ - x1_));
}
public:
@ -793,7 +801,9 @@ Status FinalizeImageRect(
}
ssize_t ensure_padding_y0, ensure_padding_y1;
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,
frame_header.extra_channel_upsampling[ec]);
size_t ecys = DivCeil(frame_dim.ysize_upsampled,
@ -836,8 +846,10 @@ Status FinalizeImageRect(
extra_channels[ec].second.ysize() + rect_for_if_storage.ysize() -
rect_for_upsampling.ysize());
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,
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_y1);
}
@ -1048,7 +1060,8 @@ Status FinalizeImageRect(
dec_state->rgb_output_is_rgba, alpha,
alpha_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);
}
if (dec_state->pixel_callback != nullptr) {
@ -1157,7 +1170,9 @@ Status FinalizeFrameDecoding(ImageBundle* decoded,
std::vector<std::pair<ImageF*, Rect>> ec_rects;
ec_rects.reserve(decoded->extra_channels().size());
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) {
Rect ec_input_rect(kBlockDim, 2, r.xsize(), r.ysize());
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 {
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
// is a DCT_ROWS*DCT_COLS-sized DCT block, by doing a ROWS*COLS DCT on the
// 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.
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) {
for (size_t y = 0; y < LF_ROWS; y++) {
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];
}
}
ComputeTransposedScaledIDCT<4>()(
ComputeScaledIDCT<4, 4>()(
block,
DCTTo(pixels + afv_y * 4 * pixels_stride + (afv_x == 1 ? 0 : 4),
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];
}
}
ComputeTransposedScaledIDCT<4>()(
ComputeScaledIDCT<4, 4>()(
block,
DCTTo(pixels + y * 4 * pixels_stride + x * 4, pixels_stride),
scratch_space);
@ -599,8 +582,8 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
}
case Type::DCT16X16: {
PROFILER_ZONE("IDCT 16");
ComputeTransposedScaledIDCT<16>()(
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
ComputeScaledIDCT<16, 16>()(coefficients, DCTTo(pixels, pixels_stride),
scratch_space);
break;
}
case Type::DCT16X8: {
@ -641,14 +624,14 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
}
case Type::DCT32X32: {
PROFILER_ZONE("IDCT 32");
ComputeTransposedScaledIDCT<32>()(
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
ComputeScaledIDCT<32, 32>()(coefficients, DCTTo(pixels, pixels_stride),
scratch_space);
break;
}
case Type::DCT: {
PROFILER_ZONE("IDCT 8");
ComputeTransposedScaledIDCT<8>()(
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
ComputeScaledIDCT<8, 8>()(coefficients, DCTTo(pixels, pixels_stride),
scratch_space);
break;
}
case Type::AFV0: {
@ -685,8 +668,8 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
}
case Type::DCT64X64: {
PROFILER_ZONE("IDCT 64");
ComputeTransposedScaledIDCT<64>()(
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
ComputeScaledIDCT<64, 64>()(coefficients, DCTTo(pixels, pixels_stride),
scratch_space);
break;
}
case Type::DCT128X64: {
@ -703,8 +686,8 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
}
case Type::DCT128X128: {
PROFILER_ZONE("IDCT 128");
ComputeTransposedScaledIDCT<128>()(
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
ComputeScaledIDCT<128, 128>()(coefficients, DCTTo(pixels, pixels_stride),
scratch_space);
break;
}
case Type::DCT256X128: {
@ -721,8 +704,8 @@ HWY_MAYBE_UNUSED void TransformToPixels(const AcStrategy::Type strategy,
}
case Type::DCT256X256: {
PROFILER_ZONE("IDCT 256");
ComputeTransposedScaledIDCT<256>()(
coefficients, DCTTo(pixels, pixels_stride), scratch_space);
ComputeScaledIDCT<256, 256>()(coefficients, DCTTo(pixels, pixels_stride),
scratch_space);
break;
}
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 {
kHeader, // Parsing box header of the next box, or start of non-container
// stream
kFtyp, // The ftyp box
kSkip, // Box whose contents are skipped
kCodestream, // Handling codestream box contents, or non-container stream
kPartialCodestream, // Handling the extra header of partial codestream 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.
struct Sections {
// sections_begin = position in the frame where the sections begin, after
@ -444,6 +452,7 @@ struct JxlDecoderStruct {
// Settings
bool keep_orientation;
bool render_spotcolors;
// 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,
@ -455,6 +464,7 @@ struct JxlDecoderStruct {
// Fields for reading the basic info from the header.
size_t basic_info_size_hint;
bool have_container;
size_t box_count;
// 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
@ -551,12 +561,33 @@ struct JxlDecoderStruct {
jxl::JxlToJpegDecoder jpeg_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
uint64_t dec_pixels;
const uint8_t* next_in;
size_t avail_in;
bool input_closed;
void AdvanceInput(size_t size) {
next_in += size;
@ -601,10 +632,19 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
dec->box_out_buffer_size = 0;
dec->box_out_buffer_begin = 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->basic_info_size_hint = InitialBasicInfoSizeHint();
dec->have_container = 0;
dec->box_count = 0;
dec->preview_out_buffer_set = false;
dec->image_out_buffer_set = false;
dec->preview_out_buffer = nullptr;
@ -617,6 +657,7 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
dec->dec_pixels = 0;
dec->next_in = 0;
dec->avail_in = 0;
dec->input_closed = false;
dec->passes_state.reset(nullptr);
dec->frame_dec.reset(nullptr);
@ -645,6 +686,7 @@ void JxlDecoderReset(JxlDecoder* dec) {
dec->thread_pool.reset();
dec->keep_orientation = false;
dec->render_spotcolors = true;
dec->orig_events_wanted = 0;
dec->frame_references.clear();
dec->frame_saved_as.clear();
@ -746,6 +788,15 @@ JxlDecoderStatus JxlDecoderSetKeepOrientation(JxlDecoder* dec,
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 {
@ -1119,6 +1170,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
auto reader = GetBitReader(compressed);
jxl::DecompressParams dparams;
dparams.preview = want_preview ? jxl::Override::kOn : jxl::Override::kOff;
dparams.render_spotcolors = dec->render_spotcolors;
jxl::ImageBundle ib(&dec->metadata.m);
PassesDecoderState preview_dec_state;
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->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;
if (pos >= size) {
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->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
// the ImageBundle.
@ -1381,10 +1443,17 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
if (!dec->frame_dec->FinalizeFrame()) {
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_stage = FrameStage::kFullOutput;
}
bool output_jpeg_reconstruction = false;
if (dec->frame_stage == FrameStage::kFullOutput) {
if (dec->is_last_of_still) {
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
// status without outputting pixels.
if (dec->jpeg_decoder.IsOutputSet() && dec->ib->jpeg_data != nullptr) {
JxlDecoderStatus status =
dec->jpeg_decoder.WriteOutput(*dec->ib->jpeg_data);
if (status != JXL_DEC_SUCCESS) return status;
output_jpeg_reconstruction = true;
} else if (return_full_image && dec->image_out_buffer_set) {
if (!dec->frame_dec->HasRGBBuffer()) {
// 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_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;
} 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,
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->avail_in = size;
@ -1467,8 +1545,19 @@ size_t JxlDecoderReleaseInput(JxlDecoder* dec) {
return result;
}
void JxlDecoderCloseInput(JxlDecoder* dec) { dec->input_closed = true; }
JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec, uint8_t* data,
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);
}
@ -1521,29 +1610,8 @@ static JxlDecoderStatus ParseBoxHeader(const uint8_t* in, size_t size,
return JXL_DEC_SUCCESS;
}
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");
}
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;
}
}
// This includes handling the codestream if it is not a box-based jxl file.
static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
// Box handling loop
for (;;) {
if (dec->box_stage != BoxStage::kHeader) {
@ -1567,6 +1635,77 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
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) {
@ -1577,12 +1716,27 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
continue;
}
if (dec->avail_in == 0) {
if (dec->stage == DecoderStage::kFinished) {
// All codestream boxes done, return success. However, if the user
// still has more input, which could be a next metadata box, it's
// still possible to continue next JxlDecoderProcessInput calls.
if (dec->stage != DecoderStage::kFinished) {
// Not yet seen (all) codestream boxes.
return JXL_DEC_NEED_MORE_INPUT;
}
if (dec->JbrdNeedMoreBoxes()) {
return JXL_DEC_NEED_MORE_INPUT;
}
if (dec->input_closed) {
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;
}
@ -1597,7 +1751,6 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
}
return status;
}
if (memcmp(dec->box_type, "brob", 4) == 0) {
if (dec->avail_in < header_size + 4) {
return JXL_DEC_NEED_MORE_INPUT;
@ -1609,6 +1762,17 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
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->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_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) {
bool decompress =
dec->decompress_boxes && memcmp(dec->box_type, "brob", 4) == 0;
dec->box_content_decoder.StartBox(
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;
} else if (memcmp(dec->box_type, "jxlp", 4) == 0) {
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;
} else {
dec->box_stage = BoxStage::kSkip;
@ -1642,6 +1832,16 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
dec->box_out_buffer_set_current_box = false;
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) {
if (dec->last_codestream_seen) {
return JXL_API_ERROR("cannot have codestream after last codestream");
@ -1681,19 +1881,29 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
JxlDecoderStatus status =
jxl::JxlDecoderProcessCodestream(dec, codestream, avail_codestream);
if (!have_copy && status == JXL_DEC_NEED_MORE_INPUT) {
dec->codestream_copy.insert(dec->codestream_copy.end(), dec->next_in,
dec->next_in + avail_codestream);
dec->AdvanceInput(avail_codestream);
if (status == JXL_DEC_FULL_IMAGE) {
if (dec->recon_output_jpeg != JpegReconStage::kNone) {
continue;
}
}
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 &&
dec->file_pos == dec->box_contents_end) {
dec->box_stage = BoxStage::kHeader;
continue;
if (dec->file_pos == dec->box_contents_end) {
dec->box_stage = BoxStage::kHeader;
continue;
}
}
if (status == JXL_DEC_SUCCESS) {
if (dec->JbrdNeedMoreBoxes()) {
dec->box_stage = BoxStage::kSkip;
continue;
}
if (dec->box_contents_unbounded) {
// Last box reached and codestream done, nothing more to do.
dec->AdvanceInput(dec->avail_in);
@ -1704,7 +1914,7 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
dec->box_stage = BoxStage::kSkip;
continue;
} 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.
dec->AdvanceInput(dec->avail_in);
break;
@ -1724,6 +1934,30 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
size_t consumed = next_in - dec->next_in;
dec->AdvanceInput(consumed);
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;
// If successful JPEG reconstruction, return the success if the user
// cares about it, otherwise continue.
@ -1737,9 +1971,19 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
}
} else if (dec->box_stage == BoxStage::kSkip) {
if (dec->box_contents_unbounded) {
// Nothing further to do, an unbounded box is the last box,
// can end early.
break;
if (dec->input_closed) {
return JXL_DEC_SUCCESS;
}
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.
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_API_ERROR("codestream never finished");
return JXL_DEC_SUCCESS;
}
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.

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

@ -1474,6 +1474,7 @@ struct PixelTestConfig {
// Exif orientation, 1-8
JxlOrientation orientation;
bool keep_orientation;
size_t upsampling;
};
class DecodeTestParam : public ::testing::TestWithParam<PixelTestConfig> {};
@ -1495,6 +1496,8 @@ TEST_P(DecodeTestParam, PixelTest) {
0};
jxl::CompressParams cparams;
cparams.SetLossless(); // Lossless to verify pixels exactly after roundtrip.
cparams.resampling = config.upsampling;
cparams.ec_resampling = config.upsampling;
jxl::PaddedBytes compressed = jxl::CreateTestJXLCodestream(
jxl::Span<const uint8_t>(pixels.data(), pixels.size()), config.xsize,
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(),
nullptr, nullptr, static_cast<jxl::Orientation>(config.orientation)));
}
EXPECT_EQ(0u, ComparePixels(pixels.data(), pixels2.data(), xsize, ysize,
format_orig, format));
if (config.upsampling == 1) {
EXPECT_EQ(0u, ComparePixels(pixels.data(), pixels2.data(), xsize, ysize,
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);
}
@ -1581,7 +1594,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
CodeStreamBoxFormat box, JxlOrientation orientation,
bool keep_orientation, OutputFormat format,
bool use_callback, bool set_buffer_early,
bool resizable_runner) {
bool resizable_runner, size_t upsampling) {
PixelTestConfig c;
c.grayscale = ch.grayscale;
c.include_alpha = ch.include_alpha;
@ -1597,17 +1610,21 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
c.use_resizable_runner = resizable_runner;
c.orientation = orientation;
c.keep_orientation = keep_orientation;
c.upsampling = upsampling;
all_tests.push_back(c);
};
// Test output formats and methods.
for (ChannelInfo ch : ch_info) {
for (int use_callback = 0; use_callback <= 1; use_callback++) {
for (OutputFormat fmt : out_formats) {
make_test(ch, 301, 33, /*add_preview=*/false,
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
/*keep_orientation=*/false, fmt, use_callback,
/*set_buffer_early=*/false, /*resizable_runner=*/false);
for (size_t upsampling : {1, 2, 4, 8}) {
for (OutputFormat fmt : out_formats) {
make_test(ch, 301, 33, /*add_preview=*/false,
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
/*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,
/*keep_orientation=*/false, out_formats[0],
/*use_callback=*/false,
/*set_buffer_early=*/false, /*resizable_runner=*/false);
/*set_buffer_early=*/false, /*resizable_runner=*/false, 1);
}
// Test previews.
for (int add_preview = 0; add_preview <= 1; add_preview++) {
make_test(ch_info[0], 77, 33, add_preview, CodeStreamBoxFormat::kCSBF_None,
JXL_ORIENT_IDENTITY, /*keep_orientation=*/false, out_formats[0],
/*use_callback=*/false, /*set_buffer_early=*/false,
/*resizable_runner=*/false);
/*resizable_runner=*/false, 1);
}
// Test setting buffers early.
make_test(ch_info[0], 300, 33, /*add_preview=*/false,
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
/*keep_orientation=*/false, out_formats[0],
/*use_callback=*/false, /*set_buffer_early=*/true,
/*resizable_runner=*/false);
/*resizable_runner=*/false, 1);
// Test using the resizable runner
for (size_t i = 0; i < 4; i++) {
@ -1639,7 +1656,7 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
CodeStreamBoxFormat::kCSBF_None, JXL_ORIENT_IDENTITY,
/*keep_orientation=*/false, out_formats[0],
/*use_callback=*/false, /*set_buffer_early=*/false,
/*resizable_runner=*/true);
/*resizable_runner=*/true, 1);
}
// Test orientations.
@ -1649,13 +1666,13 @@ std::vector<PixelTestConfig> GeneratePixelTests() {
static_cast<JxlOrientation>(orientation),
/*keep_orientation=*/false, out_formats[0],
/*use_callback=*/false, /*set_buffer_early=*/true,
/*resizable_runner=*/false);
/*resizable_runner=*/false, 1);
make_test(ch_info[0], 280, 12, /*add_preview=*/false,
CodeStreamBoxFormat::kCSBF_None,
static_cast<JxlOrientation>(orientation),
/*keep_orientation=*/true, out_formats[0],
/*use_callback=*/false, /*set_buffer_early=*/true,
/*resizable_runner=*/false);
/*resizable_runner=*/false, 1);
}
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.orientation != 1) os << "O" << c.orientation;
if (c.keep_orientation) os << "Keep";
if (c.upsampling > 1) os << "x" << c.upsampling;
return os;
}
@ -3315,6 +3333,14 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
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) {
size_t xsize = 80, ysize = 90;
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));
JxlDecoderDestroy(dec);
@ -3448,6 +3478,7 @@ TEST(DecodeTest, ExifBrobBoxTest) {
if (!streaming) {
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
JxlDecoderCloseInput(dec);
}
// for streaming input case
const uint8_t* next_in = compressed.data();
@ -3476,6 +3507,7 @@ TEST(DecodeTest, ExifBrobBoxTest) {
total_in += amount;
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetInput(dec, next_in, avail_in));
if (total_in == compressed.size()) JxlDecoderCloseInput(dec);
} else {
FAIL();
break;
@ -3528,6 +3560,7 @@ TEST(DecodeTest, ExifBrobBoxTest) {
if (!streaming) {
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
JxlDecoderCloseInput(dec);
}
// for streaming input case
const uint8_t* next_in = compressed.data();
@ -3558,6 +3591,7 @@ TEST(DecodeTest, ExifBrobBoxTest) {
total_in += amount;
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetInput(dec, next_in, avail_in));
if (total_in == compressed.size()) JxlDecoderCloseInput(dec);
} else {
FAIL();
break;
@ -3628,6 +3662,7 @@ TEST(DecodeTest, PartialCodestreamBoxTest) {
dec, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE | JXL_DEC_BOX));
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetInput(dec, compressed.data(), compressed.size()));
JxlDecoderCloseInput(dec);
size_t num_jxlp = 0;
@ -3707,6 +3742,7 @@ TEST(DecodeTest, PartialCodestreamBoxTest) {
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetInput(dec, extracted_codestream.data(),
extracted_codestream.size()));
JxlDecoderCloseInput(dec);
size_t num_boxes = 0;
@ -3756,3 +3792,140 @@ TEST(DecodeTest, PartialCodestreamBoxTest) {
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;
}
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
} // 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.
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.
JxlDecoderStatus SetOutputBuffer(uint8_t* data, size_t size) {
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
// consume. Potentially stores unparsed data in buffer_.
// 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);
// 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.
// Releases the JpegData from this decoder if set.
Status SetImageBundleJpegData(ImageBundle* ib) {
@ -145,9 +170,6 @@ class JxlToJpegDecoder {
bool IsOutputSet() 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 */) {
return JXL_DEC_ERROR;
}
@ -158,9 +180,31 @@ class JxlToJpegDecoder {
JxlDecoderStatus Process(const uint8_t** next_in, size_t* avail_in) {
return JXL_DEC_ERROR;
}
jpeg::JPEGData* GetJpegData() { return nullptr; }
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 */) {
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,
EntropyEncodingData* codes, size_t* log_alpha_size) {
codes->uint_config.resize(clustered_histograms->size());
if (params.uint_method == HistogramParams::HybridUintMethod::kNone) return;
if (params.uint_method == HistogramParams::HybridUintMethod::kContextMap) {
codes->uint_config.clear();
@ -622,6 +623,9 @@ void ChooseUintConfigs(const HistogramParams& params,
for (size_t i = 0; i < clustered_histograms->size(); i++) {
if (!is_valid[i]) continue;
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]) {
codes->uint_config[i] = cfg;
costs[i] = cost;

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

@ -31,6 +31,7 @@
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/field_encodings.h"
#include "lib/jxl/linalg.h"

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

@ -21,6 +21,7 @@
#include "lib/jxl/base/compiler_specific.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/status.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/base/compiler_specific.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/compressed_dc.h"
#include "lib/jxl/dec_ans.h"

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

@ -23,24 +23,6 @@ namespace jxl {
namespace HWY_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.
template <size_t DCT_ROWS, size_t DCT_COLS, size_t LF_ROWS, size_t LF_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.
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>
@ -435,7 +418,7 @@ void AFVTransformFromPixels(const float* JXL_RESTRICT pixels,
}
}
// 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),
pixels_stride),
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 x = 0; x < 2; x++) {
HWY_ALIGN float block[4 * 4];
ComputeTransposedScaledDCT<4>()(
ComputeScaledDCT<4, 4>()(
DCTFrom(pixels + y * 4 * pixels_stride + x * 4, pixels_stride),
block, scratch_space);
for (size_t iy = 0; iy < 4; iy++) {
@ -574,8 +557,8 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
}
case Type::DCT16X16: {
PROFILER_ZONE("DCT 16");
ComputeTransposedScaledDCT<16>()(DCTFrom(pixels, pixels_stride),
coefficients, scratch_space);
ComputeScaledDCT<16, 16>()(DCTFrom(pixels, pixels_stride), coefficients,
scratch_space);
break;
}
case Type::DCT16X8: {
@ -616,14 +599,14 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
}
case Type::DCT32X32: {
PROFILER_ZONE("DCT 32");
ComputeTransposedScaledDCT<32>()(DCTFrom(pixels, pixels_stride),
coefficients, scratch_space);
ComputeScaledDCT<32, 32>()(DCTFrom(pixels, pixels_stride), coefficients,
scratch_space);
break;
}
case Type::DCT: {
PROFILER_ZONE("DCT 8");
ComputeTransposedScaledDCT<8>()(DCTFrom(pixels, pixels_stride),
coefficients, scratch_space);
ComputeScaledDCT<8, 8>()(DCTFrom(pixels, pixels_stride), coefficients,
scratch_space);
break;
}
case Type::AFV0: {
@ -648,8 +631,8 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
}
case Type::DCT64X64: {
PROFILER_ZONE("DCT 64x64");
ComputeTransposedScaledDCT<64>()(DCTFrom(pixels, pixels_stride),
coefficients, scratch_space);
ComputeScaledDCT<64, 64>()(DCTFrom(pixels, pixels_stride), coefficients,
scratch_space);
break;
}
case Type::DCT64X32: {
@ -666,8 +649,8 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
}
case Type::DCT128X128: {
PROFILER_ZONE("DCT 128x128");
ComputeTransposedScaledDCT<128>()(DCTFrom(pixels, pixels_stride),
coefficients, scratch_space);
ComputeScaledDCT<128, 128>()(DCTFrom(pixels, pixels_stride), coefficients,
scratch_space);
break;
}
case Type::DCT128X64: {
@ -684,8 +667,8 @@ HWY_MAYBE_UNUSED void TransformFromPixels(const AcStrategy::Type strategy,
}
case Type::DCT256X256: {
PROFILER_ZONE("DCT 256x256");
ComputeTransposedScaledDCT<256>()(DCTFrom(pixels, pixels_stride),
coefficients, scratch_space);
ComputeScaledDCT<256, 256>()(DCTFrom(pixels, pixels_stride), coefficients,
scratch_space);
break;
}
case Type::DCT256X128: {

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

@ -48,10 +48,18 @@ JxlEncoderStatus JxlEncoderStruct::RefillOutputByteQueue() {
jxl::BitWriter writer;
if (!wrote_bytes) {
if (use_container) {
if (MustUseContainer()) {
// Add "JXL " and ftyp box.
output_byte_queue.insert(
output_byte_queue.end(), 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) {
jxl::AppendBoxHeader(jxl::MakeBoxType("jbrd"), jpeg_metadata.size(),
false, &output_byte_queue);
@ -100,7 +108,7 @@ JxlEncoderStatus JxlEncoderStruct::RefillOutputByteQueue() {
jxl::PaddedBytes bytes = std::move(writer).TakeBytes();
if (use_container && !wrote_bytes) {
if (MustUseContainer() && !wrote_bytes) {
if (input_closed && input_frame_queue.empty()) {
jxl::AppendBoxHeader(jxl::MakeBoxType("jxlc"), bytes.size(),
/*unbounded=*/false, &output_byte_queue);
@ -251,11 +259,7 @@ JxlEncoderStatus JxlEncoderOptionsSetLossless(JxlEncoderOptions* options,
JxlEncoderStatus JxlEncoderOptionsSetEffort(JxlEncoderOptions* options,
const int effort) {
if (effort < 1 || effort > 9) {
return JXL_ENC_ERROR;
}
options->values.cparams.speed_tier = static_cast<jxl::SpeedTier>(10 - effort);
return JXL_ENC_SUCCESS;
return JxlEncoderOptionsSetInteger(options, JXL_ENC_OPTION_EFFORT, effort);
}
JxlEncoderStatus JxlEncoderOptionsSetDistance(JxlEncoderOptions* options,
@ -264,40 +268,193 @@ JxlEncoderStatus JxlEncoderOptionsSetDistance(JxlEncoderOptions* options,
return JXL_ENC_ERROR;
}
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;
}
JxlEncoderStatus JxlEncoderOptionsSetAsInteger(JxlEncoderOptions* options,
JxlEncoderOptionId option,
int32_t value) {
JxlEncoderStatus JxlEncoderOptionsSetDecodingSpeed(JxlEncoderOptions* options,
int tier) {
return JxlEncoderOptionsSetInteger(options, JXL_ENC_OPTION_DECODING_SPEED,
tier);
}
JxlEncoderStatus JxlEncoderOptionsSetInteger(JxlEncoderOptions* options,
JxlEncoderOptionId option,
int32_t value) {
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:
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;
}
options->values.cparams.resampling = value;
return JXL_ENC_SUCCESS;
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;
}
// The implementation doesn't support the default choice between 1x1 and
// 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;
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:
if (value < -1 || value > 1) return JXL_ENC_ERROR;
options->values.cparams.noise = static_cast<jxl::Override>(value);
return JXL_ENC_SUCCESS;
case JXL_ENC_OPTION_DOTS:
if (value < -1 || value > 1) return JXL_ENC_ERROR;
options->values.cparams.dots = static_cast<jxl::Override>(value);
return JXL_ENC_SUCCESS;
case JXL_ENC_OPTION_PATCHES:
if (value < -1 || value > 1) return JXL_ENC_ERROR;
options->values.cparams.patches = static_cast<jxl::Override>(value);
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:
if (value < -1 || value > 1) return JXL_ENC_ERROR;
options->values.cparams.gaborish = static_cast<jxl::Override>(value);
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:
return JXL_ENC_ERROR;
}
@ -329,6 +486,8 @@ void JxlEncoderReset(JxlEncoder* enc) {
enc->input_closed = false;
enc->basic_info_set = false;
enc->color_encoding_set = false;
enc->use_container = false;
enc->codestream_level = 5;
}
void JxlEncoderDestroy(JxlEncoder* enc) {
@ -341,16 +500,31 @@ void JxlEncoderDestroy(JxlEncoder* enc) {
JxlEncoderStatus JxlEncoderUseContainer(JxlEncoder* enc,
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);
return JXL_ENC_SUCCESS;
}
JxlEncoderStatus JxlEncoderStoreJPEGMetadata(JxlEncoder* enc,
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);
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,
JxlParallelRunner parallel_runner,
void* parallel_runner_opaque) {
@ -507,15 +681,6 @@ JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc, uint8_t** next_out,
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,
JXL_BOOL is_gray) {
ConvertInternalToExternalColorEncoding(jxl::ColorEncoding::SRGB(is_gray),

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

@ -19,6 +19,8 @@
namespace jxl {
// Options per-frame, this is not used for codestream-wide settings or global
// encoder settings.
typedef struct JxlEncoderOptionsValuesStruct {
// lossless is a separate setting from cparams because it is a combination
// 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',
'l', ' ', 0, 0, 0, 0, 'j', 'x', 'l', ' '};
constexpr unsigned char kLevelBoxHeader[] = {0, 0, 0, 0x9, 'j', 'x', 'l', 'l'};
namespace {
template <typename T>
uint8_t* Extend(T* vec, size_t size) {
@ -91,6 +95,11 @@ struct JxlEncoderStruct {
std::vector<uint8_t> output_byte_queue;
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;
jxl::CodecMetadata metadata;
std::vector<uint8_t> jpeg_metadata;
@ -106,6 +115,10 @@ struct JxlEncoderStruct {
// bytes to the output_byte_queue.
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
// 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.

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

@ -56,9 +56,6 @@ TEST(EncodeTest, AddJPEGAfterCloseTest) {
const std::string jpeg_path =
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
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);
@ -234,7 +231,8 @@ TEST(EncodeTest, OptionsTest) {
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
EXPECT_NE(nullptr, enc.get());
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);
EXPECT_EQ(jxl::SpeedTier::kHare, enc->last_used_cparams.speed_tier);
}
@ -244,9 +242,11 @@ TEST(EncodeTest, OptionsTest) {
EXPECT_NE(nullptr, enc.get());
JxlEncoderOptions* options = JxlEncoderOptionsCreate(enc.get(), NULL);
// 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
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);
EXPECT_NE(nullptr, enc.get());
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);
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 {
@ -466,18 +555,68 @@ TEST(EncodeTest, SingleFrameBoundedJXLCTest) {
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)) {
const std::string jpeg_path =
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
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);
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,
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()));
}
// 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)) {
for (int skip_basic_info = 0; skip_basic_info < 2; skip_basic_info++) {
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 "lib/jxl/base/bits.h"
#include "lib/jxl/base/printf_macros.h"
namespace jxl {

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

@ -8,6 +8,7 @@
// Forward/backward-compatible 'bundles' with auto-serialized 'fields'.
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
@ -40,7 +41,8 @@ class BitsCoder {
size_t* JXL_RESTRICT encoded_bits) {
*encoded_bits = 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;
}
@ -53,8 +55,8 @@ class BitsCoder {
static Status Write(const size_t bits, const uint32_t value,
BitWriter* JXL_RESTRICT writer) {
if (value >= (1ULL << bits)) {
return JXL_FAILURE("Value %d too large to encode in %" PRIuS " bits",
value, bits);
return JXL_FAILURE("Value %d too large to encode in %" PRIu64 " bits",
value, static_cast<uint64_t>(bits));
}
writer->Write(bits, value);
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/aux_out.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/fields.h"

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

@ -11,6 +11,7 @@
#include "gtest/gtest.h"
#include "lib/extras/time.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/convolve.h"
#include "lib/jxl/image_ops.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/base/printf_macros.h"
#include "lib/jxl/common.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.
#include <inttypes.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
@ -84,7 +85,7 @@ struct PlaneBase {
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
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
@ -223,6 +224,12 @@ class Rect {
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()`.
Rect Lines(size_t y, size_t num) const {
JXL_DASSERT(y + num <= ysize_);
@ -416,9 +423,10 @@ class Image3 {
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
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",
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
}

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

@ -11,6 +11,7 @@
#include "lib/jxl/alpha.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/profiler.h"
#include "lib/jxl/codec_in_out.h"
#include "lib/jxl/color_management.h"

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

@ -12,6 +12,7 @@
#include <utility>
#include "gtest/gtest.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/image.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) {
// Never had a valid relative value, don't print it.
if (max_relative < 0) {
fprintf(stderr, "c=%" PRIuS ": max +/- %E exceeds +/- %.2E\n", c, max_l1,
threshold_l1);
fprintf(stderr, "c=%" PRIu64 ": max +/- %E exceeds +/- %.2E\n",
static_cast<uint64_t>(c), max_l1, threshold_l1);
} else {
fprintf(stderr,
"c=%" PRIuS ": max +/- %E, x %E exceeds +/- %.2E, x %.2E\n", c,
max_l1, max_relative, threshold_l1, threshold_relative);
"c=%" PRIu64 ": max +/- %E, x %E exceeds +/- %.2E, x %.2E\n",
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.
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);
}
void RandomFillImage(Plane<float>* image) {
JXL_INLINE void RandomFillImage(Plane<float>* image) {
Rng rng(129);
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);
}
void RandomFillImage(Image3F* image) {
JXL_INLINE void RandomFillImage(Image3F* image) {
Rng rng(129);
GenerateImage(rng, image, 0.0f, std::numeric_limits<float>::max());
}

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

@ -12,6 +12,7 @@
#include <string>
#include <vector>
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/common.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/base/printf_macros.h"
#include "lib/jxl/base/status.h"
namespace jxl {
@ -143,15 +144,14 @@ Status JPEGData::VisitFields(Visitor* visitor) {
}
JPEGComponentType component_type =
components.size() == 1 && components[0].id == 1
? JPEGComponentType::kGray
: components.size() == 3 && components[0].id == 1 &&
components[1].id == 2 && components[2].id == 3
? JPEGComponentType::kYCbCr
: components.size() == 3 && components[0].id == 'R' &&
components[1].id == 'G' && components[2].id == 'B'
? JPEGComponentType::kRGB
: JPEGComponentType::kCustom;
components.size() == 1 && components[0].id == 1 ? JPEGComponentType::kGray
: components.size() == 3 && components[0].id == 1 &&
components[1].id == 2 && components[2].id == 3
? JPEGComponentType::kYCbCr
: components.size() == 3 && components[0].id == 'R' &&
components[1].id == 'G' && components[2].id == 'B'
? JPEGComponentType::kRGB
: JPEGComponentType::kCustom;
JXL_RETURN_IF_ERROR(
visitor->Bits(2, JPEGComponentType::kYCbCr,
reinterpret_cast<uint32_t*>(&component_type)));
@ -195,10 +195,15 @@ Status JPEGData::VisitFields(Visitor* visitor) {
}
used_tables |= 1U << components[i].quant_idx;
}
if (used_tables + 1 != 1U << quant.size()) {
return JXL_FAILURE("Not all quant tables are used (%" PRIuS
" tables, %" PRIx64 " used table mask)",
quant.size(), static_cast<uint64_t>(used_tables));
for (size_t i = 0; i < quant.size(); i++) {
if (used_tables & (1 << i)) continue;
if (i == 0) return JXL_FAILURE("First quant table unused.");
// 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();

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/override.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/codec_in_out.h"
#include "lib/jxl/codec_y4m_testonly.h"
@ -30,6 +31,7 @@
#include "lib/jxl/enc_cache.h"
#include "lib/jxl/enc_file.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/fake_parallel_runner_testonly.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_ops.h"
@ -196,7 +198,7 @@ TEST(JxlTest, RoundtripOtherTransforms) {
EXPECT_LE(compressed_size, 23000u);
EXPECT_THAT(ButteraugliDistance(*io, *io2, cparams.ba_params,
/*distmap=*/nullptr, pool),
IsSlightlyBelow(4.0));
IsSlightlyBelow(3.0));
// Check the consistency when performing another roundtrip.
std::unique_ptr<CodecInOut> io3 = jxl::make_unique<CodecInOut>();
@ -205,7 +207,7 @@ TEST(JxlTest, RoundtripOtherTransforms) {
EXPECT_LE(compressed_size2, 23000u);
EXPECT_THAT(ButteraugliDistance(*io, *io3, cparams.ba_params,
/*distmap=*/nullptr, pool),
IsSlightlyBelow(4.0));
IsSlightlyBelow(3.0));
}
TEST(JxlTest, RoundtripResample2) {
@ -222,7 +224,7 @@ TEST(JxlTest, RoundtripResample2) {
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 17000u);
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
/*distmap=*/nullptr, pool),
IsSlightlyBelow(10));
IsSlightlyBelow(8));
}
TEST(JxlTest, RoundtripResample2MT) {
@ -248,6 +250,31 @@ TEST(JxlTest, RoundtripResample2MT) {
#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) {
ThreadPool* pool = nullptr;
const PaddedBytes orig =
@ -262,7 +289,7 @@ TEST(JxlTest, RoundtripResample4) {
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 6000u);
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
/*distmap=*/nullptr, pool),
IsSlightlyBelow(28));
IsSlightlyBelow(22));
}
TEST(JxlTest, RoundtripResample8) {
@ -279,7 +306,7 @@ TEST(JxlTest, RoundtripResample8) {
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 2100u);
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
/*distmap=*/nullptr, pool),
IsSlightlyBelow(80));
IsSlightlyBelow(50));
}
TEST(JxlTest, RoundtripUnalignedD2) {
@ -706,7 +733,7 @@ TEST(JxlTest, RoundtripGrayscale) {
EXPECT_LE(compressed.size(), 1300u);
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params,
/*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
EXPECT_THAT(ButteraugliDistance(io, io3, cparams.ba_params,
/*distmap=*/nullptr, pool),
IsSlightlyBelow(2.8));
IsSlightlyBelow(3.0));
}
TEST(JxlTest, RoundtripDots) {

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

@ -9,6 +9,7 @@
#include <stdlib.h>
#include "lib/jxl/base/os_macros.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/modular/encoding/context_predict.h"
#include "lib/jxl/modular/encoding/dec_ma.h"
@ -22,9 +23,7 @@
namespace jxl {
namespace {
inline const char *PredictorName(Predictor p) {
const char *PredictorName(Predictor p) {
switch (p) {
case Predictor::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");
switch (i) {
case 0:
@ -99,8 +98,6 @@ inline std::string PropertyName(size_t i) {
}
}
} // namespace
void PrintTree(const Tree &tree, const std::string &path) {
FILE *f = fopen((path + ".dot").c_str(), "w");
fprintf(f, "graph{\n");

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

@ -9,6 +9,7 @@
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "lib/jxl/modular/encoding/dec_ma.h"
@ -16,6 +17,9 @@
namespace jxl {
const char *PredictorName(Predictor p);
std::string PropertyName(size_t i);
void PrintTree(const Tree &tree, const std::string &path);
} // namespace jxl

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

@ -14,6 +14,7 @@
#include <unordered_map>
#include <unordered_set>
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/common.h"
#include "lib/jxl/dec_ans.h"

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

@ -10,6 +10,7 @@
#include <queue>
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/modular/encoding/context_predict.h"
#include "lib/jxl/modular/options.h"
@ -446,7 +447,7 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header,
max_tree_size += pixels;
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(DecodeHistograms(br, (tree_storage.size() + 1) / 2,
&code_storage, &context_map_storage));

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

@ -13,7 +13,7 @@ namespace jxl {
void Image::undo_transforms(const weighted::Header &wp_header,
jxl::ThreadPool *pool) {
while (transform.size() > 0) {
while (!transform.empty()) {
Transform t = transform.back();
JXL_DEBUG_V(4, "Undoing transform");
Status result = t.Inverse(*this, wp_header, pool);

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

@ -8,6 +8,7 @@
#include <stdlib.h>
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/common.h"
#include "lib/jxl/modular/modular_image.h"
#include "lib/jxl/modular/transform/transform.h"

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

@ -5,6 +5,7 @@
#include "lib/jxl/modular/transform/transform.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/fields.h"
#include "lib/jxl/modular/modular_image.h"
#include "lib/jxl/modular/transform/palette.h"

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

@ -13,6 +13,7 @@
#include <utility>
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/common.h"
#include "lib/jxl/dct_scales.h"

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

@ -567,6 +567,7 @@ TEST(RoundtripTest, TestICCProfile) {
JxlDecoderDestroy(dec);
}
#if JPEGXL_ENABLE_JPEG // Loading .jpg files requires libjpeg support.
TEST(RoundtripTest, JXL_TRANSCODE_JPEG_TEST(TestJPEGReconstruction)) {
const std::string jpeg_path =
"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());
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_
#define LIB_JXL_SANITIZERS_H_
#include <inttypes.h>
#include <stddef.h>
#include "lib/jxl/base/compiler_specific.h"
@ -99,8 +100,8 @@ template <typename T>
static JXL_INLINE JXL_MAYBE_UNUSED void PrintImageUninitialized(
const Plane<T>& im) {
fprintf(stderr,
"Uninitialized regions for image of size %" PRIuS "x%" PRIuS ":\n",
im.xsize(), im.ysize());
"Uninitialized regions for image of size %" PRIu64 "x%" PRIu64 ":\n",
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).
typedef std::pair<size_t, size_t> PixelSegment;
@ -138,15 +139,18 @@ static JXL_INLINE JXL_MAYBE_UNUSED void PrintImageUninitialized(
return;
}
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 {
fprintf(stderr, " y=[%" PRIdS "]:", start_y_);
fprintf(stderr, " y=[%" PRId64 "]:", static_cast<int64_t>(start_y_));
}
for (const auto& seg : segments_) {
if (seg.first + 1 == seg.second) {
fprintf(stderr, " [%" PRIdS "]", seg.first);
fprintf(stderr, " [%" PRId64 "]", static_cast<int64_t>(seg.first));
} 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");
@ -203,16 +207,20 @@ static JXL_INLINE JXL_MAYBE_UNUSED void CheckImageInitialized(
const auto* row = im.Row(y);
intptr_t ret = __msan_test_shadow(row + r.x0(), sizeof(*row) * r.xsize());
if (ret != -1) {
JXL_DEBUG(1,
"Checking an image of %" PRIuS " x %" PRIuS ", rect x0=%" PRIuS
", y0=%" PRIuS
", "
"xsize=%" PRIuS ", ysize=%" PRIuS,
im.xsize(), im.ysize(), r.x0(), r.y0(), r.xsize(), r.ysize());
JXL_DEBUG(
1,
"Checking an image of %" PRIu64 " x %" PRIu64 ", rect x0=%" PRIu64
", y0=%" PRIu64
", "
"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);
JXL_DEBUG(
1, "CheckImageInitialized failed at x=%" PRIuS ", y=%" PRIuS ": %s",
r.x0() + x, y, message ? message : "");
1, "CheckImageInitialized failed at x=%" PRIu64 ", y=%" PRIu64 ": %s",
static_cast<uint64_t>(r.x0() + x), static_cast<uint64_t>(y),
message ? message : "");
PrintImageUninitialized(im);
}
// 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 "lib/jxl/ans_params.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/chroma_from_luma.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 "gtest/gtest.h"
#include "lib/extras/codec.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/dec_file.h"
#include "lib/jxl/enc_butteraugli_comparator.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
extras/tone_mapping_gbench.cc
jxl/dec_external_image_gbench.cc
jxl/dec_reconstruct_gbench.cc
jxl/enc_external_image_gbench.cc
jxl/gauss_blur_gbench.cc
jxl/splines_gbench.cc
@ -31,7 +32,7 @@ if(benchmark_FOUND)
# Compiles all the benchmark files into a single binary. Individual benchmarks
# 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
-DTEST_DATA_PATH="${PROJECT_SOURCE_DIR}/third_party/testdata")
@ -39,7 +40,6 @@ if(benchmark_FOUND)
jxl_extras-static
jxl-static
benchmark::benchmark
benchmark::benchmark_main
)
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/dec_transforms_testonly.cc
jxl/dec_transforms_testonly.h
jxl/fake_parallel_runner_testonly.h
jxl/image_test_utils.h
jxl/test_utils.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/padded_bytes.cc",
"jxl/base/padded_bytes.h",
"jxl/base/printf_macros.h",
"jxl/base/profiler.h",
"jxl/base/random.cc",
"jxl/base/random.h",
@ -321,6 +322,7 @@ libjxl_enc_sources = [
libjxl_gbench_sources = [
"extras/tone_mapping_gbench.cc",
"jxl/dec_external_image_gbench.cc",
"jxl/dec_reconstruct_gbench.cc",
"jxl/enc_external_image_gbench.cc",
"jxl/gauss_blur_gbench.cc",
"jxl/splines_gbench.cc",
@ -389,6 +391,7 @@ libjxl_testlib_sources = [
"jxl/dct_for_test.h",
"jxl/dec_transforms_testonly.cc",
"jxl/dec_transforms_testonly.h",
"jxl/fake_parallel_runner_testonly.h",
"jxl/image_test_utils.h",
"jxl/test_utils.h",
"jxl/testdata.h",

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

@ -432,7 +432,7 @@ void ThreadSpecific::ComputeOverhead() {
std::sort(samples, samples + kNumSamples);
self_overhead = samples[kNumSamples / 2];
#if PROFILER_PRINT_OVERHEAD
printf("Overhead: %" PRIuS "\n", self_overhead);
printf("Overhead: %" PRIu64 "\n", static_cast<uint64_t>(self_overhead));
#endif
results_->SetSelfOverhead(self_overhead);
}
@ -468,7 +468,8 @@ void ThreadSpecific::ComputeOverhead() {
std::sort(samples, samples + kNumSamples);
const uint64_t child_overhead = samples[9 * kNumSamples / 10];
#if PROFILER_PRINT_OVERHEAD
printf("Child overhead: %" PRIuS "\n", child_overhead);
printf("Child overhead: %" PRIu64 "\n",
static_cast<uint64_t>(child_overhead));
#endif
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.
#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 <hwy/base.h>
#include <hwy/cache_control.h> // LoadFence
#if HWY_COMPILER_MSVC
#include <chrono>
#endif // HWY_COMPILER_MSVC
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
// before and after the region to measure. The functions are distinct because
// they use different fences.
// before and after the region to measure. We provide separate Before/After
// functions because they use different fences.
//
// Background: RDTSC is not 'serializing'; earlier instructions may complete
// 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.
// However, After+After includes an LFENCE in the region measurements, which
// 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
// 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
@ -71,19 +94,18 @@ namespace profiler {
// 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,
// divide by InvariantTicksPerSecond. Although 32-bit ticks are faster to read,
// they overflow too quickly to measure long regions.
static HWY_INLINE HWY_MAYBE_UNUSED uint64_t TicksBefore() {
uint64_t t;
#if HWY_ARCH_PPC
// divide by InvariantTicksPerSecond.
static HWY_INLINE HWY_MAYBE_UNUSED Ticks TicksBefore() {
Ticks t;
#if HWY_ARCH_PPC && defined(__GLIBC__)
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_FENCE;
t = __rdtsc();
hwy::LoadFence();
HWY_FENCE;
#elif HWY_ARCH_X86_64 && (HWY_COMPILER_CLANG || HWY_COMPILER_GCC)
#elif HWY_ARCH_X86_64
asm volatile(
"lfence\n\t"
"rdtsc\n\t"
@ -95,30 +117,35 @@ static HWY_INLINE HWY_MAYBE_UNUSED uint64_t TicksBefore() {
// "memory" avoids reordering. rdx = TSC >> 32.
// "cc" = flags modified by SHL.
: "rdx", "memory", "cc");
#elif HWY_COMPILER_MSVC
// Use std::chrono in MSVC 32-bit.
t = std::chrono::time_point_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now())
.time_since_epoch()
.count();
#else
// Fall back to OS - unsure how to reliably query cntvct_el0 frequency.
#elif HWY_ARCH_RVV
asm volatile("rdcycle %0" : "=r"(t));
#elif defined(_WIN32) || defined(_WIN64)
LARGE_INTEGER counter;
(void)QueryPerformanceCounter(&counter);
t = counter.QuadPart;
#elif defined(__MACH__)
t = mach_absolute_time();
#elif defined(__HAIKU__)
t = system_time_nsecs(); // since boot
#else // POSIX
timespec 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
return t;
}
static HWY_INLINE HWY_MAYBE_UNUSED uint64_t TicksAfter() {
uint64_t t;
#if HWY_ARCH_X86_64 && HWY_COMPILER_MSVC
static HWY_INLINE HWY_MAYBE_UNUSED Ticks TicksAfter() {
Ticks t;
#if HWY_ARCH_PPC && defined(__GLIBC__)
asm volatile("mfspr %0, %1" : "=r"(t) : "i"(268));
#elif HWY_ARCH_X86 && HWY_COMPILER_MSVC
HWY_FENCE;
unsigned aux;
t = __rdtscp(&aux);
hwy::LoadFence();
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).
asm volatile(
"rdtscp\n\t"

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

@ -761,8 +761,10 @@ bool SaveJpegXlImage(const gint32 image_id, const gint32 drawable_id,
JxlEncoderOptions* enc_opts;
enc_opts = JxlEncoderOptionsCreate(enc.get(), nullptr);
JxlEncoderOptionsSetEffort(enc_opts, jxl_save_opts.encoding_effort);
JxlEncoderOptionsSetDecodingSpeed(enc_opts, jxl_save_opts.faster_decoding);
JxlEncoderOptionsSetInteger(enc_opts, JXL_ENC_OPTION_EFFORT,
jxl_save_opts.encoding_effort);
JxlEncoderOptionsSetInteger(enc_opts, JXL_ENC_OPTION_DECODING_SPEED,
jxl_save_opts.faster_decoding);
// lossless mode
if (jxl_save_opts.lossless || jxl_save_opts.distance < 0.01) {