Bug 1749780 - Update libjxl to 9a74bd70b7932750deb78a8aebd6e041ce7f8b01 r=tnikkel

Differential Revision: https://phabricator.services.mozilla.com/D135769
This commit is contained in:
Kagami Sascha Rosylight 2022-01-13 12:49:48 +00:00
Родитель cbb0d57034
Коммит edcdc5567a
217 изменённых файлов: 11445 добавлений и 4790 удалений

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

@ -48,6 +48,7 @@ SOURCES += [
"/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc",
"/third_party/jpeg-xl/lib/jxl/entropy_coder.cc",
"/third_party/jpeg-xl/lib/jxl/epf.cc",
"/third_party/jpeg-xl/lib/jxl/fast_dct.cc",
"/third_party/jpeg-xl/lib/jxl/fields.cc",
"/third_party/jpeg-xl/lib/jxl/filters.cc",
"/third_party/jpeg-xl/lib/jxl/frame_header.cc",
@ -75,6 +76,19 @@ SOURCES += [
"/third_party/jpeg-xl/lib/jxl/passes_state.cc",
"/third_party/jpeg-xl/lib/jxl/quant_weights.cc",
"/third_party/jpeg-xl/lib/jxl/quantizer.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write_to_ib.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc",
"/third_party/jpeg-xl/lib/jxl/splines.cc",
"/third_party/jpeg-xl/lib/jxl/toc.cc",
]
@ -93,6 +107,7 @@ EXPORTS.jxl += [
"./include/jxl/jxl_threads_export.h",
"/third_party/jpeg-xl/lib/include/jxl/butteraugli.h",
"/third_party/jpeg-xl/lib/include/jxl/butteraugli_cxx.h",
"/third_party/jpeg-xl/lib/include/jxl/cms_interface.h",
"/third_party/jpeg-xl/lib/include/jxl/codestream_header.h",
"/third_party/jpeg-xl/lib/include/jxl/color_encoding.h",
"/third_party/jpeg-xl/lib/include/jxl/decode.h",

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

@ -20,12 +20,12 @@ origin:
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
release: commit a9100da7143e4f367d2e26f4d11e457e85343543 (2021-11-25T17:57:37Z).
release: commit 9a74bd70b7932750deb78a8aebd6e041ce7f8b01 (2021-12-28T23:30:10Z).
# Revision to pull in
# Must be a long or short commit SHA (long preferred)
# NOTE(krosylight): Update highway together when updating this!
revision: a9100da7143e4f367d2e26f4d11e457e85343543
revision: 9a74bd70b7932750deb78a8aebd6e041ce7f8b01
# The package's license, where possible using the mnemonic from
# https://spdx.org/licenses/
@ -54,3 +54,5 @@ vendoring:
- tools/

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

@ -27,6 +27,7 @@ Kleis Auke Wolthuizen <github@kleisauke.nl>
Leo Izen <leo.izen@gmail.com>
Lovell Fuller
Marcin Konicki <ahwayakchih@gmail.com>
Mathieu Malaterre <mathieu.malaterre@gmail.com>
Misaki Kasumi <misakikasumi@outlook.com>
Petr Diblík
Pieter Wuille

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

@ -23,6 +23,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
the non-coalesced case.
- decoder API: new function `JxlDecoderGetExtraChannelBlendInfo` to get
the blending information for extra channels in the non-coalesced case.
- encoder API: added ability to set several encoder options to frames using
`JxlEncoderFrameSettingsSetOption`
- encoder API: new functions `JxlEncoderSetFrameHeader` and
`JxlEncoderSetExtraChannelBlendInfo` to set animation
and blending parameters of the frame, and `JxlEncoderInitFrameHeader` and
`JxlEncoderInitBlendInfo` to initialize the structs to set.
- decoder/encoder API: add two fields to `JXLBasicInfo`: `intrinsic_xsize`
and `intrinsic_ysize` to signal the intrinsic size.
- encoder API: ability to encode arbitrary extra channels:
`JxlEncoderInitExtraChannelInfo`, `JxlEncoderSetExtraChannelInfo`,
`JxlEncoderSetExtraChannelName` and `JxlEncoderSetExtraChannelBuffer`.
### Changed
- decoder API: using `JxlDecoderCloseInput` at the end of all input is required
@ -30,6 +41,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
required in those other cases for backwards compatiblity.
- encoder API: `JxlEncoderCloseInput` now closes both frames and boxes input.
### Deprecated
- encoder API: `JxlEncoderOptions`: use `JxlEncoderFrameSettings` instead
- encoder API: `JxlEncoderOptionsCreate`: use `JxlEncoderFrameSettingsCreate`
instead
- encoder API: `JxlEncoderOptionsSetDistance`: use `JxlEncoderSetFrameDistance`
instead
- encoder API: `JxlEncoderOptionsSetLossless`: use `JxlEncoderSetFrameLossless`
instead
- encoder API: `JxlEncoderOptionsSetEffort`: use `JxlEncoderFrameSettingsSetOption(
frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, effort)` instead.
- encoder API: `JxlEncoderOptionsSetDecodingSpeed`: use
`JxlEncoderFrameSettingsSetOption(frame_settings,
JXL_ENC_FRAME_SETTING_DECODING_SPEED, tier)` instead.
- encoder API: deprecated `JXL_ENC_NOT_SUPPORTED`, the encoder returns
`JXL_ENC_ERROR` instead and there is no need to handle
`JXL_ENC_NOT_SUPPORTED`.
## [0.6.1] - 2021-10-29
### Changed
- Security: Fix OOB read in splines rendering (#735 -

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

@ -39,6 +39,7 @@ include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("-fsanitize=fuzzer-no-link" CXX_FUZZERS_SUPPORTED)
check_cxx_compiler_flag("-Xclang -mconstructor-aliases" CXX_CONSTRUCTOR_ALIASES_SUPPORTED)
check_cxx_compiler_flag("-fmacro-prefix-map=OLD=NEW" CXX_MACRO_PREFIX_MAP)
check_cxx_compiler_flag("-fno-rtti" CXX_NO_RTTI_SUPPORTED)
# Enabled PIE binaries by default if supported.
include(CheckPIESupported OPTIONAL RESULT_VARIABLE CHECK_PIE_SUPPORTED)
@ -73,6 +74,12 @@ endif()
set(WARNINGS_AS_ERRORS_DEFAULT false)
if((SANITIZER STREQUAL "msan") OR JPEGXL_EMSCRIPTEN)
set(BUNDLE_LIBPNG_DEFAULT YES)
else()
set(BUNDLE_LIBPNG_DEFAULT NO)
endif()
# Standard cmake naming for building shared libraries.
option(BUILD_SHARED_LIBS "Build shared libraries instead of static ones" ON)
@ -88,6 +95,8 @@ set(JPEGXL_ENABLE_BENCHMARK true CACHE BOOL
"Build JPEGXL benchmark tools.")
set(JPEGXL_ENABLE_EXAMPLES true CACHE BOOL
"Build JPEGXL library usage examples.")
set(JPEGXL_BUNDLE_LIBPNG ${BUNDLE_LIBPNG_DEFAULT} CACHE BOOL
"Build libpng from source and link it statically.")
set(JPEGXL_ENABLE_JNI true CACHE BOOL
"Build JPEGXL JNI Java wrapper, if Java dependencies are installed.")
set(JPEGXL_ENABLE_SJPEG true CACHE BOOL
@ -214,6 +223,10 @@ if ("${CXX_MACRO_PREFIX_MAP}")
add_compile_options(-fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
endif()
if (CXX_NO_RTTI_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif()
if (MSVC)
# TODO(janwas): add flags
else ()
@ -231,12 +244,6 @@ if(MSVC)
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
if("${JPEGXL_ENABLE_FUZZERS}" OR "${JPEGXL_ENABLE_COVERAGE}")
add_definitions(
-DJXL_ENABLE_FUZZERS
)
endif() # JPEGXL_ENABLE_FUZZERS
# In CMake before 3.12 it is problematic to pass repeated flags like -Xclang.
# For this reason we place them in CMAKE_CXX_FLAGS instead.
# See https://gitlab.kitware.com/cmake/cmake/issues/15826

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

@ -600,8 +600,8 @@ cmd_gbench() {
}
cmd_asanfuzz() {
CMAKE_CXX_FLAGS+=" -fsanitize=fuzzer-no-link"
CMAKE_C_FLAGS+=" -fsanitize=fuzzer-no-link"
CMAKE_CXX_FLAGS+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"
CMAKE_C_FLAGS+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"
cmd_asan -DJPEGXL_ENABLE_FUZZERS=ON "$@"
}
@ -615,8 +615,8 @@ cmd_msanfuzz() {
cmd_msan_install
fi
CMAKE_CXX_FLAGS+=" -fsanitize=fuzzer-no-link"
CMAKE_C_FLAGS+=" -fsanitize=fuzzer-no-link"
CMAKE_CXX_FLAGS+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"
CMAKE_C_FLAGS+=" -fsanitize=fuzzer-no-link -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION=1"
cmd_msan -DJPEGXL_ENABLE_FUZZERS=ON "$@"
}
@ -697,7 +697,7 @@ cmd_msan() {
strip_dead_code
cmake_configure "$@" \
-DCMAKE_CROSSCOMPILING=1 -DRUN_HAVE_STD_REGEX=0 -DRUN_HAVE_POSIX_REGEX=0 \
-DJPEGXL_ENABLE_TCMALLOC=OFF
-DJPEGXL_ENABLE_TCMALLOC=OFF -DJPEGXL_WARNINGS_AS_ERRORS=OFF
cmake_build_and_test
}

85
third_party/jpeg-xl/cmake/FindBrotli.cmake поставляемый Normal file
Просмотреть файл

@ -0,0 +1,85 @@
# 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.
set(brlibs brotlicommon brotlienc brotlidec)
find_package(PkgConfig QUIET)
if (PkgConfig_FOUND)
foreach(brlib IN ITEMS ${brlibs})
string(TOUPPER "${brlib}" BRPREFIX)
pkg_check_modules("PC_${BRPREFIX}" lib${brlib})
endforeach()
endif()
find_path(BROTLI_INCLUDE_DIR
NAMES brotli/decode.h
HINTS ${PC_BROTLICOMMON_INCLUDEDIR} ${PC_BROTLICOMMON_INCLUDE_DIRS}
)
foreach(brlib IN ITEMS ${brlibs})
string(TOUPPER "${brlib}" BRPREFIX)
find_library(${BRPREFIX}_LIBRARY
NAMES ${${BRPREFIX}_NAMES} ${brlib}
HINTS ${PC_${BRPREFIX}_LIBDIR} ${PC_${BRPREFIX}_LIBRARY_DIRS}
)
if (${BRPREFIX}_LIBRARY AND NOT TARGET ${brlib})
if(${CMAKE_VERSION} VERSION_LESS "3.13.5")
add_library(${brlib} INTERFACE IMPORTED GLOBAL)
set_property(TARGET ${brlib} PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${BROTLI_INCLUDE_DIR})
target_link_libraries(${brlib} INTERFACE ${${BRPREFIX}_LIBRARY})
set_property(TARGET ${brlib} PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_${BRPREFIX}_CFLAGS_OTHER})
add_library(${brlib}-static INTERFACE IMPORTED GLOBAL)
set_property(TARGET ${brlib}-static PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${BROTLI_INCLUDE_DIR})
target_link_libraries(${brlib}-static INTERFACE ${${BRPREFIX}_LIBRARY})
set_property(TARGET ${brlib}-static PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_${BRPREFIX}_CFLAGS_OTHER})
else()
add_library(${brlib} INTERFACE IMPORTED GLOBAL)
target_include_directories(${brlib}
INTERFACE ${BROTLI_INCLUDE_DIR})
target_link_libraries(${brlib}
INTERFACE ${${BRPREFIX}_LIBRARY})
target_link_options(${brlib}
INTERFACE ${PC_${BRPREFIX}_LDFLAGS_OTHER})
target_compile_options(${brlib}
INTERFACE ${PC_${BRPREFIX}_CFLAGS_OTHER})
# TODO(deymo): Remove the -static library versions, this target is
# currently needed by brunsli.cmake. When importing it this way, the
# brotli*-static target is just an alias.
add_library(${brlib}-static ALIAS ${brlib})
endif()
endif()
endforeach()
if (BROTLICOMMON_FOUND AND BROTLIENC_FOUND AND BROTLIDEC_FOUND)
set(Brotli_FOUND ON)
else ()
set(Brotli_FOUND OFF)
endif()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(Brotli
FOUND_VAR Brotli_FOUND
REQUIRED_VARS
BROTLI_INCLUDE_DIR
BROTLICOMMON_LIBRARY
BROTLIENC_LIBRARY
BROTLIDEC_LIBRARY
VERSION_VAR Brotli_VERSION
)
mark_as_advanced(
BROTLI_INCLUDE_DIR
BROTLICOMMON_LIBRARY
BROTLIENC_LIBRARY
BROTLIDEC_LIBRARY
)
if (Brotli_FOUND)
set(Brotli_LIBRARIES ${BROTLICOMMON_LIBRARY} ${BROTLIENC_LIBRARY} ${BROTLIDEC_LIBRARY})
set(Brotli_INCLUDE_DIRS ${BROTLI_INCLUDE_DIR})
endif()

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

@ -9,28 +9,6 @@ Files: third_party/sjpeg/*
Copyright: 2017 Google, Inc
License: Apache-2.0
Files: third_party/lodepng/*
Copyright: 2005-2018 Lode Vandevenne
License: Zlib License
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
.
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
.
3. This notice may not be removed or altered from any source
distribution.
Files: third_party/skcms/*
Copyright: 2018 Google Inc.
License: BSD-3-clause

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

@ -14,9 +14,10 @@ MYDIR=$(dirname $(realpath "$0"))
# Git revisions we use for the given submodules. Update these whenever you
# update a git submodule.
THIRD_PARTY_HIGHWAY="e69083a12a05caf037cabecdf1b248b7579705a5"
THIRD_PARTY_LODEPNG="8c6a9e30576f07bf470ad6f09458a2dcd7a6a84a"
THIRD_PARTY_SKCMS="64374756e03700d649f897dbd98c95e78c30c7da"
THIRD_PARTY_SJPEG="868ab558fad70fcbe8863ba4e85179eeb81cc840"
THIRD_PARTY_ZLIB="cacf7f1d4e3d44d871b605da3b647f07d718623f"
THIRD_PARTY_LIBPNG="a40189cf881e9f0db80511c382292a5604c3c3d1"
# Download the target revision from GitHub.
download_github() {
@ -70,10 +71,11 @@ EOF
# Sources downloaded from a tarball.
download_github third_party/highway google/highway
download_github third_party/lodepng lvandeve/lodepng
download_github third_party/sjpeg webmproject/sjpeg
download_github third_party/skcms \
"https://skia.googlesource.com/skcms/+archive/"
download_github third_party/zlib madler/zlib
download_github third_party/libpng glennrp/libpng
echo "Done."
}

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

@ -183,13 +183,17 @@ bool EncodeJxlOneshot(const std::vector<float>& pixels, const uint32_t xsize,
return false;
}
JxlEncoderFrameSettings* frame_settings =
JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
if (JXL_ENC_SUCCESS !=
JxlEncoderAddImageFrame(JxlEncoderOptionsCreate(enc.get(), nullptr),
&pixel_format, (void*)pixels.data(),
JxlEncoderAddImageFrame(frame_settings, &pixel_format,
(void*)pixels.data(),
sizeof(float) * pixels.size())) {
fprintf(stderr, "JxlEncoderAddImageFrame failed\n");
return false;
}
JxlEncoderCloseInput(enc.get());
compressed->resize(64);
uint8_t* next_out = compressed->data();

6
third_party/jpeg-xl/examples/jxlinfo.c поставляемый
Просмотреть файл

@ -106,6 +106,8 @@ int PrintBasicInfo(FILE* file) {
printf("num_loops: %u\n", info.animation.num_loops);
printf("have_timecodes: %d\n", info.animation.have_timecodes);
}
printf("intrinsic xsize: %u\n", info.intrinsic_xsize);
printf("intrinsic ysize: %u\n", info.intrinsic_ysize);
const char* const orientation_string[8] = {
"Normal", "Flipped horizontally",
"Upside down", "Flipped vertically",
@ -153,8 +155,8 @@ int PrintBasicInfo(FILE* file) {
free(name);
break;
}
free(name);
printf(" name: %s\n", name);
free(name);
}
if (extra.type == JXL_CHANNEL_ALPHA)
printf(" alpha_premultiplied: %d (%s)\n", extra.alpha_premultiplied,
@ -268,8 +270,8 @@ int PrintBasicInfo(FILE* file) {
free(name);
break;
}
free(name);
printf(" name: %s\n", name);
free(name);
}
float ms = frame_header.duration * 1000.f *
info.animation.tps_denominator / info.animation.tps_numerator;

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

@ -5,22 +5,22 @@
#include "lib/extras/codec.h"
#include "lib/jxl/base/file_io.h"
#if JPEGXL_ENABLE_APNG
#include "lib/extras/codec_apng.h"
#include "lib/extras/enc/apng.h"
#endif
#if JPEGXL_ENABLE_JPEG
#include "lib/extras/enc/jpg.h"
#endif
#if JPEGXL_ENABLE_EXR
#include "lib/extras/codec_exr.h"
#include "lib/extras/enc/exr.h"
#endif
#if JPEGXL_ENABLE_GIF
#include "lib/extras/codec_gif.h"
#endif
#include "lib/extras/codec_jpg.h"
#include "lib/extras/codec_pgx.h"
#include "lib/extras/codec_png.h"
#include "lib/extras/codec_pnm.h"
#include "lib/extras/codec_psd.h"
#include "lib/extras/dec/decode.h"
#include "lib/extras/enc/pgx.h"
#include "lib/extras/enc/pnm.h"
#include "lib/extras/packed_image_convert.h"
#include "lib/jxl/base/file_io.h"
#include "lib/jxl/image_bundle.h"
namespace jxl {
@ -31,127 +31,26 @@ constexpr size_t kMinBytes = 9;
} // namespace
std::string ExtensionFromCodec(Codec codec, const bool is_gray,
const size_t bits_per_sample) {
switch (codec) {
case Codec::kJPG:
return ".jpg";
case Codec::kPGX:
return ".pgx";
case Codec::kPNG:
return ".png";
case Codec::kPNM:
if (is_gray) return ".pgm";
return (bits_per_sample == 32) ? ".pfm" : ".ppm";
case Codec::kGIF:
return ".gif";
case Codec::kEXR:
return ".exr";
case Codec::kPSD:
return ".psd";
case Codec::kUnknown:
return std::string();
}
JXL_UNREACHABLE;
return std::string();
}
Codec CodecFromExtension(const std::string& extension,
size_t* JXL_RESTRICT bits_per_sample) {
if (extension == ".png") return Codec::kPNG;
if (extension == ".jpg") return Codec::kJPG;
if (extension == ".jpeg") return Codec::kJPG;
if (extension == ".pgx") return Codec::kPGX;
if (extension == ".pbm") {
*bits_per_sample = 1;
return Codec::kPNM;
}
if (extension == ".pgm") return Codec::kPNM;
if (extension == ".ppm") return Codec::kPNM;
if (extension == ".pfm") {
*bits_per_sample = 32;
return Codec::kPNM;
}
if (extension == ".gif") return Codec::kGIF;
if (extension == ".exr") return Codec::kEXR;
if (extension == ".psd") return Codec::kPSD;
return Codec::kUnknown;
}
Status SetFromBytes(const Span<const uint8_t> bytes,
const ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool, Codec* orig_codec) {
const extras::ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool, extras::Codec* orig_codec) {
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
extras::PackedPixelFile ppf;
// Default values when not set by decoders.
ppf.info.uses_original_profile = true;
ppf.info.orientation = JXL_ORIENT_IDENTITY;
Codec codec;
bool skip_ppf_conversion = false;
if (extras::DecodeImagePNG(bytes, color_hints, io->constraints, &ppf)) {
codec = Codec::kPNG;
}
#if JPEGXL_ENABLE_APNG
else if (extras::DecodeImageAPNG(bytes, color_hints, io->constraints, &ppf)) {
codec = Codec::kPNG;
}
#endif
else if (extras::DecodeImagePGX(bytes, color_hints, io->constraints, &ppf)) {
codec = Codec::kPGX;
} else if (extras::DecodeImagePNM(bytes, color_hints, io->constraints,
&ppf)) {
codec = Codec::kPNM;
}
#if JPEGXL_ENABLE_GIF
else if (extras::DecodeImageGIF(bytes, color_hints, io->constraints, &ppf)) {
codec = Codec::kGIF;
}
#endif
else if (io->dec_target == DecodeTarget::kQuantizedCoeffs &&
extras::DecodeImageJPGCoefficients(bytes, io)) {
// TODO(deymo): In this case the tools should use a different API to
// transcode the input JPEG to JXL instead of expressing it as a
// PackedPixelFile.
codec = Codec::kJPG;
skip_ppf_conversion = true;
} else if (io->dec_target == DecodeTarget::kPixels &&
extras::DecodeImageJPG(bytes, color_hints, io->constraints,
&ppf)) {
codec = Codec::kJPG;
if (extras::DecodeBytes(bytes, color_hints, io->constraints, &ppf, pool,
orig_codec)) {
return ConvertPackedPixelFileToCodecInOut(ppf, pool, io);
} else if (extras::DecodeImagePSD(bytes, color_hints, pool, io)) {
// TODO(deymo): Migrate PSD codec too.
codec = Codec::kPSD;
skip_ppf_conversion = true;
if (orig_codec) *orig_codec = extras::Codec::kPSD;
return true;
}
#if JPEGXL_ENABLE_EXR
else if (extras::DecodeImageEXR(bytes, color_hints, io->constraints,
io->target_nits, pool, &ppf)) {
codec = Codec::kEXR;
}
#endif
else {
return JXL_FAILURE("Codecs failed to decode");
}
if (orig_codec) *orig_codec = codec;
if (!skip_ppf_conversion) {
JXL_RETURN_IF_ERROR(ConvertPackedPixelFileToCodecInOut(ppf, pool, io));
}
return true;
return JXL_FAILURE("Codecs failed to decode");
}
Status SetFromFile(const std::string& pathname, const ColorHints& color_hints,
CodecInOut* io, ThreadPool* pool, Codec* orig_codec) {
Status SetFromFile(const std::string& pathname,
const extras::ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool, extras::Codec* orig_codec) {
PaddedBytes encoded;
JXL_RETURN_IF_ERROR(ReadFile(pathname, &encoded));
JXL_RETURN_IF_ERROR(SetFromBytes(Span<const uint8_t>(encoded), color_hints,
@ -159,54 +58,52 @@ Status SetFromFile(const std::string& pathname, const ColorHints& color_hints,
return true;
}
Status Encode(const CodecInOut& io, const Codec codec,
Status Encode(const CodecInOut& io, const extras::Codec codec,
const ColorEncoding& c_desired, size_t bits_per_sample,
PaddedBytes* bytes, ThreadPool* pool) {
JXL_CHECK(!io.Main().c_current().ICC().empty());
JXL_CHECK(!c_desired.ICC().empty());
io.CheckMetadata();
if (io.Main().IsJPEG() && codec != Codec::kJPG) {
return JXL_FAILURE(
"Output format has to be JPEG for losslessly recompressed JPEG "
"reconstruction");
if (io.Main().IsJPEG()) {
JXL_WARNING("Writing JPEG data as pixels");
}
switch (codec) {
case Codec::kPNG:
return extras::EncodeImagePNG(&io, c_desired, bits_per_sample, pool,
bytes);
case Codec::kJPG:
if (io.Main().IsJPEG()) {
return extras::EncodeImageJPGCoefficients(&io, bytes);
} else {
#if JPEGXL_ENABLE_JPEG
return EncodeImageJPG(&io,
io.use_sjpeg ? extras::JpegEncoder::kSJpeg
: extras::JpegEncoder::kLibJpeg,
io.jpeg_quality, YCbCrChromaSubsampling(), pool,
bytes);
case extras::Codec::kPNG:
#if JPEGXL_ENABLE_APNG
return extras::EncodeImageAPNG(&io, c_desired, bits_per_sample, pool,
bytes);
#else
return JXL_FAILURE("JPEG XL was built without JPEG support");
return JXL_FAILURE("JPEG XL was built without (A)PNG support");
#endif
}
case Codec::kPNM:
case extras::Codec::kJPG:
#if JPEGXL_ENABLE_JPEG
return EncodeImageJPG(&io,
io.use_sjpeg ? extras::JpegEncoder::kSJpeg
: extras::JpegEncoder::kLibJpeg,
io.jpeg_quality, YCbCrChromaSubsampling(), pool,
bytes);
#else
return JXL_FAILURE("JPEG XL was built without JPEG support");
#endif
case extras::Codec::kPNM:
return extras::EncodeImagePNM(&io, c_desired, bits_per_sample, pool,
bytes);
case Codec::kPGX:
case extras::Codec::kPGX:
return extras::EncodeImagePGX(&io, c_desired, bits_per_sample, pool,
bytes);
case Codec::kGIF:
case extras::Codec::kGIF:
return JXL_FAILURE("Encoding to GIF is not implemented");
case Codec::kPSD:
case extras::Codec::kPSD:
return extras::EncodeImagePSD(&io, c_desired, bits_per_sample, pool,
bytes);
case Codec::kEXR:
case extras::Codec::kEXR:
#if JPEGXL_ENABLE_EXR
return extras::EncodeImageEXR(&io, c_desired, pool, bytes);
#else
return JXL_FAILURE("JPEG XL was built without OpenEXR support");
#endif
case Codec::kUnknown:
case extras::Codec::kUnknown:
return JXL_FAILURE("Cannot encode using Codec::kUnknown");
}
@ -217,11 +114,12 @@ Status EncodeToFile(const CodecInOut& io, const ColorEncoding& c_desired,
size_t bits_per_sample, const std::string& pathname,
ThreadPool* pool) {
const std::string extension = Extension(pathname);
const Codec codec = CodecFromExtension(extension, &bits_per_sample);
const extras::Codec codec =
extras::CodecFromExtension(extension, &bits_per_sample);
// Warn about incorrect usage of PBM/PGM/PGX/PPM - only the latter supports
// color, but CodecFromExtension lumps them all together.
if (codec == Codec::kPNM && extension != ".pfm") {
if (codec == extras::Codec::kPNM && extension != ".pfm") {
if (!io.Main().IsGray() && extension != ".ppm") {
JXL_WARNING("For color images, the filename should end with .ppm.\n");
} else if (io.Main().IsGray() && extension == ".ppm") {
@ -232,10 +130,10 @@ Status EncodeToFile(const CodecInOut& io, const ColorEncoding& c_desired,
JXL_WARNING("PPM only supports up to 16 bits per sample");
bits_per_sample = 16;
}
} else if (codec == Codec::kPGX && !io.Main().IsGray()) {
} else if (codec == extras::Codec::kPGX && !io.Main().IsGray()) {
JXL_WARNING("Storing color image to PGX - use .ppm extension instead.\n");
}
if (bits_per_sample > 16 && codec == Codec::kPNG) {
if (bits_per_sample > 16 && codec == extras::Codec::kPNG) {
JXL_WARNING("PNG only supports up to 16 bits per sample");
bits_per_sample = 16;
}

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

@ -13,7 +13,8 @@
#include <string>
#include "lib/extras/color_hints.h"
#include "lib/extras/dec/color_hints.h"
#include "lib/extras/dec/decode.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
@ -25,61 +26,30 @@
namespace jxl {
// Codecs supported by CodecInOut::Encode.
enum class Codec : uint32_t {
kUnknown, // for CodecFromExtension
kPNG,
kPNM,
kPGX,
kJPG,
kGIF,
kEXR,
kPSD
};
static inline constexpr uint64_t EnumBits(Codec /*unused*/) {
// Return only fully-supported codecs (kGIF is decode-only).
return MakeBit(Codec::kPNM) | MakeBit(Codec::kPNG)
#if JPEGXL_ENABLE_JPEG
| MakeBit(Codec::kJPG)
#endif
#if JPEGXL_ENABLE_EXR
| MakeBit(Codec::kEXR)
#endif
| MakeBit(Codec::kPSD);
}
// Lower case ASCII including dot, e.g. ".png".
std::string ExtensionFromCodec(Codec codec, bool is_gray,
size_t bits_per_sample);
// If and only if extension is ".pfm", *bits_per_sample is updated to 32 so
// that Encode() would encode to PFM instead of PPM.
Codec CodecFromExtension(const std::string& extension,
size_t* JXL_RESTRICT bits_per_sample);
// Decodes "bytes" and sets io->metadata.m.
// color_space_hint may specify the color space, otherwise, defaults to sRGB.
Status SetFromBytes(const Span<const uint8_t> bytes,
const ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool, Codec* orig_codec);
Status SetFromBytes(Span<const uint8_t> bytes,
const extras::ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool = nullptr,
extras::Codec* orig_codec = nullptr);
// Helper function to use no color_space_hint.
JXL_INLINE Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io,
ThreadPool* pool = nullptr,
Codec* orig_codec = nullptr) {
return SetFromBytes(bytes, ColorHints(), io, pool, orig_codec);
extras::Codec* orig_codec = nullptr) {
return SetFromBytes(bytes, extras::ColorHints(), io, pool, orig_codec);
}
// Reads from file and calls SetFromBytes.
Status SetFromFile(const std::string& pathname, const ColorHints& color_hints,
CodecInOut* io, ThreadPool* pool = nullptr,
Codec* orig_codec = nullptr);
Status SetFromFile(const std::string& pathname,
const extras::ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool = nullptr,
extras::Codec* orig_codec = nullptr);
// Replaces "bytes" with an encoding of pixels transformed from c_current
// color space to c_desired.
Status Encode(const CodecInOut& io, Codec codec, const ColorEncoding& c_desired,
size_t bits_per_sample, PaddedBytes* bytes,
ThreadPool* pool = nullptr);
Status Encode(const CodecInOut& io, extras::Codec codec,
const ColorEncoding& c_desired, size_t bits_per_sample,
PaddedBytes* bytes, ThreadPool* pool = nullptr);
// Deduces codec, calls Encode and writes to file.
Status EncodeToFile(const CodecInOut& io, const ColorEncoding& c_desired,

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

@ -1,412 +0,0 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/codec_apng.h"
// Parts of this code are taken from apngdis, which has the following license:
/* APNG Disassembler 2.8
*
* Deconstructs APNG files into individual frames.
*
* http://apngdis.sourceforge.net
*
* Copyright (c) 2010-2015 Max Stepin
* maxst at users.sourceforge.net
*
* zlib license
* ------------
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
*/
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "jxl/encode.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/headers.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/luminance.h"
#include "png.h" /* original (unpatched) libpng is ok */
namespace jxl {
namespace extras {
namespace {
constexpr bool isAbc(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
#define notabc(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
constexpr uint32_t kId_IHDR = 0x52444849;
constexpr uint32_t kId_acTL = 0x4C546361;
constexpr uint32_t kId_fcTL = 0x4C546366;
constexpr uint32_t kId_IDAT = 0x54414449;
constexpr uint32_t kId_fdAT = 0x54416466;
constexpr uint32_t kId_IEND = 0x444E4549;
struct CHUNK {
unsigned char* p;
unsigned int size;
};
struct APNGFrame {
unsigned char *p, **rows;
unsigned int w, h, delay_num, delay_den;
};
struct Reader {
const uint8_t* next;
const uint8_t* last;
bool Read(void* data, size_t len) {
size_t cap = last - next;
size_t to_copy = std::min(cap, len);
memcpy(data, next, to_copy);
next += to_copy;
return (len == to_copy);
}
bool Eof() { return next == last; }
};
const unsigned long cMaxPNGSize = 1000000UL;
const size_t kMaxPNGChunkSize = 100000000; // 100 MB
void info_fn(png_structp png_ptr, png_infop info_ptr) {
png_set_expand(png_ptr);
png_set_strip_16(png_ptr);
png_set_gray_to_rgb(png_ptr);
png_set_palette_to_rgb(png_ptr);
png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);
(void)png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);
}
void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num,
int pass) {
APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr);
png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row);
}
inline unsigned int read_chunk(Reader* r, CHUNK* pChunk) {
unsigned char len[4];
pChunk->size = 0;
pChunk->p = 0;
if (r->Read(&len, 4)) {
const auto size = png_get_uint_32(len);
// Check first, to avoid overflow.
if (size > kMaxPNGChunkSize) {
JXL_WARNING("APNG chunk size is too big");
return 0;
}
pChunk->size = size + 12;
pChunk->p = new unsigned char[pChunk->size];
memcpy(pChunk->p, len, 4);
if (r->Read(pChunk->p + 4, pChunk->size - 4)) {
return *(unsigned int*)(pChunk->p + 4);
}
}
return 0;
}
int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
bool hasInfo, CHUNK& chunkIHDR,
std::vector<CHUNK>& chunksInfo) {
unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10};
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info_ptr = png_create_info_struct(png_ptr);
if (!png_ptr || !info_ptr) return 1;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 1;
}
png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, NULL);
png_process_data(png_ptr, info_ptr, header, 8);
png_process_data(png_ptr, info_ptr, chunkIHDR.p, chunkIHDR.size);
if (hasInfo) {
for (unsigned int i = 0; i < chunksInfo.size(); i++) {
png_process_data(png_ptr, info_ptr, chunksInfo[i].p, chunksInfo[i].size);
}
}
return 0;
}
int processing_data(png_structp png_ptr, png_infop info_ptr, unsigned char* p,
unsigned int size) {
if (!png_ptr || !info_ptr) return 1;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 1;
}
png_process_data(png_ptr, info_ptr, p, size);
return 0;
}
int processing_finish(png_structp png_ptr, png_infop info_ptr) {
unsigned char footer[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130};
if (!png_ptr || !info_ptr) return 1;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 1;
}
png_process_data(png_ptr, info_ptr, footer, 12);
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 0;
}
} // namespace
Status DecodeImageAPNG(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
Reader r;
unsigned int id, i, j, w, h, w0, h0, x0, y0;
unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize;
unsigned char sig[8];
png_structp png_ptr;
png_infop info_ptr;
CHUNK chunk;
CHUNK chunkIHDR;
std::vector<CHUNK> chunksInfo;
bool isAnimated = false;
bool skipFirst = false;
bool hasInfo = false;
bool all_dispose_bg = true;
APNGFrame frameRaw = {};
r = {bytes.data(), bytes.data() + bytes.size()};
// Not an aPNG => not an error
unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
if (!r.Read(sig, 8) || memcmp(sig, png_signature, 8) != 0) {
return false;
}
id = read_chunk(&r, &chunkIHDR);
// todo: get data from png metadata
JxlColorEncodingSetToSRGB(&ppf->color_encoding, /*is_gray=*/false);
JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/true,
/*is_gray=*/false, ppf));
// Only 8-bit supported.
ppf->info.bits_per_sample = 8;
ppf->info.exponent_bits_per_sample = 0;
ppf->info.alpha_bits = 8;
ppf->info.alpha_exponent_bits = 0;
ppf->info.num_color_channels = 3; // RGBA
ppf->info.orientation = JXL_ORIENT_IDENTITY;
const JxlPixelFormat format{
/*num_channels=*/4,
/*data_type=*/JXL_TYPE_UINT8,
/*endianness=*/JXL_BIG_ENDIAN,
/*align=*/0,
};
ppf->frames.clear();
bool errorstate = true;
if (id == kId_IHDR && chunkIHDR.size == 25) {
w0 = w = png_get_uint_32(chunkIHDR.p + 8);
h0 = h = png_get_uint_32(chunkIHDR.p + 12);
if (w > cMaxPNGSize || h > cMaxPNGSize) {
return false;
}
ppf->info.xsize = w;
ppf->info.ysize = h;
JXL_RETURN_IF_ERROR(VerifyDimensions(&constraints, w, h));
x0 = 0;
y0 = 0;
delay_num = 1;
delay_den = 10;
dop = 0;
bop = 0;
rowbytes = w * 4;
imagesize = h * rowbytes;
frameRaw.p = new unsigned char[imagesize];
frameRaw.rows = new png_bytep[h * sizeof(png_bytep)];
for (j = 0; j < h; j++) frameRaw.rows[j] = frameRaw.p + j * rowbytes;
if (!processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
chunkIHDR, chunksInfo)) {
bool last_base_was_none = true;
while (!r.Eof()) {
id = read_chunk(&r, &chunk);
if (!id) break;
JXL_ASSERT(chunk.p != nullptr);
if (id == kId_acTL && !hasInfo && !isAnimated) {
isAnimated = true;
skipFirst = true;
ppf->info.have_animation = true;
ppf->info.animation.tps_numerator = 1000;
ppf->info.animation.tps_denominator = 1;
} else if (id == kId_IEND ||
(id == kId_fcTL && (!hasInfo || isAnimated))) {
if (hasInfo) {
if (!processing_finish(png_ptr, info_ptr)) {
// Allocates the frame buffer.
ppf->frames.emplace_back(w0, h0, format);
auto* frame = &ppf->frames.back();
frame->frame_info.duration = delay_num * 1000 / delay_den;
frame->x0 = x0;
frame->y0 = y0;
// TODO(veluca): this could in principle be implemented.
if (last_base_was_none && !all_dispose_bg &&
(x0 != 0 || y0 != 0 || w0 != w || h0 != h || bop != 0)) {
delete[] frameRaw.rows;
delete[] frameRaw.p;
return JXL_FAILURE(
"APNG with dispose-to-0 is not supported for non-full or "
"blended frames");
}
switch (dop) {
case 0:
frame->use_for_next_frame = true;
last_base_was_none = false;
all_dispose_bg = false;
break;
case 2:
frame->use_for_next_frame = false;
all_dispose_bg = false;
break;
default:
frame->use_for_next_frame = false;
last_base_was_none = true;
}
frame->blend = bop != 0;
for (size_t y = 0; y < h0; ++y) {
memcpy(static_cast<uint8_t*>(frame->color.pixels()) +
frame->color.stride * y,
frameRaw.rows[y], 4 * w0);
}
} else {
delete[] chunk.p;
break;
}
}
if (id == kId_IEND) {
errorstate = false;
break;
}
// At this point the old frame is done. Let's start a new one.
w0 = png_get_uint_32(chunk.p + 12);
h0 = png_get_uint_32(chunk.p + 16);
x0 = png_get_uint_32(chunk.p + 20);
y0 = png_get_uint_32(chunk.p + 24);
delay_num = png_get_uint_16(chunk.p + 28);
delay_den = png_get_uint_16(chunk.p + 30);
dop = chunk.p[32];
bop = chunk.p[33];
if (!delay_den) delay_den = 100;
if (w0 > cMaxPNGSize || h0 > cMaxPNGSize || x0 > cMaxPNGSize ||
y0 > cMaxPNGSize || x0 + w0 > w || y0 + h0 > h || dop > 2 ||
bop > 1) {
delete[] chunk.p;
break;
}
if (hasInfo) {
memcpy(chunkIHDR.p + 8, chunk.p + 12, 8);
if (processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
chunkIHDR, chunksInfo)) {
delete[] chunk.p;
break;
}
} else
skipFirst = false;
if (ppf->frames.size() == (skipFirst ? 1 : 0)) {
bop = 0;
if (dop == 2) dop = 1;
}
} else if (id == kId_IDAT) {
hasInfo = true;
if (processing_data(png_ptr, info_ptr, chunk.p, chunk.size)) {
delete[] chunk.p;
break;
}
} else if (id == kId_fdAT && isAnimated) {
png_save_uint_32(chunk.p + 4, chunk.size - 16);
memcpy(chunk.p + 8, "IDAT", 4);
if (processing_data(png_ptr, info_ptr, chunk.p + 4, chunk.size - 4)) {
delete[] chunk.p;
break;
}
} else if (!isAbc(chunk.p[4]) || !isAbc(chunk.p[5]) ||
!isAbc(chunk.p[6]) || !isAbc(chunk.p[7])) {
delete[] chunk.p;
break;
} else if (!hasInfo) {
if (processing_data(png_ptr, info_ptr, chunk.p, chunk.size)) {
delete[] chunk.p;
break;
}
chunksInfo.push_back(chunk);
continue;
}
delete[] chunk.p;
}
}
delete[] frameRaw.rows;
delete[] frameRaw.p;
}
for (i = 0; i < chunksInfo.size(); i++) delete[] chunksInfo[i].p;
chunksInfo.clear();
delete[] chunkIHDR.p;
if (errorstate) return false;
return true;
}
} // namespace extras
} // namespace jxl

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

@ -1,59 +0,0 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_EXTRAS_CODEC_JPG_H_
#define LIB_EXTRAS_CODEC_JPG_H_
// Encodes JPG pixels and metadata in memory.
#include <stdint.h>
#include "lib/extras/codec.h"
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
namespace extras {
enum class JpegEncoder {
kLibJpeg,
kSJpeg,
};
static inline bool IsJPG(const Span<const uint8_t> bytes) {
if (bytes.size() < 2) return false;
if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false;
return true;
}
// Decodes `bytes` into `io`. color_hints are ignored.
// `elapsed_deinterleave`, if non-null, will be set to the time (in seconds)
// that it took to deinterleave the raw JSAMPLEs to planar floats.
Status DecodeImageJPG(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
// Encodes into `bytes`.
Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
YCbCrChromaSubsampling chroma_subsampling,
ThreadPool* pool, PaddedBytes* bytes);
// Temporary wrappers to load the JPEG coefficients to a CodecInOut. This should
// be replaced by calling the corresponding JPEG input and output functions on
// the API.
// Decodes the JPEG image coefficients to a CodecIO for lossless recompression.
Status DecodeImageJPGCoefficients(Span<const uint8_t> bytes, CodecInOut* io);
// Reconstructs the JPEG from the coefficients and metadata in CodecInOut.
Status EncodeImageJPGCoefficients(const CodecInOut* io, PaddedBytes* bytes);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_JPG_H_

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

@ -1,852 +0,0 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/codec_png.h"
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Lodepng library:
#include <lodepng.h>
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#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"
#include "lib/jxl/enc_external_image.h"
#include "lib/jxl/enc_image_bundle.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/luminance.h"
namespace jxl {
namespace extras {
namespace {
#define JXL_PNG_VERBOSE 0
// Retrieves XMP and EXIF/IPTC from itext and text.
class BlobsReaderPNG {
public:
static Status Decode(const LodePNGInfo& info, PackedMetadata* metadata) {
for (unsigned idx_itext = 0; idx_itext < info.itext_num; ++idx_itext) {
// We trust these are properly null-terminated by LodePNG.
const char* key = info.itext_keys[idx_itext];
const char* value = info.itext_strings[idx_itext];
if (strstr(key, "XML:com.adobe.xmp")) {
metadata->xmp.resize(strlen(value)); // safe, see above
memcpy(metadata->xmp.data(), value, metadata->xmp.size());
}
}
for (unsigned idx_text = 0; idx_text < info.text_num; ++idx_text) {
// We trust these are properly null-terminated by LodePNG.
const char* key = info.text_keys[idx_text];
const char* value = info.text_strings[idx_text];
std::string type;
std::vector<uint8_t> bytes;
// Handle text chunks annotated with key "Raw profile type ####", with
// #### a type, which may contain metadata.
const char* kKey = "Raw profile type ";
if (strncmp(key, kKey, strlen(kKey)) != 0) continue;
if (!MaybeDecodeBase16(key, value, &type, &bytes)) {
JXL_WARNING("Couldn't parse 'Raw format type' text chunk");
continue;
}
if (type == "exif") {
if (!metadata->exif.empty()) {
JXL_WARNING("overwriting EXIF (%" PRIuS " bytes) with base16 (%" PRIuS
" bytes)",
metadata->exif.size(), bytes.size());
}
metadata->exif = std::move(bytes);
} else if (type == "iptc") {
// TODO (jon): Deal with IPTC in some way
} else if (type == "8bim") {
// TODO (jon): Deal with 8bim in some way
} else if (type == "xmp") {
if (!metadata->xmp.empty()) {
JXL_WARNING("overwriting XMP (%" PRIuS " bytes) with base16 (%" PRIuS
" bytes)",
metadata->xmp.size(), bytes.size());
}
metadata->xmp = std::move(bytes);
} else {
JXL_WARNING("Unknown type in 'Raw format type' text chunk: %s: %" PRIuS
" bytes",
type.c_str(), bytes.size());
}
}
return true;
}
private:
// Returns false if invalid.
static JXL_INLINE Status DecodeNibble(const char c,
uint32_t* JXL_RESTRICT nibble) {
if ('a' <= c && c <= 'f') {
*nibble = 10 + c - 'a';
} else if ('0' <= c && c <= '9') {
*nibble = c - '0';
} else {
*nibble = 0;
return JXL_FAILURE("Invalid metadata nibble");
}
JXL_ASSERT(*nibble < 16);
return true;
}
// Parses a PNG text chunk with key of the form "Raw profile type ####", with
// #### a type.
// Returns whether it could successfully parse the content.
// We trust key and encoded are null-terminated because they come from
// LodePNG.
static Status MaybeDecodeBase16(const char* key, const char* encoded,
std::string* type,
std::vector<uint8_t>* bytes) {
const char* encoded_end = encoded + strlen(encoded);
const char* kKey = "Raw profile type ";
if (strncmp(key, kKey, strlen(kKey)) != 0) return false;
*type = key + strlen(kKey);
const size_t kMaxTypeLen = 20;
if (type->length() > kMaxTypeLen) return false; // Type too long
// Header: freeform string and number of bytes
// Expected format is:
// \n
// profile name/description\n
// 40\n (the number of bytes after hex-decoding)
// 01234566789abcdef....\n (72 bytes per line max).
// 012345667\n (last line)
const char* pos = encoded;
if (*(pos++) != '\n') return false;
while (pos < encoded_end && *pos != '\n') {
pos++;
}
if (pos == encoded_end) return false;
// We parsed so far a \n, some number of non \n characters and are now
// pointing at a \n.
if (*(pos++) != '\n') return false;
unsigned long bytes_to_decode;
const int fields = sscanf(pos, "%8lu", &bytes_to_decode);
if (fields != 1) return false; // Failed to decode metadata header
JXL_ASSERT(pos + 8 <= encoded_end);
pos += 8; // read %8lu
// We need 2*bytes for the hex values plus 1 byte every 36 values.
const unsigned long needed_bytes =
bytes_to_decode * 2 + 1 + DivCeil(bytes_to_decode, 36);
if (needed_bytes != static_cast<size_t>(encoded_end - pos)) {
return JXL_FAILURE("Not enough bytes to parse %lu bytes in hex",
bytes_to_decode);
}
JXL_ASSERT(bytes->empty());
bytes->reserve(bytes_to_decode);
// Encoding: base16 with newline after 72 chars.
// pos points to the \n before the first line of hex values.
for (size_t i = 0; i < bytes_to_decode; ++i) {
if (i % 36 == 0) {
if (pos + 1 >= encoded_end) return false; // Truncated base16 1
if (*pos != '\n') return false; // Expected newline
++pos;
}
if (pos + 2 >= encoded_end) return false; // Truncated base16 2;
uint32_t nibble0, nibble1;
JXL_RETURN_IF_ERROR(DecodeNibble(pos[0], &nibble0));
JXL_RETURN_IF_ERROR(DecodeNibble(pos[1], &nibble1));
bytes->push_back(static_cast<uint8_t>((nibble0 << 4) + nibble1));
pos += 2;
}
if (pos + 1 != encoded_end) return false; // Too many encoded bytes
if (pos[0] != '\n') return false; // Incorrect metadata terminator
return true;
}
};
// Stores XMP and EXIF/IPTC into itext and text.
class BlobsWriterPNG {
public:
static Status Encode(const Blobs& blobs, LodePNGInfo* JXL_RESTRICT info) {
if (!blobs.exif.empty()) {
JXL_RETURN_IF_ERROR(EncodeBase16("exif", blobs.exif, info));
}
if (!blobs.iptc.empty()) {
JXL_RETURN_IF_ERROR(EncodeBase16("iptc", blobs.iptc, info));
}
if (!blobs.xmp.empty()) {
JXL_RETURN_IF_ERROR(EncodeBase16("xmp", blobs.xmp, info));
// Below is the official way, but it does not seem to work in ImageMagick.
// Exiv2 and exiftool are OK with either way of encoding XMP.
if (/* DISABLES CODE */ (0)) {
const char* key = "XML:com.adobe.xmp";
const std::string text(reinterpret_cast<const char*>(blobs.xmp.data()),
blobs.xmp.size());
if (lodepng_add_itext(info, key, "", "", text.c_str()) != 0) {
return JXL_FAILURE("Failed to add itext");
}
}
}
return true;
}
private:
static JXL_INLINE char EncodeNibble(const uint8_t nibble) {
JXL_ASSERT(nibble < 16);
return (nibble < 10) ? '0' + nibble : 'a' + nibble - 10;
}
static Status EncodeBase16(const std::string& type, const PaddedBytes& bytes,
LodePNGInfo* JXL_RESTRICT info) {
// Encoding: base16 with newline after 72 chars.
const size_t base16_size =
2 * bytes.size() + DivCeil(bytes.size(), size_t(36)) + 1;
std::string base16;
base16.reserve(base16_size);
for (size_t i = 0; i < bytes.size(); ++i) {
if (i % 36 == 0) base16.push_back('\n');
base16.push_back(EncodeNibble(bytes[i] >> 4));
base16.push_back(EncodeNibble(bytes[i] & 0x0F));
}
base16.push_back('\n');
JXL_ASSERT(base16.length() == base16_size);
char key[30];
snprintf(key, sizeof(key), "Raw profile type %s", type.c_str());
char header[30];
snprintf(header, sizeof(header), "\n%s\n%8" PRIuS, type.c_str(),
bytes.size());
const std::string& encoded = std::string(header) + base16;
if (lodepng_add_text(info, key, encoded.c_str()) != 0) {
return JXL_FAILURE("Failed to add text");
}
return true;
}
};
// Retrieves ColorEncoding from PNG chunks.
class ColorEncodingReaderPNG {
public:
// Fills original->color_encoding or returns false.
Status operator()(const Span<const uint8_t> bytes, const bool is_gray,
PackedPixelFile* ppf) {
JXL_RETURN_IF_ERROR(Decode(bytes, &ppf->metadata, &ppf->color_encoding));
const JxlColorSpace color_space =
is_gray ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB;
ppf->color_encoding.color_space = color_space;
if (have_pq_) {
// Synthesize the ICC with these parameters instead.
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
ppf->color_encoding.primaries = JXL_PRIMARIES_2100;
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_PQ;
ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_RELATIVE;
return true;
}
// ICC overrides anything else if present.
ppf->icc = std::move(icc_);
// PNG requires that sRGB override gAMA/cHRM.
if (have_srgb_) {
ppf->icc.clear();
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL;
return true;
}
// Try to create a custom profile:
// Attempt to set whitepoint and primaries if there is a cHRM chunk, or else
// use default sRGB (the PNG then is device-dependent).
// In case of grayscale, do not attempt to set the primaries and ignore the
// ones the PNG image has (but still set the white point).
if (!have_chrm_) {
#if JXL_PNG_VERBOSE >= 1
JXL_WARNING("No cHRM, assuming sRGB");
#endif
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
}
if (!have_gama_) {
#if JXL_PNG_VERBOSE >= 1
JXL_WARNING("No gAMA nor sRGB, assuming sRGB");
#endif
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
}
ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_RELATIVE;
return true;
}
// Whether the image has any color profile information (ICC chunk, sRGB
// chunk, cHRM chunk, and so on), or has no color information chunks at all.
bool HaveColorProfile() const {
return have_pq_ || have_srgb_ || have_gama_ || have_chrm_ || have_icc_;
}
private:
Status DecodeICC(const unsigned char* const payload,
const size_t payload_size) {
if (payload_size == 0) return JXL_FAILURE("Empty ICC payload");
const unsigned char* pos = payload;
const unsigned char* end = payload + payload_size;
// Profile name
if (*pos == '\0') return JXL_FAILURE("Expected ICC name");
for (size_t i = 0;; ++i) {
if (i == 80) return JXL_FAILURE("ICC profile name too long");
if (pos == end) return JXL_FAILURE("Not enough bytes for ICC name");
if (*pos++ == '\0') break;
}
// Special case for BT.2100 PQ (https://w3c.github.io/png-hdr-pq/) - try to
// synthesize the profile because table-based curves are less accurate.
// strcmp is safe because we already verified the string is 0-terminated.
if (!strcmp(reinterpret_cast<const char*>(payload), "ITUR_2100_PQ_FULL")) {
have_pq_ = true;
}
// Skip over compression method (only one is allowed)
if (pos == end) return JXL_FAILURE("Not enough bytes for ICC method");
if (*pos++ != 0) return JXL_FAILURE("Unsupported ICC method");
// Decompress
unsigned char* icc_buf = nullptr;
size_t icc_size = 0;
LodePNGDecompressSettings settings;
lodepng_decompress_settings_init(&settings);
const unsigned err = lodepng_zlib_decompress(
&icc_buf, &icc_size, pos, payload_size - (pos - payload), &settings);
if (err == 0) {
icc_.resize(icc_size);
memcpy(icc_.data(), icc_buf, icc_size);
}
free(icc_buf);
have_icc_ = true;
return true;
}
// Returns floating-point value from the PNG encoding (times 10^5).
static double F64FromU32(const uint32_t x) {
return static_cast<int32_t>(x) * 1E-5;
}
Status DecodeSRGB(const unsigned char* payload, const size_t payload_size,
JxlColorEncoding* color_encoding) {
if (payload_size != 1) return JXL_FAILURE("Wrong sRGB size");
// (PNG uses the same values as ICC.)
if (payload[0] >= 4) return JXL_FAILURE("Invalid Rendering Intent");
color_encoding->rendering_intent =
static_cast<JxlRenderingIntent>(payload[0]);
have_srgb_ = true;
return true;
}
Status DecodeGAMA(const unsigned char* payload, const size_t payload_size,
JxlColorEncoding* color_encoding) {
if (payload_size != 4) return JXL_FAILURE("Wrong gAMA size");
color_encoding->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
color_encoding->gamma = F64FromU32(LoadBE32(payload));
have_gama_ = true;
return true;
}
Status DecodeCHRM(const unsigned char* payload, const size_t payload_size,
JxlColorEncoding* color_encoding) {
if (payload_size != 32) return JXL_FAILURE("Wrong cHRM size");
color_encoding->white_point = JXL_WHITE_POINT_CUSTOM;
color_encoding->white_point_xy[0] = F64FromU32(LoadBE32(payload + 0));
color_encoding->white_point_xy[1] = F64FromU32(LoadBE32(payload + 4));
color_encoding->primaries = JXL_PRIMARIES_CUSTOM;
color_encoding->primaries_red_xy[0] = F64FromU32(LoadBE32(payload + 8));
color_encoding->primaries_red_xy[1] = F64FromU32(LoadBE32(payload + 12));
color_encoding->primaries_green_xy[0] = F64FromU32(LoadBE32(payload + 16));
color_encoding->primaries_green_xy[1] = F64FromU32(LoadBE32(payload + 20));
color_encoding->primaries_blue_xy[0] = F64FromU32(LoadBE32(payload + 24));
color_encoding->primaries_blue_xy[1] = F64FromU32(LoadBE32(payload + 28));
have_chrm_ = true;
return true;
}
Status DecodeEXIF(const unsigned char* payload, const size_t payload_size,
PackedMetadata* metadata) {
// If we already have EXIF, keep the larger one.
if (metadata->exif.size() > payload_size) return true;
metadata->exif.resize(payload_size);
memcpy(metadata->exif.data(), payload, payload_size);
return true;
}
Status Decode(const Span<const uint8_t> bytes, PackedMetadata* metadata,
JxlColorEncoding* color_encoding) {
// Look for colorimetry and text chunks in the PNG image. The PNG chunks
// begin after the PNG magic header of 8 bytes.
const unsigned char* chunk = bytes.data() + 8;
const unsigned char* end = bytes.data() + bytes.size();
for (;;) {
// chunk points to the first field of a PNG chunk. The chunk has
// respectively 4 bytes of length, 4 bytes type, length bytes of data,
// 4 bytes CRC.
if (chunk + 4 >= end) {
break; // Regular end reached.
}
char type_char[5];
if (chunk + 8 >= end) {
JXL_NOTIFY_ERROR("PNG: malformed chunk");
break;
}
lodepng_chunk_type(type_char, chunk);
std::string type = type_char;
if (type == "acTL" || type == "fcTL" || type == "fdAT") {
// this is an APNG file, without proper handling we would just return
// the first frame, so for now codec_apng handles animation until the
// animation chunk handling is added here
return false;
}
if (type == "eXIf" || type == "iCCP" || type == "sRGB" ||
type == "gAMA" || type == "cHRM") {
const unsigned char* payload = lodepng_chunk_data_const(chunk);
const size_t payload_size = lodepng_chunk_length(chunk);
// The entire chunk needs also 4 bytes of CRC after the payload.
if (payload + payload_size + 4 >= end) {
JXL_NOTIFY_ERROR("PNG: truncated chunk");
break;
}
if (lodepng_chunk_check_crc(chunk) != 0) {
JXL_NOTIFY_ERROR("CRC mismatch in unknown PNG chunk");
chunk = lodepng_chunk_next_const(chunk, end);
continue;
}
if (type == "eXIf") {
JXL_RETURN_IF_ERROR(DecodeEXIF(payload, payload_size, metadata));
} else if (type == "iCCP") {
JXL_RETURN_IF_ERROR(DecodeICC(payload, payload_size));
} else if (type == "sRGB") {
JXL_RETURN_IF_ERROR(
DecodeSRGB(payload, payload_size, color_encoding));
} else if (type == "gAMA") {
JXL_RETURN_IF_ERROR(
DecodeGAMA(payload, payload_size, color_encoding));
} else if (type == "cHRM") {
JXL_RETURN_IF_ERROR(
DecodeCHRM(payload, payload_size, color_encoding));
}
}
chunk = lodepng_chunk_next_const(chunk, end);
}
return true;
}
std::vector<uint8_t> icc_;
bool have_pq_ = false;
bool have_srgb_ = false;
bool have_gama_ = false;
bool have_chrm_ = false;
bool have_icc_ = false;
};
// Stores ColorEncoding into PNG chunks.
class ColorEncodingWriterPNG {
public:
static Status Encode(const ColorEncoding& c, LodePNGInfo* JXL_RESTRICT info) {
// Prefer to only write sRGB - smaller.
if (c.IsSRGB()) {
JXL_RETURN_IF_ERROR(AddSRGB(c, info));
// PNG recommends not including both sRGB and iCCP, so skip the latter.
} else if (!c.HaveFields() || !c.tf.IsGamma()) {
// Having a gamma value means that the source was a PNG with gAMA and
// without iCCP.
JXL_ASSERT(!c.ICC().empty());
JXL_RETURN_IF_ERROR(AddICC(c.ICC(), info));
}
// gAMA and cHRM are always allowed but will be overridden by sRGB/iCCP.
JXL_RETURN_IF_ERROR(MaybeAddGAMA(c, info));
JXL_RETURN_IF_ERROR(MaybeAddCHRM(c, info));
return true;
}
private:
static Status AddChunk(const char* type, const PaddedBytes& payload,
LodePNGInfo* JXL_RESTRICT info) {
// Ignore original location/order of chunks; place them in the first group.
if (lodepng_chunk_create(&info->unknown_chunks_data[0],
&info->unknown_chunks_size[0], payload.size(),
type, payload.data()) != 0) {
return JXL_FAILURE("Failed to add chunk");
}
return true;
}
static Status AddICC(const PaddedBytes& icc, LodePNGInfo* JXL_RESTRICT info) {
LodePNGCompressSettings settings;
lodepng_compress_settings_init(&settings);
unsigned char* out = nullptr;
size_t out_size = 0;
if (lodepng_zlib_compress(&out, &out_size, icc.data(), icc.size(),
&settings) != 0) {
return JXL_FAILURE("Failed to compress ICC");
}
PaddedBytes payload;
payload.resize(3 + out_size);
// TODO(janwas): use special name if PQ
payload[0] = '1'; // profile name
payload[1] = '\0';
payload[2] = 0; // compression method (zlib)
memcpy(&payload[3], out, out_size);
free(out);
return AddChunk("iCCP", payload, info);
}
static Status AddSRGB(const ColorEncoding& c,
LodePNGInfo* JXL_RESTRICT info) {
PaddedBytes payload;
payload.push_back(static_cast<uint8_t>(c.rendering_intent));
return AddChunk("sRGB", payload, info);
}
// Returns PNG encoding of floating-point value (times 10^5).
static uint32_t U32FromF64(const double x) {
return static_cast<int32_t>(roundf(x * 1E5));
}
static Status MaybeAddGAMA(const ColorEncoding& c,
LodePNGInfo* JXL_RESTRICT info) {
double gamma;
if (c.tf.IsGamma()) {
gamma = c.tf.GetGamma();
} else if (c.tf.IsLinear()) {
gamma = 1;
} else if (c.tf.IsSRGB()) {
gamma = 0.45455;
} else {
return true;
}
PaddedBytes payload(4);
StoreBE32(U32FromF64(gamma), payload.data());
return AddChunk("gAMA", payload, info);
}
static Status MaybeAddCHRM(const ColorEncoding& c,
LodePNGInfo* JXL_RESTRICT info) {
CIExy white_point = c.GetWhitePoint();
// A PNG image stores both whitepoint and primaries in the cHRM chunk, but
// for grayscale images we don't have primaries. It does not matter what
// values are stored in the PNG though (all colors are a multiple of the
// whitepoint), so choose default ones. See
// http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html section 4.2.2.1.
PrimariesCIExy primaries =
c.IsGray() ? ColorEncoding().GetPrimaries() : c.GetPrimaries();
if (c.primaries == Primaries::kSRGB && c.white_point == WhitePoint::kD65) {
// For sRGB, the cHRM chunk is supposed to have very specific values which
// don't quite match the pre-quantized ones we have (red is off by
// 0.00010). Technically, this is only required for full sRGB, but for
// consistency, we might as well use them whenever the primaries and white
// point are sRGB's.
white_point.x = 0.31270;
white_point.y = 0.32900;
primaries.r.x = 0.64000;
primaries.r.y = 0.33000;
primaries.g.x = 0.30000;
primaries.g.y = 0.60000;
primaries.b.x = 0.15000;
primaries.b.y = 0.06000;
}
PaddedBytes payload(32);
StoreBE32(U32FromF64(white_point.x), &payload[0]);
StoreBE32(U32FromF64(white_point.y), &payload[4]);
StoreBE32(U32FromF64(primaries.r.x), &payload[8]);
StoreBE32(U32FromF64(primaries.r.y), &payload[12]);
StoreBE32(U32FromF64(primaries.g.x), &payload[16]);
StoreBE32(U32FromF64(primaries.g.y), &payload[20]);
StoreBE32(U32FromF64(primaries.b.x), &payload[24]);
StoreBE32(U32FromF64(primaries.b.y), &payload[28]);
return AddChunk("cHRM", payload, info);
}
};
// RAII - ensures state is freed even if returning early.
struct PNGState {
PNGState() { lodepng_state_init(&s); }
~PNGState() { lodepng_state_cleanup(&s); }
LodePNGState s;
};
Status CheckGray(const LodePNGColorMode& mode, bool has_icc, bool* is_gray) {
switch (mode.colortype) {
case LCT_GREY:
case LCT_GREY_ALPHA:
*is_gray = true;
return true;
case LCT_RGB:
case LCT_RGBA:
*is_gray = false;
return true;
case LCT_PALETTE: {
if (has_icc) {
// If an ICC profile is present, the PNG specification requires
// palette to be interpreted as RGB colored, not grayscale, so we must
// output color in that case and unfortunately can't optimize it to
// gray if the palette only has gray entries.
*is_gray = false;
return true;
} else {
*is_gray = true;
for (size_t i = 0; i < mode.palettesize; i++) {
if (mode.palette[i * 4] != mode.palette[i * 4 + 1] ||
mode.palette[i * 4] != mode.palette[i * 4 + 2]) {
*is_gray = false;
break;
}
}
return true;
}
}
default:
*is_gray = false;
return JXL_FAILURE("Unexpected PNG color type");
}
}
Status CheckAlpha(const LodePNGColorMode& mode, bool* has_alpha) {
if (mode.key_defined) {
// Color key marks a single color as transparent.
*has_alpha = true;
return true;
}
switch (mode.colortype) {
case LCT_GREY:
case LCT_RGB:
*has_alpha = false;
return true;
case LCT_GREY_ALPHA:
case LCT_RGBA:
*has_alpha = true;
return true;
case LCT_PALETTE: {
*has_alpha = false;
for (size_t i = 0; i < mode.palettesize; i++) {
// PNG palettes are always 8-bit.
if (mode.palette[i * 4 + 3] != 255) {
*has_alpha = true;
break;
}
}
return true;
}
default:
*has_alpha = false;
return JXL_FAILURE("Unexpected PNG color type");
}
}
LodePNGColorType MakeType(const bool is_gray, const bool has_alpha) {
if (is_gray) {
return has_alpha ? LCT_GREY_ALPHA : LCT_GREY;
}
return has_alpha ? LCT_RGBA : LCT_RGB;
}
// Inspects first chunk of the given type and updates state with the information
// when the chunk is relevant and present in the file.
Status InspectChunkType(const Span<const uint8_t> bytes,
const std::string& type, LodePNGState* state) {
const unsigned char* chunk = lodepng_chunk_find_const(
bytes.data(), bytes.data() + bytes.size(), type.c_str());
if (chunk && lodepng_inspect_chunk(state, chunk - bytes.data(), bytes.data(),
bytes.size()) != 0) {
return JXL_FAILURE("Invalid chunk \"%s\" in PNG image", type.c_str());
}
return true;
}
} // namespace
Status DecodeImagePNG(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
unsigned w, h;
PNGState state;
if (lodepng_inspect(&w, &h, &state.s, bytes.data(), bytes.size()) != 0) {
return false; // not an error - just wrong format
}
JXL_RETURN_IF_ERROR(VerifyDimensions(&constraints, w, h));
// Palette RGB values
if (!InspectChunkType(bytes, "PLTE", &state.s)) {
return false;
}
// Transparent color key, or palette transparency
if (!InspectChunkType(bytes, "tRNS", &state.s)) {
return false;
}
// ICC profile
if (!InspectChunkType(bytes, "iCCP", &state.s)) {
return false;
}
const LodePNGColorMode& color_mode = state.s.info_png.color;
bool has_icc = state.s.info_png.iccp_defined;
bool is_gray, has_alpha;
JXL_RETURN_IF_ERROR(CheckGray(color_mode, has_icc, &is_gray));
JXL_RETURN_IF_ERROR(CheckAlpha(color_mode, &has_alpha));
// We want LodePNG to promote 1/2/4 bit pixels to 8.
size_t bits_per_sample = std::max(color_mode.bitdepth, 8u);
if (bits_per_sample != 8 && bits_per_sample != 16) {
return JXL_FAILURE("Unexpected PNG bit depth");
}
// Always decode to 8/16-bit RGB/RGBA, not LCT_PALETTE.
state.s.info_raw.bitdepth = static_cast<unsigned>(bits_per_sample);
state.s.info_raw.colortype = MakeType(is_gray, has_alpha);
unsigned char* out = nullptr;
const unsigned err =
lodepng_decode(&out, &w, &h, &state.s, bytes.data(), bytes.size());
// Automatically call free(out) on return.
std::unique_ptr<unsigned char, void (*)(void*)> out_ptr{out, free};
if (err != 0) {
return JXL_FAILURE("PNG decode failed: %s", lodepng_error_text(err));
}
if (!BlobsReaderPNG::Decode(state.s.info_png, &ppf->metadata)) {
JXL_WARNING("PNG metadata may be incomplete");
}
ColorEncodingReaderPNG reader;
JXL_RETURN_IF_ERROR(reader(bytes, is_gray, ppf));
const uint32_t num_channels = (is_gray ? 1 : 3) + has_alpha;
ppf->info.xsize = w;
ppf->info.ysize = h;
// Original data is uint, so exponent_bits_per_sample = 0.
ppf->info.bits_per_sample = bits_per_sample;
ppf->info.exponent_bits_per_sample = 0;
ppf->info.uses_original_profile = true;
ppf->info.have_preview = false;
ppf->info.have_animation = false;
ppf->info.alpha_bits = has_alpha ? bits_per_sample : 0;
ppf->info.num_color_channels = is_gray ? 1 : 3;
const JxlPixelFormat format{
/*num_channels=*/num_channels,
/*data_type=*/bits_per_sample == 16 ? JXL_TYPE_UINT16 : JXL_TYPE_UINT8,
/*endianness=*/JXL_BIG_ENDIAN, // PNG requirement
/*align=*/0,
};
const size_t out_size = static_cast<size_t>(w) * h * num_channels *
bits_per_sample / kBitsPerByte;
ppf->frames.emplace_back(w, h, format, out_ptr.release(), out_size);
JXL_RETURN_IF_ERROR(
ApplyColorHints(color_hints, reader.HaveColorProfile(), is_gray, ppf));
return true;
}
Status EncodeImagePNG(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes) {
if (bits_per_sample > 8) {
bits_per_sample = 16;
} else if (bits_per_sample < 8) {
// PNG can also do 4, 2, and 1 bits per sample, but it isn't implemented
bits_per_sample = 8;
}
ImageBundle ib = io->Main().Copy();
const size_t alpha_bits = ib.HasAlpha() ? bits_per_sample : 0;
ImageMetadata metadata = io->metadata.m;
ImageBundle store(&metadata);
const ImageBundle* transformed;
JXL_RETURN_IF_ERROR(
TransformIfNeeded(ib, c_desired, pool, &store, &transformed));
size_t stride = ib.oriented_xsize() *
DivCeil(c_desired.Channels() * bits_per_sample + alpha_bits,
kBitsPerByte);
PaddedBytes raw_bytes(stride * ib.oriented_ysize());
JXL_RETURN_IF_ERROR(ConvertToExternal(
*transformed, bits_per_sample, /*float_out=*/false,
c_desired.Channels() + (ib.HasAlpha() ? 1 : 0), JXL_BIG_ENDIAN, stride,
pool, raw_bytes.data(), raw_bytes.size(), /*out_callback=*/nullptr,
/*out_opaque=*/nullptr, metadata.GetOrientation()));
PNGState state;
// For maximum compatibility, still store 8-bit even if pixels are all zero.
state.s.encoder.auto_convert = 0;
LodePNGInfo* info = &state.s.info_png;
info->color.bitdepth = bits_per_sample;
info->color.colortype = MakeType(ib.IsGray(), ib.HasAlpha());
state.s.info_raw = info->color;
JXL_RETURN_IF_ERROR(ColorEncodingWriterPNG::Encode(c_desired, info));
JXL_RETURN_IF_ERROR(BlobsWriterPNG::Encode(io->blobs, info));
unsigned char* out = nullptr;
size_t out_size = 0;
const unsigned err =
lodepng_encode(&out, &out_size, raw_bytes.data(), ib.oriented_xsize(),
ib.oriented_ysize(), &state.s);
// Automatically call free(out) on return.
std::unique_ptr<unsigned char, void (*)(void*)> out_ptr{out, free};
if (err != 0) {
return JXL_FAILURE("Failed to encode PNG: %s", lodepng_error_text(err));
}
bytes->resize(out_size);
memcpy(bytes->data(), out, out_size);
return true;
}
} // namespace extras
} // namespace jxl

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

@ -11,7 +11,7 @@
#include <stddef.h>
#include <stdint.h>
#include "lib/extras/color_hints.h"
#include "lib/extras/dec/color_hints.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
@ -23,7 +23,7 @@ namespace extras {
// Decodes `bytes` into `io`.
Status DecodeImagePSD(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
const extras::ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io);
// Not implemented yet

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

@ -13,16 +13,16 @@
#include <vector>
#include "gtest/gtest.h"
#include "lib/extras/codec_pgx.h"
#include "lib/extras/codec_pnm.h"
#include "lib/extras/dec/pgx.h"
#include "lib/extras/dec/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"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_test_utils.h"
#include "lib/jxl/luminance.h"
#include "lib/jxl/testdata.h"
namespace jxl {
@ -106,7 +106,6 @@ void TestRoundTrip(Codec codec, const size_t xsize, const size_t ysize,
CodecInOut io2;
ColorHints color_hints;
io2.target_nits = io.metadata.m.IntensityTarget();
// Only for PNM because PNG will warn about ignoring them.
if (codec == Codec::kPNM) {
color_hints.Add("color_space", Description(c_external));
@ -128,7 +127,7 @@ void TestRoundTrip(Codec codec, const size_t xsize, const size_t ysize,
EXPECT_TRUE(SamePixels(ib1.alpha(), *ib2.alpha()));
}
JXL_CHECK(ib2.TransformTo(ib1.c_current(), pool));
JXL_CHECK(ib2.TransformTo(ib1.c_current(), GetJxlCms(), pool));
double max_l1, max_rel;
// Round-trip tolerances must be higher than in external_image_test because
@ -184,8 +183,7 @@ TEST(CodecTest, TestRoundTrip) {
}
#endif
CodecInOut DecodeRoundtrip(const std::string& pathname, Codec expected_codec,
ThreadPool* pool,
CodecInOut DecodeRoundtrip(const std::string& pathname, ThreadPool* pool,
const ColorHints& color_hints = ColorHints()) {
CodecInOut io;
const PaddedBytes orig = ReadTestData(pathname);
@ -195,7 +193,7 @@ CodecInOut DecodeRoundtrip(const std::string& pathname, Codec expected_codec,
// Encode/Decode again to make sure Encode carries through all metadata.
PaddedBytes encoded;
JXL_CHECK(Encode(io, expected_codec, io.metadata.m.color_encoding,
JXL_CHECK(Encode(io, Codec::kPNG, io.metadata.m.color_encoding,
io.metadata.m.bit_depth.bits_per_sample, &encoded, pool));
CodecInOut io2;
@ -345,7 +343,7 @@ TEST(CodecTest, TestPNGSuite) {
void VerifyWideGamutMetadata(const std::string& relative_pathname,
const Primaries primaries, ThreadPool* pool) {
const CodecInOut io = DecodeRoundtrip(relative_pathname, Codec::kPNG, pool);
const CodecInOut io = DecodeRoundtrip(relative_pathname, pool);
EXPECT_EQ(8u, io.metadata.m.bit_depth.bits_per_sample);
EXPECT_FALSE(io.metadata.m.bit_depth.floating_point_sample);

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

@ -0,0 +1,712 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/dec/apng.h"
// Parts of this code are taken from apngdis, which has the following license:
/* APNG Disassembler 2.8
*
* Deconstructs APNG files into individual frames.
*
* http://apngdis.sourceforge.net
*
* Copyright (c) 2010-2015 Max Stepin
* maxst at users.sourceforge.net
*
* zlib license
* ------------
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
*/
#include <stdio.h>
#include <string.h>
#include <string>
#include <utility>
#include <vector>
#include "jxl/encode.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/common.h"
#include "lib/jxl/sanitizers.h"
#include "png.h" /* original (unpatched) libpng is ok */
namespace jxl {
namespace extras {
namespace {
// Returns floating-point value from the PNG encoding (times 10^5).
static double F64FromU32(const uint32_t x) {
return static_cast<int32_t>(x) * 1E-5;
}
Status DecodeSRGB(const unsigned char* payload, const size_t payload_size,
JxlColorEncoding* color_encoding) {
if (payload_size != 1) return JXL_FAILURE("Wrong sRGB size");
// (PNG uses the same values as ICC.)
if (payload[0] >= 4) return JXL_FAILURE("Invalid Rendering Intent");
color_encoding->rendering_intent =
static_cast<JxlRenderingIntent>(payload[0]);
return true;
}
Status DecodeGAMA(const unsigned char* payload, const size_t payload_size,
JxlColorEncoding* color_encoding) {
if (payload_size != 4) return JXL_FAILURE("Wrong gAMA size");
color_encoding->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
color_encoding->gamma = F64FromU32(LoadBE32(payload));
return true;
}
Status DecodeCHRM(const unsigned char* payload, const size_t payload_size,
JxlColorEncoding* color_encoding) {
if (payload_size != 32) return JXL_FAILURE("Wrong cHRM size");
color_encoding->white_point = JXL_WHITE_POINT_CUSTOM;
color_encoding->white_point_xy[0] = F64FromU32(LoadBE32(payload + 0));
color_encoding->white_point_xy[1] = F64FromU32(LoadBE32(payload + 4));
color_encoding->primaries = JXL_PRIMARIES_CUSTOM;
color_encoding->primaries_red_xy[0] = F64FromU32(LoadBE32(payload + 8));
color_encoding->primaries_red_xy[1] = F64FromU32(LoadBE32(payload + 12));
color_encoding->primaries_green_xy[0] = F64FromU32(LoadBE32(payload + 16));
color_encoding->primaries_green_xy[1] = F64FromU32(LoadBE32(payload + 20));
color_encoding->primaries_blue_xy[0] = F64FromU32(LoadBE32(payload + 24));
color_encoding->primaries_blue_xy[1] = F64FromU32(LoadBE32(payload + 28));
return true;
}
// Retrieves XMP and EXIF/IPTC from itext and text.
class BlobsReaderPNG {
public:
static Status Decode(const png_text_struct& info, PackedMetadata* metadata) {
// We trust these are properly null-terminated by libpng.
const char* key = info.key;
const char* value = info.text;
if (strstr(key, "XML:com.adobe.xmp")) {
metadata->xmp.resize(strlen(value)); // safe, see above
memcpy(metadata->xmp.data(), value, metadata->xmp.size());
}
std::string type;
std::vector<uint8_t> bytes;
// Handle text chunks annotated with key "Raw profile type ####", with
// #### a type, which may contain metadata.
const char* kKey = "Raw profile type ";
if (strncmp(key, kKey, strlen(kKey)) != 0) return false;
if (!MaybeDecodeBase16(key, value, &type, &bytes)) {
JXL_WARNING("Couldn't parse 'Raw format type' text chunk");
return false;
}
if (type == "exif") {
if (!metadata->exif.empty()) {
JXL_WARNING("overwriting EXIF (%" PRIuS " bytes) with base16 (%" PRIuS
" bytes)",
metadata->exif.size(), bytes.size());
}
metadata->exif = std::move(bytes);
} else if (type == "iptc") {
// TODO (jon): Deal with IPTC in some way
} else if (type == "8bim") {
// TODO (jon): Deal with 8bim in some way
} else if (type == "xmp") {
if (!metadata->xmp.empty()) {
JXL_WARNING("overwriting XMP (%" PRIuS " bytes) with base16 (%" PRIuS
" bytes)",
metadata->xmp.size(), bytes.size());
}
metadata->xmp = std::move(bytes);
} else {
JXL_WARNING("Unknown type in 'Raw format type' text chunk: %s: %" PRIuS
" bytes",
type.c_str(), bytes.size());
}
return true;
}
private:
// Returns false if invalid.
static JXL_INLINE Status DecodeNibble(const char c,
uint32_t* JXL_RESTRICT nibble) {
if ('a' <= c && c <= 'f') {
*nibble = 10 + c - 'a';
} else if ('0' <= c && c <= '9') {
*nibble = c - '0';
} else {
*nibble = 0;
return JXL_FAILURE("Invalid metadata nibble");
}
JXL_ASSERT(*nibble < 16);
return true;
}
// Parses a PNG text chunk with key of the form "Raw profile type ####", with
// #### a type.
// Returns whether it could successfully parse the content.
// We trust key and encoded are null-terminated because they come from
// libpng.
static Status MaybeDecodeBase16(const char* key, const char* encoded,
std::string* type,
std::vector<uint8_t>* bytes) {
const char* encoded_end = encoded + strlen(encoded);
const char* kKey = "Raw profile type ";
if (strncmp(key, kKey, strlen(kKey)) != 0) return false;
*type = key + strlen(kKey);
const size_t kMaxTypeLen = 20;
if (type->length() > kMaxTypeLen) return false; // Type too long
// Header: freeform string and number of bytes
// Expected format is:
// \n
// profile name/description\n
// 40\n (the number of bytes after hex-decoding)
// 01234566789abcdef....\n (72 bytes per line max).
// 012345667\n (last line)
const char* pos = encoded;
if (*(pos++) != '\n') return false;
while (pos < encoded_end && *pos != '\n') {
pos++;
}
if (pos == encoded_end) return false;
// We parsed so far a \n, some number of non \n characters and are now
// pointing at a \n.
if (*(pos++) != '\n') return false;
unsigned long bytes_to_decode;
const int fields = sscanf(pos, "%8lu", &bytes_to_decode);
if (fields != 1) return false; // Failed to decode metadata header
JXL_ASSERT(pos + 8 <= encoded_end);
pos += 8; // read %8lu
// We need 2*bytes for the hex values plus 1 byte every 36 values.
const unsigned long needed_bytes =
bytes_to_decode * 2 + 1 + DivCeil(bytes_to_decode, 36);
if (needed_bytes != static_cast<size_t>(encoded_end - pos)) {
return JXL_FAILURE("Not enough bytes to parse %lu bytes in hex",
bytes_to_decode);
}
JXL_ASSERT(bytes->empty());
bytes->reserve(bytes_to_decode);
// Encoding: base16 with newline after 72 chars.
// pos points to the \n before the first line of hex values.
for (size_t i = 0; i < bytes_to_decode; ++i) {
if (i % 36 == 0) {
if (pos + 1 >= encoded_end) return false; // Truncated base16 1
if (*pos != '\n') return false; // Expected newline
++pos;
}
if (pos + 2 >= encoded_end) return false; // Truncated base16 2;
uint32_t nibble0, nibble1;
JXL_RETURN_IF_ERROR(DecodeNibble(pos[0], &nibble0));
JXL_RETURN_IF_ERROR(DecodeNibble(pos[1], &nibble1));
bytes->push_back(static_cast<uint8_t>((nibble0 << 4) + nibble1));
pos += 2;
}
if (pos + 1 != encoded_end) return false; // Too many encoded bytes
if (pos[0] != '\n') return false; // Incorrect metadata terminator
return true;
}
};
constexpr bool isAbc(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
constexpr uint32_t kId_IHDR = 0x52444849;
constexpr uint32_t kId_acTL = 0x4C546361;
constexpr uint32_t kId_fcTL = 0x4C546366;
constexpr uint32_t kId_IDAT = 0x54414449;
constexpr uint32_t kId_fdAT = 0x54416466;
constexpr uint32_t kId_IEND = 0x444E4549;
constexpr uint32_t kId_iCCP = 0x50434369;
constexpr uint32_t kId_sRGB = 0x42475273;
constexpr uint32_t kId_gAMA = 0x414D4167;
constexpr uint32_t kId_cHRM = 0x4D524863;
constexpr uint32_t kId_eXIf = 0x66495865;
struct APNGFrame {
PaddedBytes pixels;
std::vector<uint8_t*> rows;
unsigned int w, h, delay_num, delay_den;
};
struct Reader {
const uint8_t* next;
const uint8_t* last;
bool Read(void* data, size_t len) {
size_t cap = last - next;
size_t to_copy = std::min(cap, len);
memcpy(data, next, to_copy);
next += to_copy;
return (len == to_copy);
}
bool Eof() { return next == last; }
};
const unsigned long cMaxPNGSize = 1000000UL;
const size_t kMaxPNGChunkSize = 100000000; // 100 MB
void info_fn(png_structp png_ptr, png_infop info_ptr) {
png_set_expand(png_ptr);
png_set_palette_to_rgb(png_ptr);
png_set_tRNS_to_alpha(png_ptr);
(void)png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);
}
void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num,
int pass) {
APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr);
JXL_CHECK(frame);
JXL_CHECK(frame->rows[row_num] < frame->pixels.data() + frame->pixels.size());
png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row);
}
inline unsigned int read_chunk(Reader* r, PaddedBytes* pChunk) {
unsigned char len[4];
if (r->Read(&len, 4)) {
const auto size = png_get_uint_32(len);
// Check first, to avoid overflow.
if (size > kMaxPNGChunkSize) {
JXL_WARNING("APNG chunk size is too big");
return 0;
}
pChunk->resize(size + 12);
memcpy(pChunk->data(), len, 4);
if (r->Read(pChunk->data() + 4, pChunk->size() - 4)) {
return LoadLE32(pChunk->data() + 4);
}
}
return 0;
}
int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
bool hasInfo, PaddedBytes& chunkIHDR,
std::vector<PaddedBytes>& chunksInfo) {
unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10};
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info_ptr = png_create_info_struct(png_ptr);
if (!png_ptr || !info_ptr) return 1;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 1;
}
png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
png_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, NULL);
png_process_data(png_ptr, info_ptr, header, 8);
png_process_data(png_ptr, info_ptr, chunkIHDR.data(), chunkIHDR.size());
if (hasInfo) {
for (unsigned int i = 0; i < chunksInfo.size(); i++) {
png_process_data(png_ptr, info_ptr, chunksInfo[i].data(),
chunksInfo[i].size());
}
}
return 0;
}
int processing_data(png_structp png_ptr, png_infop info_ptr, unsigned char* p,
unsigned int size) {
if (!png_ptr || !info_ptr) return 1;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 1;
}
png_process_data(png_ptr, info_ptr, p, size);
return 0;
}
int processing_finish(png_structp png_ptr, png_infop info_ptr,
PackedMetadata* metadata) {
unsigned char footer[12] = {0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130};
if (!png_ptr || !info_ptr) return 1;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 1;
}
png_process_data(png_ptr, info_ptr, footer, 12);
// before destroying: check if we encountered any metadata chunks
png_textp text_ptr;
int num_text;
png_get_text(png_ptr, info_ptr, &text_ptr, &num_text);
for (int i = 0; i < num_text; i++) {
(void)BlobsReaderPNG::Decode(text_ptr[i], metadata);
}
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 0;
}
} // namespace
Status DecodeImageAPNG(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
Reader r;
unsigned int id, j, w, h, w0, h0, x0, y0;
unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize;
unsigned char sig[8];
png_structp png_ptr;
png_infop info_ptr;
PaddedBytes chunk;
PaddedBytes chunkIHDR;
std::vector<PaddedBytes> chunksInfo;
bool isAnimated = false;
bool skipFirst = false;
bool hasInfo = false;
bool all_dispose_bg = true;
APNGFrame frameRaw = {};
uint32_t num_channels;
JxlPixelFormat format;
unsigned int bytes_per_pixel = 0;
r = {bytes.data(), bytes.data() + bytes.size()};
// Not a PNG => not an error
unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
if (!r.Read(sig, 8) || memcmp(sig, png_signature, 8) != 0) {
return false;
}
id = read_chunk(&r, &chunkIHDR);
ppf->info.exponent_bits_per_sample = 0;
ppf->info.alpha_exponent_bits = 0;
ppf->info.orientation = JXL_ORIENT_IDENTITY;
ppf->frames.clear();
bool have_color = false, have_srgb = false;
bool errorstate = true;
if (id == kId_IHDR && chunkIHDR.size() == 25) {
x0 = 0;
y0 = 0;
delay_num = 1;
delay_den = 10;
dop = 0;
bop = 0;
w0 = w = png_get_uint_32(chunkIHDR.data() + 8);
h0 = h = png_get_uint_32(chunkIHDR.data() + 12);
if (w > cMaxPNGSize || h > cMaxPNGSize) {
return false;
}
// default settings in case e.g. only gAMA is given
ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB;
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
if (!processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
chunkIHDR, chunksInfo)) {
bool last_base_was_none = true;
while (!r.Eof()) {
id = read_chunk(&r, &chunk);
if (!id) break;
if (id == kId_acTL && !hasInfo && !isAnimated) {
isAnimated = true;
skipFirst = true;
ppf->info.have_animation = true;
ppf->info.animation.tps_numerator = 1000;
ppf->info.animation.tps_denominator = 1;
} else if (id == kId_IEND ||
(id == kId_fcTL && (!hasInfo || isAnimated))) {
if (hasInfo) {
if (!processing_finish(png_ptr, info_ptr, &ppf->metadata)) {
// Allocates the frame buffer.
ppf->frames.emplace_back(w0, h0, format);
auto* frame = &ppf->frames.back();
frame->frame_info.duration = delay_num * 1000 / delay_den;
frame->x0 = x0;
frame->y0 = y0;
// TODO(veluca): this could in principle be implemented.
if (last_base_was_none && !all_dispose_bg &&
(x0 != 0 || y0 != 0 || w0 != w || h0 != h || bop != 0)) {
return JXL_FAILURE(
"APNG with dispose-to-0 is not supported for non-full or "
"blended frames");
}
switch (dop) {
case 0:
frame->use_for_next_frame = true;
last_base_was_none = false;
all_dispose_bg = false;
break;
case 2:
frame->use_for_next_frame = false;
all_dispose_bg = false;
break;
default:
frame->use_for_next_frame = false;
last_base_was_none = true;
}
frame->blend = bop != 0;
for (size_t y = 0; y < h0; ++y) {
memcpy(static_cast<uint8_t*>(frame->color.pixels()) +
frame->color.stride * y,
frameRaw.rows[y], bytes_per_pixel * w0);
}
} else {
break;
}
}
if (id == kId_IEND) {
errorstate = false;
break;
}
// At this point the old frame is done. Let's start a new one.
w0 = png_get_uint_32(chunk.data() + 12);
h0 = png_get_uint_32(chunk.data() + 16);
x0 = png_get_uint_32(chunk.data() + 20);
y0 = png_get_uint_32(chunk.data() + 24);
delay_num = png_get_uint_16(chunk.data() + 28);
delay_den = png_get_uint_16(chunk.data() + 30);
dop = chunk[32];
bop = chunk[33];
if (!delay_den) delay_den = 100;
if (w0 > cMaxPNGSize || h0 > cMaxPNGSize || x0 > cMaxPNGSize ||
y0 > cMaxPNGSize || x0 + w0 > w || y0 + h0 > h || dop > 2 ||
bop > 1) {
break;
}
if (hasInfo) {
memcpy(chunkIHDR.data() + 8, chunk.data() + 12, 8);
if (processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
chunkIHDR, chunksInfo)) {
break;
}
} else
skipFirst = false;
if (ppf->frames.size() == (skipFirst ? 1 : 0)) {
bop = 0;
if (dop == 2) dop = 1;
}
} else if (id == kId_IDAT) {
// First IDAT chunk means we now have all header info
hasInfo = true;
JXL_CHECK(w == png_get_image_width(png_ptr, info_ptr));
JXL_CHECK(h == png_get_image_height(png_ptr, info_ptr));
int colortype = png_get_color_type(png_ptr, info_ptr);
ppf->info.bits_per_sample = png_get_bit_depth(png_ptr, info_ptr);
png_color_8p sigbits = NULL;
png_get_sBIT(png_ptr, info_ptr, &sigbits);
if (colortype & 1) {
// palette will actually be 8-bit regardless of the index bitdepth
ppf->info.bits_per_sample = 8;
}
if (colortype & 2) {
ppf->info.num_color_channels = 3;
ppf->color_encoding.color_space = JXL_COLOR_SPACE_RGB;
if (sigbits && sigbits->red == sigbits->green &&
sigbits->green == sigbits->blue)
ppf->info.bits_per_sample = sigbits->red;
} else {
ppf->info.num_color_channels = 1;
ppf->color_encoding.color_space = JXL_COLOR_SPACE_GRAY;
if (sigbits) ppf->info.bits_per_sample = sigbits->gray;
}
if (colortype & 4 ||
png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
ppf->info.alpha_bits = ppf->info.bits_per_sample;
if (sigbits) ppf->info.alpha_bits = sigbits->alpha;
} else {
ppf->info.alpha_bits = 0;
}
ppf->color_encoding.color_space =
(ppf->info.num_color_channels == 1 ? JXL_COLOR_SPACE_GRAY
: JXL_COLOR_SPACE_RGB);
ppf->info.xsize = w;
ppf->info.ysize = h;
JXL_RETURN_IF_ERROR(VerifyDimensions(&constraints, w, h));
num_channels =
ppf->info.num_color_channels + (ppf->info.alpha_bits ? 1 : 0);
format = {
/*num_channels=*/num_channels,
/*data_type=*/ppf->info.bits_per_sample > 8 ? JXL_TYPE_UINT16
: JXL_TYPE_UINT8,
/*endianness=*/JXL_BIG_ENDIAN,
/*align=*/0,
};
bytes_per_pixel =
num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1);
rowbytes = w * bytes_per_pixel;
imagesize = h * rowbytes;
frameRaw.pixels.resize(imagesize);
frameRaw.rows.resize(h);
for (j = 0; j < h; j++)
frameRaw.rows[j] = frameRaw.pixels.data() + j * rowbytes;
if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) {
break;
}
} else if (id == kId_fdAT && isAnimated) {
png_save_uint_32(chunk.data() + 4, chunk.size() - 16);
memcpy(chunk.data() + 8, "IDAT", 4);
if (processing_data(png_ptr, info_ptr, chunk.data() + 4,
chunk.size() - 4)) {
break;
}
} else if (id == kId_iCCP) {
if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) {
JXL_WARNING("Corrupt iCCP chunk");
break;
}
// TODO(jon): catch special case of PQ and synthesize color encoding
// in that case
int compression_type;
png_bytep profile;
png_charp name;
png_uint_32 proflen;
png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile,
&proflen);
ppf->icc.resize(proflen);
memcpy(ppf->icc.data(), profile, proflen);
have_color = true;
} else if (id == kId_sRGB) {
JXL_RETURN_IF_ERROR(DecodeSRGB(chunk.data() + 8, chunk.size() - 12,
&ppf->color_encoding));
have_srgb = true;
have_color = true;
} else if (id == kId_gAMA) {
JXL_RETURN_IF_ERROR(DecodeGAMA(chunk.data() + 8, chunk.size() - 12,
&ppf->color_encoding));
have_color = true;
} else if (id == kId_cHRM) {
JXL_RETURN_IF_ERROR(DecodeCHRM(chunk.data() + 8, chunk.size() - 12,
&ppf->color_encoding));
have_color = true;
} else if (id == kId_eXIf) {
ppf->metadata.exif.resize(chunk.size() - 12);
memcpy(ppf->metadata.exif.data(), chunk.data() + 8,
chunk.size() - 12);
} else if (!isAbc(chunk[4]) || !isAbc(chunk[5]) || !isAbc(chunk[6]) ||
!isAbc(chunk[7])) {
break;
} else {
if (processing_data(png_ptr, info_ptr, chunk.data(), chunk.size())) {
break;
}
if (!hasInfo) {
chunksInfo.push_back(chunk);
continue;
}
}
}
}
if (have_srgb) {
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL;
}
JXL_RETURN_IF_ERROR(ApplyColorHints(
color_hints, have_color, ppf->info.num_color_channels == 1, ppf));
}
if (errorstate) return false;
return true;
}
static void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) {
PaddedBytes* bytes = static_cast<PaddedBytes*>(png_get_io_ptr(png_ptr));
bytes->append(data, data + length);
}
// Stores XMP and EXIF/IPTC into key/value strings for PNG
class BlobsWriterPNG {
public:
static Status Encode(const Blobs& blobs, std::vector<std::string>* strings) {
if (!blobs.exif.empty()) {
JXL_RETURN_IF_ERROR(EncodeBase16("exif", blobs.exif, strings));
}
if (!blobs.iptc.empty()) {
JXL_RETURN_IF_ERROR(EncodeBase16("iptc", blobs.iptc, strings));
}
if (!blobs.xmp.empty()) {
JXL_RETURN_IF_ERROR(EncodeBase16("xmp", blobs.xmp, strings));
}
return true;
}
private:
static JXL_INLINE char EncodeNibble(const uint8_t nibble) {
JXL_ASSERT(nibble < 16);
return (nibble < 10) ? '0' + nibble : 'a' + nibble - 10;
}
static Status EncodeBase16(const std::string& type, const PaddedBytes& bytes,
std::vector<std::string>* strings) {
// Encoding: base16 with newline after 72 chars.
const size_t base16_size =
2 * bytes.size() + DivCeil(bytes.size(), size_t(36)) + 1;
std::string base16;
base16.reserve(base16_size);
for (size_t i = 0; i < bytes.size(); ++i) {
if (i % 36 == 0) base16.push_back('\n');
base16.push_back(EncodeNibble(bytes[i] >> 4));
base16.push_back(EncodeNibble(bytes[i] & 0x0F));
}
base16.push_back('\n');
JXL_ASSERT(base16.length() == base16_size);
char key[30];
snprintf(key, sizeof(key), "Raw profile type %s", type.c_str());
char header[30];
snprintf(header, sizeof(header), "\n%s\n%8" PRIuS, type.c_str(),
bytes.size());
strings->push_back(std::string(key));
strings->push_back(std::string(header) + base16);
return true;
}
};
} // namespace extras
} // namespace jxl

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

@ -3,14 +3,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_EXTRAS_CODEC_APNG_H_
#define LIB_EXTRAS_CODEC_APNG_H_
#ifndef LIB_EXTRAS_DEC_APNG_H_
#define LIB_EXTRAS_DEC_APNG_H_
// Decodes APNG images in memory.
#include <stdint.h>
#include "lib/extras/color_hints.h"
#include "lib/extras/dec/color_hints.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
@ -21,13 +21,12 @@
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. color_space_hint is ignored.
Status DecodeImageAPNG(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
// Decodes `bytes` into `ppf`.
Status DecodeImageAPNG(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_APNG_H_
#endif // LIB_EXTRAS_DEC_APNG_H_

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

@ -3,9 +3,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/color_description.h"
#include "lib/extras/dec/color_description.h"
#include <errno.h>
#include <cmath>
namespace jxl {

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

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

@ -3,7 +3,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/color_description.h"
#include "lib/extras/dec/color_description.h"
#include "gtest/gtest.h"
#include "lib/jxl/color_encoding_internal.h"

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

@ -3,10 +3,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/color_hints.h"
#include "lib/extras/dec/color_hints.h"
#include "jxl/encode.h"
#include "lib/extras/color_description.h"
#include "lib/extras/dec/color_description.h"
#include "lib/jxl/base/file_io.h"
namespace jxl {
@ -54,7 +54,11 @@ Status ApplyColorHints(const ColorHints& color_hints,
if (!got_color_space) {
JXL_WARNING("No color_space/icc_pathname given, assuming sRGB");
JxlColorEncodingSetToSRGB(&ppf->color_encoding, is_gray);
ppf->color_encoding.color_space =
is_gray ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB;
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
}
return true;

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

@ -21,6 +21,7 @@
#include "lib/jxl/base/status.h"
namespace jxl {
namespace extras {
class ColorHints {
public:
@ -59,8 +60,6 @@ class ColorHints {
std::vector<KeyValue> kv_;
};
namespace extras {
// Apply the color hints to the decoded image in PackedPixelFile if any.
// color_already_set tells whether the color encoding was already set, in which
// case the hints are ignored if any hint is passed.

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

@ -0,0 +1,139 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/dec/decode.h"
#include <locale>
#if JPEGXL_ENABLE_APNG
#include "lib/extras/dec/apng.h"
#endif
#if JPEGXL_ENABLE_EXR
#include "lib/extras/dec/exr.h"
#endif
#if JPEGXL_ENABLE_GIF
#include "lib/extras/dec/gif.h"
#endif
#if JPEGXL_ENABLE_JPEG
#include "lib/extras/dec/jpg.h"
#endif
#include "lib/extras/dec/pgx.h"
#include "lib/extras/dec/pnm.h"
namespace jxl {
namespace extras {
namespace {
// Any valid encoding is larger (ensures codecs can read the first few bytes)
constexpr size_t kMinBytes = 9;
} // namespace
std::string ExtensionFromCodec(Codec codec, const bool is_gray,
const size_t bits_per_sample) {
switch (codec) {
case Codec::kJPG:
return ".jpg";
case Codec::kPGX:
return ".pgx";
case Codec::kPNG:
return ".png";
case Codec::kPNM:
if (is_gray) return ".pgm";
return (bits_per_sample == 32) ? ".pfm" : ".ppm";
case Codec::kGIF:
return ".gif";
case Codec::kEXR:
return ".exr";
case Codec::kPSD:
return ".psd";
case Codec::kUnknown:
return std::string();
}
JXL_UNREACHABLE;
return std::string();
}
Codec CodecFromExtension(std::string extension,
size_t* JXL_RESTRICT bits_per_sample) {
std::transform(
extension.begin(), extension.end(), extension.begin(),
[](char c) { return std::tolower(c, std::locale::classic()); });
if (extension == ".png") return Codec::kPNG;
if (extension == ".jpg") return Codec::kJPG;
if (extension == ".jpeg") return Codec::kJPG;
if (extension == ".pgx") return Codec::kPGX;
if (extension == ".pbm") {
if (bits_per_sample != nullptr) *bits_per_sample = 1;
return Codec::kPNM;
}
if (extension == ".pgm") return Codec::kPNM;
if (extension == ".ppm") return Codec::kPNM;
if (extension == ".pfm") {
if (bits_per_sample != nullptr) *bits_per_sample = 32;
return Codec::kPNM;
}
if (extension == ".gif") return Codec::kGIF;
if (extension == ".exr") return Codec::kEXR;
if (extension == ".psd") return Codec::kPSD;
return Codec::kUnknown;
}
Status DecodeBytes(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
extras::PackedPixelFile* ppf, ThreadPool* pool,
Codec* orig_codec) {
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
*ppf = extras::PackedPixelFile();
// Default values when not set by decoders.
ppf->info.uses_original_profile = true;
ppf->info.orientation = JXL_ORIENT_IDENTITY;
Codec codec;
#if JPEGXL_ENABLE_APNG
if (DecodeImageAPNG(bytes, color_hints, constraints, ppf)) {
codec = Codec::kPNG;
} else
#endif
if (DecodeImagePGX(bytes, color_hints, constraints, ppf)) {
codec = Codec::kPGX;
} else if (DecodeImagePNM(bytes, color_hints, constraints, ppf)) {
codec = Codec::kPNM;
}
#if JPEGXL_ENABLE_GIF
else if (DecodeImageGIF(bytes, color_hints, constraints, ppf)) {
codec = Codec::kGIF;
}
#endif
#if JPEGXL_ENABLE_JPEG
else if (DecodeImageJPG(bytes, color_hints, constraints, ppf)) {
codec = Codec::kJPG;
}
#endif
#if JPEGXL_ENABLE_EXR
else if (DecodeImageEXR(bytes, color_hints, constraints, pool, ppf)) {
codec = Codec::kEXR;
}
#endif
else {
return JXL_FAILURE("Codecs failed to decode");
}
if (orig_codec) *orig_codec = codec;
return true;
}
} // namespace extras
} // namespace jxl

74
third_party/jpeg-xl/lib/extras/dec/decode.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,74 @@
// 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_EXTRAS_DEC_DECODE_H_
#define LIB_EXTRAS_DEC_DECODE_H_
// Facade for image decoders (PNG, PNM, ...).
#include <stddef.h>
#include <stdint.h>
#include <string>
#include "lib/extras/dec/color_hints.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
#include "lib/jxl/field_encodings.h" // MakeBit
namespace jxl {
namespace extras {
// Codecs supported by CodecInOut::Encode.
enum class Codec : uint32_t {
kUnknown, // for CodecFromExtension
kPNG,
kPNM,
kPGX,
kJPG,
kGIF,
kEXR,
kPSD
};
static inline constexpr uint64_t EnumBits(Codec /*unused*/) {
// Return only fully-supported codecs (kGIF is decode-only).
return MakeBit(Codec::kPNM)
#if JPEGXL_ENABLE_APNG
| MakeBit(Codec::kPNG)
#endif
#if JPEGXL_ENABLE_JPEG
| MakeBit(Codec::kJPG)
#endif
#if JPEGXL_ENABLE_EXR
| MakeBit(Codec::kEXR)
#endif
| MakeBit(Codec::kPSD);
}
// Lower case ASCII including dot, e.g. ".png".
std::string ExtensionFromCodec(Codec codec, bool is_gray,
size_t bits_per_sample);
// If and only if extension is ".pfm", *bits_per_sample is updated to 32 so
// that Encode() would encode to PFM instead of PPM.
Codec CodecFromExtension(std::string extension,
size_t* JXL_RESTRICT bits_per_sample = nullptr);
// Decodes "bytes" and sets io->metadata.m.
// color_space_hint may specify the color space, otherwise, defaults to sRGB.
Status DecodeBytes(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints,
extras::PackedPixelFile* ppf, ThreadPool* pool = nullptr,
Codec* orig_codec = nullptr);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_DEC_DECODE_H_

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

@ -3,7 +3,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/codec_exr.h"
#include "lib/extras/dec/exr.h"
#include <ImfChromaticitiesAttribute.h>
#include <ImfIO.h>
@ -12,11 +12,6 @@
#include <vector>
#include "lib/jxl/alpha.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/enc_image_bundle.h"
namespace jxl {
namespace extras {
@ -34,38 +29,15 @@ using ExrInt64 = decltype(std::declval<OpenEXR::IStream>().tellg());
constexpr int kExrBitsPerSample = 16;
constexpr int kExrAlphaBits = 16;
float GetIntensityTarget(float target_nits, const OpenEXR::Header& exr_header) {
if (OpenEXR::hasWhiteLuminance(exr_header)) {
const float exr_luminance = OpenEXR::whiteLuminance(exr_header);
if (target_nits != 0) {
JXL_WARNING(
"overriding OpenEXR whiteLuminance of %g with user-specified value "
"of %g",
exr_luminance, target_nits);
return target_nits;
}
return exr_luminance;
}
if (target_nits != 0) {
return target_nits;
}
JXL_WARNING(
"no OpenEXR whiteLuminance tag found and no intensity_target specified, "
"defaulting to %g",
kDefaultIntensityTarget);
return kDefaultIntensityTarget;
}
size_t GetNumThreads(ThreadPool* pool) {
size_t exr_num_threads = 1;
RunOnPool(
JXL_CHECK(RunOnPool(
pool, 0, 1,
[&](size_t num_threads) {
exr_num_threads = num_threads;
return true;
},
[&](const int /* task */, const int /*thread*/) {},
"DecodeImageEXRThreads");
[&](uint32_t /* task */, size_t /*thread*/) {}, "DecodeImageEXRThreads"));
return exr_num_threads;
}
@ -99,38 +71,11 @@ class InMemoryIStream : public OpenEXR::IStream {
size_t pos_ = 0;
};
class InMemoryOStream : public OpenEXR::OStream {
public:
// `bytes` must outlive the InMemoryOStream.
explicit InMemoryOStream(PaddedBytes* const bytes)
: OStream(/*fileName=*/""), bytes_(*bytes) {}
void write(const char c[], const int n) override {
if (bytes_.size() < pos_ + n) {
bytes_.resize(pos_ + n);
}
std::copy_n(c, n, bytes_.begin() + pos_);
pos_ += n;
}
ExrInt64 tellp() override { return pos_; }
void seekp(const ExrInt64 pos) override {
if (bytes_.size() + 1 < pos) {
bytes_.resize(pos - 1);
}
pos_ = pos;
}
private:
PaddedBytes& bytes_;
size_t pos_ = 0;
};
} // namespace
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, float target_nits,
ThreadPool* pool, PackedPixelFile* ppf) {
const SizeConstraints& constraints, ThreadPool* pool,
PackedPixelFile* ppf) {
// Get the number of threads we should be using for OpenEXR.
// OpenEXR creates its own set of threads, independent from ours. `pool` is
// only used for converting from a buffer of OpenEXR::Rgba to Image3F.
@ -159,8 +104,9 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
const bool has_alpha = (input.channels() & OpenEXR::RgbaChannels::WRITE_A) ==
OpenEXR::RgbaChannels::WRITE_A;
const float intensity_target =
GetIntensityTarget(target_nits, input.header());
const float intensity_target = OpenEXR::hasWhiteLuminance(input.header())
? OpenEXR::whiteLuminance(input.header())
: kDefaultIntensityTarget;
auto image_size = input.displayWindow().size();
// Size is computed as max - min, but both bounds are inclusive.
@ -203,9 +149,9 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
input_rows.data() - input.dataWindow().min.x - start_y * row_size,
/*xStride=*/1, /*yStride=*/row_size);
input.readPixels(start_y, end_y);
RunOnPool(
pool, start_y, end_y + 1, ThreadPool::SkipInit(),
[&](const int exr_y, const int /*thread*/) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, start_y, end_y + 1, ThreadPool::NoInit,
[&](const uint32_t exr_y, size_t /* thread */) {
const int image_y = exr_y - input.displayWindow().min.y;
const OpenEXR::Rgba* const JXL_RESTRICT input_row =
&input_rows[(exr_y - start_y) * row_size];
@ -223,7 +169,7 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
input_row + (exr_x - input.dataWindow().min.x), pixel_size);
}
},
"DecodeImageEXR");
"DecodeImageEXR"));
}
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
@ -256,95 +202,5 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
return true;
}
Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
ThreadPool* pool, PaddedBytes* bytes) {
// As in `DecodeImageEXR`, `pool` is only used for pixel conversion, not for
// actual OpenEXR I/O.
OpenEXR::setGlobalThreadCount(GetNumThreads(pool));
ColorEncoding c_linear = c_desired;
c_linear.tf.SetTransferFunction(TransferFunction::kLinear);
JXL_RETURN_IF_ERROR(c_linear.CreateICC());
ImageMetadata metadata = io->metadata.m;
ImageBundle store(&metadata);
const ImageBundle* linear;
JXL_RETURN_IF_ERROR(
TransformIfNeeded(io->Main(), c_linear, pool, &store, &linear));
const bool has_alpha = io->Main().HasAlpha();
const bool alpha_is_premultiplied = io->Main().AlphaIsPremultiplied();
OpenEXR::Header header(io->xsize(), io->ysize());
const PrimariesCIExy& primaries = c_linear.HasPrimaries()
? c_linear.GetPrimaries()
: ColorEncoding::SRGB().GetPrimaries();
OpenEXR::Chromaticities chromaticities;
chromaticities.red = Imath::V2f(primaries.r.x, primaries.r.y);
chromaticities.green = Imath::V2f(primaries.g.x, primaries.g.y);
chromaticities.blue = Imath::V2f(primaries.b.x, primaries.b.y);
chromaticities.white =
Imath::V2f(c_linear.GetWhitePoint().x, c_linear.GetWhitePoint().y);
OpenEXR::addChromaticities(header, chromaticities);
OpenEXR::addWhiteLuminance(header, io->metadata.m.IntensityTarget());
// Ensure that the destructor of RgbaOutputFile has run before we look at the
// size of `bytes`.
{
InMemoryOStream os(bytes);
OpenEXR::RgbaOutputFile output(
os, header, has_alpha ? OpenEXR::WRITE_RGBA : OpenEXR::WRITE_RGB);
// How many rows to write at once. Again, the OpenEXR documentation
// recommends writing the whole image in one call.
const int y_chunk_size = io->ysize();
std::vector<OpenEXR::Rgba> output_rows(io->xsize() * y_chunk_size);
for (size_t start_y = 0; start_y < io->ysize(); start_y += y_chunk_size) {
// Inclusive.
const size_t end_y =
std::min(start_y + y_chunk_size - 1, io->ysize() - 1);
output.setFrameBuffer(output_rows.data() - start_y * io->xsize(),
/*xStride=*/1, /*yStride=*/io->xsize());
RunOnPool(
pool, start_y, end_y + 1, ThreadPool::SkipInit(),
[&](const int y, const int /*thread*/) {
const float* const JXL_RESTRICT input_rows[] = {
linear->color().ConstPlaneRow(0, y),
linear->color().ConstPlaneRow(1, y),
linear->color().ConstPlaneRow(2, y),
};
OpenEXR::Rgba* const JXL_RESTRICT row_data =
&output_rows[(y - start_y) * io->xsize()];
if (has_alpha) {
const float* const JXL_RESTRICT alpha_row =
io->Main().alpha().ConstRow(y);
if (alpha_is_premultiplied) {
for (size_t x = 0; x < io->xsize(); ++x) {
row_data[x] =
OpenEXR::Rgba(input_rows[0][x], input_rows[1][x],
input_rows[2][x], alpha_row[x]);
}
} else {
for (size_t x = 0; x < io->xsize(); ++x) {
row_data[x] = OpenEXR::Rgba(alpha_row[x] * input_rows[0][x],
alpha_row[x] * input_rows[1][x],
alpha_row[x] * input_rows[2][x],
alpha_row[x]);
}
}
} else {
for (size_t x = 0; x < io->xsize(); ++x) {
row_data[x] = OpenEXR::Rgba(input_rows[0][x], input_rows[1][x],
input_rows[2][x], 1.f);
}
}
},
"EncodeImageEXR");
output.writePixels(/*numScanLines=*/end_y - start_y + 1);
}
}
return true;
}
} // namespace extras
} // namespace jxl

30
third_party/jpeg-xl/lib/extras/dec/exr.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,30 @@
// 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_EXTRAS_DEC_EXR_H_
#define LIB_EXTRAS_DEC_EXR_H_
// Decodes OpenEXR images in memory.
#include "lib/extras/dec/color_hints.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `ppf`. color_hints are ignored.
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, ThreadPool* pool,
PackedPixelFile* ppf);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_DEC_EXR_H_

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

@ -3,24 +3,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/codec_gif.h"
#include "lib/extras/dec/gif.h"
#include <gif_lib.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/headers.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_ops.h"
#include "lib/jxl/luminance.h"
#include "lib/jxl/sanitizers.h"
namespace jxl {

30
third_party/jpeg-xl/lib/extras/dec/gif.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,30 @@
// 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_EXTRAS_DEC_GIF_H_
#define LIB_EXTRAS_DEC_GIF_H_
// Decodes GIF images in memory.
#include <stdint.h>
#include "lib/extras/dec/color_hints.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `ppf`. color_hints are ignored.
Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_DEC_GIF_H_

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

@ -0,0 +1,288 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/dec/jpg.h"
#include <jpeglib.h>
#include <setjmp.h>
#include <stdint.h>
#include <algorithm>
#include <numeric>
#include <utility>
#include <vector>
#include "lib/jxl/base/status.h"
#include "lib/jxl/sanitizers.h"
namespace jxl {
namespace extras {
namespace {
constexpr unsigned char kICCSignature[12] = {
0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00};
constexpr int kICCMarker = JPEG_APP0 + 2;
constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69,
0x66, 0x00, 0x00};
constexpr int kExifMarker = JPEG_APP0 + 1;
static inline bool IsJPG(const Span<const uint8_t> bytes) {
if (bytes.size() < 2) return false;
if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false;
return true;
}
bool MarkerIsICC(const jpeg_saved_marker_ptr marker) {
return marker->marker == kICCMarker &&
marker->data_length >= sizeof kICCSignature + 2 &&
std::equal(std::begin(kICCSignature), std::end(kICCSignature),
marker->data);
}
bool MarkerIsExif(const jpeg_saved_marker_ptr marker) {
return marker->marker == kExifMarker &&
marker->data_length >= sizeof kExifSignature + 2 &&
std::equal(std::begin(kExifSignature), std::end(kExifSignature),
marker->data);
}
Status ReadICCProfile(jpeg_decompress_struct* const cinfo,
std::vector<uint8_t>* const icc) {
constexpr size_t kICCSignatureSize = sizeof kICCSignature;
// ICC signature + uint8_t index + uint8_t max_index.
constexpr size_t kICCHeadSize = kICCSignatureSize + 2;
// Markers are 1-indexed, and we keep them that way in this vector to get a
// convenient 0 at the front for when we compute the offsets later.
std::vector<size_t> marker_lengths;
int num_markers = 0;
int seen_markers_count = 0;
bool has_num_markers = false;
for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr;
marker = marker->next) {
// marker is initialized by libjpeg, which we are not instrumenting with
// msan.
msan::UnpoisonMemory(marker, sizeof(*marker));
msan::UnpoisonMemory(marker->data, marker->data_length);
if (!MarkerIsICC(marker)) continue;
const int current_marker = marker->data[kICCSignatureSize];
if (current_marker == 0) {
return JXL_FAILURE("inconsistent JPEG ICC marker numbering");
}
const int current_num_markers = marker->data[kICCSignatureSize + 1];
if (current_marker > current_num_markers) {
return JXL_FAILURE("inconsistent JPEG ICC marker numbering");
}
if (has_num_markers) {
if (current_num_markers != num_markers) {
return JXL_FAILURE("inconsistent numbers of JPEG ICC markers");
}
} else {
num_markers = current_num_markers;
has_num_markers = true;
marker_lengths.resize(num_markers + 1);
}
size_t marker_length = marker->data_length - kICCHeadSize;
if (marker_length == 0) {
// NB: if we allow empty chunks, then the next check is incorrect.
return JXL_FAILURE("Empty ICC chunk");
}
if (marker_lengths[current_marker] != 0) {
return JXL_FAILURE("duplicate JPEG ICC marker number");
}
marker_lengths[current_marker] = marker_length;
seen_markers_count++;
}
if (marker_lengths.empty()) {
// Not an error.
return false;
}
if (seen_markers_count != num_markers) {
JXL_DASSERT(has_num_markers);
return JXL_FAILURE("Incomplete set of ICC chunks");
}
std::vector<size_t> offsets = std::move(marker_lengths);
std::partial_sum(offsets.begin(), offsets.end(), offsets.begin());
icc->resize(offsets.back());
for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr;
marker = marker->next) {
if (!MarkerIsICC(marker)) continue;
const uint8_t* first = marker->data + kICCHeadSize;
uint8_t current_marker = marker->data[kICCSignatureSize];
size_t offset = offsets[current_marker - 1];
size_t marker_length = offsets[current_marker] - offset;
std::copy_n(first, marker_length, icc->data() + offset);
}
return true;
}
void ReadExif(jpeg_decompress_struct* const cinfo,
std::vector<uint8_t>* const exif) {
constexpr size_t kExifSignatureSize = sizeof kExifSignature;
for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr;
marker = marker->next) {
// marker is initialized by libjpeg, which we are not instrumenting with
// msan.
msan::UnpoisonMemory(marker, sizeof(*marker));
msan::UnpoisonMemory(marker->data, marker->data_length);
if (!MarkerIsExif(marker)) continue;
size_t marker_length = marker->data_length - kExifSignatureSize;
exif->resize(marker_length);
std::copy_n(marker->data + kExifSignatureSize, marker_length, exif->data());
return;
}
}
void MyErrorExit(j_common_ptr cinfo) {
jmp_buf* env = static_cast<jmp_buf*>(cinfo->client_data);
(*cinfo->err->output_message)(cinfo);
jpeg_destroy_decompress(reinterpret_cast<j_decompress_ptr>(cinfo));
longjmp(*env, 1);
}
void MyOutputMessage(j_common_ptr cinfo) {
#if JXL_DEBUG_WARNING == 1
char buf[JMSG_LENGTH_MAX];
(*cinfo->err->format_message)(cinfo, buf);
JXL_WARNING("%s", buf);
#endif
}
} // namespace
Status DecodeImageJPG(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
// Don't do anything for non-JPEG files (no need to report an error)
if (!IsJPG(bytes)) return false;
// TODO(veluca): use JPEGData also for pixels?
// We need to declare all the non-trivial destructor local variables before
// the call to setjmp().
std::unique_ptr<JSAMPLE[]> row;
const auto try_catch_block = [&]() -> bool {
jpeg_decompress_struct cinfo;
// cinfo is initialized by libjpeg, which we are not instrumenting with
// msan, therefore we need to initialize cinfo here.
msan::UnpoisonMemory(&cinfo, sizeof(cinfo));
// Setup error handling in jpeg library so we can deal with broken jpegs in
// the fuzzer.
jpeg_error_mgr jerr;
jmp_buf env;
cinfo.err = jpeg_std_error(&jerr);
jerr.error_exit = &MyErrorExit;
jerr.output_message = &MyOutputMessage;
if (setjmp(env)) {
return false;
}
cinfo.client_data = static_cast<void*>(&env);
jpeg_create_decompress(&cinfo);
jpeg_mem_src(&cinfo, reinterpret_cast<const unsigned char*>(bytes.data()),
bytes.size());
jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF);
jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF);
const auto failure = [&cinfo](const char* str) -> Status {
jpeg_abort_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return JXL_FAILURE("%s", str);
};
int read_header_result = jpeg_read_header(&cinfo, TRUE);
// TODO(eustas): what about JPEG_HEADER_TABLES_ONLY?
if (read_header_result == JPEG_SUSPENDED) {
return failure("truncated JPEG input");
}
if (!VerifyDimensions(&constraints, cinfo.image_width,
cinfo.image_height)) {
return failure("image too big");
}
// Might cause CPU-zip bomb.
if (cinfo.arith_code) {
return failure("arithmetic code JPEGs are not supported");
}
int nbcomp = cinfo.num_components;
if (nbcomp != 1 && nbcomp != 3) {
return failure("unsupported number of components in JPEG");
}
if (!ReadICCProfile(&cinfo, &ppf->icc)) {
ppf->icc.clear();
// Default to SRGB
// Actually, (cinfo.output_components == nbcomp) will be checked after
// `jpeg_start_decompress`.
ppf->color_encoding.color_space =
(nbcomp == 1) ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB;
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
ppf->color_encoding.rendering_intent = JXL_RENDERING_INTENT_PERCEPTUAL;
}
ReadExif(&cinfo, &ppf->metadata.exif);
if (!ApplyColorHints(color_hints, /*color_already_set=*/true,
/*is_gray=*/false, ppf)) {
return failure("ApplyColorHints failed");
}
ppf->info.xsize = cinfo.image_width;
ppf->info.ysize = cinfo.image_height;
// Original data is uint, so exponent_bits_per_sample = 0.
ppf->info.bits_per_sample = BITS_IN_JSAMPLE;
JXL_ASSERT(BITS_IN_JSAMPLE == 8 || BITS_IN_JSAMPLE == 16);
ppf->info.exponent_bits_per_sample = 0;
ppf->info.uses_original_profile = true;
// No alpha in JPG
ppf->info.alpha_bits = 0;
ppf->info.alpha_exponent_bits = 0;
ppf->info.num_color_channels = nbcomp;
ppf->info.orientation = JXL_ORIENT_IDENTITY;
jpeg_start_decompress(&cinfo);
JXL_ASSERT(cinfo.output_components == nbcomp);
const JxlPixelFormat format{
/*num_channels=*/static_cast<uint32_t>(nbcomp),
/*data_type=*/BITS_IN_JSAMPLE == 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16,
/*endianness=*/JXL_NATIVE_ENDIAN,
/*align=*/0,
};
ppf->frames.clear();
// Allocates the frame buffer.
ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format);
const auto& frame = ppf->frames.back();
JXL_ASSERT(sizeof(JSAMPLE) * cinfo.output_components * cinfo.image_width <=
frame.color.stride);
for (size_t y = 0; y < cinfo.image_height; ++y) {
JSAMPROW rows[] = {reinterpret_cast<JSAMPLE*>(
static_cast<uint8_t*>(frame.color.pixels()) +
frame.color.stride * y)};
jpeg_read_scanlines(&cinfo, rows, 1);
msan::UnpoisonMemory(rows[0], sizeof(JSAMPLE) * cinfo.output_components *
cinfo.image_width);
}
jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return true;
};
return try_catch_block();
}
} // namespace extras
} // namespace jxl

33
third_party/jpeg-xl/lib/extras/dec/jpg.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,33 @@
// 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_EXTRAS_DEC_JPG_H_
#define LIB_EXTRAS_DEC_JPG_H_
// Decodes JPG pixels and metadata in memory.
#include <stdint.h>
#include "lib/extras/codec.h"
#include "lib/extras/dec/color_hints.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `ppf`. color_hints are ignored.
// `elapsed_deinterleave`, if non-null, will be set to the time (in seconds)
// that it took to deinterleave the raw JSAMPLEs to planar floats.
Status DecodeImageJPG(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_DEC_JPG_H_

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

@ -3,30 +3,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/codec_pgx.h"
#include "lib/extras/dec/pgx.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "lib/jxl/base/bits.h"
#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"
#include "lib/jxl/enc_image_bundle.h"
#include "lib/jxl/fields.h" // AllDefault
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/luminance.h"
namespace jxl {
namespace extras {
@ -160,28 +142,6 @@ class Parser {
const uint8_t* const end_;
};
constexpr size_t kMaxHeaderSize = 200;
Status EncodeHeader(const ImageBundle& ib, const size_t bits_per_sample,
char* header, int* JXL_RESTRICT chars_written) {
if (ib.HasAlpha()) return JXL_FAILURE("PGX: can't store alpha");
if (!ib.IsGray()) return JXL_FAILURE("PGX: must be grayscale");
// TODO(lode): verify other bit depths: for other bit depths such as 1 or 4
// bits, have a test case to verify it works correctly. For bits > 16, we may
// need to change the way external_image works.
if (bits_per_sample != 8 && bits_per_sample != 16) {
return JXL_FAILURE("PGX: bits other than 8 or 16 not yet supported");
}
// Use ML (Big Endian), LM may not be well supported by all decoders.
*chars_written = snprintf(header, kMaxHeaderSize,
"PG ML + %" PRIuS " %" PRIuS " %" PRIuS "\n",
bits_per_sample, ib.xsize(), ib.ysize());
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
return true;
}
} // namespace
Status DecodeImagePGX(const Span<const uint8_t> bytes,
@ -240,45 +200,5 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes,
return true;
}
Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes) {
if (!Bundle::AllDefault(io->metadata.m)) {
JXL_WARNING("PGX encoder ignoring metadata - use a different codec");
}
if (!c_desired.IsSRGB()) {
JXL_WARNING(
"PGX encoder cannot store custom ICC profile; decoder\n"
"will need hint key=color_space to get the same values");
}
ImageBundle ib = io->Main().Copy();
ImageMetadata metadata = io->metadata.m;
ImageBundle store(&metadata);
const ImageBundle* transformed;
JXL_RETURN_IF_ERROR(
TransformIfNeeded(ib, c_desired, pool, &store, &transformed));
PaddedBytes pixels(ib.xsize() * ib.ysize() *
(bits_per_sample / kBitsPerByte));
size_t stride = ib.xsize() * (bits_per_sample / kBitsPerByte);
JXL_RETURN_IF_ERROR(
ConvertToExternal(*transformed, bits_per_sample,
/*float_out=*/false,
/*num_channels=*/1, JXL_BIG_ENDIAN, stride, pool,
pixels.data(), pixels.size(), /*out_callback=*/nullptr,
/*out_opaque=*/nullptr, metadata.GetOrientation()));
char header[kMaxHeaderSize];
int header_size = 0;
JXL_RETURN_IF_ERROR(EncodeHeader(ib, bits_per_sample, header, &header_size));
bytes->resize(static_cast<size_t>(header_size) + pixels.size());
memcpy(bytes->data(), header, static_cast<size_t>(header_size));
memcpy(bytes->data() + header_size, pixels.data(), pixels.size());
return true;
}
} // namespace extras
} // namespace jxl

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

@ -3,14 +3,15 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_EXTRAS_CODEC_GIF_H_
#define LIB_EXTRAS_CODEC_GIF_H_
#ifndef LIB_EXTRAS_DEC_PGX_H_
#define LIB_EXTRAS_DEC_PGX_H_
// Decodes GIF images in memory.
// Decodes PGX pixels in memory.
#include <stddef.h>
#include <stdint.h>
#include "lib/extras/color_hints.h"
#include "lib/extras/dec/color_hints.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
@ -21,12 +22,11 @@
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. color_hints are ignored.
Status DecodeImageGIF(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
// Decodes `bytes` into `ppf`.
Status DecodeImagePGX(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_GIF_H_
#endif // LIB_EXTRAS_DEC_PGX_H_

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

@ -3,9 +3,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/codec_pgx.h"
#include <stddef.h>
#include "lib/extras/dec/pgx.h"
#include "gtest/gtest.h"
#include "lib/extras/packed_image_convert.h"

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

@ -3,32 +3,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/codec_pnm.h"
#include "lib/extras/dec/pnm.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include "lib/jxl/base/bits.h"
#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"
#include "lib/jxl/enc_external_image.h"
#include "lib/jxl/enc_image_bundle.h"
#include "lib/jxl/fields.h" // AllDefault
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_ops.h"
#include "lib/jxl/luminance.h"
namespace jxl {
namespace extras {
@ -248,64 +230,11 @@ class Parser {
const uint8_t* const end_;
};
constexpr size_t kMaxHeaderSize = 200;
Status EncodeHeader(const ImageBundle& ib, const size_t bits_per_sample,
const bool little_endian, char* header,
int* JXL_RESTRICT chars_written) {
if (ib.HasAlpha()) return JXL_FAILURE("PNM: can't store alpha");
if (bits_per_sample == 32) { // PFM
const char type = ib.IsGray() ? 'f' : 'F';
const double scale = little_endian ? -1.0 : 1.0;
*chars_written =
snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%.1f\n",
type, ib.oriented_xsize(), ib.oriented_ysize(), scale);
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
} else if (bits_per_sample == 1) { // PBM
if (!ib.IsGray()) {
return JXL_FAILURE("Cannot encode color as PBM");
}
*chars_written =
snprintf(header, kMaxHeaderSize, "P4\n%" PRIuS " %" PRIuS "\n",
ib.oriented_xsize(), ib.oriented_ysize());
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
} else { // PGM/PPM
const uint32_t max_val = (1U << bits_per_sample) - 1;
if (max_val >= 65536) return JXL_FAILURE("PNM cannot have > 16 bits");
const char type = ib.IsGray() ? '5' : '6';
*chars_written =
snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%u\n",
type, ib.oriented_xsize(), ib.oriented_ysize(), max_val);
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
}
return true;
}
Span<const uint8_t> MakeSpan(const char* str) {
return Span<const uint8_t>(reinterpret_cast<const uint8_t*>(str),
strlen(str));
}
// Flip the image vertically for loading/saving PFM files which have the
// scanlines inverted.
void VerticallyFlipImage(Image3F* const image) {
for (int c = 0; c < 3; c++) {
for (size_t y = 0; y < image->ysize() / 2; y++) {
float* first_row = image->PlaneRow(c, y);
float* other_row = image->PlaneRow(c, image->ysize() - y - 1);
for (size_t x = 0; x < image->xsize(); ++x) {
float tmp = first_row[x];
first_row[x] = other_row[x];
other_row[x] = tmp;
}
}
}
}
} // namespace
Status DecodeImagePNM(const Span<const uint8_t> bytes,
@ -378,63 +307,6 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes,
return true;
}
Status EncodeImagePNM(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes) {
const bool floating_point = bits_per_sample > 16;
// Choose native for PFM; PGM/PPM require big-endian (N/A for PBM)
const JxlEndianness endianness =
floating_point ? JXL_NATIVE_ENDIAN : JXL_BIG_ENDIAN;
ImageMetadata metadata_copy = io->metadata.m;
// AllDefault sets all_default, which can cause a race condition.
if (!Bundle::AllDefault(metadata_copy)) {
JXL_WARNING("PNM encoder ignoring metadata - use a different codec");
}
if (!c_desired.IsSRGB()) {
JXL_WARNING(
"PNM encoder cannot store custom ICC profile; decoder\n"
"will need hint key=color_space to get the same values");
}
ImageBundle ib = io->Main().Copy();
// In case of PFM the image must be flipped upside down since that format
// is designed that way.
const ImageBundle* to_color_transform = &ib;
ImageBundle flipped;
if (floating_point) {
flipped = ib.Copy();
VerticallyFlipImage(flipped.color());
to_color_transform = &flipped;
}
ImageMetadata metadata = io->metadata.m;
ImageBundle store(&metadata);
const ImageBundle* transformed;
JXL_RETURN_IF_ERROR(TransformIfNeeded(*to_color_transform, c_desired, pool,
&store, &transformed));
size_t stride = ib.oriented_xsize() *
(c_desired.Channels() * bits_per_sample) / kBitsPerByte;
PaddedBytes pixels(stride * ib.oriented_ysize());
JXL_RETURN_IF_ERROR(ConvertToExternal(
*transformed, bits_per_sample, floating_point, c_desired.Channels(),
endianness, stride, pool, pixels.data(), pixels.size(),
/*out_callback=*/nullptr, /*out_opaque=*/nullptr,
metadata.GetOrientation()));
char header[kMaxHeaderSize];
int header_size = 0;
bool is_little_endian = endianness == JXL_LITTLE_ENDIAN ||
(endianness == JXL_NATIVE_ENDIAN && IsLittleEndian());
JXL_RETURN_IF_ERROR(EncodeHeader(*transformed, bits_per_sample,
is_little_endian, header, &header_size));
bytes->resize(static_cast<size_t>(header_size) + pixels.size());
memcpy(bytes->data(), header, static_cast<size_t>(header_size));
memcpy(bytes->data() + header_size, pixels.data(), pixels.size());
return true;
}
void TestCodecPNM() {
size_t u = 77777; // Initialized to wrong value.
double d = 77.77;

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

@ -3,10 +3,10 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_EXTRAS_CODEC_PNG_H_
#define LIB_EXTRAS_CODEC_PNG_H_
#ifndef LIB_EXTRAS_DEC_PNM_H_
#define LIB_EXTRAS_DEC_PNM_H_
// Encodes/decodes PNG pixels and metadata in memory.
// Decodes PBM/PGM/PPM/PFM pixels in memory.
#include <stddef.h>
#include <stdint.h>
@ -14,29 +14,25 @@
// TODO(janwas): workaround for incorrect Win64 codegen (cause unknown)
#include <hwy/highway.h>
#include "lib/extras/color_hints.h"
#include "lib/extras/dec/color_hints.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
#include "lib/jxl/color_encoding_internal.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `ppf`.
Status DecodeImagePNG(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
// Decodes `bytes` into `ppf`. color_hints may specify "color_space", which
// defaults to sRGB.
Status DecodeImagePNM(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
// Transforms from io->c_current to `c_desired` and encodes into `bytes`.
Status EncodeImagePNG(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes);
void TestCodecPNM();
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_PNG_H_
#endif // LIB_EXTRAS_DEC_PNM_H_

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

@ -0,0 +1,270 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/enc/apng.h"
// Parts of this code are taken from apngdis, which has the following license:
/* APNG Disassembler 2.8
*
* Deconstructs APNG files into individual frames.
*
* http://apngdis.sourceforge.net
*
* Copyright (c) 2010-2015 Max Stepin
* maxst at users.sourceforge.net
*
* zlib license
* ------------
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
*
*/
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>
#include "jxl/encode.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/dec_external_image.h"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/enc_image_bundle.h"
#include "lib/jxl/frame_header.h"
#include "lib/jxl/headers.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "png.h" /* original (unpatched) libpng is ok */
namespace jxl {
namespace extras {
namespace {
static void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) {
PaddedBytes* bytes = static_cast<PaddedBytes*>(png_get_io_ptr(png_ptr));
bytes->append(data, data + length);
}
// Stores XMP and EXIF/IPTC into key/value strings for PNG
class BlobsWriterPNG {
public:
static Status Encode(const Blobs& blobs, std::vector<std::string>* strings) {
if (!blobs.exif.empty()) {
JXL_RETURN_IF_ERROR(EncodeBase16("exif", blobs.exif, strings));
}
if (!blobs.iptc.empty()) {
JXL_RETURN_IF_ERROR(EncodeBase16("iptc", blobs.iptc, strings));
}
if (!blobs.xmp.empty()) {
JXL_RETURN_IF_ERROR(EncodeBase16("xmp", blobs.xmp, strings));
}
return true;
}
private:
static JXL_INLINE char EncodeNibble(const uint8_t nibble) {
JXL_ASSERT(nibble < 16);
return (nibble < 10) ? '0' + nibble : 'a' + nibble - 10;
}
static Status EncodeBase16(const std::string& type, const PaddedBytes& bytes,
std::vector<std::string>* strings) {
// Encoding: base16 with newline after 72 chars.
const size_t base16_size =
2 * bytes.size() + DivCeil(bytes.size(), size_t(36)) + 1;
std::string base16;
base16.reserve(base16_size);
for (size_t i = 0; i < bytes.size(); ++i) {
if (i % 36 == 0) base16.push_back('\n');
base16.push_back(EncodeNibble(bytes[i] >> 4));
base16.push_back(EncodeNibble(bytes[i] & 0x0F));
}
base16.push_back('\n');
JXL_ASSERT(base16.length() == base16_size);
char key[30];
snprintf(key, sizeof(key), "Raw profile type %s", type.c_str());
char header[30];
snprintf(header, sizeof(header), "\n%s\n%8" PRIuS, type.c_str(),
bytes.size());
strings->push_back(std::string(key));
strings->push_back(std::string(header) + base16);
return true;
}
};
} // namespace
Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes) {
if (bits_per_sample > 8) {
bits_per_sample = 16;
} else if (bits_per_sample < 8) {
// PNG can also do 4, 2, and 1 bits per sample, but it isn't implemented
bits_per_sample = 8;
}
size_t count = 0;
bool have_anim = io->metadata.m.have_animation;
size_t anim_chunks = 0;
int W = 0, H = 0;
for (auto& frame : io->frames) {
png_structp png_ptr;
png_infop info_ptr;
png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr) return JXL_FAILURE("Could not init png encoder");
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr) return JXL_FAILURE("Could not init png info struct");
png_set_write_fn(png_ptr, bytes, PngWrite, NULL);
png_set_flush(png_ptr, 0);
ImageBundle ib = frame.Copy();
const size_t alpha_bits = ib.HasAlpha() ? bits_per_sample : 0;
ImageMetadata metadata = io->metadata.m;
ImageBundle store(&metadata);
const ImageBundle* transformed;
JXL_RETURN_IF_ERROR(TransformIfNeeded(ib, c_desired, GetJxlCms(), pool,
&store, &transformed));
size_t stride = ib.oriented_xsize() *
DivCeil(c_desired.Channels() * bits_per_sample + alpha_bits,
kBitsPerByte);
PaddedBytes raw_bytes(stride * ib.oriented_ysize());
JXL_RETURN_IF_ERROR(ConvertToExternal(
*transformed, bits_per_sample, /*float_out=*/false,
c_desired.Channels() + (ib.HasAlpha() ? 1 : 0), JXL_BIG_ENDIAN, stride,
pool, raw_bytes.data(), raw_bytes.size(), /*out_callback=*/nullptr,
/*out_opaque=*/nullptr, metadata.GetOrientation()));
int width = ib.oriented_xsize();
int height = ib.oriented_ysize();
png_byte color_type =
(c_desired.Channels() == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY);
if (ib.HasAlpha()) color_type |= PNG_COLOR_MASK_ALPHA;
png_byte bit_depth = bits_per_sample;
png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
PNG_FILTER_TYPE_BASE);
if (count == 0) {
W = width;
H = height;
// TODO(jon): instead of always setting an iCCP, could try to avoid that
// have to avoid warnings on the ICC profile becoming fatal
png_set_benign_errors(png_ptr, 1);
png_set_iCCP(png_ptr, info_ptr, "1", 0, c_desired.ICC().data(),
c_desired.ICC().size());
std::vector<std::string> textstrings;
JXL_RETURN_IF_ERROR(BlobsWriterPNG::Encode(io->blobs, &textstrings));
for (size_t i = 0; i + 1 < textstrings.size(); i += 2) {
png_text text;
text.key = const_cast<png_charp>(textstrings[i].c_str());
text.text = const_cast<png_charp>(textstrings[i + 1].c_str());
text.compression = PNG_TEXT_COMPRESSION_zTXt;
png_set_text(png_ptr, info_ptr, &text, 1);
}
png_write_info(png_ptr, info_ptr);
} else {
// fake writing a header, otherwise libpng gets confused
size_t pos = bytes->size();
png_write_info(png_ptr, info_ptr);
bytes->resize(pos);
}
if (have_anim) {
if (count == 0) {
png_byte adata[8];
png_save_uint_32(adata, io->frames.size());
png_save_uint_32(adata + 4, io->metadata.m.animation.num_loops);
png_byte actl[5] = "acTL";
png_write_chunk(png_ptr, actl, adata, 8);
}
png_byte fdata[26];
JXL_ASSERT(W == width);
JXL_ASSERT(H == height);
// TODO(jon): also make this work for the non-coalesced case
png_save_uint_32(fdata, anim_chunks++);
png_save_uint_32(fdata + 4, width);
png_save_uint_32(fdata + 8, height);
png_save_uint_32(fdata + 12, 0);
png_save_uint_32(fdata + 16, 0);
png_save_uint_16(
fdata + 20,
frame.duration * io->metadata.m.animation.tps_denominator);
png_save_uint_16(fdata + 22, io->metadata.m.animation.tps_numerator);
fdata[24] = 1;
fdata[25] = 0;
png_byte fctl[5] = "fcTL";
png_write_chunk(png_ptr, fctl, fdata, 26);
}
std::vector<uint8_t*> rows(height);
for (int y = 0; y < height; ++y) {
rows[y] = raw_bytes.data() + y * stride;
}
png_write_flush(png_ptr);
const size_t pos = bytes->size();
png_write_image(png_ptr, &rows[0]);
png_write_flush(png_ptr);
if (count > 0) {
PaddedBytes fdata(4);
png_save_uint_32(fdata.data(), anim_chunks++);
size_t p = pos;
while (p + 8 < bytes->size()) {
size_t len = png_get_uint_32(bytes->data() + p);
JXL_ASSERT(bytes->operator[](p + 4) == 'I');
JXL_ASSERT(bytes->operator[](p + 5) == 'D');
JXL_ASSERT(bytes->operator[](p + 6) == 'A');
JXL_ASSERT(bytes->operator[](p + 7) == 'T');
fdata.append(bytes->data() + p + 8, bytes->data() + p + 8 + len);
p += len + 12;
}
bytes->resize(pos);
png_byte fdat[5] = "fdAT";
png_write_chunk(png_ptr, fdat, fdata.data(), fdata.size());
}
count++;
if (count == io->frames.size()) png_write_end(png_ptr, NULL);
png_destroy_write_struct(&png_ptr, &info_ptr);
}
return true;
}
} // namespace extras
} // namespace jxl

28
third_party/jpeg-xl/lib/extras/enc/apng.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,28 @@
// 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_EXTRAS_ENC_APNG_H_
#define LIB_EXTRAS_ENC_APNG_H_
// Encodes APNG images in memory.
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
namespace extras {
// Encodes `io` into `bytes`.
Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_ENC_APNG_H_

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

@ -0,0 +1,167 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/enc/exr.h"
#include <ImfChromaticitiesAttribute.h>
#include <ImfIO.h>
#include <ImfRgbaFile.h>
#include <ImfStandardAttributes.h>
#include <vector>
#include "lib/jxl/alpha.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/enc_image_bundle.h"
namespace jxl {
namespace extras {
namespace {
namespace OpenEXR = OPENEXR_IMF_NAMESPACE;
namespace Imath = IMATH_NAMESPACE;
// OpenEXR::Int64 is deprecated in favor of using uint64_t directly, but using
// uint64_t as recommended causes build failures with previous OpenEXR versions
// on macOS, where the definition for OpenEXR::Int64 was actually not equivalent
// to uint64_t. This alternative should work in all cases.
using ExrInt64 = decltype(std::declval<OpenEXR::IStream>().tellg());
size_t GetNumThreads(ThreadPool* pool) {
size_t exr_num_threads = 1;
JXL_CHECK(RunOnPool(
pool, 0, 1,
[&](size_t num_threads) {
exr_num_threads = num_threads;
return true;
},
[&](uint32_t /* task */, size_t /*thread*/) {}, "DecodeImageEXRThreads"));
return exr_num_threads;
}
class InMemoryOStream : public OpenEXR::OStream {
public:
// `bytes` must outlive the InMemoryOStream.
explicit InMemoryOStream(PaddedBytes* const bytes)
: OStream(/*fileName=*/""), bytes_(*bytes) {}
void write(const char c[], const int n) override {
if (bytes_.size() < pos_ + n) {
bytes_.resize(pos_ + n);
}
std::copy_n(c, n, bytes_.begin() + pos_);
pos_ += n;
}
ExrInt64 tellp() override { return pos_; }
void seekp(const ExrInt64 pos) override {
if (bytes_.size() + 1 < pos) {
bytes_.resize(pos - 1);
}
pos_ = pos;
}
private:
PaddedBytes& bytes_;
size_t pos_ = 0;
};
} // namespace
Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
ThreadPool* pool, PaddedBytes* bytes) {
// As in `DecodeImageEXR`, `pool` is only used for pixel conversion, not for
// actual OpenEXR I/O.
OpenEXR::setGlobalThreadCount(GetNumThreads(pool));
ColorEncoding c_linear = c_desired;
c_linear.tf.SetTransferFunction(TransferFunction::kLinear);
JXL_RETURN_IF_ERROR(c_linear.CreateICC());
ImageMetadata metadata = io->metadata.m;
ImageBundle store(&metadata);
const ImageBundle* linear;
JXL_RETURN_IF_ERROR(TransformIfNeeded(io->Main(), c_linear, GetJxlCms(), pool,
&store, &linear));
const bool has_alpha = io->Main().HasAlpha();
const bool alpha_is_premultiplied = io->Main().AlphaIsPremultiplied();
OpenEXR::Header header(io->xsize(), io->ysize());
const PrimariesCIExy& primaries = c_linear.HasPrimaries()
? c_linear.GetPrimaries()
: ColorEncoding::SRGB().GetPrimaries();
OpenEXR::Chromaticities chromaticities;
chromaticities.red = Imath::V2f(primaries.r.x, primaries.r.y);
chromaticities.green = Imath::V2f(primaries.g.x, primaries.g.y);
chromaticities.blue = Imath::V2f(primaries.b.x, primaries.b.y);
chromaticities.white =
Imath::V2f(c_linear.GetWhitePoint().x, c_linear.GetWhitePoint().y);
OpenEXR::addChromaticities(header, chromaticities);
OpenEXR::addWhiteLuminance(header, io->metadata.m.IntensityTarget());
// Ensure that the destructor of RgbaOutputFile has run before we look at the
// size of `bytes`.
{
InMemoryOStream os(bytes);
OpenEXR::RgbaOutputFile output(
os, header, has_alpha ? OpenEXR::WRITE_RGBA : OpenEXR::WRITE_RGB);
// How many rows to write at once. Again, the OpenEXR documentation
// recommends writing the whole image in one call.
const int y_chunk_size = io->ysize();
std::vector<OpenEXR::Rgba> output_rows(io->xsize() * y_chunk_size);
for (size_t start_y = 0; start_y < io->ysize(); start_y += y_chunk_size) {
// Inclusive.
const size_t end_y =
std::min(start_y + y_chunk_size - 1, io->ysize() - 1);
output.setFrameBuffer(output_rows.data() - start_y * io->xsize(),
/*xStride=*/1, /*yStride=*/io->xsize());
JXL_RETURN_IF_ERROR(RunOnPool(
pool, start_y, end_y + 1, ThreadPool::NoInit,
[&](const uint32_t y, size_t /* thread */) {
const float* const JXL_RESTRICT input_rows[] = {
linear->color().ConstPlaneRow(0, y),
linear->color().ConstPlaneRow(1, y),
linear->color().ConstPlaneRow(2, y),
};
OpenEXR::Rgba* const JXL_RESTRICT row_data =
&output_rows[(y - start_y) * io->xsize()];
if (has_alpha) {
const float* const JXL_RESTRICT alpha_row =
io->Main().alpha().ConstRow(y);
if (alpha_is_premultiplied) {
for (size_t x = 0; x < io->xsize(); ++x) {
row_data[x] =
OpenEXR::Rgba(input_rows[0][x], input_rows[1][x],
input_rows[2][x], alpha_row[x]);
}
} else {
for (size_t x = 0; x < io->xsize(); ++x) {
row_data[x] = OpenEXR::Rgba(alpha_row[x] * input_rows[0][x],
alpha_row[x] * input_rows[1][x],
alpha_row[x] * input_rows[2][x],
alpha_row[x]);
}
}
} else {
for (size_t x = 0; x < io->xsize(); ++x) {
row_data[x] = OpenEXR::Rgba(input_rows[0][x], input_rows[1][x],
input_rows[2][x], 1.f);
}
}
},
"EncodeImageEXR"));
output.writePixels(/*numScanLines=*/end_y - start_y + 1);
}
}
return true;
}
} // namespace extras
} // namespace jxl

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

@ -3,12 +3,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_EXTRAS_CODEC_EXR_H_
#define LIB_EXTRAS_CODEC_EXR_H_
#ifndef LIB_EXTRAS_ENC_EXR_H_
#define LIB_EXTRAS_ENC_EXR_H_
// Encodes OpenEXR images in memory.
#include "lib/extras/color_hints.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
@ -20,11 +19,6 @@
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. color_hints are ignored.
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, float target_nits,
ThreadPool* pool, PackedPixelFile* ppf);
// Transforms from io->c_current to `c_desired` (with the transfer function set
// to linear as that is the OpenEXR convention) and encodes into `bytes`.
Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
@ -33,4 +27,4 @@ Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_EXR_H_
#endif // LIB_EXTRAS_ENC_EXR_H_

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

@ -3,17 +3,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/codec_jpg.h"
#include "lib/extras/enc/jpg.h"
#include <stddef.h>
#include <stdio.h>
#if JPEGXL_ENABLE_JPEG
// After stddef/stdio
#include <jpeglib.h>
#include <setjmp.h>
#include <stdint.h>
#endif // JPEGXL_ENABLE_JPEG
#include <algorithm>
#include <iterator>
@ -21,20 +15,14 @@
#include <utility>
#include <vector>
#include "lib/extras/time.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/common.h"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/enc_image_bundle.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_ops.h"
#include "lib/jxl/jpeg/dec_jpeg_data_writer.h"
#include "lib/jxl/jpeg/enc_jpeg_data.h"
#include "lib/jxl/jpeg/enc_jpeg_data_reader.h"
#include "lib/jxl/luminance.h"
#include "lib/jxl/sanitizers.h"
#if JPEGXL_ENABLE_SJPEG
#include "sjpeg.h"
@ -43,7 +31,6 @@
namespace jxl {
namespace extras {
#if JPEGXL_ENABLE_JPEG
namespace {
constexpr float kJPEGSampleMultiplier = MAXJSAMPLE;
@ -59,6 +46,12 @@ constexpr int kExifMarker = JPEG_APP0 + 1;
constexpr float kJPEGSampleMin = 0;
constexpr float kJPEGSampleMax = MAXJSAMPLE;
static inline bool IsJPG(const Span<const uint8_t> bytes) {
if (bytes.size() < 2) return false;
if (bytes[0] != 0xFF || bytes[1] != 0xD8) return false;
return true;
}
bool MarkerIsICC(const jpeg_saved_marker_ptr marker) {
return marker->marker == kICCMarker &&
marker->data_length >= sizeof kICCSignature + 2 &&
@ -236,17 +229,6 @@ void MyOutputMessage(j_common_ptr cinfo) {
}
} // namespace
#endif // JPEGXL_ENABLE_JPEG
Status DecodeImageJPGCoefficients(Span<const uint8_t> bytes, CodecInOut* io) {
if (!IsJPG(bytes)) return false;
// Use brunsli JPEG decoder to read quantized coefficients.
if (!jpeg::DecodeImageJPG(bytes, io)) {
fprintf(stderr, "Corrupt or CMYK JPEG.\n");
return false;
}
return true;
}
Status DecodeImageJPG(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
@ -255,7 +237,6 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
// Don't do anything for non-JPEG files (no need to report an error)
if (!IsJPG(bytes)) return false;
#if JPEGXL_ENABLE_JPEG
// TODO(veluca): use JPEGData also for pixels?
// We need to declare all the non-trivial destructor local variables before
@ -287,12 +268,16 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
bytes.size());
jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF);
jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF);
jpeg_read_header(&cinfo, TRUE);
const auto failure = [&cinfo](const char* str) -> Status {
jpeg_abort_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return JXL_FAILURE("%s", str);
};
int read_header_result = jpeg_read_header(&cinfo, TRUE);
// TODO(eustas): what about JPEG_HEADER_TABLES_ONLY?
if (read_header_result == JPEG_SUSPENDED) {
return failure("truncated JPEG input");
}
if (!VerifyDimensions(&constraints, cinfo.image_width,
cinfo.image_height)) {
return failure("image too big");
@ -369,12 +354,8 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
};
return try_catch_block();
#else // JPEGXL_ENABLE_JPEG
return JXL_FAILURE("JPEG decoding not enabled at build time.");
#endif // JPEGXL_ENABLE_JPEG
}
#if JPEGXL_ENABLE_JPEG
Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
size_t quality,
const YCbCrChromaSubsampling& chroma_subsampling,
@ -483,15 +464,6 @@ Status EncodeWithSJpeg(const ImageBundle* ib, size_t quality,
return true;
#endif
}
#endif // JPEGXL_ENABLE_JPEG
Status EncodeImageJPGCoefficients(const CodecInOut* io, PaddedBytes* bytes) {
auto write = [&bytes](const uint8_t* buf, size_t len) {
bytes->append(buf, buf + len);
return len;
};
return jpeg::WriteJpeg(*io->Main().jpeg_data, write);
}
Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
YCbCrChromaSubsampling chroma_subsampling,
@ -503,12 +475,12 @@ Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
return JXL_FAILURE("please specify a 0-100 JPEG quality");
}
#if JPEGXL_ENABLE_JPEG
const ImageBundle* ib;
ImageMetadata metadata = io->metadata.m;
ImageBundle ib_store(&metadata);
JXL_RETURN_IF_ERROR(TransformIfNeeded(
io->Main(), io->metadata.m.color_encoding, pool, &ib_store, &ib));
JXL_RETURN_IF_ERROR(TransformIfNeeded(io->Main(),
io->metadata.m.color_encoding,
GetJxlCms(), pool, &ib_store, &ib));
switch (encoder) {
case JpegEncoder::kLibJpeg:
@ -524,9 +496,6 @@ Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
}
return true;
#else // JPEGXL_ENABLE_JPEG
return JXL_FAILURE("JPEG pixel encoding not enabled at build time");
#endif // JPEGXL_ENABLE_JPEG
}
} // namespace extras

36
third_party/jpeg-xl/lib/extras/enc/jpg.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,36 @@
// 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_EXTRAS_ENC_JPG_H_
#define LIB_EXTRAS_ENC_JPG_H_
// Encodes JPG pixels and metadata in memory.
#include <stdint.h>
#include "lib/extras/codec.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
namespace extras {
enum class JpegEncoder {
kLibJpeg,
kSJpeg,
};
// Encodes into `bytes`.
Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
YCbCrChromaSubsampling chroma_subsampling,
ThreadPool* pool, PaddedBytes* bytes);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_ENC_JPG_H_

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

@ -0,0 +1,93 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/enc/pgx.h"
#include <stdio.h>
#include <string.h>
#include "lib/jxl/base/bits.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_color_management.h"
#include "lib/jxl/enc_external_image.h"
#include "lib/jxl/enc_image_bundle.h"
#include "lib/jxl/fields.h" // AllDefault
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
namespace jxl {
namespace extras {
namespace {
constexpr size_t kMaxHeaderSize = 200;
Status EncodeHeader(const ImageBundle& ib, const size_t bits_per_sample,
char* header, int* JXL_RESTRICT chars_written) {
if (ib.HasAlpha()) return JXL_FAILURE("PGX: can't store alpha");
if (!ib.IsGray()) return JXL_FAILURE("PGX: must be grayscale");
// TODO(lode): verify other bit depths: for other bit depths such as 1 or 4
// bits, have a test case to verify it works correctly. For bits > 16, we may
// need to change the way external_image works.
if (bits_per_sample != 8 && bits_per_sample != 16) {
return JXL_FAILURE("PGX: bits other than 8 or 16 not yet supported");
}
// Use ML (Big Endian), LM may not be well supported by all decoders.
*chars_written = snprintf(header, kMaxHeaderSize,
"PG ML + %" PRIuS " %" PRIuS " %" PRIuS "\n",
bits_per_sample, ib.xsize(), ib.ysize());
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
return true;
}
} // namespace
Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes) {
if (!Bundle::AllDefault(io->metadata.m)) {
JXL_WARNING("PGX encoder ignoring metadata - use a different codec");
}
if (!c_desired.IsSRGB()) {
JXL_WARNING(
"PGX encoder cannot store custom ICC profile; decoder\n"
"will need hint key=color_space to get the same values");
}
ImageBundle ib = io->Main().Copy();
ImageMetadata metadata = io->metadata.m;
ImageBundle store(&metadata);
const ImageBundle* transformed;
JXL_RETURN_IF_ERROR(TransformIfNeeded(ib, c_desired, GetJxlCms(), pool,
&store, &transformed));
PaddedBytes pixels(ib.xsize() * ib.ysize() *
(bits_per_sample / kBitsPerByte));
size_t stride = ib.xsize() * (bits_per_sample / kBitsPerByte);
JXL_RETURN_IF_ERROR(
ConvertToExternal(*transformed, bits_per_sample,
/*float_out=*/false,
/*num_channels=*/1, JXL_BIG_ENDIAN, stride, pool,
pixels.data(), pixels.size(), /*out_callback=*/nullptr,
/*out_opaque=*/nullptr, metadata.GetOrientation()));
char header[kMaxHeaderSize];
int header_size = 0;
JXL_RETURN_IF_ERROR(EncodeHeader(ib, bits_per_sample, header, &header_size));
bytes->resize(static_cast<size_t>(header_size) + pixels.size());
memcpy(bytes->data(), header, static_cast<size_t>(header_size));
memcpy(bytes->data() + header_size, pixels.data(), pixels.size());
return true;
}
} // namespace extras
} // namespace jxl

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

@ -3,15 +3,14 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_EXTRAS_CODEC_PGX_H_
#define LIB_EXTRAS_CODEC_PGX_H_
#ifndef LIB_EXTRAS_ENC_PGX_H_
#define LIB_EXTRAS_ENC_PGX_H_
// Encodes/decodes PGX pixels in memory.
// Encodes PGX pixels in memory.
#include <stddef.h>
#include <stdint.h>
#include "lib/extras/color_hints.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
@ -23,11 +22,6 @@
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`.
Status DecodeImagePGX(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
// Transforms from io->c_current to `c_desired` and encodes into `bytes`.
Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
@ -36,4 +30,4 @@ Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_PGX_H_
#endif // LIB_EXTRAS_ENC_PGX_H_

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

@ -0,0 +1,149 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/enc/pnm.h"
#include <stdio.h>
#include <string.h>
#include <string>
#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"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/enc_external_image.h"
#include "lib/jxl/enc_image_bundle.h"
#include "lib/jxl/fields.h" // AllDefault
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
namespace jxl {
namespace extras {
namespace {
constexpr size_t kMaxHeaderSize = 200;
Status EncodeHeader(const ImageBundle& ib, const size_t bits_per_sample,
const bool little_endian, char* header,
int* JXL_RESTRICT chars_written) {
if (ib.HasAlpha()) return JXL_FAILURE("PNM: can't store alpha");
if (bits_per_sample == 32) { // PFM
const char type = ib.IsGray() ? 'f' : 'F';
const double scale = little_endian ? -1.0 : 1.0;
*chars_written =
snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%.1f\n",
type, ib.oriented_xsize(), ib.oriented_ysize(), scale);
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
} else if (bits_per_sample == 1) { // PBM
if (!ib.IsGray()) {
return JXL_FAILURE("Cannot encode color as PBM");
}
*chars_written =
snprintf(header, kMaxHeaderSize, "P4\n%" PRIuS " %" PRIuS "\n",
ib.oriented_xsize(), ib.oriented_ysize());
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
} else { // PGM/PPM
const uint32_t max_val = (1U << bits_per_sample) - 1;
if (max_val >= 65536) return JXL_FAILURE("PNM cannot have > 16 bits");
const char type = ib.IsGray() ? '5' : '6';
*chars_written =
snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%u\n",
type, ib.oriented_xsize(), ib.oriented_ysize(), max_val);
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
}
return true;
}
Span<const uint8_t> MakeSpan(const char* str) {
return Span<const uint8_t>(reinterpret_cast<const uint8_t*>(str),
strlen(str));
}
// Flip the image vertically for loading/saving PFM files which have the
// scanlines inverted.
void VerticallyFlipImage(Image3F* const image) {
for (int c = 0; c < 3; c++) {
for (size_t y = 0; y < image->ysize() / 2; y++) {
float* first_row = image->PlaneRow(c, y);
float* other_row = image->PlaneRow(c, image->ysize() - y - 1);
for (size_t x = 0; x < image->xsize(); ++x) {
float tmp = first_row[x];
first_row[x] = other_row[x];
other_row[x] = tmp;
}
}
}
}
} // namespace
Status EncodeImagePNM(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes) {
const bool floating_point = bits_per_sample > 16;
// Choose native for PFM; PGM/PPM require big-endian (N/A for PBM)
const JxlEndianness endianness =
floating_point ? JXL_NATIVE_ENDIAN : JXL_BIG_ENDIAN;
ImageMetadata metadata_copy = io->metadata.m;
// AllDefault sets all_default, which can cause a race condition.
if (!Bundle::AllDefault(metadata_copy)) {
JXL_WARNING("PNM encoder ignoring metadata - use a different codec");
}
if (!c_desired.IsSRGB()) {
JXL_WARNING(
"PNM encoder cannot store custom ICC profile; decoder\n"
"will need hint key=color_space to get the same values");
}
ImageBundle ib = io->Main().Copy();
// In case of PFM the image must be flipped upside down since that format
// is designed that way.
const ImageBundle* to_color_transform = &ib;
ImageBundle flipped;
if (floating_point) {
flipped = ib.Copy();
VerticallyFlipImage(flipped.color());
to_color_transform = &flipped;
}
ImageMetadata metadata = io->metadata.m;
ImageBundle store(&metadata);
const ImageBundle* transformed;
JXL_RETURN_IF_ERROR(TransformIfNeeded(
*to_color_transform, c_desired, GetJxlCms(), pool, &store, &transformed));
size_t stride = ib.oriented_xsize() *
(c_desired.Channels() * bits_per_sample) / kBitsPerByte;
PaddedBytes pixels(stride * ib.oriented_ysize());
JXL_RETURN_IF_ERROR(ConvertToExternal(
*transformed, bits_per_sample, floating_point, c_desired.Channels(),
endianness, stride, pool, pixels.data(), pixels.size(),
/*out_callback=*/nullptr, /*out_opaque=*/nullptr,
metadata.GetOrientation()));
char header[kMaxHeaderSize];
int header_size = 0;
bool is_little_endian = endianness == JXL_LITTLE_ENDIAN ||
(endianness == JXL_NATIVE_ENDIAN && IsLittleEndian());
JXL_RETURN_IF_ERROR(EncodeHeader(*transformed, bits_per_sample,
is_little_endian, header, &header_size));
bytes->resize(static_cast<size_t>(header_size) + pixels.size());
memcpy(bytes->data(), header, static_cast<size_t>(header_size));
memcpy(bytes->data() + header_size, pixels.data(), pixels.size());
return true;
}
} // namespace extras
} // namespace jxl

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

@ -3,22 +3,16 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_EXTRAS_CODEC_PNM_H_
#define LIB_EXTRAS_CODEC_PNM_H_
#ifndef LIB_EXTRAS_ENC_PNM_H_
#define LIB_EXTRAS_ENC_PNM_H_
// Encodes/decodes PBM/PGM/PPM/PFM pixels in memory.
#include <stddef.h>
#include <stdint.h>
// TODO(janwas): workaround for incorrect Win64 codegen (cause unknown)
#include <hwy/highway.h>
#include "lib/extras/color_hints.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
#include "lib/jxl/color_encoding_internal.h"
@ -26,20 +20,12 @@
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. color_hints may specify "color_space", which
// defaults to sRGB.
Status DecodeImagePNM(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
// Transforms from io->c_current to `c_desired` and encodes into `bytes`.
Status EncodeImagePNM(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes);
void TestCodecPNM();
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_PNM_H_
#endif // LIB_EXTRAS_ENC_PNM_H_

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

@ -7,6 +7,8 @@
#include <cmath>
#include "lib/jxl/enc_color_management.h"
namespace jxl {
float GetHlgGamma(const float peak_luminance, const float surround_luminance) {
@ -21,10 +23,10 @@ Status HlgOOTF(ImageBundle* ib, const float gamma, ThreadPool* pool) {
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(ib->TransformTo(linear_rec2020, GetJxlCms(), pool));
return RunOnPool(
pool, 0, ib->ysize(), ThreadPool::SkipInit(),
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, ib->ysize(), ThreadPool::NoInit,
[&](const int y, const int thread) {
float* const JXL_RESTRICT rows[3] = {ib->color()->PlaneRow(0, y),
ib->color()->PlaneRow(1, y),
@ -43,7 +45,8 @@ Status HlgOOTF(ImageBundle* ib, const float gamma, ThreadPool* pool) {
}
}
},
"HlgOOTF");
"HlgOOTF"));
return true;
}
Status HlgInverseOOTF(ImageBundle* ib, const float gamma, ThreadPool* pool) {

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

@ -147,7 +147,11 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
// uint case.
io->metadata.m.bit_depth.bits_per_sample = io->Main().DetectRealBitdepth();
}
SetIntensityTarget(io);
if (ppf.info.intensity_target != 0) {
io->metadata.m.SetIntensityTarget(ppf.info.intensity_target);
} else {
SetIntensityTarget(io);
}
io->CheckMetadata();
return true;
}

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

@ -10,6 +10,7 @@
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/transfer_functions-inl.h"
HWY_BEFORE_NAMESPACE();
@ -31,7 +32,7 @@ Status ToneMapFrame(const std::pair<float, float> display_nits,
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(ib->TransformTo(linear_rec2020, GetJxlCms(), pool));
const auto eotf_inv = [&df](const V luminance) -> V {
return TF_PQ().EncodedFromDisplay(df, luminance * Set(df, 1. / 10000));
@ -69,8 +70,8 @@ Status ToneMapFrame(const std::pair<float, float> display_nits,
const V inv_max_display_nits = Set(df, 1 / display_nits.second);
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, ib->ysize(), ThreadPool::SkipInit(),
[&](const int y, const int thread) {
pool, 0, ib->ysize(), ThreadPool::NoInit,
[&](const uint32_t y, size_t /* 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);
@ -128,11 +129,11 @@ Status GamutMapFrame(ImageBundle* const ib, float preserve_saturation,
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(ib->TransformTo(linear_rec2020, GetJxlCms(), pool));
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, ib->ysize(), ThreadPool::SkipInit(),
[&](const int y, const int thread) {
pool, 0, ib->ysize(), ThreadPool::NoInit,
[&](const uint32_t y, size_t /* 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);

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

@ -6,6 +6,7 @@
#include "benchmark/benchmark.h"
#include "lib/extras/codec.h"
#include "lib/extras/tone_mapping.h"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/testdata.h"
namespace jxl {
@ -24,7 +25,7 @@ static void BM_ToneMapping(benchmark::State& state) {
linear_rec2020.white_point = WhitePoint::kD65;
linear_rec2020.tf.SetTransferFunction(TransferFunction::kLinear);
JXL_CHECK(linear_rec2020.CreateICC());
JXL_CHECK(image.TransformTo(linear_rec2020));
JXL_CHECK(image.TransformTo(linear_rec2020, GetJxlCms()));
for (auto _ : state) {
state.PauseTiming();

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

@ -0,0 +1,232 @@
/* 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.
*/
/** @addtogroup libjxl_common
* @{
* @file cms_interface.h
* @brief Interface to allow the injection of different color management systems
* (CMSes, also called color management modules, or CMMs) in JPEG XL.
*
* A CMS is needed by the JPEG XL encoder and decoder to perform colorspace
* conversions. This defines an interface that can be implemented for different
* CMSes and then passed to the library.
*/
#ifndef JXL_CMS_INTERFACE_H_
#define JXL_CMS_INTERFACE_H_
#include "jxl/color_encoding.h"
#include "jxl/memory_manager.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
/** Represents an input or output colorspace to a color transform, as a
* serialized ICC profile. */
typedef struct {
/** The serialized ICC profile. This is guaranteed to be present and valid. */
struct {
const uint8_t* data;
size_t size;
} icc;
/** Structured representation of the colorspace, if applicable. If all fields
* are different from their "unknown" value, then this is equivalent to the
* ICC representation of the colorspace. If some are "unknown", those that are
* not are still valid and can still be used on their own if they are useful.
*/
JxlColorEncoding color_encoding;
/** Number of components per pixel. This can be deduced from the other
* representations of the colorspace but is provided for convenience and
* validation. */
size_t num_channels;
} JxlColorProfile;
/** Allocates and returns the data needed for @p num_threads parallel transforms
* from the @p input colorspace to @p output, with up to @p pixels_per_thread
* pixels to transform per call to JxlCmsInterface::run. @p init_data comes
* directly from the JxlCmsInterface instance. Since @c run only receives the
* data returned by @c init, a reference to @p init_data should be kept there
* if access to it is desired in @c run. Likewise for JxlCmsInterface::destroy.
*
* The ICC data in @p input and @p output is guaranteed to outlive the @c init /
* @c run / @c destroy cycle.
*
* @param init_data JxlCmsInterface::init_data passed as-is.
* @param num_threads the maximum number of threads from which
* JxlCmsInterface::run will be called.
* @param pixels_per_thread the maximum number of pixels that each call to
* JxlCmsInterface::run will have to transform.
* @param input_profile the input colorspace for the transform.
* @param output_profile the colorspace to which JxlCmsInterface::run should
* convert the input data.
* @param intensity_target for colorspaces where luminance is relative
* (essentially: not PQ), indicates the luminance at which (1, 1, 1) will
* be displayed. This is useful for conversions between PQ and a relative
* luminance colorspace, in either direction: @p intensity_target cd/m²
* in PQ should map to and from (1, 1, 1) in the relative one.\n
* It is also used for conversions to and from HLG, as it is
* scene-referred while other colorspaces are assumed to be
* display-referred. That is, conversions from HLG should apply the OOTF
* for a peak display luminance of @p intensity_target, and conversions
* to HLG should undo it. The OOTF is a gamma function applied to the
* luminance channel (https://www.itu.int/rec/R-REC-BT.2100-2-201807-I
* page 7), with the gamma value computed as
* <tt>1.2 * 1.111^log2(intensity_target / 1000)</tt> (footnote 2 page 8
* of the same document).
* @return The data needed for the transform, or @c NULL in case of failure.
* This will be passed to the other functions as @c user_data.
*/
typedef void* (*jpegxl_cms_init_func)(void* init_data, size_t num_threads,
size_t pixels_per_thread,
const JxlColorProfile* input_profile,
const JxlColorProfile* output_profile,
float intensity_target);
/** Returns a buffer that can be used by callers of the interface to store the
* input of the conversion or read its result, if they pass it as the input or
* output of the @c run function.
* @param user_data the data returned by @c init.
* @param thread the index of the thread for which to return a buffer.
* @return A buffer that can be used by the caller for passing to @c run.
*/
typedef float* (*jpegxl_cms_get_buffer_func)(void* user_data, size_t thread);
/** Executes one transform and returns true on success or false on error. It
* must be possible to call this from different threads with different values
* for @p thread, all between 0 (inclusive) and the value of @p num_threads
* passed to @c init (exclusive). It is allowed to implement this by locking
* such that the transforms are essentially performed sequentially, if such a
* performance profile is acceptable. @p user_data is the data returned by
* @c init.
* The buffers each contain @p num_pixels × @c num_channels interleaved floating
* point (0..1) samples where @c num_channels is the number of color channels of
* their respective color profiles. It is guaranteed that the only case in which
* they might overlap is if the output has fewer channels than the input, in
* which case the pointers may be identical.
* For CMYK data, 0 represents the maximum amount of ink while 1 represents no
* ink.
* @param user_data the data returned by @c init.
* @param thread the index of the thread from which the function is being
* called.
* @param input_buffer the buffer containing the pixel data to be transformed.
* @param output_buffer the buffer receiving the transformed pixel data.
* @param num_pixels the number of pixels to transform from @p input to
* @p output.
* @return JXL_TRUE on success, JXL_FALSE on failure.
*/
typedef JXL_BOOL (*jpegxl_cms_run_func)(void* user_data, size_t thread,
const float* input_buffer,
float* output_buffer,
size_t num_pixels);
/** Performs the necessary clean-up and frees the memory allocated for user
* data.
*/
typedef void (*jpegxl_cms_destroy_func)(void*);
/**
* Interface for performing colorspace transforms. The @c init function can be
* called several times to instantiate several transforms, including before
* other transforms have been destroyed.
*
* The call sequence for a given colorspace transform could look like the
* following:
* @dot
* digraph calls {
* newrank = true
* node [shape = box, fontname = monospace]
* init [label = "user_data <- init(\l\
* init_data = data,\l\
* num_threads = 3,\l\
* pixels_per_thread = 20,\l\
* input = (sRGB, 3 channels),\l\
* output = (Display-P3, 3 channels),\l\
* intensity_target = 255\l\
* )\l"]
* subgraph cluster_0 {
* color = lightgrey
* label = "thread 1"
* labeljust = "c"
* run_1_1 [label = "run(\l\
* user_data,\l\
* thread = 1,\l\
* input = in[0],\l\
* output = out[0],\l\
* num_pixels = 20\l\
* )\l"]
* run_1_2 [label = "run(\l\
* user_data,\l\
* thread = 1,\l\
* input = in[3],\l\
* output = out[3],\l\
* num_pixels = 20\l\
* )\l"]
* }
* subgraph cluster_1 {
* color = lightgrey
* label = "thread 2"
* labeljust = "l"
* run_2_1 [label = "run(\l\
* user_data,\l\
* thread = 2,\l\
* input = in[1],\l\
* output = out[1],\l\
* num_pixels = 20\l\
* )\l"]
* run_2_2 [label = "run(\l\
* user_data,\l\
* thread = 2,\l\
* input = in[4],\l\
* output = out[4],\l\
* num_pixels = 13\l\
* )\l"]
* }
* subgraph cluster_3 {
* color = lightgrey
* label = "thread 3"
* labeljust = "c"
* run_3_1 [label = "run(\l\
* user_data,\l\
* thread = 3,\l\
* input = in[2],\l\
* output = out[2],\l\
* num_pixels = 20\l\
* )\l"]
* }
* init -> {run_1_1; run_2_1; run_3_1; rank = same}
* run_1_1 -> run_1_2
* run_2_1 -> run_2_2
* {run_1_2; run_2_2, run_3_1} -> "destroy(user_data)"
* }
* @enddot
*/
typedef struct {
/** CMS-specific data that will be passed to @ref init. */
void* init_data;
/** Prepares a colorspace transform as described in the documentation of @ref
* jpegxl_cms_init_func. */
jpegxl_cms_init_func init;
/** Returns a buffer that can be used as input to @c run. */
jpegxl_cms_get_buffer_func get_src_buf;
/** Returns a buffer that can be used as output from @c run. */
jpegxl_cms_get_buffer_func get_dst_buf;
/** Executes the transform on a batch of pixels, per @ref jpegxl_cms_run_func.
*/
jpegxl_cms_run_func run;
/** Cleans up the transform. */
jpegxl_cms_destroy_func destroy;
} JxlCmsInterface;
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* JXL_CMS_INTERFACE_H_ */
/** @} */

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

@ -72,6 +72,15 @@ typedef struct {
uint32_t ysize;
} JxlPreviewHeader;
/** The intrinsic size header */
typedef struct {
/** Intrinsic width in pixels */
uint32_t xsize;
/** Intrinsic height in pixels */
uint32_t ysize;
} JxlIntrinsicSizeHeader;
/** The codestream animation header, optionally present in the beginning of
* the codestream, and if it is it applies to all animation frames, unlike
* JxlFrameHeader which applies to an individual frame.
@ -232,10 +241,26 @@ typedef struct {
*/
JxlAnimationHeader animation;
/** Intrinsic width of the image.
* The intrinsic size can be different from the actual size in pixels
* (as given by xsize and ysize) and it denotes the recommended dimensions
* for displaying the image, i.e. applications are advised to resample the
* decoded image to the intrinsic dimensions.
*/
uint32_t intrinsic_xsize;
/** Intrinsic heigth of the image.
* The intrinsic size can be different from the actual size in pixels
* (as given by xsize and ysize) and it denotes the recommended dimensions
* for displaying the image, i.e. applications are advised to resample the
* decoded image to the intrinsic dimensions.
*/
uint32_t intrinsic_ysize;
/** Padding for forwards-compatibility, in case more fields are exposed
* in a future version of the library.
*/
uint8_t padding[108];
uint8_t padding[100];
} JxlBasicInfo;
/** Information for a single extra channel.
@ -290,7 +315,7 @@ typedef struct {
} JxlHeaderExtensions;
/** Frame blend modes.
* If coalescing is enabled (default), this can be ignored.
* When decoding, if coalescing is enabled (default), this can be ignored.
*/
typedef enum {
JXL_BLEND_REPLACE = 0,
@ -301,11 +326,12 @@ typedef enum {
} JxlBlendMode;
/** The information about blending the color channels or a single extra channel.
* If coalescing is enabled (default), this can be ignored.
* When decoding, if coalescing is enabled (default), this can be ignored and
* the blend mode is considered to be JXL_BLEND_REPLACE.
* When encoding, these settings apply to the pixel data given to the encoder.
*/
typedef struct {
/** Blend mode.
* Always equal to JXL_BLEND_REPLACE if coalescing is enabled.
*/
JxlBlendMode blendmode;
/** Reference frame ID to use as the 'bottom' layer (0-3).
@ -321,32 +347,43 @@ typedef struct {
} JxlBlendInfo;
/** The information about layers.
* If coalescing is enabled (default), this can be ignored.
* When decoding, if coalescing is enabled (default), this can be ignored.
* When encoding, these settings apply to the pixel data given to the encoder,
* the encoder could choose an internal representation that differs.
*/
typedef struct {
/** Horizontal offset of the frame (can be negative, always zero if coalescing
* is enabled)
/** Whether cropping is applied for this frame. When decoding, if false,
* crop_x0 and crop_y0 are set to zero, and xsize and ysize to the main
* image dimensions. When encoding and this is false, those fields are
* ignored. When decoding, if coalescing is enabled (default), this is always
* false, regardless of the internal encoding in the JPEG XL codestream.
*/
JXL_BOOL have_crop;
/** Horizontal offset of the frame (can be negative).
*/
int32_t crop_x0;
/** Vertical offset of the frame (can be negative, always zero if coalescing
* is enabled)
/** Vertical offset of the frame (can be negative).
*/
int32_t crop_y0;
/** Width of the frame (number of columns, always equal to image width if
* coalescing is enabled)
/** Width of the frame (number of columns).
*/
uint32_t xsize;
/** Height of the frame (number of rows, always equal to image height if
* coalescing is enabled)
/** Height of the frame (number of rows).
*/
uint32_t ysize;
/** The blending info for the color channels. Blending info for extra channels
* has to be retrieved separately using JxlDecoderGetExtraChannelBlendInfo.
*/
JxlBlendInfo blend_info;
/** After blending, save the frame as reference frame with this ID (0-3).
* Special case: if the frame duration is nonzero, ID 0 means "will not be
* referenced in the future".
* referenced in the future". This value is not used for the last frame.
*/
uint32_t save_as_reference;
} JxlLayerInfo;
@ -369,11 +406,16 @@ typedef struct {
uint32_t timecode;
/** Length of the frame name in bytes, or 0 if no name.
* Excludes null termination character.
* Excludes null termination character. This value is set by the decoder.
* For the encoder, this value is ignored and @ref JxlEncoderSetFrameName is
* used instead to set the name and the length.
*/
uint32_t name_length;
/** Indicates this is the last animation frame.
/** Indicates this is the last animation frame. This value is set by the
* decoder to indicate no further frames follow. For the encoder, it is not
* required to set this value and it is ignored, @ref JxlEncoderCloseFrames is
* used to indicate the last frame to the encoder instead.
*/
JXL_BOOL is_last;

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

@ -16,6 +16,7 @@
#include <stddef.h>
#include <stdint.h>
#include "jxl/cms_interface.h"
#include "jxl/codestream_header.h"
#include "jxl/color_encoding.h"
#include "jxl/jxl_export.h"
@ -113,9 +114,9 @@ JXL_EXPORT void JxlDecoderDestroy(JxlDecoder* dec);
/**
* Return value for JxlDecoderProcessInput.
* The values above 0x40 are optional informal events that can be subscribed to,
* they are never returned if they have not been registered with
* JxlDecoderSubscribeEvents.
* The values from JXL_DEC_BASIC_INFO onwards are optional informative
* events that can be subscribed to, they are never returned if they
* have not been registered with JxlDecoderSubscribeEvents.
*/
typedef enum {
/** Function call finished successfully, or decoding is finished and there is
@ -152,8 +153,8 @@ typedef enum {
* requested and it is possible to decode a DC image from the codestream and
* the DC out buffer was not yet set. This event re-occurs for new frames
* if there are multiple animation frames.
* DEPRECATED: the DC feature in this form will be removed. You can use
* JxlDecoderFlushImage for progressive rendering.
* DEPRECATED: the DC feature in this form will be removed. For progressive
* rendering, JxlDecoderFlushImage should be used.
*/
JXL_DEC_NEED_DC_OUT_BUFFER = 4,
@ -228,8 +229,8 @@ typedef enum {
* status only indicates we're past this point in the codestream. This event
* occurs max once per frame and always later than JXL_DEC_FRAME_HEADER
* and other header events and earlier than full resolution pixel data.
* DEPRECATED: the DC feature in this form will be removed. You can use
* JxlDecoderFlushImage for progressive rendering.
* DEPRECATED: the DC feature in this form will be removed. For progressive
* rendering, JxlDecoderFlushImage should be used.
*/
JXL_DEC_DC_IMAGE = 0x800,
@ -263,17 +264,18 @@ typedef enum {
* @see JxlDecoderSetDecompressBoxes to configure whether to get the box
* data decompressed, or possibly compressed.
*
* Boxes can be compressed. This is so when their box type is "brob". In that
* case, they have an underlying decompressed box type and decompressed data.
* Use JxlDecoderSetDecompressBoxes to configure which data to get,
* decompressing them requires Brotli. JxlDecoderGetBoxType has a flag to
* get the compressed box type, which can be "brob", or the decompressed box
* type. If a box is not compressed (its compressed type is not "brob"), then
* you get the same decompressed box type and data no matter what setting is
* configured.
* Boxes can be compressed. This is so when their box type is
* "brob". In that case, they have an underlying decompressed box
* type and decompressed data. JxlDecoderSetDecompressBoxes allows
* configuring which data to get. Decompressing requires
* Brotli. JxlDecoderGetBoxType has a flag to get the compressed box
* type, which can be "brob", or the decompressed box type. If a box
* is not compressed (its compressed type is not "brob"), then
* the output decompressed box type and data is independent of what
* setting is configured.
*
* The buffer set with JxlDecoderSetBoxBuffer must be set again for each next
* box that you want to get, or can be left unset to skip outputting this box.
* box to be obtained, or can be left unset to skip outputting this box.
* The output buffer contains the full box data when the next JXL_DEC_BOX
* event or JXL_DEC_SUCCESS occurs. JXL_DEC_BOX occurs for all boxes,
* including non-metadata boxes such as the signature box or codestream boxes.
@ -282,6 +284,19 @@ typedef enum {
* "jumb" respectively.
*/
JXL_DEC_BOX = 0x4000,
/** Informative event by JxlDecoderProcessInput: a progressive step in
* decoding the frame is reached. When calling @ref JxlDecoderFlushImage at
* this point, the flushed image will correspond exactly to this point in
* decoding, and not yet contain partial results (such as partially more fine
* detail) of a next step. By default, this event will trigger maximum once
* per frame, when a 8x8th resolution (DC) image is ready (the image data is
* still returned at full resolution, giving upscaled DC). Use @ref
* JxlDecoderSetProgressiveDetail to configure more fine-grainedness. The
* event is not guaranteed to trigger, not all images have progressive steps
* or DC encoded.
*/
JXL_DEC_FRAME_PROGRESSION = 0x8000,
} JxlDecoderStatus;
/** Rewinds decoder to the beginning. The same input must be given again from
@ -377,27 +392,28 @@ JXL_EXPORT size_t JxlDecoderSizeHintBasicInfo(const JxlDecoder* dec);
JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec,
int events_wanted);
/** Enables or disables preserving of original orientation. Some images are
* encoded with an orientation tag indicating the image is rotated and/or
* mirrored (here called the original orientation).
/** Enables or disables preserving of as-in-bitstream pixeldata
* orientation. Some images are encoded with an Orientation tag
* indicating that the decoder must perform a rotation and/or
* mirroring to the encoded image data.
*
* *) If keep_orientation is JXL_FALSE (the default): the decoder will perform
* work to undo the transformation. This ensures the decoded pixels will not
* be rotated or mirrored. The decoder will always set the orientation field
* of the JxlBasicInfo to JXL_ORIENT_IDENTITY to match the returned pixel data.
* The decoder may also swap xsize and ysize in the JxlBasicInfo compared to the
* values inside of the codestream, to correctly match the decoded pixel data,
* e.g. when a 90 degree rotation was performed.
* *) If skip_reorientation is JXL_FALSE (the default): the decoder
* will apply the transformation from the orientation setting, hence
* rendering the image according to its specified intent. When
* producing a JxlBasicInfo, the decoder will always set the
* orientation field to JXL_ORIENT_IDENTITY (matching the returned
* pixel data) and also align xsize and ysize so that they correspond
* to the width and the height of the returned pixel data.
*
* *) If this option is JXL_TRUE: then the image is returned as-is, which may be
* rotated or mirrored, and the user must check the orientation field in
* JxlBasicInfo after decoding to correctly interpret the decoded pixel data.
* *) If skip_reorientation is JXL_TRUE: the decoder will skip
* applying the transformation from the orientation setting, returning
* the image in the as-in-bitstream pixeldata orientation.
* This may be faster to decode since the decoder doesn't have to apply the
* transformation, but can cause wrong display of the image if the orientation
* tag is not correctly taken into account by the user.
*
* By default, this option is disabled, and the decoder automatically corrects
* the orientation.
* By default, this option is disabled, and the returned pixel data is
* re-oriented according to the image's Orientation setting.
*
* This function must be called at the beginning, before decoding is performed.
*
@ -405,11 +421,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec,
* possible values.
*
* @param dec decoder object
* @param keep_orientation JXL_TRUE to enable, JXL_FALSE to disable.
* @param skip_reorientation JXL_TRUE to enable, JXL_FALSE to disable.
* @return JXL_DEC_SUCCESS if no error, JXL_DEC_ERROR otherwise.
*/
JXL_EXPORT JxlDecoderStatus
JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL keep_orientation);
JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL skip_reorientation);
/** Enables or disables rendering spot colors. By default, spot colors
* are rendered, which is OK for viewing the decoded image. If render_spotcolors
@ -469,7 +485,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec,
* have not been seen yet.
* @return JXL_DEC_ERROR when decoding failed, e.g. invalid codestream.
* TODO(lode) document the input data mechanism
* @return JXL_DEC_NEED_MORE_INPUT more input data is necessary.
* @return JXL_DEC_NEED_MORE_INPUT when more input data is necessary.
* @return JXL_DEC_BASIC_INFO when basic info such as image dimensions is
* available and this informative event is subscribed to.
* @return JXL_DEC_EXTENSIONS when JPEG XL codestream user extensions are
@ -479,7 +495,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec,
* @return JXL_DEC_PREVIEW_IMAGE when preview pixel information is available and
* output in the preview buffer.
* @return JXL_DEC_DC_IMAGE when DC pixel information (8x8 downscaled version
* of the image) is available and output in the DC buffer.
* of the image) is available and output is in the DC buffer.
* @return JXL_DEC_FULL_IMAGE when all pixel information at highest detail is
* available and has been output in the pixel buffer.
*/
@ -608,8 +624,8 @@ typedef enum {
* It is often possible to use JxlDecoderGetColorAsICCProfile as an
* alternative anyway. The following scenarios are possible:
* - The JPEG XL image has an attached ICC Profile, in that case, the encoded
* structured data is not available, this function will return an error status
* and you must use JxlDecoderGetColorAsICCProfile instead.
* structured data is not available, this function will return an error
* status. JxlDecoderGetColorAsICCProfile should be called instead.
* - The JPEG XL image has an encoded structured color profile, and it
* represents an RGB or grayscale color space. This function will return it.
* You can still use JxlDecoderGetColorAsICCProfile as well as an
@ -621,10 +637,10 @@ typedef enum {
* an unknown or xyb color space. In that case,
* JxlDecoderGetColorAsICCProfile is not available.
*
* If you wish to render the image using a system that supports ICC profiles,
* use JxlDecoderGetColorAsICCProfile first. If you're looking for a specific
* color space possibly indicated in the JPEG XL image, use
* JxlDecoderGetColorAsEncodedProfile first.
* When rendering an image on a system that supports ICC profiles,
* JxlDecoderGetColorAsICCProfile should be used first. When rendering
* for a specific color space, possibly indicated in the JPEG XL
* image, JxlDecoderGetColorAsEncodedProfile should be used first.
*
* @param dec decoder object
* @param format pixel format to output the data to. Only used for
@ -818,7 +834,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetExtraChannelBlendInfo(
* @return JXL_DEC_SUCCESS on success, JXL_DEC_ERROR on error, such as
* information not available yet.
*
* DEPRECATED: the DC feature in this form will be removed. You can use
* DEPRECATED: the DC feature in this form will be removed. Use
* JxlDecoderFlushImage for progressive rendering.
*/
JXL_EXPORT JXL_DEPRECATED JxlDecoderStatus JxlDecoderDCOutBufferSize(
@ -839,7 +855,7 @@ JXL_EXPORT JXL_DEPRECATED JxlDecoderStatus JxlDecoderDCOutBufferSize(
* @return JXL_DEC_SUCCESS on success, JXL_DEC_ERROR on error, such as
* size too small.
*
* DEPRECATED: the DC feature in this form will be removed. You can use
* DEPRECATED: the DC feature in this form will be removed. Use
* JxlDecoderFlushImage for progressive rendering.
*/
JXL_EXPORT JXL_DEPRECATED JxlDecoderStatus JxlDecoderSetDCOutBuffer(
@ -883,8 +899,8 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetImageOutBuffer(
JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size);
/**
* Callback function type for JxlDecoderSetImageOutCallback. @see
* JxlDecoderSetImageOutCallback for usage.
* Function type for JxlDecoderSetImageOutCallback.
* @see JxlDecoderSetImageOutCallback for usage.
*
* The callback may be called simultaneously by different threads when using a
* threaded parallel runner, on different pixels.
@ -1137,6 +1153,20 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec,
JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec,
uint64_t* size);
/**
* Configures at which progressive steps in frame decoding the @ref
* JXL_DEC_FRAME_PROGRESSION event occurs. By default, this is 0. The detail
* values mean: 0 = only trigger for the DC image, the 8x8th lower resolution
* image. 1 = also trigger when a full pass of groups is ready. Higher values
* indicate more steps but are not yet implemented. Higher values always include
* the events of lower values as well.
*
* @param dec decoder object
* @param detail at which level of detail to trigger JXL_DEC_FRAME_PROGRESSION
*/
JXL_EXPORT void JxlDecoderSetProgressiveDetail(JxlDecoder* dec,
uint32_t detail);
/**
* Outputs progressive step towards the decoded image so far when only partial
* input was received. If the flush was successful, the buffer set with

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

@ -41,14 +41,18 @@ JXL_EXPORT uint32_t JxlEncoderVersion(void);
typedef struct JxlEncoderStruct JxlEncoder;
/**
* Opaque structure that holds frame specific encoding options for a JPEG XL
* encoder.
* Settings and metadata for a single image frame. This includes encoder options
* for a frame such as compression quality and speed.
*
* Allocated and initialized with JxlEncoderOptionsCreate().
* Allocated and initialized with JxlEncoderFrameSettingsCreate().
* Cleaned up and deallocated when the encoder is destroyed with
* JxlEncoderDestroy().
*/
typedef struct JxlEncoderOptionsStruct JxlEncoderOptions;
typedef struct JxlEncoderFrameSettingsStruct JxlEncoderFrameSettings;
/** DEPRECATED: Use JxlEncoderFrameSettings instead.
*/
typedef JxlEncoderFrameSettings JxlEncoderOptions;
/**
* Return value for multiple encoder functions.
@ -67,19 +71,17 @@ typedef enum {
*/
JXL_ENC_NEED_MORE_OUTPUT = 2,
/** The encoder doesn't (yet) support this.
/** DEPRECATED: the encoder does not return this status and there is no need
* to handle or expect it.
*/
JXL_ENC_NOT_SUPPORTED = 3,
} JxlEncoderStatus;
/**
* 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.
* Id of encoder options for a frame. This includes options such as the
* image quality and compression speed for this frame. This does not include
* non-frame related encoder options such as for boxes.
*/
typedef enum {
/** Sets encoder effort/speed level without affecting decoding speed. Valid
@ -87,13 +89,13 @@ typedef enum {
* 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise.
* Default: squirrel (7).
*/
JXL_ENC_OPTION_EFFORT = 0,
JXL_ENC_FRAME_SETTING_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,
JXL_ENC_FRAME_SETTING_DECODING_SPEED = 1,
/** Sets resampling option. If enabled, the image is downsampled before
* compression, and upsampled to original size in the decoder. Integer option,
@ -101,161 +103,161 @@ typedef enum {
* 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for 4x4
* downsampling, 8 for 8x8 downsampling.
*/
JXL_ENC_OPTION_RESAMPLING = 2,
JXL_ENC_FRAME_SETTING_RESAMPLING = 2,
/** Similar to JXL_ENC_OPTION_RESAMPLING, but for extra channels. Integer
* option, use -1 for the default behavior (depends on encoder
/** Similar to JXL_ENC_FRAME_SETTING_RESAMPLING, but for extra channels.
* Integer option, use -1 for the default behavior (depends on encoder
* implementation), 1 for no downsampling (1x1), 2 for 2x2 downsampling, 4 for
* 4x4 downsampling, 8 for 8x8 downsampling.
*/
JXL_ENC_OPTION_EXTRA_CHANNEL_RESAMPLING = 3,
JXL_ENC_FRAME_SETTING_EXTRA_CHANNEL_RESAMPLING = 3,
/** Indicates the frame added with @ref JxlEncoderAddImageFrame is already
* downsampled by the downsampling factor set with @ref
* JXL_ENC_OPTION_RESAMPLING. The input frame must then be given in the
* JXL_ENC_FRAME_SETTING_RESAMPLING. The input frame must then be given in the
* downsampled resolution, not the full image resolution. The downsampled
* resolution is given by ceil(xsize / resampling), ceil(ysize / resampling)
* with xsize and ysize the dimensions given in the basic info, and resampling
* the factor set with @ref JXL_ENC_OPTION_RESAMPLING.
* the factor set with @ref JXL_ENC_FRAME_SETTING_RESAMPLING.
* Use 0 to disable, 1 to enable. Default value is 0.
*/
JXL_ENC_OPTION_ALREADY_DOWNSAMPLED = 4,
JXL_ENC_FRAME_SETTING_ALREADY_DOWNSAMPLED = 4,
/** 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_PHOTON_NOISE = 5,
JXL_ENC_FRAME_SETTING_PHOTON_NOISE = 5,
/** 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.
* use, please use JXL_ENC_FRAME_SETTING_PHOTON_NOISE instead. Use -1 for the
* default (encoder chooses), 0 to disable, 1 to enable.
*/
JXL_ENC_OPTION_NOISE = 6,
JXL_ENC_FRAME_SETTING_NOISE = 6,
/** Enables or disables dots generation. Use -1 for the default (encoder
* chooses), 0 to disable, 1 to enable.
*/
JXL_ENC_OPTION_DOTS = 7,
JXL_ENC_FRAME_SETTING_DOTS = 7,
/** Enables or disables patches generation. Use -1 for the default (encoder
* chooses), 0 to disable, 1 to enable.
*/
JXL_ENC_OPTION_PATCHES = 8,
JXL_ENC_FRAME_SETTING_PATCHES = 8,
/** 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 = 9,
JXL_ENC_FRAME_SETTING_EPF = 9,
/** Enables or disables the gaborish filter. Use -1 for the default (encoder
* chooses), 0 to disable, 1 to enable.
*/
JXL_ENC_OPTION_GABORISH = 10,
JXL_ENC_FRAME_SETTING_GABORISH = 10,
/** 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 = 11,
JXL_ENC_FRAME_SETTING_MODULAR = 11,
/** 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 = 12,
JXL_ENC_FRAME_SETTING_KEEP_INVISIBLE = 12,
/** 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 = 13,
JXL_ENC_FRAME_SETTING_GROUP_ORDER = 13,
/** 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 = 14,
JXL_ENC_FRAME_SETTING_GROUP_ORDER_CENTER_X = 14,
/** 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 = 15,
JXL_ENC_FRAME_SETTING_GROUP_ORDER_CENTER_Y = 15,
/** Enables or disables progressive encoding for modular mode. Use -1 for the
* encoder default, 0 to disable, 1 to enable.
*/
JXL_ENC_OPTION_RESPONSIVE = 16,
JXL_ENC_FRAME_SETTING_RESPONSIVE = 16,
/** 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_PROGRESSIVE_AC = 17,
JXL_ENC_FRAME_SETTING_PROGRESSIVE_AC = 17,
/** 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_QPROGRESSIVE_AC = 18,
JXL_ENC_FRAME_SETTING_QPROGRESSIVE_AC = 18,
/** 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 = 19,
JXL_ENC_FRAME_SETTING_PROGRESSIVE_DC = 19,
/** 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_GLOBAL_PERCENT = 20,
JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GLOBAL_PERCENT = 20,
/** Use Local (per-group) 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_GROUP_PERCENT = 21,
JXL_ENC_FRAME_SETTING_CHANNEL_COLORS_GROUP_PERCENT = 21,
/** 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 = 22,
JXL_ENC_FRAME_SETTING_PALETTE_COLORS = 22,
/** 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 = 23,
JXL_ENC_FRAME_SETTING_LOSSY_PALETTE = 23,
/** Color transform for internal encoding: -1 = default, 0=XYB, 1=none (RGB),
* 2=YCbCr. The XYB setting performs the forward XYB transform. None and
* YCbCr both perform no transform, but YCbCr is used to indicate that the
* encoded data losslessly represents YCbCr values.
*/
JXL_ENC_OPTION_COLOR_TRANSFORM = 24,
JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM = 24,
/** Color space for modular encoding: -1=default, 0-35=reverse color transform
* index, e.g. index 0 = none, index 6 = YCoCg.
* The default behavior is to try several, depending on the speed setting.
*/
JXL_ENC_OPTION_MODULAR_COLOR_SPACE = 25,
JXL_ENC_FRAME_SETTING_MODULAR_COLOR_SPACE = 25,
/** Group size for modular encoding: -1=default, 0=128, 1=256, 2=512, 3=1024.
*/
JXL_ENC_OPTION_MODULAR_GROUP_SIZE = 26,
JXL_ENC_FRAME_SETTING_MODULAR_GROUP_SIZE = 26,
/** 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 = 27,
JXL_ENC_FRAME_SETTING_MODULAR_PREDICTOR = 27,
/** Fraction of pixels used to learn MA trees as a percentage. -1 = default,
* 0 = no MA and fast decode, 50 = default value, 100 = all, values above
* 100 are also permitted. Higher values use more encoder memory.
*/
JXL_ENC_OPTION_MODULAR_MA_TREE_LEARNING_PERCENT = 28,
JXL_ENC_FRAME_SETTING_MODULAR_MA_TREE_LEARNING_PERCENT = 28,
/** Number of extra (previous-channel) MA tree properties to use. -1 =
* default, 0-11 = valid values. Recommended values are in the range 0 to 3,
@ -263,19 +265,19 @@ typedef enum {
* excluding color channels when using VarDCT mode). Higher value gives slower
* encoding and slower decoding.
*/
JXL_ENC_OPTION_MODULAR_NB_PREV_CHANNELS = 29,
JXL_ENC_FRAME_SETTING_MODULAR_NB_PREV_CHANNELS = 29,
/** Enable or disable CFL (chroma-from-luma) for lossless JPEG recompression.
* -1 = default, 0 = disable CFL, 1 = enable CFL.
*/
JXL_ENC_OPTION_JPEG_RECON_CFL = 30,
JXL_ENC_FRAME_SETTING_JPEG_RECON_CFL = 30,
/** 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.
*/
JXL_ENC_OPTION_FILL_ENUM = 65535,
JXL_ENC_FRAME_SETTING_FILL_ENUM = 65535,
} JxlEncoderOptionId;
} JxlEncoderFrameSettingId;
/**
* Creates an instance of JxlEncoder and initializes it.
@ -307,6 +309,17 @@ JXL_EXPORT void JxlEncoderReset(JxlEncoder* enc);
*/
JXL_EXPORT void JxlEncoderDestroy(JxlEncoder* enc);
/**
* Sets the color management system (CMS) that will be used for color conversion
* (if applicable) during encoding. May only be set before starting encoding. If
* left unset, the default CMS implementation will be used.
*
* @param enc encoder object.
* @param cms structure representing a CMS implementation. See JxlCmsInterface
* for more details.
*/
JXL_EXPORT void JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms);
/**
* Set the parallel runner for multithreading. May only be set before starting
* encoding.
@ -335,6 +348,12 @@ JxlEncoderSetParallelRunner(JxlEncoder* enc, JxlParallelRunner parallel_runner,
* When the return value is not JXL_ENC_ERROR or JXL_ENC_SUCCESS, the encoding
* requires more JxlEncoderProcessOutput calls to continue.
*
* This encodes the frames and/or boxes added so far. If the last frame or last
* box has been added, @ref JxlEncoderCloseInput, @ref JxlEncoderCloseFrames
* and/or @ref JxlEncoderCloseBoxes must be called before the next
* @ref JxlEncoderProcessOutput call, or the codestream won't be encoded
* correctly.
*
* @param enc encoder object.
* @param next_out pointer to next bytes to write to.
* @param avail_out amount of bytes available starting from *next_out.
@ -346,6 +365,80 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc,
uint8_t** next_out,
size_t* avail_out);
/**
* Sets the frame information for this frame to the encoder. This includes
* animation information such as frame duration to store in the frame header.
* The frame header fields represent the frame as passed to the encoder, but not
* necessarily the exact values as they will be encoded file format: the encoder
* could change crop and blending options of a frame for more efficient encoding
* or introduce additional internal frames. Animation duration and time code
* information is not altered since those are immutable metadata of the frame.
*
* It is not required to use this function, however if have_animation is set
* to true in the basic info, then this function should be used to set the
* time duration of this individual frame. By default individual frames have a
* time duration of 0, making them form a composite still. See @ref
* JxlFrameHeader for more information.
*
* This information is stored in the JxlEncoderFrameSettings and so is used for
* any frame encoded with these JxlEncoderFrameSettings. It is ok to change
* between @ref JxlEncoderAddImageFrame calls, each added image frame will have
* the frame header that was set in the options at the time of calling
* JxlEncoderAddImageFrame.
*
* The is_last and name_length fields of the JxlFrameHeader are ignored, use
* @ref JxlEncoderCloseFrames to indicate last frame, and @ref
* JxlEncoderSetFrameName to indicate the name and its length instead.
* Calling this function will clear any name that was previously set with @ref
* JxlEncoderSetFrameName.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param frame_header frame header data to set. Object owned by the caller and
* does not need to be kept in memory, its information is copied internally.
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus
JxlEncoderSetFrameHeader(JxlEncoderFrameSettings* frame_settings,
const JxlFrameHeader* frame_header);
/**
* Sets blend info of an extra channel. The blend info of extra channels is set
* separately from that of the color channels, the color channels are set with
* @ref JxlEncoderSetFrameHeader.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param index index of the extra channel to use.
* @param blend_info blend info to set for the extra channel
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBlendInfo(
JxlEncoderFrameSettings* frame_settings, size_t index,
const JxlBlendInfo* blend_info);
/**
* Sets the name of the animation frame. This function is optional, frames are
* not required to have a name. This setting is a part of the frame header, and
* the same principles as for @ref JxlEncoderSetFrameHeader apply. The
* name_length field of JxlFrameHeader is ignored by the encoder, this function
* determines the name length instead as the length in bytes of the C string.
*
* The maximum possible name length is 1071 bytes (excluding terminating null
* character).
*
* Calling @ref JxlEncoderSetFrameHeader clears any name that was
* previously set.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param frame_name name of the next frame to be encoded, as a UTF-8 encoded C
* string (zero terminated). Owned by the caller, and copied internally.
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameName(
JxlEncoderFrameSettings* frame_settings, const char* frame_name);
/**
* Sets the buffer to read JPEG encoded bytes from for the next frame to encode.
*
@ -361,14 +454,20 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc,
* JxlEncoderStoreJPEGMetadata and a single JPEG frame is added, it will be
* possible to losslessly reconstruct the JPEG codestream.
*
* @param options set of encoder options to use when encoding the frame.
* If this is the last frame, @ref JxlEncoderCloseInput or @ref
* JxlEncoderCloseFrames must be called before the next
* @ref JxlEncoderProcessOutput call.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param buffer bytes to read JPEG from. Owned by the caller and its contents
* are copied internally.
* @param size size of buffer in bytes.
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderAddJPEGFrame(
const JxlEncoderOptions* options, const uint8_t* buffer, size_t size);
JXL_EXPORT JxlEncoderStatus
JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings,
const uint8_t* buffer, size_t size);
/**
* Sets the buffer to read pixels from for the next image to encode. Must call
@ -411,7 +510,12 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddJPEGFrame(
* uses_original_profile=false case. They are however not allowed to be NaN or
* +-infinity.
*
* @param options set of encoder options to use when encoding the frame.
* If this is the last frame, @ref JxlEncoderCloseInput or @ref
* JxlEncoderCloseFrames must be called before the next
* @ref JxlEncoderProcessOutput call.
*
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param pixel_format format for pixels. Object owned by the caller and its
* contents are copied internally.
* @param buffer buffer type to input the pixel data from. Owned by the caller
@ -420,8 +524,8 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddJPEGFrame(
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderAddImageFrame(
const JxlEncoderOptions* options, const JxlPixelFormat* pixel_format,
const void* buffer, size_t size);
const JxlEncoderFrameSettings* frame_settings,
const JxlPixelFormat* pixel_format, const void* buffer, size_t size);
/**
* Sets the buffer to read pixels from for an extra channel at a given index.
@ -434,7 +538,8 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddImageFrame(
* 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 frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @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.
@ -445,8 +550,9 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddImageFrame(
* @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);
const JxlEncoderFrameSettings* frame_settings,
const JxlPixelFormat* pixel_format, const void* buffer, size_t size,
uint32_t index);
/** Adds a metadata box to the file format. JxlEncoderProcessOutput must be used
* to effectively write the box to the output. @ref JxlEncoderUseBoxes must
@ -582,7 +688,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderUseBoxes(JxlEncoder* enc);
* @ref JxlEncoderUseBoxes is not used. Further frames may still be added.
*
* Must be called between JxlEncoderAddBox of the last box
* and the next call to JxlEncoderProcessOutput, or JxlEncoderProcessOutput
* and the next call to JxlEncoderProcessOutput, or @ref JxlEncoderProcessOutput
* won't output the last box correctly.
*
* NOTE: if you don't need to close frames and boxes at separate times, you can
@ -595,7 +701,9 @@ JXL_EXPORT void JxlEncoderCloseBoxes(JxlEncoder* enc);
/**
* Declares that no frames will be added and @ref JxlEncoderAddImageFrame and
* @ref JxlEncoderAddJPEGFrame won't be called anymore. Further metadata boxes
* may still be added.
* may still be added. This function or @ref JxlEncoderCloseInput must be called
* after adding the last frame and the next call to
* @ref JxlEncoderProcessOutput, or the frame won't be properly marked as last.
*
* NOTE: if you don't need to close frames and boxes at separate times, you can
* use @ref JxlEncoderCloseInput instead to close both at once.
@ -611,7 +719,10 @@ JXL_EXPORT void JxlEncoderCloseFrames(JxlEncoder* enc);
* calls should be done to create the final output.
*
* The requirements of both @ref JxlEncoderCloseFrames and @ref
* JxlEncoderCloseBoxes apply to this function.
* JxlEncoderCloseBoxes apply to this function. Either this function or the
* other two must be called after the final frame and/or box, and the next
* @ref JxlEncoderProcessOutput call, or the codestream won't be encoded
* correctly.
*
* @param enc encoder object.
*/
@ -659,12 +770,35 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetICCProfile(JxlEncoder* enc,
*/
JXL_EXPORT void JxlEncoderInitBasicInfo(JxlBasicInfo* info);
/**
* Initializes a JxlFrameHeader struct to default values.
* For forwards-compatibility, this function has to be called before values
* are assigned to the struct fields.
* The default values correspond to a frame with no animation duration and the
* 'replace' blend mode. After using this function, For animation duration must
* be set, for composite still blend settings must be set.
*
* @param frame_header frame metadata. Object owned by the caller.
*/
JXL_EXPORT void JxlEncoderInitFrameHeader(JxlFrameHeader* frame_header);
/**
* Initializes a JxlBlendInfo struct to default values.
* For forwards-compatibility, this function has to be called before values
* are assigned to the struct fields.
*
* @param blend_info blending info. Object owned by the caller.
*/
JXL_EXPORT void JxlEncoderInitBlendInfo(JxlBlendInfo* blend_info);
/**
* Sets the global metadata of the image encoded by this encoder.
*
* If the JxlBasicInfo contains information of extra channels beyond an alpha
* channel, then @ref JxlEncoderSetExtraChannelInfo must be called between
* JxlEncoderSetBasicInfo and @ref JxlEncoderAddImageFrame.
* JxlEncoderSetBasicInfo and @ref JxlEncoderAddImageFrame. In order to indicate
* extra channels, the value of `info.num_extra_channels` should be set to the
* number of extra channels, also counting the alpha channel if present.
*
* @param enc encoder object.
* @param info global image metadata. Object owned by the caller and its
@ -706,6 +840,9 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelInfo(
* 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.
*
* TODO(lode): remove size parameter for consistency with
* JxlEncoderSetFrameName
*
* @param enc encoder object
* @param index index of the extra channel to set.
* @param name buffer with the name of the extra channel.
@ -720,19 +857,21 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelName(JxlEncoder* enc,
/**
* Sets a frame-specific option of integer type to the encoder options.
* The JxlEncoderOptionId argument determines which option is set.
* The JxlEncoderFrameSettingId argument determines which option is set.
*
* @param options set of encoder options to update with the new mode.
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param option ID of the option to set.
* @param value Integer value to set for this option.
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR in
* case of an error, such as invalid or unknown option id, or invalid integer
* value for the given option. If an error is returned, the state of the
* JxlEncoderOptions object is still valid and is the same as before this
* JxlEncoderFrameSettings object is still valid and is the same as before this
* function was called.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderOptionsSetInteger(
JxlEncoderOptions* options, JxlEncoderOptionId option, int32_t value);
JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetOption(
JxlEncoderFrameSettings* frame_settings, JxlEncoderFrameSettingId option,
int32_t value);
/** Forces the encoder to use the box-based container format (BMFF) even
* when not necessary.
@ -773,7 +912,8 @@ 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.
* 10. Keeping the default value of 5 is recommended for compatibility with all
* decoders.
*
* 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
@ -796,10 +936,36 @@ JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata);
* the encoder will only use those compatible with the level setting.
*
* This setting can only be set at the beginning, before encoding starts.
*
* @param enc encoder object.
* @param level the level value to set, must be 5 or 10.
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
* otherwise.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetCodestreamLevel(JxlEncoder* enc,
int level);
/** Returns the codestream level required to support the currently configured
* settings and basic info. This function can only be used at the beginning,
* before encoding starts, but after setting basic info.
*
* This does not support per-frame settings, only global configuration, such as
* the image dimensions, that are known at the time of writing the header of
* the JPEG XL file.
*
* If this returns 5, nothing needs to be done and the codestream can be
* compatible with any decoder. If this returns 10, JxlEncoderSetCodestreamLevel
* has to be used to set the codestream level to 10, or the encoder can be
* configured differently to allow using the more compatible level 5.
*
* @param enc encoder object.
* @return -1 if no level can support the configuration (e.g. image dimensions
* larger than even level 10 supports), 5 if level 5 is supported, 10 if setting
* the codestream level to 10 is required.
*
*/
JXL_EXPORT int JxlEncoderGetRequiredCodestreamLevel(const JxlEncoder* enc);
/**
* Enables lossless encoding.
*
@ -812,54 +978,67 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetCodestreamLevel(JxlEncoder* enc,
* 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 frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param lossless whether to override options for lossless mode
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
* otherwise.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameLossless(
JxlEncoderFrameSettings* frame_settings, JXL_BOOL lossless);
/** DEPRECATED: use JxlEncoderSetFrameLossless instead.
*/
JXL_EXPORT JxlEncoderStatus
JxlEncoderOptionsSetLossless(JxlEncoderOptions* options, JXL_BOOL lossless);
JxlEncoderOptionsSetLossless(JxlEncoderFrameSettings*, JXL_BOOL);
/**
* @param options set of encoder options to update with the new mode.
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @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.
* DEPRECATED: use JxlEncoderFrameSettingsSetOption(frame_settings,
* JXL_ENC_FRAME_SETTING_EFFORT, effort) instead.
*/
JXL_EXPORT JXL_DEPRECATED JxlEncoderStatus
JxlEncoderOptionsSetEffort(JxlEncoderOptions* options, int effort);
JxlEncoderOptionsSetEffort(JxlEncoderFrameSettings* frame_settings, int effort);
/**
* @param options set of encoder options to update with the new decoding speed
* tier.
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @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.
* DEPRECATED: use JxlEncoderFrameSettingsSetOption(frame_settings,
* JXL_ENC_FRAME_SETTING_DECODING_SPEED, tier) instead.
*/
JXL_EXPORT JXL_DEPRECATED JxlEncoderStatus
JxlEncoderOptionsSetDecodingSpeed(JxlEncoderOptions* options, int tier);
JXL_EXPORT JXL_DEPRECATED JxlEncoderStatus JxlEncoderOptionsSetDecodingSpeed(
JxlEncoderFrameSettings* frame_settings, 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
* 0.0 = mathematically lossless (however, use JxlEncoderSetFrameLossless
* instead to use true lossless, as setting distance to 0 alone is not the only
* requirement). 1.0 = visually lossless. Recommended range: 0.5 .. 3.0. Default
* value: 1.0.
*
* @param options set of encoder options to update with the new mode.
* @param frame_settings set of options and metadata for this frame. Also
* includes reference to the encoder object.
* @param distance the distance value to set.
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
* otherwise.
*/
JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameDistance(
JxlEncoderFrameSettings* frame_settings, float distance);
/** DEPRECATED: use JxlEncoderSetFrameDistance instead.
*/
JXL_EXPORT JxlEncoderStatus
JxlEncoderOptionsSetDistance(JxlEncoderOptions* options, float distance);
JxlEncoderOptionsSetDistance(JxlEncoderFrameSettings*, float);
/**
* Create a new set of encoder options, with all values initially copied from
@ -867,17 +1046,22 @@ JxlEncoderOptionsSetDistance(JxlEncoderOptions* options, float distance);
*
* The returned pointer is an opaque struct tied to the encoder and it will be
* deallocated by the encoder when JxlEncoderDestroy() is called. For functions
* taking both a @ref JxlEncoder and a @ref JxlEncoderOptions, only
* JxlEncoderOptions created with this function for the same encoder instance
* can be used.
* taking both a @ref JxlEncoder and a @ref JxlEncoderFrameSettings, only
* JxlEncoderFrameSettings created with this function for the same encoder
* instance can be used.
*
* @param enc encoder object.
* @param source source options to copy initial values from, or NULL to get
* defaults initialized to defaults.
* @return the opaque struct pointer identifying a new set of encoder options.
*/
JXL_EXPORT JxlEncoderOptions* JxlEncoderOptionsCreate(
JxlEncoder* enc, const JxlEncoderOptions* source);
JXL_EXPORT JxlEncoderFrameSettings* JxlEncoderFrameSettingsCreate(
JxlEncoder* enc, const JxlEncoderFrameSettings* source);
/** DEPRECATED: use JxlEncoderFrameSettingsCreate instead.
*/
JXL_EXPORT JxlEncoderFrameSettings* JxlEncoderOptionsCreate(
JxlEncoder*, const JxlEncoderFrameSettings*);
/**
* Sets a color encoding to be sRGB.

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

@ -94,7 +94,6 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/dec_patch_dictionary.h
jxl/dec_reconstruct.cc
jxl/dec_reconstruct.h
jxl/dec_render_pipeline.h
jxl/dec_transforms-inl.h
jxl/dec_upsample.cc
jxl/dec_upsample.h
@ -190,7 +189,36 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/quantizer.cc
jxl/quantizer.h
jxl/rational_polynomial-inl.h
jxl/render_pipeline/low_memory_render_pipeline.cc
jxl/render_pipeline/low_memory_render_pipeline.h
jxl/render_pipeline/render_pipeline.cc
jxl/render_pipeline/render_pipeline.h
jxl/render_pipeline/render_pipeline_stage.h
jxl/render_pipeline/simple_render_pipeline.cc
jxl/render_pipeline/simple_render_pipeline.h
jxl/render_pipeline/stage_chroma_upsampling.cc
jxl/render_pipeline/stage_chroma_upsampling.h
jxl/render_pipeline/stage_epf.cc
jxl/render_pipeline/stage_epf.h
jxl/render_pipeline/stage_gaborish.cc
jxl/render_pipeline/stage_gaborish.h
jxl/render_pipeline/stage_noise.cc
jxl/render_pipeline/stage_noise.h
jxl/render_pipeline/stage_patches.cc
jxl/render_pipeline/stage_patches.h
jxl/render_pipeline/stage_splines.cc
jxl/render_pipeline/stage_splines.h
jxl/render_pipeline/stage_upsampling.cc
jxl/render_pipeline/stage_upsampling.h
jxl/render_pipeline/stage_write_to_ib.cc
jxl/render_pipeline/stage_write_to_ib.h
jxl/render_pipeline/stage_xyb.cc
jxl/render_pipeline/stage_xyb.h
jxl/render_pipeline/stage_ycbcr.cc
jxl/render_pipeline/stage_ycbcr.h
jxl/render_pipeline/test_render_pipeline_stages.h
jxl/sanitizers.h
jxl/simd_util-inl.h
jxl/splines.cc
jxl/splines.h
jxl/toc.cc

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

@ -13,6 +13,7 @@
#include <hwy/tests/test_util-inl.h>
#include <utility>
#include "lib/jxl/base/random.h"
#include "lib/jxl/common.h"
#include "lib/jxl/dct_scales.h"
#include "lib/jxl/dec_transforms_testonly.h"
@ -32,9 +33,12 @@ class AcStrategyRoundtrip : public ::hwy::TestWithParamTargetAndT<int> {
float* scratch_space = mem.get();
float* coeffs = scratch_space + AcStrategy::kMaxCoeffArea;
float* idct = coeffs + AcStrategy::kMaxCoeffArea;
Rng rng(type * 65537 + 13);
for (size_t i = 0; i < std::min(1024u, 64u << acs.log2_covered_blocks());
i++) {
for (size_t j = 0; j < 64; j++) {
size_t i = (acs.log2_covered_blocks()
? rng.UniformU(0, 64u << acs.log2_covered_blocks())
: j);
float* input = idct + AcStrategy::kMaxCoeffArea;
std::fill_n(input, AcStrategy::kMaxCoeffArea, 0);
input[i] = 0.2f;
@ -88,9 +92,13 @@ class AcStrategyRoundtripDownsample
float* coeffs = scratch_space + AcStrategy::kMaxCoeffArea;
std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0.0f);
float* idct = coeffs + AcStrategy::kMaxCoeffArea;
Rng rng(type * 65537 + 13);
for (size_t y = 0; y < acs.covered_blocks_y(); y++) {
for (size_t x = 0; x < acs.covered_blocks_x(); x++) {
if (x > 4 || y > 4) {
if (rng.Bernoulli(0.9f)) continue;
}
float* dc = idct + AcStrategy::kMaxCoeffArea;
std::fill_n(dc, AcStrategy::kMaxCoeffArea, 0);
dc[y * acs.covered_blocks_x() * 8 + x] = 0.2f;
@ -141,9 +149,13 @@ class AcStrategyDownsample : public ::hwy::TestWithParamTargetAndT<int> {
float* scratch_space = mem.get();
float* idct = scratch_space + AcStrategy::kMaxCoeffArea;
float* idct_acs_downsampled = idct + AcStrategy::kMaxCoeffArea;
Rng rng(type * 65537 + 13);
for (size_t y = 0; y < cy; y++) {
for (size_t x = 0; x < cx; x++) {
if (x > 4 || y > 4) {
if (rng.Bernoulli(0.9f)) continue;
}
float* coeffs = idct + AcStrategy::kMaxCoeffArea;
std::fill_n(coeffs, AcStrategy::kMaxCoeffArea, 0);
coeffs[y * cx * 8 + x] = 0.2f;

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

@ -135,7 +135,7 @@ void EnsureUnchanged(const float background, const float foreground,
JXL_CHECK(
jxl::InitializePassesSharedState(frame_header, &state.shared_storage));
JXL_CHECK(state.Init());
state.InitForAC(/*pool=*/nullptr);
JXL_CHECK(state.InitForAC(/*pool=*/nullptr));
JXL_CHECK(state.filter_weights.Init(lf, frame_dim));
FillImage(-0.5f, &state.filter_weights.sigma);

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

@ -87,9 +87,9 @@ TEST(ANSTest, SingleSymbolRoundtrip) {
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
constexpr size_t kReps = 10;
constexpr size_t kReps = 3;
#else
constexpr size_t kReps = 100;
constexpr size_t kReps = 10;
#endif
void RoundtripRandomStream(int alphabet_size, size_t reps = kReps,

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

@ -84,17 +84,6 @@
#define JXL_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
#endif
#if JXL_COMPILER_MSVC
#include <intrin.h>
#pragma intrinsic(_ReadWriteBarrier)
#define JXL_COMPILER_FENCE _ReadWriteBarrier()
#elif JXL_COMPILER_GCC || JXL_COMPILER_CLANG
#define JXL_COMPILER_FENCE asm volatile("" : : : "memory")
#else
#define JXL_COMPILER_FENCE
#endif
// Returns a void* pointer which the compiler then assumes is N-byte aligned.
// Example: float* JXL_RESTRICT aligned = (float*)JXL_ASSUME_ALIGNED(in, 32);
//

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

@ -15,16 +15,15 @@
#include "jxl/parallel_runner.h"
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/status.h"
#if JXL_COMPILER_MSVC
// suppress warnings about the const & applied to function types
#pragma warning(disable : 4180)
#endif
namespace jxl {
class ThreadPool {
public:
// Use this type as an InitFunc to skip the initialization step in Run().
// When this is used the return value of Run() is always true and does not
// need to be checked.
struct SkipInit {};
ThreadPool(JxlParallelRunner runner, void* runner_opaque)
: runner_(runner ? runner : &ThreadPool::SequentialRunnerStatic),
runner_opaque_(runner ? runner_opaque : static_cast<void*>(this)) {}
@ -47,21 +46,16 @@ class ThreadPool {
if (begin == end) return true;
RunCallState<InitFunc, DataFunc> call_state(init_func, data_func);
// The runner_ uses the C convention and returns 0 in case of error, so we
// convert it to an Status.
// convert it to a Status.
return (*runner_)(runner_opaque_, static_cast<void*>(&call_state),
&call_state.CallInitFunc, &call_state.CallDataFunc, begin,
end) == 0;
}
// Specialization that returns bool when SkipInit is used.
template <class DataFunc>
bool Run(uint32_t begin, uint32_t end, const SkipInit /* tag */,
const DataFunc& data_func, const char* caller = "") {
return Run(begin, end, ReturnTrueInit, data_func, caller);
}
// Use this as init_func when no initialization is needed.
static Status NoInit(size_t num_threads) { return true; }
private:
static Status ReturnTrueInit(size_t num_threads) { return true; }
// class holding the state of a Run() call to pass to the runner_ as an
// opaque_jpegxl pointer.
@ -104,21 +98,21 @@ class ThreadPool {
void* const runner_opaque_;
};
// TODO(deymo): Convert the return value to a Status when not using SkipInit.
template <class InitFunc, class DataFunc>
bool RunOnPool(ThreadPool* pool, const uint32_t begin, const uint32_t end,
const InitFunc& init_func, const DataFunc& data_func,
const char* caller) {
Status ret = true;
Status RunOnPool(ThreadPool* pool, const uint32_t begin, const uint32_t end,
const InitFunc& init_func, const DataFunc& data_func,
const char* caller) {
if (pool == nullptr) {
ThreadPool default_pool(nullptr, nullptr);
ret = default_pool.Run(begin, end, init_func, data_func, caller);
return default_pool.Run(begin, end, init_func, data_func, caller);
} else {
ret = pool->Run(begin, end, init_func, data_func, caller);
return pool->Run(begin, end, init_func, data_func, caller);
}
return ret;
}
} // namespace jxl
#if JXL_COMPILER_MSVC
#pragma warning(default : 4180)
#endif
#endif // LIB_JXL_BASE_DATA_PARALLEL_H_

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

@ -53,88 +53,91 @@ struct Symbol {
// Reading from output gives the same values.
TEST(BitReaderTest, TestRoundTrip) {
ThreadPoolInternal pool(8);
pool.Run(0, 1000, ThreadPool::SkipInit(),
[](const int task, const int /* thread */) {
constexpr size_t kMaxBits = 8000;
BitWriter writer;
BitWriter::Allotment allotment(&writer, kMaxBits);
EXPECT_TRUE(RunOnPool(
&pool, 0, 1000, ThreadPool::NoInit,
[](const uint32_t task, size_t /* thread */) {
constexpr size_t kMaxBits = 8000;
BitWriter writer;
BitWriter::Allotment allotment(&writer, kMaxBits);
std::vector<Symbol> symbols;
symbols.reserve(1000);
std::vector<Symbol> symbols;
symbols.reserve(1000);
Rng rng(55537 + 129 * task);
Rng rng(55537 + 129 * task);
for (;;) {
const uint32_t num_bits = rng.UniformU(1, 33);
if (writer.BitsWritten() + num_bits > kMaxBits) break;
const uint32_t value = rng.UniformU(0, 1ULL << num_bits);
symbols.push_back({num_bits, value});
writer.Write(num_bits, value);
}
for (;;) {
const uint32_t num_bits = rng.UniformU(1, 33);
if (writer.BitsWritten() + num_bits > kMaxBits) break;
const uint32_t value = rng.UniformU(0, 1ULL << num_bits);
symbols.push_back({num_bits, value});
writer.Write(num_bits, value);
}
writer.ZeroPadToByte();
ReclaimAndCharge(&writer, &allotment, 0, nullptr);
BitReader reader(writer.GetSpan());
for (const Symbol& s : symbols) {
EXPECT_EQ(s.value, reader.ReadBits(s.num_bits));
}
EXPECT_TRUE(reader.Close());
});
writer.ZeroPadToByte();
ReclaimAndCharge(&writer, &allotment, 0, nullptr);
BitReader reader(writer.GetSpan());
for (const Symbol& s : symbols) {
EXPECT_EQ(s.value, reader.ReadBits(s.num_bits));
}
EXPECT_TRUE(reader.Close());
},
"TestTBitReaderRoundTrip"));
}
// SkipBits is the same as reading that many bits.
TEST(BitReaderTest, TestSkip) {
ThreadPoolInternal pool(8);
pool.Run(0, 96, ThreadPool::SkipInit(),
[](const int task, const int /* thread */) {
constexpr size_t kSize = 100;
EXPECT_TRUE(RunOnPool(
&pool, 0, 96, ThreadPool::NoInit,
[](const uint32_t task, size_t /* thread */) {
constexpr size_t kSize = 100;
for (size_t skip = 0; skip < 128; ++skip) {
BitWriter writer;
BitWriter::Allotment allotment(&writer, kSize * kBitsPerByte);
// Start with "task" 1-bits.
for (int i = 0; i < task; ++i) {
writer.Write(1, 1);
}
for (size_t skip = 0; skip < 128; ++skip) {
BitWriter writer;
BitWriter::Allotment allotment(&writer, kSize * kBitsPerByte);
// Start with "task" 1-bits.
for (size_t i = 0; i < task; ++i) {
writer.Write(1, 1);
}
// Write 0-bits that we will skip over
for (size_t i = 0; i < skip; ++i) {
writer.Write(1, 0);
}
// Write 0-bits that we will skip over
for (size_t i = 0; i < skip; ++i) {
writer.Write(1, 0);
}
// Write terminator bits '101'
writer.Write(3, 5);
EXPECT_EQ(task + skip + 3, writer.BitsWritten());
writer.ZeroPadToByte();
AuxOut aux_out;
ReclaimAndCharge(&writer, &allotment, 0, &aux_out);
EXPECT_LT(aux_out.layers[0].total_bits, kSize * 8);
// Write terminator bits '101'
writer.Write(3, 5);
EXPECT_EQ(task + skip + 3, writer.BitsWritten());
writer.ZeroPadToByte();
AuxOut aux_out;
ReclaimAndCharge(&writer, &allotment, 0, &aux_out);
EXPECT_LT(aux_out.layers[0].total_bits, kSize * 8);
BitReader reader1(writer.GetSpan());
BitReader reader2(writer.GetSpan());
// Verify initial 1-bits
for (int i = 0; i < task; ++i) {
EXPECT_EQ(1u, reader1.ReadBits(1));
EXPECT_EQ(1u, reader2.ReadBits(1));
}
BitReader reader1(writer.GetSpan());
BitReader reader2(writer.GetSpan());
// Verify initial 1-bits
for (size_t i = 0; i < task; ++i) {
EXPECT_EQ(1u, reader1.ReadBits(1));
EXPECT_EQ(1u, reader2.ReadBits(1));
}
// SkipBits or manually read "skip" bits
reader1.SkipBits(skip);
for (size_t i = 0; i < skip; ++i) {
EXPECT_EQ(0u, reader2.ReadBits(1))
<< " skip=" << skip << " i=" << i;
}
EXPECT_EQ(reader1.TotalBitsConsumed(),
reader2.TotalBitsConsumed());
// SkipBits or manually read "skip" bits
reader1.SkipBits(skip);
for (size_t i = 0; i < skip; ++i) {
EXPECT_EQ(0u, reader2.ReadBits(1))
<< " skip=" << skip << " i=" << i;
}
EXPECT_EQ(reader1.TotalBitsConsumed(), reader2.TotalBitsConsumed());
// Ensure both readers see the terminator bits.
EXPECT_EQ(5u, reader1.ReadBits(3));
EXPECT_EQ(5u, reader2.ReadBits(3));
// Ensure both readers see the terminator bits.
EXPECT_EQ(5u, reader1.ReadBits(3));
EXPECT_EQ(5u, reader2.ReadBits(3));
EXPECT_TRUE(reader1.Close());
EXPECT_TRUE(reader2.Close());
}
});
EXPECT_TRUE(reader1.Close());
EXPECT_TRUE(reader2.Close());
}
},
"TestSkip"));
}
// Verifies byte order and different groupings of bits.

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

@ -338,11 +338,11 @@ ImageBlender::RectBlender ImageBlender::PrepareRect(
return blender;
}
Status PerformBlending(
const float* const* bg, const float* const* fg, float* const* out,
size_t xsize, const PatchBlending& color_blending,
const PatchBlending* ec_blending,
const std::vector<ExtraChannelInfo>& extra_channel_info) {
void PerformBlending(const float* const* bg, const float* const* fg,
float* const* out, size_t x0, size_t xsize,
const PatchBlending& color_blending,
const PatchBlending* ec_blending,
const std::vector<ExtraChannelInfo>& extra_channel_info) {
bool has_alpha = false;
size_t num_ec = extra_channel_info.size();
for (size_t i = 0; i < num_ec; i++) {
@ -356,35 +356,37 @@ Status PerformBlending(
for (size_t i = 0; i < num_ec; i++) {
if (ec_blending[i].mode == PatchBlendMode::kAdd) {
for (size_t x = 0; x < xsize; x++) {
tmp.Row(3 + i)[x] = bg[3 + i][x] + fg[3 + i][x];
tmp.Row(3 + i)[x] = bg[3 + i][x + x0] + fg[3 + i][x + x0];
}
} else if (ec_blending[i].mode == PatchBlendMode::kBlendAbove) {
size_t alpha = ec_blending[i].alpha_channel;
bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
PerformAlphaBlending(bg[3 + i], bg[3 + alpha], fg[3 + i], fg[3 + alpha],
tmp.Row(3 + i), xsize, is_premultiplied,
ec_blending[i].clamp);
PerformAlphaBlending(bg[3 + i] + x0, bg[3 + alpha] + x0, fg[3 + i] + x0,
fg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
is_premultiplied, ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kBlendBelow) {
size_t alpha = ec_blending[i].alpha_channel;
bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
PerformAlphaBlending(fg[3 + i], fg[3 + alpha], bg[3 + i], bg[3 + alpha],
tmp.Row(3 + i), xsize, is_premultiplied,
ec_blending[i].clamp);
PerformAlphaBlending(fg[3 + i] + x0, fg[3 + alpha] + x0, bg[3 + i] + x0,
bg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
is_premultiplied, ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kAlphaWeightedAddAbove) {
size_t alpha = ec_blending[i].alpha_channel;
PerformAlphaWeightedAdd(bg[3 + i], fg[3 + i], fg[3 + alpha],
tmp.Row(3 + i), xsize, ec_blending[i].clamp);
PerformAlphaWeightedAdd(bg[3 + i] + x0, fg[3 + i] + x0,
fg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kAlphaWeightedAddBelow) {
size_t alpha = ec_blending[i].alpha_channel;
PerformAlphaWeightedAdd(fg[3 + i], bg[3 + i], bg[3 + alpha],
tmp.Row(3 + i), xsize, ec_blending[i].clamp);
PerformAlphaWeightedAdd(fg[3 + i] + x0, bg[3 + i] + x0,
bg[3 + alpha] + x0, tmp.Row(3 + i), xsize,
ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kMul) {
PerformMulBlending(bg[3 + i], fg[3 + i], tmp.Row(3 + i), xsize,
PerformMulBlending(bg[3 + i] + x0, fg[3 + i] + x0, tmp.Row(3 + i), xsize,
ec_blending[i].clamp);
} else if (ec_blending[i].mode == PatchBlendMode::kReplace) {
memcpy(tmp.Row(3 + i), fg[3 + i], xsize * sizeof(**fg));
memcpy(tmp.Row(3 + i), fg[3 + i] + x0, xsize * sizeof(**fg));
} else if (ec_blending[i].mode == PatchBlendMode::kNone) {
memcpy(tmp.Row(3 + i), bg[3 + i], xsize * sizeof(**fg));
memcpy(tmp.Row(3 + i), bg[3 + i] + x0, xsize * sizeof(**fg));
} else {
JXL_ABORT("Unreachable");
}
@ -399,7 +401,7 @@ Status PerformBlending(
for (int p = 0; p < 3; p++) {
float* out = tmp.Row(p);
for (size_t x = 0; x < xsize; x++) {
out[x] = bg[p][x] + fg[p][x];
out[x] = bg[p][x + x0] + fg[p][x + x0];
}
}
} else if (color_blending.mode == PatchBlendMode::kBlendAbove
@ -407,8 +409,8 @@ Status PerformBlending(
&& has_alpha) {
bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
PerformAlphaBlending(
{bg[0], bg[1], bg[2], bg[3 + alpha]},
{fg[0], fg[1], fg[2], fg[3 + alpha]},
{bg[0] + x0, bg[1] + x0, bg[2] + x0, bg[3 + alpha] + x0},
{fg[0] + x0, fg[1] + x0, fg[2] + x0, fg[3 + alpha] + x0},
{tmp.Row(0), tmp.Row(1), tmp.Row(2), tmp.Row(3 + alpha)}, xsize,
is_premultiplied, color_blending.clamp);
} else if (color_blending.mode == PatchBlendMode::kBlendBelow
@ -416,43 +418,43 @@ Status PerformBlending(
&& has_alpha) {
bool is_premultiplied = extra_channel_info[alpha].alpha_associated;
PerformAlphaBlending(
{fg[0], fg[1], fg[2], fg[3 + alpha]},
{bg[0], bg[1], bg[2], bg[3 + alpha]},
{fg[0] + x0, fg[1] + x0, fg[2] + x0, fg[3 + alpha] + x0},
{bg[0] + x0, bg[1] + x0, bg[2] + x0, bg[3 + alpha] + x0},
{tmp.Row(0), tmp.Row(1), tmp.Row(2), tmp.Row(3 + alpha)}, xsize,
is_premultiplied, color_blending.clamp);
} else if (color_blending.mode == PatchBlendMode::kAlphaWeightedAddAbove) {
JXL_DASSERT(has_alpha);
for (size_t c = 0; c < 3; c++) {
PerformAlphaWeightedAdd(bg[c], fg[c], fg[3 + alpha], tmp.Row(c), xsize,
color_blending.clamp);
PerformAlphaWeightedAdd(bg[c] + x0, fg[c] + x0, fg[3 + alpha] + x0,
tmp.Row(c), xsize, color_blending.clamp);
}
} else if (color_blending.mode == PatchBlendMode::kAlphaWeightedAddBelow) {
JXL_DASSERT(has_alpha);
for (size_t c = 0; c < 3; c++) {
PerformAlphaWeightedAdd(fg[c], bg[c], bg[3 + alpha], tmp.Row(c), xsize,
color_blending.clamp);
PerformAlphaWeightedAdd(fg[c] + x0, bg[c] + x0, bg[3 + alpha] + x0,
tmp.Row(c), xsize, color_blending.clamp);
}
} else if (color_blending.mode == PatchBlendMode::kMul) {
for (int p = 0; p < 3; p++) {
PerformMulBlending(bg[p], fg[p], tmp.Row(p), xsize, color_blending.clamp);
PerformMulBlending(bg[p] + x0, fg[p] + x0, tmp.Row(p), xsize,
color_blending.clamp);
}
} else if (color_blending.mode == PatchBlendMode::kReplace ||
color_blending.mode == PatchBlendMode::kBlendAbove ||
color_blending.mode == PatchBlendMode::kBlendBelow) { // kReplace
for (size_t p = 0; p < 3; p++) {
memcpy(tmp.Row(p), fg[p], xsize * sizeof(**fg));
memcpy(tmp.Row(p), fg[p] + x0, xsize * sizeof(**fg));
}
} else if (color_blending.mode == PatchBlendMode::kNone) {
for (size_t p = 0; p < 3; p++) {
memcpy(tmp.Row(p), bg[p], xsize * sizeof(**fg));
memcpy(tmp.Row(p), bg[p] + x0, xsize * sizeof(**fg));
}
} else {
JXL_ABORT("Unreachable");
}
for (size_t i = 0; i < 3 + num_ec; i++) {
memcpy(out[i], tmp.Row(i), xsize * sizeof(**out));
memcpy(out[i] + x0, tmp.Row(i), xsize * sizeof(**out));
}
return true;
}
Status ImageBlender::RectBlender::DoBlending(size_t y) {
@ -469,10 +471,11 @@ Status ImageBlender::RectBlender::DoBlending(size_t y) {
bg_row_ptrs_[c] = bg_ptrs_[c] + y * bg_strides_[c];
out_row_ptrs_[c] = out_ptrs_[c] + y * out_strides_[c];
}
return PerformBlending(bg_row_ptrs_.data(), fg_row_ptrs_.data(),
out_row_ptrs_.data(), current_overlap_.xsize(),
blending_info_[0], blending_info_.data() + 1,
*extra_channel_info_);
PerformBlending(bg_row_ptrs_.data(), fg_row_ptrs_.data(),
out_row_ptrs_.data(), 0, current_overlap_.xsize(),
blending_info_[0], blending_info_.data() + 1,
*extra_channel_info_);
return true;
}
} // namespace jxl

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

@ -11,11 +11,11 @@
namespace jxl {
Status PerformBlending(const float* const* bg, const float* const* fg,
float* const* out, size_t xsize,
const PatchBlending& color_blending,
const PatchBlending* ec_blending,
const std::vector<ExtraChannelInfo>& extra_channel_info);
void PerformBlending(const float* const* bg, const float* const* fg,
float* const* out, size_t x0, size_t xsize,
const PatchBlending& color_blending,
const PatchBlending* ec_blending,
const std::vector<ExtraChannelInfo>& extra_channel_info);
class ImageBlender {
public:

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

@ -17,6 +17,7 @@
#include "lib/jxl/common.h"
#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_butteraugli_pnorm.h"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/enc_external_image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/memory_manager_internal.h"
@ -77,6 +78,7 @@ struct JxlButteraugliApiStruct {
// Number of nits that correspond to 1.0f input values.
float intensity_target = jxl::kDefaultIntensityTarget;
JxlCmsInterface cms;
JxlMemoryManager memory_manager;
std::unique_ptr<jxl::ThreadPool> thread_pool{nullptr};
};
@ -92,6 +94,7 @@ JxlButteraugliApi* JxlButteraugliApiCreate(
if (!alloc) return nullptr;
// Placement new constructor on allocated memory
JxlButteraugliApi* ret = new (alloc) JxlButteraugliApi();
ret->cms = jxl::GetJxlCms();
ret->memory_manager = local_memory_manager;
return ret;
}
@ -164,8 +167,8 @@ JxlButteraugliResult* JxlButteraugliCompute(
result->params.hf_asymmetry = api->hf_asymmetry;
result->params.xmul = api->xmul;
result->params.intensity_target = api->intensity_target;
jxl::ButteraugliDistance(orig_ib, dist_ib, result->params, &result->distmap,
api->thread_pool.get());
jxl::ButteraugliDistance(orig_ib, dist_ib, result->params, api->cms,
&result->distmap, api->thread_pool.get());
return result;
}

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

@ -69,11 +69,6 @@ struct Blobs {
PaddedBytes xmp;
};
// For Codec::kJPG, convert between JPEG and pixels or between JPEG and
// quantized DCT coefficients
// For pixel data, the nominal range is 0..1.
enum class DecodeTarget { kPixels, kQuantizedCoeffs };
// Holds a preview, a main image or one or more frames, plus the inputs/outputs
// to/from decoding/encoding.
class CodecInOut {
@ -135,13 +130,13 @@ class CodecInOut {
}
// Calls TransformTo for each ImageBundle (preview/frames).
Status TransformTo(const ColorEncoding& c_desired,
Status TransformTo(const ColorEncoding& c_desired, const JxlCmsInterface& cms,
ThreadPool* pool = nullptr) {
if (metadata.m.have_preview) {
JXL_RETURN_IF_ERROR(preview_frame.TransformTo(c_desired, pool));
JXL_RETURN_IF_ERROR(preview_frame.TransformTo(c_desired, cms, pool));
}
for (ImageBundle& ib : frames) {
JXL_RETURN_IF_ERROR(ib.TransformTo(c_desired, pool));
JXL_RETURN_IF_ERROR(ib.TransformTo(c_desired, cms, pool));
}
return true;
}
@ -162,27 +157,6 @@ class CodecInOut {
// -- DECODER INPUT:
SizeConstraints constraints;
// Decode to pixels or keep JPEG as quantized DCT coefficients
DecodeTarget dec_target = DecodeTarget::kPixels;
// Intended white luminance, in nits (cd/m^2).
// It is used by codecs that do not know the absolute luminance of their
// images. For those codecs, decoders map from white to this luminance. There
// is no other way of knowing the target brightness for those codecs - depends
// on source material. 709 typically targets 100 nits, BT.2100 PQ up to 10K,
// but HDR content is more typically mastered to 4K nits. Codecs that do know
// the absolute luminance of their images will typically ignore it as a
// decoder input. The corresponding decoder output and encoder input is the
// intensity target in the metadata. ALL decoders MUST set that metadata
// appropriately, but it does not have to be identical to this hint. Encoders
// for codecs that do not encode absolute luminance levels should use that
// metadata to decide on what to map to white. Encoders for codecs that *do*
// encode absolute luminance levels may use it to decide on encoding values,
// but not in a way that would affect the range of interpreted luminance.
//
// 0 means that it is up to the codec to decide on a reasonable value to use.
float target_nits = 0;
// -- DECODER OUTPUT:

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

@ -40,7 +40,6 @@ void RoundtripPermutation(coeff_order_t* perm, coeff_order_t* out, size_t len,
enum Permutation { kIdentity, kFewSwaps, kFewSlides, kRandom };
constexpr size_t kNumReps = 128;
constexpr size_t kSwaps = 32;
void TestPermutation(Permutation kind, size_t len) {
@ -72,11 +71,9 @@ void TestPermutation(Permutation kind, size_t len) {
}
std::vector<coeff_order_t> out(len);
size_t size = 0;
for (size_t i = 0; i < kNumReps; i++) {
RoundtripPermutation(perm.data(), out.data(), len, &size);
for (size_t idx = 0; idx < len; idx++) {
EXPECT_EQ(perm[idx], out[idx]);
}
RoundtripPermutation(perm.data(), out.data(), len, &size);
for (size_t idx = 0; idx < len; idx++) {
EXPECT_EQ(perm[idx], out[idx]);
}
printf("Encoded size: %" PRIuS "\n", size);
}

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

@ -703,8 +703,8 @@ Status AdaptToXYZD50(float wx, float wy, float matrix[9]) {
return true;
}
Status PrimariesToXYZD50(float rx, float ry, float gx, float gy, float bx,
float by, float wx, float wy, float matrix[9]) {
Status PrimariesToXYZ(float rx, float ry, float gx, float gy, float bx,
float by, float wx, float wy, float matrix[9]) {
if (wx < 0 || wx > 1 || wy <= 0 || wy > 1) {
return JXL_FAILURE("Invalid white point");
}
@ -727,9 +727,14 @@ Status PrimariesToXYZD50(float rx, float ry, float gx, float gy, float bx,
xyz[0], 0, 0, 0, xyz[1], 0, 0, 0, xyz[2],
};
float toXYZ[9];
MatMul(primaries, a, 3, 3, 3, toXYZ);
MatMul(primaries, a, 3, 3, 3, matrix);
return true;
}
Status PrimariesToXYZD50(float rx, float ry, float gx, float gy, float bx,
float by, float wx, float wy, float matrix[9]) {
float toXYZ[9];
JXL_RETURN_IF_ERROR(PrimariesToXYZ(rx, ry, gx, gy, bx, by, wx, wy, toXYZ));
float d50[9];
JXL_RETURN_IF_ERROR(AdaptToXYZD50(wx, wy, d50));

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

@ -450,6 +450,8 @@ void ConvertInternalToExternalColorEncoding(const jxl::ColorEncoding& internal,
Status ConvertExternalToInternalColorEncoding(const JxlColorEncoding& external,
jxl::ColorEncoding* internal);
Status PrimariesToXYZ(float rx, float ry, float gx, float gy, float bx,
float by, float wx, float wy, float matrix[9]);
Status PrimariesToXYZD50(float rx, float ry, float gx, float gy, float bx,
float by, float wx, float wy, float matrix[9]);
Status AdaptToXYZD50(float wx, float wy, float matrix[9]);

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

@ -3,12 +3,6 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Defined by build system; this avoids IDE warnings. Must come before
// color_management.h (affects header definitions).
#ifndef JPEGXL_ENABLE_SKCMS
#define JPEGXL_ENABLE_SKCMS 0
#endif
#include "lib/jxl/color_management.h"
#include <math.h>

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

@ -132,20 +132,21 @@ class ColorManagementTest
static void VerifyPixelRoundTrip(const ColorEncoding& c) {
Globals* g = Globals::GetInstance();
const ColorEncoding& c_native = c.IsGray() ? g->c_gray : g->c_native;
ColorSpaceTransform xform_fwd;
ColorSpaceTransform xform_rev;
ASSERT_TRUE(xform_fwd.Init(c_native, c, kDefaultIntensityTarget, kWidth,
const JxlCmsInterface& cms = GetJxlCms();
ColorSpaceTransform xform_fwd(cms);
ColorSpaceTransform xform_rev(cms);
const float intensity_target =
c.tf.IsHLG() ? 1000 : kDefaultIntensityTarget;
ASSERT_TRUE(xform_fwd.Init(c_native, c, intensity_target, kWidth,
g->pool.NumThreads()));
ASSERT_TRUE(xform_rev.Init(c, c_native, kDefaultIntensityTarget, kWidth,
ASSERT_TRUE(xform_rev.Init(c, c_native, intensity_target, kWidth,
g->pool.NumThreads()));
const size_t thread = 0;
const ImageF& in = c.IsGray() ? g->in_gray : g->in_color;
ImageF* JXL_RESTRICT out = c.IsGray() ? &g->out_gray : &g->out_color;
DoColorSpaceTransform(&xform_fwd, thread, in.Row(0),
xform_fwd.BufDst(thread));
DoColorSpaceTransform(&xform_rev, thread, xform_fwd.BufDst(thread),
out->Row(0));
ASSERT_TRUE(xform_fwd.Run(thread, in.Row(0), xform_fwd.BufDst(thread)));
ASSERT_TRUE(xform_rev.Run(thread, xform_fwd.BufDst(thread), out->Row(0)));
#if JPEGXL_ENABLE_SKCMS
double max_l1 = 7E-4;
@ -222,18 +223,18 @@ TEST_F(ColorManagementTest, D2700ToSRGB) {
ColorEncoding sRGB_D2700;
ASSERT_TRUE(sRGB_D2700.SetICC(std::move(icc)));
ColorSpaceTransform transform;
ColorSpaceTransform transform(GetJxlCms());
ASSERT_TRUE(transform.Init(sRGB_D2700, ColorEncoding::SRGB(),
kDefaultIntensityTarget, 1, 1));
const float sRGB_D2700_values[3] = {0.863, 0.737, 0.490};
float sRGB_values[3];
DoColorSpaceTransform(&transform, 0, sRGB_D2700_values, sRGB_values);
ASSERT_TRUE(transform.Run(0, sRGB_D2700_values, sRGB_values));
EXPECT_THAT(sRGB_values,
ElementsAre(FloatNear(0.914, 1e-3), FloatNear(0.745, 1e-3),
FloatNear(0.601, 1e-3)));
}
TEST_F(ColorManagementTest, P3HLGTo2020HLG) {
TEST_F(ColorManagementTest, P3HlgTo2020Hlg) {
ColorEncoding p3_hlg;
p3_hlg.SetColorSpace(ColorSpace::kRGB);
p3_hlg.white_point = WhitePoint::kD65;
@ -245,15 +246,76 @@ TEST_F(ColorManagementTest, P3HLGTo2020HLG) {
rec2020_hlg.primaries = Primaries::k2100;
ASSERT_TRUE(rec2020_hlg.CreateICC());
ColorSpaceTransform transform;
ColorSpaceTransform transform(GetJxlCms());
ASSERT_TRUE(transform.Init(p3_hlg, rec2020_hlg, 1000, 1, 1));
const float p3_hlg_values[3] = {0., 0.75, 0.};
float rec2020_hlg_values[3];
DoColorSpaceTransform(&transform, 0, p3_hlg_values, rec2020_hlg_values);
ASSERT_TRUE(transform.Run(0, p3_hlg_values, rec2020_hlg_values));
EXPECT_THAT(rec2020_hlg_values,
ElementsAre(FloatNear(0.3973, 1e-4), FloatNear(0.7382, 1e-4),
FloatNear(0.1183, 1e-4)));
}
TEST_F(ColorManagementTest, HlgOotf) {
ColorEncoding p3_hlg;
p3_hlg.SetColorSpace(ColorSpace::kRGB);
p3_hlg.white_point = WhitePoint::kD65;
p3_hlg.primaries = Primaries::kP3;
p3_hlg.tf.SetTransferFunction(TransferFunction::kHLG);
ASSERT_TRUE(p3_hlg.CreateICC());
ColorSpaceTransform transform_to_1000(GetJxlCms());
ASSERT_TRUE(
transform_to_1000.Init(p3_hlg, ColorEncoding::LinearSRGB(), 1000, 1, 1));
// HDR reference white: https://www.itu.int/pub/R-REP-BT.2408-4-2021
float p3_hlg_values[3] = {0.75, 0.75, 0.75};
float linear_srgb_values[3];
ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values, linear_srgb_values));
// On a 1000-nit display, HDR reference white should be 203 cd/m² which is
// 0.203 times the maximum.
EXPECT_THAT(linear_srgb_values,
ElementsAre(FloatNear(0.203, 1e-3), FloatNear(0.203, 1e-3),
FloatNear(0.203, 1e-3)));
ColorSpaceTransform transform_to_400(GetJxlCms());
ASSERT_TRUE(
transform_to_400.Init(p3_hlg, ColorEncoding::LinearSRGB(), 400, 1, 1));
ASSERT_TRUE(transform_to_400.Run(0, p3_hlg_values, linear_srgb_values));
// On a 400-nit display, it should be 100 cd/m².
EXPECT_THAT(linear_srgb_values,
ElementsAre(FloatNear(0.250, 1e-3), FloatNear(0.250, 1e-3),
FloatNear(0.250, 1e-3)));
p3_hlg_values[2] = 0.50;
ASSERT_TRUE(transform_to_1000.Run(0, p3_hlg_values, linear_srgb_values));
EXPECT_THAT(linear_srgb_values,
ElementsAre(FloatNear(0.201, 1e-3), FloatNear(0.201, 1e-3),
FloatNear(0.050, 1e-3)));
ColorSpaceTransform transform_from_400(GetJxlCms());
ASSERT_TRUE(
transform_from_400.Init(ColorEncoding::LinearSRGB(), p3_hlg, 400, 1, 1));
linear_srgb_values[0] = linear_srgb_values[1] = linear_srgb_values[2] = 0.250;
ASSERT_TRUE(transform_from_400.Run(0, linear_srgb_values, p3_hlg_values));
EXPECT_THAT(p3_hlg_values,
ElementsAre(FloatNear(0.75, 1e-3), FloatNear(0.75, 1e-3),
FloatNear(0.75, 1e-3)));
ColorEncoding grayscale_hlg;
grayscale_hlg.SetColorSpace(ColorSpace::kGray);
grayscale_hlg.white_point = WhitePoint::kD65;
grayscale_hlg.tf.SetTransferFunction(TransferFunction::kHLG);
ASSERT_TRUE(grayscale_hlg.CreateICC());
ColorSpaceTransform grayscale_transform(GetJxlCms());
ASSERT_TRUE(grayscale_transform.Init(
grayscale_hlg, ColorEncoding::LinearSRGB(/*is_gray=*/true), 1000, 1, 1));
const float grayscale_hlg_value = 0.75;
float linear_grayscale_value;
ASSERT_TRUE(grayscale_transform.Run(0, &grayscale_hlg_value,
&linear_grayscale_value));
EXPECT_THAT(linear_grayscale_value, FloatNear(0.203, 1e-3));
}
} // namespace
} // namespace jxl

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

@ -117,6 +117,13 @@ struct FrameDimensions {
num_dc_groups = xsize_dc_groups * ysize_dc_groups;
}
size_t GetUpsampledXSize(bool is_color_c) {
return is_color_c ? xsize_upsampled_padded : xsize_upsampled;
}
size_t GetUpsampledYSize(bool is_color_c) {
return is_color_c ? ysize_upsampled_padded : ysize_upsampled;
}
// Image size without any upsampling, i.e. original_size / upsampling.
size_t xsize;
size_t ysize;

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

@ -149,7 +149,7 @@ void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
xsize * sizeof(float));
}
}
auto process_row = [&](int y, int /*thread*/) {
auto process_row = [&](const uint32_t y, size_t /*thread*/) {
const float* JXL_RESTRICT rows_top[3]{
dc->ConstPlaneRow(0, y - 1),
dc->ConstPlaneRow(1, y - 1),
@ -193,8 +193,8 @@ void AdaptiveDCSmoothing(const float* dc_factors, Image3F* dc,
x);
}
};
RunOnPool(pool, 1, ysize - 1, ThreadPool::SkipInit(), process_row,
"DCSmoothingRow");
JXL_CHECK(RunOnPool(pool, 1, ysize - 1, ThreadPool::NoInit, process_row,
"DCSmoothingRow"));
dc->Swap(smoothed);
}

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

@ -21,6 +21,7 @@
#include "lib/jxl/enc_adaptive_quantization.h"
#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_cache.h"
#include "lib/jxl/enc_color_management.h"
#include "lib/jxl/enc_params.h"
#include "lib/jxl/enc_xyb.h"
#include "lib/jxl/frame_header.h"
@ -49,7 +50,7 @@ void RunRGBRoundTrip(float distance, bool fast) {
io.ShrinkTo(std::min(io.xsize(), kGroupDim), std::min(io.ysize(), kGroupDim));
Image3F opsin(io.xsize(), io.ysize());
(void)ToXYB(io.Main(), &pool, &opsin);
(void)ToXYB(io.Main(), &pool, &opsin, GetJxlCms());
opsin = PadImageToMultiple(opsin, kBlockDim);
GaborishInverse(&opsin, 1.0f, &pool);
@ -82,10 +83,10 @@ void RunRGBRoundTrip(float distance, bool fast) {
enc_state.cparams = cparams;
ZeroFillImage(&enc_state.shared.epf_sharpness);
CodecInOut io1;
io1.Main() = RoundtripImage(opsin, &enc_state, &pool);
io1.Main() = RoundtripImage(opsin, &enc_state, GetJxlCms(), &pool);
io1.metadata.m.color_encoding = io1.Main().c_current();
EXPECT_LE(ButteraugliDistance(io, io1, cparams.ba_params,
EXPECT_LE(ButteraugliDistance(io, io1, cparams.ba_params, GetJxlCms(),
/*distmap=*/nullptr, &pool),
1.2);
}

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

@ -839,13 +839,13 @@ class ConvolveT {
const Weights& weights,
ThreadPool* pool, ImageF* out) {
const int64_t stride = in.PixelsPerRow();
RunOnPool(
pool, ybegin, yend, ThreadPool::SkipInit(),
[&](const int y, int /*thread*/) HWY_ATTR {
JXL_CHECK(RunOnPool(
pool, ybegin, yend, ThreadPool::NoInit,
[&](const uint32_t y, size_t /*thread*/) HWY_ATTR {
RunRow<kSizeModN>(rect.ConstRow(in, y), rect.xsize(), stride,
WrapRowUnchanged(), weights, out->Row(y));
},
"Convolve");
"Convolve"));
}
// Image3F.
@ -856,16 +856,16 @@ class ConvolveT {
const Weights& weights,
ThreadPool* pool, Image3F* out) {
const int64_t stride = in.PixelsPerRow();
RunOnPool(
pool, ybegin, yend, ThreadPool::SkipInit(),
[&](const int y, int /*thread*/) HWY_ATTR {
JXL_CHECK(RunOnPool(
pool, ybegin, yend, ThreadPool::NoInit,
[&](const uint32_t y, size_t /*thread*/) HWY_ATTR {
for (size_t c = 0; c < 3; ++c) {
RunRow<kSizeModN>(rect.ConstPlaneRow(in, c, y), rect.xsize(),
stride, WrapRowUnchanged(), weights,
out->PlaneRow(c, y));
}
},
"Convolve3");
"Convolve3"));
}
template <size_t kSizeModN, class Image, class Weights>
@ -949,9 +949,9 @@ void Symmetric5(const ImageF& in, const Rect& rect,
PROFILER_FUNC;
const size_t ysize = rect.ysize();
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_CHECK(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t iy = task;
if (iy < 2 || iy >= static_cast<ssize_t>(ysize) - 2) {
@ -960,7 +960,7 @@ void Symmetric5(const ImageF& in, const Rect& rect,
Symmetric5Row<WrapUnchanged>(in, rect, iy, weights, out->Row(iy));
}
},
"Symmetric5x5Convolution");
"Symmetric5x5Convolution"));
}
void Symmetric5_3(const Image3F& in, const Rect& rect,
@ -969,9 +969,9 @@ void Symmetric5_3(const Image3F& in, const Rect& rect,
PROFILER_FUNC;
const size_t ysize = rect.ysize();
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_CHECK(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const size_t iy = task;
if (iy < 2 || iy >= ysize - 2) {
@ -986,7 +986,7 @@ void Symmetric5_3(const Image3F& in, const Rect& rect,
}
}
},
"Symmetric5x5Convolution3");
"Symmetric5x5Convolution3"));
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
@ -1152,9 +1152,9 @@ void SlowSymmetric3(const ImageF& in, const Rect& rect,
const int64_t ysize = static_cast<int64_t>(rect.ysize());
const int64_t kRadius = 1;
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_CHECK(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t iy = task;
float* JXL_RESTRICT out_row = out->Row(static_cast<size_t>(iy));
@ -1165,7 +1165,7 @@ void SlowSymmetric3(const ImageF& in, const Rect& rect,
out_row);
}
},
"SlowSymmetric3");
"SlowSymmetric3"));
}
void SlowSymmetric3(const Image3F& in, const Rect& rect,
@ -1177,9 +1177,9 @@ void SlowSymmetric3(const Image3F& in, const Rect& rect,
const int64_t ysize = static_cast<int64_t>(rect.ysize());
const int64_t kRadius = 1;
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_CHECK(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t iy = task;
const size_t oy = static_cast<size_t>(iy);
@ -1195,7 +1195,7 @@ void SlowSymmetric3(const Image3F& in, const Rect& rect,
}
}
},
"SlowSymmetric3");
"SlowSymmetric3"));
}
namespace {
@ -1235,9 +1235,9 @@ void SlowSeparable5(const ImageF& in, const Rect& rect,
const float* vert_weights = &weights.vert[0];
const size_t ysize = rect.ysize();
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_CHECK(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
float* const JXL_RESTRICT row_out = out->Row(y);
@ -1246,7 +1246,7 @@ void SlowSeparable5(const ImageF& in, const Rect& rect,
horz_weights, vert_weights);
}
},
"SlowSeparable5");
"SlowSeparable5"));
}
void SlowSeparable5(const Image3F& in, const Rect& rect,
@ -1265,9 +1265,9 @@ void SlowSeparable7(const ImageF& in, const Rect& rect,
const float* vert_weights = &weights.vert[0];
const size_t ysize = rect.ysize();
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_CHECK(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
float* const JXL_RESTRICT row_out = out->Row(y);
@ -1276,7 +1276,7 @@ void SlowSeparable7(const ImageF& in, const Rect& rect,
horz_weights, vert_weights);
}
},
"SlowSeparable7");
"SlowSeparable7"));
}
void SlowSeparable7(const Image3F& in, const Rect& rect,
@ -1296,9 +1296,9 @@ void SlowLaplacian5(const ImageF& in, const Rect& rect, ThreadPool* pool,
const size_t ysize = rect.ysize();
const WrapMirror wrap;
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_CHECK(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
const float* const JXL_RESTRICT row_t =
@ -1318,7 +1318,7 @@ void SlowLaplacian5(const ImageF& in, const Rect& rect, ThreadPool* pool,
row_out[x] = r;
}
},
"SlowLaplacian5");
"SlowLaplacian5"));
}
void SlowLaplacian5(const Image3F& in, const Rect& rect, ThreadPool* pool,

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

@ -137,36 +137,39 @@ void TestConvolve() {
TestNeighbors();
ThreadPoolInternal pool(4);
pool.Run(kConvolveMaxRadius, 40, ThreadPool::SkipInit(),
[](const int task, int /*thread*/) {
const size_t xsize = task;
Rng rng(129 + 13 * xsize);
EXPECT_EQ(true,
RunOnPool(
&pool, kConvolveMaxRadius, 40, ThreadPool::NoInit,
[](const uint32_t task, size_t /*thread*/) {
const size_t xsize = task;
Rng rng(129 + 13 * xsize);
ThreadPool* null_pool = nullptr;
ThreadPoolInternal pool3(3);
for (size_t ysize = kConvolveMaxRadius; ysize < 16; ++ysize) {
JXL_DEBUG(JXL_DEBUG_CONVOLVE,
"%" PRIuS " x %" PRIuS
" (target %d)===============================",
xsize, ysize, HWY_TARGET);
ThreadPool* null_pool = nullptr;
ThreadPoolInternal pool3(3);
for (size_t ysize = kConvolveMaxRadius; ysize < 16; ++ysize) {
JXL_DEBUG(JXL_DEBUG_CONVOLVE,
"%" PRIuS " x %" PRIuS
" (target %d)===============================",
xsize, ysize, HWY_TARGET);
JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sym3------------------");
VerifySymmetric3(xsize, ysize, null_pool, &rng);
VerifySymmetric3(xsize, ysize, &pool3, &rng);
JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sym3------------------");
VerifySymmetric3(xsize, ysize, null_pool, &rng);
VerifySymmetric3(xsize, ysize, &pool3, &rng);
JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sym5------------------");
VerifySymmetric5(xsize, ysize, null_pool, &rng);
VerifySymmetric5(xsize, ysize, &pool3, &rng);
JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sym5------------------");
VerifySymmetric5(xsize, ysize, null_pool, &rng);
VerifySymmetric5(xsize, ysize, &pool3, &rng);
JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sep5------------------");
VerifySeparable5(xsize, ysize, null_pool, &rng);
VerifySeparable5(xsize, ysize, &pool3, &rng);
JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sep5------------------");
VerifySeparable5(xsize, ysize, null_pool, &rng);
VerifySeparable5(xsize, ysize, &pool3, &rng);
JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sep7------------------");
VerifySeparable7(xsize, ysize, null_pool, &rng);
VerifySeparable7(xsize, ysize, &pool3, &rng);
}
});
JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sep7------------------");
VerifySeparable7(xsize, ysize, null_pool, &rng);
VerifySeparable7(xsize, ysize, &pool3, &rng);
}
},
"TestConvolve"));
}
// Measures durations, verifies results, prints timings. `unpredictable1`

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

@ -53,8 +53,8 @@ int TestInit(void* jpegxl_opaque, size_t num_threads) { return 0; }
TEST_F(DataParallelTest, RunnerCalledParamenters) {
EXPECT_TRUE(pool_.Run(
1234, 5678, [](const size_t num_threads) { return true; },
[](const int task, const int thread) { return; }));
1234, 5678, [](size_t /* num_threads */) { return true; },
[](uint32_t /* task */, size_t /* thread */) { return; }));
EXPECT_EQ(1, runner_called_);
EXPECT_NE(nullptr, init_);
EXPECT_NE(nullptr, func_);
@ -66,21 +66,21 @@ TEST_F(DataParallelTest, RunnerCalledParamenters) {
TEST_F(DataParallelTest, RunnerFailurePropagates) {
runner_return_ = -1; // FakeRunner return value.
EXPECT_FALSE(pool_.Run(
1234, 5678, [](const size_t num_threads) { return false; },
[](const int task, const int thread) { return; }));
1234, 5678, [](size_t /* num_threads */) { return false; },
[](uint32_t /* task */, size_t /* thread */) { return; }));
EXPECT_FALSE(RunOnPool(
nullptr, 1234, 5678, [](const size_t num_threads) { return false; },
[](const int task, const int thread) { return; }, "Test"));
nullptr, 1234, 5678, [](size_t /* num_threads */) { return false; },
[](uint32_t /* task */, size_t /* thread */) { return; }, "Test"));
}
TEST_F(DataParallelTest, RunnerNotCalledOnEmptyRange) {
runner_return_ = -1; // FakeRunner return value.
EXPECT_TRUE(pool_.Run(
123, 123, [](const size_t num_threads) { return false; },
[](const int task, const int thread) { return; }));
123, 123, [](size_t /* num_threads */) { return false; },
[](uint32_t /* task */, size_t /* thread */) { return; }));
EXPECT_TRUE(RunOnPool(
nullptr, 123, 123, [](const size_t num_threads) { return false; },
[](const int task, const int thread) { return; }, "Test"));
nullptr, 123, 123, [](size_t /* num_threads */) { return false; },
[](uint32_t /* task */, size_t /* thread */) { return; }, "Test"));
// We don't call the external runner when the range is empty. We don't even
// need to call the init function.
EXPECT_EQ(0, runner_called_);

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

@ -159,9 +159,9 @@ template <size_t N>
void TestInverseT(float accuracy) {
ThreadPoolInternal pool(N < 32 ? 0 : 8);
enum { kBlockSize = N * N };
RunOnPool(
&pool, 0, kBlockSize, ThreadPool::SkipInit(),
[accuracy](const int task, int /*thread*/) {
EXPECT_TRUE(RunOnPool(
&pool, 0, kBlockSize, ThreadPool::NoInit,
[accuracy](const uint32_t task, size_t /*thread*/) {
const size_t i = static_cast<size_t>(task);
HWY_ALIGN float x[kBlockSize] = {0.0f};
x[i] = 1.0;
@ -174,7 +174,7 @@ void TestInverseT(float accuracy) {
<< "i = " << i << ", k = " << k;
}
},
"TestInverse");
"TestInverse"));
}
void InverseTest() {

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

@ -235,7 +235,11 @@ class ANSSymbolReader {
return ReadSymbolWithoutRefill(histo_idx, br);
}
bool CheckANSFinalState() { return state_ == (ANS_SIGNATURE << 16u); }
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
bool CheckANSFinalState() const { return true; }
#else
bool CheckANSFinalState() const { return state_ == (ANS_SIGNATURE << 16u); }
#endif
template <typename BitReader>
static JXL_INLINE uint32_t ReadHybridUintConfig(

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

@ -22,6 +22,7 @@
#include "lib/jxl/image.h"
#include "lib/jxl/passes_state.h"
#include "lib/jxl/quant_weights.h"
#include "lib/jxl/render_pipeline/render_pipeline.h"
#include "lib/jxl/sanitizers.h"
namespace jxl {
@ -106,6 +107,13 @@ struct PassesDecoderState {
// Manages the status of borders.
GroupBorderAssigner group_border_assigner;
// Rendering pipeline. TODO(veluca): eventually, this pipeline will replace
// most of the state in this struct.
std::unique_ptr<RenderPipeline> render_pipeline;
// Storage for the current frame if it can be referenced by future frames.
ImageBundle frame_storage_for_referencing;
// TODO(veluca): this should eventually become "iff no global modular
// transform was applied".
bool EagerFinalizeImageRect() const {
@ -279,7 +287,7 @@ struct PassesDecoderState {
}
// Initialize the decoder state after all of DC is decoded.
void InitForAC(ThreadPool* pool) {
Status InitForAC(ThreadPool* pool) {
shared_storage.coeff_order_size = 0;
for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) {
if (((1 << o) & used_acs) == 0) continue;
@ -293,21 +301,23 @@ struct PassesDecoderState {
if (sz > shared_storage.coeff_orders.size()) {
shared_storage.coeff_orders.resize(sz);
}
if (shared->frame_header.flags & FrameHeader::kNoise) {
if (shared->frame_header.flags & FrameHeader::kNoise && !render_pipeline) {
noise = Image3F(shared->frame_dim.xsize_upsampled_padded,
shared->frame_dim.ysize_upsampled_padded);
size_t num_x_groups = DivCeil(noise.xsize(), kGroupDim);
size_t num_y_groups = DivCeil(noise.ysize(), kGroupDim);
PROFILER_ZONE("GenerateNoise");
auto generate_noise = [&](int group_index, int _) {
auto generate_noise = [&](const uint32_t group_index,
size_t /* thread */) {
size_t gx = group_index % num_x_groups;
size_t gy = group_index / num_x_groups;
Rect rect(gx * kGroupDim, gy * kGroupDim, kGroupDim, kGroupDim,
noise.xsize(), noise.ysize());
RandomImage3(noise_seed + group_index, rect, &noise);
};
RunOnPool(pool, 0, num_x_groups * num_y_groups, ThreadPool::SkipInit(),
generate_noise, "Generate noise");
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, num_x_groups * num_y_groups,
ThreadPool::NoInit, generate_noise,
"Generate noise"));
{
PROFILER_ZONE("High pass noise");
// 4 * (1 - box kernel)
@ -336,6 +346,7 @@ struct PassesDecoderState {
// Avoid errors due to loading vectors on the outermost padding.
FillImage(msan::kSanitizerSentinel, &decoded);
#endif
return true;
}
void EnsureBordersStorage();

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

@ -123,16 +123,16 @@ void StoreLEFloat(float value, uint8_t* p) {
// The orientation may not be identity.
// TODO(lode): SIMDify where possible
template <typename T>
void UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
Plane<T>& out, jxl::ThreadPool* pool) {
Status UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
Plane<T>& out, jxl::ThreadPool* pool) {
const size_t xsize = image.xsize();
const size_t ysize = image.ysize();
if (undo_orientation == Orientation::kFlipHorizontal) {
out = Plane<T>(xsize, ysize);
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
const T* JXL_RESTRICT row_in = image.Row(y);
T* JXL_RESTRICT row_out = out.Row(y);
@ -140,12 +140,12 @@ void UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
row_out[xsize - x - 1] = row_in[x];
}
},
"UndoOrientation");
"UndoOrientation"));
} else if (undo_orientation == Orientation::kRotate180) {
out = Plane<T>(xsize, ysize);
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
const T* JXL_RESTRICT row_in = image.Row(y);
T* JXL_RESTRICT row_out = out.Row(ysize - y - 1);
@ -153,12 +153,12 @@ void UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
row_out[xsize - x - 1] = row_in[x];
}
},
"UndoOrientation");
"UndoOrientation"));
} else if (undo_orientation == Orientation::kFlipVertical) {
out = Plane<T>(xsize, ysize);
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
const T* JXL_RESTRICT row_in = image.Row(y);
T* JXL_RESTRICT row_out = out.Row(ysize - y - 1);
@ -166,56 +166,57 @@ void UndoOrientation(jxl::Orientation undo_orientation, const Plane<T>& image,
row_out[x] = row_in[x];
}
},
"UndoOrientation");
"UndoOrientation"));
} else if (undo_orientation == Orientation::kTranspose) {
out = Plane<T>(ysize, xsize);
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
const T* JXL_RESTRICT row_in = image.Row(y);
for (size_t x = 0; x < xsize; ++x) {
out.Row(x)[y] = row_in[x];
}
},
"UndoOrientation");
"UndoOrientation"));
} else if (undo_orientation == Orientation::kRotate90) {
out = Plane<T>(ysize, xsize);
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
const T* JXL_RESTRICT row_in = image.Row(y);
for (size_t x = 0; x < xsize; ++x) {
out.Row(x)[ysize - y - 1] = row_in[x];
}
},
"UndoOrientation");
"UndoOrientation"));
} else if (undo_orientation == Orientation::kAntiTranspose) {
out = Plane<T>(ysize, xsize);
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
const T* JXL_RESTRICT row_in = image.Row(y);
for (size_t x = 0; x < xsize; ++x) {
out.Row(xsize - x - 1)[ysize - y - 1] = row_in[x];
}
},
"UndoOrientation");
"UndoOrientation"));
} else if (undo_orientation == Orientation::kRotate270) {
out = Plane<T>(ysize, xsize);
RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const int64_t y = task;
const T* JXL_RESTRICT row_in = image.Row(y);
for (size_t x = 0; x < xsize; ++x) {
out.Row(xsize - x - 1)[y] = row_in[x];
}
},
"UndoOrientation");
"UndoOrientation"));
}
return true;
}
} // namespace
@ -306,7 +307,8 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
if (undo_orientation != Orientation::kIdentity) {
for (size_t c = 0; c < num_channels; ++c) {
if (channels[c]) {
UndoOrientation(undo_orientation, *channels[c], temp_channels[c], pool);
JXL_RETURN_IF_ERROR(UndoOrientation(undo_orientation, *channels[c],
temp_channels[c], pool));
channels[c] = &(temp_channels[c]);
}
}
@ -315,12 +317,15 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
// First channel may not be nullptr.
size_t xsize = channels[0]->xsize();
size_t ysize = channels[0]->ysize();
if (stride < bytes_per_pixel * xsize) {
return JXL_FAILURE("stride is smaller than scanline width in bytes: %" PRIuS
" vs %" PRIuS,
stride, bytes_per_pixel * xsize);
}
if (!out_callback &&
out_size < (ysize - 1) * stride + bytes_per_pixel * xsize) {
return JXL_FAILURE("out_size is too small to store image");
}
const bool little_endian =
endianness == JXL_LITTLE_ENDIAN ||
@ -341,7 +346,7 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
if (bits_per_sample == 16) {
bool swap_endianness = little_endian != IsLittleEndian();
Plane<hwy::float16_t> f16_cache;
RunOnPool(
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize),
[&](size_t num_threads) {
f16_cache =
@ -349,7 +354,7 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
InitOutCallback(num_threads);
return true;
},
[&](const int task, int thread) {
[&](const uint32_t task, const size_t thread) {
const int64_t y = task;
const float* JXL_RESTRICT row_in[kConvertMaxChannels];
for (size_t c = 0; c < num_channels; c++) {
@ -383,15 +388,15 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
(*out_callback)(out_opaque, 0, y, xsize, row_out);
}
},
"ConvertF16");
"ConvertF16"));
} else if (bits_per_sample == 32) {
RunOnPool(
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize),
[&](size_t num_threads) {
InitOutCallback(num_threads);
return true;
},
[&](const int task, int thread) {
[&](const uint32_t task, const size_t thread) {
const int64_t y = task;
uint8_t* row_out =
out_callback
@ -410,7 +415,7 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
(*out_callback)(out_opaque, 0, y, xsize, row_out);
}
},
"ConvertFloat");
"ConvertFloat"));
} else {
return JXL_FAILURE("float other than 16-bit and 32-bit not supported");
}
@ -419,14 +424,14 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
// range.
float mul = (1ull << bits_per_sample) - 1;
Plane<uint32_t> u32_cache;
RunOnPool(
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, static_cast<uint32_t>(ysize),
[&](size_t num_threads) {
u32_cache = Plane<uint32_t>(xsize, num_channels * num_threads);
InitOutCallback(num_threads);
return true;
},
[&](const int task, int thread) {
[&](const uint32_t task, const size_t thread) {
const int64_t y = task;
uint8_t* row_out =
out_callback
@ -465,7 +470,7 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
(*out_callback)(out_opaque, 0, y, xsize, row_out);
}
},
"ConvertUint");
"ConvertUint"));
}
return true;
}

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

@ -53,6 +53,16 @@
#include "lib/jxl/passes_state.h"
#include "lib/jxl/quant_weights.h"
#include "lib/jxl/quantizer.h"
#include "lib/jxl/render_pipeline/stage_chroma_upsampling.h"
#include "lib/jxl/render_pipeline/stage_epf.h"
#include "lib/jxl/render_pipeline/stage_gaborish.h"
#include "lib/jxl/render_pipeline/stage_noise.h"
#include "lib/jxl/render_pipeline/stage_patches.h"
#include "lib/jxl/render_pipeline/stage_splines.h"
#include "lib/jxl/render_pipeline/stage_upsampling.h"
#include "lib/jxl/render_pipeline/stage_write_to_ib.h"
#include "lib/jxl/render_pipeline/stage_xyb.h"
#include "lib/jxl/render_pipeline/stage_ycbcr.h"
#include "lib/jxl/sanitizers.h"
#include "lib/jxl/splines.h"
#include "lib/jxl/toc.h"
@ -143,7 +153,8 @@ Status DecodeFrame(const DecompressParams& dparams,
const SizeConstraints* constraints, bool is_preview) {
PROFILER_ZONE("DecodeFrame uninstrumented");
FrameDecoder frame_decoder(dec_state, metadata, pool);
FrameDecoder frame_decoder(dec_state, metadata, pool,
dparams.use_slow_render_pipeline);
frame_decoder.SetFrameSizeLimits(constraints);
@ -291,6 +302,8 @@ Status FrameDecoder::InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded,
decoded->RemoveColor();
decoded->ClearExtraChannels();
decoded->duration = frame_header_.animation_frame.duration;
// Read TOC.
uint64_t groups_total_size;
const bool has_ac_global = true;
@ -358,6 +371,7 @@ Status FrameDecoder::InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded,
decoded_ac_global_ = false;
is_finalized_ = false;
finalized_dc_ = false;
num_sections_done_ = 0;
decoded_dc_groups_.clear();
decoded_dc_groups_.resize(frame_dim_.num_dc_groups);
decoded_passes_per_ac_group_.clear();
@ -436,7 +450,7 @@ Status FrameDecoder::ProcessDCGroup(size_t dc_group_id, BitReader* br) {
frame_dim_.dc_group_dim, frame_dim_.dc_group_dim);
JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
mrect, br, 3, 1000, ModularStreamId::ModularDC(dc_group_id),
/*zerofill=*/false, nullptr, nullptr, allow_partial_frames_));
/*zerofill=*/false, nullptr, nullptr, nullptr, allow_partial_frames_));
if (frame_header_.encoding == FrameEncoding::kVarDCT) {
JXL_RETURN_IF_ERROR(
modular_frame_decoder_.DecodeAcMetadata(dc_group_id, br, dec_state_));
@ -444,7 +458,7 @@ Status FrameDecoder::ProcessDCGroup(size_t dc_group_id, BitReader* br) {
FillImage(kInvSigmaNum / lf.epf_sigma_for_modular,
&dec_state_->filter_weights.sigma);
}
decoded_dc_groups_[dc_group_id] = true;
decoded_dc_groups_[dc_group_id] = uint8_t{true};
return true;
}
@ -461,8 +475,8 @@ void FrameDecoder::FinalizeDC() {
finalized_dc_ = true;
}
void FrameDecoder::AllocateOutput() {
if (allocated_) return;
Status FrameDecoder::AllocateOutput() {
if (allocated_) return true;
const CodecMetadata& metadata = *frame_header_.nonserialized_metadata;
if (dec_state_->rgb_output == nullptr && !dec_state_->pixel_callback) {
modular_frame_decoder_.MaybeDropFullImage();
@ -470,34 +484,54 @@ void FrameDecoder::AllocateOutput() {
frame_dim_.ysize_upsampled_padded),
dec_state_->output_encoding_info.color_encoding);
}
dec_state_->extra_channels.clear();
if (metadata.m.num_extra_channels > 0) {
if (dec_state_->render_pipeline) {
// TODO(veluca): consider not reallocating ECs if not needed.
decoded_->extra_channels().clear();
for (size_t i = 0; i < metadata.m.num_extra_channels; i++) {
uint32_t ecups = frame_header_.extra_channel_upsampling[i];
dec_state_->extra_channels.emplace_back(
DivCeil(frame_dim_.xsize_upsampled_padded, ecups),
DivCeil(frame_dim_.ysize_upsampled_padded, ecups));
decoded_->extra_channels().emplace_back(
frame_dim_.xsize_upsampled_padded, frame_dim_.ysize_upsampled_padded);
}
if (frame_header_.dc_level != 0) {
dec_state_->shared_storage.dc_frames[frame_header_.dc_level - 1] =
Image3F(frame_dim_.xsize, frame_dim_.ysize);
}
if (frame_header_.CanBeReferenced()) {
// TODO(veluca): this will need to be adapted for RGB output.
JXL_ASSERT(dec_state_->rgb_output == nullptr &&
!dec_state_->pixel_callback);
dec_state_->frame_storage_for_referencing = decoded_->Copy();
}
} else {
dec_state_->extra_channels.clear();
if (metadata.m.num_extra_channels > 0) {
for (size_t i = 0; i < metadata.m.num_extra_channels; i++) {
uint32_t ecups = frame_header_.extra_channel_upsampling[i];
dec_state_->extra_channels.emplace_back(
DivCeil(frame_dim_.xsize_upsampled_padded, ecups),
DivCeil(frame_dim_.ysize_upsampled_padded, ecups));
#if JXL_MEMORY_SANITIZER
// Avoid errors due to loading vectors on the outermost padding.
// Upsample of extra channels requires this padding to be initialized.
// TODO(deymo): Remove this and use rects up to {x,y}size_upsampled
// instead of the padded one.
for (size_t y = 0; y < DivCeil(frame_dim_.ysize_upsampled_padded, ecups);
y++) {
for (size_t x = (y < DivCeil(frame_dim_.ysize_upsampled, ecups)
? DivCeil(frame_dim_.xsize_upsampled, ecups)
: 0);
x < DivCeil(frame_dim_.xsize_upsampled_padded, ecups); x++) {
dec_state_->extra_channels.back().Row(y)[x] =
msan::kSanitizerSentinel;
// Avoid errors due to loading vectors on the outermost padding.
// Upsample of extra channels requires this padding to be initialized.
// TODO(deymo): Remove this and use rects up to {x,y}size_upsampled
// instead of the padded one.
for (size_t y = 0;
y < DivCeil(frame_dim_.ysize_upsampled_padded, ecups); y++) {
for (size_t x = (y < DivCeil(frame_dim_.ysize_upsampled, ecups)
? DivCeil(frame_dim_.xsize_upsampled, ecups)
: 0);
x < DivCeil(frame_dim_.xsize_upsampled_padded, ecups); x++) {
dec_state_->extra_channels.back().Row(y)[x] =
msan::kSanitizerSentinel;
}
}
}
#endif
}
}
}
decoded_->origin = dec_state_->shared->frame_header.frame_origin;
dec_state_->InitForAC(nullptr);
JXL_RETURN_IF_ERROR(dec_state_->InitForAC(nullptr));
allocated_ = true;
return true;
}
Status FrameDecoder::ProcessACGlobal(BitReader* br) {
@ -619,13 +653,21 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
const size_t x = gx * frame_dim_.group_dim;
const size_t y = gy * frame_dim_.group_dim;
RenderPipelineInput render_pipeline_input_storage;
RenderPipelineInput* render_pipeline_input = nullptr;
if (dec_state_->render_pipeline) {
render_pipeline_input_storage =
dec_state_->render_pipeline->GetInputBuffers(ac_group_id, thread);
render_pipeline_input = &render_pipeline_input_storage;
}
if (frame_header_.encoding == FrameEncoding::kVarDCT) {
group_dec_caches_[thread].InitOnce(frame_header_.passes.num_passes,
dec_state_->used_acs);
JXL_RETURN_IF_ERROR(DecodeGroup(
br, num_passes, ac_group_id, dec_state_, &group_dec_caches_[thread],
thread, decoded_, decoded_passes_per_ac_group_[ac_group_id], force_draw,
dc_only));
thread, render_pipeline_input, decoded_,
decoded_passes_per_ac_group_[ac_group_id], force_draw, dc_only));
}
// don't limit to image dimensions here (is done in DecodeGroup)
@ -638,19 +680,203 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
mrect, br[i - decoded_passes_per_ac_group_[ac_group_id]], minShift,
maxShift, ModularStreamId::ModularAC(ac_group_id, i),
/*zerofill=*/false, dec_state_, decoded_, allow_partial_frames_));
/*zerofill=*/false, dec_state_, render_pipeline_input, decoded_,
allow_partial_frames_));
} else if (i >= decoded_passes_per_ac_group_[ac_group_id] + num_passes &&
force_draw) {
JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
mrect, nullptr, minShift, maxShift,
ModularStreamId::ModularAC(ac_group_id, i), /*zerofill=*/true,
dec_state_, decoded_, allow_partial_frames_));
dec_state_, render_pipeline_input, decoded_, allow_partial_frames_));
}
}
decoded_passes_per_ac_group_[ac_group_id] += num_passes;
if ((frame_header_.flags & FrameHeader::kNoise) != 0 &&
render_pipeline_input) {
PROFILER_ZONE("GenerateNoise");
size_t num_x_groups = DivCeil(frame_dim_.xsize_upsampled_padded, kGroupDim);
size_t noise_c_start =
3 + frame_header_.nonserialized_metadata->m.num_extra_channels;
// When the color channels are downsampled, we need to generate more noise
// input for the current group than just the group dimensions.
std::pair<ImageF*, Rect> rects[3];
for (size_t iy = 0; iy < frame_header_.upsampling; iy++) {
for (size_t ix = 0; ix < frame_header_.upsampling; ix++) {
for (size_t c = 0; c < 3; c++) {
auto r = render_pipeline_input->GetBuffer(noise_c_start + c);
rects[c].first = r.first;
size_t x1 = r.second.x0() + r.second.xsize();
size_t y1 = r.second.y0() + r.second.ysize();
if (frame_header_.encoding == FrameEncoding::kVarDCT) {
x1 = RoundUpTo(x1, kBlockDim * frame_header_.upsampling);
y1 = RoundUpTo(y1, kBlockDim * frame_header_.upsampling);
}
rects[c].second = Rect(r.second.x0() + ix * kGroupDim,
r.second.y0() + iy * kGroupDim, kGroupDim,
kGroupDim, x1, y1);
}
Random3Planes(dec_state_->noise_seed +
(gx * frame_header_.upsampling + ix) +
(gy * frame_header_.upsampling + iy) * num_x_groups,
rects[0], rects[1], rects[2]);
}
}
}
if (render_pipeline_input && !modular_frame_decoder_.UsesFullImage()) {
render_pipeline_input->Done();
}
return true;
}
void FrameDecoder::PreparePipeline() {
size_t num_c = 3 + frame_header_.nonserialized_metadata->m.num_extra_channels;
if ((frame_header_.flags & FrameHeader::kNoise) != 0) {
num_c += 3;
}
RenderPipeline::Builder builder(num_c, frame_header_.passes.num_passes);
if (use_slow_rendering_pipeline_) {
builder.UseSimpleImplementation();
} else {
JXL_ABORT("Not implemented: fast pipeline");
}
if (!frame_header_.chroma_subsampling.Is444()) {
for (size_t c = 0; c < 3; c++) {
if (frame_header_.chroma_subsampling.HShift(c) != 0) {
builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/true));
}
if (frame_header_.chroma_subsampling.VShift(c) != 0) {
builder.AddStage(GetChromaUpsamplingStage(c, /*horizontal=*/false));
}
}
}
if (frame_header_.loop_filter.gab) {
builder.AddStage(GetGaborishStage(frame_header_.loop_filter));
}
{
const LoopFilter& lf = frame_header_.loop_filter;
if (lf.epf_iters >= 3) {
builder.AddStage(GetEPFStage(lf, dec_state_->filter_weights.sigma, 0));
}
if (lf.epf_iters >= 1) {
builder.AddStage(GetEPFStage(lf, dec_state_->filter_weights.sigma, 1));
}
if (lf.epf_iters >= 2) {
builder.AddStage(GetEPFStage(lf, dec_state_->filter_weights.sigma, 2));
}
}
bool late_ec_upsample = frame_header_.upsampling != 1;
for (auto ecups : frame_header_.extra_channel_upsampling) {
if (ecups != frame_header_.upsampling) {
// If patches are applied, either frame_header.upsampling == 1 or
// late_ec_upsample is true.
late_ec_upsample = false;
}
}
if (!late_ec_upsample) {
for (size_t ec = 0; ec < frame_header_.extra_channel_upsampling.size();
ec++) {
if (frame_header_.extra_channel_upsampling[ec] != 1) {
builder.AddStage(GetUpsamplingStage(
frame_header_.nonserialized_metadata->transform_data, 3 + ec,
CeilLog2Nonzero(frame_header_.extra_channel_upsampling[ec])));
}
}
}
if ((frame_header_.flags & FrameHeader::kPatches) != 0) {
builder.AddStage(
GetPatchesStage(&dec_state_->shared->image_features.patches));
}
if ((frame_header_.flags & FrameHeader::kSplines) != 0) {
builder.AddStage(
GetSplineStage(&dec_state_->shared->image_features.splines));
}
if (frame_header_.upsampling != 1) {
size_t nb_channels =
3 +
(late_ec_upsample ? frame_header_.extra_channel_upsampling.size() : 0);
for (size_t c = 0; c < nb_channels; c++) {
builder.AddStage(GetUpsamplingStage(
frame_header_.nonserialized_metadata->transform_data, c,
CeilLog2Nonzero(frame_header_.upsampling)));
}
}
if ((frame_header_.flags & FrameHeader::kNoise) != 0) {
builder.AddStage(GetConvolveNoiseStage(num_c - 3));
builder.AddStage(
GetAddNoiseStage(dec_state_->shared->image_features.noise_params,
dec_state_->shared->cmap, num_c - 3));
builder.UsesNoise();
}
if (frame_header_.dc_level != 0) {
builder.AddStage(GetWriteToImage3FStage(
&dec_state_->shared_storage.dc_frames[frame_header_.dc_level - 1]));
}
if (!coalescing_) {
JXL_ABORT("Not implemented: skip coalescing");
}
if (frame_header_.CanBeReferenced() &&
frame_header_.save_before_color_transform) {
builder.AddStage(
GetWriteToImageBundleStage(&dec_state_->frame_storage_for_referencing));
}
if (frame_header_.color_transform == ColorTransform::kYCbCr) {
builder.AddStage(GetYCbCrStage());
} else if (frame_header_.color_transform == ColorTransform::kXYB) {
builder.AddStage(GetXYBStage(dec_state_->output_encoding_info));
} // Nothing to do for kNone.
if (ImageBlender::NeedsBlending(dec_state_)) {
JXL_ABORT("Not implemented: blending");
}
if (frame_header_.CanBeReferenced() &&
!frame_header_.save_before_color_transform) {
builder.AddStage(
GetWriteToImageBundleStage(&dec_state_->frame_storage_for_referencing));
}
if (render_spotcolors_ &&
frame_header_.nonserialized_metadata->m.Find(ExtraChannel::kSpotColor)) {
JXL_ABORT("Not implemented: rendering spot colors");
}
if (dec_state_->pixel_callback) {
JXL_ABORT("Not implemented: pixel callback");
} else if (dec_state_->fast_xyb_srgb8_conversion) {
JXL_ABORT("Not implemented: fast xyb->srgb conversion");
} else if (dec_state_->rgb_output) {
JXL_ABORT("Not implemented: u8 output");
} else {
builder.AddStage(GetWriteToImageBundleStage(decoded_));
}
dec_state_->render_pipeline = std::move(builder).Finalize(frame_dim_);
}
void FrameDecoder::MarkSections(const SectionInfo* sections, size_t num,
SectionStatus* section_status) {
num_sections_done_ = num;
for (size_t i = 0; i < num; i++) {
if (section_status[i] == SectionStatus::kSkipped ||
section_status[i] == SectionStatus::kPartial) {
processed_section_[sections[i].id] = false;
num_sections_done_--;
}
}
}
Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
SectionStatus* section_status) {
if (num == 0) return true; // Nothing to process
@ -662,7 +888,9 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
frame_dim_.num_groups,
std::vector<size_t>(frame_header_.passes.num_passes, num));
std::vector<size_t> num_ac_passes(frame_dim_.num_groups);
if (frame_dim_.num_groups == 1 && frame_header_.passes.num_passes == 1) {
bool single_section =
frame_dim_.num_groups == 1 && frame_header_.passes.num_passes == 1;
if (single_section) {
JXL_ASSERT(num == 1);
JXL_ASSERT(sections[0].id == 0);
if (processed_section_[0] == false) {
@ -724,8 +952,8 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
std::atomic<bool> has_error{false};
if (decoded_dc_global_) {
RunOnPool(
pool_, 0, dc_group_sec.size(), ThreadPool::SkipInit(),
JXL_RETURN_IF_ERROR(RunOnPool(
pool_, 0, dc_group_sec.size(), ThreadPool::NoInit,
[this, &dc_group_sec, &num, &sections, &section_status, &has_error](
size_t i, size_t thread) {
if (dc_group_sec[i] != num) {
@ -736,18 +964,49 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
}
}
},
"DecodeDCGroup");
"DecodeDCGroup"));
}
if (has_error) return JXL_FAILURE("Error in DC group");
if (*std::min_element(decoded_dc_groups_.begin(), decoded_dc_groups_.end()) ==
true &&
if (*std::min_element(decoded_dc_groups_.begin(), decoded_dc_groups_.end()) &&
!finalized_dc_) {
if (use_slow_rendering_pipeline_) {
PreparePipeline();
}
FinalizeDC();
AllocateOutput();
JXL_RETURN_IF_ERROR(AllocateOutput());
if (pause_at_progressive_ && !single_section) {
bool can_return_dc = true;
if (single_section) {
// If there's only one group and one pass, there is no separate section
// for DC and the entire full resolution image is available at once.
can_return_dc = false;
}
if (!decoded_->metadata()->extra_channel_info.empty()) {
// If extra channels are encoded with modular without squeeze, they
// don't support DC. If the are encoded with squeeze, DC works in theory
// but the implementation may not yet correctly support this for Flush.
// Therefore, can't correctly pause for a progressive step if there is
// an extra channel (including alpha channel)
can_return_dc = false;
}
if (frame_header_.encoding != FrameEncoding::kVarDCT) {
// DC is not guaranteed to be available in modular mode and may be a
// black image. If squeeze is used, it may be available depending on the
// current implementation.
// TODO(lode): do return DC if it's known that flushing at this point
// will produce a valid 1/8th downscaled image with modular encoding.
can_return_dc = false;
}
if (can_return_dc) {
MarkSections(sections, num, section_status);
return true;
}
}
}
if (finalized_dc_) dec_state_->EnsureBordersStorage();
if (finalized_dc_ && ac_global_sec != num && !decoded_ac_global_) {
JXL_RETURN_IF_ERROR(ProcessACGlobal(sections[ac_global_sec].br));
section_status[ac_global_sec] = SectionStatus::kDone;
@ -769,7 +1028,7 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
dec_state_->group_border_assigner.ClearDone(i);
}
RunOnPool(
JXL_RETURN_IF_ERROR(RunOnPool(
pool_, 0, ac_group_sec.size(),
[this](size_t num_threads) {
PrepareStorage(num_threads, decoded_passes_per_ac_group_.size());
@ -798,16 +1057,11 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
}
}
},
"DecodeGroup");
"DecodeGroup"));
}
if (has_error) return JXL_FAILURE("Error in AC group");
for (size_t i = 0; i < num; i++) {
if (section_status[i] == SectionStatus::kSkipped ||
section_status[i] == SectionStatus::kPartial) {
processed_section_[sections[i].id] = false;
}
}
MarkSections(sections, num, section_status);
return true;
}
@ -832,7 +1086,7 @@ Status FrameDecoder::Flush() {
// Nothing to do.
return true;
}
AllocateOutput();
JXL_RETURN_IF_ERROR(AllocateOutput());
uint32_t completely_decoded_ac_pass = *std::min_element(
decoded_passes_per_ac_group_.begin(), decoded_passes_per_ac_group_.end());
@ -845,13 +1099,13 @@ Status FrameDecoder::Flush() {
dec_state_->group_border_assigner.ClearDone(i);
}
std::atomic<bool> has_error{false};
RunOnPool(
JXL_RETURN_IF_ERROR(RunOnPool(
pool_, 0, decoded_passes_per_ac_group_.size(),
[this](size_t num_threads) {
[this](const size_t num_threads) {
PrepareStorage(num_threads, decoded_passes_per_ac_group_.size());
return true;
},
[this, &has_error](size_t g, size_t thread) {
[this, &has_error](const uint32_t g, size_t thread) {
if (decoded_passes_per_ac_group_[g] ==
frame_header_.passes.num_passes) {
// This group was drawn already, nothing to do.
@ -863,7 +1117,7 @@ Status FrameDecoder::Flush() {
/*force_draw=*/true, /*dc_only=*/!decoded_ac_global_);
if (!ok) has_error = true;
},
"ForceDrawGroup");
"ForceDrawGroup"));
if (has_error) {
return JXL_FAILURE("Drawing groups failed");
}
@ -895,17 +1149,23 @@ int FrameDecoder::SavedAs(const FrameHeader& header) {
return 0;
}
bool FrameDecoder::HasEverything() const {
if (!decoded_dc_global_) return false;
if (!decoded_ac_global_) return false;
for (auto& have_dc_group : decoded_dc_groups_) {
if (!have_dc_group) return false;
}
for (auto& nb_passes : decoded_passes_per_ac_group_) {
if (nb_passes < max_passes_) return false;
}
return true;
}
int FrameDecoder::References() const {
if (is_finalized_) {
return 0;
}
if ((!decoded_dc_global_ || !decoded_ac_global_ ||
*std::min_element(decoded_dc_groups_.begin(),
decoded_dc_groups_.end()) != 1 ||
*std::min_element(decoded_passes_per_ac_group_.begin(),
decoded_passes_per_ac_group_.end()) < max_passes_)) {
return 0;
}
if (!HasEverything()) return 0;
int result = 0;
@ -954,63 +1214,61 @@ Status FrameDecoder::FinalizeFrame() {
// particularly useful anyway on upsampling results), so we disable it.
dec_state_->shared_storage.frame_header.loop_filter.epf_iters = 0;
}
if ((!decoded_dc_global_ || !decoded_ac_global_ ||
*std::min_element(decoded_dc_groups_.begin(),
decoded_dc_groups_.end()) != 1 ||
*std::min_element(decoded_passes_per_ac_group_.begin(),
decoded_passes_per_ac_group_.end()) < max_passes_) &&
!allow_partial_frames_) {
if (!HasEverything() && !allow_partial_frames_) {
return JXL_FAILURE(
"FinalizeFrame called before the frame was fully decoded");
}
if (!finalized_dc_) {
JXL_ASSERT(allow_partial_frames_);
AllocateOutput();
JXL_RETURN_IF_ERROR(AllocateOutput());
}
JXL_RETURN_IF_ERROR(Flush());
if (dec_state_->shared->frame_header.CanBeReferenced() &&
(frame_header_.frame_type != kRegularFrame || coalescing_)) {
size_t id = dec_state_->shared->frame_header.save_as_reference;
auto& reference_frame = dec_state_->shared_storage.reference_frames[id];
if (dec_state_->pre_color_transform_frame.xsize() == 0) {
reference_frame.storage = decoded_->Copy();
} else {
reference_frame.storage = ImageBundle(decoded_->metadata());
reference_frame.storage.SetFromImage(
CopyImage(dec_state_->pre_color_transform_frame),
decoded_->c_current());
if (decoded_->HasExtraChannels()) {
const std::vector<ImageF>* ecs = &dec_state_->pre_color_transform_ec;
if (ecs->empty()) ecs = &decoded_->extra_channels();
std::vector<ImageF> extra_channels;
for (const auto& ec : *ecs) {
extra_channels.push_back(CopyImage(ec));
if (!dec_state_->render_pipeline) {
if (dec_state_->shared->frame_header.CanBeReferenced() &&
(frame_header_.frame_type != kRegularFrame || coalescing_)) {
size_t id = dec_state_->shared->frame_header.save_as_reference;
auto& reference_frame = dec_state_->shared_storage.reference_frames[id];
if (dec_state_->pre_color_transform_frame.xsize() == 0) {
reference_frame.storage = decoded_->Copy();
} else {
reference_frame.storage = ImageBundle(decoded_->metadata());
reference_frame.storage.SetFromImage(
CopyImage(dec_state_->pre_color_transform_frame),
decoded_->c_current());
if (decoded_->HasExtraChannels()) {
const std::vector<ImageF>* ecs = &dec_state_->pre_color_transform_ec;
if (ecs->empty()) ecs = &decoded_->extra_channels();
std::vector<ImageF> extra_channels;
for (const auto& ec : *ecs) {
extra_channels.push_back(CopyImage(ec));
}
reference_frame.storage.SetExtraChannels(std::move(extra_channels));
}
reference_frame.storage.SetExtraChannels(std::move(extra_channels));
}
}
reference_frame.frame = &reference_frame.storage;
reference_frame.ib_is_in_xyb =
dec_state_->shared->frame_header.save_before_color_transform;
if (!dec_state_->shared->frame_header.save_before_color_transform) {
const CodecMetadata* metadata =
dec_state_->shared->frame_header.nonserialized_metadata;
if (reference_frame.frame->xsize() < metadata->xsize() ||
reference_frame.frame->ysize() < metadata->ysize()) {
return JXL_FAILURE(
"trying to save a reference frame that is too small: %" PRIuS
"x%" PRIuS
" "
"instead of %" PRIuS "x%" PRIuS,
reference_frame.frame->xsize(), reference_frame.frame->ysize(),
metadata->xsize(), metadata->ysize());
reference_frame.frame = &reference_frame.storage;
reference_frame.ib_is_in_xyb =
dec_state_->shared->frame_header.save_before_color_transform;
if (!dec_state_->shared->frame_header.save_before_color_transform) {
const CodecMetadata* metadata =
dec_state_->shared->frame_header.nonserialized_metadata;
if (reference_frame.frame->xsize() < metadata->xsize() ||
reference_frame.frame->ysize() < metadata->ysize()) {
return JXL_FAILURE(
"trying to save a reference frame that is too small: %" PRIuS
"x%" PRIuS
" "
"instead of %" PRIuS "x%" PRIuS,
reference_frame.frame->xsize(), reference_frame.frame->ysize(),
metadata->xsize(), metadata->ysize());
}
reference_frame.storage.ShrinkTo(metadata->xsize(), metadata->ysize());
}
reference_frame.storage.ShrinkTo(metadata->xsize(), metadata->ysize());
}
}
if (frame_header_.nonserialized_is_preview) {
// Fix possible larger image size (multiple of kBlockDim)
// TODO(lode): verify if and when that happens.
@ -1033,7 +1291,7 @@ Status FrameDecoder::FinalizeFrame() {
}
}
if (render_spotcolors_) {
if (render_spotcolors_ && !dec_state_->render_pipeline) {
for (size_t i = 0; i < decoded_->extra_channels().size(); i++) {
// Don't use Find() because there may be multiple spot color channels.
const ExtraChannelInfo& eci = decoded_->metadata()->extra_channel_info[i];
@ -1063,12 +1321,20 @@ Status FrameDecoder::FinalizeFrame() {
}
}
}
if (dec_state_->shared->frame_header.dc_level != 0) {
if (dec_state_->shared->frame_header.dc_level != 0 &&
!dec_state_->render_pipeline) {
dec_state_->shared_storage
.dc_frames[dec_state_->shared->frame_header.dc_level - 1] =
std::move(*decoded_->color());
decoded_->RemoveColor();
}
if (dec_state_->render_pipeline && frame_header_.CanBeReferenced()) {
auto& info = dec_state_->shared_storage
.reference_frames[frame_header_.save_as_reference];
info.storage = std::move(dec_state_->frame_storage_for_referencing);
info.ib_is_in_xyb = frame_header_.save_before_color_transform;
info.frame = &info.storage;
}
return true;
}

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

@ -54,8 +54,11 @@ class FrameDecoder {
public:
// All parameters must outlive the FrameDecoder.
FrameDecoder(PassesDecoderState* dec_state, const CodecMetadata& metadata,
ThreadPool* pool)
: dec_state_(dec_state), pool_(pool), frame_header_(&metadata) {}
ThreadPool* pool, bool use_slow_rendering_pipeline)
: dec_state_(dec_state),
pool_(pool),
frame_header_(&metadata),
use_slow_rendering_pipeline_(use_slow_rendering_pipeline) {}
// `constraints` must outlive the FrameDecoder if not null, or stay alive
// until the next call to SetFrameSizeLimits.
@ -134,6 +137,15 @@ class FrameDecoder {
// Returns whether a DC image has been decoded, accessible at low resolution
// at passes.shared_storage.dc_storage
bool HasDecodedDC() const { return finalized_dc_; }
bool HasDecodedAll() const { return NumSections() == num_sections_done_; }
// If enabled, ProcessSections will stop and return true when the DC
// sections have been processed, instead of starting the AC sections. This
// will only occur if supported (that is, flushing will produce a valid
// 1/8th*1/8th resolution image). The return value of true then does not mean
// all sections have been processed, use HasDecodedDC and HasDecodedAll
// to check the true finished state.
void SetPauseAtProgressive() { pause_at_progressive_ = true; }
// Sets the buffer to which uint8 sRGB pixels will be decoded. This is not
// supported for all images. If it succeeds, HasRGBBuffer() will return true.
@ -196,11 +208,14 @@ class FrameDecoder {
Status ProcessDCGlobal(BitReader* br);
Status ProcessDCGroup(size_t dc_group_id, BitReader* br);
void FinalizeDC();
void AllocateOutput();
Status AllocateOutput();
void PreparePipeline();
Status ProcessACGlobal(BitReader* br);
Status ProcessACGroup(size_t ac_group_id, BitReader* JXL_RESTRICT* br,
size_t num_passes, size_t thread, bool force_draw,
bool dc_only);
void MarkSections(const SectionInfo* sections, size_t num,
SectionStatus* section_status);
// Allocates storage for parallel decoding using up to `num_threads` threads
// of up to `num_tasks` tasks. The value of `thread` passed to
@ -214,6 +229,9 @@ class FrameDecoder {
}
dec_state_->EnsureStorage(storage_size);
use_task_id_ = num_threads > num_tasks;
if (dec_state_->render_pipeline) {
dec_state_->render_pipeline->PrepareForThreads(storage_size);
}
}
size_t GetStorageLocation(size_t thread, size_t task) {
@ -262,7 +280,9 @@ class FrameDecoder {
std::vector<uint8_t> decoded_dc_groups_;
bool decoded_dc_global_;
bool decoded_ac_global_;
bool HasEverything() const;
bool finalized_dc_ = true;
size_t num_sections_done_ = 0;
bool is_finalized_ = true;
size_t num_renders_ = 0;
bool allocated_ = false;
@ -275,6 +295,11 @@ class FrameDecoder {
// Whether or not the task id should be used for storage indexing, instead of
// the thread id.
bool use_task_id_ = false;
// Testing setting: whether or not to use the slow rendering pipeline.
bool use_slow_rendering_pipeline_;
bool pause_at_progressive_ = false;
};
} // namespace jxl

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

@ -169,8 +169,9 @@ void DequantBlock(const AcStrategy& acs, float inv_global_scale, int quant,
Status DecodeGroupImpl(GetBlock* JXL_RESTRICT get_block,
GroupDecCache* JXL_RESTRICT group_dec_cache,
PassesDecoderState* JXL_RESTRICT dec_state,
size_t thread, size_t group_idx, ImageBundle* decoded,
DrawMode draw) {
size_t thread, size_t group_idx,
RenderPipelineInput* render_pipeline_input,
ImageBundle* decoded, DrawMode draw) {
// TODO(veluca): investigate cache usage in this function.
PROFILER_FUNC;
constexpr size_t kGroupDataXBorder = PassesDecoderState::kGroupDataXBorder;
@ -191,9 +192,17 @@ Status DecodeGroupImpl(GetBlock* JXL_RESTRICT get_block,
const YCbCrChromaSubsampling& cs =
dec_state->shared->frame_header.chroma_subsampling;
const size_t idct_stride = dec_state->EagerFinalizeImageRect()
? dec_state->group_data[thread].PixelsPerRow()
: dec_state->decoded.PixelsPerRow();
size_t idct_stride[3];
for (size_t c = 0; c < 3; c++) {
if (render_pipeline_input) {
idct_stride[c] =
render_pipeline_input->GetBuffer(c).first->PixelsPerRow();
} else {
idct_stride[c] = dec_state->EagerFinalizeImageRect()
? dec_state->group_data[thread].PixelsPerRow()
: dec_state->decoded.PixelsPerRow();
}
}
HWY_ALIGN int32_t scaled_qtable[64 * 3];
@ -278,7 +287,10 @@ Status DecodeGroupImpl(GetBlock* JXL_RESTRICT get_block,
float* JXL_RESTRICT idct_row[3];
int16_t* JXL_RESTRICT jpeg_row[3];
for (size_t c = 0; c < 3; c++) {
if (dec_state->EagerFinalizeImageRect()) {
if (render_pipeline_input) {
idct_row[c] = render_pipeline_input->GetBuffer(c).second.Row(
render_pipeline_input->GetBuffer(c).first, sby[c] * kBlockDim);
} else if (dec_state->EagerFinalizeImageRect()) {
idct_row[c] = dec_state->group_data[thread].PlaneRow(
c, sby[c] * kBlockDim + kGroupDataYBorder) +
kGroupDataXBorder;
@ -427,7 +439,7 @@ Status DecodeGroupImpl(GetBlock* JXL_RESTRICT get_block,
// IDCT
float* JXL_RESTRICT idct_pos = idct_row[c] + sbx[c] * kBlockDim;
TransformToPixels(acs.Strategy(), block + c * size, idct_pos,
idct_stride, group_dec_cache->scratch_space);
idct_stride[c], group_dec_cache->scratch_space);
}
}
bx += llf_x;
@ -689,6 +701,7 @@ Status DecodeGroup(BitReader* JXL_RESTRICT* JXL_RESTRICT readers,
size_t num_passes, size_t group_idx,
PassesDecoderState* JXL_RESTRICT dec_state,
GroupDecCache* JXL_RESTRICT group_dec_cache, size_t thread,
RenderPipelineInput* render_pipeline_input,
ImageBundle* JXL_RESTRICT decoded, size_t first_pass,
bool force_draw, bool dc_only) {
PROFILER_FUNC;
@ -727,6 +740,10 @@ Status DecodeGroup(BitReader* JXL_RESTRICT* JXL_RESTRICT readers,
PassesDecoderState::kGroupDataYBorder, dst_rect.xsize(),
dst_rect.ysize());
}
if (render_pipeline_input) {
dst_rect = render_pipeline_input->GetBuffer(c).second;
upsampling_dst = render_pipeline_input->GetBuffer(c).first;
}
JXL_ASSERT(dst_rect.IsInside(*upsampling_dst));
dec_state->upsamplers[2].UpsampleRect(
dec_state->filter_input_storage[thread].Plane(c), copy_rect,
@ -754,8 +771,8 @@ Status DecodeGroup(BitReader* JXL_RESTRICT* JXL_RESTRICT readers,
group_dec_cache, dec_state, first_pass));
JXL_RETURN_IF_ERROR(HWY_DYNAMIC_DISPATCH(DecodeGroupImpl)(
&get_block, group_dec_cache, dec_state, thread, group_idx, decoded,
draw));
&get_block, group_dec_cache, dec_state, thread, group_idx,
render_pipeline_input, decoded, draw));
for (size_t pass = 0; pass < num_passes; pass++) {
if (!get_block.decoders[pass].CheckANSFinalState()) {
@ -781,7 +798,7 @@ Status DecodeGroupForRoundtrip(const std::vector<std::unique_ptr<ACImage>>& ac,
return HWY_DYNAMIC_DISPATCH(DecodeGroupImpl)(&get_block, group_dec_cache,
dec_state, thread, group_idx,
decoded, kDraw);
nullptr, decoded, kDraw);
}
} // namespace jxl

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

@ -32,6 +32,7 @@ Status DecodeGroup(BitReader* JXL_RESTRICT* JXL_RESTRICT readers,
size_t num_passes, size_t group_idx,
PassesDecoderState* JXL_RESTRICT dec_state,
GroupDecCache* JXL_RESTRICT group_dec_cache, size_t thread,
RenderPipelineInput* render_pipeline_input,
ImageBundle* JXL_RESTRICT decoded, size_t first_pass,
bool force_draw, bool dc_only);

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

@ -50,37 +50,27 @@ void MultiplySum(const size_t xsize,
void RgbFromSingle(const size_t xsize,
const pixel_type* const JXL_RESTRICT row_in,
const float factor, Image3F* decoded, size_t /*c*/, size_t y,
Rect& rect) {
JXL_DASSERT(xsize <= rect.xsize());
const float factor, float* out_r, float* out_g,
float* out_b) {
const HWY_FULL(float) df;
const Rebind<pixel_type, HWY_FULL(float)> di; // assumes pixel_type <= float
float* const JXL_RESTRICT row_out_r = rect.PlaneRow(decoded, 0, y);
float* const JXL_RESTRICT row_out_g = rect.PlaneRow(decoded, 1, y);
float* const JXL_RESTRICT row_out_b = rect.PlaneRow(decoded, 2, y);
const auto factor_v = Set(df, factor);
for (size_t x = 0; x < xsize; x += Lanes(di)) {
const auto in = Load(di, row_in + x);
const auto out = ConvertTo(df, in) * factor_v;
Store(out, df, row_out_r + x);
Store(out, df, row_out_g + x);
Store(out, df, row_out_b + x);
Store(out, df, out_r + x);
Store(out, df, out_g + x);
Store(out, df, out_b + x);
}
}
// Same signature as RgbFromSingle so we can assign to the same pointer.
void SingleFromSingle(const size_t xsize,
const pixel_type* const JXL_RESTRICT row_in,
const float factor, Image3F* decoded, size_t c, size_t y,
Rect& rect) {
JXL_DASSERT(xsize <= rect.xsize());
const float factor, float* row_out) {
const HWY_FULL(float) df;
const Rebind<pixel_type, HWY_FULL(float)> di; // assumes pixel_type <= float
float* const JXL_RESTRICT row_out = rect.PlaneRow(decoded, c, y);
const auto factor_v = Set(df, factor);
for (size_t x = 0; x < xsize; x += Lanes(di)) {
const auto in = Load(di, row_in + x);
@ -264,7 +254,8 @@ void ModularFrameDecoder::MaybeDropFullImage() {
Status ModularFrameDecoder::DecodeGroup(
const Rect& rect, BitReader* reader, int minShift, int maxShift,
const ModularStreamId& stream, bool zerofill, PassesDecoderState* dec_state,
ImageBundle* output, bool allow_truncated) {
RenderPipelineInput* render_pipeline_input, ImageBundle* output,
bool allow_truncated) {
JXL_DASSERT(stream.kind == ModularStreamId::kModularDC ||
stream.kind == ModularStreamId::kModularAC);
const size_t xsize = rect.xsize();
@ -304,13 +295,11 @@ Status ModularFrameDecoder::DecodeGroup(
if (gi.channel.empty()) return true;
ModularOptions options;
if (!zerofill) {
if (!ModularGenericDecompress(reader, gi, /*header=*/nullptr,
stream.ID(frame_dim), &options,
/*undo_transforms=*/true, &tree, &code,
&context_map, allow_truncated) &&
!allow_truncated) {
return JXL_FAILURE("Failed to decode modular group");
}
auto status = ModularGenericDecompress(
reader, gi, /*header=*/nullptr, stream.ID(frame_dim), &options,
/*undo_transforms=*/true, &tree, &code, &context_map, allow_truncated);
if (!allow_truncated) JXL_RETURN_IF_ERROR(status);
if (status.IsFatalError()) return status;
}
// Undo global transforms that have been pushed to the group level
if (!use_full_image) {
@ -318,7 +307,8 @@ Status ModularFrameDecoder::DecodeGroup(
JXL_RETURN_IF_ERROR(t.Inverse(gi, global_header.wp_header));
}
JXL_RETURN_IF_ERROR(ModularImageToDecodedRect(
gi, dec_state, nullptr, output, rect.Crop(dec_state->decoded)));
gi, dec_state, nullptr, render_pipeline_input, output,
rect.Crop(dec_state->decoded), Rect(0, 0, gi.w, gi.h)));
return true;
}
int gic = 0;
@ -338,6 +328,7 @@ Status ModularFrameDecoder::DecodeGroup(
}
return true;
}
Status ModularFrameDecoder::DecodeVarDCTDC(size_t group_id, BitReader* reader,
PassesDecoderState* dec_state) {
const Rect r = dec_state->shared->DCGroupRect(group_id);
@ -460,16 +451,27 @@ Status ModularFrameDecoder::DecodeAcMetadata(size_t group_id, BitReader* reader,
Status ModularFrameDecoder::ModularImageToDecodedRect(
Image& gi, PassesDecoderState* dec_state, jxl::ThreadPool* pool,
ImageBundle* output, Rect rect) {
RenderPipelineInput* render_pipeline_input, ImageBundle* output, Rect rect,
Rect modular_rect) {
auto& decoded = dec_state->decoded;
const auto& frame_header = dec_state->shared->frame_header;
const auto* metadata = frame_header.nonserialized_metadata;
size_t xsize = rect.xsize();
size_t ysize = rect.ysize();
if (!xsize || !ysize) {
return true;
if (!render_pipeline_input) {
size_t xsize = rect.xsize();
size_t ysize = rect.ysize();
if (!xsize || !ysize) {
return true;
}
JXL_DASSERT(rect.IsInside(decoded));
}
JXL_DASSERT(rect.IsInside(decoded));
JXL_CHECK(gi.transform.empty());
auto get_row = [&](Rect r, size_t c, size_t y) {
return render_pipeline_input
? render_pipeline_input->GetBuffer(c).second.Row(
render_pipeline_input->GetBuffer(c).first, y)
: r.PlaneRow(&decoded, c, y);
};
size_t c = 0;
if (do_color) {
@ -496,60 +498,74 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
if (ch_in.w == 0 || ch_in.h == 0) {
return JXL_FAILURE("Empty image");
}
size_t xsize_shifted = DivCeil(xsize, 1 << ch_in.hshift);
size_t ysize_shifted = DivCeil(ysize, 1 << ch_in.vshift);
JXL_CHECK(ch_in.hshift <= 3 && ch_in.vshift <= 3);
Rect r(rect.x0() >> ch_in.hshift, rect.y0() >> ch_in.vshift,
rect.xsize() >> ch_in.hshift, rect.ysize() >> ch_in.vshift,
DivCeil(decoded.xsize(), 1 << ch_in.hshift),
DivCeil(decoded.ysize(), 1 << ch_in.vshift));
if (r.ysize() != ch_in.h || r.xsize() != ch_in.w) {
Rect mr(modular_rect.x0() >> ch_in.hshift,
modular_rect.y0() >> ch_in.vshift,
DivCeil(modular_rect.xsize(), 1 << ch_in.hshift),
DivCeil(modular_rect.ysize(), 1 << ch_in.vshift));
mr = mr.Crop(ch_in.plane);
if (render_pipeline_input) {
r = render_pipeline_input->GetBuffer(c).second;
}
size_t xsize_shifted = r.xsize();
size_t ysize_shifted = r.ysize();
if (r.ysize() != mr.ysize() || r.xsize() != mr.xsize()) {
return JXL_FAILURE("Dimension mismatch: trying to fit a %" PRIuS
"x%" PRIuS
" modular channel into "
"a %" PRIuS "x%" PRIuS " rect",
ch_in.w, ch_in.h, r.xsize(), r.ysize());
mr.xsize(), mr.ysize(), r.xsize(), r.ysize());
}
if (frame_header.color_transform == ColorTransform::kXYB && c == 2) {
JXL_ASSERT(!fp);
RunOnPool(
pool, 0, ysize_shifted, jxl::ThreadPool::SkipInit(),
[&](const int task, const int thread) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, ysize_shifted, ThreadPool::NoInit,
[&](const uint32_t task, size_t /* thread */) {
const size_t y = task;
const pixel_type* const JXL_RESTRICT row_in = ch_in.Row(y);
const pixel_type* const JXL_RESTRICT row_in =
mr.Row(&ch_in.plane, y);
const pixel_type* const JXL_RESTRICT row_in_Y =
gi.channel[0].Row(y);
float* const JXL_RESTRICT row_out = r.PlaneRow(&decoded, c, y);
mr.Row(&gi.channel[0].plane, y);
float* const JXL_RESTRICT row_out = get_row(r, c, y);
HWY_DYNAMIC_DISPATCH(MultiplySum)
(xsize_shifted, row_in, row_in_Y, factor, row_out);
},
"ModularIntToFloat");
"ModularIntToFloat"));
} else if (fp) {
int bits = metadata->m.bit_depth.bits_per_sample;
int exp_bits = metadata->m.bit_depth.exponent_bits_per_sample;
RunOnPool(
pool, 0, ysize_shifted, jxl::ThreadPool::SkipInit(),
[&](const int task, const int thread) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, ysize_shifted, ThreadPool::NoInit,
[&](const uint32_t task, size_t /* thread */) {
const size_t y = task;
const pixel_type* const JXL_RESTRICT row_in = ch_in.Row(y);
float* const JXL_RESTRICT row_out = r.PlaneRow(&decoded, c, y);
const pixel_type* const JXL_RESTRICT row_in =
mr.Row(&ch_in.plane, y);
float* const JXL_RESTRICT row_out = get_row(r, c, y);
int_to_float(row_in, row_out, xsize_shifted, bits, exp_bits);
},
"ModularIntToFloat_losslessfloat");
"ModularIntToFloat_losslessfloat"));
} else {
RunOnPool(
pool, 0, ysize_shifted, jxl::ThreadPool::SkipInit(),
[&](const int task, const int thread) {
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, ysize_shifted, ThreadPool::NoInit,
[&](const uint32_t task, size_t /* thread */) {
const size_t y = task;
const pixel_type* const JXL_RESTRICT row_in = ch_in.Row(y);
const pixel_type* const JXL_RESTRICT row_in =
mr.Row(&ch_in.plane, y);
if (rgb_from_gray) {
HWY_DYNAMIC_DISPATCH(RgbFromSingle)
(xsize_shifted, row_in, factor, &decoded, c, y, r);
(xsize_shifted, row_in, factor, get_row(r, 0, y),
get_row(r, 1, y), get_row(r, 2, y));
} else {
float* const JXL_RESTRICT row_out = get_row(r, c, y);
HWY_DYNAMIC_DISPATCH(SingleFromSingle)
(xsize_shifted, row_in, factor, &decoded, c, y, r);
(xsize_shifted, row_in, factor, row_out);
}
},
"ModularIntToFloat");
"ModularIntToFloat"));
}
if (rgb_from_gray) {
break;
@ -559,7 +575,10 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
c = 1;
}
}
for (size_t ec = 0; ec < dec_state->extra_channels.size(); ec++, c++) {
size_t num_extra_channels = render_pipeline_input
? output->extra_channels().size()
: dec_state->extra_channels.size();
for (size_t ec = 0; ec < num_extra_channels; ec++, c++) {
const ExtraChannelInfo& eci = output->metadata()->extra_channel_info[ec];
int bits = eci.bit_depth.bits_per_sample;
int exp_bits = eci.bit_depth.exponent_bits_per_sample;
@ -571,19 +590,33 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
const size_t ec_ysize = DivCeil(frame_dim.ysize_upsampled, ecups);
JXL_ASSERT(c < gi.channel.size());
Channel& ch_in = gi.channel[c];
// For x0, y0 there's no need to do a DivCeil().
JXL_DASSERT(rect.x0() % (1ul << ch_in.hshift) == 0);
JXL_DASSERT(rect.y0() % (1ul << ch_in.vshift) == 0);
if (!render_pipeline_input) {
// For x0, y0 there's no need to do a DivCeil().
JXL_DASSERT(rect.x0() % (1ul << ch_in.hshift) == 0);
JXL_DASSERT(rect.y0() % (1ul << ch_in.vshift) == 0);
}
Rect r(rect.x0() >> ch_in.hshift, rect.y0() >> ch_in.vshift,
DivCeil(rect.xsize(), 1lu << ch_in.hshift),
DivCeil(rect.ysize(), 1lu << ch_in.vshift), ec_xsize, ec_ysize);
JXL_DASSERT(r.IsInside(dec_state->extra_channels[ec]));
JXL_DASSERT(Rect(0, 0, r.xsize(), r.ysize()).IsInside(ch_in.plane));
if (render_pipeline_input) {
r = render_pipeline_input->GetBuffer(3 + ec).second;
}
Rect mr(modular_rect.x0() >> ch_in.hshift,
modular_rect.y0() >> ch_in.vshift,
DivCeil(modular_rect.xsize(), 1 << ch_in.hshift),
DivCeil(modular_rect.ysize(), 1 << ch_in.vshift));
mr = mr.Crop(ch_in.plane);
if (!render_pipeline_input) {
JXL_DASSERT(r.IsInside(dec_state->extra_channels[ec]));
}
for (size_t y = 0; y < r.ysize(); ++y) {
float* const JXL_RESTRICT row_out =
r.Row(&dec_state->extra_channels[ec], y);
const pixel_type* const JXL_RESTRICT row_in = ch_in.Row(y);
render_pipeline_input
? r.Row(render_pipeline_input->GetBuffer(3 + ec).first, y)
: r.Row(&dec_state->extra_channels[ec], y);
const pixel_type* const JXL_RESTRICT row_in = mr.Row(&ch_in.plane, y);
if (fp) {
int_to_float(row_in, row_out, r.xsize(), bits, exp_bits);
} else {
@ -592,7 +625,9 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
}
}
}
JXL_CHECK_IMAGE_INITIALIZED(dec_state->extra_channels[ec], r);
if (!render_pipeline_input) {
JXL_CHECK_IMAGE_INITIALIZED(dec_state->extra_channels[ec], r);
}
}
return true;
}
@ -618,8 +653,34 @@ Status ModularFrameDecoder::FinalizeDecoding(PassesDecoderState* dec_state,
auto& decoded = dec_state->decoded;
JXL_RETURN_IF_ERROR(
ModularImageToDecodedRect(gi, dec_state, pool, output, Rect(decoded)));
if (dec_state->render_pipeline) {
std::atomic<bool> has_error{false};
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, dec_state->shared->frame_dim.num_groups,
[&](size_t num_threads) {
dec_state->render_pipeline->PrepareForThreads(num_threads);
return true;
},
[&](const uint32_t group, size_t thread_id) {
RenderPipelineInput input =
dec_state->render_pipeline->GetInputBuffers(group, thread_id);
if (!ModularImageToDecodedRect(gi, dec_state, nullptr, &input, output,
Rect(),
dec_state->shared->GroupRect(group))) {
has_error = true;
return;
}
input.Done();
},
"ModularToRect"));
if (has_error) {
return JXL_FAILURE("Error producing input to render pipeline");
}
} else {
JXL_RETURN_IF_ERROR(ModularImageToDecodedRect(gi, dec_state, pool, nullptr,
output, Rect(decoded),
Rect(0, 0, gi.w, gi.h)));
}
return true;
}

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

@ -91,8 +91,9 @@ class ModularFrameDecoder {
bool allow_truncated_group);
Status DecodeGroup(const Rect& rect, BitReader* reader, int minShift,
int maxShift, const ModularStreamId& stream, bool zerofill,
PassesDecoderState* dec_state, ImageBundle* output,
bool allow_truncated);
PassesDecoderState* dec_state,
RenderPipelineInput* render_pipeline_input,
ImageBundle* output, bool allow_truncated);
// Decodes a VarDCT DC group (`group_id`) from the given `reader`.
Status DecodeVarDCTDC(size_t group_id, BitReader* reader,
PassesDecoderState* dec_state);
@ -113,11 +114,14 @@ class ModularFrameDecoder {
ImageBundle* output, bool inplace);
bool have_dc() const { return have_something; }
void MaybeDropFullImage();
bool UsesFullImage() const { return use_full_image; }
private:
Status ModularImageToDecodedRect(Image& gi, PassesDecoderState* dec_state,
jxl::ThreadPool* pool, ImageBundle* output,
Rect rect);
jxl::ThreadPool* pool,
RenderPipelineInput* render_pipeline_input,
ImageBundle* output, Rect rect,
Rect modular_rect);
Image full_image;
std::vector<Transform> global_transform;

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

@ -258,6 +258,15 @@ void RandomImage3(size_t seed, const Rect& rect, Image3F* JXL_RESTRICT noise) {
RandomImage(&rng, rect, &noise->Plane(2));
}
void Random3Planes(size_t seed, const std::pair<ImageF*, Rect>& plane0,
const std::pair<ImageF*, Rect>& plane1,
const std::pair<ImageF*, Rect>& plane2) {
HWY_ALIGN Xorshift128Plus rng(seed);
RandomImage(&rng, plane0.second, plane0.first);
RandomImage(&rng, plane1.second, plane1.first);
RandomImage(&rng, plane2.second, plane2.first);
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
} // namespace HWY_NAMESPACE
} // namespace jxl
@ -279,6 +288,13 @@ void RandomImage3(size_t seed, const Rect& rect, Image3F* JXL_RESTRICT noise) {
return HWY_DYNAMIC_DISPATCH(RandomImage3)(seed, rect, noise);
}
HWY_EXPORT(Random3Planes);
void Random3Planes(size_t seed, const std::pair<ImageF*, Rect>& plane0,
const std::pair<ImageF*, Rect>& plane1,
const std::pair<ImageF*, Rect>& plane2) {
return HWY_DYNAMIC_DISPATCH(Random3Planes)(seed, plane0, plane1, plane2);
}
void DecodeFloatParam(float precision, float* val, BitReader* br) {
const int absval_quant = br->ReadFixedBits<10>();
*val = absval_quant / precision;

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

@ -28,6 +28,10 @@ void AddNoise(const NoiseParams& noise_params, const Rect& noise_rect,
void RandomImage3(size_t seed, const Rect& rect, Image3F* JXL_RESTRICT noise);
void Random3Planes(size_t seed, const std::pair<ImageF*, Rect>& plane0,
const std::pair<ImageF*, Rect>& plane1,
const std::pair<ImageF*, Rect>& plane2);
// Must only call if FrameHeader.flags.kNoise.
Status DecodeNoise(BitReader* br, NoiseParams* noise_params);

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

@ -44,6 +44,10 @@ struct DecompressParams {
bool allow_partial_files = false;
// Allow even more progression.
bool allow_more_progressive_steps = false;
// Internal test-only setting: whether or not to use the slow rendering
// pipeline.
bool use_slow_render_pipeline = false;
};
} // namespace jxl

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

@ -199,11 +199,50 @@ void PatchDictionary::ComputePatchCache() {
}
}
Status PatchDictionary::AddTo(Image3F* opsin, const Rect& opsin_rect,
float* const* extra_channels,
const Rect& image_rect) const {
// Adds patches to a segment of `xsize` pixels, starting at `inout`, assumed
// to be located at position (x0, y) in the frame.
void PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0,
size_t xsize) const {
if (patch_starts_.empty()) return;
size_t num_ec = shared_->metadata->m.num_extra_channels;
std::vector<const float*> fg_ptrs(3 + num_ec);
if (y + 1 >= patch_starts_.size()) return;
for (size_t id = patch_starts_[y]; id < patch_starts_[y + 1]; id++) {
const PatchPosition& pos = positions_[sorted_patches_[id]];
size_t by = pos.y;
size_t bx = pos.x;
size_t patch_xsize = pos.ref_pos.xsize;
JXL_DASSERT(y >= by);
JXL_DASSERT(y < by + pos.ref_pos.ysize);
size_t iy = y - by;
size_t ref = pos.ref_pos.ref;
if (bx >= x0 + xsize) continue;
if (bx + patch_xsize < x0) continue;
size_t patch_x0 = std::max(bx, x0);
size_t patch_x1 = std::min(bx + patch_xsize, x0 + xsize);
for (size_t c = 0; c < 3; c++) {
fg_ptrs[c] = shared_->reference_frames[ref].frame->color()->ConstPlaneRow(
c, pos.ref_pos.y0 + iy) +
pos.ref_pos.x0 + x0 - bx;
}
for (size_t i = 0; i < num_ec; i++) {
fg_ptrs[3 + i] =
shared_->reference_frames[ref].frame->extra_channels()[i].ConstRow(
pos.ref_pos.y0 + iy) +
pos.ref_pos.x0 + x0 - bx;
}
PerformBlending(inout, fg_ptrs.data(), inout, patch_x0 - x0,
patch_x1 - patch_x0, pos.blending[0],
pos.blending.data() + 1,
shared_->metadata->m.extra_channel_info);
}
}
void PatchDictionary::AddTo(Image3F* opsin, const Rect& opsin_rect,
float* const* extra_channels,
const Rect& image_rect) const {
JXL_CHECK(SameSize(opsin_rect, image_rect));
if (patch_starts_.empty()) return true;
if (patch_starts_.empty()) return;
size_t num_ec = shared_->metadata->m.num_extra_channels;
std::vector<const float*> fg_ptrs(3 + num_ec);
std::vector<float*> bg_ptrs(3 + num_ec);
@ -238,13 +277,11 @@ Status PatchDictionary::AddTo(Image3F* opsin, const Rect& opsin_rect,
pos.ref_pos.x0 + x0 - bx;
bg_ptrs[3 + i] = extra_channels[i] + x0 - image_rect.x0();
}
JXL_RETURN_IF_ERROR(
PerformBlending(bg_ptrs.data(), fg_ptrs.data(), bg_ptrs.data(),
x1 - x0, pos.blending[0], pos.blending.data() + 1,
shared_->metadata->m.extra_channel_info));
PerformBlending(bg_ptrs.data(), fg_ptrs.data(), bg_ptrs.data(), 0,
x1 - x0, pos.blending[0], pos.blending.data() + 1,
shared_->metadata->m.extra_channel_info);
}
}
return true;
}
} // namespace jxl

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

@ -122,10 +122,14 @@ class PatchDictionary {
ComputePatchCache();
}
// Adds patches to a segment of `xsize` pixels, starting at `inout`, assumed
// to be located at position (x0, y) in the frame.
void AddOneRow(float* const* inout, size_t y, size_t x0, size_t xsize) const;
// Only adds patches that belong to the `image_rect` area of the decoded
// image, writing them to the `opsin_rect` area of `opsin`.
Status AddTo(Image3F* opsin, const Rect& opsin_rect,
float* const* extra_channels, const Rect& image_rect) const;
void AddTo(Image3F* opsin, const Rect& opsin_rect,
float* const* extra_channels, const Rect& image_rect) const;
// Returns dependencies of this patch dictionary on reference frame ids as a
// bit mask: bits 0-3 indicate reference frame 0-3.

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

@ -59,16 +59,15 @@ void DoUndoXYBInPlace(Image3F* idct, const Rect& rect, Op op,
const auto in_opsin_x = Load(d, row0 + x);
const auto in_opsin_y = Load(d, row1 + x);
const auto in_opsin_b = Load(d, row2 + x);
JXL_COMPILER_FENCE;
auto linear_r = Undefined(d);
auto linear_g = Undefined(d);
auto linear_b = Undefined(d);
auto r = Undefined(d);
auto g = Undefined(d);
auto b = Undefined(d);
XybToRgb(d, in_opsin_x, in_opsin_y, in_opsin_b,
output_encoding_info.opsin_params, &linear_r, &linear_g,
&linear_b);
Store(op.Transform(d, linear_r), d, row0 + x);
Store(op.Transform(d, linear_g), d, row1 + x);
Store(op.Transform(d, linear_b), d, row2 + x);
output_encoding_info.opsin_params, &r, &g, &b);
op.Transform(d, &r, &g, &b);
Store(r, d, row0 + x);
Store(g, d, row1 + x);
Store(b, d, row2 + x);
}
msan::PoisonMemory(row0 + xsize, sizeof(float) * (xsize_v - xsize));
msan::PoisonMemory(row1 + xsize, sizeof(float) * (xsize_v - xsize));
@ -76,6 +75,20 @@ void DoUndoXYBInPlace(Image3F* idct, const Rect& rect, Op op,
}
}
template <typename Op>
struct PerChannelOp {
template <typename... Args>
explicit PerChannelOp(Args&&... args) : op(std::forward<Args>(args)...) {}
template <typename D, typename T>
void Transform(D d, T* r, T* g, T* b) {
*r = op.Transform(d, *r);
*g = op.Transform(d, *g);
*b = op.Transform(d, *b);
}
Op op;
};
struct OpLinear {
template <typename D, typename T>
T Transform(D d, const T& linear) {
@ -102,10 +115,34 @@ struct OpPq {
};
struct OpHlg {
template <typename D, typename T>
T Transform(D d, const T& linear) {
return TF_HLG().EncodedFromDisplay(d, linear);
explicit OpHlg(const float luminances[3], const float intensity_target)
: luminances(luminances) {
if (295 <= intensity_target && intensity_target <= 305) {
apply_inverse_ootf = false;
return;
}
exponent =
(1 / 1.2f) * std::pow(1.111f, -std::log2(intensity_target * 1e-3f)) - 1;
}
template <typename D, typename T>
void Transform(D d, T* r, T* g, T* b) {
if (apply_inverse_ootf) {
const T luminance = Set(d, luminances[0]) * *r +
Set(d, luminances[1]) * *g +
Set(d, luminances[2]) * *b;
const T ratio =
Min(FastPowf(d, luminance, Set(d, exponent)), Set(d, 1e9));
*r *= ratio;
*g *= ratio;
*b *= ratio;
}
*r = TF_HLG().EncodedFromDisplay(d, *r);
*g = TF_HLG().EncodedFromDisplay(d, *g);
*b = TF_HLG().EncodedFromDisplay(d, *b);
}
bool apply_inverse_ootf = true;
const float* luminances;
float exponent;
};
struct Op709 {
@ -116,6 +153,7 @@ struct Op709 {
};
struct OpGamma {
explicit OpGamma(const float inverse_gamma) : inverse_gamma(inverse_gamma) {}
const float inverse_gamma;
template <typename D, typename T>
T Transform(D d, const T& linear) {
@ -129,19 +167,24 @@ Status UndoXYBInPlace(Image3F* idct, const Rect& rect,
PROFILER_ZONE("UndoXYB");
if (output_encoding_info.color_encoding.tf.IsLinear()) {
DoUndoXYBInPlace(idct, rect, OpLinear(), output_encoding_info);
DoUndoXYBInPlace(idct, rect, PerChannelOp<OpLinear>(),
output_encoding_info);
} else if (output_encoding_info.color_encoding.tf.IsSRGB()) {
DoUndoXYBInPlace(idct, rect, OpRgb(), output_encoding_info);
DoUndoXYBInPlace(idct, rect, PerChannelOp<OpRgb>(), output_encoding_info);
} else if (output_encoding_info.color_encoding.tf.IsPQ()) {
DoUndoXYBInPlace(idct, rect, OpPq(), output_encoding_info);
DoUndoXYBInPlace(idct, rect, PerChannelOp<OpPq>(), output_encoding_info);
} else if (output_encoding_info.color_encoding.tf.IsHLG()) {
DoUndoXYBInPlace(idct, rect, OpHlg(), output_encoding_info);
DoUndoXYBInPlace(idct, rect,
OpHlg(output_encoding_info.luminances,
output_encoding_info.intensity_target),
output_encoding_info);
} else if (output_encoding_info.color_encoding.tf.Is709()) {
DoUndoXYBInPlace(idct, rect, Op709(), output_encoding_info);
DoUndoXYBInPlace(idct, rect, PerChannelOp<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};
DoUndoXYBInPlace(idct, rect, op, output_encoding_info);
DoUndoXYBInPlace(idct, rect,
PerChannelOp<OpGamma>(output_encoding_info.inverse_gamma),
output_encoding_info);
} else {
// This is a programming error.
JXL_ABORT("Invalid target encoding");
@ -418,13 +461,13 @@ HWY_EXPORT(DoYCbCrUpsampling);
void UndoXYB(const Image3F& src, Image3F* dst,
const OutputEncodingInfo& output_info, ThreadPool* pool) {
CopyImageTo(src, dst);
RunOnPool(
pool, 0, src.ysize(), ThreadPool::SkipInit(),
[&](int y, int /*thread*/) {
JXL_CHECK(RunOnPool(
pool, 0, src.ysize(), ThreadPool::NoInit,
[&](const uint32_t y, size_t /*thread*/) {
JXL_CHECK(HWY_DYNAMIC_DISPATCH(UndoXYBInPlace)(dst, Rect(*dst).Line(y),
output_info));
},
"UndoXYB");
"UndoXYB"));
}
namespace {
@ -582,6 +625,10 @@ Status FinalizeImageRect(
const std::vector<std::pair<ImageF*, Rect>>& extra_channels,
PassesDecoderState* dec_state, size_t thread,
ImageBundle* JXL_RESTRICT output_image, const Rect& frame_rect) {
// Do nothing if using the rendering pipeline.
if (dec_state->render_pipeline) {
return true;
}
const ImageFeatures& image_features = dec_state->shared->image_features;
const FrameHeader& frame_header = dec_state->shared->frame_header;
const ImageMetadata& metadata = frame_header.nonserialized_metadata->m;
@ -918,9 +965,9 @@ Status FinalizeImageRect(
extra_channels_for_patches[i].first, available_y);
}
}
JXL_RETURN_IF_ERROR(image_features.patches.AddTo(
image_features.patches.AddTo(
storage_for_if, rect_for_if_storage.Line(available_y),
ec_ptrs_for_patches.data(), rect_for_if.Line(available_y)));
ec_ptrs_for_patches.data(), rect_for_if.Line(available_y));
image_features.splines.AddTo(storage_for_if,
rect_for_if_storage.Line(available_y),
rect_for_if.Line(available_y));
@ -1107,6 +1154,9 @@ Status FinalizeImageRect(
Status FinalizeFrameDecoding(ImageBundle* decoded,
PassesDecoderState* dec_state, ThreadPool* pool,
bool force_fir, bool skip_blending, bool move_ec) {
if (dec_state->render_pipeline) {
return true;
}
const FrameHeader& frame_header = dec_state->shared->frame_header;
const FrameDimensions& frame_dim = dec_state->shared->frame_dim;
@ -1121,7 +1171,7 @@ Status FinalizeFrameDecoding(ImageBundle* decoded,
rects_to_process.push_back(rect);
}
}
const auto allocate_storage = [&](size_t num_threads) {
const auto allocate_storage = [&](const size_t num_threads) {
dec_state->EnsureStorage(num_threads);
return true;
};
@ -1145,7 +1195,7 @@ Status FinalizeFrameDecoding(ImageBundle* decoded,
}
std::atomic<bool> apply_features_ok{true};
auto run_apply_features = [&](size_t rect_id, size_t thread) {
auto run_apply_features = [&](const uint32_t rect_id, size_t thread) {
size_t xstart = PassesDecoderState::kGroupDataXBorder;
size_t ystart = PassesDecoderState::kGroupDataYBorder;
for (size_t c = 0; c < 3; c++) {
@ -1198,8 +1248,9 @@ Status FinalizeFrameDecoding(ImageBundle* decoded,
}
};
RunOnPool(pool, 0, rects_to_process.size(), allocate_storage,
run_apply_features, "ApplyFeatures");
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, rects_to_process.size(),
allocate_storage, run_apply_features,
"ApplyFeatures"));
if (!apply_features_ok) {
return JXL_FAILURE("FinalizeImageRect failed");
@ -1254,8 +1305,8 @@ Status FinalizeFrameDecoding(ImageBundle* decoded,
std::atomic<bool> blending_ok{true};
JXL_RETURN_IF_ERROR(RunOnPool(
pool, 0, rects_to_process.size(), ThreadPool::SkipInit(),
[&](size_t i, size_t /*thread*/) {
pool, 0, rects_to_process.size(), ThreadPool::NoInit,
[&](const uint32_t i, size_t /*thread*/) {
const Rect& rect = rects_to_process[i];
auto rect_blender = blender.PrepareRect(
rect, *foreground.color(), foreground.extra_channels(), rect);

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

@ -1,91 +0,0 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_JXL_DEC_RENDER_PIPELINE_H_
#define LIB_JXL_DEC_RENDER_PIPELINE_H_
#include <stdint.h>
#include "lib/jxl/filters.h"
namespace jxl {
// The first pixel in the input to RenderPipelineStage will be located at
// this position. Pixels before this position may be accessed as padding.
constexpr size_t kRenderPipelineXOffset = 16;
enum class RenderPipelineChannelMode {
kIgnored = 0,
kInPlace = 1,
kInOut = 2,
};
class RenderPipelineStage {
public:
// `input` points to `2*MaxPaddingY() + 1` pointers, each of which points to
// `3+num_non_color_channels` pointer-to-row. So, `input[MaxPaddingY()][0]` is
// the pointer to the center row of the first color channel.
// `MaxPaddingY()` is the maximum value returned by `GetPaddingX()`;
// typically, this is a constant.
// `output` points to `1<<MaxShiftY()` pointers, each of which points to
// `3+num_non_color_channels` pointer-to-row. So, `output[0][3]` is the
// pointer to the top row of the first non-color channel.
// `MaxShiftY()` is defined similarly to `MaxPaddingY()`.
// `xsize` represents the total number of pixels to be processed in the input
// row. `xpos` and `ypos` represent the position of the first pixel in the
// center row in the input
virtual void ProcessRow(float* JXL_RESTRICT** input,
float* JXL_RESTRICT** output, size_t xsize,
size_t xpos, size_t ypos) const = 0;
virtual ~RenderPipelineStage() {}
// Amount of padding required by each channel in the various directions.
// The value for c=0 indicates padding required for color channels, subsequent
// values refer to padding for non-color channels, in order.
virtual size_t GetPaddingX(size_t c) const = 0;
virtual size_t GetPaddingY(size_t c) const = 0;
// Log2 of the number of columns/rows of output that this stage will produce
// for the given channel.
virtual size_t ShiftX(size_t c) const = 0;
virtual size_t ShiftY(size_t c) const = 0;
// How each channel will be processed. If this method returns kIgnored or
// kInPlace for a given channel, then the corresponding pointer-to-row values
// in the output of ProcessRow will be null for that channel, and
// `GetPaddingX`, `GetPaddingY`, `ShiftX` and `ShiftY` for that channel must
// return 0.
virtual RenderPipelineChannelMode GetChannelMode(size_t c) const = 0;
};
class RenderPipeline {
public:
// Initial shifts for the channels (following the same convention as
// RenderPipelineStage for naming the channels).
void Init(const std::vector<std::pair<size_t, size_t>>& channel_shifts) {
JXL_ABORT("Not implemented");
}
// Adds a stage to the pipeline. The shifts for all the channels that are not
// kIgnored by the stage must be identical at this point.
void AddStage(std::unique_ptr<RenderPipelineStage> stage) {
JXL_ABORT("Not implemented");
}
// Finalizes setup of the pipeline. Shifts for all channels should be 0 at
// this point.
void Finalize() { JXL_ABORT("Not implemented"); }
// Allocates storage to run with `num` threads.
void PrepareForThreads(size_t num) { JXL_ABORT("Not implemented"); }
// TBD: run the pipeline for a given input, on a given thread.
// void Run(Image3F* color_data, ImageF* ec_data, const Rect& input_rect,
// size_t thread, size_t xpos, size_t ypos) {}
};
} // namespace jxl
#endif // LIB_JXL_DEC_RENDER_PIPELINE_H_

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

@ -35,9 +35,9 @@ void OpsinToLinearInplace(Image3F* JXL_RESTRICT inout, ThreadPool* pool,
JXL_CHECK_IMAGE_INITIALIZED(*inout, Rect(*inout));
const size_t xsize = inout->xsize(); // not padded
RunOnPool(
pool, 0, inout->ysize(), ThreadPool::SkipInit(),
[&](const int task, const int thread) {
JXL_CHECK(RunOnPool(
pool, 0, inout->ysize(), ThreadPool::NoInit,
[&](const uint32_t task, size_t /* thread */) {
const size_t y = task;
// Faster than adding via ByteOffset at end of loop.
@ -51,7 +51,6 @@ void OpsinToLinearInplace(Image3F* JXL_RESTRICT inout, ThreadPool* pool,
const auto in_opsin_x = Load(d, row0 + x);
const auto in_opsin_y = Load(d, row1 + x);
const auto in_opsin_b = Load(d, row2 + x);
JXL_COMPILER_FENCE;
auto linear_r = Undefined(d);
auto linear_g = Undefined(d);
auto linear_b = Undefined(d);
@ -63,7 +62,7 @@ void OpsinToLinearInplace(Image3F* JXL_RESTRICT inout, ThreadPool* pool,
Store(linear_b, d, row2 + x);
}
},
"OpsinToLinear");
"OpsinToLinear"));
}
// Same, but not in-place.
@ -75,9 +74,9 @@ void OpsinToLinear(const Image3F& opsin, const Rect& rect, ThreadPool* pool,
JXL_ASSERT(SameSize(rect, *linear));
JXL_CHECK_IMAGE_INITIALIZED(opsin, rect);
RunOnPool(
pool, 0, static_cast<int>(rect.ysize()), ThreadPool::SkipInit(),
[&](const int task, int /*thread*/) {
JXL_CHECK(RunOnPool(
pool, 0, static_cast<int>(rect.ysize()), ThreadPool::NoInit,
[&](const uint32_t task, size_t /*thread*/) {
const size_t y = static_cast<size_t>(task);
// Faster than adding via ByteOffset at end of loop.
@ -94,7 +93,6 @@ void OpsinToLinear(const Image3F& opsin, const Rect& rect, ThreadPool* pool,
const auto in_opsin_x = Load(d, row_opsin_0 + x);
const auto in_opsin_y = Load(d, row_opsin_1 + x);
const auto in_opsin_b = Load(d, row_opsin_2 + x);
JXL_COMPILER_FENCE;
auto linear_r = Undefined(d);
auto linear_g = Undefined(d);
auto linear_b = Undefined(d);
@ -106,7 +104,7 @@ void OpsinToLinear(const Image3F& opsin, const Rect& rect, ThreadPool* pool,
Store(linear_b, d, row_linear_2 + x);
}
},
"OpsinToLinear(Rect)");
"OpsinToLinear(Rect)"));
JXL_CHECK_IMAGE_INITIALIZED(*linear, rect);
}
@ -208,7 +206,7 @@ Status OutputEncodingInfo::Set(const CodecMetadata& metadata,
const auto& im = metadata.transform_data.opsin_inverse_matrix;
float inverse_matrix[9];
memcpy(inverse_matrix, im.inverse_matrix, sizeof(inverse_matrix));
float intensity_target = metadata.m.IntensityTarget();
intensity_target = metadata.m.IntensityTarget();
if (metadata.m.xyb_encoded) {
const auto& orig_color_encoding = metadata.m.color_encoding;
color_encoding = default_enc;
@ -246,8 +244,8 @@ Status OutputEncodingInfo::Set(const CodecMetadata& metadata,
srgb.GetPrimaries().g.x, srgb.GetPrimaries().g.y,
srgb.GetPrimaries().b.x, srgb.GetPrimaries().b.y,
srgb.GetWhitePoint().x, srgb.GetWhitePoint().y, srgb_to_xyzd50));
float xyzd50_to_original[9];
JXL_RETURN_IF_ERROR(PrimariesToXYZD50(
float original_to_xyz[3][3];
JXL_RETURN_IF_ERROR(PrimariesToXYZ(
orig_color_encoding.GetPrimaries().r.x,
orig_color_encoding.GetPrimaries().r.y,
orig_color_encoding.GetPrimaries().g.x,
@ -255,7 +253,15 @@ Status OutputEncodingInfo::Set(const CodecMetadata& metadata,
orig_color_encoding.GetPrimaries().b.x,
orig_color_encoding.GetPrimaries().b.y,
orig_color_encoding.GetWhitePoint().x,
orig_color_encoding.GetWhitePoint().y, xyzd50_to_original));
orig_color_encoding.GetWhitePoint().y, &original_to_xyz[0][0]));
memcpy(luminances, original_to_xyz[1], sizeof luminances);
float adapt_to_d50[9];
JXL_RETURN_IF_ERROR(AdaptToXYZD50(orig_color_encoding.GetWhitePoint().x,
orig_color_encoding.GetWhitePoint().y,
adapt_to_d50));
float xyzd50_to_original[9];
MatMul(adapt_to_d50, &original_to_xyz[0][0], 3, 3, 3,
xyzd50_to_original);
JXL_RETURN_IF_ERROR(Inv3x3Matrix(xyzd50_to_original));
float srgb_to_original[9];
MatMul(xyzd50_to_original, srgb_to_xyzd50, 3, 3, 3, srgb_to_original);

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

@ -41,6 +41,11 @@ struct OutputEncodingInfo {
Status Set(const CodecMetadata& metadata, const ColorEncoding& default_enc);
bool all_default_opsin = true;
bool color_encoding_is_original = false;
// Luminances of color_encoding's primaries, used for the HLG inverse OOTF.
// Default to sRGB's.
float luminances[3] = {0.2126, 0.7152, 0.0722};
// Also used for the HLG inverse OOTF.
float intensity_target;
};
// Converts `inout` (not padded) from opsin to linear sRGB in-place. Called from

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

@ -20,28 +20,11 @@
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/loop_filter.h"
#include "lib/jxl/memory_manager_internal.h"
#include "lib/jxl/sanitizers.h"
#include "lib/jxl/toc.h"
namespace {
// If set (by fuzzer) then some operations will fail, if those would require
// allocating large objects. Actual memory usage might be two orders of
// magnitude bigger.
// TODO(eustas): this is a poor-mans replacement for memory-manager approach;
// remove, once memory-manager actually works.
size_t memory_limit_base_ = 0;
size_t cpu_limit_base_ = 0;
size_t used_cpu_base_ = 0;
bool CheckSizeLimit(size_t xsize, size_t ysize) {
if (!memory_limit_base_) return true;
if (xsize == 0 || ysize == 0) return true;
size_t num_pixels = xsize * ysize;
if (num_pixels / xsize != ysize) return false; // overflow
if (num_pixels > memory_limit_base_) return false;
return true;
}
// Checks if a + b > size, taking possible integer overflow into account.
bool OutOfBounds(size_t a, size_t b, size_t size) {
size_t pos = a + b;
@ -612,8 +595,29 @@ struct JxlDecoderStruct {
// processed, so this check works.
return stage != DecoderStage::kCodestreamFinished;
}
// If set then some operations will fail, if those would require
// allocating large objects. Actual memory usage might be two orders of
// magnitude bigger.
// TODO(eustas): remove once there is working API for memory / CPU limit.
size_t memory_limit_base = 0;
size_t cpu_limit_base = 0;
size_t used_cpu_base = 0;
};
namespace {
bool CheckSizeLimit(JxlDecoder* dec, size_t xsize, size_t ysize) {
if (!dec->memory_limit_base) return true;
if (xsize == 0 || ysize == 0) return true;
size_t num_pixels = xsize * ysize;
if (num_pixels / xsize != ysize) return false; // overflow
if (num_pixels > dec->memory_limit_base) return false;
return true;
}
} // namespace
// TODO(zond): Make this depend on the data loaded into the decoder.
JxlDecoderStatus JxlDecoderDefaultPixelFormat(const JxlDecoder* dec,
JxlPixelFormat* format) {
@ -726,6 +730,16 @@ JxlDecoder* JxlDecoderCreate(const JxlMemoryManager* memory_manager) {
JxlDecoder* dec = new (alloc) JxlDecoder();
dec->memory_manager = local_memory_manager;
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (!memory_manager) {
dec->memory_limit_base = 1 << 21;
// Allow 5 x max_image_size processing units; every frame is accounted
// as W x H CPU processing units, so there could be numerous small frames
// or few larger ones.
dec->cpu_limit_base = 5 * dec->memory_limit_base;
}
#endif
JxlDecoderReset(dec);
return dec;
@ -824,6 +838,28 @@ JxlDecoderStatus JxlDecoderSetCoalescing(JxlDecoder* dec, JXL_BOOL coalescing) {
return JXL_DEC_SUCCESS;
}
namespace {
// helper function to get the dimensions of the current image buffer
void GetCurrentDimensions(const JxlDecoder* dec, size_t& xsize, size_t& ysize,
bool oriented) {
if (dec->frame_header->nonserialized_is_preview) {
xsize = dec->metadata.oriented_preview_xsize(dec->keep_orientation);
ysize = dec->metadata.oriented_preview_ysize(dec->keep_orientation);
return;
}
xsize = dec->metadata.oriented_xsize(dec->keep_orientation || !oriented);
ysize = dec->metadata.oriented_ysize(dec->keep_orientation || !oriented);
if (!dec->coalescing) {
xsize = dec->frame_header->ToFrameDimensions().xsize;
ysize = dec->frame_header->ToFrameDimensions().ysize;
if (!dec->keep_orientation && oriented &&
static_cast<int>(dec->metadata.m.GetOrientation()) > 4) {
std::swap(xsize, ysize);
}
}
}
} // namespace
namespace jxl {
namespace {
@ -900,7 +936,8 @@ JxlDecoderStatus JxlDecoderReadBasicInfo(JxlDecoder* dec, const uint8_t* in,
dec->got_basic_info = true;
dec->basic_info_size_hint = 0;
if (!CheckSizeLimit(dec->metadata.size.xsize(), dec->metadata.size.ysize())) {
if (!CheckSizeLimit(dec, dec->metadata.size.xsize(),
dec->metadata.size.ysize())) {
return JXL_API_ERROR("image is too large");
}
@ -945,7 +982,8 @@ JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec, const uint8_t* in,
dec->header_except_icc_bits = reader->TotalBitsConsumed();
if (dec->metadata.m.color_encoding.WantICC()) {
jxl::Status status = dec->icc_reader.Init(reader.get(), memory_limit_base_);
jxl::Status status =
dec->icc_reader.Init(reader.get(), dec->memory_limit_base);
// Always check AllReadsWithinBounds, not all the C++ decoder implementation
// handles reader out of bounds correctly yet (e.g. context map). Not
// checking AllReadsWithinBounds can cause reader->Close() to trigger an
@ -992,15 +1030,9 @@ JxlDecoderStatus JxlDecoderReadAllHeaders(JxlDecoder* dec, const uint8_t* in,
return JXL_DEC_SUCCESS;
}
static size_t GetStride(const JxlDecoder* dec, const JxlPixelFormat& format,
const jxl::ImageBundle* frame = nullptr) {
size_t xsize = dec->metadata.xsize();
if (!dec->keep_orientation && dec->metadata.m.orientation > 4) {
xsize = dec->metadata.ysize();
}
if (frame) {
xsize = dec->keep_orientation ? frame->xsize() : frame->oriented_xsize();
}
static size_t GetStride(const JxlDecoder* dec, const JxlPixelFormat& format) {
size_t xsize, ysize;
GetCurrentDimensions(dec, xsize, ysize, true);
size_t stride = xsize * (BitsPerChannel(format.data_type) *
format.num_channels / jxl::kBitsPerByte);
if (format.align > 1) {
@ -1022,7 +1054,7 @@ static JxlDecoderStatus ConvertImageInternal(
JxlImageOutCallback out_callback, void* out_opaque) {
// TODO(lode): handle mismatch of RGB/grayscale color profiles and pixel data
// color/grayscale format
const size_t stride = GetStride(dec, format, &frame);
const size_t stride = GetStride(dec, format);
bool float_format = format.data_type == JXL_TYPE_FLOAT ||
format.data_type == JXL_TYPE_FLOAT16;
@ -1054,7 +1086,8 @@ static JxlDecoderStatus ConvertImageInternal(
// Parses the FrameHeader and the total frame_size, given the initial bytes
// of the frame up to and including the TOC.
// TODO(lode): merge this with FrameDecoder
JxlDecoderStatus ParseFrameHeader(jxl::FrameHeader* frame_header,
JxlDecoderStatus ParseFrameHeader(JxlDecoder* dec,
jxl::FrameHeader* frame_header,
const uint8_t* in, size_t size, size_t pos,
bool is_preview, size_t* frame_size,
int* saved_as) {
@ -1067,7 +1100,7 @@ JxlDecoderStatus ParseFrameHeader(jxl::FrameHeader* frame_header,
frame_header->nonserialized_is_preview = is_preview;
jxl::Status status = DecodeFrameHeader(reader.get(), frame_header);
jxl::FrameDimensions frame_dim = frame_header->ToFrameDimensions();
if (!CheckSizeLimit(frame_dim.xsize_upsampled_padded,
if (!CheckSizeLimit(dec, frame_dim.xsize_upsampled_padded,
frame_dim.ysize_upsampled_padded)) {
return JXL_API_ERROR("frame is too large");
}
@ -1180,9 +1213,9 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
size_t frame_size;
size_t pos = dec->frame_start;
dec->frame_header.reset(new FrameHeader(&dec->metadata));
JxlDecoderStatus status = ParseFrameHeader(dec->frame_header.get(), in,
size, pos, true, &frame_size,
/*saved_as=*/nullptr);
JxlDecoderStatus status = ParseFrameHeader(
dec, dec->frame_header.get(), in, size, pos, true, &frame_size,
/*saved_as=*/nullptr);
if (status != JXL_DEC_SUCCESS) return status;
if (OutOfBounds(pos, frame_size, size)) {
return JXL_DEC_NEED_MORE_INPUT;
@ -1255,7 +1288,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
dec->frame_header.reset(new FrameHeader(&dec->metadata));
int saved_as = 0;
JxlDecoderStatus status =
ParseFrameHeader(dec->frame_header.get(), in, size, pos,
ParseFrameHeader(dec, dec->frame_header.get(), in, size, pos,
/*is_preview=*/false, &dec->frame_size, &saved_as);
if (status != JXL_DEC_SUCCESS) return status;
@ -1349,9 +1382,13 @@ 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->passes_state.get(), dec->metadata, dec->thread_pool.get(),
/*use_slow_rendering_pipeline=*/false));
dec->frame_dec->SetRenderSpotcolors(dec->render_spotcolors);
dec->frame_dec->SetCoalescing(dec->coalescing);
if (dec->events_wanted & JXL_DEC_FRAME_PROGRESSION) {
dec->frame_dec->SetPauseAtProgressive();
}
// If JPEG reconstruction is wanted and possible, set the jpeg_data of
// the ImageBundle.
@ -1436,15 +1473,15 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
}
dec->sections->SetInput(in + pos, size - pos);
if (cpu_limit_base_ != 0) {
if (dec->cpu_limit_base != 0) {
FrameDimensions frame_dim = dec->frame_header->ToFrameDimensions();
// No overflow, checked in ParseHeader.
size_t num_pixels = frame_dim.xsize * frame_dim.ysize;
if (used_cpu_base_ + num_pixels < used_cpu_base_) {
if (dec->used_cpu_base + num_pixels < dec->used_cpu_base) {
return JXL_API_ERROR("used too much CPU");
}
used_cpu_base_ += num_pixels;
if (used_cpu_base_ > cpu_limit_base_) {
dec->used_cpu_base += num_pixels;
if (dec->used_cpu_base > dec->cpu_limit_base) {
return JXL_API_ERROR("used too much CPU");
}
}
@ -1461,8 +1498,17 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
// TODO(lode): allow next_in to move forward if sections from the
// beginning of the stream have been processed
if (status.code() == StatusCode::kNotEnoughBytes ||
dec->sections->section_info.size() < dec->frame_dec->NumSections()) {
bool all_sections_done = !!status && dec->frame_dec->HasDecodedAll();
bool got_dc_only =
!!status && !all_sections_done && dec->frame_dec->HasDecodedDC();
if ((dec->events_wanted & JXL_DEC_FRAME_PROGRESSION) && got_dc_only) {
dec->events_wanted &= ~JXL_DEC_FRAME_PROGRESSION;
return JXL_DEC_FRAME_PROGRESSION;
}
if (!all_sections_done) {
// Not all sections have been processed yet
return JXL_DEC_NEED_MORE_INPUT;
}
@ -1497,7 +1543,8 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
// Frame finished, restore the events_wanted with the per-frame events
// from orig_events_wanted, in case there is a next frame.
dec->events_wanted |=
(dec->orig_events_wanted & (JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME));
(dec->orig_events_wanted &
(JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME | JXL_DEC_FRAME_PROGRESSION));
// If no output buffer was set, we merely return the JXL_DEC_FULL_IMAGE
// status without outputting pixels.
@ -2131,7 +2178,6 @@ JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec,
info->have_preview = meta.have_preview;
info->have_animation = meta.have_animation;
// TODO(janwas): intrinsic_size
info->orientation = static_cast<JxlOrientation>(meta.orientation);
if (!dec->keep_orientation) {
@ -2174,6 +2220,14 @@ JxlDecoderStatus JxlDecoderGetBasicInfo(const JxlDecoder* dec,
info->animation.num_loops = dec->metadata.m.animation.num_loops;
info->animation.have_timecodes = dec->metadata.m.animation.have_timecodes;
}
if (meta.have_intrinsic_size) {
info->intrinsic_xsize = dec->metadata.m.intrinsic_size.xsize();
info->intrinsic_ysize = dec->metadata.m.intrinsic_size.ysize();
} else {
info->intrinsic_xsize = info->xsize;
info->intrinsic_ysize = info->ysize;
}
}
return JXL_DEC_SUCCESS;
@ -2349,21 +2403,6 @@ JxlDecoderStatus PrepareSizeCheck(const JxlDecoder* dec,
return JXL_DEC_SUCCESS;
}
// helper function to get the dimensions of the current image buffer
void GetCurrentDimensions(const JxlDecoder* dec, size_t& xsize, size_t& ysize,
bool oriented) {
xsize = dec->metadata.oriented_xsize(dec->keep_orientation || !oriented);
ysize = dec->metadata.oriented_ysize(dec->keep_orientation || !oriented);
if (!dec->coalescing) {
xsize = dec->frame_header->ToFrameDimensions().xsize;
ysize = dec->frame_header->ToFrameDimensions().ysize;
if (!dec->keep_orientation && oriented &&
static_cast<int>(dec->metadata.m.GetOrientation()) > 4) {
std::swap(xsize, ysize);
}
}
}
} // namespace
JxlDecoderStatus JxlDecoderFlushImage(JxlDecoder* dec) {
@ -2419,10 +2458,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderPreviewOutBufferSize(
size_t row_size =
jxl::DivCeil(xsize * format->num_channels * bits, jxl::kBitsPerByte);
size_t last_row_size = row_size;
if (format->align > 1) {
row_size = jxl::DivCeil(row_size, format->align) * format->align;
}
*size = row_size * ysize;
*size = row_size * (ysize - 1) + last_row_size;
return JXL_DEC_SUCCESS;
}
@ -2466,10 +2506,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderDCOutBufferSize(
size_t row_size =
jxl::DivCeil(xsize * format->num_channels * bits, jxl::kBitsPerByte);
size_t last_row_size = row_size;
if (format->align > 1) {
row_size = jxl::DivCeil(row_size, format->align) * format->align;
}
*size = row_size * ysize;
*size = row_size * (ysize - 1) + last_row_size;
return JXL_DEC_SUCCESS;
}
@ -2728,18 +2769,6 @@ JxlDecoderStatus JxlDecoderSetPreferredColorProfile(
return JXL_DEC_SUCCESS;
}
// This function is "package-private". It is only used by fuzzer to avoid
// running cases that are too memory / CPU hungry. Limitations are applied
// at mid-level API. In the future high-level API would also include the
// means of limiting / throttling memory / CPU usage.
void SetDecoderMemoryLimitBase_(size_t memory_limit_base) {
memory_limit_base_ = memory_limit_base;
// Allow 5 x max_image_size processing units; every frame is accounted
// as W x H CPU processing units, so there could be numerous small frames
// or few larger ones.
cpu_limit_base_ = 5 * memory_limit_base;
}
JxlDecoderStatus JxlDecoderSetBoxBuffer(JxlDecoder* dec, uint8_t* data,
size_t size) {
if (dec->box_out_buffer_set) {

709
third_party/jpeg-xl/lib/jxl/decode_test.cc поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -587,15 +587,15 @@ ImageF AdaptiveQuantizationMap(const float butteraugli_target,
AdaptiveQuantizationImpl impl;
impl.Init(xyb);
*mask = ImageF(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
RunOnPool(
JXL_CHECK(RunOnPool(
pool, 0,
DivCeil(frame_dim.xsize_blocks, kEncTileDimInBlocks) *
DivCeil(frame_dim.ysize_blocks, kEncTileDimInBlocks),
[&](size_t num_threads) {
[&](const size_t num_threads) {
impl.PrepareBuffers(num_threads);
return true;
},
[&](const int tid, int thread) {
[&](const uint32_t tid, const size_t thread) {
size_t n_enc_tiles =
DivCeil(frame_dim.xsize_blocks, kEncTileDimInBlocks);
size_t tx = tid % n_enc_tiles;
@ -609,7 +609,7 @@ ImageF AdaptiveQuantizationMap(const float butteraugli_target,
Rect r(bx0, by0, bx1 - bx0, by1 - by0);
impl.ComputeTile(butteraugli_target, scale, xyb, r, thread, mask);
},
"AQ DiffPrecompute");
"AQ DiffPrecompute"));
return std::move(impl).aq_map;
}
@ -732,7 +732,8 @@ static const float kDcQuant = 1.12f;
static const float kAcQuant = 0.825f;
void FindBestQuantization(const ImageBundle& linear, const Image3F& opsin,
PassesEncoderState* enc_state, ThreadPool* pool,
PassesEncoderState* enc_state,
const JxlCmsInterface& cms, ThreadPool* pool,
AuxOut* aux_out) {
const CompressParams& cparams = enc_state->cparams;
Quantizer& quantizer = enc_state->shared.quantizer;
@ -748,7 +749,7 @@ void FindBestQuantization(const ImageBundle& linear, const Image3F& opsin,
if (fabs(params.intensity_target - 255.0f) < 1e-3) {
params.intensity_target = 80.0f;
}
JxlButteraugliComparator comparator(params);
JxlButteraugliComparator comparator(params, cms);
JXL_CHECK(comparator.SetReferenceImage(linear));
bool lower_is_better =
(comparator.GoodQualityScore() < comparator.BadQualityScore());
@ -788,7 +789,7 @@ void FindBestQuantization(const ImageBundle& linear, const Image3F& opsin,
}
}
quantizer.SetQuantField(initial_quant_dc, quant_field, &raw_quant_field);
ImageBundle linear = RoundtripImage(opsin, enc_state, pool);
ImageBundle linear = RoundtripImage(opsin, enc_state, cms, pool);
PROFILER_ZONE("enc Butteraugli");
float score;
ImageF diffmap;
@ -898,7 +899,8 @@ void FindBestQuantization(const ImageBundle& linear, const Image3F& opsin,
void FindBestQuantizationMaxError(const Image3F& opsin,
PassesEncoderState* enc_state,
ThreadPool* pool, AuxOut* aux_out) {
const JxlCmsInterface& cms, ThreadPool* pool,
AuxOut* aux_out) {
// TODO(veluca): this only works if opsin is in XYB. The current encoder does
// not have code paths that produce non-XYB opsin here.
JXL_CHECK(enc_state->shared.frame_header.color_transform ==
@ -923,7 +925,7 @@ void FindBestQuantizationMaxError(const Image3F& opsin,
if (aux_out) {
aux_out->DumpXybImage(("ops" + ToString(i)).c_str(), opsin);
}
ImageBundle decoded = RoundtripImage(opsin, enc_state, pool);
ImageBundle decoded = RoundtripImage(opsin, enc_state, cms, pool);
if (aux_out) {
aux_out->DumpXybImage(("dec" + ToString(i)).c_str(), *decoded.color());
}
@ -1024,21 +1026,22 @@ ImageF InitialQuantField(const float butteraugli_target, const Image3F& opsin,
}
void FindBestQuantizer(const ImageBundle* linear, const Image3F& opsin,
PassesEncoderState* enc_state, ThreadPool* pool,
PassesEncoderState* enc_state,
const JxlCmsInterface& cms, ThreadPool* pool,
AuxOut* aux_out, double rescale) {
const CompressParams& cparams = enc_state->cparams;
if (cparams.max_error_mode) {
PROFILER_ZONE("enc find best maxerr");
FindBestQuantizationMaxError(opsin, enc_state, pool, aux_out);
FindBestQuantizationMaxError(opsin, enc_state, cms, pool, aux_out);
} else if (cparams.speed_tier <= SpeedTier::kKitten) {
// Normal encoding to a butteraugli score.
PROFILER_ZONE("enc find best2");
FindBestQuantization(*linear, opsin, enc_state, pool, aux_out);
FindBestQuantization(*linear, opsin, enc_state, cms, pool, aux_out);
}
}
ImageBundle RoundtripImage(const Image3F& opsin, PassesEncoderState* enc_state,
ThreadPool* pool) {
const JxlCmsInterface& cms, ThreadPool* pool) {
PROFILER_ZONE("enc roundtrip");
std::unique_ptr<PassesDecoderState> dec_state =
jxl::make_unique<PassesDecoderState>();
@ -1058,10 +1061,10 @@ ImageBundle RoundtripImage(const Image3F& opsin, PassesEncoderState* enc_state,
std::unique_ptr<ModularFrameEncoder> modular_frame_encoder =
jxl::make_unique<ModularFrameEncoder>(enc_state->shared.frame_header,
enc_state->cparams);
InitializePassesEncoder(opsin, pool, enc_state, modular_frame_encoder.get(),
nullptr);
JXL_CHECK(InitializePassesEncoder(opsin, cms, pool, enc_state,
modular_frame_encoder.get(), nullptr));
JXL_CHECK(dec_state->Init());
dec_state->InitForAC(pool);
JXL_CHECK(dec_state->InitForAC(pool));
ImageBundle decoded(&enc_state->shared.metadata->m);
decoded.origin = enc_state->shared.frame_header.frame_origin;
@ -1088,12 +1091,13 @@ ImageBundle RoundtripImage(const Image3F& opsin, PassesEncoderState* enc_state,
}
hwy::AlignedUniquePtr<GroupDecCache[]> group_dec_caches;
const auto allocate_storage = [&](size_t num_threads) {
const auto allocate_storage = [&](const size_t num_threads) {
dec_state->EnsureStorage(num_threads);
group_dec_caches = hwy::MakeUniqueAlignedArray<GroupDecCache>(num_threads);
return true;
};
const auto process_group = [&](const int group_index, const int thread) {
const auto process_group = [&](const uint32_t group_index,
const size_t thread) {
if (dec_state->shared->frame_header.loop_filter.epf_iters > 0) {
ComputeSigma(dec_state->shared->BlockGroupRect(group_index),
dec_state.get());
@ -1102,7 +1106,8 @@ ImageBundle RoundtripImage(const Image3F& opsin, PassesEncoderState* enc_state,
enc_state->coeffs, group_index, dec_state.get(),
&group_dec_caches[thread], thread, &decoded, nullptr));
};
RunOnPool(pool, 0, num_groups, allocate_storage, process_group, "AQ loop");
JXL_CHECK(RunOnPool(pool, 0, num_groups, allocate_storage, process_group,
"AQ loop"));
// Fine to do a JXL_ASSERT instead of error handling, since this only happens
// on the encoder side where we can't be fed with invalid data.

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше