forked from mirrors/gecko-dev
Bug 1782272 - Update libjxl to 59f7d19e454bc5e1edd692187e1178f5926fdfd9 r=saschanaz
Differential Revision: https://phabricator.services.mozilla.com/D153232
This commit is contained in:
parent
771c955e18
commit
760010bb23
219 changed files with 11566 additions and 6979 deletions
|
|
@ -26,7 +26,11 @@ SOURCES += [
|
|||
"/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/color_management.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/compressed_dc.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/convolve.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/convolve_separable5.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/convolve_separable7.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/convolve_slow.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/dct_scales.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/dec_ans.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/dec_cache.cc",
|
||||
|
|
@ -79,11 +83,14 @@ SOURCES += [
|
|||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_chroma_upsampling.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_epf.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_from_linear.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_gaborish.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_noise.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_patches.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_splines.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_to_linear.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_tone_mapping.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_upsampling.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc",
|
||||
|
|
|
|||
|
|
@ -10,9 +10,9 @@ origin:
|
|||
|
||||
url: https://github.com/libjxl/libjxl
|
||||
|
||||
release: commit 318c592d98b97d103941b90d47107f06a10c71da (2022-03-21T20:44:46Z).
|
||||
release: 59f7d19e454bc5e1edd692187e1178f5926fdfd9 (2022-07-28T11:13:01Z).
|
||||
|
||||
revision: 318c592d98b97d103941b90d47107f06a10c71da
|
||||
revision: 59f7d19e454bc5e1edd692187e1178f5926fdfd9
|
||||
|
||||
license: Apache-2.0
|
||||
|
||||
|
|
|
|||
|
|
@ -27,3 +27,11 @@ If applicable, add screenshots or example input/output images to help explain yo
|
|||
|
||||
**Additional context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
<!--
|
||||
Currently github does not allow uploading files that end in `.jxl`, but when you
|
||||
rename them for example as `image.jxl.jpg`, it will be possible to upload them
|
||||
and also view them in browsers that are configured to support it.
|
||||
|
||||
See https://github.com/orgs/github-community/discussions/18139
|
||||
-->
|
||||
|
|
|
|||
268
third_party/jpeg-xl/.github/workflows/build_test.yml
vendored
268
third_party/jpeg-xl/.github/workflows/build_test.yml
vendored
|
|
@ -90,7 +90,7 @@ jobs:
|
|||
# Whether we track the stack size.
|
||||
STACK_SIZE: ${{ matrix.env_stack_size }}
|
||||
TEST_STACK_LIMIT: ${{ matrix.env_test_stack_size }}
|
||||
WILL_RUN_TESTS: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && (matrix.test_in_pr || contains(github.event.pull_request.labels.*.names, 'CI:full'))) }}
|
||||
WILL_RUN_TESTS: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && (matrix.test_in_pr || contains(github.event.pull_request.labels.*.name, 'CI:full'))) }}
|
||||
|
||||
steps:
|
||||
- name: Install build deps
|
||||
|
|
@ -105,7 +105,6 @@ jobs:
|
|||
libbenchmark-tools \
|
||||
libbrotli-dev \
|
||||
libgdk-pixbuf2.0-dev \
|
||||
libgflags-dev \
|
||||
libgif-dev \
|
||||
libgtest-dev \
|
||||
libgtk2.0-dev \
|
||||
|
|
@ -125,6 +124,28 @@ jobs:
|
|||
with:
|
||||
submodules: true
|
||||
fetch-depth: 2
|
||||
|
||||
- name: Setup the LLVM source path
|
||||
if: matrix.name == 'msan'
|
||||
run: |
|
||||
LLVM_ROOT=${GITHUB_WORKSPACE}/llvm_root
|
||||
mkdir -p ${LLVM_ROOT}
|
||||
echo "LLVM_ROOT=${LLVM_ROOT}" >> $GITHUB_ENV
|
||||
- name: Cache LLVM sources
|
||||
if: matrix.name == 'msan'
|
||||
uses: actions/cache@v2
|
||||
with:
|
||||
path: ${{ env.LLVM_ROOT }}
|
||||
key: llvm
|
||||
- name: Checkout the LLVM source
|
||||
if: matrix.name == 'msan'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: false
|
||||
repository: llvm/llvm-project
|
||||
ref: llvmorg-7.0.1
|
||||
path: llvm_root
|
||||
|
||||
- name: Sphinx dependencies
|
||||
# Dependencies for sphinx HTML documentation
|
||||
if: matrix.name == 'release'
|
||||
|
|
@ -162,6 +183,9 @@ jobs:
|
|||
env:
|
||||
SKIP_TEST: 1
|
||||
CMAKE_CXX_FLAGS: ${{ matrix.cxxflags }}
|
||||
- name: Build stats
|
||||
run: |
|
||||
awk '!/^#/ {total[$4]+=($2-$1);cntr[$4]+=1} END {for (key in total) print total[key]/cntr[key] " " key}' build/.ninja_log | sort -n | tail -n 25
|
||||
- name: ccache stats
|
||||
run: ccache --show-stats
|
||||
- name: Build stats ${{ matrix.name }}
|
||||
|
|
@ -185,7 +209,7 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
# Test that the built binaries run.
|
||||
echo -e -n "PF\n1 1\n-1.0\nrrrrggggbbbb" > test.pfm
|
||||
echo -e -n "PF\n1 1\n-1.0\n\0\0\x80\x3f\0\0\x80\x3f\0\0\x80\x3f" > test.pfm
|
||||
build-example/encode_oneshot test.pfm test.jxl
|
||||
build-example/encode_oneshot_static test.pfm test-static.jxl
|
||||
build-example/decode_oneshot test.jxl dec.pfm dec.icc
|
||||
|
|
@ -218,7 +242,7 @@ jobs:
|
|||
matrix.name != 'coverage' && (github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' && (
|
||||
matrix.test_in_pr ||
|
||||
contains(github.event.pull_request.labels.*.names, 'CI:full'))))
|
||||
contains(github.event.pull_request.labels.*.name, 'CI:full'))))
|
||||
run: |
|
||||
STORE_IMAGES=0 ./ci.sh fast_benchmark
|
||||
# Run gbench once, just to make sure it runs, not for actual benchmarking.
|
||||
|
|
@ -231,173 +255,22 @@ jobs:
|
|||
run: |
|
||||
./ci.sh gbench --benchmark_min_time=0
|
||||
|
||||
|
||||
cross_compile_ubuntu:
|
||||
name: Cross-compiling ${{ matrix.build_target }} ${{ matrix.lowprecision }}
|
||||
runs-on: [ubuntu-18.04]
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- arch: arm64
|
||||
lowprecision:
|
||||
build_target: aarch64-linux-gnu
|
||||
cmake_args: -DCMAKE_CROSSCOMPILING_EMULATOR=/usr/bin/qemu-aarch64-static
|
||||
|
||||
- arch: arm64
|
||||
lowprecision: lowprecision
|
||||
build_target: aarch64-linux-gnu
|
||||
cmake_args: -DCMAKE_CROSSCOMPILING_EMULATOR=/usr/bin/qemu-aarch64-static -DCMAKE_CXX_FLAGS=-DJXL_HIGH_PRECISION=0
|
||||
|
||||
- arch: armhf
|
||||
lowprecision:
|
||||
build_target: arm-linux-gnueabihf
|
||||
cmake_args: -DCMAKE_CROSSCOMPILING_EMULATOR=/usr/bin/qemu-arm-static
|
||||
|
||||
- arch: i386
|
||||
lowprecision:
|
||||
test_in_pr: true
|
||||
build_target: i686-linux-gnu
|
||||
|
||||
env:
|
||||
BUILD_DIR: build
|
||||
WILL_RUN_TESTS: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && (matrix.test_in_pr || contains(github.event.pull_request.labels.*.names, 'CI:full'))) }}
|
||||
|
||||
steps:
|
||||
- name: Setup apt
|
||||
shell: bash
|
||||
run: |
|
||||
set -x
|
||||
sudo apt-get update -y
|
||||
sudo apt-get install -y curl gnupg ca-certificates
|
||||
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1E9377A2BA9EF27F
|
||||
|
||||
if [[ "${{ matrix.arch }}" != "amd64" ]]; then
|
||||
sudo dpkg --add-architecture "${{ matrix.arch }}"
|
||||
|
||||
# Update the sources.list with the split of supported architectures.
|
||||
bkplist="/etc/apt/sources.list.bkp"
|
||||
sudo mv /etc/apt/sources.list "${bkplist}"
|
||||
|
||||
newlist="/etc/apt/sources.list"
|
||||
sudo rm -f "${newlist}"
|
||||
|
||||
main_list="amd64"
|
||||
port_list=""
|
||||
if [[ "${{ matrix.arch }}" == "i386" ]]; then
|
||||
main_list="amd64,i386"
|
||||
else
|
||||
port_list="${{ matrix.arch }}"
|
||||
fi
|
||||
|
||||
if [[ -n "${port_list}" ]]; then
|
||||
port_url="http://ports.ubuntu.com/ubuntu-ports/"
|
||||
grep -v -E '^#' "${bkplist}" |
|
||||
sed -E "s;^deb (http[^ ]+) (.*)\$;deb [arch=${{ matrix.arch }}] ${port_url} \\2;" \
|
||||
| sudo tee -a "${newlist}"
|
||||
fi
|
||||
grep -v -E '^#' "${bkplist}" |
|
||||
sed -E "s;^deb (http[^ ]+) (.*)\$;deb [arch=${main_list}] \\1 \\2\ndeb-src [arch=${main_list}] \\1 \\2;" \
|
||||
| sudo tee -a "${newlist}"
|
||||
fi
|
||||
|
||||
- name: Install build deps
|
||||
shell: bash
|
||||
run: |
|
||||
set -x
|
||||
sudo apt update
|
||||
pkgs=(
|
||||
# Build dependencies
|
||||
cmake
|
||||
doxygen
|
||||
ninja-build
|
||||
pkg-config
|
||||
qemu-user-static
|
||||
xvfb
|
||||
|
||||
# Toolchain for cross-compiling.
|
||||
clang-7
|
||||
# libclang-common-7-dev:${{ matrix.arch }}
|
||||
libc6-dev-${{ matrix.arch }}-cross
|
||||
libstdc++-8-dev-${{ matrix.arch }}-cross
|
||||
libstdc++-8-dev:${{ matrix.arch }}
|
||||
|
||||
# Dependencies
|
||||
libbrotli-dev:${{ matrix.arch }}
|
||||
libgif-dev:${{ matrix.arch }}
|
||||
libjpeg-dev:${{ matrix.arch }}
|
||||
libpng-dev:${{ matrix.arch }}
|
||||
libwebp-dev:${{ matrix.arch }}
|
||||
|
||||
# For OpenEXR:
|
||||
libilmbase-dev:${{ matrix.arch }}
|
||||
libopenexr-dev:${{ matrix.arch }}
|
||||
|
||||
# GTK plugins
|
||||
libgdk-pixbuf2.0-dev:${{ matrix.arch }}
|
||||
libgtk2.0-dev:${{ matrix.arch }}
|
||||
|
||||
# QT
|
||||
libqt5x11extras5-dev:${{ matrix.arch }}
|
||||
qtbase5-dev:${{ matrix.arch }}
|
||||
)
|
||||
if [[ "${{ matrix.build_target }}" != "x86_64-linux-gnu" ]]; then
|
||||
pkgs+=(
|
||||
binutils-${{ matrix.build_target }}
|
||||
gcc-${{ matrix.build_target }}
|
||||
)
|
||||
fi
|
||||
if [[ "${{ matrix.arch }}" != "i386" ]]; then
|
||||
pkgs+=(
|
||||
# TCMalloc
|
||||
libgoogle-perftools-dev:${{ matrix.arch }}
|
||||
libgoogle-perftools4:${{ matrix.arch }}
|
||||
libtcmalloc-minimal4:${{ matrix.arch }}
|
||||
libunwind-dev:${{ matrix.arch }}
|
||||
)
|
||||
fi
|
||||
DEBIAN_FRONTEND=noninteractive sudo apt install -y "${pkgs[@]}"
|
||||
echo "CC=clang-7" >> $GITHUB_ENV
|
||||
echo "CXX=clang++-7" >> $GITHUB_ENV
|
||||
- name: Checkout the source
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Build
|
||||
run: |
|
||||
./ci.sh release \
|
||||
-DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
|
||||
-DJPEGXL_BUNDLE_GFLAGS=ON \
|
||||
-DJPEGXL_ENABLE_JNI=OFF \
|
||||
${{ matrix.cmake_args }}
|
||||
env:
|
||||
SKIP_TEST: 1
|
||||
BUILD_TARGET: ${{ matrix.build_target }}
|
||||
- name: Build stats ${{ matrix.build_target }}
|
||||
run: |
|
||||
tools/build_stats.py --save build/stats.json \
|
||||
--binutils ${{ matrix.build_target }}- \
|
||||
--max-stack ${{ matrix.max_stack || '0' }} \
|
||||
cjxl djxl libjxl.so libjxl_dec.so
|
||||
# Run the tests on push and when requested in pull_request.
|
||||
- name: Test
|
||||
if: env.WILL_RUN_TESTS == 'true'
|
||||
run: |
|
||||
./ci.sh test
|
||||
env:
|
||||
BUILD_TARGET: ${{ matrix.build_target }}
|
||||
|
||||
windows_msys:
|
||||
name: Windows MSYS2 / ${{ matrix.arch }}
|
||||
name: Windows MSYS2 / ${{ matrix.msystem }}
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: x86_64
|
||||
msystem: mingw64
|
||||
- arch: i686
|
||||
msystem: mingw32
|
||||
- msystem: mingw64
|
||||
- msystem: clang64
|
||||
- msystem: mingw32
|
||||
# TODO(eustas): investigate HWY Mul failures
|
||||
disable_tests: HwyMulTestGroup/HwyMulTest\.TestAllMulHigh/EMU128|HwyMulTestGroup/HwyMulTest\.TestAllMulFixedPoint15/EMU128
|
||||
- msystem: clang32
|
||||
# TODO(eustas): investigate HWY Sort and JXL ANS failures
|
||||
disable_tests: SortTestGroup/SortTest\.TestAllSort/.*|ANSTest\.RandomUnbalancedStreamRoundtrip3|ANSTest\.RandomUnbalancedStreamRoundtripBig
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: msys2 {0}
|
||||
|
|
@ -415,24 +288,22 @@ jobs:
|
|||
install: >-
|
||||
base-devel
|
||||
git
|
||||
mingw-w64-${{ matrix.arch }}-brotli
|
||||
mingw-w64-${{ matrix.arch }}-cmake
|
||||
mingw-w64-${{ matrix.arch }}-gcc
|
||||
mingw-w64-${{ matrix.arch }}-gflags
|
||||
mingw-w64-${{ matrix.arch }}-giflib
|
||||
mingw-w64-${{ matrix.arch }}-gtest
|
||||
mingw-w64-${{ matrix.arch }}-libavif
|
||||
mingw-w64-${{ matrix.arch }}-libjpeg-turbo
|
||||
mingw-w64-${{ matrix.arch }}-libpng
|
||||
mingw-w64-${{ matrix.arch }}-libwebp
|
||||
mingw-w64-${{ matrix.arch }}-ninja
|
||||
pacboy: >-
|
||||
brotli:p
|
||||
cmake:p
|
||||
giflib:p
|
||||
gtest:p
|
||||
libavif:p
|
||||
libjpeg-turbo:p
|
||||
libpng:p
|
||||
libwebp:p
|
||||
ninja:p
|
||||
toolchain:p
|
||||
|
||||
- name: CMake configure
|
||||
# AVX2 tests fail with segfault when built in MSYS2.
|
||||
run: |
|
||||
cmake \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_FLAGS="-DHWY_DISABLED_TARGETS=\"HWY_AVX2|HWY_AVX3\"" \
|
||||
-DJPEGXL_ENABLE_JNI=OFF \
|
||||
-DJPEGXL_ENABLE_MANPAGES=OFF \
|
||||
-DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
|
||||
|
|
@ -445,20 +316,17 @@ jobs:
|
|||
if: |
|
||||
github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' &&
|
||||
contains(github.event.pull_request.labels.*.names, 'CI:full'))
|
||||
# LibraryCLinkageTest doesn't work in here because it needs the DLL
|
||||
# which is not installed.
|
||||
run: ctest --test-dir build --parallel 2 --output-on-failure -E LibraryCLinkageTest
|
||||
contains(github.event.pull_request.labels.*.name, 'CI:full'))
|
||||
run: ctest --test-dir build --parallel 2 --output-on-failure -E "${{ matrix.disable_tests }}"
|
||||
|
||||
wasm32_build:
|
||||
name: WASM wasm32/${{ matrix.variant }}
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
EM_VERSION: 3.1.4
|
||||
V8_VERSION: 9.8.177
|
||||
V8: ${{ github.workspace }}/.jsvu/v8
|
||||
BUILD_TARGET: wasm32
|
||||
EM_VERSION: 3.1.1
|
||||
NODE_VERSION: 18
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
|
|
@ -499,6 +367,14 @@ jobs:
|
|||
restore-keys: |
|
||||
${{ runner.os }}-${{ steps.git-env.outputs.parent }}-${{ matrix.variant }}
|
||||
|
||||
- name: Install node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{env.NODE_VERSION}}
|
||||
|
||||
- name: Get non-EMSDK node path
|
||||
run: which node >> $HOME/.base_node_path
|
||||
|
||||
- name: Install emsdk
|
||||
uses: mymindstorm/setup-emsdk@v11
|
||||
# TODO(deymo): We could cache this action but it doesn't work when running
|
||||
|
|
@ -506,13 +382,12 @@ jobs:
|
|||
with:
|
||||
version: ${{env.EM_VERSION}}
|
||||
no-cache: true
|
||||
- name: Install v8
|
||||
|
||||
- name: Set EMSDK node version
|
||||
run: |
|
||||
npm install jsvu -g
|
||||
HOME="${{ github.workspace }}" jsvu --os=linux64 "v8@${V8_VERSION}"
|
||||
rm -f "${{ github.workspace }}/.jsvu/v8"
|
||||
ln -s "${{ github.workspace }}/.jsvu/v8-${V8_VERSION}" \
|
||||
"${{ github.workspace }}/.jsvu/v8"
|
||||
echo "NODE_JS='$(cat $HOME/.base_node_path)'" >> $EM_CONFIG
|
||||
emsdk construct_env
|
||||
|
||||
# TODO(deymo): Build and install other dependencies like libpng, libjpeg,
|
||||
# etc.
|
||||
- name: Build
|
||||
|
|
@ -522,12 +397,9 @@ jobs:
|
|||
if [[ "${{ matrix.variant }}" == "simd" ]]; then
|
||||
export ENABLE_WASM_SIMD=1
|
||||
fi
|
||||
emconfigure ./ci.sh release \
|
||||
./ci.sh release \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_BUILD_WITH_INSTALL_RPATH=ON \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DJPEGXL_ENABLE_JNI=OFF
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache
|
||||
env:
|
||||
SKIP_TEST: 1
|
||||
- name: ccache stats
|
||||
|
|
@ -537,6 +409,6 @@ jobs:
|
|||
if: |
|
||||
github.event_name == 'push' ||
|
||||
(github.event_name == 'pull_request' &&
|
||||
contains(github.event.pull_request.labels.*.names, 'CI:full'))
|
||||
contains(github.event.pull_request.labels.*.name, 'CI:full'))
|
||||
run: |
|
||||
emconfigure ./ci.sh test
|
||||
./ci.sh test
|
||||
|
|
|
|||
170
third_party/jpeg-xl/.github/workflows/build_test_cross.yml
vendored
Normal file
170
third_party/jpeg-xl/.github/workflows/build_test_cross.yml
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
# 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.
|
||||
|
||||
# Workflow for building and running tests.
|
||||
|
||||
name: Build/Test Cross
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- v*.*.x
|
||||
pull_request:
|
||||
types: [opened, reopened, labeled, synchronize]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
cross_compile_ubuntu:
|
||||
name: Cross-compiling ${{ matrix.build_target }} ${{ matrix.lowprecision }}
|
||||
runs-on: [ubuntu-22.04]
|
||||
container:
|
||||
image: debian:bullseye
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- arch: arm64
|
||||
lowprecision:
|
||||
build_target: aarch64-linux-gnu
|
||||
cmake_args: -DCMAKE_CROSSCOMPILING_EMULATOR=/usr/bin/qemu-aarch64-static
|
||||
|
||||
- arch: arm64
|
||||
lowprecision: lowprecision
|
||||
build_target: aarch64-linux-gnu
|
||||
cmake_args: -DCMAKE_CROSSCOMPILING_EMULATOR=/usr/bin/qemu-aarch64-static -DCMAKE_CXX_FLAGS=-DJXL_HIGH_PRECISION=0
|
||||
|
||||
- arch: armhf
|
||||
lowprecision:
|
||||
build_target: arm-linux-gnueabihf
|
||||
cmake_args: -DCMAKE_CROSSCOMPILING_EMULATOR=/usr/bin/qemu-arm-static
|
||||
|
||||
- arch: i386
|
||||
lowprecision:
|
||||
test_in_pr: true
|
||||
build_target: i686-linux-gnu
|
||||
|
||||
env:
|
||||
BUILD_DIR: build
|
||||
WILL_RUN_TESTS: ${{ github.event_name == 'push' || (github.event_name == 'pull_request' && (matrix.test_in_pr || contains(github.event.pull_request.labels.*.name, 'CI:full'))) }}
|
||||
|
||||
steps:
|
||||
- name: Setup apt
|
||||
shell: bash
|
||||
run: |
|
||||
set -x
|
||||
apt-get update -y
|
||||
apt-get install -y ca-certificates debian-ports-archive-keyring
|
||||
|
||||
dpkg --add-architecture "${{ matrix.arch }}"
|
||||
|
||||
# Update the sources.list with the split of supported architectures.
|
||||
bkplist="/etc/apt/sources.list.bkp"
|
||||
mv /etc/apt/sources.list "${bkplist}"
|
||||
|
||||
newlist="/etc/apt/sources.list"
|
||||
rm -f "${newlist}"
|
||||
|
||||
main_list="amd64,${{ matrix.arch }}"
|
||||
port_list=""
|
||||
if [[ "${{ matrix.arch }}" == "i386" ]]; then
|
||||
main_list="amd64,i386"
|
||||
else
|
||||
port_list="${{ matrix.arch }}"
|
||||
fi
|
||||
|
||||
grep -v -E '^#' "${bkplist}" |
|
||||
sed -E "s;^deb (http[^ ]+) (.*)\$;deb [arch=${main_list}] \\1 \\2\ndeb-src [arch=${main_list}] \\1 \\2;" \
|
||||
| tee -a "${newlist}"
|
||||
|
||||
- name: Install build deps
|
||||
shell: bash
|
||||
run: |
|
||||
set -x
|
||||
apt update
|
||||
pkgs=(
|
||||
# Build dependencies
|
||||
cmake
|
||||
doxygen
|
||||
git
|
||||
graphviz
|
||||
ninja-build
|
||||
pkg-config
|
||||
qemu-user-static
|
||||
xdg-utils
|
||||
xvfb
|
||||
|
||||
# Toolchain for cross-compiling.
|
||||
clang-11
|
||||
libc6-dev-${{ matrix.arch }}-cross
|
||||
libstdc++-10-dev-${{ matrix.arch }}-cross
|
||||
libstdc++-10-dev:${{ matrix.arch }}
|
||||
|
||||
# Dependencies
|
||||
libbrotli-dev:${{ matrix.arch }}
|
||||
libgif-dev:${{ matrix.arch }}
|
||||
libjpeg-dev:${{ matrix.arch }}
|
||||
libpng-dev:${{ matrix.arch }}
|
||||
libwebp-dev:${{ matrix.arch }}
|
||||
|
||||
# For OpenEXR:
|
||||
libilmbase-dev:${{ matrix.arch }}
|
||||
libopenexr-dev:${{ matrix.arch }}
|
||||
|
||||
# GTK plugins
|
||||
libgdk-pixbuf2.0-dev:${{ matrix.arch }}
|
||||
libgtk2.0-dev:${{ matrix.arch }}
|
||||
|
||||
# QT
|
||||
libqt5x11extras5-dev:${{ matrix.arch }}
|
||||
qtbase5-dev:${{ matrix.arch }}
|
||||
)
|
||||
if [[ "${{ matrix.build_target }}" != "x86_64-linux-gnu" ]]; then
|
||||
pkgs+=(
|
||||
binutils-${{ matrix.build_target }}
|
||||
gcc-${{ matrix.build_target }}
|
||||
)
|
||||
fi
|
||||
if [[ "${{ matrix.arch }}" != "i386" ]]; then
|
||||
pkgs+=(
|
||||
# TCMalloc
|
||||
libgoogle-perftools-dev:${{ matrix.arch }}
|
||||
libgoogle-perftools4:${{ matrix.arch }}
|
||||
libtcmalloc-minimal4:${{ matrix.arch }}
|
||||
libunwind-dev:${{ matrix.arch }}
|
||||
)
|
||||
fi
|
||||
DEBIAN_FRONTEND=noninteractive apt install -y "${pkgs[@]}"
|
||||
echo "CC=clang-11" >> $GITHUB_ENV
|
||||
echo "CXX=clang++-11" >> $GITHUB_ENV
|
||||
- name: Checkout the source
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
fetch-depth: 1
|
||||
- name: Build
|
||||
run: |
|
||||
./ci.sh release \
|
||||
-DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
|
||||
-DJPEGXL_ENABLE_JNI=OFF \
|
||||
${{ matrix.cmake_args }}
|
||||
env:
|
||||
SKIP_TEST: 1
|
||||
BUILD_TARGET: ${{ matrix.build_target }}
|
||||
- name: Build stats ${{ matrix.build_target }}
|
||||
run: |
|
||||
tools/build_stats.py --save build/stats.json \
|
||||
--binutils ${{ matrix.build_target }}- \
|
||||
--max-stack ${{ matrix.max_stack || '0' }} \
|
||||
cjxl djxl libjxl.so libjxl_dec.so
|
||||
# Run the tests on push and when requested in pull_request.
|
||||
- name: Test
|
||||
if: env.WILL_RUN_TESTS == 'true'
|
||||
run: |
|
||||
./ci.sh test
|
||||
env:
|
||||
BUILD_TARGET: ${{ matrix.build_target }}
|
||||
|
|
@ -14,13 +14,33 @@ on:
|
|||
pull_request:
|
||||
types: [opened, reopened, labeled, synchronize]
|
||||
|
||||
concurrency:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Conformance Build ${{ matrix.name }}
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: AVX3
|
||||
cflags: -DHWY_DISABLED_TARGETS=HWY_AVX3-1
|
||||
- name: AVX2
|
||||
cflags: -DHWY_DISABLED_TARGETS=HWY_AVX2-1
|
||||
- name: SSE4
|
||||
cflags: -DHWY_DISABLED_TARGETS=HWY_SSE4-1
|
||||
- name: SSSE3
|
||||
cflags: -DHWY_DISABLED_TARGETS=HWY_SSSE3-1
|
||||
- name: EMU128
|
||||
cflags: -DHWY_COMPILE_ONLY_EMU128=1
|
||||
- name: SCALAR
|
||||
cflags: -DHWY_COMPILE_ONLY_SCALAR=1
|
||||
- name: SCALAR_ASAN
|
||||
cflags: -DHWY_COMPILE_ONLY_SCALAR=1
|
||||
build_type: asan
|
||||
env:
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
steps:
|
||||
|
|
@ -36,7 +56,6 @@ jobs:
|
|||
libbenchmark-tools \
|
||||
libbrotli-dev \
|
||||
libgdk-pixbuf2.0-dev \
|
||||
libgflags-dev \
|
||||
libgif-dev \
|
||||
libgtest-dev \
|
||||
libgtk2.0-dev \
|
||||
|
|
@ -76,29 +95,40 @@ jobs:
|
|||
run: |
|
||||
mkdir -p ${CCACHE_DIR}
|
||||
echo "max_size = 200M" > ${CCACHE_DIR}/ccache.conf
|
||||
./ci.sh release -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
|
||||
CMAKE_FLAGS="${{ matrix.cflags }}" \
|
||||
./ci.sh ${{ matrix.build_type || 'release' }} -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
|
||||
-DBUILD_TESTING=OFF
|
||||
# Copy library to flatten the artifacts directory structure
|
||||
# Flatten the artifacts directory structure
|
||||
cp tools/conformance/conformance.py build/tools/conformance
|
||||
cp tools/conformance/lcms2.py build/tools/conformance
|
||||
cp build/tools/djxl build/tools/conformance
|
||||
cp build/libjxl_dec.so.0.7.0 build/tools/conformance
|
||||
cp build/libjxl_threads.so.0.7.0 build/tools/conformance
|
||||
env:
|
||||
SKIP_TEST: 1
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: conformance_binary
|
||||
name: conformance_binary-${{ matrix.name }}
|
||||
path: |
|
||||
build/tools/conformance/djxl_conformance
|
||||
build/tools/conformance/conformance.py
|
||||
build/tools/conformance/lcms2.py
|
||||
build/tools/conformance/djxl
|
||||
build/tools/conformance/libjxl_dec.so.0.7.0
|
||||
build/tools/conformance/libjxl_threads.so.0.7.0
|
||||
- name: ccache stats
|
||||
run: ccache --show-stats
|
||||
|
||||
run:
|
||||
name: Conformance Test ${{ matrix.name }} on ${{ matrix.target}}
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
name: [main_level5, main_level10]
|
||||
target: [AVX3, AVX2, SSE4, SSSE3, EMU128, SCALAR, SCALAR_ASAN]
|
||||
steps:
|
||||
- name: Install deps
|
||||
run: |
|
||||
|
|
@ -107,19 +137,20 @@ jobs:
|
|||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: libjxl/conformance
|
||||
ref: ce5a804a3ff834097b93c39d2f93e5445f6bf340
|
||||
ref: a6a44bbbd69830e1dc862174599ce5738a0a414f
|
||||
path: conformance
|
||||
- name: Download and link conformance files
|
||||
run: |
|
||||
${{ github.workspace }}/conformance/scripts/download_and_symlink.sh
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: conformance_binary
|
||||
name: conformance_binary-${{ matrix.target }}
|
||||
- name: Run conformance tests
|
||||
run: |
|
||||
chmod +x djxl_conformance
|
||||
chmod +x djxl
|
||||
ln -s libjxl_dec.so.0.7.0 libjxl_dec.so.0.7
|
||||
ln -s libjxl_threads.so.0.7.0 libjxl_threads.so.0.7
|
||||
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`
|
||||
python conformance/scripts/conformance.py \
|
||||
--decoder=`pwd`/djxl_conformance \
|
||||
python conformance.py \
|
||||
--decoder=`pwd`/djxl \
|
||||
--corpus=`pwd`/conformance/testcases/${{ matrix.name }}.txt
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ jobs:
|
|||
doxygen \
|
||||
libbrotli-dev \
|
||||
libgdk-pixbuf2.0-dev \
|
||||
libgflags-dev \
|
||||
libgif-dev \
|
||||
libgtest-dev \
|
||||
libgtk2.0-dev \
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ jobs:
|
|||
doxygen \
|
||||
libbrotli-dev \
|
||||
libgdk-pixbuf2.0-dev \
|
||||
libgflags-dev \
|
||||
libgif-dev \
|
||||
libgtest-dev \
|
||||
libgtk2.0-dev \
|
||||
|
|
@ -146,6 +145,20 @@ jobs:
|
|||
cd "git-${git_version}"
|
||||
make prefix=/usr -j4 install
|
||||
|
||||
- name: Install gcc-8 (only 18.04)
|
||||
if: matrix.os == 'ubuntu:18.04'
|
||||
# Compiler bug workaround: install and use gcc-8
|
||||
shell: 'bash'
|
||||
run: |
|
||||
apt install -y \
|
||||
gcc-8 \
|
||||
g++-8 \
|
||||
#
|
||||
update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-8 100
|
||||
update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-8 100
|
||||
update-alternatives --set g++ /usr/bin/g++-8
|
||||
update-alternatives --set gcc /usr/bin/gcc-8
|
||||
|
||||
- name: Set git safe dir
|
||||
run: |
|
||||
export GIT_CEILING_DIRECTORIES=/__w # only work before git v2.35.2
|
||||
|
|
@ -251,7 +264,7 @@ jobs:
|
|||
|
||||
windows_build:
|
||||
name: Windows Build (vcpkg / ${{ matrix.triplet }})
|
||||
runs-on: [windows-latest]
|
||||
runs-on: [windows-2019]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
|
@ -262,7 +275,7 @@ jobs:
|
|||
arch: '-A x64'
|
||||
|
||||
env:
|
||||
VCPKG_VERSION: '2021.05.12'
|
||||
VCPKG_VERSION: '2022.06.16.1'
|
||||
VCPKG_ROOT: vcpkg
|
||||
VCPKG_DISABLE_METRICS: 1
|
||||
|
||||
|
|
@ -302,7 +315,6 @@ jobs:
|
|||
run: |
|
||||
set -x
|
||||
${VCPKG_ROOT}/vcpkg --triplet ${{ matrix.triplet }} install \
|
||||
gflags \
|
||||
giflib \
|
||||
libjpeg-turbo \
|
||||
libpng \
|
||||
|
|
@ -324,7 +336,6 @@ jobs:
|
|||
-DJPEGXL_ENABLE_TCMALLOC=OFF \
|
||||
-DJPEGXL_ENABLE_VIEWERS=OFF \
|
||||
-DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} \
|
||||
-DJPEGXL_BUNDLE_GFLAGS=ON \
|
||||
#
|
||||
- name: Build
|
||||
shell: 'bash'
|
||||
|
|
|
|||
8
third_party/jpeg-xl/AUTHORS
vendored
8
third_party/jpeg-xl/AUTHORS
vendored
|
|
@ -19,25 +19,33 @@ Google LLC <*@google.com>
|
|||
Alex Xu (Hello71) <alex_y_xu@yahoo.ca>
|
||||
Alexander Sago <cagelight@gmail.com>
|
||||
Andrius Lukas Narbutas <andrius4669@gmail.com>
|
||||
Aous Naman <aous@unsw.edu.au>
|
||||
Artem Selishchev
|
||||
Biswapriyo Nath <nathbappai@gmail.com>
|
||||
CanadianBaconBoi <beamconnor@gmail.com>
|
||||
Daniel Novomeský <dnovomesky@gmail.com>
|
||||
David Burnett <vargolsoft@gmail.com>
|
||||
Dirk Lemstra <dirk@lemstra.org>
|
||||
Don Olmstead <don.j.olmstead@gmail.com>
|
||||
Even Rouault <even.rouault@spatialys.com>
|
||||
Heiko Becker <heirecka@exherbo.org>
|
||||
Jon Sneyers <jon@cloudinary.com>
|
||||
Kai Hollberg <Schweinepriester@users.noreply.github.com>
|
||||
Kleis Auke Wolthuizen <github@kleisauke.nl>
|
||||
L. E. Segovia
|
||||
Leo Izen <leo.izen@gmail.com>
|
||||
Lovell Fuller
|
||||
Maarten DB <anonymous.maarten@gmail.com>
|
||||
Marcin Konicki <ahwayakchih@gmail.com>
|
||||
Martin Strunz
|
||||
Mathieu Malaterre <mathieu.malaterre@gmail.com>
|
||||
Mikk Leini <mikk.leini@krakul.eu>
|
||||
Misaki Kasumi <misakikasumi@outlook.com>
|
||||
Petr Diblík
|
||||
Pieter Wuille
|
||||
roland-rollo
|
||||
Samuel Leong <wvvwvvvvwvvw@gmail.com>
|
||||
Sandro <sandro.jaeckel@gmail.com>
|
||||
Stephan T. Lavavej <stl@nuwen.net>
|
||||
Vincent Torri <vincent.torri@gmail.com>
|
||||
xiota
|
||||
|
|
|
|||
76
third_party/jpeg-xl/CHANGELOG.md
vendored
76
third_party/jpeg-xl/CHANGELOG.md
vendored
|
|
@ -6,18 +6,26 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
|
||||
## [0.7] - 2022-07-21
|
||||
|
||||
### Added
|
||||
- decoder API: Ability to decode the content of metadata boxes:
|
||||
`JXL_DEC_BOX`, `JXL_DEC_BOX_NEED_MORE_OUTPUT`, `JxlDecoderSetBoxBuffer`,
|
||||
`JXL_DEC_BOX`, `JXL_DEC_BOX_NEED_MORE_OUTPUT`, `JxlDecoderSetBoxBuffer`,
|
||||
`JxlDecoderGetBoxType`, `JxlDecoderGetBoxSizeRaw` and
|
||||
`JxlDecoderSetDecompressBoxes`
|
||||
- decoder API: ability to mark the input is finished: `JxlDecoderCloseInput`
|
||||
- encoder API: ability to add metadata boxes, added new functions
|
||||
`JxlEncoderAddBox`, `JxlEncoderUseBoxes`, `JxlEncoderCloseBoxes` and
|
||||
`JxlEncoderCloseFrames`.
|
||||
`JxlDecoderSetDecompressBoxes`.
|
||||
- decoder API: ability to mark the input is finished: `JxlDecoderCloseInput`.
|
||||
- decoder API: ability to request updates on different progressive events using
|
||||
`JxlDecoderSetProgressiveDetail`; currently supported events are
|
||||
`kDC`, `kLastPasses` and `kPasses`.
|
||||
- decoder API: ability to specify desired intensity target using
|
||||
`JxlDecoderSetDesiredIntensityTarget`
|
||||
- decoder API: new function `JxlDecoderSetCoalesced` to allow decoding
|
||||
non-coalesced (unblended) frames, e.g. layers of a composite still image
|
||||
or the cropped frames of a recompressed GIF/APNG.
|
||||
- decoder API: new function `JxlDecoderSetUnpremultiplyAlpha` to set
|
||||
preference for getting an associated alpha channel with premultiplied or
|
||||
unpremultiplied colors.
|
||||
- decoder API: field added to `JxlFrameHeader`: a `JxlLayerInfo` struct
|
||||
that contains crop dimensions and offsets and blending information for
|
||||
the non-coalesced case.
|
||||
|
|
@ -26,37 +34,67 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- decoder API: new function `JxlDecoderSetMultithreadedImageOutCallback`,
|
||||
allowing output callbacks to receive more information about the number of
|
||||
threads on which they are running.
|
||||
- encoder API: added ability to set several encoder options to frames using
|
||||
`JxlEncoderFrameSettingsSetOption`
|
||||
- decoder API: new function `JxlDecoderSkipCurrentFrame` to skip processing
|
||||
the current frame after a progressive detail is reached.
|
||||
- decoder API: new function `JxlDecoderGetIntendedDownsamplingRatio` to get
|
||||
the intended downsampling ratio of progressive steps, based on the
|
||||
information in the frame header.
|
||||
- decoder API: new function `JxlDecoderSetRenderSpotcolors` to allow disabling
|
||||
rendering of spot colors.
|
||||
- decoder/encoder API: add two fields to `JXLBasicInfo`: `intrinsic_xsize`
|
||||
and `intrinsic_ysize` to signal the intrinsic size.
|
||||
- encoder API: ability to add metadata boxes, added new functions
|
||||
`JxlEncoderAddBox`, `JxlEncoderUseBoxes`, `JxlEncoderCloseBoxes` and
|
||||
`JxlEncoderCloseFrames`.
|
||||
- encoder API: added ability to set several encoder options / extra fields to
|
||||
frames using `JxlEncoderSetFrameName`, `JxlEncoderFrameSettingsSetOption`,
|
||||
`JxlEncoderFrameSettingsSetFloatOption`.
|
||||
- encoder API: added ability to check required codestream compatibility level
|
||||
and force specified using `JxlEncoderGetRequiredCodestreamLevel` and
|
||||
`JxlEncoderSetCodestreamLevel`.
|
||||
- encoder API: added ability to force emitting box-based container format
|
||||
using `JxlEncoderUseContainer`.
|
||||
- encoder API: added ability to store JPEG metadata for lossless reconstruction
|
||||
using `JxlEncoderStoreJPEGMetadata`
|
||||
- encoder API: new functions `JxlEncoderSetFrameHeader` and
|
||||
`JxlEncoderSetExtraChannelBlendInfo` to set animation
|
||||
and blending parameters of the frame, and `JxlEncoderInitFrameHeader` and
|
||||
`JxlEncoderInitBlendInfo` to initialize the structs to set.
|
||||
- decoder/encoder API: add two fields to `JXLBasicInfo`: `intrinsic_xsize`
|
||||
and `intrinsic_ysize` to signal the intrinsic size.
|
||||
- encoder API: ability to encode arbitrary extra channels:
|
||||
`JxlEncoderInitExtraChannelInfo`, `JxlEncoderSetExtraChannelInfo`,
|
||||
`JxlEncoderSetExtraChannelName` and `JxlEncoderSetExtraChannelBuffer`.
|
||||
- encoder API: ability to plug custom CMS implementation using
|
||||
`JxlEncoderSetCms(JxlEncoder* enc, JxlCmsInterface cms)`
|
||||
- encoder API: added `JxlEncoderGetError` to retrieve last encoder error.
|
||||
|
||||
### Changed
|
||||
- decoder API: using `JxlDecoderCloseInput` at the end of all input is required
|
||||
when using JXL_DEC_BOX, and is now also encouraged in other cases, but not
|
||||
required in those other cases for backwards compatiblity.
|
||||
required in those other cases for backwards compatibility.
|
||||
- encoder API: `JxlEncoderCloseInput` now closes both frames and boxes input.
|
||||
- CLI: `cjxl` and `djxl` have been reimplemented on the base of public decoder
|
||||
and encoder API; dropped dependency on `gflags` for argument parsing.
|
||||
|
||||
### Deprecated
|
||||
- encoder API: `JxlEncoderOptions`: use `JxlEncoderFrameSettings` instead
|
||||
- decoder API: `JXL_DEC_EXTENSIONS` event: use `JXL_DEC_BASIC_INFO`
|
||||
- decoder / encoder API: pixel types `JXL_TYPE_BOOLEAN` and `JXL_TYPE_UINT32`:
|
||||
consider using `JXL_TYPE_UINT8` and `JXL_TYPE_FLOAT` correspondingly.
|
||||
- decoder API: pixel format parameter for `JxlDecoderGetColorAsEncodedProfile`
|
||||
and `JxlDecoderGetICCProfileSize`: pass `NULL`.
|
||||
- decoder API: `JxlDecoderDefaultPixelFormat`
|
||||
- encoder API: `JxlEncoderOptions`: use `JxlEncoderFrameSettings` instead.
|
||||
- encoder API: `JxlEncoderOptionsCreate`: use `JxlEncoderFrameSettingsCreate`
|
||||
instead
|
||||
instead.
|
||||
- encoder API: `JxlEncoderOptionsSetDistance`: use `JxlEncoderSetFrameDistance`
|
||||
instead
|
||||
instead.
|
||||
- encoder API: `JxlEncoderOptionsSetLossless`: use `JxlEncoderSetFrameLossless`
|
||||
instead
|
||||
- encoder API: `JxlEncoderOptionsSetEffort`: use `JxlEncoderFrameSettingsSetOption(
|
||||
frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, effort)` instead.
|
||||
instead.
|
||||
- encoder API: `JxlEncoderOptionsSetEffort`: use
|
||||
`JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_EFFORT, effort)`
|
||||
instead.
|
||||
- encoder API: `JxlEncoderOptionsSetDecodingSpeed`: use
|
||||
`JxlEncoderFrameSettingsSetOption(frame_settings,
|
||||
JXL_ENC_FRAME_SETTING_DECODING_SPEED, tier)` instead.
|
||||
`JxlEncoderFrameSettingsSetOption(frame_settings, JXL_ENC_FRAME_SETTING_DECODING_SPEED, tier)`
|
||||
instead.
|
||||
- encoder API: deprecated `JXL_ENC_NOT_SUPPORTED`, the encoder returns
|
||||
`JXL_ENC_ERROR` instead and there is no need to handle
|
||||
`JXL_ENC_NOT_SUPPORTED`.
|
||||
|
|
|
|||
73
third_party/jpeg-xl/CMakeLists.txt
vendored
73
third_party/jpeg-xl/CMakeLists.txt
vendored
|
|
@ -34,6 +34,16 @@ check_cxx_source_compiles(
|
|||
JPEGXL_EMSCRIPTEN
|
||||
)
|
||||
|
||||
check_cxx_source_compiles(
|
||||
"int main() {
|
||||
#if !defined(HWY_DISABLED_TARGETS)
|
||||
static_assert(false, \"HWY_DISABLED_TARGETS is not defined\");
|
||||
#endif
|
||||
return 0;
|
||||
}"
|
||||
JXL_HWY_DISABLED_TARGETS_FORCED
|
||||
)
|
||||
|
||||
message(STATUS "CMAKE_SYSTEM_PROCESSOR is ${CMAKE_SYSTEM_PROCESSOR}")
|
||||
include(CheckCXXCompilerFlag)
|
||||
check_cxx_compiler_flag("-fsanitize=fuzzer-no-link" CXX_FUZZERS_SUPPORTED)
|
||||
|
|
@ -51,12 +61,12 @@ if(CHECK_PIE_SUPPORTED)
|
|||
endif()
|
||||
|
||||
### Project build options:
|
||||
if(${CXX_FUZZERS_SUPPORTED})
|
||||
if(CXX_FUZZERS_SUPPORTED)
|
||||
# Enabled by default except on arm64, Windows and Apple builds.
|
||||
set(ENABLE_FUZZERS_DEFAULT true)
|
||||
endif()
|
||||
find_package(PkgConfig)
|
||||
if(NOT APPLE AND NOT WIN32 AND NOT HAIKU AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64")
|
||||
if(NOT APPLE AND NOT WIN32 AND NOT HAIKU AND CMAKE_SYSTEM_PROCESSOR MATCHES "x86_64")
|
||||
pkg_check_modules(TCMallocMinimalVersionCheck QUIET IMPORTED_TARGET
|
||||
libtcmalloc_minimal)
|
||||
if(TCMallocMinimalVersionCheck_FOUND AND
|
||||
|
|
@ -76,14 +86,13 @@ set(WARNINGS_AS_ERRORS_DEFAULT false)
|
|||
|
||||
if((SANITIZER STREQUAL "msan") OR JPEGXL_EMSCRIPTEN)
|
||||
set(BUNDLE_LIBPNG_DEFAULT YES)
|
||||
set(BUNDLE_GFLAGS_DEFAULT YES)
|
||||
else()
|
||||
set(BUNDLE_LIBPNG_DEFAULT NO)
|
||||
set(BUNDLE_GFLAGS_DEFAULT NO)
|
||||
endif()
|
||||
|
||||
# Standard cmake naming for building shared libraries.
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries instead of static ones" ON)
|
||||
get_property(SHARED_LIBS_SUPPORTED GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries instead of static ones" ${SHARED_LIBS_SUPPORTED})
|
||||
|
||||
set(JPEGXL_ENABLE_FUZZERS ${ENABLE_FUZZERS_DEFAULT} CACHE BOOL
|
||||
"Build JPEGXL fuzzer targets.")
|
||||
|
|
@ -99,8 +108,6 @@ set(JPEGXL_ENABLE_BENCHMARK true CACHE BOOL
|
|||
"Build JPEGXL benchmark tools.")
|
||||
set(JPEGXL_ENABLE_EXAMPLES true CACHE BOOL
|
||||
"Build JPEGXL library usage examples.")
|
||||
set(JPEGXL_BUNDLE_GFLAGS ${BUNDLE_GFLAGS_DEFAULT} CACHE BOOL
|
||||
"Build gflags from source and link it statically.")
|
||||
set(JPEGXL_BUNDLE_LIBPNG ${BUNDLE_LIBPNG_DEFAULT} CACHE BOOL
|
||||
"Build libpng from source and link it statically.")
|
||||
set(JPEGXL_ENABLE_JNI true CACHE BOOL
|
||||
|
|
@ -149,20 +156,20 @@ set(JPEGXL_FORCE_SYSTEM_HWY false CACHE BOOL
|
|||
|
||||
# Check minimum compiler versions. Older compilers are not supported and fail
|
||||
# with hard to understand errors.
|
||||
if (NOT ${CMAKE_C_COMPILER_ID} STREQUAL ${CMAKE_CXX_COMPILER_ID})
|
||||
if (NOT CMAKE_C_COMPILER_ID STREQUAL CMAKE_CXX_COMPILER_ID)
|
||||
message(FATAL_ERROR "Different C/C++ compilers set: "
|
||||
"${CMAKE_C_COMPILER_ID} vs ${CMAKE_CXX_COMPILER_ID}")
|
||||
endif()
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# Android NDK's toolchain.cmake fakes the clang version in
|
||||
# CMAKE_CXX_COMPILER_VERSION with an incorrect number, so ignore this.
|
||||
if (NOT ${CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION} MATCHES "clang"
|
||||
AND ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 6)
|
||||
if (NOT CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION MATCHES "clang"
|
||||
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
|
||||
message(FATAL_ERROR
|
||||
"Minimum Clang version required is Clang 6, please update.")
|
||||
"Minimum Clang version required is Clang 5, please update.")
|
||||
endif()
|
||||
elseif (${CMAKE_CXX_COMPILER_ID} MATCHES "GNU")
|
||||
if (${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 7)
|
||||
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU")
|
||||
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 7)
|
||||
message(FATAL_ERROR
|
||||
"Minimum GCC version required is 7, please update.")
|
||||
endif()
|
||||
|
|
@ -234,7 +241,13 @@ if(JPEGXL_STATIC)
|
|||
endif()
|
||||
endif() # JPEGXL_STATIC
|
||||
|
||||
if ("${CXX_MACRO_PREFIX_MAP}")
|
||||
if (JPEGXL_EMSCRIPTEN)
|
||||
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -pthread")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pthread")
|
||||
endif()
|
||||
|
||||
if (CXX_MACRO_PREFIX_MAP)
|
||||
add_compile_options(-fmacro-prefix-map=${CMAKE_CURRENT_SOURCE_DIR}=.)
|
||||
endif()
|
||||
|
||||
|
|
@ -254,6 +267,12 @@ add_definitions(
|
|||
-D__TIME__="redacted"
|
||||
)
|
||||
|
||||
# TODO(eustas): see https://github.com/google/highway/issues/836
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND NOT JXL_HWY_DISABLED_TARGETS_FORCED)
|
||||
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()
|
||||
|
||||
# Avoid log spam from fopen etc.
|
||||
if(MSVC)
|
||||
add_definitions(-D_CRT_SECURE_NO_WARNINGS)
|
||||
|
|
@ -265,10 +284,10 @@ endif()
|
|||
|
||||
# Machine flags.
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -funwind-tables")
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -mrelax-all")
|
||||
endif()
|
||||
if ("${CXX_CONSTRUCTOR_ALIASES_SUPPORTED}")
|
||||
if (CXX_CONSTRUCTOR_ALIASES_SUPPORTED)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Xclang -mconstructor-aliases")
|
||||
endif()
|
||||
|
||||
|
|
@ -281,7 +300,7 @@ endif()
|
|||
# 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(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} \
|
||||
|
|
@ -307,8 +326,10 @@ endif () # !MSVC
|
|||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
# Separately build/configure testing frameworks and other third_party libraries
|
||||
# to allow disabling tests in those libraries.
|
||||
include(third_party/testing.cmake)
|
||||
add_subdirectory(third_party)
|
||||
|
||||
# Copy the JXL license file to the output build directory.
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/LICENSE"
|
||||
${PROJECT_BINARY_DIR}/LICENSE.jpeg-xl COPYONLY)
|
||||
|
|
@ -318,7 +339,7 @@ enable_testing()
|
|||
include(CTest)
|
||||
# Specify default location of `testdata`:
|
||||
if(NOT DEFINED JPEGXL_TEST_DATA_PATH)
|
||||
set(JPEGXL_TEST_DATA_PATH "${PROJECT_SOURCE_DIR}/third_party/testdata")
|
||||
set(JPEGXL_TEST_DATA_PATH "${PROJECT_SOURCE_DIR}/testdata")
|
||||
endif()
|
||||
|
||||
# Libraries.
|
||||
|
|
@ -382,9 +403,9 @@ endif() # JPEGXL_ENABLE_DOXYGEN
|
|||
|
||||
if(JPEGXL_ENABLE_MANPAGES)
|
||||
find_program(ASCIIDOC a2x)
|
||||
if(NOT "${ASCIIDOC}" STREQUAL "ASCIIDOC-NOTFOUND")
|
||||
if(ASCIIDOC)
|
||||
file(STRINGS "${ASCIIDOC}" ASCIIDOC_SHEBANG LIMIT_COUNT 1)
|
||||
if(ASCIIDOC_SHEBANG MATCHES "/sh")
|
||||
if(ASCIIDOC_SHEBANG MATCHES "/sh|/bash")
|
||||
set(ASCIIDOC_PY_FOUND ON)
|
||||
# Run the program directly and set ASCIIDOC as empty.
|
||||
set(ASCIIDOC_PY "${ASCIIDOC}")
|
||||
|
|
@ -401,7 +422,7 @@ else()
|
|||
find_package(Python COMPONENTS Interpreter QUIET)
|
||||
if(NOT Python_Interpreter_FOUND)
|
||||
find_program(ASCIIDOC_PY python)
|
||||
if(NOT ASCIIDOC_PY STREQUAL "ASCIIDOC_PY-NOTFOUND")
|
||||
if(ASCIIDOC_PY)
|
||||
set(ASCIIDOC_PY_FOUND ON)
|
||||
endif()
|
||||
else()
|
||||
|
|
@ -432,16 +453,16 @@ if (ASCIIDOC_PY_FOUND)
|
|||
endif() # ASCIIDOC_PY_FOUND
|
||||
else()
|
||||
message(WARNING "asciidoc was not found, the man pages will not be installed.")
|
||||
endif() # ASCIIDOC != "ASCIIDOC-NOTFOUND"
|
||||
endif() # ASCIIDOC
|
||||
endif() # JPEGXL_ENABLE_MANPAGES
|
||||
|
||||
# Example usage code.
|
||||
if (${JPEGXL_ENABLE_EXAMPLES})
|
||||
if (JPEGXL_ENABLE_EXAMPLES)
|
||||
include(examples/examples.cmake)
|
||||
endif ()
|
||||
|
||||
# Plugins for third-party software
|
||||
if (${JPEGXL_ENABLE_PLUGINS})
|
||||
if (JPEGXL_ENABLE_PLUGINS)
|
||||
add_subdirectory(plugins)
|
||||
endif ()
|
||||
|
||||
|
|
|
|||
12
third_party/jpeg-xl/README.md
vendored
12
third_party/jpeg-xl/README.md
vendored
|
|
@ -1,7 +1,13 @@
|
|||
# JPEG XL reference implementation
|
||||
|
||||
[](
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/build_test.yml)
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/build_test_cross.yml)
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/conformance.yml)
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/fuzz.yml)
|
||||
[](
|
||||
https://github.com/libjxl/libjxl/actions/workflows/release.yaml)
|
||||
[](
|
||||
|
|
@ -63,7 +69,7 @@ Required dependencies for compiling the code, in a Debian/Ubuntu based
|
|||
distribution run:
|
||||
|
||||
```bash
|
||||
sudo apt install cmake pkg-config libbrotli-dev libgflags-dev
|
||||
sudo apt install cmake pkg-config libbrotli-dev
|
||||
```
|
||||
|
||||
Optional dependencies for supporting other formats in the `cjxl`/`djxl` tools,
|
||||
|
|
@ -168,7 +174,7 @@ format: Cloudinary and Google.
|
|||
* [JPEG XL Format Overview](doc/format_overview.md)
|
||||
* [Introductory paper](https://www.spiedigitallibrary.org/proceedings/Download?fullDOI=10.1117%2F12.2529237) (open-access)
|
||||
* [XL Overview](doc/xl_overview.md) - a brief introduction to the source code modules
|
||||
* [JPEG XL white paper](http://ds.jpeg.org/whitepapers/jpeg-xl-whitepaper.pdf)
|
||||
* [JPEG XL white paper](https://ds.jpeg.org/whitepapers/jpeg-xl-whitepaper.pdf)
|
||||
* [JPEG XL official website](https://jpeg.org/jpegxl)
|
||||
* [JPEG XL community website](https://jpegxl.info)
|
||||
|
||||
|
|
|
|||
8
third_party/jpeg-xl/bash_test.sh
vendored
8
third_party/jpeg-xl/bash_test.sh
vendored
|
|
@ -103,6 +103,12 @@ test_printf_size_t() {
|
|||
ret=1
|
||||
fi
|
||||
|
||||
if grep -n -E 'gmock\.h' \
|
||||
$(git ls-files | grep -E '(\.c|\.cc|\.cpp|\.h)$' | grep -v -F /test_utils.h); then
|
||||
echo "Don't include gmock directly, instead include 'test_utils.h'. " >&2
|
||||
ret=1
|
||||
fi
|
||||
|
||||
local f
|
||||
for f in $(git ls-files | grep -E "\.cc$" | xargs grep 'PRI[udx]S' |
|
||||
cut -f 1 -d : | uniq); do
|
||||
|
|
@ -116,7 +122,7 @@ test_printf_size_t() {
|
|||
fi
|
||||
done
|
||||
|
||||
for f in $(git ls-files | grep -E "\.h$" | grep -v -F printf_macros.h |
|
||||
for f in $(git ls-files | grep -E "\.h$" | grep -v -E '(printf_macros\.h|test_utils\.h)' |
|
||||
xargs grep -n 'PRI[udx]S'); do
|
||||
# Having PRIuS / PRIdS in a header file means that printf_macros.h may
|
||||
# be included before a system header, in particular before gtest headers.
|
||||
|
|
|
|||
91
third_party/jpeg-xl/ci.sh
vendored
91
third_party/jpeg-xl/ci.sh
vendored
|
|
@ -15,13 +15,14 @@ OS=`uname -s`
|
|||
MYDIR=$(dirname $(realpath "$0"))
|
||||
|
||||
### Environment parameters:
|
||||
TEST_STACK_LIMIT="${TEST_STACK_LIMIT:-128}"
|
||||
TEST_STACK_LIMIT="${TEST_STACK_LIMIT:-256}"
|
||||
CMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE:-RelWithDebInfo}
|
||||
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_TEST="${SKIP_TEST:-0}"
|
||||
TEST_SELECTOR="${TEST_SELECTOR:-}"
|
||||
BUILD_TARGET="${BUILD_TARGET:-}"
|
||||
ENABLE_WASM_SIMD="${ENABLE_WASM_SIMD:-0}"
|
||||
if [[ -n "${BUILD_TARGET}" ]]; then
|
||||
|
|
@ -41,27 +42,11 @@ FUZZER_MAX_TIME="${FUZZER_MAX_TIME:-0}"
|
|||
|
||||
SANITIZER="none"
|
||||
|
||||
if [[ "${BUILD_TARGET}" == wasm* ]]; then
|
||||
# Check that environment is setup for the WASM build target.
|
||||
if [[ -z "${EMSCRIPTEN}" ]]; then
|
||||
echo "'EMSCRIPTEN' is not defined. Use 'emconfigure' wrapper to setup WASM build environment" >&2
|
||||
return 1
|
||||
fi
|
||||
# Remove the side-effect of "emconfigure" wrapper - it considers NodeJS environment.
|
||||
unset EMMAKEN_JUST_CONFIGURE
|
||||
EMS_TOOLCHAIN_FILE="${EMSCRIPTEN}/cmake/Modules/Platform/Emscripten.cmake"
|
||||
if [[ -f "${EMS_TOOLCHAIN_FILE}" ]]; then
|
||||
CMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE:-${EMS_TOOLCHAIN_FILE}}
|
||||
else
|
||||
echo "Warning: EMSCRIPTEN CMake module not found" >&2
|
||||
fi
|
||||
CMAKE_CROSSCOMPILING_EMULATOR="${MYDIR}/js-wasm-wrapper.sh"
|
||||
fi
|
||||
|
||||
if [[ "${BUILD_TARGET%%-*}" == "x86_64" ||
|
||||
"${BUILD_TARGET%%-*}" == "i686" ]]; then
|
||||
# Default to building all targets, even if compiler baseline is SSE4
|
||||
HWY_BASELINE_TARGETS=${HWY_BASELINE_TARGETS:-HWY_SCALAR}
|
||||
HWY_BASELINE_TARGETS=${HWY_BASELINE_TARGETS:-HWY_EMU128}
|
||||
else
|
||||
HWY_BASELINE_TARGETS=${HWY_BASELINE_TARGETS:-}
|
||||
fi
|
||||
|
|
@ -350,7 +335,6 @@ cmake_configure() {
|
|||
-G Ninja
|
||||
-DCMAKE_CXX_FLAGS="${CMAKE_CXX_FLAGS}"
|
||||
-DCMAKE_C_FLAGS="${CMAKE_C_FLAGS}"
|
||||
-DCMAKE_TOOLCHAIN_FILE="${CMAKE_TOOLCHAIN_FILE}"
|
||||
-DCMAKE_EXE_LINKER_FLAGS="${CMAKE_EXE_LINKER_FLAGS}"
|
||||
-DCMAKE_MODULE_LINKER_FLAGS="${CMAKE_MODULE_LINKER_FLAGS}"
|
||||
-DCMAKE_SHARED_LINKER_FLAGS="${CMAKE_SHARED_LINKER_FLAGS}"
|
||||
|
|
@ -392,11 +376,14 @@ cmake_configure() {
|
|||
# Only the first element of the target triplet.
|
||||
-DCMAKE_SYSTEM_PROCESSOR="${BUILD_TARGET%%-*}"
|
||||
-DCMAKE_SYSTEM_NAME="${system_name}"
|
||||
-DCMAKE_TOOLCHAIN_FILE="${CMAKE_TOOLCHAIN_FILE}"
|
||||
)
|
||||
else
|
||||
# sjpeg confuses WASM SIMD with SSE.
|
||||
args+=(
|
||||
# sjpeg confuses WASM SIMD with SSE.
|
||||
-DSJPEG_ENABLE_SIMD=OFF
|
||||
# Building shared libs is not very useful for WASM.
|
||||
-DBUILD_SHARED_LIBS=OFF
|
||||
)
|
||||
fi
|
||||
args+=(
|
||||
|
|
@ -457,7 +444,11 @@ cmake_configure() {
|
|||
-DCMAKE_MAKE_PROGRAM="${CMAKE_MAKE_PROGRAM}"
|
||||
)
|
||||
fi
|
||||
cmake "${args[@]}" "$@"
|
||||
if [[ "${BUILD_TARGET}" == wasm* ]]; then
|
||||
emcmake cmake "${args[@]}" "$@"
|
||||
else
|
||||
cmake "${args[@]}" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
cmake_build_and_test() {
|
||||
|
|
@ -485,7 +476,7 @@ cmake_build_and_test() {
|
|||
(cd "${BUILD_DIR}"
|
||||
export UBSAN_OPTIONS=print_stacktrace=1
|
||||
[[ "${TEST_STACK_LIMIT}" == "none" ]] || ulimit -s "${TEST_STACK_LIMIT}"
|
||||
ctest -j $(nproc --all || echo 1) --output-on-failure)
|
||||
ctest -j $(nproc --all || echo 1) ${TEST_SELECTOR} --output-on-failure)
|
||||
fi
|
||||
}
|
||||
|
||||
|
|
@ -494,7 +485,7 @@ cmake_build_and_test() {
|
|||
# library.
|
||||
strip_dead_code() {
|
||||
# Emscripten does tree shaking without any extra flags.
|
||||
if [[ "${CMAKE_TOOLCHAIN_FILE##*/}" == "Emscripten.cmake" ]]; then
|
||||
if [[ "${BUILD_TARGET}" == wasm* ]]; then
|
||||
return 0
|
||||
fi
|
||||
# -ffunction-sections, -fdata-sections and -Wl,--gc-sections effectively
|
||||
|
|
@ -711,20 +702,24 @@ cmd_msan_install() {
|
|||
export CC="${CC:-clang}"
|
||||
export CXX="${CXX:-clang++}"
|
||||
detect_clang_version
|
||||
local llvm_tag="llvmorg-${CLANG_VERSION}.0.0"
|
||||
case "${CLANG_VERSION}" in
|
||||
"6.0")
|
||||
llvm_tag="llvmorg-6.0.1"
|
||||
;;
|
||||
"7")
|
||||
llvm_tag="llvmorg-7.0.1"
|
||||
;;
|
||||
esac
|
||||
local llvm_targz="${tmpdir}/${llvm_tag}.tar.gz"
|
||||
curl -L --show-error -o "${llvm_targz}" \
|
||||
"https://github.com/llvm/llvm-project/archive/${llvm_tag}.tar.gz"
|
||||
tar -C "${tmpdir}" -zxf "${llvm_targz}"
|
||||
local llvm_root="${tmpdir}/llvm-project-${llvm_tag}"
|
||||
# Allow overriding the LLVM checkout.
|
||||
local llvm_root="${LLVM_ROOT}"
|
||||
if [ -z "${llvm_root}" ]; then
|
||||
local llvm_tag="llvmorg-${CLANG_VERSION}.0.0"
|
||||
case "${CLANG_VERSION}" in
|
||||
"6.0")
|
||||
llvm_tag="llvmorg-6.0.1"
|
||||
;;
|
||||
"7")
|
||||
llvm_tag="llvmorg-7.0.1"
|
||||
;;
|
||||
esac
|
||||
local llvm_targz="${tmpdir}/${llvm_tag}.tar.gz"
|
||||
curl -L --show-error -o "${llvm_targz}" \
|
||||
"https://github.com/llvm/llvm-project/archive/${llvm_tag}.tar.gz"
|
||||
tar -C "${tmpdir}" -zxf "${llvm_targz}"
|
||||
llvm_root="${tmpdir}/llvm-project-${llvm_tag}"
|
||||
fi
|
||||
|
||||
local msan_prefix="${HOME}/.msan/${CLANG_VERSION}"
|
||||
rm -rf "${msan_prefix}"
|
||||
|
|
@ -1020,11 +1015,11 @@ cmd_arm_benchmark() {
|
|||
)
|
||||
|
||||
local images=(
|
||||
"third_party/testdata/third_party/imagecompression.info/flower_foveon.png"
|
||||
"testdata/jxl/flower/flower.png"
|
||||
)
|
||||
|
||||
local jpg_images=(
|
||||
"third_party/testdata/third_party/imagecompression.info/flower_foveon.png.im_q85_420.jpg"
|
||||
"testdata/jxl/flower/flower.png.im_q85_420.jpg"
|
||||
)
|
||||
|
||||
if [[ "${SKIP_CPUSET:-}" == "1" ]]; then
|
||||
|
|
@ -1219,10 +1214,10 @@ cmd_lint() {
|
|||
# We include in this linter all the changes including the uncommitted changes
|
||||
# to avoid printing changes already applied.
|
||||
set -x
|
||||
# Ignoring the error that git-clang-format outputs.
|
||||
git -C "${MYDIR}" "${clang_format}" --binary "${clang_format}" \
|
||||
--style=file --diff "${MR_ANCESTOR_SHA}" -- >"${tmppatch}"
|
||||
--style=file --diff "${MR_ANCESTOR_SHA}" -- >"${tmppatch}" || true
|
||||
{ set +x; } 2>/dev/null
|
||||
|
||||
if grep -E '^--- ' "${tmppatch}">/dev/null; then
|
||||
if [[ -n "${LINT_OUTPUT:-}" ]]; then
|
||||
cp "${tmppatch}" "${LINT_OUTPUT}"
|
||||
|
|
@ -1423,11 +1418,14 @@ cmd_bump_version() {
|
|||
# Check that the AUTHORS file contains the email of the committer.
|
||||
cmd_authors() {
|
||||
merge_request_commits
|
||||
# TODO(deymo): Handle multiple commits and check that they are all the same
|
||||
# author.
|
||||
local email=$(git log --format='%ae' "${MR_HEAD_SHA}^!")
|
||||
local name=$(git log --format='%an' "${MR_HEAD_SHA}^!")
|
||||
"${MYDIR}"/tools/check_author.py "${email}" "${name}"
|
||||
local emails
|
||||
local names
|
||||
readarray -t emails < <(git log --format='%ae' "${MR_HEAD_SHA}...${MR_ANCESTOR_SHA}")
|
||||
readarray -t names < <(git log --format='%an' "${MR_HEAD_SHA}...${MR_ANCESTOR_SHA}")
|
||||
for i in "${!names[@]}"; do
|
||||
echo "Checking name '${names[$i]}' with email '${emails[$i]}' ..."
|
||||
"${MYDIR}"/tools/check_author.py "${emails[$i]}" "${names[$i]}"
|
||||
done
|
||||
}
|
||||
|
||||
main() {
|
||||
|
|
@ -1490,6 +1488,7 @@ You can pass some optional environment variables as well:
|
|||
- 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.
|
||||
- TEST_SELECTOR: pass additional arguments to ctest, e.g. "-R .Resample.".
|
||||
- STACK_SIZE=1: Generate binaries with the .stack_sizes sections.
|
||||
|
||||
These optional environment variables are forwarded to the cmake call as
|
||||
|
|
|
|||
2
third_party/jpeg-xl/cmake/FindBrotli.cmake
vendored
2
third_party/jpeg-xl/cmake/FindBrotli.cmake
vendored
|
|
@ -26,7 +26,7 @@ foreach(brlib IN ITEMS ${brlibs})
|
|||
)
|
||||
|
||||
if (${BRPREFIX}_LIBRARY AND NOT TARGET ${brlib})
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.13.5")
|
||||
if(CMAKE_VERSION VERSION_LESS "3.13.5")
|
||||
add_library(${brlib} INTERFACE IMPORTED GLOBAL)
|
||||
set_property(TARGET ${brlib} PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${BROTLI_INCLUDE_DIR})
|
||||
target_link_libraries(${brlib} INTERFACE ${${BRPREFIX}_LIBRARY})
|
||||
|
|
|
|||
2
third_party/jpeg-xl/cmake/FindHWY.cmake
vendored
2
third_party/jpeg-xl/cmake/FindHWY.cmake
vendored
|
|
@ -46,7 +46,7 @@ find_package_handle_standard_args(HWY
|
|||
if (HWY_LIBRARY AND NOT TARGET hwy)
|
||||
add_library(hwy INTERFACE IMPORTED GLOBAL)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.13.5")
|
||||
if(CMAKE_VERSION VERSION_LESS "3.13.5")
|
||||
set_property(TARGET hwy PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${HWY_INCLUDE_DIR})
|
||||
target_link_libraries(hwy INTERFACE ${HWY_LIBRARY})
|
||||
set_property(TARGET hwy PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_HWY_CFLAGS_OTHER})
|
||||
|
|
|
|||
2
third_party/jpeg-xl/cmake/FindLCMS2.cmake
vendored
2
third_party/jpeg-xl/cmake/FindLCMS2.cmake
vendored
|
|
@ -39,7 +39,7 @@ find_package_handle_standard_args(LCMS2
|
|||
if (LCMS2_LIBRARY AND NOT TARGET lcms2)
|
||||
add_library(lcms2 INTERFACE IMPORTED GLOBAL)
|
||||
|
||||
if(${CMAKE_VERSION} VERSION_LESS "3.13.5")
|
||||
if(CMAKE_VERSION VERSION_LESS "3.13.5")
|
||||
set_property(TARGET lcms2 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${LCMS2_INCLUDE_DIR})
|
||||
target_link_libraries(lcms2 INTERFACE ${LCMS2_LIBRARY})
|
||||
set_property(TARGET lcms2 PROPERTY INTERFACE_COMPILE_OPTIONS ${PC_LCMS2_CFLAGS_OTHER})
|
||||
|
|
|
|||
1
third_party/jpeg-xl/debian/control
vendored
1
third_party/jpeg-xl/debian/control
vendored
|
|
@ -9,7 +9,6 @@ Build-Depends:
|
|||
debhelper (>= 9),
|
||||
libbrotli-dev,
|
||||
libgdk-pixbuf-2.0-dev | libgdk-pixbuf2.0-dev,
|
||||
libgflags-dev | libgflag-dev,
|
||||
libgif-dev,
|
||||
libgimp2.0-dev,
|
||||
libgmock-dev,
|
||||
|
|
|
|||
28
third_party/jpeg-xl/debian/copyright
vendored
28
third_party/jpeg-xl/debian/copyright
vendored
|
|
@ -38,27 +38,7 @@ License: BSD-3-clause
|
|||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Files: third_party/testdata/third_party/imagecompression.info/*
|
||||
Copyright: their respective owners.
|
||||
License: License without any prohibitive copyright restrictions.
|
||||
See https://imagecompression.info/test_images/ for details.
|
||||
.
|
||||
These Images are available without any prohibitive copyright restrictions.
|
||||
.
|
||||
These images are (c) there respective owners. You are granted full
|
||||
redistribution and publication rights on these images provided:
|
||||
.
|
||||
1. The origin of the pictures must not be misrepresented; you must not claim
|
||||
that you took the original pictures. If you use, publish or redistribute them,
|
||||
an acknowledgment would be appreciated but is not required.
|
||||
2. Altered versions must be plainly marked as such, and must not be
|
||||
misinterpreted as being the originals.
|
||||
3. No payment is required for distribution of this material, it must be
|
||||
available freely under the conditions stated here. That is, it is prohibited to
|
||||
sell the material.
|
||||
4. This notice may not be removed or altered from any distribution.
|
||||
|
||||
Files: third_party/testdata/third_party/pngsuite/*
|
||||
Files: testdata/external/pngsuite/*
|
||||
Copyright: Willem van Schaik, 1996, 2011
|
||||
License: PngSuite License
|
||||
See http://www.schaik.com/pngsuite/ for details.
|
||||
|
|
@ -66,15 +46,15 @@ License: PngSuite License
|
|||
Permission to use, copy, modify and distribute these images for any
|
||||
purpose and without fee is hereby granted.
|
||||
|
||||
Files: third_party/testdata/raw.pixls/*
|
||||
Files: testdata/external/raw.pixls/*
|
||||
Copyright: their respective owners listed in https://raw.pixls.us/
|
||||
License: CC0-1.0
|
||||
|
||||
Files: third_party/testdata/raw.pixls/*
|
||||
Files: testdata/external/wesaturate/*
|
||||
Copyright: their respective owners listed in https://www.wesaturate.com/
|
||||
License: CC0-1.0
|
||||
|
||||
Files: third_party/testdata/third_party/wide-gamut-tests/
|
||||
Files: testdata/external/wide-gamut-tests/
|
||||
Copyright: github.com/codelogic/wide-gamut-tests authors.
|
||||
License: Apache-2.0
|
||||
|
||||
|
|
|
|||
4
third_party/jpeg-xl/deps.sh
vendored
4
third_party/jpeg-xl/deps.sh
vendored
|
|
@ -14,8 +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="35ef5c554d888bef217d449346067de05e269b30"
|
||||
THIRD_PARTY_GFLAGS="827c769e5fc98e0f2a34c47cef953cc6328abced"
|
||||
THIRD_PARTY_HIGHWAY="f13e3b956eb226561ac79427893ec0afd66f91a8"
|
||||
THIRD_PARTY_HIGHWAY="e41973e26b3a870fcfb63fa2d4f2bcd57fe94de5"
|
||||
THIRD_PARTY_SKCMS="64374756e03700d649f897dbd98c95e78c30c7da"
|
||||
THIRD_PARTY_SJPEG="868ab558fad70fcbe8863ba4e85179eeb81cc840"
|
||||
THIRD_PARTY_ZLIB="cacf7f1d4e3d44d871b605da3b647f07d718623f"
|
||||
|
|
@ -73,7 +72,6 @@ EOF
|
|||
|
||||
# Sources downloaded from a tarball.
|
||||
download_github third_party/brotli google/brotli
|
||||
download_github third_party/gflags gflags/gflags
|
||||
download_github third_party/highway google/highway
|
||||
download_github third_party/sjpeg webmproject/sjpeg
|
||||
download_github third_party/skcms \
|
||||
|
|
|
|||
19
third_party/jpeg-xl/js-wasm-wrapper.sh
vendored
19
third_party/jpeg-xl/js-wasm-wrapper.sh
vendored
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# 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.
|
||||
|
||||
# Continuous integration helper module. This module is meant to be called from
|
||||
# the .gitlab-ci.yml file during the continuous integration build, as well as
|
||||
# from the command line for developers.
|
||||
|
||||
# This wrapper is used to enable WASM SIMD when running tests.
|
||||
# Unfortunately, it is impossible to pass the option directly via the
|
||||
# CMAKE_CROSSCOMPILING_EMULATOR variable.
|
||||
|
||||
# Fallback to default v8 binary, if override is not set.
|
||||
V8="${V8:-$(which v8)}"
|
||||
SCRIPT="$1"
|
||||
shift
|
||||
"${V8}" --experimental-wasm-simd "${SCRIPT}" -- "$@"
|
||||
10
third_party/jpeg-xl/lib/CMakeLists.txt
vendored
10
third_party/jpeg-xl/lib/CMakeLists.txt
vendored
|
|
@ -52,7 +52,7 @@ set(JPEGXL_INTERNAL_FLAGS
|
|||
)
|
||||
|
||||
# Warning flags supported by clang.
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES "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.
|
||||
|
|
@ -90,7 +90,7 @@ if (WIN32)
|
|||
-Wno-zero-as-null-pointer-constant
|
||||
)
|
||||
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS
|
||||
-Wno-used-but-marked-unused
|
||||
-Wno-unused-template
|
||||
|
|
@ -110,7 +110,7 @@ else() # WIN32
|
|||
-fmath-errno
|
||||
)
|
||||
|
||||
if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
|
||||
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS
|
||||
-fnew-alignment=8
|
||||
-fno-cxx-exceptions
|
||||
|
|
@ -136,7 +136,9 @@ endif() #!MSVC
|
|||
include(jxl.cmake)
|
||||
|
||||
# Other libraries outside the core jxl library.
|
||||
include(jxl_extras.cmake)
|
||||
if(JPEGXL_ENABLE_TOOLS)
|
||||
include(jxl_extras.cmake)
|
||||
endif()
|
||||
include(jxl_threads.cmake)
|
||||
|
||||
# Install all the library headers from the source and the generated ones. There
|
||||
|
|
|
|||
28
third_party/jpeg-xl/lib/extras/codec.cc
vendored
28
third_party/jpeg-xl/lib/extras/codec.cc
vendored
|
|
@ -52,7 +52,7 @@ 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) {
|
||||
PaddedBytes encoded;
|
||||
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));
|
||||
|
|
@ -61,7 +61,7 @@ Status SetFromFile(const std::string& pathname,
|
|||
|
||||
Status Encode(const CodecInOut& io, const extras::Codec codec,
|
||||
const ColorEncoding& c_desired, size_t bits_per_sample,
|
||||
PaddedBytes* bytes, ThreadPool* pool) {
|
||||
std::vector<uint8_t>* bytes, ThreadPool* pool) {
|
||||
JXL_CHECK(!io.Main().c_current().ICC().empty());
|
||||
JXL_CHECK(!c_desired.ICC().empty());
|
||||
io.CheckMetadata();
|
||||
|
|
@ -70,18 +70,24 @@ Status Encode(const CodecInOut& io, const extras::Codec codec,
|
|||
}
|
||||
|
||||
extras::PackedPixelFile ppf;
|
||||
size_t num_channels = io.metadata.m.color_encoding.Channels();
|
||||
JxlPixelFormat format = {
|
||||
static_cast<uint32_t>(num_channels),
|
||||
0, // num_channels is ignored by the converter
|
||||
bits_per_sample <= 8 ? JXL_TYPE_UINT8 : JXL_TYPE_UINT16,
|
||||
JXL_NATIVE_ENDIAN, 0};
|
||||
std::vector<uint8_t> bytes_vector;
|
||||
const bool floating_point = bits_per_sample > 16;
|
||||
extras::EncodedImage encoded_image;
|
||||
switch (codec) {
|
||||
case extras::Codec::kPNG:
|
||||
#if JPEGXL_ENABLE_APNG
|
||||
return extras::EncodeImageAPNG(&io, c_desired, bits_per_sample, pool,
|
||||
bytes);
|
||||
format.endianness = JXL_BIG_ENDIAN;
|
||||
JXL_RETURN_IF_ERROR(extras::ConvertCodecInOutToPackedPixelFile(
|
||||
io, format, c_desired, pool, &ppf));
|
||||
JXL_RETURN_IF_ERROR(
|
||||
extras::GetAPNGEncoder()->Encode(ppf, &encoded_image, pool));
|
||||
JXL_ASSERT(encoded_image.bitstreams.size() == 1);
|
||||
*bytes = encoded_image.bitstreams[0];
|
||||
return true;
|
||||
#else
|
||||
return JXL_FAILURE("JPEG XL was built without (A)PNG support");
|
||||
#endif
|
||||
|
|
@ -109,14 +115,14 @@ Status Encode(const CodecInOut& io, const extras::Codec codec,
|
|||
}
|
||||
JXL_RETURN_IF_ERROR(extras::ConvertCodecInOutToPackedPixelFile(
|
||||
io, format, c_desired, pool, &ppf));
|
||||
JXL_RETURN_IF_ERROR(extras::EncodeImagePNM(
|
||||
ppf, bits_per_sample, pool, /*frame_index=*/0, &bytes_vector));
|
||||
JXL_RETURN_IF_ERROR(
|
||||
extras::EncodeImagePNM(ppf.frames[0].color, ppf.info.orientation,
|
||||
bits_per_sample, &bytes_vector));
|
||||
bytes->assign(bytes_vector.data(),
|
||||
bytes_vector.data() + bytes_vector.size());
|
||||
return true;
|
||||
case extras::Codec::kPGX:
|
||||
return extras::EncodeImagePGX(&io, c_desired, bits_per_sample, pool,
|
||||
bytes);
|
||||
return JXL_FAILURE("Encoding CodecInOut to PGX is not implemented");
|
||||
case extras::Codec::kGIF:
|
||||
return JXL_FAILURE("Encoding to GIF is not implemented");
|
||||
case extras::Codec::kEXR:
|
||||
|
|
@ -163,7 +169,7 @@ Status EncodeToFile(const CodecInOut& io, const ColorEncoding& c_desired,
|
|||
bits_per_sample = 16;
|
||||
}
|
||||
|
||||
PaddedBytes encoded;
|
||||
std::vector<uint8_t> encoded;
|
||||
return Encode(io, codec, c_desired, bits_per_sample, &encoded, pool) &&
|
||||
WriteFile(encoded, pathname);
|
||||
}
|
||||
|
|
|
|||
2
third_party/jpeg-xl/lib/extras/codec.h
vendored
2
third_party/jpeg-xl/lib/extras/codec.h
vendored
|
|
@ -49,7 +49,7 @@ Status SetFromFile(const std::string& pathname,
|
|||
// color space to c_desired.
|
||||
Status Encode(const CodecInOut& io, extras::Codec codec,
|
||||
const ColorEncoding& c_desired, size_t bits_per_sample,
|
||||
PaddedBytes* bytes, ThreadPool* pool = nullptr);
|
||||
std::vector<uint8_t>* bytes, ThreadPool* pool = nullptr);
|
||||
|
||||
// Deduces codec, calls Encode and writes to file.
|
||||
Status EncodeToFile(const CodecInOut& io, const ColorEncoding& c_desired,
|
||||
|
|
|
|||
421
third_party/jpeg-xl/lib/extras/codec_test.cc
vendored
421
third_party/jpeg-xl/lib/extras/codec_test.cc
vendored
|
|
@ -9,12 +9,15 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "lib/extras/dec/pgx.h"
|
||||
#include "lib/extras/dec/pnm.h"
|
||||
#include "lib/extras/enc/encode.h"
|
||||
#include "lib/extras/packed_image_convert.h"
|
||||
#include "lib/jxl/base/printf_macros.h"
|
||||
#include "lib/jxl/base/random.h"
|
||||
#include "lib/jxl/base/thread_pool_internal.h"
|
||||
|
|
@ -23,12 +26,20 @@
|
|||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/image_bundle.h"
|
||||
#include "lib/jxl/image_test_utils.h"
|
||||
#include "lib/jxl/test_utils.h"
|
||||
#include "lib/jxl/testdata.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
namespace {
|
||||
|
||||
using ::testing::AllOf;
|
||||
using ::testing::Contains;
|
||||
using ::testing::Field;
|
||||
using ::testing::IsEmpty;
|
||||
using ::testing::NotNull;
|
||||
using ::testing::SizeIs;
|
||||
|
||||
std::string ExtensionFromCodec(Codec codec, const bool is_gray,
|
||||
const bool has_alpha,
|
||||
const size_t bits_per_sample) {
|
||||
|
|
@ -54,159 +65,207 @@ std::string ExtensionFromCodec(Codec codec, const bool is_gray,
|
|||
return std::string();
|
||||
}
|
||||
|
||||
CodecInOut CreateTestImage(const size_t xsize, const size_t ysize,
|
||||
const bool is_gray, const bool add_alpha,
|
||||
const size_t bits_per_sample,
|
||||
const ColorEncoding& c_native) {
|
||||
Image3F image(xsize, ysize);
|
||||
Rng rng(129);
|
||||
if (is_gray) {
|
||||
for (size_t y = 0; y < ysize; ++y) {
|
||||
float* JXL_RESTRICT row0 = image.PlaneRow(0, y);
|
||||
float* JXL_RESTRICT row1 = image.PlaneRow(1, y);
|
||||
float* JXL_RESTRICT row2 = image.PlaneRow(2, y);
|
||||
for (size_t x = 0; x < xsize; ++x) {
|
||||
row0[x] = row1[x] = row2[x] = rng.UniformF(0.0f, 1.0f);
|
||||
void VerifySameImage(const PackedImage& im0, size_t bits_per_sample0,
|
||||
const PackedImage& im1, size_t bits_per_sample1) {
|
||||
ASSERT_EQ(im0.xsize, im1.xsize);
|
||||
ASSERT_EQ(im0.ysize, im1.ysize);
|
||||
ASSERT_EQ(im0.format.num_channels, im1.format.num_channels);
|
||||
auto get_factor = [](JxlPixelFormat f, size_t bits) -> double {
|
||||
return 1.0 / ((1u << std::min(test::GetPrecision(f.data_type), bits)) - 1);
|
||||
};
|
||||
double factor0 = get_factor(im0.format, bits_per_sample0);
|
||||
double factor1 = get_factor(im1.format, bits_per_sample1);
|
||||
auto pixels0 = static_cast<const uint8_t*>(im0.pixels());
|
||||
auto pixels1 = static_cast<const uint8_t*>(im1.pixels());
|
||||
auto rgba0 =
|
||||
test::ConvertToRGBA32(pixels0, im0.xsize, im0.ysize, im0.format, factor0);
|
||||
auto rgba1 =
|
||||
test::ConvertToRGBA32(pixels1, im1.xsize, im1.ysize, im1.format, factor1);
|
||||
double tolerance = 0.5 * std::min(factor0, factor1);
|
||||
for (size_t y = 0; y < im0.ysize; ++y) {
|
||||
for (size_t x = 0; x < im0.xsize; ++x) {
|
||||
for (size_t c = 0; c < im0.format.num_channels; ++c) {
|
||||
size_t ix = (y * im0.xsize + x) * 4 + c;
|
||||
double val0 = rgba0[ix];
|
||||
double val1 = rgba1[ix];
|
||||
ASSERT_NEAR(val1, val0, tolerance)
|
||||
<< "y = " << y << " x = " << x << " c = " << c;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RandomFillImage(&image, 0.0f, 1.0f);
|
||||
}
|
||||
CodecInOut io;
|
||||
}
|
||||
|
||||
if (bits_per_sample == 32) {
|
||||
io.metadata.m.SetFloat32Samples();
|
||||
JxlColorEncoding CreateTestColorEncoding(bool is_gray) {
|
||||
JxlColorEncoding c;
|
||||
c.color_space = is_gray ? JXL_COLOR_SPACE_GRAY : JXL_COLOR_SPACE_RGB;
|
||||
c.white_point = JXL_WHITE_POINT_D65;
|
||||
c.primaries = JXL_PRIMARIES_P3;
|
||||
c.rendering_intent = JXL_RENDERING_INTENT_RELATIVE;
|
||||
c.transfer_function = JXL_TRANSFER_FUNCTION_LINEAR;
|
||||
return c;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> GenerateICC(JxlColorEncoding color_encoding) {
|
||||
ColorEncoding c;
|
||||
JXL_CHECK(ConvertExternalToInternalColorEncoding(color_encoding, &c));
|
||||
JXL_CHECK(c.CreateICC());
|
||||
PaddedBytes icc = c.ICC();
|
||||
return std::vector<uint8_t>(icc.begin(), icc.end());
|
||||
}
|
||||
|
||||
void StoreRandomValue(uint8_t* out, Rng* rng, JxlPixelFormat format,
|
||||
size_t bits_per_sample) {
|
||||
uint64_t max_val = (1ull << bits_per_sample) - 1;
|
||||
if (format.data_type == JXL_TYPE_UINT8) {
|
||||
*out = rng->UniformU(0, max_val);
|
||||
} else if (format.data_type == JXL_TYPE_UINT16) {
|
||||
uint32_t val = rng->UniformU(0, max_val);
|
||||
if (format.endianness == JXL_BIG_ENDIAN) {
|
||||
StoreBE16(val, out);
|
||||
} else {
|
||||
StoreLE16(val, out);
|
||||
}
|
||||
} else {
|
||||
io.metadata.m.SetUintSamples(bits_per_sample);
|
||||
ASSERT_EQ(format.data_type, JXL_TYPE_FLOAT);
|
||||
float val = rng->UniformF(0.0, 1.0);
|
||||
uint32_t uval;
|
||||
memcpy(&uval, &val, 4);
|
||||
if (format.endianness == JXL_BIG_ENDIAN) {
|
||||
StoreBE32(val, out);
|
||||
} else {
|
||||
StoreLE32(val, out);
|
||||
}
|
||||
}
|
||||
io.metadata.m.color_encoding = c_native;
|
||||
io.SetFromImage(std::move(image), c_native);
|
||||
if (add_alpha) {
|
||||
ImageF alpha(xsize, ysize);
|
||||
RandomFillImage(&alpha, 0.0f, 1.f);
|
||||
io.metadata.m.SetAlphaBits(bits_per_sample <= 8 ? 8 : 16);
|
||||
io.Main().SetAlpha(std::move(alpha), /*alpha_is_premultiplied=*/false);
|
||||
}
|
||||
|
||||
void FillPackedImage(size_t bits_per_sample, PackedImage* image) {
|
||||
JxlPixelFormat format = image->format;
|
||||
size_t bytes_per_channel = PackedImage::BitsPerChannel(format.data_type) / 8;
|
||||
uint8_t* out = static_cast<uint8_t*>(image->pixels());
|
||||
size_t stride = image->xsize * format.num_channels * bytes_per_channel;
|
||||
ASSERT_EQ(image->pixels_size, image->ysize * stride);
|
||||
Rng rng(129);
|
||||
for (size_t y = 0; y < image->ysize; ++y) {
|
||||
for (size_t x = 0; x < image->xsize; ++x) {
|
||||
for (size_t c = 0; c < format.num_channels; ++c) {
|
||||
StoreRandomValue(out, &rng, format, bits_per_sample);
|
||||
out += bytes_per_channel;
|
||||
}
|
||||
}
|
||||
}
|
||||
return io;
|
||||
}
|
||||
|
||||
struct TestImageParams {
|
||||
size_t xsize;
|
||||
size_t ysize;
|
||||
size_t bits_per_sample;
|
||||
bool is_gray;
|
||||
bool add_alpha;
|
||||
bool big_endian;
|
||||
|
||||
bool ShouldTestRoundtrip(Codec codec) const {
|
||||
if (codec == Codec::kPNG) {
|
||||
return true;
|
||||
} else if (codec == Codec::kPNM) {
|
||||
return ((bits_per_sample <= 16 && big_endian) ||
|
||||
(bits_per_sample == 32 && !add_alpha));
|
||||
} else if (codec == Codec::kPGX) {
|
||||
return ((bits_per_sample == 8 || bits_per_sample == 16) && is_gray &&
|
||||
!add_alpha);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
JxlPixelFormat PixelFormat() const {
|
||||
JxlPixelFormat format;
|
||||
format.num_channels = (is_gray ? 1 : 3) + (add_alpha ? 1 : 0);
|
||||
format.data_type = (bits_per_sample == 32 ? JXL_TYPE_FLOAT
|
||||
: bits_per_sample > 8 ? JXL_TYPE_UINT16
|
||||
: JXL_TYPE_UINT8);
|
||||
format.endianness = big_endian ? JXL_BIG_ENDIAN : JXL_LITTLE_ENDIAN;
|
||||
format.align = 0;
|
||||
return format;
|
||||
}
|
||||
|
||||
std::string DebugString() const {
|
||||
std::ostringstream os;
|
||||
os << "bps:" << bits_per_sample << " gr:" << is_gray << " al:" << add_alpha
|
||||
<< " be: " << big_endian;
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
void CreateTestImage(const TestImageParams& params, PackedPixelFile* ppf) {
|
||||
ppf->info.xsize = params.xsize;
|
||||
ppf->info.ysize = params.ysize;
|
||||
ppf->info.bits_per_sample = params.bits_per_sample;
|
||||
ppf->info.exponent_bits_per_sample = params.bits_per_sample == 32 ? 8 : 0;
|
||||
ppf->info.num_color_channels = params.is_gray ? 1 : 3;
|
||||
ppf->info.alpha_bits = params.add_alpha ? params.bits_per_sample : 0;
|
||||
|
||||
JxlColorEncoding color_encoding = CreateTestColorEncoding(params.is_gray);
|
||||
ppf->icc = GenerateICC(color_encoding);
|
||||
|
||||
PackedFrame frame(params.xsize, params.ysize, params.PixelFormat());
|
||||
FillPackedImage(params.bits_per_sample, &frame.color);
|
||||
ppf->frames.emplace_back(std::move(frame));
|
||||
}
|
||||
|
||||
// Ensures reading a newly written file leads to the same image pixels.
|
||||
void TestRoundTrip(Codec codec, const size_t xsize, const size_t ysize,
|
||||
const bool is_gray, const bool add_alpha,
|
||||
const size_t bits_per_sample, ThreadPool* pool) {
|
||||
// JPEG encoding is not lossless.
|
||||
if (codec == Codec::kJPG) return;
|
||||
if (codec == Codec::kPNM && add_alpha) return;
|
||||
// Our EXR codec always uses 16-bit premultiplied alpha, does not support
|
||||
// grayscale, and somehow does not have sufficient precision for this test.
|
||||
if (codec == Codec::kEXR) return;
|
||||
printf("Codec %s bps:%" PRIuS " gr:%d al:%d\n",
|
||||
ExtensionFromCodec(codec, is_gray, add_alpha, bits_per_sample).c_str(),
|
||||
bits_per_sample, is_gray, add_alpha);
|
||||
void TestRoundTrip(Codec codec, const TestImageParams& params,
|
||||
ThreadPool* pool) {
|
||||
if (!params.ShouldTestRoundtrip(codec)) return;
|
||||
|
||||
ColorEncoding c_native;
|
||||
c_native.SetColorSpace(is_gray ? ColorSpace::kGray : ColorSpace::kRGB);
|
||||
// Note: this must not be wider than c_external, otherwise gamut clipping
|
||||
// will cause large round-trip errors.
|
||||
c_native.primaries = Primaries::kP3;
|
||||
c_native.tf.SetTransferFunction(TransferFunction::kLinear);
|
||||
JXL_CHECK(c_native.CreateICC());
|
||||
std::string extension = ExtensionFromCodec(
|
||||
codec, params.is_gray, params.add_alpha, params.bits_per_sample);
|
||||
printf("Codec %s %s\n", extension.c_str(), params.DebugString().c_str());
|
||||
|
||||
// Generally store same color space to reduce round trip errors..
|
||||
ColorEncoding c_external = c_native;
|
||||
// .. unless we have enough precision for some transforms.
|
||||
if (bits_per_sample >= 16) {
|
||||
c_external.white_point = WhitePoint::kE;
|
||||
c_external.primaries = Primaries::k2100;
|
||||
c_external.tf.SetTransferFunction(TransferFunction::kSRGB);
|
||||
}
|
||||
JXL_CHECK(c_external.CreateICC());
|
||||
PackedPixelFile ppf_in;
|
||||
CreateTestImage(params, &ppf_in);
|
||||
|
||||
const CodecInOut io = CreateTestImage(xsize, ysize, is_gray, add_alpha,
|
||||
bits_per_sample, c_native);
|
||||
const ImageBundle& ib1 = io.Main();
|
||||
EncodedImage encoded;
|
||||
auto encoder = Encoder::FromExtension(extension);
|
||||
ASSERT_TRUE(encoder.get());
|
||||
ASSERT_TRUE(encoder->Encode(ppf_in, &encoded, pool));
|
||||
ASSERT_EQ(encoded.bitstreams.size(), 1);
|
||||
|
||||
PaddedBytes encoded;
|
||||
JXL_CHECK(Encode(io, codec, c_external, bits_per_sample, &encoded, pool));
|
||||
PackedPixelFile ppf_out;
|
||||
ASSERT_TRUE(DecodeBytes(Span<const uint8_t>(encoded.bitstreams[0]),
|
||||
ColorHints(), SizeConstraints(), &ppf_out));
|
||||
|
||||
CodecInOut io2;
|
||||
ColorHints color_hints;
|
||||
// Only for PNM because PNG will warn about ignoring them.
|
||||
if (codec == Codec::kPNM) {
|
||||
color_hints.Add("color_space", Description(c_external));
|
||||
}
|
||||
JXL_CHECK(SetFromBytes(Span<const uint8_t>(encoded), color_hints, &io2, pool,
|
||||
nullptr));
|
||||
ImageBundle& ib2 = io2.Main();
|
||||
|
||||
EXPECT_EQ(Description(c_external),
|
||||
Description(io2.metadata.m.color_encoding));
|
||||
|
||||
// See c_external above - for low bits_per_sample the encoded space is
|
||||
// already the same.
|
||||
if (bits_per_sample < 16) {
|
||||
EXPECT_EQ(Description(ib1.c_current()), Description(ib2.c_current()));
|
||||
if (codec != Codec::kPNM && codec != Codec::kPGX) {
|
||||
EXPECT_EQ(ppf_in.icc, ppf_out.icc);
|
||||
}
|
||||
|
||||
if (add_alpha) {
|
||||
EXPECT_TRUE(SamePixels(ib1.alpha(), *ib2.alpha()));
|
||||
}
|
||||
|
||||
JXL_CHECK(ib2.TransformTo(ib1.c_current(), GetJxlCms(), pool));
|
||||
|
||||
double max_l1, max_rel;
|
||||
// Round-trip tolerances must be higher than in external_image_test because
|
||||
// codecs do not support unbounded ranges.
|
||||
#if JPEGXL_ENABLE_SKCMS
|
||||
if (bits_per_sample <= 12) {
|
||||
max_l1 = 0.5;
|
||||
max_rel = 6E-3;
|
||||
} else {
|
||||
max_l1 = 1E-3;
|
||||
max_rel = 5E-4;
|
||||
}
|
||||
#else // JPEGXL_ENABLE_SKCMS
|
||||
if (bits_per_sample <= 12) {
|
||||
max_l1 = 0.5;
|
||||
max_rel = 6E-3;
|
||||
} else if (bits_per_sample == 16) {
|
||||
max_l1 = 3E-3;
|
||||
max_rel = 1E-4;
|
||||
} else {
|
||||
#ifdef __ARM_ARCH
|
||||
// pow() implementation in arm is a bit less precise than in x86 and
|
||||
// therefore we need a bigger error margin in this case.
|
||||
max_l1 = 1E-7;
|
||||
max_rel = 1E-4;
|
||||
#else
|
||||
max_l1 = 1E-7;
|
||||
max_rel = 1E-5;
|
||||
#endif
|
||||
}
|
||||
#endif // JPEGXL_ENABLE_SKCMS
|
||||
|
||||
VerifyRelativeError(ib1.color(), *ib2.color(), max_l1, max_rel);
|
||||
ASSERT_EQ(ppf_out.frames.size(), 1);
|
||||
VerifySameImage(ppf_in.frames[0].color, ppf_in.info.bits_per_sample,
|
||||
ppf_out.frames[0].color, ppf_out.info.bits_per_sample);
|
||||
}
|
||||
|
||||
#if 0
|
||||
TEST(CodecTest, TestRoundTrip) {
|
||||
ThreadPoolInternal pool(12);
|
||||
|
||||
const size_t xsize = 7;
|
||||
const size_t ysize = 4;
|
||||
TestImageParams params;
|
||||
params.xsize = 7;
|
||||
params.ysize = 4;
|
||||
|
||||
for (Codec codec : Values<Codec>()) {
|
||||
for (int bits_per_sample : {8, 10, 12, 16, 32}) {
|
||||
for (Codec codec : AvailableCodecs()) {
|
||||
for (int bits_per_sample : {4, 8, 10, 12, 16, 32}) {
|
||||
for (bool is_gray : {false, true}) {
|
||||
for (bool add_alpha : {false, true}) {
|
||||
TestRoundTrip(codec, xsize, ysize, is_gray, add_alpha,
|
||||
static_cast<size_t>(bits_per_sample), &pool);
|
||||
for (bool big_endian : {false, true}) {
|
||||
params.bits_per_sample = static_cast<size_t>(bits_per_sample);
|
||||
params.is_gray = is_gray;
|
||||
params.add_alpha = add_alpha;
|
||||
params.big_endian = big_endian;
|
||||
TestRoundTrip(codec, params, &pool);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
CodecInOut DecodeRoundtrip(const std::string& pathname, ThreadPool* pool,
|
||||
const ColorHints& color_hints = ColorHints()) {
|
||||
|
|
@ -217,7 +276,7 @@ CodecInOut DecodeRoundtrip(const std::string& pathname, ThreadPool* pool,
|
|||
const ImageBundle& ib1 = io.Main();
|
||||
|
||||
// Encode/Decode again to make sure Encode carries through all metadata.
|
||||
PaddedBytes encoded;
|
||||
std::vector<uint8_t> encoded;
|
||||
JXL_CHECK(Encode(io, Codec::kPNG, io.metadata.m.color_encoding,
|
||||
io.metadata.m.bit_depth.bits_per_sample, &encoded, pool));
|
||||
|
||||
|
|
@ -256,11 +315,11 @@ CodecInOut DecodeRoundtrip(const std::string& pathname, ThreadPool* pool,
|
|||
TEST(CodecTest, TestMetadataSRGB) {
|
||||
ThreadPoolInternal pool(12);
|
||||
|
||||
const char* paths[] = {"third_party/raw.pixls/DJI-FC6310-16bit_srgb8_v4_krita.png",
|
||||
"third_party/raw.pixls/Google-Pixel2XL-16bit_srgb8_v4_krita.png",
|
||||
"third_party/raw.pixls/HUAWEI-EVA-L09-16bit_srgb8_dt.png",
|
||||
"third_party/raw.pixls/Nikon-D300-12bit_srgb8_dt.png",
|
||||
"third_party/raw.pixls/Sony-DSC-RX1RM2-14bit_srgb8_v4_krita.png"};
|
||||
const char* paths[] = {"external/raw.pixls/DJI-FC6310-16bit_srgb8_v4_krita.png",
|
||||
"external/raw.pixls/Google-Pixel2XL-16bit_srgb8_v4_krita.png",
|
||||
"external/raw.pixls/HUAWEI-EVA-L09-16bit_srgb8_dt.png",
|
||||
"external/raw.pixls/Nikon-D300-12bit_srgb8_dt.png",
|
||||
"external/raw.pixls/Sony-DSC-RX1RM2-14bit_srgb8_v4_krita.png"};
|
||||
for (const char* relative_pathname : paths) {
|
||||
const CodecInOut io =
|
||||
DecodeRoundtrip(relative_pathname, Codec::kPNG, &pool);
|
||||
|
|
@ -285,9 +344,9 @@ TEST(CodecTest, TestMetadataLinear) {
|
|||
ThreadPoolInternal pool(12);
|
||||
|
||||
const char* paths[3] = {
|
||||
"third_party/raw.pixls/Google-Pixel2XL-16bit_acescg_g1_v4_krita.png",
|
||||
"third_party/raw.pixls/HUAWEI-EVA-L09-16bit_709_g1_dt.png",
|
||||
"third_party/raw.pixls/Nikon-D300-12bit_2020_g1_dt.png",
|
||||
"external/raw.pixls/Google-Pixel2XL-16bit_acescg_g1_v4_krita.png",
|
||||
"external/raw.pixls/HUAWEI-EVA-L09-16bit_709_g1_dt.png",
|
||||
"external/raw.pixls/Nikon-D300-12bit_2020_g1_dt.png",
|
||||
};
|
||||
const WhitePoint white_points[3] = {WhitePoint::kCustom, WhitePoint::kD65,
|
||||
WhitePoint::kD65};
|
||||
|
|
@ -317,8 +376,8 @@ TEST(CodecTest, TestMetadataICC) {
|
|||
ThreadPoolInternal pool(12);
|
||||
|
||||
const char* paths[] = {
|
||||
"third_party/raw.pixls/DJI-FC6310-16bit_709_v4_krita.png",
|
||||
"third_party/raw.pixls/Sony-DSC-RX1RM2-14bit_709_v4_krita.png",
|
||||
"external/raw.pixls/DJI-FC6310-16bit_709_v4_krita.png",
|
||||
"external/raw.pixls/Sony-DSC-RX1RM2-14bit_709_v4_krita.png",
|
||||
};
|
||||
for (const char* relative_pathname : paths) {
|
||||
const CodecInOut io =
|
||||
|
|
@ -340,28 +399,28 @@ TEST(CodecTest, TestMetadataICC) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(CodecTest, Testthird_party/pngsuite) {
|
||||
TEST(CodecTest, Testexternal/pngsuite) {
|
||||
ThreadPoolInternal pool(12);
|
||||
|
||||
// Ensure we can load PNG with text, japanese UTF-8, compressed text.
|
||||
(void)DecodeRoundtrip("third_party/pngsuite/ct1n0g04.png", Codec::kPNG, &pool);
|
||||
(void)DecodeRoundtrip("third_party/pngsuite/ctjn0g04.png", Codec::kPNG, &pool);
|
||||
(void)DecodeRoundtrip("third_party/pngsuite/ctzn0g04.png", Codec::kPNG, &pool);
|
||||
(void)DecodeRoundtrip("external/pngsuite/ct1n0g04.png", Codec::kPNG, &pool);
|
||||
(void)DecodeRoundtrip("external/pngsuite/ctjn0g04.png", Codec::kPNG, &pool);
|
||||
(void)DecodeRoundtrip("external/pngsuite/ctzn0g04.png", Codec::kPNG, &pool);
|
||||
|
||||
// Extract gAMA
|
||||
const CodecInOut b1 =
|
||||
DecodeRoundtrip("third_party/pngsuite/g10n3p04.png", Codec::kPNG, &pool);
|
||||
DecodeRoundtrip("external/pngsuite/g10n3p04.png", Codec::kPNG, &pool);
|
||||
EXPECT_TRUE(b1.metadata.color_encoding.tf.IsLinear());
|
||||
|
||||
// Extract cHRM
|
||||
const CodecInOut b_p =
|
||||
DecodeRoundtrip("third_party/pngsuite/ccwn2c08.png", Codec::kPNG, &pool);
|
||||
DecodeRoundtrip("external/pngsuite/ccwn2c08.png", Codec::kPNG, &pool);
|
||||
EXPECT_EQ(Primaries::kSRGB, b_p.metadata.color_encoding.primaries);
|
||||
EXPECT_EQ(WhitePoint::kD65, b_p.metadata.color_encoding.white_point);
|
||||
|
||||
// Extract EXIF from (new-style) dedicated chunk
|
||||
const CodecInOut b_exif =
|
||||
DecodeRoundtrip("third_party/pngsuite/exif2c08.png", Codec::kPNG, &pool);
|
||||
DecodeRoundtrip("external/pngsuite/exif2c08.png", Codec::kPNG, &pool);
|
||||
EXPECT_EQ(978, b_exif.blobs.exif.size());
|
||||
}
|
||||
#endif
|
||||
|
|
@ -384,18 +443,88 @@ void VerifyWideGamutMetadata(const std::string& relative_pathname,
|
|||
|
||||
TEST(CodecTest, TestWideGamut) {
|
||||
ThreadPoolInternal pool(12);
|
||||
// VerifyWideGamutMetadata("third_party/wide-gamut-tests/P3-sRGB-color-bars.png",
|
||||
// VerifyWideGamutMetadata("external/wide-gamut-tests/P3-sRGB-color-bars.png",
|
||||
// Primaries::kP3, &pool);
|
||||
VerifyWideGamutMetadata("third_party/wide-gamut-tests/P3-sRGB-color-ring.png",
|
||||
VerifyWideGamutMetadata("external/wide-gamut-tests/P3-sRGB-color-ring.png",
|
||||
Primaries::kP3, &pool);
|
||||
// VerifyWideGamutMetadata("third_party/wide-gamut-tests/R2020-sRGB-color-bars.png",
|
||||
// VerifyWideGamutMetadata("external/wide-gamut-tests/R2020-sRGB-color-bars.png",
|
||||
// Primaries::k2100, &pool);
|
||||
// VerifyWideGamutMetadata("third_party/wide-gamut-tests/R2020-sRGB-color-ring.png",
|
||||
// VerifyWideGamutMetadata("external/wide-gamut-tests/R2020-sRGB-color-ring.png",
|
||||
// Primaries::k2100, &pool);
|
||||
}
|
||||
|
||||
TEST(CodecTest, TestPNM) { TestCodecPNM(); }
|
||||
|
||||
TEST(CodecTest, FormatNegotiation) {
|
||||
const std::vector<JxlPixelFormat> accepted_formats = {
|
||||
{/*num_channels=*/4,
|
||||
/*data_type=*/JXL_TYPE_UINT16,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0},
|
||||
{/*num_channels=*/3,
|
||||
/*data_type=*/JXL_TYPE_UINT8,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0},
|
||||
{/*num_channels=*/3,
|
||||
/*data_type=*/JXL_TYPE_UINT16,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0},
|
||||
{/*num_channels=*/1,
|
||||
/*data_type=*/JXL_TYPE_UINT8,
|
||||
/*endianness=*/JXL_NATIVE_ENDIAN,
|
||||
/*align=*/0},
|
||||
};
|
||||
|
||||
JxlBasicInfo info;
|
||||
JxlEncoderInitBasicInfo(&info);
|
||||
info.bits_per_sample = 12;
|
||||
info.num_color_channels = 2;
|
||||
|
||||
JxlPixelFormat format;
|
||||
EXPECT_FALSE(SelectFormat(accepted_formats, info, &format));
|
||||
|
||||
info.num_color_channels = 3;
|
||||
ASSERT_TRUE(SelectFormat(accepted_formats, info, &format));
|
||||
EXPECT_EQ(format.num_channels, info.num_color_channels);
|
||||
// 16 is the smallest accepted format that can accommodate the 12-bit data.
|
||||
EXPECT_EQ(format.data_type, JXL_TYPE_UINT16);
|
||||
}
|
||||
|
||||
TEST(CodecTest, EncodeToPNG) {
|
||||
ThreadPool* const pool = nullptr;
|
||||
|
||||
std::unique_ptr<Encoder> png_encoder = Encoder::FromExtension(".png");
|
||||
ASSERT_THAT(png_encoder, NotNull());
|
||||
|
||||
const PaddedBytes original_png =
|
||||
ReadTestData("external/wesaturate/500px/tmshre_riaphotographs_srgb8.png");
|
||||
PackedPixelFile ppf;
|
||||
ASSERT_TRUE(extras::DecodeBytes(Span<const uint8_t>(original_png),
|
||||
ColorHints(), SizeConstraints(), &ppf));
|
||||
|
||||
const JxlPixelFormat& format = ppf.frames.front().color.format;
|
||||
ASSERT_THAT(
|
||||
png_encoder->AcceptedFormats(),
|
||||
Contains(AllOf(Field(&JxlPixelFormat::num_channels, format.num_channels),
|
||||
Field(&JxlPixelFormat::data_type, format.data_type),
|
||||
Field(&JxlPixelFormat::endianness, format.endianness))));
|
||||
EncodedImage encoded_png;
|
||||
ASSERT_TRUE(png_encoder->Encode(ppf, &encoded_png, pool));
|
||||
EXPECT_THAT(encoded_png.icc, IsEmpty());
|
||||
ASSERT_THAT(encoded_png.bitstreams, SizeIs(1));
|
||||
|
||||
PackedPixelFile decoded_ppf;
|
||||
ASSERT_TRUE(
|
||||
extras::DecodeBytes(Span<const uint8_t>(encoded_png.bitstreams.front()),
|
||||
ColorHints(), SizeConstraints(), &decoded_ppf));
|
||||
|
||||
ASSERT_EQ(decoded_ppf.info.bits_per_sample, ppf.info.bits_per_sample);
|
||||
ASSERT_EQ(decoded_ppf.frames.size(), 1);
|
||||
VerifySameImage(ppf.frames[0].color, ppf.info.bits_per_sample,
|
||||
decoded_ppf.frames[0].color,
|
||||
decoded_ppf.info.bits_per_sample);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
19
third_party/jpeg-xl/lib/extras/dec/decode.cc
vendored
19
third_party/jpeg-xl/lib/extras/dec/decode.cc
vendored
|
|
@ -31,6 +31,25 @@ constexpr size_t kMinBytes = 9;
|
|||
|
||||
} // namespace
|
||||
|
||||
std::vector<Codec> AvailableCodecs() {
|
||||
std::vector<Codec> out;
|
||||
#if JPEGXL_ENABLE_APNG
|
||||
out.push_back(Codec::kPNG);
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_EXR
|
||||
out.push_back(Codec::kEXR);
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_GIF
|
||||
out.push_back(Codec::kGIF);
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_JPEG
|
||||
out.push_back(Codec::kJPG);
|
||||
#endif
|
||||
out.push_back(Codec::kPGX);
|
||||
out.push_back(Codec::kPNM);
|
||||
return out;
|
||||
}
|
||||
|
||||
Codec CodecFromExtension(std::string extension,
|
||||
size_t* JXL_RESTRICT bits_per_sample) {
|
||||
std::transform(
|
||||
|
|
|
|||
22
third_party/jpeg-xl/lib/extras/dec/decode.h
vendored
22
third_party/jpeg-xl/lib/extras/dec/decode.h
vendored
|
|
@ -12,15 +12,12 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/dec/color_hints.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/codec_in_out.h"
|
||||
#include "lib/jxl/field_encodings.h" // MakeBit
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
|
@ -36,27 +33,14 @@ enum class Codec : uint32_t {
|
|||
kEXR
|
||||
};
|
||||
|
||||
static inline constexpr uint64_t EnumBits(Codec /*unused*/) {
|
||||
// Return only fully-supported codecs (kGIF is decode-only).
|
||||
return MakeBit(Codec::kPNM)
|
||||
#if JPEGXL_ENABLE_APNG
|
||||
| MakeBit(Codec::kPNG)
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_JPEG
|
||||
| MakeBit(Codec::kJPG)
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_EXR
|
||||
| MakeBit(Codec::kEXR)
|
||||
#endif
|
||||
;
|
||||
}
|
||||
std::vector<Codec> AvailableCodecs();
|
||||
|
||||
// If and only if extension is ".pfm", *bits_per_sample is updated to 32 so
|
||||
// that Encode() would encode to PFM instead of PPM.
|
||||
Codec CodecFromExtension(std::string extension,
|
||||
size_t* JXL_RESTRICT bits_per_sample = nullptr);
|
||||
|
||||
// Decodes "bytes" and sets io->metadata.m.
|
||||
// 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,
|
||||
|
|
|
|||
480
third_party/jpeg-xl/lib/extras/dec/jxl.cc
vendored
Normal file
480
third_party/jpeg-xl/lib/extras/dec/jxl.cc
vendored
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/dec/jxl.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"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
namespace {
|
||||
|
||||
struct BoxProcessor {
|
||||
BoxProcessor(JxlDecoder* dec) : dec_(dec) { Reset(); }
|
||||
|
||||
void InitializeOutput(std::vector<uint8_t>* out) {
|
||||
box_data_ = out;
|
||||
AddMoreOutput();
|
||||
}
|
||||
|
||||
bool AddMoreOutput() {
|
||||
Flush();
|
||||
static const size_t kBoxOutputChunkSize = 1 << 16;
|
||||
box_data_->resize(box_data_->size() + kBoxOutputChunkSize);
|
||||
next_out_ = box_data_->data() + total_size_;
|
||||
avail_out_ = box_data_->size() - total_size_;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetBoxBuffer(dec_, next_out_, avail_out_)) {
|
||||
fprintf(stderr, "JxlDecoderSetBoxBuffer failed\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FinalizeOutput() {
|
||||
if (box_data_ == nullptr) return;
|
||||
Flush();
|
||||
box_data_->resize(total_size_);
|
||||
Reset();
|
||||
}
|
||||
|
||||
private:
|
||||
JxlDecoder* dec_;
|
||||
std::vector<uint8_t>* box_data_;
|
||||
uint8_t* next_out_;
|
||||
size_t avail_out_;
|
||||
size_t total_size_;
|
||||
|
||||
void Reset() {
|
||||
box_data_ = nullptr;
|
||||
next_out_ = nullptr;
|
||||
avail_out_ = 0;
|
||||
total_size_ = 0;
|
||||
}
|
||||
void Flush() {
|
||||
if (box_data_ == nullptr) return;
|
||||
size_t remaining = JxlDecoderReleaseBoxBuffer(dec_);
|
||||
size_t bytes_written = avail_out_ - remaining;
|
||||
next_out_ += bytes_written;
|
||||
avail_out_ -= bytes_written;
|
||||
total_size_ += bytes_written;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
|
||||
const JXLDecompressParams& dparams, size_t* decoded_bytes,
|
||||
PackedPixelFile* ppf, std::vector<uint8_t>* jpeg_bytes) {
|
||||
auto decoder = JxlDecoderMake(/*memory_manager=*/nullptr);
|
||||
JxlDecoder* dec = decoder.get();
|
||||
ppf->frames.clear();
|
||||
|
||||
if (dparams.runner_opaque != nullptr &&
|
||||
JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec, dparams.runner,
|
||||
dparams.runner_opaque)) {
|
||||
fprintf(stderr, "JxlEncoderSetParallelRunner failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
JxlPixelFormat format;
|
||||
std::vector<JxlPixelFormat> accepted_formats = dparams.accepted_formats;
|
||||
if (accepted_formats.empty()) {
|
||||
for (const uint32_t num_channels : {1, 2, 3, 4}) {
|
||||
accepted_formats.push_back(
|
||||
{num_channels, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, /*align=*/0});
|
||||
}
|
||||
}
|
||||
JxlColorEncoding color_encoding;
|
||||
size_t num_color_channels = 0;
|
||||
if (!dparams.color_space.empty()) {
|
||||
if (!jxl::ParseDescription(dparams.color_space, &color_encoding)) {
|
||||
fprintf(stderr, "Failed to parse color space %s.\n",
|
||||
dparams.color_space.c_str());
|
||||
return false;
|
||||
}
|
||||
num_color_channels =
|
||||
color_encoding.color_space == JXL_COLOR_SPACE_GRAY ? 1 : 3;
|
||||
}
|
||||
|
||||
bool can_reconstruct_jpeg = false;
|
||||
std::vector<uint8_t> jpeg_data_chunk;
|
||||
if (jpeg_bytes != nullptr) {
|
||||
jpeg_data_chunk.resize(16384);
|
||||
jpeg_bytes->resize(0);
|
||||
}
|
||||
|
||||
int events = (JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE);
|
||||
|
||||
bool max_passes_defined =
|
||||
(dparams.max_passes < std::numeric_limits<uint32_t>::max());
|
||||
if (max_passes_defined || dparams.max_downsampling > 1) {
|
||||
events |= JXL_DEC_FRAME_PROGRESSION;
|
||||
if (max_passes_defined) {
|
||||
JxlDecoderSetProgressiveDetail(dec, JxlProgressiveDetail::kPasses);
|
||||
} else {
|
||||
JxlDecoderSetProgressiveDetail(dec, JxlProgressiveDetail::kLastPasses);
|
||||
}
|
||||
}
|
||||
if (jpeg_bytes != nullptr) {
|
||||
events |= JXL_DEC_JPEG_RECONSTRUCTION;
|
||||
} else {
|
||||
events |= (JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME | JXL_DEC_PREVIEW_IMAGE |
|
||||
JXL_DEC_BOX);
|
||||
}
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec, events)) {
|
||||
fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
|
||||
return false;
|
||||
}
|
||||
if (jpeg_bytes == nullptr) {
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetRenderSpotcolors(dec, dparams.render_spotcolors)) {
|
||||
fprintf(stderr, "JxlDecoderSetRenderSpotColors failed\n");
|
||||
return false;
|
||||
}
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetKeepOrientation(dec, dparams.keep_orientation)) {
|
||||
fprintf(stderr, "JxlDecoderSetKeepOrientation failed\n");
|
||||
return false;
|
||||
}
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetUnpremultiplyAlpha(dec, dparams.unpremultiply_alpha)) {
|
||||
fprintf(stderr, "JxlDecoderSetUnpremultiplyAlpha failed\n");
|
||||
return false;
|
||||
}
|
||||
if (dparams.display_nits > 0 &&
|
||||
JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetDesiredIntensityTarget(dec, dparams.display_nits)) {
|
||||
fprintf(stderr, "Decoder failed to set desired intensity target\n");
|
||||
return false;
|
||||
}
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetDecompressBoxes(dec, JXL_TRUE)) {
|
||||
fprintf(stderr, "JxlDecoderSetDecompressBoxes failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetInput(dec, bytes, bytes_size)) {
|
||||
fprintf(stderr, "Decoder failed to set input\n");
|
||||
return false;
|
||||
}
|
||||
uint32_t progression_index = 0;
|
||||
bool codestream_done = false;
|
||||
BoxProcessor boxes(dec);
|
||||
for (;;) {
|
||||
JxlDecoderStatus status = JxlDecoderProcessInput(dec);
|
||||
if (status == JXL_DEC_ERROR) {
|
||||
fprintf(stderr, "Failed to decode image\n");
|
||||
return false;
|
||||
} else if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
if (codestream_done) {
|
||||
break;
|
||||
}
|
||||
if (dparams.allow_partial_input) {
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec)) {
|
||||
fprintf(stderr,
|
||||
"Input file is truncated and there is no preview "
|
||||
"available yet.\n");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
fprintf(stderr,
|
||||
"Input file is truncated and allow_partial_input was disabled.");
|
||||
return false;
|
||||
} else if (status == JXL_DEC_BOX) {
|
||||
boxes.FinalizeOutput();
|
||||
JxlBoxType box_type;
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetBoxType(dec, box_type, JXL_TRUE)) {
|
||||
fprintf(stderr, "JxlDecoderGetBoxType failed\n");
|
||||
return false;
|
||||
}
|
||||
std::vector<uint8_t>* box_data = nullptr;
|
||||
if (memcmp(box_type, "Exif", 4) == 0) {
|
||||
box_data = &ppf->metadata.exif;
|
||||
} else if (memcmp(box_type, "iptc", 4) == 0) {
|
||||
box_data = &ppf->metadata.iptc;
|
||||
} else if (memcmp(box_type, "jumb", 4) == 0) {
|
||||
box_data = &ppf->metadata.jumbf;
|
||||
} else if (memcmp(box_type, "xml ", 4) == 0) {
|
||||
box_data = &ppf->metadata.xmp;
|
||||
}
|
||||
if (box_data) {
|
||||
boxes.InitializeOutput(box_data);
|
||||
}
|
||||
} else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
|
||||
boxes.AddMoreOutput();
|
||||
} else if (status == JXL_DEC_JPEG_RECONSTRUCTION) {
|
||||
can_reconstruct_jpeg = true;
|
||||
// Decoding to JPEG.
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetJPEGBuffer(dec,
|
||||
jpeg_data_chunk.data(),
|
||||
jpeg_data_chunk.size())) {
|
||||
fprintf(stderr, "Decoder failed to set JPEG Buffer\n");
|
||||
return false;
|
||||
}
|
||||
} else if (status == JXL_DEC_JPEG_NEED_MORE_OUTPUT) {
|
||||
// Decoded a chunk to JPEG.
|
||||
size_t used_jpeg_output =
|
||||
jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec);
|
||||
jpeg_bytes->insert(jpeg_bytes->end(), jpeg_data_chunk.data(),
|
||||
jpeg_data_chunk.data() + used_jpeg_output);
|
||||
if (used_jpeg_output == 0) {
|
||||
// Chunk is too small.
|
||||
jpeg_data_chunk.resize(jpeg_data_chunk.size() * 2);
|
||||
}
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetJPEGBuffer(dec,
|
||||
jpeg_data_chunk.data(),
|
||||
jpeg_data_chunk.size())) {
|
||||
fprintf(stderr, "Decoder failed to set JPEG Buffer\n");
|
||||
return false;
|
||||
}
|
||||
} else if (status == JXL_DEC_BASIC_INFO) {
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec, &ppf->info)) {
|
||||
fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
|
||||
return false;
|
||||
}
|
||||
if (num_color_channels != 0) {
|
||||
// Mark the change in number of color channels due to the requested
|
||||
// color space.
|
||||
ppf->info.num_color_channels = num_color_channels;
|
||||
}
|
||||
// Select format according to accepted formats.
|
||||
if (!jxl::extras::SelectFormat(accepted_formats, ppf->info, &format)) {
|
||||
fprintf(stderr, "SelectFormat failed\n");
|
||||
return false;
|
||||
}
|
||||
bool have_alpha = (format.num_channels == 2 || format.num_channels == 4);
|
||||
if (!have_alpha) {
|
||||
// Mark in the basic info that alpha channel was dropped.
|
||||
ppf->info.alpha_bits = 0;
|
||||
} else if (dparams.unpremultiply_alpha) {
|
||||
// Mark in the basic info that alpha was unpremultiplied.
|
||||
ppf->info.alpha_premultiplied = false;
|
||||
}
|
||||
bool alpha_found = false;
|
||||
for (uint32_t i = 0; i < ppf->info.num_extra_channels; ++i) {
|
||||
JxlExtraChannelInfo eci;
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelInfo(dec, i, &eci)) {
|
||||
fprintf(stderr, "JxlDecoderGetExtraChannelInfo failed\n");
|
||||
return false;
|
||||
}
|
||||
if (eci.type == JXL_CHANNEL_ALPHA && have_alpha && !alpha_found) {
|
||||
// Skip the first alpha channels because it is already present in the
|
||||
// interleaved image.
|
||||
alpha_found = true;
|
||||
continue;
|
||||
}
|
||||
std::string name(eci.name_length + 1, 0);
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetExtraChannelName(dec, i, &name[0], name.size())) {
|
||||
fprintf(stderr, "JxlDecoderGetExtraChannelName failed\n");
|
||||
return false;
|
||||
}
|
||||
name.resize(eci.name_length);
|
||||
ppf->extra_channels_info.push_back({eci, i, name});
|
||||
}
|
||||
} else if (status == JXL_DEC_COLOR_ENCODING) {
|
||||
if (!dparams.color_space.empty()) {
|
||||
if (ppf->info.uses_original_profile) {
|
||||
fprintf(stderr,
|
||||
"Warning: --color_space ignored because the image is "
|
||||
"not XYB encoded.\n");
|
||||
} else {
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetPreferredColorProfile(dec, &color_encoding)) {
|
||||
fprintf(stderr, "Failed to set color space.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
size_t icc_size = 0;
|
||||
JxlColorProfileTarget target = JXL_COLOR_PROFILE_TARGET_DATA;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetICCProfileSize(dec, nullptr, target, &icc_size)) {
|
||||
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
|
||||
}
|
||||
if (icc_size != 0) {
|
||||
ppf->icc.resize(icc_size);
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetColorAsICCProfile(dec, nullptr, target,
|
||||
ppf->icc.data(), icc_size)) {
|
||||
fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsEncodedProfile(
|
||||
dec, nullptr, target, &ppf->color_encoding)) {
|
||||
ppf->color_encoding.color_space = JXL_COLOR_SPACE_UNKNOWN;
|
||||
}
|
||||
icc_size = 0;
|
||||
target = JXL_COLOR_PROFILE_TARGET_ORIGINAL;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetICCProfileSize(dec, nullptr, target, &icc_size)) {
|
||||
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
|
||||
}
|
||||
if (icc_size != 0) {
|
||||
ppf->orig_icc.resize(icc_size);
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetColorAsICCProfile(dec, nullptr, target,
|
||||
ppf->orig_icc.data(), icc_size)) {
|
||||
fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (status == JXL_DEC_FRAME) {
|
||||
jxl::extras::PackedFrame frame(ppf->info.xsize, ppf->info.ysize, format);
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetFrameHeader(dec, &frame.frame_info)) {
|
||||
fprintf(stderr, "JxlDecoderGetFrameHeader failed\n");
|
||||
return false;
|
||||
}
|
||||
frame.name.resize(frame.frame_info.name_length + 1, 0);
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetFrameName(dec, &frame.name[0], frame.name.size())) {
|
||||
fprintf(stderr, "JxlDecoderGetFrameName failed\n");
|
||||
return false;
|
||||
}
|
||||
frame.name.resize(frame.frame_info.name_length);
|
||||
ppf->frames.emplace_back(std::move(frame));
|
||||
progression_index = 0;
|
||||
} else if (status == JXL_DEC_FRAME_PROGRESSION) {
|
||||
size_t downsampling = JxlDecoderGetIntendedDownsamplingRatio(dec);
|
||||
if ((max_passes_defined && progression_index >= dparams.max_passes) ||
|
||||
(!max_passes_defined && downsampling <= dparams.max_downsampling)) {
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec)) {
|
||||
fprintf(stderr, "JxlDecoderFlushImage failed\n");
|
||||
return false;
|
||||
}
|
||||
if (ppf->frames.back().frame_info.is_last) {
|
||||
break;
|
||||
}
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSkipCurrentFrame(dec)) {
|
||||
fprintf(stderr, "JxlDecoderSkipCurrentFrame failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++progression_index;
|
||||
} else if (status == JXL_DEC_NEED_PREVIEW_OUT_BUFFER) {
|
||||
size_t buffer_size;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderPreviewOutBufferSize(dec, &format, &buffer_size)) {
|
||||
fprintf(stderr, "JxlDecoderPreviewOutBufferSize failed\n");
|
||||
return false;
|
||||
}
|
||||
ppf->preview_frame = std::unique_ptr<jxl::extras::PackedFrame>(
|
||||
new jxl::extras::PackedFrame(ppf->info.preview.xsize,
|
||||
ppf->info.preview.ysize, format));
|
||||
if (buffer_size != ppf->preview_frame->color.pixels_size) {
|
||||
fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n",
|
||||
buffer_size, ppf->preview_frame->color.pixels_size);
|
||||
return false;
|
||||
}
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetPreviewOutBuffer(
|
||||
dec, &format, ppf->preview_frame->color.pixels(), buffer_size)) {
|
||||
fprintf(stderr, "JxlDecoderSetPreviewOutBuffer failed\n");
|
||||
return false;
|
||||
}
|
||||
} else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
|
||||
if (jpeg_bytes != nullptr) {
|
||||
break;
|
||||
}
|
||||
size_t buffer_size;
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderImageOutBufferSize(dec, &format, &buffer_size)) {
|
||||
fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
|
||||
return false;
|
||||
}
|
||||
jxl::extras::PackedFrame& frame = ppf->frames.back();
|
||||
if (buffer_size != frame.color.pixels_size) {
|
||||
fprintf(stderr, "Invalid out buffer size %" PRIuS " %" PRIuS "\n",
|
||||
buffer_size, frame.color.pixels_size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dparams.use_image_callback) {
|
||||
auto callback = [](void* opaque, size_t x, size_t y, size_t num_pixels,
|
||||
const void* pixels) {
|
||||
auto* ppf = reinterpret_cast<jxl::extras::PackedPixelFile*>(opaque);
|
||||
jxl::extras::PackedImage& color = ppf->frames.back().color;
|
||||
uint8_t* pixels_buffer = reinterpret_cast<uint8_t*>(color.pixels());
|
||||
size_t sample_size = color.pixel_stride();
|
||||
memcpy(pixels_buffer + (color.stride * y + sample_size * x), pixels,
|
||||
num_pixels * sample_size);
|
||||
};
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetImageOutCallback(dec, &format, callback, ppf)) {
|
||||
fprintf(stderr, "JxlDecoderSetImageOutCallback failed\n");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec, &format,
|
||||
frame.color.pixels(),
|
||||
buffer_size)) {
|
||||
fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
JxlPixelFormat ec_format = format;
|
||||
ec_format.num_channels = 1;
|
||||
for (const auto& eci : ppf->extra_channels_info) {
|
||||
frame.extra_channels.emplace_back(jxl::extras::PackedImage(
|
||||
ppf->info.xsize, ppf->info.ysize, ec_format));
|
||||
auto& ec = frame.extra_channels.back();
|
||||
size_t buffer_size;
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderExtraChannelBufferSize(
|
||||
dec, &ec_format, &buffer_size, eci.index)) {
|
||||
fprintf(stderr, "JxlDecoderExtraChannelBufferSize failed\n");
|
||||
return false;
|
||||
}
|
||||
if (buffer_size != ec.pixels_size) {
|
||||
fprintf(stderr,
|
||||
"Invalid extra channel buffer size"
|
||||
" %" PRIuS " %" PRIuS "\n",
|
||||
buffer_size, ec.pixels_size);
|
||||
return false;
|
||||
}
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetExtraChannelBuffer(dec, &ec_format, ec.pixels(),
|
||||
buffer_size, eci.index)) {
|
||||
fprintf(stderr, "JxlDecoderSetExtraChannelBuffer failed\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else if (status == JXL_DEC_SUCCESS) {
|
||||
// Decoding finished successfully.
|
||||
break;
|
||||
} else if (status == JXL_DEC_PREVIEW_IMAGE) {
|
||||
// Nothing to do.
|
||||
} else if (status == JXL_DEC_FULL_IMAGE) {
|
||||
if (jpeg_bytes != nullptr || ppf->frames.back().frame_info.is_last) {
|
||||
codestream_done = true;
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "Error: unexpected status: %d\n",
|
||||
static_cast<int>(status));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
boxes.FinalizeOutput();
|
||||
if (jpeg_bytes != nullptr) {
|
||||
if (!can_reconstruct_jpeg) return false;
|
||||
size_t used_jpeg_output =
|
||||
jpeg_data_chunk.size() - JxlDecoderReleaseJPEGBuffer(dec);
|
||||
jpeg_bytes->insert(jpeg_bytes->end(), jpeg_data_chunk.data(),
|
||||
jpeg_data_chunk.data() + used_jpeg_output);
|
||||
}
|
||||
if (decoded_bytes) {
|
||||
*decoded_bytes = bytes_size - JxlDecoderReleaseInput(dec);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
66
third_party/jpeg-xl/lib/extras/dec/jxl.h
vendored
Normal file
66
third_party/jpeg-xl/lib/extras/dec/jxl.h
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_DEC_JXL_H_
|
||||
#define LIB_EXTRAS_DEC_JXL_H_
|
||||
|
||||
// Decodes JPEG XL images in memory.
|
||||
|
||||
#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 {
|
||||
namespace extras {
|
||||
|
||||
struct JXLDecompressParams {
|
||||
// If empty, little endian float formats will be accepted.
|
||||
std::vector<JxlPixelFormat> accepted_formats;
|
||||
|
||||
// Requested output color space description.
|
||||
std::string color_space;
|
||||
// If set, performs tone mapping to this intensity target luminance.
|
||||
float display_nits = 0.0;
|
||||
// Whether spot colors are rendered on the image.
|
||||
bool render_spotcolors = true;
|
||||
// Whether to keep or undo the orientation given in the header.
|
||||
bool keep_orientation = false;
|
||||
|
||||
// If runner_opaque is set, the decoder uses this parallel runner.
|
||||
JxlParallelRunner runner;
|
||||
void* runner_opaque = nullptr;
|
||||
|
||||
// Whether truncated input should be treated as an error.
|
||||
bool allow_partial_input = false;
|
||||
|
||||
// How many passes to decode at most. By default, decode everything.
|
||||
uint32_t max_passes = std::numeric_limits<uint32_t>::max();
|
||||
|
||||
// Alternatively, one can specify the maximum tolerable downscaling factor
|
||||
// with respect to the full size of the image. By default, nothing less than
|
||||
// the full size is requested.
|
||||
size_t max_downsampling = 1;
|
||||
|
||||
// Whether to use the image callback or the image buffer to get the output.
|
||||
bool use_image_callback = true;
|
||||
// Whether to unpremultiply colors for associated alpha channels.
|
||||
bool unpremultiply_alpha = false;
|
||||
};
|
||||
|
||||
bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
|
||||
const JXLDecompressParams& dparams, size_t* decoded_bytes,
|
||||
PackedPixelFile* ppf,
|
||||
std::vector<uint8_t>* jpeg_bytes = nullptr);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_DEC_JXL_H_
|
||||
249
third_party/jpeg-xl/lib/extras/enc/apng.cc
vendored
249
third_party/jpeg-xl/lib/extras/enc/apng.cc
vendored
|
|
@ -42,18 +42,9 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "jxl/encode.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/byte_order.h"
|
||||
#include "lib/jxl/base/printf_macros.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
#include "lib/jxl/dec_external_image.h"
|
||||
#include "lib/jxl/enc_color_management.h"
|
||||
#include "lib/jxl/enc_image_bundle.h"
|
||||
#include "lib/jxl/exif.h"
|
||||
#include "lib/jxl/frame_header.h"
|
||||
#include "lib/jxl/headers.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/image_bundle.h"
|
||||
#include "png.h" /* original (unpatched) libpng is ok */
|
||||
|
||||
namespace jxl {
|
||||
|
|
@ -62,14 +53,16 @@ namespace extras {
|
|||
namespace {
|
||||
|
||||
static void PngWrite(png_structp png_ptr, png_bytep data, png_size_t length) {
|
||||
PaddedBytes* bytes = static_cast<PaddedBytes*>(png_get_io_ptr(png_ptr));
|
||||
bytes->append(data, data + length);
|
||||
std::vector<uint8_t>* bytes =
|
||||
static_cast<std::vector<uint8_t>*>(png_get_io_ptr(png_ptr));
|
||||
bytes->insert(bytes->end(), data, data + length);
|
||||
}
|
||||
|
||||
// Stores XMP and EXIF/IPTC into key/value strings for PNG
|
||||
class BlobsWriterPNG {
|
||||
public:
|
||||
static Status Encode(const Blobs& blobs, std::vector<std::string>* strings) {
|
||||
static Status Encode(const PackedMetadata& blobs,
|
||||
std::vector<std::string>* strings) {
|
||||
if (!blobs.exif.empty()) {
|
||||
// PNG viewers typically ignore Exif orientation but not all of them do
|
||||
// (and e.g. cjxl doesn't), so we overwrite the Exif orientation to the
|
||||
|
|
@ -122,26 +115,136 @@ class BlobsWriterPNG {
|
|||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
void MaybeAddCICP(JxlColorEncoding c_enc, png_structp png_ptr,
|
||||
png_infop info_ptr) {
|
||||
png_byte cicp_data[4] = {};
|
||||
png_unknown_chunk cicp_chunk;
|
||||
if (c_enc.color_space != JXL_COLOR_SPACE_RGB) {
|
||||
return;
|
||||
}
|
||||
if (c_enc.primaries == JXL_PRIMARIES_P3) {
|
||||
if (c_enc.white_point == JXL_WHITE_POINT_D65) {
|
||||
cicp_data[0] = 12;
|
||||
} else if (c_enc.white_point == JXL_WHITE_POINT_DCI) {
|
||||
cicp_data[0] = 11;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if (c_enc.primaries != JXL_PRIMARIES_CUSTOM &&
|
||||
c_enc.white_point == JXL_WHITE_POINT_D65) {
|
||||
cicp_data[0] = static_cast<png_byte>(c_enc.primaries);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
if (c_enc.transfer_function == JXL_TRANSFER_FUNCTION_UNKNOWN ||
|
||||
c_enc.transfer_function == JXL_TRANSFER_FUNCTION_GAMMA) {
|
||||
return;
|
||||
}
|
||||
cicp_data[1] = static_cast<png_byte>(c_enc.transfer_function);
|
||||
cicp_data[2] = 0;
|
||||
cicp_data[3] = 1;
|
||||
cicp_chunk.data = cicp_data;
|
||||
cicp_chunk.size = sizeof(cicp_data);
|
||||
cicp_chunk.location = PNG_HAVE_PLTE;
|
||||
memcpy(cicp_chunk.name, "cICP", 5);
|
||||
png_set_keep_unknown_chunks(png_ptr, 3,
|
||||
reinterpret_cast<const png_byte*>("cICP"), 1);
|
||||
png_set_unknown_chunks(png_ptr, info_ptr, &cicp_chunk, 1);
|
||||
}
|
||||
|
||||
Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
||||
size_t bits_per_sample, ThreadPool* pool,
|
||||
PaddedBytes* bytes) {
|
||||
if (bits_per_sample > 8) {
|
||||
bits_per_sample = 16;
|
||||
} else if (bits_per_sample < 8) {
|
||||
// PNG can also do 4, 2, and 1 bits per sample, but it isn't implemented
|
||||
bits_per_sample = 8;
|
||||
Status EncodePackedPixelFileToAPNG(const PackedPixelFile& ppf, ThreadPool* pool,
|
||||
std::vector<uint8_t>* bytes) {
|
||||
size_t xsize = ppf.info.xsize;
|
||||
size_t ysize = ppf.info.ysize;
|
||||
bool has_alpha = ppf.info.alpha_bits != 0;
|
||||
bool is_gray = ppf.info.num_color_channels == 1;
|
||||
size_t color_channels = ppf.info.num_color_channels;
|
||||
size_t num_channels = color_channels + (has_alpha ? 1 : 0);
|
||||
size_t num_samples = num_channels * xsize * ysize;
|
||||
|
||||
if (xsize == 0 || ysize == 0 || ppf.frames.empty()) {
|
||||
return JXL_FAILURE("Empty image");
|
||||
}
|
||||
if (has_alpha && ppf.info.alpha_bits != ppf.info.bits_per_sample) {
|
||||
return JXL_FAILURE("Alpha bit depth does not match image bit depth");
|
||||
}
|
||||
if (color_channels != 1 && color_channels != 3) {
|
||||
return JXL_FAILURE("Invalid number of color channels");
|
||||
}
|
||||
if (!ppf.info.have_animation && ppf.frames.size() != 1) {
|
||||
return JXL_FAILURE("Invalid number of frames");
|
||||
}
|
||||
if (ppf.info.orientation != JXL_ORIENT_IDENTITY) {
|
||||
return JXL_FAILURE("Orientation must be identity");
|
||||
}
|
||||
|
||||
size_t count = 0;
|
||||
bool have_anim = io->metadata.m.have_animation;
|
||||
size_t anim_chunks = 0;
|
||||
int W = 0, H = 0;
|
||||
|
||||
for (size_t i = 0; i < io->frames.size(); i++) {
|
||||
auto& frame = io->frames[i];
|
||||
if (!have_anim && i + 1 < io->frames.size()) continue;
|
||||
for (const auto& frame : ppf.frames) {
|
||||
const PackedImage& color = frame.color;
|
||||
const JxlPixelFormat format = color.format;
|
||||
const uint8_t* in = reinterpret_cast<const uint8_t*>(color.pixels());
|
||||
size_t data_bits_per_sample = PackedImage::BitsPerChannel(format.data_type);
|
||||
size_t bytes_per_sample = data_bits_per_sample / 8;
|
||||
size_t out_bytes_per_sample = bytes_per_sample > 1 ? 2 : 1;
|
||||
size_t out_stride = xsize * num_channels * out_bytes_per_sample;
|
||||
size_t out_size = ysize * out_stride;
|
||||
|
||||
if (in == nullptr || color.pixels_size < bytes_per_sample * num_samples) {
|
||||
return JXL_FAILURE("Invalid frame");
|
||||
}
|
||||
if (color.xsize != xsize || color.ysize != ysize ||
|
||||
format.num_channels != num_channels) {
|
||||
return JXL_FAILURE("Frame size does not match image size");
|
||||
}
|
||||
if (ppf.info.bits_per_sample > data_bits_per_sample) {
|
||||
return JXL_FAILURE("Bit depth does not fit pixel data type");
|
||||
}
|
||||
if (color.flipped_y) {
|
||||
return JXL_FAILURE("Flipped y channel not supported");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> out(out_size);
|
||||
|
||||
if (format.data_type == JXL_TYPE_UINT8) {
|
||||
if (ppf.info.bits_per_sample < 8) {
|
||||
float mul = 255.0 / ((1u << ppf.info.bits_per_sample) - 1);
|
||||
for (size_t i = 0; i < num_samples; ++i) {
|
||||
out[i] = static_cast<uint8_t>(in[i] * mul + 0.5);
|
||||
}
|
||||
} else {
|
||||
memcpy(&out[0], in, out_size);
|
||||
}
|
||||
} else if (format.data_type == JXL_TYPE_UINT16) {
|
||||
if (ppf.info.bits_per_sample < 16 ||
|
||||
format.endianness != JXL_BIG_ENDIAN) {
|
||||
float mul = 65535.0 / ((1u << ppf.info.bits_per_sample) - 1);
|
||||
const uint8_t* p_in = in;
|
||||
uint8_t* p_out = out.data();
|
||||
for (size_t i = 0; i < num_samples; ++i, p_in += 2, p_out += 2) {
|
||||
uint32_t val = (format.endianness == JXL_BIG_ENDIAN ? LoadBE16(p_in)
|
||||
: LoadLE16(p_in));
|
||||
StoreBE16(static_cast<uint32_t>(val * mul + 0.5), p_out);
|
||||
}
|
||||
} else {
|
||||
memcpy(&out[0], in, out_size);
|
||||
}
|
||||
} else if (format.data_type == JXL_TYPE_FLOAT) {
|
||||
float mul = 65535.0;
|
||||
const uint8_t* p_in = in;
|
||||
uint8_t* p_out = out.data();
|
||||
for (size_t i = 0; i < num_samples; ++i, p_in += 4, p_out += 2) {
|
||||
uint32_t val = (format.endianness == JXL_BIG_ENDIAN ? LoadBE32(p_in)
|
||||
: LoadLE32(p_in));
|
||||
float fval;
|
||||
memcpy(&fval, &val, 4);
|
||||
StoreBE16(static_cast<uint32_t>(fval * mul + 0.5), p_out);
|
||||
}
|
||||
} else {
|
||||
return JXL_FAILURE("Unsupported pixel data type");
|
||||
}
|
||||
|
||||
png_structp png_ptr;
|
||||
png_infop info_ptr;
|
||||
|
||||
|
|
@ -155,46 +258,24 @@ Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
png_set_write_fn(png_ptr, bytes, PngWrite, NULL);
|
||||
png_set_flush(png_ptr, 0);
|
||||
|
||||
ImageBundle ib = frame.Copy();
|
||||
const size_t alpha_bits = ib.HasAlpha() ? bits_per_sample : 0;
|
||||
ImageMetadata metadata = io->metadata.m;
|
||||
ImageBundle store(&metadata);
|
||||
const ImageBundle* transformed;
|
||||
JXL_RETURN_IF_ERROR(TransformIfNeeded(ib, c_desired, GetJxlCms(), pool,
|
||||
&store, &transformed));
|
||||
size_t stride = ib.oriented_xsize() *
|
||||
DivCeil(c_desired.Channels() * bits_per_sample + alpha_bits,
|
||||
kBitsPerByte);
|
||||
std::vector<uint8_t> raw_bytes(stride * ib.oriented_ysize());
|
||||
JXL_RETURN_IF_ERROR(ConvertToExternal(
|
||||
*transformed, bits_per_sample, /*float_out=*/false,
|
||||
c_desired.Channels() + (ib.HasAlpha() ? 1 : 0), JXL_BIG_ENDIAN, stride,
|
||||
pool, raw_bytes.data(), raw_bytes.size(),
|
||||
/*out_callback=*/{}, metadata.GetOrientation()));
|
||||
int width = xsize;
|
||||
int height = ysize;
|
||||
|
||||
int width = ib.oriented_xsize();
|
||||
int height = ib.oriented_ysize();
|
||||
|
||||
png_byte color_type =
|
||||
(c_desired.Channels() == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_GRAY);
|
||||
if (ib.HasAlpha()) color_type |= PNG_COLOR_MASK_ALPHA;
|
||||
png_byte bit_depth = bits_per_sample;
|
||||
png_byte color_type = (is_gray ? PNG_COLOR_TYPE_GRAY : PNG_COLOR_TYPE_RGB);
|
||||
if (has_alpha) color_type |= PNG_COLOR_MASK_ALPHA;
|
||||
png_byte bit_depth = out_bytes_per_sample * 8;
|
||||
|
||||
png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type,
|
||||
PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
|
||||
PNG_FILTER_TYPE_BASE);
|
||||
if (count == 0) {
|
||||
W = width;
|
||||
H = height;
|
||||
|
||||
// TODO(jon): instead of always setting an iCCP, could try to avoid that
|
||||
// have to avoid warnings on the ICC profile becoming fatal
|
||||
png_set_benign_errors(png_ptr, 1);
|
||||
png_set_iCCP(png_ptr, info_ptr, "1", 0, c_desired.ICC().data(),
|
||||
c_desired.ICC().size());
|
||||
|
||||
MaybeAddCICP(ppf.color_encoding, png_ptr, info_ptr);
|
||||
if (!ppf.icc.empty()) {
|
||||
png_set_benign_errors(png_ptr, 1);
|
||||
png_set_iCCP(png_ptr, info_ptr, "1", 0, ppf.icc.data(), ppf.icc.size());
|
||||
}
|
||||
std::vector<std::string> textstrings;
|
||||
JXL_RETURN_IF_ERROR(BlobsWriterPNG::Encode(io->blobs, &textstrings));
|
||||
JXL_RETURN_IF_ERROR(BlobsWriterPNG::Encode(ppf.metadata, &textstrings));
|
||||
for (size_t kk = 0; kk + 1 < textstrings.size(); kk += 2) {
|
||||
png_text text;
|
||||
text.key = const_cast<png_charp>(textstrings[kk].c_str());
|
||||
|
|
@ -211,27 +292,24 @@ Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
bytes->resize(pos);
|
||||
}
|
||||
|
||||
if (have_anim) {
|
||||
if (ppf.info.have_animation) {
|
||||
if (count == 0) {
|
||||
png_byte adata[8];
|
||||
png_save_uint_32(adata, io->frames.size());
|
||||
png_save_uint_32(adata + 4, io->metadata.m.animation.num_loops);
|
||||
png_save_uint_32(adata, ppf.frames.size());
|
||||
png_save_uint_32(adata + 4, ppf.info.animation.num_loops);
|
||||
png_byte actl[5] = "acTL";
|
||||
png_write_chunk(png_ptr, actl, adata, 8);
|
||||
}
|
||||
png_byte fdata[26];
|
||||
JXL_ASSERT(W == width);
|
||||
JXL_ASSERT(H == height);
|
||||
// TODO(jon): also make this work for the non-coalesced case
|
||||
png_save_uint_32(fdata, anim_chunks++);
|
||||
png_save_uint_32(fdata + 4, width);
|
||||
png_save_uint_32(fdata + 8, height);
|
||||
png_save_uint_32(fdata + 12, 0);
|
||||
png_save_uint_32(fdata + 16, 0);
|
||||
png_save_uint_16(
|
||||
fdata + 20,
|
||||
frame.duration * io->metadata.m.animation.tps_denominator);
|
||||
png_save_uint_16(fdata + 22, io->metadata.m.animation.tps_numerator);
|
||||
png_save_uint_16(fdata + 20, frame.frame_info.duration *
|
||||
ppf.info.animation.tps_denominator);
|
||||
png_save_uint_16(fdata + 22, ppf.info.animation.tps_numerator);
|
||||
fdata[24] = 1;
|
||||
fdata[25] = 0;
|
||||
png_byte fctl[5] = "fcTL";
|
||||
|
|
@ -240,7 +318,7 @@ Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
|
||||
std::vector<uint8_t*> rows(height);
|
||||
for (int y = 0; y < height; ++y) {
|
||||
rows[y] = raw_bytes.data() + y * stride;
|
||||
rows[y] = out.data() + y * out_stride;
|
||||
}
|
||||
|
||||
png_write_flush(png_ptr);
|
||||
|
|
@ -268,7 +346,9 @@ Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
}
|
||||
|
||||
count++;
|
||||
if (count == io->frames.size() || !have_anim) png_write_end(png_ptr, NULL);
|
||||
if (count == ppf.frames.size() || !ppf.info.have_animation) {
|
||||
png_write_end(png_ptr, NULL);
|
||||
}
|
||||
|
||||
png_destroy_write_struct(&png_ptr, &info_ptr);
|
||||
}
|
||||
|
|
@ -276,5 +356,32 @@ Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
return true;
|
||||
}
|
||||
|
||||
class APNGEncoder : public Encoder {
|
||||
public:
|
||||
std::vector<JxlPixelFormat> AcceptedFormats() const override {
|
||||
std::vector<JxlPixelFormat> formats;
|
||||
for (const uint32_t num_channels : {1, 2, 3, 4}) {
|
||||
for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) {
|
||||
formats.push_back(JxlPixelFormat{num_channels, data_type,
|
||||
JXL_BIG_ENDIAN, /*align=*/0});
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
|
||||
ThreadPool* pool) const override {
|
||||
encoded_image->icc.clear();
|
||||
encoded_image->bitstreams.resize(1);
|
||||
return EncodePackedPixelFileToAPNG(ppf, pool,
|
||||
&encoded_image->bitstreams.front());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Encoder> GetAPNGEncoder() {
|
||||
return jxl::make_unique<APNGEncoder>();
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
13
third_party/jpeg-xl/lib/extras/enc/apng.h
vendored
13
third_party/jpeg-xl/lib/extras/enc/apng.h
vendored
|
|
@ -8,19 +8,14 @@
|
|||
|
||||
// Encodes APNG images in memory.
|
||||
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/codec_in_out.h"
|
||||
#include <memory>
|
||||
|
||||
#include "lib/extras/enc/encode.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
// Encodes `io` into `bytes`.
|
||||
Status EncodeImageAPNG(const CodecInOut* io, const ColorEncoding& c_desired,
|
||||
size_t bits_per_sample, ThreadPool* pool,
|
||||
PaddedBytes* bytes);
|
||||
std::unique_ptr<Encoder> GetAPNGEncoder();
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
96
third_party/jpeg-xl/lib/extras/enc/encode.cc
vendored
Normal file
96
third_party/jpeg-xl/lib/extras/enc/encode.cc
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/enc/encode.h"
|
||||
|
||||
#include <locale>
|
||||
|
||||
#if JPEGXL_ENABLE_APNG
|
||||
#include "lib/extras/enc/apng.h"
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_EXR
|
||||
#include "lib/extras/enc/exr.h"
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_JPEG
|
||||
#include "lib/extras/enc/jpg.h"
|
||||
#endif
|
||||
#include "lib/extras/enc/npy.h"
|
||||
#include "lib/extras/enc/pgx.h"
|
||||
#include "lib/extras/enc/pnm.h"
|
||||
#include "lib/jxl/base/printf_macros.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
Status SelectFormat(const std::vector<JxlPixelFormat>& accepted_formats,
|
||||
const JxlBasicInfo& basic_info, JxlPixelFormat* format) {
|
||||
const size_t original_bit_depth = basic_info.bits_per_sample;
|
||||
size_t current_bit_depth = 0;
|
||||
size_t num_alpha_channels = (basic_info.alpha_bits != 0 ? 1 : 0);
|
||||
size_t num_channels = basic_info.num_color_channels + num_alpha_channels;
|
||||
for (;;) {
|
||||
for (const JxlPixelFormat& candidate : accepted_formats) {
|
||||
if (candidate.num_channels != num_channels) continue;
|
||||
const size_t candidate_bit_depth =
|
||||
PackedImage::BitsPerChannel(candidate.data_type);
|
||||
if (
|
||||
// Candidate bit depth is less than what we have and still enough
|
||||
(original_bit_depth <= candidate_bit_depth &&
|
||||
candidate_bit_depth < current_bit_depth) ||
|
||||
// Or larger than the too-small bit depth we currently have
|
||||
(current_bit_depth < candidate_bit_depth &&
|
||||
current_bit_depth < original_bit_depth)) {
|
||||
*format = candidate;
|
||||
current_bit_depth = candidate_bit_depth;
|
||||
}
|
||||
}
|
||||
if (current_bit_depth == 0) {
|
||||
if (num_channels > basic_info.num_color_channels) {
|
||||
// Try dropping the alpha channel.
|
||||
--num_channels;
|
||||
continue;
|
||||
}
|
||||
return JXL_FAILURE("no appropriate format found");
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (current_bit_depth < original_bit_depth) {
|
||||
JXL_WARNING("encoding %" PRIuS "-bit original to %" PRIuS " bits",
|
||||
original_bit_depth, current_bit_depth);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<Encoder> Encoder::FromExtension(std::string extension) {
|
||||
std::transform(
|
||||
extension.begin(), extension.end(), extension.begin(),
|
||||
[](char c) { return std::tolower(c, std::locale::classic()); });
|
||||
#if JPEGXL_ENABLE_APNG
|
||||
if (extension == ".png" || extension == ".apng") return GetAPNGEncoder();
|
||||
#endif
|
||||
|
||||
#if JPEGXL_ENABLE_JPEG
|
||||
if (extension == ".jpg") return GetJPEGEncoder();
|
||||
if (extension == ".jpeg") return GetJPEGEncoder();
|
||||
#endif
|
||||
|
||||
if (extension == ".npy") return GetNumPyEncoder();
|
||||
|
||||
if (extension == ".pgx") return GetPGXEncoder();
|
||||
|
||||
if (extension == ".pam") return GetPAMEncoder();
|
||||
if (extension == ".pgm") return GetPGMEncoder();
|
||||
if (extension == ".ppm") return GetPPMEncoder();
|
||||
if (extension == ".pfm") return GetPFMEncoder();
|
||||
|
||||
#if JPEGXL_ENABLE_EXR
|
||||
if (extension == ".exr") return GetEXREncoder();
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
72
third_party/jpeg-xl/lib/extras/enc/encode.h
vendored
Normal file
72
third_party/jpeg-xl/lib/extras/enc/encode.h
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_ENC_ENCODE_H_
|
||||
#define LIB_EXTRAS_ENC_ENCODE_H_
|
||||
|
||||
// Facade for image encoders.
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "lib/extras/dec/decode.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
struct EncodedImage {
|
||||
// One (if the format supports animations or the image has only one frame) or
|
||||
// more sequential bitstreams.
|
||||
std::vector<std::vector<uint8_t>> bitstreams;
|
||||
|
||||
// For each extra channel one or more sequential bitstreams.
|
||||
std::vector<std::vector<std::vector<uint8_t>>> extra_channel_bitstreams;
|
||||
|
||||
std::vector<uint8_t> preview_bitstream;
|
||||
|
||||
// If the format does not support embedding color profiles into the bitstreams
|
||||
// above, it will be present here, to be written as a separate file. If it
|
||||
// does support them, this field will be empty.
|
||||
std::vector<uint8_t> icc;
|
||||
|
||||
// Additional output for conformance testing, only filled in by NumPyEncoder.
|
||||
std::vector<uint8_t> metadata;
|
||||
};
|
||||
|
||||
class Encoder {
|
||||
public:
|
||||
static std::unique_ptr<Encoder> FromExtension(std::string extension);
|
||||
|
||||
virtual ~Encoder() = default;
|
||||
|
||||
virtual std::vector<JxlPixelFormat> AcceptedFormats() const = 0;
|
||||
|
||||
// Any existing data in encoded_image is discarded.
|
||||
virtual Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
|
||||
ThreadPool* pool = nullptr) const = 0;
|
||||
|
||||
void SetOption(std::string name, std::string value) {
|
||||
options_[std::move(name)] = std::move(value);
|
||||
}
|
||||
|
||||
protected:
|
||||
const std::unordered_map<std::string, std::string>& options() const {
|
||||
return options_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::string> options_;
|
||||
};
|
||||
|
||||
// TODO(sboukortt): consider exposing this as part of the C API.
|
||||
Status SelectFormat(const std::vector<JxlPixelFormat>& accepted_formats,
|
||||
const JxlBasicInfo& basic_info, JxlPixelFormat* format);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_ENC_ENCODE_H_
|
||||
90
third_party/jpeg-xl/lib/extras/enc/exr.cc
vendored
90
third_party/jpeg-xl/lib/extras/enc/exr.cc
vendored
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/packed_image_convert.h"
|
||||
#include "lib/jxl/alpha.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
#include "lib/jxl/color_management.h"
|
||||
|
|
@ -47,7 +48,7 @@ size_t GetNumThreads(ThreadPool* pool) {
|
|||
class InMemoryOStream : public OpenEXR::OStream {
|
||||
public:
|
||||
// `bytes` must outlive the InMemoryOStream.
|
||||
explicit InMemoryOStream(PaddedBytes* const bytes)
|
||||
explicit InMemoryOStream(std::vector<uint8_t>* const bytes)
|
||||
: OStream(/*fileName=*/""), bytes_(*bytes) {}
|
||||
|
||||
void write(const char c[], const int n) override {
|
||||
|
|
@ -67,14 +68,12 @@ class InMemoryOStream : public OpenEXR::OStream {
|
|||
}
|
||||
|
||||
private:
|
||||
PaddedBytes& bytes_;
|
||||
std::vector<uint8_t>& bytes_;
|
||||
size_t pos_ = 0;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
|
||||
ThreadPool* pool, PaddedBytes* bytes) {
|
||||
Status EncodeImageEXR(const ImageBundle& ib, const ColorEncoding& c_desired,
|
||||
ThreadPool* pool, std::vector<uint8_t>* bytes) {
|
||||
// As in `DecodeImageEXR`, `pool` is only used for pixel conversion, not for
|
||||
// actual OpenEXR I/O.
|
||||
OpenEXR::setGlobalThreadCount(GetNumThreads(pool));
|
||||
|
|
@ -82,16 +81,16 @@ Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
ColorEncoding c_linear = c_desired;
|
||||
c_linear.tf.SetTransferFunction(TransferFunction::kLinear);
|
||||
JXL_RETURN_IF_ERROR(c_linear.CreateICC());
|
||||
ImageMetadata metadata = io->metadata.m;
|
||||
ImageMetadata metadata = *ib.metadata();
|
||||
ImageBundle store(&metadata);
|
||||
const ImageBundle* linear;
|
||||
JXL_RETURN_IF_ERROR(TransformIfNeeded(io->Main(), c_linear, GetJxlCms(), pool,
|
||||
&store, &linear));
|
||||
JXL_RETURN_IF_ERROR(
|
||||
TransformIfNeeded(ib, c_linear, GetJxlCms(), pool, &store, &linear));
|
||||
|
||||
const bool has_alpha = io->Main().HasAlpha();
|
||||
const bool alpha_is_premultiplied = io->Main().AlphaIsPremultiplied();
|
||||
const bool has_alpha = ib.HasAlpha();
|
||||
const bool alpha_is_premultiplied = ib.AlphaIsPremultiplied();
|
||||
|
||||
OpenEXR::Header header(io->xsize(), io->ysize());
|
||||
OpenEXR::Header header(ib.xsize(), ib.ysize());
|
||||
const PrimariesCIExy& primaries = c_linear.HasPrimaries()
|
||||
? c_linear.GetPrimaries()
|
||||
: ColorEncoding::SRGB().GetPrimaries();
|
||||
|
|
@ -102,7 +101,7 @@ Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
chromaticities.white =
|
||||
Imath::V2f(c_linear.GetWhitePoint().x, c_linear.GetWhitePoint().y);
|
||||
OpenEXR::addChromaticities(header, chromaticities);
|
||||
OpenEXR::addWhiteLuminance(header, io->metadata.m.IntensityTarget());
|
||||
OpenEXR::addWhiteLuminance(header, ib.metadata()->IntensityTarget());
|
||||
|
||||
// Ensure that the destructor of RgbaOutputFile has run before we look at the
|
||||
// size of `bytes`.
|
||||
|
|
@ -112,15 +111,14 @@ Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
os, header, has_alpha ? OpenEXR::WRITE_RGBA : OpenEXR::WRITE_RGB);
|
||||
// How many rows to write at once. Again, the OpenEXR documentation
|
||||
// recommends writing the whole image in one call.
|
||||
const int y_chunk_size = io->ysize();
|
||||
std::vector<OpenEXR::Rgba> output_rows(io->xsize() * y_chunk_size);
|
||||
const int y_chunk_size = ib.ysize();
|
||||
std::vector<OpenEXR::Rgba> output_rows(ib.xsize() * y_chunk_size);
|
||||
|
||||
for (size_t start_y = 0; start_y < io->ysize(); start_y += y_chunk_size) {
|
||||
for (size_t start_y = 0; start_y < ib.ysize(); start_y += y_chunk_size) {
|
||||
// Inclusive.
|
||||
const size_t end_y =
|
||||
std::min(start_y + y_chunk_size - 1, io->ysize() - 1);
|
||||
output.setFrameBuffer(output_rows.data() - start_y * io->xsize(),
|
||||
/*xStride=*/1, /*yStride=*/io->xsize());
|
||||
const size_t end_y = std::min(start_y + y_chunk_size - 1, ib.ysize() - 1);
|
||||
output.setFrameBuffer(output_rows.data() - start_y * ib.xsize(),
|
||||
/*xStride=*/1, /*yStride=*/ib.xsize());
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, start_y, end_y + 1, ThreadPool::NoInit,
|
||||
[&](const uint32_t y, size_t /* thread */) {
|
||||
|
|
@ -130,18 +128,18 @@ Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
linear->color().ConstPlaneRow(2, y),
|
||||
};
|
||||
OpenEXR::Rgba* const JXL_RESTRICT row_data =
|
||||
&output_rows[(y - start_y) * io->xsize()];
|
||||
&output_rows[(y - start_y) * ib.xsize()];
|
||||
if (has_alpha) {
|
||||
const float* const JXL_RESTRICT alpha_row =
|
||||
io->Main().alpha().ConstRow(y);
|
||||
ib.alpha().ConstRow(y);
|
||||
if (alpha_is_premultiplied) {
|
||||
for (size_t x = 0; x < io->xsize(); ++x) {
|
||||
for (size_t x = 0; x < ib.xsize(); ++x) {
|
||||
row_data[x] =
|
||||
OpenEXR::Rgba(input_rows[0][x], input_rows[1][x],
|
||||
input_rows[2][x], alpha_row[x]);
|
||||
}
|
||||
} else {
|
||||
for (size_t x = 0; x < io->xsize(); ++x) {
|
||||
for (size_t x = 0; x < ib.xsize(); ++x) {
|
||||
row_data[x] = OpenEXR::Rgba(alpha_row[x] * input_rows[0][x],
|
||||
alpha_row[x] * input_rows[1][x],
|
||||
alpha_row[x] * input_rows[2][x],
|
||||
|
|
@ -149,7 +147,7 @@ Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
}
|
||||
}
|
||||
} else {
|
||||
for (size_t x = 0; x < io->xsize(); ++x) {
|
||||
for (size_t x = 0; x < ib.xsize(); ++x) {
|
||||
row_data[x] = OpenEXR::Rgba(input_rows[0][x], input_rows[1][x],
|
||||
input_rows[2][x], 1.f);
|
||||
}
|
||||
|
|
@ -163,5 +161,47 @@ Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
return true;
|
||||
}
|
||||
|
||||
class EXREncoder : public Encoder {
|
||||
std::vector<JxlPixelFormat> AcceptedFormats() const override {
|
||||
std::vector<JxlPixelFormat> formats;
|
||||
for (const uint32_t num_channels : {1, 2, 3, 4}) {
|
||||
for (const JxlDataType data_type : {JXL_TYPE_FLOAT, JXL_TYPE_FLOAT16}) {
|
||||
for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) {
|
||||
formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels,
|
||||
/*data_type=*/data_type,
|
||||
/*endianness=*/endianness,
|
||||
/*align=*/0});
|
||||
}
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
|
||||
ThreadPool* pool = nullptr) const override {
|
||||
encoded_image->icc.clear();
|
||||
CodecInOut io;
|
||||
JXL_RETURN_IF_ERROR(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io));
|
||||
encoded_image->bitstreams.clear();
|
||||
encoded_image->bitstreams.reserve(io.frames.size());
|
||||
for (const ImageBundle& ib : io.frames) {
|
||||
encoded_image->bitstreams.emplace_back();
|
||||
JXL_RETURN_IF_ERROR(EncodeImageEXR(ib, io.metadata.m.color_encoding, pool,
|
||||
&encoded_image->bitstreams.back()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Encoder> GetEXREncoder() {
|
||||
return jxl::make_unique<EXREncoder>();
|
||||
}
|
||||
|
||||
Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
|
||||
ThreadPool* pool, std::vector<uint8_t>* bytes) {
|
||||
return EncodeImageEXR(io->Main(), c_desired, pool, bytes);
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
5
third_party/jpeg-xl/lib/extras/enc/exr.h
vendored
5
third_party/jpeg-xl/lib/extras/enc/exr.h
vendored
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
// Encodes OpenEXR images in memory.
|
||||
|
||||
#include "lib/extras/enc/encode.h"
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
|
|
@ -19,10 +20,12 @@
|
|||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
std::unique_ptr<Encoder> GetEXREncoder();
|
||||
|
||||
// Transforms from io->c_current to `c_desired` (with the transfer function set
|
||||
// to linear as that is the OpenEXR convention) and encodes into `bytes`.
|
||||
Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
|
||||
ThreadPool* pool, PaddedBytes* bytes);
|
||||
ThreadPool* pool, std::vector<uint8_t>* bytes);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
163
third_party/jpeg-xl/lib/extras/enc/jpg.cc
vendored
163
third_party/jpeg-xl/lib/extras/enc/jpg.cc
vendored
|
|
@ -12,9 +12,11 @@
|
|||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/packed_image_convert.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
|
|
@ -93,12 +95,10 @@ Status SetChromaSubsampling(const YCbCrChromaSubsampling& chroma_subsampling,
|
|||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
|
||||
Status EncodeWithLibJpeg(const ImageBundle& ib, std::vector<uint8_t> exif,
|
||||
size_t quality,
|
||||
const YCbCrChromaSubsampling& chroma_subsampling,
|
||||
PaddedBytes* bytes) {
|
||||
std::vector<uint8_t>* bytes) {
|
||||
jpeg_compress_struct cinfo;
|
||||
// cinfo is initialized by libjpeg, which we are not instrumenting with
|
||||
// msan.
|
||||
|
|
@ -109,9 +109,9 @@ Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
|
|||
unsigned char* buffer = nullptr;
|
||||
unsigned long size = 0;
|
||||
jpeg_mem_dest(&cinfo, &buffer, &size);
|
||||
cinfo.image_width = ib->oriented_xsize();
|
||||
cinfo.image_height = ib->oriented_ysize();
|
||||
if (ib->IsGray()) {
|
||||
cinfo.image_width = ib.oriented_xsize();
|
||||
cinfo.image_height = ib.oriented_ysize();
|
||||
if (ib.IsGray()) {
|
||||
cinfo.input_components = 1;
|
||||
cinfo.in_color_space = JCS_GRAYSCALE;
|
||||
} else {
|
||||
|
|
@ -125,11 +125,10 @@ Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
|
|||
}
|
||||
jpeg_set_quality(&cinfo, quality, TRUE);
|
||||
jpeg_start_compress(&cinfo, TRUE);
|
||||
if (!ib->IsSRGB()) {
|
||||
WriteICCProfile(&cinfo, ib->c_current().ICC());
|
||||
if (!ib.IsSRGB()) {
|
||||
WriteICCProfile(&cinfo, ib.c_current().ICC());
|
||||
}
|
||||
if (!io->blobs.exif.empty()) {
|
||||
std::vector<uint8_t> exif = io->blobs.exif;
|
||||
if (!exif.empty()) {
|
||||
ResetExifOrientation(exif);
|
||||
WriteExif(&cinfo, exif);
|
||||
}
|
||||
|
|
@ -137,14 +136,14 @@ Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
|
|||
return JXL_FAILURE("invalid numbers of components");
|
||||
|
||||
size_t stride =
|
||||
ib->oriented_xsize() * cinfo.input_components * sizeof(JSAMPLE);
|
||||
PaddedBytes raw_bytes(stride * ib->oriented_ysize());
|
||||
ib.oriented_xsize() * cinfo.input_components * sizeof(JSAMPLE);
|
||||
std::vector<uint8_t> raw_bytes(stride * ib.oriented_ysize());
|
||||
JXL_RETURN_IF_ERROR(ConvertToExternal(
|
||||
*ib, BITS_IN_JSAMPLE, /*float_out=*/false, cinfo.input_components,
|
||||
ib, BITS_IN_JSAMPLE, /*float_out=*/false, cinfo.input_components,
|
||||
JXL_BIG_ENDIAN, stride, nullptr, raw_bytes.data(), raw_bytes.size(),
|
||||
/*out_callback=*/{}, ib->metadata()->GetOrientation()));
|
||||
/*out_callback=*/{}, ib.metadata()->GetOrientation()));
|
||||
|
||||
for (size_t y = 0; y < ib->oriented_ysize(); ++y) {
|
||||
for (size_t y = 0; y < ib.oriented_ysize(); ++y) {
|
||||
JSAMPROW row[] = {raw_bytes.data() + y * stride};
|
||||
|
||||
jpeg_write_scanlines(&cinfo, row, 1);
|
||||
|
|
@ -160,19 +159,18 @@ Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
|
|||
return true;
|
||||
}
|
||||
|
||||
Status EncodeWithSJpeg(const ImageBundle* ib, const CodecInOut* io,
|
||||
Status EncodeWithSJpeg(const ImageBundle& ib, std::vector<uint8_t> exif,
|
||||
size_t quality,
|
||||
const YCbCrChromaSubsampling& chroma_subsampling,
|
||||
PaddedBytes* bytes) {
|
||||
std::vector<uint8_t>* bytes) {
|
||||
#if !JPEGXL_ENABLE_SJPEG
|
||||
return JXL_FAILURE("JPEG XL was built without sjpeg support");
|
||||
#else
|
||||
sjpeg::EncoderParam param(quality);
|
||||
if (!ib->IsSRGB()) {
|
||||
param.iccp.assign(ib->metadata()->color_encoding.ICC().begin(),
|
||||
ib->metadata()->color_encoding.ICC().end());
|
||||
if (!ib.IsSRGB()) {
|
||||
param.iccp.assign(ib.metadata()->color_encoding.ICC().begin(),
|
||||
ib.metadata()->color_encoding.ICC().end());
|
||||
}
|
||||
std::vector<uint8_t> exif = io->blobs.exif;
|
||||
if (!exif.empty()) {
|
||||
ResetExifOrientation(exif);
|
||||
param.exif.assign(exif.begin(), exif.end());
|
||||
|
|
@ -184,16 +182,16 @@ Status EncodeWithSJpeg(const ImageBundle* ib, const CodecInOut* io,
|
|||
} else {
|
||||
return JXL_FAILURE("sjpeg does not support this chroma subsampling mode");
|
||||
}
|
||||
size_t stride = ib->oriented_xsize() * 3;
|
||||
PaddedBytes rgb(ib->xsize() * ib->ysize() * 3);
|
||||
size_t stride = ib.oriented_xsize() * 3;
|
||||
std::vector<uint8_t> rgb(ib.xsize() * ib.ysize() * 3);
|
||||
JXL_RETURN_IF_ERROR(
|
||||
ConvertToExternal(*ib, 8, /*float_out=*/false, 3, JXL_BIG_ENDIAN, stride,
|
||||
ConvertToExternal(ib, 8, /*float_out=*/false, 3, JXL_BIG_ENDIAN, stride,
|
||||
nullptr, rgb.data(), rgb.size(),
|
||||
/*out_callback=*/{}, ib->metadata()->GetOrientation()));
|
||||
/*out_callback=*/{}, ib.metadata()->GetOrientation()));
|
||||
|
||||
std::string output;
|
||||
JXL_RETURN_IF_ERROR(sjpeg::Encode(rgb.data(), ib->oriented_xsize(),
|
||||
ib->oriented_ysize(), stride, param,
|
||||
JXL_RETURN_IF_ERROR(sjpeg::Encode(rgb.data(), ib.oriented_xsize(),
|
||||
ib.oriented_ysize(), stride, param,
|
||||
&output));
|
||||
bytes->assign(
|
||||
reinterpret_cast<const uint8_t*>(output.data()),
|
||||
|
|
@ -202,31 +200,31 @@ Status EncodeWithSJpeg(const ImageBundle* ib, const CodecInOut* io,
|
|||
#endif
|
||||
}
|
||||
|
||||
Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
|
||||
Status EncodeImageJPG(const ImageBundle& ib, std::vector<uint8_t> exif,
|
||||
JpegEncoder encoder, size_t quality,
|
||||
YCbCrChromaSubsampling chroma_subsampling,
|
||||
ThreadPool* pool, PaddedBytes* bytes) {
|
||||
if (io->Main().HasAlpha()) {
|
||||
ThreadPool* pool, std::vector<uint8_t>* bytes) {
|
||||
if (ib.HasAlpha()) {
|
||||
return JXL_FAILURE("alpha is not supported");
|
||||
}
|
||||
if (quality > 100) {
|
||||
return JXL_FAILURE("please specify a 0-100 JPEG quality");
|
||||
}
|
||||
|
||||
const ImageBundle* ib;
|
||||
ImageMetadata metadata = io->metadata.m;
|
||||
const ImageBundle* transformed;
|
||||
ImageMetadata metadata = *ib.metadata();
|
||||
ImageBundle ib_store(&metadata);
|
||||
JXL_RETURN_IF_ERROR(TransformIfNeeded(io->Main(),
|
||||
io->metadata.m.color_encoding,
|
||||
GetJxlCms(), pool, &ib_store, &ib));
|
||||
JXL_RETURN_IF_ERROR(TransformIfNeeded(
|
||||
ib, metadata.color_encoding, GetJxlCms(), pool, &ib_store, &transformed));
|
||||
|
||||
switch (encoder) {
|
||||
case JpegEncoder::kLibJpeg:
|
||||
JXL_RETURN_IF_ERROR(
|
||||
EncodeWithLibJpeg(ib, io, quality, chroma_subsampling, bytes));
|
||||
JXL_RETURN_IF_ERROR(EncodeWithLibJpeg(ib, std::move(exif), quality,
|
||||
chroma_subsampling, bytes));
|
||||
break;
|
||||
case JpegEncoder::kSJpeg:
|
||||
JXL_RETURN_IF_ERROR(
|
||||
EncodeWithSJpeg(ib, io, quality, chroma_subsampling, bytes));
|
||||
JXL_RETURN_IF_ERROR(EncodeWithSJpeg(ib, std::move(exif), quality,
|
||||
chroma_subsampling, bytes));
|
||||
break;
|
||||
default:
|
||||
return JXL_FAILURE("tried to use an unknown JPEG encoder");
|
||||
|
|
@ -235,5 +233,90 @@ Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
|
|||
return true;
|
||||
}
|
||||
|
||||
Status ParseChromaSubsampling(const std::string& param,
|
||||
YCbCrChromaSubsampling* subsampling) {
|
||||
const std::pair<const char*,
|
||||
std::pair<std::array<uint8_t, 3>, std::array<uint8_t, 3>>>
|
||||
options[] = {{"444", {{{1, 1, 1}}, {{1, 1, 1}}}},
|
||||
{"420", {{{2, 1, 1}}, {{2, 1, 1}}}},
|
||||
{"422", {{{2, 1, 1}}, {{1, 1, 1}}}},
|
||||
{"440", {{{1, 1, 1}}, {{2, 1, 1}}}}};
|
||||
for (const auto& option : options) {
|
||||
if (param == option.first) {
|
||||
return subsampling->Set(option.second.first.data(),
|
||||
option.second.second.data());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
class JPEGEncoder : public Encoder {
|
||||
std::vector<JxlPixelFormat> AcceptedFormats() const override {
|
||||
std::vector<JxlPixelFormat> formats;
|
||||
for (const uint32_t num_channels : {1, 3}) {
|
||||
for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) {
|
||||
formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels,
|
||||
/*data_type=*/JXL_TYPE_UINT8,
|
||||
/*endianness=*/endianness,
|
||||
/*align=*/0});
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
|
||||
ThreadPool* pool = nullptr) const override {
|
||||
const auto& options = this->options();
|
||||
int quality = 100;
|
||||
auto it_quality = options.find("q");
|
||||
if (it_quality != options.end()) {
|
||||
std::istringstream is(it_quality->second);
|
||||
JXL_RETURN_IF_ERROR(static_cast<bool>(is >> quality));
|
||||
}
|
||||
YCbCrChromaSubsampling chroma_subsampling;
|
||||
auto it_chroma_subsampling = options.find("chroma_subsampling");
|
||||
if (it_chroma_subsampling != options.end()) {
|
||||
JXL_RETURN_IF_ERROR(ParseChromaSubsampling(it_chroma_subsampling->second,
|
||||
&chroma_subsampling));
|
||||
}
|
||||
JpegEncoder jpeg_encoder = JpegEncoder::kLibJpeg;
|
||||
auto it_encoder = options.find("jpeg_encoder");
|
||||
if (it_encoder != options.end()) {
|
||||
if (it_encoder->second == "libjpeg") {
|
||||
jpeg_encoder = JpegEncoder::kLibJpeg;
|
||||
} else if (it_encoder->second == "sjpeg") {
|
||||
jpeg_encoder = JpegEncoder::kSJpeg;
|
||||
} else {
|
||||
return JXL_FAILURE("unknown jpeg encoder \"%s\"",
|
||||
it_encoder->second.c_str());
|
||||
}
|
||||
}
|
||||
CodecInOut io;
|
||||
JXL_RETURN_IF_ERROR(ConvertPackedPixelFileToCodecInOut(ppf, pool, &io));
|
||||
encoded_image->icc = ppf.icc;
|
||||
encoded_image->bitstreams.clear();
|
||||
encoded_image->bitstreams.reserve(io.frames.size());
|
||||
for (const ImageBundle& ib : io.frames) {
|
||||
encoded_image->bitstreams.emplace_back();
|
||||
JXL_RETURN_IF_ERROR(EncodeImageJPG(ib, ppf.metadata.exif, jpeg_encoder,
|
||||
quality, chroma_subsampling, pool,
|
||||
&encoded_image->bitstreams.back()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Encoder> GetJPEGEncoder() {
|
||||
return jxl::make_unique<JPEGEncoder>();
|
||||
}
|
||||
|
||||
Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
|
||||
YCbCrChromaSubsampling chroma_subsampling,
|
||||
ThreadPool* pool, std::vector<uint8_t>* bytes) {
|
||||
return EncodeImageJPG(io->Main(), io->blobs.exif, encoder, quality,
|
||||
chroma_subsampling, pool, bytes);
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
6
third_party/jpeg-xl/lib/extras/enc/jpg.h
vendored
6
third_party/jpeg-xl/lib/extras/enc/jpg.h
vendored
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/extras/codec.h"
|
||||
#include "lib/extras/enc/encode.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
|
|
@ -25,10 +25,12 @@ enum class JpegEncoder {
|
|||
kSJpeg,
|
||||
};
|
||||
|
||||
std::unique_ptr<Encoder> GetJPEGEncoder();
|
||||
|
||||
// Encodes into `bytes`.
|
||||
Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
|
||||
YCbCrChromaSubsampling chroma_subsampling,
|
||||
ThreadPool* pool, PaddedBytes* bytes);
|
||||
ThreadPool* pool, std::vector<uint8_t>* bytes);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
321
third_party/jpeg-xl/lib/extras/enc/npy.cc
vendored
Normal file
321
third_party/jpeg-xl/lib/extras/enc/npy.cc
vendored
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/extras/enc/npy.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "jxl/types.h"
|
||||
#include "lib/extras/packed_image.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
namespace {
|
||||
|
||||
// JSON value writing
|
||||
|
||||
class JSONField {
|
||||
public:
|
||||
virtual ~JSONField() = default;
|
||||
virtual void Write(std::ostream& o, uint32_t indent) const = 0;
|
||||
|
||||
protected:
|
||||
JSONField() = default;
|
||||
};
|
||||
|
||||
class JSONValue : public JSONField {
|
||||
public:
|
||||
template <typename T>
|
||||
explicit JSONValue(const T& value) : value_(std::to_string(value)) {}
|
||||
|
||||
explicit JSONValue(const std::string& value) : value_("\"" + value + "\"") {}
|
||||
|
||||
explicit JSONValue(bool value) : value_(value ? "true" : "false") {}
|
||||
|
||||
void Write(std::ostream& o, uint32_t indent) const override { o << value_; }
|
||||
|
||||
private:
|
||||
std::string value_;
|
||||
};
|
||||
|
||||
class JSONDict : public JSONField {
|
||||
public:
|
||||
JSONDict() = default;
|
||||
|
||||
template <typename T>
|
||||
T* AddEmpty(const std::string& key) {
|
||||
static_assert(std::is_convertible<T*, JSONField*>::value,
|
||||
"T must be a JSONField");
|
||||
T* ret = new T();
|
||||
values_.emplace_back(
|
||||
key, std::unique_ptr<JSONField>(static_cast<JSONField*>(ret)));
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Add(const std::string& key, const T& value) {
|
||||
values_.emplace_back(key, std::unique_ptr<JSONField>(new JSONValue(value)));
|
||||
}
|
||||
|
||||
void Write(std::ostream& o, uint32_t indent) const override {
|
||||
std::string indent_str(indent, ' ');
|
||||
o << "{";
|
||||
bool is_first = true;
|
||||
for (const auto& key_value : values_) {
|
||||
if (!is_first) {
|
||||
o << ",";
|
||||
}
|
||||
is_first = false;
|
||||
o << std::endl << indent_str << " \"" << key_value.first << "\": ";
|
||||
key_value.second->Write(o, indent + 2);
|
||||
}
|
||||
if (!values_.empty()) {
|
||||
o << std::endl << indent_str;
|
||||
}
|
||||
o << "}";
|
||||
}
|
||||
|
||||
private:
|
||||
// Dictionary with order.
|
||||
std::vector<std::pair<std::string, std::unique_ptr<JSONField>>> values_;
|
||||
};
|
||||
|
||||
class JSONArray : public JSONField {
|
||||
public:
|
||||
JSONArray() = default;
|
||||
|
||||
template <typename T>
|
||||
T* AddEmpty() {
|
||||
static_assert(std::is_convertible<T*, JSONField*>::value,
|
||||
"T must be a JSONField");
|
||||
T* ret = new T();
|
||||
values_.emplace_back(ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void Add(const T& value) {
|
||||
values_.emplace_back(new JSONValue(value));
|
||||
}
|
||||
|
||||
void Write(std::ostream& o, uint32_t indent) const override {
|
||||
std::string indent_str(indent, ' ');
|
||||
o << "[";
|
||||
bool is_first = true;
|
||||
for (const auto& value : values_) {
|
||||
if (!is_first) {
|
||||
o << ",";
|
||||
}
|
||||
is_first = false;
|
||||
o << std::endl << indent_str << " ";
|
||||
value->Write(o, indent + 2);
|
||||
}
|
||||
if (!values_.empty()) {
|
||||
o << std::endl << indent_str;
|
||||
}
|
||||
o << "]";
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<JSONField>> values_;
|
||||
};
|
||||
|
||||
void GenerateMetadata(const PackedPixelFile& ppf, std::vector<uint8_t>* out) {
|
||||
JSONDict meta;
|
||||
// Same order as in 18181-3 CD.
|
||||
|
||||
// Frames.
|
||||
auto* meta_frames = meta.AddEmpty<JSONArray>("frames");
|
||||
for (size_t i = 0; i < ppf.frames.size(); i++) {
|
||||
auto* frame_i = meta_frames->AddEmpty<JSONDict>();
|
||||
if (ppf.info.have_animation) {
|
||||
frame_i->Add("duration",
|
||||
JSONValue(ppf.frames[i].frame_info.duration * 1.0f *
|
||||
ppf.info.animation.tps_denominator /
|
||||
ppf.info.animation.tps_numerator));
|
||||
}
|
||||
|
||||
frame_i->Add("name", JSONValue(ppf.frames[i].name));
|
||||
|
||||
if (ppf.info.animation.have_timecodes) {
|
||||
frame_i->Add("timecode", JSONValue(ppf.frames[i].frame_info.timecode));
|
||||
}
|
||||
}
|
||||
|
||||
#define METADATA(FIELD) meta.Add(#FIELD, ppf.info.FIELD)
|
||||
|
||||
METADATA(intensity_target);
|
||||
METADATA(min_nits);
|
||||
METADATA(relative_to_max_display);
|
||||
METADATA(linear_below);
|
||||
|
||||
if (ppf.info.have_preview) {
|
||||
meta.AddEmpty<JSONDict>("preview");
|
||||
// TODO(veluca): can we have duration/name/timecode here?
|
||||
}
|
||||
|
||||
{
|
||||
auto ectype = meta.AddEmpty<JSONArray>("extra_channel_type");
|
||||
auto bps = meta.AddEmpty<JSONArray>("bits_per_sample");
|
||||
auto ebps = meta.AddEmpty<JSONArray>("exp_bits_per_sample");
|
||||
bps->Add(ppf.info.bits_per_sample);
|
||||
ebps->Add(ppf.info.exponent_bits_per_sample);
|
||||
for (size_t i = 0; i < ppf.extra_channels_info.size(); i++) {
|
||||
switch (ppf.extra_channels_info[i].ec_info.type) {
|
||||
case JXL_CHANNEL_ALPHA: {
|
||||
ectype->Add(std::string("Alpha"));
|
||||
break;
|
||||
}
|
||||
case JXL_CHANNEL_DEPTH: {
|
||||
ectype->Add(std::string("Depth"));
|
||||
break;
|
||||
}
|
||||
case JXL_CHANNEL_SPOT_COLOR: {
|
||||
ectype->Add(std::string("SpotColor"));
|
||||
break;
|
||||
}
|
||||
case JXL_CHANNEL_SELECTION_MASK: {
|
||||
ectype->Add(std::string("SelectionMask"));
|
||||
break;
|
||||
}
|
||||
case JXL_CHANNEL_BLACK: {
|
||||
ectype->Add(std::string("Black"));
|
||||
break;
|
||||
}
|
||||
case JXL_CHANNEL_CFA: {
|
||||
ectype->Add(std::string("CFA"));
|
||||
break;
|
||||
}
|
||||
case JXL_CHANNEL_THERMAL: {
|
||||
ectype->Add(std::string("Thermal"));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
ectype->Add(std::string("UNKNOWN"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
bps->Add(ppf.extra_channels_info[i].ec_info.bits_per_sample);
|
||||
ebps->Add(ppf.extra_channels_info[i].ec_info.exponent_bits_per_sample);
|
||||
}
|
||||
}
|
||||
|
||||
std::ostringstream os;
|
||||
meta.Write(os, 0);
|
||||
out->resize(os.str().size());
|
||||
memcpy(out->data(), os.str().data(), os.str().size());
|
||||
}
|
||||
|
||||
void Append(std::vector<uint8_t>* out, const void* data, size_t size) {
|
||||
size_t pos = out->size();
|
||||
out->resize(pos + size);
|
||||
memcpy(out->data() + pos, data, size);
|
||||
}
|
||||
|
||||
void WriteNPYHeader(size_t xsize, size_t ysize, uint32_t num_channels,
|
||||
size_t num_frames, std::vector<uint8_t>* out) {
|
||||
const uint8_t header[] = "\x93NUMPY\x01\x00";
|
||||
Append(out, header, 8);
|
||||
std::stringstream ss;
|
||||
ss << "{'descr': '<f4', 'fortran_order': False, 'shape': (" << num_frames
|
||||
<< ", " << ysize << ", " << xsize << ", " << num_channels << "), }\n";
|
||||
// 16-bit little endian header length.
|
||||
uint8_t header_len[2] = {static_cast<uint8_t>(ss.str().size() % 256),
|
||||
static_cast<uint8_t>(ss.str().size() / 256)};
|
||||
Append(out, header_len, 2);
|
||||
Append(out, ss.str().data(), ss.str().size());
|
||||
}
|
||||
|
||||
bool WriteFrameToNPYArray(size_t xsize, size_t ysize, const PackedFrame& frame,
|
||||
std::vector<uint8_t>* out) {
|
||||
const auto& color = frame.color;
|
||||
if (color.xsize != xsize || color.ysize != ysize) {
|
||||
return false;
|
||||
}
|
||||
for (const auto& ec : frame.extra_channels) {
|
||||
if (ec.xsize != xsize || ec.ysize != ysize) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// interleave the samples from color and extra channels
|
||||
for (size_t y = 0; y < ysize; ++y) {
|
||||
for (size_t x = 0; x < xsize; ++x) {
|
||||
{
|
||||
size_t sample_size = color.pixel_stride();
|
||||
size_t offset = y * color.stride + x * sample_size;
|
||||
uint8_t* pixels = reinterpret_cast<uint8_t*>(color.pixels());
|
||||
JXL_ASSERT(offset + sample_size <= color.pixels_size);
|
||||
Append(out, pixels + offset, sample_size);
|
||||
}
|
||||
for (const auto& ec : frame.extra_channels) {
|
||||
size_t sample_size = ec.pixel_stride();
|
||||
size_t offset = y * ec.stride + x * sample_size;
|
||||
uint8_t* pixels = reinterpret_cast<uint8_t*>(ec.pixels());
|
||||
JXL_ASSERT(offset + sample_size <= ec.pixels_size);
|
||||
Append(out, pixels + offset, sample_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Writes a PackedPixelFile as a numpy 4D ndarray in binary format.
|
||||
bool WriteNPYArray(const PackedPixelFile& ppf, std::vector<uint8_t>* out) {
|
||||
size_t xsize = ppf.info.xsize;
|
||||
size_t ysize = ppf.info.ysize;
|
||||
WriteNPYHeader(xsize, ysize,
|
||||
ppf.info.num_color_channels + ppf.extra_channels_info.size(),
|
||||
ppf.frames.size(), out);
|
||||
for (const auto& frame : ppf.frames) {
|
||||
if (!WriteFrameToNPYArray(xsize, ysize, frame, out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class NumPyEncoder : public Encoder {
|
||||
public:
|
||||
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
|
||||
ThreadPool* pool = nullptr) const override {
|
||||
GenerateMetadata(ppf, &encoded_image->metadata);
|
||||
encoded_image->bitstreams.emplace_back();
|
||||
if (!WriteNPYArray(ppf, &encoded_image->bitstreams.back())) {
|
||||
return false;
|
||||
}
|
||||
if (ppf.preview_frame) {
|
||||
size_t xsize = ppf.info.preview.xsize;
|
||||
size_t ysize = ppf.info.preview.ysize;
|
||||
WriteNPYHeader(xsize, ysize, ppf.info.num_color_channels, 1,
|
||||
&encoded_image->preview_bitstream);
|
||||
if (!WriteFrameToNPYArray(xsize, ysize, *ppf.preview_frame,
|
||||
&encoded_image->preview_bitstream)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::vector<JxlPixelFormat> AcceptedFormats() const override {
|
||||
std::vector<JxlPixelFormat> formats;
|
||||
for (const uint32_t num_channels : {1, 3}) {
|
||||
formats.push_back(JxlPixelFormat{num_channels, JXL_TYPE_FLOAT,
|
||||
JXL_LITTLE_ENDIAN, /*align=*/0});
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Encoder> GetNumPyEncoder() {
|
||||
return jxl::make_unique<NumPyEncoder>();
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
23
third_party/jpeg-xl/lib/extras/enc/npy.h
vendored
Normal file
23
third_party/jpeg-xl/lib/extras/enc/npy.h
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_ENC_NPY_H_
|
||||
#define LIB_EXTRAS_ENC_NPY_H_
|
||||
|
||||
// Encodes pixels to numpy array, used for conformance testing.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "lib/extras/enc/encode.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
std::unique_ptr<Encoder> GetNumPyEncoder();
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_ENC_NPY_H_
|
||||
147
third_party/jpeg-xl/lib/extras/enc/pgx.cc
vendored
147
third_party/jpeg-xl/lib/extras/enc/pgx.cc
vendored
|
|
@ -8,18 +8,10 @@
|
|||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lib/jxl/base/bits.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/file_io.h"
|
||||
#include "jxl/codestream_header.h"
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/byte_order.h"
|
||||
#include "lib/jxl/base/printf_macros.h"
|
||||
#include "lib/jxl/color_management.h"
|
||||
#include "lib/jxl/dec_external_image.h"
|
||||
#include "lib/jxl/enc_color_management.h"
|
||||
#include "lib/jxl/enc_external_image.h"
|
||||
#include "lib/jxl/enc_image_bundle.h"
|
||||
#include "lib/jxl/fields.h" // AllDefault
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/image_bundle.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
|
@ -27,60 +19,79 @@ namespace {
|
|||
|
||||
constexpr size_t kMaxHeaderSize = 200;
|
||||
|
||||
Status EncodeHeader(const ImageBundle& ib, const size_t bits_per_sample,
|
||||
char* header, int* JXL_RESTRICT chars_written) {
|
||||
if (ib.HasAlpha()) return JXL_FAILURE("PGX: can't store alpha");
|
||||
if (!ib.IsGray()) return JXL_FAILURE("PGX: must be grayscale");
|
||||
Status EncodeHeader(const JxlBasicInfo& info, char* header,
|
||||
int* chars_written) {
|
||||
if (info.xsize == 0 || info.ysize == 0) {
|
||||
return JXL_FAILURE("Empty image");
|
||||
}
|
||||
if (info.alpha_bits > 0) {
|
||||
return JXL_FAILURE("PGX: can't store alpha");
|
||||
}
|
||||
if (info.num_color_channels != 1) {
|
||||
return JXL_FAILURE("PGX: must be grayscale");
|
||||
}
|
||||
if (info.orientation != JXL_ORIENT_IDENTITY) {
|
||||
return JXL_FAILURE("Orientation must be identity");
|
||||
}
|
||||
// TODO(lode): verify other bit depths: for other bit depths such as 1 or 4
|
||||
// bits, have a test case to verify it works correctly. For bits > 16, we may
|
||||
// need to change the way external_image works.
|
||||
if (bits_per_sample != 8 && bits_per_sample != 16) {
|
||||
if (info.bits_per_sample != 8 && info.bits_per_sample != 16) {
|
||||
return JXL_FAILURE("PGX: bits other than 8 or 16 not yet supported");
|
||||
}
|
||||
|
||||
// Use ML (Big Endian), LM may not be well supported by all decoders.
|
||||
*chars_written = snprintf(header, kMaxHeaderSize,
|
||||
"PG ML + %" PRIuS " %" PRIuS " %" PRIuS "\n",
|
||||
bits_per_sample, ib.xsize(), ib.ysize());
|
||||
*chars_written = snprintf(header, kMaxHeaderSize, "PG ML + %u %u %u\n",
|
||||
info.bits_per_sample, info.xsize, info.ysize);
|
||||
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
|
||||
kMaxHeaderSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
|
||||
size_t bits_per_sample, ThreadPool* pool,
|
||||
PaddedBytes* bytes) {
|
||||
if (!Bundle::AllDefault(io->metadata.m)) {
|
||||
JXL_WARNING("PGX encoder ignoring metadata - use a different codec");
|
||||
}
|
||||
if (!c_desired.IsSRGB()) {
|
||||
JXL_WARNING(
|
||||
"PGX encoder cannot store custom ICC profile; decoder\n"
|
||||
"will need hint key=color_space to get the same values");
|
||||
}
|
||||
|
||||
ImageBundle ib = io->Main().Copy();
|
||||
|
||||
ImageMetadata metadata = io->metadata.m;
|
||||
ImageBundle store(&metadata);
|
||||
const ImageBundle* transformed;
|
||||
JXL_RETURN_IF_ERROR(TransformIfNeeded(ib, c_desired, GetJxlCms(), pool,
|
||||
&store, &transformed));
|
||||
PaddedBytes pixels(ib.xsize() * ib.ysize() *
|
||||
(bits_per_sample / kBitsPerByte));
|
||||
size_t stride = ib.xsize() * (bits_per_sample / kBitsPerByte);
|
||||
JXL_RETURN_IF_ERROR(
|
||||
ConvertToExternal(*transformed, bits_per_sample,
|
||||
/*float_out=*/false,
|
||||
/*num_channels=*/1, JXL_BIG_ENDIAN, stride, pool,
|
||||
pixels.data(), pixels.size(),
|
||||
/*out_callback=*/{}, metadata.GetOrientation()));
|
||||
|
||||
Status EncodeImagePGX(const PackedFrame& frame, const JxlBasicInfo& info,
|
||||
std::vector<uint8_t>* bytes) {
|
||||
char header[kMaxHeaderSize];
|
||||
int header_size = 0;
|
||||
JXL_RETURN_IF_ERROR(EncodeHeader(ib, bits_per_sample, header, &header_size));
|
||||
JXL_RETURN_IF_ERROR(EncodeHeader(info, header, &header_size));
|
||||
|
||||
const PackedImage& color = frame.color;
|
||||
const JxlPixelFormat format = color.format;
|
||||
const uint8_t* in = reinterpret_cast<const uint8_t*>(color.pixels());
|
||||
size_t data_bits_per_sample = PackedImage::BitsPerChannel(format.data_type);
|
||||
size_t bytes_per_sample = data_bits_per_sample / kBitsPerByte;
|
||||
size_t num_samples = info.xsize * info.ysize;
|
||||
|
||||
if (in == nullptr || color.pixels_size != bytes_per_sample * num_samples) {
|
||||
return JXL_FAILURE("Invalid frame");
|
||||
}
|
||||
if (color.xsize != info.xsize || color.ysize != info.ysize ||
|
||||
format.num_channels != 1) {
|
||||
return JXL_FAILURE("Frame size does not match image size");
|
||||
}
|
||||
if (info.bits_per_sample != data_bits_per_sample) {
|
||||
return JXL_FAILURE("Bit depth does not match pixel data type");
|
||||
}
|
||||
if (color.flipped_y) {
|
||||
return JXL_FAILURE("Flipped y channel not supported");
|
||||
}
|
||||
|
||||
std::vector<uint8_t> pixels(num_samples * bytes_per_sample);
|
||||
|
||||
if (format.data_type == JXL_TYPE_UINT8) {
|
||||
memcpy(&pixels[0], in, num_samples * bytes_per_sample);
|
||||
} else if (format.data_type == JXL_TYPE_UINT16) {
|
||||
if (format.endianness != JXL_BIG_ENDIAN) {
|
||||
const uint8_t* p_in = in;
|
||||
uint8_t* p_out = pixels.data();
|
||||
for (size_t i = 0; i < num_samples; ++i, p_in += 2, p_out += 2) {
|
||||
StoreBE16(LoadLE16(p_in), p_out);
|
||||
}
|
||||
} else {
|
||||
memcpy(&pixels[0], in, num_samples * bytes_per_sample);
|
||||
}
|
||||
} else {
|
||||
return JXL_FAILURE("Unsupported pixel data type");
|
||||
}
|
||||
|
||||
bytes->resize(static_cast<size_t>(header_size) + pixels.size());
|
||||
memcpy(bytes->data(), header, static_cast<size_t>(header_size));
|
||||
|
|
@ -89,5 +100,39 @@ Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
|
|||
return true;
|
||||
}
|
||||
|
||||
class PGXEncoder : public Encoder {
|
||||
public:
|
||||
std::vector<JxlPixelFormat> AcceptedFormats() const override {
|
||||
std::vector<JxlPixelFormat> formats;
|
||||
for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) {
|
||||
for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) {
|
||||
formats.push_back(JxlPixelFormat{/*num_channels=*/1,
|
||||
/*data_type=*/data_type,
|
||||
/*endianness=*/endianness,
|
||||
/*align=*/0});
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
|
||||
ThreadPool* pool) const override {
|
||||
encoded_image->icc.assign(ppf.icc.begin(), ppf.icc.end());
|
||||
encoded_image->bitstreams.clear();
|
||||
encoded_image->bitstreams.reserve(ppf.frames.size());
|
||||
for (const auto& frame : ppf.frames) {
|
||||
encoded_image->bitstreams.emplace_back();
|
||||
JXL_RETURN_IF_ERROR(
|
||||
EncodeImagePGX(frame, ppf.info, &encoded_image->bitstreams.back()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Encoder> GetPGXEncoder() {
|
||||
return jxl::make_unique<PGXEncoder>();
|
||||
}
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
13
third_party/jpeg-xl/lib/extras/enc/pgx.h
vendored
13
third_party/jpeg-xl/lib/extras/enc/pgx.h
vendored
|
|
@ -11,21 +11,12 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/codec_in_out.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
#include "lib/extras/enc/encode.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
// Transforms from io->c_current to `c_desired` and encodes into `bytes`.
|
||||
Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
|
||||
size_t bits_per_sample, ThreadPool* pool,
|
||||
PaddedBytes* bytes);
|
||||
std::unique_ptr<Encoder> GetPGXEncoder();
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
165
third_party/jpeg-xl/lib/extras/enc/pnm.cc
vendored
165
third_party/jpeg-xl/lib/extras/enc/pnm.cc
vendored
|
|
@ -32,15 +32,112 @@ namespace {
|
|||
|
||||
constexpr size_t kMaxHeaderSize = 200;
|
||||
|
||||
Status EncodeHeader(const PackedPixelFile& ppf, const size_t bits_per_sample,
|
||||
const bool little_endian, char* header,
|
||||
int* JXL_RESTRICT chars_written) {
|
||||
bool is_gray = ppf.info.num_color_channels <= 2;
|
||||
size_t oriented_xsize =
|
||||
ppf.info.orientation <= 4 ? ppf.info.xsize : ppf.info.ysize;
|
||||
size_t oriented_ysize =
|
||||
ppf.info.orientation <= 4 ? ppf.info.ysize : ppf.info.xsize;
|
||||
if (ppf.info.alpha_bits > 0) { // PAM
|
||||
class PNMEncoder : public Encoder {
|
||||
public:
|
||||
Status Encode(const PackedPixelFile& ppf, EncodedImage* encoded_image,
|
||||
ThreadPool* pool = nullptr) const override {
|
||||
if (!ppf.metadata.exif.empty() || !ppf.metadata.iptc.empty() ||
|
||||
!ppf.metadata.jumbf.empty() || !ppf.metadata.xmp.empty()) {
|
||||
JXL_WARNING("PNM encoder ignoring metadata - use a different codec");
|
||||
}
|
||||
encoded_image->icc = ppf.icc;
|
||||
encoded_image->bitstreams.clear();
|
||||
encoded_image->bitstreams.reserve(ppf.frames.size());
|
||||
for (const auto& frame : ppf.frames) {
|
||||
encoded_image->bitstreams.emplace_back();
|
||||
JXL_RETURN_IF_ERROR(EncodeImagePNM(frame.color, ppf.info.orientation,
|
||||
ppf.info.bits_per_sample,
|
||||
&encoded_image->bitstreams.back()));
|
||||
}
|
||||
for (size_t i = 0; i < ppf.extra_channels_info.size(); ++i) {
|
||||
encoded_image->extra_channel_bitstreams.emplace_back();
|
||||
auto& ec_bitstreams = encoded_image->extra_channel_bitstreams.back();
|
||||
for (const auto& frame : ppf.frames) {
|
||||
ec_bitstreams.emplace_back();
|
||||
JXL_RETURN_IF_ERROR(
|
||||
EncodeImagePNM(frame.extra_channels[i], ppf.info.orientation,
|
||||
ppf.info.bits_per_sample, &ec_bitstreams.back()));
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class PPMEncoder : public PNMEncoder {
|
||||
public:
|
||||
std::vector<JxlPixelFormat> AcceptedFormats() const override {
|
||||
std::vector<JxlPixelFormat> formats;
|
||||
for (const uint32_t num_channels : {1, 2, 3, 4}) {
|
||||
for (const JxlDataType data_type : {JXL_TYPE_UINT8, JXL_TYPE_UINT16}) {
|
||||
for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) {
|
||||
formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels,
|
||||
/*data_type=*/data_type,
|
||||
/*endianness=*/endianness,
|
||||
/*align=*/0});
|
||||
}
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
};
|
||||
|
||||
class PFMEncoder : public PNMEncoder {
|
||||
public:
|
||||
std::vector<JxlPixelFormat> AcceptedFormats() const override {
|
||||
std::vector<JxlPixelFormat> formats;
|
||||
for (const uint32_t num_channels : {1, 3}) {
|
||||
for (const JxlDataType data_type : {JXL_TYPE_FLOAT16, JXL_TYPE_FLOAT}) {
|
||||
for (JxlEndianness endianness : {JXL_BIG_ENDIAN, JXL_LITTLE_ENDIAN}) {
|
||||
formats.push_back(JxlPixelFormat{/*num_channels=*/num_channels,
|
||||
/*data_type=*/data_type,
|
||||
/*endianness=*/endianness,
|
||||
/*align=*/0});
|
||||
}
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
};
|
||||
|
||||
class PGMEncoder : public PPMEncoder {
|
||||
public:
|
||||
std::vector<JxlPixelFormat> AcceptedFormats() const override {
|
||||
std::vector<JxlPixelFormat> formats = PPMEncoder::AcceptedFormats();
|
||||
for (auto it = formats.begin(); it != formats.end();) {
|
||||
if (it->num_channels > 2) {
|
||||
it = formats.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
};
|
||||
|
||||
class PAMEncoder : public PPMEncoder {
|
||||
public:
|
||||
std::vector<JxlPixelFormat> AcceptedFormats() const override {
|
||||
std::vector<JxlPixelFormat> formats = PPMEncoder::AcceptedFormats();
|
||||
for (auto it = formats.begin(); it != formats.end();) {
|
||||
if (it->num_channels != 2 && it->num_channels != 4) {
|
||||
it = formats.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return formats;
|
||||
}
|
||||
};
|
||||
|
||||
Status EncodeHeader(const PackedImage& image, JxlOrientation orientation,
|
||||
size_t bits_per_sample, bool little_endian, char* header,
|
||||
int* chars_written) {
|
||||
size_t num_channels = image.format.num_channels;
|
||||
bool is_gray = num_channels <= 2;
|
||||
bool has_alpha = num_channels == 2 || num_channels == 4;
|
||||
size_t oriented_xsize = orientation <= 4 ? image.xsize : image.ysize;
|
||||
size_t oriented_ysize = orientation <= 4 ? image.ysize : image.xsize;
|
||||
if (has_alpha) { // PAM
|
||||
if (bits_per_sample > 16) return JXL_FAILURE("PNM cannot have > 16 bits");
|
||||
const uint32_t max_val = (1U << bits_per_sample) - 1;
|
||||
*chars_written =
|
||||
|
|
@ -60,8 +157,8 @@ Status EncodeHeader(const PackedPixelFile& ppf, const size_t bits_per_sample,
|
|||
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
|
||||
kMaxHeaderSize);
|
||||
} else { // PGM/PPM
|
||||
if (bits_per_sample > 16) return JXL_FAILURE("PNM cannot have > 16 bits");
|
||||
const uint32_t max_val = (1U << bits_per_sample) - 1;
|
||||
if (max_val >= 65536) return JXL_FAILURE("PNM cannot have > 16 bits");
|
||||
const char type = is_gray ? '5' : '6';
|
||||
*chars_written =
|
||||
snprintf(header, kMaxHeaderSize, "P%c\n%" PRIuS " %" PRIuS "\n%u\n",
|
||||
|
|
@ -96,34 +193,36 @@ void VerticallyFlipImage(float* const float_image, const size_t xsize,
|
|||
|
||||
} // namespace
|
||||
|
||||
Status EncodeImagePNM(const PackedPixelFile& ppf, size_t bits_per_sample,
|
||||
ThreadPool* pool, size_t frame_index,
|
||||
std::vector<uint8_t>* bytes) {
|
||||
const bool floating_point = bits_per_sample > 16;
|
||||
// Choose native for PFM; PGM/PPM require big-endian
|
||||
const JxlEndianness endianness =
|
||||
floating_point ? JXL_NATIVE_ENDIAN : JXL_BIG_ENDIAN;
|
||||
if (!ppf.metadata.exif.empty() || !ppf.metadata.iptc.empty() ||
|
||||
!ppf.metadata.jumbf.empty() || !ppf.metadata.xmp.empty()) {
|
||||
JXL_WARNING("PNM encoder ignoring metadata - use a different codec");
|
||||
}
|
||||
std::unique_ptr<Encoder> GetPPMEncoder() {
|
||||
return jxl::make_unique<PPMEncoder>();
|
||||
}
|
||||
|
||||
std::unique_ptr<Encoder> GetPFMEncoder() {
|
||||
return jxl::make_unique<PFMEncoder>();
|
||||
}
|
||||
|
||||
std::unique_ptr<Encoder> GetPGMEncoder() {
|
||||
return jxl::make_unique<PGMEncoder>();
|
||||
}
|
||||
|
||||
std::unique_ptr<Encoder> GetPAMEncoder() {
|
||||
return jxl::make_unique<PAMEncoder>();
|
||||
}
|
||||
|
||||
Status EncodeImagePNM(const PackedImage& image, JxlOrientation orientation,
|
||||
size_t bits_per_sample, std::vector<uint8_t>* bytes) {
|
||||
// Choose native for PFM; PGM/PPM require big-endian
|
||||
bool is_little_endian = bits_per_sample > 16 && IsLittleEndian();
|
||||
char header[kMaxHeaderSize];
|
||||
int header_size = 0;
|
||||
bool is_little_endian = endianness == JXL_LITTLE_ENDIAN ||
|
||||
(endianness == JXL_NATIVE_ENDIAN && IsLittleEndian());
|
||||
JXL_RETURN_IF_ERROR(EncodeHeader(ppf, bits_per_sample, is_little_endian,
|
||||
header, &header_size));
|
||||
bytes->resize(static_cast<size_t>(header_size) +
|
||||
ppf.frames[frame_index].color.pixels_size);
|
||||
JXL_RETURN_IF_ERROR(EncodeHeader(image, orientation, bits_per_sample,
|
||||
is_little_endian, header, &header_size));
|
||||
bytes->resize(static_cast<size_t>(header_size) + image.pixels_size);
|
||||
memcpy(bytes->data(), header, static_cast<size_t>(header_size));
|
||||
memcpy(bytes->data() + header_size, ppf.frames[frame_index].color.pixels(),
|
||||
ppf.frames[frame_index].color.pixels_size);
|
||||
if (floating_point) {
|
||||
memcpy(bytes->data() + header_size, image.pixels(), image.pixels_size);
|
||||
if (bits_per_sample > 16) {
|
||||
VerticallyFlipImage(reinterpret_cast<float*>(bytes->data() + header_size),
|
||||
ppf.frames[frame_index].color.xsize,
|
||||
ppf.frames[frame_index].color.ysize,
|
||||
ppf.info.num_color_channels);
|
||||
image.xsize, image.ysize, image.format.num_channels);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
|||
17
third_party/jpeg-xl/lib/extras/enc/pnm.h
vendored
17
third_party/jpeg-xl/lib/extras/enc/pnm.h
vendored
|
|
@ -11,20 +11,19 @@
|
|||
// TODO(janwas): workaround for incorrect Win64 codegen (cause unknown)
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/extras/enc/encode.h"
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/codec_in_out.h"
|
||||
#include "lib/jxl/color_encoding_internal.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
// Transforms from io->c_current to `c_desired` and encodes into `bytes`.
|
||||
Status EncodeImagePNM(const PackedPixelFile& ppf, size_t bits_per_sample,
|
||||
ThreadPool* pool, size_t frame_index,
|
||||
std::vector<uint8_t>* bytes);
|
||||
std::unique_ptr<Encoder> GetPAMEncoder();
|
||||
std::unique_ptr<Encoder> GetPGMEncoder();
|
||||
std::unique_ptr<Encoder> GetPPMEncoder();
|
||||
std::unique_ptr<Encoder> GetPFMEncoder();
|
||||
|
||||
Status EncodeImagePNM(const PackedImage& image, JxlOrientation orientation,
|
||||
size_t bits_per_sample, std::vector<uint8_t>* bytes);
|
||||
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
||||
|
|
|
|||
18
third_party/jpeg-xl/lib/extras/packed_image.h
vendored
18
third_party/jpeg-xl/lib/extras/packed_image.h
vendored
|
|
@ -22,7 +22,6 @@
|
|||
#include "jxl/codestream_header.h"
|
||||
#include "jxl/encode.h"
|
||||
#include "jxl/types.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/common.h"
|
||||
|
||||
namespace jxl {
|
||||
|
|
@ -77,6 +76,11 @@ class PackedImage {
|
|||
JxlPixelFormat format;
|
||||
size_t pixels_size;
|
||||
|
||||
size_t pixel_stride() const {
|
||||
return (BitsPerChannel(format.data_type) * format.num_channels /
|
||||
jxl::kBitsPerByte);
|
||||
}
|
||||
|
||||
static size_t BitsPerChannel(JxlDataType data_type) {
|
||||
switch (data_type) {
|
||||
case JXL_TYPE_UINT8:
|
||||
|
|
@ -140,20 +144,20 @@ class PackedPixelFile {
|
|||
|
||||
// The extra channel metadata information.
|
||||
struct PackedExtraChannel {
|
||||
PackedExtraChannel(const JxlExtraChannelInfo& ec_info,
|
||||
const std::string& name)
|
||||
: ec_info(ec_info), name(name) {}
|
||||
|
||||
JxlExtraChannelInfo ec_info;
|
||||
size_t index;
|
||||
std::string name;
|
||||
};
|
||||
std::vector<PackedExtraChannel> extra_channels_info;
|
||||
|
||||
// Color information. If the icc is empty, the JxlColorEncoding should be used
|
||||
// instead.
|
||||
// Color information of the decoded pixels.
|
||||
// If the icc is empty, the JxlColorEncoding should be used instead.
|
||||
std::vector<uint8_t> icc;
|
||||
JxlColorEncoding color_encoding = {};
|
||||
// The icc profile of the original image.
|
||||
std::vector<uint8_t> orig_icc;
|
||||
|
||||
std::unique_ptr<PackedFrame> preview_frame;
|
||||
std::vector<PackedFrame> frames;
|
||||
|
||||
PackedMetadata metadata;
|
||||
|
|
|
|||
|
|
@ -20,6 +20,62 @@
|
|||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
Status ConvertPackedFrameToImageBundle(const JxlBasicInfo& info,
|
||||
const PackedFrame& frame,
|
||||
const CodecInOut& io, ThreadPool* pool,
|
||||
ImageBundle* bundle) {
|
||||
JXL_ASSERT(frame.color.pixels() != nullptr);
|
||||
size_t frame_bits_per_sample =
|
||||
(frame.color.bitdepth_from_format
|
||||
? frame.color.BitsPerChannel(frame.color.format.data_type)
|
||||
: info.bits_per_sample);
|
||||
JXL_ASSERT(frame_bits_per_sample != 0);
|
||||
// It is ok for the frame.color.format.num_channels to not match the
|
||||
// number of channels on the image.
|
||||
JXL_ASSERT(1 <= frame.color.format.num_channels &&
|
||||
frame.color.format.num_channels <= 4);
|
||||
|
||||
const Span<const uint8_t> span(
|
||||
static_cast<const uint8_t*>(frame.color.pixels()),
|
||||
frame.color.pixels_size);
|
||||
JXL_ASSERT(Rect(frame.frame_info.layer_info.crop_x0,
|
||||
frame.frame_info.layer_info.crop_y0,
|
||||
frame.frame_info.layer_info.xsize,
|
||||
frame.frame_info.layer_info.ysize)
|
||||
.IsInside(Rect(0, 0, info.xsize, info.ysize)));
|
||||
if (info.have_animation) {
|
||||
bundle->duration = frame.frame_info.duration;
|
||||
bundle->blend = frame.frame_info.layer_info.blend_info.blendmode > 0;
|
||||
bundle->use_for_next_frame =
|
||||
frame.frame_info.layer_info.save_as_reference > 0;
|
||||
bundle->origin.x0 = frame.frame_info.layer_info.crop_x0;
|
||||
bundle->origin.y0 = frame.frame_info.layer_info.crop_y0;
|
||||
}
|
||||
bundle->name = frame.name; // frame.frame_info.name_length is ignored here.
|
||||
JXL_ASSERT(io.metadata.m.color_encoding.IsGray() ==
|
||||
(frame.color.format.num_channels <= 2));
|
||||
|
||||
const bool float_in = frame.color.format.data_type == JXL_TYPE_FLOAT16 ||
|
||||
frame.color.format.data_type == JXL_TYPE_FLOAT;
|
||||
JXL_RETURN_IF_ERROR(ConvertFromExternal(
|
||||
span, frame.color.xsize, frame.color.ysize, io.metadata.m.color_encoding,
|
||||
frame.color.format.num_channels,
|
||||
/*alpha_is_premultiplied=*/info.alpha_premultiplied,
|
||||
frame_bits_per_sample, frame.color.format.endianness,
|
||||
/*flipped_y=*/frame.color.flipped_y, pool, bundle,
|
||||
/*float_in=*/float_in, /*align=*/0));
|
||||
|
||||
bundle->extra_channels().resize(io.metadata.m.extra_channel_info.size());
|
||||
for (size_t i = 0; i < frame.extra_channels.size(); i++) {
|
||||
const auto& ppf_ec = frame.extra_channels[i];
|
||||
bundle->extra_channels()[i] = ImageF(ppf_ec.xsize, ppf_ec.ysize);
|
||||
JXL_CHECK(BufferToImageF(ppf_ec.format, ppf_ec.xsize, ppf_ec.ysize,
|
||||
ppf_ec.pixels(), ppf_ec.pixels_size, pool,
|
||||
&bundle->extra_channels()[i]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
|
||||
ThreadPool* pool, CodecInOut* io) {
|
||||
const bool has_alpha = ppf.info.alpha_bits != 0;
|
||||
|
|
@ -63,7 +119,7 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
|
|||
PaddedBytes icc;
|
||||
icc.append(ppf.icc);
|
||||
if (!io->metadata.m.color_encoding.SetICC(std::move(icc))) {
|
||||
fprintf(stderr, "Warning: error setting ICC profile, assuming SRGB");
|
||||
fprintf(stderr, "Warning: error setting ICC profile, assuming SRGB\n");
|
||||
io->metadata.m.color_encoding = ColorEncoding::SRGB(is_gray);
|
||||
} else {
|
||||
if (io->metadata.m.color_encoding.IsGray() != is_gray) {
|
||||
|
|
@ -105,61 +161,24 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
|
|||
io->metadata.m.extra_channel_info.push_back(std::move(out));
|
||||
}
|
||||
|
||||
// Convert the preview
|
||||
if (ppf.preview_frame) {
|
||||
size_t preview_xsize = ppf.preview_frame->color.xsize;
|
||||
size_t preview_ysize = ppf.preview_frame->color.ysize;
|
||||
io->metadata.m.have_preview = true;
|
||||
JXL_RETURN_IF_ERROR(
|
||||
io->metadata.m.preview_size.Set(preview_xsize, preview_ysize));
|
||||
JXL_RETURN_IF_ERROR(ConvertPackedFrameToImageBundle(
|
||||
ppf.info, *ppf.preview_frame, *io, pool, &io->preview_frame));
|
||||
}
|
||||
|
||||
// Convert the pixels
|
||||
io->dec_pixels = 0;
|
||||
io->frames.clear();
|
||||
for (const auto& frame : ppf.frames) {
|
||||
JXL_ASSERT(frame.color.pixels() != nullptr);
|
||||
size_t frame_bits_per_sample =
|
||||
(frame.color.bitdepth_from_format
|
||||
? frame.color.BitsPerChannel(frame.color.format.data_type)
|
||||
: ppf.info.bits_per_sample);
|
||||
JXL_ASSERT(frame_bits_per_sample != 0);
|
||||
// It is ok for the frame.color.format.num_channels to not match the
|
||||
// number of channels on the image.
|
||||
JXL_ASSERT(1 <= frame.color.format.num_channels &&
|
||||
frame.color.format.num_channels <= 4);
|
||||
|
||||
const Span<const uint8_t> span(
|
||||
static_cast<const uint8_t*>(frame.color.pixels()),
|
||||
frame.color.pixels_size);
|
||||
Rect frame_rect = Rect(frame.frame_info.layer_info.crop_x0,
|
||||
frame.frame_info.layer_info.crop_y0,
|
||||
frame.frame_info.layer_info.xsize,
|
||||
frame.frame_info.layer_info.ysize);
|
||||
JXL_ASSERT(frame_rect.IsInside(Rect(0, 0, ppf.info.xsize, ppf.info.ysize)));
|
||||
ImageBundle bundle(&io->metadata.m);
|
||||
if (ppf.info.have_animation) {
|
||||
bundle.duration = frame.frame_info.duration;
|
||||
bundle.blend = frame.frame_info.layer_info.blend_info.blendmode > 0;
|
||||
bundle.use_for_next_frame =
|
||||
frame.frame_info.layer_info.save_as_reference > 0;
|
||||
bundle.origin.x0 = frame.frame_info.layer_info.crop_x0;
|
||||
bundle.origin.y0 = frame.frame_info.layer_info.crop_y0;
|
||||
}
|
||||
bundle.name = frame.name; // frame.frame_info.name_length is ignored here.
|
||||
JXL_ASSERT(io->metadata.m.color_encoding.IsGray() ==
|
||||
(frame.color.format.num_channels <= 2));
|
||||
|
||||
const bool float_in = frame.color.format.data_type == JXL_TYPE_FLOAT16 ||
|
||||
frame.color.format.data_type == JXL_TYPE_FLOAT;
|
||||
JXL_RETURN_IF_ERROR(ConvertFromExternal(
|
||||
span, frame.color.xsize, frame.color.ysize,
|
||||
io->metadata.m.color_encoding, frame.color.format.num_channels,
|
||||
/*alpha_is_premultiplied=*/ppf.info.alpha_premultiplied,
|
||||
frame_bits_per_sample, frame.color.format.endianness,
|
||||
/*flipped_y=*/frame.color.flipped_y, pool, &bundle,
|
||||
/*float_in=*/float_in, /*align=*/0));
|
||||
|
||||
bundle.extra_channels().resize(io->metadata.m.extra_channel_info.size());
|
||||
for (size_t i = 0; i < frame.extra_channels.size(); i++) {
|
||||
const auto& ppf_ec = frame.extra_channels[i];
|
||||
bundle.extra_channels()[i] = ImageF(ppf_ec.xsize, ppf_ec.ysize);
|
||||
JXL_CHECK(BufferToImageF(ppf_ec.format, ppf_ec.xsize, ppf_ec.ysize,
|
||||
ppf_ec.pixels(), ppf_ec.pixels_size, pool,
|
||||
&bundle.extra_channels()[i]));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
@ -236,8 +255,7 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
|
|||
// Convert the pixels
|
||||
ppf->frames.clear();
|
||||
for (const auto& frame : io.frames) {
|
||||
size_t frame_bits_per_sample = frame.metadata()->bit_depth.bits_per_sample;
|
||||
JXL_ASSERT(frame_bits_per_sample != 0);
|
||||
JXL_ASSERT(frame.metadata()->bit_depth.bits_per_sample != 0);
|
||||
// It is ok for the frame.color().kNumPlanes to not match the
|
||||
// number of channels on the image.
|
||||
const uint32_t num_channels =
|
||||
|
|
@ -251,9 +269,8 @@ Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
|
|||
format);
|
||||
packed_frame.color.bitdepth_from_format = float_out;
|
||||
const size_t bits_per_sample =
|
||||
packed_frame.color.bitdepth_from_format
|
||||
? packed_frame.color.BitsPerChannel(pixel_format.data_type)
|
||||
: ppf->info.bits_per_sample;
|
||||
float_out ? packed_frame.color.BitsPerChannel(pixel_format.data_type)
|
||||
: ppf->info.bits_per_sample;
|
||||
packed_frame.name = frame.name;
|
||||
packed_frame.frame_info.name_length = frame.name.size();
|
||||
// Color transform
|
||||
|
|
|
|||
60
third_party/jpeg-xl/lib/extras/render_hdr.cc
vendored
Normal file
60
third_party/jpeg-xl/lib/extras/render_hdr.cc
vendored
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/extras/render_hdr.h"
|
||||
|
||||
#include "lib/extras/hlg.h"
|
||||
#include "lib/extras/tone_mapping.h"
|
||||
#include "lib/jxl/enc_color_management.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
Status RenderHDR(CodecInOut* io, float display_nits, ThreadPool* pool) {
|
||||
const ColorEncoding& original_color_encoding = io->metadata.m.color_encoding;
|
||||
if (!(original_color_encoding.tf.IsPQ() ||
|
||||
original_color_encoding.tf.IsHLG())) {
|
||||
// Nothing to do.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (original_color_encoding.tf.IsPQ()) {
|
||||
JXL_RETURN_IF_ERROR(ToneMapTo({0, display_nits}, io, pool));
|
||||
JXL_RETURN_IF_ERROR(GamutMap(io, /*preserve_saturation=*/0.1, pool));
|
||||
} else {
|
||||
const float intensity_target = io->metadata.m.IntensityTarget();
|
||||
const float gamma_hlg_to_display = GetHlgGamma(display_nits);
|
||||
// If the image is already in display space, we need to account for the
|
||||
// already-applied OOTF.
|
||||
const float gamma_display_to_display =
|
||||
gamma_hlg_to_display / GetHlgGamma(intensity_target);
|
||||
// Ensures that conversions to linear in HlgOOTF below will not themselves
|
||||
// include the OOTF.
|
||||
io->metadata.m.SetIntensityTarget(300);
|
||||
|
||||
bool need_gamut_mapping = false;
|
||||
for (ImageBundle& ib : io->frames) {
|
||||
const float gamma = ib.c_current().tf.IsHLG() ? gamma_hlg_to_display
|
||||
: gamma_display_to_display;
|
||||
if (gamma < 1) need_gamut_mapping = true;
|
||||
JXL_RETURN_IF_ERROR(HlgOOTF(&ib, gamma, pool));
|
||||
}
|
||||
io->metadata.m.SetIntensityTarget(display_nits);
|
||||
|
||||
if (need_gamut_mapping) {
|
||||
JXL_RETURN_IF_ERROR(GamutMap(io, /*preserve_saturation=*/0.1, pool));
|
||||
}
|
||||
}
|
||||
|
||||
ColorEncoding rec2020_pq;
|
||||
rec2020_pq.SetColorSpace(ColorSpace::kRGB);
|
||||
rec2020_pq.white_point = WhitePoint::kD65;
|
||||
rec2020_pq.primaries = Primaries::k2100;
|
||||
rec2020_pq.tf.SetTransferFunction(TransferFunction::kPQ);
|
||||
JXL_RETURN_IF_ERROR(rec2020_pq.CreateICC());
|
||||
io->metadata.m.color_encoding = rec2020_pq;
|
||||
return io->TransformTo(rec2020_pq, GetJxlCms(), pool);
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
27
third_party/jpeg-xl/lib/extras/render_hdr.h
vendored
Normal file
27
third_party/jpeg-xl/lib/extras/render_hdr.h
vendored
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_EXTRAS_RENDER_HDR_H_
|
||||
#define LIB_EXTRAS_RENDER_HDR_H_
|
||||
|
||||
#include "lib/jxl/codec_in_out.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
// If `io` has an original color space using PQ or HLG, this renders it
|
||||
// appropriately for a display with a peak luminance of `display_nits` and
|
||||
// converts the result to a Rec. 2020 / PQ image. Otherwise, leaves the image as
|
||||
// is.
|
||||
// PQ images are tone-mapped using the method described in Rep. ITU-R BT.2408-5
|
||||
// annex 5, while HLG images are rendered using the HLG OOTF with a gamma
|
||||
// appropriate for the given target luminance.
|
||||
// With a sufficiently bright SDR display, converting the output of this
|
||||
// function to an SDR colorspace may look decent.
|
||||
Status RenderHDR(CodecInOut* io, float display_nits,
|
||||
ThreadPool* pool = nullptr);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_EXTRAS_RENDER_HDR_H_
|
||||
118
third_party/jpeg-xl/lib/extras/tone_mapping.cc
vendored
118
third_party/jpeg-xl/lib/extras/tone_mapping.cc
vendored
|
|
@ -10,13 +10,15 @@
|
|||
#include <hwy/foreach_target.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/dec_tone_mapping-inl.h"
|
||||
#include "lib/jxl/enc_color_management.h"
|
||||
#include "lib/jxl/transfer_functions-inl.h"
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace jxl {
|
||||
namespace HWY_NAMESPACE {
|
||||
|
||||
static constexpr float rec2020_luminances[3] = {0.2627f, 0.6780f, 0.0593f};
|
||||
|
||||
Status ToneMapFrame(const std::pair<float, float> display_nits,
|
||||
ImageBundle* const ib, ThreadPool* const pool) {
|
||||
// Perform tone mapping as described in Report ITU-R BT.2390-8, section 5.4
|
||||
|
|
@ -34,42 +36,12 @@ Status ToneMapFrame(const std::pair<float, float> display_nits,
|
|||
JXL_RETURN_IF_ERROR(linear_rec2020.CreateICC());
|
||||
JXL_RETURN_IF_ERROR(ib->TransformTo(linear_rec2020, GetJxlCms(), pool));
|
||||
|
||||
const auto eotf_inv = [&df](const V luminance) -> V {
|
||||
return TF_PQ().EncodedFromDisplay(df, luminance * Set(df, 1. / 10000));
|
||||
};
|
||||
Rec2408ToneMapper<decltype(df)> tone_mapper(
|
||||
{ib->metadata()->tone_mapping.min_nits,
|
||||
ib->metadata()->IntensityTarget()},
|
||||
display_nits, rec2020_luminances);
|
||||
|
||||
const V pq_mastering_min =
|
||||
eotf_inv(Set(df, ib->metadata()->tone_mapping.min_nits));
|
||||
const V pq_mastering_max =
|
||||
eotf_inv(Set(df, ib->metadata()->tone_mapping.intensity_target));
|
||||
const V pq_mastering_range = pq_mastering_max - pq_mastering_min;
|
||||
const V inv_pq_mastering_range =
|
||||
Set(df, 1) / (pq_mastering_max - pq_mastering_min);
|
||||
const V min_lum = (eotf_inv(Set(df, display_nits.first)) - pq_mastering_min) *
|
||||
inv_pq_mastering_range;
|
||||
const V max_lum =
|
||||
(eotf_inv(Set(df, display_nits.second)) - pq_mastering_min) *
|
||||
inv_pq_mastering_range;
|
||||
const V ks = MulAdd(Set(df, 1.5f), max_lum, Set(df, -0.5f));
|
||||
const V b = min_lum;
|
||||
|
||||
const V inv_one_minus_ks = Set(df, 1) / Max(Set(df, 1e-6f), Set(df, 1) - ks);
|
||||
const auto T = [ks, inv_one_minus_ks](const V a) {
|
||||
return (a - ks) * inv_one_minus_ks;
|
||||
};
|
||||
const auto P = [&T, &df, ks, max_lum](const V b) {
|
||||
const V t_b = T(b);
|
||||
const V t_b_2 = t_b * t_b;
|
||||
const V t_b_3 = t_b_2 * t_b;
|
||||
return MulAdd(
|
||||
MulAdd(Set(df, 2), t_b_3, MulAdd(Set(df, -3), t_b_2, Set(df, 1))), ks,
|
||||
MulAdd(t_b_3 + MulAdd(Set(df, -2), t_b_2, t_b), Set(df, 1) - ks,
|
||||
MulAdd(Set(df, -2), t_b_3, Set(df, 3) * t_b_2) * max_lum));
|
||||
};
|
||||
|
||||
const V inv_max_display_nits = Set(df, 1 / display_nits.second);
|
||||
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
return RunOnPool(
|
||||
pool, 0, ib->ysize(), ThreadPool::NoInit,
|
||||
[&](const uint32_t y, size_t /* thread */) {
|
||||
float* const JXL_RESTRICT row_r = ib->color()->PlaneRow(0, y);
|
||||
|
|
@ -79,43 +51,13 @@ Status ToneMapFrame(const std::pair<float, float> display_nits,
|
|||
V red = Load(df, row_r + x);
|
||||
V green = Load(df, row_g + x);
|
||||
V blue = Load(df, row_b + x);
|
||||
const V luminance = Set(df, ib->metadata()->IntensityTarget()) *
|
||||
(MulAdd(Set(df, 0.2627f), red,
|
||||
MulAdd(Set(df, 0.6780f), green,
|
||||
Set(df, 0.0593f) * blue)));
|
||||
const V normalized_pq =
|
||||
Min(Set(df, 1.f), (eotf_inv(luminance) - pq_mastering_min) *
|
||||
inv_pq_mastering_range);
|
||||
const V e2 =
|
||||
IfThenElse(normalized_pq < ks, normalized_pq, P(normalized_pq));
|
||||
const V one_minus_e2 = Set(df, 1) - e2;
|
||||
const V one_minus_e2_2 = one_minus_e2 * one_minus_e2;
|
||||
const V one_minus_e2_4 = one_minus_e2_2 * one_minus_e2_2;
|
||||
const V e3 = MulAdd(b, one_minus_e2_4, e2);
|
||||
const V e4 = MulAdd(e3, pq_mastering_range, pq_mastering_min);
|
||||
const V new_luminance =
|
||||
Min(Set(df, display_nits.second),
|
||||
ZeroIfNegative(Set(df, 10000) *
|
||||
TF_PQ().DisplayFromEncoded(df, e4)));
|
||||
|
||||
const V ratio = new_luminance / luminance;
|
||||
const V normalizer =
|
||||
Set(df, ib->metadata()->IntensityTarget()) * inv_max_display_nits;
|
||||
|
||||
for (V* const val : {&red, &green, &blue}) {
|
||||
*val = IfThenElse(luminance <= Set(df, 1e-6f), new_luminance,
|
||||
*val * ratio) *
|
||||
normalizer;
|
||||
}
|
||||
|
||||
tone_mapper.ToneMap(&red, &green, &blue);
|
||||
Store(red, df, row_r + x);
|
||||
Store(green, df, row_g + x);
|
||||
Store(blue, df, row_b + x);
|
||||
}
|
||||
},
|
||||
"ToneMap"));
|
||||
|
||||
return true;
|
||||
"ToneMap");
|
||||
}
|
||||
|
||||
Status GamutMapFrame(ImageBundle* const ib, float preserve_saturation,
|
||||
|
|
@ -141,44 +83,8 @@ Status GamutMapFrame(ImageBundle* const ib, float preserve_saturation,
|
|||
V red = Load(df, row_r + x);
|
||||
V green = Load(df, row_g + x);
|
||||
V blue = Load(df, row_b + x);
|
||||
const V luminance =
|
||||
MulAdd(Set(df, 0.2627f), red,
|
||||
MulAdd(Set(df, 0.6780f), green, Set(df, 0.0593f) * blue));
|
||||
|
||||
// Desaturate out-of-gamut pixels. This is done by mixing each pixel
|
||||
// with just enough gray of the target luminance to make all
|
||||
// components non-negative.
|
||||
// - For saturation preservation, if a component is still larger than
|
||||
// 1 then the pixel is normalized to have a maximum component of 1.
|
||||
// That will reduce its luminance.
|
||||
// - For luminance preservation, getting all components below 1 is
|
||||
// done by mixing in yet more gray. That will desaturate it further.
|
||||
V gray_mix_saturation = Zero(df);
|
||||
V gray_mix_luminance = Zero(df);
|
||||
for (const V val : {red, green, blue}) {
|
||||
const V inv_val_minus_gray = Set(df, 1) / (val - luminance);
|
||||
gray_mix_saturation =
|
||||
IfThenElse(val >= luminance, gray_mix_saturation,
|
||||
Max(gray_mix_saturation, val * inv_val_minus_gray));
|
||||
gray_mix_luminance =
|
||||
Max(gray_mix_luminance,
|
||||
IfThenElse(val <= luminance, gray_mix_saturation,
|
||||
(val - Set(df, 1)) * inv_val_minus_gray));
|
||||
}
|
||||
const V gray_mix =
|
||||
Clamp(Set(df, preserve_saturation) *
|
||||
(gray_mix_saturation - gray_mix_luminance) +
|
||||
gray_mix_luminance,
|
||||
Zero(df), Set(df, 1));
|
||||
for (V* const val : {&red, &green, &blue}) {
|
||||
*val = MulAdd(gray_mix, luminance - *val, *val);
|
||||
}
|
||||
const V normalizer =
|
||||
Set(df, 1) / Max(Set(df, 1), Max(red, Max(green, blue)));
|
||||
for (V* const val : {&red, &green, &blue}) {
|
||||
*val = *val * normalizer;
|
||||
}
|
||||
|
||||
GamutMap(&red, &green, &blue, rec2020_luminances,
|
||||
preserve_saturation);
|
||||
Store(red, df, row_r + x);
|
||||
Store(green, df, row_g + x);
|
||||
Store(blue, df, row_b + x);
|
||||
|
|
|
|||
|
|
@ -13,8 +13,7 @@ namespace jxl {
|
|||
|
||||
static void BM_ToneMapping(benchmark::State& state) {
|
||||
CodecInOut image;
|
||||
const PaddedBytes image_bytes =
|
||||
ReadTestData("third_party/imagecompression.info/flower_foveon.png");
|
||||
const PaddedBytes image_bytes = ReadTestData("jxl/flower/flower.png");
|
||||
JXL_CHECK(SetFromBytes(Span<const uint8_t>(image_bytes), &image));
|
||||
|
||||
// Convert to linear Rec. 2020 so that `ToneMapTo` doesn't have to and we
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
#define JXL_CMS_INTERFACE_H_
|
||||
|
||||
#include "jxl/color_encoding.h"
|
||||
#include "jxl/memory_manager.h"
|
||||
#include "jxl/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jxl/color_encoding.h"
|
||||
#include "jxl/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
|
|
@ -175,10 +174,14 @@ typedef struct {
|
|||
* (linear if outputting to floating point, nonlinear with standard sRGB
|
||||
* transfer function if outputting to unsigned integers) but will not convert
|
||||
* it to to the original color profile. The decoder also does not convert to
|
||||
* the target display color profile, but instead will always indicate which
|
||||
* color profile the returned pixel data is encoded in when using @see
|
||||
* JXL_COLOR_PROFILE_TARGET_DATA so that a CMS can be used to convert the
|
||||
* data.
|
||||
* the target display color profile. To convert the pixel data produced by
|
||||
* the decoder to the original color profile, one of the JxlDecoderGetColor*
|
||||
* functions needs to be called with @ref JXL_COLOR_PROFILE_TARGET_DATA to get
|
||||
* the color profile of the decoder output, and then an external CMS can be
|
||||
* used for conversion.
|
||||
* Note that for lossy compression, this should be set to false for most use
|
||||
* cases, and if needed, the image should be converted to the original color
|
||||
* profile after decoding, as described above.
|
||||
*/
|
||||
JXL_BOOL uses_original_profile;
|
||||
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "jxl/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
|
@ -90,7 +88,7 @@ typedef enum {
|
|||
JXL_TRANSFER_FUNCTION_LINEAR = 8,
|
||||
/** As specified in IEC 61966-2-1 sRGB */
|
||||
JXL_TRANSFER_FUNCTION_SRGB = 13,
|
||||
/** As specified in SMPTE ST 428-1 */
|
||||
/** As specified in SMPTE ST 2084 */
|
||||
JXL_TRANSFER_FUNCTION_PQ = 16,
|
||||
/** As specified in SMPTE ST 428-1 */
|
||||
JXL_TRANSFER_FUNCTION_DCI = 17,
|
||||
|
|
|
|||
1262
third_party/jpeg-xl/lib/include/jxl/decode.h
vendored
1262
third_party/jpeg-xl/lib/include/jxl/decode.h
vendored
File diff suppressed because it is too large
Load diff
122
third_party/jpeg-xl/lib/include/jxl/encode.h
vendored
122
third_party/jpeg-xl/lib/include/jxl/encode.h
vendored
|
|
@ -13,8 +13,8 @@
|
|||
#ifndef JXL_ENCODE_H_
|
||||
#define JXL_ENCODE_H_
|
||||
|
||||
#include "jxl/cms_interface.h"
|
||||
#include "jxl/codestream_header.h"
|
||||
#include "jxl/decode.h"
|
||||
#include "jxl/jxl_export.h"
|
||||
#include "jxl/memory_manager.h"
|
||||
#include "jxl/parallel_runner.h"
|
||||
|
|
@ -73,15 +73,60 @@ typedef enum {
|
|||
|
||||
/** DEPRECATED: the encoder does not return this status and there is no need
|
||||
* to handle or expect it.
|
||||
* Instead, JXL_ENC_ERROR is returned with error condition
|
||||
* JXL_ENC_ERR_NOT_SUPPORTED.
|
||||
*/
|
||||
JXL_ENC_NOT_SUPPORTED = 3,
|
||||
|
||||
} JxlEncoderStatus;
|
||||
|
||||
/**
|
||||
* Id of encoder options for a frame. This includes options such as the
|
||||
* image quality and compression speed for this frame. This does not include
|
||||
* non-frame related encoder options such as for boxes.
|
||||
* Error conditions:
|
||||
* API usage errors have the 0x80 bit set to 1
|
||||
* Other errors have the 0x80 bit set to 0
|
||||
*/
|
||||
typedef enum {
|
||||
/** No error
|
||||
*/
|
||||
JXL_ENC_ERR_OK = 0,
|
||||
|
||||
/** Generic encoder error due to unspecified cause
|
||||
*/
|
||||
JXL_ENC_ERR_GENERIC = 1,
|
||||
|
||||
/** Out of memory
|
||||
* TODO(jon): actually catch this and return this error
|
||||
*/
|
||||
JXL_ENC_ERR_OOM = 2,
|
||||
|
||||
/** JPEG bitstream reconstruction data could not be
|
||||
* represented (e.g. too much tail data)
|
||||
*/
|
||||
JXL_ENC_ERR_JBRD = 3,
|
||||
|
||||
/** Input is invalid (e.g. corrupt JPEG file or ICC profile)
|
||||
*/
|
||||
JXL_ENC_ERR_BAD_INPUT = 4,
|
||||
|
||||
/** The encoder doesn't (yet) support this. Either no version of libjxl
|
||||
* supports this, and the API is used incorrectly, or the libjxl version
|
||||
* should have been checked before trying to do this.
|
||||
*/
|
||||
JXL_ENC_ERR_NOT_SUPPORTED = 0x80,
|
||||
|
||||
/** The encoder API is used in an incorrect way.
|
||||
* In this case, a debug build of libjxl should output a specific error
|
||||
* message. (if not, please open an issue about it)
|
||||
*/
|
||||
JXL_ENC_ERR_API_USAGE = 0x81,
|
||||
|
||||
} JxlEncoderError;
|
||||
|
||||
/**
|
||||
* Id of encoder options for a frame. This includes options such as setting
|
||||
* encoding effort/speed or overriding the use of certain coding tools, for this
|
||||
* frame. This does not include non-frame related encoder options such as for
|
||||
* boxes.
|
||||
*/
|
||||
typedef enum {
|
||||
/** Sets encoder effort/speed level without affecting decoding speed. Valid
|
||||
|
|
@ -236,9 +281,12 @@ typedef enum {
|
|||
*/
|
||||
JXL_ENC_FRAME_SETTING_COLOR_TRANSFORM = 24,
|
||||
|
||||
/** Color space for modular encoding: -1=default, 0-35=reverse color transform
|
||||
/** Reversible color transform for modular encoding: -1=default, 0-41=RCT
|
||||
* index, e.g. index 0 = none, index 6 = YCoCg.
|
||||
* The default behavior is to try several, depending on the speed setting.
|
||||
* If this option is set to a non-default value, the RCT will be globally
|
||||
* applied to the whole frame.
|
||||
* The default behavior is to try several RCTs locally per modular group,
|
||||
* depending on the speed and distance setting.
|
||||
*/
|
||||
JXL_ENC_FRAME_SETTING_MODULAR_COLOR_SPACE = 25,
|
||||
|
||||
|
|
@ -354,6 +402,15 @@ JXL_EXPORT JxlEncoderStatus
|
|||
JxlEncoderSetParallelRunner(JxlEncoder* enc, JxlParallelRunner parallel_runner,
|
||||
void* parallel_runner_opaque);
|
||||
|
||||
/**
|
||||
* Get the (last) error code in case JXL_ENC_ERROR was returned.
|
||||
*
|
||||
* @param enc encoder object.
|
||||
* @return the JxlEncoderError that caused the (last) JXL_ENC_ERROR to be
|
||||
* returned.
|
||||
*/
|
||||
JXL_EXPORT JxlEncoderError JxlEncoderGetError(JxlEncoder* enc);
|
||||
|
||||
/**
|
||||
* Encodes JPEG XL file using the available bytes. @p *avail_out indicates how
|
||||
* many output bytes are available, and @p *next_out points to the input bytes.
|
||||
|
|
@ -519,12 +576,12 @@ JxlEncoderAddJPEGFrame(const JxlEncoderFrameSettings* frame_settings,
|
|||
* If the image has alpha, and alpha is not passed here, it will implicitly be
|
||||
* set to all-opaque (an alpha value of 1.0 everywhere).
|
||||
*
|
||||
* The color profile of the pixels depends on the value of uses_original_profile
|
||||
* in the JxlBasicInfo. If true, the pixels are assumed to be encoded in the
|
||||
* original profile that is set with JxlEncoderSetColorEncoding or
|
||||
* JxlEncoderSetICCProfile. If false, the pixels are assumed to be nonlinear
|
||||
* sRGB for integer data types (JXL_TYPE_UINT8, JXL_TYPE_UINT16), and linear
|
||||
* sRGB for floating point data types (JXL_TYPE_FLOAT16, JXL_TYPE_FLOAT).
|
||||
* The pixels are assumed to be encoded in the original profile that is set with
|
||||
* JxlEncoderSetColorEncoding or JxlEncoderSetICCProfile. If none of these
|
||||
* functions were used, the pixels are assumed to be nonlinear sRGB for integer
|
||||
* data types (JXL_TYPE_UINT8, JXL_TYPE_UINT16), and linear sRGB for floating
|
||||
* point data types (JXL_TYPE_FLOAT16, JXL_TYPE_FLOAT).
|
||||
*
|
||||
* Sample values in floating-point pixel formats are allowed to be outside the
|
||||
* nominal range, e.g. to represent out-of-sRGB-gamut colors in the
|
||||
* uses_original_profile=false case. They are however not allowed to be NaN or
|
||||
|
|
@ -715,6 +772,7 @@ JXL_EXPORT void JxlEncoderCloseInput(JxlEncoder* enc);
|
|||
* is an alternative to JxlEncoderSetICCProfile and only one of these two must
|
||||
* be used. This one sets the color encoding as a @ref JxlColorEncoding, while
|
||||
* the other sets it as ICC binary data.
|
||||
* Must be called after JxlEncoderSetBasicInfo.
|
||||
*
|
||||
* @param enc encoder object.
|
||||
* @param color color encoding. Object owned by the caller and its contents are
|
||||
|
|
@ -730,6 +788,7 @@ JxlEncoderSetColorEncoding(JxlEncoder* enc, const JxlColorEncoding* color);
|
|||
* ICC color profile. This is an alternative to JxlEncoderSetColorEncoding and
|
||||
* only one of these two must be used. This one sets the color encoding as ICC
|
||||
* binary data, while the other defines it as a @ref JxlColorEncoding.
|
||||
* Must be called after JxlEncoderSetBasicInfo.
|
||||
*
|
||||
* @param enc encoder object.
|
||||
* @param icc_profile bytes of the original ICC profile
|
||||
|
|
@ -852,7 +911,25 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetExtraChannelName(JxlEncoder* enc,
|
|||
*/
|
||||
JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetOption(
|
||||
JxlEncoderFrameSettings* frame_settings, JxlEncoderFrameSettingId option,
|
||||
int32_t value);
|
||||
int64_t value);
|
||||
|
||||
/**
|
||||
* Sets a frame-specific option of float type to the encoder options.
|
||||
* The JxlEncoderFrameSettingId argument determines which option is set.
|
||||
*
|
||||
* @param frame_settings set of options and metadata for this frame. Also
|
||||
* includes reference to the encoder object.
|
||||
* @param option ID of the option to set.
|
||||
* @param value Float value to set for this option.
|
||||
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR in
|
||||
* case of an error, such as invalid or unknown option id, or invalid integer
|
||||
* value for the given option. If an error is returned, the state of the
|
||||
* JxlEncoderFrameSettings object is still valid and is the same as before this
|
||||
* function was called.
|
||||
*/
|
||||
JXL_EXPORT JxlEncoderStatus JxlEncoderFrameSettingsSetFloatOption(
|
||||
JxlEncoderFrameSettings* frame_settings, JxlEncoderFrameSettingId option,
|
||||
float value);
|
||||
|
||||
/** Forces the encoder to use the box-based container format (BMFF) even
|
||||
* when not necessary.
|
||||
|
|
@ -893,8 +970,8 @@ JXL_EXPORT JxlEncoderStatus
|
|||
JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata);
|
||||
|
||||
/** Sets the feature level of the JPEG XL codestream. Valid values are 5 and
|
||||
* 10. Keeping the default value of 5 is recommended for compatibility with all
|
||||
* decoders.
|
||||
* 10, or -1 (to choose automatically). Using the minimum required level, or
|
||||
* level 5 in most cases, is recommended for compatibility with all decoders.
|
||||
*
|
||||
* Level 5: for end-user image delivery, this level is the most widely
|
||||
* supported level by image decoders and the recommended level to use unless a
|
||||
|
|
@ -910,16 +987,19 @@ JxlEncoderStoreJPEGMetadata(JxlEncoder* enc, JXL_BOOL store_jpeg_metadata);
|
|||
* 5 limitations, allows CMYK color and up to 32 bits per color channel, but
|
||||
* may be less widely supported.
|
||||
*
|
||||
* The default value is 5. To use level 10 features, the setting must be
|
||||
* explicitly set to 10, the encoder will not automatically enable it. If
|
||||
* incompatible parameters such as too high image resolution for the current
|
||||
* level are set, the encoder will return an error. For internal coding tools,
|
||||
* the encoder will only use those compatible with the level setting.
|
||||
* The default value is -1. This means the encoder will automatically choose
|
||||
* between level 5 and level 10 based on what information is inside the @ref
|
||||
* JxlBasicInfo structure. Do note that some level 10 features, particularly
|
||||
* those used by animated JPEG XL codestreams, might require level 10, even
|
||||
* though the @ref JxlBasicInfo only suggests level 5. In this case, the level
|
||||
* must be explicitly set to 10, otherwise the encoder will return an error.
|
||||
* The encoder will restrict internal encoding choices to those compatible with
|
||||
* the level setting.
|
||||
*
|
||||
* This setting can only be set at the beginning, before encoding starts.
|
||||
*
|
||||
* @param enc encoder object.
|
||||
* @param level the level value to set, must be 5 or 10.
|
||||
* @param level the level value to set, must be -1, 5, or 10.
|
||||
* @return JXL_ENC_SUCCESS if the operation was successful, JXL_ENC_ERROR
|
||||
* otherwise.
|
||||
*/
|
||||
|
|
|
|||
27
third_party/jpeg-xl/lib/include/jxl/types.h
vendored
27
third_party/jpeg-xl/lib/include/jxl/types.h
vendored
|
|
@ -115,6 +115,33 @@ typedef struct {
|
|||
*/
|
||||
typedef char JxlBoxType[4];
|
||||
|
||||
/** Types of progressive detail.
|
||||
* Setting a progressive detail with value N implies all progressive details
|
||||
* with smaller or equal value. Currently only the following level of
|
||||
* progressive detail is implemented:
|
||||
* - kDC (which implies kFrames)
|
||||
* - kLastPasses (which implies kDC and kFrames)
|
||||
* - kPasses (which implies kLastPasses, kDC and kFrames)
|
||||
*/
|
||||
typedef enum {
|
||||
// after completed kRegularFrames
|
||||
kFrames = 0,
|
||||
// after completed DC (1:8)
|
||||
kDC = 1,
|
||||
// after completed AC passes that are the last pass for their resolution
|
||||
// target.
|
||||
kLastPasses = 2,
|
||||
// after completed AC passes that are not the last pass for their resolution
|
||||
// target.
|
||||
kPasses = 3,
|
||||
// during DC frame when lower resolution are completed (1:32, 1:16)
|
||||
kDCProgressive = 4,
|
||||
// after completed groups
|
||||
kDCGroups = 5,
|
||||
// after completed groups
|
||||
kGroups = 6,
|
||||
} JxlProgressiveDetail;
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
67
third_party/jpeg-xl/lib/jxl.cmake
vendored
67
third_party/jpeg-xl/lib/jxl.cmake
vendored
|
|
@ -62,8 +62,12 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
|||
jxl/compressed_dc.cc
|
||||
jxl/compressed_dc.h
|
||||
jxl/convolve-inl.h
|
||||
jxl/convolve.cc
|
||||
jxl/convolve.h
|
||||
jxl/convolve_separable5.cc
|
||||
jxl/convolve_separable7.cc
|
||||
jxl/convolve_slow.cc
|
||||
jxl/convolve_symmetric3.cc
|
||||
jxl/convolve_symmetric5.cc
|
||||
jxl/dct-inl.h
|
||||
jxl/dct_block-inl.h
|
||||
jxl/dct_scales.cc
|
||||
|
|
@ -90,9 +94,9 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
|||
jxl/dec_modular.h
|
||||
jxl/dec_noise.cc
|
||||
jxl/dec_noise.h
|
||||
jxl/dec_params.h
|
||||
jxl/dec_patch_dictionary.cc
|
||||
jxl/dec_patch_dictionary.h
|
||||
jxl/dec_tone_mapping-inl.h
|
||||
jxl/dec_transforms-inl.h
|
||||
jxl/dec_xyb-inl.h
|
||||
jxl/dec_xyb.cc
|
||||
|
|
@ -198,6 +202,8 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
|||
jxl/render_pipeline/stage_chroma_upsampling.h
|
||||
jxl/render_pipeline/stage_epf.cc
|
||||
jxl/render_pipeline/stage_epf.h
|
||||
jxl/render_pipeline/stage_from_linear.cc
|
||||
jxl/render_pipeline/stage_from_linear.h
|
||||
jxl/render_pipeline/stage_gaborish.cc
|
||||
jxl/render_pipeline/stage_gaborish.h
|
||||
jxl/render_pipeline/stage_noise.cc
|
||||
|
|
@ -208,6 +214,10 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
|||
jxl/render_pipeline/stage_splines.h
|
||||
jxl/render_pipeline/stage_spot.cc
|
||||
jxl/render_pipeline/stage_spot.h
|
||||
jxl/render_pipeline/stage_to_linear.cc
|
||||
jxl/render_pipeline/stage_to_linear.h
|
||||
jxl/render_pipeline/stage_tone_mapping.cc
|
||||
jxl/render_pipeline/stage_tone_mapping.h
|
||||
jxl/render_pipeline/stage_upsampling.cc
|
||||
jxl/render_pipeline/stage_upsampling.h
|
||||
jxl/render_pipeline/stage_write.cc
|
||||
|
|
@ -235,8 +245,6 @@ set(JPEGXL_INTERNAL_SOURCES_ENC
|
|||
jxl/butteraugli/butteraugli.cc
|
||||
jxl/butteraugli/butteraugli.h
|
||||
jxl/butteraugli_wrapper.cc
|
||||
jxl/dec_file.cc
|
||||
jxl/dec_file.h
|
||||
jxl/enc_ac_strategy.cc
|
||||
jxl/enc_ac_strategy.h
|
||||
jxl/enc_adaptive_quantization.cc
|
||||
|
|
@ -272,7 +280,6 @@ set(JPEGXL_INTERNAL_SOURCES_ENC
|
|||
jxl/enc_entropy_coder.h
|
||||
jxl/enc_external_image.cc
|
||||
jxl/enc_external_image.h
|
||||
jxl/enc_fast_heuristics.cc
|
||||
jxl/enc_file.cc
|
||||
jxl/enc_file.h
|
||||
jxl/enc_frame.cc
|
||||
|
|
@ -400,10 +407,10 @@ target_compile_options(jxl_dec-obj PRIVATE ${JPEGXL_INTERNAL_FLAGS})
|
|||
target_compile_options(jxl_dec-obj PUBLIC ${JPEGXL_COVERAGE_FLAGS})
|
||||
set_property(TARGET jxl_dec-obj PROPERTY POSITION_INDEPENDENT_CODE ON)
|
||||
target_include_directories(jxl_dec-obj PUBLIC
|
||||
${PROJECT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
$<TARGET_PROPERTY:hwy,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
$<TARGET_PROPERTY:brotlicommon-static,INTERFACE_INCLUDE_DIRECTORIES>
|
||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:$<TARGET_PROPERTY:hwy,INTERFACE_INCLUDE_DIRECTORIES>>"
|
||||
"$<BUILD_INTERFACE:$<TARGET_PROPERTY:brotlicommon-static,INTERFACE_INCLUDE_DIRECTORIES>>"
|
||||
)
|
||||
target_compile_definitions(jxl_dec-obj PUBLIC
|
||||
${OBJ_COMPILE_DEFINITIONS}
|
||||
|
|
@ -470,9 +477,9 @@ add_library(jxl_dec-static STATIC
|
|||
target_link_libraries(jxl_dec-static
|
||||
PUBLIC ${JPEGXL_COVERAGE_FLAGS} ${JPEGXL_DEC_INTERNAL_LIBS})
|
||||
target_include_directories(jxl_dec-static PUBLIC
|
||||
"${PROJECT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
|
||||
|
||||
# The list of objects in the static and shared libraries.
|
||||
set(JPEGXL_INTERNAL_OBJECTS
|
||||
|
|
@ -491,9 +498,9 @@ add_library(jxl-static STATIC ${JPEGXL_INTERNAL_OBJECTS})
|
|||
target_link_libraries(jxl-static
|
||||
PUBLIC ${JPEGXL_COVERAGE_FLAGS} ${JPEGXL_INTERNAL_LIBS})
|
||||
target_include_directories(jxl-static PUBLIC
|
||||
"${PROJECT_SOURCE_DIR}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
|
||||
|
||||
# JXL_EXPORT is defined to "__declspec(dllimport)" automatically by CMake
|
||||
# in Windows builds when including headers from the C API and compiling from
|
||||
|
|
@ -506,7 +513,7 @@ target_compile_definitions(jxl_dec-static INTERFACE -DJXL_EXPORT=)
|
|||
|
||||
# TODO(deymo): Move TCMalloc linkage to the tools/ directory since the library
|
||||
# shouldn't do any allocs anyway.
|
||||
if(${JPEGXL_ENABLE_TCMALLOC})
|
||||
if(JPEGXL_ENABLE_TCMALLOC)
|
||||
pkg_check_modules(TCMallocMinimal REQUIRED IMPORTED_TARGET
|
||||
libtcmalloc_minimal)
|
||||
# tcmalloc 2.8 has concurrency issues that makes it sometimes return nullptr
|
||||
|
|
@ -526,15 +533,14 @@ endif() # JPEGXL_ENABLE_TCMALLOC
|
|||
|
||||
# Install the static library too, but as jxl.a file without the -static except
|
||||
# in Windows.
|
||||
if (NOT WIN32)
|
||||
if (NOT WIN32 OR MINGW)
|
||||
set_target_properties(jxl-static PROPERTIES OUTPUT_NAME "jxl")
|
||||
set_target_properties(jxl_dec-static PROPERTIES OUTPUT_NAME "jxl_dec")
|
||||
endif()
|
||||
install(TARGETS jxl-static DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
install(TARGETS jxl_dec-static DESTINATION ${CMAKE_INSTALL_LIBDIR})
|
||||
|
||||
if (((NOT DEFINED "${TARGET_SUPPORTS_SHARED_LIBS}") OR
|
||||
TARGET_SUPPORTS_SHARED_LIBS) AND NOT JPEGXL_STATIC AND BUILD_SHARED_LIBS)
|
||||
if (BUILD_SHARED_LIBS)
|
||||
|
||||
# Public shared library.
|
||||
add_library(jxl SHARED ${JPEGXL_INTERNAL_OBJECTS})
|
||||
|
|
@ -543,8 +549,8 @@ target_link_libraries(jxl PUBLIC ${JPEGXL_COVERAGE_FLAGS})
|
|||
target_link_libraries(jxl PRIVATE ${JPEGXL_INTERNAL_SHARED_LIBS})
|
||||
# Shared library include path contains only the "include/" paths.
|
||||
target_include_directories(jxl PUBLIC
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/include"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>")
|
||||
set_target_properties(jxl PROPERTIES
|
||||
VERSION ${JPEGXL_LIBRARY_VERSION}
|
||||
SOVERSION ${JPEGXL_LIBRARY_SOVERSION}
|
||||
|
|
@ -591,7 +597,7 @@ foreach(target IN ITEMS jxl jxl_dec)
|
|||
# This hides the default visibility symbols from static libraries bundled into
|
||||
# the shared library. In particular this prevents exposing symbols from hwy
|
||||
# and skcms in the shared library.
|
||||
if(${LINKER_SUPPORT_EXCLUDE_LIBS})
|
||||
if(LINKER_SUPPORT_EXCLUDE_LIBS)
|
||||
set_property(TARGET ${target} APPEND_STRING PROPERTY
|
||||
LINK_FLAGS " ${LINKER_EXCLUDE_LIBS_FLAG}")
|
||||
endif()
|
||||
|
|
@ -607,8 +613,7 @@ install(TARGETS jxl
|
|||
else()
|
||||
add_library(jxl ALIAS jxl-static)
|
||||
add_library(jxl_dec ALIAS jxl_dec-static)
|
||||
endif() # TARGET_SUPPORTS_SHARED_LIBS AND NOT JPEGXL_STATIC AND
|
||||
# BUILD_SHARED_LIBS
|
||||
endif() # BUILD_SHARED_LIBS
|
||||
|
||||
# Add a pkg-config file for libjxl.
|
||||
set(JPEGXL_LIBRARY_REQUIRES
|
||||
|
|
@ -616,6 +621,20 @@ set(JPEGXL_LIBRARY_REQUIRES
|
|||
if(NOT JPEGXL_ENABLE_SKCMS)
|
||||
set(JPEGXL_LIBRARY_REQUIRES "${JPEGXL_LIBRARY_REQUIRES} lcms2")
|
||||
endif()
|
||||
|
||||
# Allow adding prefix if CMAKE_INSTALL_INCLUDEDIR not absolute.
|
||||
if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(PKGCONFIG_TARGET_INCLUDES "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
else()
|
||||
set(PKGCONFIG_TARGET_INCLUDES "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
# Allow adding prefix if CMAKE_INSTALL_LIBDIR not absolute.
|
||||
if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}")
|
||||
set(PKGCONFIG_TARGET_LIBS "${CMAKE_INSTALL_LIBDIR}")
|
||||
else()
|
||||
set(PKGCONFIG_TARGET_LIBS "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/jxl/libjxl.pc.in"
|
||||
"libjxl.pc" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libjxl.pc"
|
||||
|
|
|
|||
9
third_party/jpeg-xl/lib/jxl/alpha.cc
vendored
9
third_party/jpeg-xl/lib/jxl/alpha.cc
vendored
|
|
@ -108,4 +108,13 @@ void UnpremultiplyAlpha(float* JXL_RESTRICT r, float* JXL_RESTRICT g,
|
|||
}
|
||||
}
|
||||
|
||||
void UnpremultiplyAlpha(float* JXL_RESTRICT rgba, size_t num_pixels) {
|
||||
for (size_t x = 0, ix = 0; x < num_pixels; ++x, ix += 4) {
|
||||
const float multiplier = 1.f / std::max(kSmallAlpha, rgba[ix + 3]);
|
||||
rgba[ix] *= multiplier;
|
||||
rgba[ix + 1] *= multiplier;
|
||||
rgba[ix + 2] *= multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
|
|
|
|||
1
third_party/jpeg-xl/lib/jxl/alpha.h
vendored
1
third_party/jpeg-xl/lib/jxl/alpha.h
vendored
|
|
@ -60,6 +60,7 @@ void PremultiplyAlpha(float* JXL_RESTRICT r, float* JXL_RESTRICT g,
|
|||
void UnpremultiplyAlpha(float* JXL_RESTRICT r, float* JXL_RESTRICT g,
|
||||
float* JXL_RESTRICT b, const float* JXL_RESTRICT a,
|
||||
size_t num_pixels);
|
||||
void UnpremultiplyAlpha(float* JXL_RESTRICT rgba, size_t num_pixels);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
|
|
|
|||
3
third_party/jpeg-xl/lib/jxl/alpha_test.cc
vendored
3
third_party/jpeg-xl/lib/jxl/alpha_test.cc
vendored
|
|
@ -5,8 +5,7 @@
|
|||
|
||||
#include "lib/jxl/alpha.h"
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "lib/jxl/test_utils.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace {
|
||||
|
|
|
|||
6
third_party/jpeg-xl/lib/jxl/ans_common.cc
vendored
6
third_party/jpeg-xl/lib/jxl/ans_common.cc
vendored
|
|
@ -12,11 +12,11 @@
|
|||
|
||||
namespace jxl {
|
||||
|
||||
std::vector<int> CreateFlatHistogram(int length, int total_count) {
|
||||
std::vector<int32_t> CreateFlatHistogram(int length, int total_count) {
|
||||
JXL_ASSERT(length > 0);
|
||||
JXL_ASSERT(length <= total_count);
|
||||
const int count = total_count / length;
|
||||
std::vector<int> result(length, count);
|
||||
std::vector<int32_t> result(length, count);
|
||||
const int rem_counts = total_count % length;
|
||||
for (int i = 0; i < rem_counts; ++i) {
|
||||
++result[i];
|
||||
|
|
@ -48,7 +48,7 @@ std::vector<int> CreateFlatHistogram(int length, int total_count) {
|
|||
// underfull nor overfull, and represents exactly two symbols. The overfull
|
||||
// entry might be either overfull or underfull, and is pushed into the
|
||||
// corresponding stack.
|
||||
void InitAliasTable(std::vector<int> distribution, uint32_t range,
|
||||
void InitAliasTable(std::vector<int32_t> distribution, uint32_t range,
|
||||
size_t log_alpha_size, AliasTable::Entry* JXL_RESTRICT a) {
|
||||
while (!distribution.empty() && distribution.back() == 0) {
|
||||
distribution.pop_back();
|
||||
|
|
|
|||
4
third_party/jpeg-xl/lib/jxl/ans_common.h
vendored
4
third_party/jpeg-xl/lib/jxl/ans_common.h
vendored
|
|
@ -32,7 +32,7 @@ static JXL_INLINE uint32_t GetPopulationCountPrecision(uint32_t logcount,
|
|||
// Returns a histogram where the counts are positive, differ by at most 1,
|
||||
// and add up to total_count. The bigger counts (if any) are at the beginning
|
||||
// of the histogram.
|
||||
std::vector<int> CreateFlatHistogram(int length, int total_count);
|
||||
std::vector<int32_t> CreateFlatHistogram(int length, int total_count);
|
||||
|
||||
// An alias table implements a mapping from the [0, ANS_TAB_SIZE) range into
|
||||
// the [0, ANS_MAX_ALPHABET_SIZE) range, satisfying the following conditions:
|
||||
|
|
@ -135,7 +135,7 @@ struct AliasTable {
|
|||
};
|
||||
|
||||
// Computes an alias table for a given distribution.
|
||||
void InitAliasTable(std::vector<int> distribution, uint32_t range,
|
||||
void InitAliasTable(std::vector<int32_t> distribution, uint32_t range,
|
||||
size_t log_alpha_size, AliasTable::Entry* JXL_RESTRICT a);
|
||||
|
||||
} // namespace jxl
|
||||
|
|
|
|||
2
third_party/jpeg-xl/lib/jxl/ans_test.cc
vendored
2
third_party/jpeg-xl/lib/jxl/ans_test.cc
vendored
|
|
@ -112,7 +112,7 @@ void RoundtripRandomUnbalancedStream(int alphabet_size) {
|
|||
constexpr int kPrecision = 1 << 10;
|
||||
Rng rng(0);
|
||||
for (size_t i = 0; i < kReps; i++) {
|
||||
std::vector<int> distributions[kNumHistograms];
|
||||
std::vector<int> distributions[kNumHistograms] = {};
|
||||
for (int j = 0; j < kNumHistograms; j++) {
|
||||
distributions[j].resize(kPrecision);
|
||||
int symbol = 0;
|
||||
|
|
|
|||
6
third_party/jpeg-xl/lib/jxl/aux_out.cc
vendored
6
third_party/jpeg-xl/lib/jxl/aux_out.cc
vendored
|
|
@ -25,6 +25,12 @@ void AuxOut::Print(size_t num_inputs) const {
|
|||
|
||||
printf("Average butteraugli iters: %10.2f\n",
|
||||
num_butteraugli_iters * 1.0 / num_inputs);
|
||||
if (min_quant_rescale != 1.0 || max_quant_rescale != 1.0) {
|
||||
printf("quant rescale range: %f .. %f\n", min_quant_rescale,
|
||||
max_quant_rescale);
|
||||
printf("bitrate error range: %.3f%% .. %.3f%%\n",
|
||||
100.0f * min_bitrate_error, 100.0f * max_bitrate_error);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < layers.size(); ++i) {
|
||||
if (layers[i].total_bits != 0) {
|
||||
|
|
|
|||
81
third_party/jpeg-xl/lib/jxl/aux_out.h
vendored
81
third_party/jpeg-xl/lib/jxl/aux_out.h
vendored
|
|
@ -37,72 +37,54 @@ namespace jxl {
|
|||
enum {
|
||||
kLayerHeader = 0,
|
||||
kLayerTOC,
|
||||
kLayerDictionary,
|
||||
kLayerSplines,
|
||||
kLayerNoise,
|
||||
kLayerQuant,
|
||||
kLayerDequantTables,
|
||||
kLayerOrder,
|
||||
kLayerModularTree,
|
||||
kLayerModularGlobal,
|
||||
kLayerDC,
|
||||
kLayerModularDcGroup,
|
||||
kLayerControlFields,
|
||||
kLayerOrder,
|
||||
kLayerAC,
|
||||
kLayerACTokens,
|
||||
kLayerDictionary,
|
||||
kLayerDots,
|
||||
kLayerSplines,
|
||||
kLayerLossless,
|
||||
kLayerModularGlobal,
|
||||
kLayerModularDcGroup,
|
||||
kLayerModularAcGroup,
|
||||
kLayerModularTree,
|
||||
kLayerAlpha,
|
||||
kLayerDepth,
|
||||
kLayerExtraChannels,
|
||||
kNumImageLayers
|
||||
};
|
||||
|
||||
static inline const char* LayerName(size_t layer) {
|
||||
switch (layer) {
|
||||
case kLayerHeader:
|
||||
return "headers";
|
||||
return "Headers";
|
||||
case kLayerTOC:
|
||||
return "TOC";
|
||||
case kLayerDictionary:
|
||||
return "Patches";
|
||||
case kLayerSplines:
|
||||
return "Splines";
|
||||
case kLayerNoise:
|
||||
return "noise";
|
||||
return "Noise";
|
||||
case kLayerQuant:
|
||||
return "quantizer";
|
||||
case kLayerDequantTables:
|
||||
return "quant tables";
|
||||
case kLayerOrder:
|
||||
return "order";
|
||||
return "Quantizer";
|
||||
case kLayerModularTree:
|
||||
return "ModularTree";
|
||||
case kLayerModularGlobal:
|
||||
return "ModularGlobal";
|
||||
case kLayerDC:
|
||||
return "DC";
|
||||
case kLayerModularDcGroup:
|
||||
return "ModularDcGroup";
|
||||
case kLayerControlFields:
|
||||
return "ControlFields";
|
||||
case kLayerOrder:
|
||||
return "CoeffOrder";
|
||||
case kLayerAC:
|
||||
return "AC";
|
||||
return "ACHistograms";
|
||||
case kLayerACTokens:
|
||||
return "ACTokens";
|
||||
case kLayerDictionary:
|
||||
return "dictionary";
|
||||
case kLayerDots:
|
||||
return "dots";
|
||||
case kLayerSplines:
|
||||
return "splines";
|
||||
case kLayerLossless:
|
||||
return "lossless";
|
||||
case kLayerModularGlobal:
|
||||
return "modularGlobal";
|
||||
case kLayerModularDcGroup:
|
||||
return "modularDcGroup";
|
||||
case kLayerModularAcGroup:
|
||||
return "modularAcGroup";
|
||||
case kLayerModularTree:
|
||||
return "modularTree";
|
||||
case kLayerAlpha:
|
||||
return "alpha";
|
||||
case kLayerDepth:
|
||||
return "depth";
|
||||
case kLayerExtraChannels:
|
||||
return "extra channels";
|
||||
return "ModularAcGroup";
|
||||
default:
|
||||
JXL_ABORT("Invalid layer %d\n", static_cast<int>(layer));
|
||||
}
|
||||
|
|
@ -167,10 +149,22 @@ struct AuxOut {
|
|||
dc_pred_usage[i] += victim.dc_pred_usage[i];
|
||||
dc_pred_usage_xb[i] += victim.dc_pred_usage_xb[i];
|
||||
}
|
||||
max_quant_rescale = std::max(max_quant_rescale, victim.max_quant_rescale);
|
||||
min_quant_rescale = std::min(min_quant_rescale, victim.min_quant_rescale);
|
||||
max_bitrate_error = std::max(max_bitrate_error, victim.max_bitrate_error);
|
||||
min_bitrate_error = std::min(min_bitrate_error, victim.min_bitrate_error);
|
||||
}
|
||||
|
||||
void Print(size_t num_inputs) const;
|
||||
|
||||
size_t TotalBits() const {
|
||||
size_t total = 0;
|
||||
for (const auto& layer : layers) {
|
||||
total += layer.total_bits;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void DumpImage(const char* label, const Image3<T>& image) const {
|
||||
if (!dump_image) return;
|
||||
|
|
@ -285,6 +279,11 @@ struct AuxOut {
|
|||
|
||||
int num_butteraugli_iters = 0;
|
||||
|
||||
float max_quant_rescale = 1.0f;
|
||||
float min_quant_rescale = 1.0f;
|
||||
float min_bitrate_error = 0.0f;
|
||||
float max_bitrate_error = 0.0f;
|
||||
|
||||
// If not empty, additional debugging information (e.g. debug images) is
|
||||
// saved in files with this prefix.
|
||||
std::string debug_prefix;
|
||||
|
|
|
|||
|
|
@ -71,8 +71,9 @@ void* CacheAligned::Allocate(const size_t payload_size, size_t offset) {
|
|||
// To avoid wasting space, the header resides at the end of `unused`,
|
||||
// which therefore cannot be empty (offset == 0).
|
||||
if (offset == 0) {
|
||||
offset = kAlignment; // = round_up(sizeof(AllocationHeader), kAlignment)
|
||||
static_assert(sizeof(AllocationHeader) <= kAlignment, "Else: round up");
|
||||
// SVE/RVV vectors can be large, so we cannot rely on them (including the
|
||||
// padding at the end of AllocationHeader) to fit in kAlignment.
|
||||
offset = hwy::RoundUpTo(sizeof(AllocationHeader), kAlignment);
|
||||
}
|
||||
|
||||
#if JXL_USE_MMAP
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/jxl/base/sanitizer_definitions.h"
|
||||
|
||||
// #if is shorter and safer than #ifdef. *_VERSION are zero if not detected,
|
||||
// otherwise 100 * major + minor version. Note that other packages check for
|
||||
// #ifdef COMPILER_MSVC, so we cannot use that same name.
|
||||
|
|
@ -75,6 +77,16 @@
|
|||
#define JXL_MAYBE_UNUSED __attribute__((unused))
|
||||
#endif
|
||||
|
||||
// MSAN execution won't hurt if some code it not inlined, but this can greatly
|
||||
// improve compilation time. Unfortunately this macro can not be used just
|
||||
// everywhere - inside header files it leads to "multiple definition" error;
|
||||
// though it would be better not to have JXL_INLINE in header overall.
|
||||
#if JXL_MEMORY_SANITIZER || JXL_ADDRESS_SANITIZER || JXL_THREAD_SANITIZER
|
||||
#define JXL_MAYBE_INLINE JXL_MAYBE_UNUSED
|
||||
#else
|
||||
#define JXL_MAYBE_INLINE JXL_INLINE
|
||||
#endif
|
||||
|
||||
#if JXL_COMPILER_MSVC
|
||||
// Unsupported, __assume is not the same.
|
||||
#define JXL_LIKELY(expr) expr
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ class ThreadPool {
|
|||
ThreadPool(const ThreadPool&) = delete;
|
||||
ThreadPool& operator&(const ThreadPool&) = delete;
|
||||
|
||||
JxlParallelRunner runner() const { return runner_; }
|
||||
void* runner_opaque() const { return runner_opaque_; }
|
||||
|
||||
// Runs init_func(num_threads) followed by data_func(task, thread) on worker
|
||||
// thread(s) for every task in [begin, end). init_func() must return a Status
|
||||
// indicating whether the initialization succeeded.
|
||||
|
|
|
|||
3
third_party/jpeg-xl/lib/jxl/base/file_io.h
vendored
3
third_party/jpeg-xl/lib/jxl/base/file_io.h
vendored
|
|
@ -77,7 +77,8 @@ template <typename ContainerType>
|
|||
static inline Status ReadFile(const std::string& pathname,
|
||||
ContainerType* JXL_RESTRICT bytes) {
|
||||
FileWrapper f(pathname, "rb");
|
||||
if (f == nullptr) return JXL_FAILURE("Failed to open file for reading");
|
||||
if (f == nullptr)
|
||||
return JXL_FAILURE("Failed to open file for reading: %s", pathname.c_str());
|
||||
|
||||
// Get size of file in bytes
|
||||
const int64_t size = f.size();
|
||||
|
|
|
|||
3
third_party/jpeg-xl/lib/jxl/base/random.h
vendored
3
third_party/jpeg-xl/lib/jxl/base/random.h
vendored
|
|
@ -20,7 +20,8 @@
|
|||
namespace jxl {
|
||||
struct Rng {
|
||||
explicit Rng(size_t seed)
|
||||
: s{0x94D049BB133111EBull, 0xBF58476D1CE4E5B9ull + seed} {}
|
||||
: s{static_cast<uint64_t>(0x94D049BB133111EBull),
|
||||
static_cast<uint64_t>(0xBF58476D1CE4E5B9ull) + seed} {}
|
||||
|
||||
// Xorshift128+ adapted from xorshift128+-inl.h
|
||||
uint64_t operator()() {
|
||||
|
|
|
|||
7
third_party/jpeg-xl/lib/jxl/blending_test.cc
vendored
7
third_party/jpeg-xl/lib/jxl/blending_test.cc
vendored
|
|
@ -3,11 +3,9 @@
|
|||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "lib/extras/codec.h"
|
||||
#include "lib/jxl/dec_file.h"
|
||||
#include "lib/jxl/image_test_utils.h"
|
||||
#include "lib/jxl/test_utils.h"
|
||||
#include "lib/jxl/testdata.h"
|
||||
|
||||
namespace jxl {
|
||||
|
|
@ -20,9 +18,8 @@ TEST(BlendingTest, Crops) {
|
|||
|
||||
const PaddedBytes compressed =
|
||||
ReadTestData("jxl/blending/cropped_traffic_light.jxl");
|
||||
DecompressParams dparams;
|
||||
CodecInOut decoded;
|
||||
ASSERT_TRUE(DecodeFile(dparams, compressed, &decoded, pool));
|
||||
ASSERT_TRUE(test::DecodeFile({}, compressed, &decoded, pool));
|
||||
ASSERT_THAT(decoded.frames, SizeIs(4));
|
||||
|
||||
int i = 0;
|
||||
|
|
|
|||
52
third_party/jpeg-xl/lib/jxl/codec_in_out.h
vendored
52
third_party/jpeg-xl/lib/jxl/codec_in_out.h
vendored
|
|
@ -13,6 +13,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/alpha.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/common.h"
|
||||
#include "lib/jxl/frame_header.h"
|
||||
|
|
@ -133,18 +134,57 @@ class CodecInOut {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
// Calls PremultiplyAlpha for each ImageBundle (preview/frames).
|
||||
void PremultiplyAlpha() {
|
||||
// Performs "PremultiplyAlpha" for each ImageBundle (preview/frames).
|
||||
bool PremultiplyAlpha() {
|
||||
const auto doPremultiplyAlpha = [](ImageBundle& bundle) {
|
||||
if (!bundle.HasAlpha()) return;
|
||||
if (!bundle.HasColor()) return;
|
||||
auto* color = bundle.color();
|
||||
const auto* alpha = bundle.alpha();
|
||||
JXL_CHECK(color->ysize() == alpha->ysize());
|
||||
JXL_CHECK(color->xsize() == alpha->xsize());
|
||||
for (size_t y = 0; y < color->ysize(); y++) {
|
||||
::jxl::PremultiplyAlpha(color->PlaneRow(0, y), color->PlaneRow(1, y),
|
||||
color->PlaneRow(2, y), alpha->Row(y),
|
||||
color->xsize());
|
||||
}
|
||||
};
|
||||
ExtraChannelInfo* eci = metadata.m.Find(ExtraChannel::kAlpha);
|
||||
if (eci == nullptr || eci->alpha_associated) return; // nothing to do
|
||||
if (eci == nullptr || eci->alpha_associated) return false;
|
||||
if (metadata.m.have_preview) {
|
||||
preview_frame.PremultiplyAlpha();
|
||||
doPremultiplyAlpha(preview_frame);
|
||||
}
|
||||
for (ImageBundle& ib : frames) {
|
||||
ib.PremultiplyAlpha();
|
||||
doPremultiplyAlpha(ib);
|
||||
}
|
||||
eci->alpha_associated = true;
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool UnpremultiplyAlpha() {
|
||||
const auto doUnpremultiplyAlpha = [](ImageBundle& bundle) {
|
||||
if (!bundle.HasAlpha()) return;
|
||||
if (!bundle.HasColor()) return;
|
||||
auto* color = bundle.color();
|
||||
const auto* alpha = bundle.alpha();
|
||||
JXL_CHECK(color->ysize() == alpha->ysize());
|
||||
JXL_CHECK(color->xsize() == alpha->xsize());
|
||||
for (size_t y = 0; y < color->ysize(); y++) {
|
||||
::jxl::UnpremultiplyAlpha(color->PlaneRow(0, y), color->PlaneRow(1, y),
|
||||
color->PlaneRow(2, y), alpha->Row(y),
|
||||
color->xsize());
|
||||
}
|
||||
};
|
||||
ExtraChannelInfo* eci = metadata.m.Find(ExtraChannel::kAlpha);
|
||||
if (eci == nullptr || !eci->alpha_associated) return false;
|
||||
if (metadata.m.have_preview) {
|
||||
doUnpremultiplyAlpha(preview_frame);
|
||||
}
|
||||
for (ImageBundle& ib : frames) {
|
||||
doUnpremultiplyAlpha(ib);
|
||||
}
|
||||
eci->alpha_associated = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// -- DECODER INPUT:
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ static inline constexpr uint64_t EnumBits(Primaries /*unused*/) {
|
|||
}
|
||||
|
||||
// Values from CICP TransferCharacteristics
|
||||
enum TransferFunction : uint32_t {
|
||||
enum class TransferFunction : uint32_t {
|
||||
k709 = 1,
|
||||
kUnknown = 2,
|
||||
kLinear = 8,
|
||||
|
|
|
|||
|
|
@ -468,7 +468,8 @@ Status MaybeCreateProfile(const ColorEncoding& c,
|
|||
CreateICCCurvParaTag({2.6, 1.0, 0.0, 1.0, 0.0}, 3, &tags));
|
||||
break;
|
||||
default:
|
||||
JXL_ABORT("Unknown TF %d", c.tf.GetTransferFunction());
|
||||
JXL_ABORT("Unknown TF %u",
|
||||
static_cast<unsigned int>(c.tf.GetTransferFunction()));
|
||||
}
|
||||
}
|
||||
FinalizeICCTag(&tags, &tag_offset, &tag_size);
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/file_io.h"
|
||||
|
|
|
|||
177
third_party/jpeg-xl/lib/jxl/convolve-inl.h
vendored
177
third_party/jpeg-xl/lib/jxl/convolve-inl.h
vendored
|
|
@ -12,7 +12,10 @@
|
|||
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/base/profiler.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/image_ops.h"
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace jxl {
|
||||
namespace HWY_NAMESPACE {
|
||||
|
|
@ -110,6 +113,180 @@ class Neighbors {
|
|||
}
|
||||
};
|
||||
|
||||
#if HWY_TARGET != HWY_SCALAR
|
||||
|
||||
// Returns indices for SetTableIndices such that TableLookupLanes on the
|
||||
// rightmost unaligned vector (rightmost sample in its most-significant lane)
|
||||
// returns the mirrored values, with the mirror outside the last valid sample.
|
||||
static inline const int32_t* MirrorLanes(const size_t mod) {
|
||||
const HWY_CAPPED(float, 16) d;
|
||||
constexpr size_t kN = MaxLanes(d);
|
||||
|
||||
// For mod = `image width mod 16` 0..15:
|
||||
// last full vec mirrored (mem order) loadedVec mirrorVec idxVec
|
||||
// 0123456789abcdef| fedcba9876543210 fed..210 012..def 012..def
|
||||
// 0123456789abcdef|0 0fedcba98765432 0fe..321 234..f00 123..eff
|
||||
// 0123456789abcdef|01 10fedcba987654 10f..432 456..110 234..ffe
|
||||
// 0123456789abcdef|012 210fedcba9876 210..543 67..2210 34..ffed
|
||||
// 0123456789abcdef|0123 3210fedcba98 321..654 8..33210 4..ffedc
|
||||
// 0123456789abcdef|01234 43210fedcba
|
||||
// 0123456789abcdef|012345 543210fedc
|
||||
// 0123456789abcdef|0123456 6543210fe
|
||||
// 0123456789abcdef|01234567 76543210
|
||||
// 0123456789abcdef|012345678 8765432
|
||||
// 0123456789abcdef|0123456789 987654
|
||||
// 0123456789abcdef|0123456789A A9876
|
||||
// 0123456789abcdef|0123456789AB BA98
|
||||
// 0123456789abcdef|0123456789ABC CBA
|
||||
// 0123456789abcdef|0123456789ABCD DC
|
||||
// 0123456789abcdef|0123456789ABCDE E EDC..10f EED..210 ffe..321
|
||||
#if HWY_CAP_GE512
|
||||
HWY_ALIGN static constexpr int32_t idx_lanes[2 * kN - 1] = {
|
||||
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, //
|
||||
14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
|
||||
#elif HWY_CAP_GE256
|
||||
HWY_ALIGN static constexpr int32_t idx_lanes[2 * kN - 1] = {
|
||||
1, 2, 3, 4, 5, 6, 7, 7, //
|
||||
6, 5, 4, 3, 2, 1, 0};
|
||||
#else // 128-bit
|
||||
HWY_ALIGN static constexpr int32_t idx_lanes[2 * kN - 1] = {1, 2, 3, 3, //
|
||||
2, 1, 0};
|
||||
#endif
|
||||
return idx_lanes + kN - 1 - mod;
|
||||
}
|
||||
|
||||
#endif // HWY_TARGET != HWY_SCALAR
|
||||
|
||||
// Single entry point for convolution.
|
||||
// "Strategy" (Direct*/Separable*) decides kernel size and how to evaluate it.
|
||||
template <class Strategy>
|
||||
class ConvolveT {
|
||||
static constexpr int64_t kRadius = Strategy::kRadius;
|
||||
using Simd = HWY_CAPPED(float, 16);
|
||||
|
||||
public:
|
||||
static size_t MinWidth() {
|
||||
#if HWY_TARGET == HWY_SCALAR
|
||||
// First/Last use mirrored loads of up to +/- kRadius.
|
||||
return 2 * kRadius;
|
||||
#else
|
||||
return Lanes(Simd()) + kRadius;
|
||||
#endif
|
||||
}
|
||||
|
||||
// "Image" is ImageF or Image3F.
|
||||
template <class Image, class Weights>
|
||||
static void Run(const Image& in, const Rect& rect, const Weights& weights,
|
||||
ThreadPool* pool, Image* out) {
|
||||
PROFILER_ZONE("ConvolveT::Run");
|
||||
JXL_CHECK(SameSize(rect, *out));
|
||||
JXL_CHECK(rect.xsize() >= MinWidth());
|
||||
|
||||
static_assert(int64_t(kRadius) <= 3,
|
||||
"Must handle [0, kRadius) and >= kRadius");
|
||||
switch (rect.xsize() % Lanes(Simd())) {
|
||||
case 0:
|
||||
return RunRows<0>(in, rect, weights, pool, out);
|
||||
case 1:
|
||||
return RunRows<1>(in, rect, weights, pool, out);
|
||||
case 2:
|
||||
return RunRows<2>(in, rect, weights, pool, out);
|
||||
default:
|
||||
return RunRows<3>(in, rect, weights, pool, out);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <size_t kSizeModN, class WrapRow, class Weights>
|
||||
static JXL_INLINE void RunRow(const float* JXL_RESTRICT in,
|
||||
const size_t xsize, const int64_t stride,
|
||||
const WrapRow& wrap_row, const Weights& weights,
|
||||
float* JXL_RESTRICT out) {
|
||||
Strategy::template ConvolveRow<kSizeModN>(in, xsize, stride, wrap_row,
|
||||
weights, out);
|
||||
}
|
||||
|
||||
template <size_t kSizeModN, class Weights>
|
||||
static JXL_INLINE void RunBorderRows(const ImageF& in, const Rect& rect,
|
||||
const int64_t ybegin, const int64_t yend,
|
||||
const Weights& weights, ImageF* out) {
|
||||
const int64_t stride = in.PixelsPerRow();
|
||||
const WrapRowMirror wrap_row(in, rect.ysize());
|
||||
for (int64_t y = ybegin; y < yend; ++y) {
|
||||
RunRow<kSizeModN>(rect.ConstRow(in, y), rect.xsize(), stride, wrap_row,
|
||||
weights, out->Row(y));
|
||||
}
|
||||
}
|
||||
|
||||
// Image3F.
|
||||
template <size_t kSizeModN, class Weights>
|
||||
static JXL_INLINE void RunBorderRows(const Image3F& in, const Rect& rect,
|
||||
const int64_t ybegin, const int64_t yend,
|
||||
const Weights& weights, Image3F* out) {
|
||||
const int64_t stride = in.PixelsPerRow();
|
||||
for (int64_t y = ybegin; y < yend; ++y) {
|
||||
for (size_t c = 0; c < 3; ++c) {
|
||||
const WrapRowMirror wrap_row(in.Plane(c), rect.ysize());
|
||||
RunRow<kSizeModN>(rect.ConstPlaneRow(in, c, y), rect.xsize(), stride,
|
||||
wrap_row, weights, out->PlaneRow(c, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t kSizeModN, class Weights>
|
||||
static JXL_INLINE void RunInteriorRows(const ImageF& in, const Rect& rect,
|
||||
const int64_t ybegin,
|
||||
const int64_t yend,
|
||||
const Weights& weights,
|
||||
ThreadPool* pool, ImageF* out) {
|
||||
const int64_t stride = in.PixelsPerRow();
|
||||
JXL_CHECK(RunOnPool(
|
||||
pool, ybegin, yend, ThreadPool::NoInit,
|
||||
[&](const uint32_t y, size_t /*thread*/) HWY_ATTR {
|
||||
RunRow<kSizeModN>(rect.ConstRow(in, y), rect.xsize(), stride,
|
||||
WrapRowUnchanged(), weights, out->Row(y));
|
||||
},
|
||||
"Convolve"));
|
||||
}
|
||||
|
||||
// Image3F.
|
||||
template <size_t kSizeModN, class Weights>
|
||||
static JXL_INLINE void RunInteriorRows(const Image3F& in, const Rect& rect,
|
||||
const int64_t ybegin,
|
||||
const int64_t yend,
|
||||
const Weights& weights,
|
||||
ThreadPool* pool, Image3F* out) {
|
||||
const int64_t stride = in.PixelsPerRow();
|
||||
JXL_CHECK(RunOnPool(
|
||||
pool, ybegin, yend, ThreadPool::NoInit,
|
||||
[&](const uint32_t y, size_t /*thread*/) HWY_ATTR {
|
||||
for (size_t c = 0; c < 3; ++c) {
|
||||
RunRow<kSizeModN>(rect.ConstPlaneRow(in, c, y), rect.xsize(),
|
||||
stride, WrapRowUnchanged(), weights,
|
||||
out->PlaneRow(c, y));
|
||||
}
|
||||
},
|
||||
"Convolve3"));
|
||||
}
|
||||
|
||||
template <size_t kSizeModN, class Image, class Weights>
|
||||
static JXL_INLINE void RunRows(const Image& in, const Rect& rect,
|
||||
const Weights& weights, ThreadPool* pool,
|
||||
Image* out) {
|
||||
const int64_t ysize = rect.ysize();
|
||||
RunBorderRows<kSizeModN>(in, rect, 0, std::min(int64_t(kRadius), ysize),
|
||||
weights, out);
|
||||
if (ysize > 2 * int64_t(kRadius)) {
|
||||
RunInteriorRows<kSizeModN>(in, rect, int64_t(kRadius),
|
||||
ysize - int64_t(kRadius), weights, pool, out);
|
||||
}
|
||||
if (ysize > int64_t(kRadius)) {
|
||||
RunBorderRows<kSizeModN>(in, rect, ysize - int64_t(kRadius), ysize,
|
||||
weights, out);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
// NOLINTNEXTLINE(google-readability-namespace-comments)
|
||||
} // namespace HWY_NAMESPACE
|
||||
|
|
|
|||
1332
third_party/jpeg-xl/lib/jxl/convolve.cc
vendored
1332
third_party/jpeg-xl/lib/jxl/convolve.cc
vendored
File diff suppressed because it is too large
Load diff
26
third_party/jpeg-xl/lib/jxl/convolve.h
vendored
26
third_party/jpeg-xl/lib/jxl/convolve.h
vendored
|
|
@ -75,28 +75,14 @@ const WeightsSymmetric5& WeightsSymmetric5Lowpass();
|
|||
void SlowSymmetric3(const ImageF& in, const Rect& rect,
|
||||
const WeightsSymmetric3& weights, ThreadPool* pool,
|
||||
ImageF* JXL_RESTRICT out);
|
||||
void SlowSymmetric3(const Image3F& in, const Rect& rect,
|
||||
const WeightsSymmetric3& weights, ThreadPool* pool,
|
||||
Image3F* JXL_RESTRICT out);
|
||||
|
||||
void SlowSeparable5(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable5& weights, ThreadPool* pool,
|
||||
ImageF* out);
|
||||
void SlowSeparable5(const Image3F& in, const Rect& rect,
|
||||
const WeightsSeparable5& weights, ThreadPool* pool,
|
||||
Image3F* out);
|
||||
|
||||
void SlowSeparable7(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable7& weights, ThreadPool* pool,
|
||||
ImageF* out);
|
||||
void SlowSeparable7(const Image3F& in, const Rect& rect,
|
||||
const WeightsSeparable7& weights, ThreadPool* pool,
|
||||
Image3F* out);
|
||||
|
||||
void SlowLaplacian5(const ImageF& in, const Rect& rect, ThreadPool* pool,
|
||||
ImageF* out);
|
||||
void SlowLaplacian5(const Image3F& in, const Rect& rect, ThreadPool* pool,
|
||||
Image3F* out);
|
||||
|
||||
void Symmetric3(const ImageF& in, const Rect& rect,
|
||||
const WeightsSymmetric3& weights, ThreadPool* pool,
|
||||
|
|
@ -106,26 +92,14 @@ void Symmetric5(const ImageF& in, const Rect& rect,
|
|||
const WeightsSymmetric5& weights, ThreadPool* pool,
|
||||
ImageF* JXL_RESTRICT out);
|
||||
|
||||
void Symmetric5_3(const Image3F& in, const Rect& rect,
|
||||
const WeightsSymmetric5& weights, ThreadPool* pool,
|
||||
Image3F* JXL_RESTRICT out);
|
||||
|
||||
void Separable5(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable5& weights, ThreadPool* pool,
|
||||
ImageF* out);
|
||||
|
||||
void Separable5_3(const Image3F& in, const Rect& rect,
|
||||
const WeightsSeparable5& weights, ThreadPool* pool,
|
||||
Image3F* out);
|
||||
|
||||
void Separable7(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable7& weights, ThreadPool* pool,
|
||||
ImageF* out);
|
||||
|
||||
void Separable7_3(const Image3F& in, const Rect& rect,
|
||||
const WeightsSeparable7& weights, ThreadPool* pool,
|
||||
Image3F* out);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_JXL_CONVOLVE_H_
|
||||
|
|
|
|||
257
third_party/jpeg-xl/lib/jxl/convolve_separable5.cc
vendored
Normal file
257
third_party/jpeg-xl/lib/jxl/convolve_separable5.cc
vendored
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/jxl/convolve.h"
|
||||
|
||||
#undef HWY_TARGET_INCLUDE
|
||||
#define HWY_TARGET_INCLUDE "lib/jxl/convolve_separable5.cc"
|
||||
#include <hwy/foreach_target.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/convolve-inl.h"
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace jxl {
|
||||
namespace HWY_NAMESPACE {
|
||||
|
||||
// These templates are not found via ADL.
|
||||
using hwy::HWY_NAMESPACE::Vec;
|
||||
|
||||
// 5x5 convolution by separable kernel with a single scan through the input.
|
||||
// This is more cache-efficient than separate horizontal/vertical passes, and
|
||||
// possibly faster (given enough registers) than tiling and/or transposing.
|
||||
//
|
||||
// Overview: imagine a 5x5 window around a central pixel. First convolve the
|
||||
// rows by multiplying the pixels with the corresponding weights from
|
||||
// WeightsSeparable5.horz[abs(x_offset) * 4]. Then multiply each of these
|
||||
// intermediate results by the corresponding vertical weight, i.e.
|
||||
// vert[abs(y_offset) * 4]. Finally, store the sum of these values as the
|
||||
// convolution result at the position of the central pixel in the output.
|
||||
//
|
||||
// Each of these operations uses SIMD vectors. The central pixel and most
|
||||
// importantly the output are aligned, so neighnoring pixels (e.g. x_offset=1)
|
||||
// require unaligned loads. Because weights are supplied in identical groups of
|
||||
// 4, we can use LoadDup128 to load them (slightly faster).
|
||||
//
|
||||
// Uses mirrored boundary handling. Until x >= kRadius, the horizontal
|
||||
// convolution uses Neighbors class to shuffle vectors as if each of its lanes
|
||||
// had been loaded from the mirrored offset. Similarly, the last full vector to
|
||||
// write uses mirroring. In the case of scalar vectors, Neighbors is not usable
|
||||
// and the value is loaded directly. Otherwise, the number of valid pixels
|
||||
// modulo the vector size enables a small optimization: for smaller offsets,
|
||||
// a non-mirrored load is sufficient.
|
||||
class Separable5Strategy {
|
||||
using D = HWY_CAPPED(float, 16);
|
||||
using V = Vec<D>;
|
||||
|
||||
public:
|
||||
static constexpr int64_t kRadius = 2;
|
||||
|
||||
template <size_t kSizeModN, class WrapRow>
|
||||
static JXL_MAYBE_INLINE void ConvolveRow(
|
||||
const float* const JXL_RESTRICT row_m, const size_t xsize,
|
||||
const int64_t stride, const WrapRow& wrap_row,
|
||||
const WeightsSeparable5& weights, float* const JXL_RESTRICT row_out) {
|
||||
const D d;
|
||||
const int64_t neg_stride = -stride; // allows LEA addressing.
|
||||
const float* const JXL_RESTRICT row_t2 =
|
||||
wrap_row(row_m + 2 * neg_stride, stride);
|
||||
const float* const JXL_RESTRICT row_t1 =
|
||||
wrap_row(row_m + 1 * neg_stride, stride);
|
||||
const float* const JXL_RESTRICT row_b1 =
|
||||
wrap_row(row_m + 1 * stride, stride);
|
||||
const float* const JXL_RESTRICT row_b2 =
|
||||
wrap_row(row_m + 2 * stride, stride);
|
||||
|
||||
const V wh0 = LoadDup128(d, weights.horz + 0 * 4);
|
||||
const V wh1 = LoadDup128(d, weights.horz + 1 * 4);
|
||||
const V wh2 = LoadDup128(d, weights.horz + 2 * 4);
|
||||
const V wv0 = LoadDup128(d, weights.vert + 0 * 4);
|
||||
const V wv1 = LoadDup128(d, weights.vert + 1 * 4);
|
||||
const V wv2 = LoadDup128(d, weights.vert + 2 * 4);
|
||||
|
||||
size_t x = 0;
|
||||
|
||||
// More than one iteration for scalars.
|
||||
for (; x < kRadius; x += Lanes(d)) {
|
||||
const V conv0 = HorzConvolveFirst(row_m, x, xsize, wh0, wh1, wh2) * wv0;
|
||||
|
||||
const V conv1t = HorzConvolveFirst(row_t1, x, xsize, wh0, wh1, wh2);
|
||||
const V conv1b = HorzConvolveFirst(row_b1, x, xsize, wh0, wh1, wh2);
|
||||
const V conv1 = MulAdd(conv1t + conv1b, wv1, conv0);
|
||||
|
||||
const V conv2t = HorzConvolveFirst(row_t2, x, xsize, wh0, wh1, wh2);
|
||||
const V conv2b = HorzConvolveFirst(row_b2, x, xsize, wh0, wh1, wh2);
|
||||
const V conv2 = MulAdd(conv2t + conv2b, wv2, conv1);
|
||||
Store(conv2, d, row_out + x);
|
||||
}
|
||||
|
||||
// Main loop: load inputs without padding
|
||||
for (; x + Lanes(d) + kRadius <= xsize; x += Lanes(d)) {
|
||||
const V conv0 = HorzConvolve(row_m + x, wh0, wh1, wh2) * wv0;
|
||||
|
||||
const V conv1t = HorzConvolve(row_t1 + x, wh0, wh1, wh2);
|
||||
const V conv1b = HorzConvolve(row_b1 + x, wh0, wh1, wh2);
|
||||
const V conv1 = MulAdd(conv1t + conv1b, wv1, conv0);
|
||||
|
||||
const V conv2t = HorzConvolve(row_t2 + x, wh0, wh1, wh2);
|
||||
const V conv2b = HorzConvolve(row_b2 + x, wh0, wh1, wh2);
|
||||
const V conv2 = MulAdd(conv2t + conv2b, wv2, conv1);
|
||||
Store(conv2, d, row_out + x);
|
||||
}
|
||||
|
||||
// Last full vector to write (the above loop handled mod >= kRadius)
|
||||
#if HWY_TARGET == HWY_SCALAR
|
||||
while (x < xsize) {
|
||||
#else
|
||||
if (kSizeModN < kRadius) {
|
||||
#endif
|
||||
const V conv0 =
|
||||
HorzConvolveLast<kSizeModN>(row_m, x, xsize, wh0, wh1, wh2) * wv0;
|
||||
|
||||
const V conv1t =
|
||||
HorzConvolveLast<kSizeModN>(row_t1, x, xsize, wh0, wh1, wh2);
|
||||
const V conv1b =
|
||||
HorzConvolveLast<kSizeModN>(row_b1, x, xsize, wh0, wh1, wh2);
|
||||
const V conv1 = MulAdd(conv1t + conv1b, wv1, conv0);
|
||||
|
||||
const V conv2t =
|
||||
HorzConvolveLast<kSizeModN>(row_t2, x, xsize, wh0, wh1, wh2);
|
||||
const V conv2b =
|
||||
HorzConvolveLast<kSizeModN>(row_b2, x, xsize, wh0, wh1, wh2);
|
||||
const V conv2 = MulAdd(conv2t + conv2b, wv2, conv1);
|
||||
Store(conv2, d, row_out + x);
|
||||
x += Lanes(d);
|
||||
}
|
||||
|
||||
// If mod = 0, the above vector was the last.
|
||||
if (kSizeModN != 0) {
|
||||
for (; x < xsize; ++x) {
|
||||
float mul = 0.0f;
|
||||
for (int64_t dy = -kRadius; dy <= kRadius; ++dy) {
|
||||
const float wy = weights.vert[std::abs(dy) * 4];
|
||||
const float* clamped_row = wrap_row(row_m + dy * stride, stride);
|
||||
for (int64_t dx = -kRadius; dx <= kRadius; ++dx) {
|
||||
const float wx = weights.horz[std::abs(dx) * 4];
|
||||
const int64_t clamped_x = Mirror(x + dx, xsize);
|
||||
mul += clamped_row[clamped_x] * wx * wy;
|
||||
}
|
||||
}
|
||||
row_out[x] = mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Same as HorzConvolve for the first/last vector in a row.
|
||||
static JXL_MAYBE_INLINE V HorzConvolveFirst(
|
||||
const float* const JXL_RESTRICT row, const int64_t x, const int64_t xsize,
|
||||
const V wh0, const V wh1, const V wh2) {
|
||||
const D d;
|
||||
const V c = LoadU(d, row + x);
|
||||
const V mul0 = c * wh0;
|
||||
|
||||
#if HWY_TARGET == HWY_SCALAR
|
||||
const V l1 = LoadU(d, row + Mirror(x - 1, xsize));
|
||||
const V l2 = LoadU(d, row + Mirror(x - 2, xsize));
|
||||
#else
|
||||
(void)xsize;
|
||||
const V l1 = Neighbors::FirstL1(c);
|
||||
const V l2 = Neighbors::FirstL2(c);
|
||||
#endif
|
||||
|
||||
const V r1 = LoadU(d, row + x + 1);
|
||||
const V r2 = LoadU(d, row + x + 2);
|
||||
|
||||
const V mul1 = MulAdd(l1 + r1, wh1, mul0);
|
||||
const V mul2 = MulAdd(l2 + r2, wh2, mul1);
|
||||
return mul2;
|
||||
}
|
||||
|
||||
template <size_t kSizeModN>
|
||||
static JXL_MAYBE_INLINE V
|
||||
HorzConvolveLast(const float* const JXL_RESTRICT row, const int64_t x,
|
||||
const int64_t xsize, const V wh0, const V wh1, const V wh2) {
|
||||
const D d;
|
||||
const V c = LoadU(d, row + x);
|
||||
const V mul0 = c * wh0;
|
||||
|
||||
const V l1 = LoadU(d, row + x - 1);
|
||||
const V l2 = LoadU(d, row + x - 2);
|
||||
|
||||
V r1, r2;
|
||||
#if HWY_TARGET == HWY_SCALAR
|
||||
r1 = LoadU(d, row + Mirror(x + 1, xsize));
|
||||
r2 = LoadU(d, row + Mirror(x + 2, xsize));
|
||||
#else
|
||||
const size_t N = Lanes(d);
|
||||
if (kSizeModN == 0) {
|
||||
r2 = TableLookupLanes(c, SetTableIndices(d, MirrorLanes(N - 2)));
|
||||
r1 = TableLookupLanes(c, SetTableIndices(d, MirrorLanes(N - 1)));
|
||||
} else { // == 1
|
||||
const auto last = LoadU(d, row + xsize - N);
|
||||
r2 = TableLookupLanes(last, SetTableIndices(d, MirrorLanes(N - 1)));
|
||||
r1 = last;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sum of pixels with Manhattan distance i, multiplied by weights[i].
|
||||
const V sum1 = l1 + r1;
|
||||
const V mul1 = MulAdd(sum1, wh1, mul0);
|
||||
const V sum2 = l2 + r2;
|
||||
const V mul2 = MulAdd(sum2, wh2, mul1);
|
||||
return mul2;
|
||||
}
|
||||
|
||||
// Requires kRadius valid pixels before/after pos.
|
||||
static JXL_MAYBE_INLINE V HorzConvolve(const float* const JXL_RESTRICT pos,
|
||||
const V wh0, const V wh1,
|
||||
const V wh2) {
|
||||
const D d;
|
||||
const V c = LoadU(d, pos);
|
||||
const V mul0 = c * wh0;
|
||||
|
||||
// Loading anew is faster than combining vectors.
|
||||
const V l1 = LoadU(d, pos - 1);
|
||||
const V r1 = LoadU(d, pos + 1);
|
||||
const V l2 = LoadU(d, pos - 2);
|
||||
const V r2 = LoadU(d, pos + 2);
|
||||
// Sum of pixels with Manhattan distance i, multiplied by weights[i].
|
||||
const V sum1 = l1 + r1;
|
||||
const V mul1 = MulAdd(sum1, wh1, mul0);
|
||||
const V sum2 = l2 + r2;
|
||||
const V mul2 = MulAdd(sum2, wh2, mul1);
|
||||
return mul2;
|
||||
}
|
||||
};
|
||||
|
||||
void Separable5(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable5& weights, ThreadPool* pool,
|
||||
ImageF* out) {
|
||||
using Conv = ConvolveT<Separable5Strategy>;
|
||||
if (rect.xsize() >= Conv::MinWidth()) {
|
||||
return Conv::Run(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
return SlowSeparable5(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-readability-namespace-comments)
|
||||
} // namespace HWY_NAMESPACE
|
||||
} // namespace jxl
|
||||
HWY_AFTER_NAMESPACE();
|
||||
|
||||
#if HWY_ONCE
|
||||
namespace jxl {
|
||||
|
||||
HWY_EXPORT(Separable5);
|
||||
void Separable5(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable5& weights, ThreadPool* pool,
|
||||
ImageF* out) {
|
||||
return HWY_DYNAMIC_DISPATCH(Separable5)(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
#endif // HWY_ONCE
|
||||
282
third_party/jpeg-xl/lib/jxl/convolve_separable7.cc
vendored
Normal file
282
third_party/jpeg-xl/lib/jxl/convolve_separable7.cc
vendored
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/jxl/convolve.h"
|
||||
|
||||
#undef HWY_TARGET_INCLUDE
|
||||
#define HWY_TARGET_INCLUDE "lib/jxl/convolve_separable7.cc"
|
||||
#include <hwy/foreach_target.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/convolve-inl.h"
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace jxl {
|
||||
namespace HWY_NAMESPACE {
|
||||
|
||||
// These templates are not found via ADL.
|
||||
using hwy::HWY_NAMESPACE::Vec;
|
||||
|
||||
// 7x7 convolution by separable kernel with a single scan through the input.
|
||||
// Extended version of Separable5, see documentation there.
|
||||
class Separable7Strategy {
|
||||
using D = HWY_CAPPED(float, 16);
|
||||
using V = Vec<D>;
|
||||
|
||||
public:
|
||||
static constexpr int64_t kRadius = 3;
|
||||
|
||||
template <size_t kSizeModN, class WrapRow>
|
||||
static JXL_MAYBE_INLINE void ConvolveRow(
|
||||
const float* const JXL_RESTRICT row_m, const size_t xsize,
|
||||
const int64_t stride, const WrapRow& wrap_row,
|
||||
const WeightsSeparable7& weights, float* const JXL_RESTRICT row_out) {
|
||||
const D d;
|
||||
const int64_t neg_stride = -stride; // allows LEA addressing.
|
||||
const float* const JXL_RESTRICT row_t3 =
|
||||
wrap_row(row_m + 3 * neg_stride, stride);
|
||||
const float* const JXL_RESTRICT row_t2 =
|
||||
wrap_row(row_m + 2 * neg_stride, stride);
|
||||
const float* const JXL_RESTRICT row_t1 =
|
||||
wrap_row(row_m + 1 * neg_stride, stride);
|
||||
const float* const JXL_RESTRICT row_b1 =
|
||||
wrap_row(row_m + 1 * stride, stride);
|
||||
const float* const JXL_RESTRICT row_b2 =
|
||||
wrap_row(row_m + 2 * stride, stride);
|
||||
const float* const JXL_RESTRICT row_b3 =
|
||||
wrap_row(row_m + 3 * stride, stride);
|
||||
|
||||
const V wh0 = LoadDup128(d, weights.horz + 0 * 4);
|
||||
const V wh1 = LoadDup128(d, weights.horz + 1 * 4);
|
||||
const V wh2 = LoadDup128(d, weights.horz + 2 * 4);
|
||||
const V wh3 = LoadDup128(d, weights.horz + 3 * 4);
|
||||
const V wv0 = LoadDup128(d, weights.vert + 0 * 4);
|
||||
const V wv1 = LoadDup128(d, weights.vert + 1 * 4);
|
||||
const V wv2 = LoadDup128(d, weights.vert + 2 * 4);
|
||||
const V wv3 = LoadDup128(d, weights.vert + 3 * 4);
|
||||
|
||||
size_t x = 0;
|
||||
|
||||
// More than one iteration for scalars.
|
||||
for (; x < kRadius; x += Lanes(d)) {
|
||||
const V conv0 =
|
||||
HorzConvolveFirst(row_m, x, xsize, wh0, wh1, wh2, wh3) * wv0;
|
||||
|
||||
const V conv1t = HorzConvolveFirst(row_t1, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv1b = HorzConvolveFirst(row_b1, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv1 = MulAdd(conv1t + conv1b, wv1, conv0);
|
||||
|
||||
const V conv2t = HorzConvolveFirst(row_t2, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv2b = HorzConvolveFirst(row_b2, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv2 = MulAdd(conv2t + conv2b, wv2, conv1);
|
||||
|
||||
const V conv3t = HorzConvolveFirst(row_t3, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv3b = HorzConvolveFirst(row_b3, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv3 = MulAdd(conv3t + conv3b, wv3, conv2);
|
||||
|
||||
Store(conv3, d, row_out + x);
|
||||
}
|
||||
|
||||
// Main loop: load inputs without padding
|
||||
for (; x + Lanes(d) + kRadius <= xsize; x += Lanes(d)) {
|
||||
const V conv0 = HorzConvolve(row_m + x, wh0, wh1, wh2, wh3) * wv0;
|
||||
|
||||
const V conv1t = HorzConvolve(row_t1 + x, wh0, wh1, wh2, wh3);
|
||||
const V conv1b = HorzConvolve(row_b1 + x, wh0, wh1, wh2, wh3);
|
||||
const V conv1 = MulAdd(conv1t + conv1b, wv1, conv0);
|
||||
|
||||
const V conv2t = HorzConvolve(row_t2 + x, wh0, wh1, wh2, wh3);
|
||||
const V conv2b = HorzConvolve(row_b2 + x, wh0, wh1, wh2, wh3);
|
||||
const V conv2 = MulAdd(conv2t + conv2b, wv2, conv1);
|
||||
|
||||
const V conv3t = HorzConvolve(row_t3 + x, wh0, wh1, wh2, wh3);
|
||||
const V conv3b = HorzConvolve(row_b3 + x, wh0, wh1, wh2, wh3);
|
||||
const V conv3 = MulAdd(conv3t + conv3b, wv3, conv2);
|
||||
|
||||
Store(conv3, d, row_out + x);
|
||||
}
|
||||
|
||||
// Last full vector to write (the above loop handled mod >= kRadius)
|
||||
#if HWY_TARGET == HWY_SCALAR
|
||||
while (x < xsize) {
|
||||
#else
|
||||
if (kSizeModN < kRadius) {
|
||||
#endif
|
||||
const V conv0 =
|
||||
HorzConvolveLast<kSizeModN>(row_m, x, xsize, wh0, wh1, wh2, wh3) *
|
||||
wv0;
|
||||
|
||||
const V conv1t =
|
||||
HorzConvolveLast<kSizeModN>(row_t1, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv1b =
|
||||
HorzConvolveLast<kSizeModN>(row_b1, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv1 = MulAdd(conv1t + conv1b, wv1, conv0);
|
||||
|
||||
const V conv2t =
|
||||
HorzConvolveLast<kSizeModN>(row_t2, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv2b =
|
||||
HorzConvolveLast<kSizeModN>(row_b2, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv2 = MulAdd(conv2t + conv2b, wv2, conv1);
|
||||
|
||||
const V conv3t =
|
||||
HorzConvolveLast<kSizeModN>(row_t3, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv3b =
|
||||
HorzConvolveLast<kSizeModN>(row_b3, x, xsize, wh0, wh1, wh2, wh3);
|
||||
const V conv3 = MulAdd(conv3t + conv3b, wv3, conv2);
|
||||
|
||||
Store(conv3, d, row_out + x);
|
||||
x += Lanes(d);
|
||||
}
|
||||
|
||||
// If mod = 0, the above vector was the last.
|
||||
if (kSizeModN != 0) {
|
||||
for (; x < xsize; ++x) {
|
||||
float mul = 0.0f;
|
||||
for (int64_t dy = -kRadius; dy <= kRadius; ++dy) {
|
||||
const float wy = weights.vert[std::abs(dy) * 4];
|
||||
const float* clamped_row = wrap_row(row_m + dy * stride, stride);
|
||||
for (int64_t dx = -kRadius; dx <= kRadius; ++dx) {
|
||||
const float wx = weights.horz[std::abs(dx) * 4];
|
||||
const int64_t clamped_x = Mirror(x + dx, xsize);
|
||||
mul += clamped_row[clamped_x] * wx * wy;
|
||||
}
|
||||
}
|
||||
row_out[x] = mul;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Same as HorzConvolve for the first/last vector in a row.
|
||||
static JXL_MAYBE_INLINE V HorzConvolveFirst(
|
||||
const float* const JXL_RESTRICT row, const int64_t x, const int64_t xsize,
|
||||
const V wh0, const V wh1, const V wh2, const V wh3) {
|
||||
const D d;
|
||||
const V c = LoadU(d, row + x);
|
||||
const V mul0 = c * wh0;
|
||||
|
||||
#if HWY_TARGET == HWY_SCALAR
|
||||
const V l1 = LoadU(d, row + Mirror(x - 1, xsize));
|
||||
const V l2 = LoadU(d, row + Mirror(x - 2, xsize));
|
||||
const V l3 = LoadU(d, row + Mirror(x - 3, xsize));
|
||||
#else
|
||||
(void)xsize;
|
||||
const V l1 = Neighbors::FirstL1(c);
|
||||
const V l2 = Neighbors::FirstL2(c);
|
||||
const V l3 = Neighbors::FirstL3(c);
|
||||
#endif
|
||||
|
||||
const V r1 = LoadU(d, row + x + 1);
|
||||
const V r2 = LoadU(d, row + x + 2);
|
||||
const V r3 = LoadU(d, row + x + 3);
|
||||
|
||||
const V mul1 = MulAdd(l1 + r1, wh1, mul0);
|
||||
const V mul2 = MulAdd(l2 + r2, wh2, mul1);
|
||||
const V mul3 = MulAdd(l3 + r3, wh3, mul2);
|
||||
return mul3;
|
||||
}
|
||||
|
||||
template <size_t kSizeModN>
|
||||
static JXL_MAYBE_INLINE V HorzConvolveLast(
|
||||
const float* const JXL_RESTRICT row, const int64_t x, const int64_t xsize,
|
||||
const V wh0, const V wh1, const V wh2, const V wh3) {
|
||||
const D d;
|
||||
const V c = LoadU(d, row + x);
|
||||
const V mul0 = c * wh0;
|
||||
|
||||
const V l1 = LoadU(d, row + x - 1);
|
||||
const V l2 = LoadU(d, row + x - 2);
|
||||
const V l3 = LoadU(d, row + x - 3);
|
||||
|
||||
V r1, r2, r3;
|
||||
#if HWY_TARGET == HWY_SCALAR
|
||||
r1 = LoadU(d, row + Mirror(x + 1, xsize));
|
||||
r2 = LoadU(d, row + Mirror(x + 2, xsize));
|
||||
r3 = LoadU(d, row + Mirror(x + 3, xsize));
|
||||
#else
|
||||
const size_t N = Lanes(d);
|
||||
if (kSizeModN == 0) {
|
||||
r3 = TableLookupLanes(c, SetTableIndices(d, MirrorLanes(N - 3)));
|
||||
r2 = TableLookupLanes(c, SetTableIndices(d, MirrorLanes(N - 2)));
|
||||
r1 = TableLookupLanes(c, SetTableIndices(d, MirrorLanes(N - 1)));
|
||||
} else if (kSizeModN == 1) {
|
||||
const auto last = LoadU(d, row + xsize - N);
|
||||
r3 = TableLookupLanes(last, SetTableIndices(d, MirrorLanes(N - 2)));
|
||||
r2 = TableLookupLanes(last, SetTableIndices(d, MirrorLanes(N - 1)));
|
||||
r1 = last;
|
||||
} else /* kSizeModN >= 2 */ {
|
||||
const auto last = LoadU(d, row + xsize - N);
|
||||
r3 = TableLookupLanes(last, SetTableIndices(d, MirrorLanes(N - 1)));
|
||||
r2 = last;
|
||||
r1 = LoadU(d, row + x + 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Sum of pixels with Manhattan distance i, multiplied by weights[i].
|
||||
const V sum1 = l1 + r1;
|
||||
const V mul1 = MulAdd(sum1, wh1, mul0);
|
||||
const V sum2 = l2 + r2;
|
||||
const V mul2 = MulAdd(sum2, wh2, mul1);
|
||||
const V sum3 = l3 + r3;
|
||||
const V mul3 = MulAdd(sum3, wh3, mul2);
|
||||
return mul3;
|
||||
}
|
||||
|
||||
// Returns one vector of horizontal convolution results; lane i is the result
|
||||
// for pixel pos + i. This is the fast path for interior pixels, i.e. kRadius
|
||||
// valid pixels before/after pos.
|
||||
static JXL_MAYBE_INLINE V HorzConvolve(const float* const JXL_RESTRICT pos,
|
||||
const V wh0, const V wh1, const V wh2,
|
||||
const V wh3) {
|
||||
const D d;
|
||||
const V c = LoadU(d, pos);
|
||||
const V mul0 = c * wh0;
|
||||
|
||||
// TODO(janwas): better to Combine
|
||||
const V l1 = LoadU(d, pos - 1);
|
||||
const V r1 = LoadU(d, pos + 1);
|
||||
const V l2 = LoadU(d, pos - 2);
|
||||
const V r2 = LoadU(d, pos + 2);
|
||||
const V l3 = LoadU(d, pos - 3);
|
||||
const V r3 = LoadU(d, pos + 3);
|
||||
// Sum of pixels with Manhattan distance i, multiplied by weights[i].
|
||||
const V sum1 = l1 + r1;
|
||||
const V mul1 = MulAdd(sum1, wh1, mul0);
|
||||
const V sum2 = l2 + r2;
|
||||
const V mul2 = MulAdd(sum2, wh2, mul1);
|
||||
const V sum3 = l3 + r3;
|
||||
const V mul3 = MulAdd(sum3, wh3, mul2);
|
||||
return mul3;
|
||||
}
|
||||
};
|
||||
|
||||
void Separable7(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable7& weights, ThreadPool* pool,
|
||||
ImageF* out) {
|
||||
using Conv = ConvolveT<Separable7Strategy>;
|
||||
if (rect.xsize() >= Conv::MinWidth()) {
|
||||
return Conv::Run(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
return SlowSeparable7(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-readability-namespace-comments)
|
||||
} // namespace HWY_NAMESPACE
|
||||
} // namespace jxl
|
||||
HWY_AFTER_NAMESPACE();
|
||||
|
||||
#if HWY_ONCE
|
||||
namespace jxl {
|
||||
|
||||
HWY_EXPORT(Separable7);
|
||||
void Separable7(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable7& weights, ThreadPool* pool,
|
||||
ImageF* out) {
|
||||
return HWY_DYNAMIC_DISPATCH(Separable7)(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
#endif // HWY_ONCE
|
||||
212
third_party/jpeg-xl/lib/jxl/convolve_slow.cc
vendored
Normal file
212
third_party/jpeg-xl/lib/jxl/convolve_slow.cc
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/jxl/convolve.h"
|
||||
|
||||
#include "lib/jxl/convolve-inl.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Kernels
|
||||
|
||||
// 4 instances of a given literal value, useful as input to LoadDup128.
|
||||
#define JXL_REP4(literal) literal, literal, literal, literal
|
||||
|
||||
// Concentrates energy in low-frequency components (e.g. for antialiasing).
|
||||
const WeightsSymmetric3& WeightsSymmetric3Lowpass() {
|
||||
// Computed by research/convolve_weights.py's cubic spline approximations of
|
||||
// prolate spheroidal wave functions.
|
||||
constexpr float w0 = 0.36208932f;
|
||||
constexpr float w1 = 0.12820096f;
|
||||
constexpr float w2 = 0.03127668f;
|
||||
static constexpr WeightsSymmetric3 weights = {
|
||||
{JXL_REP4(w0)}, {JXL_REP4(w1)}, {JXL_REP4(w2)}};
|
||||
return weights;
|
||||
}
|
||||
|
||||
const WeightsSeparable5& WeightsSeparable5Lowpass() {
|
||||
constexpr float w0 = 0.41714928f;
|
||||
constexpr float w1 = 0.25539268f;
|
||||
constexpr float w2 = 0.03603267f;
|
||||
static constexpr WeightsSeparable5 weights = {
|
||||
{JXL_REP4(w0), JXL_REP4(w1), JXL_REP4(w2)},
|
||||
{JXL_REP4(w0), JXL_REP4(w1), JXL_REP4(w2)}};
|
||||
return weights;
|
||||
}
|
||||
|
||||
const WeightsSymmetric5& WeightsSymmetric5Lowpass() {
|
||||
static constexpr WeightsSymmetric5 weights = {
|
||||
{JXL_REP4(0.1740135f)}, {JXL_REP4(0.1065369f)}, {JXL_REP4(0.0150310f)},
|
||||
{JXL_REP4(0.0652254f)}, {JXL_REP4(0.0012984f)}, {JXL_REP4(0.0092025f)}};
|
||||
return weights;
|
||||
}
|
||||
|
||||
const WeightsSeparable5& WeightsSeparable5Gaussian1() {
|
||||
constexpr float w0 = 0.38774f;
|
||||
constexpr float w1 = 0.24477f;
|
||||
constexpr float w2 = 0.06136f;
|
||||
static constexpr WeightsSeparable5 weights = {
|
||||
{JXL_REP4(w0), JXL_REP4(w1), JXL_REP4(w2)},
|
||||
{JXL_REP4(w0), JXL_REP4(w1), JXL_REP4(w2)}};
|
||||
return weights;
|
||||
}
|
||||
|
||||
const WeightsSeparable5& WeightsSeparable5Gaussian2() {
|
||||
constexpr float w0 = 0.250301f;
|
||||
constexpr float w1 = 0.221461f;
|
||||
constexpr float w2 = 0.153388f;
|
||||
static constexpr WeightsSeparable5 weights = {
|
||||
{JXL_REP4(w0), JXL_REP4(w1), JXL_REP4(w2)},
|
||||
{JXL_REP4(w0), JXL_REP4(w1), JXL_REP4(w2)}};
|
||||
return weights;
|
||||
}
|
||||
|
||||
#undef JXL_REP4
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Slow
|
||||
|
||||
namespace {
|
||||
|
||||
template <class WrapX, class WrapY>
|
||||
float SlowSymmetric3Pixel(const ImageF& in, const int64_t ix, const int64_t iy,
|
||||
const int64_t xsize, const int64_t ysize,
|
||||
const WeightsSymmetric3& weights) {
|
||||
float sum = 0.0f;
|
||||
|
||||
// ix: image; kx: kernel
|
||||
for (int64_t ky = -1; ky <= 1; ky++) {
|
||||
const int64_t y = WrapY()(iy + ky, ysize);
|
||||
const float* JXL_RESTRICT row_in = in.ConstRow(static_cast<size_t>(y));
|
||||
|
||||
const float wc = ky == 0 ? weights.c[0] : weights.r[0];
|
||||
const float wlr = ky == 0 ? weights.r[0] : weights.d[0];
|
||||
|
||||
const int64_t xm1 = WrapX()(ix - 1, xsize);
|
||||
const int64_t xp1 = WrapX()(ix + 1, xsize);
|
||||
sum += row_in[ix] * wc + (row_in[xm1] + row_in[xp1]) * wlr;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
template <class WrapY>
|
||||
void SlowSymmetric3Row(const ImageF& in, const int64_t iy, const int64_t xsize,
|
||||
const int64_t ysize, const WeightsSymmetric3& weights,
|
||||
float* JXL_RESTRICT row_out) {
|
||||
row_out[0] =
|
||||
SlowSymmetric3Pixel<WrapMirror, WrapY>(in, 0, iy, xsize, ysize, weights);
|
||||
for (int64_t ix = 1; ix < xsize - 1; ix++) {
|
||||
row_out[ix] = SlowSymmetric3Pixel<WrapUnchanged, WrapY>(in, ix, iy, xsize,
|
||||
ysize, weights);
|
||||
}
|
||||
{
|
||||
const int64_t ix = xsize - 1;
|
||||
row_out[ix] = SlowSymmetric3Pixel<WrapMirror, WrapY>(in, ix, iy, xsize,
|
||||
ysize, weights);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SlowSymmetric3(const ImageF& in, const Rect& rect,
|
||||
const WeightsSymmetric3& weights, ThreadPool* pool,
|
||||
ImageF* JXL_RESTRICT out) {
|
||||
PROFILER_FUNC;
|
||||
|
||||
const int64_t xsize = static_cast<int64_t>(rect.xsize());
|
||||
const int64_t ysize = static_cast<int64_t>(rect.ysize());
|
||||
const int64_t kRadius = 1;
|
||||
|
||||
JXL_CHECK(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
const int64_t iy = task;
|
||||
float* JXL_RESTRICT out_row = out->Row(static_cast<size_t>(iy));
|
||||
|
||||
if (iy < kRadius || iy >= ysize - kRadius) {
|
||||
SlowSymmetric3Row<WrapMirror>(in, iy, xsize, ysize, weights, out_row);
|
||||
} else {
|
||||
SlowSymmetric3Row<WrapUnchanged>(in, iy, xsize, ysize, weights,
|
||||
out_row);
|
||||
}
|
||||
},
|
||||
"SlowSymmetric3"));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Separable kernels, any radius.
|
||||
float SlowSeparablePixel(const ImageF& in, const Rect& rect, const int64_t x,
|
||||
const int64_t y, const int64_t radius,
|
||||
const float* JXL_RESTRICT horz_weights,
|
||||
const float* JXL_RESTRICT vert_weights) {
|
||||
const size_t xsize = rect.xsize();
|
||||
const size_t ysize = rect.ysize();
|
||||
const WrapMirror wrap;
|
||||
|
||||
float mul = 0.0f;
|
||||
for (int dy = -radius; dy <= radius; ++dy) {
|
||||
const float wy = vert_weights[std::abs(dy) * 4];
|
||||
const size_t sy = wrap(y + dy, ysize);
|
||||
JXL_CHECK(sy < ysize);
|
||||
const float* const JXL_RESTRICT row = rect.ConstRow(in, sy);
|
||||
for (int dx = -radius; dx <= radius; ++dx) {
|
||||
const float wx = horz_weights[std::abs(dx) * 4];
|
||||
const size_t sx = wrap(x + dx, xsize);
|
||||
JXL_CHECK(sx < xsize);
|
||||
mul += row[sx] * wx * wy;
|
||||
}
|
||||
}
|
||||
return mul;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void SlowSeparable5(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable5& weights, ThreadPool* pool,
|
||||
ImageF* out) {
|
||||
PROFILER_FUNC;
|
||||
const float* horz_weights = &weights.horz[0];
|
||||
const float* vert_weights = &weights.vert[0];
|
||||
|
||||
const size_t ysize = rect.ysize();
|
||||
JXL_CHECK(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
const int64_t y = task;
|
||||
|
||||
float* const JXL_RESTRICT row_out = out->Row(y);
|
||||
for (size_t x = 0; x < rect.xsize(); ++x) {
|
||||
row_out[x] = SlowSeparablePixel(in, rect, x, y, /*radius=*/2,
|
||||
horz_weights, vert_weights);
|
||||
}
|
||||
},
|
||||
"SlowSeparable5"));
|
||||
}
|
||||
|
||||
void SlowSeparable7(const ImageF& in, const Rect& rect,
|
||||
const WeightsSeparable7& weights, ThreadPool* pool,
|
||||
ImageF* out) {
|
||||
PROFILER_FUNC;
|
||||
const float* horz_weights = &weights.horz[0];
|
||||
const float* vert_weights = &weights.vert[0];
|
||||
|
||||
const size_t ysize = rect.ysize();
|
||||
JXL_CHECK(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
const int64_t y = task;
|
||||
|
||||
float* const JXL_RESTRICT row_out = out->Row(y);
|
||||
for (size_t x = 0; x < rect.xsize(); ++x) {
|
||||
row_out[x] = SlowSeparablePixel(in, rect, x, y, /*radius=*/3,
|
||||
horz_weights, vert_weights);
|
||||
}
|
||||
},
|
||||
"SlowSeparable7"));
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
191
third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc
vendored
Normal file
191
third_party/jpeg-xl/lib/jxl/convolve_symmetric3.cc
vendored
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/jxl/convolve.h"
|
||||
|
||||
#undef HWY_TARGET_INCLUDE
|
||||
#define HWY_TARGET_INCLUDE "lib/jxl/convolve_symmetric3.cc"
|
||||
#include <hwy/foreach_target.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/convolve-inl.h"
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace jxl {
|
||||
namespace HWY_NAMESPACE {
|
||||
|
||||
// These templates are not found via ADL.
|
||||
using hwy::HWY_NAMESPACE::Vec;
|
||||
|
||||
template <class WrapY, class V>
|
||||
static V WeightedSum(const ImageF& in, const WrapY wrap_y, const size_t ix,
|
||||
const int64_t iy, const size_t ysize, const V wx0,
|
||||
const V wx1, const V wx2) {
|
||||
const HWY_FULL(float) d;
|
||||
const float* JXL_RESTRICT center = in.ConstRow(wrap_y(iy, ysize)) + ix;
|
||||
const auto in_m2 = LoadU(d, center - 2);
|
||||
const auto in_p2 = LoadU(d, center + 2);
|
||||
const auto in_m1 = LoadU(d, center - 1);
|
||||
const auto in_p1 = LoadU(d, center + 1);
|
||||
const auto in_00 = Load(d, center);
|
||||
const auto sum_2 = wx2 * (in_m2 + in_p2);
|
||||
const auto sum_1 = wx1 * (in_m1 + in_p1);
|
||||
const auto sum_0 = wx0 * in_00;
|
||||
return sum_2 + sum_1 + sum_0;
|
||||
}
|
||||
|
||||
// 3x3 convolution by symmetric kernel with a single scan through the input.
|
||||
class Symmetric3Strategy {
|
||||
using D = HWY_CAPPED(float, 16);
|
||||
using V = Vec<D>;
|
||||
|
||||
public:
|
||||
static constexpr int64_t kRadius = 1;
|
||||
|
||||
// Only accesses pixels in [0, xsize).
|
||||
template <size_t kSizeModN, class WrapRow>
|
||||
static JXL_MAYBE_INLINE void ConvolveRow(
|
||||
const float* const JXL_RESTRICT row_m, const size_t xsize,
|
||||
const int64_t stride, const WrapRow& wrap_row,
|
||||
const WeightsSymmetric3& weights, float* const JXL_RESTRICT row_out) {
|
||||
const D d;
|
||||
// t, m, b = top, middle, bottom row;
|
||||
const float* const JXL_RESTRICT row_t = wrap_row(row_m - stride, stride);
|
||||
const float* const JXL_RESTRICT row_b = wrap_row(row_m + stride, stride);
|
||||
|
||||
// Must load in advance - compiler doesn't understand LoadDup128 and
|
||||
// schedules them too late.
|
||||
const V w0 = LoadDup128(d, weights.c);
|
||||
const V w1 = LoadDup128(d, weights.r);
|
||||
const V w2 = LoadDup128(d, weights.d);
|
||||
|
||||
// l, c, r = left, center, right. Leftmost vector: need FirstL1.
|
||||
{
|
||||
const V tc = LoadU(d, row_t + 0);
|
||||
const V mc = LoadU(d, row_m + 0);
|
||||
const V bc = LoadU(d, row_b + 0);
|
||||
const V tl = Neighbors::FirstL1(tc);
|
||||
const V tr = LoadU(d, row_t + 0 + 1);
|
||||
const V ml = Neighbors::FirstL1(mc);
|
||||
const V mr = LoadU(d, row_m + 0 + 1);
|
||||
const V bl = Neighbors::FirstL1(bc);
|
||||
const V br = LoadU(d, row_b + 0 + 1);
|
||||
const V conv =
|
||||
WeightedSum(tl, tc, tr, ml, mc, mr, bl, bc, br, w0, w1, w2);
|
||||
Store(conv, d, row_out + 0);
|
||||
}
|
||||
|
||||
// Loop as long as we can load enough new values:
|
||||
const size_t N = Lanes(d);
|
||||
size_t x = N;
|
||||
for (; x + N + kRadius <= xsize; x += N) {
|
||||
const auto conv = ConvolveValid(row_t, row_m, row_b, x, w0, w1, w2);
|
||||
Store(conv, d, row_out + x);
|
||||
}
|
||||
|
||||
// For final (partial) vector:
|
||||
const V tc = LoadU(d, row_t + x);
|
||||
const V mc = LoadU(d, row_m + x);
|
||||
const V bc = LoadU(d, row_b + x);
|
||||
|
||||
V tr, mr, br;
|
||||
#if HWY_TARGET == HWY_SCALAR
|
||||
tr = tc; // Single-lane => mirrored right neighbor = center value.
|
||||
mr = mc;
|
||||
br = bc;
|
||||
#else
|
||||
if (kSizeModN == 0) {
|
||||
// The above loop didn't handle the last vector because it needs an
|
||||
// additional right neighbor (generated via mirroring).
|
||||
auto mirror = SetTableIndices(d, MirrorLanes(N - 1));
|
||||
tr = TableLookupLanes(tc, mirror);
|
||||
mr = TableLookupLanes(mc, mirror);
|
||||
br = TableLookupLanes(bc, mirror);
|
||||
} else {
|
||||
auto mirror = SetTableIndices(d, MirrorLanes((xsize % N) - 1));
|
||||
// Loads last valid value into uppermost lane and mirrors.
|
||||
tr = TableLookupLanes(LoadU(d, row_t + xsize - N), mirror);
|
||||
mr = TableLookupLanes(LoadU(d, row_m + xsize - N), mirror);
|
||||
br = TableLookupLanes(LoadU(d, row_b + xsize - N), mirror);
|
||||
}
|
||||
#endif
|
||||
|
||||
const V tl = LoadU(d, row_t + x - 1);
|
||||
const V ml = LoadU(d, row_m + x - 1);
|
||||
const V bl = LoadU(d, row_b + x - 1);
|
||||
const V conv = WeightedSum(tl, tc, tr, ml, mc, mr, bl, bc, br, w0, w1, w2);
|
||||
Store(conv, d, row_out + x);
|
||||
}
|
||||
|
||||
private:
|
||||
// Returns sum{x_i * w_i}.
|
||||
template <class V>
|
||||
static JXL_MAYBE_INLINE V WeightedSum(const V tl, const V tc, const V tr,
|
||||
const V ml, const V mc, const V mr,
|
||||
const V bl, const V bc, const V br,
|
||||
const V w0, const V w1, const V w2) {
|
||||
const V sum_tb = tc + bc;
|
||||
|
||||
// Faster than 5 mul + 4 FMA.
|
||||
const V mul0 = mc * w0;
|
||||
const V sum_lr = ml + mr;
|
||||
|
||||
const V x1 = sum_tb + sum_lr;
|
||||
const V mul1 = MulAdd(x1, w1, mul0);
|
||||
|
||||
const V sum_t2 = tl + tr;
|
||||
const V sum_b2 = bl + br;
|
||||
const V x2 = sum_t2 + sum_b2;
|
||||
const V mul2 = MulAdd(x2, w2, mul1);
|
||||
return mul2;
|
||||
}
|
||||
|
||||
static JXL_MAYBE_INLINE V ConvolveValid(const float* JXL_RESTRICT row_t,
|
||||
const float* JXL_RESTRICT row_m,
|
||||
const float* JXL_RESTRICT row_b,
|
||||
const int64_t x, const V w0,
|
||||
const V w1, const V w2) {
|
||||
const D d;
|
||||
const V tc = LoadU(d, row_t + x);
|
||||
const V mc = LoadU(d, row_m + x);
|
||||
const V bc = LoadU(d, row_b + x);
|
||||
const V tl = LoadU(d, row_t + x - 1);
|
||||
const V tr = LoadU(d, row_t + x + 1);
|
||||
const V ml = LoadU(d, row_m + x - 1);
|
||||
const V mr = LoadU(d, row_m + x + 1);
|
||||
const V bl = LoadU(d, row_b + x - 1);
|
||||
const V br = LoadU(d, row_b + x + 1);
|
||||
return WeightedSum(tl, tc, tr, ml, mc, mr, bl, bc, br, w0, w1, w2);
|
||||
}
|
||||
};
|
||||
|
||||
void Symmetric3(const ImageF& in, const Rect& rect,
|
||||
const WeightsSymmetric3& weights, ThreadPool* pool,
|
||||
ImageF* out) {
|
||||
using Conv = ConvolveT<Symmetric3Strategy>;
|
||||
if (rect.xsize() >= Conv::MinWidth()) {
|
||||
return Conv::Run(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
return SlowSymmetric3(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-readability-namespace-comments)
|
||||
} // namespace HWY_NAMESPACE
|
||||
} // namespace jxl
|
||||
HWY_AFTER_NAMESPACE();
|
||||
|
||||
#if HWY_ONCE
|
||||
namespace jxl {
|
||||
|
||||
HWY_EXPORT(Symmetric3);
|
||||
void Symmetric3(const ImageF& in, const Rect& rect,
|
||||
const WeightsSymmetric3& weights, ThreadPool* pool,
|
||||
ImageF* out) {
|
||||
return HWY_DYNAMIC_DISPATCH(Symmetric3)(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
#endif // HWY_ONCE
|
||||
183
third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc
vendored
Normal file
183
third_party/jpeg-xl/lib/jxl/convolve_symmetric5.cc
vendored
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/jxl/convolve.h"
|
||||
|
||||
#undef HWY_TARGET_INCLUDE
|
||||
#define HWY_TARGET_INCLUDE "lib/jxl/convolve_symmetric5.cc"
|
||||
#include <hwy/foreach_target.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/common.h" // RoundUpTo
|
||||
#include "lib/jxl/convolve-inl.h"
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace jxl {
|
||||
namespace HWY_NAMESPACE {
|
||||
|
||||
// These templates are not found via ADL.
|
||||
using hwy::HWY_NAMESPACE::Vec;
|
||||
|
||||
// Weighted sum of 1x5 pixels around ix, iy with [wx2 wx1 wx0 wx1 wx2].
|
||||
template <class WrapY>
|
||||
static float WeightedSumBorder(const ImageF& in, const WrapY wrap_y,
|
||||
const int64_t ix, const int64_t iy,
|
||||
const size_t xsize, const size_t ysize,
|
||||
const float wx0, const float wx1,
|
||||
const float wx2) {
|
||||
const WrapMirror wrap_x;
|
||||
const float* JXL_RESTRICT row = in.ConstRow(wrap_y(iy, ysize));
|
||||
const float in_m2 = row[wrap_x(ix - 2, xsize)];
|
||||
const float in_p2 = row[wrap_x(ix + 2, xsize)];
|
||||
const float in_m1 = row[wrap_x(ix - 1, xsize)];
|
||||
const float in_p1 = row[wrap_x(ix + 1, xsize)];
|
||||
const float in_00 = row[ix];
|
||||
const float sum_2 = wx2 * (in_m2 + in_p2);
|
||||
const float sum_1 = wx1 * (in_m1 + in_p1);
|
||||
const float sum_0 = wx0 * in_00;
|
||||
return sum_2 + sum_1 + sum_0;
|
||||
}
|
||||
|
||||
template <class WrapY, class V>
|
||||
static V WeightedSum(const ImageF& in, const WrapY wrap_y, const size_t ix,
|
||||
const int64_t iy, const size_t ysize, const V wx0,
|
||||
const V wx1, const V wx2) {
|
||||
const HWY_FULL(float) d;
|
||||
const float* JXL_RESTRICT center = in.ConstRow(wrap_y(iy, ysize)) + ix;
|
||||
const auto in_m2 = LoadU(d, center - 2);
|
||||
const auto in_p2 = LoadU(d, center + 2);
|
||||
const auto in_m1 = LoadU(d, center - 1);
|
||||
const auto in_p1 = LoadU(d, center + 1);
|
||||
const auto in_00 = Load(d, center);
|
||||
const auto sum_2 = wx2 * (in_m2 + in_p2);
|
||||
const auto sum_1 = wx1 * (in_m1 + in_p1);
|
||||
const auto sum_0 = wx0 * in_00;
|
||||
return sum_2 + sum_1 + sum_0;
|
||||
}
|
||||
|
||||
// Produces result for one pixel
|
||||
template <class WrapY>
|
||||
float Symmetric5Border(const ImageF& in, const Rect& rect, const int64_t ix,
|
||||
const int64_t iy, const WeightsSymmetric5& weights) {
|
||||
const float w0 = weights.c[0];
|
||||
const float w1 = weights.r[0];
|
||||
const float w2 = weights.R[0];
|
||||
const float w4 = weights.d[0];
|
||||
const float w5 = weights.L[0];
|
||||
const float w8 = weights.D[0];
|
||||
|
||||
const size_t xsize = rect.xsize();
|
||||
const size_t ysize = rect.ysize();
|
||||
const WrapY wrap_y;
|
||||
// Unrolled loop over all 5 rows of the kernel.
|
||||
float sum0 = WeightedSumBorder(in, wrap_y, ix, iy, xsize, ysize, w0, w1, w2);
|
||||
|
||||
sum0 += WeightedSumBorder(in, wrap_y, ix, iy - 2, xsize, ysize, w2, w5, w8);
|
||||
float sum1 =
|
||||
WeightedSumBorder(in, wrap_y, ix, iy + 2, xsize, ysize, w2, w5, w8);
|
||||
|
||||
sum0 += WeightedSumBorder(in, wrap_y, ix, iy - 1, xsize, ysize, w1, w4, w5);
|
||||
sum1 += WeightedSumBorder(in, wrap_y, ix, iy + 1, xsize, ysize, w1, w4, w5);
|
||||
|
||||
return sum0 + sum1;
|
||||
}
|
||||
|
||||
// Produces result for one vector's worth of pixels
|
||||
template <class WrapY>
|
||||
static void Symmetric5Interior(const ImageF& in, const Rect& rect,
|
||||
const int64_t ix, const int64_t iy,
|
||||
const WeightsSymmetric5& weights,
|
||||
float* JXL_RESTRICT row_out) {
|
||||
const HWY_FULL(float) d;
|
||||
|
||||
const auto w0 = LoadDup128(d, weights.c);
|
||||
const auto w1 = LoadDup128(d, weights.r);
|
||||
const auto w2 = LoadDup128(d, weights.R);
|
||||
const auto w4 = LoadDup128(d, weights.d);
|
||||
const auto w5 = LoadDup128(d, weights.L);
|
||||
const auto w8 = LoadDup128(d, weights.D);
|
||||
|
||||
const size_t ysize = rect.ysize();
|
||||
const WrapY wrap_y;
|
||||
// Unrolled loop over all 5 rows of the kernel.
|
||||
auto sum0 = WeightedSum(in, wrap_y, ix, iy, ysize, w0, w1, w2);
|
||||
|
||||
sum0 += WeightedSum(in, wrap_y, ix, iy - 2, ysize, w2, w5, w8);
|
||||
auto sum1 = WeightedSum(in, wrap_y, ix, iy + 2, ysize, w2, w5, w8);
|
||||
|
||||
sum0 += WeightedSum(in, wrap_y, ix, iy - 1, ysize, w1, w4, w5);
|
||||
sum1 += WeightedSum(in, wrap_y, ix, iy + 1, ysize, w1, w4, w5);
|
||||
|
||||
Store(sum0 + sum1, d, row_out + ix);
|
||||
}
|
||||
|
||||
template <class WrapY>
|
||||
static void Symmetric5Row(const ImageF& in, const Rect& rect, const int64_t iy,
|
||||
const WeightsSymmetric5& weights,
|
||||
float* JXL_RESTRICT row_out) {
|
||||
const int64_t kRadius = 2;
|
||||
const size_t xsize = rect.xsize();
|
||||
|
||||
size_t ix = 0;
|
||||
const HWY_FULL(float) d;
|
||||
const size_t N = Lanes(d);
|
||||
const size_t aligned_x = RoundUpTo(kRadius, N);
|
||||
for (; ix < std::min(aligned_x, xsize); ++ix) {
|
||||
row_out[ix] = Symmetric5Border<WrapY>(in, rect, ix, iy, weights);
|
||||
}
|
||||
for (; ix + N + kRadius <= xsize; ix += N) {
|
||||
Symmetric5Interior<WrapY>(in, rect, ix, iy, weights, row_out);
|
||||
}
|
||||
for (; ix < xsize; ++ix) {
|
||||
row_out[ix] = Symmetric5Border<WrapY>(in, rect, ix, iy, weights);
|
||||
}
|
||||
}
|
||||
|
||||
static JXL_NOINLINE void Symmetric5BorderRow(const ImageF& in, const Rect& rect,
|
||||
const int64_t iy,
|
||||
const WeightsSymmetric5& weights,
|
||||
float* JXL_RESTRICT row_out) {
|
||||
return Symmetric5Row<WrapMirror>(in, rect, iy, weights, row_out);
|
||||
}
|
||||
|
||||
// Semi-vectorized (interior pixels Fonly); called directly like slow::, unlike
|
||||
// the fully vectorized strategies below.
|
||||
void Symmetric5(const ImageF& in, const Rect& rect,
|
||||
const WeightsSymmetric5& weights, ThreadPool* pool,
|
||||
ImageF* JXL_RESTRICT out) {
|
||||
PROFILER_FUNC;
|
||||
|
||||
const size_t ysize = rect.ysize();
|
||||
JXL_CHECK(RunOnPool(
|
||||
pool, 0, static_cast<uint32_t>(ysize), ThreadPool::NoInit,
|
||||
[&](const uint32_t task, size_t /*thread*/) {
|
||||
const int64_t iy = task;
|
||||
|
||||
if (iy < 2 || iy >= static_cast<ssize_t>(ysize) - 2) {
|
||||
Symmetric5BorderRow(in, rect, iy, weights, out->Row(iy));
|
||||
} else {
|
||||
Symmetric5Row<WrapUnchanged>(in, rect, iy, weights, out->Row(iy));
|
||||
}
|
||||
},
|
||||
"Symmetric5x5Convolution"));
|
||||
}
|
||||
|
||||
// NOLINTNEXTLINE(google-readability-namespace-comments)
|
||||
} // namespace HWY_NAMESPACE
|
||||
} // namespace jxl
|
||||
HWY_AFTER_NAMESPACE();
|
||||
|
||||
#if HWY_ONCE
|
||||
namespace jxl {
|
||||
|
||||
HWY_EXPORT(Symmetric5);
|
||||
void Symmetric5(const ImageF& in, const Rect& rect,
|
||||
const WeightsSymmetric5& weights, ThreadPool* pool,
|
||||
ImageF* JXL_RESTRICT out) {
|
||||
return HWY_DYNAMIC_DISPATCH(Symmetric5)(in, rect, weights, pool, out);
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
#endif // HWY_ONCE
|
||||
6
third_party/jpeg-xl/lib/jxl/convolve_test.cc
vendored
6
third_party/jpeg-xl/lib/jxl/convolve_test.cc
vendored
|
|
@ -148,9 +148,9 @@ void TestConvolve() {
|
|||
ThreadPoolInternal pool3(3);
|
||||
for (size_t ysize = kConvolveMaxRadius; ysize < 16; ++ysize) {
|
||||
JXL_DEBUG(JXL_DEBUG_CONVOLVE,
|
||||
"%" PRIuS " x %" PRIuS
|
||||
" (target %d)===============================",
|
||||
xsize, ysize, HWY_TARGET);
|
||||
"%" PRIuS " x %" PRIuS " (target %" PRIx64
|
||||
")===============================",
|
||||
xsize, ysize, static_cast<int64_t>(HWY_TARGET));
|
||||
|
||||
JXL_DEBUG(JXL_DEBUG_CONVOLVE, "Sym3------------------");
|
||||
VerifySymmetric3(xsize, ysize, null_pool, &rng);
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ int TestInit(void* jpegxl_opaque, size_t num_threads) { return 0; }
|
|||
|
||||
} // namespace
|
||||
|
||||
TEST_F(DataParallelTest, RunnerCalledParamenters) {
|
||||
TEST_F(DataParallelTest, RunnerCalledParameters) {
|
||||
EXPECT_TRUE(pool_.Run(
|
||||
1234, 5678, [](size_t /* num_threads */) { return true; },
|
||||
[](uint32_t /* task */, size_t /* thread */) { return; }));
|
||||
|
|
|
|||
4
third_party/jpeg-xl/lib/jxl/dec_ans.cc
vendored
4
third_party/jpeg-xl/lib/jxl/dec_ans.cc
vendored
|
|
@ -48,7 +48,7 @@ inline int DecodeVarLenUint16(BitReader* input) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
Status ReadHistogram(int precision_bits, std::vector<int>* counts,
|
||||
Status ReadHistogram(int precision_bits, std::vector<int32_t>* counts,
|
||||
BitReader* input) {
|
||||
int simple_code = input->ReadBits(1);
|
||||
if (simple_code == 1) {
|
||||
|
|
@ -228,7 +228,7 @@ Status DecodeANSCodes(const size_t num_histograms,
|
|||
AliasTable::Entry* alias_tables =
|
||||
reinterpret_cast<AliasTable::Entry*>(result->alias_tables.get());
|
||||
for (size_t c = 0; c < num_histograms; ++c) {
|
||||
std::vector<int> counts;
|
||||
std::vector<int32_t> counts;
|
||||
if (!ReadHistogram(ANS_LOG_TAB_SIZE, &counts, in)) {
|
||||
return JXL_FAILURE("Invalid histogram bitstream.");
|
||||
}
|
||||
|
|
|
|||
45
third_party/jpeg-xl/lib/jxl/dec_cache.cc
vendored
45
third_party/jpeg-xl/lib/jxl/dec_cache.cc
vendored
|
|
@ -9,11 +9,14 @@
|
|||
#include "lib/jxl/render_pipeline/stage_blending.h"
|
||||
#include "lib/jxl/render_pipeline/stage_chroma_upsampling.h"
|
||||
#include "lib/jxl/render_pipeline/stage_epf.h"
|
||||
#include "lib/jxl/render_pipeline/stage_from_linear.h"
|
||||
#include "lib/jxl/render_pipeline/stage_gaborish.h"
|
||||
#include "lib/jxl/render_pipeline/stage_noise.h"
|
||||
#include "lib/jxl/render_pipeline/stage_patches.h"
|
||||
#include "lib/jxl/render_pipeline/stage_splines.h"
|
||||
#include "lib/jxl/render_pipeline/stage_spot.h"
|
||||
#include "lib/jxl/render_pipeline/stage_to_linear.h"
|
||||
#include "lib/jxl/render_pipeline/stage_tone_mapping.h"
|
||||
#include "lib/jxl/render_pipeline/stage_upsampling.h"
|
||||
#include "lib/jxl/render_pipeline/stage_write.h"
|
||||
#include "lib/jxl/render_pipeline/stage_xyb.h"
|
||||
|
|
@ -89,7 +92,9 @@ Status PassesDecoderState::PreparePipeline(ImageBundle* decoded,
|
|||
}
|
||||
|
||||
if ((frame_header.flags & FrameHeader::kPatches) != 0) {
|
||||
builder.AddStage(GetPatchesStage(&shared->image_features.patches));
|
||||
builder.AddStage(
|
||||
GetPatchesStage(&shared->image_features.patches,
|
||||
3 + shared->metadata->m.num_extra_channels));
|
||||
}
|
||||
if ((frame_header.flags & FrameHeader::kSplines) != 0) {
|
||||
builder.AddStage(GetSplineStage(&shared->image_features.splines));
|
||||
|
|
@ -150,19 +155,29 @@ Status PassesDecoderState::PreparePipeline(ImageBundle* decoded,
|
|||
height, rgb_output_is_rgba,
|
||||
has_alpha, alpha_c));
|
||||
} else {
|
||||
bool linear = false;
|
||||
if (frame_header.color_transform == ColorTransform::kYCbCr) {
|
||||
builder.AddStage(GetYCbCrStage());
|
||||
} else if (frame_header.color_transform == ColorTransform::kXYB) {
|
||||
builder.AddStage(GetXYBStage(output_encoding_info));
|
||||
builder.AddStage(GetXYBStage(output_encoding_info.opsin_params));
|
||||
linear = true;
|
||||
} // Nothing to do for kNone.
|
||||
|
||||
if (options.coalescing && NeedsBlending(this)) {
|
||||
if (linear) {
|
||||
builder.AddStage(GetFromLinearStage(output_encoding_info));
|
||||
linear = false;
|
||||
}
|
||||
builder.AddStage(
|
||||
GetBlendingStage(this, output_encoding_info.color_encoding));
|
||||
}
|
||||
|
||||
if (options.coalescing && frame_header.CanBeReferenced() &&
|
||||
!frame_header.save_before_color_transform) {
|
||||
if (linear) {
|
||||
builder.AddStage(GetFromLinearStage(output_encoding_info));
|
||||
linear = false;
|
||||
}
|
||||
builder.AddStage(GetWriteToImageBundleStage(
|
||||
&frame_storage_for_referencing, output_encoding_info.color_encoding));
|
||||
}
|
||||
|
|
@ -180,10 +195,30 @@ Status PassesDecoderState::PreparePipeline(ImageBundle* decoded,
|
|||
}
|
||||
}
|
||||
|
||||
auto tone_mapping_stage = GetToneMappingStage(output_encoding_info);
|
||||
if (tone_mapping_stage) {
|
||||
if (!linear) {
|
||||
auto to_linear_stage = GetToLinearStage(output_encoding_info);
|
||||
if (!to_linear_stage) {
|
||||
return JXL_FAILURE(
|
||||
"attempting to perform tone mapping on colorspace not "
|
||||
"convertible to linear");
|
||||
}
|
||||
builder.AddStage(std::move(to_linear_stage));
|
||||
linear = true;
|
||||
}
|
||||
builder.AddStage(std::move(tone_mapping_stage));
|
||||
}
|
||||
|
||||
if (linear) {
|
||||
builder.AddStage(GetFromLinearStage(output_encoding_info));
|
||||
linear = false;
|
||||
}
|
||||
|
||||
if (pixel_callback.IsPresent()) {
|
||||
builder.AddStage(GetWriteToPixelCallbackStage(pixel_callback, width,
|
||||
height, rgb_output_is_rgba,
|
||||
has_alpha, alpha_c));
|
||||
builder.AddStage(GetWriteToPixelCallbackStage(
|
||||
pixel_callback, width, height, rgb_output_is_rgba, has_alpha,
|
||||
unpremul_alpha, alpha_c));
|
||||
} else if (rgb_output) {
|
||||
builder.AddStage(GetWriteToU8Stage(rgb_output, rgb_stride, height,
|
||||
rgb_output_is_rgba, has_alpha,
|
||||
|
|
|
|||
6
third_party/jpeg-xl/lib/jxl/dec_cache.h
vendored
6
third_party/jpeg-xl/lib/jxl/dec_cache.h
vendored
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <hwy/base.h> // HWY_ALIGN_MAX
|
||||
|
||||
#include "jxl/decode.h"
|
||||
|
|
@ -89,6 +90,9 @@ struct PassesDecoderState {
|
|||
// If true, rgb_output or callback output is RGBA using 4 instead of 3 bytes
|
||||
// per pixel.
|
||||
bool rgb_output_is_rgba;
|
||||
// If true, the RGBA output will be unpremultiplied before writing to the
|
||||
// output callback (the output buffer case is handled in ConvertToExternal).
|
||||
bool unpremul_alpha;
|
||||
|
||||
// Callback for line-by-line output.
|
||||
PixelCallback pixel_callback;
|
||||
|
|
@ -134,7 +138,9 @@ struct PassesDecoderState {
|
|||
|
||||
rgb_output = nullptr;
|
||||
rgb_output_is_rgba = false;
|
||||
unpremul_alpha = false;
|
||||
fast_xyb_srgb8_conversion = false;
|
||||
pixel_callback = PixelCallback();
|
||||
used_acs = 0;
|
||||
|
||||
upsampler8x = GetUpsamplingStage(shared->metadata->transform_data, 0, 3);
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ void InverseMoveToFrontTransform(uint8_t* v, int v_len) {
|
|||
}
|
||||
}
|
||||
|
||||
bool VerifyContextMap(const std::vector<uint8_t>& context_map,
|
||||
const size_t num_htrees) {
|
||||
Status VerifyContextMap(const std::vector<uint8_t>& context_map,
|
||||
const size_t num_htrees) {
|
||||
std::vector<bool> have_htree(num_htrees);
|
||||
size_t num_found = 0;
|
||||
for (const uint8_t htree : context_map) {
|
||||
|
|
@ -58,8 +58,8 @@ bool VerifyContextMap(const std::vector<uint8_t>& context_map,
|
|||
|
||||
} // namespace
|
||||
|
||||
bool DecodeContextMap(std::vector<uint8_t>* context_map, size_t* num_htrees,
|
||||
BitReader* input) {
|
||||
Status DecodeContextMap(std::vector<uint8_t>* context_map, size_t* num_htrees,
|
||||
BitReader* input) {
|
||||
bool is_simple = input->ReadFixedBits<1>();
|
||||
if (is_simple) {
|
||||
int bits_per_entry = input->ReadFixedBits<2>();
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ constexpr size_t kMaxClusters = 256;
|
|||
// context_map->size() must be the number of possible context ids.
|
||||
// Sets *num_htrees to the number of different histogram ids in
|
||||
// *context_map.
|
||||
bool DecodeContextMap(std::vector<uint8_t>* context_map, size_t* num_htrees,
|
||||
BitReader* input);
|
||||
Status DecodeContextMap(std::vector<uint8_t>* context_map, size_t* num_htrees,
|
||||
BitReader* input);
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
|
|
|
|||
|
|
@ -451,14 +451,15 @@ Status ConvertToExternal(const jxl::ImageBundle& ib, size_t bits_per_sample,
|
|||
JxlEndianness endianness, size_t stride,
|
||||
jxl::ThreadPool* pool, void* out_image,
|
||||
size_t out_size, const PixelCallback& out_callback,
|
||||
jxl::Orientation undo_orientation) {
|
||||
jxl::Orientation undo_orientation,
|
||||
bool unpremul_alpha) {
|
||||
bool want_alpha = num_channels == 2 || num_channels == 4;
|
||||
size_t color_channels = num_channels <= 2 ? 1 : 3;
|
||||
|
||||
const Image3F* color = &ib.color();
|
||||
// Undo premultiplied alpha.
|
||||
Image3F unpremul;
|
||||
if (ib.AlphaIsPremultiplied() && ib.HasAlpha()) {
|
||||
if (ib.AlphaIsPremultiplied() && ib.HasAlpha() && unpremul_alpha) {
|
||||
unpremul = Image3F(color->xsize(), color->ysize());
|
||||
CopyImageTo(*color, &unpremul);
|
||||
for (size_t y = 0; y < unpremul.ysize(); y++) {
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ Status ConvertToExternal(const jxl::ImageBundle& ib, size_t bits_per_sample,
|
|||
JxlEndianness endianness, size_t stride_out,
|
||||
jxl::ThreadPool* thread_pool, void* out_image,
|
||||
size_t out_size, const PixelCallback& out_callback,
|
||||
jxl::Orientation undo_orientation);
|
||||
jxl::Orientation undo_orientation,
|
||||
bool unpremul_alpha = false);
|
||||
|
||||
// Converts single-channel image to interleaved void* pixel buffer with the
|
||||
// given format, with a single channel.
|
||||
|
|
|
|||
177
third_party/jpeg-xl/lib/jxl/dec_file.cc
vendored
177
third_party/jpeg-xl/lib/jxl/dec_file.cc
vendored
|
|
@ -1,177 +0,0 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#include "lib/jxl/dec_file.h"
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "jxl/decode.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/override.h"
|
||||
#include "lib/jxl/base/profiler.h"
|
||||
#include "lib/jxl/color_management.h"
|
||||
#include "lib/jxl/common.h"
|
||||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/dec_frame.h"
|
||||
#include "lib/jxl/frame_header.h"
|
||||
#include "lib/jxl/headers.h"
|
||||
#include "lib/jxl/icc_codec.h"
|
||||
#include "lib/jxl/image_bundle.h"
|
||||
#include "lib/jxl/jpeg/dec_jpeg_data_writer.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace {
|
||||
|
||||
Status DecodeHeaders(BitReader* reader, CodecInOut* io) {
|
||||
JXL_RETURN_IF_ERROR(ReadSizeHeader(reader, &io->metadata.size));
|
||||
|
||||
JXL_RETURN_IF_ERROR(ReadImageMetadata(reader, &io->metadata.m));
|
||||
|
||||
io->metadata.transform_data.nonserialized_xyb_encoded =
|
||||
io->metadata.m.xyb_encoded;
|
||||
JXL_RETURN_IF_ERROR(Bundle::Read(reader, &io->metadata.transform_data));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// To avoid the complexity of file I/O and buffering, we assume the bitstream
|
||||
// is loaded (or for large images/sequences: mapped into) memory.
|
||||
Status DecodeFile(const DecompressParams& dparams,
|
||||
const Span<const uint8_t> file, CodecInOut* JXL_RESTRICT io,
|
||||
ThreadPool* pool) {
|
||||
PROFILER_ZONE("DecodeFile uninstrumented");
|
||||
|
||||
// Marker
|
||||
JxlSignature signature = JxlSignatureCheck(file.data(), file.size());
|
||||
if (signature == JXL_SIG_NOT_ENOUGH_BYTES || signature == JXL_SIG_INVALID) {
|
||||
return JXL_FAILURE("File does not start with known JPEG XL signature");
|
||||
}
|
||||
|
||||
std::unique_ptr<jpeg::JPEGData> jpeg_data = nullptr;
|
||||
if (dparams.keep_dct) {
|
||||
if (io->Main().jpeg_data == nullptr) {
|
||||
return JXL_FAILURE("Caller must set jpeg_data");
|
||||
}
|
||||
jpeg_data = std::move(io->Main().jpeg_data);
|
||||
}
|
||||
|
||||
Status ret = true;
|
||||
{
|
||||
BitReader reader(file);
|
||||
BitReaderScopedCloser reader_closer(&reader, &ret);
|
||||
if (reader.ReadFixedBits<16>() != 0x0AFF) {
|
||||
// We don't have a naked codestream. Make a quick & dirty attempt to find
|
||||
// the codestream.
|
||||
// TODO(jon): get rid of this whole function
|
||||
const unsigned char* begin = file.data();
|
||||
const unsigned char* end = file.data() + file.size() - 4;
|
||||
while (begin < end) {
|
||||
if (!memcmp(begin, "jxlc", 4)) break;
|
||||
begin++;
|
||||
}
|
||||
if (begin >= end) return JXL_FAILURE("Couldn't find jxl codestream");
|
||||
reader.SkipBits(8 * (begin - file.data() + 2));
|
||||
unsigned int firstbytes = reader.ReadFixedBits<16>();
|
||||
if (firstbytes != 0x0AFF)
|
||||
return JXL_FAILURE("Codestream didn't start with FF0A but with %X",
|
||||
firstbytes);
|
||||
}
|
||||
|
||||
{
|
||||
JXL_RETURN_IF_ERROR(DecodeHeaders(&reader, io));
|
||||
size_t xsize = io->metadata.xsize();
|
||||
size_t ysize = io->metadata.ysize();
|
||||
JXL_RETURN_IF_ERROR(VerifyDimensions(&io->constraints, xsize, ysize));
|
||||
}
|
||||
|
||||
if (io->metadata.m.color_encoding.WantICC()) {
|
||||
PaddedBytes icc;
|
||||
JXL_RETURN_IF_ERROR(ReadICC(&reader, &icc));
|
||||
JXL_RETURN_IF_ERROR(io->metadata.m.color_encoding.SetICC(std::move(icc)));
|
||||
}
|
||||
// Set ICC profile in jpeg_data.
|
||||
if (jpeg_data) {
|
||||
Status res = jpeg::SetJPEGDataFromICC(io->metadata.m.color_encoding.ICC(),
|
||||
jpeg_data.get());
|
||||
if (!res) {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
PassesDecoderState dec_state;
|
||||
JXL_RETURN_IF_ERROR(dec_state.output_encoding_info.Set(
|
||||
io->metadata,
|
||||
ColorEncoding::LinearSRGB(io->metadata.m.color_encoding.IsGray())));
|
||||
|
||||
if (io->metadata.m.have_preview) {
|
||||
JXL_RETURN_IF_ERROR(reader.JumpToByteBoundary());
|
||||
JXL_RETURN_IF_ERROR(DecodeFrame(dparams, &dec_state, pool, &reader,
|
||||
&io->preview_frame, io->metadata,
|
||||
&io->constraints, /*is_preview=*/true));
|
||||
}
|
||||
|
||||
// Only necessary if no ICC and no preview.
|
||||
JXL_RETURN_IF_ERROR(reader.JumpToByteBoundary());
|
||||
if (io->metadata.m.have_animation && dparams.keep_dct) {
|
||||
return JXL_FAILURE("Cannot decode to JPEG an animation");
|
||||
}
|
||||
|
||||
io->frames.clear();
|
||||
Status dec_ok(false);
|
||||
do {
|
||||
io->frames.emplace_back(&io->metadata.m);
|
||||
if (jpeg_data) {
|
||||
io->frames.back().jpeg_data = std::move(jpeg_data);
|
||||
}
|
||||
// Skip frames that are not displayed.
|
||||
bool found_displayed_frame = true;
|
||||
do {
|
||||
dec_ok =
|
||||
DecodeFrame(dparams, &dec_state, pool, &reader, &io->frames.back(),
|
||||
io->metadata, &io->constraints);
|
||||
if (!dparams.allow_partial_files) {
|
||||
JXL_RETURN_IF_ERROR(dec_ok);
|
||||
} else if (!dec_ok) {
|
||||
io->frames.pop_back();
|
||||
found_displayed_frame = false;
|
||||
break;
|
||||
}
|
||||
} while (dec_state.shared->frame_header.frame_type !=
|
||||
FrameType::kRegularFrame &&
|
||||
dec_state.shared->frame_header.frame_type !=
|
||||
FrameType::kSkipProgressive);
|
||||
if (found_displayed_frame) {
|
||||
// if found_displayed_frame is true io->frames shouldn't be empty
|
||||
// because we added a frame before the loop.
|
||||
JXL_ASSERT(!io->frames.empty());
|
||||
io->dec_pixels += io->frames.back().xsize() * io->frames.back().ysize();
|
||||
}
|
||||
} while (!dec_state.shared->frame_header.is_last && dec_ok);
|
||||
|
||||
if (io->frames.empty()) return JXL_FAILURE("Not enough data.");
|
||||
|
||||
if (dparams.check_decompressed_size && !dparams.allow_partial_files &&
|
||||
dparams.max_downsampling == 1) {
|
||||
if (reader.TotalBitsConsumed() != file.size() * kBitsPerByte) {
|
||||
return JXL_FAILURE("DecodeFile reader position not at EOF.");
|
||||
}
|
||||
}
|
||||
// Suppress errors when decoding partial files with DC frames.
|
||||
if (!reader.AllReadsWithinBounds() && dparams.allow_partial_files) {
|
||||
reader_closer.CloseAndSuppressError();
|
||||
}
|
||||
|
||||
io->CheckMetadata();
|
||||
// reader is closed here.
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
48
third_party/jpeg-xl/lib/jxl/dec_file.h
vendored
48
third_party/jpeg-xl/lib/jxl/dec_file.h
vendored
|
|
@ -1,48 +0,0 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_JXL_DEC_FILE_H_
|
||||
#define LIB_JXL_DEC_FILE_H_
|
||||
|
||||
// Top-level interface for JXL decoding.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/padded_bytes.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/codec_in_out.h"
|
||||
#include "lib/jxl/dec_params.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
// Decodes the preview image, if present, and stores it in `preview`.
|
||||
// Must be the first frame in the file. Does nothing if there is no preview
|
||||
// frame present according to the metadata.
|
||||
Status DecodePreview(const DecompressParams& dparams,
|
||||
const CodecMetadata& metadata,
|
||||
BitReader* JXL_RESTRICT reader, ThreadPool* pool,
|
||||
ImageBundle* JXL_RESTRICT preview, uint64_t* dec_pixels,
|
||||
const SizeConstraints* constraints);
|
||||
|
||||
// Implementation detail: currently decodes to linear sRGB. The contract is:
|
||||
// `io` appears 'identical' (modulo compression artifacts) to the encoder input
|
||||
// in a color-aware viewer. Note that `io->metadata.m.color_encoding`
|
||||
// identifies the color space that was passed to the encoder; clients that want
|
||||
// that same encoding must call `io->TransformTo` afterwards.
|
||||
Status DecodeFile(const DecompressParams& params,
|
||||
const Span<const uint8_t> file, CodecInOut* io,
|
||||
ThreadPool* pool = nullptr);
|
||||
|
||||
static inline Status DecodeFile(const DecompressParams& params,
|
||||
const PaddedBytes& file, CodecInOut* io,
|
||||
ThreadPool* pool = nullptr) {
|
||||
return DecodeFile(params, Span<const uint8_t>(file), io, pool);
|
||||
}
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_JXL_DEC_FILE_H_
|
||||
364
third_party/jpeg-xl/lib/jxl/dec_frame.cc
vendored
364
third_party/jpeg-xl/lib/jxl/dec_frame.cc
vendored
|
|
@ -15,6 +15,7 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "jxl/types.h"
|
||||
#include "lib/jxl/ac_context.h"
|
||||
#include "lib/jxl/ac_strategy.h"
|
||||
#include "lib/jxl/ans_params.h"
|
||||
|
|
@ -35,7 +36,6 @@
|
|||
#include "lib/jxl/dec_cache.h"
|
||||
#include "lib/jxl/dec_group.h"
|
||||
#include "lib/jxl/dec_modular.h"
|
||||
#include "lib/jxl/dec_params.h"
|
||||
#include "lib/jxl/dec_patch_dictionary.h"
|
||||
#include "lib/jxl/dec_xyb.h"
|
||||
#include "lib/jxl/epf.h"
|
||||
|
|
@ -77,186 +77,72 @@ Status DecodeGlobalDCInfo(BitReader* reader, bool is_jpeg,
|
|||
}
|
||||
} // namespace
|
||||
|
||||
Status DecodeFrameHeader(BitReader* JXL_RESTRICT reader,
|
||||
FrameHeader* JXL_RESTRICT frame_header) {
|
||||
JXL_ASSERT(frame_header->nonserialized_metadata != nullptr);
|
||||
JXL_RETURN_IF_ERROR(ReadFrameHeader(reader, frame_header));
|
||||
return true;
|
||||
}
|
||||
|
||||
static BitReader* GetReaderForSection(
|
||||
size_t num_groups, size_t num_passes, size_t group_codes_begin,
|
||||
const std::vector<uint64_t>& group_offsets,
|
||||
const std::vector<uint32_t>& group_sizes, BitReader* JXL_RESTRICT reader,
|
||||
BitReader* JXL_RESTRICT store, size_t index) {
|
||||
if (num_groups == 1 && num_passes == 1) return reader;
|
||||
const size_t group_offset = group_codes_begin + group_offsets[index];
|
||||
const size_t next_group_offset =
|
||||
group_codes_begin + group_offsets[index] + group_sizes[index];
|
||||
// The order of these variables must be:
|
||||
// group_codes_begin <= group_offset <= next_group_offset <= file.size()
|
||||
JXL_DASSERT(group_codes_begin <= group_offset);
|
||||
JXL_DASSERT(group_offset <= next_group_offset);
|
||||
JXL_DASSERT(next_group_offset <= reader->TotalBytes());
|
||||
const size_t group_size = next_group_offset - group_offset;
|
||||
const size_t remaining_size = reader->TotalBytes() - group_offset;
|
||||
const size_t size = std::min(group_size + 8, remaining_size);
|
||||
*store =
|
||||
BitReader(Span<const uint8_t>(reader->FirstByte() + group_offset, size));
|
||||
return store;
|
||||
}
|
||||
|
||||
Status DecodeFrame(const DecompressParams& dparams,
|
||||
PassesDecoderState* dec_state, ThreadPool* JXL_RESTRICT pool,
|
||||
BitReader* JXL_RESTRICT reader, ImageBundle* decoded,
|
||||
const CodecMetadata& metadata,
|
||||
const SizeConstraints* constraints, bool is_preview) {
|
||||
PROFILER_ZONE("DecodeFrame uninstrumented");
|
||||
|
||||
Status DecodeFrame(PassesDecoderState* dec_state, ThreadPool* JXL_RESTRICT pool,
|
||||
const uint8_t* next_in, size_t avail_in,
|
||||
ImageBundle* decoded, const CodecMetadata& metadata,
|
||||
bool use_slow_rendering_pipeline) {
|
||||
FrameDecoder frame_decoder(dec_state, metadata, pool,
|
||||
dparams.use_slow_render_pipeline);
|
||||
use_slow_rendering_pipeline);
|
||||
|
||||
frame_decoder.SetFrameSizeLimits(constraints);
|
||||
|
||||
JXL_RETURN_IF_ERROR(frame_decoder.InitFrame(
|
||||
reader, decoded, is_preview, dparams.allow_partial_files,
|
||||
dparams.allow_partial_files && dparams.allow_more_progressive_steps,
|
||||
true));
|
||||
|
||||
// Handling of progressive decoding.
|
||||
const FrameHeader& frame_header = frame_decoder.GetFrameHeader();
|
||||
{
|
||||
size_t max_passes = dparams.max_passes;
|
||||
size_t max_downsampling = std::max(
|
||||
dparams.max_downsampling >> (frame_header.dc_level * 3), size_t(1));
|
||||
// TODO(veluca): deal with downsamplings >= 8.
|
||||
if (max_downsampling >= 8) {
|
||||
max_passes = 0;
|
||||
} else {
|
||||
for (uint32_t i = 0; i < frame_header.passes.num_downsample; ++i) {
|
||||
if (max_downsampling >= frame_header.passes.downsample[i] &&
|
||||
max_passes > frame_header.passes.last_pass[i]) {
|
||||
max_passes = frame_header.passes.last_pass[i] + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Do not use downsampling for kReferenceOnly frames.
|
||||
if (frame_header.frame_type == FrameType::kReferenceOnly) {
|
||||
max_passes = frame_header.passes.num_passes;
|
||||
}
|
||||
max_passes = std::min<size_t>(max_passes, frame_header.passes.num_passes);
|
||||
frame_decoder.SetMaxPasses(max_passes);
|
||||
}
|
||||
frame_decoder.SetRenderSpotcolors(dparams.render_spotcolors);
|
||||
frame_decoder.SetCoalescing(dparams.coalescing);
|
||||
|
||||
size_t processed_bytes = reader->TotalBitsConsumed() / kBitsPerByte;
|
||||
BitReader reader(Span<const uint8_t>(next_in, avail_in));
|
||||
JXL_RETURN_IF_ERROR(frame_decoder.InitFrame(&reader, decoded,
|
||||
/*is_preview=*/false,
|
||||
/*output_needed=*/true));
|
||||
JXL_RETURN_IF_ERROR(reader.AllReadsWithinBounds());
|
||||
size_t header_bytes = reader.TotalBitsConsumed() / kBitsPerByte;
|
||||
JXL_RETURN_IF_ERROR(reader.Close());
|
||||
|
||||
size_t processed_bytes = header_bytes;
|
||||
Status close_ok = true;
|
||||
std::vector<std::unique_ptr<BitReader>> section_readers;
|
||||
{
|
||||
std::vector<std::unique_ptr<BitReaderScopedCloser>> section_closers;
|
||||
std::vector<FrameDecoder::SectionInfo> section_info;
|
||||
std::vector<FrameDecoder::SectionStatus> section_status;
|
||||
size_t bytes_to_skip = 0;
|
||||
for (size_t i = 0; i < frame_decoder.NumSections(); i++) {
|
||||
size_t b = frame_decoder.SectionOffsets()[i];
|
||||
size_t e = b + frame_decoder.SectionSizes()[i];
|
||||
bytes_to_skip += e - b;
|
||||
size_t pos = reader->TotalBitsConsumed() / kBitsPerByte;
|
||||
if (pos + (dparams.allow_more_progressive_steps &&
|
||||
(i == 0 ||
|
||||
frame_header.encoding == FrameEncoding::kModular)
|
||||
? b
|
||||
: e) <=
|
||||
reader->TotalBytes() ||
|
||||
(i == 0 && dparams.allow_more_progressive_steps)) {
|
||||
auto br = make_unique<BitReader>(Span<const uint8_t>(
|
||||
reader->FirstByte() + b + pos,
|
||||
(pos + b > reader->TotalBytes()
|
||||
? 0
|
||||
: std::min(reader->TotalBytes() - pos - b, e - b))));
|
||||
section_info.emplace_back(FrameDecoder::SectionInfo{br.get(), i});
|
||||
section_closers.emplace_back(
|
||||
make_unique<BitReaderScopedCloser>(br.get(), &close_ok));
|
||||
section_readers.emplace_back(std::move(br));
|
||||
} else if (!dparams.allow_partial_files) {
|
||||
return JXL_FAILURE("Premature end of stream.");
|
||||
}
|
||||
size_t pos = header_bytes;
|
||||
for (auto toc_entry : frame_decoder.Toc()) {
|
||||
JXL_RETURN_IF_ERROR(pos + toc_entry.size <= avail_in);
|
||||
auto br = make_unique<BitReader>(
|
||||
Span<const uint8_t>(next_in + pos, toc_entry.size));
|
||||
section_info.emplace_back(
|
||||
FrameDecoder::SectionInfo{br.get(), toc_entry.id});
|
||||
section_closers.emplace_back(
|
||||
make_unique<BitReaderScopedCloser>(br.get(), &close_ok));
|
||||
section_readers.emplace_back(std::move(br));
|
||||
pos += toc_entry.size;
|
||||
}
|
||||
// Skip over the to-be-decoded sections.
|
||||
reader->SkipBits(kBitsPerByte * bytes_to_skip);
|
||||
section_status.resize(section_info.size());
|
||||
|
||||
JXL_RETURN_IF_ERROR(frame_decoder.ProcessSections(
|
||||
section_info.data(), section_info.size(), section_status.data()));
|
||||
|
||||
for (size_t i = 0; i < section_status.size(); i++) {
|
||||
auto s = section_status[i];
|
||||
if (s == FrameDecoder::kDone) {
|
||||
processed_bytes += frame_decoder.SectionSizes()[i];
|
||||
continue;
|
||||
}
|
||||
if (dparams.allow_more_progressive_steps && s == FrameDecoder::kPartial) {
|
||||
continue;
|
||||
}
|
||||
if (dparams.max_downsampling > 1 && s == FrameDecoder::kSkipped) {
|
||||
continue;
|
||||
}
|
||||
return JXL_FAILURE("Invalid section %" PRIuS " status: %d",
|
||||
section_info[i].id, s);
|
||||
JXL_RETURN_IF_ERROR(section_status[i] == FrameDecoder::kDone);
|
||||
processed_bytes += frame_decoder.Toc()[i].size;
|
||||
}
|
||||
}
|
||||
|
||||
JXL_RETURN_IF_ERROR(close_ok);
|
||||
|
||||
JXL_RETURN_IF_ERROR(frame_decoder.FinalizeFrame());
|
||||
decoded->SetDecodedBytes(processed_bytes);
|
||||
return true;
|
||||
}
|
||||
|
||||
Status FrameDecoder::InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded,
|
||||
bool is_preview, bool allow_partial_frames,
|
||||
bool allow_partial_dc_global,
|
||||
bool output_needed) {
|
||||
bool is_preview, bool output_needed) {
|
||||
PROFILER_FUNC;
|
||||
decoded_ = decoded;
|
||||
JXL_ASSERT(is_finalized_);
|
||||
|
||||
allow_partial_frames_ = allow_partial_frames;
|
||||
allow_partial_dc_global_ = allow_partial_dc_global;
|
||||
|
||||
// Reset the dequantization matrices to their default values.
|
||||
dec_state_->shared_storage.matrices = DequantMatrices();
|
||||
|
||||
frame_header_.nonserialized_is_preview = is_preview;
|
||||
size_t pos = br->TotalBitsConsumed() / kBitsPerByte;
|
||||
Status have_frameheader =
|
||||
br->TotalBytes() > pos && DecodeFrameHeader(br, &frame_header_);
|
||||
JXL_RETURN_IF_ERROR(have_frameheader || allow_partial_frames);
|
||||
if (!have_frameheader) {
|
||||
if (dec_state_->shared_storage.dc_frames[0].xsize() > 0) {
|
||||
// If we have a (partial) DC frame available, but we don't have the next
|
||||
// frame header (so allow_partial_frames is true), then we'll assume the
|
||||
// next frame uses that DC frame (which may not be true, e.g. there might
|
||||
// first be a ReferenceOnly patch frame, but it's reasonable to assume
|
||||
// that the DC frame is a good progressive preview)
|
||||
frame_header_.flags |= FrameHeader::kUseDcFrame;
|
||||
frame_header_.encoding = FrameEncoding::kVarDCT;
|
||||
frame_header_.dc_level = 0;
|
||||
} else
|
||||
return JXL_FAILURE("Couldn't read frame header");
|
||||
}
|
||||
JXL_ASSERT(frame_header_.nonserialized_metadata != nullptr);
|
||||
JXL_RETURN_IF_ERROR(ReadFrameHeader(br, &frame_header_));
|
||||
frame_dim_ = frame_header_.ToFrameDimensions();
|
||||
JXL_DEBUG_V(2, "FrameHeader: %s", frame_header_.DebugString().c_str());
|
||||
|
||||
const size_t num_passes = frame_header_.passes.num_passes;
|
||||
const size_t xsize = frame_dim_.xsize;
|
||||
const size_t ysize = frame_dim_.ysize;
|
||||
const size_t num_groups = frame_dim_.num_groups;
|
||||
|
||||
// Check validity of frame dimensions.
|
||||
JXL_RETURN_IF_ERROR(VerifyDimensions(constraints_, xsize, ysize));
|
||||
|
||||
// If the previous frame was not a kRegularFrame, `decoded` may have different
|
||||
// dimensions; must reset to avoid errors.
|
||||
decoded->RemoveColor();
|
||||
|
|
@ -275,20 +161,31 @@ Status FrameDecoder::InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded,
|
|||
}
|
||||
|
||||
// Read TOC.
|
||||
uint64_t groups_total_size;
|
||||
const bool has_ac_global = true;
|
||||
const size_t toc_entries = NumTocEntries(num_groups, frame_dim_.num_dc_groups,
|
||||
num_passes, has_ac_global);
|
||||
JXL_RETURN_IF_ERROR(ReadGroupOffsets(toc_entries, br, §ion_offsets_,
|
||||
§ion_sizes_, &groups_total_size) ||
|
||||
allow_partial_frames);
|
||||
std::vector<uint32_t> sizes;
|
||||
std::vector<coeff_order_t> permutation;
|
||||
JXL_RETURN_IF_ERROR(ReadToc(toc_entries, br, &sizes, &permutation));
|
||||
bool have_permutation = !permutation.empty();
|
||||
toc_.resize(toc_entries);
|
||||
section_sizes_sum_ = 0;
|
||||
for (size_t i = 0; i < toc_entries; ++i) {
|
||||
toc_[i].size = sizes[i];
|
||||
size_t index = have_permutation ? permutation[i] : i;
|
||||
toc_[index].id = i;
|
||||
if (section_sizes_sum_ + toc_[i].size < section_sizes_sum_) {
|
||||
return JXL_FAILURE("group offset overflow");
|
||||
}
|
||||
section_sizes_sum_ += toc_[i].size;
|
||||
}
|
||||
|
||||
JXL_DASSERT((br->TotalBitsConsumed() % kBitsPerByte) == 0);
|
||||
const size_t group_codes_begin = br->TotalBitsConsumed() / kBitsPerByte;
|
||||
JXL_DASSERT(!section_offsets_.empty());
|
||||
JXL_DASSERT(!toc_.empty());
|
||||
|
||||
// Overflow check.
|
||||
if (group_codes_begin + groups_total_size < group_codes_begin) {
|
||||
if (group_codes_begin + section_sizes_sum_ < group_codes_begin) {
|
||||
return JXL_FAILURE("Invalid group codes");
|
||||
}
|
||||
|
||||
|
|
@ -347,9 +244,7 @@ Status FrameDecoder::InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded,
|
|||
decoded_passes_per_ac_group_.clear();
|
||||
decoded_passes_per_ac_group_.resize(frame_dim_.num_groups, 0);
|
||||
processed_section_.clear();
|
||||
processed_section_.resize(section_offsets_.size());
|
||||
max_passes_ = frame_header_.passes.num_passes;
|
||||
num_renders_ = 0;
|
||||
processed_section_.resize(toc_.size());
|
||||
allocated_ = false;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -382,14 +277,11 @@ Status FrameDecoder::ProcessDCGlobal(BitReader* br) {
|
|||
if (shared.frame_header.flags & FrameHeader::kNoise) {
|
||||
JXL_RETURN_IF_ERROR(DecodeNoise(br, &shared.image_features.noise_params));
|
||||
}
|
||||
if (!allow_partial_dc_global_ ||
|
||||
br->TotalBitsConsumed() < br->TotalBytes() * kBitsPerByte) {
|
||||
JXL_RETURN_IF_ERROR(dec_state_->shared_storage.matrices.DecodeDC(br));
|
||||
JXL_RETURN_IF_ERROR(dec_state_->shared_storage.matrices.DecodeDC(br));
|
||||
|
||||
if (frame_header_.encoding == FrameEncoding::kVarDCT) {
|
||||
JXL_RETURN_IF_ERROR(
|
||||
jxl::DecodeGlobalDCInfo(br, decoded_->IsJPEG(), dec_state_, pool_));
|
||||
}
|
||||
if (frame_header_.encoding == FrameEncoding::kVarDCT) {
|
||||
JXL_RETURN_IF_ERROR(
|
||||
jxl::DecodeGlobalDCInfo(br, decoded_->IsJPEG(), dec_state_, pool_));
|
||||
}
|
||||
// Splines' draw cache uses the color correlation map.
|
||||
if (shared.frame_header.flags & FrameHeader::kSplines) {
|
||||
|
|
@ -398,7 +290,7 @@ Status FrameDecoder::ProcessDCGlobal(BitReader* br) {
|
|||
dec_state_->shared->cmap));
|
||||
}
|
||||
Status dec_status = modular_frame_decoder_.DecodeGlobalInfo(
|
||||
br, frame_header_, allow_partial_dc_global_);
|
||||
br, frame_header_, /*allow_truncated_group=*/false);
|
||||
if (dec_status.IsFatalError()) return dec_status;
|
||||
if (dec_status) {
|
||||
decoded_dc_global_ = true;
|
||||
|
|
@ -420,7 +312,8 @@ Status FrameDecoder::ProcessDCGroup(size_t dc_group_id, BitReader* br) {
|
|||
frame_dim_.dc_group_dim, frame_dim_.dc_group_dim);
|
||||
JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
|
||||
mrect, br, 3, 1000, ModularStreamId::ModularDC(dc_group_id),
|
||||
/*zerofill=*/false, nullptr, nullptr, nullptr, allow_partial_frames_));
|
||||
/*zerofill=*/false, nullptr, nullptr,
|
||||
/*allow_truncated=*/false));
|
||||
if (frame_header_.encoding == FrameEncoding::kVarDCT) {
|
||||
JXL_RETURN_IF_ERROR(
|
||||
modular_frame_decoder_.DecodeAcMetadata(dc_group_id, br, dec_state_));
|
||||
|
|
@ -562,6 +455,11 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
|
|||
const size_t gy = ac_group_id / frame_dim_.xsize_groups;
|
||||
const size_t x = gx * group_dim;
|
||||
const size_t y = gy * group_dim;
|
||||
JXL_DEBUG_V(3,
|
||||
"Processing AC group %" PRIuS "(%" PRIuS ",%" PRIuS
|
||||
") group_dim: %" PRIuS " decoded passes: %u new passes: %" PRIuS,
|
||||
ac_group_id, gx, gy, group_dim,
|
||||
decoded_passes_per_ac_group_[ac_group_id], num_passes);
|
||||
|
||||
RenderPipelineInput render_pipeline_input =
|
||||
dec_state_->render_pipeline->GetInputBuffers(ac_group_id, thread);
|
||||
|
|
@ -580,23 +478,28 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
|
|||
|
||||
// don't limit to image dimensions here (is done in DecodeGroup)
|
||||
const Rect mrect(x, y, group_dim, group_dim);
|
||||
for (size_t i = 0; i < frame_header_.passes.num_passes; i++) {
|
||||
bool modular_ready = false;
|
||||
size_t pass0 = decoded_passes_per_ac_group_[ac_group_id];
|
||||
size_t pass1 =
|
||||
force_draw ? frame_header_.passes.num_passes : pass0 + num_passes;
|
||||
for (size_t i = pass0; i < pass1; ++i) {
|
||||
int minShift, maxShift;
|
||||
frame_header_.passes.GetDownsamplingBracket(i, minShift, maxShift);
|
||||
if (i >= decoded_passes_per_ac_group_[ac_group_id] &&
|
||||
i < decoded_passes_per_ac_group_[ac_group_id] + num_passes) {
|
||||
bool modular_pass_ready = true;
|
||||
if (i < pass0 + num_passes) {
|
||||
JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
|
||||
mrect, br[i - decoded_passes_per_ac_group_[ac_group_id]], minShift,
|
||||
maxShift, ModularStreamId::ModularAC(ac_group_id, i),
|
||||
/*zerofill=*/false, dec_state_, &render_pipeline_input, decoded_,
|
||||
allow_partial_frames_));
|
||||
} else if (i >= decoded_passes_per_ac_group_[ac_group_id] + num_passes &&
|
||||
force_draw) {
|
||||
mrect, br[i - pass0], minShift, maxShift,
|
||||
ModularStreamId::ModularAC(ac_group_id, i),
|
||||
/*zerofill=*/false, dec_state_, &render_pipeline_input,
|
||||
/*allow_truncated=*/false, &modular_pass_ready));
|
||||
} else {
|
||||
JXL_RETURN_IF_ERROR(modular_frame_decoder_.DecodeGroup(
|
||||
mrect, nullptr, minShift, maxShift,
|
||||
ModularStreamId::ModularAC(ac_group_id, i), /*zerofill=*/true,
|
||||
dec_state_, &render_pipeline_input, decoded_, allow_partial_frames_));
|
||||
dec_state_, &render_pipeline_input,
|
||||
/*allow_truncated=*/false, &modular_pass_ready));
|
||||
}
|
||||
if (modular_pass_ready) modular_ready = true;
|
||||
}
|
||||
decoded_passes_per_ac_group_[ac_group_id] += num_passes;
|
||||
|
||||
|
|
@ -627,19 +530,21 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
|
|||
}
|
||||
}
|
||||
|
||||
if (!modular_frame_decoder_.UsesFullImage() && !decoded_->IsJPEG() &&
|
||||
should_run_pipeline) {
|
||||
render_pipeline_input.Done();
|
||||
if (!modular_frame_decoder_.UsesFullImage() && !decoded_->IsJPEG()) {
|
||||
if (should_run_pipeline && modular_ready) {
|
||||
render_pipeline_input.Done();
|
||||
} else if (force_draw) {
|
||||
return JXL_FAILURE("Modular group decoding failed.");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FrameDecoder::MarkSections(const SectionInfo* sections, size_t num,
|
||||
SectionStatus* section_status) {
|
||||
num_sections_done_ = num;
|
||||
num_sections_done_ += num;
|
||||
for (size_t i = 0; i < num; i++) {
|
||||
if (section_status[i] == SectionStatus::kSkipped ||
|
||||
section_status[i] == SectionStatus::kPartial) {
|
||||
if (section_status[i] != SectionStatus::kDone) {
|
||||
processed_section_[sections[i].id] = false;
|
||||
num_sections_done_--;
|
||||
}
|
||||
|
|
@ -656,7 +561,9 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
|
|||
std::vector<std::vector<size_t>> ac_group_sec(
|
||||
frame_dim_.num_groups,
|
||||
std::vector<size_t>(frame_header_.passes.num_passes, num));
|
||||
std::vector<size_t> num_ac_passes(frame_dim_.num_groups);
|
||||
// This keeps track of the number of ac passes we want to process during this
|
||||
// call of ProcessSections.
|
||||
std::vector<size_t> desired_num_ac_passes(frame_dim_.num_groups);
|
||||
bool single_section =
|
||||
frame_dim_.num_groups == 1 && frame_header_.passes.num_passes == 1;
|
||||
if (single_section) {
|
||||
|
|
@ -666,7 +573,7 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
|
|||
processed_section_[0] = true;
|
||||
ac_group_sec[0].resize(1);
|
||||
dc_global_sec = ac_global_sec = dc_group_sec[0] = ac_group_sec[0][0] = 0;
|
||||
num_ac_passes[0] = 1;
|
||||
desired_num_ac_passes[0] = 1;
|
||||
} else {
|
||||
section_status[0] = SectionStatus::kDuplicate;
|
||||
}
|
||||
|
|
@ -691,9 +598,6 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
|
|||
if (acp >= frame_header_.passes.num_passes) {
|
||||
return JXL_FAILURE("Invalid section ID");
|
||||
}
|
||||
if (acp >= max_passes_) {
|
||||
continue;
|
||||
}
|
||||
ac_group_sec[acg][acp] = i;
|
||||
}
|
||||
processed_section_[sections[i].id] = true;
|
||||
|
|
@ -701,12 +605,14 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
|
|||
// Count number of new passes per group.
|
||||
for (size_t g = 0; g < ac_group_sec.size(); g++) {
|
||||
size_t j = 0;
|
||||
for (; j + decoded_passes_per_ac_group_[g] < max_passes_; j++) {
|
||||
for (; j + decoded_passes_per_ac_group_[g] <
|
||||
frame_header_.passes.num_passes;
|
||||
j++) {
|
||||
if (ac_group_sec[g][j + decoded_passes_per_ac_group_[g]] == num) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
num_ac_passes[g] = j;
|
||||
desired_num_ac_passes[g] = j;
|
||||
}
|
||||
}
|
||||
if (dc_global_sec != num) {
|
||||
|
|
@ -747,33 +653,9 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
|
|||
dec_state_->PreparePipeline(decoded_, pipeline_options));
|
||||
FinalizeDC();
|
||||
JXL_RETURN_IF_ERROR(AllocateOutput());
|
||||
if (pause_at_progressive_ && !single_section) {
|
||||
bool can_return_dc = true;
|
||||
if (single_section) {
|
||||
// If there's only one group and one pass, there is no separate section
|
||||
// for DC and the entire full resolution image is available at once.
|
||||
can_return_dc = false;
|
||||
}
|
||||
if (!decoded_->metadata()->extra_channel_info.empty()) {
|
||||
// If extra channels are encoded with modular without squeeze, they
|
||||
// don't support DC. If the are encoded with squeeze, DC works in theory
|
||||
// but the implementation may not yet correctly support this for Flush.
|
||||
// Therefore, can't correctly pause for a progressive step if there is
|
||||
// an extra channel (including alpha channel)
|
||||
can_return_dc = false;
|
||||
}
|
||||
if (frame_header_.encoding != FrameEncoding::kVarDCT) {
|
||||
// DC is not guaranteed to be available in modular mode and may be a
|
||||
// black image. If squeeze is used, it may be available depending on the
|
||||
// current implementation.
|
||||
// TODO(lode): do return DC if it's known that flushing at this point
|
||||
// will produce a valid 1/8th downscaled image with modular encoding.
|
||||
can_return_dc = false;
|
||||
}
|
||||
if (can_return_dc) {
|
||||
MarkSections(sections, num, section_status);
|
||||
return true;
|
||||
}
|
||||
if (progressive_detail_ >= JxlProgressiveDetail::kDC) {
|
||||
MarkSections(sections, num, section_status);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -782,13 +664,22 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
|
|||
section_status[ac_global_sec] = SectionStatus::kDone;
|
||||
}
|
||||
|
||||
if (progressive_detail_ >= JxlProgressiveDetail::kLastPasses) {
|
||||
// Mark that we only want the next progression pass.
|
||||
size_t target_complete_passes = NextNumPassesToPause();
|
||||
for (size_t i = 0; i < ac_group_sec.size(); i++) {
|
||||
desired_num_ac_passes[i] =
|
||||
std::min(desired_num_ac_passes[i],
|
||||
target_complete_passes - decoded_passes_per_ac_group_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (decoded_ac_global_) {
|
||||
// Mark all the AC groups that we received as not complete yet.
|
||||
for (size_t i = 0; i < ac_group_sec.size(); i++) {
|
||||
if (num_ac_passes[i] == 0 && !modular_frame_decoder_.UsesFullImage()) {
|
||||
continue;
|
||||
if (desired_num_ac_passes[i] != 0) {
|
||||
dec_state_->render_pipeline->ClearDone(i);
|
||||
}
|
||||
dec_state_->render_pipeline->ClearDone(i);
|
||||
}
|
||||
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
|
|
@ -797,24 +688,25 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
|
|||
return PrepareStorage(num_threads,
|
||||
decoded_passes_per_ac_group_.size());
|
||||
},
|
||||
[this, &ac_group_sec, &num_ac_passes, &num, §ions, §ion_status,
|
||||
&has_error](size_t g, size_t thread) {
|
||||
if (num_ac_passes[g] == 0) { // no new AC pass, nothing to do.
|
||||
[this, &ac_group_sec, &desired_num_ac_passes, &num, §ions,
|
||||
§ion_status, &has_error](size_t g, size_t thread) {
|
||||
if (desired_num_ac_passes[g] == 0) {
|
||||
// no new AC pass, nothing to do
|
||||
return;
|
||||
}
|
||||
(void)num;
|
||||
size_t first_pass = decoded_passes_per_ac_group_[g];
|
||||
BitReader* JXL_RESTRICT readers[kMaxNumPasses];
|
||||
for (size_t i = 0; i < num_ac_passes[g]; i++) {
|
||||
for (size_t i = 0; i < desired_num_ac_passes[g]; i++) {
|
||||
JXL_ASSERT(ac_group_sec[g][first_pass + i] != num);
|
||||
readers[i] = sections[ac_group_sec[g][first_pass + i]].br;
|
||||
}
|
||||
if (!ProcessACGroup(g, readers, num_ac_passes[g],
|
||||
if (!ProcessACGroup(g, readers, desired_num_ac_passes[g],
|
||||
GetStorageLocation(thread, g),
|
||||
/*force_draw=*/false, /*dc_only=*/false)) {
|
||||
has_error = true;
|
||||
} else {
|
||||
for (size_t i = 0; i < num_ac_passes[g]; i++) {
|
||||
for (size_t i = 0; i < desired_num_ac_passes[g]; i++) {
|
||||
section_status[ac_group_sec[g][first_pass + i]] =
|
||||
SectionStatus::kDone;
|
||||
}
|
||||
|
|
@ -857,9 +749,9 @@ Status FrameDecoder::Flush() {
|
|||
// We don't have all AC yet: force a draw of all the missing areas.
|
||||
// Mark all sections as not complete.
|
||||
for (size_t i = 0; i < decoded_passes_per_ac_group_.size(); i++) {
|
||||
if (decoded_passes_per_ac_group_[i] == frame_header_.passes.num_passes)
|
||||
continue;
|
||||
dec_state_->render_pipeline->ClearDone(i);
|
||||
if (decoded_passes_per_ac_group_[i] < frame_header_.passes.num_passes) {
|
||||
dec_state_->render_pipeline->ClearDone(i);
|
||||
}
|
||||
}
|
||||
std::atomic<bool> has_error{false};
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
|
|
@ -887,10 +779,9 @@ Status FrameDecoder::Flush() {
|
|||
}
|
||||
|
||||
// undo global modular transforms and copy int pixel buffers to float ones
|
||||
JXL_RETURN_IF_ERROR(modular_frame_decoder_.FinalizeDecoding(
|
||||
dec_state_, pool_, decoded_, is_finalized_));
|
||||
JXL_RETURN_IF_ERROR(modular_frame_decoder_.FinalizeDecoding(dec_state_, pool_,
|
||||
is_finalized_));
|
||||
|
||||
num_renders_++;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -913,7 +804,7 @@ bool FrameDecoder::HasEverything() const {
|
|||
if (!have_dc_group) return false;
|
||||
}
|
||||
for (auto& nb_passes : decoded_passes_per_ac_group_) {
|
||||
if (nb_passes < max_passes_) return false;
|
||||
if (nb_passes < frame_header_.passes.num_passes) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -967,18 +858,9 @@ Status FrameDecoder::FinalizeFrame() {
|
|||
return true;
|
||||
}
|
||||
if (!finalized_dc_) {
|
||||
// We don't have all of DC: EPF might not behave correctly (and is not
|
||||
// particularly useful anyway on upsampling results), so we disable it.
|
||||
dec_state_->shared_storage.frame_header.loop_filter.epf_iters = 0;
|
||||
}
|
||||
if (!HasEverything() && !allow_partial_frames_) {
|
||||
return JXL_FAILURE(
|
||||
"FinalizeFrame called before the frame was fully decoded");
|
||||
}
|
||||
|
||||
if (!finalized_dc_) {
|
||||
JXL_ASSERT(allow_partial_frames_);
|
||||
JXL_RETURN_IF_ERROR(AllocateOutput());
|
||||
// We don't have all of DC, and render pipeline is not created yet, so we
|
||||
// can not call Flush() yet.
|
||||
return JXL_FAILURE("FinalizeFrame called before DC was fully decoded");
|
||||
}
|
||||
|
||||
JXL_RETURN_IF_ERROR(Flush());
|
||||
|
|
|
|||
140
third_party/jpeg-xl/lib/jxl/dec_frame.h
vendored
140
third_party/jpeg-xl/lib/jxl/dec_frame.h
vendored
|
|
@ -9,6 +9,7 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "jxl/decode.h"
|
||||
#include "jxl/types.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
|
|
@ -19,31 +20,20 @@
|
|||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/dec_cache.h"
|
||||
#include "lib/jxl/dec_modular.h"
|
||||
#include "lib/jxl/dec_params.h"
|
||||
#include "lib/jxl/frame_header.h"
|
||||
#include "lib/jxl/headers.h"
|
||||
#include "lib/jxl/image_bundle.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
// TODO(veluca): remove DecodeFrameHeader once the API migrates to FrameDecoder.
|
||||
|
||||
// `frame_header` must have nonserialized_metadata and
|
||||
// nonserialized_is_preview set.
|
||||
Status DecodeFrameHeader(BitReader* JXL_RESTRICT reader,
|
||||
FrameHeader* JXL_RESTRICT frame_header);
|
||||
|
||||
// Decodes a frame. Groups may be processed in parallel by `pool`.
|
||||
// See DecodeFile for explanation of c_decoded.
|
||||
// `io` is only used for reading maximum image size. Also updates
|
||||
// `dec_state` with the new frame header.
|
||||
// `metadata` is the metadata that applies to all frames of the codestream
|
||||
// `decoded->metadata` must already be set and must match metadata.m.
|
||||
Status DecodeFrame(const DecompressParams& dparams,
|
||||
PassesDecoderState* dec_state, ThreadPool* JXL_RESTRICT pool,
|
||||
BitReader* JXL_RESTRICT reader, ImageBundle* decoded,
|
||||
const CodecMetadata& metadata,
|
||||
const SizeConstraints* constraints, bool is_preview = false);
|
||||
// Used in the encoder to model decoder behaviour, and in tests.
|
||||
Status DecodeFrame(PassesDecoderState* dec_state, ThreadPool* JXL_RESTRICT pool,
|
||||
const uint8_t* next_in, size_t avail_in,
|
||||
ImageBundle* decoded, const CodecMetadata& metadata,
|
||||
bool use_slow_rendering_pipeline = false);
|
||||
|
||||
// TODO(veluca): implement "forced drawing".
|
||||
class FrameDecoder {
|
||||
|
|
@ -56,28 +46,25 @@ class FrameDecoder {
|
|||
frame_header_(&metadata),
|
||||
use_slow_rendering_pipeline_(use_slow_rendering_pipeline) {}
|
||||
|
||||
// `constraints` must outlive the FrameDecoder if not null, or stay alive
|
||||
// until the next call to SetFrameSizeLimits.
|
||||
void SetFrameSizeLimits(const SizeConstraints* constraints) {
|
||||
constraints_ = constraints;
|
||||
}
|
||||
void SetRenderSpotcolors(bool rsc) { render_spotcolors_ = rsc; }
|
||||
void SetCoalescing(bool c) { coalescing_ = c; }
|
||||
|
||||
// Read FrameHeader and table of contents from the given BitReader.
|
||||
// Also checks frame dimensions for their limits, and sets the output
|
||||
// image buffer.
|
||||
// TODO(veluca): remove the `allow_partial_frames` flag - this should be moved
|
||||
// on callers.
|
||||
Status InitFrame(BitReader* JXL_RESTRICT br, ImageBundle* decoded,
|
||||
bool is_preview, bool allow_partial_frames,
|
||||
bool allow_partial_dc_global, bool output_needed);
|
||||
bool is_preview, bool output_needed);
|
||||
|
||||
struct SectionInfo {
|
||||
BitReader* JXL_RESTRICT br;
|
||||
size_t id;
|
||||
};
|
||||
|
||||
struct TocEntry {
|
||||
size_t size;
|
||||
size_t id;
|
||||
};
|
||||
|
||||
enum SectionStatus {
|
||||
// Processed correctly.
|
||||
kDone = 0,
|
||||
|
|
@ -118,22 +105,20 @@ class FrameDecoder {
|
|||
// soon as the frame header is known.
|
||||
static int SavedAs(const FrameHeader& header);
|
||||
|
||||
// Returns offset of this section after the end of the TOC. The end of the TOC
|
||||
// is the byte position of the bit reader after InitFrame was called.
|
||||
const std::vector<uint64_t>& SectionOffsets() const {
|
||||
return section_offsets_;
|
||||
}
|
||||
const std::vector<uint32_t>& SectionSizes() const { return section_sizes_; }
|
||||
size_t NumSections() const { return section_sizes_.size(); }
|
||||
uint64_t SumSectionSizes() const { return section_sizes_sum_; }
|
||||
const std::vector<TocEntry>& Toc() const { return toc_; }
|
||||
|
||||
// TODO(veluca): remove once we remove --downsampling flag.
|
||||
void SetMaxPasses(size_t max_passes) { max_passes_ = max_passes; }
|
||||
const FrameHeader& GetFrameHeader() const { return frame_header_; }
|
||||
|
||||
// Returns whether a DC image has been decoded, accessible at low resolution
|
||||
// at passes.shared_storage.dc_storage
|
||||
bool HasDecodedDC() const { return finalized_dc_; }
|
||||
bool HasDecodedAll() const { return NumSections() == num_sections_done_; }
|
||||
bool HasDecodedAll() const { return toc_.size() == num_sections_done_; }
|
||||
|
||||
size_t NumCompletePasses() const {
|
||||
return *std::min_element(decoded_passes_per_ac_group_.begin(),
|
||||
decoded_passes_per_ac_group_.end());
|
||||
};
|
||||
|
||||
// If enabled, ProcessSections will stop and return true when the DC
|
||||
// sections have been processed, instead of starting the AC sections. This
|
||||
|
|
@ -141,7 +126,59 @@ class FrameDecoder {
|
|||
// 1/8th*1/8th resolution image). The return value of true then does not mean
|
||||
// all sections have been processed, use HasDecodedDC and HasDecodedAll
|
||||
// to check the true finished state.
|
||||
void SetPauseAtProgressive() { pause_at_progressive_ = true; }
|
||||
// Returns the progressive detail that will be effective for the frame.
|
||||
JxlProgressiveDetail SetPauseAtProgressive(JxlProgressiveDetail prog_detail) {
|
||||
bool single_section =
|
||||
frame_dim_.num_groups == 1 && frame_header_.passes.num_passes == 1;
|
||||
if (frame_header_.frame_type != kSkipProgressive &&
|
||||
// If there's only one group and one pass, there is no separate section
|
||||
// for DC and the entire full resolution image is available at once.
|
||||
!single_section &&
|
||||
// If extra channels are encoded with modular without squeeze, they
|
||||
// don't support DC. If the are encoded with squeeze, DC works in theory
|
||||
// but the implementation may not yet correctly support this for Flush.
|
||||
// Therefore, can't correctly pause for a progressive step if there is
|
||||
// an extra channel (including alpha channel)
|
||||
// TOOD(firsching): Check if this is still the case.
|
||||
decoded_->metadata()->extra_channel_info.empty() &&
|
||||
// DC is not guaranteed to be available in modular mode and may be a
|
||||
// black image. If squeeze is used, it may be available depending on the
|
||||
// current implementation.
|
||||
// TODO(lode): do return DC if it's known that flushing at this point
|
||||
// will produce a valid 1/8th downscaled image with modular encoding.
|
||||
frame_header_.encoding == FrameEncoding::kVarDCT) {
|
||||
progressive_detail_ = prog_detail;
|
||||
} else {
|
||||
progressive_detail_ = JxlProgressiveDetail::kFrames;
|
||||
}
|
||||
if (progressive_detail_ >= JxlProgressiveDetail::kPasses) {
|
||||
for (size_t i = 1; i < frame_header_.passes.num_passes; ++i) {
|
||||
passes_to_pause_.push_back(i);
|
||||
}
|
||||
} else if (progressive_detail_ >= JxlProgressiveDetail::kLastPasses) {
|
||||
for (size_t i = 0; i < frame_header_.passes.num_downsample; ++i) {
|
||||
passes_to_pause_.push_back(frame_header_.passes.last_pass[i] + 1);
|
||||
}
|
||||
// The format does not guarantee that these values are sorted.
|
||||
std::sort(passes_to_pause_.begin(), passes_to_pause_.end());
|
||||
}
|
||||
return progressive_detail_;
|
||||
}
|
||||
|
||||
size_t NextNumPassesToPause() const {
|
||||
auto it = std::upper_bound(passes_to_pause_.begin(), passes_to_pause_.end(),
|
||||
NumCompletePasses());
|
||||
return (it != passes_to_pause_.end() ? *it
|
||||
: std::numeric_limits<size_t>::max());
|
||||
}
|
||||
|
||||
void MaybeSetUnpremultiplyAlpha(bool unpremul_alpha) {
|
||||
const jxl::ExtraChannelInfo* alpha =
|
||||
decoded_->metadata()->Find(jxl::ExtraChannel::kAlpha);
|
||||
if (alpha && alpha->alpha_associated && unpremul_alpha) {
|
||||
dec_state_->unpremul_alpha = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the buffer to which uint8 sRGB pixels will be decoded. This is not
|
||||
// supported for all images. If it succeeds, HasRGBBuffer() will return true.
|
||||
|
|
@ -156,7 +193,9 @@ class FrameDecoder {
|
|||
// orientation. When outputting to the ImageBundle, no orientation is undone.
|
||||
void MaybeSetRGB8OutputBuffer(uint8_t* rgb_output, size_t stride,
|
||||
bool is_rgba, bool undo_orientation) const {
|
||||
if (!CanDoLowMemoryPath(undo_orientation)) return;
|
||||
if (!CanDoLowMemoryPath(undo_orientation) || dec_state_->unpremul_alpha) {
|
||||
return;
|
||||
}
|
||||
dec_state_->rgb_output = rgb_output;
|
||||
dec_state_->rgb_output_is_rgba = is_rgba;
|
||||
dec_state_->rgb_stride = stride;
|
||||
|
|
@ -165,6 +204,8 @@ class FrameDecoder {
|
|||
if (decoded_->metadata()->xyb_encoded &&
|
||||
dec_state_->output_encoding_info.color_encoding.IsSRGB() &&
|
||||
dec_state_->output_encoding_info.all_default_opsin &&
|
||||
dec_state_->output_encoding_info.desired_intensity_target ==
|
||||
dec_state_->output_encoding_info.orig_intensity_target &&
|
||||
HasFastXYBTosRGB8() && frame_header_.needs_color_transform()) {
|
||||
dec_state_->fast_xyb_srgb8_conversion = true;
|
||||
}
|
||||
|
|
@ -183,7 +224,7 @@ class FrameDecoder {
|
|||
// results in not setting the buffer if the image has a non-identity EXIF
|
||||
// orientation. When outputting to the ImageBundle, no orientation is undone.
|
||||
void MaybeSetFloatCallback(const PixelCallback& pixel_callback, bool is_rgba,
|
||||
bool undo_orientation) const {
|
||||
bool unpremul_alpha, bool undo_orientation) const {
|
||||
if (!CanDoLowMemoryPath(undo_orientation)) return;
|
||||
dec_state_->pixel_callback = pixel_callback;
|
||||
dec_state_->rgb_output_is_rgba = is_rgba;
|
||||
|
|
@ -221,11 +262,12 @@ class FrameDecoder {
|
|||
group_dec_caches_.resize(storage_size);
|
||||
}
|
||||
use_task_id_ = num_threads > num_tasks;
|
||||
bool use_group_ids = (modular_frame_decoder_.UsesFullImage() &&
|
||||
(frame_header_.encoding == FrameEncoding::kVarDCT ||
|
||||
(frame_header_.flags & FrameHeader::kNoise)));
|
||||
if (dec_state_->render_pipeline) {
|
||||
JXL_RETURN_IF_ERROR(dec_state_->render_pipeline->PrepareForThreads(
|
||||
storage_size,
|
||||
/*use_group_ids=*/modular_frame_decoder_.UsesFullImage() &&
|
||||
frame_header_.encoding == FrameEncoding::kVarDCT));
|
||||
storage_size, use_group_ids));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -248,16 +290,13 @@ class FrameDecoder {
|
|||
|
||||
PassesDecoderState* dec_state_;
|
||||
ThreadPool* pool_;
|
||||
std::vector<uint64_t> section_offsets_;
|
||||
std::vector<uint32_t> section_sizes_;
|
||||
size_t max_passes_;
|
||||
std::vector<TocEntry> toc_;
|
||||
uint64_t section_sizes_sum_;
|
||||
// TODO(veluca): figure out the duplication between these and dec_state_.
|
||||
FrameHeader frame_header_;
|
||||
FrameDimensions frame_dim_;
|
||||
ImageBundle* decoded_;
|
||||
ModularFrameDecoder modular_frame_decoder_;
|
||||
bool allow_partial_frames_;
|
||||
bool allow_partial_dc_global_;
|
||||
bool render_spotcolors_ = true;
|
||||
bool coalescing_ = true;
|
||||
|
||||
|
|
@ -270,14 +309,10 @@ class FrameDecoder {
|
|||
bool finalized_dc_ = true;
|
||||
size_t num_sections_done_ = 0;
|
||||
bool is_finalized_ = true;
|
||||
size_t num_renders_ = 0;
|
||||
bool allocated_ = false;
|
||||
|
||||
std::vector<GroupDecCache> group_dec_caches_;
|
||||
|
||||
// Frame size limits.
|
||||
const SizeConstraints* constraints_ = nullptr;
|
||||
|
||||
// Whether or not the task id should be used for storage indexing, instead of
|
||||
// the thread id.
|
||||
bool use_task_id_ = false;
|
||||
|
|
@ -285,7 +320,10 @@ class FrameDecoder {
|
|||
// Testing setting: whether or not to use the slow rendering pipeline.
|
||||
bool use_slow_rendering_pipeline_;
|
||||
|
||||
bool pause_at_progressive_ = false;
|
||||
JxlProgressiveDetail progressive_detail_ = kFrames;
|
||||
// Number of completed passes where section decoding should pause.
|
||||
// Used for progressive details at least kLastPasses.
|
||||
std::vector<int> passes_to_pause_;
|
||||
};
|
||||
|
||||
} // namespace jxl
|
||||
|
|
|
|||
6
third_party/jpeg-xl/lib/jxl/dec_group.cc
vendored
6
third_party/jpeg-xl/lib/jxl/dec_group.cc
vendored
|
|
@ -241,8 +241,10 @@ Status DecodeGroupImpl(GetBlock* JXL_RESTRICT get_block,
|
|||
r[i] =
|
||||
Rect(block_rect.x0() >> hshift[i], block_rect.y0() >> vshift[i],
|
||||
block_rect.xsize() >> hshift[i], block_rect.ysize() >> vshift[i]);
|
||||
JXL_ASSERT(r[i].IsInside({0, 0, dec_state->shared->dc->Plane(i).xsize(),
|
||||
dec_state->shared->dc->Plane(i).ysize()}));
|
||||
if (!r[i].IsInside({0, 0, dec_state->shared->dc->Plane(i).xsize(),
|
||||
dec_state->shared->dc->Plane(i).ysize()})) {
|
||||
return JXL_FAILURE("Frame dimensions are too big for the image.");
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t by = 0; by < ysize_blocks; ++by) {
|
||||
|
|
|
|||
1
third_party/jpeg-xl/lib/jxl/dec_group.h
vendored
1
third_party/jpeg-xl/lib/jxl/dec_group.h
vendored
|
|
@ -21,7 +21,6 @@
|
|||
#include "lib/jxl/dec_ans.h"
|
||||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/dec_cache.h"
|
||||
#include "lib/jxl/dec_params.h"
|
||||
#include "lib/jxl/frame_header.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/quantizer.h"
|
||||
|
|
|
|||
92
third_party/jpeg-xl/lib/jxl/dec_modular.cc
vendored
92
third_party/jpeg-xl/lib/jxl/dec_modular.cc
vendored
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "lib/jxl/frame_header.h"
|
||||
|
|
@ -149,6 +151,28 @@ void int_to_float(const pixel_type* const JXL_RESTRICT row_in,
|
|||
}
|
||||
}
|
||||
|
||||
std::string ModularStreamId::DebugString() const {
|
||||
std::ostringstream os;
|
||||
os << (kind == kGlobalData ? "ModularGlobal"
|
||||
: kind == kVarDCTDC ? "VarDCTDC"
|
||||
: kind == kModularDC ? "ModularDC"
|
||||
: kind == kACMetadata ? "ACMeta"
|
||||
: kind == kQuantTable ? "QuantTable"
|
||||
: kind == kModularAC ? "ModularAC"
|
||||
: "");
|
||||
if (kind == kVarDCTDC || kind == kModularDC || kind == kACMetadata ||
|
||||
kind == kModularAC) {
|
||||
os << " group " << group_id;
|
||||
}
|
||||
if (kind == kModularAC) {
|
||||
os << " pass " << pass_id;
|
||||
}
|
||||
if (kind == kQuantTable) {
|
||||
os << " " << quant_table_id;
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader,
|
||||
const FrameHeader& frame_header,
|
||||
bool allow_truncated_group) {
|
||||
|
|
@ -218,6 +242,8 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader,
|
|||
all_same_shift = false;
|
||||
}
|
||||
|
||||
JXL_DEBUG_V(6, "DecodeGlobalInfo: full_image (w/o transforms) %s",
|
||||
gi.DebugString().c_str());
|
||||
ModularOptions options;
|
||||
options.max_chan_size = frame_dim.group_dim;
|
||||
options.group_dim = frame_dim.group_dim;
|
||||
|
|
@ -248,12 +274,15 @@ Status ModularFrameDecoder::DecodeGlobalInfo(BitReader* reader,
|
|||
}
|
||||
}
|
||||
full_image = std::move(gi);
|
||||
JXL_DEBUG_V(6, "DecodeGlobalInfo: full_image (with transforms) %s",
|
||||
full_image.DebugString().c_str());
|
||||
return dec_status;
|
||||
}
|
||||
|
||||
void ModularFrameDecoder::MaybeDropFullImage() {
|
||||
if (full_image.transform.empty() && !have_something && all_same_shift) {
|
||||
use_full_image = false;
|
||||
JXL_DEBUG_V(6, "Dropping full image");
|
||||
for (auto& ch : full_image.channel) {
|
||||
// keep metadata on channels around, but dealloc their planes
|
||||
ch.plane = Plane<pixel_type>();
|
||||
|
|
@ -264,8 +293,11 @@ void ModularFrameDecoder::MaybeDropFullImage() {
|
|||
Status ModularFrameDecoder::DecodeGroup(
|
||||
const Rect& rect, BitReader* reader, int minShift, int maxShift,
|
||||
const ModularStreamId& stream, bool zerofill, PassesDecoderState* dec_state,
|
||||
RenderPipelineInput* render_pipeline_input, ImageBundle* output,
|
||||
bool allow_truncated) {
|
||||
RenderPipelineInput* render_pipeline_input, bool allow_truncated,
|
||||
bool* should_run_pipeline) {
|
||||
JXL_DEBUG_V(6, "Decoding %s with rect %s and shift bracket %d..%d %s",
|
||||
stream.DebugString().c_str(), Description(rect).c_str(), minShift,
|
||||
maxShift, zerofill ? "using zerofill" : "");
|
||||
JXL_DASSERT(stream.kind == ModularStreamId::kModularDC ||
|
||||
stream.kind == ModularStreamId::kModularAC);
|
||||
const size_t xsize = rect.xsize();
|
||||
|
|
@ -302,7 +334,19 @@ Status ModularFrameDecoder::DecodeGroup(
|
|||
if (zerofill && use_full_image) return true;
|
||||
// Return early if there's nothing to decode. Otherwise there might be
|
||||
// problems later (in ModularImageToDecodedRect).
|
||||
if (gi.channel.empty()) return true;
|
||||
if (gi.channel.empty()) {
|
||||
if (dec_state && should_run_pipeline) {
|
||||
const auto& frame_header = dec_state->shared->frame_header;
|
||||
const auto* metadata = frame_header.nonserialized_metadata;
|
||||
if (do_color || metadata->m.num_extra_channels > 0) {
|
||||
// Signal to FrameDecoder that we do not have some of the required input
|
||||
// for the render pipeline.
|
||||
*should_run_pipeline = false;
|
||||
}
|
||||
}
|
||||
JXL_DEBUG_V(6, "Nothing to decode, returning early.");
|
||||
return true;
|
||||
}
|
||||
ModularOptions options;
|
||||
if (!zerofill) {
|
||||
auto status = ModularGenericDecompress(
|
||||
|
|
@ -407,11 +451,11 @@ Status ModularFrameDecoder::DecodeAcMetadata(size_t group_id, BitReader* reader,
|
|||
uint32_t local_used_acs = 0;
|
||||
for (size_t iy = 0; iy < r.ysize(); iy++) {
|
||||
size_t y = r.y0() + iy;
|
||||
int* row_qf = r.Row(&dec_state->shared_storage.raw_quant_field, iy);
|
||||
int32_t* row_qf = r.Row(&dec_state->shared_storage.raw_quant_field, iy);
|
||||
uint8_t* row_epf = r.Row(&dec_state->shared_storage.epf_sharpness, iy);
|
||||
int* row_in_1 = image.channel[2].plane.Row(0);
|
||||
int* row_in_2 = image.channel[2].plane.Row(1);
|
||||
int* row_in_3 = image.channel[3].plane.Row(iy);
|
||||
int32_t* row_in_1 = image.channel[2].plane.Row(0);
|
||||
int32_t* row_in_2 = image.channel[2].plane.Row(1);
|
||||
int32_t* row_in_3 = image.channel[3].plane.Row(iy);
|
||||
for (size_t ix = 0; ix < r.xsize(); ix++) {
|
||||
size_t x = r.x0() + ix;
|
||||
int sharpness = row_in_3[ix];
|
||||
|
|
@ -448,8 +492,8 @@ Status ModularFrameDecoder::DecodeAcMetadata(size_t group_id, BitReader* reader,
|
|||
}
|
||||
JXL_RETURN_IF_ERROR(
|
||||
ac_strategy.SetNoBoundsCheck(x, y, AcStrategy::Type(row_in_1[num])));
|
||||
row_qf[ix] =
|
||||
1 + std::max(0, std::min(Quantizer::kQuantMax - 1, row_in_2[num]));
|
||||
row_qf[ix] = 1 + std::max<int32_t>(0, std::min(Quantizer::kQuantMax - 1,
|
||||
row_in_2[num]));
|
||||
num++;
|
||||
}
|
||||
}
|
||||
|
|
@ -605,7 +649,13 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
|
|||
DivCeil(modular_rect.xsize(), 1 << ch_in.hshift),
|
||||
DivCeil(modular_rect.ysize(), 1 << ch_in.vshift));
|
||||
mr = mr.Crop(ch_in.plane);
|
||||
|
||||
if (r.ysize() != mr.ysize() || r.xsize() != mr.xsize()) {
|
||||
return JXL_FAILURE("Dimension mismatch: trying to fit a %" PRIuS
|
||||
"x%" PRIuS
|
||||
" modular channel into "
|
||||
"a %" PRIuS "x%" PRIuS " rect",
|
||||
mr.xsize(), mr.ysize(), r.xsize(), r.ysize());
|
||||
}
|
||||
for (size_t y = 0; y < r.ysize(); ++y) {
|
||||
float* const JXL_RESTRICT row_out =
|
||||
r.Row(render_pipeline_input.GetBuffer(3 + ec).first, y);
|
||||
|
|
@ -627,31 +677,35 @@ Status ModularFrameDecoder::ModularImageToDecodedRect(
|
|||
|
||||
Status ModularFrameDecoder::FinalizeDecoding(PassesDecoderState* dec_state,
|
||||
jxl::ThreadPool* pool,
|
||||
ImageBundle* output,
|
||||
bool inplace) {
|
||||
if (!use_full_image) return true;
|
||||
Image gi = (inplace ? std::move(full_image) : full_image.clone());
|
||||
size_t xsize = gi.w;
|
||||
size_t ysize = gi.h;
|
||||
|
||||
JXL_DEBUG_V(3, "Finalizing decoding for modular image: %s",
|
||||
gi.DebugString().c_str());
|
||||
|
||||
// Don't use threads if total image size is smaller than a group
|
||||
if (xsize * ysize < frame_dim.group_dim * frame_dim.group_dim) pool = nullptr;
|
||||
|
||||
// Undo the global transforms
|
||||
gi.undo_transforms(global_header.wp_header, pool);
|
||||
for (auto t : global_transform) {
|
||||
JXL_RETURN_IF_ERROR(t.Inverse(gi, global_header.wp_header));
|
||||
}
|
||||
JXL_DASSERT(global_transform.empty());
|
||||
if (gi.error) return JXL_FAILURE("Undoing transforms failed");
|
||||
|
||||
for (size_t i = 0; i < dec_state->shared->frame_dim.num_groups; i++) {
|
||||
dec_state->render_pipeline->ClearDone(i);
|
||||
}
|
||||
std::atomic<bool> has_error{false};
|
||||
JXL_RETURN_IF_ERROR(RunOnPool(
|
||||
pool, 0, dec_state->shared->frame_dim.num_groups,
|
||||
[&](size_t num_threads) {
|
||||
return dec_state->render_pipeline->PrepareForThreads(
|
||||
num_threads,
|
||||
/*use_group_ids=*/dec_state->shared->frame_header.encoding ==
|
||||
FrameEncoding::kVarDCT);
|
||||
const auto& frame_header = dec_state->shared->frame_header;
|
||||
bool use_group_ids = (frame_header.encoding == FrameEncoding::kVarDCT ||
|
||||
(frame_header.flags & FrameHeader::kNoise));
|
||||
return dec_state->render_pipeline->PrepareForThreads(num_threads,
|
||||
use_group_ids);
|
||||
},
|
||||
[&](const uint32_t group, size_t thread_id) {
|
||||
RenderPipelineInput input =
|
||||
|
|
@ -701,7 +755,7 @@ Status ModularFrameDecoder::DecodeQuantTable(
|
|||
encoding->qraw.qtable->resize(required_size_x * required_size_y * 3);
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
for (size_t y = 0; y < required_size_y; y++) {
|
||||
int* JXL_RESTRICT row = image.channel[c].Row(y);
|
||||
int32_t* JXL_RESTRICT row = image.channel[c].Row(y);
|
||||
for (size_t x = 0; x < required_size_x; x++) {
|
||||
(*encoding->qraw.qtable)[c * required_size_x * required_size_y +
|
||||
y * required_size_x + x] = row[x];
|
||||
|
|
|
|||
9
third_party/jpeg-xl/lib/jxl/dec_modular.h
vendored
9
third_party/jpeg-xl/lib/jxl/dec_modular.h
vendored
|
|
@ -8,15 +8,15 @@
|
|||
|
||||
#include <stddef.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "lib/jxl/aux_out_fwd.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/dec_bit_reader.h"
|
||||
#include "lib/jxl/dec_cache.h"
|
||||
#include "lib/jxl/dec_params.h"
|
||||
#include "lib/jxl/frame_header.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/image_bundle.h"
|
||||
#include "lib/jxl/modular/encoding/encoding.h"
|
||||
#include "lib/jxl/modular/modular_image.h"
|
||||
|
||||
|
|
@ -82,6 +82,7 @@ struct ModularStreamId {
|
|||
static size_t Num(const FrameDimensions& frame_dim, size_t passes) {
|
||||
return ModularAC(0, passes).ID(frame_dim);
|
||||
}
|
||||
std::string DebugString() const;
|
||||
};
|
||||
|
||||
class ModularFrameDecoder {
|
||||
|
|
@ -93,7 +94,7 @@ class ModularFrameDecoder {
|
|||
int maxShift, const ModularStreamId& stream, bool zerofill,
|
||||
PassesDecoderState* dec_state,
|
||||
RenderPipelineInput* render_pipeline_input,
|
||||
ImageBundle* output, bool allow_truncated);
|
||||
bool allow_truncated, bool* should_run_pipeline = nullptr);
|
||||
// Decodes a VarDCT DC group (`group_id`) from the given `reader`.
|
||||
Status DecodeVarDCTDC(size_t group_id, BitReader* reader,
|
||||
PassesDecoderState* dec_state);
|
||||
|
|
@ -111,7 +112,7 @@ class ModularFrameDecoder {
|
|||
// if it is false, it can be called multiple times (e.g. for progressive
|
||||
// steps)
|
||||
Status FinalizeDecoding(PassesDecoderState* dec_state, jxl::ThreadPool* pool,
|
||||
ImageBundle* output, bool inplace);
|
||||
bool inplace);
|
||||
bool have_dc() const { return have_something; }
|
||||
void MaybeDropFullImage();
|
||||
bool UsesFullImage() const { return use_full_image; }
|
||||
|
|
|
|||
52
third_party/jpeg-xl/lib/jxl/dec_params.h
vendored
52
third_party/jpeg-xl/lib/jxl/dec_params.h
vendored
|
|
@ -1,52 +0,0 @@
|
|||
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
#ifndef LIB_JXL_DEC_PARAMS_H_
|
||||
#define LIB_JXL_DEC_PARAMS_H_
|
||||
|
||||
// Parameters and flags that govern JXL decompression.
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "lib/jxl/base/override.h"
|
||||
|
||||
namespace jxl {
|
||||
|
||||
struct DecompressParams {
|
||||
// If true, checks at the end of decoding that all of the compressed data
|
||||
// was consumed by the decoder.
|
||||
bool check_decompressed_size = true;
|
||||
|
||||
// If true, skip dequant and iDCT and decode to JPEG (only if possible)
|
||||
bool keep_dct = false;
|
||||
// If true, render spot colors (otherwise only returned as extra channels)
|
||||
bool render_spotcolors = true;
|
||||
// If true, coalesce frames (otherwise return unblended frames)
|
||||
bool coalescing = true;
|
||||
|
||||
// How many passes to decode at most. By default, decode everything.
|
||||
uint32_t max_passes = std::numeric_limits<uint32_t>::max();
|
||||
// Alternatively, one can specify the maximum tolerable downscaling factor
|
||||
// with respect to the full size of the image. By default, nothing less than
|
||||
// the full size is requested.
|
||||
size_t max_downsampling = 1;
|
||||
|
||||
// Try to decode as much as possible of a truncated codestream, but only whole
|
||||
// sections at a time.
|
||||
bool allow_partial_files = false;
|
||||
// Allow even more progression.
|
||||
bool allow_more_progressive_steps = false;
|
||||
|
||||
// Internal test-only setting: whether or not to use the slow rendering
|
||||
// pipeline.
|
||||
bool use_slow_render_pipeline = false;
|
||||
};
|
||||
|
||||
} // namespace jxl
|
||||
|
||||
#endif // LIB_JXL_DEC_PARAMS_H_
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue