Bug 1754067 - Update libjxl to 4322679b1c418addc2284c5ea84fc2c3935b4a75; r=saschanaz

Differential Revision: https://phabricator.services.mozilla.com/D138418
This commit is contained in:
june wilde 2022-02-14 17:57:57 +00:00
Родитель 79d6ccf336
Коммит 8b8d16f20c
108 изменённых файлов: 5467 добавлений и 984 удалений

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

@ -79,14 +79,16 @@ SOURCES += [
"/third_party/jpeg-xl/lib/jxl/render_pipeline/low_memory_render_pipeline.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/render_pipeline.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/simple_render_pipeline.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_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_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_upsampling.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write_to_ib.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_xyb.cc",
"/third_party/jpeg-xl/lib/jxl/render_pipeline/stage_ycbcr.cc",
"/third_party/jpeg-xl/lib/jxl/splines.cc",

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

@ -1,36 +1,19 @@
# Version of this schema
schema: 1
bugzilla:
# Bugzilla product and component for this directory and subdirectories
product: Core
component: "ImageLib"
# Document the source of externally hosted code
origin:
# Short name of the package/library
name: libjxl
description: JPEG XL image format reference implementation
# Full URL for the package's homepage/etc
# Usually different from repository url
url: https://github.com/libjxl/libjxl
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
release: commit 9a74bd70b7932750deb78a8aebd6e041ce7f8b01 (2021-12-28T23:30:10Z).
release: commit 4322679b1c418addc2284c5ea84fc2c3935b4a75 (2022-02-07T20:56:39Z).
# Revision to pull in
# Must be a long or short commit SHA (long preferred)
# NOTE(krosylight): Update highway together when updating this!
revision: 9a74bd70b7932750deb78a8aebd6e041ce7f8b01
revision: 4322679b1c418addc2284c5ea84fc2c3935b4a75
# The package's license, where possible using the mnemonic from
# https://spdx.org/licenses/
# Multiple licenses can be specified (as a YAML list)
# A "LICENSE" file must exist containing the full license text
license: Apache-2.0
license-file: LICENSE
@ -49,10 +32,6 @@ vendoring:
vendor-directory: third_party/jpeg-xl
exclude:
- doc/
- third_party/testdata/
- tools/
- doc
- third_party/testdata
- tools

29
third_party/jpeg-xl/.github/ISSUE_TEMPLATE/bug_report.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,29 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots or example input/output images to help explain your problem.
**Environment**
- OS: [e.g. Windows]
- Compiler version: [e.g. clang 11.0.1]
- CPU type: [e.g. x86_64]
- cjxl/djxl version string: [e.g. cjxl [v0.3.7 | SIMD supported: SSE4,Scalar]]
**Additional context**
Add any other context about the problem here.

20
third_party/jpeg-xl/.github/ISSUE_TEMPLATE/feature_request.md поставляемый Normal file
Просмотреть файл

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

548
third_party/jpeg-xl/.github/workflows/build_test.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,548 @@
# 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
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:
ubuntu_build:
name: Ubuntu Build ${{ matrix.name }}
runs-on: ${{ matrix.os || 'ubuntu-latest' }}
strategy:
matrix:
# We have one job per "name" in the matrix. Attributes are set on the
# specific job names.
name: [release, debug, asan, msan, scalar]
include:
- name: release
test_in_pr: true
# Track static stack size on build and check it doesn't exceed 3 kB.
env_stack_size: 1
max_stack: 3000
# Conformance tooling test requires numpy.
apt_pkgs: python3-numpy
- name: lowprecision
mode: release
test_in_pr: true
cmake_args: -DCMAKE_CXX_FLAGS=-DJXL_HIGH_PRECISION=0
- name: debug
# Runs on AVX3 CPUs require more stack than others. Make sure to
# test on AVX3-enabled CPUs when changing this value.
env_test_stack_size: 4000
# Build scalar-only hwy instructions.
- name: scalar
mode: release
cxxflags: -DHWY_DISABLED_TARGETS=~HWY_SCALAR
# Disabling optional features to speed up msan build a little bit.
- name: msan
skip_install: true
cmake_args: >-
-DJPEGXL_ENABLE_DEVTOOLS=OFF -DJPEGXL_ENABLE_PLUGINS=OFF
-DJPEGXL_ENABLE_VIEWERS=OFF
- name: asan
skip_install: true
- name: coverage
apt_pkgs: gcovr
# Coverage builds require a bit more RAM.
env_test_stack_size: 2048
# Exclude roundtrip tests from the unittest coverage.
ctest_args: -E '^JxlTest'
# Build with support for decoding to JPEG bytes disabled. Produces a
# smaller build if only decoding to pixels is needed.
- name: release-nojpeg
mode: release
cxxflags: -DJXL_DEBUG_ON_ABORT=0
cmake_args: >-
-DJPEGXL_ENABLE_TRANSCODE_JPEG=OFF
-DJPEGXL_ENABLE_PLUGINS=OFF
-DJPEGXL_ENABLE_VIEWERS=OFF
# Builds with gcc in release mode
- name: release:gcc8
mode: release
apt_pkgs: gcc-8 g++-8
cmake_args: >-
-DCMAKE_C_COMPILER=gcc-8 -DCMAKE_CXX_COMPILER=g++-8
# Builds with clang-5 in release mode
- name: release:clang-5
os: ubuntu-18.04
mode: release
# TODO(eustas): investigate, why static brotli library is not found.
skip_install: true
apt_pkgs: clang-5.0
cmake_args: >-
-DCMAKE_C_COMPILER=clang-5.0 -DCMAKE_CXX_COMPILER=clang++-5.0
-DJPEGXL_ENABLE_PLUGINS=OFF
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
# 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'))) }}
steps:
- name: Install build deps
run: |
sudo apt update
sudo apt install -y \
ccache \
clang-7 \
cmake \
doxygen \
libbenchmark-dev \
libbenchmark-tools \
libbrotli-dev \
libgdk-pixbuf2.0-dev \
libgflags-dev \
libgif-dev \
libgtest-dev \
libgtk2.0-dev \
libjpeg-dev \
libopenexr-dev \
libpng-dev \
libwebp-dev \
ninja-build \
pkg-config \
xvfb \
${{ matrix.apt_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: 2
- name: Sphinx dependencies
# Dependencies for sphinx HTML documentation
if: matrix.name == 'release'
run: |
pip3 install -r doc/sphinx/requirements.txt
- name: Git environment
id: git-env
run: |
echo "::set-output name=parent::$(git rev-parse ${{ github.sha }}^)"
shell: bash
- name: ccache
uses: actions/cache@v2
with:
path: ${{ env.CCACHE_DIR }}
# When the cache hits the key it is not updated, so if this is a rebuild
# of the same Pull Request it will reuse the cache if still around. For
# either Pull Requests or new pushes to main, this will use the parent
# hash as the starting point from the restore-keys entry.
key: ${{ runner.os }}-${{ github.sha }}-${{ matrix.name }}
restore-keys: |
${{ runner.os }}-${{ steps.git-env.outputs.parent }}-${{ matrix.name }}
- name: Build
run: |
mkdir -p ${CCACHE_DIR}
echo "max_size = 200M" > ${CCACHE_DIR}/ccache.conf
mode="${{ matrix.mode }}"
build_tests=$([ "$WILL_RUN_TESTS" == "true" ] && echo "ON" || echo "OFF")
[[ -n "${mode}" ]] || mode="${{ matrix.name }}"
./ci.sh ${mode} -DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
-DCMAKE_C_COMPILER_LAUNCHER=ccache \
-DBUILD_TESTING=${build_tests} \
${{ matrix.cmake_args }}
env:
SKIP_TEST: 1
CMAKE_CXX_FLAGS: ${{ matrix.cxxflags }}
- name: ccache stats
run: ccache --show-stats
- name: Build stats ${{ matrix.name }}
if: matrix.mode == 'release' || matrix.name == 'release'
run: |
tools/build_stats.py --save build/stats.json \
--max-stack ${{ matrix.max_stack || '0' }} \
cjxl djxl libjxl.so libjxl_dec.so
# Check that we can build the example project against the installed libs.
- name: Install and build examples
if: |
(matrix.mode == 'release' || matrix.name == 'release') &&
!matrix.skip_install
run: |
set -x
sudo cmake --build build -- install
cmake -Bbuild-example -Hexamples -G Ninja
cmake --build build-example
if ldd build-example/decode_oneshot_static | grep libjxl; then
echo "decode_oneshot_static is not using the static lib" >&2
exit 1
fi
# Test that the built binaries run.
echo -e -n "PF\n1 1\n-1.0\nrrrrggggbbbb" > 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
build-example/decode_oneshot_static test.jxl dec-static.pfm dec-static.icc
# Run the tests on push and when requested in pull_request.
- name: Test ${{ matrix.mode }}
if: env.WILL_RUN_TESTS == 'true'
run: |
./ci.sh test ${{ matrix.ctest_args }}
# Print the running time summary for the slowest tests.
- name: Test runtime stats
run: |
sort build/Testing/Temporary/CTestCostData.txt -k 3 -n | tail -n 20 || true
- name: Build HTML documentation (sphinx/readthetdocs)
if: matrix.name == 'release'
run: |
cmake --build build -- rtd-html
- name: Coverage report
if: github.event_name == 'push' && matrix.name == 'coverage'
run: |
./ci.sh coverage_report
- name: Coverage upload to Codecov
if: github.event_name == 'push' && matrix.name == 'coverage'
uses: codecov/codecov-action@v2
with:
flags: unittests
files: build/coverage.xml
- name: Fast benchmark ${{ matrix.mode }}
if: |
github.event_name == 'push' ||
(github.event_name == 'pull_request' && (
matrix.test_in_pr ||
contains(github.event.pull_request.labels.*.names, 'CI:full')))
run: |
STORE_IMAGES=0 ./ci.sh fast_benchmark
# Run gbench once, just to make sure it runs, not for actual benchmarking.
# This doesn't work on msan because we use gbench library from the system
# which is not instrumented by msan.
- name: gbench check
if: |
matrix.name == 'release' || (
github.event_name == 'push' && matrix.name != 'msan')
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:
build_target: i686-linux-gnu
env:
BUILD_DIR: build
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
libgtest-dev:${{ matrix.arch }}
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
# Some tests have a small floating point error on i686.
# TODO(deymo): Re-enable i686 tests.
if: |
matrix.build_target != 'i686-linux-gnu' && (
github.event_name == 'push' ||
(github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.names, 'CI:full')))
run: |
./ci.sh test
env:
BUILD_TARGET: ${{ matrix.build_target }}
windows_msys:
name: Windows MSYS2 / ${{ matrix.arch }}
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- arch: x86_64
msystem: mingw64
- arch: i686
msystem: mingw32
defaults:
run:
shell: msys2 {0}
steps:
- name: Checkout the source
uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 1
- uses: msys2/setup-msys2@v2
with:
msystem: ${{ matrix.msystem }}
update: true
path-type: inherit
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
- 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 \
-DJPEGXL_FORCE_SYSTEM_GTEST=ON \
-B build \
-G Ninja
- name: CMake build
run: cmake --build build
- name: Test
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
wasm32_build:
name: WASM wasm32/${{ matrix.variant }}
runs-on: ubuntu-latest
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
EM_VERSION: 2.0.23
V8_VERSION: 9.3.22
V8: ${{ github.workspace }}/.jsvu/v8
BUILD_TARGET: wasm32
strategy:
matrix:
include:
- variant: scalar
- variant: simd
steps:
- uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 1
- name: Install build deps
shell: bash
run: |
set -x
sudo apt update
pkgs=(
# Build dependencies
ccache
cmake
doxygen
ninja-build
pkg-config
)
DEBIAN_FRONTEND=noninteractive sudo apt install -y "${pkgs[@]}"
- name: Git environment
id: git-env
run: |
echo "::set-output name=parent::$(git rev-parse ${{ github.sha }}^)"
shell: bash
- name: ccache
uses: actions/cache@v2
with:
path: ${{ env.CCACHE_DIR }}
key: ${{ runner.os }}-${{ github.sha }}-${{ matrix.variant }}
restore-keys: |
${{ runner.os }}-${{ steps.git-env.outputs.parent }}-${{ matrix.variant }}
- name: Install emsdk
uses: mymindstorm/setup-emsdk@v10
# TODO(deymo): We could cache this action but it doesn't work when running
# in a matrix.
with:
version: ${{env.EM_VERSION}}
no-cache: true
- name: Install v8
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"
# TODO(deymo): Build and install other dependencies like libpng, libjpeg,
# etc.
- name: Build
run: |
mkdir -p ${CCACHE_DIR}
echo "max_size = 200M" > ${CCACHE_DIR}/ccache.conf
if [[ "${{ matrix.variant }}" == "simd" ]]; then
export ENABLE_WASM_SIMD=1
fi
emconfigure ./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
env:
SKIP_TEST: 1
- name: ccache stats
run: ccache --show-stats
- name: Test
if: |
github.event_name == 'push' ||
(github.event_name == 'pull_request' &&
contains(github.event.pull_request.labels.*.names, 'CI:full'))
run: |
emconfigure ./ci.sh test

125
third_party/jpeg-xl/.github/workflows/conformance.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,125 @@
# 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 running conformance tests.
name: Conformance
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:
build:
runs-on: ubuntu-latest
env:
CCACHE_DIR: ${{ github.workspace }}/.ccache
steps:
- name: Install build deps
run: |
sudo apt update
sudo apt install -y \
ccache \
clang-7 \
cmake \
doxygen \
libbenchmark-dev \
libbenchmark-tools \
libbrotli-dev \
libgdk-pixbuf2.0-dev \
libgflags-dev \
libgif-dev \
libgtest-dev \
libgtk2.0-dev \
libjpeg-dev \
libopenexr-dev \
libpng-dev \
libwebp-dev \
ninja-build \
pkg-config \
xvfb \
${{ matrix.apt_pkgs }} \
#
echo "CC=clang-7" >> $GITHUB_ENV
echo "CXX=clang++-7" >> $GITHUB_ENV
- name: Checkout the jxl source
uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 2
- name: Git environment
id: git-env
run: |
echo "::set-output name=parent::$(git rev-parse ${{ github.sha }}^)"
shell: bash
- name: ccache
uses: actions/cache@v2
with:
path: ${{ env.CCACHE_DIR }}
# When the cache hits the key it is not updated, so if this is a rebuild
# of the same Pull Request it will reuse the cache if still around. For
# either Pull Requests or new pushes to main, this will use the parent
# hash as the starting point from the restore-keys entry.
key: ${{ runner.os }}-${{ github.sha }}-${{ matrix.name }}
restore-keys: |
${{ runner.os }}-${{ steps.git-env.outputs.parent }}-${{ matrix.name }}
- name: Build
run: |
mkdir -p ${CCACHE_DIR}
echo "max_size = 200M" > ${CCACHE_DIR}/ccache.conf
./ci.sh 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
cp build/libjxl_dec.so.0.7.0 build/tools/conformance
env:
SKIP_TEST: 1
- uses: actions/upload-artifact@v2
with:
name: conformance_binary
path: |
build/tools/conformance/djxl_conformance
build/tools/conformance/libjxl_dec.so.0.7.0
- name: ccache stats
run: ccache --show-stats
run:
needs: build
runs-on: ubuntu-latest
strategy:
matrix:
# 'main_level10' currently fails
name: [main_level5]
steps:
- name: Install deps
run: |
pip install numpy
- name: Checkout the conformance source
uses: actions/checkout@v2
with:
repository: libjxl/conformance
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: Run conformance tests
run: |
chmod +x djxl_conformance
ln -s libjxl_dec.so.0.7.0 libjxl_dec.so.0.7
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:`pwd`
python conformance/scripts/conformance.py \
--decoder=`pwd`/djxl_conformance \
--corpus=`pwd`/conformance/testcases/${{ matrix.name }}.txt

60
third_party/jpeg-xl/.github/workflows/debug_ci.yml поставляемый 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.
# Workflow for building and then debugging on a specific commit.
name: Build and Test debugging
on:
push:
branches:
- ci-*-debug
jobs:
ubuntu_build:
name: Ubuntu Build and SSH
runs-on: [ubuntu-latest]
steps:
- name: Install build deps
run: |
sudo apt update
sudo apt install -y \
ccache \
clang-7 \
cmake \
doxygen \
libbrotli-dev \
libgdk-pixbuf2.0-dev \
libgflags-dev \
libgif-dev \
libgtest-dev \
libgtk2.0-dev \
libjpeg-dev \
libopenexr-dev \
libpng-dev \
libwebp-dev \
ninja-build \
pkg-config \
xvfb \
${{ matrix.apt_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: 2
- name: Build
run: |
./ci.sh $(echo ${{ github.ref }} | sed 's_refs/heads/ci-\([a-z_]*\)-debug_\1_') \
-DJPEGXL_FORCE_SYSTEM_BROTLI=ON
env:
SKIP_TEST: 1
- name: Setup tmate session
uses: mxschmitt/action-tmate@v3

56
third_party/jpeg-xl/.github/workflows/fuzz.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,56 @@
# 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.
# CI on pull-requests to run the fuzzer from oss-fuzz. See:
#
# https://google.github.io/oss-fuzz/getting-started/continuous-integration/
name: CIFuzz
on:
pull_request:
types: [opened, reopened, synchronize]
paths:
- '**.c'
- '**.cc'
- '**.cmake'
- '**.h'
- '**CMakeLists.txt'
- .github/workflows/fuzz.yml
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
fuzzing:
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v2
id: checkout
with:
# The build_fuzzers action checks out the code to the storage/libjxl
# directory already, but doesn't check out the submodules. This step
# is a workaround for checking out the submodules.
path: storage/libjxl
submodules: true
- name: Build Fuzzers
id: build
uses: google/oss-fuzz/infra/cifuzz/actions/build_fuzzers@master
with:
oss-fuzz-project-name: 'libjxl'
language: c++
- name: Run Fuzzers
uses: google/oss-fuzz/infra/cifuzz/actions/run_fuzzers@master
with:
oss-fuzz-project-name: 'libjxl'
language: c++
fuzz-seconds: 600
- name: Upload Crash
uses: actions/upload-artifact@v1
if: failure() && steps.build.outcome == 'success'
with:
name: artifacts
path: ./out/artifacts

42
third_party/jpeg-xl/.github/workflows/pull_request.yml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,42 @@
# 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 to run pull-requests specific checks.
name: PR
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
# Checks that the AUTHORS files is updated with new contributors.
authors:
runs-on: [ubuntu-latest]
steps:
- name: Checkout the source
uses: actions/checkout@v2
- name: Check AUTHORS file
run:
./ci.sh authors
format:
runs-on: [ubuntu-latest]
steps:
- name: Install build deps
run: |
sudo apt update
sudo apt install -y \
clang-format \
clang-format-7 \
clang-format-8 \
clang-format-9 \
clang-format-10 \
clang-format-11 \
#
- name: Checkout the source
uses: actions/checkout@v2
- name: clang-format
run:
./ci.sh lint >&2

362
third_party/jpeg-xl/.github/workflows/release.yaml поставляемый Normal file
Просмотреть файл

@ -0,0 +1,362 @@
# 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 the release binaries.
#
# This workflow runs as a post-submit step, when pushing to main or the release
# branches (v*.*.x), and when creating a release in GitHub.
#
# In the GitHub release case, in addition to build the release binaries it also
# uploads the binaries to the given release automatically.
name: Release build / deploy
on:
push:
branches:
- main
- v*.*.x
release:
types: [ published ]
jobs:
ubuntu_static_x86_64:
name: Release linux x86_64 static
runs-on: [ubuntu-latest]
steps:
- name: Install build deps
run: |
sudo apt update
sudo apt install -y \
asciidoc \
clang \
cmake \
doxygen \
libbrotli-dev \
libgdk-pixbuf2.0-dev \
libgflags-dev \
libgif-dev \
libgtest-dev \
libgtk2.0-dev \
libjpeg-dev \
libopenexr-dev \
libpng-dev \
libwebp-dev \
ninja-build \
pkg-config \
#
echo "CC=clang" >> $GITHUB_ENV
echo "CXX=clang++" >> $GITHUB_ENV
- name: Checkout the source
uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 1
- name: Build
env:
SKIP_TEST: 1
run: |
./ci.sh release \
-DJPEGXL_DEP_LICENSE_DIR=/usr/share/doc \
-DJPEGXL_STATIC=ON \
-DBUILD_TESTING=OFF \
-DJPEGXL_ENABLE_VIEWERS=OFF \
-DJPEGXL_ENABLE_PLUGINS=OFF \
-DJPEGXL_ENABLE_OPENEXR=OFF \
- name: Package release tarball
run: |
cd build
tar -zcvf ${{ runner.workspace }}/release_file.tar.gz \
LICENSE* tools/{cjxl,djxl,benchmark_xl}
ln -s ${{ runner.workspace }}/release_file.tar.gz \
${{ runner.workspace }}/jxl-linux-x86_64-static-${{ github.event.release.tag_name }}.tar.gz
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: jxl-linux-x86_64-static
path: ${{ runner.workspace }}/release_file.tar.gz
- name: Upload binaries to release
if: github.event_name == 'release'
uses: AButler/upload-release-assets@v2.0
with:
files: ${{ runner.workspace }}/jxl-linux-x86_64-static-${{ github.event.release.tag_name }}.tar.gz
repo-token: ${{ secrets.GITHUB_TOKEN }}
# Build .deb packages Ubuntu/Debian
release_ubuntu_pkg:
name: .deb packages / ${{ matrix.os }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
os:
- ubuntu:20.04
- ubuntu:18.04
- debian:buster
- debian:bullseye
- debian:bookworm
- debian:sid
container:
image: ${{ matrix.os }}
steps:
- name: Set env
shell: 'bash'
id: 'env'
run: |
artifact_name="jxl-debs-amd64-${matrix_os/:/-}"
echo ${artifact_name}
echo "::set-output name=artifact_name::${artifact_name}"
env:
matrix_os: ${{ matrix.os }}
- name: Install build deps
run: |
apt update
DEBIAN_FRONTEND=noninteractive apt install -y \
build-essential \
devscripts \
#
- name: Install git (only 18.04)
if: matrix.os == 'ubuntu:18.04'
# Ubuntu 18.04 ships with git 2.17 but we need 2.18 or newer for
# actions/checkout@v2 to work
shell: 'bash'
run: |
apt install -y \
libcurl4-openssl-dev \
libexpat1-dev \
libssl-dev \
wget \
zlib1g-dev \
#
git_version="2.32.0"
wget -nv \
"https://github.com/git/git/archive/refs/tags/v${git_version}.tar.gz"
tar -zxf "v${git_version}.tar.gz"
cd "git-${git_version}"
make prefix=/usr -j4 install
- name: Checkout the source
uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 1
- name: Stamp non-release versions
# Stamps the built package with the commit date as part of the version
# after the version number so newer release candidates can override older
# ones.
if: github.event_name != 'release'
shell: 'bash'
run: |
# Committer timestamp.
set -x
commit_timestamp=$(git show -s --format=%ct)
commit_datetime=$(date --utc "--date=@${commit_timestamp}" '+%Y%m%d%H%M%S')
commit_ref=$(git rev-parse --short HEAD)
sem_version=$(dpkg-parsechangelog --show-field Version)
sem_version="${sem_version%%-*}"
deb_version="${sem_version}~alpha${commit_datetime}-0+git${commit_ref}"
dch -M --distribution unstable -b --newversion "${deb_version}" \
"Stamping build with version ${deb_version}"
- name: Stamp release versions
# Mark the version as released
if: github.event_name == 'release'
shell: 'bash'
run: |
if head -n1 debian/changelog | grep UNRELEASED; then
dch -M --distribution unstable --release ''
fi
- name: Install gtest (only 18.04)
if: matrix.os == 'ubuntu:18.04'
# In Ubuntu 18.04 no package installed the libgtest.a. libgtest-dev
# installs the source files only.
run: |
apt install -y libgtest-dev cmake
for prj in googletest googlemock; do
(cd /usr/src/googletest/${prj}/ &&
cmake CMakeLists.txt -DCMAKE_INSTALL_PREFIX=/usr &&
make all install)
done
# Remove libgmock-dev dependency in Ubuntu 18.04. It doesn't exist there.
sed '/libgmock-dev,/d' -i debian/control
- name: Install gmock-dev (debian:sid)
# gtest-dev cmake depends on gmock-dev, but it is not installed by the
# package.
if: matrix.os == 'debian:sid'
run: |
apt install -y libgmock-dev
- name: Remove libjxl-gimp-plugin package (only 18.04)
if: matrix.os == 'ubuntu:18.04'
run: |
# Gimp 2.8 is not supported.
sed -i '/Package: libjxl-gimp-plugin/,/^$/d' debian/control
- name: Build hwy
run: |
apt build-dep -y ./third_party/highway
./ci.sh debian_build highway
dpkg -i build/debs/libhwy-dev_*_amd64.deb
- name: Build libjxl
run: |
apt build-dep -y .
./ci.sh debian_build jpeg-xl
- name: Stats
run: |
./ci.sh debian_stats
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: ${{ steps.env.outputs.artifact_name }}
path: |
build/debs/*jxl*.*
- name: Package release tarball
if: github.event_name == 'release'
run: |
(cd build/debs/; find -maxdepth 1 -name '*jxl*.*') | \
tar -zcvf release_file.tar.gz -C build/debs/ -T -
ln -s release_file.tar.gz \
${{ steps.env.outputs.artifact_name }}-${{ github.event.release.tag_name }}.tar.gz
- name: Upload binaries to release
if: github.event_name == 'release'
uses: AButler/upload-release-assets@v2.0
with:
files: ${{ steps.env.outputs.artifact_name }}-${{ github.event.release.tag_name }}.tar.gz
repo-token: ${{ secrets.GITHUB_TOKEN }}
windows_build:
name: Windows Build (vcpkg / ${{ matrix.triplet }})
runs-on: [windows-latest]
strategy:
fail-fast: false
matrix:
include:
- triplet: x86-windows-static
arch: '-A Win32'
- triplet: x64-windows-static
arch: '-A x64'
env:
VCPKG_VERSION: '2021.05.12'
VCPKG_ROOT: vcpkg
VCPKG_DISABLE_METRICS: 1
steps:
- name: Checkout the source
uses: actions/checkout@v2
with:
submodules: true
fetch-depth: 2
- uses: actions/cache@v2
id: cache-vcpkg
with:
path: vcpkg
key: ${{ runner.os }}-vcpkg-${{ env.VCPKG_VERSION }}-${{ matrix.triplet }}
- name: Download vcpkg
if: steps.cache-vcpkg.outputs.cache-hit != 'true'
# wget doesn't seem to work under bash.
shell: 'powershell'
run: |
C:\msys64\usr\bin\wget.exe -nv `
https://github.com/microsoft/vcpkg/archive/refs/tags/${{ env.VCPKG_VERSION }}.zip `
-O vcpkg.zip
- name: Bootstrap vcpkg
if: steps.cache-vcpkg.outputs.cache-hit != 'true'
shell: 'bash'
run: |
set -x
unzip -q vcpkg.zip
rm -rf ${VCPKG_ROOT}
mv vcpkg-${VCPKG_VERSION} ${VCPKG_ROOT}
${VCPKG_ROOT}/bootstrap-vcpkg.sh
- name: Install libraries with vcpkg
shell: 'bash'
run: |
set -x
${VCPKG_ROOT}/vcpkg --triplet ${{ matrix.triplet }} install \
gflags \
giflib \
libjpeg-turbo \
libpng \
libwebp \
#
- name: Configure
shell: 'bash'
run: |
set -x
mkdir build
cmake -Bbuild -H. ${{ matrix.arch }} \
-DBUILD_TESTING=OFF \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=`pwd`/prefix \
-DCMAKE_TOOLCHAIN_FILE=${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake \
-DJPEGXL_ENABLE_OPENEXR=OFF \
-DJPEGXL_ENABLE_PLUGINS=OFF \
-DJPEGXL_ENABLE_TCMALLOC=OFF \
-DJPEGXL_ENABLE_VIEWERS=OFF \
-DVCPKG_TARGET_TRIPLET=${{ matrix.triplet }} \
-DJPEGXL_BUNDLE_GFLAGS=ON \
#
- name: Build
shell: 'bash'
run: |
set -x
cmake --build build --config Release
- name: Install
shell: 'bash'
run: |
set -x
cmake --build build --config Release --target install
for pkg in giflib libjpeg-turbo libpng libwebp zlib; do
cp vcpkg/installed/${{matrix.triplet}}/share/${pkg}/copyright \
prefix/bin/LICENSE.${pkg}
done
cp third_party/sjpeg/COPYING prefix/bin/LICENSE.sjpeg
cp third_party/skcms/LICENSE prefix/bin/LICENSE.skcms
cp third_party/highway/LICENSE prefix/bin/LICENSE.highway
cp third_party/brotli/LICENSE prefix/bin/LICENSE.brotli
cp LICENSE prefix/bin/LICENSE.libjxl
- name: Upload artifacts
uses: actions/upload-artifact@v2
with:
name: jxl-${{matrix.triplet}}
path: |
prefix/bin/*
- name: Package release zip
if: github.event_name == 'release'
shell: 'powershell'
run: |
Compress-Archive -Path prefix\bin\* `
-DestinationPath jxl-${{matrix.triplet}}.zip
- name: Upload binaries to release
if: github.event_name == 'release'
uses: AButler/upload-release-assets@v2.0
with:
files: jxl-${{matrix.triplet}}.zip
repo-token: ${{ secrets.GITHUB_TOKEN }}

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

@ -20,6 +20,9 @@ Alex Xu (Hello71) <alex_y_xu@yahoo.ca>
Alexander Sago <cagelight@gmail.com>
Andrius Lukas Narbutas <andrius4669@gmail.com>
Artem Selishchev
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>
Jon Sneyers <jon@cloudinary.com>
@ -34,4 +37,5 @@ Pieter Wuille
Samuel Leong <wvvwvvvvwvvw@gmail.com>
Vincent Torri <vincent.torri@gmail.com>
xiota
Yonatan Nebenzhal <yonatan.nebenzhl@gmail.com>
Ziemowit Zabawa <ziemek.zabawa@outlook.com>

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

@ -76,8 +76,10 @@ 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.
@ -95,6 +97,8 @@ set(JPEGXL_ENABLE_BENCHMARK true CACHE BOOL
"Build JPEGXL benchmark tools.")
set(JPEGXL_ENABLE_EXAMPLES true CACHE BOOL
"Build JPEGXL library usage examples.")
set(JPEGXL_BUNDLE_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
@ -326,7 +330,9 @@ set(DOXYGEN_GENERATE_HTML "YES")
set(DOXYGEN_GENERATE_XML "YES")
set(DOXYGEN_STRIP_FROM_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/include")
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "README.md")
if(JPEGXL_WARNINGS_AS_ERRORS)
set(DOXYGEN_WARN_AS_ERROR "YES")
endif()
set(DOXYGEN_QUIET "YES")
doxygen_add_docs(doc
"${CMAKE_CURRENT_SOURCE_DIR}/lib/include"

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

@ -30,7 +30,7 @@ For more details and other workflows see the "Advanced guide" below.
### Checking out the code
```bash
git clone https://github.com/libjxl/libjxl.git --recursive
git clone https://github.com/libjxl/libjxl.git --recursive --shallow-submodules
```
This repository uses git submodules to handle some third party dependencies
@ -38,9 +38,20 @@ under `third_party`, that's why is important to pass `--recursive`. If you
didn't check out with `--recursive`, or any submodule has changed, run:
```bash
git submodule update --init --recursive --depth 1 --recommend-shallow
```
The `--shallow-submodules` and `--depth 1 --recommend-shallow` options create
shallow clones which only downloads the commits requested, and is all that is
needed to build `libjxl`. Should full clones be necessary, you could always run:
```bash
git submodule foreach git fetch --unshallow
git submodule update --init --recursive
```
which pulls the rest of the commits in the submodules.
Important: If you downloaded a zip file or tarball from the web interface you
won't get the needed submodules and the code will not compile. You can download
these external dependencies from source running `./deps.sh`. The git workflow
@ -52,7 +63,7 @@ Required dependencies for compiling the code, in a Debian/Ubuntu based
distribution run:
```bash
sudo apt install cmake pkg-config libbrotli-dev
sudo apt install cmake pkg-config libbrotli-dev libgflags-dev
```
Optional dependencies for supporting other formats in the `cjxl`/`djxl` tools,

0
third_party/jpeg-xl/bash_test.sh поставляемый Normal file → Executable file
Просмотреть файл

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

@ -697,7 +697,8 @@ cmd_msan() {
strip_dead_code
cmake_configure "$@" \
-DCMAKE_CROSSCOMPILING=1 -DRUN_HAVE_STD_REGEX=0 -DRUN_HAVE_POSIX_REGEX=0 \
-DJPEGXL_ENABLE_TCMALLOC=OFF -DJPEGXL_WARNINGS_AS_ERRORS=OFF
-DJPEGXL_ENABLE_TCMALLOC=OFF -DJPEGXL_WARNINGS_AS_ERRORS=OFF \
-DCMAKE_REQUIRED_LINK_OPTIONS="${msan_linker_flags[@]}"
cmake_build_and_test
}

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

@ -9,6 +9,7 @@ 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,

0
third_party/jpeg-xl/debian/rules поставляемый Normal file → Executable file
Просмотреть файл

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

@ -13,6 +13,7 @@ MYDIR=$(dirname $(realpath "$0"))
# Git revisions we use for the given submodules. Update these whenever you
# update a git submodule.
THIRD_PARTY_GFLAGS="827c769e5fc98e0f2a34c47cef953cc6328abced"
THIRD_PARTY_HIGHWAY="e69083a12a05caf037cabecdf1b248b7579705a5"
THIRD_PARTY_SKCMS="64374756e03700d649f897dbd98c95e78c30c7da"
THIRD_PARTY_SJPEG="868ab558fad70fcbe8863ba4e85179eeb81cc840"
@ -65,11 +66,12 @@ Current directory is a git repository, downloading dependencies via git:
git submodule update --init --recursive
EOF
git -C "${MYDIR}" submodule update --init --recursive
git -C "${MYDIR}" submodule update --init --recursive --depth 1 --recommend-shallow
return 0
fi
# Sources downloaded from a tarball.
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 \

0
third_party/jpeg-xl/docker/build.sh поставляемый Normal file → Executable file
Просмотреть файл

0
third_party/jpeg-xl/docker/scripts/emsdk_install.sh поставляемый Normal file → Executable file
Просмотреть файл

0
third_party/jpeg-xl/docker/scripts/jpegxl_builder.sh поставляемый Normal file → Executable file
Просмотреть файл

0
third_party/jpeg-xl/docker/scripts/msan_install.sh поставляемый Normal file → Executable file
Просмотреть файл

0
third_party/jpeg-xl/docker/scripts/qemu_install.sh поставляемый Normal file → Executable file
Просмотреть файл

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

@ -19,6 +19,9 @@ pkg_check_modules(JxlThreads REQUIRED IMPORTED_TARGET libjxl_threads)
add_executable(decode_oneshot decode_oneshot.cc)
target_link_libraries(decode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)
add_executable(decode_progressive decode_progressive.cc)
target_link_libraries(decode_progressive PkgConfig::Jxl PkgConfig::JxlThreads)
add_executable(encode_oneshot encode_oneshot.cc)
target_link_libraries(encode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)

238
third_party/jpeg-xl/examples/decode_progressive.cc поставляемый Normal file
Просмотреть файл

@ -0,0 +1,238 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This C++ example decodes a JPEG XL image progressively (input bytes are
// passed in chunks). The example outputs the intermediate steps to PAM files.
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include "jxl/decode.h"
#include "jxl/decode_cxx.h"
#include "jxl/resizable_parallel_runner.h"
#include "jxl/resizable_parallel_runner_cxx.h"
bool WritePAM(const char* filename, const uint8_t* buffer, size_t w, size_t h) {
FILE* fp = fopen(filename, "wb");
if (!fp) {
fprintf(stderr, "Could not open %s for writing", filename);
return false;
}
fprintf(fp,
"P7\nWIDTH %" PRIu64 "\nHEIGHT %" PRIu64
"\nDEPTH 4\nMAXVAL 255\nTUPLTYPE "
"RGB_ALPHA\nENDHDR\n",
static_cast<uint64_t>(w), static_cast<uint64_t>(h));
fwrite(buffer, 1, w * h * 4, fp);
if (fclose(fp) != 0) {
return false;
}
return true;
}
/** Decodes JPEG XL image to 8-bit integer RGBA pixels and an ICC Profile, in a
* progressive way, saving the intermediate steps.
*/
bool DecodeJpegXlProgressive(const uint8_t* jxl, size_t size,
const char* filename, size_t chunksize) {
std::vector<uint8_t> pixels;
std::vector<uint8_t> icc_profile;
size_t xsize = 0, ysize = 0;
// Multi-threaded parallel runner.
auto runner = JxlResizableParallelRunnerMake(nullptr);
auto dec = JxlDecoderMake(nullptr);
if (JXL_DEC_SUCCESS !=
JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
JXL_DEC_COLOR_ENCODING |
JXL_DEC_FULL_IMAGE)) {
fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
return false;
}
if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(),
JxlResizableParallelRunner,
runner.get())) {
fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
return false;
}
JxlBasicInfo info;
JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
size_t seen = 0;
JxlDecoderSetInput(dec.get(), jxl, chunksize);
size_t remaining = chunksize;
for (;;) {
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
if (status == JXL_DEC_ERROR) {
fprintf(stderr, "Decoder error\n");
return false;
} else if (status == JXL_DEC_NEED_MORE_INPUT || status == JXL_DEC_SUCCESS ||
status == JXL_DEC_FULL_IMAGE) {
seen += remaining - JxlDecoderReleaseInput(dec.get());
printf("Flushing after %" PRIu64 " bytes\n", static_cast<uint64_t>(seen));
if (status == JXL_DEC_NEED_MORE_INPUT &&
JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec.get())) {
printf("flush error (no preview yet)\n");
} else {
char fname[1024];
if (snprintf(fname, 1024, "%s-%" PRIu64 ".pam", filename,
static_cast<uint64_t>(seen)) >= 1024) {
fprintf(stderr, "Filename too long\n");
return false;
};
if (!WritePAM(fname, pixels.data(), xsize, ysize)) {
fprintf(stderr, "Error writing progressive output\n");
}
}
remaining = size - seen;
if (remaining > chunksize) remaining = chunksize;
if (remaining == 0) {
if (status == JXL_DEC_NEED_MORE_INPUT) {
fprintf(stderr, "Error, already provided all input\n");
return false;
} else {
return true;
}
}
JxlDecoderSetInput(dec.get(), jxl + seen, remaining);
} else if (status == JXL_DEC_BASIC_INFO) {
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
return false;
}
xsize = info.xsize;
ysize = info.ysize;
JxlResizableParallelRunnerSetThreads(
runner.get(),
JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
} else if (status == JXL_DEC_COLOR_ENCODING) {
// Get the ICC color profile of the pixel data
size_t icc_size;
if (JXL_DEC_SUCCESS !=
JxlDecoderGetICCProfileSize(dec.get(), &format,
JXL_COLOR_PROFILE_TARGET_ORIGINAL,
&icc_size)) {
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
return false;
}
icc_profile.resize(icc_size);
if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
dec.get(), &format,
JXL_COLOR_PROFILE_TARGET_ORIGINAL,
icc_profile.data(), icc_profile.size())) {
fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
return false;
}
} else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
size_t buffer_size;
if (JXL_DEC_SUCCESS !=
JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
return false;
}
if (buffer_size != xsize * ysize * 4) {
fprintf(stderr, "Invalid out buffer size %" PRIu64 " != %" PRIu64 "\n",
static_cast<uint64_t>(buffer_size),
static_cast<uint64_t>(xsize * ysize * 4));
return false;
}
pixels.resize(xsize * ysize * 4);
void* pixels_buffer = (void*)pixels.data();
size_t pixels_buffer_size = pixels.size() * sizeof(float);
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
pixels_buffer,
pixels_buffer_size)) {
fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
return false;
}
} else {
fprintf(stderr, "Unknown decoder status\n");
return false;
}
}
}
bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
FILE* file = fopen(filename, "rb");
if (!file) {
return false;
}
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return false;
}
long size = ftell(file);
// Avoid invalid file or directory.
if (size >= LONG_MAX || size < 0) {
fclose(file);
return false;
}
if (fseek(file, 0, SEEK_SET) != 0) {
fclose(file);
return false;
}
out->resize(size);
size_t readsize = fread(out->data(), 1, size, file);
if (fclose(file) != 0) {
return false;
}
return readsize == static_cast<size_t>(size);
}
int main(int argc, char* argv[]) {
if (argc < 3) {
fprintf(
stderr,
"Usage: %s <jxl> <basename> [chunksize]\n"
"Where:\n"
" jxl = input JPEG XL image filename\n"
" basename = prefix of output filenames\n"
" chunksize = loads chunksize bytes at a time and writes\n"
" intermediate results to basename-[bytes loaded].pam\n"
"Output files will be overwritten.\n",
argv[0]);
return 1;
}
const char* jxl_filename = argv[1];
const char* png_filename = argv[2];
std::vector<uint8_t> jxl;
if (!LoadFile(jxl_filename, &jxl)) {
fprintf(stderr, "couldn't load %s\n", jxl_filename);
return 1;
}
size_t chunksize = jxl.size();
if (argc > 3) {
long cs = atol(argv[3]);
if (cs < 100) {
fprintf(stderr, "Chunk size is too low, try at least 100 bytes\n");
return 1;
}
chunksize = cs;
}
if (!DecodeJpegXlProgressive(jxl.data(), jxl.size(), png_filename,
chunksize)) {
fprintf(stderr, "Error while decoding the jxl file\n");
return 1;
}
return 0;
}

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

@ -5,6 +5,8 @@
add_executable(decode_oneshot ${CMAKE_CURRENT_LIST_DIR}/decode_oneshot.cc)
target_link_libraries(decode_oneshot jxl_dec jxl_threads)
add_executable(decode_progressive ${CMAKE_CURRENT_LIST_DIR}/decode_progressive.cc)
target_link_libraries(decode_progressive jxl_dec jxl_threads)
add_executable(encode_oneshot ${CMAKE_CURRENT_LIST_DIR}/encode_oneshot.cc)
target_link_libraries(encode_oneshot jxl jxl_threads)

1
third_party/jpeg-xl/experimental/fast_lossless/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1 @@
build/

26
third_party/jpeg-xl/experimental/fast_lossless/build-android.sh поставляемый Executable file
Просмотреть файл

@ -0,0 +1,26 @@
#!/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.
set -e
DIR=$(realpath "$(dirname "$0")")
mkdir -p /tmp/build-android
cd /tmp/build-android
CXX="$ANDROID_NDK"/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android30-clang++
if ! command -v "$CXX" >/dev/null ; then
printf >&2 '%s: Android C++ compiler not found, is ANDROID_NDK set properly?\n' "${0##*/}"
exit 1
fi
[ -f lodepng.cpp ] || curl -o lodepng.cpp --url 'https://raw.githubusercontent.com/lvandeve/lodepng/8c6a9e30576f07bf470ad6f09458a2dcd7a6a84a/lodepng.cpp'
[ -f lodepng.h ] || curl -o lodepng.h --url 'https://raw.githubusercontent.com/lvandeve/lodepng/8c6a9e30576f07bf470ad6f09458a2dcd7a6a84a/lodepng.h'
[ -f lodepng.o ] || "$CXX" lodepng.cpp -O3 -o lodepng.o -c
"$CXX" -O3 -DFASTLL_ENABLE_NEON_INTRINSICS -fopenmp \
-I. lodepng.o \
"${DIR}"/fast_lossless.cc "${DIR}"/fast_lossless_main.cc \
-o fast_lossless

26
third_party/jpeg-xl/experimental/fast_lossless/build.sh поставляемый Executable file
Просмотреть файл

@ -0,0 +1,26 @@
#!/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.
set -e
DIR=$(realpath "$(dirname "$0")")
mkdir -p "$DIR"/build
cd "$DIR"/build
# set CXX to clang++ if not set in the environment
CXX="${CXX-clang++}"
if ! command -v "$CXX" >/dev/null ; then
printf >&2 '%s: C++ compiler not found\n' "${0##*/}"
exit 1
fi
[ -f lodepng.cpp ] || curl -o lodepng.cpp --url 'https://raw.githubusercontent.com/lvandeve/lodepng/8c6a9e30576f07bf470ad6f09458a2dcd7a6a84a/lodepng.cpp'
[ -f lodepng.h ] || curl -o lodepng.h --url 'https://raw.githubusercontent.com/lvandeve/lodepng/8c6a9e30576f07bf470ad6f09458a2dcd7a6a84a/lodepng.h'
[ -f lodepng.o ] || "$CXX" lodepng.cpp -O3 -mavx2 -o lodepng.o -c
"$CXX" -O3 -mavx2 -DFASTLL_ENABLE_AVX2_INTRINSICS -fopenmp \
-I. lodepng.o \
"$DIR"/fast_lossless.cc "$DIR"/fast_lossless_main.cc \
-o fast_lossless

1354
third_party/jpeg-xl/experimental/fast_lossless/fast_lossless.cc поставляемый Normal file

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

14
third_party/jpeg-xl/experimental/fast_lossless/fast_lossless.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,14 @@
// 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 FAST_LOSSLESS_H
#define FAST_LOSSLESS_H
#include <stdlib.h>
size_t FastLosslessEncode(const unsigned char* rgba, size_t width,
size_t row_stride, size_t height, size_t nb_chans,
size_t bitdepth, int effort, unsigned char** output);
#endif

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

@ -0,0 +1,78 @@
// 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <chrono>
#include <thread>
#include "fast_lossless.h"
#include "lodepng.h"
#include "pam-input.h"
int main(int argc, char** argv) {
if (argc < 3) {
fprintf(stderr, "Usage: %s in.png out.jxl [effort] [num_reps]\n", argv[0]);
return 1;
}
const char* in = argv[1];
const char* out = argv[2];
int effort = argc >= 4 ? atoi(argv[3]) : 2;
size_t num_reps = argc >= 5 ? atoi(argv[4]) : 1;
if (effort < 0 || effort > 127) {
fprintf(
stderr,
"Effort should be between 0 and 127 (default is 2, more is slower)\n");
return 1;
}
unsigned char* png;
unsigned w, h;
size_t nb_chans = 4, bitdepth = 8;
unsigned error = lodepng_decode32_file(&png, &w, &h, in);
size_t width = w, height = h;
if (error && !DecodePAM(in, &png, &width, &height, &nb_chans, &bitdepth)) {
fprintf(stderr, "lodepng error %u: %s\n", error, lodepng_error_text(error));
return 1;
}
size_t encoded_size = 0;
unsigned char* encoded = nullptr;
size_t stride = width * nb_chans * (bitdepth > 8 ? 2 : 1);
auto start = std::chrono::high_resolution_clock::now();
for (size_t _ = 0; _ < num_reps; _++) {
free(encoded);
encoded_size = FastLosslessEncode(png, width, stride, height, nb_chans,
bitdepth, effort, &encoded);
}
auto stop = std::chrono::high_resolution_clock::now();
if (num_reps > 1) {
float us =
std::chrono::duration_cast<std::chrono::microseconds>(stop - start)
.count();
size_t pixels = size_t{width} * size_t{height} * num_reps;
float mps = pixels / us;
fprintf(stderr, "%10.3f MP/s\n", mps);
fprintf(stderr, "%10.3f bits/pixel\n",
encoded_size * 8.0 / float(width) / float(height));
}
FILE* o = fopen(out, "wb");
if (!o) {
fprintf(stderr, "error opening %s: %s\n", out, strerror(errno));
return 1;
}
if (fwrite(encoded, 1, encoded_size, o) != encoded_size) {
fprintf(stderr, "error writing to %s: %s\n", out, strerror(errno));
}
fclose(o);
}

289
third_party/jpeg-xl/experimental/fast_lossless/pam-input.h поставляемый Normal file
Просмотреть файл

@ -0,0 +1,289 @@
// 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 <limits.h>
#include <stdlib.h>
#include <string.h>
bool error_msg(const char* message) {
fprintf(stderr, "%s\n", message);
return false;
}
#define return_on_error(X) \
if (!X) return false;
size_t Log2(uint32_t value) { return 31 - __builtin_clz(value); }
struct HeaderPNM {
size_t xsize;
size_t ysize;
bool is_gray; // PGM
bool has_alpha; // PAM
size_t bits_per_sample;
};
class Parser {
public:
explicit Parser(uint8_t* data, size_t length)
: pos_(data), end_(data + length) {}
// Sets "pos" to the first non-header byte/pixel on success.
bool ParseHeader(HeaderPNM* header, const uint8_t** pos) {
// codec.cc ensures we have at least two bytes => no range check here.
if (pos_[0] != 'P') return false;
const uint8_t type = pos_[1];
pos_ += 2;
switch (type) {
case '5':
header->is_gray = true;
return ParseHeaderPNM(header, pos);
case '6':
header->is_gray = false;
return ParseHeaderPNM(header, pos);
case '7':
return ParseHeaderPAM(header, pos);
}
return false;
}
// Exposed for testing
bool ParseUnsigned(size_t* number) {
if (pos_ == end_) return error_msg("PNM: reached end before number");
if (!IsDigit(*pos_)) return error_msg("PNM: expected unsigned number");
*number = 0;
while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') {
*number *= 10;
*number += *pos_ - '0';
++pos_;
}
return true;
}
bool ParseSigned(double* number) {
if (pos_ == end_) return error_msg("PNM: reached end before signed");
if (*pos_ != '-' && *pos_ != '+' && !IsDigit(*pos_)) {
return error_msg("PNM: expected signed number");
}
// Skip sign
const bool is_neg = *pos_ == '-';
if (is_neg || *pos_ == '+') {
++pos_;
if (pos_ == end_) return error_msg("PNM: reached end before digits");
}
// Leading digits
*number = 0.0;
while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') {
*number *= 10;
*number += *pos_ - '0';
++pos_;
}
// Decimal places?
if (pos_ < end_ && *pos_ == '.') {
++pos_;
double place = 0.1;
while (pos_ < end_ && *pos_ >= '0' && *pos_ <= '9') {
*number += (*pos_ - '0') * place;
place *= 0.1;
++pos_;
}
}
if (is_neg) *number = -*number;
return true;
}
private:
static bool IsDigit(const uint8_t c) { return '0' <= c && c <= '9'; }
static bool IsLineBreak(const uint8_t c) { return c == '\r' || c == '\n'; }
static bool IsWhitespace(const uint8_t c) {
return IsLineBreak(c) || c == '\t' || c == ' ';
}
bool SkipBlank() {
if (pos_ == end_) return error_msg("PNM: reached end before blank");
const uint8_t c = *pos_;
if (c != ' ' && c != '\n') return error_msg("PNM: expected blank");
++pos_;
return true;
}
bool SkipSingleWhitespace() {
if (pos_ == end_) return error_msg("PNM: reached end before whitespace");
if (!IsWhitespace(*pos_)) return error_msg("PNM: expected whitespace");
++pos_;
return true;
}
bool SkipWhitespace() {
if (pos_ == end_) return error_msg("PNM: reached end before whitespace");
if (!IsWhitespace(*pos_) && *pos_ != '#') {
return error_msg("PNM: expected whitespace/comment");
}
while (pos_ < end_ && IsWhitespace(*pos_)) {
++pos_;
}
// Comment(s)
while (pos_ != end_ && *pos_ == '#') {
while (pos_ != end_ && !IsLineBreak(*pos_)) {
++pos_;
}
// Newline(s)
while (pos_ != end_ && IsLineBreak(*pos_)) pos_++;
}
while (pos_ < end_ && IsWhitespace(*pos_)) {
++pos_;
}
return true;
}
bool MatchString(const char* keyword) {
const uint8_t* ppos = pos_;
while (*keyword) {
if (ppos >= end_) return error_msg("PAM: unexpected end of input");
if (*keyword != *ppos) return false;
ppos++;
keyword++;
}
pos_ = ppos;
return_on_error(SkipWhitespace());
return true;
}
bool ParseHeaderPAM(HeaderPNM* header, const uint8_t** pos) {
size_t num_channels = 3;
size_t max_val = 255;
while (!MatchString("ENDHDR")) {
return_on_error(SkipWhitespace());
if (MatchString("WIDTH")) {
return_on_error(ParseUnsigned(&header->xsize));
} else if (MatchString("HEIGHT")) {
return_on_error(ParseUnsigned(&header->ysize));
} else if (MatchString("DEPTH")) {
return_on_error(ParseUnsigned(&num_channels));
} else if (MatchString("MAXVAL")) {
return_on_error(ParseUnsigned(&max_val));
} else if (MatchString("TUPLTYPE")) {
if (MatchString("RGB_ALPHA")) {
header->has_alpha = true;
} else if (MatchString("RGB")) {
} else if (MatchString("GRAYSCALE_ALPHA")) {
header->has_alpha = true;
header->is_gray = true;
} else if (MatchString("GRAYSCALE")) {
header->is_gray = true;
} else if (MatchString("BLACKANDWHITE_ALPHA")) {
header->has_alpha = true;
header->is_gray = true;
max_val = 1;
} else if (MatchString("BLACKANDWHITE")) {
header->is_gray = true;
max_val = 1;
} else {
return error_msg("PAM: unknown TUPLTYPE");
}
} else {
return error_msg("PAM: unknown header keyword");
}
}
if (num_channels !=
(header->has_alpha ? 1 : 0) + (header->is_gray ? 1 : 3)) {
return error_msg("PAM: bad DEPTH");
}
if (max_val == 0 || max_val >= 65536) {
return error_msg("PAM: bad MAXVAL");
}
header->bits_per_sample = Log2(max_val + 1);
*pos = pos_;
return true;
}
bool ParseHeaderPNM(HeaderPNM* header, const uint8_t** pos) {
return_on_error(SkipWhitespace());
return_on_error(ParseUnsigned(&header->xsize));
return_on_error(SkipWhitespace());
return_on_error(ParseUnsigned(&header->ysize));
return_on_error(SkipWhitespace());
size_t max_val;
return_on_error(ParseUnsigned(&max_val));
if (max_val == 0 || max_val >= 65536) {
return error_msg("PNM: bad MaxVal");
}
header->bits_per_sample = Log2(max_val + 1);
return_on_error(SkipSingleWhitespace());
*pos = pos_;
return true;
}
const uint8_t* pos_;
const uint8_t* const end_;
};
bool load_file(unsigned char** out, size_t* outsize, const char* filename) {
FILE* file;
file = fopen(filename, "rb");
if (!file) return false;
if (fseek(file, 0, SEEK_END) != 0) {
fclose(file);
return false;
}
*outsize = ftell(file);
if (*outsize == LONG_MAX || *outsize < 9 || fseek(file, 0, SEEK_SET)) {
fclose(file);
return false;
}
*out = (unsigned char*)malloc(*outsize);
if (!(*out)) return false;
size_t readsize;
readsize = fread(*out, 1, *outsize, file);
fclose(file);
if (readsize != *outsize) return false;
return true;
}
bool DecodePAM(const char* filename, uint8_t** buffer, size_t* w, size_t* h,
size_t* nb_chans, size_t* bitdepth) {
unsigned char* in_file;
size_t in_size;
if (!load_file(&in_file, &in_size, filename))
return error_msg("Could not read input file");
Parser parser(in_file, in_size);
HeaderPNM header = {};
const uint8_t* pos = nullptr;
if (!parser.ParseHeader(&header, &pos)) return false;
if (header.bits_per_sample == 0 || header.bits_per_sample > 12) {
return error_msg("PNM: bits_per_sample invalid (can do at most 12-bit)");
}
*w = header.xsize;
*h = header.ysize;
*bitdepth = header.bits_per_sample;
*nb_chans = (header.is_gray ? 1 : 3) + (header.has_alpha ? 1 : 0);
size_t pnm_remaining_size = in_file + in_size - pos;
size_t buffer_size = *w * *h * *nb_chans * (*bitdepth > 8 ? 2 : 1);
if (pnm_remaining_size < buffer_size) {
return error_msg("PNM file too small");
}
*buffer = (uint8_t*)malloc(buffer_size);
memcpy(*buffer, pos, buffer_size);
return true;
}

0
third_party/jpeg-xl/js-wasm-wrapper.sh поставляемый Normal file → Executable file
Просмотреть файл

27
third_party/jpeg-xl/lib/extras/LICENSE.apngdis поставляемый Normal file
Просмотреть файл

@ -0,0 +1,27 @@
APNG Disassembler 2.8
Deconstructs APNG files into individual frames.
http://apngdis.sourceforge.net
Copyright (c) 2010-2015 Max Stepin
maxst at users.sourceforge.net
zlib license
------------
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

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

