mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 05:11:16 +00:00
Bug 1767432 - Update libjxl to 192ddd r=tnikkel
Differential Revision: https://phabricator.services.mozilla.com/D145332
This commit is contained in:
parent
733cea14b9
commit
0d3b3ecabe
@ -8,6 +8,9 @@
|
|||||||
#define JXL_EXPORT_H
|
#define JXL_EXPORT_H
|
||||||
|
|
||||||
#define JXL_EXPORT
|
#define JXL_EXPORT
|
||||||
#define JXL_DEPRECATED [[deprecated]]
|
|
||||||
|
// TODO: go back to [[deprecated]]
|
||||||
|
// https://github.com/libjxl/libjxl/issues/1388
|
||||||
|
#define JXL_DEPRECATED __attribute__((__deprecated__))
|
||||||
|
|
||||||
#endif /* JXL_EXPORT_H */
|
#endif /* JXL_EXPORT_H */
|
||||||
|
@ -19,7 +19,6 @@ SOURCES += [
|
|||||||
"/third_party/jpeg-xl/lib/jxl/base/data_parallel.cc",
|
"/third_party/jpeg-xl/lib/jxl/base/data_parallel.cc",
|
||||||
"/third_party/jpeg-xl/lib/jxl/base/padded_bytes.cc",
|
"/third_party/jpeg-xl/lib/jxl/base/padded_bytes.cc",
|
||||||
"/third_party/jpeg-xl/lib/jxl/base/random.cc",
|
"/third_party/jpeg-xl/lib/jxl/base/random.cc",
|
||||||
"/third_party/jpeg-xl/lib/jxl/base/status.cc",
|
|
||||||
"/third_party/jpeg-xl/lib/jxl/blending.cc",
|
"/third_party/jpeg-xl/lib/jxl/blending.cc",
|
||||||
"/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc",
|
"/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc",
|
||||||
"/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc",
|
"/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc",
|
||||||
@ -46,6 +45,7 @@ SOURCES += [
|
|||||||
"/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc",
|
"/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/entropy_coder.cc",
|
||||||
"/third_party/jpeg-xl/lib/jxl/epf.cc",
|
"/third_party/jpeg-xl/lib/jxl/epf.cc",
|
||||||
|
"/third_party/jpeg-xl/lib/jxl/exif.cc",
|
||||||
"/third_party/jpeg-xl/lib/jxl/fast_dct.cc",
|
"/third_party/jpeg-xl/lib/jxl/fast_dct.cc",
|
||||||
"/third_party/jpeg-xl/lib/jxl/fields.cc",
|
"/third_party/jpeg-xl/lib/jxl/fields.cc",
|
||||||
"/third_party/jpeg-xl/lib/jxl/frame_header.cc",
|
"/third_party/jpeg-xl/lib/jxl/frame_header.cc",
|
||||||
|
@ -46,7 +46,7 @@ jobs:
|
|||||||
# Build scalar-only hwy instructions.
|
# Build scalar-only hwy instructions.
|
||||||
- name: scalar
|
- name: scalar
|
||||||
mode: release
|
mode: release
|
||||||
cxxflags: -DHWY_DISABLED_TARGETS=~HWY_SCALAR
|
cxxflags: -DHWY_COMPILE_ONLY_SCALAR
|
||||||
# Disabling optional features to speed up msan build a little bit.
|
# Disabling optional features to speed up msan build a little bit.
|
||||||
- name: msan
|
- name: msan
|
||||||
skip_install: true
|
skip_install: true
|
||||||
@ -59,8 +59,6 @@ jobs:
|
|||||||
apt_pkgs: gcovr
|
apt_pkgs: gcovr
|
||||||
# Coverage builds require a bit more RAM.
|
# Coverage builds require a bit more RAM.
|
||||||
env_test_stack_size: 2048
|
env_test_stack_size: 2048
|
||||||
# Exclude roundtrip tests from the unittest coverage.
|
|
||||||
ctest_args: -E '^JxlTest'
|
|
||||||
# Build with support for decoding to JPEG bytes disabled. Produces a
|
# Build with support for decoding to JPEG bytes disabled. Produces a
|
||||||
# smaller build if only decoding to pixels is needed.
|
# smaller build if only decoding to pixels is needed.
|
||||||
- name: release-nojpeg
|
- name: release-nojpeg
|
||||||
@ -149,6 +147,7 @@ jobs:
|
|||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-${{ steps.git-env.outputs.parent }}-${{ matrix.name }}
|
${{ runner.os }}-${{ steps.git-env.outputs.parent }}-${{ matrix.name }}
|
||||||
- name: Build
|
- name: Build
|
||||||
|
if: matrix.name != 'coverage' || env.WILL_RUN_TESTS == 'true'
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ${CCACHE_DIR}
|
mkdir -p ${CCACHE_DIR}
|
||||||
echo "max_size = 200M" > ${CCACHE_DIR}/ccache.conf
|
echo "max_size = 200M" > ${CCACHE_DIR}/ccache.conf
|
||||||
@ -216,10 +215,10 @@ jobs:
|
|||||||
files: build/coverage.xml
|
files: build/coverage.xml
|
||||||
- name: Fast benchmark ${{ matrix.mode }}
|
- name: Fast benchmark ${{ matrix.mode }}
|
||||||
if: |
|
if: |
|
||||||
github.event_name == 'push' ||
|
matrix.name != 'coverage' && (github.event_name == 'push' ||
|
||||||
(github.event_name == 'pull_request' && (
|
(github.event_name == 'pull_request' && (
|
||||||
matrix.test_in_pr ||
|
matrix.test_in_pr ||
|
||||||
contains(github.event.pull_request.labels.*.names, 'CI:full')))
|
contains(github.event.pull_request.labels.*.names, 'CI:full'))))
|
||||||
run: |
|
run: |
|
||||||
STORE_IMAGES=0 ./ci.sh fast_benchmark
|
STORE_IMAGES=0 ./ci.sh fast_benchmark
|
||||||
# Run gbench once, just to make sure it runs, not for actual benchmarking.
|
# Run gbench once, just to make sure it runs, not for actual benchmarking.
|
||||||
@ -310,7 +309,6 @@ jobs:
|
|||||||
# Build dependencies
|
# Build dependencies
|
||||||
cmake
|
cmake
|
||||||
doxygen
|
doxygen
|
||||||
libgtest-dev:${{ matrix.arch }}
|
|
||||||
ninja-build
|
ninja-build
|
||||||
pkg-config
|
pkg-config
|
||||||
qemu-user-static
|
qemu-user-static
|
||||||
|
@ -98,8 +98,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
# 'main_level10' currently fails
|
name: [main_level5, main_level10]
|
||||||
name: [main_level5]
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: |
|
run: |
|
||||||
|
@ -146,6 +146,11 @@ jobs:
|
|||||||
cd "git-${git_version}"
|
cd "git-${git_version}"
|
||||||
make prefix=/usr -j4 install
|
make prefix=/usr -j4 install
|
||||||
|
|
||||||
|
- name: Set git safe dir
|
||||||
|
run: |
|
||||||
|
export GIT_CEILING_DIRECTORIES=/__w # only work before git v2.35.2
|
||||||
|
git config --global --add safe.directory /__w/libjxl/libjxl
|
||||||
|
|
||||||
- name: Checkout the source
|
- name: Checkout the source
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
|
4
third_party/jpeg-xl/AUTHORS
vendored
4
third_party/jpeg-xl/AUTHORS
vendored
@ -25,16 +25,20 @@ Daniel Novomeský <dnovomesky@gmail.com>
|
|||||||
David Burnett <vargolsoft@gmail.com>
|
David Burnett <vargolsoft@gmail.com>
|
||||||
Dirk Lemstra <dirk@lemstra.org>
|
Dirk Lemstra <dirk@lemstra.org>
|
||||||
Don Olmstead <don.j.olmstead@gmail.com>
|
Don Olmstead <don.j.olmstead@gmail.com>
|
||||||
|
Heiko Becker <heirecka@exherbo.org>
|
||||||
Jon Sneyers <jon@cloudinary.com>
|
Jon Sneyers <jon@cloudinary.com>
|
||||||
Kleis Auke Wolthuizen <github@kleisauke.nl>
|
Kleis Auke Wolthuizen <github@kleisauke.nl>
|
||||||
|
L. E. Segovia
|
||||||
Leo Izen <leo.izen@gmail.com>
|
Leo Izen <leo.izen@gmail.com>
|
||||||
Lovell Fuller
|
Lovell Fuller
|
||||||
Marcin Konicki <ahwayakchih@gmail.com>
|
Marcin Konicki <ahwayakchih@gmail.com>
|
||||||
|
Martin Strunz
|
||||||
Mathieu Malaterre <mathieu.malaterre@gmail.com>
|
Mathieu Malaterre <mathieu.malaterre@gmail.com>
|
||||||
Misaki Kasumi <misakikasumi@outlook.com>
|
Misaki Kasumi <misakikasumi@outlook.com>
|
||||||
Petr Diblík
|
Petr Diblík
|
||||||
Pieter Wuille
|
Pieter Wuille
|
||||||
Samuel Leong <wvvwvvvvwvvw@gmail.com>
|
Samuel Leong <wvvwvvvvwvvw@gmail.com>
|
||||||
|
Stephan T. Lavavej <stl@nuwen.net>
|
||||||
Vincent Torri <vincent.torri@gmail.com>
|
Vincent Torri <vincent.torri@gmail.com>
|
||||||
xiota
|
xiota
|
||||||
Yonatan Nebenzhal <yonatan.nebenzhl@gmail.com>
|
Yonatan Nebenzhal <yonatan.nebenzhl@gmail.com>
|
||||||
|
3
third_party/jpeg-xl/CHANGELOG.md
vendored
3
third_party/jpeg-xl/CHANGELOG.md
vendored
@ -23,6 +23,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
the non-coalesced case.
|
the non-coalesced case.
|
||||||
- decoder API: new function `JxlDecoderGetExtraChannelBlendInfo` to get
|
- decoder API: new function `JxlDecoderGetExtraChannelBlendInfo` to get
|
||||||
the blending information for extra channels in the non-coalesced case.
|
the blending information for extra channels in the non-coalesced case.
|
||||||
|
- decoder API: new function `JxlDecoderSetMultithreadedImageOutCallback`,
|
||||||
|
allowing output callbacks to receive more information about the number of
|
||||||
|
threads on which they are running.
|
||||||
- encoder API: added ability to set several encoder options to frames using
|
- encoder API: added ability to set several encoder options to frames using
|
||||||
`JxlEncoderFrameSettingsSetOption`
|
`JxlEncoderFrameSettingsSetOption`
|
||||||
- encoder API: new functions `JxlEncoderSetFrameHeader` and
|
- encoder API: new functions `JxlEncoderSetFrameHeader` and
|
||||||
|
19
third_party/jpeg-xl/CMakeLists.txt
vendored
19
third_party/jpeg-xl/CMakeLists.txt
vendored
@ -191,6 +191,15 @@ endif() # JPEGXL_STATIC
|
|||||||
set(THREADS_PREFER_PTHREAD_FLAG YES)
|
set(THREADS_PREFER_PTHREAD_FLAG YES)
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
# These settings are important to drive check_cxx_source_compiles
|
||||||
|
# See CMP0067 (min cmake version is 3.10 anyway)
|
||||||
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
||||||
|
|
||||||
|
# Atomics
|
||||||
|
find_package(Atomics REQUIRED)
|
||||||
|
|
||||||
if(JPEGXL_STATIC)
|
if(JPEGXL_STATIC)
|
||||||
if (MINGW)
|
if (MINGW)
|
||||||
# In MINGW libstdc++ uses pthreads directly. When building statically a
|
# In MINGW libstdc++ uses pthreads directly. When building statically a
|
||||||
@ -298,10 +307,6 @@ endif () # !MSVC
|
|||||||
|
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED YES)
|
|
||||||
|
|
||||||
add_subdirectory(third_party)
|
add_subdirectory(third_party)
|
||||||
|
|
||||||
# Copy the JXL license file to the output build directory.
|
# Copy the JXL license file to the output build directory.
|
||||||
@ -311,6 +316,10 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/LICENSE"
|
|||||||
# Enable tests regardless of where they are defined.
|
# Enable tests regardless of where they are defined.
|
||||||
enable_testing()
|
enable_testing()
|
||||||
include(CTest)
|
include(CTest)
|
||||||
|
# Specify default location of `testdata`:
|
||||||
|
if(NOT DEFINED JPEGXL_TEST_DATA_PATH)
|
||||||
|
set(JPEGXL_TEST_DATA_PATH "${PROJECT_SOURCE_DIR}/third_party/testdata")
|
||||||
|
endif()
|
||||||
|
|
||||||
# Libraries.
|
# Libraries.
|
||||||
add_subdirectory(lib)
|
add_subdirectory(lib)
|
||||||
@ -419,7 +428,7 @@ if (ASCIIDOC_PY_FOUND)
|
|||||||
list(APPEND MANPAGES "${PAGE}.1")
|
list(APPEND MANPAGES "${PAGE}.1")
|
||||||
endforeach()
|
endforeach()
|
||||||
add_custom_target(manpages ALL DEPENDS ${MANPAGES})
|
add_custom_target(manpages ALL DEPENDS ${MANPAGES})
|
||||||
install(FILES ${MANPAGE_FILES} DESTINATION share/man/man1)
|
install(FILES ${MANPAGE_FILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
|
||||||
endif() # ASCIIDOC_PY_FOUND
|
endif() # ASCIIDOC_PY_FOUND
|
||||||
else()
|
else()
|
||||||
message(WARNING "asciidoc was not found, the man pages will not be installed.")
|
message(WARNING "asciidoc was not found, the man pages will not be installed.")
|
||||||
|
6
third_party/jpeg-xl/ci.sh
vendored
6
third_party/jpeg-xl/ci.sh
vendored
@ -905,7 +905,7 @@ run_benchmark() {
|
|||||||
|
|
||||||
local benchmark_args=(
|
local benchmark_args=(
|
||||||
--input "${src_img_dir}/*.png"
|
--input "${src_img_dir}/*.png"
|
||||||
--codec=jpeg:yuv420:q85,webp:q80,jxl:fast:d1,jxl:fast:d1:downsampling=8,jxl:fast:d4,jxl:fast:d4:downsampling=8,jxl:cheetah:m,jxl:m:cheetah:P6,jxl:m:falcon:q80
|
--codec=jpeg:yuv420:q85,webp:q80,jxl:d1:6,jxl:d1:6:downsampling=8,jxl:d5:6,jxl:d5:6:downsampling=8,jxl:m:d0:2,jxl:m:d0:3,jxl:m:d2:2
|
||||||
--output_dir "${output_dir}"
|
--output_dir "${output_dir}"
|
||||||
--noprofiler --show_progress
|
--noprofiler --show_progress
|
||||||
--num_threads="${num_threads}"
|
--num_threads="${num_threads}"
|
||||||
@ -1020,11 +1020,11 @@ cmd_arm_benchmark() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
local images=(
|
local images=(
|
||||||
"third_party/testdata/imagecompression.info/flower_foveon.png"
|
"third_party/testdata/third_party/imagecompression.info/flower_foveon.png"
|
||||||
)
|
)
|
||||||
|
|
||||||
local jpg_images=(
|
local jpg_images=(
|
||||||
"third_party/testdata/imagecompression.info/flower_foveon.png.im_q85_420.jpg"
|
"third_party/testdata/third_party/imagecompression.info/flower_foveon.png.im_q85_420.jpg"
|
||||||
)
|
)
|
||||||
|
|
||||||
if [[ "${SKIP_CPUSET:-}" == "1" ]]; then
|
if [[ "${SKIP_CPUSET:-}" == "1" ]]; then
|
||||||
|
53
third_party/jpeg-xl/cmake/FindAtomics.cmake
vendored
Normal file
53
third_party/jpeg-xl/cmake/FindAtomics.cmake
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# Original issue:
|
||||||
|
# * https://gitlab.kitware.com/cmake/cmake/-/issues/23021#note_1098733
|
||||||
|
#
|
||||||
|
# For reference:
|
||||||
|
# * https://gcc.gnu.org/wiki/Atomic/GCCMM
|
||||||
|
#
|
||||||
|
# riscv64 specific:
|
||||||
|
# * https://lists.debian.org/debian-riscv/2022/01/msg00009.html
|
||||||
|
#
|
||||||
|
# ATOMICS_FOUND - system has c++ atomics
|
||||||
|
# ATOMICS_LIBRARIES - libraries needed to use c++ atomics
|
||||||
|
|
||||||
|
include(CheckCXXSourceCompiles)
|
||||||
|
|
||||||
|
# RISC-V only has 32-bit and 64-bit atomic instructions. GCC is supposed
|
||||||
|
# to convert smaller atomics to those larger ones via masking and
|
||||||
|
# shifting like LLVM, but it’s a known bug that it does not. This means
|
||||||
|
# anything that wants to use atomics on 1-byte or 2-byte types needs
|
||||||
|
# -latomic, but not 4-byte or 8-byte (though it does no harm).
|
||||||
|
set(atomic_code
|
||||||
|
"
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdint>
|
||||||
|
std::atomic<uint8_t> n8 (0); // riscv64
|
||||||
|
std::atomic<uint64_t> n64 (0); // armel, mipsel, powerpc
|
||||||
|
int main() {
|
||||||
|
++n8;
|
||||||
|
++n64;
|
||||||
|
return 0;
|
||||||
|
}")
|
||||||
|
|
||||||
|
check_cxx_source_compiles("${atomic_code}" ATOMICS_LOCK_FREE_INSTRUCTIONS)
|
||||||
|
|
||||||
|
if(ATOMICS_LOCK_FREE_INSTRUCTIONS)
|
||||||
|
set(ATOMICS_FOUND TRUE)
|
||||||
|
set(ATOMICS_LIBRARIES)
|
||||||
|
else()
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES "-latomic")
|
||||||
|
check_cxx_source_compiles("${atomic_code}" ATOMICS_IN_LIBRARY)
|
||||||
|
set(CMAKE_REQUIRED_LIBRARIES)
|
||||||
|
if(ATOMICS_IN_LIBRARY)
|
||||||
|
set(ATOMICS_LIBRARY atomic)
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
find_package_handle_standard_args(Atomics DEFAULT_MSG ATOMICS_LIBRARY)
|
||||||
|
set(ATOMICS_LIBRARIES ${ATOMICS_LIBRARY})
|
||||||
|
unset(ATOMICS_LIBRARY)
|
||||||
|
else()
|
||||||
|
if(Atomics_FIND_REQUIRED)
|
||||||
|
message(FATAL_ERROR "Neither lock free instructions nor -latomic found.")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
unset(atomic_code)
|
6
third_party/jpeg-xl/debian/copyright
vendored
6
third_party/jpeg-xl/debian/copyright
vendored
@ -38,7 +38,7 @@ License: BSD-3-clause
|
|||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
Files: third_party/testdata/imagecompression.info/*
|
Files: third_party/testdata/third_party/imagecompression.info/*
|
||||||
Copyright: their respective owners.
|
Copyright: their respective owners.
|
||||||
License: License without any prohibitive copyright restrictions.
|
License: License without any prohibitive copyright restrictions.
|
||||||
See https://imagecompression.info/test_images/ for details.
|
See https://imagecompression.info/test_images/ for details.
|
||||||
@ -58,7 +58,7 @@ License: License without any prohibitive copyright restrictions.
|
|||||||
sell the material.
|
sell the material.
|
||||||
4. This notice may not be removed or altered from any distribution.
|
4. This notice may not be removed or altered from any distribution.
|
||||||
|
|
||||||
Files: third_party/testdata/pngsuite/*
|
Files: third_party/testdata/third_party/pngsuite/*
|
||||||
Copyright: Willem van Schaik, 1996, 2011
|
Copyright: Willem van Schaik, 1996, 2011
|
||||||
License: PngSuite License
|
License: PngSuite License
|
||||||
See http://www.schaik.com/pngsuite/ for details.
|
See http://www.schaik.com/pngsuite/ for details.
|
||||||
@ -74,7 +74,7 @@ Files: third_party/testdata/raw.pixls/*
|
|||||||
Copyright: their respective owners listed in https://www.wesaturate.com/
|
Copyright: their respective owners listed in https://www.wesaturate.com/
|
||||||
License: CC0-1.0
|
License: CC0-1.0
|
||||||
|
|
||||||
Files: third_party/testdata/wide-gamut-tests/
|
Files: third_party/testdata/third_party/wide-gamut-tests/
|
||||||
Copyright: github.com/codelogic/wide-gamut-tests authors.
|
Copyright: github.com/codelogic/wide-gamut-tests authors.
|
||||||
License: Apache-2.0
|
License: Apache-2.0
|
||||||
|
|
||||||
|
2
third_party/jpeg-xl/deps.sh
vendored
2
third_party/jpeg-xl/deps.sh
vendored
@ -13,6 +13,7 @@ MYDIR=$(dirname $(realpath "$0"))
|
|||||||
|
|
||||||
# Git revisions we use for the given submodules. Update these whenever you
|
# Git revisions we use for the given submodules. Update these whenever you
|
||||||
# update a git submodule.
|
# update a git submodule.
|
||||||
|
THIRD_PARTY_BROTLI="35ef5c554d888bef217d449346067de05e269b30"
|
||||||
THIRD_PARTY_GFLAGS="827c769e5fc98e0f2a34c47cef953cc6328abced"
|
THIRD_PARTY_GFLAGS="827c769e5fc98e0f2a34c47cef953cc6328abced"
|
||||||
THIRD_PARTY_HIGHWAY="f13e3b956eb226561ac79427893ec0afd66f91a8"
|
THIRD_PARTY_HIGHWAY="f13e3b956eb226561ac79427893ec0afd66f91a8"
|
||||||
THIRD_PARTY_SKCMS="64374756e03700d649f897dbd98c95e78c30c7da"
|
THIRD_PARTY_SKCMS="64374756e03700d649f897dbd98c95e78c30c7da"
|
||||||
@ -71,6 +72,7 @@ EOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Sources downloaded from a tarball.
|
# Sources downloaded from a tarball.
|
||||||
|
download_github third_party/brotli google/brotli
|
||||||
download_github third_party/gflags gflags/gflags
|
download_github third_party/gflags gflags/gflags
|
||||||
download_github third_party/highway google/highway
|
download_github third_party/highway google/highway
|
||||||
download_github third_party/sjpeg webmproject/sjpeg
|
download_github third_party/sjpeg webmproject/sjpeg
|
||||||
|
3
third_party/jpeg-xl/examples/CMakeLists.txt
vendored
3
third_party/jpeg-xl/examples/CMakeLists.txt
vendored
@ -25,9 +25,6 @@ target_link_libraries(decode_progressive PkgConfig::Jxl PkgConfig::JxlThreads)
|
|||||||
add_executable(encode_oneshot encode_oneshot.cc)
|
add_executable(encode_oneshot encode_oneshot.cc)
|
||||||
target_link_libraries(encode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)
|
target_link_libraries(encode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)
|
||||||
|
|
||||||
add_executable(jxlinfo jxlinfo.c)
|
|
||||||
target_link_libraries(jxlinfo PkgConfig::Jxl)
|
|
||||||
|
|
||||||
|
|
||||||
# Building a static binary with the static libjxl dependencies. How to load
|
# Building a static binary with the static libjxl dependencies. How to load
|
||||||
# static library configs from pkg-config and how to build static binaries
|
# static library configs from pkg-config and how to build static binaries
|
||||||
|
10
third_party/jpeg-xl/examples/examples.cmake
vendored
10
third_party/jpeg-xl/examples/examples.cmake
vendored
@ -9,13 +9,3 @@ add_executable(decode_progressive ${CMAKE_CURRENT_LIST_DIR}/decode_progressive.c
|
|||||||
target_link_libraries(decode_progressive jxl_dec jxl_threads)
|
target_link_libraries(decode_progressive jxl_dec jxl_threads)
|
||||||
add_executable(encode_oneshot ${CMAKE_CURRENT_LIST_DIR}/encode_oneshot.cc)
|
add_executable(encode_oneshot ${CMAKE_CURRENT_LIST_DIR}/encode_oneshot.cc)
|
||||||
target_link_libraries(encode_oneshot jxl jxl_threads)
|
target_link_libraries(encode_oneshot jxl jxl_threads)
|
||||||
|
|
||||||
add_executable(jxlinfo ${CMAKE_CURRENT_LIST_DIR}/jxlinfo.c)
|
|
||||||
target_link_libraries(jxlinfo jxl)
|
|
||||||
|
|
||||||
if(NOT ${SANITIZER} STREQUAL "none")
|
|
||||||
# Linking a C test binary with the C++ JPEG XL implementation when using
|
|
||||||
# address sanitizer is not well supported by clang 9, so force using clang++
|
|
||||||
# for linking this test if a sanitizer is used.
|
|
||||||
set_target_properties(jxlinfo PROPERTIES LINKER_LANGUAGE CXX)
|
|
||||||
endif() # SANITIZER != "none"
|
|
||||||
|
332
third_party/jpeg-xl/examples/jxlinfo.c
vendored
332
third_party/jpeg-xl/examples/jxlinfo.c
vendored
@ -1,332 +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.
|
|
||||||
|
|
||||||
// This example prints information from the main codestream header.
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "jxl/decode.h"
|
|
||||||
|
|
||||||
int PrintBasicInfo(FILE* file) {
|
|
||||||
uint8_t* data = NULL;
|
|
||||||
size_t data_size = 0;
|
|
||||||
// In how large chunks to read from the file and try decoding the basic info.
|
|
||||||
const size_t chunk_size = 64;
|
|
||||||
|
|
||||||
JxlDecoder* dec = JxlDecoderCreate(NULL);
|
|
||||||
if (!dec) {
|
|
||||||
fprintf(stderr, "JxlDecoderCreate failed\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
JxlDecoderSetKeepOrientation(dec, 1);
|
|
||||||
|
|
||||||
if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(
|
|
||||||
dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING |
|
|
||||||
JXL_DEC_FRAME | JXL_DEC_BOX)) {
|
|
||||||
fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
|
|
||||||
JxlDecoderDestroy(dec);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
JxlBasicInfo info;
|
|
||||||
int seen_basic_info = 0;
|
|
||||||
JxlFrameHeader frame_header;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
// The first time, this will output JXL_DEC_NEED_MORE_INPUT because no
|
|
||||||
// input is set yet, this is ok since the input is set when handling this
|
|
||||||
// event.
|
|
||||||
JxlDecoderStatus status = JxlDecoderProcessInput(dec);
|
|
||||||
|
|
||||||
if (status == JXL_DEC_ERROR) {
|
|
||||||
fprintf(stderr, "Decoder error\n");
|
|
||||||
break;
|
|
||||||
} else if (status == JXL_DEC_NEED_MORE_INPUT) {
|
|
||||||
// The first time there is nothing to release and it returns 0, but that
|
|
||||||
// is ok.
|
|
||||||
size_t remaining = JxlDecoderReleaseInput(dec);
|
|
||||||
// move any remaining bytes to the front if necessary
|
|
||||||
if (remaining != 0) {
|
|
||||||
memmove(data, data + data_size - remaining, remaining);
|
|
||||||
}
|
|
||||||
// resize the buffer to append one more chunk of data
|
|
||||||
// TODO(lode): avoid unnecessary reallocations
|
|
||||||
data = (uint8_t*)realloc(data, remaining + chunk_size);
|
|
||||||
// append bytes read from the file behind the remaining bytes
|
|
||||||
size_t read_size = fread(data + remaining, 1, chunk_size, file);
|
|
||||||
if (read_size == 0 && feof(file)) {
|
|
||||||
fprintf(stderr, "Unexpected EOF\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
data_size = remaining + read_size;
|
|
||||||
JxlDecoderSetInput(dec, data, data_size);
|
|
||||||
if (feof(file)) JxlDecoderCloseInput(dec);
|
|
||||||
} else if (status == JXL_DEC_SUCCESS) {
|
|
||||||
// Finished all processing.
|
|
||||||
break;
|
|
||||||
} else if (status == JXL_DEC_BASIC_INFO) {
|
|
||||||
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec, &info)) {
|
|
||||||
fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
seen_basic_info = 1;
|
|
||||||
|
|
||||||
printf("dimensions: %ux%u\n", info.xsize, info.ysize);
|
|
||||||
printf("have_container: %d\n", info.have_container);
|
|
||||||
printf("uses_original_profile: %d\n", info.uses_original_profile);
|
|
||||||
printf("bits_per_sample: %d\n", info.bits_per_sample);
|
|
||||||
if (info.exponent_bits_per_sample)
|
|
||||||
printf("float, with exponent_bits_per_sample: %d\n",
|
|
||||||
info.exponent_bits_per_sample);
|
|
||||||
if (info.intensity_target != 255.f || info.min_nits != 0.f ||
|
|
||||||
info.relative_to_max_display != 0 ||
|
|
||||||
info.relative_to_max_display != 0.f) {
|
|
||||||
printf("intensity_target: %f\n", info.intensity_target);
|
|
||||||
printf("min_nits: %f\n", info.min_nits);
|
|
||||||
printf("relative_to_max_display: %d\n", info.relative_to_max_display);
|
|
||||||
printf("linear_below: %f\n", info.linear_below);
|
|
||||||
}
|
|
||||||
printf("have_preview: %d\n", info.have_preview);
|
|
||||||
if (info.have_preview) {
|
|
||||||
printf("preview xsize: %u\n", info.preview.xsize);
|
|
||||||
printf("preview ysize: %u\n", info.preview.ysize);
|
|
||||||
}
|
|
||||||
printf("have_animation: %d\n", info.have_animation);
|
|
||||||
if (info.have_animation) {
|
|
||||||
printf("ticks per second (numerator / denominator): %u / %u\n",
|
|
||||||
info.animation.tps_numerator, info.animation.tps_denominator);
|
|
||||||
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",
|
|
||||||
"Transposed", "90 degrees clockwise",
|
|
||||||
"Anti-Transposed", "90 degrees counter-clockwise"};
|
|
||||||
if (info.orientation > 0 && info.orientation < 9) {
|
|
||||||
printf("orientation: %d (%s)\n", info.orientation,
|
|
||||||
orientation_string[info.orientation - 1]);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Invalid orientation\n");
|
|
||||||
}
|
|
||||||
printf("num_color_channels: %d\n", info.num_color_channels);
|
|
||||||
printf("num_extra_channels: %d\n", info.num_extra_channels);
|
|
||||||
|
|
||||||
const char* const ec_type_names[7] = {"Alpha", "Depth",
|
|
||||||
"Spot color", "Selection mask",
|
|
||||||
"K (of CMYK)", "CFA (Bayer data)",
|
|
||||||
"Thermal"};
|
|
||||||
for (uint32_t i = 0; i < info.num_extra_channels; i++) {
|
|
||||||
JxlExtraChannelInfo extra;
|
|
||||||
if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelInfo(dec, i, &extra)) {
|
|
||||||
fprintf(stderr, "JxlDecoderGetExtraChannelInfo failed\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf("extra channel %u:\n", i);
|
|
||||||
printf(" type: %s\n",
|
|
||||||
(extra.type < 7 ? ec_type_names[extra.type]
|
|
||||||
: (extra.type == JXL_CHANNEL_OPTIONAL
|
|
||||||
? "Unknown but can be ignored"
|
|
||||||
: "Unknown, please update your libjxl")));
|
|
||||||
printf(" bits_per_sample: %u\n", extra.bits_per_sample);
|
|
||||||
if (extra.exponent_bits_per_sample > 0) {
|
|
||||||
printf(" float, with exponent_bits_per_sample: %u\n",
|
|
||||||
extra.exponent_bits_per_sample);
|
|
||||||
}
|
|
||||||
if (extra.dim_shift > 0) {
|
|
||||||
printf(" dim_shift: %u (upsampled %ux)\n", extra.dim_shift,
|
|
||||||
1 << extra.dim_shift);
|
|
||||||
}
|
|
||||||
if (extra.name_length) {
|
|
||||||
char* name = malloc(extra.name_length + 1);
|
|
||||||
if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelName(
|
|
||||||
dec, i, name, extra.name_length + 1)) {
|
|
||||||
fprintf(stderr, "JxlDecoderGetExtraChannelName failed\n");
|
|
||||||
free(name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf(" name: %s\n", name);
|
|
||||||
free(name);
|
|
||||||
}
|
|
||||||
if (extra.type == JXL_CHANNEL_ALPHA)
|
|
||||||
printf(" alpha_premultiplied: %d (%s)\n", extra.alpha_premultiplied,
|
|
||||||
extra.alpha_premultiplied ? "Premultiplied"
|
|
||||||
: "Non-premultiplied");
|
|
||||||
if (extra.type == JXL_CHANNEL_SPOT_COLOR) {
|
|
||||||
printf(" spot_color: (%f, %f, %f) with opacity %f\n",
|
|
||||||
extra.spot_color[0], extra.spot_color[1], extra.spot_color[2],
|
|
||||||
extra.spot_color[3]);
|
|
||||||
}
|
|
||||||
if (extra.type == JXL_CHANNEL_CFA)
|
|
||||||
printf(" cfa_channel: %u\n", extra.cfa_channel);
|
|
||||||
}
|
|
||||||
} else if (status == JXL_DEC_COLOR_ENCODING) {
|
|
||||||
JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
|
|
||||||
printf("color profile:\n");
|
|
||||||
|
|
||||||
JxlColorEncoding color_encoding;
|
|
||||||
if (JXL_DEC_SUCCESS ==
|
|
||||||
JxlDecoderGetColorAsEncodedProfile(dec, &format,
|
|
||||||
JXL_COLOR_PROFILE_TARGET_ORIGINAL,
|
|
||||||
&color_encoding)) {
|
|
||||||
printf(" format: JPEG XL encoded color profile\n");
|
|
||||||
const char* const cs_string[4] = {"RGB color", "Grayscale", "XYB",
|
|
||||||
"Unknown"};
|
|
||||||
const char* const wp_string[12] = {"", "D65", "Custom", "", "", "",
|
|
||||||
"", "", "", "", "E", "P3"};
|
|
||||||
const char* const pr_string[12] = {
|
|
||||||
"", "sRGB", "Custom", "", "", "", "", "", "", "Rec.2100", "", "P3"};
|
|
||||||
const char* const tf_string[19] = {
|
|
||||||
"", "709", "Unknown", "", "", "", "", "", "Linear", "",
|
|
||||||
"", "", "", "sRGB", "", "", "PQ", "DCI", "HLG"};
|
|
||||||
const char* const ri_string[4] = {"Perceptual", "Relative",
|
|
||||||
"Saturation", "Absolute"};
|
|
||||||
printf(" color_space: %d (%s)\n", color_encoding.color_space,
|
|
||||||
cs_string[color_encoding.color_space]);
|
|
||||||
printf(" white_point: %d (%s)\n", color_encoding.white_point,
|
|
||||||
wp_string[color_encoding.white_point]);
|
|
||||||
if (color_encoding.white_point == JXL_WHITE_POINT_CUSTOM) {
|
|
||||||
printf(" white_point XY: %f %f\n", color_encoding.white_point_xy[0],
|
|
||||||
color_encoding.white_point_xy[1]);
|
|
||||||
}
|
|
||||||
if (color_encoding.color_space == JXL_COLOR_SPACE_RGB ||
|
|
||||||
color_encoding.color_space == JXL_COLOR_SPACE_UNKNOWN) {
|
|
||||||
printf(" primaries: %d (%s)\n", color_encoding.primaries,
|
|
||||||
pr_string[color_encoding.primaries]);
|
|
||||||
if (color_encoding.primaries == JXL_PRIMARIES_CUSTOM) {
|
|
||||||
printf(" red primaries XY: %f %f\n",
|
|
||||||
color_encoding.primaries_red_xy[0],
|
|
||||||
color_encoding.primaries_red_xy[1]);
|
|
||||||
printf(" green primaries XY: %f %f\n",
|
|
||||||
color_encoding.primaries_green_xy[0],
|
|
||||||
color_encoding.primaries_green_xy[1]);
|
|
||||||
printf(" blue primaries XY: %f %f\n",
|
|
||||||
color_encoding.primaries_blue_xy[0],
|
|
||||||
color_encoding.primaries_blue_xy[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_GAMMA) {
|
|
||||||
printf(" transfer_function: gamma: %f\n", color_encoding.gamma);
|
|
||||||
} else {
|
|
||||||
printf(" transfer_function: %d (%s)\n",
|
|
||||||
color_encoding.transfer_function,
|
|
||||||
tf_string[color_encoding.transfer_function]);
|
|
||||||
}
|
|
||||||
printf(" rendering_intent: %d (%s)\n", color_encoding.rendering_intent,
|
|
||||||
ri_string[color_encoding.rendering_intent]);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// The profile is not in JPEG XL encoded form, get as ICC profile
|
|
||||||
// instead.
|
|
||||||
printf(" format: ICC profile\n");
|
|
||||||
size_t profile_size;
|
|
||||||
if (JXL_DEC_SUCCESS !=
|
|
||||||
JxlDecoderGetICCProfileSize(dec, &format,
|
|
||||||
JXL_COLOR_PROFILE_TARGET_ORIGINAL,
|
|
||||||
&profile_size)) {
|
|
||||||
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
printf(" ICC profile size: %" PRIu64 "\n", (uint64_t)profile_size);
|
|
||||||
if (profile_size < 132) {
|
|
||||||
fprintf(stderr, "ICC profile too small\n");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
uint8_t* profile = (uint8_t*)malloc(profile_size);
|
|
||||||
if (JXL_DEC_SUCCESS !=
|
|
||||||
JxlDecoderGetColorAsICCProfile(dec, &format,
|
|
||||||
JXL_COLOR_PROFILE_TARGET_ORIGINAL,
|
|
||||||
profile, profile_size)) {
|
|
||||||
fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
|
|
||||||
free(profile);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
printf(" CMM type: \"%.4s\"\n", profile + 4);
|
|
||||||
printf(" color space: \"%.4s\"\n", profile + 16);
|
|
||||||
printf(" rendering intent: %d\n", (int)profile[67]);
|
|
||||||
free(profile);
|
|
||||||
}
|
|
||||||
} else if (status == JXL_DEC_FRAME) {
|
|
||||||
if (JXL_DEC_SUCCESS != JxlDecoderGetFrameHeader(dec, &frame_header)) {
|
|
||||||
fprintf(stderr, "JxlDecoderGetFrameHeader failed\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf("frame:\n");
|
|
||||||
if (frame_header.name_length) {
|
|
||||||
char* name = malloc(frame_header.name_length + 1);
|
|
||||||
if (JXL_DEC_SUCCESS !=
|
|
||||||
JxlDecoderGetFrameName(dec, name, frame_header.name_length + 1)) {
|
|
||||||
fprintf(stderr, "JxlDecoderGetFrameName failed\n");
|
|
||||||
free(name);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
printf(" name: %s\n", name);
|
|
||||||
free(name);
|
|
||||||
}
|
|
||||||
float ms = frame_header.duration * 1000.f *
|
|
||||||
info.animation.tps_denominator / info.animation.tps_numerator;
|
|
||||||
if (info.have_animation) {
|
|
||||||
printf(" duration: %u ticks (%f ms)\n", frame_header.duration, ms);
|
|
||||||
if (info.animation.have_timecodes) {
|
|
||||||
printf(" time code: %X\n", frame_header.timecode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!frame_header.name_length && !info.have_animation) {
|
|
||||||
printf(" still frame, unnamed\n");
|
|
||||||
}
|
|
||||||
} else if (status == JXL_DEC_BOX) {
|
|
||||||
JxlBoxType type;
|
|
||||||
uint64_t size;
|
|
||||||
JxlDecoderGetBoxType(dec, type, JXL_FALSE);
|
|
||||||
JxlDecoderGetBoxSizeRaw(dec, &size);
|
|
||||||
printf("box: type: \"%c%c%c%c\" size: %" PRIu64 "\n", type[0], type[1],
|
|
||||||
type[2], type[3], (uint64_t)size);
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Unexpected decoder status\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
JxlDecoderDestroy(dec);
|
|
||||||
free(data);
|
|
||||||
|
|
||||||
return seen_basic_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
|
||||||
if (argc != 2) {
|
|
||||||
fprintf(stderr,
|
|
||||||
"Usage: %s <jxl>\n"
|
|
||||||
"Where:\n"
|
|
||||||
" jxl = input JPEG XL image filename\n",
|
|
||||||
argv[0]);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* jxl_filename = argv[1];
|
|
||||||
|
|
||||||
FILE* file = fopen(jxl_filename, "rb");
|
|
||||||
if (!file) {
|
|
||||||
fprintf(stderr, "Failed to read file %s\n", jxl_filename);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PrintBasicInfo(file)) {
|
|
||||||
fclose(file);
|
|
||||||
fprintf(stderr, "Couldn't print basic info\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(file);
|
|
||||||
return 0;
|
|
||||||
}
|
|
2
third_party/jpeg-xl/lib/CMakeLists.txt
vendored
2
third_party/jpeg-xl/lib/CMakeLists.txt
vendored
@ -126,7 +126,7 @@ endif() # WIN32
|
|||||||
# Internal flags for coverage builds:
|
# Internal flags for coverage builds:
|
||||||
if(JPEGXL_ENABLE_COVERAGE)
|
if(JPEGXL_ENABLE_COVERAGE)
|
||||||
set(JPEGXL_COVERAGE_FLAGS
|
set(JPEGXL_COVERAGE_FLAGS
|
||||||
-g -O0 -fprofile-arcs -ftest-coverage -DJXL_DISABLE_SLOW_TESTS
|
-g -O0 -fprofile-arcs -ftest-coverage
|
||||||
-DJXL_ENABLE_ASSERT=0 -DJXL_ENABLE_CHECK=0
|
-DJXL_ENABLE_ASSERT=0 -DJXL_ENABLE_CHECK=0
|
||||||
)
|
)
|
||||||
endif() # JPEGXL_ENABLE_COVERAGE
|
endif() # JPEGXL_ENABLE_COVERAGE
|
||||||
|
17
third_party/jpeg-xl/lib/extras/codec.cc
vendored
17
third_party/jpeg-xl/lib/extras/codec.cc
vendored
@ -21,7 +21,6 @@
|
|||||||
#include "lib/extras/enc/exr.h"
|
#include "lib/extras/enc/exr.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "lib/extras/codec_psd.h"
|
|
||||||
#include "lib/extras/dec/decode.h"
|
#include "lib/extras/dec/decode.h"
|
||||||
#include "lib/extras/enc/pgx.h"
|
#include "lib/extras/enc/pgx.h"
|
||||||
#include "lib/extras/enc/pnm.h"
|
#include "lib/extras/enc/pnm.h"
|
||||||
@ -43,13 +42,9 @@ Status SetFromBytes(const Span<const uint8_t> bytes,
|
|||||||
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
|
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
|
||||||
|
|
||||||
extras::PackedPixelFile ppf;
|
extras::PackedPixelFile ppf;
|
||||||
if (extras::DecodeBytes(bytes, color_hints, io->constraints, &ppf, pool,
|
if (extras::DecodeBytes(bytes, color_hints, io->constraints, &ppf,
|
||||||
orig_codec)) {
|
orig_codec)) {
|
||||||
return ConvertPackedPixelFileToCodecInOut(ppf, pool, io);
|
return ConvertPackedPixelFileToCodecInOut(ppf, pool, io);
|
||||||
} else if (extras::DecodeImagePSD(bytes, color_hints, pool, io)) {
|
|
||||||
// TODO(deymo): Migrate PSD codec too.
|
|
||||||
if (orig_codec) *orig_codec = extras::Codec::kPSD;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return JXL_FAILURE("Codecs failed to decode");
|
return JXL_FAILURE("Codecs failed to decode");
|
||||||
}
|
}
|
||||||
@ -124,9 +119,6 @@ Status Encode(const CodecInOut& io, const extras::Codec codec,
|
|||||||
bytes);
|
bytes);
|
||||||
case extras::Codec::kGIF:
|
case extras::Codec::kGIF:
|
||||||
return JXL_FAILURE("Encoding to GIF is not implemented");
|
return JXL_FAILURE("Encoding to GIF is not implemented");
|
||||||
case extras::Codec::kPSD:
|
|
||||||
return extras::EncodeImagePSD(&io, c_desired, bits_per_sample, pool,
|
|
||||||
bytes);
|
|
||||||
case extras::Codec::kEXR:
|
case extras::Codec::kEXR:
|
||||||
#if JPEGXL_ENABLE_EXR
|
#if JPEGXL_ENABLE_EXR
|
||||||
return extras::EncodeImageEXR(&io, c_desired, pool, bytes);
|
return extras::EncodeImageEXR(&io, c_desired, pool, bytes);
|
||||||
@ -147,10 +139,13 @@ Status EncodeToFile(const CodecInOut& io, const ColorEncoding& c_desired,
|
|||||||
const extras::Codec codec =
|
const extras::Codec codec =
|
||||||
extras::CodecFromExtension(extension, &bits_per_sample);
|
extras::CodecFromExtension(extension, &bits_per_sample);
|
||||||
|
|
||||||
// Warn about incorrect usage of PBM/PGM/PGX/PPM - only the latter supports
|
// Warn about incorrect usage of PGM/PGX/PPM - only the latter supports
|
||||||
// color, but CodecFromExtension lumps them all together.
|
// color, but CodecFromExtension lumps them all together.
|
||||||
if (codec == extras::Codec::kPNM && extension != ".pfm") {
|
if (codec == extras::Codec::kPNM && extension != ".pfm") {
|
||||||
if (!io.Main().IsGray() && extension != ".ppm") {
|
if (io.Main().HasAlpha() && extension != ".pam") {
|
||||||
|
JXL_WARNING(
|
||||||
|
"For images with alpha, the filename should end with .pam.\n");
|
||||||
|
} else if (!io.Main().IsGray() && extension == ".pgm") {
|
||||||
JXL_WARNING("For color images, the filename should end with .ppm.\n");
|
JXL_WARNING("For color images, the filename should end with .ppm.\n");
|
||||||
} else if (io.Main().IsGray() && extension == ".ppm") {
|
} else if (io.Main().IsGray() && extension == ".ppm") {
|
||||||
JXL_WARNING(
|
JXL_WARNING(
|
||||||
|
621
third_party/jpeg-xl/lib/extras/codec_psd.cc
vendored
621
third_party/jpeg-xl/lib/extras/codec_psd.cc
vendored
@ -1,621 +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_psd.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <numeric>
|
|
||||||
#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/common.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 {
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
uint64_t get_be_int(int bytes, const uint8_t*& pos, const uint8_t* maxpos) {
|
|
||||||
uint64_t r = 0;
|
|
||||||
if (pos + bytes <= maxpos) {
|
|
||||||
if (bytes == 1) {
|
|
||||||
r = *pos;
|
|
||||||
} else if (bytes == 2) {
|
|
||||||
r = LoadBE16(pos);
|
|
||||||
} else if (bytes == 4) {
|
|
||||||
r = LoadBE32(pos);
|
|
||||||
} else if (bytes == 8) {
|
|
||||||
r = LoadBE64(pos);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos += bytes;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copies up to n bytes, without reading from maxpos (the STL-style end).
|
|
||||||
void safe_copy(const uint8_t* JXL_RESTRICT pos,
|
|
||||||
const uint8_t* JXL_RESTRICT maxpos, char* JXL_RESTRICT out,
|
|
||||||
size_t n) {
|
|
||||||
for (size_t i = 0; i < n; ++i) {
|
|
||||||
if (pos + i >= maxpos) return;
|
|
||||||
out[i] = pos[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// maxpos is the STL-style end! The valid range is up to [pos, maxpos).
|
|
||||||
int safe_strncmp(const uint8_t* pos, const uint8_t* maxpos, const char* s2,
|
|
||||||
size_t n) {
|
|
||||||
if (pos + n > maxpos) return 1;
|
|
||||||
return strncmp((const char*)pos, s2, n);
|
|
||||||
}
|
|
||||||
constexpr int PSD_VERBOSITY = 1;
|
|
||||||
|
|
||||||
Status decode_layer(const uint8_t*& pos, const uint8_t* maxpos,
|
|
||||||
ImageBundle& layer, std::vector<int> chans,
|
|
||||||
std::vector<bool> invert, int w, int h, int version,
|
|
||||||
int colormodel, bool is_layer, int depth) {
|
|
||||||
int compression_method = 2;
|
|
||||||
int nb_channels = chans.size();
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY,
|
|
||||||
"Trying to decode layer with dimensions %ix%i and %i channels", w,
|
|
||||||
h, nb_channels);
|
|
||||||
if (w <= 0 || h <= 0) return JXL_FAILURE("PSD: empty layer");
|
|
||||||
for (int c = 0; c < nb_channels; c++) {
|
|
||||||
// skip nop byte padding
|
|
||||||
while (pos < maxpos && *pos == 128) pos++;
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Channel %i (pos %" PRIuS ")", c, (size_t)pos);
|
|
||||||
// Merged image stores all channels together (same compression method)
|
|
||||||
// Layers store channel per channel
|
|
||||||
if (is_layer || c == 0) {
|
|
||||||
compression_method = get_be_int(2, pos, maxpos);
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "compression method: %i", compression_method);
|
|
||||||
if (compression_method > 1 || compression_method < 0) {
|
|
||||||
return JXL_FAILURE("PSD: can't handle compression method %i",
|
|
||||||
compression_method);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_layer && c < colormodel) {
|
|
||||||
// skip to the extra channels
|
|
||||||
if (compression_method == 0) {
|
|
||||||
pos += w * h * (depth >> 3) * colormodel;
|
|
||||||
c = colormodel - 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
size_t skip_amount = 0;
|
|
||||||
for (int i = 0; i < nb_channels; i++) {
|
|
||||||
if (i < colormodel) {
|
|
||||||
for (int y = 0; y < h; y++) {
|
|
||||||
skip_amount += get_be_int(2 * version, pos, maxpos);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
pos += h * 2 * version;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos += skip_amount;
|
|
||||||
c = colormodel - 1;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (is_layer || c == 0) {
|
|
||||||
// skip the line-counts, we don't need them
|
|
||||||
if (compression_method == 1) {
|
|
||||||
pos += h * (is_layer ? 1 : nb_channels) * 2 *
|
|
||||||
version; // PSB uses 4 bytes per rowsize instead of 2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int c_id = chans[c];
|
|
||||||
if (c_id < 0) continue; // skip
|
|
||||||
if (static_cast<unsigned int>(c_id) >= 3 + layer.extra_channels().size())
|
|
||||||
return JXL_FAILURE("PSD: can't handle channel id %i", c_id);
|
|
||||||
ImageF& ch = (c_id < 3 ? layer.color()->Plane(c_id)
|
|
||||||
: layer.extra_channels()[c_id - 3]);
|
|
||||||
|
|
||||||
for (int y = 0; y < h; y++) {
|
|
||||||
if (pos > maxpos) return JXL_FAILURE("PSD: premature end of input");
|
|
||||||
float* const JXL_RESTRICT row = ch.Row(y);
|
|
||||||
if (compression_method == 0) {
|
|
||||||
// uncompressed is easy
|
|
||||||
if (depth == 8) {
|
|
||||||
for (int x = 0; x < w; x++) {
|
|
||||||
row[x] = get_be_int(1, pos, maxpos) * (1.f / 255.f);
|
|
||||||
}
|
|
||||||
} else if (depth == 16) {
|
|
||||||
for (int x = 0; x < w; x++) {
|
|
||||||
row[x] = get_be_int(2, pos, maxpos) * (1.f / 65535.f);
|
|
||||||
}
|
|
||||||
} else if (depth == 32) {
|
|
||||||
for (int x = 0; x < w; x++) {
|
|
||||||
uint32_t f = get_be_int(4, pos, maxpos);
|
|
||||||
memcpy(&row[x], &f, 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// RLE is not that hard
|
|
||||||
if (depth != 8)
|
|
||||||
return JXL_FAILURE("PSD: did not expect RLE with depth>1");
|
|
||||||
for (int x = 0; x < w;) {
|
|
||||||
if (pos >= maxpos) return JXL_FAILURE("PSD: out of bounds");
|
|
||||||
int8_t rle = *pos++;
|
|
||||||
if (rle <= 0) {
|
|
||||||
if (rle == -128) continue; // nop
|
|
||||||
int count = 1 - rle;
|
|
||||||
float v = get_be_int(1, pos, maxpos) * (1.f / 255.f);
|
|
||||||
while (count && x < w) {
|
|
||||||
row[x] = v;
|
|
||||||
count--;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
if (count) return JXL_FAILURE("PSD: row overflow");
|
|
||||||
} else {
|
|
||||||
int count = 1 + rle;
|
|
||||||
while (count && x < w) {
|
|
||||||
row[x] = get_be_int(1, pos, maxpos) * (1.f / 255.f);
|
|
||||||
count--;
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
if (count) return JXL_FAILURE("PSD: row overflow");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (invert[c]) {
|
|
||||||
// sometimes 0 means full ink
|
|
||||||
for (int x = 0; x < w; x++) {
|
|
||||||
row[x] = 1.f - row[x];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Channel %i read.", c);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
Status DecodeImagePSD(const Span<const uint8_t> bytes,
|
|
||||||
const ColorHints& /*color_hints*/, ThreadPool* pool,
|
|
||||||
CodecInOut* io) {
|
|
||||||
const uint8_t* pos = bytes.data();
|
|
||||||
const uint8_t* maxpos = bytes.data() + bytes.size();
|
|
||||||
if (safe_strncmp(pos, maxpos, "8BPS", 4)) return false; // not a PSD file
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "trying psd decode");
|
|
||||||
pos += 4;
|
|
||||||
int version = get_be_int(2, pos, maxpos);
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Version=%i", version);
|
|
||||||
if (version < 1 || version > 2)
|
|
||||||
return JXL_FAILURE("PSD: unknown format version");
|
|
||||||
// PSD = version 1, PSB = version 2
|
|
||||||
pos += 6;
|
|
||||||
int nb_channels = get_be_int(2, pos, maxpos);
|
|
||||||
size_t ysize = get_be_int(4, pos, maxpos);
|
|
||||||
size_t xsize = get_be_int(4, pos, maxpos);
|
|
||||||
const SizeConstraints* constraints = &io->constraints;
|
|
||||||
JXL_RETURN_IF_ERROR(VerifyDimensions(constraints, xsize, ysize));
|
|
||||||
uint64_t total_pixel_count = static_cast<uint64_t>(xsize) * ysize;
|
|
||||||
int bitdepth = get_be_int(2, pos, maxpos);
|
|
||||||
if (bitdepth != 8 && bitdepth != 16 && bitdepth != 32) {
|
|
||||||
return JXL_FAILURE("PSD: bit depth %i invalid or not supported", bitdepth);
|
|
||||||
}
|
|
||||||
if (bitdepth == 32) {
|
|
||||||
io->metadata.m.SetFloat32Samples();
|
|
||||||
} else {
|
|
||||||
io->metadata.m.SetUintSamples(bitdepth);
|
|
||||||
}
|
|
||||||
int colormodel = get_be_int(2, pos, maxpos);
|
|
||||||
// 1 = Grayscale, 3 = RGB, 4 = CMYK
|
|
||||||
if (colormodel != 1 && colormodel != 3 && colormodel != 4)
|
|
||||||
return JXL_FAILURE("PSD: unsupported color model");
|
|
||||||
|
|
||||||
int real_nb_channels = colormodel;
|
|
||||||
std::vector<std::vector<float>> spotcolor;
|
|
||||||
|
|
||||||
if (get_be_int(4, pos, maxpos))
|
|
||||||
return JXL_FAILURE("PSD: Unsupported color mode section");
|
|
||||||
|
|
||||||
bool hasmergeddata = true;
|
|
||||||
bool have_alpha = false;
|
|
||||||
bool merged_has_alpha = false;
|
|
||||||
bool color_already_set = false;
|
|
||||||
size_t metalength = get_be_int(4, pos, maxpos);
|
|
||||||
const uint8_t* metaoffset = pos;
|
|
||||||
while (pos < metaoffset + metalength) {
|
|
||||||
char header[5] = "????";
|
|
||||||
safe_copy(pos, maxpos, header, 4);
|
|
||||||
if (memcmp(header, "8BIM", 4) != 0) {
|
|
||||||
return JXL_FAILURE("PSD: Unexpected image resource header: %s", header);
|
|
||||||
}
|
|
||||||
pos += 4;
|
|
||||||
int id = get_be_int(2, pos, maxpos);
|
|
||||||
int namelength = get_be_int(1, pos, maxpos);
|
|
||||||
pos += namelength;
|
|
||||||
if (!(namelength & 1)) pos++; // padding to even length
|
|
||||||
size_t blocklength = get_be_int(4, pos, maxpos);
|
|
||||||
// JXL_DEBUG_V(PSD_VERBOSITY, "block id: %i | block length: %" PRIuS,id,
|
|
||||||
// blocklength);
|
|
||||||
if (pos > maxpos) return JXL_FAILURE("PSD: Unexpected end of file");
|
|
||||||
if (id == 1039) { // ICC profile
|
|
||||||
size_t delta = maxpos - pos;
|
|
||||||
if (delta < blocklength) {
|
|
||||||
return JXL_FAILURE("PSD: Invalid block length");
|
|
||||||
}
|
|
||||||
PaddedBytes icc;
|
|
||||||
icc.resize(blocklength);
|
|
||||||
memcpy(icc.data(), pos, blocklength);
|
|
||||||
if (!io->metadata.m.color_encoding.SetICC(std::move(icc))) {
|
|
||||||
return JXL_FAILURE("PSD: Invalid color profile");
|
|
||||||
}
|
|
||||||
color_already_set = true;
|
|
||||||
} else if (id == 1057) { // compatibility mode or not?
|
|
||||||
if (get_be_int(4, pos, maxpos) != 1) {
|
|
||||||
return JXL_FAILURE("PSD: expected version=1 in id=1057 resource block");
|
|
||||||
}
|
|
||||||
hasmergeddata = get_be_int(1, pos, maxpos);
|
|
||||||
pos++;
|
|
||||||
blocklength -= 6; // already skipped these bytes
|
|
||||||
} else if (id == 1077) { // spot colors
|
|
||||||
int version = get_be_int(4, pos, maxpos);
|
|
||||||
if (version != 1) {
|
|
||||||
return JXL_FAILURE(
|
|
||||||
"PSD: expected DisplayInfo version 1, got version %i", version);
|
|
||||||
}
|
|
||||||
int spotcolorcount = nb_channels - colormodel;
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Reading %i spot colors. %" PRIuS,
|
|
||||||
spotcolorcount, blocklength);
|
|
||||||
for (int k = 0; k < spotcolorcount; k++) {
|
|
||||||
int colorspace = get_be_int(2, pos, maxpos);
|
|
||||||
if ((colormodel == 3 && colorspace != 0) ||
|
|
||||||
(colormodel == 4 && colorspace != 2)) {
|
|
||||||
return JXL_FAILURE(
|
|
||||||
"PSD: cannot handle spot colors in different color spaces than "
|
|
||||||
"image itself");
|
|
||||||
}
|
|
||||||
if (colorspace == 2) JXL_WARNING("PSD: K ignored in CMYK spot color");
|
|
||||||
std::vector<float> color;
|
|
||||||
color.push_back(get_be_int(2, pos, maxpos) / 65535.f); // R or C
|
|
||||||
color.push_back(get_be_int(2, pos, maxpos) / 65535.f); // G or M
|
|
||||||
color.push_back(get_be_int(2, pos, maxpos) / 65535.f); // B or Y
|
|
||||||
color.push_back(get_be_int(2, pos, maxpos) / 65535.f); // ignored or K
|
|
||||||
color.push_back(get_be_int(2, pos, maxpos) /
|
|
||||||
100.f); // solidity (alpha, basically)
|
|
||||||
int kind = get_be_int(1, pos, maxpos);
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Kind=%i", kind);
|
|
||||||
color.push_back(kind);
|
|
||||||
spotcolor.push_back(color);
|
|
||||||
if (kind == 2) {
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Actual spot color");
|
|
||||||
} else if (kind == 1) {
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Mask (alpha) channel");
|
|
||||||
} else if (kind == 0) {
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Selection (alpha) channel");
|
|
||||||
} else {
|
|
||||||
return JXL_FAILURE("PSD: Unknown extra channel type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (blocklength & 1) pos++;
|
|
||||||
blocklength = 0;
|
|
||||||
}
|
|
||||||
pos += blocklength;
|
|
||||||
if (blocklength & 1) pos++; // padding again
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(deymo): Apply color hints when PSD is converted to PackedPixelFile.
|
|
||||||
(void)color_already_set;
|
|
||||||
// JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, color_already_set,
|
|
||||||
// /*is_gray=*/false, io));
|
|
||||||
|
|
||||||
size_t layerlength = get_be_int(4 * version, pos, maxpos);
|
|
||||||
const uint8_t* after_layers_pos = pos + layerlength;
|
|
||||||
if (after_layers_pos < pos) return JXL_FAILURE("PSD: invalid layer length");
|
|
||||||
if (layerlength) {
|
|
||||||
pos += 4 * version; // don't care about layerinfolength
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Layer section length: %" PRIuS, layerlength);
|
|
||||||
int layercount = static_cast<int16_t>(get_be_int(2, pos, maxpos));
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Layer count: %i", layercount);
|
|
||||||
io->frames.clear();
|
|
||||||
|
|
||||||
if (layercount == 0) {
|
|
||||||
if (get_be_int(2, pos, maxpos) != 0) {
|
|
||||||
return JXL_FAILURE(
|
|
||||||
"PSD: Expected zero padding before additional layer info");
|
|
||||||
}
|
|
||||||
while (pos < after_layers_pos) {
|
|
||||||
if (safe_strncmp(pos, maxpos, "8BIM", 4) &&
|
|
||||||
safe_strncmp(pos, maxpos, "8B64", 4))
|
|
||||||
return JXL_FAILURE("PSD: Unexpected layer info signature");
|
|
||||||
pos += 4;
|
|
||||||
const uint8_t* tpos = pos;
|
|
||||||
pos += 4;
|
|
||||||
size_t blocklength = get_be_int(4 * version, pos, maxpos);
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Length=%" PRIuS, blocklength);
|
|
||||||
if (blocklength > 0) {
|
|
||||||
if (pos >= maxpos) return JXL_FAILURE("PSD: Unexpected end of file");
|
|
||||||
size_t delta = maxpos - pos;
|
|
||||||
if (delta < blocklength) {
|
|
||||||
return JXL_FAILURE("PSD: Invalid block length");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!safe_strncmp(tpos, maxpos, "Layr", 4) ||
|
|
||||||
!safe_strncmp(tpos, maxpos, "Lr16", 4) ||
|
|
||||||
!safe_strncmp(tpos, maxpos, "Lr32", 4)) {
|
|
||||||
layercount = static_cast<int16_t>(get_be_int(2, pos, maxpos));
|
|
||||||
if (layercount < 0) {
|
|
||||||
return JXL_FAILURE("PSD: Invalid layer count");
|
|
||||||
}
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Real layer count: %i", layercount);
|
|
||||||
if (layercount > 1) have_alpha = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!safe_strncmp(tpos, maxpos, "Mtrn", 4) ||
|
|
||||||
!safe_strncmp(tpos, maxpos, "Mt16", 4) ||
|
|
||||||
!safe_strncmp(tpos, maxpos, "Mt32", 4)) {
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Merged layer has transparency channel");
|
|
||||||
if (nb_channels > real_nb_channels) {
|
|
||||||
have_alpha = true;
|
|
||||||
merged_has_alpha = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pos += blocklength;
|
|
||||||
}
|
|
||||||
} else if (layercount < 0) {
|
|
||||||
// negative layer count indicates merged has alpha and it is to be shown
|
|
||||||
if (nb_channels > real_nb_channels) {
|
|
||||||
have_alpha = true;
|
|
||||||
merged_has_alpha = true;
|
|
||||||
}
|
|
||||||
layercount = -layercount;
|
|
||||||
} else {
|
|
||||||
// multiple layers implies there is alpha
|
|
||||||
have_alpha = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ExtraChannelInfo info;
|
|
||||||
info.bit_depth.bits_per_sample = bitdepth;
|
|
||||||
info.dim_shift = 0;
|
|
||||||
|
|
||||||
if (colormodel == 4) { // cmyk
|
|
||||||
info.type = ExtraChannel::kBlack;
|
|
||||||
io->metadata.m.extra_channel_info.push_back(info);
|
|
||||||
}
|
|
||||||
if (have_alpha) {
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "Have alpha");
|
|
||||||
real_nb_channels++;
|
|
||||||
info.type = ExtraChannel::kAlpha;
|
|
||||||
info.alpha_associated =
|
|
||||||
false; // true? PSD is not consistent with this, need to check
|
|
||||||
io->metadata.m.extra_channel_info.push_back(info);
|
|
||||||
}
|
|
||||||
if (merged_has_alpha && !spotcolor.empty() && spotcolor[0][5] == 1) {
|
|
||||||
// first alpha channel
|
|
||||||
spotcolor.erase(spotcolor.begin());
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < spotcolor.size(); i++) {
|
|
||||||
real_nb_channels++;
|
|
||||||
if (spotcolor[i][5] == 2) {
|
|
||||||
info.type = ExtraChannel::kSpotColor;
|
|
||||||
info.spot_color[0] = spotcolor[i][0];
|
|
||||||
info.spot_color[1] = spotcolor[i][1];
|
|
||||||
info.spot_color[2] = spotcolor[i][2];
|
|
||||||
info.spot_color[3] = spotcolor[i][4];
|
|
||||||
} else if (spotcolor[i][5] == 1) {
|
|
||||||
info.type = ExtraChannel::kAlpha;
|
|
||||||
} else if (spotcolor[i][5] == 0) {
|
|
||||||
info.type = ExtraChannel::kSelectionMask;
|
|
||||||
} else
|
|
||||||
return JXL_FAILURE("PSD: unhandled extra channel");
|
|
||||||
io->metadata.m.extra_channel_info.push_back(info);
|
|
||||||
}
|
|
||||||
std::vector<std::vector<int>> layer_chan_id;
|
|
||||||
std::vector<size_t> layer_offsets(layercount + 1, 0);
|
|
||||||
std::vector<bool> is_real_layer(layercount, false);
|
|
||||||
for (int l = 0; l < layercount; l++) {
|
|
||||||
ImageBundle layer(&io->metadata.m);
|
|
||||||
layer.duration = 0;
|
|
||||||
layer.blend = (l > 0);
|
|
||||||
|
|
||||||
layer.use_for_next_frame = (l + 1 < layercount);
|
|
||||||
layer.origin.y0 = get_be_int(4, pos, maxpos);
|
|
||||||
layer.origin.x0 = get_be_int(4, pos, maxpos);
|
|
||||||
size_t height = get_be_int(4, pos, maxpos) - layer.origin.y0;
|
|
||||||
size_t width = get_be_int(4, pos, maxpos) - layer.origin.x0;
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY,
|
|
||||||
"Layer %i: %" PRIuS " x %" PRIuS " at origin (%i, %i)", l,
|
|
||||||
width, height, layer.origin.x0, layer.origin.y0);
|
|
||||||
int nb_chs = get_be_int(2, pos, maxpos);
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, " channels: %i", nb_chs);
|
|
||||||
std::vector<int> chan_ids;
|
|
||||||
layer_offsets[l + 1] = layer_offsets[l];
|
|
||||||
for (int lc = 0; lc < nb_chs; lc++) {
|
|
||||||
int id = get_be_int(2, pos, maxpos);
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, " id=%i", id);
|
|
||||||
if (id == 65535) {
|
|
||||||
chan_ids.push_back(colormodel); // alpha
|
|
||||||
} else if (id == 65534) {
|
|
||||||
chan_ids.push_back(-1); // layer mask, ignored
|
|
||||||
} else {
|
|
||||||
chan_ids.push_back(id); // color channel
|
|
||||||
}
|
|
||||||
layer_offsets[l + 1] += get_be_int(4 * version, pos, maxpos);
|
|
||||||
}
|
|
||||||
layer_chan_id.push_back(chan_ids);
|
|
||||||
if (safe_strncmp(pos, maxpos, "8BIM", 4))
|
|
||||||
return JXL_FAILURE("PSD: Layer %i: Unexpected signature (not 8BIM)", l);
|
|
||||||
pos += 4;
|
|
||||||
if (safe_strncmp(pos, maxpos, "norm", 4)) {
|
|
||||||
return JXL_FAILURE(
|
|
||||||
"PSD: Layer %i: Cannot handle non-default blend mode", l);
|
|
||||||
}
|
|
||||||
pos += 4;
|
|
||||||
int opacity = get_be_int(1, pos, maxpos);
|
|
||||||
if (opacity < 100) {
|
|
||||||
JXL_WARNING(
|
|
||||||
"PSD: ignoring opacity of semi-transparent layer %i (opacity=%i)",
|
|
||||||
l, opacity);
|
|
||||||
}
|
|
||||||
pos++; // clipping
|
|
||||||
int flags = get_be_int(1, pos, maxpos);
|
|
||||||
pos++;
|
|
||||||
bool invisible = (flags & 2);
|
|
||||||
if (invisible) {
|
|
||||||
if (l + 1 < layercount) {
|
|
||||||
layer.blend = false;
|
|
||||||
layer.use_for_next_frame = false;
|
|
||||||
} else {
|
|
||||||
// TODO: instead add dummy last frame?
|
|
||||||
JXL_WARNING("PSD: invisible top layer was made visible");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
size_t extradata = get_be_int(4, pos, maxpos);
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, " extradata: %" PRIuS " bytes", extradata);
|
|
||||||
const uint8_t* after_extra = pos + extradata;
|
|
||||||
// TODO: deal with non-empty layer masks
|
|
||||||
pos += get_be_int(4, pos, maxpos); // skip layer mask data
|
|
||||||
pos += get_be_int(4, pos, maxpos); // skip layer blend range data
|
|
||||||
size_t namelength = get_be_int(1, pos, maxpos);
|
|
||||||
size_t delta = maxpos - pos;
|
|
||||||
if (delta < namelength) return JXL_FAILURE("PSD: Invalid block length");
|
|
||||||
char lname[256] = {};
|
|
||||||
memcpy(lname, pos, namelength);
|
|
||||||
lname[namelength] = 0;
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, " name: %s", lname);
|
|
||||||
pos = after_extra;
|
|
||||||
if (width == 0 || height == 0) {
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY,
|
|
||||||
" NOT A REAL LAYER"); // probably layer group
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
is_real_layer[l] = true;
|
|
||||||
JXL_RETURN_IF_ERROR(VerifyDimensions(constraints, width, height));
|
|
||||||
uint64_t pixel_count = static_cast<uint64_t>(width) * height;
|
|
||||||
if (!SafeAdd(total_pixel_count, pixel_count, total_pixel_count)) {
|
|
||||||
return JXL_FAILURE("Image too big");
|
|
||||||
}
|
|
||||||
if (total_pixel_count > constraints->dec_max_pixels) {
|
|
||||||
return JXL_FAILURE("Image too big");
|
|
||||||
}
|
|
||||||
Image3F rgb(width, height);
|
|
||||||
layer.SetFromImage(std::move(rgb), io->metadata.m.color_encoding);
|
|
||||||
std::vector<ImageF> ec;
|
|
||||||
for (const auto& ec_meta : layer.metadata()->extra_channel_info) {
|
|
||||||
ImageF extra(width, height);
|
|
||||||
if (ec_meta.type == ExtraChannel::kAlpha) {
|
|
||||||
FillPlane(1.0f, &extra, Rect(extra)); // opaque
|
|
||||||
} else {
|
|
||||||
ZeroFillPlane(&extra, Rect(extra)); // zeroes
|
|
||||||
}
|
|
||||||
ec.push_back(std::move(extra));
|
|
||||||
}
|
|
||||||
if (!ec.empty()) layer.SetExtraChannels(std::move(ec));
|
|
||||||
layer.name = lname;
|
|
||||||
io->dec_pixels += layer.xsize() * layer.ysize();
|
|
||||||
io->frames.push_back(std::move(layer));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<bool> invert(real_nb_channels, false);
|
|
||||||
int il = 0;
|
|
||||||
const uint8_t* bpos = pos;
|
|
||||||
for (int l = 0; l < layercount; l++) {
|
|
||||||
if (!is_real_layer[l]) continue;
|
|
||||||
pos = bpos + layer_offsets[l];
|
|
||||||
if (pos < bpos) return JXL_FAILURE("PSD: invalid layer offset");
|
|
||||||
JXL_DEBUG_V(PSD_VERBOSITY, "At position %i (%" PRIuS ")",
|
|
||||||
(int)(pos - bytes.data()), (size_t)pos);
|
|
||||||
ImageBundle& layer = io->frames[il++];
|
|
||||||
std::vector<int>& chan_id = layer_chan_id[l];
|
|
||||||
if (chan_id.size() > invert.size()) invert.resize(chan_id.size(), false);
|
|
||||||
JXL_RETURN_IF_ERROR(decode_layer(pos, maxpos, layer, chan_id, invert,
|
|
||||||
layer.xsize(), layer.ysize(), version,
|
|
||||||
colormodel, true, bitdepth));
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
return JXL_FAILURE("PSD: no layer data found");
|
|
||||||
|
|
||||||
if (!hasmergeddata && !spotcolor.empty()) {
|
|
||||||
return JXL_FAILURE("PSD: extra channel data declared but not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!spotcolor.empty() || (hasmergeddata && io->frames.empty())) {
|
|
||||||
// PSD only has spot colors / extra alpha/mask data in the merged image
|
|
||||||
// We don't redundantly store the merged image, so we put it in the first
|
|
||||||
// layer (the next layers will kAdd zeroes to it)
|
|
||||||
pos = after_layers_pos;
|
|
||||||
bool have_only_merged = false;
|
|
||||||
if (io->frames.empty()) {
|
|
||||||
// There is only the merged image, no layers
|
|
||||||
ImageBundle nlayer(&io->metadata.m);
|
|
||||||
Image3F rgb(xsize, ysize);
|
|
||||||
nlayer.SetFromImage(std::move(rgb), io->metadata.m.color_encoding);
|
|
||||||
std::vector<ImageF> ec;
|
|
||||||
for (const auto& ec_meta : nlayer.metadata()->extra_channel_info) {
|
|
||||||
ImageF extra(xsize, ysize);
|
|
||||||
if (ec_meta.type == ExtraChannel::kAlpha) {
|
|
||||||
FillPlane(1.0f, &extra, Rect(extra)); // opaque
|
|
||||||
} else {
|
|
||||||
ZeroFillPlane(&extra, Rect(extra)); // zeroes
|
|
||||||
}
|
|
||||||
ec.push_back(std::move(extra));
|
|
||||||
}
|
|
||||||
if (!ec.empty()) nlayer.SetExtraChannels(std::move(ec));
|
|
||||||
io->dec_pixels += nlayer.xsize() * nlayer.ysize();
|
|
||||||
io->frames.push_back(std::move(nlayer));
|
|
||||||
have_only_merged = true;
|
|
||||||
}
|
|
||||||
ImageBundle& layer = io->frames[0];
|
|
||||||
std::vector<int> chan_id(real_nb_channels);
|
|
||||||
std::iota(chan_id.begin(), chan_id.end(), 0);
|
|
||||||
std::vector<bool> invert(real_nb_channels, false);
|
|
||||||
if (static_cast<int>(spotcolor.size()) + colormodel + 1 <
|
|
||||||
real_nb_channels) {
|
|
||||||
return JXL_FAILURE("Inconsistent layer configuration");
|
|
||||||
}
|
|
||||||
if (!merged_has_alpha) {
|
|
||||||
if (colormodel >= real_nb_channels) {
|
|
||||||
return JXL_FAILURE("Inconsistent layer configuration");
|
|
||||||
}
|
|
||||||
chan_id.erase(chan_id.begin() + colormodel);
|
|
||||||
invert.erase(invert.begin() + colormodel);
|
|
||||||
} else {
|
|
||||||
colormodel++;
|
|
||||||
}
|
|
||||||
for (size_t i = colormodel; i < invert.size(); i++) {
|
|
||||||
if (spotcolor[i - colormodel][5] == 2) invert[i] = true;
|
|
||||||
if (spotcolor[i - colormodel][5] == 0) invert[i] = true;
|
|
||||||
}
|
|
||||||
JXL_RETURN_IF_ERROR(decode_layer(
|
|
||||||
pos, maxpos, layer, chan_id, invert, layer.xsize(), layer.ysize(),
|
|
||||||
version, (have_only_merged ? 0 : colormodel), false, bitdepth));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (io->frames.empty()) return JXL_FAILURE("PSD: no layers");
|
|
||||||
|
|
||||||
io->SetSize(xsize, ysize);
|
|
||||||
|
|
||||||
SetIntensityTarget(io);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status EncodeImagePSD(const CodecInOut* io, const ColorEncoding& c_desired,
|
|
||||||
size_t bits_per_sample, ThreadPool* pool,
|
|
||||||
PaddedBytes* bytes) {
|
|
||||||
return JXL_FAILURE("PSD encoding not yet implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace extras
|
|
||||||
} // namespace jxl
|
|
37
third_party/jpeg-xl/lib/extras/codec_psd.h
vendored
37
third_party/jpeg-xl/lib/extras/codec_psd.h
vendored
@ -1,37 +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_PSD_H_
|
|
||||||
#define LIB_EXTRAS_CODEC_PSD_H_
|
|
||||||
|
|
||||||
// Decodes Photoshop PSD/PSB, preserving the layers
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.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"
|
|
||||||
#include "lib/jxl/codec_in_out.h"
|
|
||||||
#include "lib/jxl/color_encoding_internal.h"
|
|
||||||
|
|
||||||
namespace jxl {
|
|
||||||
namespace extras {
|
|
||||||
|
|
||||||
// Decodes `bytes` into `io`.
|
|
||||||
Status DecodeImagePSD(const Span<const uint8_t> bytes,
|
|
||||||
const extras::ColorHints& color_hints, ThreadPool* pool,
|
|
||||||
CodecInOut* io);
|
|
||||||
|
|
||||||
// Not implemented yet
|
|
||||||
Status EncodeImagePSD(const CodecInOut* io, const ColorEncoding& c_desired,
|
|
||||||
size_t bits_per_sample, ThreadPool* pool,
|
|
||||||
PaddedBytes* bytes);
|
|
||||||
|
|
||||||
} // namespace extras
|
|
||||||
} // namespace jxl
|
|
||||||
|
|
||||||
#endif // LIB_EXTRAS_CODEC_PSD_H_
|
|
69
third_party/jpeg-xl/lib/extras/codec_test.cc
vendored
69
third_party/jpeg-xl/lib/extras/codec_test.cc
vendored
@ -29,6 +29,31 @@ namespace jxl {
|
|||||||
namespace extras {
|
namespace extras {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
std::string ExtensionFromCodec(Codec codec, const bool is_gray,
|
||||||
|
const bool has_alpha,
|
||||||
|
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 (has_alpha) return ".pam";
|
||||||
|
if (is_gray) return ".pgm";
|
||||||
|
return (bits_per_sample == 32) ? ".pfm" : ".ppm";
|
||||||
|
case Codec::kGIF:
|
||||||
|
return ".gif";
|
||||||
|
case Codec::kEXR:
|
||||||
|
return ".exr";
|
||||||
|
case Codec::kUnknown:
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
JXL_UNREACHABLE;
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
CodecInOut CreateTestImage(const size_t xsize, const size_t ysize,
|
CodecInOut CreateTestImage(const size_t xsize, const size_t ysize,
|
||||||
const bool is_gray, const bool add_alpha,
|
const bool is_gray, const bool add_alpha,
|
||||||
const size_t bits_per_sample,
|
const size_t bits_per_sample,
|
||||||
@ -76,7 +101,7 @@ void TestRoundTrip(Codec codec, const size_t xsize, const size_t ysize,
|
|||||||
// grayscale, and somehow does not have sufficient precision for this test.
|
// grayscale, and somehow does not have sufficient precision for this test.
|
||||||
if (codec == Codec::kEXR) return;
|
if (codec == Codec::kEXR) return;
|
||||||
printf("Codec %s bps:%" PRIuS " gr:%d al:%d\n",
|
printf("Codec %s bps:%" PRIuS " gr:%d al:%d\n",
|
||||||
ExtensionFromCodec(codec, is_gray, bits_per_sample).c_str(),
|
ExtensionFromCodec(codec, is_gray, add_alpha, bits_per_sample).c_str(),
|
||||||
bits_per_sample, is_gray, add_alpha);
|
bits_per_sample, is_gray, add_alpha);
|
||||||
|
|
||||||
ColorEncoding c_native;
|
ColorEncoding c_native;
|
||||||
@ -231,11 +256,11 @@ CodecInOut DecodeRoundtrip(const std::string& pathname, ThreadPool* pool,
|
|||||||
TEST(CodecTest, TestMetadataSRGB) {
|
TEST(CodecTest, TestMetadataSRGB) {
|
||||||
ThreadPoolInternal pool(12);
|
ThreadPoolInternal pool(12);
|
||||||
|
|
||||||
const char* paths[] = {"raw.pixls/DJI-FC6310-16bit_srgb8_v4_krita.png",
|
const char* paths[] = {"third_party/raw.pixls/DJI-FC6310-16bit_srgb8_v4_krita.png",
|
||||||
"raw.pixls/Google-Pixel2XL-16bit_srgb8_v4_krita.png",
|
"third_party/raw.pixls/Google-Pixel2XL-16bit_srgb8_v4_krita.png",
|
||||||
"raw.pixls/HUAWEI-EVA-L09-16bit_srgb8_dt.png",
|
"third_party/raw.pixls/HUAWEI-EVA-L09-16bit_srgb8_dt.png",
|
||||||
"raw.pixls/Nikon-D300-12bit_srgb8_dt.png",
|
"third_party/raw.pixls/Nikon-D300-12bit_srgb8_dt.png",
|
||||||
"raw.pixls/Sony-DSC-RX1RM2-14bit_srgb8_v4_krita.png"};
|
"third_party/raw.pixls/Sony-DSC-RX1RM2-14bit_srgb8_v4_krita.png"};
|
||||||
for (const char* relative_pathname : paths) {
|
for (const char* relative_pathname : paths) {
|
||||||
const CodecInOut io =
|
const CodecInOut io =
|
||||||
DecodeRoundtrip(relative_pathname, Codec::kPNG, &pool);
|
DecodeRoundtrip(relative_pathname, Codec::kPNG, &pool);
|
||||||
@ -260,9 +285,9 @@ TEST(CodecTest, TestMetadataLinear) {
|
|||||||
ThreadPoolInternal pool(12);
|
ThreadPoolInternal pool(12);
|
||||||
|
|
||||||
const char* paths[3] = {
|
const char* paths[3] = {
|
||||||
"raw.pixls/Google-Pixel2XL-16bit_acescg_g1_v4_krita.png",
|
"third_party/raw.pixls/Google-Pixel2XL-16bit_acescg_g1_v4_krita.png",
|
||||||
"raw.pixls/HUAWEI-EVA-L09-16bit_709_g1_dt.png",
|
"third_party/raw.pixls/HUAWEI-EVA-L09-16bit_709_g1_dt.png",
|
||||||
"raw.pixls/Nikon-D300-12bit_2020_g1_dt.png",
|
"third_party/raw.pixls/Nikon-D300-12bit_2020_g1_dt.png",
|
||||||
};
|
};
|
||||||
const WhitePoint white_points[3] = {WhitePoint::kCustom, WhitePoint::kD65,
|
const WhitePoint white_points[3] = {WhitePoint::kCustom, WhitePoint::kD65,
|
||||||
WhitePoint::kD65};
|
WhitePoint::kD65};
|
||||||
@ -292,8 +317,8 @@ TEST(CodecTest, TestMetadataICC) {
|
|||||||
ThreadPoolInternal pool(12);
|
ThreadPoolInternal pool(12);
|
||||||
|
|
||||||
const char* paths[] = {
|
const char* paths[] = {
|
||||||
"raw.pixls/DJI-FC6310-16bit_709_v4_krita.png",
|
"third_party/raw.pixls/DJI-FC6310-16bit_709_v4_krita.png",
|
||||||
"raw.pixls/Sony-DSC-RX1RM2-14bit_709_v4_krita.png",
|
"third_party/raw.pixls/Sony-DSC-RX1RM2-14bit_709_v4_krita.png",
|
||||||
};
|
};
|
||||||
for (const char* relative_pathname : paths) {
|
for (const char* relative_pathname : paths) {
|
||||||
const CodecInOut io =
|
const CodecInOut io =
|
||||||
@ -315,28 +340,28 @@ TEST(CodecTest, TestMetadataICC) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(CodecTest, TestPNGSuite) {
|
TEST(CodecTest, Testthird_party/pngsuite) {
|
||||||
ThreadPoolInternal pool(12);
|
ThreadPoolInternal pool(12);
|
||||||
|
|
||||||
// Ensure we can load PNG with text, japanese UTF-8, compressed text.
|
// Ensure we can load PNG with text, japanese UTF-8, compressed text.
|
||||||
(void)DecodeRoundtrip("pngsuite/ct1n0g04.png", Codec::kPNG, &pool);
|
(void)DecodeRoundtrip("third_party/pngsuite/ct1n0g04.png", Codec::kPNG, &pool);
|
||||||
(void)DecodeRoundtrip("pngsuite/ctjn0g04.png", Codec::kPNG, &pool);
|
(void)DecodeRoundtrip("third_party/pngsuite/ctjn0g04.png", Codec::kPNG, &pool);
|
||||||
(void)DecodeRoundtrip("pngsuite/ctzn0g04.png", Codec::kPNG, &pool);
|
(void)DecodeRoundtrip("third_party/pngsuite/ctzn0g04.png", Codec::kPNG, &pool);
|
||||||
|
|
||||||
// Extract gAMA
|
// Extract gAMA
|
||||||
const CodecInOut b1 =
|
const CodecInOut b1 =
|
||||||
DecodeRoundtrip("pngsuite/g10n3p04.png", Codec::kPNG, &pool);
|
DecodeRoundtrip("third_party/pngsuite/g10n3p04.png", Codec::kPNG, &pool);
|
||||||
EXPECT_TRUE(b1.metadata.color_encoding.tf.IsLinear());
|
EXPECT_TRUE(b1.metadata.color_encoding.tf.IsLinear());
|
||||||
|
|
||||||
// Extract cHRM
|
// Extract cHRM
|
||||||
const CodecInOut b_p =
|
const CodecInOut b_p =
|
||||||
DecodeRoundtrip("pngsuite/ccwn2c08.png", Codec::kPNG, &pool);
|
DecodeRoundtrip("third_party/pngsuite/ccwn2c08.png", Codec::kPNG, &pool);
|
||||||
EXPECT_EQ(Primaries::kSRGB, b_p.metadata.color_encoding.primaries);
|
EXPECT_EQ(Primaries::kSRGB, b_p.metadata.color_encoding.primaries);
|
||||||
EXPECT_EQ(WhitePoint::kD65, b_p.metadata.color_encoding.white_point);
|
EXPECT_EQ(WhitePoint::kD65, b_p.metadata.color_encoding.white_point);
|
||||||
|
|
||||||
// Extract EXIF from (new-style) dedicated chunk
|
// Extract EXIF from (new-style) dedicated chunk
|
||||||
const CodecInOut b_exif =
|
const CodecInOut b_exif =
|
||||||
DecodeRoundtrip("pngsuite/exif2c08.png", Codec::kPNG, &pool);
|
DecodeRoundtrip("third_party/pngsuite/exif2c08.png", Codec::kPNG, &pool);
|
||||||
EXPECT_EQ(978, b_exif.blobs.exif.size());
|
EXPECT_EQ(978, b_exif.blobs.exif.size());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -359,13 +384,13 @@ void VerifyWideGamutMetadata(const std::string& relative_pathname,
|
|||||||
|
|
||||||
TEST(CodecTest, TestWideGamut) {
|
TEST(CodecTest, TestWideGamut) {
|
||||||
ThreadPoolInternal pool(12);
|
ThreadPoolInternal pool(12);
|
||||||
// VerifyWideGamutMetadata("wide-gamut-tests/P3-sRGB-color-bars.png",
|
// VerifyWideGamutMetadata("third_party/wide-gamut-tests/P3-sRGB-color-bars.png",
|
||||||
// Primaries::kP3, &pool);
|
// Primaries::kP3, &pool);
|
||||||
VerifyWideGamutMetadata("wide-gamut-tests/P3-sRGB-color-ring.png",
|
VerifyWideGamutMetadata("third_party/wide-gamut-tests/P3-sRGB-color-ring.png",
|
||||||
Primaries::kP3, &pool);
|
Primaries::kP3, &pool);
|
||||||
// VerifyWideGamutMetadata("wide-gamut-tests/R2020-sRGB-color-bars.png",
|
// VerifyWideGamutMetadata("third_party/wide-gamut-tests/R2020-sRGB-color-bars.png",
|
||||||
// Primaries::k2100, &pool);
|
// Primaries::k2100, &pool);
|
||||||
// VerifyWideGamutMetadata("wide-gamut-tests/R2020-sRGB-color-ring.png",
|
// VerifyWideGamutMetadata("third_party/wide-gamut-tests/R2020-sRGB-color-ring.png",
|
||||||
// Primaries::k2100, &pool);
|
// Primaries::k2100, &pool);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
164
third_party/jpeg-xl/lib/extras/dec/apng.cc
vendored
164
third_party/jpeg-xl/lib/extras/dec/apng.cc
vendored
@ -43,6 +43,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "jxl/codestream_header.h"
|
||||||
#include "jxl/encode.h"
|
#include "jxl/encode.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/printf_macros.h"
|
#include "lib/jxl/base/printf_macros.h"
|
||||||
@ -56,6 +57,12 @@ namespace extras {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
/* hIST chunk tail is not proccesed properly; skip this chunk completely;
|
||||||
|
see https://github.com/glennrp/libpng/pull/413 */
|
||||||
|
const png_byte kIgnoredPngChunks[] = {
|
||||||
|
104, 73, 83, 84, '\0' /* hIST */
|
||||||
|
};
|
||||||
|
|
||||||
// Returns floating-point value from the PNG encoding (times 10^5).
|
// Returns floating-point value from the PNG encoding (times 10^5).
|
||||||
static double F64FromU32(const uint32_t x) {
|
static double F64FromU32(const uint32_t x) {
|
||||||
return static_cast<int32_t>(x) * 1E-5;
|
return static_cast<int32_t>(x) * 1E-5;
|
||||||
@ -163,6 +170,31 @@ class BlobsReaderPNG {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns false if invalid.
|
||||||
|
static JXL_INLINE Status DecodeDecimal(const char** pos, const char* end,
|
||||||
|
uint32_t* JXL_RESTRICT value) {
|
||||||
|
size_t len = 0;
|
||||||
|
*value = 0;
|
||||||
|
while (*pos < end) {
|
||||||
|
char next = **pos;
|
||||||
|
if (next >= '0' && next <= '9') {
|
||||||
|
*value = (*value * 10) + static_cast<uint32_t>(next - '0');
|
||||||
|
len++;
|
||||||
|
if (len > 8) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Do not consume terminator (non-decimal digit).
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(*pos)++;
|
||||||
|
}
|
||||||
|
if (len == 0 || len > 8) {
|
||||||
|
return JXL_FAILURE("Failed to parse decimal");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Parses a PNG text chunk with key of the form "Raw profile type ####", with
|
// Parses a PNG text chunk with key of the form "Raw profile type ####", with
|
||||||
// #### a type.
|
// #### a type.
|
||||||
// Returns whether it could successfully parse the content.
|
// Returns whether it could successfully parse the content.
|
||||||
@ -196,17 +228,15 @@ class BlobsReaderPNG {
|
|||||||
// We parsed so far a \n, some number of non \n characters and are now
|
// We parsed so far a \n, some number of non \n characters and are now
|
||||||
// pointing at a \n.
|
// pointing at a \n.
|
||||||
if (*(pos++) != '\n') return false;
|
if (*(pos++) != '\n') return false;
|
||||||
unsigned long bytes_to_decode;
|
uint32_t bytes_to_decode = 0;
|
||||||
const int fields = sscanf(pos, "%8lu", &bytes_to_decode);
|
JXL_RETURN_IF_ERROR(DecodeDecimal(&pos, encoded_end, &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.
|
// We need 2*bytes for the hex values plus 1 byte every 36 values,
|
||||||
|
// plus terminal \n for length.
|
||||||
const unsigned long needed_bytes =
|
const unsigned long needed_bytes =
|
||||||
bytes_to_decode * 2 + 1 + DivCeil(bytes_to_decode, 36);
|
bytes_to_decode * 2 + 1 + DivCeil(bytes_to_decode, 36);
|
||||||
if (needed_bytes != static_cast<size_t>(encoded_end - pos)) {
|
if (needed_bytes != static_cast<size_t>(encoded_end - pos)) {
|
||||||
return JXL_FAILURE("Not enough bytes to parse %lu bytes in hex",
|
return JXL_FAILURE("Not enough bytes to parse %d bytes in hex",
|
||||||
bytes_to_decode);
|
bytes_to_decode);
|
||||||
}
|
}
|
||||||
JXL_ASSERT(bytes->empty());
|
JXL_ASSERT(bytes->empty());
|
||||||
@ -251,7 +281,7 @@ constexpr uint32_t kId_cHRM = 0x4D524863;
|
|||||||
constexpr uint32_t kId_eXIf = 0x66495865;
|
constexpr uint32_t kId_eXIf = 0x66495865;
|
||||||
|
|
||||||
struct APNGFrame {
|
struct APNGFrame {
|
||||||
PaddedBytes pixels;
|
std::vector<uint8_t> pixels;
|
||||||
std::vector<uint8_t*> rows;
|
std::vector<uint8_t*> rows;
|
||||||
unsigned int w, h, delay_num, delay_den;
|
unsigned int w, h, delay_num, delay_den;
|
||||||
};
|
};
|
||||||
@ -270,7 +300,7 @@ struct Reader {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const unsigned long cMaxPNGSize = 1000000UL;
|
const unsigned long cMaxPNGSize = 1000000UL;
|
||||||
const size_t kMaxPNGChunkSize = 100000000; // 100 MB
|
const size_t kMaxPNGChunkSize = 1lu << 30; // 1 GB
|
||||||
|
|
||||||
void info_fn(png_structp png_ptr, png_infop info_ptr) {
|
void info_fn(png_structp png_ptr, png_infop info_ptr) {
|
||||||
png_set_expand(png_ptr);
|
png_set_expand(png_ptr);
|
||||||
@ -284,11 +314,12 @@ void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num,
|
|||||||
int pass) {
|
int pass) {
|
||||||
APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr);
|
APNGFrame* frame = (APNGFrame*)png_get_progressive_ptr(png_ptr);
|
||||||
JXL_CHECK(frame);
|
JXL_CHECK(frame);
|
||||||
|
JXL_CHECK(row_num < frame->rows.size());
|
||||||
JXL_CHECK(frame->rows[row_num] < frame->pixels.data() + frame->pixels.size());
|
JXL_CHECK(frame->rows[row_num] < frame->pixels.data() + frame->pixels.size());
|
||||||
png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row);
|
png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline unsigned int read_chunk(Reader* r, PaddedBytes* pChunk) {
|
inline unsigned int read_chunk(Reader* r, std::vector<uint8_t>* pChunk) {
|
||||||
unsigned char len[4];
|
unsigned char len[4];
|
||||||
if (r->Read(&len, 4)) {
|
if (r->Read(&len, 4)) {
|
||||||
const auto size = png_get_uint_32(len);
|
const auto size = png_get_uint_32(len);
|
||||||
@ -307,8 +338,8 @@ inline unsigned int read_chunk(Reader* r, PaddedBytes* pChunk) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
|
int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
|
||||||
bool hasInfo, PaddedBytes& chunkIHDR,
|
bool hasInfo, std::vector<uint8_t>& chunkIHDR,
|
||||||
std::vector<PaddedBytes>& chunksInfo) {
|
std::vector<std::vector<uint8_t>>& chunksInfo) {
|
||||||
unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10};
|
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);
|
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
|
||||||
@ -319,6 +350,9 @@ int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
png_set_keep_unknown_chunks(png_ptr, 1, kIgnoredPngChunks,
|
||||||
|
(int)sizeof(kIgnoredPngChunks) / 5);
|
||||||
|
|
||||||
png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
|
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_set_progressive_read_fn(png_ptr, frame_ptr, info_fn, row_fn, NULL);
|
||||||
|
|
||||||
@ -380,9 +414,9 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
|||||||
unsigned char sig[8];
|
unsigned char sig[8];
|
||||||
png_structp png_ptr = nullptr;
|
png_structp png_ptr = nullptr;
|
||||||
png_infop info_ptr = nullptr;
|
png_infop info_ptr = nullptr;
|
||||||
PaddedBytes chunk;
|
std::vector<uint8_t> chunk;
|
||||||
PaddedBytes chunkIHDR;
|
std::vector<uint8_t> chunkIHDR;
|
||||||
std::vector<PaddedBytes> chunksInfo;
|
std::vector<std::vector<uint8_t>> chunksInfo;
|
||||||
bool isAnimated = false;
|
bool isAnimated = false;
|
||||||
bool hasInfo = false;
|
bool hasInfo = false;
|
||||||
APNGFrame frameRaw = {};
|
APNGFrame frameRaw = {};
|
||||||
@ -594,8 +628,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
|||||||
auto ok = png_get_iCCP(png_ptr, info_ptr, &name, &compression_type,
|
auto ok = png_get_iCCP(png_ptr, info_ptr, &name, &compression_type,
|
||||||
&profile, &proflen);
|
&profile, &proflen);
|
||||||
if (ok && proflen) {
|
if (ok && proflen) {
|
||||||
ppf->icc.resize(proflen);
|
ppf->icc.assign(profile, profile + proflen);
|
||||||
memcpy(ppf->icc.data(), profile, proflen);
|
|
||||||
have_color = true;
|
have_color = true;
|
||||||
} else {
|
} else {
|
||||||
// TODO(eustas): JXL_WARNING?
|
// TODO(eustas): JXL_WARNING?
|
||||||
@ -670,7 +703,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
|||||||
has_nontrivial_background && frame.dispose_op != DISPOSE_OP_PREVIOUS;
|
has_nontrivial_background && frame.dispose_op != DISPOSE_OP_PREVIOUS;
|
||||||
size_t x0 = frame.x0;
|
size_t x0 = frame.x0;
|
||||||
size_t y0 = frame.y0;
|
size_t y0 = frame.y0;
|
||||||
|
size_t xsize = frame.data.xsize;
|
||||||
|
size_t ysize = frame.data.ysize;
|
||||||
if (previous_frame_should_be_cleared) {
|
if (previous_frame_should_be_cleared) {
|
||||||
size_t xs = frame.data.xsize;
|
size_t xs = frame.data.xsize;
|
||||||
size_t ys = frame.data.ysize;
|
size_t ys = frame.data.ysize;
|
||||||
@ -710,6 +744,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
|||||||
|
|
||||||
x0 = px0;
|
x0 = px0;
|
||||||
y0 = py0;
|
y0 = py0;
|
||||||
|
xsize = pxs;
|
||||||
|
ysize = pys;
|
||||||
should_blend = false;
|
should_blend = false;
|
||||||
ppf->frames.emplace_back(std::move(new_data));
|
ppf->frames.emplace_back(std::move(new_data));
|
||||||
} else {
|
} else {
|
||||||
@ -718,12 +754,15 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
|||||||
memset(blank.pixels(), 0, blank.pixels_size);
|
memset(blank.pixels(), 0, blank.pixels_size);
|
||||||
ppf->frames.emplace_back(std::move(blank));
|
ppf->frames.emplace_back(std::move(blank));
|
||||||
auto& pframe = ppf->frames.back();
|
auto& pframe = ppf->frames.back();
|
||||||
pframe.x0 = px0;
|
pframe.frame_info.layer_info.crop_x0 = px0;
|
||||||
pframe.y0 = py0;
|
pframe.frame_info.layer_info.crop_y0 = py0;
|
||||||
|
pframe.frame_info.layer_info.xsize = frame.xsize;
|
||||||
|
pframe.frame_info.layer_info.ysize = frame.ysize;
|
||||||
pframe.frame_info.duration = 0;
|
pframe.frame_info.duration = 0;
|
||||||
pframe.blend = false;
|
pframe.frame_info.layer_info.have_crop = 0;
|
||||||
pframe.use_for_next_frame = true;
|
pframe.frame_info.layer_info.blend_info.blendmode = JXL_BLEND_REPLACE;
|
||||||
|
pframe.frame_info.layer_info.blend_info.source = 0;
|
||||||
|
pframe.frame_info.layer_info.save_as_reference = 1;
|
||||||
ppf->frames.emplace_back(std::move(frame.data));
|
ppf->frames.emplace_back(std::move(frame.data));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -731,18 +770,22 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto& pframe = ppf->frames.back();
|
auto& pframe = ppf->frames.back();
|
||||||
pframe.x0 = x0;
|
pframe.frame_info.layer_info.crop_x0 = x0;
|
||||||
pframe.y0 = y0;
|
pframe.frame_info.layer_info.crop_y0 = y0;
|
||||||
|
pframe.frame_info.layer_info.xsize = xsize;
|
||||||
|
pframe.frame_info.layer_info.ysize = ysize;
|
||||||
pframe.frame_info.duration = frame.duration;
|
pframe.frame_info.duration = frame.duration;
|
||||||
pframe.blend = should_blend;
|
pframe.frame_info.layer_info.blend_info.blendmode =
|
||||||
pframe.use_for_next_frame = use_for_next_frame;
|
should_blend ? JXL_BLEND_BLEND : JXL_BLEND_REPLACE;
|
||||||
|
bool is_full_size = x0 == 0 && y0 == 0 && xsize == ppf->info.xsize &&
|
||||||
|
ysize == ppf->info.ysize;
|
||||||
|
pframe.frame_info.layer_info.have_crop = is_full_size ? 0 : 1;
|
||||||
|
pframe.frame_info.layer_info.blend_info.source = should_blend ? 1 : 0;
|
||||||
|
pframe.frame_info.layer_info.blend_info.alpha = 0;
|
||||||
|
pframe.frame_info.layer_info.save_as_reference = use_for_next_frame ? 1 : 0;
|
||||||
|
|
||||||
if (has_nontrivial_background &&
|
previous_frame_should_be_cleared =
|
||||||
frame.dispose_op == DISPOSE_OP_BACKGROUND) {
|
has_nontrivial_background && frame.dispose_op == DISPOSE_OP_BACKGROUND;
|
||||||
previous_frame_should_be_cleared = true;
|
|
||||||
} else {
|
|
||||||
previous_frame_should_be_cleared = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded");
|
if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded");
|
||||||
ppf->frames.back().frame_info.is_last = true;
|
ppf->frames.back().frame_info.is_last = true;
|
||||||
@ -750,60 +793,5 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
|
|||||||
return true;
|
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 extras
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
@ -42,9 +42,7 @@ Status ApplyColorHints(const ColorHints& color_hints,
|
|||||||
|
|
||||||
got_color_space = true;
|
got_color_space = true;
|
||||||
} else if (key == "icc_pathname") {
|
} else if (key == "icc_pathname") {
|
||||||
PaddedBytes icc;
|
JXL_RETURN_IF_ERROR(ReadFile(value, &ppf->icc));
|
||||||
JXL_RETURN_IF_ERROR(ReadFile(value, &icc));
|
|
||||||
ppf->icc = std::vector<uint8_t>{icc.data(), icc.data() + icc.size()};
|
|
||||||
got_color_space = true;
|
got_color_space = true;
|
||||||
} else {
|
} else {
|
||||||
JXL_WARNING("Ignoring %s hint", key.c_str());
|
JXL_WARNING("Ignoring %s hint", key.c_str());
|
||||||
|
38
third_party/jpeg-xl/lib/extras/dec/decode.cc
vendored
38
third_party/jpeg-xl/lib/extras/dec/decode.cc
vendored
@ -31,31 +31,6 @@ constexpr size_t kMinBytes = 9;
|
|||||||
|
|
||||||
} // namespace
|
} // 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,
|
Codec CodecFromExtension(std::string extension,
|
||||||
size_t* JXL_RESTRICT bits_per_sample) {
|
size_t* JXL_RESTRICT bits_per_sample) {
|
||||||
std::transform(
|
std::transform(
|
||||||
@ -68,10 +43,8 @@ Codec CodecFromExtension(std::string extension,
|
|||||||
|
|
||||||
if (extension == ".pgx") return Codec::kPGX;
|
if (extension == ".pgx") return Codec::kPGX;
|
||||||
|
|
||||||
if (extension == ".pbm") {
|
if (extension == ".pam") return Codec::kPNM;
|
||||||
if (bits_per_sample != nullptr) *bits_per_sample = 1;
|
if (extension == ".pnm") return Codec::kPNM;
|
||||||
return Codec::kPNM;
|
|
||||||
}
|
|
||||||
if (extension == ".pgm") return Codec::kPNM;
|
if (extension == ".pgm") return Codec::kPNM;
|
||||||
if (extension == ".ppm") return Codec::kPNM;
|
if (extension == ".ppm") return Codec::kPNM;
|
||||||
if (extension == ".pfm") {
|
if (extension == ".pfm") {
|
||||||
@ -83,16 +56,13 @@ Codec CodecFromExtension(std::string extension,
|
|||||||
|
|
||||||
if (extension == ".exr") return Codec::kEXR;
|
if (extension == ".exr") return Codec::kEXR;
|
||||||
|
|
||||||
if (extension == ".psd") return Codec::kPSD;
|
|
||||||
|
|
||||||
return Codec::kUnknown;
|
return Codec::kUnknown;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status DecodeBytes(const Span<const uint8_t> bytes,
|
Status DecodeBytes(const Span<const uint8_t> bytes,
|
||||||
const ColorHints& color_hints,
|
const ColorHints& color_hints,
|
||||||
const SizeConstraints& constraints,
|
const SizeConstraints& constraints,
|
||||||
extras::PackedPixelFile* ppf, ThreadPool* pool,
|
extras::PackedPixelFile* ppf, Codec* orig_codec) {
|
||||||
Codec* orig_codec) {
|
|
||||||
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
|
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
|
||||||
|
|
||||||
*ppf = extras::PackedPixelFile();
|
*ppf = extras::PackedPixelFile();
|
||||||
@ -123,7 +93,7 @@ Status DecodeBytes(const Span<const uint8_t> bytes,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#if JPEGXL_ENABLE_EXR
|
#if JPEGXL_ENABLE_EXR
|
||||||
else if (DecodeImageEXR(bytes, color_hints, constraints, pool, ppf)) {
|
else if (DecodeImageEXR(bytes, color_hints, constraints, ppf)) {
|
||||||
codec = Codec::kEXR;
|
codec = Codec::kEXR;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
12
third_party/jpeg-xl/lib/extras/dec/decode.h
vendored
12
third_party/jpeg-xl/lib/extras/dec/decode.h
vendored
@ -33,8 +33,7 @@ enum class Codec : uint32_t {
|
|||||||
kPGX,
|
kPGX,
|
||||||
kJPG,
|
kJPG,
|
||||||
kGIF,
|
kGIF,
|
||||||
kEXR,
|
kEXR
|
||||||
kPSD
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline constexpr uint64_t EnumBits(Codec /*unused*/) {
|
static inline constexpr uint64_t EnumBits(Codec /*unused*/) {
|
||||||
@ -49,13 +48,9 @@ static inline constexpr uint64_t EnumBits(Codec /*unused*/) {
|
|||||||
#if JPEGXL_ENABLE_EXR
|
#if JPEGXL_ENABLE_EXR
|
||||||
| MakeBit(Codec::kEXR)
|
| MakeBit(Codec::kEXR)
|
||||||
#endif
|
#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
|
// If and only if extension is ".pfm", *bits_per_sample is updated to 32 so
|
||||||
// that Encode() would encode to PFM instead of PPM.
|
// that Encode() would encode to PFM instead of PPM.
|
||||||
Codec CodecFromExtension(std::string extension,
|
Codec CodecFromExtension(std::string extension,
|
||||||
@ -65,8 +60,7 @@ Codec CodecFromExtension(std::string extension,
|
|||||||
// color_space_hint may specify the color space, otherwise, defaults to sRGB.
|
// color_space_hint may specify the color space, otherwise, defaults to sRGB.
|
||||||
Status DecodeBytes(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
Status DecodeBytes(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
||||||
const SizeConstraints& constraints,
|
const SizeConstraints& constraints,
|
||||||
extras::PackedPixelFile* ppf, ThreadPool* pool = nullptr,
|
extras::PackedPixelFile* ppf, Codec* orig_codec = nullptr);
|
||||||
Codec* orig_codec = nullptr);
|
|
||||||
|
|
||||||
} // namespace extras
|
} // namespace extras
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
60
third_party/jpeg-xl/lib/extras/dec/exr.cc
vendored
60
third_party/jpeg-xl/lib/extras/dec/exr.cc
vendored
@ -29,18 +29,6 @@ using ExrInt64 = decltype(std::declval<OpenEXR::IStream>().tellg());
|
|||||||
constexpr int kExrBitsPerSample = 16;
|
constexpr int kExrBitsPerSample = 16;
|
||||||
constexpr int kExrAlphaBits = 16;
|
constexpr int kExrAlphaBits = 16;
|
||||||
|
|
||||||
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 InMemoryIStream : public OpenEXR::IStream {
|
class InMemoryIStream : public OpenEXR::IStream {
|
||||||
public:
|
public:
|
||||||
// The data pointed to by `bytes` must outlive the InMemoryIStream.
|
// The data pointed to by `bytes` must outlive the InMemoryIStream.
|
||||||
@ -74,15 +62,8 @@ class InMemoryIStream : public OpenEXR::IStream {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
||||||
const SizeConstraints& constraints, ThreadPool* pool,
|
const SizeConstraints& constraints,
|
||||||
PackedPixelFile* ppf) {
|
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.
|
|
||||||
// TODO(sboukortt): look into changing that with OpenEXR 2.3 which allows
|
|
||||||
// custom thread pools according to its changelog.
|
|
||||||
OpenEXR::setGlobalThreadCount(GetNumThreads(pool));
|
|
||||||
|
|
||||||
InMemoryIStream is(bytes);
|
InMemoryIStream is(bytes);
|
||||||
|
|
||||||
#ifdef __EXCEPTIONS
|
#ifdef __EXCEPTIONS
|
||||||
@ -149,27 +130,24 @@ Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
|||||||
input_rows.data() - input.dataWindow().min.x - start_y * row_size,
|
input_rows.data() - input.dataWindow().min.x - start_y * row_size,
|
||||||
/*xStride=*/1, /*yStride=*/row_size);
|
/*xStride=*/1, /*yStride=*/row_size);
|
||||||
input.readPixels(start_y, end_y);
|
input.readPixels(start_y, end_y);
|
||||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
for (int exr_y = start_y; exr_y <= end_y; ++exr_y) {
|
||||||
pool, start_y, end_y + 1, ThreadPool::NoInit,
|
const int image_y = exr_y - input.displayWindow().min.y;
|
||||||
[&](const uint32_t exr_y, size_t /* thread */) {
|
const OpenEXR::Rgba* const JXL_RESTRICT input_row =
|
||||||
const int image_y = exr_y - input.displayWindow().min.y;
|
&input_rows[(exr_y - start_y) * row_size];
|
||||||
const OpenEXR::Rgba* const JXL_RESTRICT input_row =
|
uint8_t* row = static_cast<uint8_t*>(frame.color.pixels()) +
|
||||||
&input_rows[(exr_y - start_y) * row_size];
|
frame.color.stride * image_y;
|
||||||
uint8_t* row = static_cast<uint8_t*>(frame.color.pixels()) +
|
const uint32_t pixel_size =
|
||||||
frame.color.stride * image_y;
|
(3 + (has_alpha ? 1 : 0)) * kExrBitsPerSample / 8;
|
||||||
const uint32_t pixel_size =
|
for (int exr_x =
|
||||||
(3 + (has_alpha ? 1 : 0)) * kExrBitsPerSample / 8;
|
std::max(input.dataWindow().min.x, input.displayWindow().min.x);
|
||||||
for (int exr_x = std::max(input.dataWindow().min.x,
|
exr_x <=
|
||||||
input.displayWindow().min.x);
|
std::min(input.dataWindow().max.x, input.displayWindow().max.x);
|
||||||
exr_x <=
|
++exr_x) {
|
||||||
std::min(input.dataWindow().max.x, input.displayWindow().max.x);
|
const int image_x = exr_x - input.displayWindow().min.x;
|
||||||
++exr_x) {
|
memcpy(row + image_x * pixel_size,
|
||||||
const int image_x = exr_x - input.displayWindow().min.x;
|
input_row + (exr_x - input.dataWindow().min.x), pixel_size);
|
||||||
memcpy(row + image_x * pixel_size,
|
}
|
||||||
input_row + (exr_x - input.dataWindow().min.x), pixel_size);
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
"DecodeImageEXR"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
|
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
|
||||||
|
3
third_party/jpeg-xl/lib/extras/dec/exr.h
vendored
3
third_party/jpeg-xl/lib/extras/dec/exr.h
vendored
@ -21,8 +21,7 @@ namespace extras {
|
|||||||
|
|
||||||
// Decodes `bytes` into `ppf`. color_hints are ignored.
|
// Decodes `bytes` into `ppf`. color_hints are ignored.
|
||||||
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
||||||
const SizeConstraints& constraints, ThreadPool* pool,
|
const SizeConstraints& constraints, PackedPixelFile* ppf);
|
||||||
PackedPixelFile* ppf);
|
|
||||||
|
|
||||||
} // namespace extras
|
} // namespace extras
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
26
third_party/jpeg-xl/lib/extras/dec/gif.cc
vendored
26
third_party/jpeg-xl/lib/extras/dec/gif.cc
vendored
@ -12,6 +12,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "jxl/codestream_header.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/sanitizers.h"
|
#include "lib/jxl/sanitizers.h"
|
||||||
|
|
||||||
@ -257,15 +258,26 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
|||||||
GraphicsControlBlock gcb;
|
GraphicsControlBlock gcb;
|
||||||
DGifSavedExtensionToGCB(gif.get(), i, &gcb);
|
DGifSavedExtensionToGCB(gif.get(), i, &gcb);
|
||||||
msan::UnpoisonMemory(&gcb, sizeof(gcb));
|
msan::UnpoisonMemory(&gcb, sizeof(gcb));
|
||||||
|
bool is_full_size = total_rect.x0() == 0 && total_rect.y0() == 0 &&
|
||||||
|
total_rect.xsize() == canvas.color.xsize &&
|
||||||
|
total_rect.ysize() == canvas.color.ysize;
|
||||||
if (ppf->info.have_animation) {
|
if (ppf->info.have_animation) {
|
||||||
frame->frame_info.duration = gcb.DelayTime;
|
frame->frame_info.duration = gcb.DelayTime;
|
||||||
frame->x0 = total_rect.x0();
|
frame->frame_info.layer_info.have_crop = static_cast<int>(!is_full_size);
|
||||||
frame->y0 = total_rect.y0();
|
frame->frame_info.layer_info.crop_x0 = total_rect.x0();
|
||||||
|
frame->frame_info.layer_info.crop_y0 = total_rect.y0();
|
||||||
|
frame->frame_info.layer_info.xsize = frame->color.xsize;
|
||||||
|
frame->frame_info.layer_info.ysize = frame->color.ysize;
|
||||||
if (last_base_was_none) {
|
if (last_base_was_none) {
|
||||||
replace = true;
|
replace = true;
|
||||||
}
|
}
|
||||||
frame->blend = !replace;
|
frame->frame_info.layer_info.blend_info.blendmode =
|
||||||
|
replace ? JXL_BLEND_REPLACE : JXL_BLEND_BLEND;
|
||||||
|
// We always only reference at most the last frame
|
||||||
|
frame->frame_info.layer_info.blend_info.source =
|
||||||
|
last_base_was_none ? 0u : 1u;
|
||||||
|
frame->frame_info.layer_info.blend_info.clamp = 1;
|
||||||
|
frame->frame_info.layer_info.blend_info.alpha = 0;
|
||||||
// TODO(veluca): this could in principle be implemented.
|
// TODO(veluca): this could in principle be implemented.
|
||||||
if (last_base_was_none &&
|
if (last_base_was_none &&
|
||||||
(total_rect.x0() != 0 || total_rect.y0() != 0 ||
|
(total_rect.x0() != 0 || total_rect.y0() != 0 ||
|
||||||
@ -278,14 +290,14 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
|
|||||||
switch (gcb.DisposalMode) {
|
switch (gcb.DisposalMode) {
|
||||||
case DISPOSE_DO_NOT:
|
case DISPOSE_DO_NOT:
|
||||||
case DISPOSE_BACKGROUND:
|
case DISPOSE_BACKGROUND:
|
||||||
frame->use_for_next_frame = true;
|
frame->frame_info.layer_info.save_as_reference = 1u;
|
||||||
last_base_was_none = false;
|
last_base_was_none = false;
|
||||||
break;
|
break;
|
||||||
case DISPOSE_PREVIOUS:
|
case DISPOSE_PREVIOUS:
|
||||||
frame->use_for_next_frame = false;
|
frame->frame_info.layer_info.save_as_reference = 0u;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
frame->use_for_next_frame = false;
|
frame->frame_info.layer_info.save_as_reference = 0u;
|
||||||
last_base_was_none = true;
|
last_base_was_none = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
10
third_party/jpeg-xl/lib/extras/dec/pgx.cc
vendored
10
third_party/jpeg-xl/lib/extras/dec/pgx.cc
vendored
@ -117,6 +117,8 @@ class Parser {
|
|||||||
// 0xa, or 0xd 0xa.
|
// 0xa, or 0xd 0xa.
|
||||||
JXL_RETURN_IF_ERROR(SkipLineBreak());
|
JXL_RETURN_IF_ERROR(SkipLineBreak());
|
||||||
|
|
||||||
|
// TODO(jon): could do up to 24-bit by converting the values to
|
||||||
|
// JXL_TYPE_FLOAT.
|
||||||
if (header->bits_per_sample > 16) {
|
if (header->bits_per_sample > 16) {
|
||||||
return JXL_FAILURE("PGX: >16 bits not yet supported");
|
return JXL_FAILURE("PGX: >16 bits not yet supported");
|
||||||
}
|
}
|
||||||
@ -127,9 +129,7 @@ class Parser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
size_t numpixels = header->xsize * header->ysize;
|
size_t numpixels = header->xsize * header->ysize;
|
||||||
size_t bytes_per_pixel = header->bits_per_sample <= 8 ? 1
|
size_t bytes_per_pixel = header->bits_per_sample <= 8 ? 1 : 2;
|
||||||
: header->bits_per_sample <= 16 ? 2
|
|
||||||
: 4;
|
|
||||||
if (pos_ + numpixels * bytes_per_pixel > end_) {
|
if (pos_ + numpixels * bytes_per_pixel > end_) {
|
||||||
return JXL_FAILURE("PGX: data too small");
|
return JXL_FAILURE("PGX: data too small");
|
||||||
}
|
}
|
||||||
@ -174,9 +174,7 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes,
|
|||||||
ppf->info.orientation = JXL_ORIENT_IDENTITY;
|
ppf->info.orientation = JXL_ORIENT_IDENTITY;
|
||||||
|
|
||||||
JxlDataType data_type;
|
JxlDataType data_type;
|
||||||
if (header.bits_per_sample > 16) {
|
if (header.bits_per_sample > 8) {
|
||||||
data_type = JXL_TYPE_UINT32;
|
|
||||||
} else if (header.bits_per_sample > 8) {
|
|
||||||
data_type = JXL_TYPE_UINT16;
|
data_type = JXL_TYPE_UINT16;
|
||||||
} else {
|
} else {
|
||||||
data_type = JXL_TYPE_UINT8;
|
data_type = JXL_TYPE_UINT8;
|
||||||
|
54
third_party/jpeg-xl/lib/extras/dec/pnm.cc
vendored
54
third_party/jpeg-xl/lib/extras/dec/pnm.cc
vendored
@ -19,7 +19,6 @@ namespace {
|
|||||||
struct HeaderPNM {
|
struct HeaderPNM {
|
||||||
size_t xsize;
|
size_t xsize;
|
||||||
size_t ysize;
|
size_t ysize;
|
||||||
bool is_bit; // PBM
|
|
||||||
bool is_gray; // PGM
|
bool is_gray; // PGM
|
||||||
bool has_alpha; // PAM
|
bool has_alpha; // PAM
|
||||||
size_t bits_per_sample;
|
size_t bits_per_sample;
|
||||||
@ -39,14 +38,9 @@ class Parser {
|
|||||||
const uint8_t type = pos_[1];
|
const uint8_t type = pos_[1];
|
||||||
pos_ += 2;
|
pos_ += 2;
|
||||||
|
|
||||||
header->is_bit = false;
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case '4':
|
case '4':
|
||||||
header->is_bit = true;
|
return JXL_FAILURE("pbm not supported");
|
||||||
header->is_gray = true;
|
|
||||||
header->bits_per_sample = 1;
|
|
||||||
return ParseHeaderPNM(header, pos);
|
|
||||||
|
|
||||||
case '5':
|
case '5':
|
||||||
header->is_gray = true;
|
header->is_gray = true;
|
||||||
@ -169,7 +163,7 @@ class Parser {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status MatchString(const char* keyword) {
|
Status MatchString(const char* keyword, bool skipws = true) {
|
||||||
const uint8_t* ppos = pos_;
|
const uint8_t* ppos = pos_;
|
||||||
while (*keyword) {
|
while (*keyword) {
|
||||||
if (ppos >= end_) return JXL_FAILURE("PAM: unexpected end of input");
|
if (ppos >= end_) return JXL_FAILURE("PAM: unexpected end of input");
|
||||||
@ -178,14 +172,18 @@ class Parser {
|
|||||||
keyword++;
|
keyword++;
|
||||||
}
|
}
|
||||||
pos_ = ppos;
|
pos_ = ppos;
|
||||||
JXL_RETURN_IF_ERROR(SkipWhitespace());
|
if (skipws) {
|
||||||
|
JXL_RETURN_IF_ERROR(SkipWhitespace());
|
||||||
|
} else {
|
||||||
|
JXL_RETURN_IF_ERROR(SkipSingleWhitespace());
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status ParseHeaderPAM(HeaderPNM* header, const uint8_t** pos) {
|
Status ParseHeaderPAM(HeaderPNM* header, const uint8_t** pos) {
|
||||||
size_t depth = 3;
|
size_t depth = 3;
|
||||||
size_t max_val = 255;
|
size_t max_val = 255;
|
||||||
while (!MatchString("ENDHDR")) {
|
while (!MatchString("ENDHDR", /*skipws=*/false)) {
|
||||||
JXL_RETURN_IF_ERROR(SkipWhitespace());
|
JXL_RETURN_IF_ERROR(SkipWhitespace());
|
||||||
if (MatchString("WIDTH")) {
|
if (MatchString("WIDTH")) {
|
||||||
JXL_RETURN_IF_ERROR(ParseUnsigned(&header->xsize));
|
JXL_RETURN_IF_ERROR(ParseUnsigned(&header->xsize));
|
||||||
@ -231,7 +229,11 @@ class Parser {
|
|||||||
if (max_val == 0 || max_val >= 65536) {
|
if (max_val == 0 || max_val >= 65536) {
|
||||||
return JXL_FAILURE("PAM: bad MAXVAL");
|
return JXL_FAILURE("PAM: bad MAXVAL");
|
||||||
}
|
}
|
||||||
header->bits_per_sample = CeilLog2Nonzero(max_val);
|
// e.g When `max_val` is 1 , we want 1 bit:
|
||||||
|
header->bits_per_sample = FloorLog2Nonzero(max_val) + 1;
|
||||||
|
if ((1u << header->bits_per_sample) - 1 != max_val)
|
||||||
|
return JXL_FAILURE("PNM: unsupported MaxVal (expected 2^n - 1)");
|
||||||
|
// PAM does not pack bits as in PBM.
|
||||||
|
|
||||||
header->floating_point = false;
|
header->floating_point = false;
|
||||||
header->big_endian = true;
|
header->big_endian = true;
|
||||||
@ -246,15 +248,15 @@ class Parser {
|
|||||||
JXL_RETURN_IF_ERROR(SkipWhitespace());
|
JXL_RETURN_IF_ERROR(SkipWhitespace());
|
||||||
JXL_RETURN_IF_ERROR(ParseUnsigned(&header->ysize));
|
JXL_RETURN_IF_ERROR(ParseUnsigned(&header->ysize));
|
||||||
|
|
||||||
if (!header->is_bit) {
|
JXL_RETURN_IF_ERROR(SkipWhitespace());
|
||||||
JXL_RETURN_IF_ERROR(SkipWhitespace());
|
size_t max_val;
|
||||||
size_t max_val;
|
JXL_RETURN_IF_ERROR(ParseUnsigned(&max_val));
|
||||||
JXL_RETURN_IF_ERROR(ParseUnsigned(&max_val));
|
if (max_val == 0 || max_val >= 65536) {
|
||||||
if (max_val == 0 || max_val >= 65536) {
|
return JXL_FAILURE("PNM: bad MaxVal");
|
||||||
return JXL_FAILURE("PNM: bad MaxVal");
|
|
||||||
}
|
|
||||||
header->bits_per_sample = CeilLog2Nonzero(max_val);
|
|
||||||
}
|
}
|
||||||
|
header->bits_per_sample = FloorLog2Nonzero(max_val) + 1;
|
||||||
|
if ((1u << header->bits_per_sample) - 1 != max_val)
|
||||||
|
return JXL_FAILURE("PNM: unsupported MaxVal (expected 2^n - 1)");
|
||||||
header->floating_point = false;
|
header->floating_point = false;
|
||||||
header->big_endian = true;
|
header->big_endian = true;
|
||||||
|
|
||||||
@ -276,7 +278,12 @@ class Parser {
|
|||||||
// indicate endianness. All software expects nominal range 0..1.
|
// indicate endianness. All software expects nominal range 0..1.
|
||||||
double scale;
|
double scale;
|
||||||
JXL_RETURN_IF_ERROR(ParseSigned(&scale));
|
JXL_RETURN_IF_ERROR(ParseSigned(&scale));
|
||||||
header->big_endian = scale >= 0.0;
|
if (scale == 0.0) {
|
||||||
|
return JXL_FAILURE("PFM: bad scale factor value.");
|
||||||
|
} else if (std::abs(scale) != 1.0) {
|
||||||
|
JXL_WARNING("PFM: Discarding non-unit scale factor");
|
||||||
|
}
|
||||||
|
header->big_endian = scale > 0.0;
|
||||||
header->bits_per_sample = 32;
|
header->bits_per_sample = 32;
|
||||||
header->floating_point = true;
|
header->floating_point = true;
|
||||||
|
|
||||||
@ -341,12 +348,8 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes,
|
|||||||
// There's no float16 pnm version.
|
// There's no float16 pnm version.
|
||||||
data_type = JXL_TYPE_FLOAT;
|
data_type = JXL_TYPE_FLOAT;
|
||||||
} else {
|
} else {
|
||||||
if (header.bits_per_sample > 16) {
|
if (header.bits_per_sample > 8) {
|
||||||
data_type = JXL_TYPE_UINT32;
|
|
||||||
} else if (header.bits_per_sample > 8) {
|
|
||||||
data_type = JXL_TYPE_UINT16;
|
data_type = JXL_TYPE_UINT16;
|
||||||
} else if (header.is_bit) {
|
|
||||||
data_type = JXL_TYPE_BOOLEAN;
|
|
||||||
} else {
|
} else {
|
||||||
data_type = JXL_TYPE_UINT8;
|
data_type = JXL_TYPE_UINT8;
|
||||||
}
|
}
|
||||||
@ -363,6 +366,7 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes,
|
|||||||
ppf->frames.emplace_back(header.xsize, header.ysize, format);
|
ppf->frames.emplace_back(header.xsize, header.ysize, format);
|
||||||
auto* frame = &ppf->frames.back();
|
auto* frame = &ppf->frames.back();
|
||||||
|
|
||||||
|
frame->color.bitdepth_from_format = false;
|
||||||
frame->color.flipped_y = header.bits_per_sample == 32; // PFMs are flipped
|
frame->color.flipped_y = header.bits_per_sample == 32; // PFMs are flipped
|
||||||
size_t pnm_remaining_size = bytes.data() + bytes.size() - pos;
|
size_t pnm_remaining_size = bytes.data() + bytes.size() - pos;
|
||||||
if (pnm_remaining_size < frame->color.pixels_size) {
|
if (pnm_remaining_size < frame->color.pixels_size) {
|
||||||
|
28
third_party/jpeg-xl/lib/extras/enc/apng.cc
vendored
28
third_party/jpeg-xl/lib/extras/enc/apng.cc
vendored
@ -49,6 +49,7 @@
|
|||||||
#include "lib/jxl/dec_external_image.h"
|
#include "lib/jxl/dec_external_image.h"
|
||||||
#include "lib/jxl/enc_color_management.h"
|
#include "lib/jxl/enc_color_management.h"
|
||||||
#include "lib/jxl/enc_image_bundle.h"
|
#include "lib/jxl/enc_image_bundle.h"
|
||||||
|
#include "lib/jxl/exif.h"
|
||||||
#include "lib/jxl/frame_header.h"
|
#include "lib/jxl/frame_header.h"
|
||||||
#include "lib/jxl/headers.h"
|
#include "lib/jxl/headers.h"
|
||||||
#include "lib/jxl/image.h"
|
#include "lib/jxl/image.h"
|
||||||
@ -70,7 +71,12 @@ class BlobsWriterPNG {
|
|||||||
public:
|
public:
|
||||||
static Status Encode(const Blobs& blobs, std::vector<std::string>* strings) {
|
static Status Encode(const Blobs& blobs, std::vector<std::string>* strings) {
|
||||||
if (!blobs.exif.empty()) {
|
if (!blobs.exif.empty()) {
|
||||||
JXL_RETURN_IF_ERROR(EncodeBase16("exif", blobs.exif, strings));
|
// PNG viewers typically ignore Exif orientation but not all of them do
|
||||||
|
// (and e.g. cjxl doesn't), so we overwrite the Exif orientation to the
|
||||||
|
// identity to avoid repeated orientation.
|
||||||
|
std::vector<uint8_t> exif = blobs.exif;
|
||||||
|
ResetExifOrientation(exif);
|
||||||
|
JXL_RETURN_IF_ERROR(EncodeBase16("exif", exif, strings));
|
||||||
}
|
}
|
||||||
if (!blobs.iptc.empty()) {
|
if (!blobs.iptc.empty()) {
|
||||||
JXL_RETURN_IF_ERROR(EncodeBase16("iptc", blobs.iptc, strings));
|
JXL_RETURN_IF_ERROR(EncodeBase16("iptc", blobs.iptc, strings));
|
||||||
@ -87,7 +93,8 @@ class BlobsWriterPNG {
|
|||||||
return (nibble < 10) ? '0' + nibble : 'a' + nibble - 10;
|
return (nibble < 10) ? '0' + nibble : 'a' + nibble - 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Status EncodeBase16(const std::string& type, const PaddedBytes& bytes,
|
static Status EncodeBase16(const std::string& type,
|
||||||
|
const std::vector<uint8_t>& bytes,
|
||||||
std::vector<std::string>* strings) {
|
std::vector<std::string>* strings) {
|
||||||
// Encoding: base16 with newline after 72 chars.
|
// Encoding: base16 with newline after 72 chars.
|
||||||
const size_t base16_size =
|
const size_t base16_size =
|
||||||
@ -158,12 +165,12 @@ Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||||||
size_t stride = ib.oriented_xsize() *
|
size_t stride = ib.oriented_xsize() *
|
||||||
DivCeil(c_desired.Channels() * bits_per_sample + alpha_bits,
|
DivCeil(c_desired.Channels() * bits_per_sample + alpha_bits,
|
||||||
kBitsPerByte);
|
kBitsPerByte);
|
||||||
PaddedBytes raw_bytes(stride * ib.oriented_ysize());
|
std::vector<uint8_t> raw_bytes(stride * ib.oriented_ysize());
|
||||||
JXL_RETURN_IF_ERROR(ConvertToExternal(
|
JXL_RETURN_IF_ERROR(ConvertToExternal(
|
||||||
*transformed, bits_per_sample, /*float_out=*/false,
|
*transformed, bits_per_sample, /*float_out=*/false,
|
||||||
c_desired.Channels() + (ib.HasAlpha() ? 1 : 0), JXL_BIG_ENDIAN, stride,
|
c_desired.Channels() + (ib.HasAlpha() ? 1 : 0), JXL_BIG_ENDIAN, stride,
|
||||||
pool, raw_bytes.data(), raw_bytes.size(), /*out_callback=*/nullptr,
|
pool, raw_bytes.data(), raw_bytes.size(),
|
||||||
/*out_opaque=*/nullptr, metadata.GetOrientation()));
|
/*out_callback=*/{}, metadata.GetOrientation()));
|
||||||
|
|
||||||
int width = ib.oriented_xsize();
|
int width = ib.oriented_xsize();
|
||||||
int height = ib.oriented_ysize();
|
int height = ib.oriented_ysize();
|
||||||
@ -188,10 +195,10 @@ Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||||||
|
|
||||||
std::vector<std::string> textstrings;
|
std::vector<std::string> textstrings;
|
||||||
JXL_RETURN_IF_ERROR(BlobsWriterPNG::Encode(io->blobs, &textstrings));
|
JXL_RETURN_IF_ERROR(BlobsWriterPNG::Encode(io->blobs, &textstrings));
|
||||||
for (size_t i = 0; i + 1 < textstrings.size(); i += 2) {
|
for (size_t kk = 0; kk + 1 < textstrings.size(); kk += 2) {
|
||||||
png_text text;
|
png_text text;
|
||||||
text.key = const_cast<png_charp>(textstrings[i].c_str());
|
text.key = const_cast<png_charp>(textstrings[kk].c_str());
|
||||||
text.text = const_cast<png_charp>(textstrings[i + 1].c_str());
|
text.text = const_cast<png_charp>(textstrings[kk + 1].c_str());
|
||||||
text.compression = PNG_TEXT_COMPRESSION_zTXt;
|
text.compression = PNG_TEXT_COMPRESSION_zTXt;
|
||||||
png_set_text(png_ptr, info_ptr, &text, 1);
|
png_set_text(png_ptr, info_ptr, &text, 1);
|
||||||
}
|
}
|
||||||
@ -241,7 +248,7 @@ Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||||||
png_write_image(png_ptr, &rows[0]);
|
png_write_image(png_ptr, &rows[0]);
|
||||||
png_write_flush(png_ptr);
|
png_write_flush(png_ptr);
|
||||||
if (count > 0) {
|
if (count > 0) {
|
||||||
PaddedBytes fdata(4);
|
std::vector<uint8_t> fdata(4);
|
||||||
png_save_uint_32(fdata.data(), anim_chunks++);
|
png_save_uint_32(fdata.data(), anim_chunks++);
|
||||||
size_t p = pos;
|
size_t p = pos;
|
||||||
while (p + 8 < bytes->size()) {
|
while (p + 8 < bytes->size()) {
|
||||||
@ -250,7 +257,8 @@ Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||||||
JXL_ASSERT(bytes->operator[](p + 5) == 'D');
|
JXL_ASSERT(bytes->operator[](p + 5) == 'D');
|
||||||
JXL_ASSERT(bytes->operator[](p + 6) == 'A');
|
JXL_ASSERT(bytes->operator[](p + 6) == 'A');
|
||||||
JXL_ASSERT(bytes->operator[](p + 7) == 'T');
|
JXL_ASSERT(bytes->operator[](p + 7) == 'T');
|
||||||
fdata.append(bytes->data() + p + 8, bytes->data() + p + 8 + len);
|
fdata.insert(fdata.end(), bytes->data() + p + 8,
|
||||||
|
bytes->data() + p + 8 + len);
|
||||||
p += len + 12;
|
p += len + 12;
|
||||||
}
|
}
|
||||||
bytes->resize(pos);
|
bytes->resize(pos);
|
||||||
|
94
third_party/jpeg-xl/lib/extras/enc/jpg.cc
vendored
94
third_party/jpeg-xl/lib/extras/enc/jpg.cc
vendored
@ -19,8 +19,10 @@
|
|||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/color_encoding_internal.h"
|
#include "lib/jxl/color_encoding_internal.h"
|
||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
|
#include "lib/jxl/dec_external_image.h"
|
||||||
#include "lib/jxl/enc_color_management.h"
|
#include "lib/jxl/enc_color_management.h"
|
||||||
#include "lib/jxl/enc_image_bundle.h"
|
#include "lib/jxl/enc_image_bundle.h"
|
||||||
|
#include "lib/jxl/exif.h"
|
||||||
#include "lib/jxl/image.h"
|
#include "lib/jxl/image.h"
|
||||||
#include "lib/jxl/image_bundle.h"
|
#include "lib/jxl/image_bundle.h"
|
||||||
#include "lib/jxl/sanitizers.h"
|
#include "lib/jxl/sanitizers.h"
|
||||||
@ -33,7 +35,6 @@ namespace extras {
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr float kJPEGSampleMultiplier = MAXJSAMPLE;
|
|
||||||
constexpr unsigned char kICCSignature[12] = {
|
constexpr unsigned char kICCSignature[12] = {
|
||||||
0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00};
|
0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00};
|
||||||
constexpr int kICCMarker = JPEG_APP0 + 2;
|
constexpr int kICCMarker = JPEG_APP0 + 2;
|
||||||
@ -43,13 +44,6 @@ constexpr unsigned char kExifSignature[6] = {0x45, 0x78, 0x69,
|
|||||||
0x66, 0x00, 0x00};
|
0x66, 0x00, 0x00};
|
||||||
constexpr int kExifMarker = JPEG_APP0 + 1;
|
constexpr int kExifMarker = JPEG_APP0 + 1;
|
||||||
|
|
||||||
constexpr float kJPEGSampleMin = 0;
|
|
||||||
constexpr float kJPEGSampleMax = MAXJSAMPLE;
|
|
||||||
|
|
||||||
// TODO (jon): take orientation into account when writing jpeg output
|
|
||||||
// TODO (jon): write Exif blob also in sjpeg encoding
|
|
||||||
// TODO (jon): overwrite orientation in Exif blob to avoid double orientation
|
|
||||||
|
|
||||||
void WriteICCProfile(jpeg_compress_struct* const cinfo,
|
void WriteICCProfile(jpeg_compress_struct* const cinfo,
|
||||||
const PaddedBytes& icc) {
|
const PaddedBytes& icc) {
|
||||||
constexpr size_t kMaxIccBytesInMarker =
|
constexpr size_t kMaxIccBytesInMarker =
|
||||||
@ -73,15 +67,15 @@ void WriteICCProfile(jpeg_compress_struct* const cinfo,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void WriteExif(jpeg_compress_struct* const cinfo, const PaddedBytes& exif) {
|
void WriteExif(jpeg_compress_struct* const cinfo,
|
||||||
if (exif.size() < 4) return;
|
const std::vector<uint8_t>& exif) {
|
||||||
jpeg_write_m_header(
|
jpeg_write_m_header(
|
||||||
cinfo, kExifMarker,
|
cinfo, kExifMarker,
|
||||||
static_cast<unsigned int>(exif.size() - 4 + sizeof kExifSignature));
|
static_cast<unsigned int>(exif.size() + sizeof kExifSignature));
|
||||||
for (const unsigned char c : kExifSignature) {
|
for (const unsigned char c : kExifSignature) {
|
||||||
jpeg_write_m_byte(cinfo, c);
|
jpeg_write_m_byte(cinfo, c);
|
||||||
}
|
}
|
||||||
for (size_t i = 4; i < exif.size(); ++i) {
|
for (size_t i = 0; i < exif.size(); ++i) {
|
||||||
jpeg_write_m_byte(cinfo, exif[i]);
|
jpeg_write_m_byte(cinfo, exif[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -115,8 +109,8 @@ Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
|
|||||||
unsigned char* buffer = nullptr;
|
unsigned char* buffer = nullptr;
|
||||||
unsigned long size = 0;
|
unsigned long size = 0;
|
||||||
jpeg_mem_dest(&cinfo, &buffer, &size);
|
jpeg_mem_dest(&cinfo, &buffer, &size);
|
||||||
cinfo.image_width = ib->xsize();
|
cinfo.image_width = ib->oriented_xsize();
|
||||||
cinfo.image_height = ib->ysize();
|
cinfo.image_height = ib->oriented_ysize();
|
||||||
if (ib->IsGray()) {
|
if (ib->IsGray()) {
|
||||||
cinfo.input_components = 1;
|
cinfo.input_components = 1;
|
||||||
cinfo.in_color_space = JCS_GRAYSCALE;
|
cinfo.in_color_space = JCS_GRAYSCALE;
|
||||||
@ -134,27 +128,26 @@ Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
|
|||||||
if (!ib->IsSRGB()) {
|
if (!ib->IsSRGB()) {
|
||||||
WriteICCProfile(&cinfo, ib->c_current().ICC());
|
WriteICCProfile(&cinfo, ib->c_current().ICC());
|
||||||
}
|
}
|
||||||
WriteExif(&cinfo, io->blobs.exif);
|
if (!io->blobs.exif.empty()) {
|
||||||
|
std::vector<uint8_t> exif = io->blobs.exif;
|
||||||
|
ResetExifOrientation(exif);
|
||||||
|
WriteExif(&cinfo, exif);
|
||||||
|
}
|
||||||
if (cinfo.input_components > 3 || cinfo.input_components < 0)
|
if (cinfo.input_components > 3 || cinfo.input_components < 0)
|
||||||
return JXL_FAILURE("invalid numbers of components");
|
return JXL_FAILURE("invalid numbers of components");
|
||||||
|
|
||||||
std::unique_ptr<JSAMPLE[]> row(
|
size_t stride =
|
||||||
new JSAMPLE[cinfo.input_components * cinfo.image_width]);
|
ib->oriented_xsize() * cinfo.input_components * sizeof(JSAMPLE);
|
||||||
for (size_t y = 0; y < ib->ysize(); ++y) {
|
PaddedBytes raw_bytes(stride * ib->oriented_ysize());
|
||||||
const float* const JXL_RESTRICT input_row[3] = {
|
JXL_RETURN_IF_ERROR(ConvertToExternal(
|
||||||
ib->color().ConstPlaneRow(0, y), ib->color().ConstPlaneRow(1, y),
|
*ib, BITS_IN_JSAMPLE, /*float_out=*/false, cinfo.input_components,
|
||||||
ib->color().ConstPlaneRow(2, y)};
|
JXL_BIG_ENDIAN, stride, nullptr, raw_bytes.data(), raw_bytes.size(),
|
||||||
for (size_t x = 0; x < ib->xsize(); ++x) {
|
/*out_callback=*/{}, ib->metadata()->GetOrientation()));
|
||||||
for (size_t c = 0; c < static_cast<size_t>(cinfo.input_components); ++c) {
|
|
||||||
JXL_RETURN_IF_ERROR(c < 3);
|
for (size_t y = 0; y < ib->oriented_ysize(); ++y) {
|
||||||
row[cinfo.input_components * x + c] = static_cast<JSAMPLE>(
|
JSAMPROW row[] = {raw_bytes.data() + y * stride};
|
||||||
std::max(std::min(kJPEGSampleMultiplier * input_row[c][x] + .5f,
|
|
||||||
kJPEGSampleMax),
|
jpeg_write_scanlines(&cinfo, row, 1);
|
||||||
kJPEGSampleMin));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
JSAMPROW rows[] = {row.get()};
|
|
||||||
jpeg_write_scanlines(&cinfo, rows, 1);
|
|
||||||
}
|
}
|
||||||
jpeg_finish_compress(&cinfo);
|
jpeg_finish_compress(&cinfo);
|
||||||
jpeg_destroy_compress(&cinfo);
|
jpeg_destroy_compress(&cinfo);
|
||||||
@ -167,7 +160,8 @@ Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status EncodeWithSJpeg(const ImageBundle* ib, size_t quality,
|
Status EncodeWithSJpeg(const ImageBundle* ib, const CodecInOut* io,
|
||||||
|
size_t quality,
|
||||||
const YCbCrChromaSubsampling& chroma_subsampling,
|
const YCbCrChromaSubsampling& chroma_subsampling,
|
||||||
PaddedBytes* bytes) {
|
PaddedBytes* bytes) {
|
||||||
#if !JPEGXL_ENABLE_SJPEG
|
#if !JPEGXL_ENABLE_SJPEG
|
||||||
@ -178,6 +172,11 @@ Status EncodeWithSJpeg(const ImageBundle* ib, size_t quality,
|
|||||||
param.iccp.assign(ib->metadata()->color_encoding.ICC().begin(),
|
param.iccp.assign(ib->metadata()->color_encoding.ICC().begin(),
|
||||||
ib->metadata()->color_encoding.ICC().end());
|
ib->metadata()->color_encoding.ICC().end());
|
||||||
}
|
}
|
||||||
|
std::vector<uint8_t> exif = io->blobs.exif;
|
||||||
|
if (!exif.empty()) {
|
||||||
|
ResetExifOrientation(exif);
|
||||||
|
param.exif.assign(exif.begin(), exif.end());
|
||||||
|
}
|
||||||
if (chroma_subsampling.Is444()) {
|
if (chroma_subsampling.Is444()) {
|
||||||
param.yuv_mode = SJPEG_YUV_444;
|
param.yuv_mode = SJPEG_YUV_444;
|
||||||
} else if (chroma_subsampling.Is420()) {
|
} else if (chroma_subsampling.Is420()) {
|
||||||
@ -185,24 +184,17 @@ Status EncodeWithSJpeg(const ImageBundle* ib, size_t quality,
|
|||||||
} else {
|
} else {
|
||||||
return JXL_FAILURE("sjpeg does not support this chroma subsampling mode");
|
return JXL_FAILURE("sjpeg does not support this chroma subsampling mode");
|
||||||
}
|
}
|
||||||
std::vector<uint8_t> rgb;
|
size_t stride = ib->oriented_xsize() * 3;
|
||||||
rgb.reserve(ib->xsize() * ib->ysize() * 3);
|
PaddedBytes rgb(ib->xsize() * ib->ysize() * 3);
|
||||||
for (size_t y = 0; y < ib->ysize(); ++y) {
|
JXL_RETURN_IF_ERROR(
|
||||||
const float* const rows[] = {
|
ConvertToExternal(*ib, 8, /*float_out=*/false, 3, JXL_BIG_ENDIAN, stride,
|
||||||
ib->color().ConstPlaneRow(0, y),
|
nullptr, rgb.data(), rgb.size(),
|
||||||
ib->color().ConstPlaneRow(1, y),
|
/*out_callback=*/{}, ib->metadata()->GetOrientation()));
|
||||||
ib->color().ConstPlaneRow(2, y),
|
|
||||||
};
|
|
||||||
for (size_t x = 0; x < ib->xsize(); ++x) {
|
|
||||||
for (const float* const row : rows) {
|
|
||||||
rgb.push_back(static_cast<uint8_t>(
|
|
||||||
std::max(0.f, std::min(255.f, roundf(255.f * row[x])))));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::string output;
|
std::string output;
|
||||||
JXL_RETURN_IF_ERROR(sjpeg::Encode(rgb.data(), ib->xsize(), ib->ysize(),
|
JXL_RETURN_IF_ERROR(sjpeg::Encode(rgb.data(), ib->oriented_xsize(),
|
||||||
ib->xsize() * 3, param, &output));
|
ib->oriented_ysize(), stride, param,
|
||||||
|
&output));
|
||||||
bytes->assign(
|
bytes->assign(
|
||||||
reinterpret_cast<const uint8_t*>(output.data()),
|
reinterpret_cast<const uint8_t*>(output.data()),
|
||||||
reinterpret_cast<const uint8_t*>(output.data() + output.size()));
|
reinterpret_cast<const uint8_t*>(output.data() + output.size()));
|
||||||
@ -234,7 +226,7 @@ Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
|
|||||||
break;
|
break;
|
||||||
case JpegEncoder::kSJpeg:
|
case JpegEncoder::kSJpeg:
|
||||||
JXL_RETURN_IF_ERROR(
|
JXL_RETURN_IF_ERROR(
|
||||||
EncodeWithSJpeg(ib, quality, chroma_subsampling, bytes));
|
EncodeWithSJpeg(ib, io, quality, chroma_subsampling, bytes));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return JXL_FAILURE("tried to use an unknown JPEG encoder");
|
return JXL_FAILURE("tried to use an unknown JPEG encoder");
|
||||||
|
4
third_party/jpeg-xl/lib/extras/enc/pgx.cc
vendored
4
third_party/jpeg-xl/lib/extras/enc/pgx.cc
vendored
@ -75,8 +75,8 @@ Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||||||
ConvertToExternal(*transformed, bits_per_sample,
|
ConvertToExternal(*transformed, bits_per_sample,
|
||||||
/*float_out=*/false,
|
/*float_out=*/false,
|
||||||
/*num_channels=*/1, JXL_BIG_ENDIAN, stride, pool,
|
/*num_channels=*/1, JXL_BIG_ENDIAN, stride, pool,
|
||||||
pixels.data(), pixels.size(), /*out_callback=*/nullptr,
|
pixels.data(), pixels.size(),
|
||||||
/*out_opaque=*/nullptr, metadata.GetOrientation()));
|
/*out_callback=*/{}, metadata.GetOrientation()));
|
||||||
|
|
||||||
char header[kMaxHeaderSize];
|
char header[kMaxHeaderSize];
|
||||||
int header_size = 0;
|
int header_size = 0;
|
||||||
|
26
third_party/jpeg-xl/lib/extras/enc/pnm.cc
vendored
26
third_party/jpeg-xl/lib/extras/enc/pnm.cc
vendored
@ -35,14 +35,23 @@ constexpr size_t kMaxHeaderSize = 200;
|
|||||||
Status EncodeHeader(const PackedPixelFile& ppf, const size_t bits_per_sample,
|
Status EncodeHeader(const PackedPixelFile& ppf, const size_t bits_per_sample,
|
||||||
const bool little_endian, char* header,
|
const bool little_endian, char* header,
|
||||||
int* JXL_RESTRICT chars_written) {
|
int* JXL_RESTRICT chars_written) {
|
||||||
if (ppf.info.alpha_bits > 0) return JXL_FAILURE("PNM: can't store alpha");
|
|
||||||
bool is_gray = ppf.info.num_color_channels <= 2;
|
bool is_gray = ppf.info.num_color_channels <= 2;
|
||||||
size_t oriented_xsize =
|
size_t oriented_xsize =
|
||||||
ppf.info.orientation <= 4 ? ppf.info.xsize : ppf.info.ysize;
|
ppf.info.orientation <= 4 ? ppf.info.xsize : ppf.info.ysize;
|
||||||
size_t oriented_ysize =
|
size_t oriented_ysize =
|
||||||
ppf.info.orientation <= 4 ? ppf.info.ysize : ppf.info.xsize;
|
ppf.info.orientation <= 4 ? ppf.info.ysize : ppf.info.xsize;
|
||||||
|
if (ppf.info.alpha_bits > 0) { // PAM
|
||||||
if (bits_per_sample == 32) { // PFM
|
if (bits_per_sample > 16) return JXL_FAILURE("PNM cannot have > 16 bits");
|
||||||
|
const uint32_t max_val = (1U << bits_per_sample) - 1;
|
||||||
|
*chars_written =
|
||||||
|
snprintf(header, kMaxHeaderSize,
|
||||||
|
"P7\nWIDTH %" PRIuS "\nHEIGHT %" PRIuS
|
||||||
|
"\nDEPTH %u\nMAXVAL %u\nTUPLTYPE %s\nENDHDR\n",
|
||||||
|
oriented_xsize, oriented_ysize, is_gray ? 2 : 4, max_val,
|
||||||
|
is_gray ? "GRAYSCALE_ALPHA" : "RGB_ALPHA");
|
||||||
|
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
|
||||||
|
kMaxHeaderSize);
|
||||||
|
} else if (bits_per_sample == 32) { // PFM
|
||||||
const char type = is_gray ? 'f' : 'F';
|
const char type = is_gray ? 'f' : 'F';
|
||||||
const double scale = little_endian ? -1.0 : 1.0;
|
const double scale = little_endian ? -1.0 : 1.0;
|
||||||
*chars_written =
|
*chars_written =
|
||||||
@ -50,15 +59,6 @@ Status EncodeHeader(const PackedPixelFile& ppf, const size_t bits_per_sample,
|
|||||||
type, oriented_xsize, oriented_ysize, scale);
|
type, oriented_xsize, oriented_ysize, scale);
|
||||||
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
|
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
|
||||||
kMaxHeaderSize);
|
kMaxHeaderSize);
|
||||||
} else if (bits_per_sample == 1) { // PBM
|
|
||||||
if (is_gray) {
|
|
||||||
return JXL_FAILURE("Cannot encode color as PBM");
|
|
||||||
}
|
|
||||||
*chars_written =
|
|
||||||
snprintf(header, kMaxHeaderSize, "P4\n%" PRIuS " %" PRIuS "\n",
|
|
||||||
oriented_xsize, oriented_ysize);
|
|
||||||
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
|
|
||||||
kMaxHeaderSize);
|
|
||||||
} else { // PGM/PPM
|
} else { // PGM/PPM
|
||||||
const uint32_t max_val = (1U << bits_per_sample) - 1;
|
const uint32_t max_val = (1U << bits_per_sample) - 1;
|
||||||
if (max_val >= 65536) return JXL_FAILURE("PNM cannot have > 16 bits");
|
if (max_val >= 65536) return JXL_FAILURE("PNM cannot have > 16 bits");
|
||||||
@ -100,7 +100,7 @@ Status EncodeImagePNM(const PackedPixelFile& ppf, size_t bits_per_sample,
|
|||||||
ThreadPool* pool, size_t frame_index,
|
ThreadPool* pool, size_t frame_index,
|
||||||
std::vector<uint8_t>* bytes) {
|
std::vector<uint8_t>* bytes) {
|
||||||
const bool floating_point = bits_per_sample > 16;
|
const bool floating_point = bits_per_sample > 16;
|
||||||
// Choose native for PFM; PGM/PPM require big-endian (N/A for PBM)
|
// Choose native for PFM; PGM/PPM require big-endian
|
||||||
const JxlEndianness endianness =
|
const JxlEndianness endianness =
|
||||||
floating_point ? JXL_NATIVE_ENDIAN : JXL_BIG_ENDIAN;
|
floating_point ? JXL_NATIVE_ENDIAN : JXL_BIG_ENDIAN;
|
||||||
if (!ppf.metadata.exif.empty() || !ppf.metadata.iptc.empty() ||
|
if (!ppf.metadata.exif.empty() || !ppf.metadata.iptc.empty() ||
|
||||||
|
24
third_party/jpeg-xl/lib/extras/packed_image.h
vendored
24
third_party/jpeg-xl/lib/extras/packed_image.h
vendored
@ -64,6 +64,12 @@ class PackedImage {
|
|||||||
// Whether the y coordinate is flipped (y=0 is the last row).
|
// Whether the y coordinate is flipped (y=0 is the last row).
|
||||||
bool flipped_y = false;
|
bool flipped_y = false;
|
||||||
|
|
||||||
|
// Whether the range is determined by format or by JxlBasicInfo
|
||||||
|
// e.g. if format is UINT16 and JxlBasicInfo bits_per_sample is 10,
|
||||||
|
// then if bitdepth_from_format == true, the range is 0..65535
|
||||||
|
// while if bitdepth_from_format == false, the range is 0..1023.
|
||||||
|
bool bitdepth_from_format = true;
|
||||||
|
|
||||||
// The number of bytes per row.
|
// The number of bytes per row.
|
||||||
size_t stride;
|
size_t stride;
|
||||||
|
|
||||||
@ -73,21 +79,17 @@ class PackedImage {
|
|||||||
|
|
||||||
static size_t BitsPerChannel(JxlDataType data_type) {
|
static size_t BitsPerChannel(JxlDataType data_type) {
|
||||||
switch (data_type) {
|
switch (data_type) {
|
||||||
case JXL_TYPE_BOOLEAN:
|
|
||||||
return 1;
|
|
||||||
case JXL_TYPE_UINT8:
|
case JXL_TYPE_UINT8:
|
||||||
return 8;
|
return 8;
|
||||||
case JXL_TYPE_UINT16:
|
case JXL_TYPE_UINT16:
|
||||||
return 16;
|
return 16;
|
||||||
case JXL_TYPE_UINT32:
|
|
||||||
return 32;
|
|
||||||
case JXL_TYPE_FLOAT:
|
case JXL_TYPE_FLOAT:
|
||||||
return 32;
|
return 32;
|
||||||
case JXL_TYPE_FLOAT16:
|
case JXL_TYPE_FLOAT16:
|
||||||
return 16;
|
return 16;
|
||||||
// No default, give compiler error if new type not handled.
|
default:
|
||||||
|
JXL_ABORT("Unhandled JxlDataType");
|
||||||
}
|
}
|
||||||
return 0; // Indicate invalid data type.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -116,16 +118,6 @@ class PackedFrame {
|
|||||||
JxlFrameHeader frame_info = {};
|
JxlFrameHeader frame_info = {};
|
||||||
std::string name;
|
std::string name;
|
||||||
|
|
||||||
// Offset of the frame in the image.
|
|
||||||
// TODO(deymo): Add support in the API for this.
|
|
||||||
size_t x0 = 0;
|
|
||||||
size_t y0 = 0;
|
|
||||||
|
|
||||||
// Whether this frame should be blended with the previous one.
|
|
||||||
// TODO(deymo): Maybe add support for this in the API.
|
|
||||||
bool blend = false;
|
|
||||||
bool use_for_next_frame = false;
|
|
||||||
|
|
||||||
// The pixel data for the color (or grayscale) channels.
|
// The pixel data for the color (or grayscale) channels.
|
||||||
PackedImage color;
|
PackedImage color;
|
||||||
// Extra channel image data.
|
// Extra channel image data.
|
||||||
|
@ -65,21 +65,25 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
|
|||||||
if (!io->metadata.m.color_encoding.SetICC(std::move(icc))) {
|
if (!io->metadata.m.color_encoding.SetICC(std::move(icc))) {
|
||||||
fprintf(stderr, "Warning: error setting ICC profile, assuming SRGB");
|
fprintf(stderr, "Warning: error setting ICC profile, assuming SRGB");
|
||||||
io->metadata.m.color_encoding = ColorEncoding::SRGB(is_gray);
|
io->metadata.m.color_encoding = ColorEncoding::SRGB(is_gray);
|
||||||
|
} else {
|
||||||
|
if (io->metadata.m.color_encoding.IsGray() != is_gray) {
|
||||||
|
// E.g. JPG image has 3 channels, but gray ICC.
|
||||||
|
return JXL_FAILURE("Embedded ICC does not match image color type");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
JXL_RETURN_IF_ERROR(ConvertExternalToInternalColorEncoding(
|
JXL_RETURN_IF_ERROR(ConvertExternalToInternalColorEncoding(
|
||||||
ppf.color_encoding, &io->metadata.m.color_encoding));
|
ppf.color_encoding, &io->metadata.m.color_encoding));
|
||||||
|
if (io->metadata.m.color_encoding.ICC().empty()) {
|
||||||
|
return JXL_FAILURE("Failed to serialize ICC");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the extra blobs
|
// Convert the extra blobs
|
||||||
io->blobs.exif.clear();
|
io->blobs.exif = ppf.metadata.exif;
|
||||||
io->blobs.exif.append(ppf.metadata.exif);
|
io->blobs.iptc = ppf.metadata.iptc;
|
||||||
io->blobs.iptc.clear();
|
io->blobs.jumbf = ppf.metadata.jumbf;
|
||||||
io->blobs.iptc.append(ppf.metadata.iptc);
|
io->blobs.xmp = ppf.metadata.xmp;
|
||||||
io->blobs.jumbf.clear();
|
|
||||||
io->blobs.jumbf.append(ppf.metadata.jumbf);
|
|
||||||
io->blobs.xmp.clear();
|
|
||||||
io->blobs.xmp.append(ppf.metadata.xmp);
|
|
||||||
|
|
||||||
// Append all other extra channels.
|
// Append all other extra channels.
|
||||||
for (const PackedPixelFile::PackedExtraChannel& info :
|
for (const PackedPixelFile::PackedExtraChannel& info :
|
||||||
@ -107,7 +111,9 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
|
|||||||
for (const auto& frame : ppf.frames) {
|
for (const auto& frame : ppf.frames) {
|
||||||
JXL_ASSERT(frame.color.pixels() != nullptr);
|
JXL_ASSERT(frame.color.pixels() != nullptr);
|
||||||
size_t frame_bits_per_sample =
|
size_t frame_bits_per_sample =
|
||||||
frame.color.BitsPerChannel(frame.color.format.data_type);
|
(frame.color.bitdepth_from_format
|
||||||
|
? frame.color.BitsPerChannel(frame.color.format.data_type)
|
||||||
|
: ppf.info.bits_per_sample);
|
||||||
JXL_ASSERT(frame_bits_per_sample != 0);
|
JXL_ASSERT(frame_bits_per_sample != 0);
|
||||||
// It is ok for the frame.color.format.num_channels to not match the
|
// It is ok for the frame.color.format.num_channels to not match the
|
||||||
// number of channels on the image.
|
// number of channels on the image.
|
||||||
@ -117,20 +123,21 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
|
|||||||
const Span<const uint8_t> span(
|
const Span<const uint8_t> span(
|
||||||
static_cast<const uint8_t*>(frame.color.pixels()),
|
static_cast<const uint8_t*>(frame.color.pixels()),
|
||||||
frame.color.pixels_size);
|
frame.color.pixels_size);
|
||||||
Rect frame_rect =
|
Rect frame_rect = Rect(frame.frame_info.layer_info.crop_x0,
|
||||||
Rect(frame.x0, frame.y0, frame.color.xsize, frame.color.ysize);
|
frame.frame_info.layer_info.crop_y0,
|
||||||
|
frame.frame_info.layer_info.xsize,
|
||||||
|
frame.frame_info.layer_info.ysize);
|
||||||
JXL_ASSERT(frame_rect.IsInside(Rect(0, 0, ppf.info.xsize, ppf.info.ysize)));
|
JXL_ASSERT(frame_rect.IsInside(Rect(0, 0, ppf.info.xsize, ppf.info.ysize)));
|
||||||
|
|
||||||
ImageBundle bundle(&io->metadata.m);
|
ImageBundle bundle(&io->metadata.m);
|
||||||
if (ppf.info.have_animation) {
|
if (ppf.info.have_animation) {
|
||||||
bundle.duration = frame.frame_info.duration;
|
bundle.duration = frame.frame_info.duration;
|
||||||
bundle.blend = frame.blend;
|
bundle.blend = frame.frame_info.layer_info.blend_info.blendmode > 0;
|
||||||
bundle.use_for_next_frame = frame.use_for_next_frame;
|
bundle.use_for_next_frame =
|
||||||
|
frame.frame_info.layer_info.save_as_reference > 0;
|
||||||
|
bundle.origin.x0 = frame.frame_info.layer_info.crop_x0;
|
||||||
|
bundle.origin.y0 = frame.frame_info.layer_info.crop_y0;
|
||||||
}
|
}
|
||||||
bundle.name = frame.name; // frame.frame_info.name_length is ignored here.
|
bundle.name = frame.name; // frame.frame_info.name_length is ignored here.
|
||||||
bundle.origin.x0 = frame.x0;
|
|
||||||
bundle.origin.y0 = frame.y0;
|
|
||||||
|
|
||||||
JXL_ASSERT(io->metadata.m.color_encoding.IsGray() ==
|
JXL_ASSERT(io->metadata.m.color_encoding.IsGray() ==
|
||||||
(frame.color.format.num_channels <= 2));
|
(frame.color.format.num_channels <= 2));
|
||||||
|
|
||||||
@ -144,11 +151,13 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
|
|||||||
/*flipped_y=*/frame.color.flipped_y, pool, &bundle,
|
/*flipped_y=*/frame.color.flipped_y, pool, &bundle,
|
||||||
/*float_in=*/float_in, /*align=*/0));
|
/*float_in=*/float_in, /*align=*/0));
|
||||||
|
|
||||||
for (const auto& ppf_ec : frame.extra_channels) {
|
bundle.extra_channels().resize(io->metadata.m.extra_channel_info.size());
|
||||||
bundle.extra_channels().emplace_back(ppf_ec.xsize, ppf_ec.ysize);
|
for (size_t i = 0; i < frame.extra_channels.size(); i++) {
|
||||||
|
const auto& ppf_ec = frame.extra_channels[i];
|
||||||
|
bundle.extra_channels()[i] = ImageF(ppf_ec.xsize, ppf_ec.ysize);
|
||||||
JXL_CHECK(BufferToImageF(ppf_ec.format, ppf_ec.xsize, ppf_ec.ysize,
|
JXL_CHECK(BufferToImageF(ppf_ec.format, ppf_ec.xsize, ppf_ec.ysize,
|
||||||
ppf_ec.pixels(), ppf_ec.pixels_size, pool,
|
ppf_ec.pixels(), ppf_ec.pixels_size, pool,
|
||||||
&bundle.extra_channels().back()));
|
&bundle.extra_channels()[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
io->frames.push_back(std::move(bundle));
|
io->frames.push_back(std::move(bundle));
|
||||||
@ -218,10 +227,10 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Convert the extra blobs
|
// Convert the extra blobs
|
||||||
ppf->metadata.exif.assign(io.blobs.exif.begin(), io.blobs.exif.end());
|
ppf->metadata.exif = io.blobs.exif;
|
||||||
ppf->metadata.iptc.assign(io.blobs.iptc.begin(), io.blobs.iptc.end());
|
ppf->metadata.iptc = io.blobs.iptc;
|
||||||
ppf->metadata.jumbf.assign(io.blobs.jumbf.begin(), io.blobs.jumbf.end());
|
ppf->metadata.jumbf = io.blobs.jumbf;
|
||||||
ppf->metadata.xmp.assign(io.blobs.xmp.begin(), io.blobs.xmp.end());
|
ppf->metadata.xmp = io.blobs.xmp;
|
||||||
const bool float_out = pixel_format.data_type == JXL_TYPE_FLOAT ||
|
const bool float_out = pixel_format.data_type == JXL_TYPE_FLOAT ||
|
||||||
pixel_format.data_type == JXL_TYPE_FLOAT16;
|
pixel_format.data_type == JXL_TYPE_FLOAT16;
|
||||||
// Convert the pixels
|
// Convert the pixels
|
||||||
@ -240,8 +249,11 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
|
|||||||
|
|
||||||
PackedFrame packed_frame(frame.oriented_xsize(), frame.oriented_ysize(),
|
PackedFrame packed_frame(frame.oriented_xsize(), frame.oriented_ysize(),
|
||||||
format);
|
format);
|
||||||
|
packed_frame.color.bitdepth_from_format = float_out;
|
||||||
const size_t bits_per_sample =
|
const size_t bits_per_sample =
|
||||||
packed_frame.color.BitsPerChannel(pixel_format.data_type);
|
packed_frame.color.bitdepth_from_format
|
||||||
|
? packed_frame.color.BitsPerChannel(pixel_format.data_type)
|
||||||
|
: ppf->info.bits_per_sample;
|
||||||
packed_frame.name = frame.name;
|
packed_frame.name = frame.name;
|
||||||
packed_frame.frame_info.name_length = frame.name.size();
|
packed_frame.frame_info.name_length = frame.name.size();
|
||||||
// Color transform
|
// Color transform
|
||||||
@ -264,8 +276,7 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
|
|||||||
format.endianness,
|
format.endianness,
|
||||||
/* stride_out=*/packed_frame.color.stride, pool,
|
/* stride_out=*/packed_frame.color.stride, pool,
|
||||||
packed_frame.color.pixels(), packed_frame.color.pixels_size,
|
packed_frame.color.pixels(), packed_frame.color.pixels_size,
|
||||||
/*out_callback=*/nullptr, /*out_opaque=*/nullptr,
|
/*out_callback=*/{}, frame.metadata()->GetOrientation()));
|
||||||
frame.metadata()->GetOrientation()));
|
|
||||||
|
|
||||||
// TODO(firsching): Convert the extra channels, beside one potential alpha
|
// TODO(firsching): Convert the extra channels, beside one potential alpha
|
||||||
// channel. FIXME!
|
// channel. FIXME!
|
||||||
|
@ -14,7 +14,7 @@ namespace jxl {
|
|||||||
static void BM_ToneMapping(benchmark::State& state) {
|
static void BM_ToneMapping(benchmark::State& state) {
|
||||||
CodecInOut image;
|
CodecInOut image;
|
||||||
const PaddedBytes image_bytes =
|
const PaddedBytes image_bytes =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
JXL_CHECK(SetFromBytes(Span<const uint8_t>(image_bytes), &image));
|
JXL_CHECK(SetFromBytes(Span<const uint8_t>(image_bytes), &image));
|
||||||
|
|
||||||
// Convert to linear Rec. 2020 so that `ToneMapTo` doesn't have to and we
|
// Convert to linear Rec. 2020 so that `ToneMapTo` doesn't have to and we
|
||||||
|
132
third_party/jpeg-xl/lib/include/jxl/decode.h
vendored
132
third_party/jpeg-xl/lib/include/jxl/decode.h
vendored
@ -917,6 +917,56 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetImageOutBuffer(
|
|||||||
typedef void (*JxlImageOutCallback)(void* opaque, size_t x, size_t y,
|
typedef void (*JxlImageOutCallback)(void* opaque, size_t x, size_t y,
|
||||||
size_t num_pixels, const void* pixels);
|
size_t num_pixels, const void* pixels);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initialization callback for @c JxlDecoderSetMultithreadedImageOutCallback.
|
||||||
|
*
|
||||||
|
* @see JxlDecoderSetMultithreadedImageOutCallback
|
||||||
|
*
|
||||||
|
* @param init_opaque optional user data, as given to
|
||||||
|
* @c JxlDecoderSetMultithreadedImageOutCallback.
|
||||||
|
* @param num_threads maximum number of threads that will call the @c run
|
||||||
|
* callback concurrently.
|
||||||
|
* @param num_pixels_per_thread maximum number of pixels that will be passed in
|
||||||
|
* one call to @c run.
|
||||||
|
* @return a pointer to data that will be passed to the @c run callback, or
|
||||||
|
* @c NULL if initialization failed.
|
||||||
|
*/
|
||||||
|
typedef void* (*JxlImageOutInitCallback)(void* init_opaque, size_t num_threads,
|
||||||
|
size_t num_pixels_per_thread);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Worker callback for @c JxlDecoderSetMultithreadedImageOutCallback.
|
||||||
|
*
|
||||||
|
* @see JxlDecoderSetMultithreadedImageOutCallback
|
||||||
|
*
|
||||||
|
* @param run_opaque user data returned by the @c init callback.
|
||||||
|
* @param thread_id number in `[0, num_threads)` identifying the thread of the
|
||||||
|
* current invocation of the callback.
|
||||||
|
* @param x horizontal position of the first (leftmost) pixel of the pixel data.
|
||||||
|
* @param y vertical position of the pixel data.
|
||||||
|
* @param num_pixels number of pixels in the pixel data. May be less than the
|
||||||
|
* full @c xsize of the image, and will be at most equal to the @c
|
||||||
|
* num_pixels_per_thread that was passed to @c init.
|
||||||
|
* @param pixels pixel data as a horizontal stripe, in the format passed to @c
|
||||||
|
* JxlDecoderSetMultithreadedImageOutCallback. The data pointed to remains owned
|
||||||
|
* by the caller and is only guaranteed to outlive the current callback
|
||||||
|
* invocation.
|
||||||
|
*/
|
||||||
|
typedef void (*JxlImageOutRunCallback)(void* run_opaque, size_t thread_id,
|
||||||
|
size_t x, size_t y, size_t num_pixels,
|
||||||
|
const void* pixels);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destruction callback for @c JxlDecoderSetMultithreadedImageOutCallback,
|
||||||
|
* called after all invocations of the @c run callback to perform any
|
||||||
|
* appropriate clean-up of the @c run_opaque data returned by @c init.
|
||||||
|
*
|
||||||
|
* @see JxlDecoderSetMultithreadedImageOutCallback
|
||||||
|
*
|
||||||
|
* @param run_opaque user data returned by the @c init callback.
|
||||||
|
*/
|
||||||
|
typedef void (*JxlImageOutDestroyCallback)(void* run_opaque);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets pixel output callback. This is an alternative to
|
* Sets pixel output callback. This is an alternative to
|
||||||
* JxlDecoderSetImageOutBuffer. This can be set when the JXL_DEC_FRAME event
|
* JxlDecoderSetImageOutBuffer. This can be set when the JXL_DEC_FRAME event
|
||||||
@ -950,8 +1000,8 @@ typedef void (*JxlImageOutCallback)(void* opaque, size_t x, size_t y,
|
|||||||
* guaranteed).
|
* guaranteed).
|
||||||
*
|
*
|
||||||
* @param dec decoder object
|
* @param dec decoder object
|
||||||
* @param format format of the pixels. Object owned by user and its contents
|
* @param format format of the pixels. Object owned by user; its contents are
|
||||||
* are copied internally.
|
* copied internally.
|
||||||
* @param callback the callback function receiving partial scanlines of pixel
|
* @param callback the callback function receiving partial scanlines of pixel
|
||||||
* data.
|
* data.
|
||||||
* @param opaque optional user data, which will be passed on to the callback,
|
* @param opaque optional user data, which will be passed on to the callback,
|
||||||
@ -963,6 +1013,30 @@ JXL_EXPORT JxlDecoderStatus
|
|||||||
JxlDecoderSetImageOutCallback(JxlDecoder* dec, const JxlPixelFormat* format,
|
JxlDecoderSetImageOutCallback(JxlDecoder* dec, const JxlPixelFormat* format,
|
||||||
JxlImageOutCallback callback, void* opaque);
|
JxlImageOutCallback callback, void* opaque);
|
||||||
|
|
||||||
|
/** Similar to @c JxlDecoderSetImageOutCallback except that the callback is
|
||||||
|
* allowed an initialization phase during which it is informed of how many
|
||||||
|
* threads will call it concurrently, and those calls are further informed of
|
||||||
|
* which thread they are occurring in.
|
||||||
|
*
|
||||||
|
* @param dec decoder object
|
||||||
|
* @param format format of the pixels. Object owned by user; its contents are
|
||||||
|
* copied internally.
|
||||||
|
* @param init_callback initialization callback.
|
||||||
|
* @param run_callback the callback function receiving partial scanlines of
|
||||||
|
* pixel data.
|
||||||
|
* @param destroy_callback clean-up callback invoked after all calls to @c
|
||||||
|
* run_callback. May be NULL if no clean-up is necessary.
|
||||||
|
* @param init_opaque optional user data passed to @c init_callback, may be NULL
|
||||||
|
* (unlike the return value from @c init_callback which may only be NULL if
|
||||||
|
* initialization failed).
|
||||||
|
* @return JXL_DEC_SUCCESS on success, JXL_DEC_ERROR on error, such as @c
|
||||||
|
* JxlDecoderSetImageOutBuffer having already been called.
|
||||||
|
*/
|
||||||
|
JXL_EXPORT JxlDecoderStatus JxlDecoderSetMultithreadedImageOutCallback(
|
||||||
|
JxlDecoder* dec, const JxlPixelFormat* format,
|
||||||
|
JxlImageOutInitCallback init_callback, JxlImageOutRunCallback run_callback,
|
||||||
|
JxlImageOutDestroyCallback destroy_callback, void* init_opaque);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the minimum size in bytes of an extra channel pixel buffer for the
|
* Returns the minimum size in bytes of an extra channel pixel buffer for the
|
||||||
* given format. This is the buffer for JxlDecoderSetExtraChannelBuffer.
|
* given format. This is the buffer for JxlDecoderSetExtraChannelBuffer.
|
||||||
@ -1123,10 +1197,60 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec,
|
|||||||
* box, this will return "brob" if the decompressed argument is JXL_FALSE, or
|
* box, this will return "brob" if the decompressed argument is JXL_FALSE, or
|
||||||
* the underlying box type if the decompressed argument is JXL_TRUE.
|
* the underlying box type if the decompressed argument is JXL_TRUE.
|
||||||
*
|
*
|
||||||
|
* The following box types are currently described in ISO/IEC 18181-2:
|
||||||
|
* - "Exif": a box with EXIF metadata. Starts with a 4-byte tiff header
|
||||||
|
* offset (big-endian uint32) that indicates the start of the actual EXIF data
|
||||||
|
* (which starts with a tiff header). Usually the offset will be zero and the
|
||||||
|
* EXIF data starts immediately after the offset field. The Exif orientation
|
||||||
|
* should be ignored by applications; the JPEG XL codestream orientation takes
|
||||||
|
* precedence and libjxl will by default apply the correct orientation
|
||||||
|
* automatically (see JxlDecoderSetKeepOrientation).
|
||||||
|
* - "xml ": a box with XML data, in particular XMP metadata.
|
||||||
|
* - "jumb": a JUMBF superbox (JPEG Universal Metadata Box Format, ISO/IEC
|
||||||
|
* 19566-5).
|
||||||
|
* - "JXL ": mandatory signature box, must come first, 12 bytes long including
|
||||||
|
* the box header
|
||||||
|
* - "ftyp": a second mandatory signature box, must come second, 20 bytes long
|
||||||
|
* including the box header
|
||||||
|
* - "jxll": a JXL level box. This indicates if the codestream is level 5 or
|
||||||
|
* level 10 compatible. If not present, it is level 5. Level 10 allows more
|
||||||
|
* features such as very high image resolution and bit-depths above 16 bits
|
||||||
|
* per channel. Added automatically by the encoder when
|
||||||
|
* JxlEncoderSetCodestreamLevel is used
|
||||||
|
* - "jxlc": a box with the image codestream, in case the codestream is not
|
||||||
|
* split across multiple boxes. The codestream contains the JPEG XL image
|
||||||
|
* itself, including the basic info such as image dimensions, ICC color
|
||||||
|
* profile, and all the pixel data of all the image frames.
|
||||||
|
* - "jxlp": a codestream box in case it is split across multiple boxes.
|
||||||
|
* The contents are the same as in case of a jxlc box, when concatenated.
|
||||||
|
* - "brob": a Brotli-compressed box, which otherwise represents an existing
|
||||||
|
* type of box such as Exif or "xml ". When JxlDecoderSetDecompressBoxes is
|
||||||
|
* set to JXL_TRUE, these boxes will be transparently decompressed by the
|
||||||
|
* decoder.
|
||||||
|
* - "jxli": frame index box, can list the keyframes in case of a JXL animation,
|
||||||
|
* allowing the decoder to jump to individual frames more efficiently.
|
||||||
|
* - "jbrd": JPEG reconstruction box, contains the information required to
|
||||||
|
* byte-for-byte losslessly recontruct a JPEG-1 image. The JPEG DCT
|
||||||
|
* coefficients (pixel content) themselves as well as the ICC profile are
|
||||||
|
* encoded in the JXL codestream (jxlc or jxlp) itself. EXIF, XMP and JUMBF
|
||||||
|
* metadata is encoded in the corresponding boxes. The jbrd box itself
|
||||||
|
* contains information such as the remaining app markers of the JPEG-1 file
|
||||||
|
* and everything else required to fit the information together into the
|
||||||
|
* exact original JPEG file.
|
||||||
|
*
|
||||||
|
* Other application-specific boxes can exist. Their typename should not begin
|
||||||
|
* with "jxl" or "JXL" or conflict with other existing typenames.
|
||||||
|
*
|
||||||
|
* The signature, jxl* and jbrd boxes are processed by the decoder and would
|
||||||
|
* typically be ignored by applications. The typical way to use this function is
|
||||||
|
* to check if an encountered box contains metadata that the application is
|
||||||
|
* interested in (e.g. EXIF or XMP metadata), in order to conditionally set a
|
||||||
|
* box buffer.
|
||||||
|
*
|
||||||
* @param dec decoder object
|
* @param dec decoder object
|
||||||
* @param type buffer to copy the type into
|
* @param type buffer to copy the type into
|
||||||
* @param decompressed which box type to get: JXL_TRUE to get the raw box type,
|
* @param decompressed which box type to get: JXL_FALSE to get the raw box type,
|
||||||
* which can be "brob", JXL_FALSE, get the underlying box type.
|
* which can be "brob", JXL_TRUE, get the underlying box type.
|
||||||
* @return JXL_DEC_SUCCESS if the value is available, JXL_DEC_ERROR if not, for
|
* @return JXL_DEC_SUCCESS if the value is available, JXL_DEC_ERROR if not, for
|
||||||
* example the JXL file does not use the container format.
|
* example the JXL file does not use the container format.
|
||||||
*/
|
*/
|
||||||
|
107
third_party/jpeg-xl/lib/include/jxl/encode.h
vendored
107
third_party/jpeg-xl/lib/include/jxl/encode.h
vendored
@ -283,6 +283,13 @@ typedef enum {
|
|||||||
*/
|
*/
|
||||||
JXL_ENC_FRAME_INDEX_BOX = 31,
|
JXL_ENC_FRAME_INDEX_BOX = 31,
|
||||||
|
|
||||||
|
/** Sets brotli encode effort for use in JPEG recompression and compressed
|
||||||
|
* metadata boxes (brob). Can be -1 (default) or 0 (fastest) to 11 (slowest).
|
||||||
|
* Default is based on the general encode effort in case of JPEG
|
||||||
|
* recompression, and 4 for brob boxes.
|
||||||
|
*/
|
||||||
|
JXL_ENC_FRAME_SETTING_BROTLI_EFFORT = 32,
|
||||||
|
|
||||||
/** Enum value not to be used as an option. This value is added to force the
|
/** Enum value not to be used as an option. This value is added to force the
|
||||||
* C compiler to have the enum to take a known size.
|
* C compiler to have the enum to take a known size.
|
||||||
*/
|
*/
|
||||||
@ -509,6 +516,8 @@ JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings,
|
|||||||
*
|
*
|
||||||
* Extra channels not handled here need to be set by @ref
|
* Extra channels not handled here need to be set by @ref
|
||||||
* JxlEncoderSetExtraChannelBuffer.
|
* JxlEncoderSetExtraChannelBuffer.
|
||||||
|
* If the image has alpha, and alpha is not passed here, it will implicitly be
|
||||||
|
* set to all-opaque (an alpha value of 1.0 everywhere).
|
||||||
*
|
*
|
||||||
* The color profile of the pixels depends on the value of uses_original_profile
|
* The color profile of the pixels depends on the value of uses_original_profile
|
||||||
* in the JxlBasicInfo. If true, the pixels are assumed to be encoded in the
|
* in the JxlBasicInfo. If true, the pixels are assumed to be encoded in the
|
||||||
@ -571,10 +580,8 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer(
|
|||||||
* to effectively write the box to the output. @ref JxlEncoderUseBoxes must
|
* to effectively write the box to the output. @ref JxlEncoderUseBoxes must
|
||||||
* be enabled before using this function.
|
* be enabled before using this function.
|
||||||
*
|
*
|
||||||
* Background information about the container format and boxes follows here:
|
* Boxes allow inserting application-specific data and metadata (Exif, XML/XMP,
|
||||||
*
|
* JUMBF and user defined boxes).
|
||||||
* For users of libjxl, boxes allow inserting application-specific data and
|
|
||||||
* metadata (Exif, XML, JUMBF and user defined boxes).
|
|
||||||
*
|
*
|
||||||
* The box format follows ISO BMFF and shares features and box types with other
|
* The box format follows ISO BMFF and shares features and box types with other
|
||||||
* image and video formats, including the Exif, XML and JUMBF boxes. The box
|
* image and video formats, including the Exif, XML and JUMBF boxes. The box
|
||||||
@ -582,7 +589,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer(
|
|||||||
*
|
*
|
||||||
* Boxes in general don't contain other boxes inside, except a JUMBF superbox.
|
* Boxes in general don't contain other boxes inside, except a JUMBF superbox.
|
||||||
* Boxes follow each other sequentially and are byte-aligned. If the container
|
* Boxes follow each other sequentially and are byte-aligned. If the container
|
||||||
* format is used, the JXL stream exists out of 3 or more concatenated boxes.
|
* format is used, the JXL stream consists of concatenated boxes.
|
||||||
* It is also possible to use a direct codestream without boxes, but in that
|
* It is also possible to use a direct codestream without boxes, but in that
|
||||||
* case metadata cannot be added.
|
* case metadata cannot be added.
|
||||||
*
|
*
|
||||||
@ -594,80 +601,42 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelBuffer(
|
|||||||
* - N bytes: box contents.
|
* - N bytes: box contents.
|
||||||
*
|
*
|
||||||
* Only the box contents are provided to the contents argument of this function,
|
* Only the box contents are provided to the contents argument of this function,
|
||||||
* the encoder encodes the size header itself.
|
* the encoder encodes the size header itself. Most boxes are written
|
||||||
|
* automatically by the encoder as needed ("JXL ", "ftyp", "jxll", "jxlc",
|
||||||
|
* "jxlp", "jxli", "jbrd"), and this function only needs to be called to add
|
||||||
|
* optional metadata when encoding from pixels (using JxlEncoderAddImageFrame).
|
||||||
|
* When recompressing JPEG files (using JxlEncoderAddJPEGFrame), if the input
|
||||||
|
* JPEG contains EXIF, XMP or JUMBF metadata, the corresponding boxes are
|
||||||
|
* already added automatically.
|
||||||
*
|
*
|
||||||
* Box types are given by 4 characters. A list of known types follows:
|
* Box types are given by 4 characters. The following boxes can be added with
|
||||||
* - "JXL ": mandatory signature box, must come first, 12 bytes long including
|
* this function:
|
||||||
* the box header
|
|
||||||
* - "ftyp": a second mandatory signature box, must come second, 20 bytes long
|
|
||||||
* including the box header
|
|
||||||
* - "jxll": A JXL level box. This indicates if the codestream is level 5 or
|
|
||||||
* level 10 compatible. If not present, it is level 5. Level 10 allows more
|
|
||||||
* features such as very high image resolution and bit-depths above 16 bits
|
|
||||||
* per channel. Added automatically by the encoder when
|
|
||||||
* JxlEncoderSetCodestreamLevel is used
|
|
||||||
* - "jxlc": a box with the image codestream, in case the codestream is not
|
|
||||||
* split across multiple boxes. The codestream contains the JPEG XL image
|
|
||||||
* itself, including the basic info such as image dimensions, ICC color
|
|
||||||
* profile, and all the pixel data of all the image frames.
|
|
||||||
* - "jxlp": a codestream box in case it is split across multiple boxes. The
|
|
||||||
* encoder will automatically do this if necessary. The contents are the same
|
|
||||||
* as in case of a jxlc box, when concatenated.
|
|
||||||
* - "Exif": a box with EXIF metadata, can be added by libjxl users, or is
|
* - "Exif": a box with EXIF metadata, can be added by libjxl users, or is
|
||||||
* automatically added when needed for JPEG reconstruction. The contents of
|
* automatically added when needed for JPEG reconstruction. The contents of
|
||||||
* this box must be prepended by a 4-byte tiff header offset, which may
|
* this box must be prepended by a 4-byte tiff header offset, which may
|
||||||
* be 4 zero bytes.
|
* be 4 zero bytes in case the tiff header follows immediately.
|
||||||
* - "XML ": a box with XMP or IPTC metadata, can be added by libjxl users, or
|
* The EXIF metadata must be in sync with what is encoded in the JPEG XL
|
||||||
* is automatically added when needed for JPEG reconstruction
|
* codestream, specifically the image orientation. While this is not
|
||||||
|
* recommended in practice, in case of conflicting metadata, the JPEG XL
|
||||||
|
* codestream takes precedence.
|
||||||
|
* - "xml ": a box with XML data, in particular XMP metadata, can be added by
|
||||||
|
* libjxl users, or is automatically added when needed for JPEG reconstruction
|
||||||
* - "jumb": a JUMBF superbox, which can contain boxes with different types of
|
* - "jumb": a JUMBF superbox, which can contain boxes with different types of
|
||||||
* metadata inside. This box type can be added by the encoder transparently,
|
* metadata inside. This box type can be added by the encoder transparently,
|
||||||
* and other libraries to create and handle JUMBF content exist.
|
* and other libraries to create and handle JUMBF content exist.
|
||||||
* - "brob": a Brotli-compressed box, which otherwise represents an existing
|
* - Application-specific boxes. Their typename should not begin with "jxl" or
|
||||||
* type of box such as Exif or XML. The encoder creates these when enabled and
|
* "JXL" or conflict with other existing typenames, and they should be
|
||||||
* users of libjxl don't need to create them directly. Some box types are not
|
* registered with MP4RA (mp4ra.org).
|
||||||
* allowed to be compressed: any of the signature, jxl* and jbrd boxes.
|
|
||||||
* - "jxli": frame index box, can list the keyframes in case of a JXL animation,
|
|
||||||
* allowing the decoder to jump to individual frames more efficiently. This
|
|
||||||
* box type is specified, but not currently supported by the encoder or
|
|
||||||
* decoder.
|
|
||||||
* - "jbrd": JPEG reconstruction box, contains the information required to
|
|
||||||
* byte-for-byte losslessly recontruct a JPEG-1 image. The JPEG coefficients
|
|
||||||
* (pixel content) themselves are encoded in the JXL codestream (jxlc or jxlp)
|
|
||||||
* itself. Exif and XMP metadata will be encoded in Exif and XMP boxes. The
|
|
||||||
* jbrd box itself contains information such as the app markers of the JPEG-1
|
|
||||||
* file and everything else required to fit the information together into the
|
|
||||||
* exact original JPEG file. This box is added automatically by the encoder
|
|
||||||
* when needed, and only when JPEG reconstruction is used.
|
|
||||||
* - other: other application-specific boxes can be added. Their typename should
|
|
||||||
* not begin with "jxl" or "JXL" or conflict with other existing typenames.
|
|
||||||
*
|
*
|
||||||
* Most boxes are automatically added by the encoder and should not be added
|
* These boxes can be stored uncompressed or Brotli-compressed (using a "brob"
|
||||||
* with JxlEncoderAddBox. Boxes that one may wish to add with JxlEncoderAddBox
|
* box), depending on the compress_box parameter.
|
||||||
* are: Exif and XML (but not when using JPEG reconstruction since if the
|
|
||||||
* JPEG has those, these boxes are already added automatically), jumb, and
|
|
||||||
* application-specific boxes.
|
|
||||||
*
|
|
||||||
* Adding metadata boxes increases the filesize. When adding Exif metadata, the
|
|
||||||
* data must be in sync with what is encoded in the JPEG XL codestream,
|
|
||||||
* specifically the image orientation. While this is not recommended in
|
|
||||||
* practice, in case of conflicting metadata, the JPEG XL codestream takes
|
|
||||||
* precedence.
|
|
||||||
*
|
|
||||||
* It is possible to create a codestream without boxes, then what would be in
|
|
||||||
* the jxlc box is written directly to the output
|
|
||||||
*
|
|
||||||
* It is possible to split the codestream across multiple boxes, in that case
|
|
||||||
* multiple boxes of type jxlp are used. This is handled by the encoder when
|
|
||||||
* needed.
|
|
||||||
*
|
*
|
||||||
* @param enc encoder object.
|
* @param enc encoder object.
|
||||||
* @param type the box type, e.g. "Exif" for EXIF metadata, "XML " for XMP or
|
* @param type the box type, e.g. "Exif" for EXIF metadata, "xml " for XMP or
|
||||||
* IPTC metadata, "jumb" for JUMBF metadata.
|
* IPTC metadata, "jumb" for JUMBF metadata.
|
||||||
* @param contents the full contents of the box, for example EXIF
|
* @param contents the full contents of the box, for example EXIF
|
||||||
* data. For an "Exif" box, the EXIF data must be prepended by a 4-byte tiff
|
* data. ISO BMFF box header must not be included, only the contents. Owned by
|
||||||
* header offset, which may be 4 zero-bytes. The ISO BMFF box header must not
|
* the caller and its contents are copied internally.
|
||||||
* be included, only the contents. Owned by the caller and its contents are
|
|
||||||
* copied internally.
|
|
||||||
* @param size size of the box contents.
|
* @param size size of the box contents.
|
||||||
* @param compress_box Whether to compress this box as a "brob" box. Requires
|
* @param compress_box Whether to compress this box as a "brob" box. Requires
|
||||||
* Brotli support.
|
* Brotli support.
|
||||||
@ -1049,7 +1018,7 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetFrameDistance(
|
|||||||
|
|
||||||
/** DEPRECATED: use JxlEncoderSetFrameDistance instead.
|
/** DEPRECATED: use JxlEncoderSetFrameDistance instead.
|
||||||
*/
|
*/
|
||||||
JXL_EXPORT JxlEncoderStatus
|
JXL_EXPORT JXL_DEPRECATED JxlEncoderStatus
|
||||||
JxlEncoderOptionsSetDistance(JxlEncoderFrameSettings*, float);
|
JxlEncoderOptionsSetDistance(JxlEncoderFrameSettings*, float);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1072,7 +1041,7 @@ JXL_EXPORT JxlEncoderFrameSettings* JxlEncoderFrameSettingsCreate(
|
|||||||
|
|
||||||
/** DEPRECATED: use JxlEncoderFrameSettingsCreate instead.
|
/** DEPRECATED: use JxlEncoderFrameSettingsCreate instead.
|
||||||
*/
|
*/
|
||||||
JXL_EXPORT JxlEncoderFrameSettings* JxlEncoderOptionsCreate(
|
JXL_EXPORT JXL_DEPRECATED JxlEncoderFrameSettings* JxlEncoderOptionsCreate(
|
||||||
JxlEncoder*, const JxlEncoderFrameSettings*);
|
JxlEncoder*, const JxlEncoderFrameSettings*);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
26
third_party/jpeg-xl/lib/include/jxl/types.h
vendored
26
third_party/jpeg-xl/lib/include/jxl/types.h
vendored
@ -16,6 +16,8 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "jxl/jxl_export.h"
|
||||||
|
|
||||||
#if defined(__cplusplus) || defined(c_plusplus)
|
#if defined(__cplusplus) || defined(c_plusplus)
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@ -41,28 +43,26 @@ typedef enum {
|
|||||||
* for HDR and wide gamut images when color profile conversion is required. */
|
* for HDR and wide gamut images when color profile conversion is required. */
|
||||||
JXL_TYPE_FLOAT = 0,
|
JXL_TYPE_FLOAT = 0,
|
||||||
|
|
||||||
/** Use 1-bit packed in uint8_t, first pixel in LSB, padded to uint8_t per
|
|
||||||
* row.
|
|
||||||
* TODO(lode): support first in MSB, other padding.
|
|
||||||
*/
|
|
||||||
JXL_TYPE_BOOLEAN,
|
|
||||||
|
|
||||||
/** Use type uint8_t. May clip wide color gamut data.
|
/** Use type uint8_t. May clip wide color gamut data.
|
||||||
*/
|
*/
|
||||||
JXL_TYPE_UINT8,
|
JXL_TYPE_UINT8 = 2,
|
||||||
|
|
||||||
/** Use type uint16_t. May clip wide color gamut data.
|
/** Use type uint16_t. May clip wide color gamut data.
|
||||||
*/
|
*/
|
||||||
JXL_TYPE_UINT16,
|
JXL_TYPE_UINT16 = 3,
|
||||||
|
|
||||||
/** Use type uint32_t. May clip wide color gamut data.
|
|
||||||
*/
|
|
||||||
JXL_TYPE_UINT32,
|
|
||||||
|
|
||||||
/** Use 16-bit IEEE 754 half-precision floating point values */
|
/** Use 16-bit IEEE 754 half-precision floating point values */
|
||||||
JXL_TYPE_FLOAT16,
|
JXL_TYPE_FLOAT16 = 5,
|
||||||
} JxlDataType;
|
} JxlDataType;
|
||||||
|
|
||||||
|
/* DEPRECATED: bit-packed 1-bit data type. Use JXL_TYPE_UINT8 instead.
|
||||||
|
*/
|
||||||
|
static const int JXL_DEPRECATED JXL_TYPE_BOOLEAN = 1;
|
||||||
|
|
||||||
|
/* DEPRECATED: uint32_t data type. Use JXL_TYPE_FLOAT instead.
|
||||||
|
*/
|
||||||
|
static const int JXL_DEPRECATED JXL_TYPE_UINT32 = 4;
|
||||||
|
|
||||||
/** Ordering of multi-byte data.
|
/** Ordering of multi-byte data.
|
||||||
*/
|
*/
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
11
third_party/jpeg-xl/lib/jxl.cmake
vendored
11
third_party/jpeg-xl/lib/jxl.cmake
vendored
@ -39,9 +39,9 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
|||||||
jxl/base/profiler.h
|
jxl/base/profiler.h
|
||||||
jxl/base/random.cc
|
jxl/base/random.cc
|
||||||
jxl/base/random.h
|
jxl/base/random.h
|
||||||
|
jxl/base/sanitizer_definitions.h
|
||||||
jxl/base/scope_guard.h
|
jxl/base/scope_guard.h
|
||||||
jxl/base/span.h
|
jxl/base/span.h
|
||||||
jxl/base/status.cc
|
|
||||||
jxl/base/status.h
|
jxl/base/status.h
|
||||||
jxl/base/thread_pool_internal.h
|
jxl/base/thread_pool_internal.h
|
||||||
jxl/blending.cc
|
jxl/blending.cc
|
||||||
@ -106,6 +106,8 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
|||||||
jxl/entropy_coder.h
|
jxl/entropy_coder.h
|
||||||
jxl/epf.cc
|
jxl/epf.cc
|
||||||
jxl/epf.h
|
jxl/epf.h
|
||||||
|
jxl/exif.cc
|
||||||
|
jxl/exif.h
|
||||||
jxl/fast_dct-inl.h
|
jxl/fast_dct-inl.h
|
||||||
jxl/fast_dct.cc
|
jxl/fast_dct.cc
|
||||||
jxl/fast_dct.h
|
jxl/fast_dct.h
|
||||||
@ -344,6 +346,8 @@ set(JPEGXL_DEC_INTERNAL_LIBS
|
|||||||
brotlidec-static
|
brotlidec-static
|
||||||
brotlicommon-static
|
brotlicommon-static
|
||||||
hwy
|
hwy
|
||||||
|
Threads::Threads
|
||||||
|
${ATOMICS_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
|
||||||
if(JPEGXL_ENABLE_PROFILER)
|
if(JPEGXL_ENABLE_PROFILER)
|
||||||
@ -353,7 +357,6 @@ endif()
|
|||||||
set(JPEGXL_INTERNAL_LIBS
|
set(JPEGXL_INTERNAL_LIBS
|
||||||
${JPEGXL_DEC_INTERNAL_LIBS}
|
${JPEGXL_DEC_INTERNAL_LIBS}
|
||||||
brotlienc-static
|
brotlienc-static
|
||||||
Threads::Threads
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# strips the -static suffix from all the elements in LIST
|
# strips the -static suffix from all the elements in LIST
|
||||||
@ -465,7 +468,7 @@ add_library(jxl_dec-static STATIC
|
|||||||
$<TARGET_OBJECTS:jxl_dec-obj>
|
$<TARGET_OBJECTS:jxl_dec-obj>
|
||||||
)
|
)
|
||||||
target_link_libraries(jxl_dec-static
|
target_link_libraries(jxl_dec-static
|
||||||
PUBLIC ${JPEGXL_COVERAGE_FLAGS} ${JPEGXL_DEC_INTERNAL_LIBS} hwy)
|
PUBLIC ${JPEGXL_COVERAGE_FLAGS} ${JPEGXL_DEC_INTERNAL_LIBS})
|
||||||
target_include_directories(jxl_dec-static PUBLIC
|
target_include_directories(jxl_dec-static PUBLIC
|
||||||
"${PROJECT_SOURCE_DIR}"
|
"${PROJECT_SOURCE_DIR}"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||||
@ -486,7 +489,7 @@ endif()
|
|||||||
# to do, remove $<TARGET_OBJECTS:jxl_dec-obj> here and depend on jxl_dec-static
|
# to do, remove $<TARGET_OBJECTS:jxl_dec-obj> here and depend on jxl_dec-static
|
||||||
add_library(jxl-static STATIC ${JPEGXL_INTERNAL_OBJECTS})
|
add_library(jxl-static STATIC ${JPEGXL_INTERNAL_OBJECTS})
|
||||||
target_link_libraries(jxl-static
|
target_link_libraries(jxl-static
|
||||||
PUBLIC ${JPEGXL_COVERAGE_FLAGS} ${JPEGXL_INTERNAL_LIBS} hwy)
|
PUBLIC ${JPEGXL_COVERAGE_FLAGS} ${JPEGXL_INTERNAL_LIBS})
|
||||||
target_include_directories(jxl-static PUBLIC
|
target_include_directories(jxl-static PUBLIC
|
||||||
"${PROJECT_SOURCE_DIR}"
|
"${PROJECT_SOURCE_DIR}"
|
||||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||||
|
2
third_party/jpeg-xl/lib/jxl/aux_out.h
vendored
2
third_party/jpeg-xl/lib/jxl/aux_out.h
vendored
@ -220,7 +220,7 @@ struct AuxOut {
|
|||||||
Image3MinMax(image, &min, &max);
|
Image3MinMax(image, &min, &max);
|
||||||
Image3B normalized(image.xsize(), image.ysize());
|
Image3B normalized(image.xsize(), image.ysize());
|
||||||
for (size_t c = 0; c < 3; ++c) {
|
for (size_t c = 0; c < 3; ++c) {
|
||||||
float mul = min[c] == max[c] ? 0 : (1.0f / (max[c] - min[c]));
|
float mul = min[c] == max[c] ? 0 : (255.0f / (max[c] - min[c]));
|
||||||
for (size_t y = 0; y < image.ysize(); ++y) {
|
for (size_t y = 0; y < image.ysize(); ++y) {
|
||||||
const T* JXL_RESTRICT row_in = image.ConstPlaneRow(c, y);
|
const T* JXL_RESTRICT row_in = image.ConstPlaneRow(c, y);
|
||||||
uint8_t* JXL_RESTRICT row_out = normalized.PlaneRow(c, y);
|
uint8_t* JXL_RESTRICT row_out = normalized.PlaneRow(c, y);
|
||||||
|
44
third_party/jpeg-xl/lib/jxl/base/sanitizer_definitions.h
vendored
Normal file
44
third_party/jpeg-xl/lib/jxl/base/sanitizer_definitions.h
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef LIB_JXL_BASE_SANITIZER_DEFINITIONS_H_
|
||||||
|
#define LIB_JXL_BASE_SANITIZER_DEFINITIONS_H_
|
||||||
|
|
||||||
|
#ifdef MEMORY_SANITIZER
|
||||||
|
#define JXL_MEMORY_SANITIZER 1
|
||||||
|
#elif defined(__has_feature)
|
||||||
|
#if __has_feature(memory_sanitizer)
|
||||||
|
#define JXL_MEMORY_SANITIZER 1
|
||||||
|
#else
|
||||||
|
#define JXL_MEMORY_SANITIZER 0
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define JXL_MEMORY_SANITIZER 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ADDRESS_SANITIZER
|
||||||
|
#define JXL_ADDRESS_SANITIZER 1
|
||||||
|
#elif defined(__has_feature)
|
||||||
|
#if __has_feature(address_sanitizer)
|
||||||
|
#define JXL_ADDRESS_SANITIZER 1
|
||||||
|
#else
|
||||||
|
#define JXL_ADDRESS_SANITIZER 0
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define JXL_ADDRESS_SANITIZER 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef THREAD_SANITIZER
|
||||||
|
#define JXL_THREAD_SANITIZER 1
|
||||||
|
#elif defined(__has_feature)
|
||||||
|
#if __has_feature(thread_sanitizer)
|
||||||
|
#define JXL_THREAD_SANITIZER 1
|
||||||
|
#else
|
||||||
|
#define JXL_THREAD_SANITIZER 0
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#define JXL_THREAD_SANITIZER 0
|
||||||
|
#endif
|
||||||
|
#endif // LIB_JXL_BASE_SANITIZER_DEFINITIONS_H
|
46
third_party/jpeg-xl/lib/jxl/base/status.cc
vendored
46
third_party/jpeg-xl/lib/jxl/base/status.cc
vendored
@ -1,46 +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/jxl/base/status.h"
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "lib/jxl/sanitizers.h"
|
|
||||||
|
|
||||||
#if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER
|
|
||||||
#include "sanitizer/common_interface_defs.h" // __sanitizer_print_stack_trace
|
|
||||||
#endif // defined(*_SANITIZER)
|
|
||||||
|
|
||||||
namespace jxl {
|
|
||||||
|
|
||||||
bool Debug(const char* format, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
vfprintf(stderr, format, args);
|
|
||||||
va_end(args);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Abort() {
|
|
||||||
#if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER
|
|
||||||
// If compiled with any sanitizer print a stack trace. This call doesn't crash
|
|
||||||
// the program, instead the trap below will crash it also allowing gdb to
|
|
||||||
// break there.
|
|
||||||
__sanitizer_print_stack_trace();
|
|
||||||
#endif // *_SANITIZER)
|
|
||||||
|
|
||||||
#if JXL_COMPILER_MSVC
|
|
||||||
__debugbreak();
|
|
||||||
abort();
|
|
||||||
#else
|
|
||||||
__builtin_trap();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jxl
|
|
29
third_party/jpeg-xl/lib/jxl/base/status.h
vendored
29
third_party/jpeg-xl/lib/jxl/base/status.h
vendored
@ -14,6 +14,11 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
|
#include "lib/jxl/base/sanitizer_definitions.h"
|
||||||
|
|
||||||
|
#if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER
|
||||||
|
#include "sanitizer/common_interface_defs.h" // __sanitizer_print_stack_trace
|
||||||
|
#endif // defined(*_SANITIZER)
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
||||||
@ -70,7 +75,13 @@ namespace jxl {
|
|||||||
// instead of calling Debug directly. This function returns false, so it can be
|
// instead of calling Debug directly. This function returns false, so it can be
|
||||||
// used as a return value in JXL_FAILURE.
|
// used as a return value in JXL_FAILURE.
|
||||||
JXL_FORMAT(1, 2)
|
JXL_FORMAT(1, 2)
|
||||||
bool Debug(const char* format, ...);
|
inline JXL_NOINLINE bool Debug(const char* format, ...) {
|
||||||
|
va_list args;
|
||||||
|
va_start(args, format);
|
||||||
|
vfprintf(stderr, format, args);
|
||||||
|
va_end(args);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Print a debug message on standard error if "enabled" is true. "enabled" is
|
// Print a debug message on standard error if "enabled" is true. "enabled" is
|
||||||
// normally a macro that evaluates to 0 or 1 at compile time, so the Debug
|
// normally a macro that evaluates to 0 or 1 at compile time, so the Debug
|
||||||
@ -113,7 +124,21 @@ bool Debug(const char* format, ...);
|
|||||||
JXL_DEBUG(JXL_DEBUG_WARNING, format, ##__VA_ARGS__)
|
JXL_DEBUG(JXL_DEBUG_WARNING, format, ##__VA_ARGS__)
|
||||||
|
|
||||||
// Exits the program after printing a stack trace when possible.
|
// Exits the program after printing a stack trace when possible.
|
||||||
JXL_NORETURN bool Abort();
|
JXL_NORETURN inline JXL_NOINLINE bool Abort() {
|
||||||
|
#if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER
|
||||||
|
// If compiled with any sanitizer print a stack trace. This call doesn't crash
|
||||||
|
// the program, instead the trap below will crash it also allowing gdb to
|
||||||
|
// break there.
|
||||||
|
__sanitizer_print_stack_trace();
|
||||||
|
#endif // *_SANITIZER)
|
||||||
|
|
||||||
|
#if JXL_COMPILER_MSVC
|
||||||
|
__debugbreak();
|
||||||
|
abort();
|
||||||
|
#else
|
||||||
|
__builtin_trap();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// Exits the program after printing file/line plus a formatted string.
|
// Exits the program after printing file/line plus a formatted string.
|
||||||
#define JXL_ABORT(format, ...) \
|
#define JXL_ABORT(format, ...) \
|
||||||
|
8
third_party/jpeg-xl/lib/jxl/bits_test.cc
vendored
8
third_party/jpeg-xl/lib/jxl/bits_test.cc
vendored
@ -41,6 +41,10 @@ TEST(BitsTest, TestFloorLog2) {
|
|||||||
EXPECT_EQ(expected[i - 1], FloorLog2Nonzero(uint64_t(i))) << " " << i;
|
EXPECT_EQ(expected[i - 1], FloorLog2Nonzero(uint64_t(i))) << " " << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(11u, FloorLog2Nonzero(0x00000fffu)); // 4095
|
||||||
|
EXPECT_EQ(12u, FloorLog2Nonzero(0x00001000u)); // 4096
|
||||||
|
EXPECT_EQ(12u, FloorLog2Nonzero(0x00001001u)); // 4097
|
||||||
|
|
||||||
EXPECT_EQ(31u, FloorLog2Nonzero(0x80000000u));
|
EXPECT_EQ(31u, FloorLog2Nonzero(0x80000000u));
|
||||||
EXPECT_EQ(31u, FloorLog2Nonzero(0x80000001u));
|
EXPECT_EQ(31u, FloorLog2Nonzero(0x80000001u));
|
||||||
EXPECT_EQ(31u, FloorLog2Nonzero(0xFFFFFFFFu));
|
EXPECT_EQ(31u, FloorLog2Nonzero(0xFFFFFFFFu));
|
||||||
@ -62,6 +66,10 @@ TEST(BitsTest, TestCeilLog2) {
|
|||||||
EXPECT_EQ(expected[i - 1], CeilLog2Nonzero(uint64_t(i))) << " " << i;
|
EXPECT_EQ(expected[i - 1], CeilLog2Nonzero(uint64_t(i))) << " " << i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(12u, CeilLog2Nonzero(0x00000fffu)); // 4095
|
||||||
|
EXPECT_EQ(12u, CeilLog2Nonzero(0x00001000u)); // 4096
|
||||||
|
EXPECT_EQ(13u, CeilLog2Nonzero(0x00001001u)); // 4097
|
||||||
|
|
||||||
EXPECT_EQ(31u, CeilLog2Nonzero(0x80000000u));
|
EXPECT_EQ(31u, CeilLog2Nonzero(0x80000000u));
|
||||||
EXPECT_EQ(32u, CeilLog2Nonzero(0x80000001u));
|
EXPECT_EQ(32u, CeilLog2Nonzero(0x80000001u));
|
||||||
EXPECT_EQ(32u, CeilLog2Nonzero(0xFFFFFFFFu));
|
EXPECT_EQ(32u, CeilLog2Nonzero(0xFFFFFFFFu));
|
||||||
|
2
third_party/jpeg-xl/lib/jxl/blending.cc
vendored
2
third_party/jpeg-xl/lib/jxl/blending.cc
vendored
@ -78,7 +78,7 @@ void PerformBlending(const float* const* bg, const float* const* fg,
|
|||||||
} else if (ec_blending[i].mode == PatchBlendMode::kReplace) {
|
} else if (ec_blending[i].mode == PatchBlendMode::kReplace) {
|
||||||
memcpy(tmp.Row(3 + i), fg[3 + i] + x0, xsize * sizeof(**fg));
|
memcpy(tmp.Row(3 + i), fg[3 + i] + x0, xsize * sizeof(**fg));
|
||||||
} else if (ec_blending[i].mode == PatchBlendMode::kNone) {
|
} else if (ec_blending[i].mode == PatchBlendMode::kNone) {
|
||||||
memcpy(tmp.Row(3 + i), bg[3 + i] + x0, xsize * sizeof(**fg));
|
if (xsize) memcpy(tmp.Row(3 + i), bg[3 + i] + x0, xsize * sizeof(**fg));
|
||||||
} else {
|
} else {
|
||||||
JXL_ABORT("Unreachable");
|
JXL_ABORT("Unreachable");
|
||||||
}
|
}
|
||||||
|
@ -36,10 +36,6 @@ void SetMetadataFromPixelFormat(const JxlPixelFormat* pixel_format,
|
|||||||
metadata->SetFloat16Samples();
|
metadata->SetFloat16Samples();
|
||||||
potential_alpha_bits = 16;
|
potential_alpha_bits = 16;
|
||||||
break;
|
break;
|
||||||
case JXL_TYPE_UINT32:
|
|
||||||
metadata->SetUintSamples(32);
|
|
||||||
potential_alpha_bits = 16;
|
|
||||||
break;
|
|
||||||
case JXL_TYPE_UINT16:
|
case JXL_TYPE_UINT16:
|
||||||
metadata->SetUintSamples(16);
|
metadata->SetUintSamples(16);
|
||||||
potential_alpha_bits = 16;
|
potential_alpha_bits = 16;
|
||||||
@ -48,10 +44,8 @@ void SetMetadataFromPixelFormat(const JxlPixelFormat* pixel_format,
|
|||||||
metadata->SetUintSamples(8);
|
metadata->SetUintSamples(8);
|
||||||
potential_alpha_bits = 8;
|
potential_alpha_bits = 8;
|
||||||
break;
|
break;
|
||||||
case JXL_TYPE_BOOLEAN:
|
default:
|
||||||
metadata->SetUintSamples(2);
|
JXL_ABORT("Unhandled JxlDataType");
|
||||||
potential_alpha_bits = 2;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
if (pixel_format->num_channels == 2 || pixel_format->num_channels == 4) {
|
if (pixel_format->num_channels == 2 || pixel_format->num_channels == 4) {
|
||||||
metadata->SetAlphaBits(potential_alpha_bits);
|
metadata->SetAlphaBits(potential_alpha_bits);
|
||||||
@ -116,9 +110,10 @@ void JxlButteraugliApiSetIntensityTarget(JxlButteraugliApi* api, float v) {
|
|||||||
|
|
||||||
void JxlButteraugliApiDestroy(JxlButteraugliApi* api) {
|
void JxlButteraugliApiDestroy(JxlButteraugliApi* api) {
|
||||||
if (api) {
|
if (api) {
|
||||||
|
JxlMemoryManager local_memory_manager = api->memory_manager;
|
||||||
// Call destructor directly since custom free function is used.
|
// Call destructor directly since custom free function is used.
|
||||||
api->~JxlButteraugliApi();
|
api->~JxlButteraugliApi();
|
||||||
jxl::MemoryManagerFree(&api->memory_manager, api);
|
jxl::MemoryManagerFree(&local_memory_manager, api);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,8 +195,9 @@ float JxlButteraugliResultGetMaxDistance(const JxlButteraugliResult* result) {
|
|||||||
|
|
||||||
void JxlButteraugliResultDestroy(JxlButteraugliResult* result) {
|
void JxlButteraugliResultDestroy(JxlButteraugliResult* result) {
|
||||||
if (result) {
|
if (result) {
|
||||||
|
JxlMemoryManager local_memory_manager = result->memory_manager;
|
||||||
// Call destructor directly since custom free function is used.
|
// Call destructor directly since custom free function is used.
|
||||||
result->~JxlButteraugliResult();
|
result->~JxlButteraugliResult();
|
||||||
jxl::MemoryManagerFree(&result->memory_manager, result);
|
jxl::MemoryManagerFree(&local_memory_manager, result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
8
third_party/jpeg-xl/lib/jxl/codec_in_out.h
vendored
8
third_party/jpeg-xl/lib/jxl/codec_in_out.h
vendored
@ -56,10 +56,10 @@ using CodecIntervals = std::array<CodecInterval, 4>; // RGB[A] or Y[A]
|
|||||||
|
|
||||||
// Optional text/EXIF metadata.
|
// Optional text/EXIF metadata.
|
||||||
struct Blobs {
|
struct Blobs {
|
||||||
PaddedBytes exif;
|
std::vector<uint8_t> exif;
|
||||||
PaddedBytes iptc;
|
std::vector<uint8_t> iptc;
|
||||||
PaddedBytes jumbf;
|
std::vector<uint8_t> jumbf;
|
||||||
PaddedBytes xmp;
|
std::vector<uint8_t> xmp;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Holds a preview, a main image or one or more frames, plus the inputs/outputs
|
// Holds a preview, a main image or one or more frames, plus the inputs/outputs
|
||||||
|
41
third_party/jpeg-xl/lib/jxl/common.h
vendored
41
third_party/jpeg-xl/lib/jxl/common.h
vendored
@ -17,6 +17,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
|
#include "lib/jxl/base/padded_bytes.h"
|
||||||
|
|
||||||
#ifndef JXL_HIGH_PRECISION
|
#ifndef JXL_HIGH_PRECISION
|
||||||
#define JXL_HIGH_PRECISION 1
|
#define JXL_HIGH_PRECISION 1
|
||||||
@ -192,6 +193,46 @@ std::string ToString(T n) {
|
|||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static inline uint64_t DecodeVarInt(const uint8_t* input, size_t inputSize,
|
||||||
|
size_t* pos) {
|
||||||
|
size_t i;
|
||||||
|
uint64_t ret = 0;
|
||||||
|
for (i = 0; *pos + i < inputSize && i < 10; ++i) {
|
||||||
|
ret |= uint64_t(input[*pos + i] & 127) << uint64_t(7 * i);
|
||||||
|
// If the next-byte flag is not set, stop
|
||||||
|
if ((input[*pos + i] & 128) == 0) break;
|
||||||
|
}
|
||||||
|
// TODO: Return a decoding error if i == 10.
|
||||||
|
*pos += i + 1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool EncodeVarInt(uint64_t value, size_t output_size,
|
||||||
|
size_t* output_pos, uint8_t* output) {
|
||||||
|
// While more than 7 bits of data are left,
|
||||||
|
// store 7 bits and set the next byte flag
|
||||||
|
while (value > 127) {
|
||||||
|
if (*output_pos > output_size) return false;
|
||||||
|
// |128: Set the next byte flag
|
||||||
|
output[(*output_pos)++] = ((uint8_t)(value & 127)) | 128;
|
||||||
|
// Remove the seven bits we just wrote
|
||||||
|
value >>= 7;
|
||||||
|
}
|
||||||
|
if (*output_pos > output_size) return false;
|
||||||
|
output[(*output_pos)++] = ((uint8_t)value) & 127;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void EncodeVarInt(uint64_t value, PaddedBytes* data) {
|
||||||
|
size_t pos = data->size();
|
||||||
|
data->resize(data->size() + 9);
|
||||||
|
JXL_CHECK(EncodeVarInt(value, data->size(), &pos, data->data()));
|
||||||
|
data->resize(pos);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
|
||||||
#endif // LIB_JXL_COMMON_H_
|
#endif // LIB_JXL_COMMON_H_
|
||||||
|
6
third_party/jpeg-xl/lib/jxl/convolve-inl.h
vendored
6
third_party/jpeg-xl/lib/jxl/convolve-inl.h
vendored
@ -44,7 +44,7 @@ class Neighbors {
|
|||||||
return c; // Same (the first mirrored value is the last valid one)
|
return c; // Same (the first mirrored value is the last valid one)
|
||||||
#else // 128 bit
|
#else // 128 bit
|
||||||
// c = LKJI
|
// c = LKJI
|
||||||
#if HWY_ARCH_X86
|
#if HWY_TARGET <= (1 << HWY_HIGHEST_TARGET_BIT_X86)
|
||||||
return V{_mm_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(2, 1, 0, 0))}; // KJII
|
return V{_mm_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(2, 1, 0, 0))}; // KJII
|
||||||
#else
|
#else
|
||||||
const D d;
|
const D d;
|
||||||
@ -72,7 +72,7 @@ class Neighbors {
|
|||||||
return Zero(d);
|
return Zero(d);
|
||||||
#else // 128 bit
|
#else // 128 bit
|
||||||
// c = LKJI
|
// c = LKJI
|
||||||
#if HWY_ARCH_X86
|
#if HWY_TARGET <= (1 << HWY_HIGHEST_TARGET_BIT_X86)
|
||||||
return V{_mm_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(1, 0, 0, 1))}; // JIIJ
|
return V{_mm_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(1, 0, 0, 1))}; // JIIJ
|
||||||
#else
|
#else
|
||||||
const D d;
|
const D d;
|
||||||
@ -98,7 +98,7 @@ class Neighbors {
|
|||||||
return Zero(d);
|
return Zero(d);
|
||||||
#else // 128 bit
|
#else // 128 bit
|
||||||
// c = LKJI
|
// c = LKJI
|
||||||
#if HWY_ARCH_X86
|
#if HWY_TARGET <= (1 << HWY_HIGHEST_TARGET_BIT_X86)
|
||||||
return V{_mm_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(0, 0, 1, 2))}; // IIJK
|
return V{_mm_shuffle_ps(c.raw, c.raw, _MM_SHUFFLE(0, 0, 1, 2))}; // IIJK
|
||||||
#else
|
#else
|
||||||
const D d;
|
const D d;
|
||||||
|
3
third_party/jpeg-xl/lib/jxl/dec_ans.cc
vendored
3
third_party/jpeg-xl/lib/jxl/dec_ans.cc
vendored
@ -74,9 +74,6 @@ Status ReadHistogram(int precision_bits, std::vector<int>* counts,
|
|||||||
int is_flat = input->ReadBits(1);
|
int is_flat = input->ReadBits(1);
|
||||||
if (is_flat == 1) {
|
if (is_flat == 1) {
|
||||||
int alphabet_size = DecodeVarLenUint8(input) + 1;
|
int alphabet_size = DecodeVarLenUint8(input) + 1;
|
||||||
if (alphabet_size == 0) {
|
|
||||||
return JXL_FAILURE("Invalid alphabet size for flat histogram.");
|
|
||||||
}
|
|
||||||
*counts = CreateFlatHistogram(alphabet_size, 1 << precision_bits);
|
*counts = CreateFlatHistogram(alphabet_size, 1 << precision_bits);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
4
third_party/jpeg-xl/lib/jxl/dec_cache.cc
vendored
4
third_party/jpeg-xl/lib/jxl/dec_cache.cc
vendored
@ -180,12 +180,12 @@ Status PassesDecoderState::PreparePipeline(ImageBundle* decoded,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pixel_callback) {
|
if (pixel_callback.IsPresent()) {
|
||||||
builder.AddStage(GetWriteToPixelCallbackStage(pixel_callback, width,
|
builder.AddStage(GetWriteToPixelCallbackStage(pixel_callback, width,
|
||||||
height, rgb_output_is_rgba,
|
height, rgb_output_is_rgba,
|
||||||
has_alpha, alpha_c));
|
has_alpha, alpha_c));
|
||||||
} else if (rgb_output) {
|
} else if (rgb_output) {
|
||||||
builder.AddStage(GetWriteToU8Stage(rgb_output, rgb_stride, width, height,
|
builder.AddStage(GetWriteToU8Stage(rgb_output, rgb_stride, height,
|
||||||
rgb_output_is_rgba, has_alpha,
|
rgb_output_is_rgba, has_alpha,
|
||||||
alpha_c));
|
alpha_c));
|
||||||
} else {
|
} else {
|
||||||
|
30
third_party/jpeg-xl/lib/jxl/dec_cache.h
vendored
30
third_party/jpeg-xl/lib/jxl/dec_cache.h
vendored
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
#include <hwy/base.h> // HWY_ALIGN_MAX
|
#include <hwy/base.h> // HWY_ALIGN_MAX
|
||||||
|
|
||||||
|
#include "jxl/decode.h"
|
||||||
#include "lib/jxl/ac_strategy.h"
|
#include "lib/jxl/ac_strategy.h"
|
||||||
#include "lib/jxl/base/profiler.h"
|
#include "lib/jxl/base/profiler.h"
|
||||||
#include "lib/jxl/coeff_order.h"
|
#include "lib/jxl/coeff_order.h"
|
||||||
@ -29,6 +30,31 @@ namespace jxl {
|
|||||||
constexpr size_t kSigmaBorder = 1;
|
constexpr size_t kSigmaBorder = 1;
|
||||||
constexpr size_t kSigmaPadding = 2;
|
constexpr size_t kSigmaPadding = 2;
|
||||||
|
|
||||||
|
struct PixelCallback {
|
||||||
|
PixelCallback() = default;
|
||||||
|
PixelCallback(JxlImageOutInitCallback init, JxlImageOutRunCallback run,
|
||||||
|
JxlImageOutDestroyCallback destroy, void* init_opaque)
|
||||||
|
: init(init), run(run), destroy(destroy), init_opaque(init_opaque) {
|
||||||
|
#if JXL_ENABLE_ASSERT
|
||||||
|
const bool has_init = init != nullptr;
|
||||||
|
const bool has_run = run != nullptr;
|
||||||
|
const bool has_destroy = destroy != nullptr;
|
||||||
|
JXL_ASSERT(has_init == has_run && has_run == has_destroy);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsPresent() const { return run != nullptr; }
|
||||||
|
|
||||||
|
void* Init(size_t num_threads, size_t num_pixels) const {
|
||||||
|
return init(init_opaque, num_threads, num_pixels);
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlImageOutInitCallback init = nullptr;
|
||||||
|
JxlImageOutRunCallback run = nullptr;
|
||||||
|
JxlImageOutDestroyCallback destroy = nullptr;
|
||||||
|
void* init_opaque = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
// Per-frame decoder state. All the images here should be accessed through a
|
// Per-frame decoder state. All the images here should be accessed through a
|
||||||
// group rect (either with block units or pixel units).
|
// group rect (either with block units or pixel units).
|
||||||
struct PassesDecoderState {
|
struct PassesDecoderState {
|
||||||
@ -65,7 +91,8 @@ struct PassesDecoderState {
|
|||||||
bool rgb_output_is_rgba;
|
bool rgb_output_is_rgba;
|
||||||
|
|
||||||
// Callback for line-by-line output.
|
// Callback for line-by-line output.
|
||||||
std::function<void(const float*, size_t, size_t, size_t)> pixel_callback;
|
PixelCallback pixel_callback;
|
||||||
|
|
||||||
// Buffer of upsampling * kApplyImageFeaturesTileDim ones.
|
// Buffer of upsampling * kApplyImageFeaturesTileDim ones.
|
||||||
std::vector<float> opaque_alpha;
|
std::vector<float> opaque_alpha;
|
||||||
// One row per thread
|
// One row per thread
|
||||||
@ -106,7 +133,6 @@ struct PassesDecoderState {
|
|||||||
std::pow(1 / (1.25f), shared->frame_header.b_qm_scale - 2.0f);
|
std::pow(1 / (1.25f), shared->frame_header.b_qm_scale - 2.0f);
|
||||||
|
|
||||||
rgb_output = nullptr;
|
rgb_output = nullptr;
|
||||||
pixel_callback = nullptr;
|
|
||||||
rgb_output_is_rgba = false;
|
rgb_output_is_rgba = false;
|
||||||
fast_xyb_srgb8_conversion = false;
|
fast_xyb_srgb8_conversion = false;
|
||||||
used_acs = 0;
|
used_acs = 0;
|
||||||
|
111
third_party/jpeg-xl/lib/jxl/dec_external_image.cc
vendored
111
third_party/jpeg-xl/lib/jxl/dec_external_image.cc
vendored
@ -32,28 +32,9 @@ HWY_BEFORE_NAMESPACE();
|
|||||||
namespace jxl {
|
namespace jxl {
|
||||||
namespace HWY_NAMESPACE {
|
namespace HWY_NAMESPACE {
|
||||||
|
|
||||||
|
// TODO(jon): check if this can be replaced by a FloatToU16 function
|
||||||
void FloatToU32(const float* in, uint32_t* out, size_t num, float mul,
|
void FloatToU32(const float* in, uint32_t* out, size_t num, float mul,
|
||||||
size_t bits_per_sample) {
|
size_t bits_per_sample) {
|
||||||
// TODO(eustas): investigate 24..31 bpp cases.
|
|
||||||
if (bits_per_sample == 32) {
|
|
||||||
// Conversion to real 32-bit *unsigned* integers requires more intermediate
|
|
||||||
// precision that what is given by the usual f32 -> i32 conversion
|
|
||||||
// instructions, so we run the non-SIMD path for those.
|
|
||||||
const uint32_t cap = (1ull << bits_per_sample) - 1;
|
|
||||||
for (size_t x = 0; x < num; x++) {
|
|
||||||
float v = in[x];
|
|
||||||
if (v >= 1.0f) {
|
|
||||||
out[x] = cap;
|
|
||||||
} else if (v >= 0.0f) { // Inverted condition => NaN -> 0.
|
|
||||||
out[x] = static_cast<uint32_t>(v * mul + 0.5f);
|
|
||||||
} else {
|
|
||||||
out[x] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// General SIMD case for less than 32 bits output.
|
|
||||||
const HWY_FULL(float) d;
|
const HWY_FULL(float) d;
|
||||||
const hwy::HWY_NAMESPACE::Rebind<uint32_t, decltype(d)> du;
|
const hwy::HWY_NAMESPACE::Rebind<uint32_t, decltype(d)> du;
|
||||||
|
|
||||||
@ -267,39 +248,36 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
|
|||||||
JxlEndianness endianness, size_t stride,
|
JxlEndianness endianness, size_t stride,
|
||||||
jxl::ThreadPool* pool, void* out_image,
|
jxl::ThreadPool* pool, void* out_image,
|
||||||
size_t out_size,
|
size_t out_size,
|
||||||
JxlImageOutCallback out_callback,
|
const PixelCallback& out_callback,
|
||||||
void* out_opaque,
|
|
||||||
jxl::Orientation undo_orientation) {
|
jxl::Orientation undo_orientation) {
|
||||||
JXL_DASSERT(num_channels != 0 && num_channels <= kConvertMaxChannels);
|
JXL_DASSERT(num_channels != 0 && num_channels <= kConvertMaxChannels);
|
||||||
JXL_DASSERT(channels[0] != nullptr);
|
JXL_DASSERT(channels[0] != nullptr);
|
||||||
|
JXL_CHECK(float_out ? bits_per_sample == 16 || bits_per_sample == 32
|
||||||
if (bits_per_sample < 1 || bits_per_sample > 32) {
|
: bits_per_sample > 0 && bits_per_sample <= 16);
|
||||||
return JXL_FAILURE("Invalid bits_per_sample value.");
|
if (!!out_image == out_callback.IsPresent()) {
|
||||||
}
|
|
||||||
if (!!out_image == !!out_callback) {
|
|
||||||
return JXL_FAILURE(
|
return JXL_FAILURE(
|
||||||
"Must provide either an out_image or an out_callback, but not both.");
|
"Must provide either an out_image or an out_callback, but not both.");
|
||||||
}
|
}
|
||||||
// TODO(deymo): Implement 1-bit per pixel packed in 8 samples per byte.
|
|
||||||
if (bits_per_sample == 1) {
|
|
||||||
return JXL_FAILURE("packed 1-bit per sample is not yet supported");
|
|
||||||
}
|
|
||||||
if (bits_per_sample > 16 && bits_per_sample < 32) {
|
|
||||||
return JXL_FAILURE("not supported, try bits_per_sample=32");
|
|
||||||
}
|
|
||||||
|
|
||||||
// bytes_per_channel and is only valid for bits_per_sample > 1.
|
|
||||||
const size_t bytes_per_channel = DivCeil(bits_per_sample, jxl::kBitsPerByte);
|
const size_t bytes_per_channel = DivCeil(bits_per_sample, jxl::kBitsPerByte);
|
||||||
const size_t bytes_per_pixel = num_channels * bytes_per_channel;
|
const size_t bytes_per_pixel = num_channels * bytes_per_channel;
|
||||||
|
|
||||||
std::vector<std::vector<uint8_t>> row_out_callback;
|
std::vector<std::vector<uint8_t>> row_out_callback;
|
||||||
auto InitOutCallback = [&](size_t num_threads) {
|
const auto FreeCallbackOpaque = [&out_callback](void* p) {
|
||||||
if (out_callback) {
|
out_callback.destroy(p);
|
||||||
|
};
|
||||||
|
std::unique_ptr<void, decltype(FreeCallbackOpaque)> out_run_opaque(
|
||||||
|
nullptr, FreeCallbackOpaque);
|
||||||
|
auto InitOutCallback = [&](size_t num_threads) -> Status {
|
||||||
|
if (out_callback.IsPresent()) {
|
||||||
|
out_run_opaque.reset(out_callback.Init(num_threads, stride));
|
||||||
|
JXL_RETURN_IF_ERROR(out_run_opaque != nullptr);
|
||||||
row_out_callback.resize(num_threads);
|
row_out_callback.resize(num_threads);
|
||||||
for (size_t i = 0; i < num_threads; ++i) {
|
for (size_t i = 0; i < num_threads; ++i) {
|
||||||
row_out_callback[i].resize(stride);
|
row_out_callback[i].resize(stride);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Channels used to store the transformed original channels if needed.
|
// Channels used to store the transformed original channels if needed.
|
||||||
@ -322,7 +300,7 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
|
|||||||
" vs %" PRIuS,
|
" vs %" PRIuS,
|
||||||
stride, bytes_per_pixel * xsize);
|
stride, bytes_per_pixel * xsize);
|
||||||
}
|
}
|
||||||
if (!out_callback &&
|
if (!out_callback.IsPresent() &&
|
||||||
out_size < (ysize - 1) * stride + bytes_per_pixel * xsize) {
|
out_size < (ysize - 1) * stride + bytes_per_pixel * xsize) {
|
||||||
return JXL_FAILURE("out_size is too small to store image");
|
return JXL_FAILURE("out_size is too small to store image");
|
||||||
}
|
}
|
||||||
@ -351,8 +329,7 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
|
|||||||
[&](size_t num_threads) {
|
[&](size_t num_threads) {
|
||||||
f16_cache =
|
f16_cache =
|
||||||
Plane<hwy::float16_t>(xsize, num_channels * num_threads);
|
Plane<hwy::float16_t>(xsize, num_channels * num_threads);
|
||||||
InitOutCallback(num_threads);
|
return InitOutCallback(num_threads);
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
[&](const uint32_t task, const size_t thread) {
|
[&](const uint32_t task, const size_t thread) {
|
||||||
const int64_t y = task;
|
const int64_t y = task;
|
||||||
@ -367,7 +344,7 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
|
|||||||
(row_in[c], row_f16[c], xsize);
|
(row_in[c], row_f16[c], xsize);
|
||||||
}
|
}
|
||||||
uint8_t* row_out =
|
uint8_t* row_out =
|
||||||
out_callback
|
out_callback.IsPresent()
|
||||||
? row_out_callback[thread].data()
|
? row_out_callback[thread].data()
|
||||||
: &(reinterpret_cast<uint8_t*>(out_image))[stride * y];
|
: &(reinterpret_cast<uint8_t*>(out_image))[stride * y];
|
||||||
// interleave the one scanline
|
// interleave the one scanline
|
||||||
@ -384,22 +361,20 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
|
|||||||
std::swap(row_out[i + 0], row_out[i + 1]);
|
std::swap(row_out[i + 0], row_out[i + 1]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (out_callback) {
|
if (out_callback.IsPresent()) {
|
||||||
(*out_callback)(out_opaque, 0, y, xsize, row_out);
|
out_callback.run(out_run_opaque.get(), thread, 0, y, xsize,
|
||||||
|
row_out);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConvertF16"));
|
"ConvertF16"));
|
||||||
} else if (bits_per_sample == 32) {
|
} else if (bits_per_sample == 32) {
|
||||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||||
pool, 0, static_cast<uint32_t>(ysize),
|
pool, 0, static_cast<uint32_t>(ysize),
|
||||||
[&](size_t num_threads) {
|
[&](size_t num_threads) { return InitOutCallback(num_threads); },
|
||||||
InitOutCallback(num_threads);
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
[&](const uint32_t task, const size_t thread) {
|
[&](const uint32_t task, const size_t thread) {
|
||||||
const int64_t y = task;
|
const int64_t y = task;
|
||||||
uint8_t* row_out =
|
uint8_t* row_out =
|
||||||
out_callback
|
out_callback.IsPresent()
|
||||||
? row_out_callback[thread].data()
|
? row_out_callback[thread].data()
|
||||||
: &(reinterpret_cast<uint8_t*>(out_image))[stride * y];
|
: &(reinterpret_cast<uint8_t*>(out_image))[stride * y];
|
||||||
const float* JXL_RESTRICT row_in[kConvertMaxChannels];
|
const float* JXL_RESTRICT row_in[kConvertMaxChannels];
|
||||||
@ -411,8 +386,9 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
|
|||||||
} else {
|
} else {
|
||||||
StoreFloatRow<StoreBEFloat>(row_in, num_channels, xsize, row_out);
|
StoreFloatRow<StoreBEFloat>(row_in, num_channels, xsize, row_out);
|
||||||
}
|
}
|
||||||
if (out_callback) {
|
if (out_callback.IsPresent()) {
|
||||||
(*out_callback)(out_opaque, 0, y, xsize, row_out);
|
out_callback.run(out_run_opaque.get(), thread, 0, y, xsize,
|
||||||
|
row_out);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConvertFloat"));
|
"ConvertFloat"));
|
||||||
@ -428,13 +404,12 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
|
|||||||
pool, 0, static_cast<uint32_t>(ysize),
|
pool, 0, static_cast<uint32_t>(ysize),
|
||||||
[&](size_t num_threads) {
|
[&](size_t num_threads) {
|
||||||
u32_cache = Plane<uint32_t>(xsize, num_channels * num_threads);
|
u32_cache = Plane<uint32_t>(xsize, num_channels * num_threads);
|
||||||
InitOutCallback(num_threads);
|
return InitOutCallback(num_threads);
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
[&](const uint32_t task, const size_t thread) {
|
[&](const uint32_t task, const size_t thread) {
|
||||||
const int64_t y = task;
|
const int64_t y = task;
|
||||||
uint8_t* row_out =
|
uint8_t* row_out =
|
||||||
out_callback
|
out_callback.IsPresent()
|
||||||
? row_out_callback[thread].data()
|
? row_out_callback[thread].data()
|
||||||
: &(reinterpret_cast<uint8_t*>(out_image))[stride * y];
|
: &(reinterpret_cast<uint8_t*>(out_image))[stride * y];
|
||||||
const float* JXL_RESTRICT row_in[kConvertMaxChannels];
|
const float* JXL_RESTRICT row_in[kConvertMaxChannels];
|
||||||
@ -450,24 +425,18 @@ Status ConvertChannelsToExternal(const ImageF* channels[], size_t num_channels,
|
|||||||
HWY_DYNAMIC_DISPATCH(FloatToU32)
|
HWY_DYNAMIC_DISPATCH(FloatToU32)
|
||||||
(row_in[c], row_u32[c], xsize, mul, bits_per_sample);
|
(row_in[c], row_u32[c], xsize, mul, bits_per_sample);
|
||||||
}
|
}
|
||||||
// TODO(deymo): add bits_per_sample == 1 case here.
|
|
||||||
if (bits_per_sample <= 8) {
|
if (bits_per_sample <= 8) {
|
||||||
StoreUintRow<Store8>(row_u32, num_channels, xsize, 1, row_out);
|
StoreUintRow<Store8>(row_u32, num_channels, xsize, 1, row_out);
|
||||||
} else if (bits_per_sample <= 16) {
|
} else {
|
||||||
if (little_endian) {
|
if (little_endian) {
|
||||||
StoreUintRow<StoreLE16>(row_u32, num_channels, xsize, 2, row_out);
|
StoreUintRow<StoreLE16>(row_u32, num_channels, xsize, 2, row_out);
|
||||||
} else {
|
} else {
|
||||||
StoreUintRow<StoreBE16>(row_u32, num_channels, xsize, 2, row_out);
|
StoreUintRow<StoreBE16>(row_u32, num_channels, xsize, 2, row_out);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (little_endian) {
|
|
||||||
StoreUintRow<StoreLE32>(row_u32, num_channels, xsize, 4, row_out);
|
|
||||||
} else {
|
|
||||||
StoreUintRow<StoreBE32>(row_u32, num_channels, xsize, 4, row_out);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (out_callback) {
|
if (out_callback.IsPresent()) {
|
||||||
(*out_callback)(out_opaque, 0, y, xsize, row_out);
|
out_callback.run(out_run_opaque.get(), thread, 0, y, xsize,
|
||||||
|
row_out);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConvertUint"));
|
"ConvertUint"));
|
||||||
@ -481,8 +450,8 @@ Status ConvertToExternal(const jxl::ImageBundle& ib, size_t bits_per_sample,
|
|||||||
bool float_out, size_t num_channels,
|
bool float_out, size_t num_channels,
|
||||||
JxlEndianness endianness, size_t stride,
|
JxlEndianness endianness, size_t stride,
|
||||||
jxl::ThreadPool* pool, void* out_image,
|
jxl::ThreadPool* pool, void* out_image,
|
||||||
size_t out_size, JxlImageOutCallback out_callback,
|
size_t out_size, const PixelCallback& out_callback,
|
||||||
void* out_opaque, jxl::Orientation undo_orientation) {
|
jxl::Orientation undo_orientation) {
|
||||||
bool want_alpha = num_channels == 2 || num_channels == 4;
|
bool want_alpha = num_channels == 2 || num_channels == 4;
|
||||||
size_t color_channels = num_channels <= 2 ? 1 : 3;
|
size_t color_channels = num_channels <= 2 ? 1 : 3;
|
||||||
|
|
||||||
@ -512,19 +481,19 @@ Status ConvertToExternal(const jxl::ImageBundle& ib, size_t bits_per_sample,
|
|||||||
|
|
||||||
return ConvertChannelsToExternal(
|
return ConvertChannelsToExternal(
|
||||||
channels, num_channels, bits_per_sample, float_out, endianness, stride,
|
channels, num_channels, bits_per_sample, float_out, endianness, stride,
|
||||||
pool, out_image, out_size, out_callback, out_opaque, undo_orientation);
|
pool, out_image, out_size, out_callback, undo_orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status ConvertToExternal(const jxl::ImageF& channel, size_t bits_per_sample,
|
Status ConvertToExternal(const jxl::ImageF& channel, size_t bits_per_sample,
|
||||||
bool float_out, JxlEndianness endianness,
|
bool float_out, JxlEndianness endianness,
|
||||||
size_t stride, jxl::ThreadPool* pool, void* out_image,
|
size_t stride, jxl::ThreadPool* pool, void* out_image,
|
||||||
size_t out_size, JxlImageOutCallback out_callback,
|
size_t out_size, const PixelCallback& out_callback,
|
||||||
void* out_opaque, jxl::Orientation undo_orientation) {
|
jxl::Orientation undo_orientation) {
|
||||||
const ImageF* channels[1];
|
const ImageF* channels[1];
|
||||||
channels[0] = &channel;
|
channels[0] = &channel;
|
||||||
return ConvertChannelsToExternal(
|
return ConvertChannelsToExternal(channels, 1, bits_per_sample, float_out,
|
||||||
channels, 1, bits_per_sample, float_out, endianness, stride, pool,
|
endianness, stride, pool, out_image,
|
||||||
out_image, out_size, out_callback, out_opaque, undo_orientation);
|
out_size, out_callback, undo_orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
20
third_party/jpeg-xl/lib/jxl/dec_external_image.h
vendored
20
third_party/jpeg-xl/lib/jxl/dec_external_image.h
vendored
@ -15,18 +15,18 @@
|
|||||||
#include "jxl/types.h"
|
#include "jxl/types.h"
|
||||||
#include "lib/jxl/base/status.h"
|
#include "lib/jxl/base/status.h"
|
||||||
#include "lib/jxl/color_encoding_internal.h"
|
#include "lib/jxl/color_encoding_internal.h"
|
||||||
|
#include "lib/jxl/dec_cache.h"
|
||||||
#include "lib/jxl/image.h"
|
#include "lib/jxl/image.h"
|
||||||
#include "lib/jxl/image_bundle.h"
|
#include "lib/jxl/image_bundle.h"
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
||||||
// Converts ib to interleaved void* pixel buffer with the given format.
|
// Converts ib to interleaved void* pixel buffer with the given format.
|
||||||
// bits_per_sample: must be 8, 16 or 32, and must be 32 if float_out
|
// bits_per_sample: must be 16 or 32 if float_out is true, and at most 16
|
||||||
// is true. 1 and 32 int are not yet implemented.
|
// if it is false. No bit packing is done.
|
||||||
// num_channels: must be 1, 2, 3 or 4 for gray, gray+alpha, RGB, RGB+alpha.
|
// num_channels: must be 1, 2, 3 or 4 for gray, gray+alpha, RGB, RGB+alpha.
|
||||||
// This supports the features needed for the C API and does not perform
|
// This supports the features needed for the C API and does not perform
|
||||||
// color space conversion.
|
// color space conversion.
|
||||||
// TODO(lode): support 1-bit output (bits_per_sample == 1)
|
|
||||||
// TODO(lode): support rectangle crop.
|
// TODO(lode): support rectangle crop.
|
||||||
// stride_out is output scanline size in bytes, must be >=
|
// stride_out is output scanline size in bytes, must be >=
|
||||||
// output_xsize * output_bytes_per_pixel.
|
// output_xsize * output_bytes_per_pixel.
|
||||||
@ -37,24 +37,18 @@ Status ConvertToExternal(const jxl::ImageBundle& ib, size_t bits_per_sample,
|
|||||||
bool float_out, size_t num_channels,
|
bool float_out, size_t num_channels,
|
||||||
JxlEndianness endianness, size_t stride_out,
|
JxlEndianness endianness, size_t stride_out,
|
||||||
jxl::ThreadPool* thread_pool, void* out_image,
|
jxl::ThreadPool* thread_pool, void* out_image,
|
||||||
size_t out_size, JxlImageOutCallback out_callback,
|
size_t out_size, const PixelCallback& out_callback,
|
||||||
void* out_opaque, jxl::Orientation undo_orientation);
|
jxl::Orientation undo_orientation);
|
||||||
|
|
||||||
// Converts single-channel image to interleaved void* pixel buffer with the
|
// Converts single-channel image to interleaved void* pixel buffer with the
|
||||||
// given format, with a single channel.
|
// given format, with a single channel.
|
||||||
// bits_per_sample: must be 8, 16 or 32, and must be 32 if float_out
|
|
||||||
// is true. 1 and 32 int are not yet implemented.
|
|
||||||
// This supports the features needed for the C API to get extra channels.
|
// This supports the features needed for the C API to get extra channels.
|
||||||
// stride_out is output scanline size in bytes, must be >=
|
// Arguments are similar to the multi-channel function above.
|
||||||
// output_xsize * output_bytes_per_pixel.
|
|
||||||
// undo_orientation is an EXIF orientation to undo. Depending on the
|
|
||||||
// orientation, the output xsize and ysize are swapped compared to input
|
|
||||||
// xsize and ysize.
|
|
||||||
Status ConvertToExternal(const jxl::ImageF& channel, size_t bits_per_sample,
|
Status ConvertToExternal(const jxl::ImageF& channel, size_t bits_per_sample,
|
||||||
bool float_out, JxlEndianness endianness,
|
bool float_out, JxlEndianness endianness,
|
||||||
size_t stride_out, jxl::ThreadPool* thread_pool,
|
size_t stride_out, jxl::ThreadPool* thread_pool,
|
||||||
void* out_image, size_t out_size,
|
void* out_image, size_t out_size,
|
||||||
JxlImageOutCallback out_callback, void* out_opaque,
|
const PixelCallback& out_callback,
|
||||||
jxl::Orientation undo_orientation);
|
jxl::Orientation undo_orientation);
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ void BM_DecExternalImage_ConvertImageRGBA(benchmark::State& state) {
|
|||||||
/*float_out=*/false, num_channels, JXL_NATIVE_ENDIAN,
|
/*float_out=*/false, num_channels, JXL_NATIVE_ENDIAN,
|
||||||
/*stride*/ bytes_per_row,
|
/*stride*/ bytes_per_row,
|
||||||
/*thread_pool=*/nullptr, interleaved.data(), interleaved.size(),
|
/*thread_pool=*/nullptr, interleaved.data(), interleaved.size(),
|
||||||
/*out_callback=*/nullptr, /*out_opaque=*/nullptr,
|
/*out_callback=*/{},
|
||||||
/*undo_orientation=*/jxl::Orientation::kIdentity));
|
/*undo_orientation=*/jxl::Orientation::kIdentity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
18
third_party/jpeg-xl/lib/jxl/dec_file.cc
vendored
18
third_party/jpeg-xl/lib/jxl/dec_file.cc
vendored
@ -66,7 +66,23 @@ Status DecodeFile(const DecompressParams& dparams,
|
|||||||
{
|
{
|
||||||
BitReader reader(file);
|
BitReader reader(file);
|
||||||
BitReaderScopedCloser reader_closer(&reader, &ret);
|
BitReaderScopedCloser reader_closer(&reader, &ret);
|
||||||
(void)reader.ReadFixedBits<16>(); // skip marker
|
if (reader.ReadFixedBits<16>() != 0x0AFF) {
|
||||||
|
// We don't have a naked codestream. Make a quick & dirty attempt to find
|
||||||
|
// the codestream.
|
||||||
|
// TODO(jon): get rid of this whole function
|
||||||
|
const unsigned char* begin = file.data();
|
||||||
|
const unsigned char* end = file.data() + file.size() - 4;
|
||||||
|
while (begin < end) {
|
||||||
|
if (!memcmp(begin, "jxlc", 4)) break;
|
||||||
|
begin++;
|
||||||
|
}
|
||||||
|
if (begin >= end) return JXL_FAILURE("Couldn't find jxl codestream");
|
||||||
|
reader.SkipBits(8 * (begin - file.data() + 2));
|
||||||
|
unsigned int firstbytes = reader.ReadFixedBits<16>();
|
||||||
|
if (firstbytes != 0x0AFF)
|
||||||
|
return JXL_FAILURE("Codestream didn't start with FF0A but with %X",
|
||||||
|
firstbytes);
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
JXL_RETURN_IF_ERROR(DecodeHeaders(&reader, io));
|
JXL_RETURN_IF_ERROR(DecodeHeaders(&reader, io));
|
||||||
|
26
third_party/jpeg-xl/lib/jxl/dec_frame.cc
vendored
26
third_party/jpeg-xl/lib/jxl/dec_frame.cc
vendored
@ -265,6 +265,7 @@ Status FrameDecoder::InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded,
|
|||||||
decoded->duration = frame_header_.animation_frame.duration;
|
decoded->duration = frame_header_.animation_frame.duration;
|
||||||
|
|
||||||
if (!frame_header_.nonserialized_is_preview &&
|
if (!frame_header_.nonserialized_is_preview &&
|
||||||
|
(frame_header_.is_last || frame_header_.animation_frame.duration > 0) &&
|
||||||
(frame_header_.frame_type == kRegularFrame ||
|
(frame_header_.frame_type == kRegularFrame ||
|
||||||
frame_header_.frame_type == kSkipProgressive)) {
|
frame_header_.frame_type == kSkipProgressive)) {
|
||||||
++dec_state_->visible_frame_index;
|
++dec_state_->visible_frame_index;
|
||||||
@ -556,10 +557,11 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
|
|||||||
size_t num_passes, size_t thread,
|
size_t num_passes, size_t thread,
|
||||||
bool force_draw, bool dc_only) {
|
bool force_draw, bool dc_only) {
|
||||||
PROFILER_ZONE("process_group");
|
PROFILER_ZONE("process_group");
|
||||||
|
size_t group_dim = frame_dim_.group_dim;
|
||||||
const size_t gx = ac_group_id % frame_dim_.xsize_groups;
|
const size_t gx = ac_group_id % frame_dim_.xsize_groups;
|
||||||
const size_t gy = ac_group_id / frame_dim_.xsize_groups;
|
const size_t gy = ac_group_id / frame_dim_.xsize_groups;
|
||||||
const size_t x = gx * frame_dim_.group_dim;
|
const size_t x = gx * group_dim;
|
||||||
const size_t y = gy * frame_dim_.group_dim;
|
const size_t y = gy * group_dim;
|
||||||
|
|
||||||
RenderPipelineInput render_pipeline_input =
|
RenderPipelineInput render_pipeline_input =
|
||||||
dec_state_->render_pipeline->GetInputBuffers(ac_group_id, thread);
|
dec_state_->render_pipeline->GetInputBuffers(ac_group_id, thread);
|
||||||
@ -577,7 +579,7 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// don't limit to image dimensions here (is done in DecodeGroup)
|
// don't limit to image dimensions here (is done in DecodeGroup)
|
||||||
const Rect mrect(x, y, frame_dim_.group_dim, frame_dim_.group_dim);
|
const Rect mrect(x, y, group_dim, group_dim);
|
||||||
for (size_t i = 0; i < frame_header_.passes.num_passes; i++) {
|
for (size_t i = 0; i < frame_header_.passes.num_passes; i++) {
|
||||||
int minShift, maxShift;
|
int minShift, maxShift;
|
||||||
frame_header_.passes.GetDownsamplingBracket(i, minShift, maxShift);
|
frame_header_.passes.GetDownsamplingBracket(i, minShift, maxShift);
|
||||||
@ -612,14 +614,14 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
|
|||||||
rects[c].first = r.first;
|
rects[c].first = r.first;
|
||||||
size_t x1 = r.second.x0() + r.second.xsize();
|
size_t x1 = r.second.x0() + r.second.xsize();
|
||||||
size_t y1 = r.second.y0() + r.second.ysize();
|
size_t y1 = r.second.y0() + r.second.ysize();
|
||||||
rects[c].second = Rect(r.second.x0() + ix * kGroupDim,
|
rects[c].second = Rect(r.second.x0() + ix * group_dim,
|
||||||
r.second.y0() + iy * kGroupDim, kGroupDim,
|
r.second.y0() + iy * group_dim, group_dim,
|
||||||
kGroupDim, x1, y1);
|
group_dim, x1, y1);
|
||||||
}
|
}
|
||||||
Random3Planes(dec_state_->visible_frame_index,
|
Random3Planes(dec_state_->visible_frame_index,
|
||||||
dec_state_->nonvisible_frame_index,
|
dec_state_->nonvisible_frame_index,
|
||||||
(gx * frame_header_.upsampling + ix) * kGroupDim,
|
(gx * frame_header_.upsampling + ix) * group_dim,
|
||||||
(gy * frame_header_.upsampling + iy) * kGroupDim,
|
(gy * frame_header_.upsampling + iy) * group_dim,
|
||||||
rects[0], rects[1], rects[2]);
|
rects[0], rects[1], rects[2]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -792,8 +794,8 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
|
|||||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||||
pool_, 0, ac_group_sec.size(),
|
pool_, 0, ac_group_sec.size(),
|
||||||
[this](size_t num_threads) {
|
[this](size_t num_threads) {
|
||||||
PrepareStorage(num_threads, decoded_passes_per_ac_group_.size());
|
return PrepareStorage(num_threads,
|
||||||
return true;
|
decoded_passes_per_ac_group_.size());
|
||||||
},
|
},
|
||||||
[this, &ac_group_sec, &num_ac_passes, &num, §ions, §ion_status,
|
[this, &ac_group_sec, &num_ac_passes, &num, §ions, §ion_status,
|
||||||
&has_error](size_t g, size_t thread) {
|
&has_error](size_t g, size_t thread) {
|
||||||
@ -863,8 +865,8 @@ Status FrameDecoder::Flush() {
|
|||||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||||
pool_, 0, decoded_passes_per_ac_group_.size(),
|
pool_, 0, decoded_passes_per_ac_group_.size(),
|
||||||
[this](const size_t num_threads) {
|
[this](const size_t num_threads) {
|
||||||
PrepareStorage(num_threads, decoded_passes_per_ac_group_.size());
|
return PrepareStorage(num_threads,
|
||||||
return true;
|
decoded_passes_per_ac_group_.size());
|
||||||
},
|
},
|
||||||
[this, &has_error](const uint32_t g, size_t thread) {
|
[this, &has_error](const uint32_t g, size_t thread) {
|
||||||
if (decoded_passes_per_ac_group_[g] ==
|
if (decoded_passes_per_ac_group_[g] ==
|
||||||
|
34
third_party/jpeg-xl/lib/jxl/dec_frame.h
vendored
34
third_party/jpeg-xl/lib/jxl/dec_frame.h
vendored
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "jxl/decode.h"
|
||||||
#include "lib/jxl/base/compiler_specific.h"
|
#include "lib/jxl/base/compiler_specific.h"
|
||||||
#include "lib/jxl/base/data_parallel.h"
|
#include "lib/jxl/base/data_parallel.h"
|
||||||
#include "lib/jxl/base/span.h"
|
#include "lib/jxl/base/span.h"
|
||||||
@ -159,7 +160,7 @@ class FrameDecoder {
|
|||||||
dec_state_->rgb_output = rgb_output;
|
dec_state_->rgb_output = rgb_output;
|
||||||
dec_state_->rgb_output_is_rgba = is_rgba;
|
dec_state_->rgb_output_is_rgba = is_rgba;
|
||||||
dec_state_->rgb_stride = stride;
|
dec_state_->rgb_stride = stride;
|
||||||
JXL_ASSERT(dec_state_->pixel_callback == nullptr);
|
JXL_ASSERT(!dec_state_->pixel_callback.IsPresent());
|
||||||
#if !JXL_HIGH_PRECISION
|
#if !JXL_HIGH_PRECISION
|
||||||
if (decoded_->metadata()->xyb_encoded &&
|
if (decoded_->metadata()->xyb_encoded &&
|
||||||
dec_state_->output_encoding_info.color_encoding.IsSRGB() &&
|
dec_state_->output_encoding_info.color_encoding.IsSRGB() &&
|
||||||
@ -181,12 +182,10 @@ class FrameDecoder {
|
|||||||
// orientation. Performing this operation is not yet supported, so this
|
// orientation. Performing this operation is not yet supported, so this
|
||||||
// results in not setting the buffer if the image has a non-identity EXIF
|
// results in not setting the buffer if the image has a non-identity EXIF
|
||||||
// orientation. When outputting to the ImageBundle, no orientation is undone.
|
// orientation. When outputting to the ImageBundle, no orientation is undone.
|
||||||
void MaybeSetFloatCallback(
|
void MaybeSetFloatCallback(const PixelCallback& pixel_callback, bool is_rgba,
|
||||||
const std::function<void(const float* pixels, size_t x, size_t y,
|
bool undo_orientation) const {
|
||||||
size_t num_pixels)>& cb,
|
|
||||||
bool is_rgba, bool undo_orientation) const {
|
|
||||||
if (!CanDoLowMemoryPath(undo_orientation)) return;
|
if (!CanDoLowMemoryPath(undo_orientation)) return;
|
||||||
dec_state_->pixel_callback = cb;
|
dec_state_->pixel_callback = pixel_callback;
|
||||||
dec_state_->rgb_output_is_rgba = is_rgba;
|
dec_state_->rgb_output_is_rgba = is_rgba;
|
||||||
JXL_ASSERT(dec_state_->rgb_output == nullptr);
|
JXL_ASSERT(dec_state_->rgb_output == nullptr);
|
||||||
}
|
}
|
||||||
@ -196,7 +195,7 @@ class FrameDecoder {
|
|||||||
// callback has been used.
|
// callback has been used.
|
||||||
bool HasRGBBuffer() const {
|
bool HasRGBBuffer() const {
|
||||||
return dec_state_->rgb_output != nullptr ||
|
return dec_state_->rgb_output != nullptr ||
|
||||||
dec_state_->pixel_callback != nullptr;
|
dec_state_->pixel_callback.IsPresent();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -216,18 +215,19 @@ class FrameDecoder {
|
|||||||
// `GetStorageLocation` must be smaller than the `num_threads` value passed
|
// `GetStorageLocation` must be smaller than the `num_threads` value passed
|
||||||
// here. The value of `task` passed to `GetStorageLocation` must be smaller
|
// here. The value of `task` passed to `GetStorageLocation` must be smaller
|
||||||
// than the value of `num_tasks` passed here.
|
// than the value of `num_tasks` passed here.
|
||||||
void PrepareStorage(size_t num_threads, size_t num_tasks) {
|
Status PrepareStorage(size_t num_threads, size_t num_tasks) {
|
||||||
size_t storage_size = std::min(num_threads, num_tasks);
|
size_t storage_size = std::min(num_threads, num_tasks);
|
||||||
if (storage_size > group_dec_caches_.size()) {
|
if (storage_size > group_dec_caches_.size()) {
|
||||||
group_dec_caches_.resize(storage_size);
|
group_dec_caches_.resize(storage_size);
|
||||||
}
|
}
|
||||||
use_task_id_ = num_threads > num_tasks;
|
use_task_id_ = num_threads > num_tasks;
|
||||||
if (dec_state_->render_pipeline) {
|
if (dec_state_->render_pipeline) {
|
||||||
dec_state_->render_pipeline->PrepareForThreads(
|
JXL_RETURN_IF_ERROR(dec_state_->render_pipeline->PrepareForThreads(
|
||||||
storage_size,
|
storage_size,
|
||||||
/*use_group_ids=*/modular_frame_decoder_.UsesFullImage() &&
|
/*use_group_ids=*/modular_frame_decoder_.UsesFullImage() &&
|
||||||
frame_header_.encoding == FrameEncoding::kVarDCT);
|
frame_header_.encoding == FrameEncoding::kVarDCT));
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetStorageLocation(size_t thread, size_t task) {
|
size_t GetStorageLocation(size_t thread, size_t task) {
|
||||||
@ -242,18 +242,8 @@ class FrameDecoder {
|
|||||||
// (uint8 output buffer or float pixel callback).
|
// (uint8 output buffer or float pixel callback).
|
||||||
// TODO(veluca): reduce this set of restrictions.
|
// TODO(veluca): reduce this set of restrictions.
|
||||||
bool CanDoLowMemoryPath(bool undo_orientation) const {
|
bool CanDoLowMemoryPath(bool undo_orientation) const {
|
||||||
if (undo_orientation &&
|
return !(undo_orientation &&
|
||||||
decoded_->metadata()->GetOrientation() != Orientation::kIdentity) {
|
decoded_->metadata()->GetOrientation() != Orientation::kIdentity);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (NeedsBlending(dec_state_)) return false;
|
|
||||||
if (frame_header_.CanBeReferenced()) return false;
|
|
||||||
if (render_spotcolors_ &&
|
|
||||||
decoded_->metadata()->Find(ExtraChannel::kSpotColor)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (decoded_->AlphaIsPremultiplied()) return false;
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PassesDecoderState* dec_state_;
|
PassesDecoderState* dec_state_;
|
||||||
|
2
third_party/jpeg-xl/lib/jxl/dec_group.cc
vendored
2
third_party/jpeg-xl/lib/jxl/dec_group.cc
vendored
@ -740,7 +740,7 @@ Status DecodeGroup(BitReader* JXL_RESTRICT* JXL_RESTRICT readers,
|
|||||||
// Arguments set to 0/nullptr are not used.
|
// Arguments set to 0/nullptr are not used.
|
||||||
dec_state->upsampler8x->ProcessRow(input_rows, output_rows,
|
dec_state->upsampler8x->ProcessRow(input_rows, output_rows,
|
||||||
/*xextra=*/0, src_rect.xsize(), 0, 0,
|
/*xextra=*/0, src_rect.xsize(), 0, 0,
|
||||||
nullptr);
|
thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
66
third_party/jpeg-xl/lib/jxl/dec_modular.cc
vendored
66
third_party/jpeg-xl/lib/jxl/dec_modular.cc
vendored
@ -89,6 +89,16 @@ HWY_EXPORT(MultiplySum); // Local function
|
|||||||
HWY_EXPORT(RgbFromSingle); // Local function
|
HWY_EXPORT(RgbFromSingle); // Local function
|
||||||
HWY_EXPORT(SingleFromSingle); // Local function
|
HWY_EXPORT(SingleFromSingle); // Local function
|
||||||
|
|
||||||
|
// Slow conversion using double precision multiplication, only
|
||||||
|
// needed when the bit depth is too high for single precision
|
||||||
|
void SingleFromSingleAccurate(const size_t xsize,
|
||||||
|
const pixel_type* const JXL_RESTRICT row_in,
|
||||||
|
const double factor, float* row_out) {
|
||||||
|
for (size_t x = 0; x < xsize; x++) {
|
||||||
|
row_out[x] = row_in[x] * factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// convert custom [bits]-bit float (with [exp_bits] exponent bits) stored as int
|
// convert custom [bits]-bit float (with [exp_bits] exponent bits) stored as int
|
||||||
// back to binary32 float
|
// back to binary32 float
|
||||||
void int_to_float(const pixel_type* const JXL_RESTRICT row_in,
|
void int_to_float(const pixel_type* const JXL_RESTRICT row_in,
|
||||||
@ -457,9 +467,9 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
|
|||||||
const auto* metadata = frame_header.nonserialized_metadata;
|
const auto* metadata = frame_header.nonserialized_metadata;
|
||||||
JXL_CHECK(gi.transform.empty());
|
JXL_CHECK(gi.transform.empty());
|
||||||
|
|
||||||
auto get_row = [&](Rect r, size_t c, size_t y) {
|
auto get_row = [&](size_t c, size_t y) {
|
||||||
return render_pipeline_input.GetBuffer(c).second.Row(
|
const auto& buffer = render_pipeline_input.GetBuffer(c);
|
||||||
render_pipeline_input.GetBuffer(c).first, y);
|
return buffer.second.Row(buffer.first, y);
|
||||||
};
|
};
|
||||||
|
|
||||||
size_t c = 0;
|
size_t c = 0;
|
||||||
@ -470,9 +480,9 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
|
|||||||
const bool fp = metadata->m.bit_depth.floating_point_sample &&
|
const bool fp = metadata->m.bit_depth.floating_point_sample &&
|
||||||
frame_header.color_transform != ColorTransform::kXYB;
|
frame_header.color_transform != ColorTransform::kXYB;
|
||||||
for (; c < 3; c++) {
|
for (; c < 3; c++) {
|
||||||
float factor = full_image.bitdepth < 32
|
double factor = full_image.bitdepth < 32
|
||||||
? 1.f / ((1u << full_image.bitdepth) - 1)
|
? 1.0 / ((1u << full_image.bitdepth) - 1)
|
||||||
: 0;
|
: 0;
|
||||||
size_t c_in = c;
|
size_t c_in = c;
|
||||||
if (frame_header.color_transform == ColorTransform::kXYB) {
|
if (frame_header.color_transform == ColorTransform::kXYB) {
|
||||||
factor = dec_state->shared->matrices.DCQuants()[c];
|
factor = dec_state->shared->matrices.DCQuants()[c];
|
||||||
@ -513,7 +523,7 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
|
|||||||
mr.Row(&ch_in.plane, y);
|
mr.Row(&ch_in.plane, y);
|
||||||
const pixel_type* const JXL_RESTRICT row_in_Y =
|
const pixel_type* const JXL_RESTRICT row_in_Y =
|
||||||
mr.Row(&gi.channel[0].plane, y);
|
mr.Row(&gi.channel[0].plane, y);
|
||||||
float* const JXL_RESTRICT row_out = get_row(r, c, y);
|
float* const JXL_RESTRICT row_out = get_row(c, y);
|
||||||
HWY_DYNAMIC_DISPATCH(MultiplySum)
|
HWY_DYNAMIC_DISPATCH(MultiplySum)
|
||||||
(xsize_shifted, row_in, row_in_Y, factor, row_out);
|
(xsize_shifted, row_in, row_in_Y, factor, row_out);
|
||||||
},
|
},
|
||||||
@ -529,11 +539,11 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
|
|||||||
mr.Row(&ch_in.plane, y);
|
mr.Row(&ch_in.plane, y);
|
||||||
if (rgb_from_gray) {
|
if (rgb_from_gray) {
|
||||||
for (size_t cc = 0; cc < 3; cc++) {
|
for (size_t cc = 0; cc < 3; cc++) {
|
||||||
float* const JXL_RESTRICT row_out = get_row(r, cc, y);
|
float* const JXL_RESTRICT row_out = get_row(cc, y);
|
||||||
int_to_float(row_in, row_out, xsize_shifted, bits, exp_bits);
|
int_to_float(row_in, row_out, xsize_shifted, bits, exp_bits);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
float* const JXL_RESTRICT row_out = get_row(r, c, y);
|
float* const JXL_RESTRICT row_out = get_row(c, y);
|
||||||
int_to_float(row_in, row_out, xsize_shifted, bits, exp_bits);
|
int_to_float(row_in, row_out, xsize_shifted, bits, exp_bits);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -546,13 +556,27 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
|
|||||||
const pixel_type* const JXL_RESTRICT row_in =
|
const pixel_type* const JXL_RESTRICT row_in =
|
||||||
mr.Row(&ch_in.plane, y);
|
mr.Row(&ch_in.plane, y);
|
||||||
if (rgb_from_gray) {
|
if (rgb_from_gray) {
|
||||||
HWY_DYNAMIC_DISPATCH(RgbFromSingle)
|
if (full_image.bitdepth < 23) {
|
||||||
(xsize_shifted, row_in, factor, get_row(r, 0, y),
|
HWY_DYNAMIC_DISPATCH(RgbFromSingle)
|
||||||
get_row(r, 1, y), get_row(r, 2, y));
|
(xsize_shifted, row_in, factor, get_row(0, y), get_row(1, y),
|
||||||
|
get_row(2, y));
|
||||||
|
} else {
|
||||||
|
SingleFromSingleAccurate(xsize_shifted, row_in, factor,
|
||||||
|
get_row(0, y));
|
||||||
|
SingleFromSingleAccurate(xsize_shifted, row_in, factor,
|
||||||
|
get_row(1, y));
|
||||||
|
SingleFromSingleAccurate(xsize_shifted, row_in, factor,
|
||||||
|
get_row(2, y));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
float* const JXL_RESTRICT row_out = get_row(r, c, y);
|
float* const JXL_RESTRICT row_out = get_row(c, y);
|
||||||
HWY_DYNAMIC_DISPATCH(SingleFromSingle)
|
if (full_image.bitdepth < 23) {
|
||||||
(xsize_shifted, row_in, factor, row_out);
|
HWY_DYNAMIC_DISPATCH(SingleFromSingle)
|
||||||
|
(xsize_shifted, row_in, factor, row_out);
|
||||||
|
} else {
|
||||||
|
SingleFromSingleAccurate(xsize_shifted, row_in, factor,
|
||||||
|
row_out);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ModularIntToFloat"));
|
"ModularIntToFloat"));
|
||||||
@ -572,7 +596,7 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
|
|||||||
int exp_bits = eci.bit_depth.exponent_bits_per_sample;
|
int exp_bits = eci.bit_depth.exponent_bits_per_sample;
|
||||||
bool fp = eci.bit_depth.floating_point_sample;
|
bool fp = eci.bit_depth.floating_point_sample;
|
||||||
JXL_ASSERT(fp || bits < 32);
|
JXL_ASSERT(fp || bits < 32);
|
||||||
const float mul = fp ? 0 : (1.0f / ((1u << bits) - 1));
|
const double factor = fp ? 0 : (1.0 / ((1u << bits) - 1));
|
||||||
JXL_ASSERT(c < gi.channel.size());
|
JXL_ASSERT(c < gi.channel.size());
|
||||||
Channel& ch_in = gi.channel[c];
|
Channel& ch_in = gi.channel[c];
|
||||||
Rect r = render_pipeline_input.GetBuffer(3 + ec).second;
|
Rect r = render_pipeline_input.GetBuffer(3 + ec).second;
|
||||||
@ -589,8 +613,11 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
|
|||||||
if (fp) {
|
if (fp) {
|
||||||
int_to_float(row_in, row_out, r.xsize(), bits, exp_bits);
|
int_to_float(row_in, row_out, r.xsize(), bits, exp_bits);
|
||||||
} else {
|
} else {
|
||||||
for (size_t x = 0; x < r.xsize(); ++x) {
|
if (full_image.bitdepth < 23) {
|
||||||
row_out[x] = row_in[x] * mul;
|
HWY_DYNAMIC_DISPATCH(SingleFromSingle)
|
||||||
|
(r.xsize(), row_in, factor, row_out);
|
||||||
|
} else {
|
||||||
|
SingleFromSingleAccurate(r.xsize(), row_in, factor, row_out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -621,11 +648,10 @@ Status ModularFrameDecoder::FinalizeDecoding(PassesDecoderState* dec_state,
|
|||||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||||
pool, 0, dec_state->shared->frame_dim.num_groups,
|
pool, 0, dec_state->shared->frame_dim.num_groups,
|
||||||
[&](size_t num_threads) {
|
[&](size_t num_threads) {
|
||||||
dec_state->render_pipeline->PrepareForThreads(
|
return dec_state->render_pipeline->PrepareForThreads(
|
||||||
num_threads,
|
num_threads,
|
||||||
/*use_group_ids=*/dec_state->shared->frame_header.encoding ==
|
/*use_group_ids=*/dec_state->shared->frame_header.encoding ==
|
||||||
FrameEncoding::kVarDCT);
|
FrameEncoding::kVarDCT);
|
||||||
return true;
|
|
||||||
},
|
},
|
||||||
[&](const uint32_t group, size_t thread_id) {
|
[&](const uint32_t group, size_t thread_id) {
|
||||||
RenderPipelineInput input =
|
RenderPipelineInput input =
|
||||||
|
192
third_party/jpeg-xl/lib/jxl/dec_noise.cc
vendored
192
third_party/jpeg-xl/lib/jxl/dec_noise.cc
vendored
@ -85,182 +85,6 @@ void RandomImage(Xorshift128Plus* rng, const Rect& rect,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// [0, max_value]
|
|
||||||
template <class D, class V>
|
|
||||||
static HWY_INLINE V Clamp0ToMax(D d, const V x, const V max_value) {
|
|
||||||
const auto clamped = Min(x, max_value);
|
|
||||||
return ZeroIfNegative(clamped);
|
|
||||||
}
|
|
||||||
|
|
||||||
// x is in [0+delta, 1+delta], delta ~= 0.06
|
|
||||||
template <class StrengthEval>
|
|
||||||
typename StrengthEval::V NoiseStrength(const StrengthEval& eval,
|
|
||||||
const typename StrengthEval::V x) {
|
|
||||||
return Clamp0ToMax(D(), eval(x), Set(D(), 1.0f));
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(veluca): SIMD-fy.
|
|
||||||
class StrengthEvalLut {
|
|
||||||
public:
|
|
||||||
using V = Vec<D>;
|
|
||||||
|
|
||||||
explicit StrengthEvalLut(const NoiseParams& noise_params)
|
|
||||||
#if HWY_TARGET == HWY_SCALAR
|
|
||||||
: noise_params_(noise_params)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
#if HWY_TARGET != HWY_SCALAR
|
|
||||||
uint32_t lut[8];
|
|
||||||
memcpy(lut, noise_params.lut, sizeof(lut));
|
|
||||||
for (size_t i = 0; i < 8; i++) {
|
|
||||||
low16_lut[2 * i] = (lut[i] >> 0) & 0xFF;
|
|
||||||
low16_lut[2 * i + 1] = (lut[i] >> 8) & 0xFF;
|
|
||||||
high16_lut[2 * i] = (lut[i] >> 16) & 0xFF;
|
|
||||||
high16_lut[2 * i + 1] = (lut[i] >> 24) & 0xFF;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
V operator()(const V vx) const {
|
|
||||||
constexpr size_t kScale = NoiseParams::kNumNoisePoints - 2;
|
|
||||||
auto scaled_vx = Max(Zero(D()), vx * Set(D(), kScale));
|
|
||||||
auto floor_x = Floor(scaled_vx);
|
|
||||||
auto frac_x = scaled_vx - floor_x;
|
|
||||||
floor_x = IfThenElse(scaled_vx >= Set(D(), kScale + 1), Set(D(), kScale),
|
|
||||||
floor_x);
|
|
||||||
frac_x = IfThenElse(scaled_vx >= Set(D(), kScale + 1), Set(D(), 1), frac_x);
|
|
||||||
auto floor_x_int = ConvertTo(DI(), floor_x);
|
|
||||||
#if HWY_TARGET == HWY_SCALAR
|
|
||||||
auto low = Set(D(), noise_params_.lut[floor_x_int.raw]);
|
|
||||||
auto hi = Set(D(), noise_params_.lut[floor_x_int.raw + 1]);
|
|
||||||
#else
|
|
||||||
// Set each lane's bytes to {0, 0, 2x+1, 2x}.
|
|
||||||
auto floorx_indices_low =
|
|
||||||
floor_x_int * Set(DI(), 0x0202) + Set(DI(), 0x0100);
|
|
||||||
// Set each lane's bytes to {2x+1, 2x, 0, 0}.
|
|
||||||
auto floorx_indices_hi =
|
|
||||||
floor_x_int * Set(DI(), 0x02020000) + Set(DI(), 0x01000000);
|
|
||||||
// load LUT
|
|
||||||
auto low16 = BitCast(DI(), LoadDup128(DI8(), low16_lut));
|
|
||||||
auto lowm = Set(DI(), 0xFFFF);
|
|
||||||
auto hi16 = BitCast(DI(), LoadDup128(DI8(), high16_lut));
|
|
||||||
auto him = Set(DI(), 0xFFFF0000);
|
|
||||||
// low = noise_params.lut[floor_x]
|
|
||||||
auto low =
|
|
||||||
BitCast(D(), (TableLookupBytes(low16, floorx_indices_low) & lowm) |
|
|
||||||
(TableLookupBytes(hi16, floorx_indices_hi) & him));
|
|
||||||
// hi = noise_params.lut[floor_x+1]
|
|
||||||
floorx_indices_low += Set(DI(), 0x0202);
|
|
||||||
floorx_indices_hi += Set(DI(), 0x02020000);
|
|
||||||
auto hi =
|
|
||||||
BitCast(D(), (TableLookupBytes(low16, floorx_indices_low) & lowm) |
|
|
||||||
(TableLookupBytes(hi16, floorx_indices_hi) & him));
|
|
||||||
#endif
|
|
||||||
return MulAdd(hi - low, frac_x, low);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
#if HWY_TARGET != HWY_SCALAR
|
|
||||||
// noise_params.lut transformed into two 16-bit lookup tables.
|
|
||||||
HWY_ALIGN uint8_t high16_lut[16];
|
|
||||||
HWY_ALIGN uint8_t low16_lut[16];
|
|
||||||
#else
|
|
||||||
const NoiseParams& noise_params_;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class D>
|
|
||||||
void AddNoiseToRGB(const D d, const Vec<D> rnd_noise_r,
|
|
||||||
const Vec<D> rnd_noise_g, const Vec<D> rnd_noise_cor,
|
|
||||||
const Vec<D> noise_strength_g, const Vec<D> noise_strength_r,
|
|
||||||
float ytox, float ytob, float* JXL_RESTRICT out_x,
|
|
||||||
float* JXL_RESTRICT out_y, float* JXL_RESTRICT out_b) {
|
|
||||||
const auto kRGCorr = Set(d, 0.9921875f); // 127/128
|
|
||||||
const auto kRGNCorr = Set(d, 0.0078125f); // 1/128
|
|
||||||
|
|
||||||
const auto red_noise = kRGNCorr * rnd_noise_r * noise_strength_r +
|
|
||||||
kRGCorr * rnd_noise_cor * noise_strength_r;
|
|
||||||
const auto green_noise = kRGNCorr * rnd_noise_g * noise_strength_g +
|
|
||||||
kRGCorr * rnd_noise_cor * noise_strength_g;
|
|
||||||
|
|
||||||
auto vx = Load(d, out_x);
|
|
||||||
auto vy = Load(d, out_y);
|
|
||||||
auto vb = Load(d, out_b);
|
|
||||||
|
|
||||||
vx += red_noise - green_noise + Set(d, ytox) * (red_noise + green_noise);
|
|
||||||
vy += red_noise + green_noise;
|
|
||||||
vb += Set(d, ytob) * (red_noise + green_noise);
|
|
||||||
|
|
||||||
Store(vx, d, out_x);
|
|
||||||
Store(vy, d, out_y);
|
|
||||||
Store(vb, d, out_b);
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddNoise(const NoiseParams& noise_params, const Rect& noise_rect,
|
|
||||||
const Image3F& noise, const Rect& opsin_rect,
|
|
||||||
const ColorCorrelationMap& cmap, Image3F* opsin) {
|
|
||||||
if (!noise_params.HasAny()) return;
|
|
||||||
const StrengthEvalLut noise_model(noise_params);
|
|
||||||
D d;
|
|
||||||
const auto half = Set(d, 0.5f);
|
|
||||||
|
|
||||||
const size_t xsize = opsin_rect.xsize();
|
|
||||||
const size_t ysize = opsin_rect.ysize();
|
|
||||||
|
|
||||||
// With the prior subtract-random Laplacian approximation, rnd_* ranges were
|
|
||||||
// about [-1.5, 1.6]; Laplacian3 about doubles this to [-3.6, 3.6], so the
|
|
||||||
// normalizer is half of what it was before (0.5).
|
|
||||||
const auto norm_const = Set(d, 0.22f);
|
|
||||||
|
|
||||||
float ytox = cmap.YtoXRatio(0);
|
|
||||||
float ytob = cmap.YtoBRatio(0);
|
|
||||||
|
|
||||||
const size_t xsize_v = RoundUpTo(xsize, Lanes(d));
|
|
||||||
|
|
||||||
for (size_t y = 0; y < ysize; ++y) {
|
|
||||||
float* JXL_RESTRICT row_x = opsin_rect.PlaneRow(opsin, 0, y);
|
|
||||||
float* JXL_RESTRICT row_y = opsin_rect.PlaneRow(opsin, 1, y);
|
|
||||||
float* JXL_RESTRICT row_b = opsin_rect.PlaneRow(opsin, 2, y);
|
|
||||||
const float* JXL_RESTRICT row_rnd_r = noise_rect.ConstPlaneRow(noise, 0, y);
|
|
||||||
const float* JXL_RESTRICT row_rnd_g = noise_rect.ConstPlaneRow(noise, 1, y);
|
|
||||||
const float* JXL_RESTRICT row_rnd_c = noise_rect.ConstPlaneRow(noise, 2, y);
|
|
||||||
// Needed by the calls to Floor() in StrengthEvalLut. Only arithmetic and
|
|
||||||
// shuffles are otherwise done on the data, so this is safe.
|
|
||||||
msan::UnpoisonMemory(row_x + xsize, (xsize_v - xsize) * sizeof(float));
|
|
||||||
msan::UnpoisonMemory(row_y + xsize, (xsize_v - xsize) * sizeof(float));
|
|
||||||
for (size_t x = 0; x < xsize; x += Lanes(d)) {
|
|
||||||
const auto vx = Load(d, row_x + x);
|
|
||||||
const auto vy = Load(d, row_y + x);
|
|
||||||
const auto in_g = vy - vx;
|
|
||||||
const auto in_r = vy + vx;
|
|
||||||
const auto noise_strength_g = NoiseStrength(noise_model, in_g * half);
|
|
||||||
const auto noise_strength_r = NoiseStrength(noise_model, in_r * half);
|
|
||||||
const auto addit_rnd_noise_red = Load(d, row_rnd_r + x) * norm_const;
|
|
||||||
const auto addit_rnd_noise_green = Load(d, row_rnd_g + x) * norm_const;
|
|
||||||
const auto addit_rnd_noise_correlated =
|
|
||||||
Load(d, row_rnd_c + x) * norm_const;
|
|
||||||
AddNoiseToRGB(D(), addit_rnd_noise_red, addit_rnd_noise_green,
|
|
||||||
addit_rnd_noise_correlated, noise_strength_g,
|
|
||||||
noise_strength_r, ytox, ytob, row_x + x, row_y + x,
|
|
||||||
row_b + x);
|
|
||||||
}
|
|
||||||
msan::PoisonMemory(row_x + xsize, (xsize_v - xsize) * sizeof(float));
|
|
||||||
msan::PoisonMemory(row_y + xsize, (xsize_v - xsize) * sizeof(float));
|
|
||||||
msan::PoisonMemory(row_b + xsize, (xsize_v - xsize) * sizeof(float));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RandomImage3(size_t visible_frame_index, size_t nonvisible_frame_index,
|
|
||||||
size_t x0, size_t y0, const Rect& rect,
|
|
||||||
Image3F* JXL_RESTRICT noise) {
|
|
||||||
HWY_ALIGN Xorshift128Plus rng(visible_frame_index, nonvisible_frame_index, x0,
|
|
||||||
y0);
|
|
||||||
RandomImage(&rng, rect, &noise->Plane(0));
|
|
||||||
RandomImage(&rng, rect, &noise->Plane(1));
|
|
||||||
RandomImage(&rng, rect, &noise->Plane(2));
|
|
||||||
}
|
|
||||||
|
|
||||||
void Random3Planes(size_t visible_frame_index, size_t nonvisible_frame_index,
|
void Random3Planes(size_t visible_frame_index, size_t nonvisible_frame_index,
|
||||||
size_t x0, size_t y0, const std::pair<ImageF*, Rect>& plane0,
|
size_t x0, size_t y0, const std::pair<ImageF*, Rect>& plane0,
|
||||||
const std::pair<ImageF*, Rect>& plane1,
|
const std::pair<ImageF*, Rect>& plane1,
|
||||||
@ -280,22 +104,6 @@ HWY_AFTER_NAMESPACE();
|
|||||||
#if HWY_ONCE
|
#if HWY_ONCE
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
||||||
HWY_EXPORT(AddNoise);
|
|
||||||
void AddNoise(const NoiseParams& noise_params, const Rect& noise_rect,
|
|
||||||
const Image3F& noise, const Rect& opsin_rect,
|
|
||||||
const ColorCorrelationMap& cmap, Image3F* opsin) {
|
|
||||||
return HWY_DYNAMIC_DISPATCH(AddNoise)(noise_params, noise_rect, noise,
|
|
||||||
opsin_rect, cmap, opsin);
|
|
||||||
}
|
|
||||||
|
|
||||||
HWY_EXPORT(RandomImage3);
|
|
||||||
void RandomImage3(size_t visible_frame_index, size_t nonvisible_frame_index,
|
|
||||||
size_t x0, size_t y0, const Rect& rect,
|
|
||||||
Image3F* JXL_RESTRICT noise) {
|
|
||||||
return HWY_DYNAMIC_DISPATCH(RandomImage3)(
|
|
||||||
visible_frame_index, nonvisible_frame_index, x0, y0, rect, noise);
|
|
||||||
}
|
|
||||||
|
|
||||||
HWY_EXPORT(Random3Planes);
|
HWY_EXPORT(Random3Planes);
|
||||||
void Random3Planes(size_t visible_frame_index, size_t nonvisible_frame_index,
|
void Random3Planes(size_t visible_frame_index, size_t nonvisible_frame_index,
|
||||||
size_t x0, size_t y0, const std::pair<ImageF*, Rect>& plane0,
|
size_t x0, size_t y0, const std::pair<ImageF*, Rect>& plane0,
|
||||||
|
10
third_party/jpeg-xl/lib/jxl/dec_noise.h
vendored
10
third_party/jpeg-xl/lib/jxl/dec_noise.h
vendored
@ -20,16 +20,6 @@
|
|||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
|
||||||
// Add a noise to Opsin image, loading generated random noise from `noise_rect`
|
|
||||||
// in `noise`.
|
|
||||||
void AddNoise(const NoiseParams& noise_params, const Rect& noise_rect,
|
|
||||||
const Image3F& noise, const Rect& opsin_rect,
|
|
||||||
const ColorCorrelationMap& cmap, Image3F* opsin);
|
|
||||||
|
|
||||||
void RandomImage3(size_t visible_frame_index, size_t nonvisible_frame_index,
|
|
||||||
size_t x0, size_t y0, const Rect& rect,
|
|
||||||
Image3F* JXL_RESTRICT noise);
|
|
||||||
|
|
||||||
void Random3Planes(size_t visible_frame_index, size_t nonvisible_frame_index,
|
void Random3Planes(size_t visible_frame_index, size_t nonvisible_frame_index,
|
||||||
size_t x0, size_t y0, const std::pair<ImageF*, Rect>& plane0,
|
size_t x0, size_t y0, const std::pair<ImageF*, Rect>& plane0,
|
||||||
const std::pair<ImageF*, Rect>& plane1,
|
const std::pair<ImageF*, Rect>& plane1,
|
||||||
|
@ -156,9 +156,6 @@ Status PatchDictionary::Decode(BitReader* br, size_t xsize, size_t ysize,
|
|||||||
if (!decoder.CheckANSFinalState()) {
|
if (!decoder.CheckANSFinalState()) {
|
||||||
return JXL_FAILURE("ANS checksum failure.");
|
return JXL_FAILURE("ANS checksum failure.");
|
||||||
}
|
}
|
||||||
if (!HasAny()) {
|
|
||||||
return JXL_FAILURE("Decoded patch dictionary but got none");
|
|
||||||
}
|
|
||||||
|
|
||||||
ComputePatchCache();
|
ComputePatchCache();
|
||||||
return true;
|
return true;
|
||||||
@ -237,51 +234,4 @@ void PatchDictionary::AddOneRow(float* const* inout, size_t y, size_t x0,
|
|||||||
shared_->metadata->m.extra_channel_info);
|
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;
|
|
||||||
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);
|
|
||||||
for (size_t y = image_rect.y0(); y < image_rect.y0() + image_rect.ysize();
|
|
||||||
y++) {
|
|
||||||
if (y + 1 >= patch_starts_.size()) continue;
|
|
||||||
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 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 >= image_rect.x0() + image_rect.xsize()) continue;
|
|
||||||
if (bx + xsize < image_rect.x0()) continue;
|
|
||||||
size_t x0 = std::max(bx, image_rect.x0());
|
|
||||||
size_t x1 = std::min(bx + xsize, image_rect.x0() + image_rect.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;
|
|
||||||
bg_ptrs[c] = opsin_rect.PlaneRow(opsin, c, y - image_rect.y0()) + x0 -
|
|
||||||
image_rect.x0();
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
bg_ptrs[3 + i] = extra_channels[i] + x0 - image_rect.x0();
|
|
||||||
}
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
@ -126,11 +126,6 @@ class PatchDictionary {
|
|||||||
// to be located at position (x0, y) in the frame.
|
// 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;
|
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`.
|
|
||||||
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
|
// Returns dependencies of this patch dictionary on reference frame ids as a
|
||||||
// bit mask: bits 0-3 indicate reference frame 0-3.
|
// bit mask: bits 0-3 indicate reference frame 0-3.
|
||||||
int GetReferences() const;
|
int GetReferences() const;
|
||||||
|
133
third_party/jpeg-xl/lib/jxl/decode.cc
vendored
133
third_party/jpeg-xl/lib/jxl/decode.cc
vendored
@ -139,21 +139,17 @@ namespace {
|
|||||||
|
|
||||||
size_t BitsPerChannel(JxlDataType data_type) {
|
size_t BitsPerChannel(JxlDataType data_type) {
|
||||||
switch (data_type) {
|
switch (data_type) {
|
||||||
case JXL_TYPE_BOOLEAN:
|
|
||||||
return 1;
|
|
||||||
case JXL_TYPE_UINT8:
|
case JXL_TYPE_UINT8:
|
||||||
return 8;
|
return 8;
|
||||||
case JXL_TYPE_UINT16:
|
case JXL_TYPE_UINT16:
|
||||||
return 16;
|
return 16;
|
||||||
case JXL_TYPE_UINT32:
|
|
||||||
return 32;
|
|
||||||
case JXL_TYPE_FLOAT:
|
case JXL_TYPE_FLOAT:
|
||||||
return 32;
|
return 32;
|
||||||
case JXL_TYPE_FLOAT16:
|
case JXL_TYPE_FLOAT16:
|
||||||
return 16;
|
return 16;
|
||||||
// No default, give compiler error if new type not handled.
|
default:
|
||||||
|
return 0; // signals unhandled JxlDataType
|
||||||
}
|
}
|
||||||
return 0; // Indicate invalid data type.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class DecoderStage : uint32_t {
|
enum class DecoderStage : uint32_t {
|
||||||
@ -465,8 +461,15 @@ struct JxlDecoderStruct {
|
|||||||
// Owned by the caller, buffers for DC image and full resolution images
|
// Owned by the caller, buffers for DC image and full resolution images
|
||||||
void* preview_out_buffer;
|
void* preview_out_buffer;
|
||||||
void* image_out_buffer;
|
void* image_out_buffer;
|
||||||
JxlImageOutCallback image_out_callback;
|
JxlImageOutInitCallback image_out_init_callback;
|
||||||
void* image_out_opaque;
|
JxlImageOutRunCallback image_out_run_callback;
|
||||||
|
JxlImageOutDestroyCallback image_out_destroy_callback;
|
||||||
|
void* image_out_init_opaque;
|
||||||
|
struct SimpleImageOutCallback {
|
||||||
|
JxlImageOutCallback callback;
|
||||||
|
void* opaque;
|
||||||
|
};
|
||||||
|
SimpleImageOutCallback simple_image_out_callback;
|
||||||
|
|
||||||
size_t preview_out_size;
|
size_t preview_out_size;
|
||||||
size_t image_out_size;
|
size_t image_out_size;
|
||||||
@ -609,6 +612,11 @@ namespace {
|
|||||||
bool CheckSizeLimit(JxlDecoder* dec, size_t xsize, size_t ysize) {
|
bool CheckSizeLimit(JxlDecoder* dec, size_t xsize, size_t ysize) {
|
||||||
if (!dec->memory_limit_base) return true;
|
if (!dec->memory_limit_base) return true;
|
||||||
if (xsize == 0 || ysize == 0) return true;
|
if (xsize == 0 || ysize == 0) return true;
|
||||||
|
if (xsize >= dec->memory_limit_base || ysize >= dec->memory_limit_base) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Rough estimate of real row length.
|
||||||
|
xsize = jxl::DivCeil(xsize, 32) * 32;
|
||||||
size_t num_pixels = xsize * ysize;
|
size_t num_pixels = xsize * ysize;
|
||||||
if (num_pixels / xsize != ysize) return false; // overflow
|
if (num_pixels / xsize != ysize) return false; // overflow
|
||||||
if (num_pixels > dec->memory_limit_base) return false;
|
if (num_pixels > dec->memory_limit_base) return false;
|
||||||
@ -670,8 +678,10 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
|
|||||||
dec->image_out_buffer_set = false;
|
dec->image_out_buffer_set = false;
|
||||||
dec->preview_out_buffer = nullptr;
|
dec->preview_out_buffer = nullptr;
|
||||||
dec->image_out_buffer = nullptr;
|
dec->image_out_buffer = nullptr;
|
||||||
dec->image_out_callback = nullptr;
|
dec->image_out_init_callback = nullptr;
|
||||||
dec->image_out_opaque = nullptr;
|
dec->image_out_run_callback = nullptr;
|
||||||
|
dec->image_out_destroy_callback = nullptr;
|
||||||
|
dec->image_out_init_opaque = nullptr;
|
||||||
dec->preview_out_size = 0;
|
dec->preview_out_size = 0;
|
||||||
dec->image_out_size = 0;
|
dec->image_out_size = 0;
|
||||||
dec->extra_channel_output.clear();
|
dec->extra_channel_output.clear();
|
||||||
@ -731,7 +741,7 @@ JxlDecoder* JxlDecoderCreate(const JxlMemoryManager* memory_manager) {
|
|||||||
|
|
||||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||||
if (!memory_manager) {
|
if (!memory_manager) {
|
||||||
dec->memory_limit_base = 1 << 21;
|
dec->memory_limit_base = 53 << 16;
|
||||||
// Allow 5 x max_image_size processing units; every frame is accounted
|
// 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
|
// as W x H CPU processing units, so there could be numerous small frames
|
||||||
// or few larger ones.
|
// or few larger ones.
|
||||||
@ -746,9 +756,10 @@ JxlDecoder* JxlDecoderCreate(const JxlMemoryManager* memory_manager) {
|
|||||||
|
|
||||||
void JxlDecoderDestroy(JxlDecoder* dec) {
|
void JxlDecoderDestroy(JxlDecoder* dec) {
|
||||||
if (dec) {
|
if (dec) {
|
||||||
|
JxlMemoryManager local_memory_manager = dec->memory_manager;
|
||||||
// Call destructor directly since custom free function is used.
|
// Call destructor directly since custom free function is used.
|
||||||
dec->~JxlDecoder();
|
dec->~JxlDecoder();
|
||||||
jxl::MemoryManagerFree(&dec->memory_manager, dec);
|
jxl::MemoryManagerFree(&local_memory_manager, dec);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1051,7 +1062,7 @@ static JxlDecoderStatus ConvertImageInternal(
|
|||||||
const JxlDecoder* dec, const jxl::ImageBundle& frame,
|
const JxlDecoder* dec, const jxl::ImageBundle& frame,
|
||||||
const JxlPixelFormat& format, bool want_extra_channel,
|
const JxlPixelFormat& format, bool want_extra_channel,
|
||||||
size_t extra_channel_index, void* out_image, size_t out_size,
|
size_t extra_channel_index, void* out_image, size_t out_size,
|
||||||
JxlImageOutCallback out_callback, void* out_opaque) {
|
const PixelCallback& out_callback) {
|
||||||
// TODO(lode): handle mismatch of RGB/grayscale color profiles and pixel data
|
// TODO(lode): handle mismatch of RGB/grayscale color profiles and pixel data
|
||||||
// color/grayscale format
|
// color/grayscale format
|
||||||
const size_t stride = GetStride(dec, format);
|
const size_t stride = GetStride(dec, format);
|
||||||
@ -1065,19 +1076,17 @@ static JxlDecoderStatus ConvertImageInternal(
|
|||||||
|
|
||||||
jxl::Status status(true);
|
jxl::Status status(true);
|
||||||
if (want_extra_channel) {
|
if (want_extra_channel) {
|
||||||
status = jxl::ConvertToExternal(
|
JXL_ASSERT(extra_channel_index < frame.extra_channels().size());
|
||||||
frame.extra_channels()[extra_channel_index],
|
status = jxl::ConvertToExternal(frame.extra_channels()[extra_channel_index],
|
||||||
BitsPerChannel(format.data_type), float_format, format.endianness,
|
BitsPerChannel(format.data_type),
|
||||||
stride, dec->thread_pool.get(), out_image, out_size,
|
float_format, format.endianness, stride,
|
||||||
/*out_callback=*/out_callback,
|
dec->thread_pool.get(), out_image, out_size,
|
||||||
/*out_opaque=*/out_opaque, undo_orientation);
|
out_callback, undo_orientation);
|
||||||
} else {
|
} else {
|
||||||
status = jxl::ConvertToExternal(
|
status = jxl::ConvertToExternal(
|
||||||
frame, BitsPerChannel(format.data_type), float_format,
|
frame, BitsPerChannel(format.data_type), float_format,
|
||||||
format.num_channels, format.endianness, stride, dec->thread_pool.get(),
|
format.num_channels, format.endianness, stride, dec->thread_pool.get(),
|
||||||
out_image, out_size,
|
out_image, out_size, out_callback, undo_orientation);
|
||||||
/*out_callback=*/out_callback,
|
|
||||||
/*out_opaque=*/out_opaque, undo_orientation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return status ? JXL_DEC_SUCCESS : JXL_DEC_ERROR;
|
return status ? JXL_DEC_SUCCESS : JXL_DEC_ERROR;
|
||||||
@ -1252,8 +1261,8 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
|||||||
JxlDecoderStatus status = ConvertImageInternal(
|
JxlDecoderStatus status = ConvertImageInternal(
|
||||||
dec, ib, dec->preview_out_format, /*want_extra_channel=*/false,
|
dec, ib, dec->preview_out_format, /*want_extra_channel=*/false,
|
||||||
/*extra_channel_index=*/0, dec->preview_out_buffer,
|
/*extra_channel_index=*/0, dec->preview_out_buffer,
|
||||||
dec->preview_out_size, /*out_callback=*/nullptr,
|
dec->preview_out_size,
|
||||||
/*out_opaque=*/nullptr);
|
/*out_callback=*/{});
|
||||||
if (status != JXL_DEC_SUCCESS) return status;
|
if (status != JXL_DEC_SUCCESS) return status;
|
||||||
}
|
}
|
||||||
return JXL_DEC_PREVIEW_IMAGE;
|
return JXL_DEC_PREVIEW_IMAGE;
|
||||||
@ -1453,17 +1462,16 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
|||||||
|
|
||||||
// TODO(lode): Support more formats than just native endian float32 for
|
// TODO(lode): Support more formats than just native endian float32 for
|
||||||
// the low-memory callback path
|
// the low-memory callback path
|
||||||
if (dec->image_out_buffer_set && !!dec->image_out_callback &&
|
if (dec->image_out_buffer_set && !!dec->image_out_init_callback &&
|
||||||
|
!!dec->image_out_run_callback &&
|
||||||
dec->image_out_format.data_type == JXL_TYPE_FLOAT &&
|
dec->image_out_format.data_type == JXL_TYPE_FLOAT &&
|
||||||
dec->image_out_format.num_channels >= 3 && !swap_endianness &&
|
dec->image_out_format.num_channels >= 3 && !swap_endianness &&
|
||||||
dec->frame_dec_in_progress) {
|
dec->frame_dec_in_progress) {
|
||||||
bool is_rgba = dec->image_out_format.num_channels == 4;
|
bool is_rgba = dec->image_out_format.num_channels == 4;
|
||||||
dec->frame_dec->MaybeSetFloatCallback(
|
dec->frame_dec->MaybeSetFloatCallback(
|
||||||
[dec](const float* pixels, size_t x, size_t y, size_t num_pixels) {
|
PixelCallback{
|
||||||
JXL_DASSERT(num_pixels > 0);
|
dec->image_out_init_callback, dec->image_out_run_callback,
|
||||||
dec->image_out_callback(dec->image_out_opaque, x, y, num_pixels,
|
dec->image_out_destroy_callback, dec->image_out_init_opaque},
|
||||||
pixels);
|
|
||||||
},
|
|
||||||
is_rgba, !dec->keep_orientation);
|
is_rgba, !dec->keep_orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1557,21 +1565,30 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec, const uint8_t* in,
|
|||||||
dec, *dec->ib, dec->image_out_format,
|
dec, *dec->ib, dec->image_out_format,
|
||||||
/*want_extra_channel=*/false,
|
/*want_extra_channel=*/false,
|
||||||
/*extra_channel_index=*/0, dec->image_out_buffer,
|
/*extra_channel_index=*/0, dec->image_out_buffer,
|
||||||
dec->image_out_size, dec->image_out_callback,
|
dec->image_out_size,
|
||||||
dec->image_out_opaque);
|
PixelCallback{dec->image_out_init_callback,
|
||||||
|
dec->image_out_run_callback,
|
||||||
|
dec->image_out_destroy_callback,
|
||||||
|
dec->image_out_init_opaque});
|
||||||
if (status != JXL_DEC_SUCCESS) return status;
|
if (status != JXL_DEC_SUCCESS) return status;
|
||||||
}
|
}
|
||||||
dec->image_out_buffer_set = false;
|
dec->image_out_buffer_set = false;
|
||||||
|
|
||||||
|
bool has_ec = !dec->ib->extra_channels().empty();
|
||||||
for (size_t i = 0; i < dec->extra_channel_output.size(); ++i) {
|
for (size_t i = 0; i < dec->extra_channel_output.size(); ++i) {
|
||||||
void* buffer = dec->extra_channel_output[i].buffer;
|
void* buffer = dec->extra_channel_output[i].buffer;
|
||||||
// buffer nullptr indicates this extra channel is not requested
|
// buffer nullptr indicates this extra channel is not requested
|
||||||
if (!buffer) continue;
|
if (!buffer) continue;
|
||||||
|
if (!has_ec) {
|
||||||
|
JXL_WARNING(
|
||||||
|
"Extra channels are not supported when callback is used");
|
||||||
|
return JXL_DEC_ERROR;
|
||||||
|
}
|
||||||
const JxlPixelFormat* format = &dec->extra_channel_output[i].format;
|
const JxlPixelFormat* format = &dec->extra_channel_output[i].format;
|
||||||
JxlDecoderStatus status = ConvertImageInternal(
|
JxlDecoderStatus status = ConvertImageInternal(
|
||||||
dec, *dec->ib, *format,
|
dec, *dec->ib, *format,
|
||||||
/*want_extra_channel=*/true, i, buffer,
|
/*want_extra_channel=*/true, /*extra_channel_index=*/i, buffer,
|
||||||
dec->extra_channel_output[i].buffer_size, nullptr, nullptr);
|
dec->extra_channel_output[i].buffer_size, /*out_callback=*/{});
|
||||||
if (status != JXL_DEC_SUCCESS) return status;
|
if (status != JXL_DEC_SUCCESS) return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2385,17 +2402,11 @@ JxlDecoderStatus PrepareSizeCheck(const JxlDecoder* dec,
|
|||||||
if (format->num_channels > 4) {
|
if (format->num_channels > 4) {
|
||||||
return JXL_API_ERROR("More than 4 channels not supported");
|
return JXL_API_ERROR("More than 4 channels not supported");
|
||||||
}
|
}
|
||||||
if (format->data_type == JXL_TYPE_BOOLEAN) {
|
|
||||||
return JXL_API_ERROR("Boolean data type not yet supported");
|
|
||||||
}
|
|
||||||
if (format->data_type == JXL_TYPE_UINT32) {
|
|
||||||
return JXL_API_ERROR("uint32 data type not yet supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
*bits = BitsPerChannel(format->data_type);
|
*bits = BitsPerChannel(format->data_type);
|
||||||
|
|
||||||
if (*bits == 0) {
|
if (*bits == 0) {
|
||||||
return JXL_API_ERROR("Invalid data type");
|
return JXL_API_ERROR("Invalid/unsupported data type");
|
||||||
}
|
}
|
||||||
|
|
||||||
return JXL_DEC_SUCCESS;
|
return JXL_DEC_SUCCESS;
|
||||||
@ -2440,7 +2451,7 @@ JxlDecoderStatus JxlDecoderFlushImage(JxlDecoder* dec) {
|
|||||||
dec, *dec->ib, dec->image_out_format,
|
dec, *dec->ib, dec->image_out_format,
|
||||||
/*want_extra_channel=*/false,
|
/*want_extra_channel=*/false,
|
||||||
/*extra_channel_index=*/0, dec->image_out_buffer, dec->image_out_size,
|
/*extra_channel_index=*/0, dec->image_out_buffer, dec->image_out_size,
|
||||||
/*out_callback=*/nullptr, /*out_opaque=*/nullptr);
|
/*out_callback=*/{});
|
||||||
dec->ib->ShrinkTo(xsize, ysize);
|
dec->ib->ShrinkTo(xsize, ysize);
|
||||||
if (status != JXL_DEC_SUCCESS) return status;
|
if (status != JXL_DEC_SUCCESS) return status;
|
||||||
return JXL_DEC_SUCCESS;
|
return JXL_DEC_SUCCESS;
|
||||||
@ -2548,7 +2559,7 @@ JxlDecoderStatus JxlDecoderSetImageOutBuffer(JxlDecoder* dec,
|
|||||||
if (!dec->got_basic_info || !(dec->orig_events_wanted & JXL_DEC_FULL_IMAGE)) {
|
if (!dec->got_basic_info || !(dec->orig_events_wanted & JXL_DEC_FULL_IMAGE)) {
|
||||||
return JXL_API_ERROR("No image out buffer needed at this time");
|
return JXL_API_ERROR("No image out buffer needed at this time");
|
||||||
}
|
}
|
||||||
if (dec->image_out_buffer_set && !!dec->image_out_callback) {
|
if (dec->image_out_buffer_set && !!dec->image_out_run_callback) {
|
||||||
return JXL_API_ERROR(
|
return JXL_API_ERROR(
|
||||||
"Cannot change from image out callback to image out buffer");
|
"Cannot change from image out callback to image out buffer");
|
||||||
}
|
}
|
||||||
@ -2634,19 +2645,51 @@ JxlDecoderStatus JxlDecoderSetImageOutCallback(JxlDecoder* dec,
|
|||||||
const JxlPixelFormat* format,
|
const JxlPixelFormat* format,
|
||||||
JxlImageOutCallback callback,
|
JxlImageOutCallback callback,
|
||||||
void* opaque) {
|
void* opaque) {
|
||||||
|
dec->simple_image_out_callback.callback = callback;
|
||||||
|
dec->simple_image_out_callback.opaque = opaque;
|
||||||
|
const auto init_callback =
|
||||||
|
+[](void* init_opaque, size_t num_threads, size_t num_pixels_per_thread) {
|
||||||
|
// No initialization to do, just reuse init_opaque as run_opaque.
|
||||||
|
return init_opaque;
|
||||||
|
};
|
||||||
|
const auto run_callback =
|
||||||
|
+[](void* run_opaque, size_t thread_id, size_t x, size_t y,
|
||||||
|
size_t num_pixels, const void* pixels) {
|
||||||
|
const auto* const simple_callback =
|
||||||
|
static_cast<const JxlDecoder::SimpleImageOutCallback*>(run_opaque);
|
||||||
|
simple_callback->callback(simple_callback->opaque, x, y, num_pixels,
|
||||||
|
pixels);
|
||||||
|
};
|
||||||
|
const auto destroy_callback = +[](void* run_opaque) {};
|
||||||
|
return JxlDecoderSetMultithreadedImageOutCallback(
|
||||||
|
dec, format, init_callback, run_callback,
|
||||||
|
/*destroy_callback=*/destroy_callback, &dec->simple_image_out_callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
JxlDecoderStatus JxlDecoderSetMultithreadedImageOutCallback(
|
||||||
|
JxlDecoder* dec, const JxlPixelFormat* format,
|
||||||
|
JxlImageOutInitCallback init_callback, JxlImageOutRunCallback run_callback,
|
||||||
|
JxlImageOutDestroyCallback destroy_callback, void* init_opaque) {
|
||||||
if (dec->image_out_buffer_set && !!dec->image_out_buffer) {
|
if (dec->image_out_buffer_set && !!dec->image_out_buffer) {
|
||||||
return JXL_API_ERROR(
|
return JXL_API_ERROR(
|
||||||
"Cannot change from image out buffer to image out callback");
|
"Cannot change from image out buffer to image out callback");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (init_callback == nullptr || run_callback == nullptr ||
|
||||||
|
destroy_callback == nullptr) {
|
||||||
|
return JXL_API_ERROR("All callbacks are required");
|
||||||
|
}
|
||||||
|
|
||||||
// Perform error checking for invalid format.
|
// Perform error checking for invalid format.
|
||||||
size_t bits_dummy;
|
size_t bits_dummy;
|
||||||
JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits_dummy);
|
JxlDecoderStatus status = PrepareSizeCheck(dec, format, &bits_dummy);
|
||||||
if (status != JXL_DEC_SUCCESS) return status;
|
if (status != JXL_DEC_SUCCESS) return status;
|
||||||
|
|
||||||
dec->image_out_buffer_set = true;
|
dec->image_out_buffer_set = true;
|
||||||
dec->image_out_callback = callback;
|
dec->image_out_init_callback = init_callback;
|
||||||
dec->image_out_opaque = opaque;
|
dec->image_out_run_callback = run_callback;
|
||||||
|
dec->image_out_destroy_callback = destroy_callback;
|
||||||
|
dec->image_out_init_opaque = init_opaque;
|
||||||
dec->image_out_format = *format;
|
dec->image_out_format = *format;
|
||||||
|
|
||||||
return JXL_DEC_SUCCESS;
|
return JXL_DEC_SUCCESS;
|
||||||
|
20
third_party/jpeg-xl/lib/jxl/decode_test.cc
vendored
20
third_party/jpeg-xl/lib/jxl/decode_test.cc
vendored
@ -211,7 +211,7 @@ PaddedBytes CreateTestJXLCodestream(
|
|||||||
jpeg_bytes.data() + jpeg_bytes.size());
|
jpeg_bytes.data() + jpeg_bytes.size());
|
||||||
EXPECT_TRUE(jxl::jpeg::DecodeImageJPG(
|
EXPECT_TRUE(jxl::jpeg::DecodeImageJPG(
|
||||||
jxl::Span<const uint8_t>(jpeg_bytes.data(), jpeg_bytes.size()), &io));
|
jxl::Span<const uint8_t>(jpeg_bytes.data(), jpeg_bytes.size()), &io));
|
||||||
EXPECT_TRUE(EncodeJPEGData(*io.Main().jpeg_data, &jpeg_data));
|
EXPECT_TRUE(EncodeJPEGData(*io.Main().jpeg_data, &jpeg_data, cparams));
|
||||||
io.metadata.m.xyb_encoded = false;
|
io.metadata.m.xyb_encoded = false;
|
||||||
#else // JPEGXL_ENABLE_JPEG
|
#else // JPEGXL_ENABLE_JPEG
|
||||||
JXL_ABORT(
|
JXL_ABORT(
|
||||||
@ -1234,7 +1234,8 @@ TEST_P(DecodeTestParam, PixelTest) {
|
|||||||
io.Main(), 16,
|
io.Main(), 16,
|
||||||
/*float_out=*/false, orig_channels, JXL_BIG_ENDIAN,
|
/*float_out=*/false, orig_channels, JXL_BIG_ENDIAN,
|
||||||
xsize * 2 * orig_channels, nullptr, pixels.data(), pixels.size(),
|
xsize * 2 * orig_channels, nullptr, pixels.data(), pixels.size(),
|
||||||
nullptr, nullptr, static_cast<jxl::Orientation>(config.orientation)));
|
/*out_callback=*/{},
|
||||||
|
static_cast<jxl::Orientation>(config.orientation)));
|
||||||
}
|
}
|
||||||
if (config.upsampling == 1) {
|
if (config.upsampling == 1) {
|
||||||
EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
|
EXPECT_EQ(0u, jxl::test::ComparePixels(pixels.data(), pixels2.data(), xsize,
|
||||||
@ -1406,12 +1407,8 @@ std::ostream& operator<<(std::ostream& os, const PixelTestConfig& c) {
|
|||||||
case JXL_TYPE_FLOAT16:
|
case JXL_TYPE_FLOAT16:
|
||||||
os << "f16";
|
os << "f16";
|
||||||
break;
|
break;
|
||||||
case JXL_TYPE_UINT32:
|
default:
|
||||||
os << "u32";
|
JXL_ASSERT(false);
|
||||||
break;
|
|
||||||
case JXL_TYPE_BOOLEAN:
|
|
||||||
os << "b";
|
|
||||||
break;
|
|
||||||
};
|
};
|
||||||
if (jxl::test::GetDataBits(c.data_type) > jxl::kBitsPerByte) {
|
if (jxl::test::GetDataBits(c.data_type) > jxl::kBitsPerByte) {
|
||||||
if (c.endianness == JXL_NATIVE_ENDIAN) {
|
if (c.endianness == JXL_NATIVE_ENDIAN) {
|
||||||
@ -3568,7 +3565,7 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructTestCodestream)) {
|
|||||||
|
|
||||||
TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
|
TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
|
||||||
const std::string jpeg_path =
|
const std::string jpeg_path =
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
||||||
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
||||||
jxl::CodecInOut orig_io;
|
jxl::CodecInOut orig_io;
|
||||||
ASSERT_TRUE(
|
ASSERT_TRUE(
|
||||||
@ -3586,7 +3583,8 @@ TEST(DecodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
|
|||||||
/*aux_out=*/nullptr));
|
/*aux_out=*/nullptr));
|
||||||
|
|
||||||
jxl::PaddedBytes jpeg_data;
|
jxl::PaddedBytes jpeg_data;
|
||||||
ASSERT_TRUE(EncodeJPEGData(*orig_io.Main().jpeg_data.get(), &jpeg_data));
|
ASSERT_TRUE(
|
||||||
|
EncodeJPEGData(*orig_io.Main().jpeg_data.get(), &jpeg_data, cparams));
|
||||||
jxl::PaddedBytes container;
|
jxl::PaddedBytes container;
|
||||||
container.append(jxl::kContainerHeader,
|
container.append(jxl::kContainerHeader,
|
||||||
jxl::kContainerHeader + sizeof(jxl::kContainerHeader));
|
jxl::kContainerHeader + sizeof(jxl::kContainerHeader));
|
||||||
@ -4100,7 +4098,7 @@ TEST(DecodeTest, SpotColorTest) {
|
|||||||
cparams.speed_tier = jxl::SpeedTier::kLightning;
|
cparams.speed_tier = jxl::SpeedTier::kLightning;
|
||||||
cparams.modular_mode = true;
|
cparams.modular_mode = true;
|
||||||
cparams.color_transform = jxl::ColorTransform::kNone;
|
cparams.color_transform = jxl::ColorTransform::kNone;
|
||||||
cparams.quality_pair = {100, 100};
|
cparams.butteraugli_distance = 0.f;
|
||||||
|
|
||||||
jxl::PaddedBytes compressed;
|
jxl::PaddedBytes compressed;
|
||||||
std::unique_ptr<jxl::PassesEncoderState> enc_state =
|
std::unique_ptr<jxl::PassesEncoderState> enc_state =
|
||||||
|
@ -1102,9 +1102,10 @@ ImageBundle RoundtripImage(const Image3F& opsin, PassesEncoderState* enc_state,
|
|||||||
JXL_CHECK(dec_state->PreparePipeline(&decoded, options));
|
JXL_CHECK(dec_state->PreparePipeline(&decoded, options));
|
||||||
|
|
||||||
hwy::AlignedUniquePtr<GroupDecCache[]> group_dec_caches;
|
hwy::AlignedUniquePtr<GroupDecCache[]> group_dec_caches;
|
||||||
const auto allocate_storage = [&](const size_t num_threads) {
|
const auto allocate_storage = [&](const size_t num_threads) -> Status {
|
||||||
dec_state->render_pipeline->PrepareForThreads(num_threads,
|
JXL_RETURN_IF_ERROR(
|
||||||
/*use_group_ids=*/false);
|
dec_state->render_pipeline->PrepareForThreads(num_threads,
|
||||||
|
/*use_group_ids=*/false));
|
||||||
group_dec_caches = hwy::MakeUniqueAlignedArray<GroupDecCache>(num_threads);
|
group_dec_caches = hwy::MakeUniqueAlignedArray<GroupDecCache>(num_threads);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
6
third_party/jpeg-xl/lib/jxl/enc_cache.cc
vendored
6
third_party/jpeg-xl/lib/jxl/enc_cache.cc
vendored
@ -98,8 +98,10 @@ Status InitializePassesEncoder(const Image3F& opsin, const JxlCmsInterface& cms,
|
|||||||
// and kModular for the smallest DC (first in the bitstream)
|
// and kModular for the smallest DC (first in the bitstream)
|
||||||
if (cparams.progressive_dc == 0) {
|
if (cparams.progressive_dc == 0) {
|
||||||
cparams.modular_mode = true;
|
cparams.modular_mode = true;
|
||||||
cparams.quality_pair.first = cparams.quality_pair.second =
|
// TODO(jon): tweak mapping from image dist to dist for modular DC
|
||||||
99.f - enc_state->cparams.butteraugli_distance * 0.2f;
|
cparams.butteraugli_distance =
|
||||||
|
std::max(kMinButteraugliDistance,
|
||||||
|
enc_state->cparams.butteraugli_distance * 0.03f);
|
||||||
}
|
}
|
||||||
ImageBundle ib(&shared.metadata->m);
|
ImageBundle ib(&shared.metadata->m);
|
||||||
// This is a lie - dc is in XYB
|
// This is a lie - dc is in XYB
|
||||||
|
@ -86,7 +86,6 @@ uint32_t JXL_INLINE Load8(const uint8_t* p) { return *p; }
|
|||||||
|
|
||||||
Status PixelFormatToExternal(const JxlPixelFormat& pixel_format,
|
Status PixelFormatToExternal(const JxlPixelFormat& pixel_format,
|
||||||
size_t* bitdepth, bool* float_in) {
|
size_t* bitdepth, bool* float_in) {
|
||||||
// TODO(zond): Make this accept uint32.
|
|
||||||
if (pixel_format.data_type == JXL_TYPE_FLOAT) {
|
if (pixel_format.data_type == JXL_TYPE_FLOAT) {
|
||||||
*bitdepth = 32;
|
*bitdepth = 32;
|
||||||
*float_in = true;
|
*float_in = true;
|
||||||
@ -100,7 +99,7 @@ Status PixelFormatToExternal(const JxlPixelFormat& pixel_format,
|
|||||||
*bitdepth = 16;
|
*bitdepth = 16;
|
||||||
*float_in = false;
|
*float_in = false;
|
||||||
} else {
|
} else {
|
||||||
return JXL_FAILURE("unsupported bitdepth");
|
return JXL_FAILURE("unsupported pixel format data type");
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -111,20 +110,9 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
JxlEndianness endianness, ThreadPool* pool,
|
JxlEndianness endianness, ThreadPool* pool,
|
||||||
ImageF* channel, bool float_in, size_t align) {
|
ImageF* channel, bool float_in, size_t align) {
|
||||||
// TODO(firsching): Avoid code duplication with the function below.
|
// TODO(firsching): Avoid code duplication with the function below.
|
||||||
if (bits_per_sample < 1 || bits_per_sample > 32) {
|
JXL_CHECK(float_in ? bits_per_sample == 16 || bits_per_sample == 32
|
||||||
return JXL_FAILURE("Invalid bits_per_sample value.");
|
: bits_per_sample > 0 && bits_per_sample <= 16);
|
||||||
}
|
|
||||||
// TODO(deymo): Implement 1-bit per sample as 8 samples per byte. In
|
|
||||||
// any other case we use DivCeil(bits_per_sample, 8) bytes per pixel per
|
|
||||||
// channel.
|
|
||||||
if (bits_per_sample == 1) {
|
|
||||||
return JXL_FAILURE("packed 1-bit per sample is not yet supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
// bytes_per_pixel are only valid for
|
|
||||||
// bits_per_sample > 1.
|
|
||||||
const size_t bytes_per_pixel = DivCeil(bits_per_sample, jxl::kBitsPerByte);
|
const size_t bytes_per_pixel = DivCeil(bits_per_sample, jxl::kBitsPerByte);
|
||||||
|
|
||||||
const size_t last_row_size = xsize * bytes_per_pixel;
|
const size_t last_row_size = xsize * bytes_per_pixel;
|
||||||
const size_t row_size =
|
const size_t row_size =
|
||||||
(align > 1 ? jxl::DivCeil(last_row_size, align) * align : last_row_size);
|
(align > 1 ? jxl::DivCeil(last_row_size, align) * align : last_row_size);
|
||||||
@ -153,7 +141,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
const size_t y = task;
|
const size_t y = task;
|
||||||
size_t i = row_size * task;
|
size_t i = row_size * task;
|
||||||
float* JXL_RESTRICT row_out = channel->Row(y);
|
float* JXL_RESTRICT row_out = channel->Row(y);
|
||||||
if (bits_per_sample <= 16) {
|
if (bits_per_sample == 16) {
|
||||||
if (little_endian) {
|
if (little_endian) {
|
||||||
for (size_t x = 0; x < xsize; ++x) {
|
for (size_t x = 0; x < xsize; ++x) {
|
||||||
row_out[x] = LoadLEFloat16(in + i);
|
row_out[x] = LoadLEFloat16(in + i);
|
||||||
@ -188,11 +176,9 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
const size_t y = task;
|
const size_t y = task;
|
||||||
size_t i = row_size * task;
|
size_t i = row_size * task;
|
||||||
float* JXL_RESTRICT row_out = channel->Row(y);
|
float* JXL_RESTRICT row_out = channel->Row(y);
|
||||||
// TODO(deymo): add bits_per_sample == 1 case here. Also maybe
|
|
||||||
// implement masking if bits_per_sample is not a multiple of 8.
|
|
||||||
if (bits_per_sample <= 8) {
|
if (bits_per_sample <= 8) {
|
||||||
LoadFloatRow<Load8>(row_out, in + i, mul, xsize, bytes_per_pixel);
|
LoadFloatRow<Load8>(row_out, in + i, mul, xsize, bytes_per_pixel);
|
||||||
} else if (bits_per_sample <= 16) {
|
} else {
|
||||||
if (little_endian) {
|
if (little_endian) {
|
||||||
LoadFloatRow<LoadLE16>(row_out, in + i, mul, xsize,
|
LoadFloatRow<LoadLE16>(row_out, in + i, mul, xsize,
|
||||||
bytes_per_pixel);
|
bytes_per_pixel);
|
||||||
@ -200,14 +186,6 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
LoadFloatRow<LoadBE16>(row_out, in + i, mul, xsize,
|
LoadFloatRow<LoadBE16>(row_out, in + i, mul, xsize,
|
||||||
bytes_per_pixel);
|
bytes_per_pixel);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (little_endian) {
|
|
||||||
LoadFloatRow<LoadLE32>(row_out, in + i, mul, xsize,
|
|
||||||
bytes_per_pixel);
|
|
||||||
} else {
|
|
||||||
LoadFloatRow<LoadBE32>(row_out, in + i, mul, xsize,
|
|
||||||
bytes_per_pixel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConvertExtraChannelUint"));
|
"ConvertExtraChannelUint"));
|
||||||
@ -221,15 +199,8 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
size_t bits_per_sample, JxlEndianness endianness,
|
size_t bits_per_sample, JxlEndianness endianness,
|
||||||
bool flipped_y, ThreadPool* pool, ImageBundle* ib,
|
bool flipped_y, ThreadPool* pool, ImageBundle* ib,
|
||||||
bool float_in, size_t align) {
|
bool float_in, size_t align) {
|
||||||
if (bits_per_sample < 1 || bits_per_sample > 32) {
|
JXL_CHECK(float_in ? bits_per_sample == 16 || bits_per_sample == 32
|
||||||
return JXL_FAILURE("Invalid bits_per_sample value.");
|
: bits_per_sample > 0 && bits_per_sample <= 16);
|
||||||
}
|
|
||||||
// TODO(deymo): Implement 1-bit per sample as 8 samples per byte. In
|
|
||||||
// any other case we use DivCeil(bits_per_sample, 8) bytes per pixel per
|
|
||||||
// channel.
|
|
||||||
if (bits_per_sample == 1) {
|
|
||||||
return JXL_FAILURE("packed 1-bit per sample is not yet supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t color_channels = c_current.Channels();
|
const size_t color_channels = c_current.Channels();
|
||||||
bool has_alpha = channels == 2 || channels == 4;
|
bool has_alpha = channels == 2 || channels == 4;
|
||||||
@ -239,8 +210,6 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
color_channels, channels);
|
color_channels, channels);
|
||||||
}
|
}
|
||||||
|
|
||||||
// bytes_per_channel and bytes_per_pixel are only valid for
|
|
||||||
// bits_per_sample > 1.
|
|
||||||
const size_t bytes_per_channel = DivCeil(bits_per_sample, jxl::kBitsPerByte);
|
const size_t bytes_per_channel = DivCeil(bits_per_sample, jxl::kBitsPerByte);
|
||||||
const size_t bytes_per_pixel = channels * bytes_per_channel;
|
const size_t bytes_per_pixel = channels * bytes_per_channel;
|
||||||
if (bits_per_sample > 16 && bits_per_sample < 32) {
|
if (bits_per_sample > 16 && bits_per_sample < 32) {
|
||||||
@ -287,7 +256,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
size_t i =
|
size_t i =
|
||||||
row_size * task + (c * bits_per_sample / jxl::kBitsPerByte);
|
row_size * task + (c * bits_per_sample / jxl::kBitsPerByte);
|
||||||
float* JXL_RESTRICT row_out = color.PlaneRow(c, y);
|
float* JXL_RESTRICT row_out = color.PlaneRow(c, y);
|
||||||
if (bits_per_sample <= 16) {
|
if (bits_per_sample == 16) {
|
||||||
if (little_endian) {
|
if (little_endian) {
|
||||||
for (size_t x = 0; x < xsize; ++x) {
|
for (size_t x = 0; x < xsize; ++x) {
|
||||||
row_out[x] = LoadLEFloat16(in + i);
|
row_out[x] = LoadLEFloat16(in + i);
|
||||||
@ -325,11 +294,9 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
const size_t y = get_y(task);
|
const size_t y = get_y(task);
|
||||||
size_t i = row_size * task + c * bytes_per_channel;
|
size_t i = row_size * task + c * bytes_per_channel;
|
||||||
float* JXL_RESTRICT row_out = color.PlaneRow(c, y);
|
float* JXL_RESTRICT row_out = color.PlaneRow(c, y);
|
||||||
// TODO(deymo): add bits_per_sample == 1 case here. Also maybe
|
|
||||||
// implement masking if bits_per_sample is not a multiple of 8.
|
|
||||||
if (bits_per_sample <= 8) {
|
if (bits_per_sample <= 8) {
|
||||||
LoadFloatRow<Load8>(row_out, in + i, mul, xsize, bytes_per_pixel);
|
LoadFloatRow<Load8>(row_out, in + i, mul, xsize, bytes_per_pixel);
|
||||||
} else if (bits_per_sample <= 16) {
|
} else {
|
||||||
if (little_endian) {
|
if (little_endian) {
|
||||||
LoadFloatRow<LoadLE16>(row_out, in + i, mul, xsize,
|
LoadFloatRow<LoadLE16>(row_out, in + i, mul, xsize,
|
||||||
bytes_per_pixel);
|
bytes_per_pixel);
|
||||||
@ -337,14 +304,6 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
LoadFloatRow<LoadBE16>(row_out, in + i, mul, xsize,
|
LoadFloatRow<LoadBE16>(row_out, in + i, mul, xsize,
|
||||||
bytes_per_pixel);
|
bytes_per_pixel);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (little_endian) {
|
|
||||||
LoadFloatRow<LoadLE32>(row_out, in + i, mul, xsize,
|
|
||||||
bytes_per_pixel);
|
|
||||||
} else {
|
|
||||||
LoadFloatRow<LoadBE32>(row_out, in + i, mul, xsize,
|
|
||||||
bytes_per_pixel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConvertRGBUint"));
|
"ConvertRGBUint"));
|
||||||
@ -371,7 +330,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
size_t i = row_size * task +
|
size_t i = row_size * task +
|
||||||
((channels - 1) * bits_per_sample / jxl::kBitsPerByte);
|
((channels - 1) * bits_per_sample / jxl::kBitsPerByte);
|
||||||
float* JXL_RESTRICT row_out = alpha.Row(y);
|
float* JXL_RESTRICT row_out = alpha.Row(y);
|
||||||
if (bits_per_sample <= 16) {
|
if (bits_per_sample == 16) {
|
||||||
if (little_endian) {
|
if (little_endian) {
|
||||||
for (size_t x = 0; x < xsize; ++x) {
|
for (size_t x = 0; x < xsize; ++x) {
|
||||||
row_out[x] = LoadLEFloat16(in + i);
|
row_out[x] = LoadLEFloat16(in + i);
|
||||||
@ -406,11 +365,9 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
const size_t y = get_y(task);
|
const size_t y = get_y(task);
|
||||||
size_t i = row_size * task + (channels - 1) * bytes_per_channel;
|
size_t i = row_size * task + (channels - 1) * bytes_per_channel;
|
||||||
float* JXL_RESTRICT row_out = alpha.Row(y);
|
float* JXL_RESTRICT row_out = alpha.Row(y);
|
||||||
// TODO(deymo): add bits_per_sample == 1 case here. Also maybe
|
|
||||||
// implement masking if bits_per_sample is not a multiple of 8.
|
|
||||||
if (bits_per_sample <= 8) {
|
if (bits_per_sample <= 8) {
|
||||||
LoadFloatRow<Load8>(row_out, in + i, mul, xsize, bytes_per_pixel);
|
LoadFloatRow<Load8>(row_out, in + i, mul, xsize, bytes_per_pixel);
|
||||||
} else if (bits_per_sample <= 16) {
|
} else {
|
||||||
if (little_endian) {
|
if (little_endian) {
|
||||||
LoadFloatRow<LoadLE16>(row_out, in + i, mul, xsize,
|
LoadFloatRow<LoadLE16>(row_out, in + i, mul, xsize,
|
||||||
bytes_per_pixel);
|
bytes_per_pixel);
|
||||||
@ -418,19 +375,17 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
|
|||||||
LoadFloatRow<LoadBE16>(row_out, in + i, mul, xsize,
|
LoadFloatRow<LoadBE16>(row_out, in + i, mul, xsize,
|
||||||
bytes_per_pixel);
|
bytes_per_pixel);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if (little_endian) {
|
|
||||||
LoadFloatRow<LoadLE32>(row_out, in + i, mul, xsize,
|
|
||||||
bytes_per_pixel);
|
|
||||||
} else {
|
|
||||||
LoadFloatRow<LoadBE32>(row_out, in + i, mul, xsize,
|
|
||||||
bytes_per_pixel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"ConvertAlphaUint"));
|
"ConvertAlphaUint"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ib->SetAlpha(std::move(alpha), alpha_is_premultiplied);
|
||||||
|
} else if (!has_alpha && ib->HasAlpha()) {
|
||||||
|
// if alpha is not passed, but it is expected, then assume
|
||||||
|
// it is all-opaque
|
||||||
|
ImageF alpha(xsize, ysize);
|
||||||
|
FillImage(1.0f, &alpha);
|
||||||
ib->SetAlpha(std::move(alpha), alpha_is_premultiplied);
|
ib->SetAlpha(std::move(alpha), alpha_is_premultiplied);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
50
third_party/jpeg-xl/lib/jxl/enc_file.cc
vendored
50
third_party/jpeg-xl/lib/jxl/enc_file.cc
vendored
@ -20,6 +20,7 @@
|
|||||||
#include "lib/jxl/enc_cache.h"
|
#include "lib/jxl/enc_cache.h"
|
||||||
#include "lib/jxl/enc_frame.h"
|
#include "lib/jxl/enc_frame.h"
|
||||||
#include "lib/jxl/enc_icc_codec.h"
|
#include "lib/jxl/enc_icc_codec.h"
|
||||||
|
#include "lib/jxl/exif.h"
|
||||||
#include "lib/jxl/frame_header.h"
|
#include "lib/jxl/frame_header.h"
|
||||||
#include "lib/jxl/headers.h"
|
#include "lib/jxl/headers.h"
|
||||||
#include "lib/jxl/image_bundle.h"
|
#include "lib/jxl/image_bundle.h"
|
||||||
@ -64,51 +65,6 @@ PassDefinition progressive_passes_dc_quant_ac_full_ac[] = {
|
|||||||
/*suitable_for_downsampling_of_at_least=*/0},
|
/*suitable_for_downsampling_of_at_least=*/0},
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint16_t kExifOrientationTag = 274;
|
|
||||||
|
|
||||||
// Parses the Exif data just enough to extract any render-impacting info.
|
|
||||||
// If the Exif data is invalid or could not be parsed, then it is treated
|
|
||||||
// as a no-op.
|
|
||||||
// TODO (jon): tag 1 can be used to represent Adobe RGB 1998 if it has value
|
|
||||||
// "R03"
|
|
||||||
// TODO (jon): set intrinsic dimensions according to
|
|
||||||
// https://discourse.wicg.io/t/proposal-exif-image-resolution-auto-and-from-image/4326/24
|
|
||||||
void InterpretExif(const PaddedBytes& exif, CodecMetadata* metadata) {
|
|
||||||
if (exif.size() < 12) return; // not enough bytes for a valid exif blob
|
|
||||||
const uint8_t* t = exif.data();
|
|
||||||
bool bigendian = false;
|
|
||||||
if (LoadLE32(t) == 0x2A004D4D) {
|
|
||||||
bigendian = true;
|
|
||||||
} else if (LoadLE32(t) != 0x002A4949) {
|
|
||||||
return; // not a valid tiff header
|
|
||||||
}
|
|
||||||
t += 4;
|
|
||||||
uint32_t offset = (bigendian ? LoadBE32(t) : LoadLE32(t));
|
|
||||||
if (exif.size() < 12 + offset + 2 || offset < 8) return;
|
|
||||||
t += offset - 4;
|
|
||||||
uint16_t nb_tags = (bigendian ? LoadBE16(t) : LoadLE16(t));
|
|
||||||
t += 2;
|
|
||||||
while (nb_tags > 0) {
|
|
||||||
if (t + 12 >= exif.data() + exif.size()) return;
|
|
||||||
uint16_t tag = (bigendian ? LoadBE16(t) : LoadLE16(t));
|
|
||||||
t += 2;
|
|
||||||
uint16_t type = (bigendian ? LoadBE16(t) : LoadLE16(t));
|
|
||||||
t += 2;
|
|
||||||
uint32_t count = (bigendian ? LoadBE32(t) : LoadLE32(t));
|
|
||||||
t += 4;
|
|
||||||
uint16_t value = (bigendian ? LoadBE16(t) : LoadLE16(t));
|
|
||||||
t += 4;
|
|
||||||
if (tag == kExifOrientationTag) {
|
|
||||||
if (type == 3 && count == 1) {
|
|
||||||
if (value >= 1 && value <= 8) {
|
|
||||||
metadata->m.orientation = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nb_tags--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Status PrepareCodecMetadataFromIO(const CompressParams& cparams,
|
Status PrepareCodecMetadataFromIO(const CompressParams& cparams,
|
||||||
const CodecInOut* io,
|
const CodecInOut* io,
|
||||||
CodecMetadata* metadata) {
|
CodecMetadata* metadata) {
|
||||||
@ -121,9 +77,7 @@ Status PrepareCodecMetadataFromIO(const CompressParams& cparams,
|
|||||||
// Keep ICC profile in lossless modes because a reconstructed profile may be
|
// Keep ICC profile in lossless modes because a reconstructed profile may be
|
||||||
// slightly different (quantization).
|
// slightly different (quantization).
|
||||||
// Also keep ICC in JPEG reconstruction mode as we need byte-exact profiles.
|
// Also keep ICC in JPEG reconstruction mode as we need byte-exact profiles.
|
||||||
const bool lossless_modular =
|
if (!cparams.IsLossless() && !io->Main().IsJPEG()) {
|
||||||
cparams.modular_mode && cparams.quality_pair.first == 100.0f;
|
|
||||||
if (!lossless_modular && !io->Main().IsJPEG()) {
|
|
||||||
metadata->m.color_encoding.DecideIfWantICC();
|
metadata->m.color_encoding.DecideIfWantICC();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
14
third_party/jpeg-xl/lib/jxl/enc_frame.cc
vendored
14
third_party/jpeg-xl/lib/jxl/enc_frame.cc
vendored
@ -254,10 +254,9 @@ Status LoopFilterFromParams(const CompressParams& cparams,
|
|||||||
}
|
}
|
||||||
// Strength of EPF in modular mode.
|
// Strength of EPF in modular mode.
|
||||||
if (frame_header->encoding == FrameEncoding::kModular &&
|
if (frame_header->encoding == FrameEncoding::kModular &&
|
||||||
cparams.quality_pair.first < 100) {
|
!cparams.IsLossless()) {
|
||||||
// TODO(veluca): this formula is nonsense.
|
// TODO(veluca): this formula is nonsense.
|
||||||
loop_filter->epf_sigma_for_modular =
|
loop_filter->epf_sigma_for_modular = cparams.butteraugli_distance;
|
||||||
20.0f * (1.0f - cparams.quality_pair.first / 100);
|
|
||||||
}
|
}
|
||||||
if (frame_header->encoding == FrameEncoding::kModular &&
|
if (frame_header->encoding == FrameEncoding::kModular &&
|
||||||
cparams.lossy_palette) {
|
cparams.lossy_palette) {
|
||||||
@ -1105,12 +1104,6 @@ Status EncodeFrame(const CompressParams& cparams_orig,
|
|||||||
return JXL_FAILURE("Butteraugli distance is too low (%f)",
|
return JXL_FAILURE("Butteraugli distance is too low (%f)",
|
||||||
cparams.butteraugli_distance);
|
cparams.butteraugli_distance);
|
||||||
}
|
}
|
||||||
if (cparams.butteraugli_distance > 0.9f && cparams.modular_mode == false &&
|
|
||||||
cparams.quality_pair.first == 100) {
|
|
||||||
// in case the color image is lossy, make the alpha slightly lossy too
|
|
||||||
cparams.quality_pair.first =
|
|
||||||
std::max(90.f, 99.f - 0.3f * cparams.butteraugli_distance);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ib.IsJPEG()) {
|
if (ib.IsJPEG()) {
|
||||||
cparams.gaborish = Override::kOff;
|
cparams.gaborish = Override::kOff;
|
||||||
@ -1228,8 +1221,7 @@ Status EncodeFrame(const CompressParams& cparams_orig,
|
|||||||
// input is already in XYB.
|
// input is already in XYB.
|
||||||
CopyImageTo(ib.color(), &opsin);
|
CopyImageTo(ib.color(), &opsin);
|
||||||
}
|
}
|
||||||
bool lossless = (frame_header->encoding == FrameEncoding::kModular &&
|
bool lossless = cparams.IsLossless();
|
||||||
cparams.quality_pair.first == 100);
|
|
||||||
if (ib.HasAlpha() && !ib.AlphaIsPremultiplied() &&
|
if (ib.HasAlpha() && !ib.AlphaIsPremultiplied() &&
|
||||||
frame_header->frame_type == FrameType::kRegularFrame &&
|
frame_header->frame_type == FrameType::kRegularFrame &&
|
||||||
!ApplyOverride(cparams.keep_invisible, lossless) &&
|
!ApplyOverride(cparams.keep_invisible, lossless) &&
|
||||||
|
29
third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc
vendored
29
third_party/jpeg-xl/lib/jxl/enc_icc_codec.cc
vendored
@ -22,29 +22,6 @@
|
|||||||
namespace jxl {
|
namespace jxl {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
bool EncodeVarInt(uint64_t value, size_t output_size, size_t* output_pos,
|
|
||||||
uint8_t* output) {
|
|
||||||
// While more than 7 bits of data are left,
|
|
||||||
// store 7 bits and set the next byte flag
|
|
||||||
while (value > 127) {
|
|
||||||
if (*output_pos > output_size) return false;
|
|
||||||
// |128: Set the next byte flag
|
|
||||||
output[(*output_pos)++] = ((uint8_t)(value & 127)) | 128;
|
|
||||||
// Remove the seven bits we just wrote
|
|
||||||
value >>= 7;
|
|
||||||
}
|
|
||||||
if (*output_pos > output_size) return false;
|
|
||||||
output[(*output_pos)++] = ((uint8_t)value) & 127;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EncodeVarInt(uint64_t value, PaddedBytes* data) {
|
|
||||||
size_t pos = data->size();
|
|
||||||
data->resize(data->size() + 9);
|
|
||||||
JXL_CHECK(EncodeVarInt(value, data->size(), &pos, data->data()));
|
|
||||||
data->resize(pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unshuffles or de-interleaves bytes, for example with width 2, turns
|
// Unshuffles or de-interleaves bytes, for example with width 2, turns
|
||||||
// "AaBbCcDc" into "ABCDabcd", this for example de-interleaves UTF-16 bytes into
|
// "AaBbCcDc" into "ABCDabcd", this for example de-interleaves UTF-16 bytes into
|
||||||
// first all the high order bytes, then all the low order bytes.
|
// first all the high order bytes, then all the low order bytes.
|
||||||
@ -159,9 +136,9 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) {
|
|||||||
ok &= DecodeKeyword(icc, size, pos + 0) == kGtrcTag;
|
ok &= DecodeKeyword(icc, size, pos + 0) == kGtrcTag;
|
||||||
ok &= DecodeKeyword(icc, size, pos + 12) == kBtrcTag;
|
ok &= DecodeKeyword(icc, size, pos + 12) == kBtrcTag;
|
||||||
if (ok) {
|
if (ok) {
|
||||||
for (size_t i = 0; i < 8; i++) {
|
for (size_t kk = 0; kk < 8; kk++) {
|
||||||
if (icc[pos - 8 + i] != icc[pos + 4 + i]) ok = false;
|
if (icc[pos - 8 + kk] != icc[pos + 4 + kk]) ok = false;
|
||||||
if (icc[pos - 8 + i] != icc[pos + 16 + i]) ok = false;
|
if (icc[pos - 8 + kk] != icc[pos + 16 + kk]) ok = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ok) {
|
if (ok) {
|
||||||
|
134
third_party/jpeg-xl/lib/jxl/enc_modular.cc
vendored
134
third_party/jpeg-xl/lib/jxl/enc_modular.cc
vendored
@ -228,11 +228,18 @@ void QuantizeChannel(Channel& ch, const int q) {
|
|||||||
// fit in pixel_type
|
// fit in pixel_type
|
||||||
Status float_to_int(const float* const row_in, pixel_type* const row_out,
|
Status float_to_int(const float* const row_in, pixel_type* const row_out,
|
||||||
size_t xsize, unsigned int bits, unsigned int exp_bits,
|
size_t xsize, unsigned int bits, unsigned int exp_bits,
|
||||||
bool fp, float factor) {
|
bool fp, double dfactor) {
|
||||||
JXL_ASSERT(sizeof(pixel_type) * 8 >= bits);
|
JXL_ASSERT(sizeof(pixel_type) * 8 >= bits);
|
||||||
if (!fp) {
|
if (!fp) {
|
||||||
for (size_t x = 0; x < xsize; ++x) {
|
if (bits > 22) {
|
||||||
row_out[x] = row_in[x] * factor + (row_in[x] < 0 ? -0.5f : 0.5f);
|
for (size_t x = 0; x < xsize; ++x) {
|
||||||
|
row_out[x] = row_in[x] * dfactor + (row_in[x] < 0 ? -0.5 : 0.5);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
float factor = dfactor;
|
||||||
|
for (size_t x = 0; x < xsize; ++x) {
|
||||||
|
row_out[x] = row_in[x] * factor + (row_in[x] < 0 ? -0.5f : 0.5f);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -296,8 +303,7 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header,
|
|||||||
: frame_dim(frame_header.ToFrameDimensions()), cparams(cparams_orig) {
|
: frame_dim(frame_header.ToFrameDimensions()), cparams(cparams_orig) {
|
||||||
size_t num_streams =
|
size_t num_streams =
|
||||||
ModularStreamId::Num(frame_dim, frame_header.passes.num_passes);
|
ModularStreamId::Num(frame_dim, frame_header.passes.num_passes);
|
||||||
if (cparams.modular_mode &&
|
if (cparams.IsLossless()) {
|
||||||
cparams.quality_pair == std::pair<float, float>{100.0, 100.0}) {
|
|
||||||
switch (cparams.decoding_speed_tier) {
|
switch (cparams.decoding_speed_tier) {
|
||||||
case 0:
|
case 0:
|
||||||
break;
|
break;
|
||||||
@ -322,18 +328,17 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cparams.decoding_speed_tier >= 1 && cparams.responsive &&
|
if (cparams.decoding_speed_tier >= 1 && cparams.responsive &&
|
||||||
cparams.quality_pair == std::make_pair(100.f, 100.f)) {
|
cparams.IsLossless()) {
|
||||||
cparams.options.tree_kind =
|
cparams.options.tree_kind =
|
||||||
ModularOptions::TreeKind::kTrivialTreeNoPredictor;
|
ModularOptions::TreeKind::kTrivialTreeNoPredictor;
|
||||||
cparams.options.nb_repeats = 0;
|
cparams.options.nb_repeats = 0;
|
||||||
}
|
}
|
||||||
stream_images.resize(num_streams);
|
stream_images.resize(num_streams);
|
||||||
if (cquality > 100) cquality = quality;
|
|
||||||
|
|
||||||
// use a sensible default if nothing explicit is specified:
|
// use a sensible default if nothing explicit is specified:
|
||||||
// Squeeze for lossy, no squeeze for lossless
|
// Squeeze for lossy, no squeeze for lossless
|
||||||
if (cparams.responsive < 0) {
|
if (cparams.responsive < 0) {
|
||||||
if (quality == 100) {
|
if (cparams.IsLossless()) {
|
||||||
cparams.responsive = 0;
|
cparams.responsive = 0;
|
||||||
} else {
|
} else {
|
||||||
cparams.responsive = 1;
|
cparams.responsive = 1;
|
||||||
@ -396,14 +401,14 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header,
|
|||||||
// no explicit predictor(s) given, set a good default
|
// no explicit predictor(s) given, set a good default
|
||||||
if ((cparams.speed_tier <= SpeedTier::kTortoise ||
|
if ((cparams.speed_tier <= SpeedTier::kTortoise ||
|
||||||
cparams.modular_mode == false) &&
|
cparams.modular_mode == false) &&
|
||||||
quality == 100 && cparams.responsive == false) {
|
cparams.IsLossless() && cparams.responsive == false) {
|
||||||
// TODO(veluca): allow all predictors that don't break residual
|
// TODO(veluca): allow all predictors that don't break residual
|
||||||
// multipliers in lossy mode.
|
// multipliers in lossy mode.
|
||||||
cparams.options.predictor = Predictor::Variable;
|
cparams.options.predictor = Predictor::Variable;
|
||||||
} else if (cparams.responsive || cparams.lossy_palette) {
|
} else if (cparams.responsive || cparams.lossy_palette) {
|
||||||
// zero predictor for Squeeze residues and lossy palette
|
// zero predictor for Squeeze residues and lossy palette
|
||||||
cparams.options.predictor = Predictor::Zero;
|
cparams.options.predictor = Predictor::Zero;
|
||||||
} else if (quality < 100) {
|
} else if (!cparams.IsLossless()) {
|
||||||
// If not responsive and lossy. TODO(veluca): use near_lossless instead?
|
// If not responsive and lossy. TODO(veluca): use near_lossless instead?
|
||||||
cparams.options.predictor = Predictor::Gradient;
|
cparams.options.predictor = Predictor::Gradient;
|
||||||
} else if (cparams.speed_tier < SpeedTier::kFalcon) {
|
} else if (cparams.speed_tier < SpeedTier::kFalcon) {
|
||||||
@ -420,6 +425,12 @@ ModularFrameEncoder::ModularFrameEncoder(const FrameHeader& frame_header,
|
|||||||
delta_pred = cparams.options.predictor;
|
delta_pred = cparams.options.predictor;
|
||||||
if (cparams.lossy_palette) cparams.options.predictor = Predictor::Zero;
|
if (cparams.lossy_palette) cparams.options.predictor = Predictor::Zero;
|
||||||
}
|
}
|
||||||
|
if (!cparams.IsLossless()) {
|
||||||
|
if (cparams.options.predictor == Predictor::Weighted ||
|
||||||
|
cparams.options.predictor == Predictor::Variable ||
|
||||||
|
cparams.options.predictor == Predictor::Best)
|
||||||
|
cparams.options.predictor = Predictor::Zero;
|
||||||
|
}
|
||||||
tree_splits.push_back(0);
|
tree_splits.push_back(0);
|
||||||
if (cparams.modular_mode == false) {
|
if (cparams.modular_mode == false) {
|
||||||
cparams.options.fast_decode_multiplier = 1.0f;
|
cparams.options.fast_decode_multiplier = 1.0f;
|
||||||
@ -499,17 +510,29 @@ Status ModularFrameEncoder::ComputeEncodingData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in the non-float case, there is an implicit 0 sign bit
|
||||||
|
int max_bitdepth =
|
||||||
|
do_color ? metadata.bit_depth.bits_per_sample + (fp ? 0 : 1) : 0;
|
||||||
Image& gi = stream_images[0];
|
Image& gi = stream_images[0];
|
||||||
gi = Image(xsize, ysize, metadata.bit_depth.bits_per_sample, nb_chans);
|
gi = Image(xsize, ysize, metadata.bit_depth.bits_per_sample, nb_chans);
|
||||||
int c = 0;
|
int c = 0;
|
||||||
if (cparams.color_transform == ColorTransform::kXYB &&
|
if (cparams.color_transform == ColorTransform::kXYB &&
|
||||||
cparams.modular_mode == true) {
|
cparams.modular_mode == true) {
|
||||||
static const float enc_factors[3] = {32768.0f, 2048.0f, 2048.0f};
|
float enc_factors[3] = {32768.0f, 2048.0f, 2048.0f};
|
||||||
|
if (cparams.butteraugli_distance > 0 && !cparams.responsive) {
|
||||||
|
// quantize XYB here and then treat it as a lossless image
|
||||||
|
enc_factors[0] *= 1.f / (1.f + 23.f * cparams.butteraugli_distance);
|
||||||
|
enc_factors[1] *= 1.f / (1.f + 14.f * cparams.butteraugli_distance);
|
||||||
|
enc_factors[2] *= 1.f / (1.f + 14.f * cparams.butteraugli_distance);
|
||||||
|
cparams.butteraugli_distance = 0;
|
||||||
|
}
|
||||||
if (cparams.manual_xyb_factors.size() == 3) {
|
if (cparams.manual_xyb_factors.size() == 3) {
|
||||||
DequantMatricesSetCustomDC(&enc_state->shared.matrices,
|
DequantMatricesSetCustomDC(&enc_state->shared.matrices,
|
||||||
cparams.manual_xyb_factors.data());
|
cparams.manual_xyb_factors.data());
|
||||||
|
// TODO(jon): update max_bitdepth in this case
|
||||||
} else {
|
} else {
|
||||||
DequantMatricesSetCustomDC(&enc_state->shared.matrices, enc_factors);
|
DequantMatricesSetCustomDC(&enc_state->shared.matrices, enc_factors);
|
||||||
|
max_bitdepth = 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pixel_type maxval = gi.bitdepth < 32 ? (1u << gi.bitdepth) - 1 : 0;
|
pixel_type maxval = gi.bitdepth < 32 ? (1u << gi.bitdepth) - 1 : 0;
|
||||||
@ -523,7 +546,7 @@ Status ModularFrameEncoder::ComputeEncodingData(
|
|||||||
// XYB is encoded as YX(B-Y)
|
// XYB is encoded as YX(B-Y)
|
||||||
if (cparams.color_transform == ColorTransform::kXYB && c < 2)
|
if (cparams.color_transform == ColorTransform::kXYB && c < 2)
|
||||||
c_out = 1 - c_out;
|
c_out = 1 - c_out;
|
||||||
float factor = maxval;
|
double factor = maxval;
|
||||||
if (cparams.color_transform == ColorTransform::kXYB)
|
if (cparams.color_transform == ColorTransform::kXYB)
|
||||||
factor = enc_state->shared.matrices.InvDCQuant(c);
|
factor = enc_state->shared.matrices.InvDCQuant(c);
|
||||||
if (c == 2 && cparams.color_transform == ColorTransform::kXYB) {
|
if (c == 2 && cparams.color_transform == ColorTransform::kXYB) {
|
||||||
@ -535,6 +558,8 @@ Status ModularFrameEncoder::ComputeEncodingData(
|
|||||||
for (size_t x = 0; x < xsize; ++x) {
|
for (size_t x = 0; x < xsize; ++x) {
|
||||||
row_out[x] = row_in[x] * factor + 0.5f;
|
row_out[x] = row_in[x] * factor + 0.5f;
|
||||||
row_out[x] -= row_Y[x];
|
row_out[x] -= row_Y[x];
|
||||||
|
// zero the lsb of B
|
||||||
|
row_out[x] = row_out[x] / 2 * 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -581,7 +606,8 @@ Status ModularFrameEncoder::ComputeEncodingData(
|
|||||||
int bits = eci.bit_depth.bits_per_sample;
|
int bits = eci.bit_depth.bits_per_sample;
|
||||||
int exp_bits = eci.bit_depth.exponent_bits_per_sample;
|
int exp_bits = eci.bit_depth.exponent_bits_per_sample;
|
||||||
bool fp = eci.bit_depth.floating_point_sample;
|
bool fp = eci.bit_depth.floating_point_sample;
|
||||||
float factor = (fp ? 1 : ((1u << eci.bit_depth.bits_per_sample) - 1));
|
double factor = (fp ? 1 : ((1u << eci.bit_depth.bits_per_sample) - 1));
|
||||||
|
if (bits + (fp ? 0 : 1) > max_bitdepth) max_bitdepth = bits + (fp ? 0 : 1);
|
||||||
std::atomic<bool> has_error{false};
|
std::atomic<bool> has_error{false};
|
||||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||||
pool, 0, gi.channel[c].plane.ysize(), ThreadPool::NoInit,
|
pool, 0, gi.channel[c].plane.ysize(), ThreadPool::NoInit,
|
||||||
@ -599,9 +625,16 @@ Status ModularFrameEncoder::ComputeEncodingData(
|
|||||||
}
|
}
|
||||||
JXL_ASSERT(c == nb_chans);
|
JXL_ASSERT(c == nb_chans);
|
||||||
|
|
||||||
|
int level_max_bitdepth = (cparams.level == 5 ? 16 : 32);
|
||||||
|
if (max_bitdepth > level_max_bitdepth)
|
||||||
|
return JXL_FAILURE(
|
||||||
|
"Bitdepth too high for level %i (need %i bits, have only %i in this "
|
||||||
|
"level)",
|
||||||
|
cparams.level, max_bitdepth, level_max_bitdepth);
|
||||||
|
|
||||||
// Set options and apply transformations
|
// Set options and apply transformations
|
||||||
|
|
||||||
if (quality < 100) {
|
if (cparams.butteraugli_distance > 0) {
|
||||||
if (cparams.palette_colors != 0) {
|
if (cparams.palette_colors != 0) {
|
||||||
JXL_DEBUG_V(3, "Lossy encode, not doing palette transforms");
|
JXL_DEBUG_V(3, "Lossy encode, not doing palette transforms");
|
||||||
}
|
}
|
||||||
@ -704,65 +737,59 @@ Status ModularFrameEncoder::ComputeEncodingData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// don't do an RCT if we're short on bits
|
||||||
if (cparams.color_transform == ColorTransform::kNone && do_color && !fp &&
|
if (cparams.color_transform == ColorTransform::kNone && do_color && !fp &&
|
||||||
gi.channel.size() - gi.nb_meta_channels >= 3) {
|
gi.channel.size() - gi.nb_meta_channels >= 3 &&
|
||||||
|
max_bitdepth + 1 < level_max_bitdepth) {
|
||||||
if (cparams.colorspace == 1 ||
|
if (cparams.colorspace == 1 ||
|
||||||
(cparams.colorspace < 0 &&
|
(cparams.colorspace < 0 &&
|
||||||
(quality < 100 || cparams.speed_tier > SpeedTier::kHare))) {
|
(!cparams.IsLossless() || cparams.speed_tier > SpeedTier::kHare))) {
|
||||||
Transform ycocg{TransformId::kRCT};
|
Transform ycocg{TransformId::kRCT};
|
||||||
ycocg.rct_type = 6;
|
ycocg.rct_type = 6;
|
||||||
ycocg.begin_c = gi.nb_meta_channels;
|
ycocg.begin_c = gi.nb_meta_channels;
|
||||||
do_transform(gi, ycocg, weighted::Header(), pool);
|
do_transform(gi, ycocg, weighted::Header(), pool);
|
||||||
|
max_bitdepth++;
|
||||||
} else if (cparams.colorspace >= 2) {
|
} else if (cparams.colorspace >= 2) {
|
||||||
Transform sg(TransformId::kRCT);
|
Transform sg(TransformId::kRCT);
|
||||||
sg.begin_c = gi.nb_meta_channels;
|
sg.begin_c = gi.nb_meta_channels;
|
||||||
sg.rct_type = cparams.colorspace - 2;
|
sg.rct_type = cparams.colorspace - 2;
|
||||||
do_transform(gi, sg, weighted::Header(), pool);
|
do_transform(gi, sg, weighted::Header(), pool);
|
||||||
|
max_bitdepth++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cparams.responsive && !gi.channel.empty()) {
|
// don't do squeeze if we don't have some spare bits
|
||||||
|
if (cparams.responsive && !gi.channel.empty() &&
|
||||||
|
max_bitdepth + 2 < level_max_bitdepth) {
|
||||||
Transform t(TransformId::kSqueeze);
|
Transform t(TransformId::kSqueeze);
|
||||||
t.squeezes = cparams.squeezes;
|
t.squeezes = cparams.squeezes;
|
||||||
do_transform(gi, t, weighted::Header(), pool);
|
do_transform(gi, t, weighted::Header(), pool);
|
||||||
|
max_bitdepth += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (max_bitdepth + 1 > level_max_bitdepth) {
|
||||||
|
// force no group RCTs if we don't have a spare bit
|
||||||
|
cparams.colorspace = 0;
|
||||||
|
}
|
||||||
|
JXL_ASSERT(max_bitdepth <= level_max_bitdepth);
|
||||||
|
|
||||||
std::vector<uint32_t> quants;
|
std::vector<uint32_t> quants;
|
||||||
|
|
||||||
if (quality < 100 || cquality < 100) {
|
if (cparams.butteraugli_distance > 0) {
|
||||||
quants.resize(gi.channel.size(), 1);
|
quants.resize(gi.channel.size(), 1);
|
||||||
JXL_DEBUG_V(
|
float quality = 0.25f * cparams.butteraugli_distance;
|
||||||
2,
|
JXL_DEBUG_V(2,
|
||||||
"Adding quantization constants corresponding to luma quality %.2f "
|
"Adding quantization constants corresponding to distance %.3f ",
|
||||||
"and chroma quality %.2f",
|
quality);
|
||||||
quality, cquality);
|
|
||||||
if (!cparams.responsive) {
|
if (!cparams.responsive) {
|
||||||
JXL_DEBUG_V(1,
|
JXL_DEBUG_V(1,
|
||||||
"Warning: lossy compression without Squeeze "
|
"Warning: lossy compression without Squeeze "
|
||||||
"transform is just color quantization.");
|
"transform is just color quantization.");
|
||||||
quality = (400 + quality) / 5;
|
quality *= 0.1f;
|
||||||
cquality = (400 + cquality) / 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert 'quality' to quantization scaling factor
|
|
||||||
if (quality > 50) {
|
|
||||||
quality = 200.0 - quality * 2.0;
|
|
||||||
} else {
|
|
||||||
quality = 900.0 - quality * 16.0;
|
|
||||||
}
|
|
||||||
if (cquality > 50) {
|
|
||||||
cquality = 200.0 - cquality * 2.0;
|
|
||||||
} else {
|
|
||||||
cquality = 900.0 - cquality * 16.0;
|
|
||||||
}
|
}
|
||||||
if (cparams.color_transform != ColorTransform::kXYB) {
|
if (cparams.color_transform != ColorTransform::kXYB) {
|
||||||
quality *= 0.01f * maxval / 255.f;
|
quality *= maxval / 255.f;
|
||||||
cquality *= 0.01f * maxval / 255.f;
|
|
||||||
} else {
|
|
||||||
quality *= 0.01f;
|
|
||||||
cquality *= 0.01f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cparams.options.nb_repeats == 0) {
|
if (cparams.options.nb_repeats == 0) {
|
||||||
return JXL_FAILURE("nb_repeats = 0 not supported with modular lossy!");
|
return JXL_FAILURE("nb_repeats = 0 not supported with modular lossy!");
|
||||||
}
|
}
|
||||||
@ -773,17 +800,18 @@ Status ModularFrameEncoder::ComputeEncodingData(
|
|||||||
if (shift > 0) shift--;
|
if (shift > 0) shift--;
|
||||||
int q;
|
int q;
|
||||||
// assuming default Squeeze here
|
// assuming default Squeeze here
|
||||||
int component = ((i - gi.nb_meta_channels) % nb_chans);
|
int component =
|
||||||
|
(do_color ? 0 : 3) + ((i - gi.nb_meta_channels) % nb_chans);
|
||||||
// last 4 channels are final chroma residuals
|
// last 4 channels are final chroma residuals
|
||||||
if (nb_chans > 2 && i >= gi.channel.size() - 4 && cparams.responsive) {
|
if (nb_chans > 2 && i >= gi.channel.size() - 4 && cparams.responsive) {
|
||||||
component = 1;
|
component = 1;
|
||||||
}
|
}
|
||||||
if (cparams.color_transform == ColorTransform::kXYB && component < 3) {
|
if (cparams.color_transform == ColorTransform::kXYB && component < 3) {
|
||||||
q = (component == 0 ? quality : cquality) * squeeze_quality_factor_xyb *
|
q = quality * squeeze_quality_factor_xyb *
|
||||||
squeeze_xyb_qtable[component][shift];
|
squeeze_xyb_qtable[component][shift];
|
||||||
} else {
|
} else {
|
||||||
if (cparams.colorspace != 0 && component > 0 && component < 3) {
|
if (cparams.colorspace != 0 && component > 0 && component < 3) {
|
||||||
q = cquality * squeeze_quality_factor * squeeze_chroma_qtable[shift];
|
q = quality * squeeze_quality_factor * squeeze_chroma_qtable[shift];
|
||||||
} else {
|
} else {
|
||||||
q = quality * squeeze_quality_factor * squeeze_luma_factor *
|
q = quality * squeeze_quality_factor * squeeze_luma_factor *
|
||||||
squeeze_luma_qtable[shift];
|
squeeze_luma_qtable[shift];
|
||||||
@ -1288,12 +1316,10 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
|
|||||||
if (gi.channel.empty()) return true;
|
if (gi.channel.empty()) return true;
|
||||||
// Do some per-group transforms
|
// Do some per-group transforms
|
||||||
|
|
||||||
float quality = cparams.quality_pair.first;
|
|
||||||
|
|
||||||
// Local palette
|
// Local palette
|
||||||
// TODO(veluca): make this work with quantize-after-prediction in lossy
|
// TODO(veluca): make this work with quantize-after-prediction in lossy
|
||||||
// mode.
|
// mode.
|
||||||
if (quality == 100 && cparams.palette_colors != 0 &&
|
if (cparams.butteraugli_distance == 0.f && cparams.palette_colors != 0 &&
|
||||||
cparams.speed_tier < SpeedTier::kCheetah) {
|
cparams.speed_tier < SpeedTier::kCheetah) {
|
||||||
// all-channel palette (e.g. RGBA)
|
// all-channel palette (e.g. RGBA)
|
||||||
if (gi.channel.size() - gi.nb_meta_channels > 1) {
|
if (gi.channel.size() - gi.nb_meta_channels > 1) {
|
||||||
@ -1321,8 +1347,9 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Local channel palette
|
// Local channel palette
|
||||||
if (cparams.channel_colors_percent > 0 && quality == 100 &&
|
if (cparams.channel_colors_percent > 0 &&
|
||||||
!cparams.lossy_palette && cparams.speed_tier < SpeedTier::kCheetah &&
|
cparams.butteraugli_distance == 0.f && !cparams.lossy_palette &&
|
||||||
|
cparams.speed_tier < SpeedTier::kCheetah &&
|
||||||
!(cparams.responsive && cparams.decoding_speed_tier >= 1)) {
|
!(cparams.responsive && cparams.decoding_speed_tier >= 1)) {
|
||||||
// single channel palette (like FLIF's ChannelCompact)
|
// single channel palette (like FLIF's ChannelCompact)
|
||||||
size_t nb_channels = gi.channel.size() - gi.nb_meta_channels;
|
size_t nb_channels = gi.channel.size() - gi.nb_meta_channels;
|
||||||
@ -1348,8 +1375,9 @@ Status ModularFrameEncoder::PrepareStreamParams(const Rect& rect,
|
|||||||
|
|
||||||
// lossless and no specific color transform specified: try Nothing, YCoCg,
|
// lossless and no specific color transform specified: try Nothing, YCoCg,
|
||||||
// and 17 RCTs
|
// and 17 RCTs
|
||||||
if (cparams.color_transform == ColorTransform::kNone && quality == 100 &&
|
if (cparams.color_transform == ColorTransform::kNone &&
|
||||||
cparams.colorspace < 0 && gi.channel.size() - gi.nb_meta_channels >= 3 &&
|
cparams.IsLossless() && cparams.colorspace < 0 &&
|
||||||
|
gi.channel.size() - gi.nb_meta_channels >= 3 &&
|
||||||
cparams.responsive == false && do_color &&
|
cparams.responsive == false && do_color &&
|
||||||
cparams.speed_tier <= SpeedTier::kHare) {
|
cparams.speed_tier <= SpeedTier::kHare) {
|
||||||
Transform sg(TransformId::kRCT);
|
Transform sg(TransformId::kRCT);
|
||||||
|
3
third_party/jpeg-xl/lib/jxl/enc_modular.h
vendored
3
third_party/jpeg-xl/lib/jxl/enc_modular.h
vendored
@ -80,8 +80,7 @@ class ModularFrameEncoder {
|
|||||||
std::vector<uint8_t> context_map;
|
std::vector<uint8_t> context_map;
|
||||||
FrameDimensions frame_dim;
|
FrameDimensions frame_dim;
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
float quality = cparams.quality_pair.first;
|
float quality = 100.f;
|
||||||
float cquality = cparams.quality_pair.second;
|
|
||||||
std::vector<size_t> tree_splits;
|
std::vector<size_t> tree_splits;
|
||||||
std::vector<ModularMultiplierInfo> multiplier_info;
|
std::vector<ModularMultiplierInfo> multiplier_info;
|
||||||
std::vector<std::vector<uint32_t>> gi_channel;
|
std::vector<std::vector<uint32_t>> gi_channel;
|
||||||
|
17
third_party/jpeg-xl/lib/jxl/enc_params.h
vendored
17
third_party/jpeg-xl/lib/jxl/enc_params.h
vendored
@ -127,6 +127,7 @@ struct CompressParams {
|
|||||||
float max_error[3] = {0.0, 0.0, 0.0};
|
float max_error[3] = {0.0, 0.0, 0.0};
|
||||||
|
|
||||||
SpeedTier speed_tier = SpeedTier::kSquirrel;
|
SpeedTier speed_tier = SpeedTier::kSquirrel;
|
||||||
|
int brotli_effort = -1;
|
||||||
|
|
||||||
// 0 = default.
|
// 0 = default.
|
||||||
// 1 = slightly worse quality.
|
// 1 = slightly worse quality.
|
||||||
@ -218,8 +219,6 @@ struct CompressParams {
|
|||||||
int responsive = -1;
|
int responsive = -1;
|
||||||
// empty for default squeeze
|
// empty for default squeeze
|
||||||
std::vector<SqueezeParams> squeezes;
|
std::vector<SqueezeParams> squeezes;
|
||||||
// A pair of <quality, cquality>.
|
|
||||||
std::pair<float, float> quality_pair{100.f, 100.f};
|
|
||||||
int colorspace = -1;
|
int colorspace = -1;
|
||||||
// Use Global channel palette if #colors < this percentage of range
|
// Use Global channel palette if #colors < this percentage of range
|
||||||
float channel_colors_pre_transform_percent = 95.f;
|
float channel_colors_pre_transform_percent = 95.f;
|
||||||
@ -230,16 +229,16 @@ struct CompressParams {
|
|||||||
|
|
||||||
// Returns whether these params are lossless as defined by SetLossless();
|
// Returns whether these params are lossless as defined by SetLossless();
|
||||||
bool IsLossless() const {
|
bool IsLossless() const {
|
||||||
return modular_mode && quality_pair.first == 100 &&
|
// YCbCr is also considered lossless here since it's intended for
|
||||||
quality_pair.second == 100 &&
|
// source material that is already YCbCr (we don't do the fwd transform)
|
||||||
color_transform == jxl::ColorTransform::kNone;
|
return modular_mode && butteraugli_distance == 0.0f &&
|
||||||
|
color_transform != jxl::ColorTransform::kXYB;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the parameters required to make the codec lossless.
|
// Sets the parameters required to make the codec lossless.
|
||||||
void SetLossless() {
|
void SetLossless() {
|
||||||
modular_mode = true;
|
modular_mode = true;
|
||||||
quality_pair.first = 100;
|
butteraugli_distance = 0.0f;
|
||||||
quality_pair.second = 100;
|
|
||||||
color_transform = jxl::ColorTransform::kNone;
|
color_transform = jxl::ColorTransform::kNone;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,6 +254,10 @@ struct CompressParams {
|
|||||||
// Skip the downsampling before encoding if this is true.
|
// Skip the downsampling before encoding if this is true.
|
||||||
bool already_downsampled = false;
|
bool already_downsampled = false;
|
||||||
|
|
||||||
|
// Codestream level to conform to.
|
||||||
|
// -1: don't care
|
||||||
|
int level = -1;
|
||||||
|
|
||||||
std::vector<float> manual_noise;
|
std::vector<float> manual_noise;
|
||||||
std::vector<float> manual_xyb_factors;
|
std::vector<float> manual_xyb_factors;
|
||||||
};
|
};
|
||||||
|
@ -710,11 +710,6 @@ void FindBestPatchDictionary(const Image3F& opsin,
|
|||||||
CompressParams cparams = state->cparams;
|
CompressParams cparams = state->cparams;
|
||||||
// Recursive application of patches could create very weird issues.
|
// Recursive application of patches could create very weird issues.
|
||||||
cparams.patches = Override::kOff;
|
cparams.patches = Override::kOff;
|
||||||
// TODO(veluca): possibly change heuristics here.
|
|
||||||
if (!cparams.modular_mode) {
|
|
||||||
cparams.quality_pair.first = cparams.quality_pair.second =
|
|
||||||
90.f - cparams.butteraugli_distance * 5.f;
|
|
||||||
}
|
|
||||||
|
|
||||||
RoundtripPatchFrame(&reference_frame, state, 0, cparams, cms, pool, true);
|
RoundtripPatchFrame(&reference_frame, state, 0, cparams, cms, pool, true);
|
||||||
|
|
||||||
|
129
third_party/jpeg-xl/lib/jxl/encode.cc
vendored
129
third_party/jpeg-xl/lib/jxl/encode.cc
vendored
@ -146,6 +146,10 @@ int VerifyLevelSettings(const JxlEncoder* enc, std::string* debug_string) {
|
|||||||
|
|
||||||
// Level 5 checks
|
// Level 5 checks
|
||||||
|
|
||||||
|
if (!m.modular_16_bit_buffer_sufficient) {
|
||||||
|
if (debug_string) *debug_string = "Too high modular bit depth";
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
if (xsize > (1ull << 18ull) || ysize > (1ull << 18ull) ||
|
if (xsize > (1ull << 18ull) || ysize > (1ull << 18ull) ||
|
||||||
xsize * ysize > (1ull << 28ull)) {
|
xsize * ysize > (1ull << 28ull)) {
|
||||||
if (debug_string) *debug_string = "Too large image dimensions";
|
if (debug_string) *debug_string = "Too large image dimensions";
|
||||||
@ -248,6 +252,12 @@ JxlEncoderStatus JxlEncoderStruct::RefillOutputByteQueue() {
|
|||||||
// TODO(lode): preview should be added here if a preview image is added
|
// TODO(lode): preview should be added here if a preview image is added
|
||||||
|
|
||||||
writer.ZeroPadToByte();
|
writer.ZeroPadToByte();
|
||||||
|
|
||||||
|
// Not actually the end of frame, but the end of metadata/ICC, but helps
|
||||||
|
// the next frame to start here for indexing purposes.
|
||||||
|
codestream_bytes_written_end_of_frame +=
|
||||||
|
jxl::DivCeil(writer.BitsWritten(), 8);
|
||||||
|
|
||||||
bytes = std::move(writer).TakeBytes();
|
bytes = std::move(writer).TakeBytes();
|
||||||
|
|
||||||
if (MustUseContainer()) {
|
if (MustUseContainer()) {
|
||||||
@ -363,9 +373,10 @@ JxlEncoderStatus JxlEncoderStruct::RefillOutputByteQueue() {
|
|||||||
input_frame->option_values.header.layer_info.blend_info.alpha;
|
input_frame->option_values.header.layer_info.blend_info.alpha;
|
||||||
frame_info.extra_channel_blending_info.resize(
|
frame_info.extra_channel_blending_info.resize(
|
||||||
metadata.m.num_extra_channels);
|
metadata.m.num_extra_channels);
|
||||||
// If extra channel blend info has not been set, use the default values.
|
// If extra channel blend info has not been set, use the blend mode from the
|
||||||
JxlBlendInfo default_blend_info;
|
// layer_info.
|
||||||
JxlEncoderInitBlendInfo(&default_blend_info);
|
JxlBlendInfo default_blend_info =
|
||||||
|
input_frame->option_values.header.layer_info.blend_info;
|
||||||
for (size_t i = 0; i < metadata.m.num_extra_channels; ++i) {
|
for (size_t i = 0; i < metadata.m.num_extra_channels; ++i) {
|
||||||
auto& to = frame_info.extra_channel_blending_info[i];
|
auto& to = frame_info.extra_channel_blending_info[i];
|
||||||
const auto& from =
|
const auto& from =
|
||||||
@ -375,23 +386,28 @@ JxlEncoderStatus JxlEncoderStruct::RefillOutputByteQueue() {
|
|||||||
to.mode = static_cast<jxl::BlendMode>(from.blendmode);
|
to.mode = static_cast<jxl::BlendMode>(from.blendmode);
|
||||||
to.source = from.source;
|
to.source = from.source;
|
||||||
to.alpha_channel = from.alpha;
|
to.alpha_channel = from.alpha;
|
||||||
|
to.clamp = (from.clamp != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input_frame->option_values.header.layer_info.have_crop) {
|
if (input_frame->option_values.header.layer_info.have_crop) {
|
||||||
ib.origin.x0 = input_frame->option_values.header.layer_info.crop_x0;
|
ib.origin.x0 = input_frame->option_values.header.layer_info.crop_x0;
|
||||||
ib.origin.y0 = input_frame->option_values.header.layer_info.crop_y0;
|
ib.origin.y0 = input_frame->option_values.header.layer_info.crop_y0;
|
||||||
}
|
}
|
||||||
|
JXL_ASSERT(writer.BitsWritten() == 0);
|
||||||
if (!jxl::EncodeFrame(input_frame->option_values.cparams, frame_info,
|
if (!jxl::EncodeFrame(input_frame->option_values.cparams, frame_info,
|
||||||
&metadata, input_frame->frame, &enc_state, cms,
|
&metadata, input_frame->frame, &enc_state, cms,
|
||||||
thread_pool.get(), &writer,
|
thread_pool.get(), &writer,
|
||||||
/*aux_out=*/nullptr)) {
|
/*aux_out=*/nullptr)) {
|
||||||
return JXL_API_ERROR("Failed to encode frame");
|
return JXL_API_ERROR("Failed to encode frame");
|
||||||
}
|
}
|
||||||
|
codestream_bytes_written_beginning_of_frame =
|
||||||
|
codestream_bytes_written_end_of_frame;
|
||||||
|
codestream_bytes_written_end_of_frame +=
|
||||||
|
jxl::DivCeil(writer.BitsWritten(), 8);
|
||||||
|
|
||||||
// Possibly bytes already contains the codestream header: in case this is
|
// Possibly bytes already contains the codestream header: in case this is
|
||||||
// the first frame, and the codestream header was not encoded as jxlp above.
|
// the first frame, and the codestream header was not encoded as jxlp above.
|
||||||
bytes.append(std::move(writer).TakeBytes());
|
bytes.append(std::move(writer).TakeBytes());
|
||||||
|
|
||||||
if (MustUseContainer()) {
|
if (MustUseContainer()) {
|
||||||
if (last_frame && jxlp_counter == 0) {
|
if (last_frame && jxlp_counter == 0) {
|
||||||
// If this is the last frame and no jxlp boxes were used yet, it's
|
// If this is the last frame and no jxlp boxes were used yet, it's
|
||||||
@ -423,9 +439,10 @@ JxlEncoderStatus JxlEncoderStruct::RefillOutputByteQueue() {
|
|||||||
for (size_t i = 0; i < 4; i++) {
|
for (size_t i = 0; i < 4; i++) {
|
||||||
compressed[i] = static_cast<uint8_t>(box->type[i]);
|
compressed[i] = static_cast<uint8_t>(box->type[i]);
|
||||||
}
|
}
|
||||||
if (JXL_ENC_SUCCESS != BrotliCompress(9, box->contents.data(),
|
if (JXL_ENC_SUCCESS !=
|
||||||
box->contents.size(),
|
BrotliCompress((brotli_effort >= 0 ? brotli_effort : 4),
|
||||||
&compressed)) {
|
box->contents.data(), box->contents.size(),
|
||||||
|
&compressed)) {
|
||||||
return JXL_API_ERROR("Brotli compression for brob box failed");
|
return JXL_API_ERROR("Brotli compression for brob box failed");
|
||||||
}
|
}
|
||||||
jxl::AppendBoxHeader(jxl::MakeBoxType("brob"), compressed.size(), false,
|
jxl::AppendBoxHeader(jxl::MakeBoxType("brob"), compressed.size(), false,
|
||||||
@ -561,7 +578,8 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
|
|||||||
enc->metadata.m.bit_depth.floating_point_sample =
|
enc->metadata.m.bit_depth.floating_point_sample =
|
||||||
(info->exponent_bits_per_sample != 0u);
|
(info->exponent_bits_per_sample != 0u);
|
||||||
enc->metadata.m.modular_16_bit_buffer_sufficient =
|
enc->metadata.m.modular_16_bit_buffer_sufficient =
|
||||||
(info->exponent_bits_per_sample == 0u) && info->bits_per_sample <= 12;
|
(!info->uses_original_profile || info->bits_per_sample <= 12) &&
|
||||||
|
info->alpha_bits <= 12;
|
||||||
|
|
||||||
// The number of extra channels includes the alpha channel, so for example and
|
// The number of extra channels includes the alpha channel, so for example and
|
||||||
// RGBA with no other extra channels, has exactly num_extra_channels == 1
|
// RGBA with no other extra channels, has exactly num_extra_channels == 1
|
||||||
@ -620,7 +638,15 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
|
|||||||
enc->metadata.m.animation.num_loops = info->animation.num_loops;
|
enc->metadata.m.animation.num_loops = info->animation.num_loops;
|
||||||
enc->metadata.m.animation.have_timecodes = info->animation.have_timecodes;
|
enc->metadata.m.animation.have_timecodes = info->animation.have_timecodes;
|
||||||
}
|
}
|
||||||
|
std::string level_message;
|
||||||
|
int required_level = VerifyLevelSettings(enc, &level_message);
|
||||||
|
if (required_level == -1 ||
|
||||||
|
static_cast<int>(enc->codestream_level) < required_level) {
|
||||||
|
return JXL_API_ERROR("%s", ("Codestream level verification for level " +
|
||||||
|
std::to_string(enc->codestream_level) +
|
||||||
|
" failed: " + level_message)
|
||||||
|
.c_str());
|
||||||
|
}
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,6 +678,8 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelInfo(
|
|||||||
jxl::ExtraChannelInfo& channel = enc->metadata.m.extra_channel_info[index];
|
jxl::ExtraChannelInfo& channel = enc->metadata.m.extra_channel_info[index];
|
||||||
channel.type = static_cast<jxl::ExtraChannel>(info->type);
|
channel.type = static_cast<jxl::ExtraChannel>(info->type);
|
||||||
channel.bit_depth.bits_per_sample = info->bits_per_sample;
|
channel.bit_depth.bits_per_sample = info->bits_per_sample;
|
||||||
|
enc->metadata.m.modular_16_bit_buffer_sufficient &=
|
||||||
|
info->bits_per_sample <= 12;
|
||||||
channel.bit_depth.exponent_bits_per_sample = info->exponent_bits_per_sample;
|
channel.bit_depth.exponent_bits_per_sample = info->exponent_bits_per_sample;
|
||||||
channel.bit_depth.floating_point_sample = info->exponent_bits_per_sample != 0;
|
channel.bit_depth.floating_point_sample = info->exponent_bits_per_sample != 0;
|
||||||
channel.dim_shift = info->dim_shift;
|
channel.dim_shift = info->dim_shift;
|
||||||
@ -662,6 +690,15 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelInfo(
|
|||||||
channel.spot_color[1] = info->spot_color[1];
|
channel.spot_color[1] = info->spot_color[1];
|
||||||
channel.spot_color[2] = info->spot_color[2];
|
channel.spot_color[2] = info->spot_color[2];
|
||||||
channel.spot_color[3] = info->spot_color[3];
|
channel.spot_color[3] = info->spot_color[3];
|
||||||
|
std::string level_message;
|
||||||
|
int required_level = VerifyLevelSettings(enc, &level_message);
|
||||||
|
if (required_level == -1 ||
|
||||||
|
static_cast<int>(enc->codestream_level) < required_level) {
|
||||||
|
return JXL_API_ERROR("%s", ("Codestream level verification for level " +
|
||||||
|
std::to_string(enc->codestream_level) +
|
||||||
|
" failed: " + level_message)
|
||||||
|
.c_str());
|
||||||
|
}
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -688,6 +725,7 @@ JxlEncoderFrameSettings* JxlEncoderFrameSettingsCreate(
|
|||||||
} else {
|
} else {
|
||||||
opts->values.lossless = false;
|
opts->values.lossless = false;
|
||||||
}
|
}
|
||||||
|
opts->values.cparams.level = enc->codestream_level;
|
||||||
JxlEncoderFrameSettings* ret = opts.get();
|
JxlEncoderFrameSettings* ret = opts.get();
|
||||||
enc->encoder_options.emplace_back(std::move(opts));
|
enc->encoder_options.emplace_back(std::move(opts));
|
||||||
return ret;
|
return ret;
|
||||||
@ -701,6 +739,10 @@ JxlEncoderFrameSettings* JxlEncoderOptionsCreate(
|
|||||||
|
|
||||||
JxlEncoderStatus JxlEncoderSetFrameLossless(
|
JxlEncoderStatus JxlEncoderSetFrameLossless(
|
||||||
JxlEncoderFrameSettings* frame_settings, const JXL_BOOL lossless) {
|
JxlEncoderFrameSettings* frame_settings, const JXL_BOOL lossless) {
|
||||||
|
if (lossless && frame_settings->enc->basic_info_set &&
|
||||||
|
frame_settings->enc->metadata.m.xyb_encoded) {
|
||||||
|
return JXL_API_ERROR("Set use_original_profile=true for lossless encoding");
|
||||||
|
}
|
||||||
frame_settings->values.lossless = lossless;
|
frame_settings->values.lossless = lossless;
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
}
|
}
|
||||||
@ -726,29 +768,6 @@ JxlEncoderStatus JxlEncoderSetFrameDistance(
|
|||||||
distance = 0.01f;
|
distance = 0.01f;
|
||||||
}
|
}
|
||||||
frame_settings->values.cparams.butteraugli_distance = distance;
|
frame_settings->values.cparams.butteraugli_distance = distance;
|
||||||
float jpeg_quality;
|
|
||||||
// Formula to translate butteraugli distance roughly into JPEG 0-100 quality.
|
|
||||||
// This is the inverse of the formula in cjxl.cc to translate JPEG quality
|
|
||||||
// into butteraugli distance.
|
|
||||||
if (distance > 6.56f) {
|
|
||||||
jpeg_quality = -5.456783f * std::log(0.0256f * distance - 0.16384f);
|
|
||||||
} else {
|
|
||||||
jpeg_quality = -11.11111f * distance + 101.11111f;
|
|
||||||
}
|
|
||||||
// Translate JPEG quality into the quality_pair setting for modular encoding.
|
|
||||||
// This is the formula also used in cjxl.cc to convert the command line JPEG
|
|
||||||
// quality parameter to the quality_pair setting.
|
|
||||||
// TODO(lode): combine the distance -> quality_pair conversion into a single
|
|
||||||
// formula, possibly altering it to a more suitable heuristic.
|
|
||||||
float quality;
|
|
||||||
if (jpeg_quality < 7.f) {
|
|
||||||
quality = std::min<float>(35.f + (jpeg_quality - 7.f) * 3.0f, 100.0f);
|
|
||||||
} else {
|
|
||||||
quality =
|
|
||||||
std::min<float>(35.f + (jpeg_quality - 7.f) * 65.f / 93.f, 100.0f);
|
|
||||||
}
|
|
||||||
frame_settings->values.cparams.quality_pair.first =
|
|
||||||
frame_settings->values.cparams.quality_pair.second = quality;
|
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -775,6 +794,15 @@ JxlEncoderStatus JxlEncoderFrameSettingsSetOption(
|
|||||||
frame_settings->values.cparams.speed_tier =
|
frame_settings->values.cparams.speed_tier =
|
||||||
static_cast<jxl::SpeedTier>(10 - value);
|
static_cast<jxl::SpeedTier>(10 - value);
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
|
case JXL_ENC_FRAME_SETTING_BROTLI_EFFORT:
|
||||||
|
if (value < -1 || value > 11) {
|
||||||
|
return JXL_ENC_ERROR;
|
||||||
|
}
|
||||||
|
// set cparams for brotli use in JPEG frames
|
||||||
|
frame_settings->values.cparams.brotli_effort = value;
|
||||||
|
// set enc option for brotli use in brob boxes
|
||||||
|
frame_settings->enc->brotli_effort = value;
|
||||||
|
return JXL_ENC_SUCCESS;
|
||||||
case JXL_ENC_FRAME_SETTING_DECODING_SPEED:
|
case JXL_ENC_FRAME_SETTING_DECODING_SPEED:
|
||||||
if (value < 0 || value > 4) {
|
if (value < 0 || value > 4) {
|
||||||
return JXL_ENC_ERROR;
|
return JXL_ENC_ERROR;
|
||||||
@ -1008,7 +1036,8 @@ void JxlEncoderReset(JxlEncoder* enc) {
|
|||||||
enc->num_queued_boxes = 0;
|
enc->num_queued_boxes = 0;
|
||||||
enc->encoder_options.clear();
|
enc->encoder_options.clear();
|
||||||
enc->output_byte_queue.clear();
|
enc->output_byte_queue.clear();
|
||||||
enc->output_bytes_flushed = 0;
|
enc->codestream_bytes_written_beginning_of_frame = 0;
|
||||||
|
enc->codestream_bytes_written_end_of_frame = 0;
|
||||||
enc->wrote_bytes = false;
|
enc->wrote_bytes = false;
|
||||||
enc->jxlp_counter = 0;
|
enc->jxlp_counter = 0;
|
||||||
enc->metadata = jxl::CodecMetadata();
|
enc->metadata = jxl::CodecMetadata();
|
||||||
@ -1025,9 +1054,10 @@ void JxlEncoderReset(JxlEncoder* enc) {
|
|||||||
|
|
||||||
void JxlEncoderDestroy(JxlEncoder* enc) {
|
void JxlEncoderDestroy(JxlEncoder* enc) {
|
||||||
if (enc) {
|
if (enc) {
|
||||||
|
JxlMemoryManager local_memory_manager = enc->memory_manager;
|
||||||
// Call destructor directly since custom free function is used.
|
// Call destructor directly since custom free function is used.
|
||||||
enc->~JxlEncoder();
|
enc->~JxlEncoder();
|
||||||
jxl::MemoryManagerFree(&enc->memory_manager, enc);
|
jxl::MemoryManagerFree(&local_memory_manager, enc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1062,7 +1092,10 @@ int JxlEncoderGetRequiredCodestreamLevel(const JxlEncoder* enc) {
|
|||||||
return VerifyLevelSettings(enc, nullptr);
|
return VerifyLevelSettings(enc, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms) { enc->cms = cms; }
|
void JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms) {
|
||||||
|
jxl::msan::MemoryIsInitialized(&cms, sizeof(cms));
|
||||||
|
enc->cms = cms;
|
||||||
|
}
|
||||||
|
|
||||||
JxlEncoderStatus JxlEncoderSetParallelRunner(JxlEncoder* enc,
|
JxlEncoderStatus JxlEncoderSetParallelRunner(JxlEncoder* enc,
|
||||||
JxlParallelRunner parallel_runner,
|
JxlParallelRunner parallel_runner,
|
||||||
@ -1138,7 +1171,8 @@ JxlEncoderStatus JxlEncoderAddJPEGFrame(
|
|||||||
if (frame_settings->enc->store_jpeg_metadata) {
|
if (frame_settings->enc->store_jpeg_metadata) {
|
||||||
jxl::jpeg::JPEGData data_in = *io.Main().jpeg_data;
|
jxl::jpeg::JPEGData data_in = *io.Main().jpeg_data;
|
||||||
jxl::PaddedBytes jpeg_data;
|
jxl::PaddedBytes jpeg_data;
|
||||||
if (!jxl::jpeg::EncodeJPEGData(data_in, &jpeg_data)) {
|
if (!jxl::jpeg::EncodeJPEGData(data_in, &jpeg_data,
|
||||||
|
frame_settings->values.cparams)) {
|
||||||
return JXL_ENC_ERROR;
|
return JXL_ENC_ERROR;
|
||||||
}
|
}
|
||||||
frame_settings->enc->jpeg_metadata = std::vector<uint8_t>(
|
frame_settings->enc->jpeg_metadata = std::vector<uint8_t>(
|
||||||
@ -1247,15 +1281,31 @@ JxlEncoderStatus JxlEncoderAddImageFrame(
|
|||||||
queued_frame->ec_initialized.push_back(0);
|
queued_frame->ec_initialized.push_back(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
queued_frame->frame.origin.x0 =
|
||||||
|
frame_settings->values.header.layer_info.crop_x0;
|
||||||
|
queued_frame->frame.origin.y0 =
|
||||||
|
frame_settings->values.header.layer_info.crop_y0;
|
||||||
|
queued_frame->frame.use_for_next_frame =
|
||||||
|
(frame_settings->values.header.layer_info.save_as_reference != 0u);
|
||||||
|
queued_frame->frame.blendmode =
|
||||||
|
frame_settings->values.header.layer_info.blend_info.blendmode ==
|
||||||
|
JXL_BLEND_REPLACE
|
||||||
|
? jxl::BlendMode::kReplace
|
||||||
|
: jxl::BlendMode::kBlend;
|
||||||
|
queued_frame->frame.blend =
|
||||||
|
frame_settings->values.header.layer_info.blend_info.source > 0;
|
||||||
|
|
||||||
if (!jxl::BufferToImageBundle(*pixel_format, xsize, ysize, buffer, size,
|
if (!jxl::BufferToImageBundle(*pixel_format, xsize, ysize, buffer, size,
|
||||||
frame_settings->enc->thread_pool.get(),
|
frame_settings->enc->thread_pool.get(),
|
||||||
c_current, &(queued_frame->frame))) {
|
c_current, &(queued_frame->frame))) {
|
||||||
return JXL_ENC_ERROR;
|
return JXL_ENC_ERROR;
|
||||||
}
|
}
|
||||||
if (frame_settings->values.lossless) {
|
if (frame_settings->values.lossless &&
|
||||||
queued_frame->option_values.cparams.SetLossless();
|
frame_settings->enc->metadata.m.xyb_encoded) {
|
||||||
|
return JXL_API_ERROR("Set use_original_profile=true for lossless encoding");
|
||||||
}
|
}
|
||||||
|
queued_frame->option_values.cparams.level =
|
||||||
|
frame_settings->enc->codestream_level;
|
||||||
|
|
||||||
QueueFrame(frame_settings, queued_frame);
|
QueueFrame(frame_settings, queued_frame);
|
||||||
return JXL_ENC_SUCCESS;
|
return JXL_ENC_SUCCESS;
|
||||||
@ -1350,7 +1400,6 @@ JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc, uint8_t** next_out,
|
|||||||
std::copy_n(enc->output_byte_queue.begin(), to_copy, *next_out);
|
std::copy_n(enc->output_byte_queue.begin(), to_copy, *next_out);
|
||||||
*next_out += to_copy;
|
*next_out += to_copy;
|
||||||
*avail_out -= to_copy;
|
*avail_out -= to_copy;
|
||||||
enc->output_bytes_flushed += to_copy;
|
|
||||||
enc->output_byte_queue.erase(enc->output_byte_queue.begin(),
|
enc->output_byte_queue.erase(enc->output_byte_queue.begin(),
|
||||||
enc->output_byte_queue.begin() + to_copy);
|
enc->output_byte_queue.begin() + to_copy);
|
||||||
} else if (!enc->input_queue.empty()) {
|
} else if (!enc->input_queue.empty()) {
|
||||||
@ -1406,7 +1455,7 @@ JxlEncoderStatus JxlEncoderSetExtraChannelBlendInfo(
|
|||||||
|
|
||||||
JxlEncoderStatus JxlEncoderSetFrameName(JxlEncoderFrameSettings* frame_settings,
|
JxlEncoderStatus JxlEncoderSetFrameName(JxlEncoderFrameSettings* frame_settings,
|
||||||
const char* frame_name) {
|
const char* frame_name) {
|
||||||
std::string str = frame_name;
|
std::string str = frame_name ? frame_name : "";
|
||||||
if (str.size() > 1071) {
|
if (str.size() > 1071) {
|
||||||
return JXL_API_ERROR("frame name can be max 1071 bytes long");
|
return JXL_API_ERROR("frame name can be max 1071 bytes long");
|
||||||
}
|
}
|
||||||
|
14
third_party/jpeg-xl/lib/jxl/encode_internal.h
vendored
14
third_party/jpeg-xl/lib/jxl/encode_internal.h
vendored
@ -117,12 +117,15 @@ struct JxlEncoderStruct {
|
|||||||
size_t num_queued_boxes;
|
size_t num_queued_boxes;
|
||||||
std::vector<jxl::JxlEncoderQueuedInput> input_queue;
|
std::vector<jxl::JxlEncoderQueuedInput> input_queue;
|
||||||
std::deque<uint8_t> output_byte_queue;
|
std::deque<uint8_t> output_byte_queue;
|
||||||
size_t output_bytes_flushed;
|
|
||||||
|
|
||||||
// Get the current write position in the stream (for indexing use).
|
// How many codestream bytes have been written, i.e.,
|
||||||
size_t BytePosition() const {
|
// content of jxlc and jxlp boxes. Frame index box jxli
|
||||||
return output_bytes_flushed + output_byte_queue.size();
|
// requires position indices to point to codestream bytes,
|
||||||
}
|
// so we need to keep track of the total of flushed or queue
|
||||||
|
// codestream bytes. These bytes may be in a single jxlc box
|
||||||
|
// or accross multiple jxlp boxes.
|
||||||
|
size_t codestream_bytes_written_beginning_of_frame;
|
||||||
|
size_t codestream_bytes_written_end_of_frame;
|
||||||
|
|
||||||
// Force using the container even if not needed
|
// Force using the container even if not needed
|
||||||
bool use_container;
|
bool use_container;
|
||||||
@ -153,6 +156,7 @@ struct JxlEncoderStruct {
|
|||||||
bool basic_info_set;
|
bool basic_info_set;
|
||||||
bool color_encoding_set;
|
bool color_encoding_set;
|
||||||
bool intensity_target_set;
|
bool intensity_target_set;
|
||||||
|
int brotli_effort = -1;
|
||||||
|
|
||||||
// Takes the first frame in the input_queue, encodes it, and appends
|
// Takes the first frame in the input_queue, encodes it, and appends
|
||||||
// the bytes to the output_byte_queue.
|
// the bytes to the output_byte_queue.
|
||||||
|
25
third_party/jpeg-xl/lib/jxl/encode_test.cc
vendored
25
third_party/jpeg-xl/lib/jxl/encode_test.cc
vendored
@ -38,6 +38,7 @@ TEST(EncodeTest, AddFrameAfterCloseInputTest) {
|
|||||||
basic_info.xsize = xsize;
|
basic_info.xsize = xsize;
|
||||||
basic_info.ysize = ysize;
|
basic_info.ysize = ysize;
|
||||||
basic_info.uses_original_profile = false;
|
basic_info.uses_original_profile = false;
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding,
|
JxlColorEncodingSetToSRGB(&color_encoding,
|
||||||
@ -58,7 +59,7 @@ TEST(EncodeTest, AddJPEGAfterCloseTest) {
|
|||||||
JxlEncoderCloseInput(enc.get());
|
JxlEncoderCloseInput(enc.get());
|
||||||
|
|
||||||
const std::string jpeg_path =
|
const std::string jpeg_path =
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
||||||
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
||||||
|
|
||||||
JxlEncoderFrameSettings* frame_settings =
|
JxlEncoderFrameSettings* frame_settings =
|
||||||
@ -85,6 +86,7 @@ TEST(EncodeTest, AddFrameBeforeColorEncodingTest) {
|
|||||||
basic_info.xsize = xsize;
|
basic_info.xsize = xsize;
|
||||||
basic_info.ysize = ysize;
|
basic_info.ysize = ysize;
|
||||||
basic_info.uses_original_profile = true;
|
basic_info.uses_original_profile = true;
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
JxlEncoderFrameSettings* frame_settings =
|
JxlEncoderFrameSettings* frame_settings =
|
||||||
JxlEncoderFrameSettingsCreate(enc.get(), NULL);
|
JxlEncoderFrameSettingsCreate(enc.get(), NULL);
|
||||||
@ -173,6 +175,8 @@ void VerifyFrameEncoding(size_t xsize, size_t ysize, JxlEncoder* enc,
|
|||||||
} else {
|
} else {
|
||||||
basic_info.uses_original_profile = false;
|
basic_info.uses_original_profile = false;
|
||||||
}
|
}
|
||||||
|
// 16-bit alpha means this requires level 10
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc, 10));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc, &basic_info));
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding,
|
JxlColorEncodingSetToSRGB(&color_encoding,
|
||||||
@ -198,7 +202,6 @@ void VerifyFrameEncoding(size_t xsize, size_t ysize, JxlEncoder* enc,
|
|||||||
}
|
}
|
||||||
compressed.resize(next_out - compressed.data());
|
compressed.resize(next_out - compressed.data());
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, process_result);
|
EXPECT_EQ(JXL_ENC_SUCCESS, process_result);
|
||||||
|
|
||||||
jxl::DecompressParams dparams;
|
jxl::DecompressParams dparams;
|
||||||
jxl::CodecInOut decoded_io;
|
jxl::CodecInOut decoded_io;
|
||||||
EXPECT_TRUE(jxl::DecodeFile(
|
EXPECT_TRUE(jxl::DecodeFile(
|
||||||
@ -630,6 +633,7 @@ TEST(EncodeTest, SingleFrameBoundedJXLCTest) {
|
|||||||
basic_info.xsize = xsize;
|
basic_info.xsize = xsize;
|
||||||
basic_info.ysize = ysize;
|
basic_info.ysize = ysize;
|
||||||
basic_info.uses_original_profile = false;
|
basic_info.uses_original_profile = false;
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding,
|
JxlColorEncodingSetToSRGB(&color_encoding,
|
||||||
@ -739,7 +743,7 @@ TEST(EncodeTest, CodestreamLevelTest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(EncodeTest, CodestreamLevelVerificationTest) {
|
TEST(EncodeTest, CodestreamLevelVerificationTest) {
|
||||||
JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
|
JxlPixelFormat pixel_format = {4, JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0};
|
||||||
|
|
||||||
JxlBasicInfo basic_info;
|
JxlBasicInfo basic_info;
|
||||||
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
|
jxl::test::JxlBasicInfoSetFromPixelFormat(&basic_info, &pixel_format);
|
||||||
@ -755,21 +759,20 @@ TEST(EncodeTest, CodestreamLevelVerificationTest) {
|
|||||||
// Set an image dimension that is too large for level 5, but fits in level 10
|
// Set an image dimension that is too large for level 5, but fits in level 10
|
||||||
|
|
||||||
basic_info.xsize = 1ull << 30ull;
|
basic_info.xsize = 1ull << 30ull;
|
||||||
|
EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
|
|
||||||
EXPECT_EQ(10, JxlEncoderGetRequiredCodestreamLevel(enc.get()));
|
EXPECT_EQ(10, JxlEncoderGetRequiredCodestreamLevel(enc.get()));
|
||||||
|
|
||||||
// Set an image dimension that is too large even for level 10
|
// Set an image dimension that is too large even for level 10
|
||||||
|
|
||||||
basic_info.xsize = 1ull << 31ull;
|
basic_info.xsize = 1ull << 31ull;
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
EXPECT_EQ(JXL_ENC_ERROR, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
|
|
||||||
EXPECT_EQ(-1, JxlEncoderGetRequiredCodestreamLevel(enc.get()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
|
TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGReconstructionTest)) {
|
||||||
const std::string jpeg_path =
|
const std::string jpeg_path =
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_420.jpg";
|
||||||
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
||||||
|
|
||||||
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
JxlEncoderPtr enc = JxlEncoderMake(nullptr);
|
||||||
@ -900,6 +903,7 @@ TEST(EncodeTest, BasicInfoTest) {
|
|||||||
basic_info.animation.tps_denominator = 77;
|
basic_info.animation.tps_denominator = 77;
|
||||||
basic_info.animation.num_loops = 10;
|
basic_info.animation.num_loops = 10;
|
||||||
basic_info.animation.have_timecodes = JXL_TRUE;
|
basic_info.animation.have_timecodes = JXL_TRUE;
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
|
JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
|
||||||
@ -1001,6 +1005,7 @@ TEST(EncodeTest, AnimationHeaderTest) {
|
|||||||
basic_info.animation.tps_numerator = 1000;
|
basic_info.animation.tps_numerator = 1000;
|
||||||
basic_info.animation.tps_denominator = 1;
|
basic_info.animation.tps_denominator = 1;
|
||||||
basic_info.animation.have_timecodes = JXL_TRUE;
|
basic_info.animation.have_timecodes = JXL_TRUE;
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
|
JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
|
||||||
@ -1102,6 +1107,7 @@ TEST(EncodeTest, CroppedFrameTest) {
|
|||||||
basic_info.xsize = 100;
|
basic_info.xsize = 100;
|
||||||
basic_info.ysize = 100;
|
basic_info.ysize = 100;
|
||||||
basic_info.uses_original_profile = JXL_TRUE;
|
basic_info.uses_original_profile = JXL_TRUE;
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
|
JxlColorEncodingSetToSRGB(&color_encoding, /*is_gray=*/false);
|
||||||
@ -1194,6 +1200,7 @@ TEST(EncodeTest, BoxTest) {
|
|||||||
basic_info.xsize = xsize;
|
basic_info.xsize = xsize;
|
||||||
basic_info.ysize = ysize;
|
basic_info.ysize = ysize;
|
||||||
basic_info.uses_original_profile = false;
|
basic_info.uses_original_profile = false;
|
||||||
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetCodestreamLevel(enc.get(), 10));
|
||||||
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
EXPECT_EQ(JXL_ENC_SUCCESS, JxlEncoderSetBasicInfo(enc.get(), &basic_info));
|
||||||
JxlColorEncoding color_encoding;
|
JxlColorEncoding color_encoding;
|
||||||
JxlColorEncodingSetToSRGB(&color_encoding,
|
JxlColorEncodingSetToSRGB(&color_encoding,
|
||||||
@ -1300,7 +1307,7 @@ TEST(EncodeTest, JXL_TRANSCODE_JPEG_TEST(JPEGFrameTest)) {
|
|||||||
for (int skip_color_encoding = 0; skip_color_encoding < 2;
|
for (int skip_color_encoding = 0; skip_color_encoding < 2;
|
||||||
skip_color_encoding++) {
|
skip_color_encoding++) {
|
||||||
const std::string jpeg_path =
|
const std::string jpeg_path =
|
||||||
"imagecompression.info/flower_foveon_cropped.jpg";
|
"third_party/imagecompression.info/flower_foveon_cropped.jpg";
|
||||||
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
const jxl::PaddedBytes orig = jxl::ReadTestData(jpeg_path);
|
||||||
jxl::CodecInOut orig_io;
|
jxl::CodecInOut orig_io;
|
||||||
ASSERT_TRUE(SetFromBytes(jxl::Span<const uint8_t>(orig), &orig_io,
|
ASSERT_TRUE(SetFromBytes(jxl::Span<const uint8_t>(orig), &orig_io,
|
||||||
|
89
third_party/jpeg-xl/lib/jxl/exif.cc
vendored
Normal file
89
third_party/jpeg-xl/lib/jxl/exif.cc
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// 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/jxl/exif.h"
|
||||||
|
|
||||||
|
namespace jxl {
|
||||||
|
|
||||||
|
constexpr uint16_t kExifOrientationTag = 274;
|
||||||
|
|
||||||
|
// Checks if a blob looks like Exif, and if so, sets bigendian
|
||||||
|
// according to the tiff endianness
|
||||||
|
bool IsExif(const std::vector<uint8_t>& exif, bool* bigendian) {
|
||||||
|
if (exif.size() < 12) return false; // not enough bytes for a valid exif blob
|
||||||
|
const uint8_t* t = exif.data();
|
||||||
|
if (LoadLE32(t) == 0x2A004D4D) {
|
||||||
|
*bigendian = true;
|
||||||
|
return true;
|
||||||
|
} else if (LoadLE32(t) == 0x002A4949) {
|
||||||
|
*bigendian = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false; // not a valid tiff header
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finds the position of an Exif tag, or 0 if it is not found
|
||||||
|
size_t FindExifTagPosition(const std::vector<uint8_t>& exif, uint16_t tagname) {
|
||||||
|
bool bigendian;
|
||||||
|
if (!IsExif(exif, &bigendian)) return 0;
|
||||||
|
const uint8_t* t = exif.data() + 4;
|
||||||
|
uint32_t offset = (bigendian ? LoadBE32(t) : LoadLE32(t));
|
||||||
|
if (exif.size() < 12 + offset + 2 || offset < 8) return 0;
|
||||||
|
t += offset - 4;
|
||||||
|
uint16_t nb_tags = (bigendian ? LoadBE16(t) : LoadLE16(t));
|
||||||
|
t += 2;
|
||||||
|
while (nb_tags > 0) {
|
||||||
|
if (t + 12 >= exif.data() + exif.size()) return 0;
|
||||||
|
uint16_t tag = (bigendian ? LoadBE16(t) : LoadLE16(t));
|
||||||
|
t += 2;
|
||||||
|
if (tag == tagname) return static_cast<size_t>(t - exif.data());
|
||||||
|
t += 10;
|
||||||
|
nb_tags--;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO (jon): tag 1 can be used to represent Adobe RGB 1998 if it has value
|
||||||
|
// "R03"
|
||||||
|
// TODO (jon): set intrinsic dimensions according to
|
||||||
|
// https://discourse.wicg.io/t/proposal-exif-image-resolution-auto-and-from-image/4326/24
|
||||||
|
void InterpretExif(const std::vector<uint8_t>& exif, CodecMetadata* metadata) {
|
||||||
|
bool bigendian;
|
||||||
|
if (!IsExif(exif, &bigendian)) return;
|
||||||
|
size_t o_pos = FindExifTagPosition(exif, kExifOrientationTag);
|
||||||
|
if (o_pos) {
|
||||||
|
const uint8_t* t = exif.data() + o_pos;
|
||||||
|
uint16_t type = (bigendian ? LoadBE16(t) : LoadLE16(t));
|
||||||
|
t += 2;
|
||||||
|
uint32_t count = (bigendian ? LoadBE32(t) : LoadLE32(t));
|
||||||
|
t += 4;
|
||||||
|
uint16_t value = (bigendian ? LoadBE16(t) : LoadLE16(t));
|
||||||
|
t += 4;
|
||||||
|
if (type == 3 && count == 1 && value >= 1 && value <= 8) {
|
||||||
|
metadata->m.orientation = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResetExifOrientation(std::vector<uint8_t>& exif) {
|
||||||
|
bool bigendian;
|
||||||
|
if (!IsExif(exif, &bigendian)) return;
|
||||||
|
size_t o_pos = FindExifTagPosition(exif, kExifOrientationTag);
|
||||||
|
if (o_pos) {
|
||||||
|
uint8_t* t = exif.data() + o_pos;
|
||||||
|
uint16_t type = (bigendian ? LoadBE16(t) : LoadLE16(t));
|
||||||
|
t += 2;
|
||||||
|
uint32_t count = (bigendian ? LoadBE32(t) : LoadLE32(t));
|
||||||
|
t += 4;
|
||||||
|
if (type == 3 && count == 1) {
|
||||||
|
if (bigendian)
|
||||||
|
StoreBE16(1, t);
|
||||||
|
else
|
||||||
|
StoreLE16(1, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace jxl
|
27
third_party/jpeg-xl/lib/jxl/exif.h
vendored
Normal file
27
third_party/jpeg-xl/lib/jxl/exif.h
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
// 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_EXIF_H_
|
||||||
|
#define LIB_JXL_EXIF_H_
|
||||||
|
|
||||||
|
// Basic parsing of Exif (just enough for the render-impacting things
|
||||||
|
// like orientation)
|
||||||
|
|
||||||
|
#include "lib/jxl/base/padded_bytes.h"
|
||||||
|
#include "lib/jxl/image_metadata.h"
|
||||||
|
|
||||||
|
namespace jxl {
|
||||||
|
|
||||||
|
// Parses the Exif data just enough to extract any render-impacting info.
|
||||||
|
// If the Exif data is invalid or could not be parsed, then it is treated
|
||||||
|
// as a no-op.
|
||||||
|
void InterpretExif(const std::vector<uint8_t>& exif, CodecMetadata* metadata);
|
||||||
|
|
||||||
|
// Sets the Exif orientation to the identity, to avoid repeated orientation
|
||||||
|
void ResetExifOrientation(std::vector<uint8_t>& exif);
|
||||||
|
|
||||||
|
} // namespace jxl
|
||||||
|
|
||||||
|
#endif // LIB_JXL_EXIF_H_
|
2
third_party/jpeg-xl/lib/jxl/frame_header.h
vendored
2
third_party/jpeg-xl/lib/jxl/frame_header.h
vendored
@ -40,7 +40,7 @@ static inline Status VisitNameString(Visitor* JXL_RESTRICT visitor,
|
|||||||
name->resize(name_length);
|
name->resize(name_length);
|
||||||
}
|
}
|
||||||
for (size_t i = 0; i < name_length; i++) {
|
for (size_t i = 0; i < name_length; i++) {
|
||||||
uint32_t c = (*name)[i];
|
uint32_t c = static_cast<uint8_t>((*name)[i]);
|
||||||
JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(8, 0, &c));
|
JXL_QUIET_RETURN_IF_ERROR(visitor->Bits(8, 0, &c));
|
||||||
(*name)[i] = static_cast<char>(c);
|
(*name)[i] = static_cast<char>(c);
|
||||||
}
|
}
|
||||||
|
13
third_party/jpeg-xl/lib/jxl/icc_codec.cc
vendored
13
third_party/jpeg-xl/lib/jxl/icc_codec.cc
vendored
@ -22,19 +22,6 @@
|
|||||||
namespace jxl {
|
namespace jxl {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
uint64_t DecodeVarInt(const uint8_t* input, size_t inputSize, size_t* pos) {
|
|
||||||
size_t i;
|
|
||||||
uint64_t ret = 0;
|
|
||||||
for (i = 0; *pos + i < inputSize && i < 10; ++i) {
|
|
||||||
ret |= uint64_t(input[*pos + i] & 127) << uint64_t(7 * i);
|
|
||||||
// If the next-byte flag is not set, stop
|
|
||||||
if ((input[*pos + i] & 128) == 0) break;
|
|
||||||
}
|
|
||||||
// TODO: Return a decoding error if i == 10.
|
|
||||||
*pos += i + 1;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shuffles or interleaves bytes, for example with width 2, turns "ABCDabcd"
|
// Shuffles or interleaves bytes, for example with width 2, turns "ABCDabcd"
|
||||||
// into "AaBbCcDc". Transposes a matrix of ceil(size / width) columns and
|
// into "AaBbCcDc". Transposes a matrix of ceil(size / width) columns and
|
||||||
// width rows. There are size elements, size may be < width * height, if so the
|
// width rows. There are size elements, size may be < width * height, if so the
|
||||||
|
54
third_party/jpeg-xl/lib/jxl/image.cc
vendored
54
third_party/jpeg-xl/lib/jxl/image.cc
vendored
@ -133,47 +133,6 @@ void PlaneBase::Swap(PlaneBase& other) {
|
|||||||
std::swap(bytes_, other.bytes_);
|
std::swap(bytes_, other.bytes_);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageB ImageFromPacked(const uint8_t* packed, const size_t xsize,
|
|
||||||
const size_t ysize, const size_t bytes_per_row) {
|
|
||||||
JXL_ASSERT(bytes_per_row >= xsize);
|
|
||||||
ImageB image(xsize, ysize);
|
|
||||||
PROFILER_FUNC;
|
|
||||||
for (size_t y = 0; y < ysize; ++y) {
|
|
||||||
uint8_t* const JXL_RESTRICT row = image.Row(y);
|
|
||||||
const uint8_t* const JXL_RESTRICT packed_row = packed + y * bytes_per_row;
|
|
||||||
memcpy(row, packed_row, xsize);
|
|
||||||
}
|
|
||||||
return image;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note that using mirroring here gives slightly worse results.
|
|
||||||
ImageF PadImage(const ImageF& in, const size_t xsize, const size_t ysize) {
|
|
||||||
JXL_ASSERT(xsize >= in.xsize());
|
|
||||||
JXL_ASSERT(ysize >= in.ysize());
|
|
||||||
ImageF out(xsize, ysize);
|
|
||||||
size_t y = 0;
|
|
||||||
for (; y < in.ysize(); ++y) {
|
|
||||||
const float* JXL_RESTRICT row_in = in.ConstRow(y);
|
|
||||||
float* JXL_RESTRICT row_out = out.Row(y);
|
|
||||||
memcpy(row_out, row_in, in.xsize() * sizeof(row_in[0]));
|
|
||||||
const int lastcol = in.xsize() - 1;
|
|
||||||
const float lastval = row_out[lastcol];
|
|
||||||
for (size_t x = in.xsize(); x < xsize; ++x) {
|
|
||||||
row_out[x] = lastval;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(janwas): no need to copy if we can 'extend' image: if rows are
|
|
||||||
// pointers to any memory? Or allocate larger image before IO?
|
|
||||||
const int lastrow = in.ysize() - 1;
|
|
||||||
for (; y < ysize; ++y) {
|
|
||||||
const float* JXL_RESTRICT row_in = out.ConstRow(lastrow);
|
|
||||||
float* JXL_RESTRICT row_out = out.Row(y);
|
|
||||||
memcpy(row_out, row_in, xsize * sizeof(row_out[0]));
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
Image3F PadImageMirror(const Image3F& in, const size_t xborder,
|
Image3F PadImageMirror(const Image3F& in, const size_t xborder,
|
||||||
const size_t yborder) {
|
const size_t yborder) {
|
||||||
size_t xsize = in.xsize();
|
size_t xsize = in.xsize();
|
||||||
@ -216,19 +175,6 @@ Image3F PadImageMirror(const Image3F& in, const size_t xborder,
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
Image3F PadImageToMultiple(const Image3F& in, const size_t N) {
|
|
||||||
PROFILER_FUNC;
|
|
||||||
const size_t xsize_blocks = DivCeil(in.xsize(), N);
|
|
||||||
const size_t ysize_blocks = DivCeil(in.ysize(), N);
|
|
||||||
const size_t xsize = N * xsize_blocks;
|
|
||||||
const size_t ysize = N * ysize_blocks;
|
|
||||||
ImageF out[3];
|
|
||||||
for (size_t c = 0; c < 3; ++c) {
|
|
||||||
out[c] = PadImage(in.Plane(c), xsize, ysize);
|
|
||||||
}
|
|
||||||
return Image3F(std::move(out[0]), std::move(out[1]), std::move(out[2]));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in) {
|
void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in) {
|
||||||
PROFILER_FUNC;
|
PROFILER_FUNC;
|
||||||
const size_t xsize_orig = in->xsize();
|
const size_t xsize_orig = in->xsize();
|
||||||
|
99
third_party/jpeg-xl/lib/jxl/image.h
vendored
99
third_party/jpeg-xl/lib/jxl/image.h
vendored
@ -194,87 +194,94 @@ class Image3;
|
|||||||
// shifting the pointer by x0/y0 allows this to apply to multiple images with
|
// shifting the pointer by x0/y0 allows this to apply to multiple images with
|
||||||
// different resolutions (e.g. color transform and quantization field).
|
// different resolutions (e.g. color transform and quantization field).
|
||||||
// Can compare using SameSize(rect1, rect2).
|
// Can compare using SameSize(rect1, rect2).
|
||||||
class Rect {
|
template <typename T>
|
||||||
|
class RectT {
|
||||||
public:
|
public:
|
||||||
// Most windows are xsize_max * ysize_max, except those on the borders where
|
// Most windows are xsize_max * ysize_max, except those on the borders where
|
||||||
// begin + size_max > end.
|
// begin + size_max > end.
|
||||||
constexpr Rect(size_t xbegin, size_t ybegin, size_t xsize_max,
|
constexpr RectT(T xbegin, T ybegin, size_t xsize_max, size_t ysize_max,
|
||||||
size_t ysize_max, size_t xend, size_t yend)
|
T xend, T yend)
|
||||||
: x0_(xbegin),
|
: x0_(xbegin),
|
||||||
y0_(ybegin),
|
y0_(ybegin),
|
||||||
xsize_(ClampedSize(xbegin, xsize_max, xend)),
|
xsize_(ClampedSize(xbegin, xsize_max, xend)),
|
||||||
ysize_(ClampedSize(ybegin, ysize_max, yend)) {}
|
ysize_(ClampedSize(ybegin, ysize_max, yend)) {}
|
||||||
|
|
||||||
// Construct with origin and known size (typically from another Rect).
|
// Construct with origin and known size (typically from another Rect).
|
||||||
constexpr Rect(size_t xbegin, size_t ybegin, size_t xsize, size_t ysize)
|
constexpr RectT(T xbegin, T ybegin, size_t xsize, size_t ysize)
|
||||||
: x0_(xbegin), y0_(ybegin), xsize_(xsize), ysize_(ysize) {}
|
: x0_(xbegin), y0_(ybegin), xsize_(xsize), ysize_(ysize) {}
|
||||||
|
|
||||||
// Construct a rect that covers a whole image/plane/ImageBundle etc.
|
// Construct a rect that covers a whole image/plane/ImageBundle etc.
|
||||||
template <typename Image>
|
template <typename ImageT>
|
||||||
explicit Rect(const Image& image)
|
explicit RectT(const ImageT& image)
|
||||||
: Rect(0, 0, image.xsize(), image.ysize()) {}
|
: RectT(0, 0, image.xsize(), image.ysize()) {}
|
||||||
|
|
||||||
Rect() : Rect(0, 0, 0, 0) {}
|
RectT() : RectT(0, 0, 0, 0) {}
|
||||||
|
|
||||||
Rect(const Rect&) = default;
|
RectT(const RectT&) = default;
|
||||||
Rect& operator=(const Rect&) = default;
|
RectT& operator=(const RectT&) = default;
|
||||||
|
|
||||||
// Construct a subrect that resides in an image/plane/ImageBundle etc.
|
// Construct a subrect that resides in an image/plane/ImageBundle etc.
|
||||||
template <typename Image>
|
template <typename ImageT>
|
||||||
Rect Crop(const Image& image) const {
|
RectT Crop(const ImageT& image) const {
|
||||||
return Rect(x0_, y0_, xsize_, ysize_, image.xsize(), image.ysize());
|
return Intersection(RectT(image));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct a subrect that resides in the [0, ysize) x [0, xsize) region of
|
// Construct a subrect that resides in the [0, ysize) x [0, xsize) region of
|
||||||
// the current rect.
|
// the current rect.
|
||||||
Rect Crop(size_t area_xsize, size_t area_ysize) const {
|
RectT Crop(size_t area_xsize, size_t area_ysize) const {
|
||||||
return Rect(x0_, y0_, xsize_, ysize_, area_xsize, area_ysize);
|
return Intersection(RectT(0, 0, area_xsize, area_ysize));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a rect that only contains `num` lines with offset `y` from `y0()`.
|
// Returns a rect that only contains `num` lines with offset `y` from `y0()`.
|
||||||
Rect Lines(size_t y, size_t num) const {
|
RectT Lines(size_t y, size_t num) const {
|
||||||
JXL_DASSERT(y + num <= ysize_);
|
JXL_DASSERT(y + num <= ysize_);
|
||||||
return Rect(x0_, y0_ + y, xsize_, num);
|
return RectT(x0_, y0_ + y, xsize_, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
Rect Line(size_t y) const { return Lines(y, 1); }
|
RectT Line(size_t y) const { return Lines(y, 1); }
|
||||||
|
|
||||||
JXL_MUST_USE_RESULT Rect Intersection(const Rect& other) const {
|
JXL_MUST_USE_RESULT RectT Intersection(const RectT& other) const {
|
||||||
return Rect(std::max(x0_, other.x0_), std::max(y0_, other.y0_), xsize_,
|
return RectT(std::max(x0_, other.x0_), std::max(y0_, other.y0_), xsize_,
|
||||||
ysize_, std::min(x0_ + xsize_, other.x0_ + other.xsize_),
|
ysize_, std::min(x0_ + xsize_, other.x0_ + other.xsize_),
|
||||||
std::min(y0_ + ysize_, other.y0_ + other.ysize_));
|
std::min(y0_ + ysize_, other.y0_ + other.ysize_));
|
||||||
}
|
}
|
||||||
|
|
||||||
JXL_MUST_USE_RESULT Rect Translate(int64_t x_offset, int64_t y_offset) const {
|
JXL_MUST_USE_RESULT RectT Translate(int64_t x_offset,
|
||||||
return Rect(x0_ + x_offset, y0_ + y_offset, xsize_, ysize_);
|
int64_t y_offset) const {
|
||||||
|
return RectT(x0_ + x_offset, y0_ + y_offset, xsize_, ysize_);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename V>
|
||||||
T* Row(Plane<T>* image, size_t y) const {
|
V* Row(Plane<V>* image, size_t y) const {
|
||||||
|
JXL_DASSERT(y + y0_ >= 0);
|
||||||
return image->Row(y + y0_) + x0_;
|
return image->Row(y + y0_) + x0_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename V>
|
||||||
const T* Row(const Plane<T>* image, size_t y) const {
|
const V* Row(const Plane<V>* image, size_t y) const {
|
||||||
|
JXL_DASSERT(y + y0_ >= 0);
|
||||||
return image->Row(y + y0_) + x0_;
|
return image->Row(y + y0_) + x0_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename V>
|
||||||
T* PlaneRow(Image3<T>* image, const size_t c, size_t y) const {
|
V* PlaneRow(Image3<V>* image, const size_t c, size_t y) const {
|
||||||
|
JXL_DASSERT(y + y0_ >= 0);
|
||||||
return image->PlaneRow(c, y + y0_) + x0_;
|
return image->PlaneRow(c, y + y0_) + x0_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename V>
|
||||||
const T* ConstRow(const Plane<T>& image, size_t y) const {
|
const V* ConstRow(const Plane<V>& image, size_t y) const {
|
||||||
|
JXL_DASSERT(y + y0_ >= 0);
|
||||||
return image.ConstRow(y + y0_) + x0_;
|
return image.ConstRow(y + y0_) + x0_;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename V>
|
||||||
const T* ConstPlaneRow(const Image3<T>& image, size_t c, size_t y) const {
|
const V* ConstPlaneRow(const Image3<V>& image, size_t c, size_t y) const {
|
||||||
|
JXL_DASSERT(y + y0_ >= 0);
|
||||||
return image.ConstPlaneRow(c, y + y0_) + x0_;
|
return image.ConstPlaneRow(c, y + y0_) + x0_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsInside(const Rect& other) const {
|
bool IsInside(const RectT& other) const {
|
||||||
return x0_ >= other.x0() && x0_ + xsize_ <= other.x0() + other.xsize_ &&
|
return x0_ >= other.x0() && x0_ + xsize_ <= other.x0() + other.xsize_ &&
|
||||||
y0_ >= other.y0() && y0_ + ysize_ <= other.y0() + other.ysize();
|
y0_ >= other.y0() && y0_ + ysize_ <= other.y0() + other.ysize();
|
||||||
}
|
}
|
||||||
@ -283,29 +290,33 @@ class Rect {
|
|||||||
// Plane<T> or Image3<T>; however if ImageT is Rect, results are nonsensical.
|
// Plane<T> or Image3<T>; however if ImageT is Rect, results are nonsensical.
|
||||||
template <class ImageT>
|
template <class ImageT>
|
||||||
bool IsInside(const ImageT& image) const {
|
bool IsInside(const ImageT& image) const {
|
||||||
return (x0_ + xsize_ <= image.xsize()) && (y0_ + ysize_ <= image.ysize());
|
return IsInside(RectT(image));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t x0() const { return x0_; }
|
T x0() const { return x0_; }
|
||||||
size_t y0() const { return y0_; }
|
T y0() const { return y0_; }
|
||||||
size_t xsize() const { return xsize_; }
|
size_t xsize() const { return xsize_; }
|
||||||
size_t ysize() const { return ysize_; }
|
size_t ysize() const { return ysize_; }
|
||||||
|
T x1() const { return x0_ + xsize_; }
|
||||||
|
T y1() const { return y0_ + ysize_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Returns size_max, or whatever is left in [begin, end).
|
// Returns size_max, or whatever is left in [begin, end).
|
||||||
static constexpr size_t ClampedSize(size_t begin, size_t size_max,
|
static constexpr size_t ClampedSize(T begin, size_t size_max, T end) {
|
||||||
size_t end) {
|
return (static_cast<T>(begin + size_max) <= end)
|
||||||
return (begin + size_max <= end) ? size_max
|
? size_max
|
||||||
: (end > begin ? end - begin : 0);
|
: (end > begin ? end - begin : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t x0_;
|
T x0_;
|
||||||
size_t y0_;
|
T y0_;
|
||||||
|
|
||||||
size_t xsize_;
|
size_t xsize_;
|
||||||
size_t ysize_;
|
size_t ysize_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using Rect = RectT<size_t>;
|
||||||
|
|
||||||
// Currently, we abuse Image to either refer to an image that owns its storage
|
// Currently, we abuse Image to either refer to an image that owns its storage
|
||||||
// or one that doesn't. In similar vein, we abuse Image* function parameters to
|
// or one that doesn't. In similar vein, we abuse Image* function parameters to
|
||||||
// either mean "assign to me" or "fill the provided image with data".
|
// either mean "assign to me" or "fill the provided image with data".
|
||||||
|
9
third_party/jpeg-xl/lib/jxl/image_ops.h
vendored
9
third_party/jpeg-xl/lib/jxl/image_ops.h
vendored
@ -789,19 +789,10 @@ void ZeroFillPlane(Plane<T>* image, Rect rect) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// First, image is padded horizontally, with the rightmost value.
|
|
||||||
// Next, image is padded vertically, by repeating the last line.
|
|
||||||
ImageF PadImage(const ImageF& in, size_t xsize, size_t ysize);
|
|
||||||
|
|
||||||
// Pad an image with xborder columns on each vertical side and yboder rows
|
// Pad an image with xborder columns on each vertical side and yboder rows
|
||||||
// above and below, mirroring the image.
|
// above and below, mirroring the image.
|
||||||
Image3F PadImageMirror(const Image3F& in, size_t xborder, size_t yborder);
|
Image3F PadImageMirror(const Image3F& in, size_t xborder, size_t yborder);
|
||||||
|
|
||||||
// First, image is padded horizontally, with the rightmost value.
|
|
||||||
// Next, image is padded vertically, by repeating the last line.
|
|
||||||
// Prefer PadImageToBlockMultipleInPlace if padding to kBlockDim.
|
|
||||||
Image3F PadImageToMultiple(const Image3F& in, size_t N);
|
|
||||||
|
|
||||||
// Same as above, but operates in-place. Assumes that the `in` image was
|
// Same as above, but operates in-place. Assumes that the `in` image was
|
||||||
// allocated large enough.
|
// allocated large enough.
|
||||||
void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in);
|
void PadImageToBlockMultipleInPlace(Image3F* JXL_RESTRICT in);
|
||||||
|
@ -230,7 +230,8 @@ Status SetColorEncodingFromJpegData(const jpeg::JPEGData& jpg,
|
|||||||
return color_encoding->SetICC(std::move(icc_profile));
|
return color_encoding->SetICC(std::move(icc_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
Status EncodeJPEGData(JPEGData& jpeg_data, PaddedBytes* bytes) {
|
Status EncodeJPEGData(JPEGData& jpeg_data, PaddedBytes* bytes,
|
||||||
|
const CompressParams& cparams) {
|
||||||
jpeg_data.app_marker_type.resize(jpeg_data.app_data.size(),
|
jpeg_data.app_marker_type.resize(jpeg_data.app_data.size(),
|
||||||
AppMarkerType::kUnknown);
|
AppMarkerType::kUnknown);
|
||||||
JXL_RETURN_IF_ERROR(DetectIccProfile(jpeg_data));
|
JXL_RETURN_IF_ERROR(DetectIccProfile(jpeg_data));
|
||||||
@ -241,7 +242,9 @@ Status EncodeJPEGData(JPEGData& jpeg_data, PaddedBytes* bytes) {
|
|||||||
*bytes = std::move(writer).TakeBytes();
|
*bytes = std::move(writer).TakeBytes();
|
||||||
BrotliEncoderState* brotli_enc =
|
BrotliEncoderState* brotli_enc =
|
||||||
BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
|
BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
|
||||||
BrotliEncoderSetParameter(brotli_enc, BROTLI_PARAM_QUALITY, 11);
|
int effort = cparams.brotli_effort;
|
||||||
|
if (effort < 0) effort = 11 - static_cast<int>(cparams.speed_tier);
|
||||||
|
BrotliEncoderSetParameter(brotli_enc, BROTLI_PARAM_QUALITY, effort);
|
||||||
size_t total_data = 0;
|
size_t total_data = 0;
|
||||||
for (size_t i = 0; i < jpeg_data.app_data.size(); i++) {
|
for (size_t i = 0; i < jpeg_data.app_data.size(); i++) {
|
||||||
if (jpeg_data.app_marker_type[i] != AppMarkerType::kUnknown) {
|
if (jpeg_data.app_marker_type[i] != AppMarkerType::kUnknown) {
|
||||||
|
@ -8,11 +8,13 @@
|
|||||||
|
|
||||||
#include "lib/jxl/base/padded_bytes.h"
|
#include "lib/jxl/base/padded_bytes.h"
|
||||||
#include "lib/jxl/codec_in_out.h"
|
#include "lib/jxl/codec_in_out.h"
|
||||||
|
#include "lib/jxl/enc_params.h"
|
||||||
#include "lib/jxl/jpeg/jpeg_data.h"
|
#include "lib/jxl/jpeg/jpeg_data.h"
|
||||||
|
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
namespace jpeg {
|
namespace jpeg {
|
||||||
Status EncodeJPEGData(JPEGData& jpeg_data, PaddedBytes* bytes);
|
Status EncodeJPEGData(JPEGData& jpeg_data, PaddedBytes* bytes,
|
||||||
|
const CompressParams& cparams);
|
||||||
|
|
||||||
Status SetColorEncodingFromJpegData(const jpeg::JPEGData& jpg,
|
Status SetColorEncodingFromJpegData(const jpeg::JPEGData& jpg,
|
||||||
ColorEncoding* color_encoding);
|
ColorEncoding* color_encoding);
|
||||||
|
@ -95,6 +95,7 @@ bool ProcessSOF(const uint8_t* data, const size_t len, JpegReadMode mode,
|
|||||||
int height = ReadUint16(data, pos);
|
int height = ReadUint16(data, pos);
|
||||||
int width = ReadUint16(data, pos);
|
int width = ReadUint16(data, pos);
|
||||||
int num_components = ReadUint8(data, pos);
|
int num_components = ReadUint8(data, pos);
|
||||||
|
// 'jbrd' is hardcoded for 8bits:
|
||||||
JXL_JPEG_VERIFY_INPUT(precision, 8, 8, PRECISION);
|
JXL_JPEG_VERIFY_INPUT(precision, 8, 8, PRECISION);
|
||||||
JXL_JPEG_VERIFY_INPUT(height, 1, kMaxDimPixels, HEIGHT);
|
JXL_JPEG_VERIFY_INPUT(height, 1, kMaxDimPixels, HEIGHT);
|
||||||
JXL_JPEG_VERIFY_INPUT(width, 1, kMaxDimPixels, WIDTH);
|
JXL_JPEG_VERIFY_INPUT(width, 1, kMaxDimPixels, WIDTH);
|
||||||
|
180
third_party/jpeg-xl/lib/jxl/jxl_test.cc
vendored
180
third_party/jpeg-xl/lib/jxl/jxl_test.cc
vendored
@ -124,7 +124,7 @@ TEST(JxlTest, RoundtripMarker) {
|
|||||||
TEST(JxlTest, RoundtripTinyFast) {
|
TEST(JxlTest, RoundtripTinyFast) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(32, 32);
|
io.ShrinkTo(32, 32);
|
||||||
@ -142,7 +142,7 @@ TEST(JxlTest, RoundtripTinyFast) {
|
|||||||
TEST(JxlTest, RoundtripSmallD1) {
|
TEST(JxlTest, RoundtripSmallD1) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.butteraugli_distance = 1.0;
|
cparams.butteraugli_distance = 1.0;
|
||||||
DecompressParams dparams;
|
DecompressParams dparams;
|
||||||
@ -183,7 +183,7 @@ TEST(JxlTest, RoundtripSmallD1) {
|
|||||||
TEST(JxlTest, RoundtripOtherTransforms) {
|
TEST(JxlTest, RoundtripOtherTransforms) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/64px/a2d1un_nkitzmiller_srgb8.png");
|
ReadTestData("third_party/wesaturate/64px/a2d1un_nkitzmiller_srgb8.png");
|
||||||
std::unique_ptr<CodecInOut> io = jxl::make_unique<CodecInOut>();
|
std::unique_ptr<CodecInOut> io = jxl::make_unique<CodecInOut>();
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), io.get(), pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), io.get(), pool));
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ TEST(JxlTest, RoundtripOtherTransforms) {
|
|||||||
TEST(JxlTest, RoundtripResample2) {
|
TEST(JxlTest, RoundtripResample2) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize(), io.ysize());
|
io.ShrinkTo(io.xsize(), io.ysize());
|
||||||
@ -232,7 +232,7 @@ TEST(JxlTest, RoundtripResample2) {
|
|||||||
TEST(JxlTest, RoundtripResample2MT) {
|
TEST(JxlTest, RoundtripResample2MT) {
|
||||||
ThreadPoolInternal pool(4);
|
ThreadPoolInternal pool(4);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
// image has to be large enough to have multiple groups after downsampling
|
// image has to be large enough to have multiple groups after downsampling
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
@ -254,7 +254,7 @@ TEST(JxlTest, RoundtripOutOfOrderProcessing) {
|
|||||||
FakeParallelRunner fake_pool(/*order_seed=*/123, /*num_threads=*/8);
|
FakeParallelRunner fake_pool(/*order_seed=*/123, /*num_threads=*/8);
|
||||||
ThreadPool pool(&JxlFakeParallelRunner, &fake_pool);
|
ThreadPool pool(&JxlFakeParallelRunner, &fake_pool);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
// Image size is selected so that the block border needed is larger than the
|
// Image size is selected so that the block border needed is larger than the
|
||||||
@ -277,7 +277,7 @@ TEST(JxlTest, RoundtripOutOfOrderProcessingBorder) {
|
|||||||
FakeParallelRunner fake_pool(/*order_seed=*/47, /*num_threads=*/8);
|
FakeParallelRunner fake_pool(/*order_seed=*/47, /*num_threads=*/8);
|
||||||
ThreadPool pool(&JxlFakeParallelRunner, &fake_pool);
|
ThreadPool pool(&JxlFakeParallelRunner, &fake_pool);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
// Image size is selected so that the block border needed is larger than the
|
// Image size is selected so that the block border needed is larger than the
|
||||||
@ -300,7 +300,7 @@ TEST(JxlTest, RoundtripOutOfOrderProcessingBorder) {
|
|||||||
TEST(JxlTest, RoundtripResample4) {
|
TEST(JxlTest, RoundtripResample4) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize(), io.ysize());
|
io.ShrinkTo(io.xsize(), io.ysize());
|
||||||
@ -317,7 +317,7 @@ TEST(JxlTest, RoundtripResample4) {
|
|||||||
TEST(JxlTest, RoundtripResample8) {
|
TEST(JxlTest, RoundtripResample8) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize(), io.ysize());
|
io.ShrinkTo(io.xsize(), io.ysize());
|
||||||
@ -334,7 +334,7 @@ TEST(JxlTest, RoundtripResample8) {
|
|||||||
TEST(JxlTest, RoundtripUnalignedD2) {
|
TEST(JxlTest, RoundtripUnalignedD2) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize() / 12, io.ysize() / 7);
|
io.ShrinkTo(io.xsize() / 12, io.ysize() / 7);
|
||||||
@ -355,7 +355,7 @@ TEST(JxlTest, RoundtripUnalignedD2) {
|
|||||||
TEST(JxlTest, RoundtripMultiGroupNL) {
|
TEST(JxlTest, RoundtripMultiGroupNL) {
|
||||||
ThreadPoolInternal pool(4);
|
ThreadPoolInternal pool(4);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
io.ShrinkTo(600, 1024); // partial X, full Y group
|
io.ShrinkTo(600, 1024); // partial X, full Y group
|
||||||
@ -384,7 +384,7 @@ TEST(JxlTest, RoundtripMultiGroupNL) {
|
|||||||
TEST(JxlTest, RoundtripMultiGroup) {
|
TEST(JxlTest, RoundtripMultiGroup) {
|
||||||
ThreadPoolInternal pool(4);
|
ThreadPoolInternal pool(4);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
io.ShrinkTo(600, 1024);
|
io.ShrinkTo(600, 1024);
|
||||||
@ -410,7 +410,7 @@ TEST(JxlTest, RoundtripMultiGroup) {
|
|||||||
TEST(JxlTest, RoundtripLargeFast) {
|
TEST(JxlTest, RoundtripLargeFast) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -425,7 +425,7 @@ TEST(JxlTest, RoundtripLargeFast) {
|
|||||||
TEST(JxlTest, RoundtripDotsForceEpf) {
|
TEST(JxlTest, RoundtripDotsForceEpf) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/cvo9xd_keong_macan_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/cvo9xd_keong_macan_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -445,7 +445,7 @@ TEST(JxlTest, RoundtripDotsForceEpf) {
|
|||||||
TEST(JxlTest, RoundtripD2Consistent) {
|
TEST(JxlTest, RoundtripD2Consistent) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -478,7 +478,7 @@ TEST(JxlTest, RoundtripD2Consistent) {
|
|||||||
TEST(JxlTest, RoundtripLargeConsistent) {
|
TEST(JxlTest, RoundtripLargeConsistent) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -508,7 +508,7 @@ TEST(JxlTest, RoundtripLargeConsistent) {
|
|||||||
TEST(JxlTest, RoundtripSmallNL) {
|
TEST(JxlTest, RoundtripSmallNL) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
||||||
@ -529,7 +529,7 @@ TEST(JxlTest, RoundtripSmallNL) {
|
|||||||
TEST(JxlTest, RoundtripNoGaborishNoAR) {
|
TEST(JxlTest, RoundtripNoGaborishNoAR) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
@ -549,7 +549,7 @@ TEST(JxlTest, RoundtripNoGaborishNoAR) {
|
|||||||
TEST(JxlTest, RoundtripSmallNoGaborish) {
|
TEST(JxlTest, RoundtripSmallNoGaborish) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
||||||
@ -595,7 +595,7 @@ TEST(JxlTest, RoundtripSmallPatchesAlpha) {
|
|||||||
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 2000u);
|
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 2000u);
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(0.2f));
|
IsSlightlyBelow(0.04f));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripSmallPatches) {
|
TEST(JxlTest, RoundtripSmallPatches) {
|
||||||
@ -623,7 +623,7 @@ TEST(JxlTest, RoundtripSmallPatches) {
|
|||||||
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 2000u);
|
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 2000u);
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(0.2f));
|
IsSlightlyBelow(0.04f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test header encoding of original bits per sample
|
// Test header encoding of original bits per sample
|
||||||
@ -700,8 +700,8 @@ TEST(JxlTest, RoundtripImageBundleOriginalBits) {
|
|||||||
|
|
||||||
TEST(JxlTest, RoundtripGrayscale) {
|
TEST(JxlTest, RoundtripGrayscale) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/cvo9xd_keong_macan_grayscale.png");
|
"third_party/wesaturate/500px/cvo9xd_keong_macan_grayscale.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
ASSERT_NE(io.xsize(), 0u);
|
ASSERT_NE(io.xsize(), 0u);
|
||||||
@ -756,8 +756,8 @@ TEST(JxlTest, RoundtripGrayscale) {
|
|||||||
|
|
||||||
TEST(JxlTest, RoundtripAlpha) {
|
TEST(JxlTest, RoundtripAlpha) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
@ -786,13 +786,13 @@ TEST(JxlTest, RoundtripAlpha) {
|
|||||||
|
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(1.4));
|
IsSlightlyBelow(1.2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripAlphaPremultiplied) {
|
TEST(JxlTest, RoundtripAlphaPremultiplied) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
||||||
CodecInOut io, io_nopremul;
|
CodecInOut io, io_nopremul;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io_nopremul, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io_nopremul, pool));
|
||||||
@ -821,18 +821,18 @@ TEST(JxlTest, RoundtripAlphaPremultiplied) {
|
|||||||
|
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(1.4));
|
IsSlightlyBelow(1.2));
|
||||||
io2.Main().UnpremultiplyAlpha();
|
io2.Main().UnpremultiplyAlpha();
|
||||||
EXPECT_THAT(
|
EXPECT_THAT(
|
||||||
ButteraugliDistance(io_nopremul, io2, cparams.ba_params, GetJxlCms(),
|
ButteraugliDistance(io_nopremul, io2, cparams.ba_params, GetJxlCms(),
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(1.8));
|
IsSlightlyBelow(1.35));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripAlphaResampling) {
|
TEST(JxlTest, RoundtripAlphaResampling) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
@ -864,8 +864,8 @@ TEST(JxlTest, RoundtripAlphaResampling) {
|
|||||||
|
|
||||||
TEST(JxlTest, RoundtripAlphaResamplingOnlyAlpha) {
|
TEST(JxlTest, RoundtripAlphaResamplingOnlyAlpha) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
@ -896,8 +896,8 @@ TEST(JxlTest, RoundtripAlphaResamplingOnlyAlpha) {
|
|||||||
|
|
||||||
TEST(JxlTest, RoundtripAlphaNonMultipleOf8) {
|
TEST(JxlTest, RoundtripAlphaNonMultipleOf8) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
@ -922,7 +922,7 @@ TEST(JxlTest, RoundtripAlphaNonMultipleOf8) {
|
|||||||
CodecInOut io2;
|
CodecInOut io2;
|
||||||
EXPECT_TRUE(DecodeFile(dparams, compressed, &io2, pool));
|
EXPECT_TRUE(DecodeFile(dparams, compressed, &io2, pool));
|
||||||
|
|
||||||
EXPECT_LE(compressed.size(), 200u);
|
EXPECT_LE(compressed.size(), 180u);
|
||||||
|
|
||||||
// TODO(robryk): Fix the following line in presence of different alpha_bits in
|
// TODO(robryk): Fix the following line in presence of different alpha_bits in
|
||||||
// the two contexts.
|
// the two contexts.
|
||||||
@ -930,7 +930,7 @@ TEST(JxlTest, RoundtripAlphaNonMultipleOf8) {
|
|||||||
// TODO(robryk): Fix the distance estimate used in the encoder.
|
// TODO(robryk): Fix the distance estimate used in the encoder.
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(0.8));
|
IsSlightlyBelow(0.9));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripAlpha16) {
|
TEST(JxlTest, RoundtripAlpha16) {
|
||||||
@ -965,8 +965,7 @@ TEST(JxlTest, RoundtripAlpha16) {
|
|||||||
|
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.butteraugli_distance = 0.5;
|
cparams.butteraugli_distance = 0.5;
|
||||||
// Prevent the test to be too slow, does not affect alpha
|
cparams.speed_tier = SpeedTier::kWombat;
|
||||||
cparams.speed_tier = SpeedTier::kSquirrel;
|
|
||||||
DecompressParams dparams;
|
DecompressParams dparams;
|
||||||
|
|
||||||
io.metadata.m.SetUintSamples(16);
|
io.metadata.m.SetUintSamples(16);
|
||||||
@ -978,8 +977,9 @@ TEST(JxlTest, RoundtripAlpha16) {
|
|||||||
aux_out, &pool));
|
aux_out, &pool));
|
||||||
CodecInOut io2;
|
CodecInOut io2;
|
||||||
EXPECT_TRUE(DecodeFile(dparams, compressed, &io2, &pool));
|
EXPECT_TRUE(DecodeFile(dparams, compressed, &io2, &pool));
|
||||||
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
||||||
EXPECT_TRUE(SamePixels(*io.Main().alpha(), *io2.Main().alpha()));
|
/*distmap=*/nullptr, &pool),
|
||||||
|
IsSlightlyBelow(0.8));
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -987,7 +987,7 @@ CompressParams CParamsForLossless() {
|
|||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.modular_mode = true;
|
cparams.modular_mode = true;
|
||||||
cparams.color_transform = jxl::ColorTransform::kNone;
|
cparams.color_transform = jxl::ColorTransform::kNone;
|
||||||
cparams.quality_pair = {100, 100};
|
cparams.butteraugli_distance = 0.f;
|
||||||
cparams.options.predictor = {Predictor::Weighted};
|
cparams.options.predictor = {Predictor::Weighted};
|
||||||
return cparams;
|
return cparams;
|
||||||
}
|
}
|
||||||
@ -995,8 +995,8 @@ CompressParams CParamsForLossless() {
|
|||||||
|
|
||||||
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLossless8)) {
|
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLossless8)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -1023,8 +1023,8 @@ TEST(JxlTest, JXL_SLOW_TEST(RoundtripLossless8)) {
|
|||||||
|
|
||||||
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLosslessNoEncoderFastPathWP)) {
|
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLosslessNoEncoderFastPathWP)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -1040,8 +1040,8 @@ TEST(JxlTest, JXL_SLOW_TEST(RoundtripLosslessNoEncoderFastPathWP)) {
|
|||||||
|
|
||||||
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLosslessNoEncoderFastPathGradient)) {
|
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLosslessNoEncoderFastPathGradient)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -1058,8 +1058,8 @@ TEST(JxlTest, JXL_SLOW_TEST(RoundtripLosslessNoEncoderFastPathGradient)) {
|
|||||||
|
|
||||||
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLosslessNoEncoderVeryFastPathGradient)) {
|
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLosslessNoEncoderVeryFastPathGradient)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -1079,8 +1079,8 @@ TEST(JxlTest, JXL_SLOW_TEST(RoundtripLosslessNoEncoderVeryFastPathGradient)) {
|
|||||||
|
|
||||||
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLossless8Falcon)) {
|
TEST(JxlTest, JXL_SLOW_TEST(RoundtripLossless8Falcon)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -1096,8 +1096,8 @@ TEST(JxlTest, JXL_SLOW_TEST(RoundtripLossless8Falcon)) {
|
|||||||
|
|
||||||
TEST(JxlTest, RoundtripLossless8Alpha) {
|
TEST(JxlTest, RoundtripLossless8Alpha) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_alpha.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
EXPECT_EQ(8u, io.metadata.m.GetAlphaBits());
|
EXPECT_EQ(8u, io.metadata.m.GetAlphaBits());
|
||||||
@ -1221,11 +1221,11 @@ TEST(JxlTest, RoundtripLossless16AlphaNotMisdetectedAs8Bit) {
|
|||||||
TEST(JxlTest, RoundtripYCbCr420) {
|
TEST(JxlTest, RoundtripYCbCr420) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
const PaddedBytes yuv420 =
|
const PaddedBytes yuv420 = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png.ffmpeg.y4m");
|
"third_party/imagecompression.info/flower_foveon.png.ffmpeg.y4m");
|
||||||
CodecInOut io2;
|
CodecInOut io2;
|
||||||
ASSERT_TRUE(test::DecodeImageY4M(Span<const uint8_t>(yuv420), &io2));
|
ASSERT_TRUE(test::DecodeImageY4M(Span<const uint8_t>(yuv420), &io2));
|
||||||
|
|
||||||
@ -1251,7 +1251,7 @@ TEST(JxlTest, RoundtripYCbCr420) {
|
|||||||
TEST(JxlTest, RoundtripDots) {
|
TEST(JxlTest, RoundtripDots) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/cvo9xd_keong_macan_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/cvo9xd_keong_macan_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
@ -1284,7 +1284,7 @@ TEST(JxlTest, RoundtripDots) {
|
|||||||
TEST(JxlTest, RoundtripNoise) {
|
TEST(JxlTest, RoundtripNoise) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/cvo9xd_keong_macan_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/cvo9xd_keong_macan_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
@ -1315,8 +1315,8 @@ TEST(JxlTest, RoundtripNoise) {
|
|||||||
|
|
||||||
TEST(JxlTest, RoundtripLossless8Gray) {
|
TEST(JxlTest, RoundtripLossless8Gray) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/cvo9xd_keong_macan_grayscale.png");
|
"third_party/wesaturate/500px/cvo9xd_keong_macan_grayscale.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
@ -1448,7 +1448,7 @@ size_t RoundtripJpeg(const PaddedBytes& jpeg_in, ThreadPool* pool) {
|
|||||||
enc_container.codestream = std::move(codestream);
|
enc_container.codestream = std::move(codestream);
|
||||||
jpeg::JPEGData data_in = *io.Main().jpeg_data;
|
jpeg::JPEGData data_in = *io.Main().jpeg_data;
|
||||||
jxl::PaddedBytes jpeg_data;
|
jxl::PaddedBytes jpeg_data;
|
||||||
EXPECT_TRUE(EncodeJPEGData(data_in, &jpeg_data));
|
EXPECT_TRUE(EncodeJPEGData(data_in, &jpeg_data, cparams));
|
||||||
enc_container.jpeg_reconstruction = jpeg_data.data();
|
enc_container.jpeg_reconstruction = jpeg_data.data();
|
||||||
enc_container.jpeg_reconstruction_size = jpeg_data.size();
|
enc_container.jpeg_reconstruction_size = jpeg_data.size();
|
||||||
EXPECT_TRUE(EncodeJpegXlContainerOneShot(enc_container, &compressed));
|
EXPECT_TRUE(EncodeJpegXlContainerOneShot(enc_container, &compressed));
|
||||||
@ -1474,8 +1474,8 @@ size_t RoundtripJpeg(const PaddedBytes& jpeg_in, ThreadPool* pool) {
|
|||||||
|
|
||||||
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444)) {
|
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png.im_q85_444.jpg");
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_444.jpg");
|
||||||
// JPEG size is 326'916 bytes.
|
// JPEG size is 326'916 bytes.
|
||||||
EXPECT_LE(RoundtripJpeg(orig, &pool), 256000u);
|
EXPECT_LE(RoundtripJpeg(orig, &pool), 256000u);
|
||||||
}
|
}
|
||||||
@ -1484,8 +1484,8 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444)) {
|
|||||||
|
|
||||||
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels)) {
|
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png.im_q85_444.jpg");
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_444.jpg");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
||||||
|
|
||||||
@ -1508,8 +1508,8 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels)) {
|
|||||||
|
|
||||||
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420)) {
|
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png.im_q85_420.jpg");
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_420.jpg");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
||||||
|
|
||||||
@ -1531,8 +1531,8 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420)) {
|
|||||||
TEST(JxlTest,
|
TEST(JxlTest,
|
||||||
JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420EarlyFlush)) {
|
JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420EarlyFlush)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png.im_q85_420.jpg");
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_420.jpg");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
||||||
|
|
||||||
@ -1555,8 +1555,8 @@ TEST(JxlTest,
|
|||||||
TEST(JxlTest,
|
TEST(JxlTest,
|
||||||
JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420Mul16)) {
|
JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels420Mul16)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon_cropped.jpg");
|
"third_party/imagecompression.info/flower_foveon_cropped.jpg");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
||||||
|
|
||||||
@ -1579,7 +1579,8 @@ TEST(JxlTest,
|
|||||||
JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels_asymmetric)) {
|
JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionToPixels_asymmetric)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig = ReadTestData(
|
const PaddedBytes orig = ReadTestData(
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_asymmetric.jpg");
|
"third_party/imagecompression.info/"
|
||||||
|
"flower_foveon.png.im_q85_asymmetric.jpg");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
ASSERT_TRUE(jpeg::DecodeImageJPG(Span<const uint8_t>(orig), &io));
|
||||||
|
|
||||||
@ -1602,16 +1603,16 @@ TEST(JxlTest,
|
|||||||
|
|
||||||
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionGray)) {
|
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompressionGray)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png.im_q85_gray.jpg");
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_gray.jpg");
|
||||||
// JPEG size is 167'025 bytes.
|
// JPEG size is 167'025 bytes.
|
||||||
EXPECT_LE(RoundtripJpeg(orig, &pool), 140000u);
|
EXPECT_LE(RoundtripJpeg(orig, &pool), 140000u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420)) {
|
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png.im_q85_420.jpg");
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_420.jpg");
|
||||||
// JPEG size is 226'018 bytes.
|
// JPEG size is 226'018 bytes.
|
||||||
EXPECT_LE(RoundtripJpeg(orig, &pool), 181050u);
|
EXPECT_LE(RoundtripJpeg(orig, &pool), 181050u);
|
||||||
}
|
}
|
||||||
@ -1620,7 +1621,8 @@ TEST(JxlTest,
|
|||||||
JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression_luma_subsample)) {
|
JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression_luma_subsample)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig = ReadTestData(
|
const PaddedBytes orig = ReadTestData(
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_luma_subsample.jpg");
|
"third_party/imagecompression.info/"
|
||||||
|
"flower_foveon.png.im_q85_luma_subsample.jpg");
|
||||||
// JPEG size is 216'069 bytes.
|
// JPEG size is 216'069 bytes.
|
||||||
EXPECT_LE(RoundtripJpeg(orig, &pool), 181000u);
|
EXPECT_LE(RoundtripJpeg(orig, &pool), 181000u);
|
||||||
}
|
}
|
||||||
@ -1629,23 +1631,23 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression444_12)) {
|
|||||||
// 444 JPEG that has an interesting sampling-factor (1x2, 1x2, 1x2).
|
// 444 JPEG that has an interesting sampling-factor (1x2, 1x2, 1x2).
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig = ReadTestData(
|
const PaddedBytes orig = ReadTestData(
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_444_1x2.jpg");
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_444_1x2.jpg");
|
||||||
// JPEG size is 329'942 bytes.
|
// JPEG size is 329'942 bytes.
|
||||||
EXPECT_LE(RoundtripJpeg(orig, &pool), 256000u);
|
EXPECT_LE(RoundtripJpeg(orig, &pool), 256000u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression422)) {
|
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression422)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png.im_q85_422.jpg");
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_422.jpg");
|
||||||
// JPEG size is 265'590 bytes.
|
// JPEG size is 265'590 bytes.
|
||||||
EXPECT_LE(RoundtripJpeg(orig, &pool), 209000u);
|
EXPECT_LE(RoundtripJpeg(orig, &pool), 209000u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression440)) {
|
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression440)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png.im_q85_440.jpg");
|
"third_party/imagecompression.info/flower_foveon.png.im_q85_440.jpg");
|
||||||
// JPEG size is 262'249 bytes.
|
// JPEG size is 262'249 bytes.
|
||||||
EXPECT_LE(RoundtripJpeg(orig, &pool), 209000u);
|
EXPECT_LE(RoundtripJpeg(orig, &pool), 209000u);
|
||||||
}
|
}
|
||||||
@ -1655,7 +1657,8 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression_asymmetric)) {
|
|||||||
// the other.
|
// the other.
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig = ReadTestData(
|
const PaddedBytes orig = ReadTestData(
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_asymmetric.jpg");
|
"third_party/imagecompression.info/"
|
||||||
|
"flower_foveon.png.im_q85_asymmetric.jpg");
|
||||||
// JPEG size is 262'249 bytes.
|
// JPEG size is 262'249 bytes.
|
||||||
EXPECT_LE(RoundtripJpeg(orig, &pool), 209000u);
|
EXPECT_LE(RoundtripJpeg(orig, &pool), 209000u);
|
||||||
}
|
}
|
||||||
@ -1663,14 +1666,15 @@ TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression_asymmetric)) {
|
|||||||
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420Progr)) {
|
TEST(JxlTest, JXL_TRANSCODE_JPEG_TEST(RoundtripJpegRecompression420Progr)) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig = ReadTestData(
|
const PaddedBytes orig = ReadTestData(
|
||||||
"imagecompression.info/flower_foveon.png.im_q85_420_progr.jpg");
|
"third_party/imagecompression.info/"
|
||||||
|
"flower_foveon.png.im_q85_420_progr.jpg");
|
||||||
EXPECT_LE(RoundtripJpeg(orig, &pool), 181000u);
|
EXPECT_LE(RoundtripJpeg(orig, &pool), 181000u);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(JxlTest, RoundtripProgressive) {
|
TEST(JxlTest, RoundtripProgressive) {
|
||||||
ThreadPoolInternal pool(4);
|
ThreadPoolInternal pool(4);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
io.ShrinkTo(600, 1024);
|
io.ShrinkTo(600, 1024);
|
||||||
@ -1686,7 +1690,7 @@ TEST(JxlTest, RoundtripProgressive) {
|
|||||||
EXPECT_LE(Roundtrip(&io, cparams, dparams, &pool, &io2), 40000u);
|
EXPECT_LE(Roundtrip(&io, cparams, dparams, &pool, &io2), 40000u);
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
|
||||||
/*distmap=*/nullptr, &pool),
|
/*distmap=*/nullptr, &pool),
|
||||||
IsSlightlyBelow(2.5f));
|
IsSlightlyBelow(1.1f));
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -304,7 +304,7 @@ class MATreeLookup {
|
|||||||
int64_t offset;
|
int64_t offset;
|
||||||
int32_t multiplier;
|
int32_t multiplier;
|
||||||
};
|
};
|
||||||
LookupResult Lookup(const Properties &properties) const {
|
JXL_INLINE LookupResult Lookup(const Properties &properties) const {
|
||||||
uint32_t pos = 0;
|
uint32_t pos = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
const FlatDecisionNode &node = nodes_[pos];
|
const FlatDecisionNode &node = nodes_[pos];
|
||||||
@ -416,6 +416,7 @@ enum PredictorMode {
|
|||||||
kUseWP = 2,
|
kUseWP = 2,
|
||||||
kForceComputeProperties = 4,
|
kForceComputeProperties = 4,
|
||||||
kAllPredictions = 8,
|
kAllPredictions = 8,
|
||||||
|
kNoEdgeCases = 16
|
||||||
};
|
};
|
||||||
|
|
||||||
JXL_INLINE pixel_type_w PredictOne(Predictor p, pixel_type_w left,
|
JXL_INLINE pixel_type_w PredictOne(Predictor p, pixel_type_w left,
|
||||||
@ -461,7 +462,7 @@ JXL_INLINE pixel_type_w PredictOne(Predictor p, pixel_type_w left,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <int mode>
|
template <int mode>
|
||||||
inline PredictionResult Predict(
|
JXL_INLINE PredictionResult Predict(
|
||||||
Properties *p, size_t w, const pixel_type *JXL_RESTRICT pp,
|
Properties *p, size_t w, const pixel_type *JXL_RESTRICT pp,
|
||||||
const intptr_t onerow, const size_t x, const size_t y, Predictor predictor,
|
const intptr_t onerow, const size_t x, const size_t y, Predictor predictor,
|
||||||
const MATreeLookup *lookup, const Channel *references,
|
const MATreeLookup *lookup, const Channel *references,
|
||||||
@ -470,13 +471,15 @@ inline PredictionResult Predict(
|
|||||||
size_t offset = 3;
|
size_t offset = 3;
|
||||||
constexpr bool compute_properties =
|
constexpr bool compute_properties =
|
||||||
mode & kUseTree || mode & kForceComputeProperties;
|
mode & kUseTree || mode & kForceComputeProperties;
|
||||||
pixel_type_w left = (x ? pp[-1] : (y ? pp[-onerow] : 0));
|
constexpr bool nec = mode & kNoEdgeCases;
|
||||||
pixel_type_w top = (y ? pp[-onerow] : left);
|
pixel_type_w left = (nec || x ? pp[-1] : (y ? pp[-onerow] : 0));
|
||||||
pixel_type_w topleft = (x && y ? pp[-1 - onerow] : left);
|
pixel_type_w top = (nec || y ? pp[-onerow] : left);
|
||||||
pixel_type_w topright = (x + 1 < w && y ? pp[1 - onerow] : top);
|
pixel_type_w topleft = (nec || (x && y) ? pp[-1 - onerow] : left);
|
||||||
pixel_type_w leftleft = (x > 1 ? pp[-2] : left);
|
pixel_type_w topright = (nec || (x + 1 < w && y) ? pp[1 - onerow] : top);
|
||||||
pixel_type_w toptop = (y > 1 ? pp[-onerow - onerow] : top);
|
pixel_type_w leftleft = (nec || x > 1 ? pp[-2] : left);
|
||||||
pixel_type_w toprightright = (x + 2 < w && y ? pp[2 - onerow] : topright);
|
pixel_type_w toptop = (nec || y > 1 ? pp[-onerow - onerow] : top);
|
||||||
|
pixel_type_w toprightright =
|
||||||
|
(nec || (x + 2 < w && y) ? pp[2 - onerow] : topright);
|
||||||
|
|
||||||
if (compute_properties) {
|
if (compute_properties) {
|
||||||
// location
|
// location
|
||||||
@ -506,7 +509,7 @@ inline PredictionResult Predict(
|
|||||||
wp_pred = wp_state->Predict<compute_properties>(
|
wp_pred = wp_state->Predict<compute_properties>(
|
||||||
x, y, w, top, left, topright, topleft, toptop, p, offset);
|
x, y, w, top, left, topright, topleft, toptop, p, offset);
|
||||||
}
|
}
|
||||||
if (compute_properties) {
|
if (!nec && compute_properties) {
|
||||||
offset += weighted::kNumProperties;
|
offset += weighted::kNumProperties;
|
||||||
// Extra properties.
|
// Extra properties.
|
||||||
const pixel_type *JXL_RESTRICT rp = references->Row(x);
|
const pixel_type *JXL_RESTRICT rp = references->Row(x);
|
||||||
@ -565,6 +568,15 @@ inline PredictionResult PredictTreeNoWP(Properties *p, size_t w,
|
|||||||
p, w, pp, onerow, x, y, Predictor::Zero, &tree_lookup, &references,
|
p, w, pp, onerow, x, y, Predictor::Zero, &tree_lookup, &references,
|
||||||
/*wp_state=*/nullptr, /*predictions=*/nullptr);
|
/*wp_state=*/nullptr, /*predictions=*/nullptr);
|
||||||
}
|
}
|
||||||
|
// Only use for y > 1, x > 1, x < w-2, and empty references
|
||||||
|
JXL_INLINE PredictionResult
|
||||||
|
PredictTreeNoWPNEC(Properties *p, size_t w, const pixel_type *JXL_RESTRICT pp,
|
||||||
|
const intptr_t onerow, const int x, const int y,
|
||||||
|
const MATreeLookup &tree_lookup, const Channel &references) {
|
||||||
|
return detail::Predict<detail::kUseTree | detail::kNoEdgeCases>(
|
||||||
|
p, w, pp, onerow, x, y, Predictor::Zero, &tree_lookup, &references,
|
||||||
|
/*wp_state=*/nullptr, /*predictions=*/nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
inline PredictionResult PredictTreeWP(Properties *p, size_t w,
|
inline PredictionResult PredictTreeWP(Properties *p, size_t w,
|
||||||
const pixel_type *JXL_RESTRICT pp,
|
const pixel_type *JXL_RESTRICT pp,
|
||||||
|
@ -186,14 +186,23 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader,
|
|||||||
pixel_type *JXL_RESTRICT r = channel.Row(y);
|
pixel_type *JXL_RESTRICT r = channel.Row(y);
|
||||||
std::fill(r, r + channel.w, v);
|
std::fill(r, r + channel.w, v);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
JXL_DEBUG_V(8, "Fast track.");
|
JXL_DEBUG_V(8, "Fast track.");
|
||||||
for (size_t y = 0; y < channel.h; y++) {
|
if (multiplier == 1 && offset == 0) {
|
||||||
pixel_type *JXL_RESTRICT r = channel.Row(y);
|
for (size_t y = 0; y < channel.h; y++) {
|
||||||
for (size_t x = 0; x < channel.w; x++) {
|
pixel_type *JXL_RESTRICT r = channel.Row(y);
|
||||||
uint32_t v = reader->ReadHybridUintClustered(ctx_id, br);
|
for (size_t x = 0; x < channel.w; x++) {
|
||||||
r[x] = make_pixel(v, multiplier, offset);
|
uint32_t v = reader->ReadHybridUintClustered(ctx_id, br);
|
||||||
|
r[x] = UnpackSigned(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t y = 0; y < channel.h; y++) {
|
||||||
|
pixel_type *JXL_RESTRICT r = channel.Row(y);
|
||||||
|
for (size_t x = 0; x < channel.w; x++) {
|
||||||
|
uint32_t v = reader->ReadHybridUintClustered(ctx_id, br);
|
||||||
|
r[x] = make_pixel(v, multiplier, offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -355,12 +364,36 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader,
|
|||||||
pixel_type *JXL_RESTRICT p = channel.Row(y);
|
pixel_type *JXL_RESTRICT p = channel.Row(y);
|
||||||
PrecomputeReferences(channel, y, *image, chan, &references);
|
PrecomputeReferences(channel, y, *image, chan, &references);
|
||||||
InitPropsRow(&properties, static_props, y);
|
InitPropsRow(&properties, static_props, y);
|
||||||
for (size_t x = 0; x < channel.w; x++) {
|
if (y > 1 && channel.w > 8 && references.w == 0) {
|
||||||
PredictionResult res =
|
for (size_t x = 0; x < 2; x++) {
|
||||||
PredictTreeNoWP(&properties, channel.w, p + x, onerow, x, y,
|
PredictionResult res =
|
||||||
tree_lookup, references);
|
PredictTreeNoWP(&properties, channel.w, p + x, onerow, x, y,
|
||||||
uint64_t v = reader->ReadHybridUintClustered(res.context, br);
|
tree_lookup, references);
|
||||||
p[x] = make_pixel(v, res.multiplier, res.guess);
|
uint64_t v = reader->ReadHybridUintClustered(res.context, br);
|
||||||
|
p[x] = make_pixel(v, res.multiplier, res.guess);
|
||||||
|
}
|
||||||
|
for (size_t x = 2; x < channel.w - 2; x++) {
|
||||||
|
PredictionResult res =
|
||||||
|
PredictTreeNoWPNEC(&properties, channel.w, p + x, onerow, x, y,
|
||||||
|
tree_lookup, references);
|
||||||
|
uint64_t v = reader->ReadHybridUintClustered(res.context, br);
|
||||||
|
p[x] = make_pixel(v, res.multiplier, res.guess);
|
||||||
|
}
|
||||||
|
for (size_t x = channel.w - 2; x < channel.w; x++) {
|
||||||
|
PredictionResult res =
|
||||||
|
PredictTreeNoWP(&properties, channel.w, p + x, onerow, x, y,
|
||||||
|
tree_lookup, references);
|
||||||
|
uint64_t v = reader->ReadHybridUintClustered(res.context, br);
|
||||||
|
p[x] = make_pixel(v, res.multiplier, res.guess);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (size_t x = 0; x < channel.w; x++) {
|
||||||
|
PredictionResult res =
|
||||||
|
PredictTreeNoWP(&properties, channel.w, p + x, onerow, x, y,
|
||||||
|
tree_lookup, references);
|
||||||
|
uint64_t v = reader->ReadHybridUintClustered(res.context, br);
|
||||||
|
p[x] = make_pixel(v, res.multiplier, res.guess);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -550,18 +550,15 @@ Status FwdPalette(Image &input, uint32_t begin_c, uint32_t end_c,
|
|||||||
bool lossy, Predictor &predictor,
|
bool lossy, Predictor &predictor,
|
||||||
const weighted::Header &wp_header) {
|
const weighted::Header &wp_header) {
|
||||||
PaletteIterationData palette_iteration_data;
|
PaletteIterationData palette_iteration_data;
|
||||||
uint32_t nb = end_c - begin_c + 1;
|
|
||||||
uint32_t nb_colors_orig = nb_colors;
|
uint32_t nb_colors_orig = nb_colors;
|
||||||
uint32_t nb_deltas_orig = nb_deltas;
|
uint32_t nb_deltas_orig = nb_deltas;
|
||||||
// if no channel palette special case
|
// preprocessing pass in case of lossy palette
|
||||||
if ((lossy || nb != 1) && input.bitdepth >= 8) {
|
if (lossy && input.bitdepth >= 8) {
|
||||||
JXL_RETURN_IF_ERROR(FwdPaletteIteration(
|
JXL_RETURN_IF_ERROR(FwdPaletteIteration(
|
||||||
input, begin_c, end_c, nb_colors, nb_deltas, ordered, lossy, predictor,
|
input, begin_c, end_c, nb_colors_orig, nb_deltas_orig, ordered, lossy,
|
||||||
wp_header, palette_iteration_data));
|
predictor, wp_header, palette_iteration_data));
|
||||||
}
|
}
|
||||||
palette_iteration_data.final_run = true;
|
palette_iteration_data.final_run = true;
|
||||||
nb_colors = nb_colors_orig;
|
|
||||||
nb_deltas = nb_deltas_orig;
|
|
||||||
return FwdPaletteIteration(input, begin_c, end_c, nb_colors, nb_deltas,
|
return FwdPaletteIteration(input, begin_c, end_c, nb_colors, nb_deltas,
|
||||||
ordered, lossy, predictor, wp_header,
|
ordered, lossy, predictor, wp_header,
|
||||||
palette_iteration_data);
|
palette_iteration_data);
|
||||||
|
@ -12,13 +12,80 @@
|
|||||||
#include "lib/jxl/common.h"
|
#include "lib/jxl/common.h"
|
||||||
#include "lib/jxl/modular/modular_image.h"
|
#include "lib/jxl/modular/modular_image.h"
|
||||||
#include "lib/jxl/modular/transform/transform.h"
|
#include "lib/jxl/modular/transform/transform.h"
|
||||||
|
#undef HWY_TARGET_INCLUDE
|
||||||
|
#define HWY_TARGET_INCLUDE "lib/jxl/modular/transform/squeeze.cc"
|
||||||
|
#include <hwy/foreach_target.h>
|
||||||
|
#include <hwy/highway.h>
|
||||||
|
|
||||||
|
#include "lib/jxl/simd_util-inl.h"
|
||||||
|
|
||||||
|
HWY_BEFORE_NAMESPACE();
|
||||||
namespace jxl {
|
namespace jxl {
|
||||||
|
namespace HWY_NAMESPACE {
|
||||||
|
|
||||||
|
using hwy::HWY_NAMESPACE::RebindToUnsigned;
|
||||||
|
using hwy::HWY_NAMESPACE::ShiftLeft;
|
||||||
|
using hwy::HWY_NAMESPACE::ShiftRight;
|
||||||
|
|
||||||
|
#if HWY_TARGET != HWY_SCALAR
|
||||||
|
|
||||||
|
JXL_INLINE void FastUnsqueeze(const pixel_type *JXL_RESTRICT p_residual,
|
||||||
|
const pixel_type *JXL_RESTRICT p_avg,
|
||||||
|
const pixel_type *JXL_RESTRICT p_navg,
|
||||||
|
const pixel_type *p_pout,
|
||||||
|
pixel_type *JXL_RESTRICT p_out,
|
||||||
|
pixel_type *p_nout) {
|
||||||
|
const HWY_CAPPED(pixel_type, 8) d;
|
||||||
|
const RebindToUnsigned<decltype(d)> du;
|
||||||
|
const size_t N = Lanes(d);
|
||||||
|
auto onethird = Set(d, 0x55555556);
|
||||||
|
for (size_t x = 0; x < 8; x += N) {
|
||||||
|
auto avg = Load(d, p_avg + x);
|
||||||
|
auto next_avg = Load(d, p_navg + x);
|
||||||
|
auto top = Load(d, p_pout + x);
|
||||||
|
// Equivalent to SmoothTendency(top,avg,next_avg), but without branches
|
||||||
|
auto Ba = top - avg;
|
||||||
|
auto an = avg - next_avg;
|
||||||
|
auto nonmono = Ba ^ an;
|
||||||
|
auto absBa = Abs(Ba);
|
||||||
|
auto absan = Abs(an);
|
||||||
|
auto absBn = Abs(top - next_avg);
|
||||||
|
// Compute a3 = absBa / 3
|
||||||
|
auto a3e = BitCast(d, ShiftRight<32>(MulEven(absBa, onethird)));
|
||||||
|
auto a3oi = MulEven(Reverse(d, absBa), onethird);
|
||||||
|
auto a3o = BitCast(
|
||||||
|
d, Reverse(hwy::HWY_NAMESPACE::Repartition<pixel_type_w, decltype(d)>(),
|
||||||
|
a3oi));
|
||||||
|
auto a3 = OddEven(a3o, a3e);
|
||||||
|
a3 += absBn + Set(d, 2);
|
||||||
|
auto absdiff = ShiftRight<2>(a3);
|
||||||
|
auto skipdiff = Ba != Zero(d);
|
||||||
|
skipdiff = And(skipdiff, an != Zero(d));
|
||||||
|
skipdiff = And(skipdiff, nonmono < Zero(d));
|
||||||
|
auto absBa2 = ShiftLeft<1>(absBa) + (absdiff & Set(d, 1));
|
||||||
|
absdiff =
|
||||||
|
IfThenElse(absdiff > absBa2, ShiftLeft<1>(absBa) + Set(d, 1), absdiff);
|
||||||
|
auto absan2 = ShiftLeft<1>(absan);
|
||||||
|
absdiff =
|
||||||
|
IfThenElse(absdiff + (absdiff & Set(d, 1)) > absan2, absan2, absdiff);
|
||||||
|
auto diff1 = IfThenElse(top < next_avg, Neg(absdiff), absdiff);
|
||||||
|
auto tendency = IfThenZeroElse(skipdiff, diff1);
|
||||||
|
|
||||||
|
auto diff_minus_tendency = Load(d, p_residual + x);
|
||||||
|
auto diff = diff_minus_tendency + tendency;
|
||||||
|
auto out = avg + ShiftRight<1>(
|
||||||
|
diff + BitCast(d, ShiftRight<31>(BitCast(du, diff))));
|
||||||
|
Store(out, d, p_out + x);
|
||||||
|
Store(out - diff, d, p_nout + x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
Status InvHSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
|
Status InvHSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
|
||||||
JXL_ASSERT(c < input.channel.size());
|
JXL_ASSERT(c < input.channel.size());
|
||||||
JXL_ASSERT(rc < input.channel.size());
|
JXL_ASSERT(rc < input.channel.size());
|
||||||
const Channel &chin = input.channel[c];
|
Channel &chin = input.channel[c];
|
||||||
const Channel &chin_residual = input.channel[rc];
|
const Channel &chin_residual = input.channel[rc];
|
||||||
// These must be valid since we ran MetaApply already.
|
// These must be valid since we ran MetaApply already.
|
||||||
JXL_ASSERT(chin.w == DivCeil(chin.w + chin_residual.w, 2));
|
JXL_ASSERT(chin.w == DivCeil(chin.w + chin_residual.w, 2));
|
||||||
@ -42,42 +109,84 @@ Status InvHSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
|
|||||||
input.channel[c] = std::move(chout);
|
input.channel[c] = std::move(chout);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
auto unsqueeze_row = [&](size_t y, size_t x0) {
|
||||||
|
const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y);
|
||||||
|
const pixel_type *JXL_RESTRICT p_avg = chin.Row(y);
|
||||||
|
pixel_type *JXL_RESTRICT p_out = chout.Row(y);
|
||||||
|
for (size_t x = x0; x < chin_residual.w; x++) {
|
||||||
|
pixel_type diff_minus_tendency = p_residual[x];
|
||||||
|
pixel_type avg = p_avg[x];
|
||||||
|
pixel_type next_avg = (x + 1 < chin.w ? p_avg[x + 1] : avg);
|
||||||
|
pixel_type left = (x ? p_out[(x << 1) - 1] : avg);
|
||||||
|
pixel_type tendency = SmoothTendency(left, avg, next_avg);
|
||||||
|
pixel_type diff = diff_minus_tendency + tendency;
|
||||||
|
pixel_type A = avg + (diff / 2);
|
||||||
|
p_out[(x << 1)] = A;
|
||||||
|
pixel_type B = A - diff;
|
||||||
|
p_out[(x << 1) + 1] = B;
|
||||||
|
}
|
||||||
|
if (chout.w & 1) p_out[chout.w - 1] = p_avg[chin.w - 1];
|
||||||
|
};
|
||||||
|
|
||||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
// somewhat complicated trickery just to be able to SIMD this.
|
||||||
pool, 0, chin.h, ThreadPool::NoInit,
|
// Horizontal unsqueeze has horizontal data dependencies, so we do
|
||||||
[&](const uint32_t task, size_t /* thread */) {
|
// 8 rows at a time and treat it as a vertical unsqueeze of a
|
||||||
const size_t y = task;
|
// transposed 8x8 block (or 9x8 for one input).
|
||||||
const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y);
|
static constexpr const size_t kRowsPerThread = 8;
|
||||||
const pixel_type *JXL_RESTRICT p_avg = chin.Row(y);
|
const auto unsqueeze_span = [&](const uint32_t task, size_t /* thread */) {
|
||||||
pixel_type *JXL_RESTRICT p_out = chout.Row(y);
|
const size_t y0 = task * kRowsPerThread;
|
||||||
|
const size_t rows = std::min(kRowsPerThread, chin.h - y0);
|
||||||
|
size_t x = 0;
|
||||||
|
|
||||||
// special case for x=0 so we don't have to check x>0
|
#if HWY_TARGET != HWY_SCALAR
|
||||||
pixel_type_w avg = p_avg[0];
|
intptr_t onerow_in = chin.plane.PixelsPerRow();
|
||||||
pixel_type_w next_avg = (1 < chin.w ? p_avg[1] : avg);
|
intptr_t onerow_inr = chin_residual.plane.PixelsPerRow();
|
||||||
pixel_type_w tendency = SmoothTendency(avg, avg, next_avg);
|
intptr_t onerow_out = chout.plane.PixelsPerRow();
|
||||||
pixel_type_w diff = p_residual[0] + tendency;
|
const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y0);
|
||||||
pixel_type_w A =
|
const pixel_type *JXL_RESTRICT p_avg = chin.Row(y0);
|
||||||
((avg * 2) + diff + (diff > 0 ? -(diff & 1) : (diff & 1))) >> 1;
|
pixel_type *JXL_RESTRICT p_out = chout.Row(y0);
|
||||||
pixel_type_w B = A - diff;
|
HWY_ALIGN pixel_type b_p_avg[9 * kRowsPerThread];
|
||||||
p_out[0] = A;
|
HWY_ALIGN pixel_type b_p_residual[8 * kRowsPerThread];
|
||||||
p_out[1] = B;
|
HWY_ALIGN pixel_type b_p_out_even[8 * kRowsPerThread];
|
||||||
|
HWY_ALIGN pixel_type b_p_out_odd[8 * kRowsPerThread];
|
||||||
for (size_t x = 1; x < chin_residual.w; x++) {
|
HWY_ALIGN pixel_type b_p_out_evenT[8 * kRowsPerThread];
|
||||||
pixel_type_w diff_minus_tendency = p_residual[x];
|
HWY_ALIGN pixel_type b_p_out_oddT[8 * kRowsPerThread];
|
||||||
pixel_type_w avg = p_avg[x];
|
const HWY_CAPPED(pixel_type, 8) d;
|
||||||
pixel_type_w next_avg = (x + 1 < chin.w ? p_avg[x + 1] : avg);
|
const size_t N = Lanes(d);
|
||||||
pixel_type_w left = p_out[(x << 1) - 1];
|
if (chin_residual.w > 16 && rows == kRowsPerThread) {
|
||||||
pixel_type_w tendency = SmoothTendency(left, avg, next_avg);
|
for (; x < chin_residual.w - 9; x += 8) {
|
||||||
pixel_type_w diff = diff_minus_tendency + tendency;
|
Transpose8x8Block(p_residual + x, b_p_residual, onerow_inr);
|
||||||
pixel_type_w A =
|
Transpose8x8Block(p_avg + x, b_p_avg, onerow_in);
|
||||||
((avg * 2) + diff + (diff > 0 ? -(diff & 1) : (diff & 1))) >> 1;
|
for (size_t y = 0; y < kRowsPerThread; y++) {
|
||||||
p_out[x << 1] = A;
|
b_p_avg[8 * 8 + y] = p_avg[x + 8 + onerow_in * y];
|
||||||
pixel_type_w B = A - diff;
|
|
||||||
p_out[(x << 1) + 1] = B;
|
|
||||||
}
|
}
|
||||||
if (chout.w & 1) p_out[chout.w - 1] = p_avg[chin.w - 1];
|
for (size_t i = 0; i < 8; i++) {
|
||||||
},
|
FastUnsqueeze(
|
||||||
"InvHorizontalSqueeze"));
|
b_p_residual + 8 * i, b_p_avg + 8 * i, b_p_avg + 8 * (i + 1),
|
||||||
|
(x + i ? b_p_out_odd + 8 * ((x + i - 1) & 7) : b_p_avg + 8 * i),
|
||||||
|
b_p_out_even + 8 * i, b_p_out_odd + 8 * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Transpose8x8Block(b_p_out_even, b_p_out_evenT, 8);
|
||||||
|
Transpose8x8Block(b_p_out_odd, b_p_out_oddT, 8);
|
||||||
|
for (size_t y = 0; y < kRowsPerThread; y++) {
|
||||||
|
for (size_t i = 0; i < kRowsPerThread; i += N) {
|
||||||
|
auto even = Load(d, b_p_out_evenT + 8 * y + i);
|
||||||
|
auto odd = Load(d, b_p_out_oddT + 8 * y + i);
|
||||||
|
StoreInterleaved(d, even, odd,
|
||||||
|
p_out + ((x + i) << 1) + onerow_out * y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
for (size_t y = 0; y < rows; y++) {
|
||||||
|
unsqueeze_row(y0 + y, x);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, DivCeil(chin.h, kRowsPerThread),
|
||||||
|
ThreadPool::NoInit, unsqueeze_span,
|
||||||
|
"InvHorizontalSqueeze"));
|
||||||
input.channel[c] = std::move(chout);
|
input.channel[c] = std::move(chout);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -111,42 +220,47 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
intptr_t onerow_in = chin.plane.PixelsPerRow();
|
static constexpr const int kColsPerThread = 64;
|
||||||
intptr_t onerow_out = chout.plane.PixelsPerRow();
|
const auto unsqueeze_slice = [&](const uint32_t task, size_t /* thread */) {
|
||||||
constexpr int kColsPerThread = 64;
|
const size_t x0 = task * kColsPerThread;
|
||||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
const size_t x1 = std::min((size_t)(task + 1) * kColsPerThread, chin.w);
|
||||||
pool, 0, DivCeil(chin.w, kColsPerThread), ThreadPool::NoInit,
|
const size_t w = x1 - x0;
|
||||||
[&](const uint32_t task, size_t /* thread */) {
|
// We only iterate up to std::min(chin_residual.h, chin.h) which is
|
||||||
const size_t x0 = task * kColsPerThread;
|
// always chin_residual.h.
|
||||||
const size_t x1 = std::min((size_t)(task + 1) * kColsPerThread, chin.w);
|
for (size_t y = 0; y < chin_residual.h; y++) {
|
||||||
// We only iterate up to std::min(chin_residual.h, chin.h) which is
|
const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y) + x0;
|
||||||
// always chin_residual.h.
|
const pixel_type *JXL_RESTRICT p_avg = chin.Row(y) + x0;
|
||||||
for (size_t y = 0; y < chin_residual.h; y++) {
|
const pixel_type *JXL_RESTRICT p_navg =
|
||||||
const pixel_type *JXL_RESTRICT p_residual = chin_residual.Row(y);
|
chin.Row(y + 1 < chin.h ? y + 1 : y) + x0;
|
||||||
const pixel_type *JXL_RESTRICT p_avg = chin.Row(y);
|
pixel_type *JXL_RESTRICT p_out = chout.Row(y << 1) + x0;
|
||||||
pixel_type *JXL_RESTRICT p_out = chout.Row(y << 1);
|
pixel_type *JXL_RESTRICT p_nout = chout.Row((y << 1) + 1) + x0;
|
||||||
for (size_t x = x0; x < x1; x++) {
|
const pixel_type *p_pout = y > 0 ? chout.Row((y << 1) - 1) + x0 : p_avg;
|
||||||
pixel_type_w diff_minus_tendency = p_residual[x];
|
size_t x = 0;
|
||||||
pixel_type_w avg = p_avg[x];
|
#if HWY_TARGET != HWY_SCALAR
|
||||||
|
for (; x + 7 < w; x += 8) {
|
||||||
pixel_type_w next_avg = avg;
|
FastUnsqueeze(p_residual + x, p_avg + x, p_navg + x, p_pout + x,
|
||||||
if (y + 1 < chin.h) next_avg = p_avg[x + onerow_in];
|
p_out + x, p_nout + x);
|
||||||
pixel_type_w top =
|
}
|
||||||
(y > 0 ? p_out[static_cast<ssize_t>(x) - onerow_out] : avg);
|
#endif
|
||||||
pixel_type_w tendency = SmoothTendency(top, avg, next_avg);
|
for (; x < w; x++) {
|
||||||
pixel_type_w diff = diff_minus_tendency + tendency;
|
pixel_type avg = p_avg[x];
|
||||||
pixel_type_w out =
|
pixel_type next_avg = p_navg[x];
|
||||||
((avg * 2) + diff + (diff > 0 ? -(diff & 1) : (diff & 1))) >> 1;
|
pixel_type top = p_pout[x];
|
||||||
|
pixel_type tendency = SmoothTendency(top, avg, next_avg);
|
||||||
p_out[x] = out;
|
pixel_type diff_minus_tendency = p_residual[x];
|
||||||
// If the chin_residual.h == chin.h, the output has an even number
|
pixel_type diff = diff_minus_tendency + tendency;
|
||||||
// of rows so the next line is fine. Otherwise, this loop won't
|
pixel_type out = avg + (diff / 2);
|
||||||
// write to the last output row which is handled separately.
|
p_out[x] = out;
|
||||||
p_out[x + onerow_out] = p_out[x] - diff;
|
// If the chin_residual.h == chin.h, the output has an even number
|
||||||
}
|
// of rows so the next line is fine. Otherwise, this loop won't
|
||||||
}
|
// write to the last output row which is handled separately.
|
||||||
},
|
p_nout[x] = out - diff;
|
||||||
"InvVertSqueeze"));
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
JXL_RETURN_IF_ERROR(RunOnPool(pool, 0, DivCeil(chin.w, kColsPerThread),
|
||||||
|
ThreadPool::NoInit, unsqueeze_slice,
|
||||||
|
"InvVertSqueeze"));
|
||||||
|
|
||||||
if (chout.h & 1) {
|
if (chout.h & 1) {
|
||||||
size_t y = chin.h - 1;
|
size_t y = chin.h - 1;
|
||||||
@ -160,6 +274,62 @@ Status InvVSqueeze(Image &input, uint32_t c, uint32_t rc, ThreadPool *pool) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status InvSqueeze(Image &input, std::vector<SqueezeParams> parameters,
|
||||||
|
ThreadPool *pool) {
|
||||||
|
for (int i = parameters.size() - 1; i >= 0; i--) {
|
||||||
|
JXL_RETURN_IF_ERROR(
|
||||||
|
CheckMetaSqueezeParams(parameters[i], input.channel.size()));
|
||||||
|
bool horizontal = parameters[i].horizontal;
|
||||||
|
bool in_place = parameters[i].in_place;
|
||||||
|
uint32_t beginc = parameters[i].begin_c;
|
||||||
|
uint32_t endc = parameters[i].begin_c + parameters[i].num_c - 1;
|
||||||
|
uint32_t offset;
|
||||||
|
if (in_place) {
|
||||||
|
offset = endc + 1;
|
||||||
|
} else {
|
||||||
|
offset = input.channel.size() + beginc - endc - 1;
|
||||||
|
}
|
||||||
|
if (beginc < input.nb_meta_channels) {
|
||||||
|
// This is checked in MetaSqueeze.
|
||||||
|
JXL_ASSERT(input.nb_meta_channels > parameters[i].num_c);
|
||||||
|
input.nb_meta_channels -= parameters[i].num_c;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t c = beginc; c <= endc; c++) {
|
||||||
|
uint32_t rc = offset + c - beginc;
|
||||||
|
// MetaApply should imply that `rc` is within range, otherwise there's a
|
||||||
|
// programming bug.
|
||||||
|
JXL_ASSERT(rc < input.channel.size());
|
||||||
|
if ((input.channel[c].w < input.channel[rc].w) ||
|
||||||
|
(input.channel[c].h < input.channel[rc].h)) {
|
||||||
|
return JXL_FAILURE("Corrupted squeeze transform");
|
||||||
|
}
|
||||||
|
if (horizontal) {
|
||||||
|
JXL_RETURN_IF_ERROR(InvHSqueeze(input, c, rc, pool));
|
||||||
|
} else {
|
||||||
|
JXL_RETURN_IF_ERROR(InvVSqueeze(input, c, rc, pool));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input.channel.erase(input.channel.begin() + offset,
|
||||||
|
input.channel.begin() + offset + (endc - beginc + 1));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace HWY_NAMESPACE
|
||||||
|
} // namespace jxl
|
||||||
|
HWY_AFTER_NAMESPACE();
|
||||||
|
|
||||||
|
#if HWY_ONCE
|
||||||
|
|
||||||
|
namespace jxl {
|
||||||
|
|
||||||
|
HWY_EXPORT(InvSqueeze);
|
||||||
|
Status InvSqueeze(Image &input, std::vector<SqueezeParams> parameters,
|
||||||
|
ThreadPool *pool) {
|
||||||
|
return HWY_DYNAMIC_DISPATCH(InvSqueeze)(input, parameters, pool);
|
||||||
|
}
|
||||||
|
|
||||||
void DefaultSqueezeParameters(std::vector<SqueezeParams> *parameters,
|
void DefaultSqueezeParameters(std::vector<SqueezeParams> *parameters,
|
||||||
const Image &image) {
|
const Image &image) {
|
||||||
int nb_channels = image.channel.size() - image.nb_meta_channels;
|
int nb_channels = image.channel.size() - image.nb_meta_channels;
|
||||||
@ -285,46 +455,6 @@ Status MetaSqueeze(Image &image, std::vector<SqueezeParams> *parameters) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status InvSqueeze(Image &input, std::vector<SqueezeParams> parameters,
|
|
||||||
ThreadPool *pool) {
|
|
||||||
for (int i = parameters.size() - 1; i >= 0; i--) {
|
|
||||||
JXL_RETURN_IF_ERROR(
|
|
||||||
CheckMetaSqueezeParams(parameters[i], input.channel.size()));
|
|
||||||
bool horizontal = parameters[i].horizontal;
|
|
||||||
bool in_place = parameters[i].in_place;
|
|
||||||
uint32_t beginc = parameters[i].begin_c;
|
|
||||||
uint32_t endc = parameters[i].begin_c + parameters[i].num_c - 1;
|
|
||||||
uint32_t offset;
|
|
||||||
if (in_place) {
|
|
||||||
offset = endc + 1;
|
|
||||||
} else {
|
|
||||||
offset = input.channel.size() + beginc - endc - 1;
|
|
||||||
}
|
|
||||||
if (beginc < input.nb_meta_channels) {
|
|
||||||
// This is checked in MetaSqueeze.
|
|
||||||
JXL_ASSERT(input.nb_meta_channels > parameters[i].num_c);
|
|
||||||
input.nb_meta_channels -= parameters[i].num_c;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint32_t c = beginc; c <= endc; c++) {
|
|
||||||
uint32_t rc = offset + c - beginc;
|
|
||||||
// MetaApply should imply that `rc` is within range, otherwise there's a
|
|
||||||
// programming bug.
|
|
||||||
JXL_ASSERT(rc < input.channel.size());
|
|
||||||
if ((input.channel[c].w < input.channel[rc].w) ||
|
|
||||||
(input.channel[c].h < input.channel[rc].h)) {
|
|
||||||
return JXL_FAILURE("Corrupted squeeze transform");
|
|
||||||
}
|
|
||||||
if (horizontal) {
|
|
||||||
JXL_RETURN_IF_ERROR(InvHSqueeze(input, c, rc, pool));
|
|
||||||
} else {
|
|
||||||
JXL_RETURN_IF_ERROR(InvVSqueeze(input, c, rc, pool));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input.channel.erase(input.channel.begin() + offset,
|
|
||||||
input.channel.begin() + offset + (endc - beginc + 1));
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace jxl
|
} // namespace jxl
|
||||||
|
|
||||||
|
#endif
|
@ -75,10 +75,6 @@ inline pixel_type_w SmoothTendency(pixel_type_w B, pixel_type_w a,
|
|||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvHSqueeze(Image &input, int c, int rc, ThreadPool *pool);
|
|
||||||
|
|
||||||
void InvVSqueeze(Image &input, int c, int rc, ThreadPool *pool);
|
|
||||||
|
|
||||||
void DefaultSqueezeParameters(std::vector<SqueezeParams> *parameters,
|
void DefaultSqueezeParameters(std::vector<SqueezeParams> *parameters,
|
||||||
const Image &image);
|
const Image &image);
|
||||||
|
|
||||||
|
141
third_party/jpeg-xl/lib/jxl/modular_test.cc
vendored
141
third_party/jpeg-xl/lib/jxl/modular_test.cc
vendored
@ -25,6 +25,7 @@
|
|||||||
#include "lib/jxl/dec_file.h"
|
#include "lib/jxl/dec_file.h"
|
||||||
#include "lib/jxl/dec_params.h"
|
#include "lib/jxl/dec_params.h"
|
||||||
#include "lib/jxl/enc_butteraugli_comparator.h"
|
#include "lib/jxl/enc_butteraugli_comparator.h"
|
||||||
|
#include "lib/jxl/enc_butteraugli_pnorm.h"
|
||||||
#include "lib/jxl/enc_cache.h"
|
#include "lib/jxl/enc_cache.h"
|
||||||
#include "lib/jxl/enc_color_management.h"
|
#include "lib/jxl/enc_color_management.h"
|
||||||
#include "lib/jxl/enc_file.h"
|
#include "lib/jxl/enc_file.h"
|
||||||
@ -45,11 +46,10 @@ using test::Roundtrip;
|
|||||||
void TestLosslessGroups(size_t group_size_shift) {
|
void TestLosslessGroups(size_t group_size_shift) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.modular_mode = true;
|
cparams.SetLossless();
|
||||||
cparams.modular_group_size_shift = group_size_shift;
|
cparams.modular_group_size_shift = group_size_shift;
|
||||||
cparams.color_transform = jxl::ColorTransform::kNone;
|
|
||||||
DecompressParams dparams;
|
DecompressParams dparams;
|
||||||
|
|
||||||
CodecInOut io_out;
|
CodecInOut io_out;
|
||||||
@ -79,15 +79,14 @@ TEST(ModularTest, JXL_TSAN_SLOW_TEST(RoundtripLosslessGroups1024)) {
|
|||||||
TEST(ModularTest, RoundtripLosslessCustomWP_PermuteRCT) {
|
TEST(ModularTest, RoundtripLosslessCustomWP_PermuteRCT) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.modular_mode = true;
|
cparams.SetLossless();
|
||||||
// 9 = permute to GBR, to test the special case of permutation-only
|
// 9 = permute to GBR, to test the special case of permutation-only
|
||||||
cparams.colorspace = 9;
|
cparams.colorspace = 9;
|
||||||
// slowest speed so different WP modes are tried
|
// slowest speed so different WP modes are tried
|
||||||
cparams.speed_tier = SpeedTier::kTortoise;
|
cparams.speed_tier = SpeedTier::kTortoise;
|
||||||
cparams.options.predictor = {Predictor::Weighted};
|
cparams.options.predictor = {Predictor::Weighted};
|
||||||
cparams.color_transform = jxl::ColorTransform::kNone;
|
|
||||||
DecompressParams dparams;
|
DecompressParams dparams;
|
||||||
|
|
||||||
CodecInOut io_out;
|
CodecInOut io_out;
|
||||||
@ -107,7 +106,7 @@ TEST(ModularTest, RoundtripLosslessCustomWP_PermuteRCT) {
|
|||||||
TEST(ModularTest, RoundtripLossyDeltaPalette) {
|
TEST(ModularTest, RoundtripLossyDeltaPalette) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.modular_mode = true;
|
cparams.modular_mode = true;
|
||||||
cparams.color_transform = jxl::ColorTransform::kNone;
|
cparams.color_transform = jxl::ColorTransform::kNone;
|
||||||
@ -133,10 +132,9 @@ TEST(ModularTest, RoundtripLossyDeltaPalette) {
|
|||||||
TEST(ModularTest, RoundtripLossyDeltaPaletteWP) {
|
TEST(ModularTest, RoundtripLossyDeltaPaletteWP) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.modular_mode = true;
|
cparams.SetLossless();
|
||||||
cparams.color_transform = jxl::ColorTransform::kNone;
|
|
||||||
cparams.lossy_palette = true;
|
cparams.lossy_palette = true;
|
||||||
cparams.palette_colors = 0;
|
cparams.palette_colors = 0;
|
||||||
cparams.options.predictor = jxl::Predictor::Weighted;
|
cparams.options.predictor = jxl::Predictor::Weighted;
|
||||||
@ -161,10 +159,10 @@ TEST(ModularTest, RoundtripLossyDeltaPaletteWP) {
|
|||||||
TEST(ModularTest, RoundtripLossy) {
|
TEST(ModularTest, RoundtripLossy) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.modular_mode = true;
|
cparams.modular_mode = true;
|
||||||
cparams.quality_pair = {80.0f, 80.0f};
|
cparams.butteraugli_distance = 2.f;
|
||||||
DecompressParams dparams;
|
DecompressParams dparams;
|
||||||
|
|
||||||
CodecInOut io_out;
|
CodecInOut io_out;
|
||||||
@ -174,20 +172,20 @@ TEST(ModularTest, RoundtripLossy) {
|
|||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
compressed_size = Roundtrip(&io, cparams, dparams, pool, &io_out);
|
compressed_size = Roundtrip(&io, cparams, dparams, pool, &io_out);
|
||||||
EXPECT_LE(compressed_size, 40000u);
|
EXPECT_LE(compressed_size, 30000u);
|
||||||
cparams.ba_params.intensity_target = 80.0f;
|
cparams.ba_params.intensity_target = 80.0f;
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io_out, cparams.ba_params, GetJxlCms(),
|
EXPECT_THAT(ButteraugliDistance(io, io_out, cparams.ba_params, GetJxlCms(),
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(2.0));
|
IsSlightlyBelow(2.3));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ModularTest, RoundtripLossy16) {
|
TEST(ModularTest, RoundtripLossy16) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("raw.pixls/DJI-FC6310-16bit_709_v4_krita.png");
|
ReadTestData("third_party/raw.pixls/DJI-FC6310-16bit_709_v4_krita.png");
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.modular_mode = true;
|
cparams.modular_mode = true;
|
||||||
cparams.quality_pair = {80.0f, 80.0f};
|
cparams.butteraugli_distance = 2.f;
|
||||||
DecompressParams dparams;
|
DecompressParams dparams;
|
||||||
|
|
||||||
CodecInOut io_out;
|
CodecInOut io_out;
|
||||||
@ -199,11 +197,11 @@ TEST(ModularTest, RoundtripLossy16) {
|
|||||||
io.metadata.m.color_encoding = ColorEncoding::SRGB();
|
io.metadata.m.color_encoding = ColorEncoding::SRGB();
|
||||||
|
|
||||||
compressed_size = Roundtrip(&io, cparams, dparams, pool, &io_out);
|
compressed_size = Roundtrip(&io, cparams, dparams, pool, &io_out);
|
||||||
EXPECT_LE(compressed_size, 400u);
|
EXPECT_LE(compressed_size, 300u);
|
||||||
cparams.ba_params.intensity_target = 80.0f;
|
cparams.ba_params.intensity_target = 80.0f;
|
||||||
EXPECT_THAT(ButteraugliDistance(io, io_out, cparams.ba_params, GetJxlCms(),
|
EXPECT_THAT(ButteraugliDistance(io, io_out, cparams.ba_params, GetJxlCms(),
|
||||||
/*distmap=*/nullptr, pool),
|
/*distmap=*/nullptr, pool),
|
||||||
IsSlightlyBelow(1.5));
|
IsSlightlyBelow(1.6));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(ModularTest, RoundtripExtraProperties) {
|
TEST(ModularTest, RoundtripExtraProperties) {
|
||||||
@ -250,15 +248,15 @@ TEST(ModularTest, RoundtripExtraProperties) {
|
|||||||
|
|
||||||
TEST(ModularTest, RoundtripLosslessCustomSqueeze) {
|
TEST(ModularTest, RoundtripLosslessCustomSqueeze) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
"third_party/wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.modular_mode = true;
|
cparams.modular_mode = true;
|
||||||
cparams.color_transform = jxl::ColorTransform::kNone;
|
cparams.color_transform = jxl::ColorTransform::kNone;
|
||||||
cparams.quality_pair = {100, 100};
|
cparams.butteraugli_distance = 0.f;
|
||||||
cparams.options.predictor = {Predictor::Zero};
|
cparams.options.predictor = {Predictor::Zero};
|
||||||
cparams.speed_tier = SpeedTier::kThunder;
|
cparams.speed_tier = SpeedTier::kThunder;
|
||||||
cparams.responsive = 1;
|
cparams.responsive = 1;
|
||||||
@ -281,6 +279,105 @@ TEST(ModularTest, RoundtripLosslessCustomSqueeze) {
|
|||||||
/*distmap=*/nullptr, pool));
|
/*distmap=*/nullptr, pool));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct RoundtripLosslessConfig {
|
||||||
|
int bitdepth;
|
||||||
|
int responsive;
|
||||||
|
};
|
||||||
|
class ModularTestParam
|
||||||
|
: public ::testing::TestWithParam<RoundtripLosslessConfig> {};
|
||||||
|
|
||||||
|
std::vector<RoundtripLosslessConfig> GenerateLosslessTests() {
|
||||||
|
std::vector<RoundtripLosslessConfig> all;
|
||||||
|
for (int responsive = 0; responsive <= 1; responsive++) {
|
||||||
|
for (int bitdepth = 1; bitdepth < 32; bitdepth++) {
|
||||||
|
if (responsive && bitdepth > 30) continue;
|
||||||
|
all.push_back({bitdepth, responsive});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
std::string LosslessTestDescription(
|
||||||
|
const testing::TestParamInfo<ModularTestParam::ParamType>& info) {
|
||||||
|
std::stringstream name;
|
||||||
|
name << info.param.bitdepth << "bit";
|
||||||
|
if (info.param.responsive) name << "Squeeze";
|
||||||
|
return name.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
JXL_GTEST_INSTANTIATE_TEST_SUITE_P(RoundtripLossless, ModularTestParam,
|
||||||
|
testing::ValuesIn(GenerateLosslessTests()),
|
||||||
|
LosslessTestDescription);
|
||||||
|
|
||||||
|
TEST_P(ModularTestParam, RoundtripLossless) {
|
||||||
|
RoundtripLosslessConfig config = GetParam();
|
||||||
|
int bitdepth = config.bitdepth;
|
||||||
|
int responsive = config.responsive;
|
||||||
|
|
||||||
|
ThreadPool* pool = nullptr;
|
||||||
|
Rng generator(123);
|
||||||
|
const PaddedBytes orig =
|
||||||
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
|
CodecInOut io1;
|
||||||
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io1, pool));
|
||||||
|
|
||||||
|
// vary the dimensions a bit, in case of bugs related to
|
||||||
|
// even vs odd width or height.
|
||||||
|
size_t xsize = 423 + bitdepth;
|
||||||
|
size_t ysize = 467 + bitdepth;
|
||||||
|
|
||||||
|
CodecInOut io;
|
||||||
|
io.SetSize(xsize, ysize);
|
||||||
|
io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
|
||||||
|
io.metadata.m.SetUintSamples(bitdepth);
|
||||||
|
|
||||||
|
double factor = ((1lu << bitdepth) - 1lu);
|
||||||
|
double ifactor = 1.0 / factor;
|
||||||
|
Image3F noise_added(xsize, ysize);
|
||||||
|
|
||||||
|
for (size_t c = 0; c < 3; c++) {
|
||||||
|
for (size_t y = 0; y < ysize; y++) {
|
||||||
|
const float* in = io1.Main().color()->PlaneRow(c, y);
|
||||||
|
float* out = noise_added.PlaneRow(c, y);
|
||||||
|
for (size_t x = 0; x < xsize; x++) {
|
||||||
|
// make the least significant bits random
|
||||||
|
float f = in[x] + generator.UniformF(0.0f, 1.f / 255.f);
|
||||||
|
if (f > 1.f) f = 1.f;
|
||||||
|
// quantize to the bitdepth we're testing
|
||||||
|
unsigned int u = f * factor + 0.5;
|
||||||
|
out[x] = u * ifactor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
io.SetFromImage(std::move(noise_added), jxl::ColorEncoding::SRGB(false));
|
||||||
|
|
||||||
|
CompressParams cparams;
|
||||||
|
cparams.modular_mode = true;
|
||||||
|
cparams.color_transform = jxl::ColorTransform::kNone;
|
||||||
|
cparams.butteraugli_distance = 0.f;
|
||||||
|
cparams.options.predictor = {Predictor::Zero};
|
||||||
|
cparams.speed_tier = SpeedTier::kThunder;
|
||||||
|
cparams.responsive = responsive;
|
||||||
|
DecompressParams dparams;
|
||||||
|
CodecInOut io2;
|
||||||
|
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2),
|
||||||
|
bitdepth * xsize * ysize / 3);
|
||||||
|
EXPECT_LE(0, ComputeDistance2(io.Main(), io2.Main(), GetJxlCms()));
|
||||||
|
size_t different = 0;
|
||||||
|
for (size_t c = 0; c < 3; c++) {
|
||||||
|
for (size_t y = 0; y < ysize; y++) {
|
||||||
|
const float* in = io.Main().color()->PlaneRow(c, y);
|
||||||
|
const float* out = io2.Main().color()->PlaneRow(c, y);
|
||||||
|
for (size_t x = 0; x < xsize; x++) {
|
||||||
|
uint32_t uin = in[x] * factor + 0.5;
|
||||||
|
uint32_t uout = out[x] * factor + 0.5;
|
||||||
|
// check that the integer values are identical
|
||||||
|
if (uin != uout) different++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_EQ(different, 0);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(ModularTest, RoundtripLosslessCustomFloat) {
|
TEST(ModularTest, RoundtripLosslessCustomFloat) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
@ -310,7 +407,7 @@ TEST(ModularTest, RoundtripLosslessCustomFloat) {
|
|||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.modular_mode = true;
|
cparams.modular_mode = true;
|
||||||
cparams.color_transform = jxl::ColorTransform::kNone;
|
cparams.color_transform = jxl::ColorTransform::kNone;
|
||||||
cparams.quality_pair = {100, 100};
|
cparams.butteraugli_distance = 0.f;
|
||||||
cparams.options.predictor = {Predictor::Zero};
|
cparams.options.predictor = {Predictor::Zero};
|
||||||
cparams.speed_tier = SpeedTier::kThunder;
|
cparams.speed_tier = SpeedTier::kThunder;
|
||||||
cparams.decoding_speed_tier = 2;
|
cparams.decoding_speed_tier = 2;
|
||||||
|
24
third_party/jpeg-xl/lib/jxl/passes_test.cc
vendored
24
third_party/jpeg-xl/lib/jxl/passes_test.cc
vendored
@ -37,7 +37,7 @@ using test::Roundtrip;
|
|||||||
TEST(PassesTest, RoundtripSmallPasses) {
|
TEST(PassesTest, RoundtripSmallPasses) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
||||||
@ -57,7 +57,7 @@ TEST(PassesTest, RoundtripSmallPasses) {
|
|||||||
TEST(PassesTest, RoundtripUnalignedPasses) {
|
TEST(PassesTest, RoundtripUnalignedPasses) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize() / 12, io.ysize() / 7);
|
io.ShrinkTo(io.xsize() / 12, io.ysize() / 7);
|
||||||
@ -77,7 +77,7 @@ TEST(PassesTest, RoundtripUnalignedPasses) {
|
|||||||
TEST(PassesTest, RoundtripMultiGroupPasses) {
|
TEST(PassesTest, RoundtripMultiGroupPasses) {
|
||||||
ThreadPoolInternal pool(4);
|
ThreadPoolInternal pool(4);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
io.ShrinkTo(600, 1024); // partial X, full Y group
|
io.ShrinkTo(600, 1024); // partial X, full Y group
|
||||||
@ -104,7 +104,7 @@ TEST(PassesTest, RoundtripMultiGroupPasses) {
|
|||||||
TEST(PassesTest, RoundtripLargeFastPasses) {
|
TEST(PassesTest, RoundtripLargeFastPasses) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -123,7 +123,7 @@ TEST(PassesTest, RoundtripLargeFastPasses) {
|
|||||||
TEST(PassesTest, RoundtripProgressiveConsistent) {
|
TEST(PassesTest, RoundtripProgressiveConsistent) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ TEST(PassesTest, RoundtripProgressiveConsistent) {
|
|||||||
TEST(PassesTest, AllDownsampleFeasible) {
|
TEST(PassesTest, AllDownsampleFeasible) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -207,7 +207,7 @@ TEST(PassesTest, AllDownsampleFeasible) {
|
|||||||
TEST(PassesTest, AllDownsampleFeasibleQProgressive) {
|
TEST(PassesTest, AllDownsampleFeasibleQProgressive) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -253,8 +253,8 @@ TEST(PassesTest, AllDownsampleFeasibleQProgressive) {
|
|||||||
|
|
||||||
TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) {
|
TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig = ReadTestData(
|
||||||
ReadTestData("wesaturate/500px/cvo9xd_keong_macan_grayscale.png");
|
"third_party/wesaturate/500px/cvo9xd_keong_macan_grayscale.png");
|
||||||
CodecInOut io_orig;
|
CodecInOut io_orig;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io_orig, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io_orig, &pool));
|
||||||
Rect rect(0, 0, io_orig.xsize(), 128);
|
Rect rect(0, 0, io_orig.xsize(), 128);
|
||||||
@ -302,7 +302,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectlyGrayscale) {
|
|||||||
TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) {
|
TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io_orig;
|
CodecInOut io_orig;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io_orig, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io_orig, &pool));
|
||||||
Rect rect(0, 0, io_orig.xsize(), 128);
|
Rect rect(0, 0, io_orig.xsize(), 128);
|
||||||
@ -349,7 +349,7 @@ TEST(PassesTest, ProgressiveDownsample2DegradesCorrectly) {
|
|||||||
TEST(PassesTest, NonProgressiveDCImage) {
|
TEST(PassesTest, NonProgressiveDCImage) {
|
||||||
ThreadPoolInternal pool(8);
|
ThreadPoolInternal pool(8);
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("imagecompression.info/flower_foveon.png");
|
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
|
||||||
|
|
||||||
@ -377,7 +377,7 @@ TEST(PassesTest, NonProgressiveDCImage) {
|
|||||||
TEST(PassesTest, RoundtripSmallNoGaborishPasses) {
|
TEST(PassesTest, RoundtripSmallNoGaborishPasses) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
||||||
|
@ -24,8 +24,7 @@ TEST(PatchDictionaryTest, GrayscaleModular) {
|
|||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
|
|
||||||
CompressParams cparams;
|
CompressParams cparams;
|
||||||
cparams.color_transform = jxl::ColorTransform::kNone;
|
cparams.SetLossless();
|
||||||
cparams.modular_mode = true;
|
|
||||||
cparams.patches = jxl::Override::kOn;
|
cparams.patches = jxl::Override::kOn;
|
||||||
DecompressParams dparams;
|
DecompressParams dparams;
|
||||||
|
|
||||||
|
2
third_party/jpeg-xl/lib/jxl/preview_test.cc
vendored
2
third_party/jpeg-xl/lib/jxl/preview_test.cc
vendored
@ -34,7 +34,7 @@ using test::Roundtrip;
|
|||||||
TEST(PreviewTest, RoundtripGivenPreview) {
|
TEST(PreviewTest, RoundtripGivenPreview) {
|
||||||
ThreadPool* pool = nullptr;
|
ThreadPool* pool = nullptr;
|
||||||
const PaddedBytes orig =
|
const PaddedBytes orig =
|
||||||
ReadTestData("wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
ReadTestData("third_party/wesaturate/500px/u76c0g_bliznaca_srgb8.png");
|
||||||
CodecInOut io;
|
CodecInOut io;
|
||||||
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
|
||||||
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
io.ShrinkTo(io.xsize() / 8, io.ysize() / 8);
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user