Bug 1834208 - Update libjxl to 91760e31a40fb811867fbe9bda6ddf73c962389e r=saschanaz

Differential Revision: https://phabricator.services.mozilla.com/D178591
This commit is contained in:
Updatebot 2023-05-22 11:53:20 +00:00
Родитель 4967f36274
Коммит 2d81f310e8
245 изменённых файлов: 15174 добавлений и 5972 удалений

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

@ -17,6 +17,7 @@ SOURCES += [
"/third_party/jpeg-xl/lib/jxl/base/cache_aligned.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/profiler.cc",
"/third_party/jpeg-xl/lib/jxl/base/random.cc",
"/third_party/jpeg-xl/lib/jxl/blending.cc",
"/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc",

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

@ -10,9 +10,9 @@ origin:
url: https://github.com/libjxl/libjxl
release: 73cb36f7129e1ce192992792778b146ed9b655a5 (2023-02-23T15:08:08Z).
release: 91760e31a40fb811867fbe9bda6ddf73c962389e (2023-05-16T11:48:39Z).
revision: 73cb36f7129e1ce192992792778b146ed9b655a5
revision: 91760e31a40fb811867fbe9bda6ddf73c962389e
license: Apache-2.0

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

@ -37,6 +37,7 @@ gi-man
Heiko Becker <heirecka@exherbo.org>
Jim Robinson <jimbo2150@gmail.com>
Jon Sneyers <jon@cloudinary.com>
Joshua Root <jmr@macports.org>
Kai Hollberg <Schweinepriester@users.noreply.github.com>
Kleis Auke Wolthuizen <github@kleisauke.nl>
L. E. Segovia
@ -55,6 +56,7 @@ Pieter Wuille
roland-rollo
Samuel Leong <wvvwvvvvwvvw@gmail.com>
Sandro <sandro.jaeckel@gmail.com>
Sergey Fedorov <vital.had@gmail.com>
Stephan T. Lavavej <stl@nuwen.net>
Thomas Bonfort <thomas.bonfort@airbus.com>
tmkk <tmkkmac@gmail.com>
@ -62,3 +64,4 @@ Vincent Torri <vincent.torri@gmail.com>
xiota
Yonatan Nebenzhal <yonatan.nebenzhl@gmail.com>
Ziemowit Zabawa <ziemek.zabawa@outlook.com>
源文雨 <41315874+fumiama@users.noreply.github.com>

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

@ -104,6 +104,10 @@ set(JPEGXL_ENABLE_JPEGLI true CACHE BOOL
"Build jpegli library.")
set(JPEGXL_ENABLE_JPEGLI_LIBJPEG true CACHE BOOL
"Build libjpeg.so shared library based on jpegli.")
set(JPEGLI_LIBJPEG_LIBRARY_VERSION "62.3.0" CACHE STRING
"Library version of the libjpeg.so shared library that we build.")
set(JPEGLI_LIBJPEG_LIBRARY_SOVERSION "62" CACHE STRING
"Library so-version of the libjpeg.so shared library that we build.")
set(JPEGXL_ENABLE_DOXYGEN true CACHE BOOL
"Generate C API documentation using Doxygen.")
set(JPEGXL_ENABLE_MANPAGES true CACHE BOOL
@ -152,7 +156,8 @@ set(JPEGXL_DEP_LICENSE_DIR "" CACHE STRING
"Directory where to search for system dependencies \"copyright\" files.")
set(JPEGXL_FORCE_NEON false CACHE BOOL
"Set flags to enable NEON in arm if not enabled by your toolchain.")
set(JPEGXL_TEST_TOOLS false CACHE BOOL
"Run scripts that test the encoding / decoding tools.")
# Force system dependencies.
set(JPEGXL_FORCE_SYSTEM_BROTLI false CACHE BOOL
@ -265,73 +270,82 @@ if (CXX_NO_RTTI_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
endif()
# Internal flags for coverage builds:
set(JPEGXL_COVERAGE_FLAGS)
set(JPEGXL_COVERAGE_LINK_FLAGS)
if (MSVC)
# TODO(janwas): add flags
else ()
# Global compiler flags for all targets here and in subdirectories.
add_definitions(
# Avoid changing the binary based on the current time and date.
-D__DATE__="redacted"
-D__TIMESTAMP__="redacted"
-D__TIME__="redacted"
)
# Avoid log spam from fopen etc.
if(MSVC)
# TODO(janwas): add flags
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
endif()
else ()
# Global compiler flags for all targets here and in subdirectories.
add_definitions(
# Avoid changing the binary based on the current time and date.
-D__DATE__="redacted"
-D__TIMESTAMP__="redacted"
-D__TIME__="redacted"
)
# TODO(eustas): JXL currently compiles, but does not pass tests...
if (NOT JXL_HWY_DISABLED_TARGETS_FORCED AND NOT JPEGXL_ENABLE_SIZELESS_VECTORS)
add_definitions(-DHWY_DISABLED_TARGETS=\(HWY_SVE|HWY_SVE2|HWY_SVE_256|HWY_SVE2_128|HWY_RVV\))
message("Warning: HWY_SVE, HWY_SVE2, HWY_SVE_256, HWY_SVE2_128 and HWY_RVV CPU targets are disabled")
endif()
# TODO(eustas): JXL currently compiles, but does not pass tests...
if (NOT JXL_HWY_DISABLED_TARGETS_FORCED AND NOT JPEGXL_ENABLE_SIZELESS_VECTORS)
add_definitions(-DHWY_DISABLED_TARGETS=\(HWY_SVE|HWY_SVE2|HWY_SVE_256|HWY_SVE2_128|HWY_RVV\))
message("Warning: HWY_SVE, HWY_SVE2, HWY_SVE_256, HWY_SVE2_128 and HWY_RVV CPU targets are disabled")
endif()
# In CMake before 3.12 it is problematic to pass repeated flags like -Xclang.
# For this reason we place them in CMAKE_CXX_FLAGS instead.
# See https://gitlab.kitware.com/cmake/cmake/issues/15826
# In CMake before 3.12 it is problematic to pass repeated flags like -Xclang.
# For this reason we place them in CMAKE_CXX_FLAGS instead.
# See https://gitlab.kitware.com/cmake/cmake/issues/15826
# Machine flags.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funwind-tables")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -mrelax-all")
endif()
if (CXX_CONSTRUCTOR_ALIASES_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -mconstructor-aliases")
endif()
# Machine flags.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funwind-tables")
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -mrelax-all")
endif()
if (CXX_CONSTRUCTOR_ALIASES_SUPPORTED)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -mconstructor-aliases")
endif()
if(WIN32)
# Not supported by clang-cl, but frame pointers are default on Windows
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
endif()
if(WIN32)
# Not supported by clang-cl, but frame pointers are default on Windows
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-omit-frame-pointer")
endif()
# CPU flags - remove once we have NEON dynamic dispatch
# CPU flags - remove once we have NEON dynamic dispatch
# TODO(janwas): this also matches M1, but only ARMv7 is intended/needed.
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
if(JPEGXL_FORCE_NEON)
# GCC requires these flags, otherwise __ARM_NEON is undefined.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \
-mfpu=neon-vfpv4 -mfloat-abi=hard")
endif()
endif()
# TODO(janwas): this also matches M1, but only ARMv7 is intended/needed.
if(CMAKE_SYSTEM_PROCESSOR MATCHES "arm")
if(JPEGXL_FORCE_NEON)
# GCC requires these flags, otherwise __ARM_NEON is undefined.
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \
-mfpu=neon-vfpv4 -mfloat-abi=hard")
endif()
endif()
# Force build with optimizations in release mode.
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
# Force build with optimizations in release mode.
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2")
add_compile_options(
# Ignore this to allow redefining __DATE__ and others.
-Wno-builtin-macro-redefined
add_compile_options(
# Ignore this to allow redefining __DATE__ and others.
-Wno-builtin-macro-redefined
# Global warning settings.
-Wall
)
# Global warning settings.
-Wall
)
if (JPEGXL_WARNINGS_AS_ERRORS)
add_compile_options(-Werror)
endif ()
if (JPEGXL_WARNINGS_AS_ERRORS)
add_compile_options(-Werror)
endif ()
if(JPEGXL_ENABLE_COVERAGE)
set(JPEGXL_COVERAGE_FLAGS
-g -O0 -fprofile-arcs -ftest-coverage
-DJXL_ENABLE_ASSERT=0 -DJXL_ENABLE_CHECK=0
)
set(JPEGXL_COVERAGE_LINK_FLAGS
--coverage
)
endif() # JPEGXL_ENABLE_COVERAGE
endif () # !MSVC
include(GNUInstallDirs)
@ -356,124 +370,124 @@ endif()
add_subdirectory(lib)
if(BUILD_TESTING)
# Script to run tests over the source code in bash.
find_program (BASH_PROGRAM bash)
if(BASH_PROGRAM)
add_test(
NAME bash_test
COMMAND ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/bash_test.sh)
endif()
# Script to run tests over the source code in bash.
find_program (BASH_PROGRAM bash)
if(BASH_PROGRAM)
add_test(
NAME bash_test
COMMAND ${BASH_PROGRAM} ${CMAKE_CURRENT_SOURCE_DIR}/bash_test.sh)
endif()
endif() # BUILD_TESTING
# Documentation generated by Doxygen
if(JPEGXL_ENABLE_DOXYGEN)
find_package(Doxygen)
if(DOXYGEN_FOUND)
set(DOXYGEN_GENERATE_HTML "YES")
set(DOXYGEN_GENERATE_XML "YES")
set(DOXYGEN_STRIP_FROM_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/include")
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "README.md")
if(JPEGXL_WARNINGS_AS_ERRORS)
set(DOXYGEN_WARN_AS_ERROR "YES")
endif()
set(DOXYGEN_QUIET "YES")
doxygen_add_docs(doc
"${CMAKE_CURRENT_SOURCE_DIR}/lib/include"
"${CMAKE_CURRENT_SOURCE_DIR}/doc/api.txt"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Generating C API documentation")
find_package(Doxygen)
if(DOXYGEN_FOUND)
set(DOXYGEN_GENERATE_HTML "YES")
set(DOXYGEN_GENERATE_XML "YES")
set(DOXYGEN_STRIP_FROM_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/include")
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "README.md")
if(JPEGXL_WARNINGS_AS_ERRORS)
set(DOXYGEN_WARN_AS_ERROR "YES")
endif()
set(DOXYGEN_QUIET "YES")
doxygen_add_docs(doc
"${CMAKE_CURRENT_SOURCE_DIR}/lib/include"
"${CMAKE_CURRENT_SOURCE_DIR}/doc/api.txt"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Generating C API documentation")
# Add sphinx doc build step for readthedocs.io (requires doxygen too).
find_program(SPHINX_BUILD_PROGRAM sphinx-build)
if(SPHINX_BUILD_PROGRAM)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent"
COMMENT "Generating readthedocs.io output on ${CMAKE_CURRENT_BINARY_DIR}/rtd"
COMMAND ${SPHINX_BUILD_PROGRAM} -q -W -b html -j auto
${CMAKE_SOURCE_DIR}/doc/sphinx
${CMAKE_CURRENT_BINARY_DIR}/rtd
DEPENDS doc
)
# This command runs the documentation generation every time since the output
# target file doesn't exist.
add_custom_target(rtd-html
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent
)
else() # SPHINX_BUILD_PROGRAM\
message(WARNING "sphinx-build not found, skipping rtd documentation")
endif() # SPHINX_BUILD_PROGRAM
# Add sphinx doc build step for readthedocs.io (requires doxygen too).
find_program(SPHINX_BUILD_PROGRAM sphinx-build)
if(SPHINX_BUILD_PROGRAM)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent"
COMMENT "Generating readthedocs.io output on ${CMAKE_CURRENT_BINARY_DIR}/rtd"
COMMAND ${SPHINX_BUILD_PROGRAM} -q -W -b html -j auto
${CMAKE_SOURCE_DIR}/doc/sphinx
${CMAKE_CURRENT_BINARY_DIR}/rtd
DEPENDS doc
)
# This command runs the documentation generation every time since the output
# target file doesn't exist.
add_custom_target(rtd-html
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent
)
else() # SPHINX_BUILD_PROGRAM\
message(WARNING "sphinx-build not found, skipping rtd documentation")
endif() # SPHINX_BUILD_PROGRAM
else()
# Create a "doc" target for compatibility since "doc" is not otherwise added to
# the build when doxygen is not installed.
add_custom_target(doc false
COMMENT "Error: Can't generate doc since Doxygen not installed.")
endif() # DOXYGEN_FOUND
else()
# Create a "doc" target for compatibility since "doc" is not otherwise added to
# the build when doxygen is not installed.
add_custom_target(doc false
COMMENT "Error: Can't generate doc since Doxygen not installed.")
endif() # DOXYGEN_FOUND
endif() # JPEGXL_ENABLE_DOXYGEN
if(JPEGXL_ENABLE_MANPAGES)
find_program(ASCIIDOC a2x)
if(ASCIIDOC)
file(STRINGS "${ASCIIDOC}" ASCIIDOC_SHEBANG LIMIT_COUNT 1)
if(ASCIIDOC_SHEBANG MATCHES "/sh|/bash" OR MINGW)
set(ASCIIDOC_PY_FOUND ON)
# Run the program directly and set ASCIIDOC as empty.
set(ASCIIDOC_PY "${ASCIIDOC}")
set(ASCIIDOC "")
elseif(ASCIIDOC_SHEBANG MATCHES "python2")
find_package(Python2 COMPONENTS Interpreter)
set(ASCIIDOC_PY_FOUND "${Python2_Interpreter_FOUND}")
set(ASCIIDOC_PY Python2::Interpreter)
elseif(ASCIIDOC_SHEBANG MATCHES "python3")
find_package(Python3 COMPONENTS Interpreter)
set(ASCIIDOC_PY_FOUND "${Python3_Interpreter_FOUND}")
set(ASCIIDOC_PY Python3::Interpreter)
else()
find_package(Python COMPONENTS Interpreter QUIET)
if(NOT Python_Interpreter_FOUND)
find_program(ASCIIDOC_PY python)
if(ASCIIDOC_PY)
find_program(ASCIIDOC a2x)
if(ASCIIDOC)
file(STRINGS "${ASCIIDOC}" ASCIIDOC_SHEBANG LIMIT_COUNT 1)
if(ASCIIDOC_SHEBANG MATCHES "/sh|/bash" OR MINGW)
set(ASCIIDOC_PY_FOUND ON)
# Run the program directly and set ASCIIDOC as empty.
set(ASCIIDOC_PY "${ASCIIDOC}")
set(ASCIIDOC "")
elseif(ASCIIDOC_SHEBANG MATCHES "python2")
find_package(Python2 COMPONENTS Interpreter)
set(ASCIIDOC_PY_FOUND "${Python2_Interpreter_FOUND}")
set(ASCIIDOC_PY Python2::Interpreter)
elseif(ASCIIDOC_SHEBANG MATCHES "python3")
find_package(Python3 COMPONENTS Interpreter)
set(ASCIIDOC_PY_FOUND "${Python3_Interpreter_FOUND}")
set(ASCIIDOC_PY Python3::Interpreter)
else()
find_package(Python COMPONENTS Interpreter QUIET)
if(NOT Python_Interpreter_FOUND)
find_program(ASCIIDOC_PY python)
if(ASCIIDOC_PY)
set(ASCIIDOC_PY_FOUND ON)
endif()
else()
set(ASCIIDOC_PY_FOUND "${Python_Interpreter_FOUND}")
set(ASCIIDOC_PY Python::Interpreter)
endif()
endif()
else()
set(ASCIIDOC_PY_FOUND "${Python_Interpreter_FOUND}")
set(ASCIIDOC_PY Python::Interpreter)
endif()
endif()
if (ASCIIDOC_PY_FOUND)
set(MANPAGE_FILES "")
set(MANPAGES "")
foreach(PAGE IN ITEMS cjxl djxl)
# Invoking the Python interpreter ourselves instead of running the a2x binary
# directly is necessary on MSYS2, otherwise it is run through cmd.exe which
# does not recognize it.
add_custom_command(
OUTPUT "${PAGE}.1"
COMMAND "${ASCIIDOC_PY}"
ARGS ${ASCIIDOC}
--format manpage --destination-dir="${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt"
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt")
list(APPEND MANPAGE_FILES "${CMAKE_CURRENT_BINARY_DIR}/${PAGE}.1")
list(APPEND MANPAGES "${PAGE}.1")
endforeach()
add_custom_target(manpages ALL DEPENDS ${MANPAGES})
install(FILES ${MANPAGE_FILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
endif() # ASCIIDOC_PY_FOUND
else()
message(WARNING "asciidoc was not found, the man pages will not be installed.")
endif() # ASCIIDOC
if (ASCIIDOC_PY_FOUND)
set(MANPAGE_FILES "")
set(MANPAGES "")
foreach(PAGE IN ITEMS cjxl djxl)
# Invoking the Python interpreter ourselves instead of running the a2x binary
# directly is necessary on MSYS2, otherwise it is run through cmd.exe which
# does not recognize it.
add_custom_command(
OUTPUT "${PAGE}.1"
COMMAND "${ASCIIDOC_PY}"
ARGS ${ASCIIDOC}
--format manpage --destination-dir="${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt"
MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt")
list(APPEND MANPAGE_FILES "${CMAKE_CURRENT_BINARY_DIR}/${PAGE}.1")
list(APPEND MANPAGES "${PAGE}.1")
endforeach()
add_custom_target(manpages ALL DEPENDS ${MANPAGES})
install(FILES ${MANPAGE_FILES} DESTINATION ${CMAKE_INSTALL_MANDIR}/man1)
endif() # ASCIIDOC_PY_FOUND
else()
message(WARNING "asciidoc was not found, the man pages will not be installed.")
endif() # ASCIIDOC
endif() # JPEGXL_ENABLE_MANPAGES
# Example usage code.
if (JPEGXL_ENABLE_EXAMPLES)
include(examples/examples.cmake)
include(examples/examples.cmake)
endif ()
# Plugins for third-party software
if (JPEGXL_ENABLE_PLUGINS)
add_subdirectory(plugins)
add_subdirectory(plugins)
endif ()
# Binary tools

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

@ -40,8 +40,9 @@ On MacOS, you can use [Homebrew](https://brew.sh/): `brew install jpeg-xl`.
[![libjxl packaging status](https://repology.org/badge/vertical-allrepos/libjxl.svg?exclude_unsupported=1&columns=3&exclude_sources=modules,site&header=libjxl%20packaging%20status)](https://repology.org/project/libjxl/versions)
For Windows, binaries can be downloaded from the [releases page](https://github.com/libjxl/libjxl/releases/).
From the [releases page](https://github.com/libjxl/libjxl/releases/) the following can be downloaded:
- Windows binaries
- Debian and Ubuntu .deb packages
Of course you can also [build libjxl from sources](BUILDING.md).

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

@ -1,15 +1,17 @@
load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "new_git_repository")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository", "new_git_repository")
http_archive(
name = "bazel_skylib",
sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
urls = [
"https://mirror.bazel.build/github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
"https://github.com/bazelbuild/bazel-skylib/releases/download/1.3.0/bazel-skylib-1.3.0.tar.gz",
],
sha256 = "74d544d96f4a5bb630d465ca8bbcfe231e3594e5aae57e1edbf17a6eb3ca2506",
)
load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
bazel_skylib_workspace()
local_repository(
@ -24,13 +26,12 @@ local_repository(
new_local_repository(
name = "googletest",
path = "third_party/googletest",
build_file = "third_party/googletest/BUILD.bazel",
path = "third_party/googletest",
)
new_local_repository(
name = "skcms",
path = "third_party/skcms",
build_file_content = """
cc_library(
name = "skcms",
@ -43,13 +44,11 @@ cc_library(
visibility = ["//visibility:public"],
)
""",
path = "third_party/skcms",
)
# TODO(eustas): zconf.h might be deleted after CMake build; consider fetching
# pristine sources with hash taken from `deps.sh`.
new_local_repository(
new_git_repository(
name = "zlib",
path = "third_party/zlib",
build_file_content = """
cc_library(
name = "zlib",
@ -86,11 +85,12 @@ cc_library(
visibility = ["//visibility:public"],
)
""",
remote = "https://github.com/madler/zlib",
tag = "v1.2.13",
)
new_local_repository(
name = "png",
path = "third_party/libpng",
build_file_content = """
genrule(
name = "pnglibconf",
@ -130,12 +130,11 @@ cc_library(
deps = ["@zlib//:zlib"],
)
""",
path = "third_party/libpng",
)
new_git_repository(
name = "libjpeg_turbo",
remote = "https://github.com/libjpeg-turbo/libjpeg-turbo.git",
tag = "2.1.4",
build_file_content = """
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
SUBSTITUTIONS = {
@ -265,14 +264,13 @@ cc_library(
includes = ["."],
visibility = ["//visibility:public"],
)
"""
""",
remote = "https://github.com/libjpeg-turbo/libjpeg-turbo.git",
tag = "2.1.4",
)
http_archive(
name = "gif",
url = "https://netcologne.dl.sourceforge.net/project/giflib/giflib-5.2.1.tar.gz",
sha256 = "31da5562f44c5f15d63340a09a4fd62b48c45620cd302f77a6d9acf0077879bd",
strip_prefix = "giflib-5.2.1",
build_file_content = """
cc_library(
name = "gif",
@ -285,5 +283,460 @@ cc_library(
includes = ["."],
visibility = ["//visibility:public"],
)
"""
""",
sha256 = "31da5562f44c5f15d63340a09a4fd62b48c45620cd302f77a6d9acf0077879bd",
strip_prefix = "giflib-5.2.1",
url = "https://netcologne.dl.sourceforge.net/project/giflib/giflib-5.2.1.tar.gz",
)
new_git_repository(
name = "imath",
build_file_content = """
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
SUBSTITUTIONS = {
"@IMATH_INTERNAL_NAMESPACE@": "Imath_3_1",
"@IMATH_LIB_VERSION@": "3.1.4",
"@IMATH_NAMESPACE_CUSTOM@": "0",
"@IMATH_NAMESPACE@": "Imath",
"@IMATH_PACKAGE_NAME@": "Imath 3.1.4",
"@IMATH_VERSION_MAJOR@": "3",
"@IMATH_VERSION_MINOR@": "1",
"@IMATH_VERSION_PATCH@": "4",
"@IMATH_VERSION@": "3.1.4",
}
YES_DEFINES = [
"IMATH_HALF_USE_LOOKUP_TABLE", "IMATH_ENABLE_API_VISIBILITY",
]
NO_DEFINES = [
"IMATH_HAVE_LARGE_STACK",
]
ONE_DEFINES = [
"IMATH_USE_NOEXCEPT",
]
SUBSTITUTIONS.update({
"#cmakedefine " + key : "#define " + key for key in YES_DEFINES
})
SUBSTITUTIONS.update({
"#cmakedefine " + key : "// #define " + key for key in NO_DEFINES
})
SUBSTITUTIONS.update({
"#cmakedefine01 " + key : "#define " + key + " 1" for key in ONE_DEFINES
})
expand_template(
name = "expand_ImathConfig",
template = "config/ImathConfig.h.in",
out = "src/Imath/ImathConfig.h",
substitutions = SUBSTITUTIONS,
)
cc_library(
name = "Imath",
srcs = [
"src/Imath/ImathColorAlgo.cpp",
":src/Imath/ImathConfig.h",
"src/Imath/ImathFun.cpp",
"src/Imath/ImathMatrixAlgo.cpp",
"src/Imath/ImathRandom.cpp",
"src/Imath/half.cpp",
"src/Imath/toFloat.h",
],
hdrs = [
"src/Imath/ImathBox.h",
"src/Imath/ImathBoxAlgo.h",
"src/Imath/ImathColor.h",
"src/Imath/ImathColorAlgo.h",
"src/Imath/ImathEuler.h",
"src/Imath/ImathExport.h",
"src/Imath/ImathForward.h",
"src/Imath/ImathFrame.h",
"src/Imath/ImathFrustum.h",
"src/Imath/ImathFrustumTest.h",
"src/Imath/ImathFun.h",
"src/Imath/ImathGL.h",
"src/Imath/ImathGLU.h",
"src/Imath/ImathInt64.h",
"src/Imath/ImathInterval.h",
"src/Imath/ImathLine.h",
"src/Imath/ImathLineAlgo.h",
"src/Imath/ImathMath.h",
"src/Imath/ImathMatrix.h",
"src/Imath/ImathMatrixAlgo.h",
"src/Imath/ImathNamespace.h",
"src/Imath/ImathPlane.h",
"src/Imath/ImathPlatform.h",
"src/Imath/ImathQuat.h",
"src/Imath/ImathRandom.h",
"src/Imath/ImathRoots.h",
"src/Imath/ImathShear.h",
"src/Imath/ImathSphere.h",
"src/Imath/ImathTypeTraits.h",
"src/Imath/ImathVec.h",
"src/Imath/ImathVecAlgo.h",
"src/Imath/half.h",
"src/Imath/halfFunction.h",
"src/Imath/halfLimits.h",
],
includes = ["src/Imath"],
visibility = ["//visibility:public"],
)
""",
remote = "https://github.com/AcademySoftwareFoundation/imath",
tag = "v3.1.5",
)
new_git_repository(
name = "openexr",
build_file_content = """
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
SUBSTITUTIONS = {
"@IEX_INTERNAL_NAMESPACE@": "Iex_3_0",
"@IEX_NAMESPACE_CUSTOM@": "0",
"@IEX_NAMESPACE@": "Iex",
"@ILMTHREAD_INTERNAL_NAMESPACE@": "IlmThread_3_0",
"@ILMTHREAD_NAMESPACE_CUSTOM@": "0",
"@ILMTHREAD_NAMESPACE@": "IlmThread",
"@OPENEXR_IMF_NAMESPACE@": "Imf",
"@OPENEXR_INTERNAL_IMF_NAMESPACE@": "Imf_3_0",
"@OPENEXR_LIB_VERSION@": "3.0.4",
"@OPENEXR_NAMESPACE_CUSTOM@": "0",
"@OPENEXR_PACKAGE_NAME@": "OpenEXR 3.0.4",
"@OPENEXR_VERSION_EXTRA@": "",
"@OPENEXR_VERSION_MAJOR@": "3",
"@OPENEXR_VERSION_MINOR@": "0",
"@OPENEXR_VERSION_PATCH@": "4",
"@OPENEXR_VERSION@": "3.0.4",
}
YES_DEFINES = [
"OPENEXR_ENABLE_API_VISIBILITY", "OPENEXR_IMF_HAVE_COMPLETE_IOMANIP",
"OPENEXR_HAVE_LARGE_STACK",
]
NO_DEFINES = [
"HAVE_UCONTEXT_H", "IEX_HAVE_CONTROL_REGISTER_SUPPORT",
"IEX_HAVE_SIGCONTEXT_CONTROL_REGISTER_SUPPORT", "OPENEXR_IMF_HAVE_DARWIN",
"OPENEXR_IMF_HAVE_GCC_INLINE_ASM_AVX", "OPENEXR_IMF_HAVE_LINUX_PROCFS",
"OPENEXR_IMF_HAVE_SYSCONF_NPROCESSORS_ONLN",
]
ONE_DEFINES = [
"ILMTHREAD_THREADING_ENABLED",
]
ZERO_DEFINES = [
"ILMTHREAD_HAVE_POSIX_SEMAPHORES",
]
SUBSTITUTIONS.update({
"#cmakedefine " + key : "#define " + key for key in YES_DEFINES
})
SUBSTITUTIONS.update({
"#cmakedefine " + key : "// #define " + key for key in NO_DEFINES
})
SUBSTITUTIONS.update({
"#cmakedefine01 " + key : "#define " + key + " 1" for key in ONE_DEFINES
})
SUBSTITUTIONS.update({
"#cmakedefine01 " + key : "#define " + key + " 0" for key in ZERO_DEFINES
})
[
expand_template(
name = "expand_" + item,
template = "cmake/" + item + ".h.in",
out = "src/lib/Iex/" + item + ".h",
substitutions = SUBSTITUTIONS,
) for item in ["IexConfig", "IexConfigInternal"]
]
[
expand_template(
name = "expand_" + item,
template = "cmake/" + item + ".h.in",
out = "src/lib/IlmThread/" + item + ".h",
substitutions = SUBSTITUTIONS,
) for item in ["IlmThreadConfig"]
]
[
expand_template(
name = "expand_" + item,
template = "cmake/" + item + ".h.in",
out = "src/lib/OpenEXR/" + item + ".h",
substitutions = SUBSTITUTIONS,
) for item in ["OpenEXRConfig", "OpenEXRConfigInternal"]
]
cc_library(
name = "Iex",
srcs = [
"src/lib/Iex/IexBaseExc.cpp",
"src/lib/Iex/IexMathFloatExc.cpp",
"src/lib/Iex/IexMathFpu.cpp",
"src/lib/Iex/IexThrowErrnoExc.cpp",
],
hdrs = [
"src/lib/Iex/Iex.h",
"src/lib/Iex/IexBaseExc.h",
":src/lib/Iex/IexConfig.h",
":src/lib/Iex/IexConfigInternal.h",
"src/lib/Iex/IexErrnoExc.h",
"src/lib/Iex/IexExport.h",
"src/lib/Iex/IexForward.h",
"src/lib/Iex/IexMacros.h",
"src/lib/Iex/IexMathExc.h",
"src/lib/Iex/IexMathFloatExc.h",
"src/lib/Iex/IexMathFpu.h",
"src/lib/Iex/IexMathIeeeExc.h",
"src/lib/Iex/IexNamespace.h",
"src/lib/Iex/IexThrowErrnoExc.h",
":src/lib/OpenEXR/OpenEXRConfig.h",
],
includes = [
"src/lib/Iex",
"src/lib/OpenEXR",
],
)
cc_library(
name = "IlmThread",
srcs = [
"src/lib/IlmThread/IlmThread.cpp",
"src/lib/IlmThread/IlmThreadPool.cpp",
"src/lib/IlmThread/IlmThreadSemaphore.cpp",
"src/lib/IlmThread/IlmThreadSemaphoreOSX.cpp",
"src/lib/IlmThread/IlmThreadSemaphorePosix.cpp",
"src/lib/IlmThread/IlmThreadSemaphorePosixCompat.cpp",
"src/lib/IlmThread/IlmThreadSemaphoreWin32.cpp",
],
hdrs = [
"src/lib/IlmThread/IlmThread.h",
":src/lib/IlmThread/IlmThreadConfig.h",
"src/lib/IlmThread/IlmThreadExport.h",
"src/lib/IlmThread/IlmThreadForward.h",
"src/lib/IlmThread/IlmThreadMutex.h",
"src/lib/IlmThread/IlmThreadNamespace.h",
"src/lib/IlmThread/IlmThreadPool.h",
"src/lib/IlmThread/IlmThreadSemaphore.h",
],
includes = ["src/lib/IlmThread"],
deps = [":Iex"],
)
cc_library(
name = "OpenEXR",
srcs = [
"src/lib/OpenEXR/ImfAcesFile.cpp",
"src/lib/OpenEXR/ImfAttribute.cpp",
"src/lib/OpenEXR/ImfB44Compressor.cpp",
"src/lib/OpenEXR/ImfBoxAttribute.cpp",
"src/lib/OpenEXR/ImfCRgbaFile.cpp",
"src/lib/OpenEXR/ImfChannelList.cpp",
"src/lib/OpenEXR/ImfChannelListAttribute.cpp",
"src/lib/OpenEXR/ImfChromaticities.cpp",
"src/lib/OpenEXR/ImfChromaticitiesAttribute.cpp",
"src/lib/OpenEXR/ImfCompositeDeepScanLine.cpp",
"src/lib/OpenEXR/ImfCompressionAttribute.cpp",
"src/lib/OpenEXR/ImfCompressor.cpp",
"src/lib/OpenEXR/ImfConvert.cpp",
"src/lib/OpenEXR/ImfDeepCompositing.cpp",
"src/lib/OpenEXR/ImfDeepFrameBuffer.cpp",
"src/lib/OpenEXR/ImfDeepImageStateAttribute.cpp",
"src/lib/OpenEXR/ImfDeepScanLineInputFile.cpp",
"src/lib/OpenEXR/ImfDeepScanLineInputPart.cpp",
"src/lib/OpenEXR/ImfDeepScanLineOutputFile.cpp",
"src/lib/OpenEXR/ImfDeepScanLineOutputPart.cpp",
"src/lib/OpenEXR/ImfDeepTiledInputFile.cpp",
"src/lib/OpenEXR/ImfDeepTiledInputPart.cpp",
"src/lib/OpenEXR/ImfDeepTiledOutputFile.cpp",
"src/lib/OpenEXR/ImfDeepTiledOutputPart.cpp",
"src/lib/OpenEXR/ImfDoubleAttribute.cpp",
"src/lib/OpenEXR/ImfDwaCompressor.cpp",
"src/lib/OpenEXR/ImfEnvmap.cpp",
"src/lib/OpenEXR/ImfEnvmapAttribute.cpp",
"src/lib/OpenEXR/ImfFastHuf.cpp",
"src/lib/OpenEXR/ImfFloatAttribute.cpp",
"src/lib/OpenEXR/ImfFloatVectorAttribute.cpp",
"src/lib/OpenEXR/ImfFrameBuffer.cpp",
"src/lib/OpenEXR/ImfFramesPerSecond.cpp",
"src/lib/OpenEXR/ImfGenericInputFile.cpp",
"src/lib/OpenEXR/ImfGenericOutputFile.cpp",
"src/lib/OpenEXR/ImfHeader.cpp",
"src/lib/OpenEXR/ImfHuf.cpp",
"src/lib/OpenEXR/ImfIDManifest.cpp",
"src/lib/OpenEXR/ImfIDManifestAttribute.cpp",
"src/lib/OpenEXR/ImfIO.cpp",
"src/lib/OpenEXR/ImfInputFile.cpp",
"src/lib/OpenEXR/ImfInputPart.cpp",
"src/lib/OpenEXR/ImfInputPartData.cpp",
"src/lib/OpenEXR/ImfIntAttribute.cpp",
"src/lib/OpenEXR/ImfKeyCode.cpp",
"src/lib/OpenEXR/ImfKeyCodeAttribute.cpp",
"src/lib/OpenEXR/ImfLineOrderAttribute.cpp",
"src/lib/OpenEXR/ImfLut.cpp",
"src/lib/OpenEXR/ImfMatrixAttribute.cpp",
"src/lib/OpenEXR/ImfMisc.cpp",
"src/lib/OpenEXR/ImfMultiPartInputFile.cpp",
"src/lib/OpenEXR/ImfMultiPartOutputFile.cpp",
"src/lib/OpenEXR/ImfMultiView.cpp",
"src/lib/OpenEXR/ImfOpaqueAttribute.cpp",
"src/lib/OpenEXR/ImfOutputFile.cpp",
"src/lib/OpenEXR/ImfOutputPart.cpp",
"src/lib/OpenEXR/ImfOutputPartData.cpp",
"src/lib/OpenEXR/ImfPartType.cpp",
"src/lib/OpenEXR/ImfPizCompressor.cpp",
"src/lib/OpenEXR/ImfPreviewImage.cpp",
"src/lib/OpenEXR/ImfPreviewImageAttribute.cpp",
"src/lib/OpenEXR/ImfPxr24Compressor.cpp",
"src/lib/OpenEXR/ImfRational.cpp",
"src/lib/OpenEXR/ImfRationalAttribute.cpp",
"src/lib/OpenEXR/ImfRgbaFile.cpp",
"src/lib/OpenEXR/ImfRgbaYca.cpp",
"src/lib/OpenEXR/ImfRle.cpp",
"src/lib/OpenEXR/ImfRleCompressor.cpp",
"src/lib/OpenEXR/ImfScanLineInputFile.cpp",
"src/lib/OpenEXR/ImfStandardAttributes.cpp",
"src/lib/OpenEXR/ImfStdIO.cpp",
"src/lib/OpenEXR/ImfStringAttribute.cpp",
"src/lib/OpenEXR/ImfStringVectorAttribute.cpp",
"src/lib/OpenEXR/ImfSystemSpecific.cpp",
"src/lib/OpenEXR/ImfTestFile.cpp",
"src/lib/OpenEXR/ImfThreading.cpp",
"src/lib/OpenEXR/ImfTileDescriptionAttribute.cpp",
"src/lib/OpenEXR/ImfTileOffsets.cpp",
"src/lib/OpenEXR/ImfTiledInputFile.cpp",
"src/lib/OpenEXR/ImfTiledInputPart.cpp",
"src/lib/OpenEXR/ImfTiledMisc.cpp",
"src/lib/OpenEXR/ImfTiledOutputFile.cpp",
"src/lib/OpenEXR/ImfTiledOutputPart.cpp",
"src/lib/OpenEXR/ImfTiledRgbaFile.cpp",
"src/lib/OpenEXR/ImfTimeCode.cpp",
"src/lib/OpenEXR/ImfTimeCodeAttribute.cpp",
"src/lib/OpenEXR/ImfVecAttribute.cpp",
"src/lib/OpenEXR/ImfVersion.cpp",
"src/lib/OpenEXR/ImfWav.cpp",
"src/lib/OpenEXR/ImfZip.cpp",
"src/lib/OpenEXR/ImfZipCompressor.cpp",
"src/lib/OpenEXR/b44ExpLogTable.h",
"src/lib/OpenEXR/dwaLookups.h",
],
hdrs = [
":src/lib/Iex/IexConfig.h",
":src/lib/Iex/IexConfigInternal.h",
":src/lib/IlmThread/IlmThreadConfig.h",
"src/lib/OpenEXR/ImfAcesFile.h",
"src/lib/OpenEXR/ImfArray.h",
"src/lib/OpenEXR/ImfAttribute.h",
"src/lib/OpenEXR/ImfAutoArray.h",
"src/lib/OpenEXR/ImfB44Compressor.h",
"src/lib/OpenEXR/ImfBoxAttribute.h",
"src/lib/OpenEXR/ImfCRgbaFile.h",
"src/lib/OpenEXR/ImfChannelList.h",
"src/lib/OpenEXR/ImfChannelListAttribute.h",
"src/lib/OpenEXR/ImfCheckedArithmetic.h",
"src/lib/OpenEXR/ImfChromaticities.h",
"src/lib/OpenEXR/ImfChromaticitiesAttribute.h",
"src/lib/OpenEXR/ImfCompositeDeepScanLine.h",
"src/lib/OpenEXR/ImfCompression.h",
"src/lib/OpenEXR/ImfCompressionAttribute.h",
"src/lib/OpenEXR/ImfCompressor.h",
"src/lib/OpenEXR/ImfConvert.h",
"src/lib/OpenEXR/ImfDeepCompositing.h",
"src/lib/OpenEXR/ImfDeepFrameBuffer.h",
"src/lib/OpenEXR/ImfDeepImageState.h",
"src/lib/OpenEXR/ImfDeepImageStateAttribute.h",
"src/lib/OpenEXR/ImfDeepScanLineInputFile.h",
"src/lib/OpenEXR/ImfDeepScanLineInputPart.h",
"src/lib/OpenEXR/ImfDeepScanLineOutputFile.h",
"src/lib/OpenEXR/ImfDeepScanLineOutputPart.h",
"src/lib/OpenEXR/ImfDeepTiledInputFile.h",
"src/lib/OpenEXR/ImfDeepTiledInputPart.h",
"src/lib/OpenEXR/ImfDeepTiledOutputFile.h",
"src/lib/OpenEXR/ImfDeepTiledOutputPart.h",
"src/lib/OpenEXR/ImfDoubleAttribute.h",
"src/lib/OpenEXR/ImfDwaCompressor.h",
"src/lib/OpenEXR/ImfDwaCompressorSimd.h",
"src/lib/OpenEXR/ImfEnvmap.h",
"src/lib/OpenEXR/ImfEnvmapAttribute.h",
"src/lib/OpenEXR/ImfExport.h",
"src/lib/OpenEXR/ImfFastHuf.h",
"src/lib/OpenEXR/ImfFloatAttribute.h",
"src/lib/OpenEXR/ImfFloatVectorAttribute.h",
"src/lib/OpenEXR/ImfForward.h",
"src/lib/OpenEXR/ImfFrameBuffer.h",
"src/lib/OpenEXR/ImfFramesPerSecond.h",
"src/lib/OpenEXR/ImfGenericInputFile.h",
"src/lib/OpenEXR/ImfGenericOutputFile.h",
"src/lib/OpenEXR/ImfHeader.h",
"src/lib/OpenEXR/ImfHuf.h",
"src/lib/OpenEXR/ImfIDManifest.h",
"src/lib/OpenEXR/ImfIDManifestAttribute.h",
"src/lib/OpenEXR/ImfIO.h",
"src/lib/OpenEXR/ImfInputFile.h",
"src/lib/OpenEXR/ImfInputPart.h",
"src/lib/OpenEXR/ImfInputPartData.h",
"src/lib/OpenEXR/ImfInputStreamMutex.h",
"src/lib/OpenEXR/ImfInt64.h",
"src/lib/OpenEXR/ImfIntAttribute.h",
"src/lib/OpenEXR/ImfKeyCode.h",
"src/lib/OpenEXR/ImfKeyCodeAttribute.h",
"src/lib/OpenEXR/ImfLineOrder.h",
"src/lib/OpenEXR/ImfLineOrderAttribute.h",
"src/lib/OpenEXR/ImfLut.h",
"src/lib/OpenEXR/ImfMatrixAttribute.h",
"src/lib/OpenEXR/ImfMisc.h",
"src/lib/OpenEXR/ImfMultiPartInputFile.h",
"src/lib/OpenEXR/ImfMultiPartOutputFile.h",
"src/lib/OpenEXR/ImfMultiView.h",
"src/lib/OpenEXR/ImfName.h",
"src/lib/OpenEXR/ImfNamespace.h",
"src/lib/OpenEXR/ImfOpaqueAttribute.h",
"src/lib/OpenEXR/ImfOptimizedPixelReading.h",
"src/lib/OpenEXR/ImfOutputFile.h",
"src/lib/OpenEXR/ImfOutputPart.h",
"src/lib/OpenEXR/ImfOutputPartData.h",
"src/lib/OpenEXR/ImfOutputStreamMutex.h",
"src/lib/OpenEXR/ImfPartHelper.h",
"src/lib/OpenEXR/ImfPartType.h",
"src/lib/OpenEXR/ImfPixelType.h",
"src/lib/OpenEXR/ImfPizCompressor.h",
"src/lib/OpenEXR/ImfPreviewImage.h",
"src/lib/OpenEXR/ImfPreviewImageAttribute.h",
"src/lib/OpenEXR/ImfPxr24Compressor.h",
"src/lib/OpenEXR/ImfRational.h",
"src/lib/OpenEXR/ImfRationalAttribute.h",
"src/lib/OpenEXR/ImfRgba.h",
"src/lib/OpenEXR/ImfRgbaFile.h",
"src/lib/OpenEXR/ImfRgbaYca.h",
"src/lib/OpenEXR/ImfRle.h",
"src/lib/OpenEXR/ImfRleCompressor.h",
"src/lib/OpenEXR/ImfScanLineInputFile.h",
"src/lib/OpenEXR/ImfSimd.h",
"src/lib/OpenEXR/ImfStandardAttributes.h",
"src/lib/OpenEXR/ImfStdIO.h",
"src/lib/OpenEXR/ImfStringAttribute.h",
"src/lib/OpenEXR/ImfStringVectorAttribute.h",
"src/lib/OpenEXR/ImfSystemSpecific.h",
"src/lib/OpenEXR/ImfTestFile.h",
"src/lib/OpenEXR/ImfThreading.h",
"src/lib/OpenEXR/ImfTileDescription.h",
"src/lib/OpenEXR/ImfTileDescriptionAttribute.h",
"src/lib/OpenEXR/ImfTileOffsets.h",
"src/lib/OpenEXR/ImfTiledInputFile.h",
"src/lib/OpenEXR/ImfTiledInputPart.h",
"src/lib/OpenEXR/ImfTiledMisc.h",
"src/lib/OpenEXR/ImfTiledOutputFile.h",
"src/lib/OpenEXR/ImfTiledOutputPart.h",
"src/lib/OpenEXR/ImfTiledRgbaFile.h",
"src/lib/OpenEXR/ImfTimeCode.h",
"src/lib/OpenEXR/ImfTimeCodeAttribute.h",
"src/lib/OpenEXR/ImfVecAttribute.h",
"src/lib/OpenEXR/ImfVersion.h",
"src/lib/OpenEXR/ImfWav.h",
"src/lib/OpenEXR/ImfXdr.h",
"src/lib/OpenEXR/ImfZip.h",
"src/lib/OpenEXR/ImfZipCompressor.h",
":src/lib/OpenEXR/OpenEXRConfig.h",
":src/lib/OpenEXR/OpenEXRConfigInternal.h",
],
includes = ["src/lib/OpenEXR"],
deps = [
":IlmThread",
"@imath//:Imath",
"@zlib//:zlib",
],
visibility = ["//visibility:public"],
)
""",
remote = "https://github.com/AcademySoftwareFoundation/openexr",
tag = "v3.1.5",
)

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

@ -21,6 +21,7 @@ CMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH:-}
CMAKE_C_COMPILER_LAUNCHER=${CMAKE_C_COMPILER_LAUNCHER:-}
CMAKE_CXX_COMPILER_LAUNCHER=${CMAKE_CXX_COMPILER_LAUNCHER:-}
CMAKE_MAKE_PROGRAM=${CMAKE_MAKE_PROGRAM:-}
SKIP_BUILD="${SKIP_BUILD:-0}"
SKIP_TEST="${SKIP_TEST:-0}"
TARGETS="${TARGETS:-all doc}"
TEST_SELECTOR="${TEST_SELECTOR:-}"
@ -459,6 +460,9 @@ cmake_configure() {
}
cmake_build_and_test() {
if [[ "${SKIP_BUILD}" -eq "1" ]]; then
return 0
fi
# gtest_discover_tests() runs the test binaries to discover the list of tests
# at build time, which fails under qemu.
ASAN_OPTIONS=detect_leaks=0 cmake --build "${BUILD_DIR}" -- $TARGETS
@ -1192,11 +1196,11 @@ cmd_fuzz() {
)
}
# Runs the linter (clang-format) on the pending CLs.
# Runs the linters (clang-format, build_cleaner, buildirier) on the pending CLs.
cmd_lint() {
merge_request_commits
{ set +x; } 2>/dev/null
local versions=(${1:-6.0 7 8 9 10 11})
local versions=(${1:-16 15 14 13 12 11 10 9 8 7 6.0})
local clang_format_bins=("${versions[@]/#/clang-format-}" clang-format)
local tmpdir=$(mktemp -d)
CLEANUP_FILES+=("${tmpdir}")
@ -1210,6 +1214,22 @@ cmd_lint() {
echo "Run \`tools/scripts/build_cleaner.py --update\` to apply them" >&2
fi
# It is ok, if buildifier is not installed.
if which buildifier >/dev/null; then
local buildifier_patch="${tmpdir}/buildifier.patch"
local bazel_files=`git -C ${MYDIR} ls-files | grep -E "/BUILD$|WORKSPACE|.bzl$"`
set -x
buildifier -d ${bazel_files} >"${buildifier_patch}"|| true
{ set +x; } 2>/dev/null
if [ -s "${buildifier_patch}" ]; then
ret=1
echo 'buildifier have found some problems in Bazel build files:' >&2
"${COLORDIFF_BIN}" <"${buildifier_patch}"
echo 'To fix them run (from the base directory):' >&2
echo ' buildifier `git ls-files | grep -E "/BUILD$|WORKSPACE|.bzl$"`' >&2
fi
fi
local installed=()
local clang_patch
local clang_format
@ -1495,6 +1515,7 @@ You can pass some optional environment variables as well:
- FUZZER_MAX_TIME: "fuzz" command fuzzer running timeout in seconds.
- LINT_OUTPUT: Path to the output patch from the "lint" command.
- SKIP_CPUSET=1: Skip modifying the cpuset in the arm_benchmark.
- SKIP_BUILD=1: Skip the build stage, cmake configure only.
- SKIP_TEST=1: Skip the test stage.
- STORE_IMAGES=0: Makes the benchmark discard the computed images.
- TEST_STACK_LIMIT: Stack size limit (ulimit -s) during tests, in KiB.

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

@ -8,11 +8,14 @@ include /usr/share/dpkg/pkg-info.mk
override_dh_auto_configure:
# TODO(deymo): Remove the DCMAKE_BUILD_TYPE once builds without NDEBUG
# are as useful as Release builds.
# TODO(szabadka) Re-enable jpegli after tests are fixed on Ubuntu 20.04,
# and debian:buster
dh_auto_configure -- \
-DJPEGXL_VERSION=$(DEB_VERSION) \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DJPEGXL_FORCE_SYSTEM_GTEST=ON \
-DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
-DJPEGXL_FORCE_SYSTEM_HWY=ON \
-DJPEGXL_ENABLE_JPEGLI=OFF \
-DJPEGXL_ENABLE_JPEGLI_LIBJPEG=OFF \
-DJPEGXL_ENABLE_PLUGINS=ON
-DJPEGXL_ENABLE_PLUGINS=ON

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

@ -14,7 +14,7 @@ MYDIR=$(dirname $(realpath "$0"))
# Git revisions we use for the given submodules. Update these whenever you
# update a git submodule.
THIRD_PARTY_BROTLI="36533a866ed1ca4b75cf049f4521e4ec5fe24727"
THIRD_PARTY_HIGHWAY="58746ca5b9f9444a2a3549704602ecc6239f8f41"
THIRD_PARTY_HIGHWAY="46e365d6770f5d7a4240d8ac9d8e928a520478ea"
THIRD_PARTY_SKCMS="b25b07b4b07990811de121c0356155b2ba0f4318"
THIRD_PARTY_SJPEG="868ab558fad70fcbe8863ba4e85179eeb81cc840"
THIRD_PARTY_ZLIB="cacf7f1d4e3d44d871b605da3b647f07d718623f"

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

@ -31,7 +31,6 @@ RUN set -ex; \
libgoogle-perftools4 \
libopenexr22 \
libpng16-16 \
libqt5x11extras5 \
libsdl2-2.0-0 \
parallel; \
rm -rf /var/lib/apt/lists/*;

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

@ -214,10 +214,8 @@ install_pkgs() {
libgif7:"${ubarch}"
libjpeg-dev:"${ubarch}"
libpng-dev:"${ubarch}"
libqt5x11extras5-dev:"${ubarch}"
libstdc++-8-dev:"${ubarch}"
qtbase5-dev:"${ubarch}"
# For OpenEXR:
libilmbase12:"${ubarch}"

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

@ -7,6 +7,8 @@
// available at once). The example outputs the pixels and color information to a
// floating point image and an ICC profile on disk.
#include <jxl/decode.h>
#include <jxl/decode_cxx.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
@ -14,9 +16,6 @@
#include <vector>
#include "jxl/decode.h"
#include "jxl/decode_cxx.h"
bool DecodeJpegXlExif(const uint8_t* jxl, size_t size,
std::vector<uint8_t>* exif) {
auto dec = JxlDecoderMake(nullptr);

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

@ -7,7 +7,15 @@
// available at once). The example outputs the pixels and color information to a
// floating point image and an ICC profile on disk.
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#include <jxl/decode.h>
#include <jxl/decode_cxx.h>
#include <jxl/resizable_parallel_runner.h>
#include <jxl/resizable_parallel_runner_cxx.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
@ -15,11 +23,6 @@
#include <vector>
#include "jxl/decode.h"
#include "jxl/decode_cxx.h"
#include "jxl/resizable_parallel_runner.h"
#include "jxl/resizable_parallel_runner_cxx.h"
/** Decodes JPEG XL image to floating point pixels and ICC Profile. Pixel are
* stored as floating point, as interleaved RGBA (4 floating point values per
* pixel), line per line from top to bottom. Pixel values have nominal range

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

@ -6,7 +6,15 @@
// This C++ example decodes a JPEG XL image progressively (input bytes are
// passed in chunks). The example outputs the intermediate steps to PAM files.
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <inttypes.h>
#include <jxl/decode.h>
#include <jxl/decode_cxx.h>
#include <jxl/resizable_parallel_runner.h>
#include <jxl/resizable_parallel_runner_cxx.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
@ -14,11 +22,6 @@
#include <vector>
#include "jxl/decode.h"
#include "jxl/decode_cxx.h"
#include "jxl/resizable_parallel_runner.h"
#include "jxl/resizable_parallel_runner_cxx.h"
bool WritePAM(const char* filename, const uint8_t* buffer, size_t w, size_t h) {
FILE* fp = fopen(filename, "wb");
if (!fp) {
@ -30,7 +33,11 @@ bool WritePAM(const char* filename, const uint8_t* buffer, size_t w, size_t h) {
"\nDEPTH 4\nMAXVAL 255\nTUPLTYPE "
"RGB_ALPHA\nENDHDR\n",
static_cast<uint64_t>(w), static_cast<uint64_t>(h));
fwrite(buffer, 1, w * h * 4, fp);
size_t num_bytes = w * h * 4;
if (fwrite(buffer, 1, num_bytes, fp) != num_bytes) {
fclose(fp);
return false;
};
if (fclose(fp) != 0) {
return false;
}

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

@ -6,6 +6,10 @@
// This example encodes a file containing a floating point image to another
// file containing JPEG XL image with a single frame.
#include <jxl/encode.h>
#include <jxl/encode_cxx.h>
#include <jxl/thread_parallel_runner.h>
#include <jxl/thread_parallel_runner_cxx.h>
#include <limits.h>
#include <string.h>
@ -13,11 +17,6 @@
#include <string>
#include <vector>
#include "jxl/encode.h"
#include "jxl/encode_cxx.h"
#include "jxl/thread_parallel_runner.h"
#include "jxl/thread_parallel_runner_cxx.h"
/**
* Reads from .pfm file (Portable FloatMap)
*
@ -229,6 +228,7 @@ bool WriteFile(const std::vector<uint8_t>& bytes, const char* filename) {
if (fwrite(bytes.data(), sizeof(uint8_t), bytes.size(), file) !=
bytes.size()) {
fprintf(stderr, "Could not write bytes to %s\n", filename);
fclose(file);
return false;
}
if (fclose(file) != 0) {

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

@ -1,58 +1,76 @@
package(default_visibility = ['//:__subpackages__'])
# 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.
load('jxl_lists.bzl',
'libjxl_codec_apng_sources',
'libjxl_codec_gif_sources',
'libjxl_codec_jpegli_sources',
'libjxl_codec_jpg_sources',
'libjxl_codec_jxl_sources',
'libjxl_codec_npy_sources',
'libjxl_codec_pgx_sources',
'libjxl_codec_pnm_sources',
'libjxl_dec_box_sources',
'libjxl_dec_jpeg_sources',
'libjxl_dec_sources',
'libjxl_enc_sources',
'libjxl_extras_for_tools_sources',
'libjxl_extras_sources',
'libjxl_jpegli_sources',
'libjxl_jpegli_testlib_files',
'libjxl_jpegli_tests',
'libjxl_major_version',
'libjxl_minor_version',
'libjxl_patch_version',
'libjxl_public_headers',
'libjxl_testlib_files',
'libjxl_tests',
'libjxl_threads_public_headers',
'libjxl_threads_sources',
#'libjxl_codec_exr_sources',
# Load sources/headers/tests lists.
load(
"jxl_lists.bzl",
"libjxl_base_sources",
"libjxl_codec_apng_sources",
"libjxl_codec_exr_sources",
"libjxl_codec_gif_sources",
"libjxl_codec_jpegli_sources",
"libjxl_codec_jpg_sources",
"libjxl_codec_jxl_sources",
"libjxl_codec_npy_sources",
"libjxl_codec_pgx_sources",
"libjxl_codec_pnm_sources",
"libjxl_dec_box_sources",
"libjxl_dec_jpeg_sources",
"libjxl_dec_sources",
"libjxl_enc_sources",
"libjxl_extras_for_tools_sources",
"libjxl_extras_sources",
#'libjxl_gbench_sources',
#'libjxl_profiler_sources',
"libjxl_jpegli_sources",
"libjxl_jpegli_testlib_files",
"libjxl_jpegli_tests",
"libjxl_major_version",
"libjxl_minor_version",
"libjxl_patch_version",
"libjxl_public_headers",
"libjxl_testlib_files",
"libjxl_tests",
"libjxl_threads_public_headers",
"libjxl_threads_sources",
)
load(
"jxl_vars.bzl",
"libjxl_deps_brotli",
"libjxl_deps_exr",
"libjxl_deps_gif",
"libjxl_deps_gtest",
"libjxl_deps_hwy",
"libjxl_deps_hwy_nanobenchmark",
"libjxl_deps_hwy_test_util",
"libjxl_deps_jpeg",
"libjxl_deps_jxl_box",
"libjxl_deps_png",
"libjxl_deps_runfiles",
"libjxl_deps_skcms",
"libjxl_deps_testdata",
"libjxl_root_package",
"libjxl_test_shards",
"libjxl_test_timeouts",
)
load("@bazel_skylib//rules:expand_template.bzl", "expand_template")
DEFAULT_VISIBILITY = ["//:__subpackages__"]
DEFAULT_COMPATIBILITY = []
INCLUDES_DIR = "include"
package(
default_visibility = ["//:__subpackages__"],
)
load('jxl_vars.bzl',
'libjxl_deps_brotli',
'libjxl_deps_gif',
'libjxl_deps_gtest',
'libjxl_deps_hwy_nanobenchmark',
'libjxl_deps_hwy_test_util',
'libjxl_deps_hwy',
'libjxl_deps_jpeg',
'libjxl_deps_jxl_box',
'libjxl_deps_png',
'libjxl_deps_runfiles',
'libjxl_deps_skcms',
'libjxl_deps_testdata',
'libjxl_root_package',
'libjxl_test_shards',
'libjxl_test_timeouts',
)
licenses(["notice"])
load('@bazel_skylib//rules:expand_template.bzl', 'expand_template')
exports_files(["LICENSE"])
EXPORT_TEMPLATE = '''
EXPORT_TEMPLATE = """
#ifndef @_EXPORT_H
#define @_EXPORT_H
@ -64,132 +82,175 @@ EXPORT_TEMPLATE = '''
#endif
#endif
'''
"""
JXL_EXPORT_H = INCLUDES_DIR + "/jxl/jxl_export.h"
JXL_EXPORT_H = 'include/jxl/jxl_export.h'
genrule(
name = 'create_jxl_export',
name = "create_jxl_export",
outs = [JXL_EXPORT_H],
cmd = 'echo \'' + EXPORT_TEMPLATE.replace('@', 'JXL') + '\' > $@',
cmd = "echo '" + EXPORT_TEMPLATE.replace("@", "JXL") + "' > $@",
compatible_with = DEFAULT_COMPATIBILITY,
)
JXL_THREADS_EXPORT_H = 'include/jxl/jxl_threads_export.h'
JXL_THREADS_EXPORT_H = INCLUDES_DIR + "/jxl/jxl_threads_export.h"
genrule(
name = 'create_jxl_threads_export',
name = "create_jxl_threads_export",
outs = [JXL_THREADS_EXPORT_H],
cmd = 'echo \'' + EXPORT_TEMPLATE.replace('@', 'JXL_THREADS') + '\' > $@',
cmd = "echo '" + EXPORT_TEMPLATE.replace("@", "JXL_THREADS") + "' > $@",
compatible_with = DEFAULT_COMPATIBILITY,
)
JXL_VERSION_H = 'include/jxl/version.h'
JXL_VERSION_H = INCLUDES_DIR + "/jxl/version.h"
# TODO(eustas): extract version from CMake file
expand_template(
name = 'expand_jxl_version',
template = 'jxl/version.h.in',
name = "expand_jxl_version",
out = JXL_VERSION_H,
compatible_with = DEFAULT_COMPATIBILITY,
substitutions = {
'@JPEGXL_MAJOR_VERSION@': str(libjxl_major_version),
'@JPEGXL_MINOR_VERSION@': str(libjxl_minor_version),
'@JPEGXL_PATCH_VERSION@': str(libjxl_patch_version),
"@JPEGXL_MAJOR_VERSION@": str(libjxl_major_version),
"@JPEGXL_MINOR_VERSION@": str(libjxl_minor_version),
"@JPEGXL_PATCH_VERSION@": str(libjxl_patch_version),
},
template = "jxl/version.h.in",
)
cc_library(
name = 'jpegxl_version',
name = "jxl_version",
hdrs = [JXL_VERSION_H],
strip_include_prefix = 'include',
compatible_with = DEFAULT_COMPATIBILITY,
strip_include_prefix = INCLUDES_DIR,
)
cc_library(
name = 'jpegxl_private',
srcs = libjxl_dec_sources + libjxl_dec_box_sources + libjxl_dec_jpeg_sources + libjxl_enc_sources,
name = "includes",
hdrs = libjxl_public_headers + [JXL_EXPORT_H],
strip_include_prefix = 'include',
defines = ['JPEGXL_ENABLE_SKCMS=1'],
deps = [
':jpegxl_version',
] + libjxl_deps_brotli + libjxl_deps_hwy + libjxl_deps_skcms
compatible_with = DEFAULT_COMPATIBILITY,
strip_include_prefix = INCLUDES_DIR,
deps = [":jxl_version"],
)
cc_library(
name = 'jpegxl_threads',
name = "base",
srcs = [path for path in libjxl_base_sources if path.endswith(".cc")],
hdrs = [path for path in libjxl_base_sources if path.endswith(".h")],
compatible_with = DEFAULT_COMPATIBILITY,
deps = [
":includes",
] + libjxl_deps_hwy,
)
cc_library(
name = "jpegxl",
srcs = libjxl_dec_sources + libjxl_dec_box_sources + libjxl_dec_jpeg_sources + libjxl_enc_sources,
compatible_with = DEFAULT_COMPATIBILITY,
defines = ["JPEGXL_ENABLE_SKCMS=1"],
deps = [
":base",
":includes",
] + libjxl_deps_brotli + libjxl_deps_hwy + libjxl_deps_skcms,
)
cc_library(
name = "jpegxl_private",
hdrs = [
path
for path in libjxl_dec_sources + libjxl_dec_box_sources + libjxl_dec_jpeg_sources + libjxl_enc_sources
if path.endswith(".h") and not path.endswith("-inl.h")
],
compatible_with = DEFAULT_COMPATIBILITY,
deps = [":jpegxl"],
)
cc_library(
name = "jpegxl_threads",
srcs = libjxl_threads_sources,
hdrs = libjxl_threads_public_headers + [JXL_THREADS_EXPORT_H],
strip_include_prefix = 'include',
# TODO(eustas): used only for [memory_manager.h, profiler.h, parallel_runner.h]
deps = [':jpegxl_private'],
compatible_with = DEFAULT_COMPATIBILITY,
strip_include_prefix = INCLUDES_DIR,
deps = [
":base",
":includes",
],
)
CODEC_FILES = libjxl_codec_apng_sources + libjxl_codec_gif_sources + libjxl_codec_jpegli_sources + libjxl_codec_jpg_sources + libjxl_codec_jxl_sources + libjxl_codec_npy_sources + libjxl_codec_pgx_sources + libjxl_codec_pnm_sources
CODEC_SRCS = [path for path in CODEC_FILES if path.endswith('.cc')]
CODEC_HDRS = [path for path in CODEC_FILES if path.endswith('.h')]
CODEC_FILES = libjxl_codec_apng_sources + libjxl_codec_exr_sources + libjxl_codec_gif_sources + libjxl_codec_jpegli_sources + libjxl_codec_jpg_sources + libjxl_codec_jxl_sources + libjxl_codec_npy_sources + libjxl_codec_pgx_sources + libjxl_codec_pnm_sources
CODEC_SRCS = [path for path in CODEC_FILES if path.endswith(".cc")]
CODEC_HDRS = [path for path in CODEC_FILES if path.endswith(".h")]
cc_library(
name = 'jpegli',
name = "jpegli",
srcs = libjxl_jpegli_sources,
hdrs = [
'jpegli/common_internal.h', # TODO(eustas): should not be here
"jpegli/common_internal.h", # TODO(eustas): should not be here
],
compatible_with = DEFAULT_COMPATIBILITY,
deps = [
':jpegxl_private',
":jpegxl_private",
] + libjxl_deps_hwy + libjxl_deps_jpeg,
)
# TODO(eustas): build codecs separately?
cc_library(
name = 'jpegxl_extras',
name = "jpegxl_extras",
srcs = libjxl_extras_sources + libjxl_extras_for_tools_sources + CODEC_SRCS,
hdrs = CODEC_HDRS,
compatible_with = DEFAULT_COMPATIBILITY,
defines = [
'JPEGXL_ENABLE_APNG=1',
'JPEGXL_ENABLE_EXR=0', # TODO(eustas): add
'JPEGXL_ENABLE_GIF=1',
'JPEGXL_ENABLE_JPEG=1',
'JPEGXL_ENABLE_JPEGLI=1',
"JPEGXL_ENABLE_APNG=1",
"JPEGXL_ENABLE_EXR=1",
"JPEGXL_ENABLE_GIF=1",
"JPEGXL_ENABLE_JPEG=1",
"JPEGXL_ENABLE_JPEGLI=1",
],
deps = [
':jpegli',
':jpegxl_private',
':jpegxl_threads',
':jpegxl_version'
] + libjxl_deps_gif + libjxl_deps_jpeg + libjxl_deps_png
":jpegli",
":jpegxl_private",
":jpegxl_threads",
":jxl_version",
] + libjxl_deps_exr + libjxl_deps_gif + libjxl_deps_jpeg + libjxl_deps_png,
)
TESTLIB_FILES = libjxl_testlib_files + libjxl_jpegli_testlib_files
cc_library(
name = 'test_utils',
name = "test_utils",
testonly = 1,
srcs = [src for src in TESTLIB_FILES if not src.endswith('.h')],
hdrs = [src for src in TESTLIB_FILES if src.endswith('.h')],
srcs = [path for path in TESTLIB_FILES if not path.endswith(".h")],
hdrs = [path for path in TESTLIB_FILES if path.endswith(".h")],
compatible_with = DEFAULT_COMPATIBILITY,
defines = [
'JPEGXL_ROOT_PACKAGE=\'"' + libjxl_root_package + '"\'',
],
deps = [
':jpegxl_extras',
':jpegxl_private',
':jpegli',
] + libjxl_deps_runfiles
":jpegli",
":jpegxl_extras",
":jpegxl_private",
] + libjxl_deps_runfiles,
)
TESTS = [src.partition('.')[0] for src in libjxl_tests + libjxl_jpegli_tests]
TESTS = [path.partition(".")[0] for path in libjxl_tests + libjxl_jpegli_tests]
[
cc_test(
name = test,
timeout = libjxl_test_timeouts.get(test, "moderate"),
srcs = [
test + '.cc',
'jxl/testing.h',
'jpegli/testing.h',
test + ".cc",
"jpegli/testing.h",
"jxl/testing.h",
],
data = ['//:testdata'],
timeout = libjxl_test_timeouts.get(test, 'moderate'),
data = ["//:testdata"],
shard_count = libjxl_test_shards.get(test, 1),
deps = [
':jpegxl_extras',
':jpegxl_private',
':jpegxl_threads', # Some tests use the public threads API.
':test_utils',
] + libjxl_deps_gtest + libjxl_deps_hwy_test_util + libjxl_deps_hwy_nanobenchmark + libjxl_deps_jxl_box
) for test in TESTS
":jpegxl_extras",
":jpegxl_private",
":jpegxl_threads",
":test_utils",
] + libjxl_deps_gtest + libjxl_deps_hwy_test_util + libjxl_deps_hwy_nanobenchmark + libjxl_deps_jxl_box,
)
for test in TESTS
]

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

@ -17,119 +17,111 @@ set(JPEGXL_LIBRARY_VERSION
# versioning allows 0.y.z to have incompatible changes in minor versions.
set(JPEGXL_SO_MINOR_VERSION 9)
if (JPEGXL_MAJOR_VERSION EQUAL 0)
set(JPEGXL_LIBRARY_SOVERSION
"${JPEGXL_MAJOR_VERSION}.${JPEGXL_SO_MINOR_VERSION}")
set(JPEGXL_LIBRARY_SOVERSION
"${JPEGXL_MAJOR_VERSION}.${JPEGXL_SO_MINOR_VERSION}")
else()
set(JPEGXL_LIBRARY_SOVERSION "${JPEGXL_MAJOR_VERSION}")
set(JPEGXL_LIBRARY_SOVERSION "${JPEGXL_MAJOR_VERSION}")
endif()
# List of warning and feature flags for our library and tests.
if (MSVC)
set(JPEGXL_INTERNAL_FLAGS
# TODO(janwas): add flags
)
set(JPEGXL_INTERNAL_FLAGS
# TODO(janwas): add flags
)
else ()
set(JPEGXL_INTERNAL_FLAGS
# F_FLAGS
-fmerge-all-constants
-fno-builtin-fwrite
-fno-builtin-fread
set(JPEGXL_INTERNAL_FLAGS
# F_FLAGS
-fmerge-all-constants
-fno-builtin-fwrite
-fno-builtin-fread
# WARN_FLAGS
-Wall
-Wextra
-Wc++11-compat
-Warray-bounds
-Wformat-security
-Wimplicit-fallthrough
-Wno-register # Needed by public headers in lcms
-Wno-unused-function
-Wno-unused-parameter
-Wnon-virtual-dtor
-Woverloaded-virtual
-Wvla
)
# Warning flags supported by clang.
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND JPEGXL_INTERNAL_FLAGS
-Wdeprecated-increment-bool
# TODO(deymo): Add -Wextra-semi once we update third_party/highway.
# -Wextra-semi
-Wfloat-overflow-conversion
-Wfloat-zero-conversion
-Wfor-loop-analysis
-Wgnu-redeclared-enum
-Winfinite-recursion
-Wliteral-conversion
-Wno-c++98-compat
-Wno-unused-command-line-argument
-Wprivate-header
-Wself-assign
-Wstring-conversion
-Wtautological-overlap-compare
-Wthread-safety-analysis
-Wundefined-func-template
-Wunreachable-code
-Wunused-comparison
)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0)
list(APPEND HWY_FLAGS -Wc++2a-extensions)
endif()
endif() # Clang
if (WIN32)
list(APPEND JPEGXL_INTERNAL_FLAGS
-Wno-cast-align
-Wno-double-promotion
-Wno-float-equal
-Wno-format-nonliteral
-Wno-shadow
-Wno-sign-conversion
-Wno-zero-as-null-pointer-constant
# WARN_FLAGS
-Wall
-Wextra
-Wc++11-compat
-Warray-bounds
-Wformat-security
-Wimplicit-fallthrough
-Wno-register # Needed by public headers in lcms
-Wno-unused-function
-Wno-unused-parameter
-Wnon-virtual-dtor
-Woverloaded-virtual
-Wvla
)
# Warning flags supported by clang.
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND JPEGXL_INTERNAL_FLAGS
-Wno-used-but-marked-unused
-Wno-unused-template
-Wno-unused-member-function
-Wno-shadow-field-in-constructor
-Wno-language-extension-token
-Wno-global-constructors
-Wno-c++98-compat-pedantic
-Wdeprecated-increment-bool
# TODO(deymo): Add -Wextra-semi once we update third_party/highway.
# -Wextra-semi
-Wfloat-overflow-conversion
-Wfloat-zero-conversion
-Wfor-loop-analysis
-Wgnu-redeclared-enum
-Winfinite-recursion
-Wliteral-conversion
-Wno-c++98-compat
-Wno-unused-command-line-argument
-Wprivate-header
-Wself-assign
-Wstring-conversion
-Wtautological-overlap-compare
-Wthread-safety-analysis
-Wundefined-func-template
-Wunreachable-code
-Wunused-comparison
)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0)
list(APPEND HWY_FLAGS -Wc++2a-extensions)
endif()
endif() # Clang
else() # WIN32
list(APPEND JPEGXL_INTERNAL_FLAGS
-fsized-deallocation
-fno-exceptions
# Language flags
-fmath-errno
)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
if (WIN32)
list(APPEND JPEGXL_INTERNAL_FLAGS
-fnew-alignment=8
-fno-cxx-exceptions
-fno-slp-vectorize
-fno-vectorize
-disable-free
-disable-llvm-verifier
-Wno-cast-align
-Wno-double-promotion
-Wno-float-equal
-Wno-format-nonliteral
-Wno-shadow
-Wno-sign-conversion
-Wno-zero-as-null-pointer-constant
)
endif() # Clang
endif() # WIN32
# Internal flags for coverage builds:
if(JPEGXL_ENABLE_COVERAGE)
set(JPEGXL_COVERAGE_FLAGS
-g -O0 -fprofile-arcs -ftest-coverage
-DJXL_ENABLE_ASSERT=0 -DJXL_ENABLE_CHECK=0
)
endif() # JPEGXL_ENABLE_COVERAGE
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND JPEGXL_INTERNAL_FLAGS
-Wno-used-but-marked-unused
-Wno-unused-template
-Wno-unused-member-function
-Wno-shadow-field-in-constructor
-Wno-language-extension-token
-Wno-global-constructors
-Wno-c++98-compat-pedantic
)
endif() # Clang
else() # WIN32
list(APPEND JPEGXL_INTERNAL_FLAGS
-fsized-deallocation
-fno-exceptions
# Language flags
-fmath-errno
)
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
list(APPEND JPEGXL_INTERNAL_FLAGS
-fnew-alignment=8
-fno-cxx-exceptions
-fno-slp-vectorize
-fno-vectorize
-disable-free
-disable-llvm-verifier
)
endif() # Clang
endif() # WIN32
endif() #!MSVC
# strips the -static suffix from all the elements in LIST
@ -162,9 +154,6 @@ install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/include/jxl
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/jxl
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
# Profiler for libjxl
include(jxl_profiler.cmake)
if(BUILD_TESTING)
cmake_policy(SET CMP0057 NEW) # https://gitlab.kitware.com/cmake/cmake/issues/18198
include(GoogleTest)

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

@ -11,6 +11,18 @@ function(jxl_discover_tests TESTNAME)
endif ()
endfunction()
function(jxl_link_libraries DST SRC)
if (CMAKE_VERSION VERSION_LESS "3.13.5")
target_include_directories(${DST} SYSTEM PUBLIC
$<BUILD_INTERFACE:$<TARGET_PROPERTY:${SRC},INTERFACE_SYSTEM_INCLUDE_DIRECTORIES>>
)
add_dependencies(${DST} ${SRC})
else()
target_link_libraries(${DST} PUBLIC ${SRC})
endif()
endfunction()
if (CMAKE_VERSION VERSION_LESS "3.12.4")
set(JXL_HWY_INCLUDE_DIRS "$<BUILD_INTERFACE:$<TARGET_PROPERTY:hwy,INTERFACE_INCLUDE_DIRECTORIES>>")
else()

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

@ -5,8 +5,9 @@
#include "lib/extras/codec.h"
#include "jxl/decode.h"
#include "jxl/types.h"
#include <jxl/decode.h>
#include <jxl/types.h>
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/status.h"
@ -38,12 +39,12 @@ constexpr size_t kMinBytes = 9;
Status SetFromBytes(const Span<const uint8_t> bytes,
const extras::ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool, extras::Codec* orig_codec) {
ThreadPool* pool, const SizeConstraints* constraints,
extras::Codec* orig_codec) {
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
extras::PackedPixelFile ppf;
if (extras::DecodeBytes(bytes, color_hints, io->constraints, &ppf,
orig_codec)) {
if (extras::DecodeBytes(bytes, color_hints, &ppf, constraints, orig_codec)) {
return ConvertPackedPixelFileToCodecInOut(ppf, pool, io);
}
return JXL_FAILURE("Codecs failed to decode");
@ -51,11 +52,12 @@ Status SetFromBytes(const Span<const uint8_t> bytes,
Status SetFromFile(const std::string& pathname,
const extras::ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool, extras::Codec* orig_codec) {
ThreadPool* pool, const SizeConstraints* constraints,
extras::Codec* orig_codec) {
std::vector<uint8_t> encoded;
JXL_RETURN_IF_ERROR(ReadFile(pathname, &encoded));
JXL_RETURN_IF_ERROR(SetFromBytes(Span<const uint8_t>(encoded), color_hints,
io, pool, orig_codec));
io, pool, constraints, orig_codec));
return true;
}
@ -132,6 +134,7 @@ Status Encode(const CodecInOut& io, const extras::Codec codec,
ConvertCodecInOutToPackedPixelFile(io, format, c_desired, pool, &ppf));
ppf.info.bits_per_sample = bits_per_sample;
if (format.data_type == JXL_TYPE_FLOAT) {
ppf.info.bits_per_sample = 32;
ppf.info.exponent_bits_per_sample = 8;
}
extras::EncodedImage encoded_image;

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

@ -26,23 +26,29 @@
namespace jxl {
struct SizeConstraints;
// Decodes "bytes" and sets io->metadata.m.
// color_space_hint may specify the color space, otherwise, defaults to sRGB.
Status SetFromBytes(Span<const uint8_t> bytes,
const extras::ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool = nullptr,
const SizeConstraints* constraints = nullptr,
extras::Codec* orig_codec = nullptr);
// Helper function to use no color_space_hint.
JXL_INLINE Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io,
ThreadPool* pool = nullptr,
const SizeConstraints* constraints = nullptr,
extras::Codec* orig_codec = nullptr) {
return SetFromBytes(bytes, extras::ColorHints(), io, pool, orig_codec);
return SetFromBytes(bytes, extras::ColorHints(), io, pool, constraints,
orig_codec);
}
// Reads from file and calls SetFromBytes.
Status SetFromFile(const std::string& pathname,
const extras::ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool = nullptr,
const SizeConstraints* constraints = nullptr,
extras::Codec* orig_codec = nullptr);
// Replaces "bytes" with an encoding of pixels transformed from c_current

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

@ -20,7 +20,6 @@
#include "lib/extras/enc/encode.h"
#include "lib/extras/packed_image_convert.h"
#include "lib/jxl/base/random.h"
#include "lib/jxl/base/thread_pool_internal.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/enc_butteraugli_comparator.h"
#include "lib/jxl/enc_color_management.h"
@ -31,6 +30,9 @@
#include "lib/jxl/testing.h"
namespace jxl {
using test::ThreadPoolForTests;
namespace extras {
namespace {
@ -275,7 +277,7 @@ void TestRoundTrip(const TestImageParams& params, ThreadPool* pool) {
params.is_gray ? "Gra_D65_Rel_SRG" : "RGB_D65_SRG_Rel_SRG");
}
ASSERT_TRUE(DecodeBytes(Span<const uint8_t>(encoded.bitstreams[0]),
color_hints, SizeConstraints(), &ppf_out));
color_hints, &ppf_out));
if (params.codec == Codec::kPNG && ppf_out.icc.empty()) {
// Decoding a PNG may drop the ICC profile if there's a valid cICP chunk.
// Rendering intent is not preserved in this case.
@ -315,7 +317,7 @@ void TestRoundTrip(const TestImageParams& params, ThreadPool* pool) {
}
TEST(CodecTest, TestRoundTrip) {
ThreadPoolInternal pool(12);
ThreadPoolForTests pool(12);
TestImageParams params;
params.xsize = 7;
@ -345,7 +347,7 @@ TEST(CodecTest, TestRoundTrip) {
}
TEST(CodecTest, LosslessPNMRoundtrip) {
ThreadPoolInternal pool(12);
ThreadPoolForTests pool(12);
static const char* kChannels[] = {"", "g", "ga", "rgb", "rgba"};
static const char* kExtension[] = {"", ".pgm", ".pam", ".ppm", ".pam"};
@ -363,7 +365,7 @@ TEST(CodecTest, LosslessPNMRoundtrip) {
color_hints.Add("color_space",
channels < 3 ? "Gra_D65_Rel_SRG" : "RGB_D65_SRG_Rel_SRG");
ASSERT_TRUE(DecodeBytes(Span<const uint8_t>(orig.data(), orig.size()),
color_hints, SizeConstraints(), &ppf));
color_hints, &ppf));
EncodedImage encoded;
auto encoder = Encoder::FromExtension(extension);
@ -381,8 +383,7 @@ void DecodeRoundtrip(const std::string& pathname, ThreadPool* pool,
CodecInOut& io,
const ColorHints& color_hints = ColorHints()) {
const PaddedBytes orig = jxl::test::ReadTestData(pathname);
JXL_CHECK(
SetFromBytes(Span<const uint8_t>(orig), color_hints, &io, pool, nullptr));
JXL_CHECK(SetFromBytes(Span<const uint8_t>(orig), color_hints, &io, pool));
const ImageBundle& ib1 = io.Main();
// Encode/Decode again to make sure Encode carries through all metadata.
@ -391,8 +392,8 @@ void DecodeRoundtrip(const std::string& pathname, ThreadPool* pool,
io.metadata.m.bit_depth.bits_per_sample, &encoded, pool));
CodecInOut io2;
JXL_CHECK(SetFromBytes(Span<const uint8_t>(encoded), color_hints, &io2, pool,
nullptr));
JXL_CHECK(
SetFromBytes(Span<const uint8_t>(encoded), color_hints, &io2, pool));
const ImageBundle& ib2 = io2.Main();
EXPECT_EQ(Description(ib1.metadata()->color_encoding),
Description(ib2.metadata()->color_encoding));
@ -422,7 +423,7 @@ void DecodeRoundtrip(const std::string& pathname, ThreadPool* pool,
#if 0
TEST(CodecTest, TestMetadataSRGB) {
ThreadPoolInternal pool(12);
ThreadPoolForTests pool(12);
const char* paths[] = {"external/raw.pixls/DJI-FC6310-16bit_srgb8_v4_krita.png",
"external/raw.pixls/Google-Pixel2XL-16bit_srgb8_v4_krita.png",
@ -450,7 +451,7 @@ TEST(CodecTest, TestMetadataSRGB) {
}
TEST(CodecTest, TestMetadataLinear) {
ThreadPoolInternal pool(12);
ThreadPoolForTests pool(12);
const char* paths[3] = {
"external/raw.pixls/Google-Pixel2XL-16bit_acescg_g1_v4_krita.png",
@ -483,7 +484,7 @@ TEST(CodecTest, TestMetadataLinear) {
}
TEST(CodecTest, TestMetadataICC) {
ThreadPoolInternal pool(12);
ThreadPoolForTests pool(12);
const char* paths[] = {
"external/raw.pixls/DJI-FC6310-16bit_709_v4_krita.png",
@ -510,7 +511,7 @@ TEST(CodecTest, TestMetadataICC) {
}
TEST(CodecTest, Testexternal/pngsuite) {
ThreadPoolInternal pool(12);
ThreadPoolForTests pool(12);
// Ensure we can load PNG with text, japanese UTF-8, compressed text.
CodecInOut tmp1;
@ -556,7 +557,7 @@ void VerifyWideGamutMetadata(const std::string& relative_pathname,
}
TEST(CodecTest, TestWideGamut) {
ThreadPoolInternal pool(12);
ThreadPoolForTests pool(12);
// VerifyWideGamutMetadata("external/wide-gamut-tests/P3-sRGB-color-bars.png",
// Primaries::kP3, &pool);
VerifyWideGamutMetadata("external/wide-gamut-tests/P3-sRGB-color-ring.png",
@ -614,7 +615,7 @@ TEST(CodecTest, EncodeToPNG) {
"external/wesaturate/500px/tmshre_riaphotographs_srgb8.png");
PackedPixelFile ppf;
ASSERT_TRUE(extras::DecodeBytes(Span<const uint8_t>(original_png),
ColorHints(), SizeConstraints(), &ppf));
ColorHints(), &ppf));
const JxlPixelFormat& format = ppf.frames.front().color.format;
ASSERT_THAT(
@ -630,7 +631,7 @@ TEST(CodecTest, EncodeToPNG) {
PackedPixelFile decoded_ppf;
ASSERT_TRUE(
extras::DecodeBytes(Span<const uint8_t>(encoded_png.bitstreams.front()),
ColorHints(), SizeConstraints(), &decoded_ppf));
ColorHints(), &decoded_ppf));
ASSERT_EQ(decoded_ppf.info.bits_per_sample, ppf.info.bits_per_sample);
ASSERT_EQ(decoded_ppf.frames.size(), 1);

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

@ -36,6 +36,8 @@
*
*/
#include <jxl/codestream_header.h>
#include <jxl/encode.h>
#include <stdio.h>
#include <string.h>
@ -43,15 +45,13 @@
#include <utility>
#include <vector>
#include "jxl/codestream_header.h"
#include "jxl/encode.h"
#include "lib/extras/size_constraints.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/scope_guard.h"
#include "lib/jxl/common.h"
#include "lib/jxl/sanitizers.h"
#include "lib/jxl/size_constraints.h"
#include "png.h" /* original (unpatched) libpng is ok */
namespace jxl {
@ -491,6 +491,12 @@ int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
std::vector<std::vector<uint8_t>>& chunksInfo) {
unsigned char header[8] = {137, 80, 78, 71, 13, 10, 26, 10};
// Cleanup prior decoder, if any.
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
// Just in case. Not all versions on libpng wipe-out the pointers.
png_ptr = nullptr;
info_ptr = nullptr;
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
info_ptr = png_create_info_struct(png_ptr);
if (!png_ptr || !info_ptr) return 1;
@ -554,9 +560,8 @@ int processing_finish(png_structp png_ptr, png_infop info_ptr,
} // namespace
Status DecodeImageAPNG(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
const ColorHints& color_hints, PackedPixelFile* ppf,
const SizeConstraints* constraints) {
Reader r;
unsigned int id, j, w, h, w0, h0, x0, y0;
unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize;
@ -699,7 +704,8 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
JXL_CHECK(w == png_get_image_width(png_ptr, info_ptr));
JXL_CHECK(h == png_get_image_height(png_ptr, info_ptr));
int colortype = png_get_color_type(png_ptr, info_ptr);
ppf->info.bits_per_sample = png_get_bit_depth(png_ptr, info_ptr);
int png_bit_depth = png_get_bit_depth(png_ptr, info_ptr);
ppf->info.bits_per_sample = png_bit_depth;
png_color_8p sigbits = NULL;
png_get_sBIT(png_ptr, info_ptr, &sigbits);
if (colortype & 1) {
@ -735,7 +741,7 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
: JXL_COLOR_SPACE_RGB);
ppf->info.xsize = w;
ppf->info.ysize = h;
JXL_RETURN_IF_ERROR(VerifyDimensions(&constraints, w, h));
JXL_RETURN_IF_ERROR(VerifyDimensions(constraints, w, h));
num_channels =
ppf->info.num_color_channels + (ppf->info.alpha_bits ? 1 : 0);
format = {
@ -745,6 +751,9 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
/*endianness=*/JXL_BIG_ENDIAN,
/*align=*/0,
};
if (png_bit_depth > 8 && format.data_type == JXL_TYPE_UINT8) {
png_set_strip_16(png_ptr);
}
bytes_per_pixel =
num_channels * (format.data_type == JXL_TYPE_UINT16 ? 2 : 1);
rowbytes = w * bytes_per_pixel;

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

@ -16,15 +16,17 @@
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
struct SizeConstraints;
namespace extras {
// Decodes `bytes` into `ppf`.
Status DecodeImageAPNG(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf);
PackedPixelFile* ppf,
const SizeConstraints* constraints = nullptr);
} // namespace extras
} // namespace jxl

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

@ -6,9 +6,10 @@
#ifndef LIB_EXTRAS_COLOR_DESCRIPTION_H_
#define LIB_EXTRAS_COLOR_DESCRIPTION_H_
#include <jxl/color_encoding.h>
#include <string>
#include "jxl/color_encoding.h"
#include "lib/jxl/base/status.h"
namespace jxl {

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

@ -5,7 +5,8 @@
#include "lib/extras/dec/color_hints.h"
#include "jxl/encode.h"
#include <jxl/encode.h>
#include "lib/extras/dec/color_description.h"
#include "lib/jxl/base/file_io.h"
@ -51,7 +52,6 @@ Status ApplyColorHints(const ColorHints& color_hints,
}));
if (!got_color_space) {
JXL_WARNING("No color_space/icc_pathname given, assuming sRGB");
ppf->color_encoding.color_space =
is_gray ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB;
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;

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

@ -79,9 +79,8 @@ Codec CodecFromExtension(std::string extension,
}
Status DecodeBytes(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
extras::PackedPixelFile* ppf, Codec* orig_codec) {
const ColorHints& color_hints, extras::PackedPixelFile* ppf,
const SizeConstraints* constraints, Codec* orig_codec) {
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
*ppf = extras::PackedPixelFile();
@ -92,28 +91,28 @@ Status DecodeBytes(const Span<const uint8_t> bytes,
const auto choose_codec = [&]() -> Codec {
#if JPEGXL_ENABLE_APNG
if (DecodeImageAPNG(bytes, color_hints, constraints, ppf)) {
if (DecodeImageAPNG(bytes, color_hints, ppf, constraints)) {
return Codec::kPNG;
}
#endif
if (DecodeImagePGX(bytes, color_hints, constraints, ppf)) {
if (DecodeImagePGX(bytes, color_hints, ppf, constraints)) {
return Codec::kPGX;
}
if (DecodeImagePNM(bytes, color_hints, constraints, ppf)) {
if (DecodeImagePNM(bytes, color_hints, ppf, constraints)) {
return Codec::kPNM;
}
#if JPEGXL_ENABLE_GIF
if (DecodeImageGIF(bytes, color_hints, constraints, ppf)) {
if (DecodeImageGIF(bytes, color_hints, ppf, constraints)) {
return Codec::kGIF;
}
#endif
#if JPEGXL_ENABLE_JPEG
if (DecodeImageJPG(bytes, color_hints, constraints, ppf)) {
if (DecodeImageJPG(bytes, color_hints, ppf, constraints)) {
return Codec::kJPG;
}
#endif
#if JPEGXL_ENABLE_EXR
if (DecodeImageEXR(bytes, color_hints, constraints, ppf)) {
if (DecodeImageEXR(bytes, color_hints, ppf, constraints)) {
return Codec::kEXR;
}
#endif

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

@ -17,9 +17,11 @@
#include "lib/extras/dec/color_hints.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
struct SizeConstraints;
namespace extras {
// Codecs supported by CodecInOut::Encode.
@ -43,8 +45,9 @@ Codec CodecFromExtension(std::string extension,
// Decodes "bytes" info *ppf.
// color_space_hint may specify the color space, otherwise, defaults to sRGB.
Status DecodeBytes(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints,
extras::PackedPixelFile* ppf, Codec* orig_codec = nullptr);
extras::PackedPixelFile* ppf,
const SizeConstraints* constraints = nullptr,
Codec* orig_codec = nullptr);
} // namespace extras
} // namespace jxl

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

@ -62,8 +62,8 @@ class InMemoryIStream : public OpenEXR::IStream {
} // namespace
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
PackedPixelFile* ppf,
const SizeConstraints* constraints) {
InMemoryIStream is(bytes);
#ifdef __EXCEPTIONS

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

@ -14,14 +14,17 @@
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
struct SizeConstraints;
namespace extras {
// Decodes `bytes` into `ppf`. color_hints are ignored.
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
PackedPixelFile* ppf,
const SizeConstraints* constraints = nullptr);
} // namespace extras
} // namespace jxl

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

@ -6,16 +6,16 @@
#include "lib/extras/dec/gif.h"
#include <gif_lib.h>
#include <jxl/codestream_header.h>
#include <string.h>
#include <memory>
#include <utility>
#include <vector>
#include "jxl/codestream_header.h"
#include "lib/extras/size_constraints.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/sanitizers.h"
#include "lib/jxl/size_constraints.h"
namespace jxl {
namespace extras {
@ -57,8 +57,8 @@ void ensure_have_alpha(PackedFrame* frame) {
} // namespace
Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
PackedPixelFile* ppf,
const SizeConstraints* constraints) {
int error = GIF_OK;
ReadState state = {bytes};
const auto ReadFromSpan = [](GifFileType* const gif, GifByteType* const bytes,
@ -97,20 +97,20 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
sizeof(*gif->SavedImages) * gif->ImageCount);
JXL_RETURN_IF_ERROR(
VerifyDimensions<uint32_t>(&constraints, gif->SWidth, gif->SHeight));
VerifyDimensions<uint32_t>(constraints, gif->SWidth, gif->SHeight));
uint64_t total_pixel_count =
static_cast<uint64_t>(gif->SWidth) * gif->SHeight;
for (int i = 0; i < gif->ImageCount; ++i) {
const SavedImage& image = gif->SavedImages[i];
uint32_t w = image.ImageDesc.Width;
uint32_t h = image.ImageDesc.Height;
JXL_RETURN_IF_ERROR(VerifyDimensions<uint32_t>(&constraints, w, h));
JXL_RETURN_IF_ERROR(VerifyDimensions<uint32_t>(constraints, w, h));
uint64_t pixel_count = static_cast<uint64_t>(w) * h;
if (total_pixel_count + pixel_count < total_pixel_count) {
return JXL_FAILURE("Image too big");
}
total_pixel_count += pixel_count;
if (total_pixel_count > constraints.dec_max_pixels) {
if (constraints && (total_pixel_count > constraints->dec_max_pixels)) {
return JXL_FAILURE("Image too big");
}
}

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

@ -15,14 +15,17 @@
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
struct SizeConstraints;
namespace extras {
// Decodes `bytes` into `ppf`. color_hints are ignored.
Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
PackedPixelFile* ppf,
const SizeConstraints* constraints = nullptr);
} // namespace extras
} // namespace jxl

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

@ -82,6 +82,30 @@ JpegliDataType ConvertDataType(JxlDataType type) {
}
}
JpegliEndianness ConvertEndianness(JxlEndianness type) {
switch (type) {
case JXL_NATIVE_ENDIAN:
return JPEGLI_NATIVE_ENDIAN;
case JXL_BIG_ENDIAN:
return JPEGLI_BIG_ENDIAN;
case JXL_LITTLE_ENDIAN:
return JPEGLI_LITTLE_ENDIAN;
default:
return JPEGLI_NATIVE_ENDIAN;
}
}
JxlColorSpace ConvertColorSpace(J_COLOR_SPACE colorspace) {
switch (colorspace) {
case JCS_GRAYSCALE:
return JXL_COLOR_SPACE_GRAY;
case JCS_RGB:
return JXL_COLOR_SPACE_RGB;
default:
return JXL_COLOR_SPACE_UNKNOWN;
}
}
void MyErrorExit(j_common_ptr cinfo) {
jmp_buf* env = static_cast<jmp_buf*>(cinfo->client_data);
(*cinfo->err->output_message)(cinfo);
@ -98,10 +122,23 @@ void MyOutputMessage(j_common_ptr cinfo) {
#endif
}
void UnmapColors(uint8_t* row, size_t xsize, int components,
JSAMPARRAY colormap, size_t num_colors) {
JXL_CHECK(colormap != nullptr);
std::vector<uint8_t> tmp(xsize * components);
for (size_t x = 0; x < xsize; ++x) {
JXL_CHECK(row[x] < num_colors);
for (int c = 0; c < components; ++c) {
tmp[x * components + c] = colormap[c][row[x]];
}
}
memcpy(row, tmp.data(), tmp.size());
}
} // namespace
Status DecodeJpeg(const std::vector<uint8_t>& compressed,
JxlDataType output_data_type, ThreadPool* pool,
const JpegDecompressParams& dparams, ThreadPool* pool,
PackedPixelFile* ppf) {
// Don't do anything for non-JPEG files (no need to report an error)
if (!IsJPG(compressed)) return false;
@ -112,11 +149,8 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
// the call to setjmp().
std::unique_ptr<JSAMPLE[]> row;
jpeg_decompress_struct cinfo;
const auto try_catch_block = [&]() -> bool {
jpeg_decompress_struct cinfo;
// cinfo is initialized by libjpeg, which we are not instrumenting with
// msan, therefore we need to initialize cinfo here.
msan::UnpoisonMemory(&cinfo, sizeof(cinfo));
// Setup error handling in jpeg library so we can deal with broken jpegs in
// the fuzzer.
jpeg_error_mgr jerr;
@ -149,13 +183,16 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
if (nbcomp != 1 && nbcomp != 3) {
return failure("unsupported number of components in JPEG");
}
if (dparams.force_rgb) {
cinfo.out_color_space = JCS_RGB;
} else if (dparams.force_grayscale) {
cinfo.out_color_space = JCS_GRAYSCALE;
}
if (!ReadICCProfile(&cinfo, &ppf->icc)) {
ppf->icc.clear();
// Default to SRGB
// Actually, (cinfo.output_components == nbcomp) will be checked after
// `jpegli_start_decompress`.
ppf->color_encoding.color_space =
(nbcomp == 1) ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB;
ConvertColorSpace(cinfo.out_color_space);
ppf->color_encoding.white_point = JXL_WHITE_POINT_D65;
ppf->color_encoding.primaries = JXL_PRIMARIES_SRGB;
ppf->color_encoding.transfer_function = JXL_TRANSFER_FUNCTION_SRGB;
@ -165,39 +202,50 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
ppf->info.xsize = cinfo.image_width;
ppf->info.ysize = cinfo.image_height;
if (output_data_type == JXL_TYPE_UINT8) {
if (dparams.output_data_type == JXL_TYPE_UINT8) {
ppf->info.bits_per_sample = 8;
} else if (output_data_type == JXL_TYPE_UINT16) {
ppf->info.exponent_bits_per_sample = 0;
} else if (dparams.output_data_type == JXL_TYPE_UINT16) {
ppf->info.bits_per_sample = 16;
ppf->info.exponent_bits_per_sample = 0;
} else if (dparams.output_data_type == JXL_TYPE_FLOAT) {
ppf->info.bits_per_sample = 32;
ppf->info.exponent_bits_per_sample = 8;
} else {
return failure("unsupported data type");
}
ppf->info.exponent_bits_per_sample = 0;
ppf->info.uses_original_profile = true;
// No alpha in JPG
ppf->info.alpha_bits = 0;
ppf->info.alpha_exponent_bits = 0;
ppf->info.num_color_channels = nbcomp;
ppf->info.orientation = JXL_ORIENT_IDENTITY;
jpegli_set_output_format(&cinfo, ConvertDataType(output_data_type),
JPEGLI_NATIVE_ENDIAN);
jpegli_start_decompress(&cinfo);
JXL_ASSERT(cinfo.output_components == nbcomp);
jpegli_set_output_format(&cinfo, ConvertDataType(dparams.output_data_type),
ConvertEndianness(dparams.output_endianness));
if (dparams.num_colors > 0) {
cinfo.quantize_colors = TRUE;
cinfo.desired_number_of_colors = dparams.num_colors;
cinfo.two_pass_quantize = dparams.two_pass_quant;
cinfo.dither_mode = (J_DITHER_MODE)dparams.dither_mode;
}
jpegli_start_decompress(&cinfo);
ppf->info.num_color_channels = cinfo.out_color_components;
const JxlPixelFormat format{
/*num_channels=*/static_cast<uint32_t>(nbcomp),
output_data_type,
/*endianness=*/JXL_NATIVE_ENDIAN,
/*num_channels=*/static_cast<uint32_t>(cinfo.out_color_components),
dparams.output_data_type,
dparams.output_endianness,
/*align=*/0,
};
ppf->frames.clear();
// Allocates the frame buffer.
ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format);
const auto& frame = ppf->frames.back();
JXL_ASSERT(sizeof(JSAMPLE) * cinfo.output_components * cinfo.image_width <=
JXL_ASSERT(sizeof(JSAMPLE) * cinfo.out_color_components *
cinfo.image_width <=
frame.color.stride);
for (size_t y = 0; y < cinfo.image_height; ++y) {
@ -205,16 +253,18 @@ Status DecodeJpeg(const std::vector<uint8_t>& compressed,
static_cast<uint8_t*>(frame.color.pixels()) +
frame.color.stride * y)};
jpegli_read_scanlines(&cinfo, rows, 1);
msan::UnpoisonMemory(rows[0], sizeof(JSAMPLE) * cinfo.output_components *
cinfo.image_width);
if (dparams.num_colors > 0) {
UnmapColors(rows[0], cinfo.output_width, cinfo.out_color_components,
cinfo.colormap, cinfo.actual_number_of_colors);
}
}
jpegli_finish_decompress(&cinfo);
jpegli_destroy_decompress(&cinfo);
return true;
};
return try_catch_block();
bool success = try_catch_block();
jpegli_destroy_decompress(&cinfo);
return success;
}
} // namespace extras

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

@ -8,11 +8,11 @@
// Decodes JPG pixels and metadata in memory using the libjpegli library.
#include <jxl/types.h>
#include <stdint.h>
#include <vector>
#include "jxl/types.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
@ -20,8 +20,19 @@
namespace jxl {
namespace extras {
struct JpegDecompressParams {
JxlDataType output_data_type = JXL_TYPE_UINT8;
JxlEndianness output_endianness = JXL_NATIVE_ENDIAN;
bool force_rgb = false;
bool force_grayscale = false;
int num_colors = 0;
bool two_pass_quant = true;
// 0 = none, 1 = ordered, 2 = Floyd-Steinberg
int dither_mode = 2;
};
Status DecodeJpeg(const std::vector<uint8_t>& compressed,
JxlDataType output_data_type, ThreadPool* pool,
const JpegDecompressParams& dparams, ThreadPool* pool,
PackedPixelFile* ppf);
} // namespace extras

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

@ -14,9 +14,9 @@
#include <utility>
#include <vector>
#include "lib/extras/size_constraints.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/sanitizers.h"
#include "lib/jxl/size_constraints.h"
namespace jxl {
namespace extras {
@ -161,12 +161,25 @@ void MyOutputMessage(j_common_ptr cinfo) {
#endif
}
void UnmapColors(uint8_t* row, size_t xsize, int components,
JSAMPARRAY colormap, size_t num_colors) {
JXL_CHECK(colormap != nullptr);
std::vector<uint8_t> tmp(xsize * components);
for (size_t x = 0; x < xsize; ++x) {
JXL_CHECK(row[x] < num_colors);
for (int c = 0; c < components; ++c) {
tmp[x * components + c] = colormap[c][row[x]];
}
}
memcpy(row, tmp.data(), tmp.size());
}
} // namespace
Status DecodeImageJPG(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
const ColorHints& color_hints, PackedPixelFile* ppf,
const SizeConstraints* constraints,
const JPGDecompressParams* dparams) {
// Don't do anything for non-JPEG files (no need to report an error)
if (!IsJPG(bytes)) return false;
@ -205,8 +218,7 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
if (read_header_result == JPEG_SUSPENDED) {
return failure("truncated JPEG input");
}
if (!VerifyDimensions(&constraints, cinfo.image_width,
cinfo.image_height)) {
if (!VerifyDimensions(constraints, cinfo.image_width, cinfo.image_height)) {
return failure("image too big");
}
// Might cause CPU-zip bomb.
@ -250,8 +262,15 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
ppf->info.num_color_channels = nbcomp;
ppf->info.orientation = JXL_ORIENT_IDENTITY;
if (dparams && dparams->num_colors > 0) {
cinfo.quantize_colors = TRUE;
cinfo.desired_number_of_colors = dparams->num_colors;
cinfo.two_pass_quantize = dparams->two_pass_quant;
cinfo.dither_mode = (J_DITHER_MODE)dparams->dither_mode;
}
jpeg_start_decompress(&cinfo);
JXL_ASSERT(cinfo.output_components == nbcomp);
JXL_ASSERT(cinfo.out_color_components == nbcomp);
JxlDataType data_type =
ppf->info.bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16;
@ -265,9 +284,19 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
// Allocates the frame buffer.
ppf->frames.emplace_back(cinfo.image_width, cinfo.image_height, format);
const auto& frame = ppf->frames.back();
JXL_ASSERT(sizeof(JSAMPLE) * cinfo.output_components * cinfo.image_width <=
JXL_ASSERT(sizeof(JSAMPLE) * cinfo.out_color_components *
cinfo.image_width <=
frame.color.stride);
if (cinfo.quantize_colors) {
jxl::msan::UnpoisonMemory(cinfo.colormap, cinfo.out_color_components *
sizeof(cinfo.colormap[0]));
for (int c = 0; c < cinfo.out_color_components; ++c) {
jxl::msan::UnpoisonMemory(
cinfo.colormap[c],
cinfo.actual_number_of_colors * sizeof(cinfo.colormap[c][0]));
}
}
for (size_t y = 0; y < cinfo.image_height; ++y) {
JSAMPROW rows[] = {reinterpret_cast<JSAMPLE*>(
static_cast<uint8_t*>(frame.color.pixels()) +
@ -275,6 +304,10 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes,
jpeg_read_scanlines(&cinfo, rows, 1);
msan::UnpoisonMemory(rows[0], sizeof(JSAMPLE) * cinfo.output_components *
cinfo.image_width);
if (dparams && dparams->num_colors > 0) {
UnmapColors(rows[0], cinfo.output_width, cinfo.out_color_components,
cinfo.colormap, cinfo.actual_number_of_colors);
}
}
jpeg_finish_decompress(&cinfo);

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

@ -16,16 +16,27 @@
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
struct SizeConstraints;
namespace extras {
struct JPGDecompressParams {
int num_colors = 0;
bool two_pass_quant = false;
// 0 = none, 1 = ordered, 2 = Floyd-Steinberg
int dither_mode = 0;
};
// Decodes `bytes` into `ppf`. color_hints are ignored.
// `elapsed_deinterleave`, if non-null, will be set to the time (in seconds)
// that it took to deinterleave the raw JSAMPLEs to planar floats.
Status DecodeImageJPG(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
PackedPixelFile* ppf,
const SizeConstraints* constraints = nullptr,
const JPGDecompressParams* dparams = nullptr);
} // namespace extras
} // namespace jxl

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

@ -5,9 +5,10 @@
#include "lib/extras/dec/jxl.h"
#include "jxl/decode.h"
#include "jxl/decode_cxx.h"
#include "jxl/types.h"
#include <jxl/decode.h>
#include <jxl/decode_cxx.h>
#include <jxl/types.h>
#include "lib/extras/dec/color_description.h"
#include "lib/extras/enc/encode.h"
#include "lib/jxl/base/printf_macros.h"

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

@ -8,14 +8,14 @@
// Decodes JPEG XL images in memory.
#include <jxl/parallel_runner.h>
#include <jxl/types.h>
#include <stdint.h>
#include <limits>
#include <string>
#include <vector>
#include "jxl/parallel_runner.h"
#include "jxl/types.h"
#include "lib/extras/packed_image.h"
namespace jxl {

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

@ -7,9 +7,9 @@
#include <string.h>
#include "lib/extras/size_constraints.h"
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/size_constraints.h"
namespace jxl {
namespace extras {
@ -146,15 +146,14 @@ class Parser {
} // namespace
Status DecodeImagePGX(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
const ColorHints& color_hints, PackedPixelFile* ppf,
const SizeConstraints* constraints) {
Parser parser(bytes);
HeaderPGX header = {};
const uint8_t* pos;
if (!parser.ParseHeader(&header, &pos)) return false;
JXL_RETURN_IF_ERROR(
VerifyDimensions(&constraints, header.xsize, header.ysize));
VerifyDimensions(constraints, header.xsize, header.ysize));
if (header.bits_per_sample == 0 || header.bits_per_sample > 32) {
return JXL_FAILURE("PGX: bits_per_sample invalid");
}

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

@ -17,14 +17,17 @@
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
struct SizeConstraints;
namespace extras {
// Decodes `bytes` into `ppf`.
Status DecodeImagePGX(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
PackedPixelFile* ppf,
const SizeConstraints* constraints = nullptr);
} // namespace extras
} // namespace jxl

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

@ -24,8 +24,7 @@ TEST(CodecPGXTest, Test8bits) {
PackedPixelFile ppf;
ThreadPool* pool = nullptr;
EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(),
SizeConstraints(), &ppf));
EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(), &ppf));
CodecInOut io;
EXPECT_TRUE(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io));
@ -52,8 +51,7 @@ TEST(CodecPGXTest, Test16bits) {
PackedPixelFile ppf;
ThreadPool* pool = nullptr;
EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(),
SizeConstraints(), &ppf));
EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(), &ppf));
CodecInOut io;
EXPECT_TRUE(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io));

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

@ -8,10 +8,12 @@
#include <stdlib.h>
#include <string.h>
#include <cmath>
#include "lib/extras/size_constraints.h"
#include "lib/jxl/base/bits.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/size_constraints.h"
namespace jxl {
namespace extras {
@ -325,15 +327,14 @@ Span<const uint8_t> MakeSpan(const char* str) {
} // namespace
Status DecodeImagePNM(const Span<const uint8_t> bytes,
const ColorHints& color_hints,
const SizeConstraints& constraints,
PackedPixelFile* ppf) {
const ColorHints& color_hints, PackedPixelFile* ppf,
const SizeConstraints* constraints) {
Parser parser(bytes);
HeaderPNM header = {};
const uint8_t* pos = nullptr;
if (!parser.ParseHeader(&header, &pos)) return false;
JXL_RETURN_IF_ERROR(
VerifyDimensions(&constraints, header.xsize, header.ysize));
VerifyDimensions(constraints, header.xsize, header.ysize));
if (header.bits_per_sample == 0 || header.bits_per_sample > 32) {
return JXL_FAILURE("PNM: bits_per_sample invalid");

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

@ -20,15 +20,18 @@
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
struct SizeConstraints;
namespace extras {
// Decodes `bytes` into `ppf`. color_hints may specify "color_space", which
// defaults to sRGB.
Status DecodeImagePNM(Span<const uint8_t> bytes, const ColorHints& color_hints,
const SizeConstraints& constraints, PackedPixelFile* ppf);
PackedPixelFile* ppf,
const SizeConstraints* constraints = nullptr);
void TestCodecPNM();

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

@ -9,10 +9,10 @@
#include <ImfIO.h>
#include <ImfRgbaFile.h>
#include <ImfStandardAttributes.h>
#include <jxl/codestream_header.h>
#include <vector>
#include "jxl/codestream_header.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/byte_order.h"
@ -110,7 +110,7 @@ Status EncodeImageEXR(const PackedImage& image, const JxlBasicInfo& info,
chromaticities.white =
Imath::V2f(c_enc.white_point_xy[0], c_enc.white_point_xy[1]);
OpenEXR::addChromaticities(header, chromaticities);
OpenEXR::addWhiteLuminance(header, 255.0f);
OpenEXR::addWhiteLuminance(header, info.intensity_target);
auto loadFloat =
format.endianness == JXL_BIG_ENDIAN ? LoadBEFloat : LoadLEFloat;

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

@ -5,10 +5,10 @@
#include "lib/extras/enc/jpegli.h"
#include <jxl/codestream_header.h>
#include <setjmp.h>
#include <stdint.h>
#include "jxl/codestream_header.h"
#include "lib/extras/enc/encode.h"
#include "lib/jpegli/encode.h"
#include "lib/jxl/enc_color_management.h"
@ -105,6 +105,106 @@ Status WriteAppData(j_compress_ptr cinfo,
return true;
}
static constexpr int kICCMarker = 0xe2;
constexpr unsigned char kICCSignature[12] = {
0x49, 0x43, 0x43, 0x5F, 0x50, 0x52, 0x4F, 0x46, 0x49, 0x4C, 0x45, 0x00};
static constexpr uint8_t kUnknownTf = 2;
static constexpr unsigned char kCICPTagSignature[4] = {0x63, 0x69, 0x63, 0x70};
static constexpr size_t kCICPTagSize = 12;
bool FindCICPTag(const uint8_t* icc_data, size_t len, bool is_first_chunk,
size_t* cicp_offset, size_t* cicp_length, uint8_t* cicp_tag,
size_t* cicp_pos) {
if (is_first_chunk) {
// Look up the offset of the CICP tag from the first chunk of ICC data.
if (len < 132) {
return false;
}
uint32_t tag_count = LoadBE32(&icc_data[128]);
if (len < 132 + 12 * tag_count) {
return false;
}
for (uint32_t i = 0; i < tag_count; ++i) {
if (memcmp(&icc_data[132 + 12 * i], kCICPTagSignature, 4) == 0) {
*cicp_offset = LoadBE32(&icc_data[136 + 12 * i]);
*cicp_length = LoadBE32(&icc_data[140 + 12 * i]);
}
}
if (*cicp_length < kCICPTagSize) {
return false;
}
}
if (*cicp_offset < len) {
size_t n_bytes = std::min(len - *cicp_offset, kCICPTagSize - *cicp_pos);
memcpy(&cicp_tag[*cicp_pos], &icc_data[*cicp_offset], n_bytes);
*cicp_pos += n_bytes;
*cicp_offset = 0;
} else {
*cicp_offset -= len;
}
return true;
}
uint8_t LookupCICPTransferFunctionFromAppData(const uint8_t* app_data,
size_t len) {
size_t last_index = 0;
size_t cicp_offset = 0;
size_t cicp_length = 0;
uint8_t cicp_tag[kCICPTagSize] = {};
size_t cicp_pos = 0;
size_t pos = 0;
while (pos < len) {
const uint8_t* marker = &app_data[pos];
if (pos + 4 > len) {
return kUnknownTf;
}
size_t marker_size = (marker[2] << 8) + marker[3] + 2;
if (pos + marker_size > len) {
return kUnknownTf;
}
if (marker_size < 18 || marker[0] != 0xff || marker[1] != kICCMarker ||
memcmp(&marker[4], kICCSignature, 12) != 0) {
pos += marker_size;
continue;
}
uint8_t index = marker[16];
uint8_t total = marker[17];
const uint8_t* payload = marker + 18;
const size_t payload_size = marker_size - 18;
if (index != last_index + 1 || index > total) {
return kUnknownTf;
}
if (!FindCICPTag(payload, payload_size, last_index == 0, &cicp_offset,
&cicp_length, &cicp_tag[0], &cicp_pos)) {
return kUnknownTf;
}
if (cicp_pos == kCICPTagSize) {
break;
}
++last_index;
}
if (cicp_pos >= kCICPTagSize && memcmp(cicp_tag, kCICPTagSignature, 4) == 0) {
return cicp_tag[9];
}
return kUnknownTf;
}
uint8_t LookupCICPTransferFunctionFromICCProfile(const uint8_t* icc_data,
size_t len) {
size_t cicp_offset = 0;
size_t cicp_length = 0;
uint8_t cicp_tag[kCICPTagSize] = {};
size_t cicp_pos = 0;
if (!FindCICPTag(icc_data, len, true, &cicp_offset, &cicp_length,
&cicp_tag[0], &cicp_pos)) {
return kUnknownTf;
}
if (cicp_pos >= kCICPTagSize && memcmp(cicp_tag, kCICPTagSignature, 4) == 0) {
return cicp_tag[9];
}
return kUnknownTf;
}
JpegliDataType ConvertDataType(JxlDataType type) {
switch (type) {
case JXL_TYPE_UINT8:
@ -165,13 +265,16 @@ Status EncodeJpegToTargetSize(const PackedPixelFile& ppf,
const JpegSettings& jpeg_settings,
size_t target_size, ThreadPool* pool,
std::vector<uint8_t>* output) {
float distance = 1.0f;
output->clear();
size_t best_error = std::numeric_limits<size_t>::max();
for (int step = 0; step < 10; ++step) {
float distance0 = -1.0f;
float distance1 = -1.0f;
float distance = 1.0f;
for (int step = 0; step < 15; ++step) {
JpegSettings settings = jpeg_settings;
settings.libjpeg_quality = 0;
settings.distance = distance;
settings.target_size = 0;
std::vector<uint8_t> compressed;
JXL_RETURN_IF_ERROR(EncodeJpeg(ppf, settings, pool, &compressed));
size_t size = compressed.size();
@ -184,10 +287,21 @@ Status EncodeJpegToTargetSize(const PackedPixelFile& ppf,
std::swap(*output, compressed);
}
float rel_error = size * 1.0f / target_size;
if (std::abs(rel_error - 1.0f) < 0.0005f) {
if (std::abs(rel_error - 1.0f) < 0.002f) {
break;
}
distance *= rel_error;
if (size < target_size) {
distance1 = distance;
} else {
distance0 = distance;
}
if (distance1 == -1) {
distance *= std::pow(rel_error, 1.5) * 1.05;
} else if (distance0 == -1) {
distance *= std::pow(rel_error, 1.5) * 0.95;
} else {
distance = 0.5 * (distance0 + distance1);
}
}
return true;
}
@ -209,6 +323,10 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
return EncodeJpegToTargetSize(ppf, jpeg_settings, target_size, pool,
compressed);
}
if (jpeg_settings.target_size > 0) {
return EncodeJpegToTargetSize(ppf, jpeg_settings, jpeg_settings.target_size,
pool, compressed);
}
JXL_RETURN_IF_ERROR(VerifyInput(ppf));
ColorEncoding color_encoding;
@ -269,6 +387,15 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
} else if (jpeg_settings.use_std_quant_tables) {
jpegli_use_standard_quant_tables(&cinfo);
}
uint8_t cicp_tf = kUnknownTf;
if (!jpeg_settings.app_data.empty()) {
cicp_tf = LookupCICPTransferFunctionFromAppData(
jpeg_settings.app_data.data(), jpeg_settings.app_data.size());
} else if (!output_encoding.IsSRGB()) {
cicp_tf = LookupCICPTransferFunctionFromICCProfile(
output_encoding.ICC().data(), output_encoding.ICC().size());
}
jpegli_set_cicp_transfer_function(&cinfo, cicp_tf);
jpegli_set_defaults(&cinfo);
if (!jpeg_settings.chroma_subsampling.empty()) {
if (jpeg_settings.chroma_subsampling == "444") {
@ -293,13 +420,21 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
}
jpegli_enable_adaptive_quantization(
&cinfo, jpeg_settings.use_adaptive_quantization);
jpegli_set_distance(&cinfo, jpeg_settings.distance);
jpegli_set_distance(&cinfo, jpeg_settings.distance, TRUE);
jpegli_set_progressive_level(&cinfo, jpeg_settings.progressive_level);
cinfo.optimize_coding = jpeg_settings.optimize_coding;
if (!jpeg_settings.app_data.empty()) {
// Make sure jpegli_start_compress() does not write any APP markers.
cinfo.write_JFIF_header = false;
cinfo.write_Adobe_marker = false;
}
const PackedImage& image = ppf.frames[0].color;
if (jpeg_settings.xyb) {
jpegli_set_input_format(&cinfo, JPEGLI_TYPE_FLOAT, JPEGLI_NATIVE_ENDIAN);
} else {
jpegli_set_input_format(&cinfo, ConvertDataType(image.format.data_type),
ConvertEndianness(image.format.endianness));
}
jpegli_start_compress(&cinfo, TRUE);
if (!jpeg_settings.app_data.empty()) {
JXL_RETURN_IF_ERROR(WriteAppData(&cinfo, jpeg_settings.app_data));
@ -309,10 +444,8 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
jpegli_write_icc_profile(&cinfo, output_encoding.ICC().data(),
output_encoding.ICC().size());
}
const PackedImage& image = ppf.frames[0].color;
const uint8_t* pixels = reinterpret_cast<const uint8_t*>(image.pixels());
if (jpeg_settings.xyb) {
jpegli_set_input_format(&cinfo, JPEGLI_TYPE_FLOAT, JPEGLI_NATIVE_ENDIAN);
float* src_buf = c_transform.BufSrc(0);
float* dst_buf = c_transform.BufDst(0);
for (size_t y = 0; y < image.ysize; ++y) {
@ -348,8 +481,6 @@ Status EncodeJpeg(const PackedPixelFile& ppf, const JpegSettings& jpeg_settings,
jpegli_write_scanlines(&cinfo, row, 1);
}
} else {
jpegli_set_input_format(&cinfo, ConvertDataType(image.format.data_type),
ConvertEndianness(image.format.endianness));
row_bytes.resize(image.stride);
for (size_t y = 0; y < info.ysize; ++y) {
memcpy(&row_bytes[0], pixels + y * image.stride, image.stride);

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

@ -27,6 +27,7 @@ struct JpegSettings {
bool use_adaptive_quantization = true;
bool use_std_quant_tables = false;
int progressive_level = 2;
bool optimize_coding = true;
std::string chroma_subsampling;
int libjpeg_quality = 0;
std::string libjpeg_chroma_subsampling;

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

@ -230,7 +230,8 @@ Status EncodeWithLibJpeg(const PackedImage& image, const JxlBasicInfo& info,
const std::vector<uint8_t>& icc,
std::vector<uint8_t> exif, size_t quality,
const std::string& chroma_subsampling,
int progressive_id, std::vector<uint8_t>* bytes) {
int progressive_id, bool optimize_coding,
std::vector<uint8_t>* bytes) {
if (BITS_IN_JSAMPLE != 8 || sizeof(JSAMPLE) != 1) {
return JXL_FAILURE("Only 8 bit JSAMPLE is supported.");
}
@ -246,7 +247,7 @@ Status EncodeWithLibJpeg(const PackedImage& image, const JxlBasicInfo& info,
cinfo.input_components = info.num_color_channels;
cinfo.in_color_space = info.num_color_channels == 1 ? JCS_GRAYSCALE : JCS_RGB;
jpeg_set_defaults(&cinfo);
cinfo.optimize_coding = TRUE;
cinfo.optimize_coding = optimize_coding;
if (cinfo.input_components == 3) {
JXL_RETURN_IF_ERROR(SetChromaSubsampling(chroma_subsampling, &cinfo));
}
@ -327,8 +328,8 @@ Status EncodeImageJPG(const PackedImage& image, const JxlBasicInfo& info,
const std::vector<uint8_t>& icc,
std::vector<uint8_t> exif, JpegEncoder encoder,
size_t quality, const std::string& chroma_subsampling,
int progressive_id, ThreadPool* pool,
std::vector<uint8_t>* bytes) {
int progressive_id, bool optimize_coding,
ThreadPool* pool, std::vector<uint8_t>* bytes) {
if (image.format.data_type != JXL_TYPE_UINT8) {
return JXL_FAILURE("Unsupported pixel data type");
}
@ -343,7 +344,7 @@ Status EncodeImageJPG(const PackedImage& image, const JxlBasicInfo& info,
case JpegEncoder::kLibJpeg:
JXL_RETURN_IF_ERROR(EncodeWithLibJpeg(
image, info, color_encoding, icc, std::move(exif), quality,
chroma_subsampling, progressive_id, bytes));
chroma_subsampling, progressive_id, optimize_coding, bytes));
break;
case JpegEncoder::kSJpeg:
JXL_RETURN_IF_ERROR(EncodeWithSJpeg(image, info, icc, std::move(exif),
@ -376,6 +377,7 @@ class JPEGEncoder : public Encoder {
std::string chroma_subsampling = "444";
JpegEncoder jpeg_encoder = JpegEncoder::kLibJpeg;
int progressive_id = -1;
bool optimize_coding = true;
for (const auto& it : options()) {
if (it.first == "q") {
std::istringstream is(it.second);
@ -393,6 +395,8 @@ class JPEGEncoder : public Encoder {
} else if (it.first == "progressive") {
std::istringstream is(it.second);
JXL_RETURN_IF_ERROR(static_cast<bool>(is >> progressive_id));
} else if (it.first == "optimize" && it.second == "OFF") {
optimize_coding = false;
}
}
std::vector<uint8_t> icc;
@ -406,8 +410,8 @@ class JPEGEncoder : public Encoder {
encoded_image->bitstreams.emplace_back();
JXL_RETURN_IF_ERROR(EncodeImageJPG(
frame.color, ppf.info, ppf.color_encoding, icc, ppf.metadata.exif,
jpeg_encoder, quality, chroma_subsampling, progressive_id, pool,
&encoded_image->bitstreams.back()));
jpeg_encoder, quality, chroma_subsampling, progressive_id,
optimize_coding, pool, &encoded_image->bitstreams.back()));
}
return true;
}

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

@ -5,7 +5,8 @@
#include "lib/extras/enc/jxl.h"
#include "jxl/encode_cxx.h"
#include <jxl/encode_cxx.h>
#include "lib/jxl/exif.h"
namespace jxl {

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

@ -6,14 +6,14 @@
#ifndef LIB_EXTRAS_ENC_JXL_H_
#define LIB_EXTRAS_ENC_JXL_H_
#include <jxl/encode.h>
#include <jxl/parallel_runner.h>
#include <jxl/thread_parallel_runner.h>
#include <jxl/types.h>
#include <stdint.h>
#include <vector>
#include "jxl/encode.h"
#include "jxl/parallel_runner.h"
#include "jxl/thread_parallel_runner.h"
#include "jxl/types.h"
#include "lib/extras/packed_image.h"
namespace jxl {

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

@ -5,13 +5,13 @@
#include "lib/extras/enc/npy.h"
#include <jxl/types.h>
#include <stdio.h>
#include <sstream>
#include <string>
#include <vector>
#include "jxl/types.h"
#include "lib/extras/packed_image.h"
namespace jxl {

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

@ -5,10 +5,10 @@
#include "lib/extras/enc/pgx.h"
#include <jxl/codestream_header.h>
#include <stdio.h>
#include <string.h>
#include "jxl/codestream_header.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/byte_order.h"

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

@ -7,12 +7,12 @@
#include "lib/extras/dec/jpegli.h"
#include <jxl/color_encoding.h>
#include <stdint.h>
#include <memory>
#include <string>
#include "jxl/color_encoding.h"
#include "lib/extras/dec/color_hints.h"
#include "lib/extras/dec/decode.h"
#include "lib/extras/dec/jpg.h"
@ -20,18 +20,18 @@
#include "lib/extras/enc/jpegli.h"
#include "lib/extras/enc/jpg.h"
#include "lib/extras/packed_image.h"
#include "lib/jpegli/testing.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/size_constraints.h"
#include "lib/jxl/test_image.h"
#include "lib/jxl/test_utils.h"
#include "lib/jxl/testing.h"
namespace jxl {
namespace extras {
namespace {
using test::Butteraugli3Norm;
using test::ButteraugliDistance;
using test::TestImage;
@ -43,8 +43,7 @@ Status ReadTestImage(const std::string& pathname, PackedPixelFile* ppf) {
} else if (pathname.find(".pgm") != std::string::npos) {
color_hints.Add("color_space", "Gra_D65_Rel_SRG");
}
return DecodeBytes(Span<const uint8_t>(encoded), color_hints,
SizeConstraints(), ppf);
return DecodeBytes(Span<const uint8_t>(encoded), color_hints, ppf);
}
std::vector<uint8_t> GetAppData(const std::vector<uint8_t>& compressed) {
@ -66,9 +65,10 @@ std::vector<uint8_t> GetAppData(const std::vector<uint8_t>& compressed) {
}
Status DecodeWithLibjpeg(const std::vector<uint8_t>& compressed,
PackedPixelFile* ppf) {
return DecodeImageJPG(Span<const uint8_t>(compressed), ColorHints(),
SizeConstraints(), ppf);
PackedPixelFile* ppf,
const JPGDecompressParams* dparams = nullptr) {
return DecodeImageJPG(Span<const uint8_t>(compressed), ColorHints(), ppf,
/*constraints=*/nullptr, dparams);
}
Status EncodeWithLibjpeg(const PackedPixelFile& ppf, int quality,
@ -107,7 +107,8 @@ TEST(JpegliTest, JpegliSRGBDecodeTest) {
PackedPixelFile ppf1;
ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf1));
PackedPixelFile ppf2;
ASSERT_TRUE(DecodeJpeg(compressed, JXL_TYPE_UINT8, nullptr, &ppf2));
JpegDecompressParams dparams;
ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf2));
EXPECT_LT(ButteraugliDistance(ppf0, ppf2), ButteraugliDistance(ppf0, ppf1));
}
@ -124,7 +125,8 @@ TEST(JpegliTest, JpegliGrayscaleDecodeTest) {
PackedPixelFile ppf1;
ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf1));
PackedPixelFile ppf2;
ASSERT_TRUE(DecodeJpeg(compressed, JXL_TYPE_UINT8, nullptr, &ppf2));
JpegDecompressParams dparams;
ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf2));
EXPECT_LT(ButteraugliDistance(ppf0, ppf2), ButteraugliDistance(ppf0, ppf1));
}
@ -170,7 +172,8 @@ TEST(JpegliTest, JpegliDecodeTestLargeSmoothArea) {
ASSERT_TRUE(EncodeWithLibjpeg(ppf0, 90, &compressed));
PackedPixelFile ppf1;
ASSERT_TRUE(DecodeJpeg(compressed, JXL_TYPE_UINT8, nullptr, &ppf1));
JpegDecompressParams dparams;
ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf1));
EXPECT_LT(ButteraugliDistance(ppf0, ppf1), 3.0f);
}
@ -188,8 +191,8 @@ TEST(JpegliTest, JpegliYUVEncodeTest) {
PackedPixelFile ppf_out;
ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out));
EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.68f));
EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.28f));
EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.7f));
EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.32f));
}
TEST(JpegliTest, JpegliYUVChromaSubsamplingEncodeTest) {
@ -208,8 +211,8 @@ TEST(JpegliTest, JpegliYUVChromaSubsamplingEncodeTest) {
PackedPixelFile ppf_out;
ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out));
EXPECT_LE(BitsPerPixel(ppf_in, compressed), 1.6f);
EXPECT_LE(ButteraugliDistance(ppf_in, ppf_out), 1.8f);
EXPECT_LE(BitsPerPixel(ppf_in, compressed), 1.55f);
EXPECT_LE(ButteraugliDistance(ppf_in, ppf_out), 1.82f);
}
}
@ -228,8 +231,8 @@ TEST(JpegliTest, JpegliYUVEncodeTestNoAq) {
PackedPixelFile ppf_out;
ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf_out));
EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.72f));
EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.45f));
EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(1.85f));
EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.25f));
}
TEST(JpegliTest, JpegliHDRRoundtripTest) {
@ -245,7 +248,9 @@ TEST(JpegliTest, JpegliHDRRoundtripTest) {
ASSERT_TRUE(EncodeJpeg(ppf_in, settings, nullptr, &compressed));
PackedPixelFile ppf_out;
ASSERT_TRUE(DecodeJpeg(compressed, JXL_TYPE_UINT16, nullptr, &ppf_out));
JpegDecompressParams dparams;
dparams.output_data_type = JXL_TYPE_UINT16;
ASSERT_TRUE(DecodeJpeg(compressed, dparams, nullptr, &ppf_out));
EXPECT_THAT(BitsPerPixel(ppf_in, compressed), IsSlightlyBelow(2.95f));
EXPECT_THAT(ButteraugliDistance(ppf_in, ppf_out), IsSlightlyBelow(1.05f));
}
@ -305,6 +310,95 @@ TEST(JpegliTest, JpegliSetAppData) {
EXPECT_FALSE(EncodeJpeg(ppf_in, settings, nullptr, &compressed));
}
struct TestConfig {
int num_colors;
int passes;
int dither;
};
class JpegliColorQuantTestParam : public ::testing::TestWithParam<TestConfig> {
};
TEST_P(JpegliColorQuantTestParam, JpegliColorQuantizeTest) {
TestConfig config = GetParam();
std::string testimage = "jxl/flower/flower_small.rgb.depth8.ppm";
PackedPixelFile ppf0;
ASSERT_TRUE(ReadTestImage(testimage, &ppf0));
EXPECT_EQ("RGB_D65_SRG_Rel_SRG", Description(ppf0.color_encoding));
EXPECT_EQ(8, ppf0.info.bits_per_sample);
std::vector<uint8_t> compressed;
ASSERT_TRUE(EncodeWithLibjpeg(ppf0, 90, &compressed));
PackedPixelFile ppf1;
JPGDecompressParams dparams1;
dparams1.two_pass_quant = (config.passes == 2);
dparams1.num_colors = config.num_colors;
dparams1.dither_mode = config.dither;
ASSERT_TRUE(DecodeWithLibjpeg(compressed, &ppf1, &dparams1));
PackedPixelFile ppf2;
JpegDecompressParams dparams2;
dparams2.two_pass_quant = (config.passes == 2);
dparams2.num_colors = config.num_colors;
dparams2.dither_mode = config.dither;
ASSERT_TRUE(DecodeJpeg(compressed, dparams2, nullptr, &ppf2));
double dist1 = Butteraugli3Norm(ppf0, ppf1);
double dist2 = Butteraugli3Norm(ppf0, ppf2);
printf("distance: %f vs %f\n", dist2, dist1);
if (config.passes == 1) {
if (config.num_colors == 16 && config.dither == 2) {
// TODO(szabadka) Fix this case.
EXPECT_LT(dist2, dist1 * 1.5);
} else {
EXPECT_LT(dist2, dist1 * 1.05);
}
} else if (config.num_colors > 64) {
// TODO(szabadka) Fix 2pass quantization for <= 64 colors.
EXPECT_LT(dist2, dist1 * 1.1);
} else if (config.num_colors > 32) {
EXPECT_LT(dist2, dist1 * 1.2);
} else {
EXPECT_LT(dist2, dist1 * 1.7);
}
}
std::vector<TestConfig> GenerateTests() {
std::vector<TestConfig> all_tests;
for (int num_colors = 8; num_colors <= 256; num_colors *= 2) {
for (int passes = 1; passes <= 2; ++passes) {
for (int dither = 0; dither < 3; dither += passes) {
TestConfig config;
config.num_colors = num_colors;
config.passes = passes;
config.dither = dither;
all_tests.push_back(config);
}
}
}
return all_tests;
}
std::ostream& operator<<(std::ostream& os, const TestConfig& c) {
static constexpr const char* kDitherModeStr[] = {"No", "Ordered", "FS"};
os << c.passes << "pass";
os << c.num_colors << "colors";
os << kDitherModeStr[c.dither] << "dither";
return os;
}
std::string TestDescription(const testing::TestParamInfo<TestConfig>& info) {
std::stringstream name;
name << info.param;
return name.str();
}
JXL_GTEST_INSTANTIATE_TEST_SUITE_P(JpegliColorQuantTest,
JpegliColorQuantTestParam,
testing::ValuesIn(GenerateTests()),
TestDescription);
} // namespace
} // namespace extras
} // namespace jxl

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

@ -9,6 +9,9 @@
// Helper class for storing external (int or float, interleaved) images. This is
// the common format used by other libraries and in the libjxl API.
#include <jxl/codestream_header.h>
#include <jxl/encode.h>
#include <jxl/types.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
@ -19,9 +22,6 @@
#include <string>
#include <vector>
#include "jxl/codestream_header.h"
#include "jxl/encode.h"
#include "jxl/types.h"
#include "lib/jxl/common.h"
namespace jxl {

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

@ -5,10 +5,11 @@
#include "lib/extras/packed_image_convert.h"
#include <jxl/color_encoding.h>
#include <jxl/types.h>
#include <cstdint>
#include "jxl/color_encoding.h"
#include "jxl/types.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/color_management.h"
@ -168,14 +169,12 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
}
// Convert the pixels
io->dec_pixels = 0;
io->frames.clear();
for (const auto& frame : ppf.frames) {
ImageBundle bundle(&io->metadata.m);
JXL_RETURN_IF_ERROR(
ConvertPackedFrameToImageBundle(ppf.info, frame, *io, pool, &bundle));
io->frames.push_back(std::move(bundle));
io->dec_pixels += frame.color.xsize * frame.color.ysize;
}
if (ppf.info.exponent_bits_per_sample == 0) {
@ -218,6 +217,12 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
ppf->info.exponent_bits_per_sample =
io.metadata.m.bit_depth.exponent_bits_per_sample;
ppf->info.intensity_target = io.metadata.m.tone_mapping.intensity_target;
ppf->info.linear_below = io.metadata.m.tone_mapping.linear_below;
ppf->info.min_nits = io.metadata.m.tone_mapping.min_nits;
ppf->info.relative_to_max_display =
io.metadata.m.tone_mapping.relative_to_max_display;
ppf->info.alpha_bits = io.metadata.m.GetAlphaBits();
ppf->info.alpha_premultiplied = alpha_premultiplied;

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

@ -9,7 +9,8 @@
// Helper functions to convert from the external image types to the internal
// CodecInOut to help transitioning to the external types.
#include "jxl/types.h"
#include <jxl/types.h>
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"

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

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

@ -17,10 +17,10 @@
extern "C" {
#endif
#include "jxl/jxl_export.h"
#include "jxl/memory_manager.h"
#include "jxl/parallel_runner.h"
#include "jxl/types.h"
#include <jxl/jxl_export.h>
#include <jxl/memory_manager.h>
#include <jxl/parallel_runner.h>
#include <jxl/types.h>
/**
* Opaque structure that holds a butteraugli API.

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

@ -15,9 +15,9 @@
#ifndef JXL_BUTTERAUGLI_CXX_H_
#define JXL_BUTTERAUGLI_CXX_H_
#include <memory>
#include <jxl/butteraugli.h>
#include "jxl/butteraugli.h"
#include <memory>
#if !(defined(__cplusplus) || defined(c_plusplus))
#error "This a C++ only header. Use jxl/butteraugli.h from C sources."

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

@ -18,8 +18,8 @@
#ifndef JXL_CMS_INTERFACE_H_
#define JXL_CMS_INTERFACE_H_
#include "jxl/color_encoding.h"
#include "jxl/types.h"
#include <jxl/color_encoding.h>
#include <jxl/types.h>
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {

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

@ -15,11 +15,10 @@
#ifndef JXL_CODESTREAM_HEADER_H_
#define JXL_CODESTREAM_HEADER_H_
#include <jxl/types.h>
#include <stddef.h>
#include <stdint.h>
#include "jxl/types.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
@ -245,7 +244,7 @@ typedef struct {
*/
uint32_t intrinsic_xsize;
/** Intrinsic heigth of the image.
/** Intrinsic height of the image.
* The intrinsic size can be different from the actual size in pixels
* (as given by xsize and ysize) and it denotes the recommended dimensions
* for displaying the image, i.e. applications are advised to resample the
@ -380,6 +379,8 @@ typedef struct {
/** After blending, save the frame as reference frame with this ID (0-3).
* Special case: if the frame duration is nonzero, ID 0 means "will not be
* referenced in the future". This value is not used for the last frame.
* When encoding, ID 3 is reserved to frames that are generated internally by
* the encoder, and should not be used by applications.
*/
uint32_t save_as_reference;
} JxlLayerInfo;

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

@ -13,18 +13,17 @@
#ifndef JXL_DECODE_H_
#define JXL_DECODE_H_
#include <jxl/cms_interface.h>
#include <jxl/codestream_header.h>
#include <jxl/color_encoding.h>
#include <jxl/jxl_export.h>
#include <jxl/memory_manager.h>
#include <jxl/parallel_runner.h>
#include <jxl/types.h>
#include <jxl/version.h>
#include <stddef.h>
#include <stdint.h>
#include "jxl/cms_interface.h"
#include "jxl/codestream_header.h"
#include "jxl/color_encoding.h"
#include "jxl/jxl_export.h"
#include "jxl/memory_manager.h"
#include "jxl/parallel_runner.h"
#include "jxl/types.h"
#include "jxl/version.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif

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

@ -15,9 +15,9 @@
#ifndef JXL_DECODE_CXX_H_
#define JXL_DECODE_CXX_H_
#include <memory>
#include <jxl/decode.h>
#include "jxl/decode.h"
#include <memory>
#if !(defined(__cplusplus) || defined(c_plusplus))
#error "This a C++ only header. Use jxl/decode.h from C sources."

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

@ -13,12 +13,12 @@
#ifndef JXL_ENCODE_H_
#define JXL_ENCODE_H_
#include "jxl/cms_interface.h"
#include "jxl/codestream_header.h"
#include "jxl/jxl_export.h"
#include "jxl/memory_manager.h"
#include "jxl/parallel_runner.h"
#include "jxl/version.h"
#include <jxl/cms_interface.h>
#include <jxl/codestream_header.h>
#include <jxl/jxl_export.h>
#include <jxl/memory_manager.h>
#include <jxl/parallel_runner.h>
#include <jxl/version.h>
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {

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

@ -15,9 +15,9 @@
#ifndef JXL_ENCODE_CXX_H_
#define JXL_ENCODE_CXX_H_
#include <memory>
#include <jxl/encode.h>
#include "jxl/encode.h"
#include <memory>
#if !(defined(__cplusplus) || defined(c_plusplus))
#error "This a C++ only header. Use jxl/encode.h from C sources."

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

@ -30,15 +30,14 @@
#ifndef JXL_RESIZABLE_PARALLEL_RUNNER_H_
#define JXL_RESIZABLE_PARALLEL_RUNNER_H_
#include <jxl/jxl_threads_export.h>
#include <jxl/memory_manager.h>
#include <jxl/parallel_runner.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "jxl/jxl_threads_export.h"
#include "jxl/memory_manager.h"
#include "jxl/parallel_runner.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif

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

@ -16,9 +16,9 @@
#ifndef JXL_RESIZABLE_PARALLEL_RUNNER_CXX_H_
#define JXL_RESIZABLE_PARALLEL_RUNNER_CXX_H_
#include <memory>
#include <jxl/resizable_parallel_runner.h>
#include "jxl/resizable_parallel_runner.h"
#include <memory>
#if !(defined(__cplusplus) || defined(c_plusplus))
#error \

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

@ -30,15 +30,14 @@
#ifndef JXL_THREAD_PARALLEL_RUNNER_H_
#define JXL_THREAD_PARALLEL_RUNNER_H_
#include <jxl/jxl_threads_export.h>
#include <jxl/memory_manager.h>
#include <jxl/parallel_runner.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "jxl/jxl_threads_export.h"
#include "jxl/memory_manager.h"
#include "jxl/parallel_runner.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif

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

@ -15,9 +15,9 @@
#ifndef JXL_THREAD_PARALLEL_RUNNER_CXX_H_
#define JXL_THREAD_PARALLEL_RUNNER_CXX_H_
#include <memory>
#include <jxl/thread_parallel_runner.h>
#include "jxl/thread_parallel_runner.h"
#include <memory>
#if !(defined(__cplusplus) || defined(c_plusplus))
#error \

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

@ -13,11 +13,10 @@
#ifndef JXL_TYPES_H_
#define JXL_TYPES_H_
#include <jxl/jxl_export.h>
#include <stddef.h>
#include <stdint.h>
#include "jxl/jxl_export.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif

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

@ -8,7 +8,6 @@ include(jxl_lists.cmake)
set(JPEGLI_INTERNAL_LIBS
hwy
jxl-static
Threads::Threads
${ATOMICS_LIBRARIES}
)
@ -55,6 +54,7 @@ foreach (TESTFILE IN LISTS JPEGXL_INTERNAL_JPEGLI_TESTS)
GTest::Main
${JPEG_LIBRARIES}
)
set_target_properties(${TESTNAME} PROPERTIES LINK_FLAGS "${JPEGXL_COVERAGE_LINK_FLAGS}")
# Output test targets in the test directory.
set_target_properties(${TESTNAME} PROPERTIES PREFIX "tests/")
if (WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
@ -69,21 +69,6 @@ endif()
#
if (JPEGXL_ENABLE_JPEGLI_LIBJPEG AND NOT APPLE AND NOT WIN32 AND NOT JPEGXL_EMSCRIPTEN)
set(JPEGLI_LIBJPEG_MAJOR_VERSION 62)
set(JPEGLI_LIBJPEG_MINOR_VERSION 3)
set(JPEGLI_LIBJPEG_PATCH_VERSION 0)
set(JPEGLI_LIBJPEG_LIBRARY_VERSION
"${JPEGLI_LIBJPEG_MAJOR_VERSION}.${JPEGLI_LIBJPEG_MINOR_VERSION}.${JPEGLI_LIBJPEG_PATCH_VERSION}"
)
set(JPEGLI_LIBJPEG_LIBRARY_SOVERSION "${JPEGLI_LIBJPEG_MAJOR_VERSION}")
set(JPEGLI_LIBJPEG_OBJ_COMPILE_DEFINITIONS
JPEGLI_LIBJPEG_MAJOR_VERSION=${JPEGLI_LIBJPEG_MAJOR_VERSION}
JPEGLI_LIBJPEG_MINOR_VERSION=${JPEGLI_LIBJPEG_MINOR_VERSION}
JPEGLI_LIBJPEG_PATCH_VERSION=${JPEGLI_LIBJPEG_PATCH_VERSION}
)
add_library(jpegli-libjpeg-obj OBJECT "${JPEGXL_INTERNAL_JPEGLI_WRAPPER_SOURCES}")
target_compile_options(jpegli-libjpeg-obj PRIVATE ${JPEGXL_INTERNAL_FLAGS})
target_compile_options(jpegli-libjpeg-obj PUBLIC ${JPEGXL_COVERAGE_FLAGS})
@ -107,9 +92,9 @@ set_target_properties(jpeg PROPERTIES
# Add a jpeg.version file as a version script to tag symbols with the
# appropriate version number.
set_target_properties(jpeg PROPERTIES
LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/jpegli/jpeg.version)
LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/jpegli/jpeg.version.${JPEGLI_LIBJPEG_LIBRARY_SOVERSION})
set_property(TARGET jpeg APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/jpegli/jpeg.version")
LINK_FLAGS " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/jpegli/jpeg.version.${JPEGLI_LIBJPEG_LIBRARY_SOVERSION}")
# This hides the default visibility symbols from static libraries bundled into
# the shared library. In particular this prevents exposing symbols from hwy

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

@ -11,6 +11,7 @@
#include <algorithm>
#include <cmath>
#include <limits>
#include <string>
#include <vector>
@ -21,10 +22,7 @@
#include "lib/jpegli/encode_internal.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/image.h"
#include "lib/jxl/image_ops.h"
HWY_BEFORE_NAMESPACE();
namespace jpegli {
namespace HWY_NAMESPACE {
@ -49,9 +47,7 @@ using hwy::HWY_NAMESPACE::Sqrt;
using hwy::HWY_NAMESPACE::Sub;
using hwy::HWY_NAMESPACE::ZeroIfNegative;
static constexpr size_t kEncTileDim = 64;
;
static constexpr size_t kEncTileDimInBlocks = kEncTileDim / jxl::kBlockDim;
static constexpr float kInputScaling = 1.0f / 255.0f;
// Primary template: default to actual division.
template <typename T, class V>
@ -211,16 +207,22 @@ V RatioOfDerivativesOfCubicRootToSimpleGamma(const D d, V v) {
// SimpleGamma(v * v * v) is the psychovisual space in butteraugli.
// This ratio allows quantization to move from jxl's opsin space to
// butteraugli's log-gamma space.
float kEpsilon = 1e-2;
static const float kEpsilon = 1e-2;
static const float kNumOffset = kEpsilon / kInputScaling / kInputScaling;
static const float kNumMul = kSGRetMul * 3 * kSGmul;
static const float kVOffset = (kSGVOffset * kLog2 + kEpsilon) / kInputScaling;
static const float kDenMul = kLog2 * kSGmul * kInputScaling * kInputScaling;
v = ZeroIfNegative(v);
const auto kNumMul = Set(d, kSGRetMul * 3 * kSGmul);
const auto kVOffset = Set(d, kSGVOffset * kLog2 + kEpsilon);
const auto kDenMul = Set(d, kLog2 * kSGmul);
const auto num_mul = Set(d, kNumMul);
const auto num_offset = Set(d, kNumOffset);
const auto den_offset = Set(d, kVOffset);
const auto den_mul = Set(d, kDenMul);
const auto v2 = Mul(v, v);
const auto num = MulAdd(kNumMul, v2, Set(d, kEpsilon));
const auto den = MulAdd(Mul(kDenMul, v), v2, kVOffset);
const auto num = MulAdd(num_mul, v2, num_offset);
const auto den = MulAdd(Mul(den_mul, v), v2, den_offset);
return invert ? Div(num, den) : Div(den, num);
}
@ -257,20 +259,23 @@ V SimpleGamma(const D d, V v) {
template <class D, class V>
V GammaModulation(const D d, const size_t x, const size_t y,
const jxl::ImageF& xyb_y, const V out_val) {
const float kBias = 0.16f;
const RowBuffer<float>& input, const V out_val) {
static const float kBias = 0.16f / kInputScaling;
static const float kScale = kInputScaling / 64.0f;
auto overall_ratio = Zero(d);
auto bias = Set(d, kBias);
const auto bias = Set(d, kBias);
const auto scale = Set(d, kScale);
const float* const JXL_RESTRICT block_start = input.Row(y) + x;
for (size_t dy = 0; dy < 8; ++dy) {
const float* const JXL_RESTRICT row_in_y = xyb_y.Row(y + dy);
const float* const JXL_RESTRICT row_in = block_start + dy * input.stride();
for (size_t dx = 0; dx < 8; dx += Lanes(d)) {
const auto iny = Add(Load(d, row_in_y + x + dx), bias);
const auto iny = Add(Load(d, row_in + dx), bias);
const auto ratio_g =
RatioOfDerivativesOfCubicRootToSimpleGamma</*invert=*/true>(d, iny);
overall_ratio = Add(overall_ratio, ratio_g);
}
}
overall_ratio = Mul(SumOfLanes(d, overall_ratio), Set(d, 1.0f / 64));
overall_ratio = Mul(SumOfLanes(d, overall_ratio), scale);
// ideally -1.0, but likely optimal correction adds some entropy, so slightly
// less than that.
// ln(2) constant folded in because we want std::log but have FastLog2f.
@ -281,77 +286,64 @@ V GammaModulation(const D d, const size_t x, const size_t y,
// Change precision in 8x8 blocks that have high frequency content.
template <class D, class V>
V HfModulation(const D d, const size_t x, const size_t y,
const jxl::ImageF& xyb, const V out_val) {
const RowBuffer<float>& input, const V out_val) {
// Zero out the invalid differences for the rightmost value per row.
const Rebind<uint32_t, D> du;
HWY_ALIGN constexpr uint32_t kMaskRight[jxl::kBlockDim] = {~0u, ~0u, ~0u, ~0u,
~0u, ~0u, ~0u, 0};
HWY_ALIGN constexpr uint32_t kMaskRight[8] = {~0u, ~0u, ~0u, ~0u,
~0u, ~0u, ~0u, 0};
auto sum = Zero(d); // sum of absolute differences with right and below
static const float kSumCoeff = -2.0052193233688884f * kInputScaling / 112.0;
auto sumcoeff = Set(d, kSumCoeff);
const float* const JXL_RESTRICT block_start = input.Row(y) + x;
for (size_t dy = 0; dy < 8; ++dy) {
const float* JXL_RESTRICT row_in = xyb.Row(y + dy) + x;
const float* JXL_RESTRICT row_in = block_start + dy * input.stride();
const float* JXL_RESTRICT row_in_next =
dy == 7 ? row_in : xyb.Row(y + dy + 1) + x;
dy == 7 ? row_in : row_in + input.stride();
// In SCALAR, there is no guarantee of having extra row padding.
// Hence, we need to ensure we don't access pixels outside the row itself.
// In SIMD modes, however, rows are padded, so it's safe to access one
// garbage value after the row. The vector then gets masked with kMaskRight
// to remove the influence of that value.
#if HWY_TARGET != HWY_SCALAR
for (size_t dx = 0; dx < 8; dx += Lanes(d)) {
#else
for (size_t dx = 0; dx < 7; dx += Lanes(d)) {
#endif
const auto p = Load(d, row_in + dx);
const auto pr = LoadU(d, row_in + dx + 1);
const auto mask = BitCast(d, Load(du, kMaskRight + dx));
sum = Add(sum, And(mask, AbsDiff(p, pr)));
const auto pd = Load(d, row_in_next + dx);
sum = Add(sum, AbsDiff(p, pd));
}
#if HWY_TARGET == HWY_SCALAR
const auto p = Load(d, row_in + 7);
const auto pd = Load(d, row_in_next + 7);
sum = Add(sum, AbsDiff(p, pd));
#endif
}
sum = SumOfLanes(d, sum);
return MulAdd(sum, Set(d, -2.0052193233688884f / 112), out_val);
return MulAdd(sum, sumcoeff, out_val);
}
void PerBlockModulations(const float butteraugli_target,
const jxl::ImageF& xyb_y, const float scale,
const jxl::Rect& rect, jxl::ImageF* out) {
JXL_ASSERT(DivCeil(xyb_y.xsize(), jxl::kBlockDim) == out->xsize());
JXL_ASSERT(DivCeil(xyb_y.ysize(), jxl::kBlockDim) == out->ysize());
float base_level = 0.48f * scale;
float kDampenRampStart = 2.0f;
float kDampenRampEnd = 14.0f;
void PerBlockModulations(const float y_quant_01, const RowBuffer<float>& input,
const size_t yb0, const size_t yblen,
RowBuffer<float>* aq_map) {
static const float kAcQuant = 0.841f;
float base_level = 0.48f * kAcQuant;
float kDampenRampStart = 9.0f;
float kDampenRampEnd = 65.0f;
float dampen = 1.0f;
if (butteraugli_target >= kDampenRampStart) {
dampen = 1.0f - ((butteraugli_target - kDampenRampStart) /
if (y_quant_01 >= kDampenRampStart) {
dampen = 1.0f - ((y_quant_01 - kDampenRampStart) /
(kDampenRampEnd - kDampenRampStart));
if (dampen < 0) {
dampen = 0;
}
}
const float mul = scale * dampen;
const float mul = kAcQuant * dampen;
const float add = (1.0f - dampen) * base_level;
for (size_t iy = rect.y0(); iy < rect.y0() + rect.ysize(); iy++) {
const size_t y = iy * 8;
float* const JXL_RESTRICT row_out = out->Row(iy);
const HWY_CAPPED(float, jxl::kBlockDim) df;
for (size_t ix = rect.x0(); ix < rect.x0() + rect.xsize(); ix++) {
for (size_t iy = 0; iy < yblen; iy++) {
const size_t yb = yb0 + iy;
const size_t y = yb * 8;
float* const JXL_RESTRICT row_out = aq_map->Row(yb);
const HWY_CAPPED(float, 8) df;
for (size_t ix = 0; ix < aq_map->xsize(); ix++) {
size_t x = ix * 8;
auto out_val = Set(df, row_out[ix]);
out_val = ComputeMask(df, out_val);
out_val = HfModulation(df, x, y, xyb_y, out_val);
out_val = GammaModulation(df, x, y, xyb_y, out_val);
out_val = HfModulation(df, x, y, input, out_val);
out_val = GammaModulation(df, x, y, input, out_val);
// We want multiplicative quantization field, so everything
// until this point has been modulating the exponent.
row_out[ix] = FastPow2f(GetLane(out_val) * 1.442695041f) * mul + add;
@ -368,241 +360,133 @@ V MaskingSqrt(const D d, V v) {
return Mul(Set(d, 0.25f), Sqrt(MulAdd(v, Sqrt(mul_v), offset_v)));
}
float MaskingSqrt(const float v) {
using DScalar = HWY_CAPPED(float, 1);
auto vscalar = Load(DScalar(), &v);
return GetLane(MaskingSqrt(DScalar(), vscalar));
template <typename V>
void Sort4(V& min0, V& min1, V& min2, V& min3) {
const auto tmp0 = Min(min0, min1);
const auto tmp1 = Max(min0, min1);
const auto tmp2 = Min(min2, min3);
const auto tmp3 = Max(min2, min3);
const auto tmp4 = Max(tmp0, tmp2);
const auto tmp5 = Min(tmp1, tmp3);
min0 = Min(tmp0, tmp2);
min1 = Min(tmp4, tmp5);
min2 = Max(tmp4, tmp5);
min3 = Max(tmp1, tmp3);
}
void StoreMin4(const float v, float& min0, float& min1, float& min2,
float& min3) {
if (v < min3) {
if (v < min0) {
min3 = min2;
min2 = min1;
min1 = min0;
min0 = v;
} else if (v < min1) {
min3 = min2;
min2 = min1;
min1 = v;
} else if (v < min2) {
min3 = min2;
min2 = v;
} else {
min3 = v;
template <typename V>
void UpdateMin4(const V v, V& min0, V& min1, V& min2, V& min3) {
const auto tmp0 = Max(min0, v);
const auto tmp1 = Max(min1, tmp0);
const auto tmp2 = Max(min2, tmp1);
min0 = Min(min0, v);
min1 = Min(min1, tmp0);
min2 = Min(min2, tmp1);
min3 = Min(min3, tmp2);
}
// Computes a linear combination of the 4 lowest values of the 3x3 neighborhood
// of each pixel. Output is downsampled 2x.
void FuzzyErosion(const RowBuffer<float>& pre_erosion, const size_t yb0,
const size_t yblen, RowBuffer<float>* tmp,
RowBuffer<float>* aq_map) {
int xsize_blocks = aq_map->xsize();
int xsize = pre_erosion.xsize();
HWY_FULL(float) d;
const auto mul0 = Set(d, 0.125f);
const auto mul1 = Set(d, 0.075f);
const auto mul2 = Set(d, 0.06f);
const auto mul3 = Set(d, 0.05f);
for (size_t iy = 0; iy < 2 * yblen; ++iy) {
size_t y = 2 * yb0 + iy;
const float* JXL_RESTRICT rowt = pre_erosion.Row(y - 1);
const float* JXL_RESTRICT rowm = pre_erosion.Row(y);
const float* JXL_RESTRICT rowb = pre_erosion.Row(y + 1);
float* row_out = tmp->Row(y);
for (int x = 0; x < xsize; x += Lanes(d)) {
int xm1 = x - 1;
int xp1 = x + 1;
auto min0 = LoadU(d, rowm + x);
auto min1 = LoadU(d, rowm + xm1);
auto min2 = LoadU(d, rowm + xp1);
auto min3 = LoadU(d, rowt + xm1);
Sort4(min0, min1, min2, min3);
UpdateMin4(LoadU(d, rowt + x), min0, min1, min2, min3);
UpdateMin4(LoadU(d, rowt + xp1), min0, min1, min2, min3);
UpdateMin4(LoadU(d, rowb + xm1), min0, min1, min2, min3);
UpdateMin4(LoadU(d, rowb + x), min0, min1, min2, min3);
UpdateMin4(LoadU(d, rowb + xp1), min0, min1, min2, min3);
const auto v = Add(Add(Mul(mul0, min0), Mul(mul1, min1)),
Add(Mul(mul2, min2), Mul(mul3, min3)));
Store(v, d, row_out + x);
}
}
}
// Look for smooth areas near the area of degradation.
// If the areas are generally smooth, don't do masking.
// Output is downsampled 2x.
void FuzzyErosion(const jxl::Rect& from_rect, const jxl::ImageF& from,
const jxl::Rect& to_rect, jxl::ImageF* to) {
const size_t xsize = from.xsize();
const size_t ysize = from.ysize();
constexpr int kStep = 1;
static_assert(kStep == 1, "Step must be 1");
JXL_ASSERT(to_rect.xsize() * 2 == from_rect.xsize());
JXL_ASSERT(to_rect.ysize() * 2 == from_rect.ysize());
for (size_t fy = 0; fy < from_rect.ysize(); ++fy) {
size_t y = fy + from_rect.y0();
size_t ym1 = y >= kStep ? y - kStep : y;
size_t yp1 = y + kStep < ysize ? y + kStep : y;
const float* rowt = from.Row(ym1);
const float* row = from.Row(y);
const float* rowb = from.Row(yp1);
float* row_out = to_rect.Row(to, fy / 2);
for (size_t fx = 0; fx < from_rect.xsize(); ++fx) {
size_t x = fx + from_rect.x0();
size_t xm1 = x >= kStep ? x - kStep : x;
size_t xp1 = x + kStep < xsize ? x + kStep : x;
float min0 = row[x];
float min1 = row[xm1];
float min2 = row[xp1];
float min3 = rowt[xm1];
// Sort the first four values.
if (min0 > min1) std::swap(min0, min1);
if (min0 > min2) std::swap(min0, min2);
if (min0 > min3) std::swap(min0, min3);
if (min1 > min2) std::swap(min1, min2);
if (min1 > min3) std::swap(min1, min3);
if (min2 > min3) std::swap(min2, min3);
// The remaining five values of a 3x3 neighbourhood.
StoreMin4(rowt[x], min0, min1, min2, min3);
StoreMin4(rowt[xp1], min0, min1, min2, min3);
StoreMin4(rowb[xm1], min0, min1, min2, min3);
StoreMin4(rowb[x], min0, min1, min2, min3);
StoreMin4(rowb[xp1], min0, min1, min2, min3);
static const float kMul0 = 0.125f;
static const float kMul1 = 0.075f;
static const float kMul2 = 0.06f;
static const float kMul3 = 0.05f;
float v = kMul0 * min0 + kMul1 * min1 + kMul2 * min2 + kMul3 * min3;
if (fx % 2 == 0 && fy % 2 == 0) {
row_out[fx / 2] = v;
} else {
row_out[fx / 2] += v;
if (iy % 2 == 1) {
const float* JXL_RESTRICT row_out0 = tmp->Row(y - 1);
float* JXL_RESTRICT aq_out = aq_map->Row(yb0 + iy / 2);
for (int bx = 0, x = 0; bx < xsize_blocks; ++bx, x += 2) {
aq_out[bx] =
(row_out[x] + row_out[x + 1] + row_out0[x] + row_out0[x + 1]);
}
}
}
}
struct AdaptiveQuantizationImpl {
void Init(const jxl::ImageF& xyb_y) {
JXL_DASSERT(xyb_y.xsize() % jxl::kBlockDim == 0);
JXL_DASSERT(xyb_y.ysize() % jxl::kBlockDim == 0);
const size_t xsize = xyb_y.xsize();
const size_t ysize = xyb_y.ysize();
aq_map = jxl::ImageF(xsize / jxl::kBlockDim, ysize / jxl::kBlockDim);
}
void PrepareBuffers(size_t num_threads) {
diff_buffer = jxl::ImageF(kEncTileDim + 8, num_threads);
for (size_t i = pre_erosion.size(); i < num_threads; i++) {
pre_erosion.emplace_back(kEncTileDimInBlocks * 2 + 2,
kEncTileDimInBlocks * 2 + 2);
void ComputePreErosion(const RowBuffer<float>& input, const size_t xsize,
const size_t y0, const size_t ylen, int border,
float* diff_buffer, RowBuffer<float>* pre_erosion) {
const size_t xsize_out = xsize / 4;
const size_t y0_out = y0 / 4;
// The XYB gamma is 3.0 to be able to decode faster with two muls.
// Butteraugli's gamma is matching the gamma of human eye, around 2.6.
// We approximate the gamma difference by adding one cubic root into
// the adaptive quantization. This gives us a total gamma of 2.6666
// for quantization uses.
static const float match_gamma_offset = 0.019 / kInputScaling;
const HWY_CAPPED(float, 8) df;
static const float limit = 0.2f;
// Computes image (padded to multiple of 8x8) of local pixel differences.
// Subsample both directions by 4.
for (size_t iy = 0; iy < ylen; ++iy) {
size_t y = y0 + iy;
const float* row_in = input.Row(y);
const float* row_in1 = input.Row(y + 1);
const float* row_in2 = input.Row(y - 1);
float* JXL_RESTRICT row_out = diff_buffer;
const auto match_gamma_offset_v = Set(df, match_gamma_offset);
const auto quarter = Set(df, 0.25f);
for (size_t x = 0; x < xsize; x += Lanes(df)) {
const auto in = LoadU(df, row_in + x);
const auto in_r = LoadU(df, row_in + x + 1);
const auto in_l = LoadU(df, row_in + x - 1);
const auto in_t = LoadU(df, row_in2 + x);
const auto in_b = LoadU(df, row_in1 + x);
const auto base = Mul(quarter, Add(Add(in_r, in_l), Add(in_t, in_b)));
const auto gammacv =
RatioOfDerivativesOfCubicRootToSimpleGamma</*invert=*/false>(
df, Add(in, match_gamma_offset_v));
auto diff = Mul(gammacv, Sub(in, base));
diff = Mul(diff, diff);
diff = Min(diff, Set(df, limit));
diff = MaskingSqrt(df, diff);
if ((iy & 3) != 0) {
diff = Add(diff, LoadU(df, row_out + x));
}
StoreU(diff, df, row_out + x);
}
if (iy % 4 == 3) {
size_t y_out = y0_out + iy / 4;
float* row_dout = pre_erosion->Row(y_out);
for (size_t x = 0; x < xsize_out; x++) {
row_dout[x] = (row_out[x * 4] + row_out[x * 4 + 1] +
row_out[x * 4 + 2] + row_out[x * 4 + 3]) *
0.25f;
}
pre_erosion->PadRow(y_out, xsize_out, border);
}
}
void ComputeTile(float butteraugli_target, float scale,
const jxl::ImageF& xyb_y, const jxl::Rect& rect,
const int thread) {
const size_t xsize = xyb_y.xsize();
const size_t ysize = xyb_y.ysize();
// The XYB gamma is 3.0 to be able to decode faster with two muls.
// Butteraugli's gamma is matching the gamma of human eye, around 2.6.
// We approximate the gamma difference by adding one cubic root into
// the adaptive quantization. This gives us a total gamma of 2.6666
// for quantization uses.
const float match_gamma_offset = 0.019;
const HWY_FULL(float) df;
size_t y_start = rect.y0() * 8;
size_t y_end = y_start + rect.ysize() * 8;
size_t x0 = rect.x0() * 8;
size_t x1 = x0 + rect.xsize() * 8;
if (x0 != 0) x0 -= 4;
if (x1 != xyb_y.xsize()) x1 += 4;
if (y_start != 0) y_start -= 4;
if (y_end != xyb_y.ysize()) y_end += 4;
pre_erosion[thread].ShrinkTo((x1 - x0) / 4, (y_end - y_start) / 4);
static const float limit = 0.2f;
// Computes image (padded to multiple of 8x8) of local pixel differences.
// Subsample both directions by 4.
for (size_t y = y_start; y < y_end; ++y) {
size_t y2 = y + 1 < ysize ? y + 1 : y;
size_t y1 = y > 0 ? y - 1 : y;
const float* row_in = xyb_y.Row(y);
const float* row_in1 = xyb_y.Row(y1);
const float* row_in2 = xyb_y.Row(y2);
float* JXL_RESTRICT row_out = diff_buffer.Row(thread);
auto scalar_pixel = [&](size_t x) {
const size_t x2 = x + 1 < xsize ? x + 1 : x;
const size_t x1 = x > 0 ? x - 1 : x;
const float base =
0.25f * (row_in2[x] + row_in1[x] + row_in[x1] + row_in[x2]);
const float gammac = RatioOfDerivativesOfCubicRootToSimpleGamma(
row_in[x] + match_gamma_offset);
float diff = gammac * (row_in[x] - base);
diff *= diff;
if (diff >= limit) {
diff = limit;
}
diff = MaskingSqrt(diff);
if ((y % 4) != 0) {
row_out[x - x0] += diff;
} else {
row_out[x - x0] = diff;
}
};
size_t x = x0;
// First pixel of the row.
if (x0 == 0) {
scalar_pixel(x0);
++x;
}
// SIMD
const auto match_gamma_offset_v = Set(df, match_gamma_offset);
const auto quarter = Set(df, 0.25f);
for (; x + 1 + Lanes(df) < x1; x += Lanes(df)) {
const auto in = LoadU(df, row_in + x);
const auto in_r = LoadU(df, row_in + x + 1);
const auto in_l = LoadU(df, row_in + x - 1);
const auto in_t = LoadU(df, row_in2 + x);
const auto in_b = LoadU(df, row_in1 + x);
auto base = Mul(quarter, Add(Add(in_r, in_l), Add(in_t, in_b)));
auto gammacv =
RatioOfDerivativesOfCubicRootToSimpleGamma</*invert=*/false>(
df, Add(in, match_gamma_offset_v));
auto diff = Mul(gammacv, Sub(in, base));
diff = Mul(diff, diff);
diff = Min(diff, Set(df, limit));
diff = MaskingSqrt(df, diff);
if ((y & 3) != 0) {
diff = Add(diff, LoadU(df, row_out + x - x0));
}
StoreU(diff, df, row_out + x - x0);
}
// Scalar
for (; x < x1; ++x) {
scalar_pixel(x);
}
if (y % 4 == 3) {
float* row_dout = pre_erosion[thread].Row((y - y_start) / 4);
for (size_t x = 0; x < (x1 - x0) / 4; x++) {
row_dout[x] = (row_out[x * 4] + row_out[x * 4 + 1] +
row_out[x * 4 + 2] + row_out[x * 4 + 3]) *
0.25f;
}
}
}
jxl::Rect from_rect(x0 % 8 == 0 ? 0 : 1, y_start % 8 == 0 ? 0 : 1,
rect.xsize() * 2, rect.ysize() * 2);
FuzzyErosion(from_rect, pre_erosion[thread], rect, &aq_map);
PerBlockModulations(butteraugli_target, xyb_y, scale, rect, &aq_map);
}
std::vector<jxl::ImageF> pre_erosion;
jxl::ImageF aq_map;
jxl::ImageF diff_buffer;
};
jxl::ImageF AdaptiveQuantizationMap(const float butteraugli_target,
const jxl::ImageF& xyb_y, float scale,
jxl::ThreadPool* pool) {
AdaptiveQuantizationImpl impl;
impl.Init(xyb_y);
const size_t xsize_blocks = xyb_y.xsize() / jxl::kBlockDim;
const size_t ysize_blocks = xyb_y.ysize() / jxl::kBlockDim;
JXL_CHECK(RunOnPool(
pool, 0,
DivCeil(xsize_blocks, kEncTileDimInBlocks) *
DivCeil(ysize_blocks, kEncTileDimInBlocks),
[&](const size_t num_threads) {
impl.PrepareBuffers(num_threads);
return true;
},
[&](const uint32_t tid, const size_t thread) {
size_t n_enc_tiles = DivCeil(xsize_blocks, kEncTileDimInBlocks);
size_t tx = tid % n_enc_tiles;
size_t ty = tid / n_enc_tiles;
size_t by0 = ty * kEncTileDimInBlocks;
size_t by1 = std::min((ty + 1) * kEncTileDimInBlocks, ysize_blocks);
size_t bx0 = tx * kEncTileDimInBlocks;
size_t bx1 = std::min((tx + 1) * kEncTileDimInBlocks, xsize_blocks);
jxl::Rect r(bx0, by0, bx1 - bx0, by1 - by0);
impl.ComputeTile(butteraugli_target, scale, xyb_y, r, thread);
},
"AQ DiffPrecompute"));
return std::move(impl).aq_map;
}
} // namespace
@ -614,72 +498,63 @@ HWY_AFTER_NAMESPACE();
#if HWY_ONCE
namespace jpegli {
HWY_EXPORT(AdaptiveQuantizationMap);
HWY_EXPORT(ComputePreErosion);
HWY_EXPORT(FuzzyErosion);
HWY_EXPORT(PerBlockModulations);
namespace {
constexpr float kDcQuantPow = 0.66f;
static const float kDcQuant = 1.1f;
static const float kAcQuant = 0.841f;
static constexpr int kPreErosionBorder = 1;
} // namespace
float InitialQuantDC(float butteraugli_target) {
const float kDcMul = 1.5; // Butteraugli target where non-linearity kicks in.
const float butteraugli_target_dc = std::max<float>(
0.5f * butteraugli_target,
std::min<float>(butteraugli_target,
kDcMul * std::pow((1.0f / kDcMul) * butteraugli_target,
kDcQuantPow)));
// We want the maximum DC value to be at most 2**15 * kInvDCQuant / quant_dc.
// The maximum DC value might not be in the kXybRange because of inverse
// gaborish, so we add some slack to the maximum theoretical quant obtained
// this way (64).
return std::min(kDcQuant / butteraugli_target_dc, 50.f);
}
jxl::ImageF InitialQuantField(const float butteraugli_target,
const jxl::ImageF& opsin_y, jxl::ThreadPool* pool,
float rescale) {
const float quant_ac = kAcQuant / butteraugli_target;
return HWY_DYNAMIC_DISPATCH(AdaptiveQuantizationMap)(
butteraugli_target, opsin_y, quant_ac * rescale, pool);
}
void ComputeAdaptiveQuantField(j_compress_ptr cinfo) {
jpeg_comp_master* m = cinfo->master;
const size_t xsize_blocks = DivCeil(cinfo->image_width, DCTSIZE);
const size_t ysize_blocks = DivCeil(cinfo->image_height, DCTSIZE);
if (!m->use_adaptive_quantization) {
return;
}
int y_channel = cinfo->jpeg_color_space == JCS_RGB ? 1 : 0;
jpeg_component_info* y_comp = &cinfo->comp_info[y_channel];
m->quant_field.Allocate(ysize_blocks, xsize_blocks);
if (m->use_adaptive_quantization &&
y_comp->h_samp_factor == cinfo->max_h_samp_factor &&
y_comp->v_samp_factor == cinfo->max_v_samp_factor) {
JXL_ASSERT(y_comp->width_in_blocks == xsize_blocks);
JXL_ASSERT(y_comp->height_in_blocks == ysize_blocks);
jxl::ImageF input(y_comp->width_in_blocks * DCTSIZE,
y_comp->height_in_blocks * DCTSIZE);
for (size_t y = 0; y < input.ysize(); ++y) {
memcpy(input.Row(y), m->input_buffer[y_channel].Row(y),
input.xsize() * sizeof(float));
}
jxl::ImageF qf =
jpegli::InitialQuantField(m->distance, input, nullptr, m->distance);
float qfmin, qfmax;
ImageMinMax(qf, &qfmin, &qfmax);
m->quant_field_max = qfmax;
for (size_t y = 0; y < y_comp->height_in_blocks; ++y) {
const float* row_in = qf.Row(y);
float* row_out = m->quant_field.Row(y);
for (size_t x = 0; x < y_comp->width_in_blocks; ++x) {
row_out[x] = (qfmax / row_in[x]) - 1.0f;
}
}
int y_quant_01 = cinfo->quant_tbl_ptrs[y_comp->quant_tbl_no]->quantval[1];
if (m->next_iMCU_row == 0) {
m->input_buffer[y_channel].CopyRow(-1, 0, 1);
}
if (m->next_iMCU_row + 1 == cinfo->total_iMCU_rows) {
size_t last_row = m->ysize_blocks * DCTSIZE - 1;
m->input_buffer[y_channel].CopyRow(last_row + 1, last_row, 1);
}
const RowBuffer<float>& input = m->input_buffer[y_channel];
const size_t xsize_blocks = y_comp->width_in_blocks;
const size_t xsize = xsize_blocks * DCTSIZE;
const size_t yb0 = m->next_iMCU_row * cinfo->max_v_samp_factor;
const size_t yblen = cinfo->max_v_samp_factor;
size_t y0 = yb0 * DCTSIZE;
size_t ylen = cinfo->max_v_samp_factor * DCTSIZE;
if (y0 == 0) {
ylen += 4;
} else {
m->quant_field_max = kDefaultQuantFieldMax;
for (size_t y = 0; y < ysize_blocks; ++y) {
m->quant_field.FillRow(y, 0.0f, xsize_blocks);
y0 += 4;
}
if (m->next_iMCU_row + 1 == cinfo->total_iMCU_rows) {
ylen -= 4;
}
HWY_DYNAMIC_DISPATCH(ComputePreErosion)
(input, xsize, y0, ylen, kPreErosionBorder, m->diff_buffer, &m->pre_erosion);
if (y0 == 0) {
m->pre_erosion.CopyRow(-1, 0, kPreErosionBorder);
}
if (m->next_iMCU_row + 1 == cinfo->total_iMCU_rows) {
size_t last_row = m->ysize_blocks * 2 - 1;
m->pre_erosion.CopyRow(last_row + 1, last_row, kPreErosionBorder);
}
HWY_DYNAMIC_DISPATCH(FuzzyErosion)
(m->pre_erosion, yb0, yblen, &m->fuzzy_erosion_tmp, &m->quant_field);
HWY_DYNAMIC_DISPATCH(PerBlockModulations)
(y_quant_01, input, yb0, yblen, &m->quant_field);
for (int y = 0; y < cinfo->max_v_samp_factor; ++y) {
float* row = m->quant_field.Row(yb0 + y);
for (size_t x = 0; x < xsize_blocks; ++x) {
row[x] = std::max(0.0f, (0.6f / row[x]) - 1.0f);
}
}
}

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

@ -16,8 +16,6 @@ namespace jpegli {
void ComputeAdaptiveQuantField(j_compress_ptr cinfo);
float InitialQuantDC(float butteraugli_target);
} // namespace jpegli
#endif // LIB_JPEGLI_ADAPTIVE_QUANTIZATION_H_

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

@ -0,0 +1,60 @@
// 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/jpegli/bit_writer.h"
#include "lib/jpegli/encode_internal.h"
namespace jpegli {
void JpegBitWriterInit(j_compress_ptr cinfo) {
jpeg_comp_master* m = cinfo->master;
JpegBitWriter* bw = &m->bw;
size_t buffer_size = m->blocks_per_iMCU_row * (DCTSIZE2 * 16 + 8) + (1 << 16);
bw->cinfo = cinfo;
bw->data = Allocate<uint8_t>(cinfo, buffer_size, JPOOL_IMAGE);
bw->len = buffer_size;
bw->pos = 0;
bw->output_pos = 0;
bw->put_buffer = 0;
bw->free_bits = 64;
bw->healthy = true;
}
bool EmptyBitWriterBuffer(JpegBitWriter* bw) {
while (bw->output_pos < bw->pos) {
j_compress_ptr cinfo = bw->cinfo;
if (cinfo->dest->free_in_buffer == 0 &&
!(*cinfo->dest->empty_output_buffer)(cinfo)) {
return false;
}
size_t buflen = bw->pos - bw->output_pos;
size_t copylen = std::min<size_t>(cinfo->dest->free_in_buffer, buflen);
memcpy(cinfo->dest->next_output_byte, bw->data + bw->output_pos, copylen);
bw->output_pos += copylen;
cinfo->dest->free_in_buffer -= copylen;
cinfo->dest->next_output_byte += copylen;
}
bw->output_pos = bw->pos = 0;
return true;
}
void JumpToByteBoundary(JpegBitWriter* bw) {
size_t n_bits = bw->free_bits & 7u;
if (n_bits > 0) {
WriteBits(bw, n_bits, (1u << n_bits) - 1);
}
bw->put_buffer <<= bw->free_bits;
while (bw->free_bits <= 56) {
int c = (bw->put_buffer >> 56) & 0xFF;
EmitByte(bw, c);
bw->put_buffer <<= 8;
bw->free_bits += 8;
}
bw->put_buffer = 0;
bw->free_bits = 64;
}
} // namespace jpegli

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

@ -0,0 +1,107 @@
// 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_JPEGLI_BIT_WRITER_H_
#define LIB_JPEGLI_BIT_WRITER_H_
/* clang-format off */
#include <stdio.h>
#include <jpeglib.h>
#include <stdint.h>
#include <string.h>
/* clang-format on */
#include "lib/jxl/base/compiler_specific.h"
namespace jpegli {
// Handles the packing of bits into output bytes.
struct JpegBitWriter {
j_compress_ptr cinfo;
uint8_t* data;
size_t len;
size_t pos;
size_t output_pos;
uint64_t put_buffer;
int free_bits;
bool healthy;
};
void JpegBitWriterInit(j_compress_ptr cinfo);
bool EmptyBitWriterBuffer(JpegBitWriter* bw);
void JumpToByteBoundary(JpegBitWriter* bw);
// Returns non-zero if and only if x has a zero byte, i.e. one of
// x & 0xff, x & 0xff00, ..., x & 0xff00000000000000 is zero.
static JXL_INLINE uint64_t HasZeroByte(uint64_t x) {
return (x - 0x0101010101010101ULL) & ~x & 0x8080808080808080ULL;
}
/**
* Writes the given byte to the output, writes an extra zero if byte is 0xFF.
*
* This method is "careless" - caller must make sure that there is enough
* space in the output buffer. Emits up to 2 bytes to buffer.
*/
static JXL_INLINE void EmitByte(JpegBitWriter* bw, int byte) {
bw->data[bw->pos++] = byte;
if (byte == 0xFF) bw->data[bw->pos++] = 0;
}
static JXL_INLINE void DischargeBitBuffer(JpegBitWriter* bw) {
// At this point we are ready to emit the bytes of put_buffer to the output.
// The JPEG format requires that after every 0xff byte in the entropy
// coded section, there is a zero byte, therefore we first check if any of
// the bytes of put_buffer is 0xFF.
if (HasZeroByte(~bw->put_buffer)) {
// We have a 0xFF byte somewhere, examine each byte and append a zero
// byte if necessary.
EmitByte(bw, (bw->put_buffer >> 56) & 0xFF);
EmitByte(bw, (bw->put_buffer >> 48) & 0xFF);
EmitByte(bw, (bw->put_buffer >> 40) & 0xFF);
EmitByte(bw, (bw->put_buffer >> 32) & 0xFF);
EmitByte(bw, (bw->put_buffer >> 24) & 0xFF);
EmitByte(bw, (bw->put_buffer >> 16) & 0xFF);
EmitByte(bw, (bw->put_buffer >> 8) & 0xFF);
EmitByte(bw, (bw->put_buffer >> 0) & 0xFF);
} else {
// We don't have any 0xFF bytes, output all 6 bytes without checking.
bw->data[bw->pos] = (bw->put_buffer >> 56) & 0xFF;
bw->data[bw->pos + 1] = (bw->put_buffer >> 48) & 0xFF;
bw->data[bw->pos + 2] = (bw->put_buffer >> 40) & 0xFF;
bw->data[bw->pos + 3] = (bw->put_buffer >> 32) & 0xFF;
bw->data[bw->pos + 4] = (bw->put_buffer >> 24) & 0xFF;
bw->data[bw->pos + 5] = (bw->put_buffer >> 16) & 0xFF;
bw->data[bw->pos + 6] = (bw->put_buffer >> 8) & 0xFF;
bw->data[bw->pos + 7] = (bw->put_buffer >> 0) & 0xFF;
bw->pos += 8;
}
}
static JXL_INLINE void WriteBits(JpegBitWriter* bw, int nbits, uint64_t bits) {
// This is an optimization; if everything goes well,
// then |nbits| is positive; if non-existing Huffman symbol is going to be
// encoded, its length should be zero; later encoder could check the
// "health" of JpegBitWriter.
if (nbits == 0) {
bw->healthy = false;
return;
}
bw->free_bits -= nbits;
if (bw->free_bits < 0) {
bw->put_buffer <<= (bw->free_bits + nbits);
bw->put_buffer |= (bits >> -bw->free_bits);
DischargeBitBuffer(bw);
bw->free_bits += 64;
bw->put_buffer = nbits;
}
bw->put_buffer <<= nbits;
bw->put_buffer |= bits;
}
} // namespace jpegli
#endif // LIB_JPEGLI_BIT_WRITER_H_

832
third_party/jpeg-xl/lib/jpegli/bitstream.cc поставляемый

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

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

@ -19,19 +19,18 @@ void WriteOutput(j_compress_ptr cinfo, std::initializer_list<uint8_t> bytes);
void EncodeAPP0(j_compress_ptr cinfo);
void EncodeAPP14(j_compress_ptr cinfo);
void EncodeSOF(j_compress_ptr cinfo);
void EncodeSOF(j_compress_ptr cinfo, bool is_baseline);
void EncodeSOS(j_compress_ptr cinfo, int scan_index);
void EncodeDHT(j_compress_ptr cinfo, const JPEGHuffmanCode* huffman_codes,
size_t num_huffman_codes);
void EncodeDQT(j_compress_ptr cinfo);
size_t num_huffman_codes, bool pre_shifted = false);
void EncodeDQT(j_compress_ptr cinfo, bool write_all_tables, bool* is_baseline);
bool EncodeDRI(j_compress_ptr cinfo);
bool EncodeScan(j_compress_ptr cinfo,
const std::vector<std::vector<coeff_t>>& coeffs,
int scan_index);
bool EncodeScan(j_compress_ptr cinfo, int scan_index);
void EncodeSingleScan(j_compress_ptr cinfo,
const std::vector<std::vector<coeff_t>>& coeffs);
void EncodeSingleScan(j_compress_ptr cinfo);
void WriteiMCURow(j_compress_ptr cinfo);
} // namespace jpegli

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

@ -0,0 +1,533 @@
// 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/jpegli/color_quantize.h"
#include <cmath>
#include <limits>
#include <unordered_map>
#include "lib/jpegli/decode_internal.h"
#include "lib/jpegli/error.h"
namespace jpegli {
namespace {
static constexpr int kNumColorCellBits[kMaxComponents] = {3, 4, 3, 3};
static constexpr int kCompW[kMaxComponents] = {2, 3, 1, 1};
int Pow(int a, int b) {
int r = 1;
for (int i = 0; i < b; ++i) {
r *= a;
}
return r;
}
int ComponentOrder(j_decompress_ptr cinfo, int i) {
if (cinfo->out_color_components == 3) {
return i < 2 ? 1 - i : i;
}
return i;
}
int GetColorComponent(int i, int N) {
return (i * 255 + (N - 1) / 2) / (N - 1);
}
} // namespace
void ChooseColorMap1Pass(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
int components = cinfo->out_color_components;
int desired = std::min(cinfo->desired_number_of_colors, 256);
int num = 1;
while (Pow(num + 1, components) <= desired) {
++num;
}
if (num == 1) {
JPEGLI_ERROR("Too few colors (%d) in requested colormap", desired);
}
int actual = Pow(num, components);
for (int i = 0; i < components; ++i) {
m->num_colors_[i] = num;
}
while (actual < desired) {
int total = actual;
for (int i = 0; i < components; ++i) {
int c = ComponentOrder(cinfo, i);
int new_total = (actual / m->num_colors_[c]) * (m->num_colors_[c] + 1);
if (new_total <= desired) {
++m->num_colors_[c];
actual = new_total;
}
}
if (actual == total) {
break;
}
}
cinfo->actual_number_of_colors = actual;
cinfo->colormap = (*cinfo->mem->alloc_sarray)(
reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, actual, components);
int next_color[kMaxComponents] = {0};
for (int i = 0; i < actual; ++i) {
for (int c = 0; c < components; ++c) {
cinfo->colormap[c][i] =
GetColorComponent(next_color[c], m->num_colors_[c]);
}
int c = components - 1;
while (c > 0 && next_color[c] + 1 == m->num_colors_[c]) {
next_color[c--] = 0;
}
++next_color[c];
}
if (!m->colormap_lut_) {
m->colormap_lut_ = Allocate<uint8_t>(cinfo, components * 256, JPOOL_IMAGE);
}
int stride = actual;
for (int c = 0; c < components; ++c) {
int N = m->num_colors_[c];
stride /= N;
for (int i = 0; i < 256; ++i) {
int index = ((2 * i - 1) * (N - 1) + 254) / 510;
m->colormap_lut_[c * 256 + i] = index * stride;
}
}
}
namespace {
// 2^13 priority levels for the PQ seems to be a good compromise between
// accuracy, running time and stack space usage.
static const int kMaxPriority = 1 << 13;
static const int kMaxLevel = 3;
// This function is used in the multi-resolution grid to be able to compute
// the keys for the different resolutions by just shifting the first key.
inline int InterlaceBitsRGB(uint8_t r, uint8_t g, uint8_t b) {
int z = 0;
for (int i = 0; i < 7; ++i) {
z += (r >> 5) & 4;
z += (g >> 6) & 2;
z += (b >> 7);
z <<= 3;
r <<= 1;
g <<= 1;
b <<= 1;
}
z += (r >> 5) & 4;
z += (g >> 6) & 2;
z += (b >> 7);
return z;
}
// This function will compute the actual priorities of the colors based on
// the current distance from the palette, the population count and the signals
// from the multi-resolution grid.
inline int Priority(int d, int n, const int* density, const int* radius) {
int p = d * n;
for (int level = 0; level < kMaxLevel; ++level) {
if (d > radius[level]) {
p += density[level] * (d - radius[level]);
}
}
return std::min(kMaxPriority - 1, p >> 4);
}
inline int ColorIntQuadDistanceRGB(uint8_t r1, uint8_t g1, uint8_t b1,
uint8_t r2, uint8_t g2, uint8_t b2) {
// weights for the intensity calculation
static constexpr int ired = 2;
static constexpr int igreen = 5;
static constexpr int iblue = 1;
// normalization factor for the intensity calculation (2^ishift)
static constexpr int ishift = 3;
const int rd = r1 - r2;
const int gd = g1 - g2;
const int bd = b1 - b2;
const int id = ired * rd + igreen * gd + iblue * bd;
return rd * rd + gd * gd + bd * bd + ((id * id) >> (2 * ishift));
}
inline int ScaleQuadDistanceRGB(int d) {
return static_cast<int>(sqrt(d * 0.25) + 0.5);
}
// The function updates the minimal distances, the clustering and the
// quantization error after the insertion of the new color into the palette.
void AddToRGBPalette(const uint8_t* red, const uint8_t* green,
const uint8_t* blue,
const int* count, // histogram of colors
const int index, // index of color to be added
const int k, // size of current palette
const int n, // number of colors
int* dist, // array of distances from palette
int* cluster, // mapping of color indices to palette
int* center, // the inverse mapping
int64_t* error) { // measure of the quantization error
center[k] = index;
cluster[index] = k;
*error -=
static_cast<int64_t>(dist[index]) * static_cast<int64_t>(count[index]);
dist[index] = 0;
for (int j = 0; j < n; ++j) {
if (dist[j] > 0) {
const int d = ColorIntQuadDistanceRGB(
red[index], green[index], blue[index], red[j], green[j], blue[j]);
if (d < dist[j]) {
*error += static_cast<int64_t>((d - dist[j])) *
static_cast<int64_t>(count[j]);
dist[j] = d;
cluster[j] = k;
}
}
}
}
struct RGBPixelHasher {
// A quick but good-enough hash to get 24 bits of RGB into the lower 12 bits.
size_t operator()(uint32_t a) const { return (a ^ (a >> 12)) * 0x9e3779b9; }
};
struct WangHasher {
// Thomas Wang's Hash. Nearly perfect and still quite fast. Above (for
// pixels) we use a simpler hash because the number of hash calls is
// proportional to the number of pixels and that hash dominates; we want the
// cost to be minimal and we start with a large table. We can use a better
// hash for the histogram since the number of hash calls is proportional to
// the number of unique colors in the image, which is hopefully much smaller.
// Note that the difference is slight; e.g. replacing RGBPixelHasher with
// WangHasher only slows things down by 5% on an Opteron.
size_t operator()(uint32_t a) const {
a = (a ^ 61) ^ (a >> 16);
a = a + (a << 3);
a = a ^ (a >> 4);
a = a * 0x27d4eb2d;
a = a ^ (a >> 15);
return a;
}
};
// Build an index of all the different colors in the input
// image. To do this we map the 24 bit RGB representation of the colors
// to a unique integer index assigned to the different colors in order of
// appearence in the image. Return the number of unique colors found.
// The colors are pre-quantized to 3 * 6 bits precision.
static int BuildRGBColorIndex(const uint8_t* const image, int const num_pixels,
int* const count, uint8_t* const red,
uint8_t* const green, uint8_t* const blue) {
// Impossible because rgb are in the low 24 bits, and the upper 8 bits is 0.
const uint32_t impossible_pixel_value = 0x10000000;
std::unordered_map<uint32_t, int, RGBPixelHasher> index_map(1 << 12);
std::unordered_map<uint32_t, int, RGBPixelHasher>::iterator index_map_lookup;
const uint8_t* imagep = &image[0];
uint32_t prev_pixel = impossible_pixel_value;
int index = 0;
int n = 0;
for (int i = 0; i < num_pixels; ++i) {
uint8_t r = ((*imagep++) & 0xfc) + 2;
uint8_t g = ((*imagep++) & 0xfc) + 2;
uint8_t b = ((*imagep++) & 0xfc) + 2;
uint32_t pixel = (b << 16) | (g << 8) | r;
if (pixel != prev_pixel) {
prev_pixel = pixel;
index_map_lookup = index_map.find(pixel);
if (index_map_lookup != index_map.end()) {
index = index_map_lookup->second;
} else {
index_map[pixel] = index = n++;
red[index] = r;
green[index] = g;
blue[index] = b;
}
}
++count[index];
}
return n;
}
} // namespace
void ChooseColorMap2Pass(j_decompress_ptr cinfo) {
if (cinfo->out_color_space != JCS_RGB) {
JPEGLI_ERROR("Two-pass quantizer must use RGB output color space.");
}
jpeg_decomp_master* m = cinfo->master;
const size_t num_pixels = cinfo->output_width * cinfo->output_height;
const int max_color_count = std::max<size_t>(num_pixels, 1u << 18);
const int max_palette_size = cinfo->desired_number_of_colors;
std::unique_ptr<uint8_t[]> red(new uint8_t[max_color_count]);
std::unique_ptr<uint8_t[]> green(new uint8_t[max_color_count]);
std::unique_ptr<uint8_t[]> blue(new uint8_t[max_color_count]);
std::vector<int> count(max_color_count, 0);
// number of colors
int n = BuildRGBColorIndex(m->pixels_, num_pixels, &count[0], &red[0],
&green[0], &blue[0]);
std::vector<int> dist(n, std::numeric_limits<int>::max());
std::vector<int> cluster(n);
std::vector<bool> in_palette(n, false);
int center[256];
int k = 0; // palette size
const int count_threshold = (num_pixels * 4) / max_palette_size;
static constexpr int kAveragePixelErrorThreshold = 1;
const int64_t error_threshold = num_pixels * kAveragePixelErrorThreshold;
int64_t error = 0; // quantization error
int max_count = 0;
int winner = 0;
for (int i = 0; i < n; ++i) {
if (count[i] > max_count) {
max_count = count[i];
winner = i;
}
if (!in_palette[i] && count[i] > count_threshold) {
AddToRGBPalette(&red[0], &green[0], &blue[0], &count[0], i, k++, n,
&dist[0], &cluster[0], &center[0], &error);
in_palette[i] = true;
}
}
if (k == 0) {
AddToRGBPalette(&red[0], &green[0], &blue[0], &count[0], winner, k++, n,
&dist[0], &cluster[0], &center[0], &error);
in_palette[winner] = true;
}
// Calculation of the multi-resolution density grid.
std::vector<int> density(n * kMaxLevel);
std::vector<int> radius(n * kMaxLevel);
std::unordered_map<uint32_t, int, WangHasher> histogram[kMaxLevel];
for (int level = 0; level < kMaxLevel; ++level) {
// This value is never used because key = InterlaceBitsRGB(...) >> 6
}
for (int i = 0; i < n; ++i) {
if (!in_palette[i]) {
const int key = InterlaceBitsRGB(red[i], green[i], blue[i]) >> 6;
for (int level = 0; level < kMaxLevel; ++level) {
histogram[level][key >> (3 * level)] += count[i];
}
}
}
for (int i = 0; i < n; ++i) {
if (!in_palette[i]) {
for (int level = 0; level < kMaxLevel; ++level) {
const int mask = (4 << level) - 1;
const int rd = std::max(red[i] & mask, mask - (red[i] & mask));
const int gd = std::max(green[i] & mask, mask - (green[i] & mask));
const int bd = std::max(blue[i] & mask, mask - (blue[i] & mask));
radius[i * kMaxLevel + level] =
ScaleQuadDistanceRGB(ColorIntQuadDistanceRGB(0, 0, 0, rd, gd, bd));
}
const int key = InterlaceBitsRGB(red[i], green[i], blue[i]) >> 6;
if (kMaxLevel > 0) {
density[i * kMaxLevel] = histogram[0][key] - count[i];
}
for (int level = 1; level < kMaxLevel; ++level) {
density[i * kMaxLevel + level] =
(histogram[level][key >> (3 * level)] -
histogram[level - 1][key >> (3 * level - 3)]);
}
}
}
// Calculate the initial error now that the palette has been initialized.
error = 0;
for (int i = 0; i < n; ++i) {
error += static_cast<int64_t>(dist[i]) * static_cast<int64_t>(count[i]);
}
std::unique_ptr<std::vector<int>[]> bucket_array(
new std::vector<int>[kMaxPriority]);
int top_priority = -1;
for (int i = 0; i < n; ++i) {
if (!in_palette[i]) {
int priority = Priority(ScaleQuadDistanceRGB(dist[i]), count[i],
&density[i * kMaxLevel], &radius[i * kMaxLevel]);
bucket_array[priority].push_back(i);
top_priority = std::max(priority, top_priority);
}
}
double error_accum = 0;
while (top_priority >= 0 && k < max_palette_size) {
if (error < error_threshold) {
error_accum += std::min(error_threshold, error_threshold - error);
if (error_accum >= 10 * error_threshold) {
break;
}
}
int i = bucket_array[top_priority].back();
int priority = Priority(ScaleQuadDistanceRGB(dist[i]), count[i],
&density[i * kMaxLevel], &radius[i * kMaxLevel]);
if (priority < top_priority) {
bucket_array[priority].push_back(i);
} else {
AddToRGBPalette(&red[0], &green[0], &blue[0], &count[0], i, k++, n,
&dist[0], &cluster[0], &center[0], &error);
}
bucket_array[top_priority].pop_back();
while (top_priority >= 0 && bucket_array[top_priority].empty()) {
--top_priority;
}
}
cinfo->actual_number_of_colors = k;
cinfo->colormap = (*cinfo->mem->alloc_sarray)(
reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, k, 3);
for (int i = 0; i < k; ++i) {
int index = center[i];
cinfo->colormap[0][i] = red[index];
cinfo->colormap[1][i] = green[index];
cinfo->colormap[2][i] = blue[index];
}
}
namespace {
void FindCandidatesForCell(j_decompress_ptr cinfo, int ncomp, int cell[],
std::vector<uint8_t>* candidates) {
int cell_min[kMaxComponents];
int cell_max[kMaxComponents];
int cell_center[kMaxComponents];
for (int c = 0; c < ncomp; ++c) {
cell_min[c] = cell[c] << (8 - kNumColorCellBits[c]);
cell_max[c] = cell_min[c] + (1 << (8 - kNumColorCellBits[c])) - 1;
cell_center[c] = (cell_min[c] + cell_max[c]) >> 1;
}
int min_maxdist = std::numeric_limits<int>::max();
int mindist[256];
for (int i = 0; i < cinfo->actual_number_of_colors; ++i) {
int dmin = 0;
int dmax = 0;
for (int c = 0; c < ncomp; ++c) {
int palette_c = cinfo->colormap[c][i];
int dminc = 0, dmaxc;
if (palette_c < cell_min[c]) {
dminc = cell_min[c] - palette_c;
dmaxc = cell_max[c] - palette_c;
} else if (palette_c > cell_max[c]) {
dminc = palette_c - cell_max[c];
dmaxc = palette_c - cell_min[c];
} else if (palette_c > cell_center[c]) {
dmaxc = palette_c - cell_min[c];
} else {
dmaxc = cell_max[c] - palette_c;
}
dminc *= kCompW[c];
dmaxc *= kCompW[c];
dmin += dminc * dminc;
dmax += dmaxc * dmaxc;
}
mindist[i] = dmin;
min_maxdist = std::min(dmax, min_maxdist);
}
for (int i = 0; i < cinfo->actual_number_of_colors; ++i) {
if (mindist[i] < min_maxdist) {
candidates->push_back(i);
}
}
}
} // namespace
void CreateInverseColorMap(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
int ncomp = cinfo->out_color_components;
int num_cells = 1;
for (int c = 0; c < ncomp; ++c) {
num_cells *= (1 << kNumColorCellBits[c]);
}
m->candidate_lists_.resize(num_cells);
int next_cell[kMaxComponents] = {0};
for (int i = 0; i < num_cells; ++i) {
m->candidate_lists_[i].clear();
FindCandidatesForCell(cinfo, ncomp, next_cell, &m->candidate_lists_[i]);
int c = ncomp - 1;
while (c > 0 && next_cell[c] + 1 == (1 << kNumColorCellBits[c])) {
next_cell[c--] = 0;
}
++next_cell[c];
}
m->regenerate_inverse_colormap_ = false;
}
int LookupColorIndex(j_decompress_ptr cinfo, JSAMPLE* pixel) {
jpeg_decomp_master* m = cinfo->master;
int num_channels = cinfo->out_color_components;
int index = 0;
if (m->quant_mode_ == 1) {
for (int c = 0; c < num_channels; ++c) {
index += m->colormap_lut_[c * 256 + pixel[c]];
}
} else {
size_t cell_idx = 0;
size_t stride = 1;
for (int c = num_channels - 1; c >= 0; --c) {
cell_idx += (pixel[c] >> (8 - kNumColorCellBits[c])) * stride;
stride <<= kNumColorCellBits[c];
}
JXL_ASSERT(cell_idx < m->candidate_lists_.size());
int mindist = std::numeric_limits<int>::max();
const auto& candidates = m->candidate_lists_[cell_idx];
for (uint8_t i : candidates) {
int dist = 0;
for (int c = 0; c < num_channels; ++c) {
int d = (cinfo->colormap[c][i] - pixel[c]) * kCompW[c];
dist += d * d;
}
if (dist < mindist) {
mindist = dist;
index = i;
}
}
}
JXL_ASSERT(index < cinfo->actual_number_of_colors);
return index;
}
void CreateOrderedDitherTables(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
static constexpr size_t kDitherSize = 4;
static constexpr size_t kDitherMask = kDitherSize - 1;
static constexpr float kBaseDitherMatrix[] = {
0, 8, 2, 10, //
12, 4, 14, 6, //
3, 11, 1, 9, //
15, 7, 13, 5, //
};
m->dither_size_ = kDitherSize;
m->dither_mask_ = kDitherMask;
size_t ncells = m->dither_size_ * m->dither_size_;
for (int c = 0; c < cinfo->out_color_components; ++c) {
float spread = 1.0f / (m->num_colors_[c] - 1);
float mul = spread / ncells;
float offset = 0.5f * spread;
if (m->dither_[c] == nullptr) {
m->dither_[c] = Allocate<float>(cinfo, ncells, JPOOL_IMAGE_ALIGNED);
}
for (size_t idx = 0; idx < ncells; ++idx) {
m->dither_[c][idx] = kBaseDitherMatrix[idx] * mul - offset;
}
}
}
void InitFSDitherState(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
for (int c = 0; c < cinfo->out_color_components; ++c) {
if (m->error_row_[c] == nullptr) {
m->error_row_[c] =
Allocate<float>(cinfo, cinfo->output_width, JPOOL_IMAGE_ALIGNED);
m->error_row_[c + kMaxComponents] =
Allocate<float>(cinfo, cinfo->output_width, JPOOL_IMAGE_ALIGNED);
}
memset(m->error_row_[c], 0.0, cinfo->output_width * sizeof(float));
memset(m->error_row_[c + kMaxComponents], 0.0,
cinfo->output_width * sizeof(float));
}
}
} // namespace jpegli

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

@ -0,0 +1,30 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_JPEGLI_COLOR_QUANTIZE_H_
#define LIB_JPEGLI_COLOR_QUANTIZE_H_
/* clang-format off */
#include <stdio.h>
#include <jpeglib.h>
/* clang-format on */
namespace jpegli {
void ChooseColorMap1Pass(j_decompress_ptr cinfo);
void ChooseColorMap2Pass(j_decompress_ptr cinfo);
void CreateInverseColorMap(j_decompress_ptr cinfo);
void CreateOrderedDitherTables(j_decompress_ptr cinfo);
void InitFSDitherState(j_decompress_ptr cinfo);
int LookupColorIndex(j_decompress_ptr cinfo, JSAMPLE* pixel);
} // namespace jpegli
#endif // LIB_JPEGLI_COLOR_QUANTIZE_H_

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

@ -10,6 +10,11 @@
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
#include "lib/jpegli/decode_internal.h"
#include "lib/jpegli/encode_internal.h"
#include "lib/jpegli/error.h"
#include "lib/jxl/base/compiler_specific.h"
HWY_BEFORE_NAMESPACE();
namespace jpegli {
namespace HWY_NAMESPACE {
@ -21,50 +26,21 @@ using hwy::HWY_NAMESPACE::Mul;
using hwy::HWY_NAMESPACE::MulAdd;
using hwy::HWY_NAMESPACE::Sub;
void YCCKToCMYK(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, float* JXL_RESTRICT row3,
size_t xsize) {
void YCbCrToRGB(float* row[kMaxComponents], size_t xsize) {
const HWY_CAPPED(float, 8) df;
float* JXL_RESTRICT row0 = row[0];
float* JXL_RESTRICT row1 = row[1];
float* JXL_RESTRICT row2 = row[2];
// Full-range BT.601 as defined by JFIF Clause 7:
// https://www.itu.int/rec/T-REC-T.871-201105-I/en
// After, C = 1-R, M = 1-G, Y = 1-B, K = K.
const auto c128 = Set(df, 128.0f / 255);
const auto crcr = Set(df, 1.402f);
const auto cgcb = Set(df, -0.114f * 1.772f / 0.587f);
const auto cgcr = Set(df, -0.299f * 1.402f / 0.587f);
const auto cbcb = Set(df, 1.772f);
const auto unity = Set(df, 1.0f);
for (size_t x = 0; x < xsize; x += Lanes(df)) {
const auto y_vec = Add(Load(df, row0 + x), c128);
const auto cb_vec = Load(df, row1 + x);
const auto cr_vec = Load(df, row2 + x);
const auto r_vec = Sub(unity, MulAdd(crcr, cr_vec, y_vec));
const auto g_vec =
Sub(unity, MulAdd(cgcr, cr_vec, MulAdd(cgcb, cb_vec, y_vec)));
const auto b_vec = Sub(unity, MulAdd(cbcb, cb_vec, y_vec));
Store(r_vec, df, row0 + x);
Store(g_vec, df, row1 + x);
Store(b_vec, df, row2 + x);
Store(Add(Load(df, row3 + x), c128), df, row3 + x);
}
}
void YCbCrToRGB(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, size_t xsize) {
const HWY_CAPPED(float, 8) df;
// Full-range BT.601 as defined by JFIF Clause 7:
// https://www.itu.int/rec/T-REC-T.871-201105-I/en
const auto c128 = Set(df, 128.0f / 255);
const auto crcr = Set(df, 1.402f);
const auto cgcb = Set(df, -0.114f * 1.772f / 0.587f);
const auto cgcr = Set(df, -0.299f * 1.402f / 0.587f);
const auto cbcb = Set(df, 1.772f);
for (size_t x = 0; x < xsize; x += Lanes(df)) {
const auto y_vec = Add(Load(df, row0 + x), c128);
const auto y_vec = Load(df, row0 + x);
const auto cb_vec = Load(df, row1 + x);
const auto cr_vec = Load(df, row2 + x);
const auto r_vec = MulAdd(crcr, cr_vec, y_vec);
@ -76,13 +52,28 @@ void YCbCrToRGB(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
}
}
void RGBToYCbCr(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, size_t xsize) {
void YCCKToCMYK(float* row[kMaxComponents], size_t xsize) {
const HWY_CAPPED(float, 8) df;
float* JXL_RESTRICT row0 = row[0];
float* JXL_RESTRICT row1 = row[1];
float* JXL_RESTRICT row2 = row[2];
YCbCrToRGB(row, xsize);
const auto offset = Set(df, -1.0f / 255.0f);
for (size_t x = 0; x < xsize; x += Lanes(df)) {
Store(Sub(offset, Load(df, row0 + x)), df, row0 + x);
Store(Sub(offset, Load(df, row1 + x)), df, row1 + x);
Store(Sub(offset, Load(df, row2 + x)), df, row2 + x);
}
}
void RGBToYCbCr(float* row[kMaxComponents], size_t xsize) {
const HWY_CAPPED(float, 8) df;
float* JXL_RESTRICT row0 = row[0];
float* JXL_RESTRICT row1 = row[1];
float* JXL_RESTRICT row2 = row[2];
// Full-range BT.601 as defined by JFIF Clause 7:
// https://www.itu.int/rec/T-REC-T.871-201105-I/en
const auto c128 = Set(df, 128.0f / 255);
const auto c128 = Set(df, 128.0f);
const auto kR = Set(df, 0.299f); // NTSC luma
const auto kG = Set(df, 0.587f);
const auto kB = Set(df, 0.114f);
@ -111,17 +102,18 @@ void RGBToYCbCr(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
}
}
void CMYKToYCCK(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, float* JXL_RESTRICT row3,
size_t xsize) {
void CMYKToYCCK(float* row[kMaxComponents], size_t xsize) {
const HWY_CAPPED(float, 8) df;
const auto unity = Set(df, 1.0f);
float* JXL_RESTRICT row0 = row[0];
float* JXL_RESTRICT row1 = row[1];
float* JXL_RESTRICT row2 = row[2];
const auto unity = Set(df, 255.0f);
for (size_t x = 0; x < xsize; x += Lanes(df)) {
Store(Sub(unity, Load(df, row0 + x)), df, row0 + x);
Store(Sub(unity, Load(df, row1 + x)), df, row1 + x);
Store(Sub(unity, Load(df, row2 + x)), df, row2 + x);
}
RGBToYCbCr(row0, row1, row2, xsize);
RGBToYCbCr(row, xsize);
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
@ -137,26 +129,152 @@ HWY_EXPORT(YCCKToCMYK);
HWY_EXPORT(YCbCrToRGB);
HWY_EXPORT(RGBToYCbCr);
void CMYKToYCCK(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, float* JXL_RESTRICT row3,
size_t xsize) {
return HWY_DYNAMIC_DISPATCH(CMYKToYCCK)(row0, row1, row2, row3, xsize);
bool CheckColorSpaceComponents(int num_components, J_COLOR_SPACE colorspace) {
switch (colorspace) {
case JCS_GRAYSCALE:
return num_components == 1;
case JCS_RGB:
case JCS_YCbCr:
case JCS_EXT_RGB:
case JCS_EXT_BGR:
return num_components == 3;
case JCS_CMYK:
case JCS_YCCK:
case JCS_EXT_RGBX:
case JCS_EXT_BGRX:
case JCS_EXT_XBGR:
case JCS_EXT_XRGB:
case JCS_EXT_RGBA:
case JCS_EXT_BGRA:
case JCS_EXT_ABGR:
case JCS_EXT_ARGB:
return num_components == 4;
default:
// Unrecognized colorspaces can have any number of channels, since no
// color transform will be performed on them.
return true;
}
}
void YCCKToCMYK(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, float* JXL_RESTRICT row3,
size_t xsize) {
return HWY_DYNAMIC_DISPATCH(YCCKToCMYK)(row0, row1, row2, row3, xsize);
void NullTransform(float* row[kMaxComponents], size_t len) {}
void GrayscaleToRGB(float* row[kMaxComponents], size_t len) {
memcpy(row[1], row[0], len * sizeof(row[1][0]));
memcpy(row[2], row[0], len * sizeof(row[2][0]));
}
void YCbCrToRGB(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, size_t xsize) {
return HWY_DYNAMIC_DISPATCH(YCbCrToRGB)(row0, row1, row2, xsize);
void GrayscaleToYCbCr(float* row[kMaxComponents], size_t len) {
memset(row[1], 0, len * sizeof(row[1][0]));
memset(row[2], 0, len * sizeof(row[2][0]));
}
void RGBToYCbCr(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, size_t xsize) {
return HWY_DYNAMIC_DISPATCH(RGBToYCbCr)(row0, row1, row2, xsize);
void ChooseColorTransform(j_compress_ptr cinfo) {
jpeg_comp_master* m = cinfo->master;
if (!CheckColorSpaceComponents(cinfo->input_components,
cinfo->in_color_space)) {
JPEGLI_ERROR("Invalid number of input components %d for colorspace %d",
cinfo->input_components, cinfo->in_color_space);
}
if (!CheckColorSpaceComponents(cinfo->num_components,
cinfo->jpeg_color_space)) {
JPEGLI_ERROR("Invalid number of components %d for colorspace %d",
cinfo->num_components, cinfo->jpeg_color_space);
}
if (cinfo->jpeg_color_space == cinfo->in_color_space) {
if (cinfo->num_components != cinfo->input_components) {
JPEGLI_ERROR("Input/output components mismatch: %d vs %d",
cinfo->input_components, cinfo->num_components);
}
// No color transform requested.
m->color_transform = NullTransform;
return;
}
if (cinfo->in_color_space == JCS_RGB && m->xyb_mode) {
JPEGLI_ERROR("Color transform on XYB colorspace is not supported.");
}
m->color_transform = nullptr;
if (cinfo->jpeg_color_space == JCS_GRAYSCALE) {
if (cinfo->in_color_space == JCS_RGB) {
m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr);
} else if (cinfo->in_color_space == JCS_YCbCr ||
cinfo->in_color_space == JCS_YCCK) {
// Since the first luminance channel is the grayscale version of the
// image, nothing to do here
m->color_transform = NullTransform;
}
} else if (cinfo->jpeg_color_space == JCS_RGB) {
if (cinfo->in_color_space == JCS_GRAYSCALE) {
m->color_transform = GrayscaleToRGB;
}
} else if (cinfo->jpeg_color_space == JCS_YCbCr) {
if (cinfo->in_color_space == JCS_RGB) {
m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr);
} else if (cinfo->in_color_space == JCS_GRAYSCALE) {
m->color_transform = GrayscaleToYCbCr;
}
} else if (cinfo->jpeg_color_space == JCS_YCCK) {
if (cinfo->in_color_space == JCS_CMYK) {
m->color_transform = HWY_DYNAMIC_DISPATCH(CMYKToYCCK);
}
}
if (m->color_transform == nullptr) {
// TODO(szabadka) Support more color transforms.
JPEGLI_ERROR("Unsupported color transform %d -> %d", cinfo->in_color_space,
cinfo->jpeg_color_space);
}
}
void ChooseColorTransform(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
if (!CheckColorSpaceComponents(cinfo->out_color_components,
cinfo->out_color_space)) {
JPEGLI_ERROR("Invalid number of output components %d for colorspace %d",
cinfo->out_color_components, cinfo->out_color_space);
}
if (!CheckColorSpaceComponents(cinfo->num_components,
cinfo->jpeg_color_space)) {
JPEGLI_ERROR("Invalid number of components %d for colorspace %d",
cinfo->num_components, cinfo->jpeg_color_space);
}
if (cinfo->jpeg_color_space == cinfo->out_color_space) {
if (cinfo->num_components != cinfo->out_color_components) {
JPEGLI_ERROR("Input/output components mismatch: %d vs %d",
cinfo->num_components, cinfo->out_color_components);
}
// No color transform requested.
m->color_transform = NullTransform;
return;
}
m->color_transform = nullptr;
if (cinfo->jpeg_color_space == JCS_GRAYSCALE) {
if (cinfo->out_color_space == JCS_RGB) {
m->color_transform = GrayscaleToRGB;
}
} else if (cinfo->jpeg_color_space == JCS_RGB) {
if (cinfo->out_color_space == JCS_GRAYSCALE) {
m->color_transform = HWY_DYNAMIC_DISPATCH(RGBToYCbCr);
}
} else if (cinfo->jpeg_color_space == JCS_YCbCr) {
if (cinfo->out_color_space == JCS_RGB) {
m->color_transform = HWY_DYNAMIC_DISPATCH(YCbCrToRGB);
} else if (cinfo->out_color_space == JCS_GRAYSCALE) {
m->color_transform = NullTransform;
}
} else if (cinfo->jpeg_color_space == JCS_YCCK) {
if (cinfo->out_color_space == JCS_CMYK) {
m->color_transform = HWY_DYNAMIC_DISPATCH(YCCKToCMYK);
}
}
if (m->color_transform == nullptr) {
// TODO(szabadka) Support more color transforms.
JPEGLI_ERROR("Unsupported color transform %d -> %d",
cinfo->jpeg_color_space, cinfo->out_color_space);
}
}
} // namespace jpegli

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

@ -6,25 +6,18 @@
#ifndef LIB_JPEGLI_COLOR_TRANSFORM_H_
#define LIB_JPEGLI_COLOR_TRANSFORM_H_
#include <stddef.h>
/* clang-format off */
#include <stdio.h>
#include <jpeglib.h>
/* clang-format on */
#include "lib/jxl/base/compiler_specific.h"
namespace jpegli {
void CMYKToYCCK(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, float* JXL_RESTRICT row3,
size_t xsize);
void ChooseColorTransform(j_compress_ptr cinfo);
void YCCKToCMYK(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, float* JXL_RESTRICT row3,
size_t xsize);
void YCbCrToRGB(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, size_t xsize);
void RGBToYCbCr(float* JXL_RESTRICT row0, float* JXL_RESTRICT row1,
float* JXL_RESTRICT row2, size_t xsize);
void ChooseColorTransform(j_decompress_ptr cinfo);
} // namespace jpegli

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

@ -30,7 +30,6 @@ void jpegli_destroy(j_common_ptr cinfo) {
delete reinterpret_cast<j_decompress_ptr>(cinfo)->master;
} else {
cinfo->global_state = jpegli::kEncNull;
delete reinterpret_cast<j_compress_ptr>(cinfo)->master;
}
}

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

@ -10,9 +10,13 @@
#include <stdint.h>
#include <string.h>
#include <algorithm>
#include <hwy/aligned_allocator.h>
#include "lib/jpegli/memory_manager.h"
#include "lib/jpegli/simd.h"
#include "lib/jxl/base/compiler_specific.h" // for ssize_t
#include "lib/jxl/base/status.h" // for JXL_CHECK
namespace jpegli {
@ -35,6 +39,11 @@ constexpr inline T1 DivCeil(T1 a, T2 b) {
return (a + b - 1) / b;
}
template <typename T1, typename T2>
constexpr inline T1 RoundUpTo(T1 a, T2 b) {
return DivCeil(a, b) * b;
}
constexpr size_t kDCTBlockSize = 64;
// This is set to the same value as MAX_COMPS_IN_SCAN, because that is the
// maximum number of channels the libjpeg-turbo decoder can decode.
@ -83,19 +92,42 @@ constexpr uint32_t kJPEGZigZagOrder[64] = {
template <typename T>
class RowBuffer {
public:
void Allocate(size_t num_rows, size_t stride) {
template <typename CInfoType>
void Allocate(CInfoType cinfo, size_t num_rows, size_t rowsize) {
size_t vec_size = std::max(VectorSize(), sizeof(T));
JXL_CHECK(vec_size % sizeof(T) == 0);
size_t alignment = std::max<size_t>(HWY_ALIGNMENT, vec_size);
size_t min_memstride = alignment + rowsize * sizeof(T) + vec_size;
size_t memstride = RoundUpTo(min_memstride, alignment);
xsize_ = rowsize;
ysize_ = num_rows;
stride_ = stride;
data_ = hwy::AllocateAligned<T>(ysize_ * stride_);
stride_ = memstride / sizeof(T);
offset_ = alignment / sizeof(T);
data_ = ::jpegli::Allocate<T>(cinfo, ysize_ * stride_, JPOOL_IMAGE_ALIGNED);
}
T* Row(ssize_t y) { return &data_[((ysize_ + y) % ysize_) * stride_]; }
T* Row(ssize_t y) const {
return &data_[((ysize_ + y) % ysize_) * stride_ + offset_];
}
size_t xsize() const { return xsize_; };
size_t ysize() const { return ysize_; };
size_t stride() const { return stride_; }
size_t memstride() const { return stride_ * sizeof(T); }
void CopyRow(ssize_t y, const T* src, size_t len) {
memcpy(Row(y), src, len * sizeof(T));
void PadRow(size_t y, size_t from, int border) {
float* row = Row(y);
for (int offset = -border; offset < 0; ++offset) {
row[offset] = row[0];
}
float last_val = row[from - 1];
for (size_t x = from; x < xsize_ + border; ++x) {
row[x] = last_val;
}
}
void CopyRow(ssize_t dst_row, ssize_t src_row, int border) {
memcpy(Row(dst_row) - border, Row(src_row) - border,
(xsize_ + 2 * border) * sizeof(T));
}
void FillRow(ssize_t y, T val, size_t len) {
@ -106,9 +138,11 @@ class RowBuffer {
}
private:
size_t ysize_ = 0;
size_t stride_ = 0;
hwy::AlignedFreeUniquePtr<T[]> data_;
size_t xsize_;
size_t ysize_;
size_t stride_;
size_t offset_;
T* data_;
};
} // namespace jpegli

266
third_party/jpeg-xl/lib/jpegli/dct-inl.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,266 @@
// 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.
#if defined(LIB_JPEGLI_DCT_INL_H_) == defined(HWY_TARGET_TOGGLE)
#ifdef LIB_JPEGLI_DCT_INL_H_
#undef LIB_JPEGLI_DCT_INL_H_
#else
#define LIB_JPEGLI_DCT_INL_H_
#endif
#include "lib/jpegli/transpose-inl.h"
#include "lib/jxl/base/compiler_specific.h"
HWY_BEFORE_NAMESPACE();
namespace jpegli {
namespace HWY_NAMESPACE {
namespace {
// These templates are not found via ADL.
using hwy::HWY_NAMESPACE::Abs;
using hwy::HWY_NAMESPACE::Add;
using hwy::HWY_NAMESPACE::DemoteTo;
using hwy::HWY_NAMESPACE::Ge;
using hwy::HWY_NAMESPACE::IfThenElseZero;
using hwy::HWY_NAMESPACE::Mul;
using hwy::HWY_NAMESPACE::MulAdd;
using hwy::HWY_NAMESPACE::Rebind;
using hwy::HWY_NAMESPACE::Round;
using hwy::HWY_NAMESPACE::Sub;
using hwy::HWY_NAMESPACE::Vec;
using D = HWY_FULL(float);
using DI = HWY_FULL(int32_t);
template <size_t N>
void AddReverse(const float* JXL_RESTRICT ain1, const float* JXL_RESTRICT ain2,
float* JXL_RESTRICT aout) {
HWY_CAPPED(float, 8) d8;
for (size_t i = 0; i < N; i++) {
auto in1 = Load(d8, ain1 + i * 8);
auto in2 = Load(d8, ain2 + (N - i - 1) * 8);
Store(Add(in1, in2), d8, aout + i * 8);
}
}
template <size_t N>
void SubReverse(const float* JXL_RESTRICT ain1, const float* JXL_RESTRICT ain2,
float* JXL_RESTRICT aout) {
HWY_CAPPED(float, 8) d8;
for (size_t i = 0; i < N; i++) {
auto in1 = Load(d8, ain1 + i * 8);
auto in2 = Load(d8, ain2 + (N - i - 1) * 8);
Store(Sub(in1, in2), d8, aout + i * 8);
}
}
template <size_t N>
void B(float* JXL_RESTRICT coeff) {
HWY_CAPPED(float, 8) d8;
constexpr float kSqrt2 = 1.41421356237f;
auto sqrt2 = Set(d8, kSqrt2);
auto in1 = Load(d8, coeff);
auto in2 = Load(d8, coeff + 8);
Store(MulAdd(in1, sqrt2, in2), d8, coeff);
for (size_t i = 1; i + 1 < N; i++) {
auto in1 = Load(d8, coeff + i * 8);
auto in2 = Load(d8, coeff + (i + 1) * 8);
Store(Add(in1, in2), d8, coeff + i * 8);
}
}
// Ideally optimized away by compiler (except the multiply).
template <size_t N>
void InverseEvenOdd(const float* JXL_RESTRICT ain, float* JXL_RESTRICT aout) {
HWY_CAPPED(float, 8) d8;
for (size_t i = 0; i < N / 2; i++) {
auto in1 = Load(d8, ain + i * 8);
Store(in1, d8, aout + 2 * i * 8);
}
for (size_t i = N / 2; i < N; i++) {
auto in1 = Load(d8, ain + i * 8);
Store(in1, d8, aout + (2 * (i - N / 2) + 1) * 8);
}
}
// Constants for DCT implementation. Generated by the following snippet:
// for i in range(N // 2):
// print(1.0 / (2 * math.cos((i + 0.5) * math.pi / N)), end=", ")
template <size_t N>
struct WcMultipliers;
template <>
struct WcMultipliers<4> {
static constexpr float kMultipliers[] = {
0.541196100146197,
1.3065629648763764,
};
};
template <>
struct WcMultipliers<8> {
static constexpr float kMultipliers[] = {
0.5097955791041592,
0.6013448869350453,
0.8999762231364156,
2.5629154477415055,
};
};
constexpr float WcMultipliers<4>::kMultipliers[];
constexpr float WcMultipliers<8>::kMultipliers[];
// Invoked on full vector.
template <size_t N>
void Multiply(float* JXL_RESTRICT coeff) {
HWY_CAPPED(float, 8) d8;
for (size_t i = 0; i < N / 2; i++) {
auto in1 = Load(d8, coeff + (N / 2 + i) * 8);
auto mul = Set(d8, WcMultipliers<N>::kMultipliers[i]);
Store(Mul(in1, mul), d8, coeff + (N / 2 + i) * 8);
}
}
void LoadFromBlock(const float* JXL_RESTRICT pixels, size_t pixels_stride,
size_t off, float* JXL_RESTRICT coeff) {
HWY_CAPPED(float, 8) d8;
for (size_t i = 0; i < 8; i++) {
Store(LoadU(d8, pixels + i * pixels_stride + off), d8, coeff + i * 8);
}
}
void StoreToBlockAndScale(const float* JXL_RESTRICT coeff, float* output,
size_t off) {
HWY_CAPPED(float, 8) d8;
auto mul = Set(d8, 1.0f / 8);
for (size_t i = 0; i < 8; i++) {
StoreU(Mul(mul, Load(d8, coeff + i * 8)), d8, output + i * 8 + off);
}
}
template <size_t N>
struct DCT1DImpl;
template <>
struct DCT1DImpl<1> {
JXL_INLINE void operator()(float* JXL_RESTRICT mem) {}
};
template <>
struct DCT1DImpl<2> {
JXL_INLINE void operator()(float* JXL_RESTRICT mem) {
HWY_CAPPED(float, 8) d8;
auto in1 = Load(d8, mem);
auto in2 = Load(d8, mem + 8);
Store(Add(in1, in2), d8, mem);
Store(Sub(in1, in2), d8, mem + 8);
}
};
template <size_t N>
struct DCT1DImpl {
void operator()(float* JXL_RESTRICT mem) {
HWY_ALIGN float tmp[N * 8];
AddReverse<N / 2>(mem, mem + N * 4, tmp);
DCT1DImpl<N / 2>()(tmp);
SubReverse<N / 2>(mem, mem + N * 4, tmp + N * 4);
Multiply<N>(tmp);
DCT1DImpl<N / 2>()(tmp + N * 4);
B<N / 2>(tmp + N * 4);
InverseEvenOdd<N>(tmp, mem);
}
};
void DCT1D(const float* JXL_RESTRICT pixels, size_t pixels_stride,
float* JXL_RESTRICT output) {
HWY_CAPPED(float, 8) d8;
HWY_ALIGN float tmp[64];
for (size_t i = 0; i < 8; i += Lanes(d8)) {
// TODO(veluca): consider removing the temporary memory here (as is done in
// IDCT), if it turns out that some compilers don't optimize away the loads
// and this is performance-critical.
LoadFromBlock(pixels, pixels_stride, i, tmp);
DCT1DImpl<8>()(tmp);
StoreToBlockAndScale(tmp, output, i);
}
}
void TransformFromPixels(const float* JXL_RESTRICT pixels, size_t pixels_stride,
float* JXL_RESTRICT coefficients,
float* JXL_RESTRICT scratch_space) {
DCT1D(pixels, pixels_stride, scratch_space);
Transpose8x8Block(scratch_space, coefficients);
DCT1D(coefficients, 8, scratch_space);
Transpose8x8Block(scratch_space, coefficients);
}
void StoreQuantizedValue(const Vec<DI>& ival, int16_t* out) {
Rebind<int16_t, DI> di16;
Store(DemoteTo(di16, ival), di16, out);
}
void StoreQuantizedValue(const Vec<DI>& ival, int32_t* out) {
DI di;
Store(ival, di, out);
}
template <typename T>
void QuantizeBlock(const float* dct, const float* qmc, float aq_strength,
const float* zero_bias_offset, const float* zero_bias_mul,
T* block) {
D d;
DI di;
const auto aq_mul = Set(d, aq_strength);
for (size_t k = 0; k < DCTSIZE2; k += Lanes(d)) {
const auto val = Load(d, dct + k);
const auto q = Load(d, qmc + k);
const auto qval = Mul(val, q);
const auto zb_offset = Load(d, zero_bias_offset + k);
const auto zb_mul = Load(d, zero_bias_mul + k);
const auto threshold = Add(zb_offset, Mul(zb_mul, aq_mul));
const auto nzero_mask = Ge(Abs(qval), threshold);
const auto ival = ConvertTo(di, IfThenElseZero(nzero_mask, Round(qval)));
StoreQuantizedValue(ival, block + k);
}
}
template <typename T>
void QuantizeBlockNoAQ(const float* dct, const float* qmc, T* block) {
D d;
DI di;
for (size_t k = 0; k < DCTSIZE2; k += Lanes(d)) {
const auto val = Load(d, dct + k);
const auto q = Load(d, qmc + k);
const auto ival = ConvertTo(di, Round(Mul(val, q)));
StoreQuantizedValue(ival, block + k);
}
}
template <typename T>
void ComputeCoefficientBlock(const float* JXL_RESTRICT pixels, size_t stride,
const float* JXL_RESTRICT qmc, float aq_strength,
const float* zero_bias_offset,
const float* zero_bias_mul,
float* JXL_RESTRICT tmp, T* block) {
float* JXL_RESTRICT dct = tmp;
float* JXL_RESTRICT scratch_space = tmp + DCTSIZE2;
TransformFromPixels(pixels, stride, dct, scratch_space);
if (aq_strength > 0.0f) {
QuantizeBlock(dct, qmc, aq_strength, zero_bias_offset, zero_bias_mul,
block);
} else {
QuantizeBlockNoAQ(dct, qmc, block);
}
// Center DC values around zero.
static constexpr float kDCBias = 128.0f;
block[0] = std::round((dct[0] - kDCBias) * qmc[0]);
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
} // namespace
} // namespace HWY_NAMESPACE
} // namespace jpegli
HWY_AFTER_NAMESPACE();
#endif // LIB_JPEGLI_DCT_INL_H_

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

@ -12,90 +12,52 @@
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
#include "lib/jxl/enc_transforms.h"
#include "lib/jpegli/dct-inl.h"
#include "lib/jpegli/encode_internal.h"
#include "lib/jpegli/memory_manager.h"
HWY_BEFORE_NAMESPACE();
namespace jpegli {
namespace HWY_NAMESPACE {
namespace {
constexpr float kZeroBiasMulXYB[] = {0.5f, 0.5f, 0.5f};
constexpr float kZeroBiasMulYCbCr[] = {0.7f, 1.0f, 0.8f};
void QuantizeBlock(const float* dct, const float* qmc, const float zero_bias,
coeff_t* block) {
for (size_t iy = 0, i = 0; iy < 8; iy++) {
for (size_t ix = 0; ix < 8; ix++, i++) {
float coeff = 2040 * dct[ix * 8 + iy] * qmc[i];
int cc = std::abs(coeff) < zero_bias ? 0 : std::round(coeff);
block[i] = cc;
}
}
// Center DC values around zero.
block[0] = std::round((2040 * dct[0] - 1024) * qmc[0]);
}
void QuantizeBlockNoAQ(const float* dct, const float* qmc, coeff_t* block) {
for (size_t iy = 0, i = 0; iy < 8; iy++) {
for (size_t ix = 0; ix < 8; ix++, i++) {
block[i] = std::round(2040 * dct[ix * 8 + iy] * qmc[i]);
}
}
// Center DC values around zero.
block[0] = std::round((2040 * dct[0] - 1024) * qmc[0]);
}
void ComputeDCTCoefficients(
j_compress_ptr cinfo,
std::vector<std::vector<jpegli::coeff_t> >* all_coeffs) {
void ComputeDCTCoefficients(j_compress_ptr cinfo) {
jpeg_comp_master* m = cinfo->master;
std::vector<float> zero_bias_mul(cinfo->num_components, 0.5f);
const bool xyb = m->xyb_mode && cinfo->jpeg_color_space == JCS_RGB;
if (m->distance <= 1.0f) {
for (int c = 0; c < 3 && c < cinfo->num_components; ++c) {
zero_bias_mul[c] = xyb ? kZeroBiasMulXYB[c] : kZeroBiasMulYCbCr[c];
}
}
HWY_ALIGN float scratch_space[2 * kDCTBlockSize];
jxl::ImageF tmp;
float* tmp = m->dct_buffer;
for (int c = 0; c < cinfo->num_components; c++) {
jpeg_component_info* comp = &cinfo->comp_info[c];
const size_t xsize_blocks = comp->width_in_blocks;
const size_t ysize_blocks = comp->height_in_blocks;
JXL_DASSERT(cinfo->max_h_samp_factor % comp->h_samp_factor == 0);
JXL_DASSERT(cinfo->max_v_samp_factor % comp->v_samp_factor == 0);
const int h_factor = cinfo->max_h_samp_factor / comp->h_samp_factor;
const int v_factor = cinfo->max_v_samp_factor / comp->v_samp_factor;
std::vector<coeff_t> coeffs(xsize_blocks * ysize_blocks * kDCTBlockSize);
JQUANT_TBL* quant_table = cinfo->quant_tbl_ptrs[comp->quant_tbl_no];
std::vector<float> qmc(kDCTBlockSize);
for (size_t k = 0; k < kDCTBlockSize; k++) {
qmc[k] = 1.0f / quant_table->quantval[k];
}
RowBuffer<float>* plane = &m->input_buffer[c];
for (size_t by = 0, bix = 0; by < ysize_blocks; by++) {
int by0 = m->next_iMCU_row * comp->v_samp_factor;
int block_rows_left = comp->height_in_blocks - by0;
int max_block_rows = std::min(comp->v_samp_factor, block_rows_left);
JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)(
reinterpret_cast<j_common_ptr>(cinfo), m->coeff_buffers[c], by0,
max_block_rows, true);
float* qmc = m->quant_mul[c];
RowBuffer<float>* plane = m->raw_data[c];
const int h_factor = m->h_factor[c];
const int v_factor = m->v_factor[c];
const float* zero_bias_offset = m->zero_bias_offset[c];
const float* zero_bias_mul = m->zero_bias_mul[c];
float aq_strength = 0.0f;
for (int iy = 0; iy < comp->v_samp_factor; iy++) {
size_t by = by0 + iy;
if (by >= comp->height_in_blocks) continue;
JBLOCKROW brow = ba[iy];
const float* row = plane->Row(8 * by);
for (size_t bx = 0; bx < xsize_blocks; bx++, bix++) {
coeff_t* block = &coeffs[bix * kDCTBlockSize];
HWY_ALIGN float dct[kDCTBlockSize];
TransformFromPixels(jxl::AcStrategy::Type::DCT, row + 8 * bx,
plane->stride(), dct, scratch_space);
for (size_t bx = 0; bx < comp->width_in_blocks; bx++) {
JCOEF* block = &brow[bx][0];
if (m->use_adaptive_quantization) {
// Create more zeros in areas where jpeg xl would have used a lower
// quantization multiplier.
float relq = m->quant_field.Row(by * v_factor)[bx * h_factor];
float zero_bias = 0.5f + zero_bias_mul[c] * relq;
zero_bias = std::min(1.5f, zero_bias);
QuantizeBlock(dct, &qmc[0], zero_bias, block);
} else {
QuantizeBlockNoAQ(dct, &qmc[0], block);
aq_strength = m->quant_field.Row(by * v_factor)[bx * h_factor];
}
ComputeCoefficientBlock(row + 8 * bx, plane->stride(), qmc, aq_strength,
zero_bias_offset, zero_bias_mul, tmp, block);
}
}
all_coeffs->emplace_back(std::move(coeffs));
}
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
} // namespace
} // namespace HWY_NAMESPACE
} // namespace jpegli
HWY_AFTER_NAMESPACE();
@ -105,9 +67,8 @@ namespace jpegli {
HWY_EXPORT(ComputeDCTCoefficients);
void ComputeDCTCoefficients(
j_compress_ptr cinfo, std::vector<std::vector<jpegli::coeff_t> >* coeffs) {
HWY_DYNAMIC_DISPATCH(ComputeDCTCoefficients)(cinfo, coeffs);
void ComputeDCTCoefficients(j_compress_ptr cinfo) {
HWY_DYNAMIC_DISPATCH(ComputeDCTCoefficients)(cinfo);
}
} // namespace jpegli

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

@ -11,14 +11,9 @@
#include <jpeglib.h>
/* clang-format on */
#include <vector>
#include "lib/jpegli/encode_internal.h"
namespace jpegli {
void ComputeDCTCoefficients(j_compress_ptr cinfo,
std::vector<std::vector<jpegli::coeff_t> >* coeffs);
void ComputeDCTCoefficients(j_compress_ptr cinfo);
} // namespace jpegli

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

@ -9,20 +9,19 @@
#include <vector>
#include "lib/jpegli/color_quantize.h"
#include "lib/jpegli/decode_internal.h"
#include "lib/jpegli/decode_marker.h"
#include "lib/jpegli/decode_scan.h"
#include "lib/jpegli/error.h"
#include "lib/jpegli/memory_manager.h"
#include "lib/jpegli/render.h"
#include "lib/jpegli/source_manager.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/status.h"
namespace jpegli {
void InitializeImage(j_decompress_ptr cinfo) {
cinfo->jpeg_color_space = JCS_UNKNOWN;
cinfo->restart_interval = 0;
cinfo->saw_JFIF_marker = FALSE;
cinfo->JFIF_major_version = 1;
@ -32,53 +31,352 @@ void InitializeImage(j_decompress_ptr cinfo) {
cinfo->Y_density = 1;
cinfo->saw_Adobe_marker = FALSE;
cinfo->Adobe_transform = 0;
for (int i = 0; i < NUM_QUANT_TBLS; ++i) {
cinfo->quant_tbl_ptrs[i] = nullptr;
cinfo->CCIR601_sampling = FALSE; // not used
cinfo->marker_list = nullptr;
cinfo->comp_info = nullptr;
cinfo->input_scan_number = 0;
cinfo->input_iMCU_row = 0;
cinfo->output_scan_number = 0;
cinfo->output_iMCU_row = 0;
cinfo->output_scanline = 0;
cinfo->unread_marker = 0;
cinfo->coef_bits = nullptr;
// We set all these to zero since we don't yet support arithmetic coding.
memset(cinfo->arith_dc_L, 0, sizeof(cinfo->arith_dc_L));
memset(cinfo->arith_dc_U, 0, sizeof(cinfo->arith_dc_U));
memset(cinfo->arith_ac_K, 0, sizeof(cinfo->arith_ac_K));
// Initialize the private fields.
jpeg_decomp_master* m = cinfo->master;
m->input_buffer_.clear();
m->input_buffer_pos_ = 0;
m->codestream_bits_ahead_ = 0;
m->is_multiscan_ = false;
m->found_soi_ = false;
m->found_dri_ = false;
m->found_sof_ = false;
m->found_eoi_ = false;
m->icc_index_ = 0;
m->icc_total_ = 0;
m->icc_profile_.clear();
memset(m->dc_huff_lut_, 0, sizeof(m->dc_huff_lut_));
memset(m->ac_huff_lut_, 0, sizeof(m->ac_huff_lut_));
// Initialize the values to an invalid symbol so that we can recognize it
// when reading the bit stream using a Huffman code with space > 0.
for (size_t i = 0; i < kAllHuffLutSize; ++i) {
m->dc_huff_lut_[i].bits = 0;
m->dc_huff_lut_[i].value = 0xffff;
m->ac_huff_lut_[i].bits = 0;
m->ac_huff_lut_[i].value = 0xffff;
}
for (int i = 0; i < NUM_HUFF_TBLS; ++i) {
cinfo->dc_huff_tbl_ptrs[i] = nullptr;
cinfo->ac_huff_tbl_ptrs[i] = nullptr;
m->colormap_lut_ = nullptr;
m->pixels_ = nullptr;
m->scanlines_ = nullptr;
m->regenerate_inverse_colormap_ = true;
for (int i = 0; i < kMaxComponents; ++i) {
m->dither_[i] = nullptr;
m->error_row_[i] = nullptr;
}
m->output_passes_done_ = 0;
m->xoffset_ = 0;
m->dequant_ = nullptr;
}
void InitializeDecompressParams(j_decompress_ptr cinfo) {
cinfo->jpeg_color_space = JCS_UNKNOWN;
cinfo->out_color_space = JCS_UNKNOWN;
cinfo->scale_num = 1;
cinfo->scale_denom = 1;
cinfo->output_gamma = 0.0f;
cinfo->buffered_image = FALSE;
cinfo->raw_data_out = FALSE;
cinfo->dct_method = JDCT_DEFAULT;
cinfo->do_fancy_upsampling = TRUE;
cinfo->do_block_smoothing = TRUE;
cinfo->quantize_colors = FALSE;
cinfo->dither_mode = JDITHER_FS;
cinfo->two_pass_quantize = TRUE;
cinfo->desired_number_of_colors = 256;
cinfo->enable_1pass_quant = FALSE;
cinfo->enable_external_quant = FALSE;
cinfo->enable_2pass_quant = FALSE;
cinfo->actual_number_of_colors = 0;
cinfo->colormap = nullptr;
}
void InitProgressMonitor(j_decompress_ptr cinfo, bool coef_only) {
if (!cinfo->progress) return;
jpeg_decomp_master* m = cinfo->master;
int nc = cinfo->num_components;
int estimated_num_scans =
cinfo->progressive_mode ? 2 + 3 * nc : (m->is_multiscan_ ? nc : 1);
cinfo->progress->pass_limit = cinfo->total_iMCU_rows * estimated_num_scans;
cinfo->progress->pass_counter = 0;
if (coef_only) {
cinfo->progress->total_passes = 1;
} else {
int input_passes = !cinfo->buffered_image && m->is_multiscan_ ? 1 : 0;
bool two_pass_quant = cinfo->quantize_colors && !cinfo->colormap &&
cinfo->two_pass_quantize && cinfo->enable_2pass_quant;
cinfo->progress->total_passes = input_passes + (two_pass_quant ? 2 : 1);
}
cinfo->progress->completed_passes = 0;
}
void InitProgressMonitorForOutput(j_decompress_ptr cinfo) {
if (!cinfo->progress) return;
jpeg_decomp_master* m = cinfo->master;
int passes_per_output = cinfo->enable_2pass_quant ? 2 : 1;
int output_passes_left = cinfo->buffered_image && !m->found_eoi_ ? 2 : 1;
cinfo->progress->total_passes =
m->output_passes_done_ + passes_per_output * output_passes_left;
cinfo->progress->completed_passes = m->output_passes_done_;
}
void ProgressMonitorInputPass(j_decompress_ptr cinfo) {
if (!cinfo->progress) return;
cinfo->progress->pass_counter =
((cinfo->input_scan_number - 1) * cinfo->total_iMCU_rows +
cinfo->input_iMCU_row);
if (cinfo->progress->pass_counter > cinfo->progress->pass_limit) {
cinfo->progress->pass_limit =
cinfo->input_scan_number * cinfo->total_iMCU_rows;
}
(*cinfo->progress->progress_monitor)(reinterpret_cast<j_common_ptr>(cinfo));
}
void ProgressMonitorOutputPass(j_decompress_ptr cinfo) {
if (!cinfo->progress) return;
jpeg_decomp_master* m = cinfo->master;
int input_passes = !cinfo->buffered_image && m->is_multiscan_ ? 1 : 0;
cinfo->progress->pass_counter = cinfo->output_scanline;
cinfo->progress->pass_limit = cinfo->output_height;
cinfo->progress->completed_passes = input_passes + m->output_passes_done_;
(*cinfo->progress->progress_monitor)(reinterpret_cast<j_common_ptr>(cinfo));
}
void BuildHuffmanLookupTable(j_decompress_ptr cinfo, JHUFF_TBL* table,
HuffmanTableEntry* huff_lut) {
uint32_t counts[kJpegHuffmanMaxBitLength + 1] = {};
counts[0] = 0;
int total_count = 0;
int space = 1 << kJpegHuffmanMaxBitLength;
int max_depth = 1;
for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) {
int count = table->bits[i];
if (count != 0) {
max_depth = i;
}
counts[i] = count;
total_count += count;
space -= count * (1 << (kJpegHuffmanMaxBitLength - i));
}
uint32_t values[kJpegHuffmanAlphabetSize + 1] = {};
uint8_t values_seen[256] = {0};
for (int i = 0; i < total_count; ++i) {
int value = table->huffval[i];
if (values_seen[value]) {
return JPEGLI_ERROR("Duplicate Huffman code value %d", value);
}
values_seen[value] = 1;
values[i] = value;
}
// Add an invalid symbol that will have the all 1 code.
++counts[max_depth];
values[total_count] = kJpegHuffmanAlphabetSize;
space -= (1 << (kJpegHuffmanMaxBitLength - max_depth));
if (space < 0) {
JPEGLI_ERROR("Invalid Huffman code lengths.");
} else if (space > 0 && huff_lut[0].value != 0xffff) {
// Re-initialize the values to an invalid symbol so that we can recognize
// it when reading the bit stream using a Huffman code with space > 0.
for (int i = 0; i < kJpegHuffmanLutSize; ++i) {
huff_lut[i].bits = 0;
huff_lut[i].value = 0xffff;
}
}
BuildJpegHuffmanTable(&counts[0], &values[0], huff_lut);
}
void PrepareForScan(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
int comp_idx = cinfo->cur_comp_info[i]->component_index;
int* prev_coef_bits = cinfo->coef_bits[comp_idx + cinfo->num_components];
for (int k = std::min(cinfo->Ss, 1); k <= std::max(cinfo->Se, 9); k++) {
prev_coef_bits[k] =
(cinfo->input_scan_number > 0) ? cinfo->coef_bits[comp_idx][k] : 0;
}
for (int k = cinfo->Ss; k <= cinfo->Se; ++k) {
cinfo->coef_bits[comp_idx][k] = cinfo->Al;
}
}
AddStandardHuffmanTables(reinterpret_cast<j_common_ptr>(cinfo),
/*is_dc=*/false);
AddStandardHuffmanTables(reinterpret_cast<j_common_ptr>(cinfo),
/*is_dc=*/true);
// Check that all the Huffman tables needed for this scan are defined and
// build derived lookup tables.
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
if (cinfo->Ss == 0) {
int dc_tbl_idx = cinfo->cur_comp_info[i]->dc_tbl_no;
JHUFF_TBL* table = cinfo->dc_huff_tbl_ptrs[dc_tbl_idx];
HuffmanTableEntry* huff_lut =
&m->dc_huff_lut_[dc_tbl_idx * kJpegHuffmanLutSize];
if (!table) {
return JPEGLI_ERROR("DC Huffman table %d not found", dc_tbl_idx);
}
BuildHuffmanLookupTable(cinfo, table, huff_lut);
}
if (cinfo->Se > 0) {
int ac_tbl_idx = cinfo->cur_comp_info[i]->ac_tbl_no;
JHUFF_TBL* table = cinfo->ac_huff_tbl_ptrs[ac_tbl_idx];
HuffmanTableEntry* huff_lut =
&m->ac_huff_lut_[ac_tbl_idx * kJpegHuffmanLutSize];
if (!table) {
return JPEGLI_ERROR("AC Huffman table %d not found", ac_tbl_idx);
}
BuildHuffmanLookupTable(cinfo, table, huff_lut);
}
}
// Copy quantization tables into comp_info.
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
jpeg_component_info* comp = cinfo->cur_comp_info[i];
if (comp->quant_table == nullptr) {
comp->quant_table = Allocate<JQUANT_TBL>(cinfo, 1, JPOOL_IMAGE);
memcpy(comp->quant_table, cinfo->quant_tbl_ptrs[comp->quant_tbl_no],
sizeof(JQUANT_TBL));
}
}
if (cinfo->comps_in_scan == 1) {
const auto& comp = *cinfo->cur_comp_info[0];
cinfo->MCUs_per_row = DivCeil(cinfo->image_width * comp.h_samp_factor,
cinfo->max_h_samp_factor * DCTSIZE);
cinfo->MCU_rows_in_scan = DivCeil(cinfo->image_height * comp.v_samp_factor,
cinfo->max_v_samp_factor * DCTSIZE);
m->mcu_rows_per_iMCU_row_ = cinfo->cur_comp_info[0]->v_samp_factor;
} else {
cinfo->MCU_rows_in_scan = cinfo->total_iMCU_rows;
cinfo->MCUs_per_row = m->iMCU_cols_;
m->mcu_rows_per_iMCU_row_ = 1;
size_t mcu_size = 0;
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
jpeg_component_info* comp = cinfo->cur_comp_info[i];
mcu_size += comp->h_samp_factor * comp->v_samp_factor;
}
if (mcu_size > D_MAX_BLOCKS_IN_MCU) {
JPEGLI_ERROR("MCU size too big");
}
}
memset(m->last_dc_coeff_, 0, sizeof(m->last_dc_coeff_));
m->restarts_to_go_ = cinfo->restart_interval;
m->next_restart_marker_ = 0;
m->eobrun_ = -1;
m->scan_mcu_row_ = 0;
m->scan_mcu_col_ = 0;
m->codestream_bits_ahead_ = 0;
++cinfo->input_scan_number;
cinfo->input_iMCU_row = 0;
PrepareForiMCURow(cinfo);
cinfo->global_state = kDecProcessScan;
}
int ConsumeInput(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
if (cinfo->global_state == kDecProcessScan && m->streaming_mode_ &&
cinfo->input_iMCU_row > cinfo->output_iMCU_row) {
// Prevent input from getting ahead of output in streaming mode.
return JPEG_SUSPENDED;
}
jpeg_source_mgr* src = cinfo->src;
std::vector<uint8_t> buffer;
const uint8_t* last_input_byte = src->next_input_byte + src->bytes_in_buffer;
int status;
for (;;) {
if (cinfo->global_state == kDecProcessScan) {
status = ProcessScan(cinfo);
const uint8_t* data;
size_t len;
if (m->input_buffer_.empty()) {
data = cinfo->src->next_input_byte;
len = cinfo->src->bytes_in_buffer;
} else {
status = ProcessMarkers(cinfo);
data = &m->input_buffer_[m->input_buffer_pos_];
len = m->input_buffer_.size() - m->input_buffer_pos_;
}
if (status != JPEG_SUSPENDED) {
size_t pos = 0;
if (cinfo->global_state == kDecProcessScan) {
status = ProcessScan(cinfo, data, len, &pos, &m->codestream_bits_ahead_);
} else {
status = ProcessMarkers(cinfo, data, len, &pos);
}
if (m->input_buffer_.empty()) {
cinfo->src->next_input_byte += pos;
cinfo->src->bytes_in_buffer -= pos;
} else {
m->input_buffer_pos_ += pos;
size_t bytes_left = m->input_buffer_.size() - m->input_buffer_pos_;
if (bytes_left <= src->bytes_in_buffer) {
src->next_input_byte += (src->bytes_in_buffer - bytes_left);
src->bytes_in_buffer = bytes_left;
m->input_buffer_.clear();
m->input_buffer_pos_ = 0;
}
}
if (status == kHandleRestart) {
JXL_DASSERT(m->input_buffer_.size() <=
m->input_buffer_pos_ + src->bytes_in_buffer);
m->input_buffer_.clear();
m->input_buffer_pos_ = 0;
if (cinfo->unread_marker == 0xd0 + m->next_restart_marker_) {
cinfo->unread_marker = 0;
} else {
if (!(*cinfo->src->resync_to_restart)(cinfo, m->next_restart_marker_)) {
return JPEG_SUSPENDED;
}
}
m->next_restart_marker_ += 1;
m->next_restart_marker_ &= 0x7;
m->restarts_to_go_ = cinfo->restart_interval;
if (cinfo->unread_marker != 0) {
JPEGLI_WARN("Failed to resync to next restart marker, skipping scan.");
return JPEG_SCAN_COMPLETED;
}
continue;
}
if (status == kHandleMarkerProcessor) {
JXL_DASSERT(m->input_buffer_.size() <=
m->input_buffer_pos_ + src->bytes_in_buffer);
m->input_buffer_.clear();
m->input_buffer_pos_ = 0;
if (!(*GetMarkerProcessor(cinfo))(cinfo)) {
return JPEG_SUSPENDED;
}
cinfo->unread_marker = 0;
continue;
}
if (status != kNeedMoreInput) {
break;
}
if (buffer.size() != src->bytes_in_buffer) {
// Save the unprocessed bytes in the input to a temporary buffer.
buffer.assign(src->next_input_byte,
src->next_input_byte + src->bytes_in_buffer);
if (m->input_buffer_.empty()) {
JXL_DASSERT(m->input_buffer_pos_ == 0);
m->input_buffer_.assign(src->next_input_byte,
src->next_input_byte + src->bytes_in_buffer);
}
if (!(*cinfo->src->fill_input_buffer)(cinfo)) {
return status;
m->input_buffer_.clear();
m->input_buffer_pos_ = 0;
return JPEG_SUSPENDED;
}
// Save the end of the current input so that we can restore it after the
// input processing succeeds.
last_input_byte = cinfo->src->next_input_byte + src->bytes_in_buffer;
// Extend the temporary buffer with the new bytes and point the input to it.
buffer.insert(buffer.end(), src->next_input_byte, last_input_byte);
src->next_input_byte = buffer.data();
src->bytes_in_buffer = buffer.size();
if (src->bytes_in_buffer == 0) {
JPEGLI_ERROR("Empty input.");
}
m->input_buffer_.insert(m->input_buffer_.end(), src->next_input_byte,
src->next_input_byte + src->bytes_in_buffer);
}
// Restore the input pointer in case we had to change it to a temporary
// buffer earlier.
src->next_input_byte = last_input_byte - src->bytes_in_buffer;
if (status == JPEG_SCAN_COMPLETED) {
cinfo->global_state = kDecProcessMarkers;
} else if (status == JPEG_REACHED_SOS) {
cinfo->global_state =
cinfo->global_state == kDecInHeader ? kDecHeaderDone : kDecProcessScan;
if (cinfo->global_state == kDecInHeader) {
cinfo->global_state = kDecHeaderDone;
} else {
PrepareForScan(cinfo);
}
}
return status;
}
@ -93,7 +391,97 @@ bool IsInputReady(j_decompress_ptr cinfo) {
if (cinfo->input_scan_number < cinfo->output_scan_number) {
return false;
}
return cinfo->input_iMCU_row > cinfo->output_iMCU_row;
if (cinfo->input_iMCU_row == cinfo->total_iMCU_rows) {
return true;
}
return cinfo->input_iMCU_row >
cinfo->output_iMCU_row + (cinfo->master->streaming_mode_ ? 0 : 2);
}
bool ReadOutputPass(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
if (!m->pixels_) {
size_t stride = cinfo->out_color_components * cinfo->output_width;
size_t num_samples = cinfo->output_height * stride;
m->pixels_ = Allocate<uint8_t>(cinfo, num_samples, JPOOL_IMAGE);
m->scanlines_ =
Allocate<JSAMPROW>(cinfo, cinfo->output_height, JPOOL_IMAGE);
for (size_t i = 0; i < cinfo->output_height; ++i) {
m->scanlines_[i] = &m->pixels_[i * stride];
}
}
size_t num_output_rows = 0;
while (num_output_rows < cinfo->output_height) {
if (IsInputReady(cinfo)) {
ProgressMonitorOutputPass(cinfo);
ProcessOutput(cinfo, &num_output_rows, m->scanlines_,
cinfo->output_height);
} else if (ConsumeInput(cinfo) == JPEG_SUSPENDED) {
return false;
}
}
cinfo->output_scanline = 0;
cinfo->output_iMCU_row = 0;
return true;
}
boolean PrepareQuantizedOutput(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
if (cinfo->raw_data_out) {
JPEGLI_ERROR("Color quantization is not supported in raw data mode.");
}
if (m->output_data_type_ != JPEGLI_TYPE_UINT8) {
JPEGLI_ERROR("Color quantization must use 8-bit mode.");
}
if (cinfo->colormap) {
m->quant_mode_ = 3;
} else if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) {
m->quant_mode_ = 2;
} else if (cinfo->enable_1pass_quant) {
m->quant_mode_ = 1;
} else {
JPEGLI_ERROR("Invalid quantization mode change");
}
if (m->quant_mode_ > 1 && cinfo->dither_mode == JDITHER_ORDERED) {
cinfo->dither_mode = JDITHER_FS;
}
if (m->quant_mode_ == 1) {
ChooseColorMap1Pass(cinfo);
} else if (m->quant_mode_ == 2) {
m->quant_pass_ = 0;
if (!ReadOutputPass(cinfo)) {
return FALSE;
}
ChooseColorMap2Pass(cinfo);
}
if (m->quant_mode_ == 2 ||
(m->quant_mode_ == 3 && m->regenerate_inverse_colormap_)) {
CreateInverseColorMap(cinfo);
}
if (cinfo->dither_mode == JDITHER_ORDERED) {
CreateOrderedDitherTables(cinfo);
} else if (cinfo->dither_mode == JDITHER_FS) {
InitFSDitherState(cinfo);
}
m->quant_pass_ = 1;
return TRUE;
}
void AllocateCoefficientBuffer(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
j_common_ptr comptr = reinterpret_cast<j_common_ptr>(cinfo);
jvirt_barray_ptr* coef_arrays = jpegli::Allocate<jvirt_barray_ptr>(
cinfo, cinfo->num_components, JPOOL_IMAGE);
for (int c = 0; c < cinfo->num_components; ++c) {
jpeg_component_info* comp = &cinfo->comp_info[c];
size_t height_in_blocks =
m->streaming_mode_ ? comp->v_samp_factor : comp->height_in_blocks;
coef_arrays[c] = (*cinfo->mem->request_virt_barray)(
comptr, JPOOL_IMAGE, TRUE, comp->width_in_blocks, height_in_blocks,
comp->v_samp_factor);
}
cinfo->master->coef_arrays = coef_arrays;
(*cinfo->mem->realize_virt_arrays)(comptr);
}
} // namespace jpegli
@ -106,25 +494,27 @@ void jpegli_CreateDecompress(j_decompress_ptr cinfo, int version,
}
jpegli::InitMemoryManager(reinterpret_cast<j_common_ptr>(cinfo));
cinfo->is_decompressor = TRUE;
cinfo->progress = nullptr;
cinfo->src = nullptr;
cinfo->marker_list = nullptr;
cinfo->input_scan_number = 0;
cinfo->quantize_colors = FALSE;
cinfo->desired_number_of_colors = 0;
for (int i = 0; i < NUM_QUANT_TBLS; i++) {
cinfo->quant_tbl_ptrs[i] = nullptr;
}
for (int i = 0; i < NUM_HUFF_TBLS; i++) {
cinfo->dc_huff_tbl_ptrs[i] = nullptr;
cinfo->ac_huff_tbl_ptrs[i] = nullptr;
}
cinfo->global_state = jpegli::kDecStart;
cinfo->buffered_image = FALSE;
cinfo->raw_data_out = FALSE;
cinfo->output_scanline = 0;
cinfo->sample_range_limit = nullptr; // not used
cinfo->rec_outbuf_height = 1; // output works with any buffer height
cinfo->unread_marker = 0; // not used
// TODO(szabadka) Fill this in for progressive mode.
cinfo->coef_bits = nullptr;
cinfo->master = new jpeg_decomp_master;
jpeg_decomp_master* m = cinfo->master;
for (int i = 0; i < 16; ++i) {
cinfo->master->app_marker_parsers[i] = nullptr;
m->app_marker_parsers[i] = nullptr;
}
cinfo->master->com_marker_parser = nullptr;
m->com_marker_parser = nullptr;
memset(m->markers_to_save_, 0, sizeof(m->markers_to_save_));
jpegli::InitializeDecompressParams(cinfo);
jpegli::InitializeImage(cinfo);
}
void jpegli_destroy_decompress(j_decompress_ptr cinfo) {
@ -137,8 +527,12 @@ void jpegli_abort_decompress(j_decompress_ptr cinfo) {
void jpegli_save_markers(j_decompress_ptr cinfo, int marker_code,
unsigned int length_limit) {
// TODO(szabadka) Limit our memory usage by taking into account length_limit.
jpeg_decomp_master* m = cinfo->master;
m->markers_to_save_.insert(marker_code);
if (marker_code < 0xe0) {
JPEGLI_ERROR("jpegli_save_markers: invalid marker code %d", marker_code);
}
m->markers_to_save_[marker_code - 0xe0] = 1;
}
void jpegli_set_marker_processor(j_decompress_ptr cinfo, int marker_code,
@ -156,7 +550,9 @@ void jpegli_set_marker_processor(j_decompress_ptr cinfo, int marker_code,
int jpegli_consume_input(j_decompress_ptr cinfo) {
if (cinfo->global_state == jpegli::kDecStart) {
(*cinfo->err->reset_error_mgr)(reinterpret_cast<j_common_ptr>(cinfo));
(*cinfo->src->init_source)(cinfo);
jpegli::InitializeDecompressParams(cinfo);
jpegli::InitializeImage(cinfo);
cinfo->global_state = jpegli::kDecInHeader;
}
@ -181,6 +577,9 @@ int jpegli_read_header(j_decompress_ptr cinfo, boolean require_image) {
JPEGLI_ERROR("jpegli_read_header: unexpected state %d",
cinfo->global_state);
}
if (cinfo->src == nullptr) {
JPEGLI_ERROR("Missing source.");
}
for (;;) {
int retcode = jpegli_consume_input(cinfo);
if (retcode == JPEG_SUSPENDED) {
@ -188,7 +587,11 @@ int jpegli_read_header(j_decompress_ptr cinfo, boolean require_image) {
} else if (retcode == JPEG_REACHED_SOS) {
break;
} else if (retcode == JPEG_REACHED_EOI) {
JPEGLI_ERROR("jpegli_read_header: unexpected EOI marker.");
if (require_image) {
JPEGLI_ERROR("jpegli_read_header: unexpected EOI marker.");
}
jpegli_abort_decompress(cinfo);
return JPEG_HEADER_TABLES_ONLY;
}
};
return JPEG_HEADER_OK;
@ -219,15 +622,71 @@ boolean jpegli_read_icc_profile(j_decompress_ptr cinfo, JOCTET** icc_data_ptr,
return TRUE;
}
void jpegli_calc_output_dimensions(j_decompress_ptr cinfo) {
void jpegli_core_output_dimensions(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
if (!m->found_sof_) {
JPEGLI_ERROR("No SOF marker found.");
}
// Resampling is not yet implemented.
cinfo->output_width = cinfo->image_width;
cinfo->output_height = cinfo->image_height;
cinfo->output_components = cinfo->out_color_components;
if (cinfo->raw_data_out) {
if (cinfo->scale_num != 1 || cinfo->scale_denom != 1) {
JPEGLI_ERROR("Output scaling is not supported in raw output mode");
}
}
if (cinfo->scale_num != 1 || cinfo->scale_denom != 1) {
int dctsize = 16;
while (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * (dctsize - 1)) {
--dctsize;
}
m->min_scaled_dct_size = dctsize;
cinfo->output_width =
jpegli::DivCeil(cinfo->image_width * dctsize, DCTSIZE);
cinfo->output_height =
jpegli::DivCeil(cinfo->image_height * dctsize, DCTSIZE);
for (int c = 0; c < cinfo->num_components; ++c) {
m->scaled_dct_size[c] = m->min_scaled_dct_size;
}
} else {
cinfo->output_width = cinfo->image_width;
cinfo->output_height = cinfo->image_height;
m->min_scaled_dct_size = DCTSIZE;
for (int c = 0; c < cinfo->num_components; ++c) {
m->scaled_dct_size[c] = DCTSIZE;
}
}
}
void jpegli_calc_output_dimensions(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
jpegli_core_output_dimensions(cinfo);
for (int c = 0; c < cinfo->num_components; ++c) {
jpeg_component_info* comp = &cinfo->comp_info[c];
m->h_factor[c] = cinfo->max_h_samp_factor / comp->h_samp_factor;
m->v_factor[c] = cinfo->max_v_samp_factor / comp->v_samp_factor;
}
if (cinfo->scale_num != 1 || cinfo->scale_denom != 1) {
for (int c = 0; c < cinfo->num_components; ++c) {
// Prefer IDCT scaling over 2x upsampling.
while (m->scaled_dct_size[c] < DCTSIZE && (m->v_factor[c] % 2) == 0 &&
(m->h_factor[c] % 2) == 0) {
m->scaled_dct_size[c] *= 2;
m->v_factor[c] /= 2;
m->h_factor[c] /= 2;
}
}
}
if (cinfo->out_color_space == JCS_GRAYSCALE) {
cinfo->out_color_components = 1;
} else if (cinfo->out_color_space == JCS_RGB ||
cinfo->out_color_space == JCS_YCbCr) {
cinfo->out_color_components = 3;
} else if (cinfo->out_color_space == JCS_CMYK ||
cinfo->out_color_space == JCS_YCCK) {
cinfo->out_color_components = 4;
} else {
cinfo->out_color_components = cinfo->num_components;
}
cinfo->output_components =
cinfo->quantize_colors ? 1 : cinfo->out_color_components;
cinfo->rec_outbuf_height = 1;
}
@ -243,51 +702,77 @@ boolean jpegli_input_complete(j_decompress_ptr cinfo) {
}
boolean jpegli_start_decompress(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
if (cinfo->global_state == jpegli::kDecHeaderDone) {
m->streaming_mode_ = !m->is_multiscan_ && !cinfo->buffered_image &&
(!cinfo->quantize_colors || !cinfo->two_pass_quantize);
jpegli::AllocateCoefficientBuffer(cinfo);
jpegli_calc_output_dimensions(cinfo);
cinfo->global_state = jpegli::kDecProcessScan;
jpegli::PrepareForScan(cinfo);
if (cinfo->quantize_colors) {
if (cinfo->colormap != nullptr) {
cinfo->enable_external_quant = TRUE;
} else if (cinfo->two_pass_quantize &&
cinfo->out_color_space == JCS_RGB) {
cinfo->enable_2pass_quant = TRUE;
} else {
cinfo->enable_1pass_quant = TRUE;
}
}
jpegli::InitProgressMonitor(cinfo, /*coef_only=*/false);
if (cinfo->buffered_image == TRUE) {
cinfo->output_scan_number = 0;
return TRUE;
}
} else if (!cinfo->master->is_multiscan_) {
} else if (!m->is_multiscan_) {
JPEGLI_ERROR("jpegli_start_decompress: unexpected state %d",
cinfo->global_state);
}
if (cinfo->master->is_multiscan_) {
if (m->is_multiscan_) {
if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_start_decompress: unexpected state %d",
cinfo->global_state);
}
while (!cinfo->master->found_eoi_) {
int retcode = jpegli::ConsumeInput(cinfo);
if (retcode == JPEG_SUSPENDED) {
while (!m->found_eoi_) {
jpegli::ProgressMonitorInputPass(cinfo);
if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) {
return FALSE;
}
}
}
cinfo->output_scan_number = cinfo->input_scan_number;
jpegli::PrepareForOutput(cinfo);
return TRUE;
if (cinfo->quantize_colors) {
return jpegli::PrepareQuantizedOutput(cinfo);
} else {
return TRUE;
}
}
boolean jpegli_start_output(j_decompress_ptr cinfo, int scan_number) {
jpeg_decomp_master* m = cinfo->master;
if (!cinfo->buffered_image) {
JPEGLI_ERROR("jpegli_start_output: buffered image mode was not set");
}
if (cinfo->global_state != jpegli::kDecProcessScan) {
if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_start_output: unexpected state %d",
cinfo->global_state);
}
cinfo->output_scan_number = std::max(1, scan_number);
if (cinfo->master->found_eoi_) {
if (m->found_eoi_) {
cinfo->output_scan_number =
std::min(cinfo->output_scan_number, cinfo->input_scan_number);
}
jpegli::InitProgressMonitorForOutput(cinfo);
// TODO(szabadka): Figure out how much we can reuse.
jpegli::PrepareForOutput(cinfo);
return TRUE;
if (cinfo->quantize_colors) {
return jpegli::PrepareQuantizedOutput(cinfo);
} else {
return TRUE;
}
}
boolean jpegli_finish_output(j_decompress_ptr cinfo) {
@ -331,6 +816,7 @@ JDIMENSION jpegli_read_scanlines(j_decompress_ptr cinfo, JSAMPARRAY scanlines,
if (cinfo->output_scanline + max_lines > cinfo->output_height) {
max_lines = cinfo->output_height - cinfo->output_scanline;
}
jpegli::ProgressMonitorOutputPass(cinfo);
size_t num_output_rows = 0;
while (num_output_rows < max_lines) {
if (jpegli::IsInputReady(cinfo)) {
@ -349,19 +835,24 @@ JDIMENSION jpegli_skip_scanlines(j_decompress_ptr cinfo, JDIMENSION num_lines) {
void jpegli_crop_scanline(j_decompress_ptr cinfo, JDIMENSION* xoffset,
JDIMENSION* width) {
jpeg_decomp_master* m = cinfo->master;
if ((cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) ||
cinfo->output_scanline != 0) {
JPEGLI_ERROR("jpegli_crop_decompress: unexpected state %d",
cinfo->global_state);
}
if (cinfo->raw_data_out) {
JPEGLI_ERROR("Output cropping is not supported in raw data mode");
}
if (xoffset == nullptr || width == nullptr || *width == 0 ||
*xoffset + *width > cinfo->output_width) {
JPEGLI_ERROR("jpegli_crop_scanline: Invalid arguments");
}
// TODO(szabadka) Skip the IDCT for skipped over blocks.
size_t xend = *xoffset + *width;
*xoffset = (*xoffset / DCTSIZE) * DCTSIZE;
size_t iMCU_width = m->min_scaled_dct_size * cinfo->max_h_samp_factor;
*xoffset = (*xoffset / iMCU_width) * iMCU_width;
*width = xend - *xoffset;
cinfo->master->xoffset_ = *xoffset;
cinfo->output_width = *width;
@ -379,6 +870,7 @@ JDIMENSION jpegli_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data,
if (max_lines < iMCU_height) {
JPEGLI_ERROR("jpegli_read_raw_data: output buffer too small");
}
jpegli::ProgressMonitorOutputPass(cinfo);
while (!jpegli::IsInputReady(cinfo)) {
if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) {
return 0;
@ -392,39 +884,29 @@ JDIMENSION jpegli_read_raw_data(j_decompress_ptr cinfo, JSAMPIMAGE data,
}
jvirt_barray_ptr* jpegli_read_coefficients(j_decompress_ptr cinfo) {
if (cinfo->global_state != jpegli::kDecHeaderDone) {
jpeg_decomp_master* m = cinfo->master;
m->streaming_mode_ = false;
if (!cinfo->buffered_image && cinfo->global_state == jpegli::kDecHeaderDone) {
jpegli::AllocateCoefficientBuffer(cinfo);
jpegli_calc_output_dimensions(cinfo);
jpegli::InitProgressMonitor(cinfo, /*coef_only=*/true);
jpegli::PrepareForScan(cinfo);
}
if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_read_coefficients: unexpected state %d",
cinfo->global_state);
}
cinfo->global_state = jpegli::kDecProcessScan;
jpeg_decomp_master* m = cinfo->master;
while (!m->found_eoi_) {
int retcode = jpegli::ConsumeInput(cinfo);
if (retcode == JPEG_SUSPENDED) {
return nullptr;
if (!cinfo->buffered_image) {
while (!m->found_eoi_) {
jpegli::ProgressMonitorInputPass(cinfo);
if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) {
return nullptr;
}
}
cinfo->output_scanline = cinfo->output_height;
}
j_common_ptr comptr = reinterpret_cast<j_common_ptr>(cinfo);
jvirt_barray_ptr* coef_arrays = jpegli::Allocate<jvirt_barray_ptr>(
cinfo, cinfo->num_components, JPOOL_IMAGE);
for (int c = 0; c < cinfo->num_components; ++c) {
size_t xsize_blocks = cinfo->comp_info[c].width_in_blocks;
size_t ysize_blocks = cinfo->comp_info[c].height_in_blocks;
coef_arrays[c] = (*cinfo->mem->request_virt_barray)(
comptr, JPOOL_IMAGE, FALSE, xsize_blocks, ysize_blocks, 1);
}
(*cinfo->mem->realize_virt_arrays)(comptr);
for (int c = 0; c < cinfo->num_components; ++c) {
jpeg_component_info* comp = &cinfo->comp_info[c];
for (size_t by = 0; by < comp->height_in_blocks; ++by) {
JBLOCKARRAY ba = (*cinfo->mem->access_virt_barray)(comptr, coef_arrays[c],
by, 1, true);
size_t stride = comp->width_in_blocks * sizeof(JBLOCK);
size_t offset = by * comp->width_in_blocks * DCTSIZE2;
memcpy(ba[0], &m->components_[c].coeffs[offset], stride);
}
}
return coef_arrays;
return m->coef_arrays;
}
boolean jpegli_finish_decompress(j_decompress_ptr cinfo) {
@ -433,26 +915,67 @@ boolean jpegli_finish_decompress(j_decompress_ptr cinfo) {
JPEGLI_ERROR("jpegli_finish_decompress: unexpected state %d",
cinfo->global_state);
}
if (!cinfo->buffered_image && cinfo->output_scanline < cinfo->output_height) {
JPEGLI_ERROR("Incomplete output");
}
while (!cinfo->master->found_eoi_) {
int retcode = jpegli::ConsumeInput(cinfo);
if (retcode == JPEG_SUSPENDED) {
if (jpegli::ConsumeInput(cinfo) == JPEG_SUSPENDED) {
return FALSE;
}
}
(*cinfo->src->term_source)(cinfo);
jpegli_abort_decompress(cinfo);
return TRUE;
}
boolean jpegli_resync_to_restart(j_decompress_ptr cinfo, int desired) {
// The default resync_to_restart will just throw an error.
JPEGLI_ERROR("Invalid restart marker found.");
JPEGLI_WARN("Invalid restart marker found: 0x%02x vs 0x%02x.",
cinfo->unread_marker, 0xd0 + desired);
// This is a trivial implementation, we just let the decoder skip the entire
// scan and attempt to render the partial input.
return TRUE;
}
void jpegli_new_colormap(j_decompress_ptr cinfo) {
if (cinfo->global_state != jpegli::kDecProcessScan &&
cinfo->global_state != jpegli::kDecProcessMarkers) {
JPEGLI_ERROR("jpegli_new_colormap: unexpected state %d",
cinfo->global_state);
}
if (!cinfo->buffered_image) {
JPEGLI_ERROR("jpegli_new_colormap: not in buffered image mode");
}
if (!cinfo->enable_external_quant) {
JPEGLI_ERROR("external colormap quantizer was not enabled");
}
if (!cinfo->quantize_colors || cinfo->colormap == nullptr) {
JPEGLI_ERROR("jpegli_new_colormap: not in external colormap mode");
}
cinfo->master->regenerate_inverse_colormap_ = true;
}
void jpegli_set_output_format(j_decompress_ptr cinfo, JpegliDataType data_type,
JpegliEndianness endianness) {
cinfo->master->output_data_type_ = data_type;
cinfo->master->swap_endianness_ =
((endianness == JPEGLI_BIG_ENDIAN && IsLittleEndian()) ||
(endianness == JPEGLI_LITTLE_ENDIAN && !IsLittleEndian()));
switch (data_type) {
case JPEGLI_TYPE_UINT8:
case JPEGLI_TYPE_UINT16:
case JPEGLI_TYPE_FLOAT:
cinfo->master->output_data_type_ = data_type;
break;
default:
JPEGLI_ERROR("Unsupported data type %d", data_type);
}
switch (endianness) {
case JPEGLI_NATIVE_ENDIAN:
cinfo->master->swap_endianness_ = false;
break;
case JPEGLI_LITTLE_ENDIAN:
cinfo->master->swap_endianness_ = !IsLittleEndian();
break;
case JPEGLI_BIG_ENDIAN:
cinfo->master->swap_endianness_ = IsLittleEndian();
break;
default:
JPEGLI_ERROR("Unsupported endianness %d", endianness);
}
}

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

@ -72,6 +72,9 @@ boolean jpegli_input_complete(j_decompress_ptr cinfo);
int jpegli_consume_input(j_decompress_ptr cinfo);
#if JPEG_LIB_VERSION >= 80
void jpegli_core_output_dimensions(j_decompress_ptr cinfo);
#endif
void jpegli_calc_output_dimensions(j_decompress_ptr cinfo);
void jpegli_save_markers(j_decompress_ptr cinfo, int marker_code,
@ -89,6 +92,8 @@ void jpegli_abort_decompress(j_decompress_ptr cinfo);
void jpegli_destroy_decompress(j_decompress_ptr cinfo);
void jpegli_new_colormap(j_decompress_ptr cinfo);
//
// New API functions that are not available in libjpeg
//

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

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

@ -9,9 +9,6 @@
#include <stdint.h>
#include <sys/types.h>
#include <array>
#include <hwy/aligned_allocator.h>
#include <set>
#include <vector>
#include "lib/jpegli/common.h"
@ -20,21 +17,20 @@
namespace jpegli {
typedef int16_t coeff_t;
static constexpr int kNeedMoreInput = 100;
static constexpr int kHandleRestart = 101;
static constexpr int kHandleMarkerProcessor = 102;
static constexpr int kProcessNextMarker = 103;
static constexpr size_t kAllHuffLutSize = NUM_HUFF_TBLS * kJpegHuffmanLutSize;
// Represents one component of a jpeg file.
struct DecJPEGComponent {
// The DCT coefficients of this component, laid out block-by-block, divided
// through the quantization matrix values.
hwy::AlignedFreeUniquePtr<coeff_t[]> coeffs;
};
typedef int16_t coeff_t;
// State of the decoder that has to be saved before decoding one MCU in case
// we run out of the bitstream.
struct MCUCodingState {
coeff_t last_dc_coeff[kMaxComponents];
int eobrun;
std::vector<coeff_t> coeffs;
coeff_t coeffs[D_MAX_BLOCKS_IN_MCU * DCTSIZE2];
};
} // namespace jpegli
@ -45,24 +41,29 @@ struct jpeg_decomp_master {
//
// Input handling state.
//
std::vector<uint8_t> input_buffer_;
size_t input_buffer_pos_;
// Number of bits after codestream_pos_ that were already processed.
size_t codestream_bits_ahead_ = 0;
size_t codestream_bits_ahead_;
bool streaming_mode_;
// Coefficient buffers
jvirt_barray_ptr* coef_arrays;
JBLOCKARRAY coeff_rows[jpegli::kMaxComponents];
//
// Marker data processing state.
//
bool found_soi_ = false;
bool found_dri_ = false;
bool found_sof_ = false;
bool found_eoi_ = false;
size_t icc_index_ = 0;
size_t icc_total_ = 0;
bool found_soi_;
bool found_dri_;
bool found_sof_;
bool found_eoi_;
size_t icc_index_;
size_t icc_total_;
std::vector<uint8_t> icc_profile_;
std::vector<jpegli::DecJPEGComponent> components_;
std::vector<jpegli::HuffmanTableEntry> dc_huff_lut_;
std::vector<jpegli::HuffmanTableEntry> ac_huff_lut_;
uint8_t huff_slot_defined_[256] = {};
std::set<int> markers_to_save_;
jpegli::HuffmanTableEntry dc_huff_lut_[jpegli::kAllHuffLutSize];
jpegli::HuffmanTableEntry ac_huff_lut_[jpegli::kAllHuffLutSize];
uint8_t markers_to_save_[32];
jpeg_marker_parser_method app_marker_parsers[16];
jpeg_marker_parser_method com_marker_parser;
// Whether this jpeg has multiple scans (progressive or non-interleaved
@ -71,6 +72,8 @@ struct jpeg_decomp_master {
// Fields defined by SOF marker.
size_t iMCU_cols_;
int h_factor[jpegli::kMaxComponents];
int v_factor[jpegli::kMaxComponents];
// Initialized at strat of frame.
uint16_t scan_progression_[jpegli::kMaxComponents][DCTSIZE2];
@ -91,30 +94,57 @@ struct jpeg_decomp_master {
//
// Rendering state.
//
int output_passes_done_;
JpegliDataType output_data_type_ = JPEGLI_TYPE_UINT8;
bool swap_endianness_ = false;
size_t xoffset_ = 0;
size_t xoffset_;
int min_scaled_dct_size;
int scaled_dct_size[jpegli::kMaxComponents];
size_t raw_height_[jpegli::kMaxComponents];
jpegli::RowBuffer<float> raw_output_[jpegli::kMaxComponents];
jpegli::RowBuffer<float> render_output_[jpegli::kMaxComponents];
void (*inverse_transform[jpegli::kMaxComponents])(
const int16_t* JXL_RESTRICT qblock, const float* JXL_RESTRICT dequant,
const float* JXL_RESTRICT biases, float* JXL_RESTRICT scratch_space,
float* JXL_RESTRICT output, size_t output_stride, size_t dctsize);
void (*color_transform)(float* row[jpegli::kMaxComponents], size_t len);
float* idct_scratch_;
float* upsample_scratch_;
uint8_t* output_scratch_;
int16_t* smoothing_scratch_;
float* dequant_;
// 1 = 1pass, 2 = 2pass, 3 = external
int quant_mode_;
int quant_pass_;
int num_colors_[jpegli::kMaxComponents];
uint8_t* colormap_lut_;
uint8_t* pixels_;
JSAMPARRAY scanlines_;
JDIMENSION max_lines_;
size_t num_output_rows_;
std::array<size_t, jpegli::kMaxComponents> raw_height_;
std::array<jpegli::RowBuffer<float>, jpegli::kMaxComponents> raw_output_;
std::array<jpegli::RowBuffer<float>, jpegli::kMaxComponents> render_output_;
hwy::AlignedFreeUniquePtr<float[]> idct_scratch_;
hwy::AlignedFreeUniquePtr<float[]> upsample_scratch_;
hwy::AlignedFreeUniquePtr<uint8_t[]> output_scratch_;
hwy::AlignedFreeUniquePtr<float[]> dequant_;
std::vector<std::vector<uint8_t>> candidate_lists_;
bool regenerate_inverse_colormap_;
float* dither_[jpegli::kMaxComponents];
float* error_row_[2 * jpegli::kMaxComponents];
size_t dither_size_;
size_t dither_mask_;
// Per channel and per frequency statistics about the number of nonzeros and
// the sum of coefficient absolute values, used in dequantization bias
// computation.
hwy::AlignedFreeUniquePtr<int[]> nonzeros_;
hwy::AlignedFreeUniquePtr<int[]> sumabs_;
std::vector<size_t> num_processed_blocks_;
hwy::AlignedFreeUniquePtr<float[]> biases_;
int* nonzeros_;
int* sumabs_;
size_t num_processed_blocks_[jpegli::kMaxComponents];
float* biases_;
#define SAVED_COEFS 10
// This holds the coef_bits of the scan before the current scan,
// i.e. the bottom half when rendering incomplete scans.
int (*coef_bits_latch)[SAVED_COEFS];
int (*prev_coef_bits_latch)[SAVED_COEFS];
bool apply_smoothing;
};
#endif // LIB_JPEGLI_DECODE_INTERNAL_H_

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

@ -12,23 +12,21 @@
#include "lib/jpegli/error.h"
#include "lib/jpegli/huffman.h"
#include "lib/jpegli/memory_manager.h"
#include "lib/jpegli/source_manager.h"
#include "lib/jxl/base/printf_macros.h"
namespace jpegli {
namespace {
constexpr int kMaxSampling = 2;
constexpr int kMaxDimPixels = 65535;
constexpr uint8_t kIccProfileTag[12] = "ICC_PROFILE";
// Macros for commonly used error conditions.
#define JPEG_VERIFY_LEN(n) \
if (pos + (n) > len) { \
return JPEGLI_ERROR("Unexpected end of input: pos=%" PRIuS \
" need=%d len=%" PRIuS, \
pos, static_cast<int>(n), len); \
#define JPEG_VERIFY_LEN(n) \
if (pos + (n) > len) { \
return JPEGLI_ERROR("Unexpected end of marker: pos=%" PRIuS \
" need=%d len=%" PRIuS, \
pos, static_cast<int>(n), len); \
}
#define JPEG_VERIFY_INPUT(var, low, high) \
@ -62,9 +60,9 @@ void ProcessSOF(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
JPEGLI_ERROR("Duplicate SOF marker.");
}
m->found_sof_ = true;
cinfo->progressive_mode = (data[1] == 0xc2);
cinfo->progressive_mode = (cinfo->unread_marker == 0xc2);
cinfo->arith_code = 0;
size_t pos = 4;
size_t pos = 2;
JPEG_VERIFY_LEN(6);
cinfo->data_precision = ReadUint8(data, &pos);
cinfo->image_height = ReadUint16(data, &pos);
@ -75,12 +73,11 @@ void ProcessSOF(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
JPEG_VERIFY_INPUT(cinfo->image_width, 1, kMaxDimPixels);
JPEG_VERIFY_INPUT(cinfo->num_components, 1, kMaxComponents);
JPEG_VERIFY_LEN(3 * cinfo->num_components);
cinfo->comp_info =
jpegli::Allocate<jpeg_component_info>(cinfo, cinfo->num_components);
m->components_.resize(cinfo->num_components);
cinfo->comp_info = jpegli::Allocate<jpeg_component_info>(
cinfo, cinfo->num_components, JPOOL_IMAGE);
// Read sampling factors and quant table index for each component.
std::vector<bool> ids_seen(256, false);
uint8_t ids_seen[256] = {0};
cinfo->max_h_samp_factor = 1;
cinfo->max_v_samp_factor = 1;
for (int i = 0; i < cinfo->num_components; ++i) {
@ -90,25 +87,26 @@ void ProcessSOF(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
if (ids_seen[id]) { // (cf. section B.2.2, syntax of Ci)
JPEGLI_ERROR("Duplicate ID %d in SOF.", id);
}
ids_seen[id] = true;
ids_seen[id] = 1;
comp->component_id = id;
int factor = ReadUint8(data, &pos);
int h_samp_factor = factor >> 4;
int v_samp_factor = factor & 0xf;
JPEG_VERIFY_INPUT(h_samp_factor, 1, kMaxSampling);
JPEG_VERIFY_INPUT(v_samp_factor, 1, kMaxSampling);
JPEG_VERIFY_INPUT(h_samp_factor, 1, MAX_SAMP_FACTOR);
JPEG_VERIFY_INPUT(v_samp_factor, 1, MAX_SAMP_FACTOR);
comp->h_samp_factor = h_samp_factor;
comp->v_samp_factor = v_samp_factor;
cinfo->max_h_samp_factor =
std::max(cinfo->max_h_samp_factor, h_samp_factor);
cinfo->max_v_samp_factor =
std::max(cinfo->max_v_samp_factor, v_samp_factor);
uint8_t quant_tbl_idx = ReadUint8(data, &pos);
int quant_tbl_idx = ReadUint8(data, &pos);
JPEG_VERIFY_INPUT(quant_tbl_idx, 0, NUM_QUANT_TBLS - 1);
comp->quant_tbl_no = quant_tbl_idx;
comp->quant_table = cinfo->quant_tbl_ptrs[quant_tbl_idx];
if (comp->quant_table == nullptr) {
if (cinfo->quant_tbl_ptrs[quant_tbl_idx] == nullptr) {
JPEGLI_ERROR("Quantization table with index %u not found", quant_tbl_idx);
}
comp->quant_table = nullptr; // will be allocated after SOS marker
}
JPEG_VERIFY_MARKER_END();
@ -141,7 +139,6 @@ void ProcessSOF(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
}
cinfo->out_color_space = JCS_CMYK;
}
cinfo->out_color_components = cinfo->num_components;
// We have checked above that none of the sampling factors are 0, so the max
// sampling factors can not be 0.
@ -151,18 +148,17 @@ void ProcessSOF(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
DivCeil(cinfo->image_width, cinfo->max_h_samp_factor * DCTSIZE);
// Compute the block dimensions for each component.
for (int i = 0; i < cinfo->num_components; ++i) {
DecJPEGComponent* c = &m->components_[i];
jpeg_component_info* comp = &cinfo->comp_info[i];
if (cinfo->max_h_samp_factor % comp->h_samp_factor != 0 ||
cinfo->max_v_samp_factor % comp->v_samp_factor != 0) {
JPEGLI_ERROR("Non-integral subsampling ratios.");
}
comp->width_in_blocks = m->iMCU_cols_ * comp->h_samp_factor;
comp->height_in_blocks = cinfo->total_iMCU_rows * comp->v_samp_factor;
const uint64_t num_blocks =
static_cast<uint64_t>(comp->width_in_blocks) * comp->height_in_blocks;
c->coeffs = hwy::AllocateAligned<coeff_t>(num_blocks * DCTSIZE2);
memset(c->coeffs.get(), 0, num_blocks * DCTSIZE2 * sizeof(coeff_t));
m->h_factor[i] = cinfo->max_h_samp_factor / comp->h_samp_factor;
m->v_factor[i] = cinfo->max_v_samp_factor / comp->v_samp_factor;
comp->downsampled_width = DivCeil(cinfo->image_width, m->h_factor[i]);
comp->downsampled_height = DivCeil(cinfo->image_height, m->v_factor[i]);
comp->width_in_blocks = DivCeil(comp->downsampled_width, DCTSIZE);
comp->height_in_blocks = DivCeil(comp->downsampled_height, DCTSIZE);
}
memset(m->scan_progression_, 0, sizeof(m->scan_progression_));
}
@ -172,21 +168,22 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
if (!m->found_sof_) {
JPEGLI_ERROR("Unexpected SOS marker.");
}
size_t pos = 4;
size_t pos = 2;
JPEG_VERIFY_LEN(1);
cinfo->comps_in_scan = ReadUint8(data, &pos);
JPEG_VERIFY_INPUT(cinfo->comps_in_scan, 1, cinfo->num_components);
JPEG_VERIFY_INPUT(cinfo->comps_in_scan, 1, MAX_COMPS_IN_SCAN);
JPEG_VERIFY_LEN(2 * cinfo->comps_in_scan);
bool is_interleaved = (cinfo->comps_in_scan > 1);
std::vector<bool> ids_seen(256, false);
uint8_t ids_seen[256] = {0};
cinfo->blocks_in_MCU = 0;
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
int id = ReadUint8(data, &pos);
if (ids_seen[id]) { // (cf. section B.2.3, regarding CSj)
return JPEGLI_ERROR("Duplicate ID %d in SOS.", id);
}
ids_seen[id] = true;
ids_seen[id] = 1;
jpeg_component_info* comp = nullptr;
for (int j = 0; j < cinfo->num_components; ++j) {
if (cinfo->comp_info[j].component_id == id) {
@ -242,6 +239,24 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
const uint16_t scan_bitmask =
cinfo->Ah == 0 ? (0xffff << cinfo->Al) : (1u << cinfo->Al);
const uint16_t refinement_bitmask = (1 << cinfo->Al) - 1;
if (!cinfo->coef_bits) {
cinfo->coef_bits =
Allocate<int[DCTSIZE2]>(cinfo, cinfo->num_components * 2, JPOOL_IMAGE);
m->coef_bits_latch =
Allocate<int[SAVED_COEFS]>(cinfo, cinfo->num_components, JPOOL_IMAGE);
m->prev_coef_bits_latch =
Allocate<int[SAVED_COEFS]>(cinfo, cinfo->num_components, JPOOL_IMAGE);
for (int c = 0; c < cinfo->num_components; ++c) {
for (int i = 0; i < DCTSIZE2; ++i) {
cinfo->coef_bits[c][i] = -1;
if (i < SAVED_COEFS) {
m->coef_bits_latch[c][i] = -1;
}
}
}
}
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
int comp_idx = cinfo->cur_comp_info[i]->component_index;
for (int k = cinfo->Ss; k <= cinfo->Se; ++k) {
@ -262,53 +277,13 @@ void ProcessSOS(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
if (cinfo->Al > 10) {
return JPEGLI_ERROR("Scan parameter Al=%d is not supported.", cinfo->Al);
}
// Check that all the Huffman tables needed for this scan are defined.
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
int dc_tbl_idx = cinfo->cur_comp_info[i]->dc_tbl_no;
int ac_tbl_idx = cinfo->cur_comp_info[i]->ac_tbl_no;
if (cinfo->Ss == 0 && !m->huff_slot_defined_[dc_tbl_idx]) {
return JPEGLI_ERROR(
"SOS marker: Could not find DC Huffman table with index %d",
dc_tbl_idx);
}
if (cinfo->Se > 0 && !m->huff_slot_defined_[ac_tbl_idx + 16]) {
return JPEGLI_ERROR(
"SOS marker: Could not find AC Huffman table with index %d",
ac_tbl_idx);
}
}
cinfo->MCU_rows_in_scan = cinfo->total_iMCU_rows;
cinfo->MCUs_per_row = m->iMCU_cols_;
m->mcu_rows_per_iMCU_row_ = 1;
if (!is_interleaved) {
const auto& comp = *cinfo->cur_comp_info[0];
cinfo->MCUs_per_row = DivCeil(cinfo->image_width * comp.h_samp_factor,
cinfo->max_h_samp_factor * DCTSIZE);
cinfo->MCU_rows_in_scan = DivCeil(cinfo->image_height * comp.v_samp_factor,
cinfo->max_v_samp_factor * DCTSIZE);
m->mcu_rows_per_iMCU_row_ = cinfo->cur_comp_info[0]->v_samp_factor;
}
memset(m->last_dc_coeff_, 0, sizeof(m->last_dc_coeff_));
m->restarts_to_go_ = cinfo->restart_interval;
m->next_restart_marker_ = 0;
m->eobrun_ = -1;
m->scan_mcu_row_ = 0;
m->scan_mcu_col_ = 0;
m->codestream_bits_ahead_ = 0;
m->mcu_.coeffs.resize(cinfo->blocks_in_MCU * DCTSIZE2);
++cinfo->input_scan_number;
cinfo->input_iMCU_row = 0;
}
// Reads the Define Huffman Table (DHT) marker segment and builds the Huffman
// decoding table in either dc_huff_lut_ or ac_huff_lut_, depending on the type
// and solt_id of Huffman code being read.
void ProcessDHT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
jpeg_decomp_master* m = cinfo->master;
constexpr int kLutSize = NUM_HUFF_TBLS * kJpegHuffmanLutSize;
m->dc_huff_lut_.resize(kLutSize);
m->ac_huff_lut_.resize(kLutSize);
size_t pos = 4;
size_t pos = 2;
if (pos == len) {
return JPEGLI_ERROR("DHT marker: no Huffman table found");
}
@ -317,79 +292,46 @@ void ProcessDHT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
// The index of the Huffman code in the current set of Huffman codes. For AC
// component Huffman codes, 0x10 is added to the index.
int slot_id = ReadUint8(data, &pos);
m->huff_slot_defined_[slot_id] = 1;
int huffman_index = slot_id;
int is_ac_table = (slot_id & 0x10) != 0;
HuffmanTableEntry* huff_lut;
JHUFF_TBL** table;
if (is_ac_table) {
huffman_index -= 0x10;
JPEG_VERIFY_INPUT(huffman_index, 0, NUM_HUFF_TBLS - 1);
huff_lut = &m->ac_huff_lut_[huffman_index * kJpegHuffmanLutSize];
table = &cinfo->ac_huff_tbl_ptrs[huffman_index];
} else {
JPEG_VERIFY_INPUT(huffman_index, 0, NUM_HUFF_TBLS - 1);
huff_lut = &m->dc_huff_lut_[huffman_index * kJpegHuffmanLutSize];
table = &cinfo->dc_huff_tbl_ptrs[huffman_index];
}
if (*table == nullptr) {
*table = jpegli_alloc_huff_table(reinterpret_cast<j_common_ptr>(cinfo));
}
// Bit length histogram
std::array<uint32_t, kJpegHuffmanMaxBitLength + 1> counts = {};
counts[0] = 0;
int total_count = 0;
int space = 1 << kJpegHuffmanMaxBitLength;
int max_depth = 1;
for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) {
int count = ReadUint8(data, &pos);
if (count != 0) {
max_depth = i;
}
counts[i] = count;
(*table)->bits[i] = count;
total_count += count;
space -= count * (1 << (kJpegHuffmanMaxBitLength - i));
}
if (is_ac_table) {
JPEG_VERIFY_INPUT(total_count, 0, kJpegHuffmanAlphabetSize);
} else {
JPEG_VERIFY_INPUT(total_count, 0, kJpegDCAlphabetSize);
// Allow symbols up to 15 here, we check later whether any invalid symbols
// are actually decoded.
// TODO(szabadka) Make sure decoder works (does not crash) with up to
// 15-nbits DC symbols and then increase kJpegDCAlphabetSize.
JPEG_VERIFY_INPUT(total_count, 0, 16);
}
JPEG_VERIFY_LEN(total_count);
// Symbol values sorted by increasing bit lengths.
std::array<uint32_t, kJpegHuffmanAlphabetSize + 1> values = {};
std::vector<bool> values_seen(256, false);
for (int i = 0; i < total_count; ++i) {
int value = ReadUint8(data, &pos);
if (!is_ac_table) {
JPEG_VERIFY_INPUT(value, 0, kJpegDCAlphabetSize - 1);
JPEG_VERIFY_INPUT(value, 0, 15);
}
if (values_seen[value]) {
return JPEGLI_ERROR("Duplicate Huffman code value %d", value);
}
values_seen[value] = true;
values[i] = value;
(*table)->huffval[i] = value;
}
for (int i = total_count; i < kJpegHuffmanAlphabetSize; ++i) {
(*table)->huffval[i] = 0;
}
// Add an invalid symbol that will have the all 1 code.
++counts[max_depth];
values[total_count] = kJpegHuffmanAlphabetSize;
space -= (1 << (kJpegHuffmanMaxBitLength - max_depth));
if (space < 0) {
JPEGLI_ERROR("Invalid Huffman code lengths.");
} else if (space > 0 && huff_lut[0].value != 0xffff) {
// Re-initialize the values to an invalid symbol so that we can recognize
// it when reading the bit stream using a Huffman code with space > 0.
for (int i = 0; i < kJpegHuffmanLutSize; ++i) {
huff_lut[i].bits = 0;
huff_lut[i].value = 0xffff;
}
}
BuildJpegHuffmanTable(&counts[0], &values[0], huff_lut);
}
JPEG_VERIFY_MARKER_END();
}
@ -399,7 +341,7 @@ void ProcessDQT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
if (m->found_sof_) {
JPEGLI_ERROR("Updating quant tables between scans is not supported.");
}
size_t pos = 4;
size_t pos = 2;
if (pos == len) {
return JPEGLI_ERROR("DQT marker: no quantization table found");
}
@ -428,13 +370,17 @@ void ProcessDQT(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
JPEG_VERIFY_MARKER_END();
}
void ProcessDNL(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
// Ignore marker.
}
void ProcessDRI(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
jpeg_decomp_master* m = cinfo->master;
if (m->found_dri_) {
return JPEGLI_ERROR("Duplicate DRI marker.");
}
m->found_dri_ = true;
size_t pos = 4;
size_t pos = 2;
JPEG_VERIFY_LEN(2);
cinfo->restart_interval = ReadUint16(data, &pos);
JPEG_VERIFY_MARKER_END();
@ -442,13 +388,9 @@ void ProcessDRI(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
void ProcessAPP(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
jpeg_decomp_master* m = cinfo->master;
const uint8_t marker = data[1];
const uint8_t* payload = data + 4;
size_t payload_size = len - 4;
if (m->app_marker_parsers[marker - 0xe0] != nullptr) {
(*m->app_marker_parsers[marker - 0xe0])(cinfo);
return;
}
const uint8_t marker = cinfo->unread_marker;
const uint8_t* payload = data + 2;
size_t payload_size = len - 2;
if (marker == 0xE0) {
if (payload_size >= 14 && memcmp(payload, "JFIF", 4) == 0) {
cinfo->saw_JFIF_marker = TRUE;
@ -495,10 +437,7 @@ void ProcessAPP(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
}
void ProcessCOM(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
jpeg_decomp_master* m = cinfo->master;
if (m->com_marker_parser != nullptr) {
(*m->com_marker_parser)(cinfo);
}
// Ignore marker.
}
void ProcessSOI(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
@ -514,63 +453,78 @@ void ProcessEOI(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
}
void SaveMarker(j_decompress_ptr cinfo, const uint8_t* data, size_t len) {
const uint8_t marker = data[1];
const uint8_t* payload = data + 4;
size_t payload_size = len - 4;
const uint8_t marker = cinfo->unread_marker;
const uint8_t* payload = data + 2;
size_t payload_size = len - 2;
// Insert new saved marker to the head of the list.
jpeg_saved_marker_ptr next = cinfo->marker_list;
cinfo->marker_list = jpegli::Allocate<jpeg_marker_struct>(cinfo, 1);
cinfo->marker_list =
jpegli::Allocate<jpeg_marker_struct>(cinfo, 1, JPOOL_IMAGE);
cinfo->marker_list->next = next;
cinfo->marker_list->marker = marker;
cinfo->marker_list->original_length = payload_size;
cinfo->marker_list->data_length = payload_size;
cinfo->marker_list->data = jpegli::Allocate<uint8_t>(cinfo, payload_size);
cinfo->marker_list->data =
jpegli::Allocate<uint8_t>(cinfo, payload_size, JPOOL_IMAGE);
memcpy(cinfo->marker_list->data, payload, payload_size);
}
uint8_t ProcessNextMarker(j_decompress_ptr cinfo) {
const uint8_t* data = cinfo->src->next_input_byte;
size_t len = cinfo->src->bytes_in_buffer;
size_t pos = 0;
uint8_t ProcessNextMarker(j_decompress_ptr cinfo, const uint8_t* const data,
const size_t len, size_t* pos) {
jpeg_decomp_master* m = cinfo->master;
// kIsValidMarker[i] == 1 means (0xc0 + i) is a valid marker.
static const uint8_t kIsValidMarker[] = {
1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
};
// Skip bytes between markers.
size_t num_skipped = 0;
while (pos + 1 < len && (data[pos] != 0xff || data[pos + 1] < 0xc0 ||
!kIsValidMarker[data[pos + 1] - 0xc0])) {
++(pos);
++num_skipped;
uint8_t marker = cinfo->unread_marker;
if (marker == 0) {
// kIsValidMarker[i] == 1 means (0xc0 + i) is a valid marker.
static const uint8_t kIsValidMarker[] = {
1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0,
};
// Skip bytes between markers.
while (*pos + 1 < len && (data[*pos] != 0xff || data[*pos + 1] < 0xc0 ||
!kIsValidMarker[data[*pos + 1] - 0xc0])) {
++(*pos);
++num_skipped;
}
if (*pos + 2 > len) {
return kNeedMoreInput;
}
marker = data[*pos + 1];
if (num_skipped > 0) {
if (m->found_soi_) {
JPEGLI_WARN("Skipped %d bytes before marker 0x%02x", (int)num_skipped,
marker);
} else {
JPEGLI_ERROR("Did not find SOI marker.");
}
}
*pos += 2;
cinfo->unread_marker = marker;
}
if (pos + 2 > len) {
return 0;
}
if (num_skipped > 0) {
AdvanceInput(cinfo, num_skipped);
}
uint8_t marker = data[pos + 1];
if (!m->found_soi_ && (num_skipped > 0 || marker != 0xd8)) {
if (!m->found_soi_ && marker != 0xd8) {
JPEGLI_ERROR("Did not find SOI marker.");
}
const uint8_t* marker_data = &data[pos];
size_t marker_len = 2;
if (GetMarkerProcessor(cinfo)) {
return kHandleMarkerProcessor;
}
const uint8_t* marker_data = &data[*pos];
size_t marker_len = 0;
if (marker != 0xd8 && marker != 0xd9) {
if (pos + 4 > len) {
return 0;
if (*pos + 2 > len) {
return kNeedMoreInput;
}
marker_len += (data[pos + 2] << 8) + data[pos + 3];
if (marker_len < 4) {
marker_len += (data[*pos] << 8) + data[*pos + 1];
if (marker_len < 2) {
JPEGLI_ERROR("Invalid marker length");
}
if (pos + marker_len > len) {
return 0;
if (*pos + marker_len > len) {
// TODO(szabadka) Limit our memory usage by using the skip_input_data
// source manager callback on APP markers that are not saved.
return kNeedMoreInput;
}
if (m->markers_to_save_.find(marker) != m->markers_to_save_.end()) {
if (marker >= 0xe0 && m->markers_to_save_[marker - 0xe0]) {
SaveMarker(cinfo, marker_data, marker_len);
}
}
@ -582,6 +536,8 @@ uint8_t ProcessNextMarker(j_decompress_ptr cinfo) {
ProcessSOS(cinfo, marker_data, marker_len);
} else if (marker == 0xdb) {
ProcessDQT(cinfo, marker_data, marker_len);
} else if (marker == 0xdc) {
ProcessDNL(cinfo, marker_data, marker_len);
} else if (marker == 0xdd) {
ProcessDRI(cinfo, marker_data, marker_len);
} else if (marker >= 0xe0 && marker <= 0xef) {
@ -595,25 +551,38 @@ uint8_t ProcessNextMarker(j_decompress_ptr cinfo) {
} else {
JPEGLI_ERROR("Unexpected marker 0x%x", marker);
}
pos += marker_len;
AdvanceInput(cinfo, marker_len);
return marker;
*pos += marker_len;
cinfo->unread_marker = 0;
if (marker == 0xda) {
return JPEG_REACHED_SOS;
} else if (marker == 0xd9) {
return JPEG_REACHED_EOI;
}
return kProcessNextMarker;
}
} // namespace
int ProcessMarkers(j_decompress_ptr cinfo) {
jpeg_marker_parser_method GetMarkerProcessor(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
uint8_t marker = cinfo->unread_marker;
jpeg_marker_parser_method callback = nullptr;
if (marker >= 0xe0 && marker <= 0xef) {
callback = m->app_marker_parsers[marker - 0xe0];
} else if (marker == 0xfe) {
callback = m->com_marker_parser;
}
return callback;
}
int ProcessMarkers(j_decompress_ptr cinfo, const uint8_t* const data,
const size_t len, size_t* pos) {
for (;;) {
uint8_t marker = ProcessNextMarker(cinfo);
if (marker == 0) {
break;
} else if (marker == 0xd9) {
return JPEG_REACHED_EOI;
} else if (marker == 0xda) {
return JPEG_REACHED_SOS;
int status = ProcessNextMarker(cinfo, data, len, pos);
if (status != kProcessNextMarker) {
return status;
}
}
return JPEG_SUSPENDED;
}
} // namespace jpegli

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

@ -24,7 +24,10 @@ namespace jpegli {
// EOI marker. Input buffer refill is handled by the caller;
// * JPEG_REACHED_SOS, if the the next SOS marker is found;
// * JPEG_REACHED_EOR, if the end of the input is found.
int ProcessMarkers(j_decompress_ptr cinfo);
int ProcessMarkers(j_decompress_ptr cinfo, const uint8_t* const data,
const size_t len, size_t* pos);
jpeg_marker_parser_method GetMarkerProcessor(j_decompress_ptr cinfo);
} // namespace jpegli

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

@ -7,9 +7,10 @@
#include <string.h>
#include <hwy/base.h>
#include "lib/jpegli/decode_internal.h"
#include "lib/jpegli/error.h"
#include "lib/jpegli/source_manager.h"
#include "lib/jxl/base/status.h"
namespace jpegli {
@ -84,16 +85,18 @@ struct BitReaderState {
--pos_;
// If we give back a 0 byte, we need to check if it was a 0xff/0x00 escape
// sequence, and if yes, we need to give back one more byte.
if (((pos_ == len_) ||
if (((pos_ == len_ && pos_ == next_marker_pos_) ||
(pos_ > 0 && pos_ < next_marker_pos_ && data_[pos_] == 0)) &&
(data_[pos_ - 1] == 0xff)) {
--pos_;
}
}
if (pos_ > next_marker_pos_) {
if (pos_ >= next_marker_pos_) {
*pos = next_marker_pos_;
// Data ran out before the scan was complete.
return false;
if (pos_ > next_marker_pos_ || *bit_pos > 0) {
// Data ran out before the scan was complete.
return false;
}
}
*pos = pos_;
return true;
@ -343,13 +346,18 @@ void SaveMCUCodingState(j_decompress_ptr cinfo) {
size_t offset = 0;
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
const jpeg_component_info* comp = cinfo->cur_comp_info[i];
DecJPEGComponent* c = &m->components_[comp->component_index];
int block_x = m->scan_mcu_col_ * comp->MCU_width;
int c = comp->component_index;
size_t block_x = m->scan_mcu_col_ * comp->MCU_width;
for (int iy = 0; iy < comp->MCU_height; ++iy) {
int block_y = m->scan_mcu_row_ * comp->MCU_height + iy;
size_t ncoeffs = comp->MCU_width * DCTSIZE2;
int block_idx = (block_y * comp->width_in_blocks + block_x) * DCTSIZE2;
coeff_t* coeffs = &c->coeffs[block_idx];
size_t block_y = m->scan_mcu_row_ * comp->MCU_height + iy;
size_t biy = block_y % comp->v_samp_factor;
if (block_y >= comp->height_in_blocks) {
continue;
}
size_t nblocks =
std::min<size_t>(comp->MCU_width, comp->width_in_blocks - block_x);
size_t ncoeffs = nblocks * DCTSIZE2;
coeff_t* coeffs = &m->coeff_rows[c][biy][block_x][0];
memcpy(&m->mcu_.coeffs[offset], coeffs, ncoeffs * sizeof(coeffs[0]));
offset += ncoeffs;
}
@ -363,83 +371,131 @@ void RestoreMCUCodingState(j_decompress_ptr cinfo) {
size_t offset = 0;
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
const jpeg_component_info* comp = cinfo->cur_comp_info[i];
DecJPEGComponent* c = &m->components_[comp->component_index];
int block_x = m->scan_mcu_col_ * comp->MCU_width;
int c = comp->component_index;
size_t block_x = m->scan_mcu_col_ * comp->MCU_width;
for (int iy = 0; iy < comp->MCU_height; ++iy) {
int block_y = m->scan_mcu_row_ * comp->MCU_height + iy;
size_t ncoeffs = comp->MCU_width * DCTSIZE2;
int block_idx = (block_y * comp->width_in_blocks + block_x) * DCTSIZE2;
coeff_t* coeffs = &c->coeffs[block_idx];
size_t block_y = m->scan_mcu_row_ * comp->MCU_height + iy;
size_t biy = block_y % comp->v_samp_factor;
if (block_y >= comp->height_in_blocks) {
continue;
}
size_t nblocks =
std::min<size_t>(comp->MCU_width, comp->width_in_blocks - block_x);
size_t ncoeffs = nblocks * DCTSIZE2;
coeff_t* coeffs = &m->coeff_rows[c][biy][block_x][0];
memcpy(coeffs, &m->mcu_.coeffs[offset], ncoeffs * sizeof(coeffs[0]));
offset += ncoeffs;
}
}
}
bool FinishScan(j_decompress_ptr cinfo, const uint8_t* data, const size_t len,
size_t* pos, size_t* bit_pos) {
jpeg_decomp_master* m = cinfo->master;
if (m->eobrun_ > 0) {
JPEGLI_ERROR("End-of-block run too long.");
}
m->eobrun_ = -1;
memset(m->last_dc_coeff_, 0, sizeof(m->last_dc_coeff_));
if (*bit_pos == 0) {
return true;
}
if (data[*pos] == 0xff) {
// After last br.FinishStream we checked that there is at least 2 bytes
// in the buffer.
JXL_DASSERT(*pos + 1 < len);
// br.FinishStream would have detected an early marker.
JXL_DASSERT(data[*pos + 1] == 0);
*pos += 2;
} else {
*pos += 1;
}
*bit_pos = 0;
return true;
}
} // namespace
int ProcessScan(j_decompress_ptr cinfo) {
const uint8_t* data = cinfo->src->next_input_byte;
size_t len = cinfo->src->bytes_in_buffer;
if (len == 0) {
return JPEG_SUSPENDED;
void PrepareForiMCURow(j_decompress_ptr cinfo) {
jpeg_decomp_master* m = cinfo->master;
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
const jpeg_component_info* comp = cinfo->cur_comp_info[i];
int c = comp->component_index;
int by0 = cinfo->input_iMCU_row * comp->v_samp_factor;
int block_rows_left = comp->height_in_blocks - by0;
int max_block_rows = std::min(comp->v_samp_factor, block_rows_left);
int offset = m->streaming_mode_ ? 0 : by0;
m->coeff_rows[c] = (*cinfo->mem->access_virt_barray)(
reinterpret_cast<j_common_ptr>(cinfo), m->coef_arrays[c], offset,
max_block_rows, true);
}
}
int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data,
const size_t len, size_t* pos, size_t* bit_pos) {
if (len == 0) {
return kNeedMoreInput;
}
size_t pos = 0;
jpeg_decomp_master* m = cinfo->master;
for (;;) {
// Handle the restart intervals.
if (cinfo->restart_interval > 0 && m->restarts_to_go_ == 0) {
if (m->eobrun_ > 0) {
JPEGLI_ERROR("End-of-block run too long.");
if (!FinishScan(cinfo, data, len, pos, bit_pos)) {
return kNeedMoreInput;
}
if (m->codestream_bits_ahead_ > 0) {
++pos;
AdvanceInput(cinfo, 1);
m->codestream_bits_ahead_ = 0;
// Go to the next marker, warn if we had to skip any data.
size_t num_skipped = 0;
while (*pos + 1 < len && (data[*pos] != 0xff || data[*pos + 1] == 0 ||
data[*pos + 1] == 0xff)) {
++(*pos);
++num_skipped;
}
if (pos + 2 > len) {
return JPEG_SUSPENDED;
if (num_skipped > 0) {
JPEGLI_WARN("Skipped %d bytes before restart marker", (int)num_skipped);
}
int expected_marker = 0xd0 + m->next_restart_marker_;
int marker = data[pos + 1];
if (marker != expected_marker) {
JPEGLI_ERROR("Did not find expected restart marker %d actual %d",
expected_marker, marker);
// TODO(szabadka) Use source manager's resync_to_restart callback here.
if (*pos + 2 > len) {
return kNeedMoreInput;
}
m->next_restart_marker_ += 1;
m->next_restart_marker_ &= 0x7;
m->restarts_to_go_ = cinfo->restart_interval;
memset(m->last_dc_coeff_, 0, sizeof(m->last_dc_coeff_));
m->eobrun_ = -1; // fresh start
pos += 2;
AdvanceInput(cinfo, 2);
cinfo->unread_marker = data[*pos + 1];
*pos += 2;
return kHandleRestart;
}
size_t start_pos = pos;
size_t start_pos = *pos;
BitReaderState br(data, len, start_pos);
if (m->codestream_bits_ahead_ > 0) {
br.ReadBits(m->codestream_bits_ahead_);
if (*bit_pos > 0) {
br.ReadBits(*bit_pos);
}
if (start_pos + kMaxMCUByteSize > len) {
SaveMCUCodingState(cinfo);
}
// Decode one MCU.
HWY_ALIGN_MAX coeff_t dummy_block[DCTSIZE2];
bool scan_ok = true;
for (int i = 0; i < cinfo->comps_in_scan; ++i) {
const jpeg_component_info* comp = cinfo->cur_comp_info[i];
DecJPEGComponent* c = &m->components_[comp->component_index];
int c = comp->component_index;
const HuffmanTableEntry* dc_lut =
&m->dc_huff_lut_[comp->dc_tbl_no * kJpegHuffmanLutSize];
const HuffmanTableEntry* ac_lut =
&m->ac_huff_lut_[comp->ac_tbl_no * kJpegHuffmanLutSize];
for (int iy = 0; iy < comp->MCU_height; ++iy) {
int block_y = m->scan_mcu_row_ * comp->MCU_height + iy;
size_t block_y = m->scan_mcu_row_ * comp->MCU_height + iy;
int biy = block_y % comp->v_samp_factor;
for (int ix = 0; ix < comp->MCU_width; ++ix) {
int block_x = m->scan_mcu_col_ * comp->MCU_width + ix;
int block_idx = block_y * comp->width_in_blocks + block_x;
coeff_t* coeffs = &c->coeffs[block_idx * DCTSIZE2];
size_t block_x = m->scan_mcu_col_ * comp->MCU_width + ix;
coeff_t* coeffs;
if (block_x >= comp->width_in_blocks ||
block_y >= comp->height_in_blocks) {
// Note that it is OK that dummy_block is uninitialized because
// it will never be used in any branches, even in the RefineDCTBlock
// case, because only DC scans can be interleaved and we don't use
// the zero-ness of the DC coeff in the DC refinement code-path.
coeffs = dummy_block;
} else {
coeffs = &m->coeff_rows[c][biy][block_x][0];
}
if (cinfo->Ah == 0) {
if (!DecodeDCTBlock(dc_lut, ac_lut, cinfo->Ss, cinfo->Se, cinfo->Al,
&m->eobrun_, &br,
@ -456,30 +512,31 @@ int ProcessScan(j_decompress_ptr cinfo) {
}
}
}
size_t bit_pos;
size_t stream_pos;
bool stream_ok = br.FinishStream(&stream_pos, &bit_pos);
if (stream_pos + 2 > len) {
size_t new_pos;
size_t new_bit_pos;
bool stream_ok = br.FinishStream(&new_pos, &new_bit_pos);
if (new_pos + 2 > len) {
// If reading stopped within the last two bytes, we have to request more
// input even if FinishStream() returned true, since the Huffman code
// reader could have peaked ahead some bits past the current input chunk
// and thus the last prefix code length could have been wrong. We can do
// this because a valid JPEG bit stream has two extra bytes at the end.
RestoreMCUCodingState(cinfo);
return JPEG_SUSPENDED;
return kNeedMoreInput;
}
*pos = new_pos;
*bit_pos = new_bit_pos;
if (!stream_ok) {
// We hit a marker during parsing.
JXL_DASSERT(data[*pos] == 0xff);
JXL_DASSERT(data[*pos + 1] != 0);
RestoreMCUCodingState(cinfo);
JPEGLI_WARN("Incomplete scan detected.");
return JPEG_SCAN_COMPLETED;
}
if (!scan_ok) {
JPEGLI_ERROR("Failed to decode DCT block");
}
if (!stream_ok) {
// We hit a marker during parsing.
JXL_DASSERT(data[stream_pos] == 0xff);
JXL_DASSERT(data[stream_pos + 1] != 0);
JPEGLI_ERROR("Unexpected end of scan.");
}
m->codestream_bits_ahead_ = bit_pos;
pos = stream_pos;
AdvanceInput(cinfo, pos - start_pos);
if (m->restarts_to_go_ > 0) {
--m->restarts_to_go_;
}
@ -488,14 +545,8 @@ int ProcessScan(j_decompress_ptr cinfo) {
++m->scan_mcu_row_;
m->scan_mcu_col_ = 0;
if (m->scan_mcu_row_ == cinfo->MCU_rows_in_scan) {
// Current scan is done, skip any remaining bits in the last byte.
if (m->codestream_bits_ahead_ > 0) {
++pos;
AdvanceInput(cinfo, 1);
m->codestream_bits_ahead_ = 0;
}
if (m->eobrun_ > 0) {
JPEGLI_ERROR("End-of-block run too long.");
if (!FinishScan(cinfo, data, len, pos, bit_pos)) {
return kNeedMoreInput;
}
break;
} else if ((m->scan_mcu_row_ % m->mcu_rows_per_iMCU_row_) == 0) {
@ -505,8 +556,11 @@ int ProcessScan(j_decompress_ptr cinfo) {
}
}
++cinfo->input_iMCU_row;
return (m->scan_mcu_row_ == cinfo->MCU_rows_in_scan ? JPEG_SCAN_COMPLETED
: JPEG_ROW_COMPLETED);
if (cinfo->input_iMCU_row < cinfo->total_iMCU_rows) {
PrepareForiMCURow(cinfo);
return JPEG_ROW_COMPLETED;
}
return JPEG_SCAN_COMPLETED;
}
} // namespace jpegli

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

@ -23,7 +23,10 @@ namespace jpegli {
// * JPEG_SUSPENDED, if the input buffer ends before the end of an iMCU row;
// * JPEG_ROW_COMPLETED, if the next iMCU row (but not the scan) is reached;
// * JPEG_SCAN_COMPLETED, if the end of the scan is reached.
int ProcessScan(j_decompress_ptr cinfo);
int ProcessScan(j_decompress_ptr cinfo, const uint8_t* const data,
const size_t len, size_t* pos, size_t* bit_pos);
void PrepareForiMCURow(j_decompress_ptr cinfo);
} // namespace jpegli

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

@ -11,8 +11,6 @@
namespace jpegli {
void init_destination(j_compress_ptr cinfo) {}
constexpr size_t kDestBufferSize = 64 << 10;
struct StdioDestinationManager {
@ -20,6 +18,12 @@ struct StdioDestinationManager {
FILE* f;
uint8_t* buffer;
static void init_destination(j_compress_ptr cinfo) {
auto dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = kDestBufferSize;
}
static boolean empty_output_buffer(j_compress_ptr cinfo) {
auto dest = reinterpret_cast<StdioDestinationManager*>(cinfo->dest);
if (fwrite(dest->buffer, 1, kDestBufferSize, dest->f) != kDestBufferSize) {
@ -55,6 +59,8 @@ struct MemoryDestinationManager {
uint8_t* current_buffer;
size_t buffer_size;
static void init_destination(j_compress_ptr cinfo) {}
static boolean empty_output_buffer(j_compress_ptr cinfo) {
auto dest = reinterpret_cast<MemoryDestinationManager*>(cinfo->dest);
uint8_t* next_buffer =
@ -82,32 +88,53 @@ struct MemoryDestinationManager {
} // namespace jpegli
void jpegli_stdio_dest(j_compress_ptr cinfo, FILE* outfile) {
jpegli::StdioDestinationManager* dest =
jpegli::Allocate<jpegli::StdioDestinationManager>(cinfo, 1);
if (outfile == nullptr) {
JPEGLI_ERROR("jpegli_stdio_dest: Invalid destination.");
}
if (cinfo->dest && cinfo->dest->init_destination !=
jpegli::StdioDestinationManager::init_destination) {
JPEGLI_ERROR("jpegli_stdio_dest: a different dest manager was already set");
}
if (!cinfo->dest) {
cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(
jpegli::Allocate<jpegli::StdioDestinationManager>(cinfo, 1));
}
auto dest = reinterpret_cast<jpegli::StdioDestinationManager*>(cinfo->dest);
dest->f = outfile;
dest->buffer = jpegli::Allocate<uint8_t>(cinfo, jpegli::kDestBufferSize);
dest->pub.next_output_byte = dest->buffer;
dest->pub.free_in_buffer = jpegli::kDestBufferSize;
dest->pub.init_destination = jpegli::init_destination;
dest->pub.init_destination =
jpegli::StdioDestinationManager::init_destination;
dest->pub.empty_output_buffer =
jpegli::StdioDestinationManager::empty_output_buffer;
dest->pub.term_destination =
jpegli::StdioDestinationManager::term_destination;
cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(dest);
}
void jpegli_mem_dest(j_compress_ptr cinfo, unsigned char** outbuffer,
unsigned long* outsize) {
jpegli::MemoryDestinationManager* dest =
jpegli::Allocate<jpegli::MemoryDestinationManager>(cinfo, 1);
dest->pub.init_destination = jpegli::init_destination;
if (outbuffer == nullptr || outsize == nullptr) {
JPEGLI_ERROR("jpegli_mem_dest: Invalid destination.");
}
if (cinfo->dest && cinfo->dest->init_destination !=
jpegli::MemoryDestinationManager::init_destination) {
JPEGLI_ERROR("jpegli_mem_dest: a different dest manager was already set");
}
if (!cinfo->dest) {
auto dest = jpegli::Allocate<jpegli::MemoryDestinationManager>(cinfo, 1);
dest->temp_buffer = nullptr;
cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(dest);
}
auto dest = reinterpret_cast<jpegli::MemoryDestinationManager*>(cinfo->dest);
dest->pub.init_destination =
jpegli::MemoryDestinationManager::init_destination;
dest->pub.empty_output_buffer =
jpegli::MemoryDestinationManager::empty_output_buffer;
dest->pub.term_destination =
jpegli::MemoryDestinationManager::term_destination;
dest->output = outbuffer;
dest->output_size = outsize;
dest->temp_buffer = nullptr;
if (*outbuffer == nullptr || *outsize == 0) {
dest->temp_buffer =
reinterpret_cast<uint8_t*>(malloc(jpegli::kDestBufferSize));
@ -118,5 +145,4 @@ void jpegli_mem_dest(j_compress_ptr cinfo, unsigned char** outbuffer,
dest->buffer_size = *outsize;
dest->pub.next_output_byte = dest->current_buffer;
dest->pub.free_in_buffer = dest->buffer_size;
cinfo->dest = reinterpret_cast<jpeg_destination_mgr*>(dest);
}

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

@ -0,0 +1,356 @@
// 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/jpegli/downsample.h"
#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "lib/jpegli/downsample.cc"
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
#include "lib/jpegli/encode_internal.h"
#include "lib/jpegli/error.h"
HWY_BEFORE_NAMESPACE();
namespace jpegli {
namespace HWY_NAMESPACE {
// These templates are not found via ADL.
using hwy::HWY_NAMESPACE::Add;
using hwy::HWY_NAMESPACE::Mul;
using hwy::HWY_NAMESPACE::Vec;
using D = HWY_CAPPED(float, 8);
constexpr D d;
void DownsampleRow2x1(const float* row_in, size_t len, float* row_out) {
const size_t N = Lanes(d);
const size_t len_out = len / 2;
const auto mul = Set(d, 0.5f);
Vec<D> v0, v1;
for (size_t x = 0; x < len_out; x += N) {
LoadInterleaved2(d, row_in + 2 * x, v0, v1);
Store(Mul(mul, Add(v0, v1)), d, row_out + x);
}
}
void DownsampleRow3x1(const float* row_in, size_t len, float* row_out) {
const size_t N = Lanes(d);
const size_t len_out = len / 3;
const auto mul = Set(d, 1.0f / 3);
Vec<D> v0, v1, v2;
for (size_t x = 0; x < len_out; x += N) {
LoadInterleaved3(d, row_in + 3 * x, v0, v1, v2);
Store(Mul(mul, Add(Add(v0, v1), v2)), d, row_out + x);
}
}
void DownsampleRow4x1(const float* row_in, size_t len, float* row_out) {
const size_t N = Lanes(d);
const size_t len_out = len / 4;
const auto mul = Set(d, 0.25f);
Vec<D> v0, v1, v2, v3;
for (size_t x = 0; x < len_out; x += N) {
LoadInterleaved4(d, row_in + 4 * x, v0, v1, v2, v3);
Store(Mul(mul, Add(Add(v0, v1), Add(v2, v3))), d, row_out + x);
}
}
void Downsample2x1(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow2x1(rows_in[0], len, row_out);
}
void Downsample3x1(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow3x1(rows_in[0], len, row_out);
}
void Downsample4x1(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow4x1(rows_in[0], len, row_out);
}
void Downsample1x2(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
const size_t N = Lanes(d);
const auto mul = Set(d, 0.5f);
float* row0 = rows_in[0];
float* row1 = rows_in[1];
for (size_t x = 0; x < len; x += N) {
Store(Mul(mul, Add(Load(d, row0 + x), Load(d, row1 + x))), d, row_out + x);
}
}
void Downsample2x2(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
const size_t N = Lanes(d);
const size_t len_out = len / 2;
const auto mul = Set(d, 0.25f);
float* row0 = rows_in[0];
float* row1 = rows_in[1];
Vec<D> v0, v1, v2, v3;
for (size_t x = 0; x < len_out; x += N) {
LoadInterleaved2(d, row0 + 2 * x, v0, v1);
LoadInterleaved2(d, row1 + 2 * x, v2, v3);
Store(Mul(mul, Add(Add(v0, v1), Add(v2, v3))), d, row_out + x);
}
}
void Downsample3x2(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow3x1(rows_in[0], len, rows_in[0]);
DownsampleRow3x1(rows_in[1], len, rows_in[1]);
Downsample1x2(rows_in, len / 3, row_out);
}
void Downsample4x2(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow4x1(rows_in[0], len, rows_in[0]);
DownsampleRow4x1(rows_in[1], len, rows_in[1]);
Downsample1x2(rows_in, len / 4, row_out);
}
void Downsample1x3(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
const size_t N = Lanes(d);
const auto mul = Set(d, 1.0f / 3);
float* row0 = rows_in[0];
float* row1 = rows_in[1];
float* row2 = rows_in[2];
for (size_t x = 0; x < len; x += N) {
const auto in0 = Load(d, row0 + x);
const auto in1 = Load(d, row1 + x);
const auto in2 = Load(d, row2 + x);
Store(Mul(mul, Add(Add(in0, in1), in2)), d, row_out + x);
}
}
void Downsample2x3(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow2x1(rows_in[0], len, rows_in[0]);
DownsampleRow2x1(rows_in[1], len, rows_in[1]);
DownsampleRow2x1(rows_in[2], len, rows_in[2]);
Downsample1x3(rows_in, len / 2, row_out);
}
void Downsample3x3(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow3x1(rows_in[0], len, rows_in[0]);
DownsampleRow3x1(rows_in[1], len, rows_in[1]);
DownsampleRow3x1(rows_in[2], len, rows_in[2]);
Downsample1x3(rows_in, len / 3, row_out);
}
void Downsample4x3(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow4x1(rows_in[0], len, rows_in[0]);
DownsampleRow4x1(rows_in[1], len, rows_in[1]);
DownsampleRow4x1(rows_in[2], len, rows_in[2]);
Downsample1x3(rows_in, len / 4, row_out);
}
void Downsample1x4(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
const size_t N = Lanes(d);
const auto mul = Set(d, 0.25f);
float* row0 = rows_in[0];
float* row1 = rows_in[1];
float* row2 = rows_in[2];
float* row3 = rows_in[3];
for (size_t x = 0; x < len; x += N) {
const auto in0 = Load(d, row0 + x);
const auto in1 = Load(d, row1 + x);
const auto in2 = Load(d, row2 + x);
const auto in3 = Load(d, row3 + x);
Store(Mul(mul, Add(Add(in0, in1), Add(in2, in3))), d, row_out + x);
}
}
void Downsample2x4(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow2x1(rows_in[0], len, rows_in[0]);
DownsampleRow2x1(rows_in[1], len, rows_in[1]);
DownsampleRow2x1(rows_in[2], len, rows_in[2]);
DownsampleRow2x1(rows_in[3], len, rows_in[3]);
Downsample1x4(rows_in, len / 2, row_out);
}
void Downsample3x4(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow3x1(rows_in[0], len, rows_in[0]);
DownsampleRow3x1(rows_in[1], len, rows_in[1]);
DownsampleRow3x1(rows_in[2], len, rows_in[2]);
DownsampleRow3x1(rows_in[3], len, rows_in[3]);
Downsample1x4(rows_in, len / 3, row_out);
}
void Downsample4x4(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {
DownsampleRow4x1(rows_in[0], len, rows_in[0]);
DownsampleRow4x1(rows_in[1], len, rows_in[1]);
DownsampleRow4x1(rows_in[2], len, rows_in[2]);
DownsampleRow4x1(rows_in[3], len, rows_in[3]);
Downsample1x4(rows_in, len / 4, row_out);
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
} // namespace HWY_NAMESPACE
} // namespace jpegli
HWY_AFTER_NAMESPACE();
#if HWY_ONCE
namespace jpegli {
HWY_EXPORT(Downsample1x2);
HWY_EXPORT(Downsample1x3);
HWY_EXPORT(Downsample1x4);
HWY_EXPORT(Downsample2x1);
HWY_EXPORT(Downsample2x2);
HWY_EXPORT(Downsample2x3);
HWY_EXPORT(Downsample2x4);
HWY_EXPORT(Downsample3x1);
HWY_EXPORT(Downsample3x2);
HWY_EXPORT(Downsample3x3);
HWY_EXPORT(Downsample3x4);
HWY_EXPORT(Downsample4x1);
HWY_EXPORT(Downsample4x2);
HWY_EXPORT(Downsample4x3);
HWY_EXPORT(Downsample4x4);
void NullDownsample(float* rows_in[MAX_SAMP_FACTOR], size_t len,
float* row_out) {}
void ChooseDownsampleMethods(j_compress_ptr cinfo) {
jpeg_comp_master* m = cinfo->master;
for (int c = 0; c < cinfo->num_components; c++) {
m->downsample_method[c] = nullptr;
jpeg_component_info* comp = &cinfo->comp_info[c];
const int h_factor = cinfo->max_h_samp_factor / comp->h_samp_factor;
const int v_factor = cinfo->max_v_samp_factor / comp->v_samp_factor;
if (v_factor == 1) {
if (h_factor == 1) {
m->downsample_method[c] = NullDownsample;
} else if (h_factor == 2) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample2x1);
} else if (h_factor == 3) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample3x1);
} else if (h_factor == 4) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample4x1);
}
} else if (v_factor == 2) {
if (h_factor == 1) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample1x2);
} else if (h_factor == 2) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample2x2);
} else if (h_factor == 3) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample3x2);
} else if (h_factor == 4) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample4x2);
}
} else if (v_factor == 3) {
if (h_factor == 1) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample1x2);
} else if (h_factor == 2) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample2x2);
} else if (h_factor == 3) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample3x2);
} else if (h_factor == 4) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample4x2);
}
} else if (v_factor == 4) {
if (h_factor == 1) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample1x4);
} else if (h_factor == 2) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample2x4);
} else if (h_factor == 3) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample3x4);
} else if (h_factor == 4) {
m->downsample_method[c] = HWY_DYNAMIC_DISPATCH(Downsample4x4);
}
}
if (m->downsample_method[c] == nullptr) {
JPEGLI_ERROR("Unsupported downsampling ratio %dx%d", h_factor, v_factor);
}
}
}
void DownsampleInputBuffer(j_compress_ptr cinfo) {
if (cinfo->max_h_samp_factor == 1 && cinfo->max_v_samp_factor == 1) {
return;
}
jpeg_comp_master* m = cinfo->master;
const size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor;
const size_t y0 = m->next_iMCU_row * iMCU_height;
const size_t y1 = y0 + iMCU_height;
const size_t xsize_padded = m->xsize_blocks * DCTSIZE;
for (int c = 0; c < cinfo->num_components; c++) {
jpeg_component_info* comp = &cinfo->comp_info[c];
const int h_factor = cinfo->max_h_samp_factor / comp->h_samp_factor;
const int v_factor = cinfo->max_v_samp_factor / comp->v_samp_factor;
if (h_factor == 1 && v_factor == 1) {
continue;
}
auto& input = *m->smooth_input[c];
auto& output = *m->raw_data[c];
const size_t yout0 = y0 / v_factor;
float* rows_in[MAX_SAMP_FACTOR];
for (size_t yin = y0, yout = yout0; yin < y1; yin += v_factor, ++yout) {
for (int iy = 0; iy < v_factor; ++iy) {
rows_in[iy] = input.Row(yin + iy);
}
float* row_out = output.Row(yout);
(*m->downsample_method[c])(rows_in, xsize_padded, row_out);
}
}
}
void ApplyInputSmoothing(j_compress_ptr cinfo) {
if (!cinfo->smoothing_factor) {
return;
}
jpeg_comp_master* m = cinfo->master;
const float kW1 = cinfo->smoothing_factor / 1024.0;
const float kW0 = 1.0f - 8.0f * kW1;
const size_t iMCU_height = DCTSIZE * cinfo->max_v_samp_factor;
const ssize_t y0 = m->next_iMCU_row * iMCU_height;
const ssize_t y1 = y0 + iMCU_height;
const ssize_t xsize_padded = m->xsize_blocks * DCTSIZE;
for (int c = 0; c < cinfo->num_components; c++) {
auto& input = m->input_buffer[c];
auto& output = *m->smooth_input[c];
if (m->next_iMCU_row == 0) {
input.CopyRow(-1, 0, 1);
}
if (m->next_iMCU_row + 1 == cinfo->total_iMCU_rows) {
size_t last_row = m->ysize_blocks * DCTSIZE - 1;
input.CopyRow(last_row + 1, last_row, 1);
}
// TODO(szabadka) SIMDify this.
for (ssize_t y = y0; y < y1; ++y) {
const float* row_t = input.Row(y - 1);
const float* row_m = input.Row(y);
const float* row_b = input.Row(y + 1);
float* row_out = output.Row(y);
for (ssize_t x = 0; x < xsize_padded; ++x) {
float val_tl = row_t[x - 1];
float val_tm = row_t[x];
float val_tr = row_t[x + 1];
float val_ml = row_m[x - 1];
float val_mm = row_m[x];
float val_mr = row_m[x + 1];
float val_bl = row_b[x - 1];
float val_bm = row_b[x];
float val_br = row_b[x + 1];
float val1 = (val_tl + val_tm + val_tr + val_ml + val_mr + val_bl +
val_bm + val_br);
row_out[x] = val_mm * kW0 + val1 * kW1;
}
}
}
}
} // namespace jpegli
#endif // HWY_ONCE

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

@ -0,0 +1,24 @@
// 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_JPEGLI_DOWNSAMPLE_H_
#define LIB_JPEGLI_DOWNSAMPLE_H_
/* clang-format off */
#include <stdio.h>
#include <jpeglib.h>
/* clang-format on */
namespace jpegli {
void ChooseDownsampleMethods(j_compress_ptr cinfo);
void DownsampleInputBuffer(j_compress_ptr cinfo);
void ApplyInputSmoothing(j_compress_ptr cinfo);
} // namespace jpegli
#endif // LIB_JPEGLI_DOWNSAMPLE_H_

833
third_party/jpeg-xl/lib/jpegli/encode.cc поставляемый

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

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

@ -54,6 +54,10 @@ void jpegli_set_quality(j_compress_ptr cinfo, int quality,
void jpegli_set_linear_quality(j_compress_ptr cinfo, int scale_factor,
boolean force_baseline);
#if JPEG_LIB_VERSION >= 70
void jpegli_default_qtables(j_compress_ptr cinfo, boolean force_baseline);
#endif
int jpegli_quality_scaling(int quality);
void jpegli_add_quant_table(j_compress_ptr cinfo, int which_tbl,
@ -64,6 +68,10 @@ void jpegli_simple_progression(j_compress_ptr cinfo);
void jpegli_suppress_tables(j_compress_ptr cinfo, boolean suppress);
#if JPEG_LIB_VERSION >= 70
void jpegli_calc_jpeg_dimensions(j_compress_ptr cinfo);
#endif
void jpegli_copy_critical_parameters(j_decompress_ptr srcinfo,
j_compress_ptr dstinfo);
@ -104,8 +112,12 @@ void jpegli_destroy_compress(j_compress_ptr cinfo);
// the future.
//
// Sets the butteraugli target distance for the compressor.
void jpegli_set_distance(j_compress_ptr cinfo, float distance);
// Sets the butteraugli target distance for the compressor. This may override
// the default quantization table indexes based on jpeg colorspace, therefore
// it must be called after jpegli_set_defaults() or after the last
// jpegli_set_colorspace() or jpegli_default_colorspace() calls.
void jpegli_set_distance(j_compress_ptr cinfo, float distance,
boolean force_baseline);
// Returns the butteraugli target distance for the given quality parameter.
float jpegli_quality_to_distance(int quality);
@ -115,6 +127,12 @@ float jpegli_quality_to_distance(int quality);
// because some default setting depend on the XYB mode.
void jpegli_set_xyb_mode(j_compress_ptr cinfo);
// Signals to the encoder that the pixel data that will be provided later
// through jpegli_write_scanlines() has this transfer function. This must be
// called before jpegli_set_defaults() because it changes the default
// quantization tables.
void jpegli_set_cicp_transfer_function(j_compress_ptr cinfo, int code);
void jpegli_set_input_format(j_compress_ptr cinfo, JpegliDataType data_type,
JpegliEndianness endianness);
@ -131,7 +149,7 @@ void jpegli_set_progressive_level(j_compress_ptr cinfo, int level);
// linear quality parameters will be used to scale the standard quantization
// tables from Annex K of the JPEG standard. By default jpegli uses a different
// set of quantization tables and used different scaling parameters for DC and
// AC coefficients.
// AC coefficients. Must be called before jpegli_set_defaults().
void jpegli_use_standard_quant_tables(j_compress_ptr cinfo);
#if defined(__cplusplus) || defined(c_plusplus)

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

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

@ -12,9 +12,7 @@
#include <jpeglib.h>
/* clang-format on */
#include <array>
#include <vector>
#include "lib/jpegli/bit_writer.h"
#include "lib/jpegli/common_internal.h"
#include "lib/jpegli/encode.h"
@ -26,20 +24,19 @@ constexpr int kICCMarker = JPEG_APP0 + 2;
struct JPEGHuffmanCode {
// Bit length histogram.
std::array<uint32_t, kJpegHuffmanMaxBitLength + 1> counts = {};
uint32_t counts[kJpegHuffmanMaxBitLength + 1];
// Symbol values sorted by increasing bit lengths.
std::array<uint32_t, kJpegHuffmanAlphabetSize + 1> values = {};
uint32_t values[kJpegHuffmanAlphabetSize + 1];
// The index of the Huffman code in the current set of Huffman codes. For AC
// component Huffman codes, 0x10 is added to the index.
int slot_id = 0;
boolean sent_table = FALSE;
int slot_id;
boolean sent_table;
};
// DCTCodingState: maximum number of correction bits to buffer
const int kJPEGMaxCorrectionBits = 1u << 16;
constexpr int kDefaultProgressiveLevel = 2;
constexpr float kDefaultQuantFieldMax = 0.575f;
constexpr int kDefaultProgressiveLevel = 0;
struct HuffmanCodeTable {
int depth[256];
@ -50,7 +47,7 @@ struct ScanCodingInfo {
uint32_t dc_tbl_idx[MAX_COMPS_IN_SCAN];
uint32_t ac_tbl_idx[MAX_COMPS_IN_SCAN];
// Number of Huffman codes defined in the DHT segment preceding this scan.
size_t num_huffman_codes = 0;
size_t num_huffman_codes;
};
typedef int16_t coeff_t;
@ -58,26 +55,47 @@ typedef int16_t coeff_t;
} // namespace jpegli
struct jpeg_comp_master {
std::array<jpegli::RowBuffer<float>, jpegli::kMaxComponents> input_buffer;
float distance = 1.0;
bool force_baseline = true;
bool xyb_mode = false;
bool use_std_tables = false;
bool use_adaptive_quantization = true;
int progressive_level = jpegli::kDefaultProgressiveLevel;
size_t xsize_blocks = 0;
size_t ysize_blocks = 0;
std::vector<jpegli::ScanCodingInfo> scan_coding_info;
std::vector<std::vector<uint8_t>> special_markers;
uint8_t* next_marker_byte = nullptr;
JpegliDataType data_type = JPEGLI_TYPE_UINT8;
JpegliEndianness endianness = JPEGLI_NATIVE_ENDIAN;
jpegli::RowBuffer<float> input_buffer[jpegli::kMaxComponents];
jpegli::RowBuffer<float>* smooth_input[jpegli::kMaxComponents];
jpegli::RowBuffer<float>* raw_data[jpegli::kMaxComponents];
bool force_baseline;
bool xyb_mode;
uint8_t cicp_transfer_function;
bool use_std_tables;
bool use_adaptive_quantization;
int progressive_level;
size_t xsize_blocks;
size_t ysize_blocks;
size_t blocks_per_iMCU_row;
jpegli::ScanCodingInfo* scan_coding_info;
JpegliDataType data_type;
JpegliEndianness endianness;
void (*input_method)(const uint8_t* row_in, size_t len,
float* row_out[jpegli::kMaxComponents]);
void (*color_transform)(float* row[jpegli::kMaxComponents], size_t len);
void (*downsample_method[jpegli::kMaxComponents])(
float* rows_in[MAX_SAMP_FACTOR], size_t len, float* row_out);
float* quant_mul[jpegli::kMaxComponents];
float* zero_bias_offset[jpegli::kMaxComponents];
float* zero_bias_mul[jpegli::kMaxComponents];
int h_factor[jpegli::kMaxComponents];
int v_factor[jpegli::kMaxComponents];
jpegli::JPEGHuffmanCode* huffman_codes;
size_t num_huffman_codes;
jpegli::HuffmanCodeTable huff_tables[8];
std::array<jpegli::HuffmanCodeTable, jpegli::kMaxHuffmanTables> dc_huff_table;
std::array<jpegli::HuffmanCodeTable, jpegli::kMaxHuffmanTables> ac_huff_table;
float* diff_buffer;
jpegli::RowBuffer<float> fuzzy_erosion_tmp;
jpegli::RowBuffer<float> pre_erosion;
jpegli::RowBuffer<float> quant_field;
float quant_field_max = jpegli::kDefaultQuantFieldMax;
jvirt_barray_ptr* coeff_buffers = nullptr;
jvirt_barray_ptr* coeff_buffers;
size_t next_input_row;
size_t next_iMCU_row;
size_t last_dht_index;
size_t last_restart_interval;
JCOEF last_dc_coeff[MAX_COMPS_IN_SCAN];
jpegli::JpegBitWriter bw;
float* dct_buffer;
int32_t* block_tmp;
};
#endif // LIB_JPEGLI_ENCODE_INTERNAL_H_

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

@ -7,21 +7,21 @@
#include "lib/jpegli/encode_internal.h"
#include "lib/jpegli/error.h"
#include "lib/jxl/enc_cluster.h"
#include "lib/jxl/enc_huffman_tree.h"
#include "lib/jpegli/huffman.h"
#include "lib/jxl/base/bits.h"
namespace jpegli {
namespace {
float HistogramCost(const jxl::Histogram& histo) {
float HistogramCost(const Histogram& histo) {
std::vector<uint32_t> counts(kJpegHuffmanAlphabetSize + 1);
std::vector<uint8_t> depths(kJpegHuffmanAlphabetSize + 1);
for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) {
counts[i] = histo.data_[i];
counts[i] = histo.count[i];
}
counts[kJpegHuffmanAlphabetSize] = 1;
jxl::CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength,
&depths[0]);
CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength,
&depths[0]);
size_t header_bits = (1 + kJpegHuffmanMaxBitLength) * 8;
size_t data_bits = 0;
for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) {
@ -32,36 +32,39 @@ float HistogramCost(const jxl::Histogram& histo) {
}
return header_bits + data_bits;
}
void AddHistograms(const Histogram& a, const Histogram& b, Histogram* c) {
for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) {
c->count[i] = a.count[i] + b.count[i];
}
}
bool IsEmptyHistogram(const Histogram& histo) {
for (size_t i = 0; i < kJpegHuffmanAlphabetSize; ++i) {
if (histo.count[i]) return false;
}
return true;
}
} // namespace
void ClusterJpegHistograms(const Histogram* histo_data, size_t num,
void ClusterJpegHistograms(const Histogram* histograms, size_t num,
JpegClusteredHistograms* clusters) {
std::vector<jxl::Histogram> histograms;
for (size_t idx = 0; idx < num; ++idx) {
jxl::Histogram histo;
histo.data_.resize(kJpegHuffmanAlphabetSize);
for (size_t i = 0; i < histo.data_.size(); ++i) {
histo.data_[i] = histo_data[idx].count[i];
histo.total_count_ += histo.data_[i];
}
histograms.push_back(histo);
}
clusters->histogram_indexes.resize(histograms.size());
clusters->histogram_indexes.resize(num);
std::vector<uint32_t> slot_histograms;
std::vector<float> slot_costs;
for (size_t i = 0; i < histograms.size(); ++i) {
const jxl::Histogram& cur = histograms[i];
if (cur.total_count_ == 0) {
for (size_t i = 0; i < num; ++i) {
const Histogram& cur = histograms[i];
if (IsEmptyHistogram(cur)) {
continue;
}
float best_cost = HistogramCost(cur);
size_t best_slot = slot_histograms.size();
for (size_t j = 0; j < slot_histograms.size(); ++j) {
size_t prev_idx = slot_histograms[j];
const jxl::Histogram& prev = clusters->histograms[prev_idx];
jxl::Histogram combined;
combined.AddHistogram(prev);
combined.AddHistogram(cur);
const Histogram& prev = clusters->histograms[prev_idx];
Histogram combined;
AddHistograms(prev, cur, &combined);
float combined_cost = HistogramCost(combined);
float cost = combined_cost - slot_costs[j];
if (cost < best_cost) {
@ -88,7 +91,8 @@ void ClusterJpegHistograms(const Histogram* histo_data, size_t num,
} else {
// Merge this histogram with a previous one.
size_t histogram_index = slot_histograms[best_slot];
clusters->histograms[histogram_index].AddHistogram(cur);
const Histogram& prev = clusters->histograms[histogram_index];
AddHistograms(prev, cur, &clusters->histograms[histogram_index]);
clusters->histogram_indexes[i] = histogram_index;
JXL_ASSERT(clusters->slot_ids[histogram_index] == best_slot);
slot_costs[best_slot] += best_cost;
@ -96,15 +100,15 @@ void ClusterJpegHistograms(const Histogram* histo_data, size_t num,
}
}
void BuildJpegHuffmanCode(const jxl::Histogram& histo, JPEGHuffmanCode* huff) {
void BuildJpegHuffmanCode(const Histogram& histo, JPEGHuffmanCode* huff) {
std::vector<uint32_t> counts(kJpegHuffmanAlphabetSize + 1);
std::vector<uint8_t> depths(kJpegHuffmanAlphabetSize + 1);
for (size_t j = 0; j < kJpegHuffmanAlphabetSize; ++j) {
counts[j] = histo.data_[j];
counts[j] = histo.count[j];
}
counts[kJpegHuffmanAlphabetSize] = 1;
jxl::CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength,
&depths[0]);
CreateHuffmanTree(counts.data(), counts.size(), kJpegHuffmanMaxBitLength,
&depths[0]);
std::fill(std::begin(huff->counts), std::end(huff->counts), 0);
std::fill(std::begin(huff->values), std::end(huff->values), 0);
for (size_t i = 0; i <= kJpegHuffmanAlphabetSize; ++i) {
@ -123,27 +127,31 @@ void BuildJpegHuffmanCode(const jxl::Histogram& histo, JPEGHuffmanCode* huff) {
}
}
void AddJpegHuffmanCode(const jxl::Histogram& histogram, size_t slot_id,
std::vector<JPEGHuffmanCode>* huff_codes) {
JPEGHuffmanCode huff_code;
void AddJpegHuffmanCode(const Histogram& histogram, size_t slot_id,
JPEGHuffmanCode* huff_codes, size_t* num_huff_codes) {
JPEGHuffmanCode huff_code = {};
huff_code.slot_id = slot_id;
BuildJpegHuffmanCode(histogram, &huff_code);
huff_codes->emplace_back(std::move(huff_code));
memcpy(&huff_codes[*num_huff_codes], &huff_code, sizeof(huff_code));
++(*num_huff_codes);
}
namespace {
void SetJpegHuffmanCode(const JpegClusteredHistograms& clusters,
size_t histogram_id, size_t slot_id_offset,
std::vector<uint32_t>& slot_histograms,
uint32_t* slot_id,
std::vector<JPEGHuffmanCode>* huff_codes) {
uint32_t* slot_id, bool* is_baseline,
JPEGHuffmanCode* huff_codes, size_t* num_huff_codes) {
JXL_ASSERT(histogram_id < clusters.histogram_indexes.size());
uint32_t histogram_index = clusters.histogram_indexes[histogram_id];
uint32_t id = clusters.slot_ids[histogram_index];
if (id > 1) {
*is_baseline = false;
}
*slot_id = id + (slot_id_offset / 4);
if (slot_histograms[id] != histogram_index) {
AddJpegHuffmanCode(clusters.histograms[histogram_index],
slot_id_offset + id, huff_codes);
slot_id_offset + id, huff_codes, num_huff_codes);
slot_histograms[id] = histogram_index;
}
}
@ -178,8 +186,7 @@ static JXL_INLINE void ProcessEndOfBand(DCTState* s, size_t new_refinement_bits,
}
bool ProcessDCTBlockSequential(const coeff_t* coeffs, Histogram* dc_histo,
Histogram* ac_histo, int num_zero_runs,
coeff_t* last_dc_coeff) {
Histogram* ac_histo, coeff_t* last_dc_coeff) {
coeff_t temp2;
coeff_t temp;
temp2 = coeffs[0];
@ -217,10 +224,6 @@ bool ProcessDCTBlockSequential(const coeff_t* coeffs, Histogram* dc_histo,
++ac_histo->count[symbol];
r = 0;
}
for (int i = 0; i < num_zero_runs; ++i) {
++ac_histo->count[0xf0];
r -= 16;
}
if (r > 0) {
++ac_histo->count[0];
}
@ -229,8 +232,7 @@ bool ProcessDCTBlockSequential(const coeff_t* coeffs, Histogram* dc_histo,
bool ProcessDCTBlockProgressive(const coeff_t* coeffs, Histogram* dc_histo,
Histogram* ac_histo, int Ss, int Se, int Al,
int num_zero_runs, DCTState* s,
coeff_t* last_dc_coeff) {
DCTState* s, coeff_t* last_dc_coeff) {
bool eob_run_allowed = Ss > 0;
coeff_t temp2;
coeff_t temp;
@ -280,13 +282,6 @@ bool ProcessDCTBlockProgressive(const coeff_t* coeffs, Histogram* dc_histo,
++ac_histo->count[symbol];
r = 0;
}
if (num_zero_runs > 0) {
ProcessFlush(s);
for (int i = 0; i < num_zero_runs; ++i) {
++ac_histo->count[0xf0];
r -= 16;
}
}
if (r > 0) {
ProcessEndOfBand(s, 0, ac_histo);
if (!eob_run_allowed) {
@ -346,10 +341,21 @@ bool ProcessRefinementBits(const coeff_t* coeffs, Histogram* ac_histo, int Ss,
return true;
}
void ProgressMonitorHistogramPass(j_compress_ptr cinfo, size_t scan_index,
size_t mcu_y) {
if (cinfo->progress == nullptr) {
return;
}
cinfo->progress->completed_passes = 1 + scan_index;
cinfo->progress->pass_counter = mcu_y;
cinfo->progress->pass_limit = cinfo->total_iMCU_rows;
(*cinfo->progress->progress_monitor)(reinterpret_cast<j_common_ptr>(cinfo));
}
bool ProcessScan(j_compress_ptr cinfo,
const std::vector<std::vector<jpegli::coeff_t> >& coeffs,
size_t scan_index, int* histo_index, Histogram* dc_histograms,
Histogram* ac_histograms) {
jpeg_comp_master* m = cinfo->master;
size_t restart_interval = RestartIntervalForScan(cinfo, scan_index);
int restarts_to_go = restart_interval;
coeff_t last_dc_coeff[MAX_COMPS_IN_SCAN] = {0};
@ -379,7 +385,20 @@ bool ProcessScan(j_compress_ptr cinfo,
const int Se = scan_info->Se;
constexpr coeff_t kDummyBlock[DCTSIZE2] = {0};
JBLOCKARRAY ba[MAX_COMPS_IN_SCAN];
for (int mcu_y = 0; mcu_y < MCU_rows; ++mcu_y) {
ProgressMonitorHistogramPass(cinfo, scan_index, mcu_y);
for (int i = 0; i < scan_info->comps_in_scan; ++i) {
int comp_idx = scan_info->component_index[i];
jpeg_component_info* comp = &cinfo->comp_info[comp_idx];
int n_blocks_y = is_interleaved ? comp->v_samp_factor : 1;
int by0 = mcu_y * n_blocks_y;
int block_rows_left = comp->height_in_blocks - by0;
int max_block_rows = std::min(n_blocks_y, block_rows_left);
ba[i] = (*cinfo->mem->access_virt_barray)(
reinterpret_cast<j_common_ptr>(cinfo), m->coeff_buffers[comp_idx],
by0, max_block_rows, false);
}
for (int mcu_x = 0; mcu_x < MCUs_per_row; ++mcu_x) {
// Possibly emit a restart marker.
if (restart_interval > 0 && restarts_to_go == 0) {
@ -400,21 +419,20 @@ bool ProcessScan(j_compress_ptr cinfo,
for (int ix = 0; ix < n_blocks_x; ++ix) {
size_t block_y = mcu_y * n_blocks_y + iy;
size_t block_x = mcu_x * n_blocks_x + ix;
size_t block_idx = block_y * comp->width_in_blocks + block_x;
size_t num_zero_runs = 0;
const coeff_t* block = &coeffs[comp_idx][block_idx << 6];
const coeff_t* block;
if (block_x >= comp->width_in_blocks ||
block_y >= comp->height_in_blocks) {
block = kDummyBlock;
} else {
block = &ba[i][iy][block_x][0];
}
bool ok;
if (!is_progressive) {
ok = ProcessDCTBlockSequential(block, dc_histo, ac_histo,
num_zero_runs, last_dc_coeff + i);
last_dc_coeff + i);
} else if (Ah == 0) {
ok = ProcessDCTBlockProgressive(block, dc_histo, ac_histo, Ss, Se,
Al, num_zero_runs, &s,
last_dc_coeff + i);
Al, &s, last_dc_coeff + i);
} else {
ok = ProcessRefinementBits(block, ac_histo, Ss, Se, Al, &s);
}
@ -431,53 +449,20 @@ bool ProcessScan(j_compress_ptr cinfo,
}
void ProcessJpeg(j_compress_ptr cinfo,
const std::vector<std::vector<jpegli::coeff_t> >& coeffs,
std::vector<Histogram>* dc_histograms,
std::vector<Histogram>* ac_histograms) {
int histo_index = 0;
for (int i = 0; i < cinfo->num_scans; ++i) {
if (!ProcessScan(cinfo, coeffs, i, &histo_index, &(*dc_histograms)[0],
if (!ProcessScan(cinfo, i, &histo_index, &(*dc_histograms)[0],
&(*ac_histograms)[0])) {
JPEGLI_ERROR("Invalid scan.");
}
}
}
void ValidateHuffmanTable(j_compress_ptr cinfo, const JHUFF_TBL* table,
bool is_dc) {
size_t total_symbols = 0;
size_t total_p = 0;
size_t max_depth = 0;
for (size_t d = 1; d <= kJpegHuffmanMaxBitLength; ++d) {
uint8_t count = table->bits[d];
if (count) {
total_symbols += count;
total_p += (1u << (kJpegHuffmanMaxBitLength - d)) * count;
max_depth = d;
}
}
total_p += 1u << (kJpegHuffmanMaxBitLength - max_depth); // sentinel symbol
if (total_symbols == 0) {
JPEGLI_ERROR("Empty Huffman table");
}
if (total_symbols > kJpegHuffmanAlphabetSize) {
JPEGLI_ERROR("Too many symbols in Huffman table");
}
if (total_p != (1u << kJpegHuffmanMaxBitLength)) {
JPEGLI_ERROR("Invalid bit length distribution");
}
uint8_t symbol_seen[kJpegHuffmanAlphabetSize] = {};
for (size_t i = 0; i < total_symbols; ++i) {
uint8_t symbol = table->huffval[i];
if (symbol_seen[symbol]) {
JPEGLI_ERROR("Duplicate symbol %d in Huffman table", symbol);
}
symbol_seen[symbol] = 1;
}
}
void CopyHuffmanTable(j_compress_ptr cinfo, int index, bool is_dc,
std::vector<JPEGHuffmanCode>* huffman_codes) {
JPEGHuffmanCode* huffman_codes,
size_t* num_huffman_codes) {
const char* type = is_dc ? "DC" : "AC";
if (index < 0 || index >= NUM_HUFF_TBLS) {
JPEGLI_ERROR("Invalid %s Huffman table index %d", type, index);
@ -485,10 +470,10 @@ void CopyHuffmanTable(j_compress_ptr cinfo, int index, bool is_dc,
JHUFF_TBL* table =
is_dc ? cinfo->dc_huff_tbl_ptrs[index] : cinfo->ac_huff_tbl_ptrs[index];
if (table == nullptr) {
JPEGLI_ERROR("Missing %s Huffman table %d", table, index);
JPEGLI_ERROR("Missing %s Huffman table %d", type, index);
}
ValidateHuffmanTable(cinfo, table, is_dc);
JPEGHuffmanCode huff;
ValidateHuffmanTable(reinterpret_cast<j_common_ptr>(cinfo), table, is_dc);
JPEGHuffmanCode huff = {};
size_t max_depth = 0;
for (size_t i = 1; i <= kJpegHuffmanMaxBitLength; ++i) {
if (table->bits[i] != 0) max_depth = i;
@ -501,94 +486,43 @@ void CopyHuffmanTable(j_compress_ptr cinfo, int index, bool is_dc,
huff.slot_id = index + (is_dc ? 0 : 0x10);
huff.sent_table = table->sent_table;
bool have_slot = false;
for (size_t i = 0; i < huffman_codes->size(); ++i) {
if ((*huffman_codes)[i].slot_id == huff.slot_id) have_slot = true;
for (size_t i = 0; i < *num_huffman_codes; ++i) {
if (huffman_codes[i].slot_id == huff.slot_id) have_slot = true;
}
if (!have_slot) {
huffman_codes->emplace_back(std::move(huff));
memcpy(&huffman_codes[*num_huffman_codes], &huff, sizeof(huff));
++(*num_huffman_codes);
}
}
} // namespace
void AddStandardHuffmanTables(j_compress_ptr cinfo, bool is_dc) {
// Huffman tables from the JPEG standard.
static constexpr JHUFF_TBL kStandardDCTables[2] = {
// DC luma
{{0, 0, 1, 5, 1, 1, 1, 1, 1, 1},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
FALSE},
// DC chroma
{{0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11},
FALSE}};
static constexpr JHUFF_TBL kStandardACTables[2] = {
// AC luma
{{0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125},
{0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06,
0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72,
0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3,
0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9,
0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4,
0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa},
FALSE},
// AC chroma
{{0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119},
{0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41,
0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1,
0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44,
0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74,
0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a,
0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4,
0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa},
FALSE}};
const JHUFF_TBL* std_tables = is_dc ? kStandardDCTables : kStandardACTables;
JHUFF_TBL** tables =
is_dc ? cinfo->dc_huff_tbl_ptrs : cinfo->ac_huff_tbl_ptrs;
for (int i = 0; i < 2; ++i) {
if (tables[i] == nullptr) {
tables[i] =
jpegli_alloc_huff_table(reinterpret_cast<j_common_ptr>(cinfo));
memcpy(tables[i], &std_tables[i], sizeof(JHUFF_TBL));
ValidateHuffmanTable(cinfo, tables[i], is_dc);
}
}
}
void CopyHuffmanCodes(j_compress_ptr cinfo,
std::vector<JPEGHuffmanCode>* huffman_codes) {
void CopyHuffmanCodes(j_compress_ptr cinfo, bool* is_baseline) {
jpeg_comp_master* m = cinfo->master;
m->huffman_codes =
Allocate<JPEGHuffmanCode>(cinfo, 2 * cinfo->num_components, JPOOL_IMAGE);
for (int c = 0; c < cinfo->num_components; ++c) {
jpeg_component_info* comp = &cinfo->comp_info[c];
CopyHuffmanTable(cinfo, comp->dc_tbl_no, /*is_dc=*/true, huffman_codes);
CopyHuffmanTable(cinfo, comp->ac_tbl_no, /*is_dc=*/false, huffman_codes);
if (comp->dc_tbl_no > 1 || comp->ac_tbl_no > 1) {
*is_baseline = false;
}
CopyHuffmanTable(cinfo, comp->dc_tbl_no, /*is_dc=*/true, m->huffman_codes,
&m->num_huffman_codes);
CopyHuffmanTable(cinfo, comp->ac_tbl_no, /*is_dc=*/false, m->huffman_codes,
&m->num_huffman_codes);
}
for (int i = 0; i < cinfo->num_scans; ++i) {
const jpeg_scan_info* si = &cinfo->scan_info[i];
ScanCodingInfo sci;
ScanCodingInfo sci = {};
for (int j = 0; j < si->comps_in_scan; ++j) {
int ci = si->component_index[j];
sci.dc_tbl_idx[j] = cinfo->comp_info[ci].dc_tbl_no;
sci.ac_tbl_idx[j] = cinfo->comp_info[ci].ac_tbl_no + 4;
}
if (i == 0) {
sci.num_huffman_codes = huffman_codes->size();
sci.num_huffman_codes = m->num_huffman_codes;
}
cinfo->master->scan_coding_info.emplace_back(std::move(sci));
memcpy(&m->scan_coding_info[i], &sci, sizeof(sci));
}
}
@ -607,10 +541,8 @@ size_t RestartIntervalForScan(j_compress_ptr cinfo, size_t scan_index) {
}
}
void OptimizeHuffmanCodes(
j_compress_ptr cinfo,
const std::vector<std::vector<jpegli::coeff_t> >& coeffs,
std::vector<JPEGHuffmanCode>* huffman_codes) {
void OptimizeHuffmanCodes(j_compress_ptr cinfo, bool* is_baseline) {
jpeg_comp_master* m = cinfo->master;
// Gather histograms.
size_t num_histo = 0;
for (int i = 0; i < cinfo->num_scans; ++i) {
@ -618,7 +550,7 @@ void OptimizeHuffmanCodes(
}
std::vector<Histogram> dc_histograms(num_histo);
std::vector<Histogram> ac_histograms(num_histo);
ProcessJpeg(cinfo, coeffs, &dc_histograms, &ac_histograms);
ProcessJpeg(cinfo, &dc_histograms, &ac_histograms);
// Cluster DC histograms.
JpegClusteredHistograms dc_clusters;
@ -633,16 +565,19 @@ void OptimizeHuffmanCodes(
// Add the first 4 DC and AC histograms in the first DHT segment.
std::vector<uint32_t> dc_slot_histograms;
std::vector<uint32_t> ac_slot_histograms;
m->huffman_codes = Allocate<JPEGHuffmanCode>(cinfo, num_histo, JPOOL_IMAGE);
for (size_t i = 0; i < dc_clusters.histograms.size(); ++i) {
if (i >= 4) break;
JXL_ASSERT(dc_clusters.slot_ids[i] == i);
AddJpegHuffmanCode(dc_clusters.histograms[i], i, huffman_codes);
AddJpegHuffmanCode(dc_clusters.histograms[i], i, m->huffman_codes,
&m->num_huffman_codes);
dc_slot_histograms.push_back(i);
}
for (size_t i = 0; i < ac_clusters.histograms.size(); ++i) {
if (i >= 4) break;
JXL_ASSERT(ac_clusters.slot_ids[i] == i);
AddJpegHuffmanCode(ac_clusters.histograms[i], 0x10 + i, huffman_codes);
AddJpegHuffmanCode(ac_clusters.histograms[i], 0x10 + i, m->huffman_codes,
&m->num_huffman_codes);
ac_slot_histograms.push_back(i);
}
@ -651,17 +586,19 @@ void OptimizeHuffmanCodes(
size_t histogram_id = 0;
size_t num_huffman_codes_sent = 0;
for (int i = 0; i < cinfo->num_scans; ++i) {
ScanCodingInfo sci;
ScanCodingInfo sci = {};
for (int c = 0; c < cinfo->scan_info[i].comps_in_scan; ++c) {
SetJpegHuffmanCode(dc_clusters, histogram_id, 0, dc_slot_histograms,
&sci.dc_tbl_idx[c], huffman_codes);
&sci.dc_tbl_idx[c], is_baseline, m->huffman_codes,
&m->num_huffman_codes);
SetJpegHuffmanCode(ac_clusters, histogram_id, 0x10, ac_slot_histograms,
&sci.ac_tbl_idx[c], huffman_codes);
&sci.ac_tbl_idx[c], is_baseline, m->huffman_codes,
&m->num_huffman_codes);
++histogram_id;
}
sci.num_huffman_codes = huffman_codes->size() - num_huffman_codes_sent;
num_huffman_codes_sent = huffman_codes->size();
cinfo->master->scan_coding_info.emplace_back(std::move(sci));
sci.num_huffman_codes = m->num_huffman_codes - num_huffman_codes_sent;
num_huffman_codes_sent = m->num_huffman_codes;
memcpy(&m->scan_coding_info[i], &sci, sizeof(sci));
}
}

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