@ -46,6 +46,7 @@
#include "jxl/encode.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/scope_guard.h"
#include "lib/jxl/common.h"
#include "lib/jxl/sanitizers.h"
#include "png.h" /* original (unpatched) libpng is ok */
@ -315,7 +316,6 @@ int processing_start(png_structp& png_ptr, png_infop& info_ptr, void* frame_ptr,
if (!png_ptr || !info_ptr) return 1;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 1;
}
@ -339,7 +339,6 @@ int processing_data(png_structp png_ptr, png_infop info_ptr, unsigned char* p,
if (!png_ptr || !info_ptr) return 1;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 1;
}
@ -354,7 +353,6 @@ int processing_finish(png_structp png_ptr, png_infop info_ptr,
if (!png_ptr || !info_ptr) return 1;
if (setjmp(png_jmpbuf(png_ptr))) {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 1;
}
@ -367,8 +365,6 @@ int processing_finish(png_structp png_ptr, png_infop info_ptr,
(void)BlobsReaderPNG::Decode(text_ptr[i], metadata);
}
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
return 0;
}
@ -382,20 +378,37 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
unsigned int id, j, w, h, w0, h0, x0, y0;
unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize;
unsigned char sig[8];
png_structp png_ptr;
png_infop info_ptr;
png_structp png_ptr = nullptr;
png_infop info_ptr = nullptr;
PaddedBytes chunk;
PaddedBytes chunkIHDR;
std::vector<PaddedBytes> chunksInfo;
bool isAnimated = false;
bool skipFirst = false;
bool hasInfo = false;
bool all_dispose_bg = true;
APNGFrame frameRaw = {};
uint32_t num_channels;
JxlPixelFormat format;
unsigned int bytes_per_pixel = 0;
struct FrameInfo {
PackedImage data;
uint32_t duration;
size_t x0, xsize;
size_t y0, ysize;
uint32_t dispose_op;
uint32_t blend_op;
};
std::vector<FrameInfo> frames;
// Make sure png memory is released in any case.
auto scope_guard = MakeScopeGuard([&]() {
png_destroy_read_struct(&png_ptr, &info_ptr, 0);
// Just in case. Not all versions on libpng wipe-out the pointers.
png_ptr = nullptr;
info_ptr = nullptr;
});
r = {bytes.data(), bytes.data() + bytes.size()};
// Not a PNG => not an error
unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
@ -434,14 +447,12 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
if (!processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
chunkIHDR, chunksInfo)) {
bool last_base_was_none = true;
while (!r.Eof()) {
id = read_chunk(&r, &chunk);
if (!id) break;
if (id == kId_acTL && !hasInfo && !isAnimated) {
isAnimated = true;
skipFirst = true;
ppf->info.have_animation = true;
ppf->info.animation.tps_numerator = 1000;
ppf->info.animation.tps_denominator = 1;
@ -450,38 +461,12 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
if (hasInfo) {
if (!processing_finish(png_ptr, info_ptr, &ppf->metadata)) {
// Allocates the frame buffer.
ppf->frames.emplace_back(w0, h0, format);
auto* frame = &ppf->frames.back();
frame->frame_info.duration = delay_num * 1000 / delay_den;
frame->x0 = x0;
frame->y0 = y0;
// TODO(veluca): this could in principle be implemented.
if (last_base_was_none && !all_dispose_bg &&
(x0 != 0 || y0 != 0 || w0 != w || h0 != h || bop != 0)) {
return JXL_FAILURE(
"APNG with dispose-to-0 is not supported for non-full or "
"blended frames");
}
switch (dop) {
case 0:
frame->use_for_next_frame = true;
last_base_was_none = false;
all_dispose_bg = false;
break;
case 2:
frame->use_for_next_frame = false;
all_dispose_bg = false;
break;
default:
frame->use_for_next_frame = false;
last_base_was_none = true;
}
frame->blend = bop != 0;
uint32_t duration = delay_num * 1000 / delay_den;
frames.push_back(FrameInfo{PackedImage(w0, h0, format), duration,
x0, w0, y0, h0, dop, bop});
auto& frame = frames.back().data;
for (size_t y = 0; y < h0; ++y) {
memcpy(static_cast<uint8_t*>(frame->color.pixels()) +
frame->color.stride * y,
memcpy(static_cast<uint8_t*>(frame.pixels()) + frame.stride * y,
frameRaw.rows[y], bytes_per_pixel * w0);
}
} else {
@ -493,6 +478,11 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
errorstate = false;
break;
}
if (chunk.size() < 34) {
return JXL_FAILURE("Received a chunk that is too small (%" PRIuS
"B)",
chunk.size());
}
// At this point the old frame is done. Let's start a new one.
w0 = png_get_uint_32(chunk.data() + 12);
h0 = png_get_uint_32(chunk.data() + 16);
@ -517,13 +507,6 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
chunkIHDR, chunksInfo)) {
break;
}
} else
skipFirst = false;
if (ppf->frames.size() == (skipFirst ? 1 : 0)) {
bop = 0;
if (dop == 2) dop = 1;
}
} else if (id == kId_IDAT) {
// First IDAT chunk means we now have all header info
@ -552,7 +535,13 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
if (colortype & 4 ||
png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
ppf->info.alpha_bits = ppf->info.bits_per_sample;
if (sigbits) ppf->info.alpha_bits = sigbits->alpha;
if (sigbits) {
if (sigbits->alpha &&
sigbits->alpha != ppf->info.bits_per_sample) {
return JXL_FAILURE("Unsupported alpha bit-depth");
}
ppf->info.alpha_bits = sigbits->alpha;
}
} else {
ppf->info.alpha_bits = 0;
}
@ -650,6 +639,110 @@ Status DecodeImageAPNG(const Span<const uint8_t> bytes,
}
if (errorstate) return false;
bool has_nontrivial_background = false;
bool previous_frame_should_be_cleared = false;
enum {
DISPOSE_OP_NONE = 0,
DISPOSE_OP_BACKGROUND = 1,
DISPOSE_OP_PREVIOUS = 2,
};
enum {
BLEND_OP_SOURCE = 0,
BLEND_OP_OVER = 1,
};
for (size_t i = 0; i < frames.size(); i++) {
auto& frame = frames[i];
JXL_ASSERT(frame.data.xsize == frame.xsize);
JXL_ASSERT(frame.data.ysize == frame.ysize);
// Before encountering a DISPOSE_OP_NONE frame, the canvas is filled with 0,
// so DISPOSE_OP_BACKGROUND and DISPOSE_OP_PREVIOUS are equivalent.
if (frame.dispose_op == DISPOSE_OP_NONE) {
has_nontrivial_background = true;
}
bool should_blend = frame.blend_op == BLEND_OP_OVER;
bool use_for_next_frame =
has_nontrivial_background && frame.dispose_op != DISPOSE_OP_PREVIOUS;
size_t x0 = frame.x0;
size_t y0 = frame.y0;
if (previous_frame_should_be_cleared) {
size_t xs = frame.data.xsize;
size_t ys = frame.data.ysize;
size_t px0 = frames[i - 1].x0;
size_t py0 = frames[i - 1].y0;
size_t pxs = frames[i - 1].xsize;
size_t pys = frames[i - 1].ysize;
if (px0 >= x0 && py0 >= y0 && px0 + pxs <= x0 + xs &&
py0 + pys <= y0 + ys && frame.blend_op == BLEND_OP_SOURCE &&
use_for_next_frame) {
// If the previous frame is entirely contained in the current frame and
// we are using BLEND_OP_SOURCE, nothing special needs to be done.
ppf->frames.emplace_back(std::move(frame.data));
} else if (px0 == x0 && py0 == y0 && px0 + pxs == x0 + xs &&
py0 + pys == y0 + ys && use_for_next_frame) {
// If the new frame has the same size as the old one, but we are
// blending, we can instead just not blend.
should_blend = false;
ppf->frames.emplace_back(std::move(frame.data));
} else if (px0 <= x0 && py0 <= y0 && px0 + pxs >= x0 + xs &&
py0 + pys >= y0 + ys && use_for_next_frame) {
// If the new frame is contained within the old frame, we can pad the
// new frame with zeros and not blend.
PackedImage new_data(pxs, pys, frame.data.format);
memset(new_data.pixels(), 0, new_data.pixels_size);
for (size_t y = 0; y < ys; y++) {
size_t bytes_per_pixel =
PackedImage::BitsPerChannel(new_data.format.data_type) *
new_data.format.num_channels / 8;
memcpy(static_cast<uint8_t*>(new_data.pixels()) +
new_data.stride * (y + y0 - py0) +
bytes_per_pixel * (x0 - px0),
static_cast<const uint8_t*>(frame.data.pixels()) +
frame.data.stride * y,
xs * bytes_per_pixel);
}
x0 = px0;
y0 = py0;
should_blend = false;
ppf->frames.emplace_back(std::move(new_data));
} else {
// If all else fails, insert a dummy blank frame with kReplace.
PackedImage blank(pxs, pys, frame.data.format);
memset(blank.pixels(), 0, blank.pixels_size);
ppf->frames.emplace_back(std::move(blank));
auto& pframe = ppf->frames.back();
pframe.x0 = px0;
pframe.y0 = py0;
pframe.frame_info.duration = 0;
pframe.blend = false;
pframe.use_for_next_frame = true;
ppf->frames.emplace_back(std::move(frame.data));
}
} else {
ppf->frames.emplace_back(std::move(frame.data));
}
auto& pframe = ppf->frames.back();
pframe.x0 = x0;
pframe.y0 = y0;
pframe.frame_info.duration = frame.duration;
pframe.blend = should_blend;
pframe.use_for_next_frame = use_for_next_frame;
if (has_nontrivial_background &&
frame.dispose_op == DISPOSE_OP_BACKGROUND) {
previous_frame_should_be_cleared = true;
} else {
previous_frame_should_be_cleared = false;
}
}
if (ppf->frames.empty()) return JXL_FAILURE("No frames decoded");
ppf->frames.back().frame_info.is_last = true;
return true;
}

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

@ -214,8 +214,8 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
if (ppf->info.have_animation) {
frame->frame_info.duration = gcb.DelayTime;
frame->x0 = image_rect.x0();
frame->y0 = image_rect.y0();
frame->x0 = total_rect.x0();
frame->y0 = total_rect.y0();
if (last_base_was_none) {
replace = true;
}

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

@ -122,8 +122,8 @@ Status EncodeImagePNM(const CodecInOut* io, const ColorEncoding& c_desired,
const ImageBundle* transformed;
JXL_RETURN_IF_ERROR(TransformIfNeeded(
*to_color_transform, c_desired, GetJxlCms(), pool, &store, &transformed));
size_t stride = ib.oriented_xsize() *
(c_desired.Channels() * bits_per_sample) / kBitsPerByte;
size_t bytes_per_sample = floating_point ? 4 : bits_per_sample > 8 ? 2 : 1;
size_t stride = ib.oriented_xsize() * c_desired.Channels() * bytes_per_sample;
PaddedBytes pixels(stride * ib.oriented_ysize());
JXL_RETURN_IF_ERROR(ConvertToExternal(
*transformed, bits_per_sample, floating_point, c_desired.Channels(),

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

@ -19,6 +19,7 @@
#include <vector>
#include "jxl/codestream_header.h"
#include "jxl/encode.h"
#include "jxl/types.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/common.h"
@ -69,7 +70,6 @@ class PackedImage {
JxlPixelFormat format;
size_t pixels_size;
private:
static size_t BitsPerChannel(JxlDataType data_type) {
switch (data_type) {
case JXL_TYPE_BOOLEAN:
@ -89,6 +89,7 @@ class PackedImage {
return 0; // Indicate invalid data type.
}
private:
static size_t CalcStride(const JxlPixelFormat& format, size_t xsize) {
size_t stride = xsize * (BitsPerChannel(format.data_type) *
format.num_channels / jxl::kBitsPerByte);
@ -108,7 +109,7 @@ class PackedImage {
class PackedFrame {
public:
template <typename... Args>
PackedFrame(Args... args) : color(args...) {}
explicit PackedFrame(Args&&... args) : color(std::forward<Args>(args)...) {}
// The Frame metadata.
JxlFrameHeader frame_info = {};
@ -163,6 +164,7 @@ class PackedPixelFile {
std::vector<PackedFrame> frames;
PackedMetadata metadata;
PackedPixelFile() { JxlEncoderInitBasicInfo(&info); };
};
} // namespace extras

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

@ -5,32 +5,21 @@
#include "lib/extras/packed_image_convert.h"
#include <cstdint>
#include "jxl/color_encoding.h"
#include "jxl/types.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/color_management.h"
#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"
namespace jxl {
namespace extras {
namespace {
size_t BitsPerSample(JxlDataType data_type) {
switch (data_type) {
case JXL_TYPE_BOOLEAN:
return 1;
case JXL_TYPE_UINT8:
return 8;
case JXL_TYPE_UINT16:
return 16;
case JXL_TYPE_UINT32:
return 32;
case JXL_TYPE_FLOAT:
return 32;
case JXL_TYPE_FLOAT16:
return 16;
// No default, give compiler error if new type not handled.
}
return 0;
}
} // namespace
Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
ThreadPool* pool, CodecInOut* io) {
const bool has_alpha = ppf.info.alpha_bits != 0;
@ -97,7 +86,8 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
io->frames.clear();
for (const auto& frame : ppf.frames) {
JXL_ASSERT(frame.color.pixels() != nullptr);
size_t frame_bits_per_sample = BitsPerSample(frame.color.format.data_type);
size_t frame_bits_per_sample =
frame.color.BitsPerChannel(frame.color.format.data_type);
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.
@ -134,7 +124,7 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
/*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));
/*float_in=*/float_in, /*align=*/0));
// TODO(deymo): Convert the extra channels. FIXME!
JXL_CHECK(frame.extra_channels.empty());
@ -156,5 +146,112 @@ Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
return true;
}
// Allows converting from internal CodecInOut to external PackedPixelFile
Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
const JxlPixelFormat& pixel_format,
const ColorEncoding& c_desired,
ThreadPool* pool,
PackedPixelFile* ppf) {
const bool has_alpha = io.metadata.m.HasAlpha();
bool alpha_premultiplied = false;
JXL_ASSERT(!io.frames.empty());
if (has_alpha) {
JXL_ASSERT(io.metadata.m.GetAlphaBits() ==
io.metadata.m.bit_depth.bits_per_sample);
const auto* alpha_channel = io.metadata.m.Find(ExtraChannel::kAlpha);
JXL_ASSERT(alpha_channel->bit_depth.exponent_bits_per_sample ==
io.metadata.m.bit_depth.exponent_bits_per_sample);
alpha_premultiplied = alpha_channel->alpha_associated;
}
// Convert the image metadata
ppf->info.xsize = io.metadata.size.xsize();
ppf->info.ysize = io.metadata.size.ysize();
ppf->info.num_color_channels = io.metadata.m.color_encoding.Channels();
ppf->info.bits_per_sample = io.metadata.m.bit_depth.bits_per_sample;
ppf->info.exponent_bits_per_sample =
io.metadata.m.bit_depth.exponent_bits_per_sample;
ppf->info.alpha_bits = io.metadata.m.GetAlphaBits();
ppf->info.alpha_premultiplied = alpha_premultiplied;
ppf->info.uses_original_profile = !io.metadata.m.xyb_encoded;
JXL_ASSERT(0 < io.metadata.m.orientation && io.metadata.m.orientation <= 8);
ppf->info.orientation =
static_cast<JxlOrientation>(io.metadata.m.orientation);
ppf->info.num_color_channels = io.metadata.m.color_encoding.Channels();
// Convert animation metadata
JXL_ASSERT(io.frames.size() == 1 || io.metadata.m.have_animation);
ppf->info.have_animation = io.metadata.m.have_animation;
ppf->info.animation.tps_numerator = io.metadata.m.animation.tps_numerator;
ppf->info.animation.tps_denominator = io.metadata.m.animation.tps_denominator;
ppf->info.animation.num_loops = io.metadata.m.animation.num_loops;
// Convert the color encoding
ppf->icc.assign(c_desired.ICC().begin(), c_desired.ICC().end());
if (ppf->icc.empty()) {
ConvertInternalToExternalColorEncoding(c_desired, &ppf->color_encoding);
}
// Convert the extra blobs
ppf->metadata.exif.assign(io.blobs.exif.begin(), io.blobs.exif.end());
ppf->metadata.iptc.assign(io.blobs.iptc.begin(), io.blobs.iptc.end());
ppf->metadata.jumbf.assign(io.blobs.jumbf.begin(), io.blobs.jumbf.end());
ppf->metadata.xmp.assign(io.blobs.xmp.begin(), io.blobs.xmp.end());
const bool float_out = pixel_format.data_type == JXL_TYPE_FLOAT ||
pixel_format.data_type == JXL_TYPE_FLOAT16;
// 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);
// It is ok for the frame.color().kNumPlanes to not match the
// number of channels on the image.
const uint32_t num_channels =
frame.metadata()->color_encoding.Channels() + has_alpha;
JxlPixelFormat format{/*num_channels=*/num_channels,
/*data_type=*/pixel_format.data_type,
/*endianness=*/pixel_format.endianness,
/*align=*/pixel_format.align};
PackedFrame packed_frame(frame.oriented_xsize(), frame.oriented_ysize(),
format);
const size_t bits_per_sample =
packed_frame.color.BitsPerChannel(pixel_format.data_type);
packed_frame.name = frame.name;
packed_frame.frame_info.name_length = frame.name.size();
// Color transform
ImageBundle ib = frame.Copy();
const ImageBundle* to_color_transform = &ib;
ImageMetadata metadata = io.metadata.m;
ImageBundle store(&metadata);
const ImageBundle* transformed;
// TODO(firsching): handle the transform here.
JXL_RETURN_IF_ERROR(TransformIfNeeded(*to_color_transform, c_desired,
GetJxlCms(), pool, &store,
&transformed));
size_t stride = ib.oriented_xsize() *
(c_desired.Channels() * ppf->info.bits_per_sample) /
kBitsPerByte;
PaddedBytes pixels(stride * ib.oriented_ysize());
JXL_RETURN_IF_ERROR(ConvertToExternal(
*transformed, bits_per_sample, float_out, format.num_channels,
format.endianness,
/* stride_out=*/packed_frame.color.stride, pool,
packed_frame.color.pixels(), packed_frame.color.pixels_size,
/*out_callback=*/nullptr, /*out_opaque=*/nullptr,
frame.metadata()->GetOrientation()));
// TODO(firsching): Convert the extra channels, beside one potential alpha
// channel. FIXME!
JXL_CHECK(frame.extra_channels().size() <= has_alpha);
ppf->frames.push_back(std::move(packed_frame));
}
return true;
}
} // namespace extras
} // namespace jxl

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

@ -9,6 +9,7 @@
// Helper functions to convert from the external image types to the internal
// CodecInOut to help transitioning to the external types.
#include "jxl/types.h"
#include "lib/extras/packed_image.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
@ -21,6 +22,13 @@ namespace extras {
Status ConvertPackedPixelFileToCodecInOut(const PackedPixelFile& ppf,
ThreadPool* pool, CodecInOut* io);
// Converts an internal CodecInOut for use with internal function to an external
// PackedPixelFile.
Status ConvertCodecInOutToPackedPixelFile(const CodecInOut& io,
const JxlPixelFormat& pixel_format,
const ColorEncoding& c_desired,
ThreadPool* pool,
PackedPixelFile* ppf);
} // namespace extras
} // namespace jxl

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

@ -272,6 +272,17 @@ typedef enum {
*/
JXL_ENC_FRAME_SETTING_JPEG_RECON_CFL = 30,
/** Prepare the frame for indexing in the frame index box.
* 0 = ignore this frame (same as not setting a value),
* 1 = index this frame within the Frame Index Box.
* If any frames are indexed, the first frame needs to
* be indexed, too. If the first frame is not indexed, and
* a later frame is attempted to be indexed, JXL_ENC_ERROR will occur.
* If non-keyframes, i.e., frames with cropping, blending or patches are
* attempted to be indexed, JXL_ENC_ERROR will occur.
*/
JXL_ENC_FRAME_INDEX_BOX = 31,
/** Enum value not to be used as an option. This value is added to force the
* C compiler to have the enum to take a known size.
*/
@ -818,7 +829,6 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
* @param type type of the extra channel.
* @param info global extra channel metadata. Object owned by the caller and its
* contents are copied internally.
* @return JXL_ENC_SUCCESS on success, JXL_ENC_ERROR on error
*/
JXL_EXPORT void JxlEncoderInitExtraChannelInfo(JxlExtraChannelType type,
JxlExtraChannelInfo* info);

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

@ -39,6 +39,7 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/base/profiler.h
jxl/base/random.cc
jxl/base/random.h
jxl/base/scope_guard.h
jxl/base/span.h
jxl/base/status.cc
jxl/base/status.h
@ -196,6 +197,8 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/render_pipeline/render_pipeline_stage.h
jxl/render_pipeline/simple_render_pipeline.cc
jxl/render_pipeline/simple_render_pipeline.h
jxl/render_pipeline/stage_blending.cc
jxl/render_pipeline/stage_blending.h
jxl/render_pipeline/stage_chroma_upsampling.cc
jxl/render_pipeline/stage_chroma_upsampling.h
jxl/render_pipeline/stage_epf.cc
@ -208,10 +211,12 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/render_pipeline/stage_patches.h
jxl/render_pipeline/stage_splines.cc
jxl/render_pipeline/stage_splines.h
jxl/render_pipeline/stage_spot.cc
jxl/render_pipeline/stage_spot.h
jxl/render_pipeline/stage_upsampling.cc
jxl/render_pipeline/stage_upsampling.h
jxl/render_pipeline/stage_write_to_ib.cc
jxl/render_pipeline/stage_write_to_ib.h
jxl/render_pipeline/stage_write.cc
jxl/render_pipeline/stage_write.h
jxl/render_pipeline/stage_xyb.cc
jxl/render_pipeline/stage_xyb.h
jxl/render_pipeline/stage_ycbcr.cc
@ -219,6 +224,7 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/render_pipeline/test_render_pipeline_stages.h
jxl/sanitizers.h
jxl/simd_util-inl.h
jxl/size_constraints.h
jxl/splines.cc
jxl/splines.h
jxl/toc.cc

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

@ -23,72 +23,70 @@ namespace jxl {
// square block frequency along the (i + j == const) diagonals is roughly the
// same. For historical reasons, consecutive diagonals are traversed
// in alternating directions - so called "zig-zag" (or "snake") order.
AcStrategy::CoeffOrderAndLut::CoeffOrderAndLut() {
for (size_t s = 0; s < AcStrategy::kNumValidStrategies; s++) {
const AcStrategy acs = AcStrategy::FromRawStrategy(s);
size_t cx = acs.covered_blocks_x();
size_t cy = acs.covered_blocks_y();
CoefficientLayout(&cy, &cx);
JXL_ASSERT((AcStrategy::CoeffOrderAndLut::kOffset[s + 1] -
AcStrategy::CoeffOrderAndLut::kOffset[s]) == cx * cy);
coeff_order_t* JXL_RESTRICT order_start =
order + AcStrategy::CoeffOrderAndLut::kOffset[s] * kDCTBlockSize;
coeff_order_t* JXL_RESTRICT lut_start =
lut + AcStrategy::CoeffOrderAndLut::kOffset[s] * kDCTBlockSize;
template <bool is_lut>
static void CoeffOrderAndLut(AcStrategy acs, coeff_order_t* out) {
size_t cx = acs.covered_blocks_x();
size_t cy = acs.covered_blocks_y();
CoefficientLayout(&cy, &cx);
// CoefficientLayout ensures cx >= cy.
// We compute the zigzag order for a cx x cx block, then discard all the
// lines that are not multiple of the ratio between cx and cy.
size_t xs = cx / cy;
size_t xsm = xs - 1;
size_t xss = CeilLog2Nonzero(xs);
// First half of the block
size_t cur = cx * cy;
for (size_t i = 0; i < cx * kBlockDim; i++) {
for (size_t j = 0; j <= i; j++) {
size_t x = j;
size_t y = i - j;
if (i % 2) std::swap(x, y);
if ((y & xsm) != 0) continue;
y >>= xss;
size_t val = 0;
if (x < cx && y < cy) {
val = y * cx + x;
} else {
val = cur++;
}
lut_start[y * cx * kBlockDim + x] = val;
order_start[val] = y * cx * kBlockDim + x;
// CoefficientLayout ensures cx >= cy.
// We compute the zigzag order for a cx x cx block, then discard all the
// lines that are not multiple of the ratio between cx and cy.
size_t xs = cx / cy;
size_t xsm = xs - 1;
size_t xss = CeilLog2Nonzero(xs);
// First half of the block
size_t cur = cx * cy;
for (size_t i = 0; i < cx * kBlockDim; i++) {
for (size_t j = 0; j <= i; j++) {
size_t x = j;
size_t y = i - j;
if (i % 2) std::swap(x, y);
if ((y & xsm) != 0) continue;
y >>= xss;
size_t val = 0;
if (x < cx && y < cy) {
val = y * cx + x;
} else {
val = cur++;
}
if (is_lut) {
out[y * cx * kBlockDim + x] = val;
} else {
out[val] = y * cx * kBlockDim + x;
}
}
// Second half
for (size_t ip = cx * kBlockDim - 1; ip > 0; ip--) {
size_t i = ip - 1;
for (size_t j = 0; j <= i; j++) {
size_t x = cx * kBlockDim - 1 - (i - j);
size_t y = cx * kBlockDim - 1 - j;
if (i % 2) std::swap(x, y);
if ((y & xsm) != 0) continue;
y >>= xss;
size_t val = cur++;
lut_start[y * cx * kBlockDim + x] = val;
order_start[val] = y * cx * kBlockDim + x;
}
// Second half
for (size_t ip = cx * kBlockDim - 1; ip > 0; ip--) {
size_t i = ip - 1;
for (size_t j = 0; j <= i; j++) {
size_t x = cx * kBlockDim - 1 - (i - j);
size_t y = cx * kBlockDim - 1 - j;
if (i % 2) std::swap(x, y);
if ((y & xsm) != 0) continue;
y >>= xss;
size_t val = cur++;
if (is_lut) {
out[y * cx * kBlockDim + x] = val;
} else {
out[val] = y * cx * kBlockDim + x;
}
}
}
}
const AcStrategy::CoeffOrderAndLut* AcStrategy::CoeffOrder() {
static AcStrategy::CoeffOrderAndLut* order =
new AcStrategy::CoeffOrderAndLut();
return order;
void AcStrategy::ComputeNaturalCoeffOrder(coeff_order_t* order) const {
CoeffOrderAndLut</*is_lut=*/false>(*this, order);
}
void AcStrategy::ComputeNaturalCoeffOrderLut(coeff_order_t* lut) const {
CoeffOrderAndLut</*is_lut=*/true>(*this, lut);
}
// These definitions are needed before C++17.
constexpr size_t AcStrategy::kMaxCoeffBlocks;
constexpr size_t AcStrategy::kMaxBlockDim;
constexpr size_t AcStrategy::kMaxCoeffArea;
constexpr size_t AcStrategy::CoeffOrderAndLut::kOffset[];
AcStrategyImage::AcStrategyImage(size_t xsize, size_t ysize)
: layers_(xsize, ysize) {

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

@ -133,15 +133,8 @@ class AcStrategy {
// Round-trip, for any given strategy s:
// X = NaturalCoeffOrder(s)[NaturalCoeffOrderLutN(s)[X]]
// X = NaturalCoeffOrderLut(s)[NaturalCoeffOrderN(s)[X]]
JXL_INLINE const coeff_order_t* NaturalCoeffOrder() const {
return CoeffOrder()->order +
CoeffOrderAndLut::kOffset[RawStrategy()] * kDCTBlockSize;
}
JXL_INLINE const coeff_order_t* NaturalCoeffOrderLut() const {
return CoeffOrder()->lut +
CoeffOrderAndLut::kOffset[RawStrategy()] * kDCTBlockSize;
}
void ComputeNaturalCoeffOrder(coeff_order_t* order) const;
void ComputeNaturalCoeffOrderLut(coeff_order_t* lut) const;
// Number of 8x8 blocks that this strategy will cover. 0 for non-top-left
// blocks inside a multi-block transform.
@ -172,23 +165,6 @@ class AcStrategy {
return kLut[size_t(strategy_)];
}
struct CoeffOrderAndLut {
// Those offsets get multiplied by kDCTBlockSize.
// TODO(veluca): reduce this array by merging together the same order type.
static constexpr size_t kOffset[kNumValidStrategies + 1] = {
0, 1, 2, 3, 4, 8, 24, 26, 28, 32, 36, 44, 52, 53,
54, 55, 56, 57, 58, 122, 154, 186, 442, 570, 698, 1722, 2234, 2746,
};
static constexpr size_t kTotalTableSize =
kOffset[kNumValidStrategies] * kDCTBlockSize;
coeff_order_t order[kTotalTableSize];
coeff_order_t lut[kTotalTableSize];
private:
CoeffOrderAndLut();
friend class AcStrategy;
};
private:
friend class AcStrategyRow;
JXL_INLINE AcStrategy(Type strategy, bool is_first)
@ -198,8 +174,6 @@ class AcStrategy {
Type strategy_;
bool is_first_;
static const CoeffOrderAndLut* CoeffOrder();
};
// Class to use a certain row of the AC strategy.

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

@ -229,7 +229,7 @@ void TestCheckpointing(bool ans, bool lz77) {
ANSSymbolReader reader(&decoded_codes, &br);
ANSSymbolReader::Checkpoint checkpoint;
size_t br_pos;
size_t br_pos = 0;
constexpr size_t kInterval = ANSSymbolReader::kMaxCheckpointInterval - 2;
for (size_t i = 0; i < input_values[0].size(); i++) {
if (i % kInterval == 0 && i > 0) {

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

@ -0,0 +1,48 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_JXL_BASE_SCOPE_GUARD_H_
#define LIB_JXL_BASE_SCOPE_GUARD_H_
#include <utility>
namespace jxl {
template <typename Callback>
class ScopeGuard {
public:
// Discourage unnecessary moves / copies.
ScopeGuard(const ScopeGuard &) = delete;
ScopeGuard &operator=(const ScopeGuard &) = delete;
ScopeGuard &operator=(ScopeGuard &&) = delete;
// Pre-C++17 does not guarantee RVO -> require move constructor.
ScopeGuard(ScopeGuard &&other) : callback_(std::move(other.callback_)) {
other.armed_ = false;
}
template <typename CallbackParam>
ScopeGuard(CallbackParam &&callback)
: callback_(std::forward<CallbackParam>(callback)), armed_(true) {}
~ScopeGuard() {
if (armed_) callback_();
}
void Disarm() { armed_ = false; }
private:
Callback callback_;
bool armed_;
};
template <typename Callback>
ScopeGuard<Callback> MakeScopeGuard(Callback &&callback) {
return {std::forward<Callback>(callback)};
}
} // namespace jxl
#endif // LIB_JXL_BASE_SCOPE_GUARD_H_

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

@ -40,7 +40,7 @@ TEST(BlendingTest, Crops) {
}
TEST(BlendingTest, Offset) {
const PaddedBytes background_bytes = ReadTestData("jxl/splines.png");
const PaddedBytes background_bytes = ReadTestData("jxl/splines.pfm");
CodecInOut background;
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(background_bytes), &background));
const PaddedBytes foreground_bytes =
@ -87,7 +87,7 @@ TEST(BlendingTest, Offset) {
}
const PaddedBytes expected_bytes =
ReadTestData("jxl/blending/grayscale_patches_on_splines.png");
ReadTestData("jxl/blending/grayscale_patches_on_splines.pfm");
CodecInOut expected;
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(expected_bytes), &expected));
VerifyRelativeError(*expected.Main().color(), *output.color(), 1. / (2 * 255),

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

@ -20,6 +20,7 @@
#include "lib/jxl/image.h"
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/luminance.h"
#include "lib/jxl/size_constraints.h"
namespace jxl {
@ -34,14 +35,6 @@ struct CodecInterval {
float width = 1.0f;
};
struct SizeConstraints {
// Upper limit on pixel dimensions/area, enforced by VerifyDimensions
// (called from decoders). Fuzzers set smaller values to limit memory use.
uint32_t dec_max_xsize = 0xFFFFFFFFu;
uint32_t dec_max_ysize = 0xFFFFFFFFu;
uint64_t dec_max_pixels = 0xFFFFFFFFu; // Might be up to ~0ull
};
template <typename T,
class = typename std::enable_if<std::is_unsigned<T>::value>::type>
Status VerifyDimensions(const SizeConstraints* constraints, T xs, T ys) {

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

@ -26,16 +26,6 @@
namespace jxl {
void SetDefaultOrder(AcStrategy acs, coeff_order_t* JXL_RESTRICT order) {
PROFILER_FUNC;
const size_t size =
kDCTBlockSize * acs.covered_blocks_x() * acs.covered_blocks_y();
const coeff_order_t* natural_coeff_order = acs.NaturalCoeffOrder();
for (size_t k = 0; k < size; ++k) {
order[k] = natural_coeff_order[k];
}
}
uint32_t CoeffOrderContext(uint32_t val) {
uint32_t token, nbits, bits;
HybridUintConfig(0, 0, 0).Encode(val, &token, &nbits, &bits);
@ -90,6 +80,7 @@ namespace {
Status DecodeCoeffOrder(AcStrategy acs, coeff_order_t* order, BitReader* br,
ANSSymbolReader* reader,
std::vector<coeff_order_t>& natural_order,
const std::vector<uint8_t>& context_map) {
PROFILER_FUNC;
const size_t llf = acs.covered_blocks_x() * acs.covered_blocks_y();
@ -98,9 +89,8 @@ Status DecodeCoeffOrder(AcStrategy acs, coeff_order_t* order, BitReader* br,
JXL_RETURN_IF_ERROR(
ReadPermutation(llf, size, order, br, reader, context_map));
if (order == nullptr) return true;
const coeff_order_t* natural_coeff_order = acs.NaturalCoeffOrder();
for (size_t k = 0; k < size; ++k) {
order[k] = natural_coeff_order[order[k]];
order[k] = natural_order[order[k]];
}
return true;
}
@ -113,6 +103,7 @@ Status DecodeCoeffOrders(uint16_t used_orders, uint32_t used_acs,
std::vector<uint8_t> context_map;
ANSCode code;
std::unique_ptr<ANSSymbolReader> reader;
std::vector<coeff_order_t> natural_order;
// Bitstream does not have histograms if no coefficient order is used.
if (used_orders != 0) {
JXL_RETURN_IF_ERROR(
@ -130,18 +121,28 @@ Status DecodeCoeffOrders(uint16_t used_orders, uint32_t used_acs,
computed |= 1 << ord;
AcStrategy acs = AcStrategy::FromRawStrategy(o);
bool used = (acs_mask & (1 << ord)) != 0;
const size_t llf = acs.covered_blocks_x() * acs.covered_blocks_y();
const size_t size = kDCTBlockSize * llf;
if (used || (used_orders & (1 << ord))) {
if (natural_order.size() < size) natural_order.resize(size);
acs.ComputeNaturalCoeffOrder(natural_order.data());
}
if ((used_orders & (1 << ord)) == 0) {
// No need to set the default order if no ACS uses this order.
if (used) {
for (size_t c = 0; c < 3; c++) {
SetDefaultOrder(acs, &order[CoeffOrderOffset(ord, c)]);
memcpy(&order[CoeffOrderOffset(ord, c)], natural_order.data(),
size * sizeof(*order));
}
}
} else {
for (size_t c = 0; c < 3; c++) {
coeff_order_t* dest = used ? &order[CoeffOrderOffset(ord, c)] : nullptr;
JXL_RETURN_IF_ERROR(
DecodeCoeffOrder(acs, dest, br, reader.get(), context_map));
JXL_RETURN_IF_ERROR(DecodeCoeffOrder(acs, dest, br, reader.get(),
natural_order, context_map));
}
}
}

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

@ -53,8 +53,6 @@ constexpr uint32_t kPermutationContexts = 8;
uint32_t CoeffOrderContext(uint32_t val);
void SetDefaultOrder(AcStrategy acs, coeff_order_t* JXL_RESTRICT order);
Status DecodeCoeffOrders(uint16_t used_orders, uint32_t used_acs,
coeff_order_t* order, BitReader* br);

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

@ -61,7 +61,7 @@ struct CoeffBundle {
}
}
static void B(float* JXL_RESTRICT coeff) {
auto sqrt2 = Set(FV<SZ>(), square_root<2>::value);
auto sqrt2 = Set(FV<SZ>(), kSqrt2);
auto in1 = Load(FV<SZ>(), coeff);
auto in2 = Load(FV<SZ>(), coeff + SZ);
Store(MulAdd(in1, sqrt2, in2), FV<SZ>(), coeff);
@ -77,7 +77,7 @@ struct CoeffBundle {
auto in2 = Load(FV<SZ>(), coeff + (i - 1) * SZ);
Store(in1 + in2, FV<SZ>(), coeff + i * SZ);
}
auto sqrt2 = Set(FV<SZ>(), square_root<2>::value);
auto sqrt2 = Set(FV<SZ>(), kSqrt2);
auto in1 = Load(FV<SZ>(), coeff);
Store(in1 * sqrt2, FV<SZ>(), coeff);
}

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

@ -11,20 +11,9 @@
#include <stddef.h>
namespace jxl {
template <size_t V>
struct square_root {
static constexpr float value = square_root<V / 4>::value * 2;
};
template <>
struct square_root<1> {
static constexpr float value = 1.0f;
};
template <>
struct square_root<2> {
static constexpr float value = 1.4142135623730951f;
};
static constexpr float kSqrt2 = 1.41421356237f;
static constexpr float kSqrt0_5 = 0.70710678118f;
// For n != 0, the n-th basis function of a N-DCT, evaluated in pixel k, has a
// value of cos((k+1/2) n/(2N) pi). When downsampling by 2x, we average

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

@ -275,6 +275,33 @@ class ANSSymbolReader {
return static_cast<uint32_t>(ret);
}
// Takes a *clustered* idx. Can only use if HuffRleOnly() is true.
void ReadHybridUintClusteredHuffRleOnly(size_t ctx,
BitReader* JXL_RESTRICT br,
uint32_t* value, uint32_t* run) {
JXL_DASSERT(HuffRleOnly());
br->Refill(); // covers ReadSymbolWithoutRefill + PeekBits
size_t token = ReadSymbolHuffWithoutRefill(ctx, br);
if (JXL_UNLIKELY(token >= lz77_threshold_)) {
*run =
ReadHybridUintConfig(lz77_length_uint_, token - lz77_threshold_, br) +
lz77_min_length_ - 1;
return;
}
*value = ReadHybridUintConfig(configs[ctx], token, br);
return;
}
bool HuffRleOnly() {
if (lz77_window_ == nullptr) return false;
if (!use_prefix_code_) return false;
for (size_t i = 0; i < kHuffmanTableBits; i++) {
if (huffman_data_[lz77_ctx_].table_[i].bits) return false;
if (huffman_data_[lz77_ctx_].table_[i].value != 1) return false;
}
if (configs[lz77_ctx_].split_token > 1) return false;
return true;
}
// Takes a *clustered* idx.
size_t ReadHybridUintClustered(size_t ctx, BitReader* JXL_RESTRICT br) {
if (JXL_UNLIKELY(num_to_copy_ > 0)) {
@ -403,9 +430,9 @@ class ANSSymbolReader {
bool use_prefix_code_;
uint32_t state_ = ANS_SIGNATURE << 16u;
const HybridUintConfig* JXL_RESTRICT configs;
uint32_t log_alpha_size_;
uint32_t log_entry_size_;
uint32_t entry_size_minus_1_;
uint32_t log_alpha_size_{};
uint32_t log_entry_size_{};
uint32_t entry_size_minus_1_{};
// LZ77 structures and constants.
static constexpr size_t kWindowMask = kWindowSize - 1;
@ -418,8 +445,8 @@ class ANSSymbolReader {
uint32_t lz77_min_length_ = 0;
uint32_t lz77_threshold_ = 1 << 20; // bigger than any symbol.
HybridUintConfig lz77_length_uint_;
uint32_t special_distances_[kNumSpecialDistances];
uint32_t num_special_distances_;
uint32_t special_distances_[kNumSpecialDistances]{};
uint32_t num_special_distances_{};
};
Status DecodeHistograms(BitReader* br, size_t num_contexts, ANSCode* code,

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

@ -53,14 +53,16 @@
#include "lib/jxl/passes_state.h"
#include "lib/jxl/quant_weights.h"
#include "lib/jxl/quantizer.h"
#include "lib/jxl/render_pipeline/stage_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_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_upsampling.h"
#include "lib/jxl/render_pipeline/stage_write_to_ib.h"
#include "lib/jxl/render_pipeline/stage_write.h"
#include "lib/jxl/render_pipeline/stage_xyb.h"
#include "lib/jxl/render_pipeline/stage_ycbcr.h"
#include "lib/jxl/sanitizers.h"
@ -730,13 +732,13 @@ Status FrameDecoder::ProcessACGroup(size_t ac_group_id,
return true;
}
void FrameDecoder::PreparePipeline() {
Status FrameDecoder::PreparePipeline() {
size_t num_c = 3 + frame_header_.nonserialized_metadata->m.num_extra_channels;
if ((frame_header_.flags & FrameHeader::kNoise) != 0) {
num_c += 3;
}
RenderPipeline::Builder builder(num_c, frame_header_.passes.num_passes);
RenderPipeline::Builder builder(num_c);
if (use_slow_rendering_pipeline_) {
builder.UseSimpleImplementation();
@ -833,36 +835,76 @@ void FrameDecoder::PreparePipeline() {
GetWriteToImageBundleStage(&dec_state_->frame_storage_for_referencing));
}
if (frame_header_.color_transform == ColorTransform::kYCbCr) {
builder.AddStage(GetYCbCrStage());
} else if (frame_header_.color_transform == ColorTransform::kXYB) {
builder.AddStage(GetXYBStage(dec_state_->output_encoding_info));
} // Nothing to do for kNone.
if (ImageBlender::NeedsBlending(dec_state_)) {
JXL_ABORT("Not implemented: blending");
bool has_alpha = false;
size_t alpha_c = 0;
for (size_t i = 0; i < decoded_->metadata()->extra_channel_info.size(); i++) {
if (decoded_->metadata()->extra_channel_info[i].type ==
ExtraChannel::kAlpha) {
has_alpha = true;
alpha_c = 3 + i;
break;
}
}
if (frame_header_.CanBeReferenced() &&
!frame_header_.save_before_color_transform) {
builder.AddStage(
GetWriteToImageBundleStage(&dec_state_->frame_storage_for_referencing));
}
// TODO(veluca): double-check when blending/no coalescing is enabled.
size_t width = coalescing_ ? frame_header_.nonserialized_metadata->xsize()
: frame_dim_.xsize_upsampled;
size_t height = coalescing_ ? frame_header_.nonserialized_metadata->ysize()
: frame_dim_.ysize_upsampled;
if (render_spotcolors_ &&
frame_header_.nonserialized_metadata->m.Find(ExtraChannel::kSpotColor)) {
JXL_ABORT("Not implemented: rendering spot colors");
}
if (dec_state_->pixel_callback) {
JXL_ABORT("Not implemented: pixel callback");
} else if (dec_state_->fast_xyb_srgb8_conversion) {
JXL_ABORT("Not implemented: fast xyb->srgb conversion");
} else if (dec_state_->rgb_output) {
JXL_ABORT("Not implemented: u8 output");
if (dec_state_->fast_xyb_srgb8_conversion) {
JXL_ASSERT(!ImageBlender::NeedsBlending(dec_state_));
JXL_ASSERT(!frame_header_.CanBeReferenced() ||
frame_header_.save_before_color_transform);
JXL_ASSERT(!render_spotcolors_);
builder.AddStage(GetFastXYBTosRGB8Stage(
dec_state_->rgb_output, dec_state_->rgb_stride, width, height,
dec_state_->rgb_output_is_rgba, has_alpha, alpha_c));
} else {
builder.AddStage(GetWriteToImageBundleStage(decoded_));
if (frame_header_.color_transform == ColorTransform::kYCbCr) {
builder.AddStage(GetYCbCrStage());
} else if (frame_header_.color_transform == ColorTransform::kXYB) {
builder.AddStage(GetXYBStage(dec_state_->output_encoding_info));
} // Nothing to do for kNone.
if (ImageBlender::NeedsBlending(dec_state_)) {
builder.AddStage(GetBlendingStage(
dec_state_, dec_state_->output_encoding_info.color_encoding));
}
if (frame_header_.CanBeReferenced() &&
!frame_header_.save_before_color_transform) {
builder.AddStage(GetWriteToImageBundleStage(
&dec_state_->frame_storage_for_referencing));
}
if (render_spotcolors_ && frame_header_.nonserialized_metadata->m.Find(
ExtraChannel::kSpotColor)) {
for (size_t i = 0; i < decoded_->metadata()->extra_channel_info.size();
i++) {
// Don't use Find() because there may be multiple spot color channels.
const ExtraChannelInfo& eci =
decoded_->metadata()->extra_channel_info[i];
if (eci.type == ExtraChannel::kSpotColor) {
builder.AddStage(GetSpotColorStage(3 + i, eci.spot_color));
}
}
}
if (dec_state_->pixel_callback) {
builder.AddStage(GetWriteToPixelCallbackStage(
dec_state_->pixel_callback, width, height,
dec_state_->rgb_output_is_rgba, has_alpha, alpha_c));
} else if (dec_state_->rgb_output) {
builder.AddStage(GetWriteToU8Stage(
dec_state_->rgb_output, dec_state_->rgb_stride, width, height,
dec_state_->rgb_output_is_rgba, has_alpha, alpha_c));
} else {
builder.AddStage(GetWriteToImageBundleStage(decoded_));
}
}
dec_state_->render_pipeline = std::move(builder).Finalize(frame_dim_);
return dec_state_->render_pipeline->IsInitialized();
}
void FrameDecoder::MarkSections(const SectionInfo* sections, size_t num,
@ -971,7 +1013,7 @@ Status FrameDecoder::ProcessSections(const SectionInfo* sections, size_t num,
if (*std::min_element(decoded_dc_groups_.begin(), decoded_dc_groups_.end()) &&
!finalized_dc_) {
if (use_slow_rendering_pipeline_) {
PreparePipeline();
JXL_RETURN_IF_ERROR(PreparePipeline());
}
FinalizeDC();
JXL_RETURN_IF_ERROR(AllocateOutput());
@ -1298,13 +1340,6 @@ Status FrameDecoder::FinalizeFrame() {
if (eci.type == ExtraChannel::kOptional) {
continue;
}
if (eci.type == ExtraChannel::kUnknown ||
(int(ExtraChannel::kReserved0) <= int(eci.type) &&
int(eci.type) <= int(ExtraChannel::kReserved7))) {
return JXL_FAILURE(
"Unknown extra channel (bits %u, shift %u, name '%s')\n",
eci.bit_depth.bits_per_sample, eci.dim_shift, eci.name.c_str());
}
if (eci.type == ExtraChannel::kSpotColor) {
float scale = eci.spot_color[3];
for (size_t c = 0; c < 3; c++) {

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

@ -209,7 +209,7 @@ class FrameDecoder {
Status ProcessDCGroup(size_t dc_group_id, BitReader* br);
void FinalizeDC();
Status AllocateOutput();
void PreparePipeline();
Status PreparePipeline();
Status ProcessACGlobal(BitReader* br);
Status ProcessACGroup(size_t ac_group_id, BitReader* JXL_RESTRICT* br,
size_t num_passes, size_t thread, bool force_draw,

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

@ -259,6 +259,8 @@ 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()}));
}
for (size_t by = 0; by < ysize_blocks; ++by) {

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

@ -100,14 +100,18 @@ void GroupBorderAssigner::GroupDone(size_t group_id, size_t padding,
// of border of this group (on the other side), end of border of next group.
size_t xpos[4] = {
block_rect.x0() == 0 ? 0 : block_rect.x0() * kBlockDim - padx,
block_rect.x0() == 0 ? 0 : block_rect.x0() * kBlockDim + padx,
block_rect.x0() == 0 ? 0
: std::min(frame_dim_.xsize_padded,
block_rect.x0() * kBlockDim + padx),
is_last_group_x ? frame_dim_.xsize_padded : x1 * kBlockDim - padx,
is_last_group_x ? frame_dim_.xsize_padded : x1 * kBlockDim + padx};
std::min(frame_dim_.xsize_padded, x1 * kBlockDim + padx)};
size_t ypos[4] = {
block_rect.y0() == 0 ? 0 : block_rect.y0() * kBlockDim - pady,
block_rect.y0() == 0 ? 0 : block_rect.y0() * kBlockDim + pady,
block_rect.y0() == 0 ? 0
: std::min(frame_dim_.ysize_padded,
block_rect.y0() * kBlockDim + pady),
is_last_group_y ? frame_dim_.ysize_padded : y1 * kBlockDim - pady,
is_last_group_y ? frame_dim_.ysize_padded : y1 * kBlockDim + pady};
std::min(frame_dim_.ysize_padded, y1 * kBlockDim + pady)};
*num_to_finalize = 0;
auto append_rect = [&](size_t x0, size_t x1, size_t y0, size_t y1) {

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

@ -1075,15 +1075,28 @@ Status FinalizeImageRect(
// the frame is not supposed to be displayed.
if (dec_state->fast_xyb_srgb8_conversion) {
FastXYBTosRGB8(
*output_pixel_data_storage,
upsampled_frame_rect_for_storage.Lines(available_y, num_ys),
upsampled_frame_rect.Lines(available_y, num_ys)
.Crop(Rect(0, 0, frame_dim.xsize_upsampled,
frame_dim.ysize_upsampled)),
alpha, alpha_rect.Lines(available_y, num_ys),
dec_state->rgb_output_is_rgba, dec_state->rgb_output, frame_dim.xsize,
dec_state->rgb_stride);
Rect output_rect = upsampled_frame_rect.Lines(available_y, num_ys)
.Crop(Rect(0, 0, frame_dim.xsize_upsampled,
frame_dim.ysize_upsampled));
for (size_t iy = 0; iy < output_rect.ysize(); iy++) {
const float* xyba[4] = {
upsampled_frame_rect_for_storage.ConstPlaneRow(
*output_pixel_data_storage, 0, available_y + iy),
upsampled_frame_rect_for_storage.ConstPlaneRow(
*output_pixel_data_storage, 1, available_y + iy),
upsampled_frame_rect_for_storage.ConstPlaneRow(
*output_pixel_data_storage, 2, available_y + iy),
nullptr};
if (alpha) {
xyba[3] = alpha_rect.ConstRow(*alpha, available_y + iy);
}
uint8_t* out_buf =
dec_state->rgb_output +
(iy + output_rect.y0()) * dec_state->rgb_stride +
(dec_state->rgb_output_is_rgba ? 4 : 3) * output_rect.x0();
FastXYBTosRGB8(xyba, out_buf, dec_state->rgb_output_is_rgba,
output_rect.xsize());
}
} else {
if (frame_header.needs_color_transform()) {
if (frame_header.color_transform == ColorTransform::kXYB) {
@ -1295,9 +1308,10 @@ Status FinalizeFrameDecoding(ImageBundle* decoded,
&decoded->extra_channels(), std::move(extra_channels_rects)));
std::vector<Rect> rects_to_process;
for (size_t y = 0; y < frame_dim.ysize; y += kGroupDim) {
for (size_t x = 0; x < frame_dim.xsize; x += kGroupDim) {
Rect rect(x, y, kGroupDim, kGroupDim, frame_dim.xsize, frame_dim.ysize);
for (size_t y = 0; y < frame_dim.ysize_upsampled; y += kGroupDim) {
for (size_t x = 0; x < frame_dim.xsize_upsampled; x += kGroupDim) {
Rect rect(x, y, kGroupDim, kGroupDim, frame_dim.xsize_upsampled,
frame_dim.ysize_upsampled);
if (rect.xsize() == 0 || rect.ysize() == 0) continue;
rects_to_process.push_back(rect);
}

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

@ -86,10 +86,9 @@ static inline HWY_MAYBE_UNUSED bool HasFastXYBTosRGB8() {
#endif
}
static inline HWY_MAYBE_UNUSED void FastXYBTosRGB8(
const Image3F& input, const Rect& input_rect, const Rect& output_buf_rect,
const ImageF* alpha, const Rect& alpha_rect, bool is_rgba,
uint8_t* JXL_RESTRICT output_buf, size_t xsize, size_t output_stride) {
static inline HWY_MAYBE_UNUSED void FastXYBTosRGB8(const float* input[4],
uint8_t* output,
bool is_rgba, size_t xsize) {
// This function is very NEON-specific. As such, it uses intrinsics directly.
#if HWY_TARGET == HWY_NEON
// WARNING: doing fixed point arithmetic correctly is very complicated.
@ -173,170 +172,161 @@ static inline HWY_MAYBE_UNUSED void FastXYBTosRGB8(
// > 0.0031308f (note that v16 has 13 mantissa bits)
return vbslq_s16(vcgeq_s16(v16, vdupq_n_s16(26)), v16_pow, v16_linear);
};
for (size_t y = 0; y < output_buf_rect.ysize(); y++) {
const float* JXL_RESTRICT row_in_x = input_rect.ConstPlaneRow(input, 0, y);
const float* JXL_RESTRICT row_in_y = input_rect.ConstPlaneRow(input, 1, y);
const float* JXL_RESTRICT row_in_b = input_rect.ConstPlaneRow(input, 2, y);
const float* JXL_RESTRICT row_in_a =
alpha == nullptr ? nullptr : alpha_rect.ConstRow(*alpha, y);
size_t cnt = !is_rgba ? 3 : 4;
size_t base_ptr =
(y + output_buf_rect.y0()) * output_stride + output_buf_rect.x0() * cnt;
for (size_t x = 0; x < output_buf_rect.xsize(); x += 8) {
// Normal ranges for xyb for in-gamut sRGB colors:
// x: -0.015386 0.028100
// y: 0.000000 0.845308
// b: 0.000000 0.845308
// We actually want x * 8 to have some extra precision.
// TODO(veluca): consider different approaches here, like vld1q_f32_x2.
float32x4_t opsin_x_left = vld1q_f32(row_in_x + x);
int16x4_t opsin_x16_times8_left =
vqmovn_s32(vcvtq_n_s32_f32(opsin_x_left, 18));
float32x4_t opsin_x_right =
vld1q_f32(row_in_x + x + (x + 4 < output_buf_rect.xsize() ? 4 : 0));
int16x4_t opsin_x16_times8_right =
vqmovn_s32(vcvtq_n_s32_f32(opsin_x_right, 18));
int16x8_t opsin_x16_times8 =
vcombine_s16(opsin_x16_times8_left, opsin_x16_times8_right);
const float* JXL_RESTRICT row_in_x = input[0];
const float* JXL_RESTRICT row_in_y = input[1];
const float* JXL_RESTRICT row_in_b = input[2];
const float* JXL_RESTRICT row_in_a = input[3];
for (size_t x = 0; x < xsize; x += 8) {
// Normal ranges for xyb for in-gamut sRGB colors:
// x: -0.015386 0.028100
// y: 0.000000 0.845308
// b: 0.000000 0.845308
float32x4_t opsin_y_left = vld1q_f32(row_in_y + x);
int16x4_t opsin_y16_left = vqmovn_s32(vcvtq_n_s32_f32(opsin_y_left, 15));
float32x4_t opsin_y_right =
vld1q_f32(row_in_y + x + (x + 4 < output_buf_rect.xsize() ? 4 : 0));
int16x4_t opsin_y16_right =
vqmovn_s32(vcvtq_n_s32_f32(opsin_y_right, 15));
int16x8_t opsin_y16 = vcombine_s16(opsin_y16_left, opsin_y16_right);
// We actually want x * 8 to have some extra precision.
// TODO(veluca): consider different approaches here, like vld1q_f32_x2.
float32x4_t opsin_x_left = vld1q_f32(row_in_x + x);
int16x4_t opsin_x16_times8_left =
vqmovn_s32(vcvtq_n_s32_f32(opsin_x_left, 18));
float32x4_t opsin_x_right =
vld1q_f32(row_in_x + x + (x + 4 < xsize ? 4 : 0));
int16x4_t opsin_x16_times8_right =
vqmovn_s32(vcvtq_n_s32_f32(opsin_x_right, 18));
int16x8_t opsin_x16_times8 =
vcombine_s16(opsin_x16_times8_left, opsin_x16_times8_right);
float32x4_t opsin_b_left = vld1q_f32(row_in_b + x);
int16x4_t opsin_b16_left = vqmovn_s32(vcvtq_n_s32_f32(opsin_b_left, 15));
float32x4_t opsin_b_right =
vld1q_f32(row_in_b + x + (x + 4 < output_buf_rect.xsize() ? 4 : 0));
int16x4_t opsin_b16_right =
vqmovn_s32(vcvtq_n_s32_f32(opsin_b_right, 15));
int16x8_t opsin_b16 = vcombine_s16(opsin_b16_left, opsin_b16_right);
float32x4_t opsin_y_left = vld1q_f32(row_in_y + x);
int16x4_t opsin_y16_left = vqmovn_s32(vcvtq_n_s32_f32(opsin_y_left, 15));
float32x4_t opsin_y_right =
vld1q_f32(row_in_y + x + (x + 4 < xsize ? 4 : 0));
int16x4_t opsin_y16_right = vqmovn_s32(vcvtq_n_s32_f32(opsin_y_right, 15));
int16x8_t opsin_y16 = vcombine_s16(opsin_y16_left, opsin_y16_right);
int16x8_t neg_bias16 = vdupq_n_s16(-124); // -0.0037930732552754493
int16x8_t neg_bias_cbrt16 = vdupq_n_s16(-5110); // -0.155954201
int16x8_t neg_bias_half16 = vdupq_n_s16(-62);
float32x4_t opsin_b_left = vld1q_f32(row_in_b + x);
int16x4_t opsin_b16_left = vqmovn_s32(vcvtq_n_s32_f32(opsin_b_left, 15));
float32x4_t opsin_b_right =
vld1q_f32(row_in_b + x + (x + 4 < xsize ? 4 : 0));
int16x4_t opsin_b16_right = vqmovn_s32(vcvtq_n_s32_f32(opsin_b_right, 15));
int16x8_t opsin_b16 = vcombine_s16(opsin_b16_left, opsin_b16_right);
// Color space: XYB -> RGB
// Compute ((y+x-bias_cbrt)^3-(y-x-bias_cbrt)^3)/2,
// ((y+x-bias_cbrt)^3+(y-x-bias_cbrt)^3)/2+bias, (b-bias_cbrt)^3+bias.
// Note that ignoring x2 in the formulas below (as x << y) results in
// errors of at least 3 in the final sRGB values.
int16x8_t opsin_yp16 = vqsubq_s16(opsin_y16, neg_bias_cbrt16);
int16x8_t ysq16 = vqrdmulhq_s16(opsin_yp16, opsin_yp16);
int16x8_t twentyfourx16 = vmulq_n_s16(opsin_x16_times8, 3);
int16x8_t twentyfourxy16 = vqrdmulhq_s16(opsin_yp16, twentyfourx16);
int16x8_t threexsq16 =
vrshrq_n_s16(vqrdmulhq_s16(opsin_x16_times8, twentyfourx16), 6);
int16x8_t neg_bias16 = vdupq_n_s16(-124); // -0.0037930732552754493
int16x8_t neg_bias_cbrt16 = vdupq_n_s16(-5110); // -0.155954201
int16x8_t neg_bias_half16 = vdupq_n_s16(-62);
// We can ignore x^3 here. Note that this is multiplied by 8.
int16x8_t mixed_rmg16 = vqrdmulhq_s16(twentyfourxy16, opsin_yp16);
// Color space: XYB -> RGB
// Compute ((y+x-bias_cbrt)^3-(y-x-bias_cbrt)^3)/2,
// ((y+x-bias_cbrt)^3+(y-x-bias_cbrt)^3)/2+bias, (b-bias_cbrt)^3+bias.
// Note that ignoring x2 in the formulas below (as x << y) results in
// errors of at least 3 in the final sRGB values.
int16x8_t opsin_yp16 = vqsubq_s16(opsin_y16, neg_bias_cbrt16);
int16x8_t ysq16 = vqrdmulhq_s16(opsin_yp16, opsin_yp16);
int16x8_t twentyfourx16 = vmulq_n_s16(opsin_x16_times8, 3);
int16x8_t twentyfourxy16 = vqrdmulhq_s16(opsin_yp16, twentyfourx16);
int16x8_t threexsq16 =
vrshrq_n_s16(vqrdmulhq_s16(opsin_x16_times8, twentyfourx16), 6);
int16x8_t mixed_rpg_sos_half = vhaddq_s16(ysq16, threexsq16);
int16x8_t mixed_rpg16 = vhaddq_s16(
vqrdmulhq_s16(opsin_yp16, mixed_rpg_sos_half), neg_bias_half16);
// We can ignore x^3 here. Note that this is multiplied by 8.
int16x8_t mixed_rmg16 = vqrdmulhq_s16(twentyfourxy16, opsin_yp16);
int16x8_t gamma_b16 = vqsubq_s16(opsin_b16, neg_bias_cbrt16);
int16x8_t gamma_bsq16 = vqrdmulhq_s16(gamma_b16, gamma_b16);
int16x8_t gamma_bcb16 = vqrdmulhq_s16(gamma_bsq16, gamma_b16);
int16x8_t mixed_b16 = vqaddq_s16(gamma_bcb16, neg_bias16);
// mixed_rpg and mixed_b are in 0-1 range.
// mixed_rmg has a smaller range (-0.035 to 0.035 for valid sRGB). Note
// that at this point it is already multiplied by 8.
int16x8_t mixed_rpg_sos_half = vhaddq_s16(ysq16, threexsq16);
int16x8_t mixed_rpg16 = vhaddq_s16(
vqrdmulhq_s16(opsin_yp16, mixed_rpg_sos_half), neg_bias_half16);
// We multiply all the mixed values by 1/4 (i.e. shift them to 13-bit
// fixed point) to ensure intermediate quantities are in range. Note that
// r-g is not shifted, and was x8 before here; this corresponds to a x32
// overall multiplicative factor and ensures that all the matrix constants
// are in 0-1 range.
// Similarly, mixed_rpg16 is already multiplied by 1/4 because of the two
// vhadd + using neg_bias_half.
mixed_b16 = vshrq_n_s16(mixed_b16, 2);
int16x8_t gamma_b16 = vqsubq_s16(opsin_b16, neg_bias_cbrt16);
int16x8_t gamma_bsq16 = vqrdmulhq_s16(gamma_b16, gamma_b16);
int16x8_t gamma_bcb16 = vqrdmulhq_s16(gamma_bsq16, gamma_b16);
int16x8_t mixed_b16 = vqaddq_s16(gamma_bcb16, neg_bias16);
// mixed_rpg and mixed_b are in 0-1 range.
// mixed_rmg has a smaller range (-0.035 to 0.035 for valid sRGB). Note
// that at this point it is already multiplied by 8.
// Unmix (multiply by 3x3 inverse_matrix)
// For increased precision, we use a matrix for converting from
// ((mixed_r - mixed_g)/2, (mixed_r + mixed_g)/2, mixed_b) to rgb. This
// avoids cancellation effects when computing (y+x)^3-(y-x)^3.
// We compute mixed_rpg - mixed_b because the (1+c)*mixed_rpg - c *
// mixed_b pattern is repeated frequently in the code below. This allows
// us to save a multiply per channel, and removes the presence of
// some constants above 1. Moreover, mixed_rmg - mixed_b is in (-1, 1)
// range, so the subtraction is safe.
// All the magic-looking constants here are derived by computing the
// inverse opsin matrix for the transformation modified as described
// above.
// We multiply all the mixed values by 1/4 (i.e. shift them to 13-bit
// fixed point) to ensure intermediate quantities are in range. Note that
// r-g is not shifted, and was x8 before here; this corresponds to a x32
// overall multiplicative factor and ensures that all the matrix constants
// are in 0-1 range.
// Similarly, mixed_rpg16 is already multiplied by 1/4 because of the two
// vhadd + using neg_bias_half.
mixed_b16 = vshrq_n_s16(mixed_b16, 2);
// Precomputation common to multiple color values.
int16x8_t mixed_rpgmb16 = vqsubq_s16(mixed_rpg16, mixed_b16);
int16x8_t mixed_rpgmb_times_016 = vqrdmulhq_n_s16(mixed_rpgmb16, 5394);
int16x8_t mixed_rg16 = vqaddq_s16(mixed_rpgmb_times_016, mixed_rpg16);
// Unmix (multiply by 3x3 inverse_matrix)
// For increased precision, we use a matrix for converting from
// ((mixed_r - mixed_g)/2, (mixed_r + mixed_g)/2, mixed_b) to rgb. This
// avoids cancellation effects when computing (y+x)^3-(y-x)^3.
// We compute mixed_rpg - mixed_b because the (1+c)*mixed_rpg - c *
// mixed_b pattern is repeated frequently in the code below. This allows
// us to save a multiply per channel, and removes the presence of
// some constants above 1. Moreover, mixed_rmg - mixed_b is in (-1, 1)
// range, so the subtraction is safe.
// All the magic-looking constants here are derived by computing the
// inverse opsin matrix for the transformation modified as described
// above.
// R
int16x8_t linear_r16 =
vqaddq_s16(mixed_rg16, vqrdmulhq_n_s16(mixed_rmg16, 21400));
// Precomputation common to multiple color values.
int16x8_t mixed_rpgmb16 = vqsubq_s16(mixed_rpg16, mixed_b16);
int16x8_t mixed_rpgmb_times_016 = vqrdmulhq_n_s16(mixed_rpgmb16, 5394);
int16x8_t mixed_rg16 = vqaddq_s16(mixed_rpgmb_times_016, mixed_rpg16);
// G
int16x8_t linear_g16 =
vqaddq_s16(mixed_rg16, vqrdmulhq_n_s16(mixed_rmg16, -7857));
// R
int16x8_t linear_r16 =
vqaddq_s16(mixed_rg16, vqrdmulhq_n_s16(mixed_rmg16, 21400));
// B
int16x8_t linear_b16 = vqrdmulhq_n_s16(mixed_rpgmb16, -30996);
linear_b16 = vqaddq_s16(linear_b16, mixed_b16);
linear_b16 = vqaddq_s16(linear_b16, vqrdmulhq_n_s16(mixed_rmg16, -6525));
// G
int16x8_t linear_g16 =
vqaddq_s16(mixed_rg16, vqrdmulhq_n_s16(mixed_rmg16, -7857));
// Apply SRGB transfer function.
int16x8_t r = srgb_tf(linear_r16);
int16x8_t g = srgb_tf(linear_g16);
int16x8_t b = srgb_tf(linear_b16);
// B
int16x8_t linear_b16 = vqrdmulhq_n_s16(mixed_rpgmb16, -30996);
linear_b16 = vqaddq_s16(linear_b16, mixed_b16);
linear_b16 = vqaddq_s16(linear_b16, vqrdmulhq_n_s16(mixed_rmg16, -6525));
uint8x8_t r8 =
vqmovun_s16(vrshrq_n_s16(vsubq_s16(r, vshrq_n_s16(r, 8)), 6));
uint8x8_t g8 =
vqmovun_s16(vrshrq_n_s16(vsubq_s16(g, vshrq_n_s16(g, 8)), 6));
uint8x8_t b8 =
vqmovun_s16(vrshrq_n_s16(vsubq_s16(b, vshrq_n_s16(b, 8)), 6));
// Apply SRGB transfer function.
int16x8_t r = srgb_tf(linear_r16);
int16x8_t g = srgb_tf(linear_g16);
int16x8_t b = srgb_tf(linear_b16);
size_t n = output_buf_rect.xsize() - x;
if (is_rgba) {
float32x4_t a_f32_left =
row_in_a ? vld1q_f32(row_in_a + x) : vdupq_n_f32(1.0f);
float32x4_t a_f32_right =
row_in_a ? vld1q_f32(row_in_a + x +
(x + 4 < output_buf_rect.xsize() ? 4 : 0))
: vdupq_n_f32(1.0f);
int16x4_t a16_left = vqmovn_s32(vcvtq_n_s32_f32(a_f32_left, 8));
int16x4_t a16_right = vqmovn_s32(vcvtq_n_s32_f32(a_f32_right, 8));
uint8x8_t a8 = vqmovun_s16(vcombine_s16(a16_left, a16_right));
uint8_t* buf = output_buf + base_ptr + 4 * x;
uint8x8x4_t data = {r8, g8, b8, a8};
if (n >= 8) {
vst4_u8(buf, data);
} else {
uint8_t tmp[8 * 4];
vst4_u8(tmp, data);
memcpy(buf, tmp, n * 4);
}
uint8x8_t r8 =
vqmovun_s16(vrshrq_n_s16(vsubq_s16(r, vshrq_n_s16(r, 8)), 6));
uint8x8_t g8 =
vqmovun_s16(vrshrq_n_s16(vsubq_s16(g, vshrq_n_s16(g, 8)), 6));
uint8x8_t b8 =
vqmovun_s16(vrshrq_n_s16(vsubq_s16(b, vshrq_n_s16(b, 8)), 6));
size_t n = xsize - x;
if (is_rgba) {
float32x4_t a_f32_left =
row_in_a ? vld1q_f32(row_in_a + x) : vdupq_n_f32(1.0f);
float32x4_t a_f32_right =
row_in_a ? vld1q_f32(row_in_a + x + (x + 4 < xsize ? 4 : 0))
: vdupq_n_f32(1.0f);
int16x4_t a16_left = vqmovn_s32(vcvtq_n_s32_f32(a_f32_left, 8));
int16x4_t a16_right = vqmovn_s32(vcvtq_n_s32_f32(a_f32_right, 8));
uint8x8_t a8 = vqmovun_s16(vcombine_s16(a16_left, a16_right));
uint8_t* buf = output + 4 * x;
uint8x8x4_t data = {r8, g8, b8, a8};
if (n >= 8) {
vst4_u8(buf, data);
} else {
uint8_t* buf = output_buf + base_ptr + 3 * x;
uint8x8x3_t data = {r8, g8, b8};
if (n >= 8) {
vst3_u8(buf, data);
} else {
uint8_t tmp[8 * 3];
vst3_u8(tmp, data);
memcpy(buf, tmp, n * 3);
}
uint8_t tmp[8 * 4];
vst4_u8(tmp, data);
memcpy(buf, tmp, n * 4);
}
} else {
uint8_t* buf = output + 3 * x;
uint8x8x3_t data = {r8, g8, b8};
if (n >= 8) {
vst3_u8(buf, data);
} else {
uint8_t tmp[8 * 3];
vst3_u8(tmp, data);
memcpy(buf, tmp, n * 3);
}
}
}
#else
(void)input;
(void)input_rect;
(void)output_buf_rect;
(void)output_buf;
(void)output;
(void)is_rgba;
(void)xsize;
JXL_ABORT("Unreachable");
#endif

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

@ -180,14 +180,9 @@ HWY_EXPORT(HasFastXYBTosRGB8);
bool HasFastXYBTosRGB8() { return HWY_DYNAMIC_DISPATCH(HasFastXYBTosRGB8)(); }
HWY_EXPORT(FastXYBTosRGB8);
void FastXYBTosRGB8(const Image3F& input, const Rect& input_rect,
const Rect& output_buf_rect, const ImageF* alpha,
const Rect& alpha_rect, bool is_rgba,
uint8_t* JXL_RESTRICT output_buf, size_t xsize,
size_t output_stride) {
return HWY_DYNAMIC_DISPATCH(FastXYBTosRGB8)(
input, input_rect, output_buf_rect, alpha, alpha_rect, is_rgba,
output_buf, xsize, output_stride);
void FastXYBTosRGB8(const float* input[4], uint8_t* output, bool is_rgba,
size_t xsize) {
return HWY_DYNAMIC_DISPATCH(FastXYBTosRGB8)(input, output, is_rgba, xsize);
}
void OpsinParams::Init(float intensity_target) {

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

@ -65,11 +65,8 @@ void OpsinToLinear(const Image3F& opsin, const Rect& rect, ThreadPool* pool,
void YcbcrToRgb(const Image3F& ycbcr, Image3F* rgb, const Rect& rect);
bool HasFastXYBTosRGB8();
void FastXYBTosRGB8(const Image3F& input, const Rect& input_rect,
const Rect& output_buf_rect, const ImageF* alpha,
const Rect& alpha_rect, bool is_rgba,
uint8_t* JXL_RESTRICT output_buf, size_t xsize,
size_t output_stride);
void FastXYBTosRGB8(const float* input[4], uint8_t* output, bool is_rgba,
size_t xsize);
} // namespace jxl

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

@ -850,8 +850,9 @@ void GetCurrentDimensions(const JxlDecoder* dec, size_t& xsize, size_t& ysize,
xsize = dec->metadata.oriented_xsize(dec->keep_orientation || !oriented);
ysize = dec->metadata.oriented_ysize(dec->keep_orientation || !oriented);
if (!dec->coalescing) {
xsize = dec->frame_header->ToFrameDimensions().xsize;
ysize = dec->frame_header->ToFrameDimensions().ysize;
const auto frame_dim = dec->frame_header->ToFrameDimensions();
xsize = frame_dim.xsize_upsampled;
ysize = frame_dim.ysize_upsampled;
if (!dec->keep_orientation && oriented &&
static_cast<int>(dec->metadata.m.GetOrientation()) > 4) {
std::swap(xsize, ysize);
@ -2671,9 +2672,11 @@ JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec,
if (!dec->coalescing && dec->frame_header->custom_size_or_origin) {
header->layer_info.crop_x0 = dec->frame_header->frame_origin.x0;
header->layer_info.crop_y0 = dec->frame_header->frame_origin.y0;
header->layer_info.have_crop = JXL_TRUE;
} else {
header->layer_info.crop_x0 = 0;
header->layer_info.crop_y0 = 0;
header->layer_info.have_crop = JXL_FALSE;
}
if (!dec->keep_orientation && !dec->coalescing) {
// orient the crop offset

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

@ -198,8 +198,8 @@ PaddedBytes CreateTestJXLCodestream(
io.metadata.m.color_encoding = color_encoding;
EXPECT_TRUE(ConvertFromExternal(
pixels, xsize, ysize, color_encoding, /*has_alpha=*/include_alpha,
/*alpha_is_premultiplied=*/false, bitdepth, JXL_BIG_ENDIAN,
/*flipped_y=*/false, &pool, &io.Main(), /*float_in=*/false));
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16, JXL_BIG_ENDIAN,
/*flipped_y=*/false, &pool, &io.Main(), /*float_in=*/false, /*align=*/0));
jxl::PaddedBytes jpeg_data;
if (jpeg_codestream != nullptr) {
#if JPEGXL_ENABLE_JPEG
@ -1224,7 +1224,8 @@ TEST_P(DecodeTestParam, PixelTest) {
EXPECT_TRUE(ConvertFromExternal(
bytes, config.xsize, config.ysize, color_encoding, config.include_alpha,
/*alpha_is_premultiplied=*/false, 16, JXL_BIG_ENDIAN,
/*flipped_y=*/false, nullptr, &io.Main(), /*float_in=*/false));
/*flipped_y=*/false, nullptr, &io.Main(), /*float_in=*/false,
/*align=*/0));
for (size_t i = 0; i < pixels.size(); i++) pixels[i] = 0;
EXPECT_TRUE(ConvertToExternal(
@ -1543,22 +1544,24 @@ TEST(DecodeTest, PixelTestWithICCProfileLossy) {
EXPECT_TRUE(ConvertFromExternal(
span0, xsize, ysize, color_encoding0,
/*has_alpha=*/false, false, 16, format_orig.endianness,
/*flipped_y=*/false, /*pool=*/nullptr, &io0.Main(), /*float_in=*/false));
/*flipped_y=*/false, /*pool=*/nullptr, &io0.Main(), /*float_in=*/false,
/*align=*/0));
jxl::ColorEncoding color_encoding1;
EXPECT_TRUE(color_encoding1.SetICC(std::move(icc)));
jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size());
jxl::CodecInOut io1;
io1.SetSize(xsize, ysize);
EXPECT_TRUE(ConvertFromExternal(
span1, xsize, ysize, color_encoding1,
/*has_alpha=*/false, false, 32, format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr, &io1.Main(), /*float_in=*/true));
EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1,
/*has_alpha=*/false, false, 32,
format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io1.Main(), /*float_in=*/true, /*align=*/0));
jxl::ButteraugliParams ba;
EXPECT_THAT(ButteraugliDistance(io0, io1, ba, jxl::GetJxlCms(),
/*distmap=*/nullptr, nullptr),
IsSlightlyBelow(0.75f));
IsSlightlyBelow(0.77f));
JxlDecoderDestroy(dec);
}
@ -1596,11 +1599,11 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossy) {
jxl::Span<const uint8_t> span0(pixels.data(), pixels.size());
jxl::CodecInOut io0;
io0.SetSize(xsize, ysize);
EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0,
/*has_alpha=*/false, false, 16,
format_orig.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io0.Main(), /*float_in=*/false));
EXPECT_TRUE(ConvertFromExternal(
span0, xsize, ysize, color_encoding0,
/*has_alpha=*/false, false, 16, format_orig.endianness,
/*flipped_y=*/false, /*pool=*/nullptr, &io0.Main(), /*float_in=*/false,
/*align=*/0));
jxl::ColorEncoding color_encoding1 = jxl::ColorEncoding::SRGB(false);
jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size());
@ -1608,19 +1611,19 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossy) {
if (channels == 4) {
io1.metadata.m.SetAlphaBits(8);
io1.SetSize(xsize, ysize);
EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1,
/*has_alpha=*/true, false, 8,
format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io1.Main(), /*float_in=*/false));
EXPECT_TRUE(
ConvertFromExternal(span1, xsize, ysize, color_encoding1,
/*has_alpha=*/true, false, 8, format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io1.Main(), /*float_in=*/false, /*align=*/0));
io1.metadata.m.SetAlphaBits(0);
io1.Main().ClearExtraChannels();
} else {
EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1,
/*has_alpha=*/false, false, 8,
format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io1.Main(), /*float_in=*/false));
EXPECT_TRUE(
ConvertFromExternal(span1, xsize, ysize, color_encoding1,
/*has_alpha=*/false, false, 8, format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io1.Main(), /*float_in=*/false, /*align=*/0));
}
jxl::ButteraugliParams ba;
@ -1665,11 +1668,11 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) {
jxl::Span<const uint8_t> span0(pixels.data(), pixels.size());
jxl::CodecInOut io0;
io0.SetSize(xsize, ysize);
EXPECT_TRUE(ConvertFromExternal(span0, xsize, ysize, color_encoding0,
/*has_alpha=*/false, false, 16,
format_orig.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io0.Main(), /*float_in=*/false));
EXPECT_TRUE(ConvertFromExternal(
span0, xsize, ysize, color_encoding0,
/*has_alpha=*/false, false, 16, format_orig.endianness,
/*flipped_y=*/false, /*pool=*/nullptr, &io0.Main(), /*float_in=*/false,
/*align=*/0));
jxl::ColorEncoding color_encoding1 = jxl::ColorEncoding::SRGB(false);
jxl::Span<const uint8_t> span1(pixels2.data(), pixels2.size());
@ -1677,19 +1680,19 @@ TEST(DecodeTest, PixelTestOpaqueSrgbLossyNoise) {
if (channels == 4) {
io1.metadata.m.SetAlphaBits(8);
io1.SetSize(xsize, ysize);
EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1,
/*has_alpha=*/true, false, 8,
format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io1.Main(), /*float_in=*/false));
EXPECT_TRUE(
ConvertFromExternal(span1, xsize, ysize, color_encoding1,
/*has_alpha=*/true, false, 8, format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io1.Main(), /*float_in=*/false, /*align=*/0));
io1.metadata.m.SetAlphaBits(0);
io1.Main().ClearExtraChannels();
} else {
EXPECT_TRUE(ConvertFromExternal(span1, xsize, ysize, color_encoding1,
/*has_alpha=*/false, false, 8,
format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io1.Main(), /*float_in=*/false));
EXPECT_TRUE(
ConvertFromExternal(span1, xsize, ysize, color_encoding1,
/*has_alpha=*/false, false, 8, format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr,
&io1.Main(), /*float_in=*/false, /*align=*/0));
}
jxl::ButteraugliParams ba;
@ -1728,7 +1731,6 @@ void TestPartialStream(bool reconstructible_jpeg) {
std::vector<jxl::PaddedBytes> jpeg_codestreams(kCSBF_NUM_ENTRIES);
for (size_t i = 0; i < kCSBF_NUM_ENTRIES; ++i) {
CodeStreamBoxFormat add_container = (CodeStreamBoxFormat)i;
codestreams[i] = jxl::CreateTestJXLCodestream(
jxl::Span<const uint8_t>(pixels.data(), pixels.size()), xsize, ysize,
channels, cparams, add_container, JXL_ORIENT_IDENTITY,
@ -1969,9 +1971,9 @@ TEST(DecodeTest, PreviewTest) {
// smaller than 8x8, but jxl's ButteraugliDistance does not. Perhaps move
// butteraugli's <8x8 handling from ButteraugliDiffmap to
// ButteraugliComparator::Diffmap in butteraugli.cc.
EXPECT_THAT(ButteraugliDistance(io0, io1, ba, jxl::GetJxlCms(),
/*distmap=*/nullptr, nullptr),
IsSlightlyBelow(0.6f));
EXPECT_LE(ButteraugliDistance(io0, io1, ba, jxl::GetJxlCms(),
/*distmap=*/nullptr, nullptr),
0.6f);
JxlDecoderDestroy(dec);
}
@ -2034,7 +2036,7 @@ TEST(DecodeTest, AnimationTest) {
ysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/false,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16,
JXL_BIG_ENDIAN, /*flipped_y=*/false, /*pool=*/nullptr, &bundle,
/*float_in=*/false));
/*float_in=*/false, /*align=*/0));
bundle.duration = frame_durations[i];
io.frames.push_back(std::move(bundle));
}
@ -2138,7 +2140,7 @@ TEST(DecodeTest, AnimationTestStreaming) {
ysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/false,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16,
JXL_BIG_ENDIAN, /*flipped_y=*/false, /*pool=*/nullptr, &bundle,
/*float_in=*/false));
/*float_in=*/false, /*align=*/0));
bundle.duration = frame_durations[i];
io.frames.push_back(std::move(bundle));
}
@ -2363,7 +2365,7 @@ TEST(DecodeTest, SkipFrameTest) {
ysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/false,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16,
JXL_BIG_ENDIAN, /*flipped_y=*/false, /*pool=*/nullptr, &bundle,
/*float_in=*/false));
/*float_in=*/false, /*align=*/0));
bundle.duration = frame_durations[i];
io.frames.push_back(std::move(bundle));
}
@ -2500,7 +2502,7 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) {
/*has_alpha=*/false,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16,
JXL_BIG_ENDIAN, /*flipped_y=*/false, /*pool=*/nullptr,
&bundle_internal, /*float_in=*/false));
&bundle_internal, /*float_in=*/false, /*align=*/0));
bundle_internal.duration = 0;
bundle_internal.use_for_next_frame = true;
io.frames.push_back(std::move(bundle_internal));
@ -2516,7 +2518,7 @@ TEST(DecodeTest, SkipFrameWithBlendingTest) {
jxl::ColorEncoding::SRGB(/*is_gray=*/false), /*has_alpha=*/false,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16,
JXL_BIG_ENDIAN, /*flipped_y=*/false, /*pool=*/nullptr, &bundle,
/*float_in=*/false));
/*float_in=*/false, /*align=*/0));
bundle.duration = frame_durations[i];
// Create some variation in which frames depend on which.
if (i != 3 && i != 9 && i != 10) {
@ -2727,7 +2729,7 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
/*has_alpha=*/true,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16,
JXL_BIG_ENDIAN, /*flipped_y=*/false, /*pool=*/nullptr,
&bundle_internal, /*float_in=*/false));
&bundle_internal, /*float_in=*/false, /*align=*/0));
bundle_internal.duration = 0;
bundle_internal.use_for_next_frame = true;
bundle_internal.origin = {13, 17};
@ -2749,7 +2751,7 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
/*has_alpha=*/true,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16,
JXL_BIG_ENDIAN, /*flipped_y=*/false, /*pool=*/nullptr, &bundle,
/*float_in=*/false));
/*float_in=*/false, /*align=*/0));
bundle.duration = 5 + i;
frame_durations_nc.push_back(5 + i);
frame_durations_c.push_back(5 + i);
@ -2981,135 +2983,145 @@ TEST(DecodeTest, SkipFrameWithAlphaBlendingTest) {
}
TEST(DecodeTest, OrientedCroppedFrameTest) {
size_t xsize = 90, ysize = 120;
JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
const auto test = [](bool keep_orientation, uint32_t orientation,
uint32_t resampling) {
size_t xsize = 90, ysize = 120;
JxlPixelFormat format = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
size_t oxsize = (!keep_orientation && orientation > 4 ? ysize : xsize);
size_t oysize = (!keep_orientation && orientation > 4 ? xsize : ysize);
jxl::CodecInOut io;
io.SetSize(xsize, ysize);
io.metadata.m.SetUintSamples(16);
io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
io.metadata.m.orientation = orientation;
io.frames.clear();
io.SetSize(xsize, ysize);
for (bool keep_orientation : {true, false}) {
for (uint32_t orientation = 1; orientation <= 8; orientation++) {
size_t oxsize = (!keep_orientation && orientation > 4 ? ysize : xsize);
size_t oysize = (!keep_orientation && orientation > 4 ? xsize : ysize);
jxl::CodecInOut io;
io.SetSize(xsize, ysize);
io.metadata.m.SetUintSamples(16);
io.metadata.m.color_encoding = jxl::ColorEncoding::SRGB(false);
io.metadata.m.orientation = orientation;
io.frames.clear();
io.SetSize(xsize, ysize);
for (size_t i = 0; i < 3; ++i) {
size_t cropxsize = 1 + xsize * 2 / (i + 1);
size_t cropysize = 1 + ysize * 3 / (i + 2);
int cropx0 = i * 3 - 8;
int cropy0 = i * 4 - 7;
for (size_t i = 0; i < 3; ++i) {
size_t cropxsize = 1 + xsize * 2 / (i + 1);
size_t cropysize = 1 + ysize * 3 / (i + 2);
int cropx0 = i * 3 - 8;
int cropy0 = i * 4 - 7;
std::vector<uint8_t> frame =
jxl::test::GetSomeTestImage(cropxsize, cropysize, 4, i * 2);
jxl::ImageBundle bundle(&io.metadata.m);
EXPECT_TRUE(ConvertFromExternal(
jxl::Span<const uint8_t>(frame.data(), frame.size()), cropxsize,
cropysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false),
/*has_alpha=*/true,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16,
JXL_BIG_ENDIAN, /*flipped_y=*/false, /*pool=*/nullptr, &bundle,
/*float_in=*/false, /*align=*/0));
bundle.origin = {cropx0, cropy0};
bundle.use_for_next_frame = true;
io.frames.push_back(std::move(bundle));
}
std::vector<uint8_t> frame =
jxl::test::GetSomeTestImage(cropxsize, cropysize, 4, i * 2);
jxl::ImageBundle bundle(&io.metadata.m);
EXPECT_TRUE(ConvertFromExternal(
jxl::Span<const uint8_t>(frame.data(), frame.size()), cropxsize,
cropysize, jxl::ColorEncoding::SRGB(/*is_gray=*/false),
/*has_alpha=*/true,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16,
JXL_BIG_ENDIAN, /*flipped_y=*/false, /*pool=*/nullptr, &bundle,
/*float_in=*/false));
bundle.origin = {cropx0, cropy0};
bundle.use_for_next_frame = true;
io.frames.push_back(std::move(bundle));
}
jxl::CompressParams cparams;
cparams
.SetLossless(); // Lossless to verify pixels exactly after roundtrip.
cparams.speed_tier = jxl::SpeedTier::kThunder;
cparams.resampling = resampling;
jxl::AuxOut aux_out;
jxl::PaddedBytes compressed;
jxl::PassesEncoderState enc_state;
EXPECT_TRUE(jxl::EncodeFile(cparams, &io, &enc_state, &compressed,
jxl::GetJxlCms(), &aux_out, nullptr));
jxl::CompressParams cparams;
cparams
.SetLossless(); // Lossless to verify pixels exactly after roundtrip.
cparams.speed_tier = jxl::SpeedTier::kThunder;
jxl::AuxOut aux_out;
jxl::PaddedBytes compressed;
jxl::PassesEncoderState enc_state;
EXPECT_TRUE(jxl::EncodeFile(cparams, &io, &enc_state, &compressed,
jxl::GetJxlCms(), &aux_out, nullptr));
// 0 is merged frame as decoded with coalescing enabled (default)
// 1-3 are non-coalesced frames as decoded with coalescing disabled
// 4 is the manually merged frame
std::vector<uint8_t> frames[5];
frames[4].resize(xsize * ysize * 8, 0);
// 0 is merged frame as decoded with coalescing enabled (default)
// 1-3 are non-coalesced frames as decoded with coalescing disabled
// 4 is the manually merged frame
std::vector<uint8_t> frames[5];
frames[4].resize(xsize * ysize * 8, 0);
// try both with and without coalescing
for (auto coalescing : {JXL_TRUE, JXL_FALSE}) {
// Independently decode all frames without any skipping, to create the
// expected blended frames, for the actual tests below to compare with.
{
JxlDecoder* dec = JxlDecoderCreate(NULL);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing));
// try both with and without coalescing
for (auto coalescing : {JXL_TRUE, JXL_FALSE}) {
// Independently decode all frames without any skipping, to create the
// expected blended frames, for the actual tests below to compare with.
{
JxlDecoder* dec = JxlDecoderCreate(NULL);
const uint8_t* next_in = compressed.data();
size_t avail_in = compressed.size();
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetCoalescing(dec, coalescing));
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetKeepOrientation(dec, keep_orientation));
void* runner = JxlThreadParallelRunnerCreate(
NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
dec, JxlThreadParallelRunner, runner));
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE));
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetInput(dec, next_in, avail_in));
for (size_t i = (coalescing ? 0 : 1); i < (coalescing ? 1 : 4); ++i) {
EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER, JxlDecoderProcessInput(dec));
JxlFrameHeader frame_header;
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetKeepOrientation(dec, keep_orientation));
void* runner = JxlThreadParallelRunnerCreate(
NULL, JxlThreadParallelRunnerDefaultNumWorkerThreads());
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderSetParallelRunner(
dec, JxlThreadParallelRunner, runner));
JxlDecoderGetFrameHeader(dec, &frame_header));
size_t buffer_size;
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSubscribeEvents(dec, JXL_DEC_FULL_IMAGE));
JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
if (coalescing) {
EXPECT_EQ(xsize * ysize * 8, buffer_size);
} else {
EXPECT_EQ(frame_header.layer_info.xsize *
frame_header.layer_info.ysize * 8,
buffer_size);
}
frames[i].resize(buffer_size);
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetInput(dec, next_in, avail_in));
for (size_t i = (coalescing ? 0 : 1); i < (coalescing ? 1 : 4); ++i) {
EXPECT_EQ(JXL_DEC_NEED_IMAGE_OUT_BUFFER,
JxlDecoderProcessInput(dec));
JxlFrameHeader frame_header;
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderGetFrameHeader(dec, &frame_header));
size_t buffer_size;
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderImageOutBufferSize(dec, &format, &buffer_size));
if (coalescing)
EXPECT_EQ(xsize * ysize * 8, buffer_size);
else
EXPECT_EQ(frame_header.layer_info.xsize *
frame_header.layer_info.ysize * 8,
buffer_size);
frames[i].resize(buffer_size);
EXPECT_EQ(JXL_DEC_SUCCESS,
JxlDecoderSetImageOutBuffer(
dec, &format, frames[i].data(), frames[i].size()));
EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
EXPECT_EQ(frame_header.layer_info.blend_info.blendmode,
JXL_BLEND_REPLACE);
if (coalescing) {
EXPECT_EQ(frame_header.layer_info.xsize, oxsize);
EXPECT_EQ(frame_header.layer_info.ysize, oysize);
EXPECT_EQ(frame_header.layer_info.crop_x0, 0);
EXPECT_EQ(frame_header.layer_info.crop_y0, 0);
} else {
// manually merge this layer
int x0 = frame_header.layer_info.crop_x0;
int y0 = frame_header.layer_info.crop_y0;
int w = frame_header.layer_info.xsize;
int h = frame_header.layer_info.ysize;
for (int y = 0; y < static_cast<int>(oysize); y++) {
if (y < y0 || y >= y0 + h) continue;
// pointers do whole 16-bit RGBA pixels at a time
uint64_t* row_merged = static_cast<uint64_t*>(
(void*)(frames[4].data() + y * oxsize * 8));
uint64_t* row_layer = static_cast<uint64_t*>(
(void*)(frames[i].data() + (y - y0) * w * 8));
for (int x = 0; x < static_cast<int>(oxsize); x++) {
if (x < x0 || x >= x0 + w) continue;
row_merged[x] = row_layer[x - x0];
}
JxlDecoderSetImageOutBuffer(dec, &format, frames[i].data(),
frames[i].size()));
EXPECT_EQ(JXL_DEC_FULL_IMAGE, JxlDecoderProcessInput(dec));
EXPECT_EQ(frame_header.layer_info.blend_info.blendmode,
JXL_BLEND_REPLACE);
if (coalescing) {
EXPECT_EQ(frame_header.layer_info.xsize, oxsize);
EXPECT_EQ(frame_header.layer_info.ysize, oysize);
EXPECT_EQ(frame_header.layer_info.crop_x0, 0);
EXPECT_EQ(frame_header.layer_info.crop_y0, 0);
} else {
// manually merge this layer
int x0 = frame_header.layer_info.crop_x0;
int y0 = frame_header.layer_info.crop_y0;
int w = frame_header.layer_info.xsize;
int h = frame_header.layer_info.ysize;
for (int y = 0; y < static_cast<int>(oysize); y++) {
if (y < y0 || y >= y0 + h) continue;
// pointers do whole 16-bit RGBA pixels at a time
uint64_t* row_merged = static_cast<uint64_t*>(
(void*)(frames[4].data() + y * oxsize * 8));
uint64_t* row_layer = static_cast<uint64_t*>(
(void*)(frames[i].data() + (y - y0) * w * 8));
for (int x = 0; x < static_cast<int>(oxsize); x++) {
if (x < x0 || x >= x0 + w) continue;
row_merged[x] = row_layer[x - x0];
}
}
}
// After all frames were decoded, JxlDecoderProcessInput should return
// success to indicate all is done.
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
JxlThreadParallelRunnerDestroy(runner);
JxlDecoderDestroy(dec);
}
}
EXPECT_EQ(0u, jxl::test::ComparePixels(frames[0].data(), frames[4].data(),
oxsize, oysize, format, format));
// After all frames were decoded, JxlDecoderProcessInput should return
// success to indicate all is done.
EXPECT_EQ(JXL_DEC_SUCCESS, JxlDecoderProcessInput(dec));
JxlThreadParallelRunnerDestroy(runner);
JxlDecoderDestroy(dec);
}
}
EXPECT_EQ(0u, jxl::test::ComparePixels(frames[0].data(), frames[4].data(),
oxsize, oysize, format, format));
};
for (bool keep_orientation : {true, false}) {
for (uint32_t orientation = 1; orientation <= 8; orientation++) {
for (uint32_t resampling : {1, 2, 4, 8}) {
SCOPED_TRACE(testing::Message()
<< "keep_orientation: " << keep_orientation << ", "
<< "orientation: " << orientation << ", "
<< "resampling: " << resampling);
test(keep_orientation, orientation, resampling);
}
}
}
}

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

@ -392,20 +392,33 @@ uint32_t ComputeBestMethod(
HistogramParams::ANSHistogramStrategy ans_histogram_strategy) {
size_t method = 0;
float fcost = ComputeHistoAndDataCost(histogram, alphabet_size, 0);
for (uint32_t shift = 0; shift <= ANS_LOG_TAB_SIZE;
ans_histogram_strategy != HistogramParams::ANSHistogramStrategy::kPrecise
? shift += 2
: shift++) {
auto try_shift = [&](size_t shift) {
float c = ComputeHistoAndDataCost(histogram, alphabet_size, shift + 1);
if (c < fcost) {
method = shift + 1;
fcost = c;
} else if (ans_histogram_strategy ==
HistogramParams::ANSHistogramStrategy::kFast) {
// do not be as precise if estimating cost.
}
};
switch (ans_histogram_strategy) {
case HistogramParams::ANSHistogramStrategy::kPrecise: {
for (uint32_t shift = 0; shift <= ANS_LOG_TAB_SIZE; shift++) {
try_shift(shift);
}
break;
}
}
case HistogramParams::ANSHistogramStrategy::kApproximate: {
for (uint32_t shift = 0; shift <= ANS_LOG_TAB_SIZE; shift += 2) {
try_shift(shift);
}
break;
}
case HistogramParams::ANSHistogramStrategy::kFast: {
try_shift(0);
try_shift(ANS_LOG_TAB_SIZE / 2);
try_shift(ANS_LOG_TAB_SIZE);
break;
}
};
*cost = fcost;
return method;
}

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

@ -27,16 +27,14 @@
namespace jxl {
uint32_t ComputeUsedOrders(const SpeedTier speed,
const AcStrategyImage& ac_strategy,
const Rect& rect) {
// Use default orders for small images.
if (ac_strategy.xsize() < 5 && ac_strategy.ysize() < 5) return 0;
std::pair<uint32_t, uint32_t> ComputeUsedOrders(
const SpeedTier speed, const AcStrategyImage& ac_strategy,
const Rect& rect) {
// Only uses DCT8 = 0, so bitfield = 1.
if (speed >= SpeedTier::kFalcon) return 1;
if (speed >= SpeedTier::kFalcon) return {1, 1};
uint32_t ret = 0;
uint32_t ret_customize = 0;
size_t xsize_blocks = rect.xsize();
size_t ysize_blocks = rect.ysize();
// TODO(veluca): precompute when doing DCT.
@ -45,19 +43,22 @@ uint32_t ComputeUsedOrders(const SpeedTier speed,
for (size_t bx = 0; bx < xsize_blocks; ++bx) {
int ord = kStrategyOrder[acs_row[bx].RawStrategy()];
// Do not customize coefficient orders for blocks bigger than 32x32.
ret |= 1u << ord;
if (ord > 6) {
continue;
}
ret |= 1u << ord;
ret_customize |= 1u << ord;
}
}
return ret;
// Use default orders for small images.
if (ac_strategy.xsize() < 5 && ac_strategy.ysize() < 5) return {ret, 0};
return {ret, ret_customize};
}
void ComputeCoeffOrder(SpeedTier speed, const ACImage& acs,
const AcStrategyImage& ac_strategy,
const FrameDimensions& frame_dim, uint32_t& used_orders,
coeff_order_t* JXL_RESTRICT order) {
uint16_t used_acs, coeff_order_t* JXL_RESTRICT order) {
std::vector<int32_t> num_zeros(kCoeffOrderMaxSize);
// If compressing at high speed and only using 8x8 DCTs, only consider a
// subset of blocks.
@ -144,6 +145,8 @@ void ComputeCoeffOrder(SpeedTier speed, const ACImage& acs,
};
auto mem = hwy::AllocateAligned<PosAndCount>(AcStrategy::kMaxCoeffArea);
std::vector<coeff_order_t> natural_order_buffer;
uint16_t computed = 0;
for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) {
uint8_t ord = kStrategyOrder[o];
@ -151,17 +154,24 @@ void ComputeCoeffOrder(SpeedTier speed, const ACImage& acs,
computed |= 1 << ord;
AcStrategy acs = AcStrategy::FromRawStrategy(o);
size_t sz = kDCTBlockSize * acs.covered_blocks_x() * acs.covered_blocks_y();
// Do nothing for transforms that don't appear.
if ((1 << ord) & ~used_acs) continue;
if (natural_order_buffer.size() < sz) natural_order_buffer.resize(sz);
acs.ComputeNaturalCoeffOrder(natural_order_buffer.data());
// Ensure natural coefficient order is not permuted if the order is
// not transmitted.
if ((1 << ord) & ~used_orders) {
for (size_t c = 0; c < 3; c++) {
size_t offset = CoeffOrderOffset(ord, c);
JXL_DASSERT(CoeffOrderOffset(ord, c + 1) - offset == sz);
SetDefaultOrder(AcStrategy::FromRawStrategy(o), &order[offset]);
memcpy(&order[offset], natural_order_buffer.data(),
sz * sizeof(*order));
}
continue;
}
const coeff_order_t* natural_coeff_order = acs.NaturalCoeffOrder();
bool is_nondefault = false;
for (uint8_t c = 0; c < 3; c++) {
@ -171,7 +181,7 @@ void ComputeCoeffOrder(SpeedTier speed, const ACImage& acs,
JXL_DASSERT(CoeffOrderOffset(ord, c + 1) - offset == sz);
float inv_sqrt_sz = 1.0f / std::sqrt(sz);
for (size_t i = 0; i < sz; ++i) {
size_t pos = natural_coeff_order[i];
size_t pos = natural_order_buffer[i];
pos_and_val[i].pos = pos;
// We don't care for the exact number -> quantize number of zeros,
// to get less permuted order.
@ -188,7 +198,7 @@ void ComputeCoeffOrder(SpeedTier speed, const ACImage& acs,
// Grab indices.
for (size_t i = 0; i < sz; ++i) {
order[offset + i] = pos_and_val[i].pos;
is_nondefault |= natural_coeff_order[i] != pos_and_val[i].pos;
is_nondefault |= natural_order_buffer[i] != pos_and_val[i].pos;
}
}
if (!is_nondefault) {
@ -232,12 +242,12 @@ void EncodePermutation(const coeff_order_t* JXL_RESTRICT order, size_t skip,
namespace {
void EncodeCoeffOrder(const coeff_order_t* JXL_RESTRICT order, AcStrategy acs,
std::vector<Token>* tokens, coeff_order_t* order_zigzag) {
std::vector<Token>* tokens, coeff_order_t* order_zigzag,
std::vector<coeff_order_t>& natural_order_lut) {
const size_t llf = acs.covered_blocks_x() * acs.covered_blocks_y();
const size_t size = kDCTBlockSize * llf;
const coeff_order_t* natural_coeff_order_lut = acs.NaturalCoeffOrderLut();
for (size_t i = 0; i < size; ++i) {
order_zigzag[i] = natural_coeff_order_lut[order[i]];
order_zigzag[i] = natural_order_lut[order[i]];
}
TokenizePermutation(order_zigzag, llf, size, tokens);
}
@ -250,15 +260,20 @@ void EncodeCoeffOrders(uint16_t used_orders,
auto mem = hwy::AllocateAligned<coeff_order_t>(AcStrategy::kMaxCoeffArea);
uint16_t computed = 0;
std::vector<std::vector<Token>> tokens(1);
std::vector<coeff_order_t> natural_order_lut;
for (uint8_t o = 0; o < AcStrategy::kNumValidStrategies; ++o) {
uint8_t ord = kStrategyOrder[o];
if (computed & (1 << ord)) continue;
computed |= 1 << ord;
if ((used_orders & (1 << ord)) == 0) continue;
AcStrategy acs = AcStrategy::FromRawStrategy(o);
const size_t llf = acs.covered_blocks_x() * acs.covered_blocks_y();
const size_t size = kDCTBlockSize * llf;
if (natural_order_lut.size() < size) natural_order_lut.resize(size);
acs.ComputeNaturalCoeffOrderLut(natural_order_lut.data());
for (size_t c = 0; c < 3; c++) {
EncodeCoeffOrder(&order[CoeffOrderOffset(ord, c)], acs, &tokens[0],
mem.get());
mem.get(), natural_order_lut);
}
}
// Do not write anything if no order is used.

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

@ -24,8 +24,9 @@
namespace jxl {
// Orders that are actually used in part of image. `rect` is in block units.
uint32_t ComputeUsedOrders(SpeedTier speed, const AcStrategyImage& ac_strategy,
const Rect& rect);
// Returns {orders that are used, orders that might be made non-default}.
std::pair<uint32_t, uint32_t> ComputeUsedOrders(
SpeedTier speed, const AcStrategyImage& ac_strategy, const Rect& rect);
// Modify zig-zag order, so that DCT bands with more zeros go later.
// Order of DCT bands with same number of zeros is untouched, so
@ -33,7 +34,7 @@ uint32_t ComputeUsedOrders(SpeedTier speed, const AcStrategyImage& ac_strategy,
void ComputeCoeffOrder(SpeedTier speed, const ACImage& acs,
const AcStrategyImage& ac_strategy,
const FrameDimensions& frame_dim, uint32_t& used_orders,
coeff_order_t* JXL_RESTRICT order);
uint16_t used_acs, coeff_order_t* JXL_RESTRICT order);
void EncodeCoeffOrders(uint16_t used_orders,
const coeff_order_t* JXL_RESTRICT order,

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

@ -16,6 +16,7 @@
#include "jxl/types.h"
#include "lib/jxl/alpha.h"
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/common.h"
@ -108,7 +109,7 @@ Status PixelFormatToExternal(const JxlPixelFormat& pixel_format,
Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
size_t ysize, size_t bits_per_sample,
JxlEndianness endianness, ThreadPool* pool,
ImageF* channel, bool float_in) {
ImageF* channel, bool float_in, size_t align) {
// TODO(firsching): Avoid code duplication with the function below.
if (bits_per_sample < 1 || bits_per_sample > 32) {
return JXL_FAILURE("Invalid bits_per_sample value.");
@ -124,8 +125,12 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
// bits_per_sample > 1.
const size_t bytes_per_pixel = DivCeil(bits_per_sample, jxl::kBitsPerByte);
const size_t row_size = xsize * bytes_per_pixel;
if (ysize && bytes.size() / ysize < row_size) {
const size_t last_row_size = xsize * bytes_per_pixel;
const size_t row_size =
(align > 1 ? jxl::DivCeil(last_row_size, align) * align : last_row_size);
const size_t bytes_to_read = row_size * (ysize - 1) + last_row_size;
if (xsize == 0 || ysize == 0) return JXL_FAILURE("Empty image");
if (bytes.size() < bytes_to_read) {
return JXL_FAILURE("Buffer size is too small");
}
JXL_ASSERT(channel->xsize() == xsize);
@ -210,7 +215,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
bool has_alpha, bool alpha_is_premultiplied,
size_t bits_per_sample, JxlEndianness endianness,
bool flipped_y, ThreadPool* pool, ImageBundle* ib,
bool float_in) {
bool float_in, size_t align) {
if (bits_per_sample < 1 || bits_per_sample > 32) {
return JXL_FAILURE("Invalid bits_per_sample value.");
}
@ -232,11 +237,17 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
return JXL_FAILURE("not supported, try bits_per_sample=32");
}
const size_t row_size = xsize * bytes_per_pixel;
if (ysize && bytes.size() / ysize < row_size) {
return JXL_FAILURE("Buffer size is too small");
const size_t last_row_size = xsize * bytes_per_pixel;
const size_t row_size =
(align > 1 ? jxl::DivCeil(last_row_size, align) * align : last_row_size);
const size_t bytes_to_read = row_size * (ysize - 1) + last_row_size;
if (xsize == 0 || ysize == 0) return JXL_FAILURE("Empty image");
if (bytes.size() < bytes_to_read) {
return JXL_FAILURE(
"Buffer size is too small: expected at least %" PRIuS
" bytes (= %" PRIuS " * %" PRIuS " * %" PRIuS "), got %" PRIuS " bytes",
bytes_to_read, xsize, ysize, bytes_per_pixel, bytes.size());
}
const bool little_endian =
endianness == JXL_LITTLE_ENDIAN ||
(endianness == JXL_NATIVE_ENDIAN && IsLittleEndian());
@ -419,8 +430,8 @@ Status BufferToImageF(const JxlPixelFormat& pixel_format, size_t xsize,
JXL_RETURN_IF_ERROR(ConvertFromExternal(
jxl::Span<const uint8_t>(static_cast<const uint8_t*>(buffer), size),
xsize, ysize, bitdepth, pixel_format.endianness, pool, channel,
float_in));
xsize, ysize, bitdepth, pixel_format.endianness, pool, channel, float_in,
pixel_format.align));
return true;
}
@ -441,7 +452,7 @@ Status BufferToImageBundle(const JxlPixelFormat& pixel_format, uint32_t xsize,
/*has_alpha=*/pixel_format.num_channels == 2 ||
pixel_format.num_channels == 4,
/*alpha_is_premultiplied=*/false, bitdepth, pixel_format.endianness,
/*flipped_y=*/false, pool, ib, float_in));
/*flipped_y=*/false, pool, ib, float_in, pixel_format.align));
ib->VerifyMetadata();
return true;

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

@ -21,20 +21,10 @@
#include "lib/jxl/image_bundle.h"
namespace jxl {
// Return the size in bytes of a given xsize, channels and bits_per_sample
// interleaved image.
constexpr size_t RowSize(size_t xsize, size_t channels,
size_t bits_per_sample) {
return bits_per_sample == 1
? DivCeil(xsize, kBitsPerByte)
: xsize * channels * DivCeil(bits_per_sample, kBitsPerByte);
}
Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
size_t ysize, size_t bits_per_sample,
JxlEndianness endianness, ThreadPool* pool,
ImageF* channel, bool float_in);
ImageF* channel, bool float_in, size_t align);
// Convert an interleaved pixel buffer to the internal ImageBundle
// representation. This is the opposite of ConvertToExternal().
@ -43,7 +33,7 @@ Status ConvertFromExternal(Span<const uint8_t> bytes, size_t xsize,
bool has_alpha, bool alpha_is_premultiplied,
size_t bits_per_sample, JxlEndianness endianness,
bool flipped_y, ThreadPool* pool, ImageBundle* ib,
bool float_in);
bool float_in, size_t align);
Status BufferToImageF(const JxlPixelFormat& pixel_format, size_t xsize,
size_t ysize, const void* buffer, size_t size,
ThreadPool* pool, ImageF* channel);

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

@ -32,7 +32,7 @@ void BM_EncExternalImage_ConvertImageRGBA(benchmark::State& state) {
/*alpha_is_premultiplied=*/false,
/*bits_per_sample=*/8, JXL_NATIVE_ENDIAN,
/*flipped_y=*/false,
/*pool=*/nullptr, &ib, /*float_in=*/false));
/*pool=*/nullptr, &ib, /*float_in=*/false, /*align=*/0));
}
}

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

@ -30,18 +30,18 @@ TEST(ExternalImageTest, InvalidSize) {
Span<const uint8_t>(buf, 10), /*xsize=*/10, /*ysize=*/100,
/*c_current=*/ColorEncoding::SRGB(), /*has_alpha=*/true,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16, JXL_BIG_ENDIAN,
/*flipped_y=*/false, nullptr, &ib, /*float_in=*/false));
/*flipped_y=*/false, nullptr, &ib, /*float_in=*/false, /*align=*/0));
EXPECT_FALSE(ConvertFromExternal(
Span<const uint8_t>(buf, sizeof(buf) - 1), /*xsize=*/10, /*ysize=*/100,
/*c_current=*/ColorEncoding::SRGB(), /*has_alpha=*/true,
/*alpha_is_premultiplied=*/false, /*bits_per_sample=*/16, JXL_BIG_ENDIAN,
/*flipped_y=*/false, nullptr, &ib, /*float_in=*/false));
/*flipped_y=*/false, nullptr, &ib, /*float_in=*/false, /*align=*/0));
EXPECT_TRUE(ConvertFromExternal(
Span<const uint8_t>(buf, sizeof(buf)), /*xsize=*/10,
/*ysize=*/100, /*c_current=*/ColorEncoding::SRGB(),
/*has_alpha=*/true, /*alpha_is_premultiplied=*/false,
/*bits_per_sample=*/16, JXL_BIG_ENDIAN,
/*flipped_y=*/false, nullptr, &ib, /*float_in=*/false));
/*flipped_y=*/false, nullptr, &ib, /*float_in=*/false, /*align=*/0));
}
#endif
@ -61,7 +61,7 @@ TEST(ExternalImageTest, AlphaMissing) {
/*c_current=*/ColorEncoding::SRGB(),
/*has_alpha=*/true, /*alpha_is_premultiplied=*/false,
/*bits_per_sample=*/8, JXL_BIG_ENDIAN,
/*flipped_y=*/false, nullptr, &ib, /*float_in=*/false));
/*flipped_y=*/false, nullptr, &ib, /*float_in=*/false, /*align=*/0));
EXPECT_FALSE(ib.HasAlpha());
}

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

@ -994,19 +994,20 @@ class LossyFrameEncoder {
private:
void ComputeAllCoeffOrders(const FrameDimensions& frame_dim) {
PROFILER_FUNC;
// No coefficient reordering in Falcon or faster.
auto used_orders_info = ComputeUsedOrders(
enc_state_->cparams.speed_tier, enc_state_->shared.ac_strategy,
Rect(enc_state_->shared.raw_quant_field));
enc_state_->used_orders.clear();
enc_state_->used_orders.resize(
enc_state_->progressive_splitter.GetNumPasses());
enc_state_->progressive_splitter.GetNumPasses(),
used_orders_info.second);
for (size_t i = 0; i < enc_state_->progressive_splitter.GetNumPasses();
i++) {
// No coefficient reordering in Falcon or faster.
if (enc_state_->cparams.speed_tier < SpeedTier::kFalcon) {
enc_state_->used_orders[i] = ComputeUsedOrders(
enc_state_->cparams.speed_tier, enc_state_->shared.ac_strategy,
Rect(enc_state_->shared.raw_quant_field));
}
ComputeCoeffOrder(
enc_state_->cparams.speed_tier, *enc_state_->coeffs[i],
enc_state_->shared.ac_strategy, frame_dim, enc_state_->used_orders[i],
used_orders_info.first,
&enc_state_->shared
.coeff_orders[i * enc_state_->shared.coeff_order_size]);
}

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

@ -5,6 +5,7 @@
#include "jxl/encode.h"
#include "enc_color_management.h"
#include "gtest/gtest.h"
#include "jxl/decode.h"
#include "jxl/decode_cxx.h"
@ -206,7 +207,11 @@ void VerifyFrameEncoding(size_t xsize, size_t ysize, JxlEncoder* enc,
EXPECT_LE(
ComputeDistance2(input_io.Main(), decoded_io.Main(), jxl::GetJxlCms()),
#if JXL_HIGH_PRECISION
1.8);
#else
4.8);
#endif
}
void VerifyFrameEncoding(JxlEncoder* enc,

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

@ -217,9 +217,12 @@ HWY_NOINLINE void TestFastXYB() {
Image3F xyb(kChunk * kChunk, kChunk);
std::vector<uint8_t> roundtrip(kChunk * kChunk * kChunk * 3);
ToXYB(ib, nullptr, &xyb, GetJxlCms());
jxl::HWY_NAMESPACE::FastXYBTosRGB8(
xyb, Rect(xyb), Rect(xyb), nullptr, Rect(), /*is_rgba=*/false,
roundtrip.data(), xyb.xsize(), xyb.xsize() * 3);
for (int y = 0; y < kChunk; y++) {
const float* xyba[4] = {xyb.PlaneRow(0, y), xyb.PlaneRow(1, y),
xyb.PlaneRow(2, y), nullptr};
jxl::HWY_NAMESPACE::FastXYBTosRGB8(
xyba, roundtrip.data() + 3 * xyb.xsize() * y, false, xyb.xsize());
}
for (int ir = 0; ir < kChunk; ir++) {
for (int ig = 0; ig < kChunk; ig++) {
for (int ib = 0; ib < kChunk; ib++) {

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

@ -234,6 +234,13 @@ Status ExtraChannelInfo::VisitFields(Visitor* JXL_RESTRICT visitor) {
JXL_QUIET_RETURN_IF_ERROR(visitor->U32(Val(1), Bits(2), BitsOffset(4, 3),
BitsOffset(8, 19), 1, &cfa_channel));
}
if (type == ExtraChannel::kUnknown ||
(int(ExtraChannel::kReserved0) <= int(type) &&
int(type) <= int(ExtraChannel::kReserved7))) {
return JXL_FAILURE("Unknown extra channel (bits %u, shift %u, name '%s')\n",
bit_depth.bits_per_sample, dim_shift, name.c_str());
}
return true;
}

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

@ -245,7 +245,7 @@ TEST(JxlTest, RoundtripResample2MT) {
// file size.
EXPECT_LE(Roundtrip(&io, cparams, dparams, &pool, &io2), 64500u);
EXPECT_THAT(ComputeDistance2(io.Main(), io2.Main(), GetJxlCms()),
IsSlightlyBelow(300));
IsSlightlyBelow(320));
}
// Roundtrip the image using a parallel runner that executes single-threaded but
@ -273,6 +273,30 @@ TEST(JxlTest, RoundtripOutOfOrderProcessing) {
/*distmap=*/nullptr, &pool));
}
TEST(JxlTest, RoundtripOutOfOrderProcessingBorder) {
FakeParallelRunner fake_pool(/*order_seed=*/47, /*num_threads=*/8);
ThreadPool pool(&JxlFakeParallelRunner, &fake_pool);
const PaddedBytes orig =
ReadTestData("imagecompression.info/flower_foveon.png");
CodecInOut io;
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io, &pool));
// Image size is selected so that the block border needed is larger than the
// amount of pixels available on the next block.
io.ShrinkTo(513, 515);
CompressParams cparams;
// Force epf so we end up needing a lot of border.
cparams.epf = 3;
cparams.resampling = 2;
DecompressParams dparams;
CodecInOut io2;
Roundtrip(&io, cparams, dparams, &pool, &io2);
EXPECT_GE(2.8, ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
/*distmap=*/nullptr, &pool));
}
TEST(JxlTest, RoundtripResample4) {
ThreadPool* pool = nullptr;
const PaddedBytes orig =
@ -571,7 +595,7 @@ TEST(JxlTest, RoundtripSmallPatchesAlpha) {
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 2000u);
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
/*distmap=*/nullptr, pool),
IsSlightlyBelow(0.22f));
IsSlightlyBelow(0.24f));
}
TEST(JxlTest, RoundtripSmallPatches) {
@ -599,7 +623,7 @@ TEST(JxlTest, RoundtripSmallPatches) {
EXPECT_LE(Roundtrip(&io, cparams, dparams, pool, &io2), 2000u);
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
/*distmap=*/nullptr, pool),
IsSlightlyBelow(0.22f));
IsSlightlyBelow(0.24f));
}
// Test header encoding of original bits per sample
@ -835,7 +859,7 @@ TEST(JxlTest, RoundtripAlphaResampling) {
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
/*distmap=*/nullptr, pool),
IsSlightlyBelow(4.6));
IsSlightlyBelow(4.7));
}
TEST(JxlTest, RoundtripAlphaResamplingOnlyAlpha) {
@ -867,7 +891,7 @@ TEST(JxlTest, RoundtripAlphaResamplingOnlyAlpha) {
EXPECT_THAT(ButteraugliDistance(io, io2, cparams.ba_params, GetJxlCms(),
/*distmap=*/nullptr, pool),
IsSlightlyBelow(1.8));
IsSlightlyBelow(1.85));
}
TEST(JxlTest, RoundtripAlphaNonMultipleOf8) {

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

@ -11,6 +11,7 @@
#include <queue>
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/base/scope_guard.h"
#include "lib/jxl/modular/encoding/context_predict.h"
#include "lib/jxl/modular/options.h"
@ -196,6 +197,39 @@ Status DecodeModularChannelMAANS(BitReader *br, ANSSymbolReader *reader,
}
}
}
} else if (predictor == Predictor::Gradient && offset == 0 &&
multiplier == 1 && reader->HuffRleOnly()) {
JXL_DEBUG_V(8, "Gradient RLE (fjxl) very fast track.");
uint32_t run = 0;
uint32_t v = 0;
pixel_type_w sv = 0;
for (size_t y = 0; y < channel.h; y++) {
pixel_type *JXL_RESTRICT r = channel.Row(y);
const pixel_type *JXL_RESTRICT rtop = (y ? channel.Row(y - 1) : r - 1);
const pixel_type *JXL_RESTRICT rtopleft =
(y ? channel.Row(y - 1) - 1 : r - 1);
pixel_type_w guess = (y ? rtop[0] : 0);
if (run == 0) {
reader->ReadHybridUintClusteredHuffRleOnly(ctx_id, br, &v, &run);
sv = UnpackSigned(v);
} else {
run--;
}
r[0] = sv + guess;
for (size_t x = 1; x < channel.w; x++) {
pixel_type left = r[x - 1];
pixel_type top = rtop[x];
pixel_type topleft = rtopleft[x];
pixel_type_w guess = ClampedGradient(top, left, topleft);
if (!run) {
reader->ReadHybridUintClusteredHuffRleOnly(ctx_id, br, &v, &run);
sv = UnpackSigned(v);
} else {
run--;
}
r[x] = sv + guess;
}
}
} else if (predictor == Predictor::Gradient && offset == 0 &&
multiplier == 1) {
JXL_DEBUG_V(8, "Gradient very fast track.");
@ -387,7 +421,19 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header,
if (image.channel.empty()) return true;
// decode transforms
JXL_RETURN_IF_ERROR(Bundle::Read(br, &header));
Status status = Bundle::Read(br, &header);
if (!allow_truncated_group) JXL_RETURN_IF_ERROR(status);
if (status.IsFatalError()) return status;
if (!br->AllReadsWithinBounds()) {
// Don't do/undo transforms if header is incomplete.
header.transforms.clear();
image.transform = header.transforms;
for (size_t c = 0; c < image.channel.size(); c++) {
ZeroFillImage(&image.channel[c].plane);
}
return Status(StatusCode::kNotEnoughBytes);
}
JXL_DEBUG_V(3, "Image data underwent %" PRIuS " transformations: ",
header.transforms.size());
image.transform = header.transforms;
@ -397,10 +443,7 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header,
if (image.error) {
return JXL_FAILURE("Corrupt file. Aborting.");
}
if (br->AllReadsWithinBounds()) {
// Only check if the transforms list is complete.
JXL_RETURN_IF_ERROR(ValidateChannelDimensions(image, *options));
}
JXL_RETURN_IF_ERROR(ValidateChannelDimensions(image, *options));
size_t nb_channels = image.channel.size();
@ -422,6 +465,15 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header,
}
if (num_chans == 0) return true;
size_t next_channel = 0;
auto scope_guard = MakeScopeGuard([&]() {
// Do not do anything if truncated groups are not allowed.
if (!allow_truncated_group) return;
for (size_t c = next_channel; c < nb_channels; c++) {
ZeroFillImage(&image.channel[c].plane);
}
});
// Read tree.
Tree tree_storage;
std::vector<uint8_t> context_map_storage;
@ -463,26 +515,29 @@ Status ModularDecode(BitReader *br, Image &image, GroupHeader &header,
// Read channels
ANSSymbolReader reader(code, br, distance_multiplier);
for (size_t i = 0; i < nb_channels; i++) {
Channel &channel = image.channel[i];
for (; next_channel < nb_channels; next_channel++) {
Channel &channel = image.channel[next_channel];
if (!channel.w || !channel.h) {
continue; // skip empty channels
}
if (i >= image.nb_meta_channels && (channel.w > options->max_chan_size ||
channel.h > options->max_chan_size)) {
if (next_channel >= image.nb_meta_channels &&
(channel.w > options->max_chan_size ||
channel.h > options->max_chan_size)) {
break;
}
JXL_RETURN_IF_ERROR(DecodeModularChannelMAANS(br, &reader, *context_map,
*tree, header.wp_header, i,
group_id, &image));
JXL_RETURN_IF_ERROR(DecodeModularChannelMAANS(
br, &reader, *context_map, *tree, header.wp_header, next_channel,
group_id, &image));
// Truncated group.
if (!br->AllReadsWithinBounds()) {
if (!allow_truncated_group) return JXL_FAILURE("Truncated input");
ZeroFillImage(&channel.plane);
while (++i < nb_channels) ZeroFillImage(&image.channel[i].plane);
return Status(StatusCode::kNotEnoughBytes);
}
}
// Make sure no zero-filling happens even if next_channel < nb_channels.
scope_guard.Disarm();
if (!reader.CheckANSFinalState()) {
return JXL_FAILURE("ANS decode final state failed");
}

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

@ -68,6 +68,9 @@ Status Transform::MetaApply(Image &input) {
"Transform: kPalette, begin_c=%" PRIu32 ", num_c=%" PRIu32
", nb_colors=%" PRIu32 ", nb_deltas=%" PRIu32,
begin_c, num_c, nb_colors, nb_deltas);
if (input.bitdepth > 24) {
return JXL_FAILURE("Palette is not allowed for bitdepth > 24");
}
return MetaPalette(input, begin_c, begin_c + num_c - 1, nb_colors,
nb_deltas, lossy_palette);
default:

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

@ -42,14 +42,14 @@ Status InitializePassesSharedState(const FrameHeader& frame_header,
}
shared->quant_dc = ImageB(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
if (!(frame_header.flags & FrameHeader::kUseDcFrame) || encoder) {
shared->dc_storage =
Image3F(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
} else {
bool use_dc_frame = !!(frame_header.flags & FrameHeader::kUseDcFrame);
if (!encoder && use_dc_frame) {
if (frame_header.dc_level == 4) {
return JXL_FAILURE("Invalid DC level for kUseDcFrame: %u",
frame_header.dc_level);
}
shared->dc_storage = Image3F();
shared->dc = &shared->dc_frames[frame_header.dc_level];
if (shared->dc->xsize() == 0) {
return JXL_FAILURE(
@ -58,10 +58,12 @@ Status InitializePassesSharedState(const FrameHeader& frame_header,
frame_header.dc_level, frame_header.dc_level + 1);
}
ZeroFillImage(&shared->quant_dc);
} else {
shared->dc_storage =
Image3F(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
shared->dc = &shared->dc_storage;
}
shared->dc_storage = Image3F(frame_dim.xsize_blocks, frame_dim.ysize_blocks);
return true;
}

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

@ -55,7 +55,6 @@ std::unique_ptr<RenderPipeline> RenderPipeline::Builder::Finalize(
res->frame_dimensions_ = frame_dimensions;
res->uses_noise_ = uses_noise_;
res->group_completed_passes_.resize(frame_dimensions.num_groups);
res->num_passes_ = num_passes_;
res->channel_shifts_.resize(stages_.size());
res->channel_shifts_[0].resize(num_c_);
for (size_t i = 1; i < stages_.size(); i++) {

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

@ -51,11 +51,7 @@ class RenderPipeline {
public:
class Builder {
public:
explicit Builder(size_t num_c, size_t num_passes)
: num_c_(num_c), num_passes_(num_passes) {
JXL_ASSERT(num_c > 0);
JXL_ASSERT(num_passes > 0);
}
explicit Builder(size_t num_c) : num_c_(num_c) { JXL_ASSERT(num_c > 0); }
// Adds a stage to the pipeline. Must be called at least once; the last
// added stage cannot have kInOut channels.
@ -77,7 +73,6 @@ class RenderPipeline {
private:
std::vector<std::unique_ptr<RenderPipelineStage>> stages_;
size_t num_c_;
size_t num_passes_;
bool use_simple_implementation_ = false;
bool uses_noise_ = false;
};
@ -86,6 +81,13 @@ class RenderPipeline {
virtual ~RenderPipeline() = default;
Status IsInitialized() const {
for (const auto& stage : stages_) {
JXL_RETURN_IF_ERROR(stage->IsInitialized());
}
return true;
}
// Allocates storage to run with `num` threads.
void PrepareForThreads(size_t num);
@ -95,9 +97,9 @@ class RenderPipeline {
// different threads, provided that a different `thread_id` is given.
RenderPipelineInput GetInputBuffers(size_t group_id, size_t thread_id);
bool ReceivedAllInput() const {
size_t PassesWithAllInput() const {
return *std::min_element(group_completed_passes_.begin(),
group_completed_passes_.end()) == num_passes_;
group_completed_passes_.end());
}
protected:
@ -114,8 +116,6 @@ class RenderPipeline {
// Indexed by thread_id
std::vector<CacheAlignedUniquePtr> temp_buffers_;
size_t num_passes_;
friend class RenderPipelineInput;
private:

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

@ -81,6 +81,8 @@ class RenderPipelineStage {
virtual ~RenderPipelineStage() = default;
protected:
virtual Status IsInitialized() const { return true; }
// Processes one row of input, producing the appropriate number of rows of
// output. Input/output rows can be obtained by calls to
// `GetInputRow`/`GetOutputRow`. `xsize+2*xextra` represents the total number
@ -121,11 +123,11 @@ class RenderPipelineStage {
float* GetOutputRow(const RowInfo& output_rows, size_t c,
size_t offset) const {
JXL_DASSERT(GetChannelMode(c) == RenderPipelineChannelMode::kInOut);
JXL_DASSERT(offset <= 1 << settings_.shift_y);
JXL_DASSERT(offset <= 1ul << settings_.shift_y);
return output_rows[c][offset] + kRenderPipelineXOffset;
}
const Settings settings_;
Settings settings_;
friend class RenderPipeline;
friend class SimpleRenderPipeline;
friend class LowMemoryRenderPipeline;

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

@ -26,7 +26,7 @@ namespace jxl {
namespace {
TEST(RenderPipelineTest, Build) {
RenderPipeline::Builder builder(/*num_c=*/1, /*num_passes=*/1);
RenderPipeline::Builder builder(/*num_c=*/1);
builder.AddStage(jxl::make_unique<UpsampleXSlowStage>());
builder.AddStage(jxl::make_unique<UpsampleYSlowStage>());
builder.AddStage(jxl::make_unique<Check0FinalStage>());
@ -39,7 +39,7 @@ TEST(RenderPipelineTest, Build) {
}
TEST(RenderPipelineTest, CallAllGroups) {
RenderPipeline::Builder builder(/*num_c=*/1, /*num_passes=*/1);
RenderPipeline::Builder builder(/*num_c=*/1);
builder.AddStage(jxl::make_unique<UpsampleXSlowStage>());
builder.AddStage(jxl::make_unique<UpsampleYSlowStage>());
builder.AddStage(jxl::make_unique<Check0FinalStage>());
@ -58,7 +58,7 @@ TEST(RenderPipelineTest, CallAllGroups) {
input_buffers.Done();
}
EXPECT_TRUE(pipeline->ReceivedAllInput());
EXPECT_TRUE(pipeline->PassesWithAllInput() == 1);
}
struct RenderPipelineTestInputSettings {
@ -71,6 +71,8 @@ struct RenderPipelineTestInputSettings {
// Short name for the encoder settings.
std::string cparams_descr;
bool add_spot_color = false;
Splines splines;
};
@ -94,6 +96,31 @@ TEST_P(RenderPipelineTestParam, PipelineTest) {
}
io.ShrinkTo(config.xsize, config.ysize);
if (config.add_spot_color) {
jxl::ImageF spot(config.xsize, config.ysize);
jxl::ZeroFillImage(&spot);
for (size_t y = 0; y < config.ysize; y++) {
float* JXL_RESTRICT row = spot.Row(y);
for (size_t x = 0; x < config.xsize; x++) {
row[x] = ((x ^ y) & 255) * (1.f / 255.f);
}
}
ExtraChannelInfo info;
info.bit_depth.bits_per_sample = 8;
info.dim_shift = 0;
info.type = jxl::ExtraChannel::kSpotColor;
info.spot_color[0] = 0.5f;
info.spot_color[1] = 0.2f;
info.spot_color[2] = 1.f;
info.spot_color[3] = 0.5f;
io.metadata.m.extra_channel_info.push_back(info);
std::vector<jxl::ImageF> ec;
ec.push_back(std::move(spot));
io.frames[0].SetExtraChannels(std::move(ec));
}
PaddedBytes compressed;
PassesEncoderState enc_state;
@ -103,6 +130,8 @@ TEST_P(RenderPipelineTestParam, PipelineTest) {
DecompressParams dparams;
dparams.render_spotcolors = true;
CodecInOut io_default;
ASSERT_TRUE(DecodeFile(dparams, compressed, &io_default, &pool));
CodecInOut io_slow_pipeline;
@ -111,13 +140,19 @@ TEST_P(RenderPipelineTestParam, PipelineTest) {
ASSERT_EQ(io_default.frames.size(), io_slow_pipeline.frames.size());
for (size_t i = 0; i < io_default.frames.size(); i++) {
#if JXL_HIGH_PRECISION
constexpr float kMaxError = 1e-5;
#else
constexpr float kMaxError = 1e-4;
#endif
VerifyRelativeError(*io_default.frames[i].color(),
*io_slow_pipeline.frames[i].color(), 1e-5, 1e-5);
*io_slow_pipeline.frames[i].color(), kMaxError,
kMaxError);
for (size_t ec = 0; ec < io_default.frames[i].extra_channels().size();
ec++) {
VerifyRelativeError(io_default.frames[i].extra_channels()[ec],
io_slow_pipeline.frames[i].extra_channels()[ec], 1e-5,
1e-5);
io_slow_pipeline.frames[i].extra_channels()[ec],
kMaxError, kMaxError);
}
}
}
@ -146,11 +181,15 @@ Splines CreateTestSplines() {
std::vector<RenderPipelineTestInputSettings> GeneratePipelineTests() {
std::vector<RenderPipelineTestInputSettings> all_tests;
for (size_t size : {128, 256, 258, 777}) {
std::pair<size_t, size_t> sizes[] = {
{128, 128}, {256, 256}, {258, 258}, {533, 401}, {777, 777},
};
for (auto size : sizes) {
RenderPipelineTestInputSettings settings;
settings.input_path = "imagecompression.info/flower_foveon.png";
settings.xsize = size;
settings.ysize = size;
settings.xsize = size.first;
settings.ysize = size.second;
// Base settings.
settings.cparams.butteraugli_distance = 1.0;
@ -285,6 +324,15 @@ std::vector<RenderPipelineTestInputSettings> GeneratePipelineTests() {
all_tests.push_back(s);
}
{
auto s = settings;
s.input_path = "wide-gamut-tests/R2020-sRGB-blue.png";
s.cparams_descr = "AlphaVarDCTUpsamplingEPF";
s.cparams.epf = 1;
s.cparams.ec_resampling = 2;
all_tests.push_back(s);
}
{
auto s = settings;
s.cparams.modular_mode = true;
@ -301,6 +349,13 @@ std::vector<RenderPipelineTestInputSettings> GeneratePipelineTests() {
s.cparams.ec_resampling = 2;
all_tests.push_back(s);
}
{
auto s = settings;
s.cparams_descr = "SpotColor";
s.add_spot_color = true;
all_tests.push_back(s);
}
}
#if JPEGXL_ENABLE_TRANSCODE_JPEG

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

@ -6,6 +6,7 @@
#include "lib/jxl/render_pipeline/simple_render_pipeline.h"
namespace jxl {
void SimpleRenderPipeline::PrepareForThreadsInternal(size_t num) {
if (!channel_data_.empty()) {
return;
@ -25,45 +26,52 @@ void SimpleRenderPipeline::PrepareForThreadsInternal(size_t num) {
}
}
std::vector<std::pair<ImageF*, Rect>> SimpleRenderPipeline::PrepareBuffers(
size_t group_id, size_t thread_id) {
std::vector<std::pair<ImageF*, Rect>> ret;
Rect SimpleRenderPipeline::MakeChannelRect(size_t group_id, size_t channel,
bool is_color) {
size_t base_color_shift =
CeilLog2Nonzero(frame_dimensions_.xsize_upsampled_padded /
frame_dimensions_.xsize_padded);
const size_t gx = group_id % frame_dimensions_.xsize_groups;
const size_t gy = group_id / frame_dimensions_.xsize_groups;
size_t xgroupdim = (frame_dimensions_.group_dim << base_color_shift) >>
channel_shifts_[0][channel].first;
size_t ygroupdim = (frame_dimensions_.group_dim << base_color_shift) >>
channel_shifts_[0][channel].second;
return Rect(kRenderPipelineXOffset + gx * xgroupdim,
kRenderPipelineXOffset + gy * ygroupdim, xgroupdim, ygroupdim,
kRenderPipelineXOffset +
DivCeil(frame_dimensions_.GetUpsampledXSize(is_color),
1 << channel_shifts_[0][channel].first),
kRenderPipelineXOffset +
DivCeil(frame_dimensions_.GetUpsampledYSize(is_color),
1 << channel_shifts_[0][channel].second));
}
std::vector<std::pair<ImageF*, Rect>> SimpleRenderPipeline::PrepareBuffers(
size_t group_id, size_t thread_id) {
std::vector<std::pair<ImageF*, Rect>> ret;
for (size_t c = 0; c < channel_data_.size(); c++) {
const size_t gx = group_id % frame_dimensions_.xsize_groups;
const size_t gy = group_id / frame_dimensions_.xsize_groups;
size_t xgroupdim = (frame_dimensions_.group_dim << base_color_shift) >>
channel_shifts_[0][c].first;
size_t ygroupdim = (frame_dimensions_.group_dim << base_color_shift) >>
channel_shifts_[0][c].second;
bool is_color_c =
c < 3 || (uses_noise_ && c >= channel_shifts_[0].size() - 3);
const Rect rect(kRenderPipelineXOffset + gx * xgroupdim,
kRenderPipelineXOffset + gy * ygroupdim, xgroupdim,
ygroupdim,
kRenderPipelineXOffset +
DivCeil(frame_dimensions_.GetUpsampledXSize(is_color_c),
1 << channel_shifts_[0][c].first),
kRenderPipelineXOffset +
DivCeil(frame_dimensions_.GetUpsampledYSize(is_color_c),
1 << channel_shifts_[0][c].second));
ret.emplace_back(&channel_data_[c], rect);
ret.emplace_back(&channel_data_[c],
MakeChannelRect(group_id, c, is_color_c));
}
return ret;
}
void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
if (!ReceivedAllInput()) return;
if (PassesWithAllInput() <= processed_passes_) return;
processed_passes_++;
for (const auto& ch : channel_data_) {
(void)ch;
for (size_t c = 0; c < channel_data_.size(); c++) {
Rect r = MakeChannelRect(group_id, c, false);
(void)r;
JXL_CHECK_IMAGE_INITIALIZED(
ch, Rect(kRenderPipelineXOffset, kRenderPipelineXOffset,
ch.xsize() - 2 * kRenderPipelineXOffset,
ch.ysize() - 2 * kRenderPipelineXOffset));
channel_data_[c], Rect(kRenderPipelineXOffset, kRenderPipelineXOffset,
r.xsize(), r.ysize()));
}
for (size_t stage_id = 0; stage_id < stages_.size(); stage_id++) {
const auto& stage = stages_[stage_id];
// Prepare buffers for kInOut channels.
@ -109,14 +117,14 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
}
}
// Vertical mirroring.
for (int iy = 0; iy < static_cast<int>(stage->settings_.border_y); iy++) {
memcpy(get_row(c, -iy - 1) - stage->settings_.border_x,
get_row(c, iy) - stage->settings_.border_x,
for (int y = 0; y < static_cast<int>(stage->settings_.border_y); y++) {
memcpy(get_row(c, -y - 1) - stage->settings_.border_x,
get_row(c, y) - stage->settings_.border_x,
sizeof(float) *
(input_sizes[c].first + 2 * stage->settings_.border_x));
memcpy(
get_row(c, input_sizes[c].second + iy) - stage->settings_.border_x,
get_row(c, input_sizes[c].second - iy - 1) -
get_row(c, input_sizes[c].second + y) - stage->settings_.border_x,
get_row(c, input_sizes[c].second - y - 1) -
stage->settings_.border_x,
sizeof(float) *
(input_sizes[c].first + 2 * stage->settings_.border_x));
@ -145,6 +153,9 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
for (size_t y = 0; y < ysize; y++) {
// Prepare input rows.
for (size_t c = 0; c < channel_data_.size(); c++) {
if (stage->GetChannelMode(c) == RenderPipelineChannelMode::kIgnored) {
continue;
}
input_rows[c].resize(2 * border_y + 1);
for (int iy = -border_y; iy <= border_y; iy++) {
input_rows[c][iy + border_y] =
@ -174,12 +185,12 @@ void SimpleRenderPipeline::ProcessBuffers(size_t group_id, size_t thread_id) {
}
channel_data_[c] = std::move(new_channels[c]);
}
for (const auto& ch : channel_data_) {
(void)ch;
for (size_t c = 0; c < channel_data_.size(); c++) {
Rect r = MakeChannelRect(group_id, c, false);
(void)r;
JXL_CHECK_IMAGE_INITIALIZED(
ch, Rect(kRenderPipelineXOffset, kRenderPipelineXOffset,
ch.xsize() - 2 * kRenderPipelineXOffset,
ch.ysize() - 2 * kRenderPipelineXOffset));
channel_data_[c], Rect(kRenderPipelineXOffset, kRenderPipelineXOffset,
r.xsize(), r.ysize()));
}
}
}

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

@ -29,6 +29,10 @@ class SimpleRenderPipeline : public RenderPipeline {
// Full frame buffers. Both X and Y dimensions are padded by
// kRenderPipelineXOffset.
std::vector<ImageF> channel_data_;
size_t processed_passes_ = 0;
private:
Rect MakeChannelRect(size_t group_id, size_t channel, bool is_color);
};
} // namespace jxl

187
third_party/jpeg-xl/lib/jxl/render_pipeline/stage_blending.cc поставляемый Normal file
Просмотреть файл

@ -0,0 +1,187 @@
// 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/render_pipeline/stage_blending.h"
#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_blending.cc"
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
#include "lib/jxl/base/printf_macros.h"
#include "lib/jxl/blending.h"
HWY_BEFORE_NAMESPACE();
namespace jxl {
namespace HWY_NAMESPACE {
class BlendingStage : public RenderPipelineStage {
public:
explicit BlendingStage(const PassesDecoderState* dec_state,
const ColorEncoding& frame_color_encoding)
: RenderPipelineStage(RenderPipelineStage::Settings()),
state_(*dec_state->shared) {
image_xsize_ = state_.frame_header.nonserialized_metadata->xsize();
image_ysize_ = state_.frame_header.nonserialized_metadata->ysize();
extra_channel_info_ =
&state_.frame_header.nonserialized_metadata->m.extra_channel_info;
info_ = state_.frame_header.blending_info;
const std::vector<BlendingInfo>& ec_info =
state_.frame_header.extra_channel_blending_info;
ImageBundle& bg = *state_.reference_frames[info_.source].frame;
bg_ = &bg;
if (bg.xsize() == 0 && bg.ysize() == 0) {
// there is no background, assume it to be all zeroes
ImageBundle empty(&state_.metadata->m);
Image3F color(image_xsize_, image_ysize_);
ZeroFillImage(&color);
empty.SetFromImage(std::move(color), frame_color_encoding);
if (!ec_info.empty()) {
std::vector<ImageF> ec;
for (size_t i = 0; i < ec_info.size(); ++i) {
ImageF eci(image_xsize_, image_ysize_);
ZeroFillImage(&eci);
ec.push_back(std::move(eci));
}
empty.SetExtraChannels(std::move(ec));
}
bg = std::move(empty);
} else if (state_.reference_frames[info_.source].ib_is_in_xyb) {
initialized_ = JXL_FAILURE(
"Trying to blend XYB reference frame %i and non-XYB frame",
info_.source);
return;
}
if (bg.xsize() < image_xsize_ || bg.ysize() < image_ysize_ ||
bg.origin.x0 != 0 || bg.origin.y0 != 0) {
initialized_ = JXL_FAILURE("Trying to use a %" PRIuS "x%" PRIuS
" crop as a background",
bg.xsize(), bg.ysize());
return;
}
if (state_.metadata->m.xyb_encoded) {
if (!dec_state->output_encoding_info.color_encoding_is_original) {
initialized_ = JXL_FAILURE("Blending in unsupported color space");
return;
}
}
blending_info_.resize(ec_info.size() + 1);
auto make_blending = [&](const BlendingInfo& info, PatchBlending* pb) {
pb->alpha_channel = info.alpha_channel;
pb->clamp = info.clamp;
switch (info.mode) {
case BlendMode::kReplace: {
pb->mode = PatchBlendMode::kReplace;
break;
}
case BlendMode::kAdd: {
pb->mode = PatchBlendMode::kAdd;
break;
}
case BlendMode::kMul: {
pb->mode = PatchBlendMode::kMul;
break;
}
case BlendMode::kBlend: {
pb->mode = PatchBlendMode::kBlendAbove;
break;
}
case BlendMode::kAlphaWeightedAdd: {
pb->mode = PatchBlendMode::kAlphaWeightedAddAbove;
break;
}
default: {
JXL_ABORT("Invalid blend mode"); // should have failed to decode
}
}
};
make_blending(info_, &blending_info_[0]);
for (size_t i = 0; i < ec_info.size(); i++) {
make_blending(ec_info[i], &blending_info_[1 + i]);
}
}
Status IsInitialized() const override { return initialized_; }
void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
size_t xextra, size_t xsize, size_t xpos, size_t ypos,
float* JXL_RESTRICT temp) const final {
PROFILER_ZONE("Blend");
JXL_ASSERT(initialized_);
const FrameOrigin& frame_origin = state_.frame_header.frame_origin;
ssize_t bg_xpos = frame_origin.x0 + static_cast<ssize_t>(xpos);
ssize_t bg_ypos = frame_origin.y0 + static_cast<ssize_t>(ypos);
int offset = 0;
if (bg_xpos + static_cast<ssize_t>(xsize) <= 0 ||
frame_origin.x0 >= static_cast<ssize_t>(image_xsize_) || bg_ypos < 0 ||
bg_ypos >= static_cast<ssize_t>(image_ysize_)) {
return;
}
if (bg_xpos < 0) {
xpos -= bg_xpos;
offset -= bg_xpos;
xsize += bg_xpos;
bg_xpos = 0;
}
if (bg_xpos + xsize > image_xsize_) {
xsize =
std::max<ssize_t>(0, static_cast<ssize_t>(image_xsize_) - bg_xpos);
}
std::vector<const float*> bg_row_ptrs_(input_rows.size());
std::vector<float*> fg_row_ptrs_(input_rows.size());
for (size_t c = 0; c < input_rows.size(); ++c) {
bg_row_ptrs_[c] =
(c < 3 ? bg_->color()->ConstPlaneRow(c, bg_ypos)
: bg_->extra_channels()[c - 3].ConstRow(bg_ypos)) +
bg_xpos;
fg_row_ptrs_[c] = GetInputRow(input_rows, c, offset);
}
PerformBlending(bg_row_ptrs_.data(), fg_row_ptrs_.data(),
fg_row_ptrs_.data(), 0, xsize, blending_info_[0],
blending_info_.data() + 1, *extra_channel_info_);
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return RenderPipelineChannelMode::kInPlace;
}
private:
const PassesSharedState& state_;
BlendingInfo info_;
ImageBundle* bg_;
Status initialized_ = true;
size_t image_xsize_;
size_t image_ysize_;
std::vector<PatchBlending> blending_info_;
const std::vector<ExtraChannelInfo>* extra_channel_info_;
};
std::unique_ptr<RenderPipelineStage> GetBlendingStage(
const PassesDecoderState* dec_state,
const ColorEncoding& frame_color_encoding) {
return jxl::make_unique<BlendingStage>(dec_state, frame_color_encoding);
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
} // namespace HWY_NAMESPACE
} // namespace jxl
HWY_AFTER_NAMESPACE();
#if HWY_ONCE
namespace jxl {
HWY_EXPORT(GetBlendingStage);
std::unique_ptr<RenderPipelineStage> GetBlendingStage(
const PassesDecoderState* dec_state,
const ColorEncoding& frame_color_encoding) {
return HWY_DYNAMIC_DISPATCH(GetBlendingStage)(dec_state,
frame_color_encoding);
}
} // namespace jxl
#endif

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

@ -0,0 +1,24 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_JXL_RENDER_PIPELINE_STAGE_BLENDING_H_
#define LIB_JXL_RENDER_PIPELINE_STAGE_BLENDING_H_
#include <utility>
#include "lib/jxl/dec_cache.h"
#include "lib/jxl/render_pipeline/render_pipeline_stage.h"
#include "lib/jxl/splines.h"
namespace jxl {
// Applies blending if applicable.
std::unique_ptr<RenderPipelineStage> GetBlendingStage(
const PassesDecoderState* dec_state,
const ColorEncoding& frame_color_encoding);
} // namespace jxl
#endif // LIB_JXL_RENDER_PIPELINE_STAGE_BLENDING_H_

50
third_party/jpeg-xl/lib/jxl/render_pipeline/stage_spot.cc поставляемый Normal file
Просмотреть файл

@ -0,0 +1,50 @@
// 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/render_pipeline/stage_spot.h"
namespace jxl {
class SpotColorStage : public RenderPipelineStage {
public:
explicit SpotColorStage(size_t spot_c, const float* spot_color)
: RenderPipelineStage(RenderPipelineStage::Settings()),
spot_c_(spot_c),
spot_color_(spot_color) {
JXL_ASSERT(spot_c_ >= 3);
}
void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
size_t xextra, size_t xsize, size_t xpos, size_t ypos,
float* JXL_RESTRICT temp) const final {
// TODO(veluca): add SIMD.
PROFILER_ZONE("RenderSpotColors");
float scale = spot_color_[3];
for (size_t c = 0; c < 3; c++) {
float* JXL_RESTRICT p = GetInputRow(input_rows, c, 0);
const float* JXL_RESTRICT s = GetInputRow(input_rows, spot_c_, 0);
for (ssize_t x = -xextra; x < ssize_t(xsize + xextra); x++) {
float mix = scale * s[x];
p[x] = mix * spot_color_[c] + (1.0f - mix) * p[x];
}
}
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return c < 3 ? RenderPipelineChannelMode::kInPlace
: c == spot_c_ ? RenderPipelineChannelMode::kInput
: RenderPipelineChannelMode::kIgnored;
}
private:
size_t spot_c_;
const float* spot_color_;
};
std::unique_ptr<RenderPipelineStage> GetSpotColorStage(
size_t spot_c, const float* spot_color) {
return jxl::make_unique<SpotColorStage>(spot_c, spot_color);
}
} // namespace jxl

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

@ -0,0 +1,21 @@
// 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_RENDER_PIPELINE_STAGE_SPOT_H_
#define LIB_JXL_RENDER_PIPELINE_STAGE_SPOT_H_
#include <utility>
#include "lib/jxl/render_pipeline/render_pipeline_stage.h"
namespace jxl {
// Render the spot color channels.
std::unique_ptr<RenderPipelineStage> GetSpotColorStage(size_t spot_c,
const float* spot_color);
} // namespace jxl
#endif // LIB_JXL_RENDER_PIPELINE_STAGE_SPOT_H_

317
third_party/jpeg-xl/lib/jxl/render_pipeline/stage_write.cc поставляемый Normal file
Просмотреть файл

@ -0,0 +1,317 @@
// 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/render_pipeline/stage_write.h"
#include "lib/jxl/common.h"
#include "lib/jxl/image_bundle.h"
#undef HWY_TARGET_INCLUDE
#define HWY_TARGET_INCLUDE "lib/jxl/render_pipeline/stage_write.cc"
#include <hwy/foreach_target.h>
#include <hwy/highway.h>
HWY_BEFORE_NAMESPACE();
namespace jxl {
namespace HWY_NAMESPACE {
template <typename D, typename V>
void StoreRGBA(D d, V r, V g, V b, V a, bool alpha, size_t n, size_t extra,
uint8_t* buf) {
#if HWY_TARGET == HWY_SCALAR
buf[0] = r.raw;
buf[1] = g.raw;
buf[2] = b.raw;
if (alpha) {
buf[3] = a.raw;
}
#elif HWY_TARGET == HWY_NEON
if (alpha) {
uint8x8x4_t data = {r.raw, g.raw, b.raw, a.raw};
if (extra >= 8) {
vst4_u8(buf, data);
} else {
uint8_t tmp[8 * 4];
vst4_u8(tmp, data);
memcpy(buf, tmp, n * 4);
}
} else {
uint8x8x3_t data = {r.raw, g.raw, b.raw};
if (extra >= 8) {
vst3_u8(buf, data);
} else {
uint8_t tmp[8 * 3];
vst3_u8(tmp, data);
memcpy(buf, tmp, n * 3);
}
}
#else
// TODO(veluca): implement this for x86.
size_t mul = alpha ? 4 : 3;
HWY_ALIGN uint8_t bytes[16];
Store(r, d, bytes);
for (size_t i = 0; i < n; i++) {
buf[mul * i] = bytes[i];
}
Store(g, d, bytes);
for (size_t i = 0; i < n; i++) {
buf[mul * i + 1] = bytes[i];
}
Store(b, d, bytes);
for (size_t i = 0; i < n; i++) {
buf[mul * i + 2] = bytes[i];
}
if (alpha) {
Store(a, d, bytes);
for (size_t i = 0; i < n; i++) {
buf[4 * i + 3] = bytes[i];
}
}
#endif
}
class WriteToU8Stage : public RenderPipelineStage {
public:
WriteToU8Stage(uint8_t* rgb, size_t stride, size_t width, size_t height,
bool rgba, bool has_alpha, size_t alpha_c)
: RenderPipelineStage(RenderPipelineStage::Settings()),
rgb_(rgb),
stride_(stride),
width_(width),
height_(height),
rgba_(rgba),
has_alpha_(has_alpha),
alpha_c_(alpha_c) {}
void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
size_t xextra, size_t xsize, size_t xpos, size_t ypos,
float* JXL_RESTRICT temp) const final {
if (ypos >= height_) return;
size_t bytes = rgba_ ? 4 : 3;
const float* JXL_RESTRICT row_in_r = GetInputRow(input_rows, 0, 0);
const float* JXL_RESTRICT row_in_g = GetInputRow(input_rows, 1, 0);
const float* JXL_RESTRICT row_in_b = GetInputRow(input_rows, 2, 0);
const float* JXL_RESTRICT row_in_a =
has_alpha_ ? GetInputRow(input_rows, alpha_c_, 0) : nullptr;
size_t base_ptr = ypos * stride_ + bytes * (xpos - xextra);
using D = HWY_CAPPED(float, 4);
const D d;
D::Rebind<uint32_t> du;
auto zero = Zero(d);
auto one = Set(d, 1.0f);
auto mul = Set(d, 255.0f);
ssize_t x0 = -RoundUpTo(xextra, Lanes(d));
ssize_t x1 = RoundUpTo(xsize + xextra, Lanes(d));
for (ssize_t x = x0; x < x1; x += Lanes(d)) {
auto rf = Clamp(zero, Load(d, row_in_r + x), one) * mul;
auto gf = Clamp(zero, Load(d, row_in_g + x), one) * mul;
auto bf = Clamp(zero, Load(d, row_in_b + x), one) * mul;
auto af = row_in_a ? Clamp(zero, Load(d, row_in_a + x), one) * mul
: Set(d, 255.0f);
auto r8 = U8FromU32(BitCast(du, NearestInt(rf)));
auto g8 = U8FromU32(BitCast(du, NearestInt(gf)));
auto b8 = U8FromU32(BitCast(du, NearestInt(bf)));
auto a8 = U8FromU32(BitCast(du, NearestInt(af)));
size_t n = width_ - xpos - x;
if (JXL_LIKELY(n >= Lanes(d))) {
StoreRGBA(D::Rebind<uint8_t>(), r8, g8, b8, a8, rgba_, Lanes(d), n,
rgb_ + base_ptr + bytes * x);
} else {
StoreRGBA(D::Rebind<uint8_t>(), r8, g8, b8, a8, rgba_, n, n,
rgb_ + base_ptr + bytes * x);
}
}
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return c < 3 || (has_alpha_ && c == alpha_c_)
? RenderPipelineChannelMode::kInput
: RenderPipelineChannelMode::kIgnored;
}
private:
uint8_t* rgb_;
size_t stride_;
size_t width_;
size_t height_;
bool rgba_;
bool has_alpha_;
size_t alpha_c_;
std::vector<float> opaque_alpha_;
};
std::unique_ptr<RenderPipelineStage> GetWriteToU8Stage(
uint8_t* rgb, size_t stride, size_t width, size_t height, bool rgba,
bool has_alpha, size_t alpha_c) {
return jxl::make_unique<WriteToU8Stage>(rgb, stride, width, height, rgba,
has_alpha, alpha_c);
}
// NOLINTNEXTLINE(google-readability-namespace-comments)
} // namespace HWY_NAMESPACE
} // namespace jxl
HWY_AFTER_NAMESPACE();
#if HWY_ONCE
namespace jxl {
HWY_EXPORT(GetWriteToU8Stage);
namespace {
class WriteToImageBundleStage : public RenderPipelineStage {
public:
explicit WriteToImageBundleStage(ImageBundle* image_bundle)
: RenderPipelineStage(RenderPipelineStage::Settings()),
image_bundle_(image_bundle) {}
void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
size_t xextra, size_t xsize, size_t xpos, size_t ypos,
float* JXL_RESTRICT temp) const final {
for (size_t c = 0; c < 3; c++) {
memcpy(image_bundle_->color()->PlaneRow(c, ypos) + xpos - xextra,
GetInputRow(input_rows, c, 0) - xextra,
sizeof(float) * (xsize + 2 * xextra));
}
for (size_t ec = 0; ec < image_bundle_->extra_channels().size(); ec++) {
JXL_ASSERT(ec < image_bundle_->extra_channels().size());
JXL_ASSERT(image_bundle_->extra_channels()[ec].xsize() <=
xpos + xsize + xextra);
memcpy(image_bundle_->extra_channels()[ec].Row(ypos) + xpos - xextra,
GetInputRow(input_rows, 3 + ec, 0) - xextra,
sizeof(float) * (xsize + 2 * xextra));
}
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return c < 3 + image_bundle_->extra_channels().size()
? RenderPipelineChannelMode::kInput
: RenderPipelineChannelMode::kIgnored;
}
private:
ImageBundle* image_bundle_;
};
class WriteToImage3FStage : public RenderPipelineStage {
public:
explicit WriteToImage3FStage(Image3F* image)
: RenderPipelineStage(RenderPipelineStage::Settings()), image_(image) {}
void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
size_t xextra, size_t xsize, size_t xpos, size_t ypos,
float* JXL_RESTRICT temp) const final {
for (size_t c = 0; c < 3; c++) {
memcpy(image_->PlaneRow(c, ypos) + xpos - xextra,
GetInputRow(input_rows, c, 0) - xextra,
sizeof(float) * (xsize + 2 * xextra));
}
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return c < 3 ? RenderPipelineChannelMode::kInput
: RenderPipelineChannelMode::kIgnored;
}
private:
Image3F* image_;
};
class WriteToPixelCallbackStage : public RenderPipelineStage {
public:
WriteToPixelCallbackStage(
const std::function<void(const float*, size_t, size_t, size_t)>&
pixel_callback,
size_t width, size_t height, bool rgba, bool has_alpha, size_t alpha_c)
: RenderPipelineStage(RenderPipelineStage::Settings()),
pixel_callback_(pixel_callback),
width_(width),
height_(height),
rgba_(rgba),
has_alpha_(has_alpha),
alpha_c_(alpha_c),
opaque_alpha_(kMaxPixelsPerCall, 1.0f) {
settings_.temp_buffer_size = kMaxPixelsPerCall * (rgba_ ? 4 : 3);
}
void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
size_t xextra, size_t xsize, size_t xpos, size_t ypos,
float* JXL_RESTRICT temp) const final {
if (ypos >= height_) return;
const float* line_buffers[4];
for (size_t c = 0; c < 3; c++) {
line_buffers[c] = GetInputRow(input_rows, c, 0);
}
if (has_alpha_) {
line_buffers[3] = GetInputRow(input_rows, alpha_c_, 0);
} else {
line_buffers[3] = opaque_alpha_.data();
}
// TODO(veluca): SIMD.
ssize_t limit = std::min(xextra + xsize, width_ - xpos);
for (ssize_t x0 = -xextra; x0 < limit; x0 += kMaxPixelsPerCall) {
size_t j = 0;
size_t ix = 0;
for (; ix < kMaxPixelsPerCall && ssize_t(ix) + x0 < limit; ix++) {
temp[j++] = line_buffers[0][x0 + ix];
temp[j++] = line_buffers[1][x0 + ix];
temp[j++] = line_buffers[2][x0 + ix];
if (rgba_) {
temp[j++] = line_buffers[3][x0 + ix];
}
}
pixel_callback_(temp, xpos + x0, ypos, ix);
}
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return c < 3 || (has_alpha_ && c == alpha_c_)
? RenderPipelineChannelMode::kInput
: RenderPipelineChannelMode::kIgnored;
}
private:
static constexpr size_t kMaxPixelsPerCall = 1024;
const std::function<void(const float*, size_t, size_t, size_t)>&
pixel_callback_;
size_t width_;
size_t height_;
bool rgba_;
bool has_alpha_;
size_t alpha_c_;
std::vector<float> opaque_alpha_;
};
} // namespace
std::unique_ptr<RenderPipelineStage> GetWriteToImageBundleStage(
ImageBundle* image_bundle) {
return jxl::make_unique<WriteToImageBundleStage>(image_bundle);
}
std::unique_ptr<RenderPipelineStage> GetWriteToImage3FStage(Image3F* image) {
return jxl::make_unique<WriteToImage3FStage>(image);
}
std::unique_ptr<RenderPipelineStage> GetWriteToU8Stage(
uint8_t* rgb, size_t stride, size_t width, size_t height, bool rgba,
bool has_alpha, size_t alpha_c) {
return HWY_DYNAMIC_DISPATCH(GetWriteToU8Stage)(rgb, stride, width, height,
rgba, has_alpha, alpha_c);
}
std::unique_ptr<RenderPipelineStage> GetWriteToPixelCallbackStage(
const std::function<void(const float*, size_t, size_t, size_t)>&
pixel_callback,
size_t width, size_t height, bool rgba, bool has_alpha, size_t alpha_c) {
return jxl::make_unique<WriteToPixelCallbackStage>(
pixel_callback, width, height, rgba, has_alpha, alpha_c);
}
} // namespace jxl
#endif

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

@ -0,0 +1,35 @@
// 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_RENDER_PIPELINE_STAGE_WRITE_H_
#define LIB_JXL_RENDER_PIPELINE_STAGE_WRITE_H_
#include <functional>
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/render_pipeline/render_pipeline_stage.h"
namespace jxl {
std::unique_ptr<RenderPipelineStage> GetWriteToImageBundleStage(
ImageBundle* image_bundle);
// Gets a stage to write color channels to an Image3F.
std::unique_ptr<RenderPipelineStage> GetWriteToImage3FStage(Image3F* image);
// Gets a stage to write to a uint8 buffer.
std::unique_ptr<RenderPipelineStage> GetWriteToU8Stage(
uint8_t* rgb, size_t stride, size_t width, size_t height, bool rgba,
bool has_alpha, size_t alpha_c);
// Gets a stage to write to a pixel callback.
std::unique_ptr<RenderPipelineStage> GetWriteToPixelCallbackStage(
const std::function<void(const float*, size_t, size_t, size_t)>&
pixel_callback,
size_t width, size_t height, bool rgba, bool has_alpha, size_t alpha_c);
} // namespace jxl
#endif // LIB_JXL_RENDER_PIPELINE_STAGE_WRITE_H_

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

@ -1,82 +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/render_pipeline/stage_write_to_ib.h"
#include "lib/jxl/common.h"
#include "lib/jxl/image_bundle.h"
namespace jxl {
namespace {
class WriteToImageBundleStage : public RenderPipelineStage {
public:
explicit WriteToImageBundleStage(ImageBundle* image_bundle)
: RenderPipelineStage(RenderPipelineStage::Settings()),
image_bundle_(image_bundle) {}
void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
size_t xextra, size_t xsize, size_t xpos, size_t ypos,
float* JXL_RESTRICT temp) const final {
for (size_t c = 0; c < 3; c++) {
memcpy(image_bundle_->color()->PlaneRow(c, ypos) + xpos - xextra,
GetInputRow(input_rows, c, 0) - xextra,
sizeof(float) * (xsize + 2 * xextra));
}
for (size_t ec = 0; ec < image_bundle_->extra_channels().size(); ec++) {
JXL_ASSERT(ec < image_bundle_->extra_channels().size());
JXL_ASSERT(image_bundle_->extra_channels()[ec].xsize() <=
xpos + xsize + xextra);
memcpy(image_bundle_->extra_channels()[ec].Row(ypos) + xpos - xextra,
GetInputRow(input_rows, 3 + ec, 0) - xextra,
sizeof(float) * (xsize + 2 * xextra));
}
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return c < 3 + image_bundle_->extra_channels().size()
? RenderPipelineChannelMode::kInput
: RenderPipelineChannelMode::kIgnored;
}
private:
ImageBundle* image_bundle_;
};
class WriteToImage3FStage : public RenderPipelineStage {
public:
explicit WriteToImage3FStage(Image3F* image)
: RenderPipelineStage(RenderPipelineStage::Settings()), image_(image) {}
void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
size_t xextra, size_t xsize, size_t xpos, size_t ypos,
float* JXL_RESTRICT temp) const final {
for (size_t c = 0; c < 3; c++) {
memcpy(image_->PlaneRow(c, ypos) + xpos - xextra,
GetInputRow(input_rows, c, 0) - xextra,
sizeof(float) * (xsize + 2 * xextra));
}
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return c < 3 ? RenderPipelineChannelMode::kInput
: RenderPipelineChannelMode::kIgnored;
}
private:
Image3F* image_;
};
} // namespace
std::unique_ptr<RenderPipelineStage> GetWriteToImageBundleStage(
ImageBundle* image_bundle) {
return jxl::make_unique<WriteToImageBundleStage>(image_bundle);
}
std::unique_ptr<RenderPipelineStage> GetWriteToImage3FStage(Image3F* image) {
return jxl::make_unique<WriteToImage3FStage>(image);
}
} // namespace jxl

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

@ -1,21 +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_RENDER_PIPELINE_STAGE_WRITE_TO_IB_H_
#define LIB_JXL_RENDER_PIPELINE_STAGE_WRITE_TO_IB_H_
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/render_pipeline/render_pipeline_stage.h"
namespace jxl {
std::unique_ptr<RenderPipelineStage> GetWriteToImageBundleStage(
ImageBundle* image_bundle);
// Gets a stage to write color channels to an Image3F.
std::unique_ptr<RenderPipelineStage> GetWriteToImage3FStage(Image3F* image);
} // namespace jxl
#endif // LIB_JXL_RENDER_PIPELINE_STAGE_WRITE_TO_IB_H_

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

@ -210,5 +210,60 @@ std::unique_ptr<RenderPipelineStage> GetXYBStage(
return HWY_DYNAMIC_DISPATCH(GetXYBStage)(output_encoding_info);
}
namespace {
class FastXYBStage : public RenderPipelineStage {
public:
FastXYBStage(uint8_t* rgb, size_t stride, size_t width, size_t height,
bool rgba, bool has_alpha, size_t alpha_c)
: RenderPipelineStage(RenderPipelineStage::Settings()),
rgb_(rgb),
stride_(stride),
width_(width),
height_(height),
rgba_(rgba),
has_alpha_(has_alpha),
alpha_c_(alpha_c) {}
void ProcessRow(const RowInfo& input_rows, const RowInfo& output_rows,
size_t xextra, size_t xsize, size_t xpos, size_t ypos,
float* JXL_RESTRICT temp) const final {
if (ypos >= height_) return;
JXL_ASSERT(xextra == 0);
const float* xyba[4] = {
GetInputRow(input_rows, 0, 0), GetInputRow(input_rows, 1, 0),
GetInputRow(input_rows, 2, 0),
has_alpha_ ? GetInputRow(input_rows, alpha_c_, 0) : nullptr};
uint8_t* out_buf = rgb_ + stride_ * ypos + (rgba_ ? 4 : 3) * xpos;
FastXYBTosRGB8(xyba, out_buf, rgba_,
xsize + xpos <= width_ ? xsize : width_ - xpos);
}
RenderPipelineChannelMode GetChannelMode(size_t c) const final {
return c < 3 || (has_alpha_ && c == alpha_c_)
? RenderPipelineChannelMode::kInput
: RenderPipelineChannelMode::kIgnored;
}
private:
uint8_t* rgb_;
size_t stride_;
size_t width_;
size_t height_;
bool rgba_;
bool has_alpha_;
size_t alpha_c_;
std::vector<float> opaque_alpha_;
};
} // namespace
std::unique_ptr<RenderPipelineStage> GetFastXYBTosRGB8Stage(
uint8_t* rgb, size_t stride, size_t width, size_t height, bool rgba,
bool has_alpha, size_t alpha_c) {
JXL_ASSERT(HasFastXYBTosRGB8());
return make_unique<FastXYBStage>(rgb, stride, width, height, rgba, has_alpha,
alpha_c);
}
} // namespace jxl
#endif

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

@ -21,6 +21,12 @@ namespace jxl {
// Converts the color channels from XYB to the specified output encoding.
std::unique_ptr<RenderPipelineStage> GetXYBStage(
const OutputEncodingInfo& output_encoding_info);
// Gets a stage to convert with fixed point arithmetic from XYB to sRGB8 and
// write to a uint8 buffer.
std::unique_ptr<RenderPipelineStage> GetFastXYBTosRGB8Stage(
uint8_t* rgb, size_t stride, size_t width, size_t height, bool rgba,
bool has_alpha, size_t alpha_c);
} // namespace jxl
#endif // LIB_JXL_RENDER_PIPELINE_STAGE_XYB_H_

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

@ -11,7 +11,6 @@
#include <utility>
#include <vector>
#include "gtest/gtest.h"
#include "lib/jxl/render_pipeline/render_pipeline_stage.h"
namespace jxl {

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

@ -98,12 +98,13 @@ jxl::CodecInOut ConvertTestImage(const std::vector<uint8_t>& buf,
} else {
color_encoding = jxl::ColorEncoding::SRGB(is_gray);
}
EXPECT_TRUE(ConvertFromExternal(
jxl::Span<const uint8_t>(buf.data(), buf.size()), xsize, ysize,
color_encoding, has_alpha,
/*alpha_is_premultiplied=*/false,
/*bits_per_sample=*/bitdepth, pixel_format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr, &io.Main(), float_in));
EXPECT_TRUE(
ConvertFromExternal(jxl::Span<const uint8_t>(buf.data(), buf.size()),
xsize, ysize, color_encoding, has_alpha,
/*alpha_is_premultiplied=*/false,
/*bits_per_sample=*/bitdepth, pixel_format.endianness,
/*flipped_y=*/false, /*pool=*/nullptr, &io.Main(),
float_in, /*align=*/0));
return io;
}
@ -239,7 +240,8 @@ void VerifyRoundtripCompression(
extra_channel_bytes.size()),
xsize, ysize, basic_info.bits_per_sample,
input_pixel_format.endianness, /*pool=*/nullptr, &alpha_channel,
/*float_in=*/input_pixel_format.data_type == JXL_TYPE_FLOAT),
/*float_in=*/input_pixel_format.data_type == JXL_TYPE_FLOAT,
/*align=*/0),
true);
original_io.metadata.m.SetAlphaBits(basic_info.bits_per_sample);

23
third_party/jpeg-xl/lib/jxl/size_constraints.h поставляемый 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_JXL_SIZE_CONSTRAINTS_H_
#define LIB_JXL_SIZE_CONSTRAINTS_H_
#include <cstdint>
namespace jxl {
struct SizeConstraints {
// Upper limit on pixel dimensions/area, enforced by VerifyDimensions
// (called from decoders). Fuzzers set smaller values to limit memory use.
uint32_t dec_max_xsize = 0xFFFFFFFFu;
uint32_t dec_max_ysize = 0xFFFFFFFFu;
uint64_t dec_max_pixels = 0xFFFFFFFFu; // Might be up to ~0ull
};
} // namespace jxl
#endif // LIB_JXL_SIZE_CONSTRAINTS_H_

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

@ -51,7 +51,7 @@ float ContinuousIDCT(const float dct[32], const float t) {
auto cos_arg = LoadU(df, kMultipliers + i) * tandhalf;
auto cos = FastCosf(df, cos_arg);
auto local_res = LoadU(df, dct + i) * cos;
result = MulAdd(Set(df, square_root<2>::value), local_res, result);
result = MulAdd(Set(df, kSqrt2), local_res, result);
}
return GetLane(SumOfLanes(df, result));
}
@ -215,17 +215,19 @@ namespace {
constexpr size_t kMaxNumControlPoints = 1u << 20u;
constexpr size_t kMaxNumControlPointsPerPixelRatio = 2;
// X, Y, B, sigma.
float ColorQuantizationWeight(const int32_t adjustment, const int channel,
const int i) {
const float multiplier = adjustment >= 0 ? 1.f + .125f * adjustment
: 1.f / (1.f + .125f * -adjustment);
static constexpr float kChannelWeight[] = {0.0042f, 0.075f, 0.07f, .3333f};
return multiplier / kChannelWeight[channel];
float AdjustedQuant(const int32_t adjustment) {
return (adjustment >= 0) ? (1.f + .125f * adjustment)
: 1.f / (1.f - .125f * adjustment);
}
float InvAdjustedQuant(const int32_t adjustment) {
return (adjustment >= 0) ? 1.f / (1.f + .125f * adjustment)
: (1.f - .125f * adjustment);
}
// X, Y, B, sigma.
static constexpr float kChannelWeight[] = {0.0042f, 0.075f, 0.07f, .3333f};
Status DecodeAllStartingPoints(std::vector<Spline::Point>* const points,
BitReader* const br, ANSSymbolReader* reader,
const std::vector<uint8_t>& context_map,
@ -272,12 +274,16 @@ Vector operator-(const Spline::Point& a, const Spline::Point& b) {
return {a.x - b.x, a.y - b.y};
}
std::vector<Spline::Point> DrawCentripetalCatmullRomSpline(
std::vector<Spline::Point> points) {
if (points.size() <= 1) return points;
// TODO(eustas): avoid making a copy of "points".
void DrawCentripetalCatmullRomSpline(std::vector<Spline::Point> points,
std::vector<Spline::Point>& result) {
if (points.empty()) return;
if (points.size() == 1) {
result.push_back(points[0]);
return;
}
// Number of points to compute between each control point.
static constexpr int kNumPoints = 16;
std::vector<Spline::Point> result;
result.reserve((points.size() - 1) * kNumPoints + 1);
points.insert(points.begin(), points[0] + (points[0] - points[1]));
points.push_back(points[points.size() - 1] +
@ -287,33 +293,38 @@ std::vector<Spline::Point> DrawCentripetalCatmullRomSpline(
// 4 of them are used, and we draw from p[1] to p[2].
const Spline::Point* const p = &points[start];
result.push_back(p[1]);
float t[4] = {0};
for (int k = 1; k < 4; ++k) {
t[k] = std::sqrt(hypotf(p[k].x - p[k - 1].x, p[k].y - p[k - 1].y)) +
t[k - 1];
float d[3];
float t[4];
t[0] = 0;
for (int k = 0; k < 3; ++k) {
// TODO(eustas): for each segment delta is calculated 3 times...
// TODO(eustas): restrict d[k] with reasonable limit and spec it.
d[k] = std::sqrt(hypotf(p[k + 1].x - p[k].x, p[k + 1].y - p[k].y));
t[k + 1] = t[k] + d[k];
}
for (int i = 1; i < kNumPoints; ++i) {
const float tt =
t[1] + (static_cast<float>(i) / kNumPoints) * (t[2] - t[1]);
const float tt = d[0] + (static_cast<float>(i) / kNumPoints) * d[1];
Spline::Point a[3];
for (int k = 0; k < 3; ++k) {
a[k] = p[k] + ((tt - t[k]) / (t[k + 1] - t[k])) * (p[k + 1] - p[k]);
// TODO(eustas): reciprocal multiplication would be faster.
a[k] = p[k] + ((tt - t[k]) / d[k]) * (p[k + 1] - p[k]);
}
Spline::Point b[2];
for (int k = 0; k < 2; ++k) {
b[k] = a[k] + ((tt - t[k]) / (t[k + 2] - t[k])) * (a[k + 1] - a[k]);
b[k] = a[k] + ((tt - t[k]) / (d[k] + d[k + 1])) * (a[k + 1] - a[k]);
}
result.push_back(b[0] + ((tt - t[1]) / (t[2] - t[1])) * (b[1] - b[0]));
result.push_back(b[0] + ((tt - t[1]) / d[1]) * (b[1] - b[0]));
}
}
result.push_back(points[points.size() - 2]);
return result;
}
// Move along the line segments defined by `points`, `kDesiredRenderingDistance`
// pixels at a time, and call `functor` with each point and the actual distance
// to the previous point (which will always be kDesiredRenderingDistance except
// possibly for the very last point).
// TODO(eustas): this method always adds the last point, but never the first
// (unless those are one); I believe both ends matter.
template <typename Points, typename Functor>
bool ForEachEquallySpacedPoint(const Points& points, const Functor& functor) {
JXL_ASSERT(!points.empty());
@ -352,7 +363,7 @@ bool ForEachEquallySpacedPoint(const Points& points, const Functor& functor) {
QuantizedSpline::QuantizedSpline(const Spline& original,
const int32_t quantization_adjustment,
const float ytox, const float ytob) {
const float y_to_x, const float y_to_b) {
JXL_ASSERT(!original.control_points.empty());
control_points_.reserve(original.control_points.size() - 1);
const Spline::Point& starting_point = original.control_points.front();
@ -373,28 +384,34 @@ QuantizedSpline::QuantizedSpline(const Spline& original,
previous_y = new_y;
}
for (int c = 0; c < 3; ++c) {
float factor = c == 0 ? ytox : c == 1 ? 0 : ytob;
const auto to_int = [](float v) -> int {
return static_cast<int>(roundf(v));
};
const auto quant = AdjustedQuant(quantization_adjustment);
const auto inv_quant = InvAdjustedQuant(quantization_adjustment);
for (int c : {1, 0, 2}) {
float factor = (c == 0) ? y_to_x : (c == 1) ? 0 : y_to_b;
for (int i = 0; i < 32; ++i) {
const float coefficient =
original.color_dct[c][i] -
factor * color_dct_[1][i] /
ColorQuantizationWeight(quantization_adjustment, 1, i);
color_dct_[c][i] = static_cast<int>(
roundf(coefficient *
ColorQuantizationWeight(quantization_adjustment, c, i)));
const float dct_factor = (i == 0) ? kSqrt2 : 1.0f;
const float inv_dct_factor = (i == 0) ? kSqrt0_5 : 1.0f;
auto restored_y =
color_dct_[1][i] * inv_dct_factor * kChannelWeight[1] * inv_quant;
auto decorellated = original.color_dct[c][i] - factor * restored_y;
color_dct_[c][i] =
to_int(decorellated * dct_factor * quant / kChannelWeight[c]);
}
}
for (int i = 0; i < 32; ++i) {
sigma_dct_[i] = static_cast<int>(
roundf(original.sigma_dct[i] *
ColorQuantizationWeight(quantization_adjustment, 3, i)));
const float dct_factor = (i == 0) ? kSqrt2 : 1.0f;
sigma_dct_[i] =
to_int(original.sigma_dct[i] * dct_factor * quant / kChannelWeight[3]);
}
}
Status QuantizedSpline::Dequantize(const Spline::Point& starting_point,
const int32_t quantization_adjustment,
const float ytox, const float ytob,
const float y_to_x, const float y_to_b,
Spline& result) const {
result.control_points.clear();
result.control_points.reserve(control_points_.size() + 1);
@ -426,21 +443,22 @@ Status QuantizedSpline::Dequantize(const Spline::Point& starting_point,
static_cast<float>(current_x), static_cast<float>(current_y)});
}
const auto inv_quant = InvAdjustedQuant(quantization_adjustment);
for (int c = 0; c < 3; ++c) {
for (int i = 0; i < 32; ++i) {
const float inv_dct_factor = (i == 0) ? kSqrt0_5 : 1.0f;
result.color_dct[c][i] =
color_dct_[c][i] * (i == 0 ? 1.0f / square_root<2>::value : 1.0f) /
ColorQuantizationWeight(quantization_adjustment, c, i);
color_dct_[c][i] * inv_dct_factor * kChannelWeight[c] * inv_quant;
}
}
for (int i = 0; i < 32; ++i) {
result.color_dct[0][i] += ytox * result.color_dct[1][i];
result.color_dct[2][i] += ytob * result.color_dct[1][i];
result.color_dct[0][i] += y_to_x * result.color_dct[1][i];
result.color_dct[2][i] += y_to_b * result.color_dct[1][i];
}
for (int i = 0; i < 32; ++i) {
const float inv_dct_factor = (i == 0) ? kSqrt0_5 : 1.0f;
result.sigma_dct[i] =
sigma_dct_[i] * (i == 0 ? 1.0f / square_root<2>::value : 1.0f) /
ColorQuantizationWeight(quantization_adjustment, 3, i);
sigma_dct_[i] * inv_dct_factor * kChannelWeight[3] * inv_quant;
}
return true;
@ -567,6 +585,7 @@ Status Splines::InitializeDrawCache(const size_t image_xsize,
size_t px_limit = (pixel_limit < static_cast<float>(kHardPixelLimit))
? static_cast<size_t>(pixel_limit)
: kHardPixelLimit;
std::vector<Spline::Point> intermediate_points;
for (size_t i = 0; i < splines_.size(); ++i) {
JXL_RETURN_IF_ERROR(
splines_[i].Dequantize(starting_points_[i], quantization_adjustment_,
@ -574,6 +593,8 @@ Status Splines::InitializeDrawCache(const size_t image_xsize,
if (std::adjacent_find(spline.control_points.begin(),
spline.control_points.end()) !=
spline.control_points.end()) {
// Otherwise division by zero might occur. Once control points coincide,
// the direction of curve is undefined...
return JXL_FAILURE(
"identical successive control points in spline %" PRIuS, i);
}
@ -583,9 +604,9 @@ Status Splines::InitializeDrawCache(const size_t image_xsize,
points_to_draw.emplace_back(point, multiplier);
return (points_to_draw.size() <= px_limit);
};
if (!ForEachEquallySpacedPoint(
DrawCentripetalCatmullRomSpline(spline.control_points),
add_point)) {
intermediate_points.clear();
DrawCentripetalCatmullRomSpline(spline.control_points, intermediate_points);
if (!ForEachEquallySpacedPoint(intermediate_points, add_point)) {
return JXL_FAILURE("Too many pixels covered with splines");
}
const float arc_length =

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

@ -277,17 +277,23 @@ TEST(SplinesTest, DuplicatePoints) {
TEST(SplinesTest, Drawing) {
CodecInOut io_expected;
const PaddedBytes orig = ReadTestData("jxl/splines.png");
const PaddedBytes orig = ReadTestData("jxl/splines.pfm");
ASSERT_TRUE(SetFromBytes(Span<const uint8_t>(orig), &io_expected,
/*pool=*/nullptr));
std::vector<Spline::Point> control_points{{9, 54}, {118, 159}, {97, 3},
{10, 40}, {150, 25}, {120, 300}};
// Use values that survive quant/decorellation roundtrip.
const Spline spline{
control_points,
/*color_dct=*/
{{0.03125f, 0.00625f, 0.003125f}, {1.f, 0.321875f}, {1.f, 0.24375f}},
/*sigma_dct=*/{0.3125f, 0.f, 0.f, 0.0625f}};
{{0.4989345073699951171875000f, 0.4997999966144561767578125f},
{0.4772970676422119140625000f, 0.f, 0.5250000357627868652343750f},
{-0.0176776945590972900390625f, 0.4900000095367431640625000f,
0.5250000357627868652343750f}},
/*sigma_dct=*/
{0.9427147507667541503906250f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f,
0.6665999889373779296875000f}};
std::vector<Spline> spline_data = {spline};
std::vector<QuantizedSpline> quantized_splines;
std::vector<Spline::Point> starting_points;
@ -304,12 +310,8 @@ TEST(SplinesTest, Drawing) {
ASSERT_TRUE(splines.InitializeDrawCache(image.xsize(), image.ysize(), *cmap));
splines.AddTo(&image, Rect(image), Rect(image));
OpsinParams opsin_params{};
opsin_params.Init(kDefaultIntensityTarget);
(void)OpsinToLinearInplace(&image, /*pool=*/nullptr, opsin_params);
CodecInOut io_actual;
io_actual.SetFromImage(CopyImage(image), ColorEncoding::LinearSRGB());
io_actual.SetFromImage(CopyImage(image), ColorEncoding::SRGB());
ASSERT_TRUE(
io_actual.TransformTo(io_expected.Main().c_current(), GetJxlCms()));

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