Bug 1731773 - Part 1: Run mach vendor for libjxl and highway r=tnikkel

Just library version updates, nothing else.

Differential Revision: https://phabricator.services.mozilla.com/D126359
This commit is contained in:
Kagami Sascha Rosylight 2021-09-28 10:54:24 +00:00
parent 057a1bac29
commit 622ed0f491
296 changed files with 15179 additions and 7669 deletions

View file

@ -20,11 +20,11 @@ origin:
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
release: commit d9882104caf9fb060d328d62cac9ce0a05a191db (2021-05-21T10:02:16Z).
release: commit e2397743fe092df68b760d358253773699a16c93 (2021-06-09T08:56:32Z).
# Revision to pull in
# Must be a long or short commit SHA (long preferred)
revision: d9882104caf9fb060d328d62cac9ce0a05a191db
revision: e2397743fe092df68b760d358253773699a16c93
# The package's license, where possible using the mnemonic from
# https://spdx.org/licenses/

View file

@ -8,5 +8,6 @@
#define JXL_EXPORT_H
#define JXL_EXPORT
#define JXL_DEPRECATED [[deprecated]]
#endif /* JXL_EXPORT_H */

View file

@ -20,7 +20,6 @@ SOURCES += [
"/third_party/jpeg-xl/lib/jxl/base/descriptive_statistics.cc",
"/third_party/jpeg-xl/lib/jxl/base/padded_bytes.cc",
"/third_party/jpeg-xl/lib/jxl/base/status.cc",
"/third_party/jpeg-xl/lib/jxl/base/time.cc",
"/third_party/jpeg-xl/lib/jxl/blending.cc",
"/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc",
"/third_party/jpeg-xl/lib/jxl/coeff_order.cc",
@ -30,6 +29,7 @@ SOURCES += [
"/third_party/jpeg-xl/lib/jxl/convolve.cc",
"/third_party/jpeg-xl/lib/jxl/dct_scales.cc",
"/third_party/jpeg-xl/lib/jxl/dec_ans.cc",
"/third_party/jpeg-xl/lib/jxl/dec_cache.cc",
"/third_party/jpeg-xl/lib/jxl/dec_context_map.cc",
"/third_party/jpeg-xl/lib/jxl/dec_external_image.cc",
"/third_party/jpeg-xl/lib/jxl/dec_frame.cc",
@ -43,6 +43,7 @@ SOURCES += [
"/third_party/jpeg-xl/lib/jxl/dec_upsample.cc",
"/third_party/jpeg-xl/lib/jxl/dec_xyb.cc",
"/third_party/jpeg-xl/lib/jxl/decode.cc",
"/third_party/jpeg-xl/lib/jxl/decode_to_jpeg.cc",
"/third_party/jpeg-xl/lib/jxl/enc_bit_writer.cc",
"/third_party/jpeg-xl/lib/jxl/entropy_coder.cc",
"/third_party/jpeg-xl/lib/jxl/epf.cc",
@ -66,6 +67,8 @@ SOURCES += [
"/third_party/jpeg-xl/lib/jxl/modular/encoding/dec_ma.cc",
"/third_party/jpeg-xl/lib/jxl/modular/encoding/encoding.cc",
"/third_party/jpeg-xl/lib/jxl/modular/modular_image.cc",
"/third_party/jpeg-xl/lib/jxl/modular/transform/rct.cc",
"/third_party/jpeg-xl/lib/jxl/modular/transform/squeeze.cc",
"/third_party/jpeg-xl/lib/jxl/modular/transform/transform.cc",
"/third_party/jpeg-xl/lib/jxl/opsin_params.cc",
"/third_party/jpeg-xl/lib/jxl/passes_state.cc",

View file

@ -1,54 +1,54 @@
# 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: jpeg-xl
description: JPEG XL image format reference implementation
# Full URL for the package's homepage/etc
# Usually different from repository url
url: https://gitlab.com/wg1/jpeg-xl
# Human-readable identifier for this version/release
# Generally "version NNN", "tag SSS", "bookmark SSS"
release: commit bdde644b94c125a15e532b2572b96306371a7d4e (2021-05-25T19:02:18.000+02:00).
# Revision to pull in
# Must be a long or short commit SHA (long preferred)
# NOTE(krosylight): Update highway together when updating this!
revision: bdde644b94c125a15e532b2572b96306371a7d4e
# 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
updatebot:
maintainer-phab: saschanaz
maintainer-bz: krosylight@mozilla.com
tasks:
- type: vendoring
enabled: True
frequency: 3 weeks
vendoring:
url: https://gitlab.com/wg1/jpeg-xl.git
source-hosting: gitlab
vendor-directory: third_party/jpeg-xl
exclude:
- doc/
- third_party/testdata/
- tools/
# 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 702cd54420513b754edb60e33b9a4438f7e9ca00 (2021-09-28T08:19:14Z).
# Revision to pull in
# Must be a long or short commit SHA (long preferred)
# NOTE(krosylight): Update highway together when updating this!
revision: 702cd54420513b754edb60e33b9a4438f7e9ca00
# 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
updatebot:
maintainer-phab: saschanaz
maintainer-bz: krosylight@mozilla.com
tasks:
- type: vendoring
enabled: True
frequency: 3 weeks
vendoring:
url: https://github.com/libjxl/libjxl.git
source-hosting: github
vendor-directory: third_party/jpeg-xl
exclude:
- doc/
- third_party/testdata/
- tools/

View file

@ -103,6 +103,11 @@ cc_library(
],
compatible_with = [],
copts = COPTS,
# TODO(janwas): remove once WASM toolchain supports *extend instead of *widen
defines = select({
"//tools/cc_target_os:wasm": ["HWY_WASM_OLD_NAMES"],
"//conditions:default": [],
}),
textual_hdrs = [
"hwy/foreach_target.h", # public
"hwy/ops/arm_neon-inl.h",

View file

@ -19,7 +19,7 @@ if(POLICY CMP0083)
cmake_policy(SET CMP0083 NEW)
endif()
project(hwy VERSION 0.12.1) # Keep in sync with highway.h version
project(hwy VERSION 0.12.2) # Keep in sync with highway.h version
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
@ -307,8 +307,11 @@ foreach (TESTFILE IN LISTS HWY_TEST_FILES)
get_filename_component(TESTNAME ${TESTFILE} NAME_WE)
add_executable(${TESTNAME} ${TESTFILE})
target_compile_options(${TESTNAME} PRIVATE ${HWY_FLAGS})
# Test all targets, not just the best/baseline.
target_compile_options(${TESTNAME} PRIVATE -DHWY_COMPILE_ALL_ATTAINABLE=1)
# Test all targets, not just the best/baseline. This changes the default
# policy to all-attainable; note that setting -DHWY_COMPILE_* directly can
# cause compile errors because only one may be set, and other CMakeLists.txt
# that include us may set them.
target_compile_options(${TESTNAME} PRIVATE -DHWY_IS_TEST=1)
if(HWY_SYSTEM_GTEST)
target_link_libraries(${TESTNAME} hwy hwy_contrib GTest::GTest GTest::Main)

View file

@ -1,3 +1,10 @@
highway (0.12.2-1) UNRELEASED; urgency=medium
* fix scalar-only test and Windows macro conflict with Load/StoreFence
* replace deprecated wasm intrinsics
-- Jan Wassenberg <janwas@google.com> Mon, 31 May 2021 16:00:00 +0200
highway (0.12.1-1) UNRELEASED; urgency=medium
* doc updates, ARM GCC support, fix s390/ppc, complete partial vectors

View file

@ -32,6 +32,14 @@
#include <emmintrin.h> // SSE2
#endif
// Windows.h #defines these, which causes infinite recursion. Temporarily
// undefine them in this header; these functions are anyway deprecated.
// TODO(janwas): remove when these functions are removed.
#pragma push_macro("LoadFence")
#pragma push_macro("StoreFence")
#undef LoadFence
#undef StoreFence
namespace hwy {
// Even if N*sizeof(T) is smaller, Stream may write a multiple of this size.
@ -92,4 +100,8 @@ HWY_INLINE HWY_ATTR_CACHE void Pause() {
} // namespace hwy
// TODO(janwas): remove when these functions are removed. (See above.)
#pragma pop_macro("StoreFence")
#pragma pop_macro("LoadFence")
#endif // HIGHWAY_HWY_CACHE_CONTROL_H_

View file

@ -28,7 +28,7 @@ namespace hwy {
// API version (https://semver.org/); keep in sync with CMakeLists.txt.
#define HWY_MAJOR 0
#define HWY_MINOR 12
#define HWY_PATCH 1
#define HWY_PATCH 2
//------------------------------------------------------------------------------
// Shorthand for descriptors (defined in shared-inl.h) used to select overloads.

View file

@ -22,6 +22,28 @@
#include "hwy/base.h"
#include "hwy/ops/shared-inl.h"
#ifdef HWY_WASM_OLD_NAMES
#define wasm_i8x16_shuffle wasm_v8x16_shuffle
#define wasm_i16x8_shuffle wasm_v16x8_shuffle
#define wasm_i32x4_shuffle wasm_v32x4_shuffle
#define wasm_i64x2_shuffle wasm_v64x2_shuffle
#define wasm_u16x8_extend_low_u8x16 wasm_i16x8_widen_low_u8x16
#define wasm_u32x4_extend_low_u16x8 wasm_i32x4_widen_low_u16x8
#define wasm_i32x4_extend_low_i16x8 wasm_i32x4_widen_low_i16x8
#define wasm_i16x8_extend_low_i8x16 wasm_i16x8_widen_low_i8x16
#define wasm_u32x4_extend_high_u16x8 wasm_i32x4_widen_high_u16x8
#define wasm_i32x4_extend_high_i16x8 wasm_i32x4_widen_high_i16x8
#define wasm_i32x4_trunc_sat_f32x4 wasm_i32x4_trunc_saturate_f32x4
#define wasm_u8x16_add_sat wasm_u8x16_add_saturate
#define wasm_u8x16_sub_sat wasm_u8x16_sub_saturate
#define wasm_u16x8_add_sat wasm_u16x8_add_saturate
#define wasm_u16x8_sub_sat wasm_u16x8_sub_saturate
#define wasm_i8x16_add_sat wasm_i8x16_add_saturate
#define wasm_i8x16_sub_sat wasm_i8x16_sub_saturate
#define wasm_i16x8_add_sat wasm_i16x8_add_saturate
#define wasm_i16x8_sub_sat wasm_i16x8_sub_saturate
#endif
HWY_BEFORE_NAMESPACE();
namespace hwy {
namespace HWY_NAMESPACE {
@ -281,24 +303,24 @@ HWY_API Vec128<float, N> operator-(const Vec128<float, N> a,
template <size_t N>
HWY_API Vec128<uint8_t, N> SaturatedAdd(const Vec128<uint8_t, N> a,
const Vec128<uint8_t, N> b) {
return Vec128<uint8_t, N>{wasm_u8x16_add_saturate(a.raw, b.raw)};
return Vec128<uint8_t, N>{wasm_u8x16_add_sat(a.raw, b.raw)};
}
template <size_t N>
HWY_API Vec128<uint16_t, N> SaturatedAdd(const Vec128<uint16_t, N> a,
const Vec128<uint16_t, N> b) {
return Vec128<uint16_t, N>{wasm_u16x8_add_saturate(a.raw, b.raw)};
return Vec128<uint16_t, N>{wasm_u16x8_add_sat(a.raw, b.raw)};
}
// Signed
template <size_t N>
HWY_API Vec128<int8_t, N> SaturatedAdd(const Vec128<int8_t, N> a,
const Vec128<int8_t, N> b) {
return Vec128<int8_t, N>{wasm_i8x16_add_saturate(a.raw, b.raw)};
return Vec128<int8_t, N>{wasm_i8x16_add_sat(a.raw, b.raw)};
}
template <size_t N>
HWY_API Vec128<int16_t, N> SaturatedAdd(const Vec128<int16_t, N> a,
const Vec128<int16_t, N> b) {
return Vec128<int16_t, N>{wasm_i16x8_add_saturate(a.raw, b.raw)};
return Vec128<int16_t, N>{wasm_i16x8_add_sat(a.raw, b.raw)};
}
// ------------------------------ Saturating subtraction
@ -309,24 +331,24 @@ HWY_API Vec128<int16_t, N> SaturatedAdd(const Vec128<int16_t, N> a,
template <size_t N>
HWY_API Vec128<uint8_t, N> SaturatedSub(const Vec128<uint8_t, N> a,
const Vec128<uint8_t, N> b) {
return Vec128<uint8_t, N>{wasm_u8x16_sub_saturate(a.raw, b.raw)};
return Vec128<uint8_t, N>{wasm_u8x16_sub_sat(a.raw, b.raw)};
}
template <size_t N>
HWY_API Vec128<uint16_t, N> SaturatedSub(const Vec128<uint16_t, N> a,
const Vec128<uint16_t, N> b) {
return Vec128<uint16_t, N>{wasm_u16x8_sub_saturate(a.raw, b.raw)};
return Vec128<uint16_t, N>{wasm_u16x8_sub_sat(a.raw, b.raw)};
}
// Signed
template <size_t N>
HWY_API Vec128<int8_t, N> SaturatedSub(const Vec128<int8_t, N> a,
const Vec128<int8_t, N> b) {
return Vec128<int8_t, N>{wasm_i8x16_sub_saturate(a.raw, b.raw)};
return Vec128<int8_t, N>{wasm_i8x16_sub_sat(a.raw, b.raw)};
}
template <size_t N>
HWY_API Vec128<int16_t, N> SaturatedSub(const Vec128<int16_t, N> a,
const Vec128<int16_t, N> b) {
return Vec128<int16_t, N>{wasm_i16x8_sub_saturate(a.raw, b.raw)};
return Vec128<int16_t, N>{wasm_i16x8_sub_sat(a.raw, b.raw)};
}
// ------------------------------ Average
@ -679,29 +701,29 @@ template <size_t N>
HWY_API Vec128<uint16_t, N> MulHigh(const Vec128<uint16_t, N> a,
const Vec128<uint16_t, N> b) {
// TODO(eustas): replace, when implemented in WASM.
const auto al = wasm_i32x4_widen_low_u16x8(a.raw);
const auto ah = wasm_i32x4_widen_high_u16x8(a.raw);
const auto bl = wasm_i32x4_widen_low_u16x8(b.raw);
const auto bh = wasm_i32x4_widen_high_u16x8(b.raw);
const auto al = wasm_u32x4_extend_low_u16x8(a.raw);
const auto ah = wasm_u32x4_extend_high_u16x8(a.raw);
const auto bl = wasm_u32x4_extend_low_u16x8(b.raw);
const auto bh = wasm_u32x4_extend_high_u16x8(b.raw);
const auto l = wasm_i32x4_mul(al, bl);
const auto h = wasm_i32x4_mul(ah, bh);
// TODO(eustas): shift-right + narrow?
return Vec128<uint16_t, N>{
wasm_v16x8_shuffle(l, h, 1, 3, 5, 7, 9, 11, 13, 15)};
wasm_i16x8_shuffle(l, h, 1, 3, 5, 7, 9, 11, 13, 15)};
}
template <size_t N>
HWY_API Vec128<int16_t, N> MulHigh(const Vec128<int16_t, N> a,
const Vec128<int16_t, N> b) {
// TODO(eustas): replace, when implemented in WASM.
const auto al = wasm_i32x4_widen_low_i16x8(a.raw);
const auto ah = wasm_i32x4_widen_high_i16x8(a.raw);
const auto bl = wasm_i32x4_widen_low_i16x8(b.raw);
const auto bh = wasm_i32x4_widen_high_i16x8(b.raw);
const auto al = wasm_i32x4_extend_low_i16x8(a.raw);
const auto ah = wasm_i32x4_extend_high_i16x8(a.raw);
const auto bl = wasm_i32x4_extend_low_i16x8(b.raw);
const auto bh = wasm_i32x4_extend_high_i16x8(b.raw);
const auto l = wasm_i32x4_mul(al, bl);
const auto h = wasm_i32x4_mul(ah, bh);
// TODO(eustas): shift-right + narrow?
return Vec128<int16_t, N>{
wasm_v16x8_shuffle(l, h, 1, 3, 5, 7, 9, 11, 13, 15)};
wasm_i16x8_shuffle(l, h, 1, 3, 5, 7, 9, 11, 13, 15)};
}
// Multiplies even lanes (0, 2 ..) and returns the double-width result.
@ -997,12 +1019,12 @@ HWY_API Mask128<int64_t, N> operator>(const Vec128<int64_t, N> a,
// Otherwise, the lower half decides.
const auto m_eq = a32 == b32;
const auto lo_in_hi = wasm_v32x4_shuffle(m_gt, m_gt, 2, 2, 0, 0);
const auto lo_in_hi = wasm_i32x4_shuffle(m_gt, m_gt, 2, 2, 0, 0);
const auto lo_gt = And(m_eq, lo_in_hi);
const auto gt = Or(lo_gt, m_gt);
// Copy result in upper 32 bits to lower 32 bits.
return Mask128<int64_t, N>{wasm_v32x4_shuffle(gt, gt, 3, 3, 1, 1)};
return Mask128<int64_t, N>{wasm_i32x4_shuffle(gt, gt, 3, 3, 1, 1)};
}
template <size_t N>
@ -1496,12 +1518,12 @@ HWY_API Vec128<T, N / 2> LowerHalf(Vec128<T, N> v) {
template <typename T>
HWY_API Vec128<T, 8 / sizeof(T)> UpperHalf(Vec128<T> v) {
// TODO(eustas): use swizzle?
return Vec128<T, 8 / sizeof(T)>{wasm_v32x4_shuffle(v.raw, v.raw, 2, 3, 2, 3)};
return Vec128<T, 8 / sizeof(T)>{wasm_i32x4_shuffle(v.raw, v.raw, 2, 3, 2, 3)};
}
template <>
HWY_INLINE Vec128<float, 2> UpperHalf(Vec128<float> v) {
// TODO(eustas): use swizzle?
return Vec128<float, 2>{wasm_v32x4_shuffle(v.raw, v.raw, 2, 3, 2, 3)};
return Vec128<float, 2>{wasm_i32x4_shuffle(v.raw, v.raw, 2, 3, 2, 3)};
}
// ------------------------------ Shift vector by constant #bytes
@ -1516,64 +1538,64 @@ HWY_API Vec128<T> ShiftLeftBytes(const Vec128<T> v) {
return v;
case 1:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 0, 1, 2, 3, 4, 5, 6,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 0, 1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14)};
case 2:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 0, 1, 2, 3, 4, 5,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 0, 1, 2, 3, 4, 5,
6, 7, 8, 9, 10, 11, 12, 13)};
case 3:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 0, 1, 2, 3,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, 10, 11, 12)};
case 4:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 0, 1, 2,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 0, 1, 2,
3, 4, 5, 6, 7, 8, 9, 10, 11)};
case 5:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 0, 1,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 0, 1,
2, 3, 4, 5, 6, 7, 8, 9, 10)};
case 6:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
0, 1, 2, 3, 4, 5, 6, 7, 8, 9)};
case 7:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
16, 0, 1, 2, 3, 4, 5, 6, 7, 8)};
case 8:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
16, 16, 0, 1, 2, 3, 4, 5, 6, 7)};
case 9:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
16, 16, 16, 0, 1, 2, 3, 4, 5, 6)};
case 10:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 0, 1, 2, 3, 4, 5)};
case 11:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 0, 1, 2, 3, 4)};
case 12:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 0, 1, 2, 3)};
case 13:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 0, 1, 2)};
case 14:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 0,
1)};
case 15:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 16, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16,
0)};
}
@ -1597,69 +1619,69 @@ HWY_API Vec128<T> ShiftRightBytes(const Vec128<T> v) {
return v;
case 1:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 1, 2, 3, 4, 5, 6, 7, 8,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16)};
case 2:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 2, 3, 4, 5, 6, 7, 8, 9,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 16)};
case 3:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 3, 4, 5, 6, 7, 8, 9, 10,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 16, 16)};
case 4:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 4, 5, 6, 7, 8, 9, 10, 11,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 4, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 16, 16, 16)};
case 5:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 5, 6, 7, 8, 9, 10, 11,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 16, 16, 16, 16)};
case 6:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 6, 7, 8, 9, 10, 11, 12,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 6, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 16, 16, 16, 16, 16)};
case 7:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 7, 8, 9, 10, 11, 12, 13,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 7, 8, 9, 10, 11, 12, 13,
14, 15, 16, 16, 16, 16, 16, 16, 16)};
case 8:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 8, 9, 10, 11, 12, 13, 14,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 8, 9, 10, 11, 12, 13, 14,
15, 16, 16, 16, 16, 16, 16, 16, 16)};
case 9:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 9, 10, 11, 12, 13, 14,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 9, 10, 11, 12, 13, 14,
15, 16, 16, 16, 16, 16, 16, 16, 16,
16)};
case 10:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 10, 11, 12, 13, 14, 15,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 10, 11, 12, 13, 14, 15,
16, 16, 16, 16, 16, 16, 16, 16, 16,
16)};
case 11:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 11, 12, 13, 14, 15, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 11, 12, 13, 14, 15, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16,
16)};
case 12:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 12, 13, 14, 15, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 12, 13, 14, 15, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16,
16)};
case 13:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 13, 14, 15, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 13, 14, 15, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16,
16)};
case 14:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 14, 15, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 14, 15, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16,
16)};
case 15:
return Vec128<T>{wasm_v8x16_shuffle(v.raw, zero, 15, 16, 16, 16, 16, 16,
return Vec128<T>{wasm_i8x16_shuffle(v.raw, zero, 15, 16, 16, 16, 16, 16,
16, 16, 16, 16, 16, 16, 16, 16, 16,
16)};
}
@ -1685,72 +1707,72 @@ HWY_API Vec128<T> CombineShiftRightBytes(const Vec128<T> hi,
return lo;
case 1:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 1, 2, 3, 4, 5, 6, 7,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15, 16)};
case 2:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 2, 3, 4, 5, 6, 7, 8,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16, 17)};
case 3:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 3, 4, 5, 6, 7, 8, 9,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18)};
case 4:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 4, 5, 6, 7, 8, 9, 10,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19)};
case 5:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 5, 6, 7, 8, 9, 10, 11,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 5, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20)};
case 6:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 6, 7, 8, 9, 10, 11,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 6, 7, 8, 9, 10, 11,
12, 13, 14, 15, 16, 17, 18, 19, 20,
21)};
case 7:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 7, 8, 9, 10, 11, 12,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 7, 8, 9, 10, 11, 12,
13, 14, 15, 16, 17, 18, 19, 20, 21,
22)};
case 8:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 8, 9, 10, 11, 12, 13,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 8, 9, 10, 11, 12, 13,
14, 15, 16, 17, 18, 19, 20, 21, 22,
23)};
case 9:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 9, 10, 11, 12, 13, 14,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23,
24)};
case 10:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 10, 11, 12, 13, 14,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23,
24, 25)};
case 11:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 11, 12, 13, 14, 15,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26)};
case 12:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 12, 13, 14, 15, 16,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27)};
case 13:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 13, 14, 15, 16, 17,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 13, 14, 15, 16, 17,
18, 19, 20, 21, 22, 23, 24, 25, 26,
27, 28)};
case 14:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 14, 15, 16, 17, 18,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27,
28, 29)};
case 15:
return Vec128<T>{wasm_v8x16_shuffle(lo.raw, hi.raw, 15, 16, 17, 18, 19,
return Vec128<T>{wasm_i8x16_shuffle(lo.raw, hi.raw, 15, 16, 17, 18, 19,
20, 21, 22, 23, 24, 25, 26, 27, 28,
29, 30)};
}
@ -1763,28 +1785,28 @@ HWY_API Vec128<T> CombineShiftRightBytes(const Vec128<T> hi,
template <int kLane, size_t N>
HWY_API Vec128<uint16_t, N> Broadcast(const Vec128<uint16_t, N> v) {
static_assert(0 <= kLane && kLane < N, "Invalid lane");
return Vec128<uint16_t, N>{wasm_v16x8_shuffle(
return Vec128<uint16_t, N>{wasm_i16x8_shuffle(
v.raw, v.raw, kLane, kLane, kLane, kLane, kLane, kLane, kLane, kLane)};
}
template <int kLane, size_t N>
HWY_API Vec128<uint32_t, N> Broadcast(const Vec128<uint32_t, N> v) {
static_assert(0 <= kLane && kLane < N, "Invalid lane");
return Vec128<uint32_t, N>{
wasm_v32x4_shuffle(v.raw, v.raw, kLane, kLane, kLane, kLane)};
wasm_i32x4_shuffle(v.raw, v.raw, kLane, kLane, kLane, kLane)};
}
// Signed
template <int kLane, size_t N>
HWY_API Vec128<int16_t, N> Broadcast(const Vec128<int16_t, N> v) {
static_assert(0 <= kLane && kLane < N, "Invalid lane");
return Vec128<int16_t, N>{wasm_v16x8_shuffle(
return Vec128<int16_t, N>{wasm_i16x8_shuffle(
v.raw, v.raw, kLane, kLane, kLane, kLane, kLane, kLane, kLane, kLane)};
}
template <int kLane, size_t N>
HWY_API Vec128<int32_t, N> Broadcast(const Vec128<int32_t, N> v) {
static_assert(0 <= kLane && kLane < N, "Invalid lane");
return Vec128<int32_t, N>{
wasm_v32x4_shuffle(v.raw, v.raw, kLane, kLane, kLane, kLane)};
wasm_i32x4_shuffle(v.raw, v.raw, kLane, kLane, kLane, kLane)};
}
// Float
@ -1792,7 +1814,7 @@ template <int kLane, size_t N>
HWY_API Vec128<float, N> Broadcast(const Vec128<float, N> v) {
static_assert(0 <= kLane && kLane < N, "Invalid lane");
return Vec128<float, N>{
wasm_v32x4_shuffle(v.raw, v.raw, kLane, kLane, kLane, kLane)};
wasm_i32x4_shuffle(v.raw, v.raw, kLane, kLane, kLane, kLane)};
}
// ------------------------------ Shuffle bytes with variable indices
@ -1807,7 +1829,7 @@ HWY_API Vec128<T, N> TableLookupBytes(const Vec128<T, N> bytes,
// V8 implementation of this had a bug, fixed on 2021-04-03:
// https://chromium-review.googlesource.com/c/v8/v8/+/2822951
#if 0
return Vec128<T, N>{wasm_v8x16_swizzle(bytes.raw, from.raw)};
return Vec128<T, N>{wasm_i8x16_swizzle(bytes.raw, from.raw)};
#else
alignas(16) uint8_t control[16];
alignas(16) uint8_t input[16];
@ -1830,56 +1852,56 @@ HWY_API Vec128<T, N> TableLookupBytes(const Vec128<T, N> bytes,
// Swap 32-bit halves in 64-bit halves.
HWY_API Vec128<uint32_t> Shuffle2301(const Vec128<uint32_t> v) {
return Vec128<uint32_t>{wasm_v32x4_shuffle(v.raw, v.raw, 1, 0, 3, 2)};
return Vec128<uint32_t>{wasm_i32x4_shuffle(v.raw, v.raw, 1, 0, 3, 2)};
}
HWY_API Vec128<int32_t> Shuffle2301(const Vec128<int32_t> v) {
return Vec128<int32_t>{wasm_v32x4_shuffle(v.raw, v.raw, 1, 0, 3, 2)};
return Vec128<int32_t>{wasm_i32x4_shuffle(v.raw, v.raw, 1, 0, 3, 2)};
}
HWY_API Vec128<float> Shuffle2301(const Vec128<float> v) {
return Vec128<float>{wasm_v32x4_shuffle(v.raw, v.raw, 1, 0, 3, 2)};
return Vec128<float>{wasm_i32x4_shuffle(v.raw, v.raw, 1, 0, 3, 2)};
}
// Swap 64-bit halves
HWY_API Vec128<uint32_t> Shuffle1032(const Vec128<uint32_t> v) {
return Vec128<uint32_t>{wasm_v64x2_shuffle(v.raw, v.raw, 1, 0)};
return Vec128<uint32_t>{wasm_i64x2_shuffle(v.raw, v.raw, 1, 0)};
}
HWY_API Vec128<int32_t> Shuffle1032(const Vec128<int32_t> v) {
return Vec128<int32_t>{wasm_v64x2_shuffle(v.raw, v.raw, 1, 0)};
return Vec128<int32_t>{wasm_i64x2_shuffle(v.raw, v.raw, 1, 0)};
}
HWY_API Vec128<float> Shuffle1032(const Vec128<float> v) {
return Vec128<float>{wasm_v64x2_shuffle(v.raw, v.raw, 1, 0)};
return Vec128<float>{wasm_i64x2_shuffle(v.raw, v.raw, 1, 0)};
}
// Rotate right 32 bits
HWY_API Vec128<uint32_t> Shuffle0321(const Vec128<uint32_t> v) {
return Vec128<uint32_t>{wasm_v32x4_shuffle(v.raw, v.raw, 1, 2, 3, 0)};
return Vec128<uint32_t>{wasm_i32x4_shuffle(v.raw, v.raw, 1, 2, 3, 0)};
}
HWY_API Vec128<int32_t> Shuffle0321(const Vec128<int32_t> v) {
return Vec128<int32_t>{wasm_v32x4_shuffle(v.raw, v.raw, 1, 2, 3, 0)};
return Vec128<int32_t>{wasm_i32x4_shuffle(v.raw, v.raw, 1, 2, 3, 0)};
}
HWY_API Vec128<float> Shuffle0321(const Vec128<float> v) {
return Vec128<float>{wasm_v32x4_shuffle(v.raw, v.raw, 1, 2, 3, 0)};
return Vec128<float>{wasm_i32x4_shuffle(v.raw, v.raw, 1, 2, 3, 0)};
}
// Rotate left 32 bits
HWY_API Vec128<uint32_t> Shuffle2103(const Vec128<uint32_t> v) {
return Vec128<uint32_t>{wasm_v32x4_shuffle(v.raw, v.raw, 3, 0, 1, 2)};
return Vec128<uint32_t>{wasm_i32x4_shuffle(v.raw, v.raw, 3, 0, 1, 2)};
}
HWY_API Vec128<int32_t> Shuffle2103(const Vec128<int32_t> v) {
return Vec128<int32_t>{wasm_v32x4_shuffle(v.raw, v.raw, 3, 0, 1, 2)};
return Vec128<int32_t>{wasm_i32x4_shuffle(v.raw, v.raw, 3, 0, 1, 2)};
}
HWY_API Vec128<float> Shuffle2103(const Vec128<float> v) {
return Vec128<float>{wasm_v32x4_shuffle(v.raw, v.raw, 3, 0, 1, 2)};
return Vec128<float>{wasm_i32x4_shuffle(v.raw, v.raw, 3, 0, 1, 2)};
}
// Reverse
HWY_API Vec128<uint32_t> Shuffle0123(const Vec128<uint32_t> v) {
return Vec128<uint32_t>{wasm_v32x4_shuffle(v.raw, v.raw, 3, 2, 1, 0)};
return Vec128<uint32_t>{wasm_i32x4_shuffle(v.raw, v.raw, 3, 2, 1, 0)};
}
HWY_API Vec128<int32_t> Shuffle0123(const Vec128<int32_t> v) {
return Vec128<int32_t>{wasm_v32x4_shuffle(v.raw, v.raw, 3, 2, 1, 0)};
return Vec128<int32_t>{wasm_i32x4_shuffle(v.raw, v.raw, 3, 2, 1, 0)};
}
HWY_API Vec128<float> Shuffle0123(const Vec128<float> v) {
return Vec128<float>{wasm_v32x4_shuffle(v.raw, v.raw, 3, 2, 1, 0)};
return Vec128<float>{wasm_i32x4_shuffle(v.raw, v.raw, 3, 2, 1, 0)};
}
// ------------------------------ TableLookupLanes
@ -1936,33 +1958,33 @@ HWY_API Vec128<float, N> TableLookupLanes(const Vec128<float, N> v,
template <size_t N>
HWY_API Vec128<uint16_t, (N + 1) / 2> ZipLower(const Vec128<uint8_t, N> a,
const Vec128<uint8_t, N> b) {
return Vec128<uint16_t, (N + 1) / 2>{wasm_v8x16_shuffle(
return Vec128<uint16_t, (N + 1) / 2>{wasm_i8x16_shuffle(
a.raw, b.raw, 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23)};
}
template <size_t N>
HWY_API Vec128<uint32_t, (N + 1) / 2> ZipLower(const Vec128<uint16_t, N> a,
const Vec128<uint16_t, N> b) {
return Vec128<uint32_t, (N + 1) / 2>{
wasm_v16x8_shuffle(a.raw, b.raw, 0, 8, 1, 9, 2, 10, 3, 11)};
wasm_i16x8_shuffle(a.raw, b.raw, 0, 8, 1, 9, 2, 10, 3, 11)};
}
template <size_t N>
HWY_API Vec128<int16_t, (N + 1) / 2> ZipLower(const Vec128<int8_t, N> a,
const Vec128<int8_t, N> b) {
return Vec128<int16_t, (N + 1) / 2>{wasm_v8x16_shuffle(
return Vec128<int16_t, (N + 1) / 2>{wasm_i8x16_shuffle(
a.raw, b.raw, 0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23)};
}
template <size_t N>
HWY_API Vec128<int32_t, (N + 1) / 2> ZipLower(const Vec128<int16_t, N> a,
const Vec128<int16_t, N> b) {
return Vec128<int32_t, (N + 1) / 2>{
wasm_v16x8_shuffle(a.raw, b.raw, 0, 8, 1, 9, 2, 10, 3, 11)};
wasm_i16x8_shuffle(a.raw, b.raw, 0, 8, 1, 9, 2, 10, 3, 11)};
}
template <size_t N>
HWY_API Vec128<uint16_t, N / 2> ZipUpper(const Vec128<uint8_t, N> a,
const Vec128<uint8_t, N> b) {
return Vec128<uint16_t, N / 2>{wasm_v8x16_shuffle(a.raw, b.raw, 8, 24, 9, 25,
return Vec128<uint16_t, N / 2>{wasm_i8x16_shuffle(a.raw, b.raw, 8, 24, 9, 25,
10, 26, 11, 27, 12, 28, 13,
29, 14, 30, 15, 31)};
}
@ -1970,13 +1992,13 @@ template <size_t N>
HWY_API Vec128<uint32_t, N / 2> ZipUpper(const Vec128<uint16_t, N> a,
const Vec128<uint16_t, N> b) {
return Vec128<uint32_t, N / 2>{
wasm_v16x8_shuffle(a.raw, b.raw, 4, 12, 5, 13, 6, 14, 7, 15)};
wasm_i16x8_shuffle(a.raw, b.raw, 4, 12, 5, 13, 6, 14, 7, 15)};
}
template <size_t N>
HWY_API Vec128<int16_t, N / 2> ZipUpper(const Vec128<int8_t, N> a,
const Vec128<int8_t, N> b) {
return Vec128<int16_t, N / 2>{wasm_v8x16_shuffle(a.raw, b.raw, 8, 24, 9, 25,
return Vec128<int16_t, N / 2>{wasm_i8x16_shuffle(a.raw, b.raw, 8, 24, 9, 25,
10, 26, 11, 27, 12, 28, 13,
29, 14, 30, 15, 31)};
}
@ -1984,7 +2006,7 @@ template <size_t N>
HWY_API Vec128<int32_t, N / 2> ZipUpper(const Vec128<int16_t, N> a,
const Vec128<int16_t, N> b) {
return Vec128<int32_t, N / 2>{
wasm_v16x8_shuffle(a.raw, b.raw, 4, 12, 5, 13, 6, 14, 7, 15)};
wasm_i16x8_shuffle(a.raw, b.raw, 4, 12, 5, 13, 6, 14, 7, 15)};
}
// ------------------------------ Interleave lanes
@ -2000,17 +2022,17 @@ HWY_API Vec128<T> InterleaveLower(const Vec128<T> a, const Vec128<T> b) {
template <>
HWY_INLINE Vec128<uint32_t> InterleaveLower<uint32_t>(
const Vec128<uint32_t> a, const Vec128<uint32_t> b) {
return Vec128<uint32_t>{wasm_v32x4_shuffle(a.raw, b.raw, 0, 4, 1, 5)};
return Vec128<uint32_t>{wasm_i32x4_shuffle(a.raw, b.raw, 0, 4, 1, 5)};
}
template <>
HWY_INLINE Vec128<int32_t> InterleaveLower<int32_t>(const Vec128<int32_t> a,
const Vec128<int32_t> b) {
return Vec128<int32_t>{wasm_v32x4_shuffle(a.raw, b.raw, 0, 4, 1, 5)};
return Vec128<int32_t>{wasm_i32x4_shuffle(a.raw, b.raw, 0, 4, 1, 5)};
}
template <>
HWY_INLINE Vec128<float> InterleaveLower<float>(const Vec128<float> a,
const Vec128<float> b) {
return Vec128<float>{wasm_v32x4_shuffle(a.raw, b.raw, 0, 4, 1, 5)};
return Vec128<float>{wasm_i32x4_shuffle(a.raw, b.raw, 0, 4, 1, 5)};
}
template <typename T>
@ -2020,17 +2042,17 @@ HWY_API Vec128<T> InterleaveUpper(const Vec128<T> a, const Vec128<T> b) {
template <>
HWY_INLINE Vec128<uint32_t> InterleaveUpper<uint32_t>(
const Vec128<uint32_t> a, const Vec128<uint32_t> b) {
return Vec128<uint32_t>{wasm_v32x4_shuffle(a.raw, b.raw, 2, 6, 3, 7)};
return Vec128<uint32_t>{wasm_i32x4_shuffle(a.raw, b.raw, 2, 6, 3, 7)};
}
template <>
HWY_INLINE Vec128<int32_t> InterleaveUpper<int32_t>(const Vec128<int32_t> a,
const Vec128<int32_t> b) {
return Vec128<int32_t>{wasm_v32x4_shuffle(a.raw, b.raw, 2, 6, 3, 7)};
return Vec128<int32_t>{wasm_i32x4_shuffle(a.raw, b.raw, 2, 6, 3, 7)};
}
template <>
HWY_INLINE Vec128<float> InterleaveUpper<float>(const Vec128<float> a,
const Vec128<float> b) {
return Vec128<float>{wasm_v32x4_shuffle(a.raw, b.raw, 2, 6, 3, 7)};
return Vec128<float>{wasm_i32x4_shuffle(a.raw, b.raw, 2, 6, 3, 7)};
}
// ------------------------------ Blocks
@ -2038,13 +2060,13 @@ HWY_INLINE Vec128<float> InterleaveUpper<float>(const Vec128<float> a,
// hiH,hiL loH,loL |-> hiL,loL (= lower halves)
template <typename T>
HWY_API Vec128<T> ConcatLowerLower(const Vec128<T> hi, const Vec128<T> lo) {
return Vec128<T>{wasm_v64x2_shuffle(lo.raw, hi.raw, 0, 2)};
return Vec128<T>{wasm_i64x2_shuffle(lo.raw, hi.raw, 0, 2)};
}
// hiH,hiL loH,loL |-> hiH,loH (= upper halves)
template <typename T>
HWY_API Vec128<T> ConcatUpperUpper(const Vec128<T> hi, const Vec128<T> lo) {
return Vec128<T>{wasm_v64x2_shuffle(lo.raw, hi.raw, 1, 3)};
return Vec128<T>{wasm_i64x2_shuffle(lo.raw, hi.raw, 1, 3)};
}
// hiH,hiL loH,loL |-> hiL,loH (= inner halves)
@ -2056,7 +2078,7 @@ HWY_API Vec128<T> ConcatLowerUpper(const Vec128<T> hi, const Vec128<T> lo) {
// hiH,hiL loH,loL |-> hiH,loL (= outer halves)
template <typename T>
HWY_API Vec128<T> ConcatUpperLower(const Vec128<T> hi, const Vec128<T> lo) {
return Vec128<T>{wasm_v64x2_shuffle(lo.raw, hi.raw, 0, 3)};
return Vec128<T>{wasm_i64x2_shuffle(lo.raw, hi.raw, 0, 3)};
}
// ------------------------------ Odd/even lanes
@ -2075,12 +2097,12 @@ HWY_API Vec128<T> odd_even_impl(hwy::SizeTag<1> /* tag */, const Vec128<T> a,
template <typename T>
HWY_API Vec128<T> odd_even_impl(hwy::SizeTag<2> /* tag */, const Vec128<T> a,
const Vec128<T> b) {
return Vec128<T>{wasm_v16x8_shuffle(a.raw, b.raw, 8, 1, 10, 3, 12, 5, 14, 7)};
return Vec128<T>{wasm_i16x8_shuffle(a.raw, b.raw, 8, 1, 10, 3, 12, 5, 14, 7)};
}
template <typename T>
HWY_API Vec128<T> odd_even_impl(hwy::SizeTag<4> /* tag */, const Vec128<T> a,
const Vec128<T> b) {
return Vec128<T>{wasm_v32x4_shuffle(a.raw, b.raw, 4, 1, 6, 3)};
return Vec128<T>{wasm_i32x4_shuffle(a.raw, b.raw, 4, 1, 6, 3)};
}
// TODO(eustas): implement
// template <typename T>
@ -2097,7 +2119,7 @@ HWY_API Vec128<T> OddEven(const Vec128<T> a, const Vec128<T> b) {
template <>
HWY_INLINE Vec128<float> OddEven<float>(const Vec128<float> a,
const Vec128<float> b) {
return Vec128<float>{wasm_v32x4_shuffle(a.raw, b.raw, 4, 1, 6, 3)};
return Vec128<float>{wasm_i32x4_shuffle(a.raw, b.raw, 4, 1, 6, 3)};
}
// ================================================== CONVERT
@ -2108,52 +2130,52 @@ HWY_INLINE Vec128<float> OddEven<float>(const Vec128<float> a,
template <size_t N>
HWY_API Vec128<uint16_t, N> PromoteTo(Simd<uint16_t, N> /* tag */,
const Vec128<uint8_t, N> v) {
return Vec128<uint16_t, N>{wasm_i16x8_widen_low_u8x16(v.raw)};
return Vec128<uint16_t, N>{wasm_u16x8_extend_low_u8x16(v.raw)};
}
template <size_t N>
HWY_API Vec128<uint32_t, N> PromoteTo(Simd<uint32_t, N> /* tag */,
const Vec128<uint8_t, N> v) {
return Vec128<uint32_t, N>{
wasm_i32x4_widen_low_u16x8(wasm_i16x8_widen_low_u8x16(v.raw))};
wasm_u32x4_extend_low_u16x8(wasm_u16x8_extend_low_u8x16(v.raw))};
}
template <size_t N>
HWY_API Vec128<int16_t, N> PromoteTo(Simd<int16_t, N> /* tag */,
const Vec128<uint8_t, N> v) {
return Vec128<int16_t, N>{wasm_i16x8_widen_low_u8x16(v.raw)};
return Vec128<int16_t, N>{wasm_u16x8_extend_low_u8x16(v.raw)};
}
template <size_t N>
HWY_API Vec128<int32_t, N> PromoteTo(Simd<int32_t, N> /* tag */,
const Vec128<uint8_t, N> v) {
return Vec128<int32_t, N>{
wasm_i32x4_widen_low_u16x8(wasm_i16x8_widen_low_u8x16(v.raw))};
wasm_u32x4_extend_low_u16x8(wasm_u16x8_extend_low_u8x16(v.raw))};
}
template <size_t N>
HWY_API Vec128<uint32_t, N> PromoteTo(Simd<uint32_t, N> /* tag */,
const Vec128<uint16_t, N> v) {
return Vec128<uint32_t, N>{wasm_i32x4_widen_low_u16x8(v.raw)};
return Vec128<uint32_t, N>{wasm_u32x4_extend_low_u16x8(v.raw)};
}
template <size_t N>
HWY_API Vec128<int32_t, N> PromoteTo(Simd<int32_t, N> /* tag */,
const Vec128<uint16_t, N> v) {
return Vec128<int32_t, N>{wasm_i32x4_widen_low_u16x8(v.raw)};
return Vec128<int32_t, N>{wasm_u32x4_extend_low_u16x8(v.raw)};
}
// Signed: replicate sign bit.
template <size_t N>
HWY_API Vec128<int16_t, N> PromoteTo(Simd<int16_t, N> /* tag */,
const Vec128<int8_t, N> v) {
return Vec128<int16_t, N>{wasm_i16x8_widen_low_i8x16(v.raw)};
return Vec128<int16_t, N>{wasm_i16x8_extend_low_i8x16(v.raw)};
}
template <size_t N>
HWY_API Vec128<int32_t, N> PromoteTo(Simd<int32_t, N> /* tag */,
const Vec128<int8_t, N> v) {
return Vec128<int32_t, N>{
wasm_i32x4_widen_low_i16x8(wasm_i16x8_widen_low_i8x16(v.raw))};
wasm_i32x4_extend_low_i16x8(wasm_i16x8_extend_low_i8x16(v.raw))};
}
template <size_t N>
HWY_API Vec128<int32_t, N> PromoteTo(Simd<int32_t, N> /* tag */,
const Vec128<int16_t, N> v) {
return Vec128<int32_t, N>{wasm_i32x4_widen_low_i16x8(v.raw)};
return Vec128<int32_t, N>{wasm_i32x4_extend_low_i16x8(v.raw)};
}
template <size_t N>
@ -2291,7 +2313,7 @@ HWY_API Vec128<float, N> ConvertTo(Simd<float, N> /* tag */,
template <size_t N>
HWY_API Vec128<int32_t, N> ConvertTo(Simd<int32_t, N> /* tag */,
const Vec128<float, N> v) {
return Vec128<int32_t, N>{wasm_i32x4_trunc_saturate_f32x4(v.raw)};
return Vec128<int32_t, N>{wasm_i32x4_trunc_sat_f32x4(v.raw)};
}
template <size_t N>

View file

@ -269,13 +269,12 @@
#define HWY_TARGETS HWY_STATIC_TARGET
// 3) For tests: include all attainable targets (in particular: scalar)
#elif defined(HWY_COMPILE_ALL_ATTAINABLE)
#elif defined(HWY_COMPILE_ALL_ATTAINABLE) || defined(HWY_IS_TEST)
#define HWY_TARGETS HWY_ATTAINABLE_TARGETS
// 4) Default: attainable WITHOUT non-best baseline. This reduces code size by
// excluding superseded targets, in particular scalar.
#else
#define HWY_TARGETS (HWY_ATTAINABLE_TARGETS & (2 * HWY_STATIC_TARGET - 1))
#endif // target policy

View file

@ -166,15 +166,21 @@ struct TestFirstN {
const size_t N = Lanes(d);
auto mask_lanes = AllocateAligned<T>(N);
// NOTE: reverse polarity (mask is true iff mask_lanes[i] == 0) because we
// cannot reliably compare against all bits set (NaN for float types).
const T off = 1;
// GCC workaround: we previously used zero to indicate true because we can
// safely compare with that value. However, that hits an ICE for u64x1 on
// GCC 8.3 but not 8.4, even if the implementation of operator== is
// simplified to return zero. Using MaskFromVec avoids this, and requires
// FF..FF and 0 constants.
T on;
memset(&on, 0xFF, sizeof(on));
const T off = 0;
for (size_t len = 0; len <= N; ++len) {
for (size_t i = 0; i < N; ++i) {
mask_lanes[i] = i < len ? T(0) : off;
mask_lanes[i] = i < len ? on : off;
}
const auto mask = Eq(Load(d, mask_lanes.get()), Zero(d));
const auto mask_vals = Load(d, mask_lanes.get());
const auto mask = MaskFromVec(mask_vals);
HWY_ASSERT_MASK_EQ(d, mask, FirstN(d, len));
}
}

View file

@ -12,6 +12,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
// Ensure incompabilities with Windows macros (e.g. #define StoreFence) are
// detected. Must come before Highway headers.
#if defined(_WIN32) || defined(_WIN64)
#include <windows.h>
#endif
#include <stddef.h>
#include <stdint.h>

17
third_party/jpeg-xl/.readthedocs.yaml vendored Normal file
View file

@ -0,0 +1,17 @@
# 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.
#
# readthedocs.io configuration file. See:
# https://docs.readthedocs.io/en/stable/config-file/v2.html
version: 2
sphinx:
configuration: doc/sphinx/conf.py
python:
version: "3.7"
install:
- requirements: doc/sphinx/requirements.txt

View file

@ -16,5 +16,14 @@ Cloudinary Ltd. <*@cloudinary.com>
Google LLC <*@google.com>
# Individuals:
Alexander Sago <cagelight@gmail.com>
Dirk Lemstra <dirk@lemstra.org>
Jon Sneyers <jon@cloudinary.com>
Lovell Fuller
Marcin Konicki <ahwayakchih@gmail.com>
Petr Diblík
Pieter Wuille
xiota
Ziemowit Zabawa <ziemek.zabawa@outlook.com>
Andrius Lukas Narbutas <andrius4669@gmail.com>
Misaki Kasumi <misakikasumi@outlook.com>

157
third_party/jpeg-xl/CHANGELOG.md vendored Normal file
View file

@ -0,0 +1,157 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.6] - Unreleased
### Added
- API: New functions to decode extra channels:
`JxlDecoderExtraChannelBufferSize` and `JxlDecoderSetExtraChannelBuffer`.
- API: New function `JxlEncoderInitBasicInfo` to initialize `JxlBasicInfo`
(only needed when encoding). NOTE: it is now required to call this function
when using the encoder. Padding was added to the struct for forward
compatibility.
- API: Support for encoding oriented images.
- API: FLOAT16 support in the encoder API.
- Rewrite of the GDK pixbuf loader plugin. Added proper color management and
animation support.
- Rewrite of GIMP plugin. Added compression parameters dialog and switched to
using the public C API.
- Debian packages for GDK pixbuf loader (`libjxl-gdk-pixbuf`) and GIMP
(`libjxl-gimp-plugin`) plugins.
- `cjxl`/`djxl` support for `stdin` and `stdout`.
### Changed
- API: Renamed the field `alpha_associated` in `JxlExtraChannelInfo` to
`alpha_premultiplied`, to match the corresponding name in `JxlBasicInfo`.
- Improved the 2x2 downscaling method in the encoder for the optional color
channel resampling for low bit rates.
- Fixed: the combination of floating point original data, XYB color encoding,
and Modular mode was broken (in both encoder and decoder). It now works.
NOTE: this can cause the current encoder to write jxl bitstreams that do
not decode with the old decoder. In particular this will happen when using
cjxl with PFM, EXR, or floating point PSD input, and a combination of XYB
and modular mode is used (which caused an encoder error before), e.g.
using options like `-m -q 80` (lossy modular), `-d 4.5` or `--progressive_dc=1`
(modular DC frame), or default lossy encoding on an image where patches
end up being used. There is no problem when using cjxl with PNG, JPEG, GIF,
APNG, PPM, PGM, PGX, or integer (8-bit or 16-bit) PSD input.
- `libjxl` static library now bundles skcms, fixing static linking in
downstream projects when skcms is used.
- Spline rendering performance improvements.
- Butteraugli changes for less visual masking.
## [0.5] - 2021-08-02
### Added
- API: New function to decode the image using a callback outputting a part of a
row per call.
- API: 16-bit float output support.
- API: `JxlDecoderRewind` and `JxlDecoderSkipFrames` functions to skip more
efficiently to earlier animation frames.
- API: `JxlDecoderSetPreferredColorProfile` function to choose color profile in
certain circumstances.
- encoder: Adding `center_x` and `center_y` flags for more control of the tile
order.
- New encoder speeds `lightning` (1) and `thunder` (2).
### Changed
- Re-licensed the project under a BSD 3-Clause license. See the
[LICENSE](LICENSE) and [PATENTS](PATENTS) files for details.
- Full JPEG XL part 1 specification support: Implemented all the spec required
to decode files to pixels, including cases that are not used by the encoder
yet. Part 2 of the spec (container format) is final but not fully implemented
here.
- Butteraugli metric improvements. Exact numbers are different from previous
versions.
- Memory reductions during decoding.
- Reduce the size of the jxl_dec library by removing dependencies.
- A few encoding speedups.
- Clarify the security policy.
- Significant encoding improvements (~5 %) and less ringing.
- Butteraugli metric to have some less masking.
- `cjxl` flag `--speed` is deprecated and replaced by the `--effort` synonym.
### Removed
- API for returning a downsampled DC was deprecated
(`JxlDecoderDCOutBufferSize` and `JxlDecoderSetDCOutBuffer`) and will be
removed in the next release.
## [0.3.7] - 2021-03-29
### Changed
- Fix a rounding issue in 8-bit decoding.
## [0.3.6] - 2021-03-25
### Changed
- Fix a bug that could result in the generation of invalid codestreams as
well as failure to decode valid streams.
## [0.3.5] - 2021-03-23
### Added
- New encode-time options for faster decoding at the cost of quality.
- Man pages for cjxl and djxl.
### Changed
- Memory usage improvements.
- Faster decoding to 8-bit output with the C API.
- GIMP plugin: avoid the sRGB conversion dialog for sRGB images, do not show
a console window on Windows.
- Various bug fixes.
## [0.3.4] - 2021-03-16
### Changed
- Improved box parsing.
- Improved metadata handling.
- Performance and memory usage improvements.
## [0.3.3] - 2021-03-05
### Changed
- Performance improvements for small images.
- Add a (flag-protected) non-high-precision mode with better speed.
- Significantly speed up the PQ EOTF.
- Allow optional HDR tone mapping in djxl (--tone_map, --display_nits).
- Change the behavior of djxl -j to make it consistent with cjxl (#153).
- Improve image quality.
- Improve EXIF handling.
## [0.3.2] - 2021-02-12
### Changed
- Fix embedded ICC encoding regression
[#149](https://gitlab.com/wg1/jpeg-xl/-/issues/149).
## [0.3.1] - 2021-02-10
### Changed
- New experimental Butteraugli API (`jxl/butteraugli.h`).
- Encoder improvements to low quality settings.
- Bug fixes, including fuzzer-found potential security bug fixes.
- Fixed `-q 100` and `-d 0` not triggering lossless modes.
## [0.3] - 2021-01-29
### Changed
- Minor change to the Decoder C API to accommodate future work for other ways
to provide input.
- Future decoder C API changes will be backwards compatible.
- Lots of bug fixes since the previous version.
## [0.2] - 2020-12-24
### Added
- JPEG XL bitstream format is frozen. Files encoded with 0.2 will be supported
by future versions.
### Changed
- Files encoded with previous versions are not supported.
## [0.1.1] - 2020-12-01
## [0.1] - 2020-11-14
### Added
- Initial release of an encoder (`cjxl`) and decoder (`djxl`) that work
together as well as a benchmark tool for comparison with other codecs
(`benchmark_xl`).
- Note: JPEG XL format is in the final stages of standardization, minor changes
to the codestream format are still possible but we are not expecting any
changes beyond what is required by bug fixing.
- API: new decoder API in C, check the `examples/` directory for its example
usage. The C API is a work in progress and likely to change both in API and
ABI in future releases.

View file

@ -20,7 +20,7 @@ if(POLICY CMP0083)
cmake_policy(SET CMP0083 NEW)
endif()
project(JPEGXL LANGUAGES C CXX)
project(LIBJXL LANGUAGES C CXX)
include(CheckCXXSourceCompiles)
check_cxx_source_compiles(
@ -71,22 +71,31 @@ endif()
set(WARNINGS_AS_ERRORS_DEFAULT false)
# Standard cmake naming for building shared libraries.
option(BUILD_SHARED_LIBS "Build shared libraries instead of static ones" ON)
set(JPEGXL_ENABLE_FUZZERS ${ENABLE_FUZZERS_DEFAULT} CACHE BOOL
"Build JPEGXL fuzzer targets.")
set(JPEGXL_ENABLE_DEVTOOLS false CACHE BOOL
"Build JPEGXL developer tools.")
set(JPEGXL_ENABLE_TOOLS true CACHE BOOL
"Build JPEGXL user tools: cjxl and djxl.")
set(JPEGXL_ENABLE_MANPAGES true CACHE BOOL
"Build and install man pages for the command-line tools.")
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_ENABLE_JNI true CACHE BOOL
"Build JPEGXL JNI Java wrapper, if Java dependencies are installed.")
set(JPEGXL_ENABLE_SJPEG true CACHE BOOL
"Build JPEGXL with support for encoding with sjpeg.")
set(JPEGXL_ENABLE_OPENEXR true CACHE BOOL
"Build JPEGXL with support for OpenEXR if available.")
set(JPEGXL_ENABLE_SKCMS true CACHE BOOL
"Build with skcms instead of lcms2.")
set(JPEGXL_BUNDLE_SKCMS true CACHE BOOL
"When building with skcms, bundle it into libjxl.a.")
set(JPEGXL_ENABLE_VIEWERS false CACHE BOOL
"Build JPEGXL viewer tools for evaluation.")
set(JPEGXL_ENABLE_TCMALLOC ${ENABLE_TCMALLOC_DEFAULT} CACHE BOOL
@ -97,6 +106,10 @@ set(JPEGXL_ENABLE_COVERAGE false CACHE BOOL
"Enable code coverage tracking for libjxl. This also enables debug and disables optimizations.")
set(JPEGXL_ENABLE_PROFILER false CACHE BOOL
"Builds in support for profiling (printed by tools if extra flags given")
set(JPEGXL_ENABLE_TRANSCODE_JPEG true CACHE BOOL
"Builds in support for decoding transcoded JXL files back to JPEG,\
disabling it makes the decoder reject JXL_DEC_JPEG_RECONSTRUCTION events,\
(default enabled)")
set(JPEGXL_STATIC false CACHE BOOL
"Build tools as static binaries.")
set(JPEGXL_WARNINGS_AS_ERRORS ${WARNINGS_AS_ERRORS_DEFAULT} CACHE BOOL
@ -146,8 +159,20 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
if(JPEGXL_STATIC)
set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
set(BUILD_SHARED_LIBS 0)
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++")
# Clang developers say that in case to use "static" we have to build stdlib
# ourselves; for real use case we don't care about stdlib, as it is "granted",
# so just linking all other libraries is fine.
if (NOT APPLE)
set(CMAKE_EXE_LINKER_FLAGS
"${CMAKE_EXE_LINKER_FLAGS} -static -static-libgcc -static-libstdc++")
endif()
endif() # JPEGXL_STATIC
# Threads
set(THREADS_PREFER_PTHREAD_FLAG YES)
find_package(Threads REQUIRED)
if(JPEGXL_STATIC)
if (MINGW)
# In MINGW libstdc++ uses pthreads directly. When building statically a
# program (regardless of whether the source code uses pthread or not) the
@ -163,9 +188,22 @@ if(JPEGXL_STATIC)
# will be discarded anyway.
# This adds these flags as dependencies for *all* targets. Adding this to
# CMAKE_EXE_LINKER_FLAGS instead would cause them to be included before any
# object files and therefore discarded.
# object files and therefore discarded. This should be set in the
# INTERFACE_LINK_LIBRARIES of Threads::Threads but some third_part targets
# don't depend on it.
link_libraries(-Wl,-Bstatic -lstdc++ -lpthread -Wl,-Bdynamic)
endif() # MINGW
elseif(CMAKE_USE_PTHREADS_INIT)
# "whole-archive" is not supported on OSX.
if (NOT APPLE)
# Set pthreads as a whole-archive, otherwise weak symbols in the static
# libraries will discard pthreads symbols leading to segmentation fault at
# runtime.
message(STATUS "Using -lpthread as --whole-archive")
set_target_properties(Threads::Threads PROPERTIES
INTERFACE_LINK_LIBRARIES
"-Wl,--whole-archive;-lpthread;-Wl,--no-whole-archive")
endif()
endif()
endif() # JPEGXL_STATIC
if (MSVC)
@ -245,9 +283,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED YES)
add_subdirectory(third_party)
set(THREADS_PREFER_PTHREAD_FLAG YES)
find_package(Threads REQUIRED)
# Copy the JXL license file to the output build directory.
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/LICENSE"
${PROJECT_BINARY_DIR}/LICENSE.jpeg-xl COPYONLY)
@ -273,15 +308,37 @@ endif() # BUILD_TESTING
find_package(Doxygen)
if(DOXYGEN_FOUND)
set(DOXYGEN_GENERATE_HTML "YES")
set(DOXYGEN_GENERATE_XML "NO")
set(DOXYGEN_STRIP_FROM_PATH "${CMAKE_CURRENT_SOURCE_DIR}/include")
set(DOXYGEN_GENERATE_XML "YES")
set(DOXYGEN_STRIP_FROM_PATH "${CMAKE_CURRENT_SOURCE_DIR}/lib/include")
set(DOXYGEN_USE_MDFILE_AS_MAINPAGE "README.md")
set(DOXYGEN_WARN_AS_ERROR "YES")
set(DOXYGEN_QUIET "YES")
doxygen_add_docs(doc
"${CMAKE_CURRENT_SOURCE_DIR}/lib/include/jxl"
"${CMAKE_CURRENT_SOURCE_DIR}/lib/include"
"${CMAKE_CURRENT_SOURCE_DIR}/doc/api.txt"
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
COMMENT "Generating C API documentation")
# Add sphinx doc build step for readthedocs.io (requires doxygen too).
find_program(SPHINX_BUILD_PROGRAM sphinx-build)
if(SPHINX_BUILD_PROGRAM)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent"
COMMENT "Generating readthedocs.io output on ${CMAKE_CURRENT_BINARY_DIR}/rtd"
COMMAND ${SPHINX_BUILD_PROGRAM} -q -W -b html -j auto
${CMAKE_SOURCE_DIR}/doc/sphinx
${CMAKE_CURRENT_BINARY_DIR}/rtd
DEPENDS doc
)
# This command runs the documentation generation every time since the output
# target file doesn't exist.
add_custom_target(rtd-html
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/rtd/nonexistent
)
else() # SPHINX_BUILD_PROGRAM\
message(WARNING "sphinx-build not found, skipping rtd documentation")
endif() # SPHINX_BUILD_PROGRAM
else()
# Create a "doc" target for compatibility since "doc" is not otherwise added to
# the build when doxygen is not installed.
@ -290,13 +347,31 @@ add_custom_target(doc false
endif() # DOXYGEN_FOUND
if(JPEGXL_ENABLE_MANPAGES)
find_package(Python COMPONENTS Interpreter)
if(Python_Interpreter_FOUND)
find_program(ASCIIDOC a2x)
endif()
if(NOT Python_Interpreter_FOUND OR "${ASCIIDOC}" STREQUAL "ASCIIDOC-NOTFOUND")
message(WARNING "asciidoc was not found, the man pages will not be installed.")
find_program(ASCIIDOC a2x)
if(NOT "${ASCIIDOC}" STREQUAL "ASCIIDOC-NOTFOUND")
file(STRINGS "${ASCIIDOC}" ASCIIDOC_SHEBANG LIMIT_COUNT 1)
if(ASCIIDOC_SHEBANG MATCHES "python2")
find_package(Python2 COMPONENTS Interpreter)
set(ASCIIDOC_PY_FOUND "${Python2_Interpreter_FOUND}")
set(ASCIIDOC_PY Python2::Interpreter)
elseif(ASCIIDOC_SHEBANG MATCHES "python3")
find_package(Python3 COMPONENTS Interpreter)
set(ASCIIDOC_PY_FOUND "${Python3_Interpreter_FOUND}")
set(ASCIIDOC_PY Python3::Interpreter)
else()
find_package(Python COMPONENTS Interpreter QUIET)
if(NOT Python_Interpreter_FOUND)
find_program(ASCIIDOC_PY python)
if(NOT ASCIIDOC_PY STREQUAL "ASCIIDOC_PY-NOTFOUND")
set(ASCIIDOC_PY_FOUND ON)
endif()
else()
set(ASCIIDOC_PY_FOUND "${Python_Interpreter_FOUND}")
set(ASCIIDOC_PY Python::Interpreter)
endif()
endif()
if (ASCIIDOC_PY_FOUND)
set(MANPAGE_FILES "")
set(MANPAGES "")
foreach(PAGE IN ITEMS cjxl djxl)
@ -305,7 +380,7 @@ else()
# does not recognize it.
add_custom_command(
OUTPUT "${PAGE}.1"
COMMAND Python::Interpreter
COMMAND "${ASCIIDOC_PY}"
ARGS "${ASCIIDOC}"
--format manpage --destination-dir="${CMAKE_CURRENT_BINARY_DIR}"
"${CMAKE_CURRENT_SOURCE_DIR}/doc/man/${PAGE}.txt"
@ -315,12 +390,15 @@ else()
endforeach()
add_custom_target(manpages ALL DEPENDS ${MANPAGES})
install(FILES ${MANPAGE_FILES} DESTINATION share/man/man1)
endif()
endif()
endif() # ASCIIDOC_PY_FOUND
else()
message(WARNING "asciidoc was not found, the man pages will not be installed.")
endif() # ASCIIDOC != "ASCIIDOC-NOTFOUND"
endif() # JPEGXL_ENABLE_MANPAGES
# Example usage code.
if (${JPEGXL_ENABLE_EXAMPLES})
add_subdirectory(examples)
include(examples/examples.cmake)
endif ()
# Plugins for third-party software

View file

@ -42,6 +42,19 @@ to accept your pull requests.
***NOTE***: Only original source code from you and other people that have signed
the CLA can be accepted into the main repository.
### License
Contributions are licensed under the project's [LICENSE](LICENSE). Each new
file must include the following header when possible, with comment style adapted
to the language as needed:
```
// 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.
```
### Code Reviews
All submissions, including submissions by project members, require review. We
@ -92,6 +105,10 @@ information on using pull requests.
Request to see the changes between versions. We use "Rebase and merge" policy
to keep a linear git history which is easier to reason about.
* Your change must pass the build and test workflows. There's a `ci.sh` script
to help building and testing these configurations. See [building and
testing](doc/building_and_testing.md) for more details.
### Contributing checklist.
* Sign the CLA (only needed once per user, see above).

View file

@ -10,7 +10,7 @@ instructions see "[README](README.md)" and
## Dependencies
```shell
pkgman install llvm9_clang ninja cmake doxygen libjpeg_turbo_devel
pkgman install llvm9_clang ninja cmake doxygen libjpeg_turbo_devel giflib_devel
```
## Building

View file

@ -1,14 +1,24 @@
# JPEG XL reference implementation
[![Build&Test](https://github.com/libjxl/libjxl/actions/workflows/build_test.yml/badge.svg)](
https://github.com/libjxl/libjxl/actions/workflows/build_test.yml)
[![Releases](https://github.com/libjxl/libjxl/actions/workflows/release.yaml/badge.svg)](
https://github.com/libjxl/libjxl/actions/workflows/release.yaml)
[![Doc](https://readthedocs.org/projects/libjxl/badge/?version=latest)](
https://libjxl.readthedocs.io/en/latest/?badge=latest)
[![codecov](https://codecov.io/gh/libjxl/libjxl/branch/main/graph/badge.svg)](
https://codecov.io/gh/libjxl/libjxl)
<img src="doc/jxl.svg" width="100" align="right" alt="JXL logo">
This repository contains a reference implementation of JPEG XL (encoder and
decoder), called `libjxl`.
decoder), called `libjxl`. This software library is
[used by many applications that support JPEG XL](doc/software_support.md).
JPEG XL is in the final stages of standardization and its codestream format is
frozen.
JPEG XL is in the final stages of standardization and its codestream and file format
are frozen.
The libraries API, command line options and tools in this repository are subject
The library API, command line options, and tools in this repository are subject
to change, however files encoded with `cjxl` conform to the JPEG XL format
specification and can be decoded with current and future `djxl` decoders or
`libjxl` decoding library.
@ -20,13 +30,16 @@ For more details and other workflows see the "Advanced guide" below.
### Checking out the code
```bash
git clone https://gitlab.com/wg1/jpeg-xl.git --recursive
git clone https://github.com/libjxl/libjxl.git --recursive
```
This repository uses git submodules to handle some third party dependencies
under `third_party/`, that's why is important to pass `--recursive`. If you
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:
`git submodule update --init --recursive`.
```bash
git submodule update --init --recursive
```
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
@ -50,17 +63,17 @@ sudo apt install libgif-dev libjpeg-dev libopenexr-dev libpng-dev libwebp-dev
```
We recommend using a recent Clang compiler (version 7 or newer), for that
install clang and set `CC` and `CXX` variables. For example, with clang-7:
install clang and set `CC` and `CXX` variables.
```bash
sudo apt install clang-7
export CC=clang-7 CXX=clang++-7
sudo apt install clang
export CC=clang CXX=clang++
```
### Building
```bash
cd jpeg-xl
cd libjxl
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF ..
@ -102,8 +115,8 @@ For speed benchmarks on single images in single or multi-threaded decoding
on the decoding options and note that the output image is optional for
benchmarking purposes.
For a more comprehensive comparison of compression density between multiple
options see "Benchmarking with benchmark_xl" section below.
For more comprehensive benchmarking options, see the
[benchmarking guide](doc/benchmarking.md).
## Advanced guide
@ -112,85 +125,19 @@ options see "Benchmarking with benchmark_xl" section below.
We build a common environment based on Debian/Ubuntu using Docker. Other
systems may have different combinations of versions and dependencies that
have not been tested and may not work. For those cases we recommend using the
Docker environment as explained in the
Docker container as explained in the
[step by step guide](doc/developing_in_docker.md).
### Building JPEG XL for developers
For experienced developers, we also provide build instructions for an [up to
date Debian-based Linux](doc/developing_in_debian.md) and [64-bit
Windows](doc/developing_in_windows.md). If you encounter any difficulties,
please use Docker instead.
For experienced developers, we provide build instructions for several other environments:
## Benchmarking with benchmark_xl
* [Building on Debian](doc/developing_in_debian.md)
* Building on Windows with [vcpkg](doc/developing_in_windows_vcpkg.md) (Visual Studio 2019)
* Building on Windows with [MSYS2](doc/developing_in_windows_msys.md)
* [Cross Compiling for Windows with Crossroad](doc/developing_with_crossroad.md)
We recommend `build/tools/benchmark_xl` as a convenient method for reading
images or image sequences, encoding them using various codecs (jpeg jxl png
webp), decoding the result, and computing objective quality metrics. An example
invocation is:
```bash
build/tools/benchmark_xl --input "/path/*.png" --codec jxl:wombat:d1,jxl:cheetah:d2
```
Multiple comma-separated codecs are allowed. The characters after : are
parameters for the codec, separated by colons, in this case specifying maximum
target psychovisual distances of 1 and 2 (higher implies lower quality) and
the encoder effort (see below). Other common parameters are `r0.5` (target
bitrate 0.5 bits per pixel) and `q92` (quality 92, on a scale of 0-100, where
higher is better). The `jxl` codec supports the following additional parameters:
Speed: `falcon`, `cheetah`, `hare`, `wombat`, `squirrel`, `kitten`, `tortoise`
control the encoder effort in ascending order. This also affects memory usage:
using lower effort will typically reduce memory consumption during encoding.
* `falcon` disables all of the following tools.
* `cheetah` enables coefficient reordering, context clustering, and heuristics
for selecting DCT sizes and quantization steps.
* `hare` enables Gaborish filtering, chroma from luma, and an initial estimate
of quantization steps.
* `wombat` enables error diffusion quantization and full DCT size selection
heuristics.
* `squirrel` (default) enables dots, patches, and spline detection, and full
context clustering.
* `kitten` optimizes the adaptive quantization for a psychovisual metric.
* `tortoise` enables a more thorough adaptive quantization search.
Mode: JPEG XL has two modes. The default is Var-DCT mode, which is suitable for
lossy compression. The other mode is Modular mode, which is suitable for lossless
compression. Modular mode can also do lossy compression (e.g. `jxl:m:q50`).
* `m` activates modular mode.
Other arguments to benchmark_xl include:
* `--save_compressed`: save codestreams to `output_dir`.
* `--save_decompressed`: save decompressed outputs to `output_dir`.
* `--output_extension`: selects the format used to output decoded images.
* `--num_threads`: number of codec instances that will independently
encode/decode images, or 0.
* `--inner_threads`: how many threads each instance should use for parallel
encoding/decoding, or 0.
* `--encode_reps`/`--decode_reps`: how many times to repeat encoding/decoding
each image, for more consistent measurements (we recommend 10).
The benchmark output begins with a header:
```
Compr Input Compr Compr Compr Decomp Butteraugli
Method Pixels Size BPP # MP/s MP/s Distance Error p norm BPP*pnorm Errors
```
`ComprMethod` lists each each comma-separated codec. `InputPixels` is the number
of pixels in the input image. `ComprSize` is the codestream size in bytes and
`ComprBPP` the bitrate. `Compr MP/s` and `Decomp MP/s` are the
compress/decompress throughput, in units of Megapixels/second.
`Butteraugli Distance` indicates the maximum psychovisual error in the decoded
image (larger is worse). `Error p norm` is a similar summary of the psychovisual
error, but closer to an average, giving less weight to small low-quality
regions. `BPP*pnorm` is the product of `ComprBPP` and `Error p norm`, which is a
figure of merit for the codec (lower is better). `Errors` is nonzero if errors
occurred while loading or encoding/decoding the image.
If you encounter any difficulties, please use Docker instead.
## License
@ -207,19 +154,22 @@ format: Cloudinary and Google.
### Codec description
* [JPEG XL Format Overview](doc/format_overview.md)
* [Introductory paper](https://www.spiedigitallibrary.org/proceedings/Download?fullDOI=10.1117%2F12.2529237) (open-access)
* [XL Overview](doc/xl_overview.md) - a brief introduction to the source code modules
* [JPEG XL white paper](http://ds.jpeg.org/whitepapers/jpeg-xl-whitepaper.pdf)
* [JPEG XL website](https://jpeg.org/jpegxl/)
* [Jon's JXL info page](https://sneyers.info/jxl/)
* [JPEG XL official website](https://jpeg.org/jpegxl)
* [JPEG XL community website](https://jpegxl.info)
### Development process
* [Docker setup - **start here**](doc/developing_in_docker.md)
* [Building on Debian](doc/developing_in_debian.md) - for experts only
* [Building on Windows](doc/developing_in_windows.md) - for experts only
* [More information on testing/build options](doc/building_and_testing.md)
* [Git guide for JPEG XL](doc/developing_in_github.md) - for developers only
* [Git guide for JPEG XL](doc/developing_in_github.md) - for developers
* [Fuzzing](doc/fuzzing.md) - for developers
* [Building Web Assembly artifacts](doc/building_wasm.md)
* [Test coverage on Codecov.io](https://app.codecov.io/gh/libjxl/libjxl) - for
developers
* [libjxl documentation on readthedocs.io](https://libjxl.readthedocs.io/)
### Contact

26
third_party/jpeg-xl/bash_test.sh vendored Executable file → Normal file
View file

@ -65,7 +65,7 @@ test_copyright() {
local f
for f in $(
git ls-files | grep -E \
'(Dockerfile.*|\.c|\.cc|\.cpp|\.gni|\.h|\.java|\.sh|\.m|\.py|\.ui)$'); do
'(Dockerfile.*|\.c|\.cc|\.cpp|\.gni|\.h|\.java|\.sh|\.m|\.py|\.ui|\.yml)$'); do
if [[ "${f#third_party/}" == "$f" ]]; then
# $f is not in third_party/
if ! head -n 10 "$f" |
@ -173,6 +173,15 @@ test_deps_version() {
cat >&2 <<EOF
deps.sh: SHA for project ${line} is at ${deps_sha} but the git submodule is at
${git_sha}. Please update deps.sh
If you did not intend to change the submodule's SHA value, it is possible that
you accidentally included this change in your commit after a rebase or checkout
without running "git submodule --init". To revert the submodule change run from
the top checkout directory:
git -C ${line} checkout ${deps_sha}
git commit --amend ${line}
EOF
return 1
fi
@ -199,6 +208,21 @@ EOF
return $ret
}
# Test that we don't use %n in C++ code to avoid using it in printf and scanf.
# This test is not very precise but in cases where "module n" is needed we would
# normally have "% n" instead of "%n". Using %n is not allowed in Android 10+.
test_percent_n() {
local ret=0
local f
for f in $(git ls-files | grep -E '(\.cc|\.cpp|\.h)$'); do
if grep -i -H -n -E '%h*n' "$f" >&2; then
echo "Don't use \"%n\"." >&2
ret=1
fi
done
return ${ret}
}
main() {
local ret=0
cd "${MYDIR}"

198
third_party/jpeg-xl/ci.sh vendored Executable file → Normal file
View file

@ -89,7 +89,7 @@ if [[ ! -z "${HWY_BASELINE_TARGETS}" ]]; then
fi
# Version inferred from the CI variables.
CI_COMMIT_SHA=${CI_COMMIT_SHA:-}
CI_COMMIT_SHA=${CI_COMMIT_SHA:-${GITHUB_SHA:-}}
JPEGXL_VERSION=${JPEGXL_VERSION:-${CI_COMMIT_SHA:0:8}}
# Benchmark parameters
@ -226,8 +226,13 @@ merge_request_commits() {
{ set +x; } 2>/dev/null
# GITHUB_SHA is the current reference being build in GitHub Actions.
if [[ -n "${GITHUB_SHA:-}" ]]; then
git -C "${MYDIR}" fetch -q origin "${GITHUB_SHA}"
MR_HEAD_SHA="${GITHUB_SHA}"
# GitHub normally does a checkout of a merge commit on a shallow repository
# by default. We want to get a bit more of the history to be able to diff
# changes on the Pull Request if needed. This fetches 10 more commits which
# should be enough given that PR normally should have 1 commit.
git -C "${MYDIR}" fetch -q origin "${GITHUB_SHA}" --depth 10
MR_HEAD_SHA="$(git rev-parse "FETCH_HEAD^2" 2>/dev/null ||
echo "${GITHUB_SHA}")"
else
# CI_BUILD_REF is the reference currently being build in the CI workflow.
MR_HEAD_SHA=$(git -C "${MYDIR}" rev-parse -q "${CI_BUILD_REF:-HEAD}")
@ -355,6 +360,8 @@ cmake_configure() {
-DJPEGXL_ENABLE_VIEWERS=ON
-DJPEGXL_ENABLE_PLUGINS=ON
-DJPEGXL_ENABLE_DEVTOOLS=ON
# We always use libfuzzer in the ci.sh wrapper.
-DJPEGXL_FUZZER_LINK_FLAGS="-fsanitize=fuzzer"
)
if [[ "${BUILD_TARGET}" != *mingw32 ]]; then
args+=(
@ -544,10 +551,12 @@ cmd_coverage_report() {
local gcovr_args=(
-r "${real_build_dir}"
--gcov-executable "${LLVM_COV} gcov"
# Only print coverage information for the jxl and fuif directories. The rest
# Only print coverage information for the libjxl directories. The rest
# is not part of the code under test.
--filter '.*jxl/.*'
--exclude '.*_test.cc'
--exclude '.*_test-only..*'
--exclude '.*test_utils..*'
--object-directory "${real_build_dir}"
)
@ -587,6 +596,27 @@ cmd_gbench() {
)
}
cmd_asanfuzz() {
CMAKE_CXX_FLAGS+=" -fsanitize=fuzzer-no-link"
CMAKE_C_FLAGS+=" -fsanitize=fuzzer-no-link"
cmd_asan -DJPEGXL_ENABLE_FUZZERS=ON "$@"
}
cmd_msanfuzz() {
# Install msan if needed before changing the flags.
detect_clang_version
local msan_prefix="${HOME}/.msan/${CLANG_VERSION}"
if [[ ! -d "${msan_prefix}" || -e "${msan_prefix}/lib/libc++abi.a" ]]; then
# Install msan libraries for this version if needed or if an older version
# with libc++abi was installed.
cmd_msan_install
fi
CMAKE_CXX_FLAGS+=" -fsanitize=fuzzer-no-link"
CMAKE_C_FLAGS+=" -fsanitize=fuzzer-no-link"
cmd_msan -DJPEGXL_ENABLE_FUZZERS=ON "$@"
}
cmd_asan() {
SANITIZER="asan"
CMAKE_C_FLAGS+=" -DJXL_ENABLE_ASSERT=1 -g -DADDRESS_SANITIZER \
@ -722,6 +752,75 @@ cmd_msan_install() {
done
}
# Internal build step shared between all cmd_ossfuzz_* commands.
_cmd_ossfuzz() {
local sanitizer="$1"
shift
mkdir -p "${BUILD_DIR}"
local real_build_dir=$(realpath "${BUILD_DIR}")
# oss-fuzz defines three directories:
# * /work, with the working directory to do re-builds
# * /src, with the source code to build
# * /out, with the output directory where to copy over the built files.
# We use $BUILD_DIR as the /work and the script directory as the /src. The
# /out directory is ignored as developers are used to look for the fuzzers in
# $BUILD_DIR/tools/ directly.
if [[ "${sanitizer}" = "memory" && ! -d "${BUILD_DIR}/msan" ]]; then
sudo docker run --rm -i \
--user $(id -u):$(id -g) \
-v "${real_build_dir}":/work \
gcr.io/oss-fuzz-base/msan-libs-builder \
bash -c "cp -r /msan /work"
fi
# Args passed to ninja. These will be evaluated as a string separated by
# spaces.
local jpegxl_extra_args="$@"
sudo docker run --rm -i \
-e JPEGXL_UID=$(id -u) \
-e JPEGXL_GID=$(id -g) \
-e FUZZING_ENGINE="${FUZZING_ENGINE:-libfuzzer}" \
-e SANITIZER="${sanitizer}" \
-e ARCHITECTURE=x86_64 \
-e FUZZING_LANGUAGE=c++ \
-e MSAN_LIBS_PATH="/work/msan" \
-e JPEGXL_EXTRA_ARGS="${jpegxl_extra_args}" \
-v "${MYDIR}":/src/libjxl \
-v "${MYDIR}/tools/ossfuzz-build.sh":/src/build.sh \
-v "${real_build_dir}":/work \
gcr.io/oss-fuzz/libjxl
}
cmd_ossfuzz_asan() {
_cmd_ossfuzz address "$@"
}
cmd_ossfuzz_msan() {
_cmd_ossfuzz memory "$@"
}
cmd_ossfuzz_ubsan() {
_cmd_ossfuzz undefined "$@"
}
cmd_ossfuzz_ninja() {
[[ -e "${BUILD_DIR}/build.ninja" ]]
local real_build_dir=$(realpath "${BUILD_DIR}")
if [[ -e "${BUILD_DIR}/msan" ]]; then
echo "ossfuzz_ninja doesn't work with msan builds. Use ossfuzz_msan." >&2
exit 1
fi
sudo docker run --rm -i \
--user $(id -u):$(id -g) \
-v "${MYDIR}":/src/libjxl \
-v "${real_build_dir}":/work \
gcr.io/oss-fuzz/libjxl \
ninja -C /work "$@"
}
cmd_fast_benchmark() {
local small_corpus_tar="${BENCHMARK_CORPORA}/jyrki-full.tar"
mkdir -p "${BENCHMARK_CORPORA}"
@ -802,7 +901,7 @@ run_benchmark() {
local benchmark_args=(
--input "${src_img_dir}/*.png"
--codec=jpeg:yuv420:q85,webp:q80,jxl:fast:d1,jxl:fast:d1:downsampling=8,jxl:fast:d4,jxl:fast:d4:downsampling=8,jxl:m:cheetah:nl,jxl:cheetah:m,jxl:m:cheetah:P6,jxl:m:falcon:q80
--codec=jpeg:yuv420:q85,webp:q80,jxl:fast:d1,jxl:fast:d1:downsampling=8,jxl:fast:d4,jxl:fast:d4:downsampling=8,jxl:cheetah:m,jxl:m:cheetah:P6,jxl:m:falcon:q80
--output_dir "${output_dir}"
--noprofiler --show_progress
--num_threads="${num_threads}"
@ -833,7 +932,7 @@ $(cat "${output_dir}/results.txt")
# Helper function to wait for the CPU temperature to cool down on ARM.
wait_for_temp() {
{ set +x; } 2>/dev/null
local temp_limit=${1:-37000}
local temp_limit=${1:-38000}
if [[ -z "${THERMAL_FILE:-}" ]]; then
echo "Must define the THERMAL_FILE with the thermal_zoneX/temp file" \
"to read the temperature from. This is normally set in the runner." >&2
@ -844,9 +943,15 @@ wait_for_temp() {
echo -n "Waiting for temp to get down from ${org_temp}... "
fi
local temp="${org_temp}"
local secs=0
while [[ "${temp}" -ge "${temp_limit}" ]]; do
sleep 1
temp=$(cat "${THERMAL_FILE}")
echo -n "${temp} "
secs=$((secs + 1))
if [[ ${secs} -ge 5 ]]; then
break
fi
done
if [[ "${org_temp}" -ge "${temp_limit}" ]]; then
echo "Done, temp=${temp}"
@ -901,7 +1006,6 @@ cmd_arm_benchmark() {
"--modular --responsive=1"
# Near-lossless options:
"--epf=0 --distance=0.3 --speed=fast"
"--modular -N 3 -I 0"
"--modular -Q 97"
)
@ -1085,7 +1189,7 @@ cmd_fuzz() {
cmd_lint() {
merge_request_commits
{ set +x; } 2>/dev/null
local versions=(${1:-6.0 7 8 9})
local versions=(${1:-6.0 7 8 9 10 11})
local clang_format_bins=("${versions[@]/#/clang-format-}" clang-format)
local tmpdir=$(mktemp -d)
CLEANUP_FILES+=("${tmpdir}")
@ -1108,7 +1212,7 @@ cmd_lint() {
fi
installed+=("${clang_format}")
local tmppatch="${tmpdir}/${clang_format}.patch"
# We include in this linter all the changes including the uncommited changes
# We include in this linter all the changes including the uncommitted changes
# to avoid printing changes already applied.
set -x
git -C "${MYDIR}" "${clang_format}" --binary "${clang_format}" \
@ -1258,6 +1362,70 @@ cmd_debian_build() {
esac
}
get_version() {
local varname=$1
local line=$(grep -F "set(${varname} " lib/CMakeLists.txt | head -n 1)
[[ -n "${line}" ]]
line="${line#set(${varname} }"
line="${line%)}"
echo "${line}"
}
cmd_bump_version() {
local newver="${1:-}"
if ! which dch >/dev/null; then
echo "Run:\n sudo apt install debhelper"
exit 1
fi
if [[ -z "${newver}" ]]; then
local major=$(get_version JPEGXL_MAJOR_VERSION)
local minor=$(get_version JPEGXL_MINOR_VERSION)
local patch=0
minor=$(( ${minor} + 1))
else
local major="${newver%%.*}"
newver="${newver#*.}"
local minor="${newver%%.*}"
newver="${newver#${minor}}"
local patch="${newver#.}"
if [[ -z "${patch}" ]]; then
patch=0
fi
fi
newver="${major}.${minor}"
if [[ "${patch}" != "0" ]]; then
newver="${newver}.${patch}"
fi
echo "Bumping version to ${newver} (${major}.${minor}.${patch})"
sed -E \
-e "s/(set\\(JPEGXL_MAJOR_VERSION) [0-9]+\\)/\\1 ${major})/" \
-e "s/(set\\(JPEGXL_MINOR_VERSION) [0-9]+\\)/\\1 ${minor})/" \
-e "s/(set\\(JPEGXL_PATCH_VERSION) [0-9]+\\)/\\1 ${patch})/" \
-i lib/CMakeLists.txt
# Update lib.gni
tools/build_cleaner.py --update
# Mark the previous version as "unstable".
DEBCHANGE_RELEASE_HEURISTIC=log dch -M --distribution unstable --release ''
DEBCHANGE_RELEASE_HEURISTIC=log dch -M \
--newversion "${newver}" \
"Bump JPEG XL version to ${newver}."
}
# Check that the AUTHORS file contains the email of the committer.
cmd_authors() {
merge_request_commits
# TODO(deymo): Handle multiple commits and check that they are all the same
# author.
local email=$(git log --format='%ae' "${MR_HEAD_SHA}^!")
local name=$(git log --format='%an' "${MR_HEAD_SHA}^!")
"${MYDIR}"/tools/check_author.py "${email}" "${name}"
}
main() {
local cmd="${1:-}"
if [[ -z "${cmd}" ]]; then
@ -1272,6 +1440,8 @@ Where cmd is one of:
msan Build and test an MSan (MemorySanitizer) build. Needs to have msan
c++ libs installed with msan_install first.
tsan Build and test a TSan (ThreadSanitizer) build.
asanfuzz Build and test an ASan (AddressSanitizer) build for fuzzing.
msanfuzz Build and test an MSan (MemorySanitizer) build for fuzzing.
test Run the tests build by opt, debug, release, asan or msan. Useful when
building with SKIP_TEST=1.
gbench Run the Google benchmark tests.
@ -1287,6 +1457,7 @@ Where cmd is one of:
lint Run the linter checks on the current commit or merge request.
tidy Run clang-tidy on the current commit or merge request.
authors Check that the last commit's author is listed in the AUTHORS file.
msan_install Install the libc++ libraries required to build in msan mode. This
needs to be done once.
@ -1294,6 +1465,15 @@ Where cmd is one of:
debian_build <srcpkg> Build the given source package.
debian_stats Print stats about the built packages.
oss-fuzz commands:
ossfuzz_asan Build the local source inside oss-fuzz docker with asan.
ossfuzz_msan Build the local source inside oss-fuzz docker with msan.
ossfuzz_ubsan Build the local source inside oss-fuzz docker with ubsan.
ossfuzz_ninja Run ninja on the BUILD_DIR inside the oss-fuzz docker. Extra
parameters are passed to ninja, for example "djxl_fuzzer" will
only build that ninja target. Use for faster build iteration
after one of the ossfuzz_*san commands.
You can pass some optional environment variables as well:
- BUILD_DIR: The output build directory (by default "$$repo/build")
- BUILD_TARGET: The target triplet used when cross-compiling.

View file

@ -1,57 +1,54 @@
jpeg-xl (0.7) UNRELEASED; urgency=medium
* Bump JPEG XL version to 0.7.
-- JPEG XL Maintainers <jpegxl@google.com> Fri, 10 Sep 2021 16:08:17 +0200
jpeg-xl (0.6) unstable; urgency=medium
* Bump JPEG XL version to 0.6.
-- JPEG XL Maintainers <jpegxl@google.com> Fri, 10 Sep 2021 16:08:17 +0200
jpeg-xl (0.5.0) unstable; urgency=medium
* Bump JPEG XL version to 0.5.0.
-- JPEG XL Maintainers <jpegxl@google.com> Thu, 12 Aug 2021 23:49:40 +0200
jpeg-xl (0.3.7) UNRELEASED; urgency=medium
* Bump JPEG XL version to 0.3.7.
* Fix a rounding issue in 8-bit decoding.
-- Sami Boukortt <sboukortt@google.com> Mon, 29 Mar 2021 12:14:20 +0200
jpeg-xl (0.3.6) UNRELEASED; urgency=medium
* Bump JPEG XL version to 0.3.6.
* Fix a bug that could result in the generation of invalid codestreams as
well as failure to decode valid streams.
-- Sami Boukortt <sboukortt@google.com> Thu, 25 Mar 2021 17:40:58 +0100
jpeg-xl (0.3.5) UNRELEASED; urgency=medium
* Bump JPEG XL version to 0.3.5.
* Memory usage improvements.
* New encode-time options for faster decoding at the cost of quality.
* Faster decoding to 8-bit output with the C API.
* GIMP plugin: avoid the sRGB conversion dialog for sRGB images, do not show
a console window on Windows.
* Various bug fixes.
* Man pages for cjxl and djxl.
-- Sami Boukortt <sboukortt@google.com> Tue, 23 Mar 2021 15:20:44 +0100
jpeg-xl (0.3.4) UNRELEASED; urgency=medium
* Bump JPEG XL version to 0.3.4.
* Improved box parsing.
* Improved metadata handling.
* Performance and memory usage improvements.
-- Sami Boukortt <sboukortt@google.com> Tue, 16 Mar 2021 12:13:59 +0100
jpeg-xl (0.3.3) UNRELEASED; urgency=medium
* Bump JPEG XL version to 0.3.3.
* Performance improvements for small images.
* Add a (flag-protected) non-high-precision mode with better speed.
* Significantly speed up the PQ EOTF.
* Allow optional HDR tone mapping in djxl (--tone_map, --display_nits).
* Change the behavior of djxl -j to make it consistent with cjxl (#153).
* Improve image quality.
* Improve EXIF handling.
-- Sami Boukortt <sboukortt@google.com> Fri, 5 Mar 2021 19:15:26 +0100
jpeg-xl (0.3.2) UNRELEASED; urgency=medium
* Bump JPEG XL version to 0.3.2.
* Fix embedded ICC encoding regression #149.
-- Alex Deymo <deymo@google.com> Fri, 12 Feb 2021 21:00:12 +0100

View file

@ -3,20 +3,26 @@ Maintainer: JPEG XL Maintainers <jpegxl@google.com>
Section: misc
Priority: optional
Standards-Version: 3.9.8
Build-Depends: cmake,
debhelper (>= 9),
libbrotli-dev,
libgif-dev,
libgmock-dev,
libgoogle-perftools-dev,
libgtest-dev,
libhwy-dev,
libjpeg-dev,
libopenexr-dev,
libpng-dev,
libwebp-dev,
pkg-config,
Homepage: https://gitlab.com/wg1/jpeg-xl
Build-Depends:
asciidoc,
cmake,
debhelper (>= 9),
libbrotli-dev,
libgdk-pixbuf-2.0-dev | libgdk-pixbuf2.0-dev,
libgif-dev,
libgimp2.0-dev,
libgmock-dev,
libgoogle-perftools-dev,
libgtest-dev,
libhwy-dev,
libjpeg-dev,
libopenexr-dev,
libpng-dev,
libwebp-dev,
pkg-config,
xdg-utils,
xmlto,
Homepage: https://github.com/libjxl/libjxl
Rules-Requires-Root: no
Package: jxl
@ -36,6 +42,7 @@ Package: libjxl-dev
Architecture: any
Section: libdevel
Depends: libjxl (= ${binary:Version}), ${misc:Depends}
libhwy-dev,
Description: JPEG XL Image Coding System - "JXL" (development files)
The JPEG XL Image Coding System (ISO/IEC 18181) is a lossy and
lossless image compression format. It has a rich feature set and is
@ -59,3 +66,23 @@ Description: JPEG XL Image Coding System - "JXL" (shared libraries)
several features that help transition from the legacy JPEG format.
.
This package installs shared libraries.
Package: libjxl-gdk-pixbuf
Architecture: any
Multi-Arch: same
Section: libs
Depends: ${shlibs:Depends}, ${misc:Depends}
Pre-Depends: ${misc:Pre-Depends}
Description: JPEG XL Plugin for gdk-pixbuf
This package installs the required files for reading JPEG XL files in
GTK applications.
Package: libjxl-gimp-plugin
Architecture: any
Multi-Arch: same
Section: graphics
Depends: ${shlibs:Depends}, ${misc:Depends}
Pre-Depends: ${misc:Pre-Depends}
Enhances: gimp
Description: JPEG XL Import and Export Plugin for GIMP
This is a plugin for GIMP version 2.10.x to import and export JPEG XL images.

View file

@ -1 +1,3 @@
debian/tmp/usr/bin/*
usr/bin/*
usr/share/man/man1/cjxl.1
usr/share/man/man1/djxl.1

View file

@ -1,4 +1,4 @@
debian/tmp/usr/include/jxl/*.h
debian/tmp/usr/lib/*/*.a
debian/tmp/usr/lib/*/*.so
debian/tmp/usr/lib/*/pkgconfig/*.pc
usr/include/jxl/*.h
usr/lib/*/*.a
usr/lib/*/*.so
usr/lib/*/pkgconfig/*.pc

View file

@ -0,0 +1,3 @@
usr/lib/*/gdk-pixbuf-*/*/loaders/*
usr/share/mime/packages/image-jxl.xml
usr/share/thumbnailers/jxl.thumbnailer

View file

@ -0,0 +1 @@
usr/lib/gimp

View file

@ -1 +1 @@
debian/tmp/usr/lib/*/libjxl*.so.*
usr/lib/*/libjxl*.so.*

7
third_party/jpeg-xl/debian/rules vendored Executable file → Normal file
View file

@ -1,4 +1,7 @@
#!/usr/bin/make -f
include /usr/share/dpkg/pkg-info.mk
%:
dh $@ --buildsystem=cmake
@ -6,7 +9,9 @@ override_dh_auto_configure:
# TODO(deymo): Remove the DCMAKE_BUILD_TYPE once builds without NDEBUG
# are as useful as Release builds.
dh_auto_configure -- \
-DJPEGXL_VERSION=$(DEB_VERSION) \
-DCMAKE_BUILD_TYPE=RelWithDebInfo \
-DJPEGXL_FORCE_SYSTEM_GTEST=ON \
-DJPEGXL_FORCE_SYSTEM_BROTLI=ON \
-DJPEGXL_FORCE_SYSTEM_HWY=ON
-DJPEGXL_FORCE_SYSTEM_HWY=ON \
-DJPEGXL_ENABLE_PLUGINS=ON

4
third_party/jpeg-xl/deps.sh vendored Executable file → Normal file
View file

@ -13,7 +13,7 @@ MYDIR=$(dirname $(realpath "$0"))
# Git revisions we use for the given submodules. Update these whenever you
# update a git submodule.
THIRD_PARTY_HIGHWAY="ca1a57c342cd815053abfcffa29b44eaead4f20b"
THIRD_PARTY_HIGHWAY="e2397743fe092df68b760d358253773699a16c93"
THIRD_PARTY_LODEPNG="48e5364ef48ec2408f44c727657ac1b6703185f8"
THIRD_PARTY_SKCMS="64374756e03700d649f897dbd98c95e78c30c7da"
THIRD_PARTY_SJPEG="868ab558fad70fcbe8863ba4e85179eeb81cc840"
@ -59,7 +59,7 @@ download_github() {
main() {
if git -C "${MYDIR}" rev-parse; then
cat >&2 <<EOF
Currenty directory is a git repository, downloading dependencies via git:
Current directory is a git repository, downloading dependencies via git:
git submodule update --init --recursive

0
third_party/jpeg-xl/docker/build.sh vendored Executable file → Normal file
View file

4
third_party/jpeg-xl/docker/scripts/emsdk_install.sh vendored Executable file → Normal file
View file

@ -4,10 +4,10 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
EMSDK_URL="https://github.com/emscripten-core/emsdk/archive/master.tar.gz"
EMSDK_URL="https://github.com/emscripten-core/emsdk/archive/main.tar.gz"
EMSDK_DIR="/opt/emsdk"
EMSDK_RELEASE="2.0.4"
EMSDK_RELEASE="2.0.23"
set -eu -x

18
third_party/jpeg-xl/docker/scripts/jpegxl_builder.sh vendored Executable file → Normal file
View file

@ -57,7 +57,7 @@ BENCHMARK_FLAGS="-DCMAKE_BUILD_TYPE=Release -DBENCHMARK_ENABLE_TESTING=OFF \
-DCMAKE_POSITION_INDEPENDENT_CODE=ON"
# V8
V8_VERSION="8.7.230"
V8_VERSION="9.3.22"
# Temporary files cleanup hooks.
CLEANUP_FILES=()
@ -168,6 +168,11 @@ install_pkgs() {
parallel
pkg-config
# For compiling / testing JNI wrapper. JDK8 is almost 2x smaller than JDK11
# openjdk-8-jdk-headless would be 50MB smaller, unfortunately, CMake
# does mistakenly thinks it does not contain JNI feature.
openjdk-8-jdk
# These are used by the ./ci.sh lint in the native builder.
clang-format-7
clang-format-8
@ -358,7 +363,7 @@ install_from_source() {
fi
if [[ -e "${srcdir}/CMakeLists.txt" ]]; then
# Most pacakges use cmake for building which is easier to configure for
# Most packages use cmake for building which is easier to configure for
# cross-compiling.
if [[ "${package}" == "JPEG_TURBO" && "${target}" == wasm* ]]; then
# JT erroneously detects WASM CPU as i386 and tries to use asm.
@ -399,6 +404,10 @@ install_from_source() {
exit 1
fi
# CMake mistakenly uses ".so" libraries and EMCC fails to link properly.
if [[ "${target}" == wasm* ]]; then
rm -f "${prefix}/lib"/*.so*
fi
done
}
@ -431,6 +440,9 @@ main() {
install_binutils
apt clean
# Remove prebuilt Java classes cache.
rm /usr/lib/jvm/java-8-openjdk-amd64/jre/lib/amd64/server/classes.jsa
# Manually extract packages for the target arch that can't install it directly
# at the same time as the native ones.
local ubarch
@ -492,7 +504,7 @@ main() {
install_from_source BENCHMARK "${LIST_TARGETS[@]}" "${LIST_MINGW_TARGETS[@]}"
# Install v8. v8 has better WASM SIMD support than NodeJS 14.
# Install v8. v8 has better WASM SIMD support than NodeJS 14 (LTS).
# First we need the installer to install v8.
npm install jsvu -g
# install specific version;

0
third_party/jpeg-xl/docker/scripts/msan_install.sh vendored Executable file → Normal file
View file

0
third_party/jpeg-xl/docker/scripts/qemu_install.sh vendored Executable file → Normal file
View file

View file

@ -3,17 +3,54 @@
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
# Example project using libjxl.
cmake_minimum_required(VERSION 3.10)
project(SAMPLE_LIBJXL LANGUAGES C CXX)
# Use pkg-config to find libjxl.
find_package(PkgConfig)
pkg_check_modules(Jxl REQUIRED IMPORTED_TARGET libjxl)
pkg_check_modules(JxlThreads REQUIRED IMPORTED_TARGET libjxl_threads)
# Build the example encoder/decoder binaries using the default shared libraries
# installed.
add_executable(decode_oneshot decode_oneshot.cc)
target_link_libraries(decode_oneshot jxl_dec jxl_threads)
target_link_libraries(decode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)
add_executable(encode_oneshot encode_oneshot.cc)
target_link_libraries(encode_oneshot jxl jxl_threads)
target_link_libraries(encode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)
add_executable(jxlinfo jxlinfo.c)
target_link_libraries(jxlinfo jxl)
target_link_libraries(jxlinfo PkgConfig::Jxl)
if(NOT ${SANITIZER} STREQUAL "none")
# Linking a C test binary with the C++ JPEG XL implementation when using
# address sanitizer is not well supported by clang 9, so force using clang++
# for linking this test if a sanitizer is used.
set_target_properties(jxlinfo PROPERTIES LINKER_LANGUAGE CXX)
endif() # SANITIZER != "none"
# Building a static binary with the static libjxl dependencies. How to load
# static library configs from pkg-config and how to build static binaries
# depends on the platform, and building static binaries in general has problems.
# If you don't need static binaries you can remove this section.
add_library(StaticJxl INTERFACE IMPORTED GLOBAL)
set_target_properties(StaticJxl PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${Jxl_STATIC_INCLUDE_DIR}"
INTERFACE_COMPILE_OPTIONS "${Jxl_STATIC_CFLAGS_OTHER}"
INTERFACE_LINK_LIBRARIES "${Jxl_STATIC_LDFLAGS}"
)
add_library(StaticJxlThreads INTERFACE IMPORTED GLOBAL)
set_target_properties(StaticJxlThreads PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${JxlThreads_STATIC_INCLUDE_DIR}"
INTERFACE_COMPILE_OPTIONS "${JxlThreads_STATIC_CFLAGS_OTHER}"
# libgcc uses weak symbols for pthread which means that -lpthread is not
# linked when compiling a static binary. This is a platform-specific fix for
# that.
INTERFACE_LINK_LIBRARIES
"${JxlThreads_STATIC_LDFLAGS} -Wl,--whole-archive -lpthread -Wl,--no-whole-archive"
)
add_executable(decode_oneshot_static decode_oneshot.cc)
target_link_libraries(decode_oneshot_static
-static StaticJxl StaticJxlThreads)
add_executable(encode_oneshot_static encode_oneshot.cc)
target_link_libraries(encode_oneshot_static
-static StaticJxl StaticJxlThreads)

View file

@ -0,0 +1,165 @@
// 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 in one shot (all input bytes
// available at once). The example outputs the pixels and color information to a
// floating point image and an ICC profile on disk.
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <vector>
#include "jxl/decode.h"
#include "jxl/decode_cxx.h"
bool DecodeJpegXlExif(const uint8_t* jxl, size_t size,
std::vector<uint8_t>* exif) {
auto dec = JxlDecoderMake(nullptr);
// We're only interested in the Exif boxes in this example, so don't
// subscribe to events related to pixel data.
if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BOX)) {
fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
return false;
}
if (JXL_DEC_SUCCESS != JxlDecoderSetDecompressBoxes(dec.get(), JXL_TRUE)) {
fprintf(stderr, "JxlDecoderSetDecompressBoxes failed\n");
return false;
}
JxlDecoderSetInput(dec.get(), jxl, size);
const constexpr size_t kChunkSize = 65536;
size_t output_pos = 0;
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) {
fprintf(stderr, "Error, already provided all input\n");
return false;
} else if (status == JXL_DEC_BOX) {
if (!exif->empty()) {
size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get());
exif->resize(exif->size() - remaining);
// No need to wait for JXL_DEC_SUCCESS or decode other boxes.
return true;
}
JxlBoxType type;
if (!JxlDecoderGetBoxType(dec.get(), &type, JXL_TRUE)) {
fprintf(stderr, "Error, failed to get box type\n");
}
if (!memcmp(type, "Exif", 4)) {
exif->resize(kChunkSize);
JxlDecoderSetBoxBuffer(dec.get(), exif->data(), exif->size());
}
} else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get());
output_pos += kChunkSize - remaining;
exif->resize(exif->size() + kChunkSize);
JxlDecoderSetBoxBuffer(dec.get(), exif->data() + output_pos,
exif->size() - output_pos);
} else if (status == JXL_DEC_SUCCESS) {
if (!exif->empty()) {
size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get());
exif->resize(exif->size() - remaining);
return true;
}
return true;
} 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);
}
bool WriteFile(const char* filename, const uint8_t* data, size_t size) {
FILE* file = fopen(filename, "wb");
if (!file) {
fprintf(stderr, "Could not open %s for writing", filename);
return false;
}
fwrite(data, 1, size, file);
if (fclose(file) != 0) {
return false;
}
return true;
}
int main(int argc, char* argv[]) {
if (argc != 3) {
fprintf(stderr,
"Usage: %s <jxl> <exif>\n"
"Where:\n"
" jxl = input JPEG XL image filename\n"
" exif = output exif filename\n"
"Output files will be overwritten.\n",
argv[0]);
return 1;
}
const char* jxl_filename = argv[1];
const char* exif_filename = argv[2];
std::vector<uint8_t> jxl;
if (!LoadFile(jxl_filename, &jxl)) {
fprintf(stderr, "couldn't load %s\n", jxl_filename);
return 1;
}
std::vector<uint8_t> exif;
if (!DecodeJpegXlExif(jxl.data(), jxl.size(), &exif)) {
fprintf(stderr, "Error while decoding the jxl file\n");
return 1;
}
if (exif.empty()) {
printf("No exif data present in this image\n");
} else {
if (!WriteFile(exif_filename, exif.data(), exif.size())) {
fprintf(stderr, "Error while writing the exif file\n");
return 1;
}
printf("Successfully wrote %s\n", exif_filename);
}
return 0;
}

View file

@ -16,8 +16,8 @@
#include "jxl/decode.h"
#include "jxl/decode_cxx.h"
#include "jxl/thread_parallel_runner.h"
#include "jxl/thread_parallel_runner_cxx.h"
#include "jxl/resizable_parallel_runner.h"
#include "jxl/resizable_parallel_runner_cxx.h"
/** Decodes JPEG XL image to floating point pixels and ICC Profile. Pixel are
* stored as floating point, as interleaved RGBA (4 floating point values per
@ -29,8 +29,7 @@ bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size,
std::vector<float>* pixels, size_t* xsize,
size_t* ysize, std::vector<uint8_t>* icc_profile) {
// Multi-threaded parallel runner.
auto runner = JxlThreadParallelRunnerMake(
nullptr, JxlThreadParallelRunnerDefaultNumWorkerThreads());
auto runner = JxlResizableParallelRunnerMake(nullptr);
auto dec = JxlDecoderMake(nullptr);
if (JXL_DEC_SUCCESS !=
@ -42,7 +41,7 @@ bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size,
}
if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(),
JxlThreadParallelRunner,
JxlResizableParallelRunner,
runner.get())) {
fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
return false;
@ -69,6 +68,9 @@ bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size,
}
*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;

View file

@ -162,13 +162,12 @@ bool EncodeJxlOneshot(const std::vector<float>& pixels, const uint32_t xsize,
JxlPixelFormat pixel_format = {3, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
JxlBasicInfo basic_info = {};
JxlBasicInfo basic_info;
JxlEncoderInitBasicInfo(&basic_info);
basic_info.xsize = xsize;
basic_info.ysize = ysize;
basic_info.bits_per_sample = 32;
basic_info.exponent_bits_per_sample = 8;
basic_info.alpha_exponent_bits = 0;
basic_info.alpha_bits = 0;
basic_info.uses_original_profile = JXL_FALSE;
if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), &basic_info)) {
fprintf(stderr, "JxlEncoderSetBasicInfo failed\n");

View file

@ -0,0 +1,19 @@
# 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.
add_executable(decode_oneshot ${CMAKE_CURRENT_LIST_DIR}/decode_oneshot.cc)
target_link_libraries(decode_oneshot jxl_dec jxl_threads)
add_executable(encode_oneshot ${CMAKE_CURRENT_LIST_DIR}/encode_oneshot.cc)
target_link_libraries(encode_oneshot jxl jxl_threads)
add_executable(jxlinfo ${CMAKE_CURRENT_LIST_DIR}/jxlinfo.c)
target_link_libraries(jxlinfo jxl)
if(NOT ${SANITIZER} STREQUAL "none")
# Linking a C test binary with the C++ JPEG XL implementation when using
# address sanitizer is not well supported by clang 9, so force using clang++
# for linking this test if a sanitizer is used.
set_target_properties(jxlinfo PROPERTIES LINKER_LANGUAGE CXX)
endif() # SANITIZER != "none"

View file

@ -26,8 +26,8 @@ int PrintBasicInfo(FILE* file) {
JxlDecoderSetKeepOrientation(dec, 1);
if (JXL_DEC_SUCCESS !=
JxlDecoderSubscribeEvents(dec,
JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING)) {
JxlDecoderSubscribeEvents(
dec, JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING | JXL_DEC_FRAME)) {
fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
JxlDecoderDestroy(dec);
return 0;
@ -35,6 +35,7 @@ int PrintBasicInfo(FILE* file) {
JxlBasicInfo info;
int seen_basic_info = 0;
JxlFrameHeader frame_header;
for (;;) {
// The first time, this will output JXL_DEC_NEED_MORE_INPUT because no
@ -75,16 +76,21 @@ int PrintBasicInfo(FILE* file) {
seen_basic_info = 1;
printf("xsize: %u\n", info.xsize);
printf("ysize: %u\n", info.ysize);
printf("dimensions: %ux%u\n", info.xsize, info.ysize);
printf("have_container: %d\n", info.have_container);
printf("uses_original_profile: %d\n", info.uses_original_profile);
printf("bits_per_sample: %d\n", info.bits_per_sample);
printf("exponent_bits_per_sample: %d\n", info.exponent_bits_per_sample);
printf("intensity_target: %f\n", info.intensity_target);
printf("min_nits: %f\n", info.min_nits);
printf("relative_to_max_display: %d\n", info.relative_to_max_display);
printf("linear_below: %f\n", info.linear_below);
if (info.exponent_bits_per_sample)
printf("float, with exponent_bits_per_sample: %d\n",
info.exponent_bits_per_sample);
if (info.intensity_target != 255.f || info.min_nits != 0.f ||
info.relative_to_max_display != 0 ||
info.relative_to_max_display != 0.f) {
printf("intensity_target: %f\n", info.intensity_target);
printf("min_nits: %f\n", info.min_nits);
printf("relative_to_max_display: %d\n", info.relative_to_max_display);
printf("linear_below: %f\n", info.linear_below);
}
printf("have_preview: %d\n", info.have_preview);
if (info.have_preview) {
printf("preview xsize: %u\n", info.preview.xsize);
@ -97,25 +103,45 @@ int PrintBasicInfo(FILE* file) {
printf("num_loops: %u\n", info.animation.num_loops);
printf("have_timecodes: %d\n", info.animation.have_timecodes);
}
printf("orientation: %d\n", info.orientation);
const char* const orientation_string[8] = {
"Normal", "Flipped horizontally",
"Upside down", "Flipped vertically",
"Transposed", "90 degrees clockwise",
"Anti-Transposed", "90 degrees counter-clockwise"};
if (info.orientation > 0 && info.orientation < 9) {
printf("orientation: %d (%s)\n", info.orientation,
orientation_string[info.orientation - 1]);
} else {
fprintf(stderr, "Invalid orientation\n");
}
printf("num_color_channels: %d\n", info.num_color_channels);
printf("num_extra_channels: %d\n", info.num_extra_channels);
printf("alpha_bits: %d\n", info.alpha_bits);
printf("alpha_exponent_bits: %d\n", info.alpha_exponent_bits);
printf("alpha_premultiplied: %d\n", info.alpha_premultiplied);
const char* const ec_type_names[7] = {"Alpha", "Depth",
"Spot color", "Selection mask",
"K (of CMYK)", "CFA (Bayer data)",
"Thermal"};
for (uint32_t i = 0; i < info.num_extra_channels; i++) {
JxlExtraChannelInfo extra;
if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelInfo(dec, i, &extra)) {
fprintf(stderr, "JxlDecoderGetExtraChannelInfo failed\n");
break;
}
printf("extra channel: %u info:\n", i);
printf(" type: %d\n", extra.type);
printf("extra channel %u:\n", i);
printf(" type: %s\n",
(extra.type < 7 ? ec_type_names[extra.type]
: (extra.type == JXL_CHANNEL_OPTIONAL
? "Unknown but can be ignored"
: "Unknown, please update your libjxl")));
printf(" bits_per_sample: %u\n", extra.bits_per_sample);
printf(" exponent_bits_per_sample: %u\n",
extra.exponent_bits_per_sample);
printf(" dim_shift: %u\n", extra.dim_shift);
printf(" name_length: %u\n", extra.name_length);
if (extra.exponent_bits_per_sample > 0) {
printf(" float, with exponent_bits_per_sample: %u\n",
extra.exponent_bits_per_sample);
}
if (extra.dim_shift > 0) {
printf(" dim_shift: %u (upsampled %ux)\n", extra.dim_shift,
1 << extra.dim_shift);
}
if (extra.name_length) {
char* name = malloc(extra.name_length + 1);
if (JXL_DEC_SUCCESS != JxlDecoderGetExtraChannelName(
@ -127,37 +153,52 @@ int PrintBasicInfo(FILE* file) {
free(name);
printf(" name: %s\n", name);
}
printf(" alpha_associated: %d\n", extra.alpha_associated);
printf(" spot_color: %f %f %f %f\n", extra.spot_color[0],
extra.spot_color[1], extra.spot_color[2], extra.spot_color[3]);
printf(" cfa_channel: %u\n", extra.cfa_channel);
if (extra.type == JXL_CHANNEL_ALPHA)
printf(" alpha_premultiplied: %d (%s)\n", extra.alpha_premultiplied,
extra.alpha_premultiplied ? "Premultiplied"
: "Non-premultiplied");
if (extra.type == JXL_CHANNEL_SPOT_COLOR) {
printf(" spot_color: (%f, %f, %f) with opacity %f\n",
extra.spot_color[0], extra.spot_color[1], extra.spot_color[2],
extra.spot_color[3]);
}
if (extra.type == JXL_CHANNEL_CFA)
printf(" cfa_channel: %u\n", extra.cfa_channel);
}
} else if (status == JXL_DEC_COLOR_ENCODING) {
JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_LITTLE_ENDIAN, 0};
JxlColorProfileTarget targets[2] = {JXL_COLOR_PROFILE_TARGET_ORIGINAL,
JXL_COLOR_PROFILE_TARGET_DATA};
for (size_t i = 0; i < 2; i++) {
JxlColorProfileTarget target = targets[i];
if (info.uses_original_profile) {
if (target != JXL_COLOR_PROFILE_TARGET_ORIGINAL) continue;
printf("color profile:\n");
} else {
printf(target == JXL_COLOR_PROFILE_TARGET_ORIGINAL
? "original color profile:\n"
: "data color profile:\n");
}
printf("color profile:\n");
JxlColorEncoding color_encoding;
if (JXL_DEC_SUCCESS == JxlDecoderGetColorAsEncodedProfile(
dec, &format, target, &color_encoding)) {
printf(" format: JPEG XL encoded color profile\n");
printf(" color_space: %d\n", color_encoding.color_space);
printf(" white_point: %d\n", color_encoding.white_point);
JxlColorEncoding color_encoding;
if (JXL_DEC_SUCCESS ==
JxlDecoderGetColorAsEncodedProfile(dec, &format,
JXL_COLOR_PROFILE_TARGET_ORIGINAL,
&color_encoding)) {
printf(" format: JPEG XL encoded color profile\n");
const char* const cs_string[4] = {"RGB color", "Grayscale", "XYB",
"Unknown"};
const char* const wp_string[12] = {"", "D65", "Custom", "", "", "",
"", "", "", "", "E", "P3"};
const char* const pr_string[12] = {
"", "sRGB", "Custom", "", "", "", "", "", "", "Rec.2100", "", "P3"};
const char* const tf_string[19] = {
"", "709", "Unknown", "", "", "", "", "", "Linear", "",
"", "", "", "sRGB", "", "", "PQ", "DCI", "HLG"};
const char* const ri_string[4] = {"Perceptual", "Relative",
"Saturation", "Absolute"};
printf(" color_space: %d (%s)\n", color_encoding.color_space,
cs_string[color_encoding.color_space]);
printf(" white_point: %d (%s)\n", color_encoding.white_point,
wp_string[color_encoding.white_point]);
if (color_encoding.white_point == JXL_WHITE_POINT_CUSTOM) {
printf(" white_point XY: %f %f\n", color_encoding.white_point_xy[0],
color_encoding.white_point_xy[1]);
if (color_encoding.color_space == JXL_COLOR_SPACE_RGB ||
color_encoding.color_space == JXL_COLOR_SPACE_UNKNOWN) {
printf(" primaries: %d\n", color_encoding.primaries);
}
if (color_encoding.color_space == JXL_COLOR_SPACE_RGB ||
color_encoding.color_space == JXL_COLOR_SPACE_UNKNOWN) {
printf(" primaries: %d (%s)\n", color_encoding.primaries,
pr_string[color_encoding.primaries]);
if (color_encoding.primaries == JXL_PRIMARIES_CUSTOM) {
printf(" red primaries XY: %f %f\n",
color_encoding.primaries_red_xy[0],
color_encoding.primaries_red_xy[1]);
@ -168,40 +209,78 @@ int PrintBasicInfo(FILE* file) {
color_encoding.primaries_blue_xy[0],
color_encoding.primaries_blue_xy[1]);
}
printf(" transfer_function: %d\n", color_encoding.transfer_function);
if (color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_GAMMA) {
printf(" transfer_function gamma: %f\n", color_encoding.gamma);
}
printf(" rendering_intent: %d\n", color_encoding.rendering_intent);
}
if (color_encoding.transfer_function == JXL_TRANSFER_FUNCTION_GAMMA) {
printf(" transfer_function: gamma: %f\n", color_encoding.gamma);
} else {
// The profile is not in JPEG XL encoded form, get as ICC profile
// instead.
printf(" format: ICC profile\n");
size_t profile_size;
if (JXL_DEC_SUCCESS != JxlDecoderGetICCProfileSize(
dec, &format, target, &profile_size)) {
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
continue;
}
printf(" ICC profile size: %zu\n", profile_size);
if (profile_size < 132) {
fprintf(stderr, "ICC profile too small\n");
continue;
}
uint8_t* profile = (uint8_t*)malloc(profile_size);
if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(dec, &format,
target, profile,
profile_size)) {
fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
free(profile);
continue;
}
printf(" CMM type: \"%.4s\"\n", profile + 4);
printf(" color space: \"%.4s\"\n", profile + 16);
printf(" rendering intent: %d\n", (int)profile[67]);
printf(" transfer_function: %d (%s)\n",
color_encoding.transfer_function,
tf_string[color_encoding.transfer_function]);
}
printf(" rendering_intent: %d (%s)\n", color_encoding.rendering_intent,
ri_string[color_encoding.rendering_intent]);
} else {
// The profile is not in JPEG XL encoded form, get as ICC profile
// instead.
printf(" format: ICC profile\n");
size_t profile_size;
if (JXL_DEC_SUCCESS !=
JxlDecoderGetICCProfileSize(dec, &format,
JXL_COLOR_PROFILE_TARGET_ORIGINAL,
&profile_size)) {
fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
continue;
}
printf(" ICC profile size: %zu\n", profile_size);
if (profile_size < 132) {
fprintf(stderr, "ICC profile too small\n");
continue;
}
uint8_t* profile = (uint8_t*)malloc(profile_size);
if (JXL_DEC_SUCCESS !=
JxlDecoderGetColorAsICCProfile(dec, &format,
JXL_COLOR_PROFILE_TARGET_ORIGINAL,
profile, profile_size)) {
fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
free(profile);
continue;
}
printf(" CMM type: \"%.4s\"\n", profile + 4);
printf(" color space: \"%.4s\"\n", profile + 16);
printf(" rendering intent: %d\n", (int)profile[67]);
free(profile);
}
} else if (status == JXL_DEC_FRAME) {
if (JXL_DEC_SUCCESS != JxlDecoderGetFrameHeader(dec, &frame_header)) {
fprintf(stderr, "JxlDecoderGetFrameHeader failed\n");
break;
}
printf("frame:\n");
if (frame_header.name_length) {
char* name = malloc(frame_header.name_length + 1);
if (JXL_DEC_SUCCESS !=
JxlDecoderGetFrameName(dec, name, frame_header.name_length + 1)) {
fprintf(stderr, "JxlDecoderGetFrameName failed\n");
free(name);
break;
}
free(name);
printf(" name: %s\n", name);
}
float ms = frame_header.duration * 1000.f *
info.animation.tps_denominator / info.animation.tps_numerator;
if (info.have_animation) {
printf(" duration: %u ticks (%f ms)\n", frame_header.duration, ms);
if (info.animation.have_timecodes) {
printf(" time code: %X\n", frame_header.timecode);
}
}
if (!frame_header.name_length && !info.have_animation) {
printf(" still frame, unnamed\n");
}
// This is the last expected event, no need to read the rest of the file.
} else {
fprintf(stderr, "Unexpected decoder status\n");

0
third_party/jpeg-xl/js-wasm-wrapper.sh vendored Executable file → Normal file
View file

View file

@ -4,8 +4,8 @@
# license that can be found in the LICENSE file.
set(JPEGXL_MAJOR_VERSION 0)
set(JPEGXL_MINOR_VERSION 3)
set(JPEGXL_PATCH_VERSION 7)
set(JPEGXL_MINOR_VERSION 7)
set(JPEGXL_PATCH_VERSION 0)
set(JPEGXL_LIBRARY_VERSION
"${JPEGXL_MAJOR_VERSION}.${JPEGXL_MINOR_VERSION}.${JPEGXL_PATCH_VERSION}")
@ -43,7 +43,6 @@ set(JPEGXL_INTERNAL_FLAGS
# Warning flags supported by clang.
if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
list(APPEND JPEGXL_INTERNAL_FLAGS
-Wc++2a-extensions
-Wdeprecated-increment-bool
# TODO(deymo): Add -Wextra-semi once we update third_party/highway.
# -Wextra-semi
@ -64,6 +63,9 @@ if (${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
-Wunreachable-code
-Wunused-comparison
)
if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 5.0)
list(APPEND HWY_FLAGS -Wc++2a-extensions)
endif()
endif() # Clang
if (WIN32)

View file

@ -84,39 +84,45 @@ Codec CodecFromExtension(const std::string& extension,
return Codec::kUnknown;
}
Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io,
Status SetFromBytes(const Span<const uint8_t> bytes,
const ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool, Codec* orig_codec) {
if (bytes.size() < kMinBytes) return JXL_FAILURE("Too few bytes");
io->metadata.m.bit_depth.bits_per_sample = 0; // (For is-set check below)
Codec codec;
if (DecodeImagePNG(bytes, pool, io)) {
if (extras::DecodeImagePNG(bytes, color_hints, pool, io)) {
codec = Codec::kPNG;
}
#if JPEGXL_ENABLE_APNG
else if (DecodeImageAPNG(bytes, pool, io)) {
else if (extras::DecodeImageAPNG(bytes, color_hints, pool, io)) {
codec = Codec::kPNG;
}
#endif
else if (DecodeImagePGX(bytes, pool, io)) {
else if (extras::DecodeImagePGX(bytes, color_hints, pool, io)) {
codec = Codec::kPGX;
} else if (DecodeImagePNM(bytes, pool, io)) {
} else if (extras::DecodeImagePNM(bytes, color_hints, pool, io)) {
codec = Codec::kPNM;
}
#if JPEGXL_ENABLE_GIF
else if (DecodeImageGIF(bytes, pool, io)) {
else if (extras::DecodeImageGIF(bytes, color_hints, pool, io)) {
codec = Codec::kGIF;
}
#endif
else if (DecodeImageJPG(bytes, pool, io)) {
else if (io->dec_target == DecodeTarget::kQuantizedCoeffs &&
extras::DecodeImageJPGCoefficients(bytes, io)) {
// TODO(deymo): In this case the tools should use a different API to
// transcode the input JPEG to JXL.
codec = Codec::kJPG;
}
else if (DecodeImagePSD(bytes, pool, io)) {
} else if (io->dec_target == DecodeTarget::kPixels &&
extras::DecodeImageJPG(bytes, color_hints, pool, io)) {
codec = Codec::kJPG;
} else if (extras::DecodeImagePSD(bytes, color_hints, pool, io)) {
codec = Codec::kPSD;
}
#if JPEGXL_ENABLE_EXR
else if (DecodeImageEXR(bytes, pool, io)) {
else if (extras::DecodeImageEXR(bytes, color_hints, pool, io)) {
codec = Codec::kEXR;
}
#endif
@ -129,12 +135,12 @@ Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io,
return true;
}
Status SetFromFile(const std::string& pathname, CodecInOut* io,
ThreadPool* pool, Codec* orig_codec) {
Status SetFromFile(const std::string& pathname, const ColorHints& color_hints,
CodecInOut* io, ThreadPool* pool, Codec* orig_codec) {
PaddedBytes encoded;
JXL_RETURN_IF_ERROR(ReadFile(pathname, &encoded));
JXL_RETURN_IF_ERROR(
SetFromBytes(Span<const uint8_t>(encoded), io, pool, orig_codec));
JXL_RETURN_IF_ERROR(SetFromBytes(Span<const uint8_t>(encoded), color_hints,
io, pool, orig_codec));
return true;
}
@ -152,28 +158,36 @@ Status Encode(const CodecInOut& io, const Codec codec,
switch (codec) {
case Codec::kPNG:
return EncodeImagePNG(&io, c_desired, bits_per_sample, pool, bytes);
return extras::EncodeImagePNG(&io, c_desired, bits_per_sample, pool,
bytes);
case Codec::kJPG:
if (io.Main().IsJPEG()) {
return extras::EncodeImageJPGCoefficients(&io, bytes);
} else {
#if JPEGXL_ENABLE_JPEG
return EncodeImageJPG(
&io, io.use_sjpeg ? JpegEncoder::kSJpeg : JpegEncoder::kLibJpeg,
io.jpeg_quality, YCbCrChromaSubsampling(), pool, bytes,
io.Main().IsJPEG() ? DecodeTarget::kQuantizedCoeffs
: DecodeTarget::kPixels);
return EncodeImageJPG(&io,
io.use_sjpeg ? extras::JpegEncoder::kSJpeg
: extras::JpegEncoder::kLibJpeg,
io.jpeg_quality, YCbCrChromaSubsampling(), pool,
bytes);
#else
return JXL_FAILURE("JPEG XL was built without JPEG support");
return JXL_FAILURE("JPEG XL was built without JPEG support");
#endif
}
case Codec::kPNM:
return EncodeImagePNM(&io, c_desired, bits_per_sample, pool, bytes);
return extras::EncodeImagePNM(&io, c_desired, bits_per_sample, pool,
bytes);
case Codec::kPGX:
return EncodeImagePGX(&io, c_desired, bits_per_sample, pool, bytes);
return extras::EncodeImagePGX(&io, c_desired, bits_per_sample, pool,
bytes);
case Codec::kGIF:
return JXL_FAILURE("Encoding to GIF is not implemented");
case Codec::kPSD:
return EncodeImagePSD(&io, c_desired, bits_per_sample, pool, bytes);
return extras::EncodeImagePSD(&io, c_desired, bits_per_sample, pool,
bytes);
case Codec::kEXR:
#if JPEGXL_ENABLE_EXR
return EncodeImageEXR(&io, c_desired, pool, bytes);
return extras::EncodeImageEXR(&io, c_desired, pool, bytes);
#else
return JXL_FAILURE("JPEG XL was built without OpenEXR support");
#endif

View file

@ -13,6 +13,7 @@
#include <string>
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
@ -58,13 +59,21 @@ Codec CodecFromExtension(const std::string& extension,
size_t* JXL_RESTRICT bits_per_sample);
// Decodes "bytes" and sets io->metadata.m.
// dec_hints may specify the "color_space" (otherwise, defaults to sRGB).
Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io,
ThreadPool* pool = nullptr, Codec* orig_codec = nullptr);
// color_space_hint may specify the color space, otherwise, defaults to sRGB.
Status SetFromBytes(const Span<const uint8_t> bytes,
const ColorHints& color_hints, CodecInOut* io,
ThreadPool* pool, Codec* orig_codec);
// Helper function to use no color_space_hint.
JXL_INLINE Status SetFromBytes(const Span<const uint8_t> bytes, CodecInOut* io,
ThreadPool* pool = nullptr,
Codec* orig_codec = nullptr) {
return SetFromBytes(bytes, ColorHints(), io, pool, orig_codec);
}
// Reads from file and calls SetFromBytes.
Status SetFromFile(const std::string& pathname, CodecInOut* io,
ThreadPool* pool = nullptr, Codec* orig_codec = nullptr);
Status SetFromFile(const std::string& pathname, const ColorHints& color_hints,
CodecInOut* io, ThreadPool* pool = nullptr,
Codec* orig_codec = nullptr);
// Replaces "bytes" with an encoding of pixels transformed from c_current
// color space to c_desired.

View file

@ -39,14 +39,6 @@
#include <stdio.h>
#include <string.h>
#if defined(_WIN32) || defined(_WIN64)
#ifndef NOMINMAX
#define NOMINMAX
#endif // NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#include <algorithm>
#include <string>
#include <utility>
@ -63,26 +55,45 @@
#include "png.h" /* original (unpatched) libpng is ok */
namespace jxl {
namespace extras {
namespace {
constexpr bool isAbc(char c) {
return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
}
#define notabc(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97))
#define id_IHDR 0x52444849
#define id_acTL 0x4C546361
#define id_fcTL 0x4C546366
#define id_IDAT 0x54414449
#define id_fdAT 0x54416466
#define id_IEND 0x444E4549
constexpr uint32_t kId_IHDR = 0x52444849;
constexpr uint32_t kId_acTL = 0x4C546361;
constexpr uint32_t kId_fcTL = 0x4C546366;
constexpr uint32_t kId_IDAT = 0x54414449;
constexpr uint32_t kId_fdAT = 0x54416466;
constexpr uint32_t kId_IEND = 0x444E4549;
struct CHUNK {
unsigned char* p;
unsigned int size;
};
struct APNGFrame {
unsigned char *p, **rows;
unsigned int w, h, delay_num, delay_den;
};
struct Reader {
const uint8_t* next;
const uint8_t* last;
bool Read(void* data, size_t len) {
size_t cap = last - next;
size_t to_copy = std::min(cap, len);
memcpy(data, next, to_copy);
next += to_copy;
return (len == to_copy);
}
bool Eof() { return next == last; }
};
const unsigned long cMaxPNGSize = 1000000UL;
const size_t kMaxPNGChunkSize = 100000000; // 100 MB
@ -102,11 +113,11 @@ void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num,
png_progressive_combine_row(png_ptr, frame->rows[row_num], new_row);
}
inline unsigned int read_chunk(FILE* f, CHUNK* pChunk) {
inline unsigned int read_chunk(Reader* r, CHUNK* pChunk) {
unsigned char len[4];
pChunk->size = 0;
pChunk->p = 0;
if (fread(&len, 4, 1, f) == 1) {
if (r->Read(&len, 4)) {
const auto size = png_get_uint_32(len);
// Check first, to avoid overflow.
if (size > kMaxPNGChunkSize) {
@ -116,8 +127,9 @@ inline unsigned int read_chunk(FILE* f, CHUNK* pChunk) {
pChunk->size = size + 12;
pChunk->p = new unsigned char[pChunk->size];
memcpy(pChunk->p, len, 4);
if (fread(pChunk->p + 4, pChunk->size - 4, 1, f) == 1)
if (r->Read(pChunk->p + 4, pChunk->size - 4)) {
return *(unsigned int*)(pChunk->p + 4);
}
}
return 0;
}
@ -179,29 +191,11 @@ int processing_finish(png_structp png_ptr, png_infop info_ptr) {
return 0;
}
#if defined(_WIN32) || defined(_WIN64)
FILE* fmemopen(void* buf, size_t size, const char* mode) {
char temp[999];
if (!GetTempPath(sizeof(temp), temp)) return nullptr;
char pathname[999];
if (!GetTempFileName(temp, "jpegxl", 0, pathname)) return nullptr;
FILE* f = fopen(pathname, "wb");
if (f == nullptr) return nullptr;
fwrite(buf, 1, size, f);
JXL_CHECK(fclose(f) == 0);
return fopen(pathname, mode);
}
#endif
} // namespace
Status DecodeImageAPNG(Span<const uint8_t> bytes, ThreadPool* pool,
CodecInOut* io) {
FILE* f;
Status DecodeImageAPNG(Span<const uint8_t> bytes, const ColorHints& color_hints,
ThreadPool* pool, CodecInOut* io) {
Reader r;
unsigned int id, i, j, w, h, w0, h0, x0, y0;
unsigned int delay_num, delay_den, dop, bop, rowbytes, imagesize;
unsigned char sig[8];
@ -216,16 +210,13 @@ Status DecodeImageAPNG(Span<const uint8_t> bytes, ThreadPool* pool,
bool all_dispose_bg = true;
APNGFrame frameRaw = {};
if (!(f = fmemopen((void*)bytes.data(), bytes.size(), "rb"))) {
return JXL_FAILURE("Failed to fmemopen");
}
r = {bytes.data(), bytes.data() + bytes.size()};
// Not an aPNG => not an error
unsigned char png_signature[8] = {137, 80, 78, 71, 13, 10, 26, 10};
if (fread(sig, 1, 8, f) != 8 || memcmp(sig, png_signature, 8) != 0) {
fclose(f);
if (!r.Read(sig, 8) || memcmp(sig, png_signature, 8) != 0) {
return false;
}
id = read_chunk(f, &chunkIHDR);
id = read_chunk(&r, &chunkIHDR);
io->frames.clear();
io->dec_pixels = 0;
@ -233,19 +224,15 @@ Status DecodeImageAPNG(Span<const uint8_t> bytes, ThreadPool* pool,
io->metadata.m.SetAlphaBits(8);
io->metadata.m.color_encoding =
ColorEncoding::SRGB(); // todo: get data from png metadata
(void)io->dec_hints.Foreach(
[](const std::string& key, const std::string& /*value*/) {
JXL_WARNING("APNG decoder ignoring %s hint", key.c_str());
return true;
});
JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/true,
/*is_gray=*/false, io));
bool errorstate = true;
if (id == id_IHDR && chunkIHDR.size == 25) {
if (id == kId_IHDR && chunkIHDR.size == 25) {
w0 = w = png_get_uint_32(chunkIHDR.p + 8);
h0 = h = png_get_uint_32(chunkIHDR.p + 12);
if (w > cMaxPNGSize || h > cMaxPNGSize) {
fclose(f);
return false;
}
@ -265,18 +252,18 @@ Status DecodeImageAPNG(Span<const uint8_t> bytes, ThreadPool* pool,
if (!processing_start(png_ptr, info_ptr, (void*)&frameRaw, hasInfo,
chunkIHDR, chunksInfo)) {
bool last_base_was_none = true;
while (!feof(f)) {
id = read_chunk(f, &chunk);
while (!r.Eof()) {
id = read_chunk(&r, &chunk);
if (!id) break;
JXL_ASSERT(chunk.p != nullptr);
if (id == id_acTL && !hasInfo && !isAnimated) {
if (id == kId_acTL && !hasInfo && !isAnimated) {
isAnimated = true;
skipFirst = true;
io->metadata.m.have_animation = true;
io->metadata.m.animation.tps_numerator = 1000;
} else if (id == id_IEND ||
(id == id_fcTL && (!hasInfo || isAnimated))) {
} else if (id == kId_IEND ||
(id == kId_fcTL && (!hasInfo || isAnimated))) {
if (hasInfo) {
if (!processing_finish(png_ptr, info_ptr)) {
ImageBundle bundle(&io->metadata.m);
@ -339,7 +326,7 @@ Status DecodeImageAPNG(Span<const uint8_t> bytes, ThreadPool* pool,
}
}
if (id == id_IEND) {
if (id == kId_IEND) {
errorstate = false;
break;
}
@ -353,6 +340,8 @@ Status DecodeImageAPNG(Span<const uint8_t> bytes, ThreadPool* pool,
dop = chunk.p[32];
bop = chunk.p[33];
if (!delay_den) delay_den = 100;
if (w0 > cMaxPNGSize || h0 > cMaxPNGSize || x0 > cMaxPNGSize ||
y0 > cMaxPNGSize || x0 + w0 > w || y0 + h0 > h || dop > 2 ||
bop > 1) {
@ -374,21 +363,21 @@ Status DecodeImageAPNG(Span<const uint8_t> bytes, ThreadPool* pool,
bop = 0;
if (dop == 2) dop = 1;
}
} else if (id == id_IDAT) {
} else if (id == kId_IDAT) {
hasInfo = true;
if (processing_data(png_ptr, info_ptr, chunk.p, chunk.size)) {
delete[] chunk.p;
break;
}
} else if (id == id_fdAT && isAnimated) {
} else if (id == kId_fdAT && isAnimated) {
png_save_uint_32(chunk.p + 4, chunk.size - 16);
memcpy(chunk.p + 8, "IDAT", 4);
if (processing_data(png_ptr, info_ptr, chunk.p + 4, chunk.size - 4)) {
delete[] chunk.p;
break;
}
} else if (notabc(chunk.p[4]) || notabc(chunk.p[5]) ||
notabc(chunk.p[6]) || notabc(chunk.p[7])) {
} else if (!isAbc(chunk.p[4]) || !isAbc(chunk.p[5]) ||
!isAbc(chunk.p[6]) || !isAbc(chunk.p[7])) {
delete[] chunk.p;
break;
} else if (!hasInfo) {
@ -411,11 +400,10 @@ Status DecodeImageAPNG(Span<const uint8_t> bytes, ThreadPool* pool,
chunksInfo.clear();
delete[] chunkIHDR.p;
fclose(f);
if (errorstate) return false;
SetIntensityTarget(io);
return true;
}
} // namespace extras
} // namespace jxl

View file

@ -10,6 +10,7 @@
#include <stdint.h>
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
@ -17,11 +18,14 @@
#include "lib/jxl/codec_in_out.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. io->dec_hints are ignored.
Status DecodeImageAPNG(const Span<const uint8_t> bytes, ThreadPool* pool,
// Decodes `bytes` into `io`. color_space_hint is ignored.
Status DecodeImageAPNG(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_APNG_H_

View file

@ -17,6 +17,7 @@
#include "lib/jxl/color_management.h"
namespace jxl {
namespace extras {
namespace {
@ -127,8 +128,8 @@ class InMemoryOStream : public OpenEXR::OStream {
} // namespace
Status DecodeImageEXR(Span<const uint8_t> bytes, ThreadPool* pool,
CodecInOut* io) {
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
ThreadPool* pool, CodecInOut* io) {
// Get the number of threads we should be using for OpenEXR.
// OpenEXR creates its own set of threads, independent from ours. `pool` is
// only used for converting from a buffer of OpenEXR::Rgba to Image3F.
@ -347,4 +348,5 @@ Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
return true;
}
} // namespace extras
} // namespace jxl

View file

@ -8,6 +8,7 @@
// Encodes OpenEXR images in memory.
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
@ -16,16 +17,18 @@
#include "lib/jxl/color_encoding_internal.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. io->dec_hints are ignored.
Status DecodeImageEXR(Span<const uint8_t> bytes, ThreadPool* pool,
CodecInOut* io);
// Decodes `bytes` into `io`. color_hints are ignored.
Status DecodeImageEXR(Span<const uint8_t> bytes, const ColorHints& color_hints,
ThreadPool* pool, CodecInOut* io);
// Transforms from io->c_current to `c_desired` (with the transfer function set
// to linear as that is the OpenEXR convention) and encodes into `bytes`.
Status EncodeImageEXR(const CodecInOut* io, const ColorEncoding& c_desired,
ThreadPool* pool, PaddedBytes* bytes);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_EXR_H_

View file

@ -21,12 +21,10 @@
#include "lib/jxl/image_bundle.h"
#include "lib/jxl/image_ops.h"
#include "lib/jxl/luminance.h"
#ifdef MEMORY_SANITIZER
#include "sanitizer/msan_interface.h"
#endif
#include "lib/jxl/sanitizers.h"
namespace jxl {
namespace extras {
namespace {
@ -55,8 +53,8 @@ bool AllOpaque(const ImageF& alpha) {
} // namespace
Status DecodeImageGIF(Span<const uint8_t> bytes, ThreadPool* pool,
CodecInOut* io) {
Status DecodeImageGIF(Span<const uint8_t> bytes, const ColorHints& color_hints,
ThreadPool* pool, CodecInOut* io) {
int error = GIF_OK;
ReadState state = {bytes};
const auto ReadFromSpan = [](GifFileType* const gif, GifByteType* const bytes,
@ -84,16 +82,15 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, ThreadPool* pool,
return JXL_FAILURE("Failed to read GIF: %s", GifErrorString(gif->Error));
}
#ifdef MEMORY_SANITIZER
__msan_unpoison(gif.get(), sizeof(*gif));
msan::UnpoisonMemory(gif.get(), sizeof(*gif));
if (gif->SColorMap) {
__msan_unpoison(gif->SColorMap, sizeof(*gif->SColorMap));
__msan_unpoison(gif->SColorMap->Colors, sizeof(*gif->SColorMap->Colors) *
gif->SColorMap->ColorCount);
msan::UnpoisonMemory(gif->SColorMap, sizeof(*gif->SColorMap));
msan::UnpoisonMemory(
gif->SColorMap->Colors,
sizeof(*gif->SColorMap->Colors) * gif->SColorMap->ColorCount);
}
__msan_unpoison(gif->SavedImages,
sizeof(*gif->SavedImages) * gif->ImageCount);
#endif
msan::UnpoisonMemory(gif->SavedImages,
sizeof(*gif->SavedImages) * gif->ImageCount);
const SizeConstraints* constraints = &io->constraints;
@ -135,24 +132,18 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, ThreadPool* pool,
io->dec_pixels = 0;
io->metadata.m.SetUintSamples(8);
io->metadata.m.color_encoding = ColorEncoding::SRGB();
io->metadata.m.SetAlphaBits(0);
(void)io->dec_hints.Foreach(
[](const std::string& key, const std::string& /*value*/) {
JXL_WARNING("GIF decoder ignoring %s hint", key.c_str());
return true;
});
JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/false,
/*is_gray=*/false, io));
Image3F canvas(gif->SWidth, gif->SHeight);
io->SetSize(gif->SWidth, gif->SHeight);
ImageF alpha(gif->SWidth, gif->SHeight);
GifColorType background_color;
if (gif->SColorMap == nullptr) {
if (gif->SColorMap == nullptr ||
gif->SBackGroundColor >= gif->SColorMap->ColorCount) {
background_color = {0, 0, 0};
} else {
if (gif->SBackGroundColor >= gif->SColorMap->ColorCount) {
return JXL_FAILURE("GIF specifies out-of-bounds background color");
}
background_color = gif->SColorMap->Colors[gif->SBackGroundColor];
}
FillPlane<float>(background_color.Red, &canvas.Plane(0));
@ -167,11 +158,9 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, ThreadPool* pool,
bool last_base_was_none = true;
for (int i = 0; i < gif->ImageCount; ++i) {
const SavedImage& image = gif->SavedImages[i];
#ifdef MEMORY_SANITIZER
__msan_unpoison(image.RasterBits, sizeof(*image.RasterBits) *
image.ImageDesc.Width *
image.ImageDesc.Height);
#endif
msan::UnpoisonMemory(image.RasterBits, sizeof(*image.RasterBits) *
image.ImageDesc.Width *
image.ImageDesc.Height);
const Rect image_rect(image.ImageDesc.Left, image.ImageDesc.Top,
image.ImageDesc.Width, image.ImageDesc.Height);
io->dec_pixels += image_rect.xsize() * image_rect.ysize();
@ -203,16 +192,12 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, ThreadPool* pool,
const ColorMapObject* const color_map =
image.ImageDesc.ColorMap ? image.ImageDesc.ColorMap : gif->SColorMap;
JXL_CHECK(color_map);
#ifdef MEMORY_SANITIZER
__msan_unpoison(color_map, sizeof(*color_map));
__msan_unpoison(color_map->Colors,
sizeof(*color_map->Colors) * color_map->ColorCount);
#endif
msan::UnpoisonMemory(color_map, sizeof(*color_map));
msan::UnpoisonMemory(color_map->Colors,
sizeof(*color_map->Colors) * color_map->ColorCount);
GraphicsControlBlock gcb;
DGifSavedExtensionToGCB(gif.get(), i, &gcb);
#ifdef MEMORY_SANITIZER
__msan_unpoison(&gcb, sizeof(gcb));
#endif
msan::UnpoisonMemory(&gcb, sizeof(gcb));
ImageBundle bundle(&io->metadata.m);
if (io->metadata.m.have_animation) {
@ -350,4 +335,5 @@ Status DecodeImageGIF(Span<const uint8_t> bytes, ThreadPool* pool,
return true;
}
} // namespace extras
} // namespace jxl

View file

@ -10,6 +10,7 @@
#include <stdint.h>
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
@ -17,11 +18,14 @@
#include "lib/jxl/codec_in_out.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. io->dec_hints are ignored.
Status DecodeImageGIF(const Span<const uint8_t> bytes, ThreadPool* pool,
// Decodes `bytes` into `io`. color_hints are ignored.
Status DecodeImageGIF(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_GIF_H_

View file

@ -21,9 +21,9 @@
#include <utility>
#include <vector>
#include "lib/extras/time.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/base/time.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/common.h"
@ -34,15 +34,13 @@
#include "lib/jxl/jpeg/enc_jpeg_data.h"
#include "lib/jxl/jpeg/enc_jpeg_data_reader.h"
#include "lib/jxl/luminance.h"
#include "lib/jxl/sanitizers.h"
#if JPEGXL_ENABLE_SJPEG
#include "sjpeg.h"
#endif
#ifdef MEMORY_SANITIZER
#include "sanitizer/msan_interface.h"
#endif
namespace jxl {
namespace extras {
#if JPEGXL_ENABLE_JPEG
namespace {
@ -86,12 +84,10 @@ Status ReadICCProfile(jpeg_decompress_struct* const cinfo,
bool has_num_markers = false;
for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr;
marker = marker->next) {
#ifdef MEMORY_SANITIZER
// marker is initialized by libjpeg, which we are not instrumenting with
// msan.
__msan_unpoison(marker, sizeof(*marker));
__msan_unpoison(marker->data, marker->data_length);
#endif
msan::UnpoisonMemory(marker, sizeof(*marker));
msan::UnpoisonMemory(marker->data, marker->data_length);
if (!MarkerIsICC(marker)) continue;
const int current_marker = marker->data[kICCSignatureSize];
@ -157,12 +153,10 @@ void ReadExif(jpeg_decompress_struct* const cinfo, PaddedBytes* const exif) {
constexpr size_t kExifSignatureSize = sizeof kExifSignature;
for (jpeg_saved_marker_ptr marker = cinfo->marker_list; marker != nullptr;
marker = marker->next) {
#ifdef MEMORY_SANITIZER
// marker is initialized by libjpeg, which we are not instrumenting with
// msan.
__msan_unpoison(marker, sizeof(*marker));
__msan_unpoison(marker->data, marker->data_length);
#endif
msan::UnpoisonMemory(marker, sizeof(*marker));
msan::UnpoisonMemory(marker->data, marker->data_length);
if (!MarkerIsExif(marker)) continue;
size_t marker_length = marker->data_length - kExifSignatureSize;
exif->resize(marker_length);
@ -242,17 +236,22 @@ void MyOutputMessage(j_common_ptr cinfo) {
} // namespace
#endif // JPEGXL_ENABLE_JPEG
Status DecodeImageJPG(const Span<const uint8_t> bytes, ThreadPool* pool,
Status DecodeImageJPGCoefficients(Span<const uint8_t> bytes, CodecInOut* io) {
if (!IsJPG(bytes)) return false;
// Use brunsli JPEG decoder to read quantized coefficients.
if (!jpeg::DecodeImageJPG(bytes, io)) {
fprintf(stderr, "Corrupt or CMYK JPEG.\n");
return false;
}
return true;
}
Status DecodeImageJPG(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io, double* const elapsed_deinterleave) {
if (elapsed_deinterleave != nullptr) *elapsed_deinterleave = 0;
// Don't do anything for non-JPEG files (no need to report an error)
if (!IsJPG(bytes)) return false;
const DecodeTarget target = io->dec_target;
// Use brunsli JPEG decoder to read quantized coefficients.
if (target == DecodeTarget::kQuantizedCoeffs) {
return jxl::jpeg::DecodeImageJPG(bytes, io);
}
#if JPEGXL_ENABLE_JPEG
// TODO(veluca): use JPEGData also for pixels?
@ -267,11 +266,9 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, ThreadPool* pool,
const auto try_catch_block = [&]() -> bool {
jpeg_decompress_struct cinfo;
#ifdef MEMORY_SANITIZER
// cinfo is initialized by libjpeg, which we are not instrumenting with
// msan, therefore we need to initialize cinfo here.
memset(&cinfo, 0, sizeof(cinfo));
#endif
msan::UnpoisonMemory(&cinfo, sizeof(cinfo));
// Setup error handling in jpeg library so we can deal with broken jpegs in
// the fuzzer.
jpeg_error_mgr jerr;
@ -290,17 +287,22 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, ThreadPool* pool,
jpeg_save_markers(&cinfo, kICCMarker, 0xFFFF);
jpeg_save_markers(&cinfo, kExifMarker, 0xFFFF);
jpeg_read_header(&cinfo, TRUE);
if (!VerifyDimensions(&io->constraints, cinfo.image_width,
cinfo.image_height)) {
const auto failure = [&cinfo](const char* str) -> Status {
jpeg_abort_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return JXL_FAILURE("image too big");
return JXL_FAILURE("%s", str);
};
if (!VerifyDimensions(&io->constraints, cinfo.image_width,
cinfo.image_height)) {
return failure("image too big");
}
// Might cause CPU-zip bomb.
if (cinfo.arith_code) {
return failure("arithmetic code JPEGs are not supported");
}
if (ReadICCProfile(&cinfo, &icc)) {
if (!color_encoding.SetICC(std::move(icc))) {
jpeg_abort_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return JXL_FAILURE("read an invalid ICC profile");
return failure("read an invalid ICC profile");
}
} else {
color_encoding = ColorEncoding::SRGB(cinfo.output_components == 1);
@ -310,16 +312,11 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, ThreadPool* pool,
io->metadata.m.color_encoding = color_encoding;
int nbcomp = cinfo.num_components;
if (nbcomp != 1 && nbcomp != 3) {
jpeg_abort_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
return JXL_FAILURE("unsupported number of components (%d) in JPEG",
cinfo.output_components);
return failure("unsupported number of components in JPEG");
}
if (!ApplyColorHints(color_hints, /*color_already_set=*/true, false, io)) {
return failure("ApplyColorHints failed");
}
(void)io->dec_hints.Foreach(
[](const std::string& key, const std::string& /*value*/) {
JXL_WARNING("JPEG decoder ignoring %s hint", key.c_str());
return true;
});
jpeg_start_decompress(&cinfo);
JXL_ASSERT(cinfo.output_components == nbcomp);
@ -328,10 +325,9 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, ThreadPool* pool,
for (size_t y = 0; y < image.ysize(); ++y) {
JSAMPROW rows[] = {row.get()};
jpeg_read_scanlines(&cinfo, rows, 1);
#ifdef MEMORY_SANITIZER
__msan_unpoison(row.get(), sizeof(JSAMPLE) * cinfo.output_components *
cinfo.image_width);
#endif
msan::UnpoisonMemory(
row.get(),
sizeof(JSAMPLE) * cinfo.output_components * cinfo.image_width);
auto start = Now();
float* const JXL_RESTRICT output_row[] = {
image.PlaneRow(0, y), image.PlaneRow(1, y), image.PlaneRow(2, y)};
@ -361,7 +357,7 @@ Status DecodeImageJPG(const Span<const uint8_t> bytes, ThreadPool* pool,
};
return try_catch_block();
#else // JPEGXL_ENABLE_JPEG
#else // JPEGXL_ENABLE_JPEG
return JXL_FAILURE("JPEG decoding not enabled at build time.");
#endif // JPEGXL_ENABLE_JPEG
}
@ -372,11 +368,9 @@ Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
const YCbCrChromaSubsampling& chroma_subsampling,
PaddedBytes* bytes) {
jpeg_compress_struct cinfo;
#ifdef MEMORY_SANITIZER
// cinfo is initialized by libjpeg, which we are not instrumenting with
// msan.
__msan_unpoison(&cinfo, sizeof(cinfo));
#endif
msan::UnpoisonMemory(&cinfo, sizeof(cinfo));
jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
@ -427,11 +421,9 @@ Status EncodeWithLibJpeg(const ImageBundle* ib, const CodecInOut* io,
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
bytes->resize(size);
#ifdef MEMORY_SANITIZER
// Compressed image data is initialized by libjpeg, which we are not
// instrumenting with msan.
__msan_unpoison(buffer, size);
#endif
msan::UnpoisonMemory(buffer, size);
std::copy_n(buffer, size, bytes->data());
std::free(buffer);
return true;
@ -481,10 +473,17 @@ Status EncodeWithSJpeg(const ImageBundle* ib, size_t quality,
}
#endif // JPEGXL_ENABLE_JPEG
Status EncodeImageJPGCoefficients(const CodecInOut* io, PaddedBytes* bytes) {
auto write = [&bytes](const uint8_t* buf, size_t len) {
bytes->append(buf, buf + len);
return len;
};
return jpeg::WriteJpeg(*io->Main().jpeg_data, write);
}
Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
YCbCrChromaSubsampling chroma_subsampling,
ThreadPool* pool, PaddedBytes* bytes,
const DecodeTarget target) {
ThreadPool* pool, PaddedBytes* bytes) {
if (io->Main().HasAlpha()) {
return JXL_FAILURE("alpha is not supported");
}
@ -492,14 +491,6 @@ Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
return JXL_FAILURE("please specify a 0-100 JPEG quality");
}
if (target == DecodeTarget::kQuantizedCoeffs) {
auto write = [&bytes](const uint8_t* buf, size_t len) {
bytes->append(buf, buf + len);
return len;
};
return jpeg::WriteJpeg(*io->Main().jpeg_data, write);
}
#if JPEGXL_ENABLE_JPEG
const ImageBundle* ib;
ImageMetadata metadata = io->metadata.m;
@ -521,9 +512,10 @@ Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
}
return true;
#else // JPEGXL_ENABLE_JPEG
#else // JPEGXL_ENABLE_JPEG
return JXL_FAILURE("JPEG pixel encoding not enabled at build time");
#endif // JPEGXL_ENABLE_JPEG
}
} // namespace extras
} // namespace jxl

View file

@ -11,6 +11,7 @@
#include <stdint.h>
#include "lib/extras/codec.h"
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
@ -18,6 +19,7 @@
#include "lib/jxl/codec_in_out.h"
namespace jxl {
namespace extras {
enum class JpegEncoder {
kLibJpeg,
@ -30,18 +32,29 @@ static inline bool IsJPG(const Span<const uint8_t> bytes) {
return true;
}
// Decodes `bytes` into `io`. io->dec_hints are ignored.
// Decodes `bytes` into `io`. color_hints are ignored.
// `elapsed_deinterleave`, if non-null, will be set to the time (in seconds)
// that it took to deinterleave the raw JSAMPLEs to planar floats.
Status DecodeImageJPG(Span<const uint8_t> bytes, ThreadPool* pool,
CodecInOut* io, double* elapsed_deinterleave = nullptr);
Status DecodeImageJPG(Span<const uint8_t> bytes, const ColorHints& color_hints,
ThreadPool* pool, CodecInOut* io,
double* elapsed_deinterleave = nullptr);
// Encodes into `bytes`.
Status EncodeImageJPG(const CodecInOut* io, JpegEncoder encoder, size_t quality,
YCbCrChromaSubsampling chroma_subsampling,
ThreadPool* pool, PaddedBytes* bytes,
DecodeTarget target = DecodeTarget::kPixels);
ThreadPool* pool, PaddedBytes* bytes);
// Temporary wrappers to load the JPEG coefficients to a CodecInOut. This should
// be replaced by calling the corresponding JPEG input and output functions on
// the API.
// Decodes the JPEG image coefficients to a CodecIO for lossless recompression.
Status DecodeImageJPGCoefficients(Span<const uint8_t> bytes, CodecInOut* io);
// Reconstructs the JPEG from the coefficients and metadata in CodecInOut.
Status EncodeImageJPGCoefficients(const CodecInOut* io, PaddedBytes* bytes);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_JPG_H_

View file

@ -27,6 +27,7 @@
#include "lib/jxl/luminance.h"
namespace jxl {
namespace extras {
namespace {
struct HeaderPGX {
@ -171,46 +172,10 @@ Status EncodeHeader(const ImageBundle& ib, const size_t bits_per_sample,
}
// Use ML (Big Endian), LM may not be well supported by all decoders.
snprintf(header, kMaxHeaderSize, "PG ML + %zu %zu %zu\n%n", bits_per_sample,
ib.xsize(), ib.ysize(), chars_written);
return true;
}
Status ApplyHints(CodecInOut* io) {
bool got_color_space = false;
JXL_RETURN_IF_ERROR(io->dec_hints.Foreach(
[io, &got_color_space](const std::string& key,
const std::string& value) -> Status {
ColorEncoding* c_original = &io->metadata.m.color_encoding;
if (key == "color_space") {
if (!ParseDescription(value, c_original) ||
!c_original->CreateICC()) {
return JXL_FAILURE("PGX: Failed to apply color_space");
}
if (!io->metadata.m.color_encoding.IsGray()) {
return JXL_FAILURE("PGX: color_space hint must be grayscale");
}
got_color_space = true;
} else if (key == "icc_pathname") {
PaddedBytes icc;
JXL_RETURN_IF_ERROR(ReadFile(value, &icc));
JXL_RETURN_IF_ERROR(c_original->SetICC(std::move(icc)));
got_color_space = true;
} else {
JXL_WARNING("PGX decoder ignoring %s hint", key.c_str());
}
return true;
}));
if (!got_color_space) {
JXL_WARNING("PGX: no color_space/icc_pathname given, assuming sRGB");
JXL_RETURN_IF_ERROR(
io->metadata.m.color_encoding.SetSRGB(ColorSpace::kGray));
}
*chars_written = snprintf(header, kMaxHeaderSize, "PG ML + %zu %zu %zu\n",
bits_per_sample, ib.xsize(), ib.ysize());
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
return true;
}
@ -226,7 +191,8 @@ Span<const uint8_t> MakeSpan(const char* str) {
} // namespace
Status DecodeImagePGX(const Span<const uint8_t> bytes, ThreadPool* pool,
Status DecodeImagePGX(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io) {
Parser parser(bytes);
HeaderPGX header = {};
@ -238,7 +204,8 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes, ThreadPool* pool,
return JXL_FAILURE("PGX: bits_per_sample invalid");
}
JXL_RETURN_IF_ERROR(ApplyHints(io));
JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/false,
/*is_gray=*/true, io));
io->metadata.m.SetUintSamples(header.bits_per_sample);
io->metadata.m.SetAlphaBits(0);
io->dec_pixels = header.xsize * header.ysize;
@ -256,7 +223,7 @@ Status DecodeImagePGX(const Span<const uint8_t> bytes, ThreadPool* pool,
/*alpha_is_premultiplied=*/false,
io->metadata.m.bit_depth.bits_per_sample,
header.big_endian ? JXL_BIG_ENDIAN : JXL_LITTLE_ENDIAN, flipped_y, pool,
&ib));
&ib, /*float_in=*/false));
io->frames.push_back(std::move(ib));
SetIntensityTarget(io);
return true;
@ -302,57 +269,5 @@ Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
return true;
}
void TestCodecPGX() {
{
std::string pgx = "PG ML + 8 2 3\npixels";
CodecInOut io;
ThreadPool* pool = nullptr;
Status ok = DecodeImagePGX(MakeSpan(pgx.c_str()), pool, &io);
JXL_CHECK(ok == true);
ScaleImage(255.f, io.Main().color());
JXL_CHECK(!io.metadata.m.bit_depth.floating_point_sample);
JXL_CHECK(io.metadata.m.bit_depth.bits_per_sample == 8);
JXL_CHECK(io.metadata.m.color_encoding.IsGray());
JXL_CHECK(io.xsize() == 2);
JXL_CHECK(io.ysize() == 3);
float eps = 1e-5;
ExpectNear<float>('p', io.Main().color()->Plane(0).Row(0)[0], eps);
ExpectNear<float>('i', io.Main().color()->Plane(0).Row(0)[1], eps);
ExpectNear<float>('x', io.Main().color()->Plane(0).Row(1)[0], eps);
ExpectNear<float>('e', io.Main().color()->Plane(0).Row(1)[1], eps);
ExpectNear<float>('l', io.Main().color()->Plane(0).Row(2)[0], eps);
ExpectNear<float>('s', io.Main().color()->Plane(0).Row(2)[1], eps);
}
{
std::string pgx = "PG ML + 16 2 3\np_i_x_e_l_s_";
CodecInOut io;
ThreadPool* pool = nullptr;
Status ok = DecodeImagePGX(MakeSpan(pgx.c_str()), pool, &io);
JXL_CHECK(ok == true);
ScaleImage(255.f, io.Main().color());
JXL_CHECK(!io.metadata.m.bit_depth.floating_point_sample);
JXL_CHECK(io.metadata.m.bit_depth.bits_per_sample == 16);
JXL_CHECK(io.metadata.m.color_encoding.IsGray());
JXL_CHECK(io.xsize() == 2);
JXL_CHECK(io.ysize() == 3);
float eps = 1e-7;
const auto& plane = io.Main().color()->Plane(0);
ExpectNear(256.0f * 'p' + '_', plane.Row(0)[0] * 257, eps);
ExpectNear(256.0f * 'i' + '_', plane.Row(0)[1] * 257, eps);
ExpectNear(256.0f * 'x' + '_', plane.Row(1)[0] * 257, eps);
ExpectNear(256.0f * 'e' + '_', plane.Row(1)[1] * 257, eps);
ExpectNear(256.0f * 'l' + '_', plane.Row(2)[0] * 257, eps);
ExpectNear(256.0f * 's' + '_', plane.Row(2)[1] * 257, eps);
}
}
} // namespace extras
} // namespace jxl

View file

@ -11,6 +11,7 @@
#include <stddef.h>
#include <stdint.h>
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
@ -19,10 +20,11 @@
#include "lib/jxl/color_encoding_internal.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. io->dec_hints may specify "color_space", which
// defaults to sRGB.
Status DecodeImagePGX(const Span<const uint8_t> bytes, ThreadPool* pool,
// Decodes `bytes` into `io`.
Status DecodeImagePGX(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io);
// Transforms from io->c_current to `c_desired` and encodes into `bytes`.
@ -30,7 +32,7 @@ Status EncodeImagePGX(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes);
void TestCodecPGX();
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_PGX_H_

View file

@ -0,0 +1,74 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/codec_pgx.h"
#include <stddef.h>
#include "gtest/gtest.h"
namespace jxl {
namespace extras {
namespace {
Span<const uint8_t> MakeSpan(const char* str) {
return Span<const uint8_t>(reinterpret_cast<const uint8_t*>(str),
strlen(str));
}
TEST(CodecPGXTest, Test8bits) {
std::string pgx = "PG ML + 8 2 3\npixels";
CodecInOut io;
ThreadPool* pool = nullptr;
EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(), pool, &io));
ScaleImage(255.f, io.Main().color());
EXPECT_FALSE(io.metadata.m.bit_depth.floating_point_sample);
EXPECT_EQ(8u, io.metadata.m.bit_depth.bits_per_sample);
EXPECT_TRUE(io.metadata.m.color_encoding.IsGray());
EXPECT_EQ(2u, io.xsize());
EXPECT_EQ(3u, io.ysize());
float eps = 1e-5;
EXPECT_NEAR('p', io.Main().color()->Plane(0).Row(0)[0], eps);
EXPECT_NEAR('i', io.Main().color()->Plane(0).Row(0)[1], eps);
EXPECT_NEAR('x', io.Main().color()->Plane(0).Row(1)[0], eps);
EXPECT_NEAR('e', io.Main().color()->Plane(0).Row(1)[1], eps);
EXPECT_NEAR('l', io.Main().color()->Plane(0).Row(2)[0], eps);
EXPECT_NEAR('s', io.Main().color()->Plane(0).Row(2)[1], eps);
}
TEST(CodecPGXTest, Test16bits) {
std::string pgx = "PG ML + 16 2 3\np_i_x_e_l_s_";
CodecInOut io;
ThreadPool* pool = nullptr;
EXPECT_TRUE(DecodeImagePGX(MakeSpan(pgx.c_str()), ColorHints(), pool, &io));
ScaleImage(255.f, io.Main().color());
EXPECT_FALSE(io.metadata.m.bit_depth.floating_point_sample);
EXPECT_EQ(16u, io.metadata.m.bit_depth.bits_per_sample);
EXPECT_TRUE(io.metadata.m.color_encoding.IsGray());
EXPECT_EQ(2u, io.xsize());
EXPECT_EQ(3u, io.ysize());
float eps = 1e-7;
const auto& plane = io.Main().color()->Plane(0);
EXPECT_NEAR(256.0f * 'p' + '_', plane.Row(0)[0] * 257, eps);
EXPECT_NEAR(256.0f * 'i' + '_', plane.Row(0)[1] * 257, eps);
EXPECT_NEAR(256.0f * 'x' + '_', plane.Row(1)[0] * 257, eps);
EXPECT_NEAR(256.0f * 'e' + '_', plane.Row(1)[1] * 257, eps);
EXPECT_NEAR(256.0f * 'l' + '_', plane.Row(2)[0] * 257, eps);
EXPECT_NEAR(256.0f * 's' + '_', plane.Row(2)[1] * 257, eps);
}
} // namespace
} // namespace extras
} // namespace jxl

View file

@ -31,6 +31,7 @@
#include "lib/jxl/luminance.h"
namespace jxl {
namespace extras {
namespace {
#define JXL_PNG_VERBOSE 0
@ -123,17 +124,40 @@ class BlobsReaderPNG {
if (type->length() > kMaxTypeLen) return false; // Type too long
// Header: freeform string and number of bytes
// Expected format is:
// \n
// profile name/description\n
// 40\n (the number of bytes after hex-decoding)
// 01234566789abcdef....\n (72 bytes per line max).
// 012345667\n (last line)
const char* pos = encoded;
if (*(pos++) != '\n') return false;
while (pos < encoded_end && *pos != '\n') {
pos++;
}
if (pos == encoded_end) return false;
// We parsed so far a \n, some number of non \n characters and are now
// pointing at a \n.
if (*(pos++) != '\n') return false;
unsigned long bytes_to_decode;
int header_len;
std::vector<char> description((encoded_end - encoded) + 1);
const int fields = sscanf(encoded, "\n%[^\n]\n%8lu%n", description.data(),
&bytes_to_decode, &header_len);
if (fields != 2) return false; // Failed to decode metadata header
const int fields = sscanf(pos, "%8lu", &bytes_to_decode);
if (fields != 1) return false; // Failed to decode metadata header
JXL_ASSERT(pos + 8 <= encoded_end);
pos += 8; // read %8lu
// We need 2*bytes for the hex values plus 1 byte every 36 values.
const unsigned long needed_bytes =
bytes_to_decode * 2 + 1 + DivCeil(bytes_to_decode, 36);
if (needed_bytes != static_cast<size_t>(encoded_end - pos)) {
return JXL_FAILURE("Not enough bytes to parse %lu bytes in hex",
bytes_to_decode);
}
JXL_ASSERT(bytes->empty());
bytes->reserve(bytes_to_decode);
// Encoding: base16 with newline after 72 chars.
const char* pos = encoded + header_len;
// pos points to the \n before the first line of hex values.
for (size_t i = 0; i < bytes_to_decode; ++i) {
if (i % 36 == 0) {
if (pos + 1 >= encoded_end) return false; // Truncated base16 1
@ -348,6 +372,7 @@ class ColorEncodingReaderPNG {
Status DecodeSRGB(const unsigned char* payload, const size_t payload_size) {
if (payload_size != 1) return JXL_FAILURE("Wrong sRGB size");
// (PNG uses the same values as ICC.)
if (payload[0] >= 4) return JXL_FAILURE("Invalid Rendering Intent");
rendering_intent_ = static_cast<RenderingIntent>(payload[0]);
have_srgb_ = true;
return true;
@ -462,45 +487,6 @@ class ColorEncodingReaderPNG {
PrimariesCIExy primaries_;
};
Status ApplyHints(const bool is_gray, CodecInOut* io) {
bool got_color_space = false;
JXL_RETURN_IF_ERROR(io->dec_hints.Foreach(
[is_gray, io, &got_color_space](const std::string& key,
const std::string& value) -> Status {
ColorEncoding* c_original = &io->metadata.m.color_encoding;
if (key == "color_space") {
if (!ParseDescription(value, c_original) ||
!c_original->CreateICC()) {
return JXL_FAILURE("PNG: Failed to apply color_space");
}
if (is_gray != io->metadata.m.color_encoding.IsGray()) {
return JXL_FAILURE(
"PNG: mismatch between file and color_space hint");
}
got_color_space = true;
} else if (key == "icc_pathname") {
PaddedBytes icc;
JXL_RETURN_IF_ERROR(ReadFile(value, &icc));
JXL_RETURN_IF_ERROR(c_original->SetICC(std::move(icc)));
got_color_space = true;
} else {
JXL_WARNING("PNG decoder ignoring %s hint", key.c_str());
}
return true;
}));
if (!got_color_space) {
JXL_WARNING("PNG: no color_space/icc_pathname given, assuming sRGB");
JXL_RETURN_IF_ERROR(io->metadata.m.color_encoding.SetSRGB(
is_gray ? ColorSpace::kGray : ColorSpace::kRGB));
}
return true;
}
// Stores ColorEncoding into PNG chunks.
class ColorEncodingWriterPNG {
public:
@ -509,7 +495,9 @@ class ColorEncodingWriterPNG {
if (c.IsSRGB()) {
JXL_RETURN_IF_ERROR(AddSRGB(c, info));
// PNG recommends not including both sRGB and iCCP, so skip the latter.
} else {
} else if (!c.HaveFields() || !c.tf.IsGamma()) {
// Having a gamma value means that the source was a PNG with gAMA and
// without iCCP.
JXL_ASSERT(!c.ICC().empty());
JXL_RETURN_IF_ERROR(AddICC(c.ICC(), info));
}
@ -568,8 +556,16 @@ class ColorEncodingWriterPNG {
static Status MaybeAddGAMA(const ColorEncoding& c,
LodePNGInfo* JXL_RESTRICT info) {
if (!c.tf.IsGamma()) return true;
const double gamma = c.tf.GetGamma();
double gamma;
if (c.tf.IsGamma()) {
gamma = c.tf.GetGamma();
} else if (c.tf.IsLinear()) {
gamma = 1;
} else if (c.tf.IsSRGB()) {
gamma = 0.45455;
} else {
return true;
}
PaddedBytes payload(4);
StoreBE32(U32FromF64(gamma), payload.data());
@ -578,21 +574,31 @@ class ColorEncodingWriterPNG {
static Status MaybeAddCHRM(const ColorEncoding& c,
LodePNGInfo* JXL_RESTRICT info) {
// TODO(lode): remove this, PNG can also have cHRM for P3, sRGB, ...
if (c.white_point != WhitePoint::kCustom &&
c.primaries != Primaries::kCustom) {
return true;
}
const CIExy white_point = c.GetWhitePoint();
CIExy white_point = c.GetWhitePoint();
// A PNG image stores both whitepoint and primaries in the cHRM chunk, but
// for grayscale images we don't have primaries. It does not matter what
// values are stored in the PNG though (all colors are a multiple of the
// whitepoint), so choose default ones. See
// http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html section 4.2.2.1.
const PrimariesCIExy primaries =
PrimariesCIExy primaries =
c.IsGray() ? ColorEncoding().GetPrimaries() : c.GetPrimaries();
if (c.primaries == Primaries::kSRGB && c.white_point == WhitePoint::kD65) {
// For sRGB, the cHRM chunk is supposed to have very specific values which
// don't quite match the pre-quantized ones we have (red is off by
// 0.00010). Technically, this is only required for full sRGB, but for
// consistency, we might as well use them whenever the primaries and white
// point are sRGB's.
white_point.x = 0.31270;
white_point.y = 0.32900;
primaries.r.x = 0.64000;
primaries.r.y = 0.33000;
primaries.g.x = 0.30000;
primaries.g.y = 0.60000;
primaries.b.x = 0.15000;
primaries.b.y = 0.06000;
}
PaddedBytes payload(32);
StoreBE32(U32FromF64(white_point.x), &payload[0]);
StoreBE32(U32FromF64(white_point.y), &payload[4]);
@ -629,7 +635,7 @@ Status CheckGray(const LodePNGColorMode& mode, bool has_icc, bool* is_gray) {
case LCT_PALETTE: {
if (has_icc) {
// If an ICC profile is present, the PNG specification requires
// palette to be intepreted as RGB colored, not grayscale, so we must
// palette to be interpreted as RGB colored, not grayscale, so we must
// output color in that case and unfortunately can't optimize it to
// gray if the palette only has gray entries.
*is_gray = false;
@ -711,7 +717,8 @@ Status InspectChunkType(const Span<const uint8_t> bytes,
} // namespace
Status DecodeImagePNG(const Span<const uint8_t> bytes, ThreadPool* pool,
Status DecodeImagePNG(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io) {
unsigned w, h;
PNGState state;
@ -773,25 +780,18 @@ Status DecodeImagePNG(const Span<const uint8_t> bytes, ThreadPool* pool,
const JxlEndianness endianness = JXL_BIG_ENDIAN; // PNG requirement
const Span<const uint8_t> span(out, out_size);
const bool ok =
ConvertFromExternal(span, w, h, io->metadata.m.color_encoding, has_alpha,
/*alpha_is_premultiplied=*/false,
io->metadata.m.bit_depth.bits_per_sample, endianness,
/*flipped_y=*/false, pool, &io->Main());
const bool ok = ConvertFromExternal(
span, w, h, io->metadata.m.color_encoding, has_alpha,
/*alpha_is_premultiplied=*/false,
io->metadata.m.bit_depth.bits_per_sample, endianness,
/*flipped_y=*/false, pool, &io->Main(), /*float_in=*/false);
JXL_RETURN_IF_ERROR(ok);
io->dec_pixels = w * h;
io->metadata.m.bit_depth.bits_per_sample = io->Main().DetectRealBitdepth();
io->metadata.m.xyb_encoded = false;
SetIntensityTarget(io);
if (!reader.HaveColorProfile()) {
JXL_RETURN_IF_ERROR(ApplyHints(is_gray, io));
} else {
(void)io->dec_hints.Foreach(
[](const std::string& key, const std::string& /*value*/) {
JXL_WARNING("PNG decoder ignoring %s hint", key.c_str());
return true;
});
}
JXL_RETURN_IF_ERROR(
ApplyColorHints(color_hints, reader.HaveColorProfile(), is_gray, io));
return true;
}
@ -848,4 +848,5 @@ Status EncodeImagePNG(const CodecInOut* io, const ColorEncoding& c_desired,
return true;
}
} // namespace extras
} // namespace jxl

View file

@ -14,6 +14,7 @@
// TODO(janwas): workaround for incorrect Win64 codegen (cause unknown)
#include <hwy/highway.h>
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
@ -22,9 +23,11 @@
#include "lib/jxl/color_encoding_internal.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. io->dec_hints are ignored.
Status DecodeImagePNG(const Span<const uint8_t> bytes, ThreadPool* pool,
// Decodes `bytes` into `io`.
Status DecodeImagePNG(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io);
// Transforms from io->c_current to `c_desired` and encodes into `bytes`.
@ -32,6 +35,7 @@ Status EncodeImagePNG(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_PNG_H_

View file

@ -18,6 +18,7 @@
#include "lib/jxl/base/byte_order.h"
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/file_io.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/color_management.h"
#include "lib/jxl/dec_external_image.h"
#include "lib/jxl/enc_external_image.h"
@ -28,6 +29,7 @@
#include "lib/jxl/luminance.h"
namespace jxl {
namespace extras {
namespace {
struct HeaderPNM {
@ -69,6 +71,8 @@ class Parser {
header->is_gray = false;
return ParseHeaderPNM(header, pos);
// TODO(jon): P7 (PAM)
case 'F':
header->is_gray = false;
return ParseHeaderPFM(header, pos);
@ -179,6 +183,18 @@ class Parser {
return true;
}
Status ReadChar(char* out) {
// Unlikely to happen.
if (pos_ + 1 < pos_) return JXL_FAILURE("Y4M: overflow");
if (pos_ >= end_) {
return JXL_FAILURE("Y4M: unexpected end of input");
}
*out = *pos_;
pos_++;
return true;
}
Status ParseHeaderPNM(HeaderPNM* header, const uint8_t** pos) {
JXL_RETURN_IF_ERROR(SkipWhitespace());
JXL_RETURN_IF_ERROR(ParseUnsigned(&header->xsize));
@ -240,63 +256,32 @@ Status EncodeHeader(const ImageBundle& ib, const size_t bits_per_sample,
if (bits_per_sample == 32) { // PFM
const char type = ib.IsGray() ? 'f' : 'F';
const double scale = little_endian ? -1.0 : 1.0;
snprintf(header, kMaxHeaderSize, "P%c\n%zu %zu\n%.1f\n%n", type,
ib.oriented_xsize(), ib.oriented_ysize(), scale, chars_written);
*chars_written =
snprintf(header, kMaxHeaderSize, "P%c\n%zu %zu\n%.1f\n", type,
ib.oriented_xsize(), ib.oriented_ysize(), scale);
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
} else if (bits_per_sample == 1) { // PBM
if (!ib.IsGray()) {
return JXL_FAILURE("Cannot encode color as PBM");
}
snprintf(header, kMaxHeaderSize, "P4\n%zu %zu\n%n", ib.oriented_xsize(),
ib.oriented_ysize(), chars_written);
*chars_written = snprintf(header, kMaxHeaderSize, "P4\n%zu %zu\n",
ib.oriented_xsize(), ib.oriented_ysize());
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
} else { // PGM/PPM
const uint32_t max_val = (1U << bits_per_sample) - 1;
if (max_val >= 65536) return JXL_FAILURE("PNM cannot have > 16 bits");
const char type = ib.IsGray() ? '5' : '6';
snprintf(header, kMaxHeaderSize, "P%c\n%zu %zu\n%u\n%n", type,
ib.oriented_xsize(), ib.oriented_ysize(), max_val, chars_written);
*chars_written =
snprintf(header, kMaxHeaderSize, "P%c\n%zu %zu\n%u\n", type,
ib.oriented_xsize(), ib.oriented_ysize(), max_val);
JXL_RETURN_IF_ERROR(static_cast<unsigned int>(*chars_written) <
kMaxHeaderSize);
}
return true;
}
Status ApplyHints(const bool is_gray, CodecInOut* io) {
bool got_color_space = false;
JXL_RETURN_IF_ERROR(io->dec_hints.Foreach(
[is_gray, io, &got_color_space](const std::string& key,
const std::string& value) -> Status {
ColorEncoding* c_original = &io->metadata.m.color_encoding;
if (key == "color_space") {
if (!ParseDescription(value, c_original) ||
!c_original->CreateICC()) {
return JXL_FAILURE("PNM: Failed to apply color_space");
}
if (is_gray != io->metadata.m.color_encoding.IsGray()) {
return JXL_FAILURE(
"PNM: mismatch between file and color_space hint");
}
got_color_space = true;
} else if (key == "icc_pathname") {
PaddedBytes icc;
JXL_RETURN_IF_ERROR(ReadFile(value, &icc));
JXL_RETURN_IF_ERROR(c_original->SetICC(std::move(icc)));
got_color_space = true;
} else {
JXL_WARNING("PNM decoder ignoring %s hint", key.c_str());
}
return true;
}));
if (!got_color_space) {
JXL_WARNING("PNM: no color_space/icc_pathname given, assuming sRGB");
JXL_RETURN_IF_ERROR(io->metadata.m.color_encoding.SetSRGB(
is_gray ? ColorSpace::kGray : ColorSpace::kRGB));
}
return true;
}
Span<const uint8_t> MakeSpan(const char* str) {
return Span<const uint8_t>(reinterpret_cast<const uint8_t*>(str),
strlen(str));
@ -320,7 +305,8 @@ void VerticallyFlipImage(Image3F* const image) {
} // namespace
Status DecodeImagePNM(const Span<const uint8_t> bytes, ThreadPool* pool,
Status DecodeImagePNM(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io) {
Parser parser(bytes);
HeaderPNM header = {};
@ -333,7 +319,9 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes, ThreadPool* pool,
return JXL_FAILURE("PNM: bits_per_sample invalid");
}
JXL_RETURN_IF_ERROR(ApplyHints(header.is_gray, io));
JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, /*color_already_set=*/false,
header.is_gray, io));
if (header.floating_point) {
io->metadata.m.SetFloat32Samples();
} else {
@ -343,13 +331,15 @@ Status DecodeImagePNM(const Span<const uint8_t> bytes, ThreadPool* pool,
io->dec_pixels = header.xsize * header.ysize;
const bool flipped_y = header.bits_per_sample == 32; // PFMs are flipped
const bool float_in = header.bits_per_sample == 32;
const Span<const uint8_t> span(pos, bytes.data() + bytes.size() - pos);
JXL_RETURN_IF_ERROR(ConvertFromExternal(
span, header.xsize, header.ysize, io->metadata.m.color_encoding,
/*has_alpha=*/false, /*alpha_is_premultiplied=*/false,
io->metadata.m.bit_depth.bits_per_sample,
header.big_endian ? JXL_BIG_ENDIAN : JXL_LITTLE_ENDIAN, flipped_y, pool,
&io->Main()));
&io->Main(), float_in));
if (!header.floating_point) {
io->metadata.m.bit_depth.bits_per_sample = io->Main().DetectRealBitdepth();
}
@ -450,4 +440,5 @@ void TestCodecPNM() {
JXL_CHECK(std::abs(d - -3.141592) < 1E-15);
}
} // namespace extras
} // namespace jxl

View file

@ -14,6 +14,7 @@
// TODO(janwas): workaround for incorrect Win64 codegen (cause unknown)
#include <hwy/highway.h>
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/data_parallel.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
@ -22,10 +23,12 @@
#include "lib/jxl/color_encoding_internal.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`. io->dec_hints may specify "color_space", which
// Decodes `bytes` into `io`. color_hints may specify "color_space", which
// defaults to sRGB.
Status DecodeImagePNM(const Span<const uint8_t> bytes, ThreadPool* pool,
Status DecodeImagePNM(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io);
// Transforms from io->c_current to `c_desired` and encodes into `bytes`.
@ -35,6 +38,7 @@ Status EncodeImagePNM(const CodecInOut* io, const ColorEncoding& c_desired,
void TestCodecPNM();
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_PNM_H_

View file

@ -28,6 +28,7 @@
#include "lib/jxl/luminance.h"
namespace jxl {
namespace extras {
namespace {
uint64_t get_be_int(int bytes, const uint8_t*& pos, const uint8_t* maxpos) {
@ -187,7 +188,8 @@ Status decode_layer(const uint8_t*& pos, const uint8_t* maxpos,
} // namespace
Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
Status DecodeImagePSD(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io) {
const uint8_t* pos = bytes.data();
const uint8_t* maxpos = bytes.data() + bytes.size();
@ -229,6 +231,7 @@ Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
bool hasmergeddata = true;
bool have_alpha = false;
bool merged_has_alpha = false;
bool color_already_set = false;
size_t metalength = get_be_int(4, pos, maxpos);
const uint8_t* metaoffset = pos;
while (pos < metaoffset + metalength) {
@ -257,6 +260,7 @@ Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
if (!io->metadata.m.color_encoding.SetICC(std::move(icc))) {
return JXL_FAILURE("PSD: Invalid color profile");
}
color_already_set = true;
} else if (id == 1057) { // compatibility mode or not?
if (get_be_int(4, pos, maxpos) != 1) {
return JXL_FAILURE("PSD: expected version=1 in id=1057 resource block");
@ -310,6 +314,9 @@ Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
if (blocklength & 1) pos++; // padding again
}
JXL_RETURN_IF_ERROR(ApplyColorHints(color_hints, color_already_set,
/*is_gray=*/false, io));
size_t layerlength = get_be_int(4 * version, pos, maxpos);
const uint8_t* after_layers_pos = pos + layerlength;
if (after_layers_pos < pos) return JXL_FAILURE("PSD: invalid layer length");
@ -349,6 +356,7 @@ Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
return JXL_FAILURE("PSD: Invalid layer count");
}
JXL_DEBUG_V(PSD_VERBOSITY, "Real layer count: %i", layercount);
if (layercount > 1) have_alpha = true;
break;
}
if (!safe_strncmp(tpos, maxpos, "Mtrn", 4) ||
@ -356,7 +364,6 @@ Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
!safe_strncmp(tpos, maxpos, "Mt32", 4)) {
JXL_DEBUG_V(PSD_VERBOSITY, "Merged layer has transparency channel");
if (nb_channels > real_nb_channels) {
real_nb_channels++;
have_alpha = true;
merged_has_alpha = true;
}
@ -366,14 +373,12 @@ Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
} else if (layercount < 0) {
// negative layer count indicates merged has alpha and it is to be shown
if (nb_channels > real_nb_channels) {
real_nb_channels++;
have_alpha = true;
merged_has_alpha = true;
}
layercount = -layercount;
} else {
// multiple layers implies there is alpha
real_nb_channels++;
have_alpha = true;
}
@ -387,6 +392,7 @@ Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
}
if (have_alpha) {
JXL_DEBUG_V(PSD_VERBOSITY, "Have alpha");
real_nb_channels++;
info.type = ExtraChannel::kAlpha;
info.alpha_associated =
false; // true? PSD is not consistent with this, need to check
@ -527,9 +533,11 @@ Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
JXL_DEBUG_V(PSD_VERBOSITY, "At position %i (%zu)",
(int)(pos - bytes.data()), (size_t)pos);
ImageBundle& layer = io->frames[il++];
JXL_RETURN_IF_ERROR(decode_layer(pos, maxpos, layer, layer_chan_id[l],
invert, layer.xsize(), layer.ysize(),
version, colormodel, true, bitdepth));
std::vector<int>& chan_id = layer_chan_id[l];
if (chan_id.size() > invert.size()) invert.resize(chan_id.size(), false);
JXL_RETURN_IF_ERROR(decode_layer(pos, maxpos, layer, chan_id, invert,
layer.xsize(), layer.ysize(), version,
colormodel, true, bitdepth));
}
} else
return JXL_FAILURE("PSD: no layer data found");
@ -573,7 +581,7 @@ Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
return JXL_FAILURE("Inconsistent layer configuration");
}
if (!merged_has_alpha) {
if (colormodel <= real_nb_channels) {
if (colormodel >= real_nb_channels) {
return JXL_FAILURE("Inconsistent layer configuration");
}
chan_id.erase(chan_id.begin() + colormodel);
@ -605,4 +613,5 @@ Status EncodeImagePSD(const CodecInOut* io, const ColorEncoding& c_desired,
return JXL_FAILURE("PSD encoding not yet implemented");
}
} // namespace extras
} // namespace jxl

View file

@ -11,6 +11,7 @@
#include <stddef.h>
#include <stdint.h>
#include "lib/extras/color_hints.h"
#include "lib/jxl/base/padded_bytes.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
@ -18,9 +19,11 @@
#include "lib/jxl/color_encoding_internal.h"
namespace jxl {
namespace extras {
// Decodes `bytes` into `io`.
Status DecodeImagePSD(const Span<const uint8_t> bytes, ThreadPool* pool,
Status DecodeImagePSD(const Span<const uint8_t> bytes,
const ColorHints& color_hints, ThreadPool* pool,
CodecInOut* io);
// Not implemented yet
@ -28,6 +31,7 @@ Status EncodeImagePSD(const CodecInOut* io, const ColorEncoding& c_desired,
size_t bits_per_sample, ThreadPool* pool,
PaddedBytes* bytes);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_CODEC_PSD_H_

View file

@ -25,6 +25,7 @@
#include "lib/jxl/testdata.h"
namespace jxl {
namespace extras {
namespace {
CodecInOut CreateTestImage(const size_t xsize, const size_t ysize,
@ -104,12 +105,14 @@ void TestRoundTrip(Codec codec, const size_t xsize, const size_t ysize,
JXL_CHECK(Encode(io, codec, c_external, bits_per_sample, &encoded, pool));
CodecInOut io2;
ColorHints color_hints;
io2.target_nits = io.metadata.m.IntensityTarget();
// Only for PNM because PNG will warn about ignoring them.
if (codec == Codec::kPNM) {
io2.dec_hints.Add("color_space", Description(c_external));
color_hints.Add("color_space", Description(c_external));
}
JXL_CHECK(SetFromBytes(Span<const uint8_t>(encoded), &io2, pool));
JXL_CHECK(SetFromBytes(Span<const uint8_t>(encoded), color_hints, &io2, pool,
nullptr));
ImageBundle& ib2 = io2.Main();
EXPECT_EQ(Description(c_external),
@ -183,11 +186,11 @@ TEST(CodecTest, TestRoundTrip) {
CodecInOut DecodeRoundtrip(const std::string& pathname, Codec expected_codec,
ThreadPool* pool,
const DecoderHints& dec_hints = DecoderHints()) {
const ColorHints& color_hints = ColorHints()) {
CodecInOut io;
io.dec_hints = dec_hints;
const PaddedBytes orig = ReadTestData(pathname);
JXL_CHECK(SetFromBytes(Span<const uint8_t>(orig), &io, pool));
JXL_CHECK(
SetFromBytes(Span<const uint8_t>(orig), color_hints, &io, pool, nullptr));
const ImageBundle& ib1 = io.Main();
// Encode/Decode again to make sure Encode carries through all metadata.
@ -196,8 +199,8 @@ CodecInOut DecodeRoundtrip(const std::string& pathname, Codec expected_codec,
io.metadata.m.bit_depth.bits_per_sample, &encoded, pool));
CodecInOut io2;
io2.dec_hints = dec_hints;
JXL_CHECK(SetFromBytes(Span<const uint8_t>(encoded), &io2, pool));
JXL_CHECK(SetFromBytes(Span<const uint8_t>(encoded), color_hints, &io2, pool,
nullptr));
const ImageBundle& ib2 = io2.Main();
EXPECT_EQ(Description(ib1.metadata()->color_encoding),
Description(ib2.metadata()->color_encoding));
@ -344,9 +347,9 @@ void VerifyWideGamutMetadata(const std::string& relative_pathname,
const Primaries primaries, ThreadPool* pool) {
const CodecInOut io = DecodeRoundtrip(relative_pathname, Codec::kPNG, pool);
EXPECT_EQ(8, io.metadata.m.bit_depth.bits_per_sample);
EXPECT_EQ(8u, io.metadata.m.bit_depth.bits_per_sample);
EXPECT_FALSE(io.metadata.m.bit_depth.floating_point_sample);
EXPECT_EQ(0, io.metadata.m.bit_depth.exponent_bits_per_sample);
EXPECT_EQ(0u, io.metadata.m.bit_depth.exponent_bits_per_sample);
const ColorEncoding& c_original = io.metadata.m.color_encoding;
EXPECT_FALSE(c_original.ICC().empty());
@ -369,7 +372,7 @@ TEST(CodecTest, TestWideGamut) {
}
TEST(CodecTest, TestPNM) { TestCodecPNM(); }
TEST(CodecTest, TestPGX) { TestCodecPGX(); }
} // namespace
} // namespace extras
} // namespace jxl

View file

@ -0,0 +1,218 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/color_description.h"
#include <errno.h>
#include <cmath>
namespace jxl {
namespace {
template <typename T>
struct EnumName {
const char* name;
T value;
};
const EnumName<JxlColorSpace> kJxlColorSpaceNames[] = {
{"RGB", JXL_COLOR_SPACE_RGB},
{"Gra", JXL_COLOR_SPACE_GRAY},
{"XYB", JXL_COLOR_SPACE_XYB},
{"CS?", JXL_COLOR_SPACE_UNKNOWN},
};
const EnumName<JxlWhitePoint> kJxlWhitePointNames[] = {
{"D65", JXL_WHITE_POINT_D65},
{"Cst", JXL_WHITE_POINT_CUSTOM},
{"EER", JXL_WHITE_POINT_E},
{"DCI", JXL_WHITE_POINT_DCI},
};
const EnumName<JxlPrimaries> kJxlPrimariesNames[] = {
{"SRG", JXL_PRIMARIES_SRGB},
{"Cst", JXL_PRIMARIES_CUSTOM},
{"202", JXL_PRIMARIES_2100},
{"DCI", JXL_PRIMARIES_P3},
};
const EnumName<JxlTransferFunction> kJxlTransferFunctionNames[] = {
{"709", JXL_TRANSFER_FUNCTION_709},
{"TF?", JXL_TRANSFER_FUNCTION_UNKNOWN},
{"Lin", JXL_TRANSFER_FUNCTION_LINEAR},
{"SRG", JXL_TRANSFER_FUNCTION_SRGB},
{"PeQ", JXL_TRANSFER_FUNCTION_PQ},
{"DCI", JXL_TRANSFER_FUNCTION_DCI},
{"HLG", JXL_TRANSFER_FUNCTION_HLG},
{"", JXL_TRANSFER_FUNCTION_GAMMA},
};
const EnumName<JxlRenderingIntent> kJxlRenderingIntentNames[] = {
{"Per", JXL_RENDERING_INTENT_PERCEPTUAL},
{"Rel", JXL_RENDERING_INTENT_RELATIVE},
{"Sat", JXL_RENDERING_INTENT_SATURATION},
{"Abs", JXL_RENDERING_INTENT_ABSOLUTE},
};
template <typename T>
Status ParseEnum(const std::string& token, const EnumName<T>* enum_values,
size_t enum_len, T* value) {
std::string str;
for (size_t i = 0; i < enum_len; i++) {
if (enum_values[i].name == token) {
*value = enum_values[i].value;
return true;
}
}
return false;
}
#define ARRAYSIZE(X) (sizeof(X) / sizeof((X)[0]))
#define PARSE_ENUM(type, token, value) \
ParseEnum<type>(token, k##type##Names, ARRAYSIZE(k##type##Names), value)
class Tokenizer {
public:
Tokenizer(const std::string* input, char separator)
: input_(input), separator_(separator) {}
Status Next(std::string* next) {
const size_t end = input_->find(separator_, start_);
if (end == std::string::npos) {
*next = input_->substr(start_); // rest of string
} else {
*next = input_->substr(start_, end - start_);
}
if (next->empty()) return JXL_FAILURE("Missing token");
start_ = end + 1;
return true;
}
private:
const std::string* const input_; // not owned
const char separator_;
size_t start_ = 0; // of next token
};
Status ParseDouble(const std::string& num, double* d) {
char* end;
errno = 0;
*d = strtod(num.c_str(), &end);
if (*d == 0.0 && end == num.c_str()) {
return JXL_FAILURE("Invalid double: %s", num.c_str());
}
if (std::isnan(*d)) {
return JXL_FAILURE("Invalid double: %s", num.c_str());
}
if (errno == ERANGE) {
return JXL_FAILURE("Double out of range: %s", num.c_str());
}
return true;
}
Status ParseDouble(Tokenizer* tokenizer, double* d) {
std::string num;
JXL_RETURN_IF_ERROR(tokenizer->Next(&num));
return ParseDouble(num, d);
}
Status ParseColorSpace(Tokenizer* tokenizer, JxlColorEncoding* c) {
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
JxlColorSpace cs;
if (PARSE_ENUM(JxlColorSpace, str, &cs)) {
c->color_space = cs;
return true;
}
return JXL_FAILURE("Unknown ColorSpace %s", str.c_str());
}
Status ParseWhitePoint(Tokenizer* tokenizer, JxlColorEncoding* c) {
if (c->color_space == JXL_COLOR_SPACE_XYB) {
// Implicit white point.
c->white_point = JXL_WHITE_POINT_D65;
return true;
}
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
if (PARSE_ENUM(JxlWhitePoint, str, &c->white_point)) return true;
Tokenizer xy_tokenizer(&str, ';');
c->white_point = JXL_WHITE_POINT_CUSTOM;
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->white_point_xy + 0));
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->white_point_xy + 1));
return true;
}
Status ParsePrimaries(Tokenizer* tokenizer, JxlColorEncoding* c) {
if (c->color_space == JXL_COLOR_SPACE_GRAY ||
c->color_space == JXL_COLOR_SPACE_XYB) {
// No primaries case.
return true;
}
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
if (PARSE_ENUM(JxlPrimaries, str, &c->primaries)) return true;
Tokenizer xy_tokenizer(&str, ';');
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 0));
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_red_xy + 1));
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_green_xy + 0));
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_green_xy + 1));
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_blue_xy + 0));
JXL_RETURN_IF_ERROR(ParseDouble(&xy_tokenizer, c->primaries_blue_xy + 1));
c->primaries = JXL_PRIMARIES_CUSTOM;
return JXL_FAILURE("Invalid primaries %s", str.c_str());
}
Status ParseRenderingIntent(Tokenizer* tokenizer, JxlColorEncoding* c) {
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
if (PARSE_ENUM(JxlRenderingIntent, str, &c->rendering_intent)) return true;
return JXL_FAILURE("Invalid RenderingIntent %s\n", str.c_str());
}
Status ParseTransferFunction(Tokenizer* tokenizer, JxlColorEncoding* c) {
if (c->color_space == JXL_COLOR_SPACE_XYB) {
// Implicit TF.
c->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
c->gamma = 1 / 3.;
return true;
}
std::string str;
JXL_RETURN_IF_ERROR(tokenizer->Next(&str));
if (PARSE_ENUM(JxlTransferFunction, str, &c->transfer_function)) {
return true;
}
if (str[0] == 'g') {
JXL_RETURN_IF_ERROR(ParseDouble(str.substr(1), &c->gamma));
c->transfer_function = JXL_TRANSFER_FUNCTION_GAMMA;
return true;
}
return JXL_FAILURE("Invalid gamma %s", str.c_str());
}
} // namespace
Status ParseDescription(const std::string& description, JxlColorEncoding* c) {
*c = {};
Tokenizer tokenizer(&description, '_');
JXL_RETURN_IF_ERROR(ParseColorSpace(&tokenizer, c));
JXL_RETURN_IF_ERROR(ParseWhitePoint(&tokenizer, c));
JXL_RETURN_IF_ERROR(ParsePrimaries(&tokenizer, c));
JXL_RETURN_IF_ERROR(ParseRenderingIntent(&tokenizer, c));
JXL_RETURN_IF_ERROR(ParseTransferFunction(&tokenizer, c));
return true;
}
} // namespace jxl

View file

@ -0,0 +1,22 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_EXTRAS_COLOR_DESCRIPTION_H_
#define LIB_EXTRAS_COLOR_DESCRIPTION_H_
#include <string>
#include "jxl/color_encoding.h"
#include "lib/jxl/base/status.h"
namespace jxl {
// Parse the color description into a JxlColorEncoding "RGB_D65_SRG_Rel_Lin".
Status ParseDescription(const std::string& description,
JxlColorEncoding* JXL_RESTRICT c);
} // namespace jxl
#endif // LIB_EXTRAS_COLOR_DESCRIPTION_H_

View file

@ -0,0 +1,38 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/color_description.h"
#include "gtest/gtest.h"
#include "lib/jxl/color_encoding_internal.h"
#include "lib/jxl/test_utils.h"
namespace jxl {
// Verify ParseDescription(Description) yields the same ColorEncoding
TEST(ColorDescriptionTest, RoundTripAll) {
for (const auto& cdesc : test::AllEncodings()) {
const ColorEncoding c_original = test::ColorEncodingFromDescriptor(cdesc);
const std::string description = Description(c_original);
printf("%s\n", description.c_str());
JxlColorEncoding c_external = {};
EXPECT_TRUE(ParseDescription(description, &c_external));
ColorEncoding c_internal;
EXPECT_TRUE(
ConvertExternalToInternalColorEncoding(c_external, &c_internal));
EXPECT_TRUE(c_original.SameColorEncoding(c_internal))
<< "Where c_original=" << c_original
<< " and c_internal=" << c_internal;
}
}
TEST(ColorDescriptionTest, NanGamma) {
const std::string description = "Gra_2_Per_gnan";
JxlColorEncoding c;
EXPECT_FALSE(ParseDescription(description, &c));
}
} // namespace jxl

View file

@ -0,0 +1,67 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/extras/color_hints.h"
#include "lib/extras/color_description.h"
#include "lib/jxl/base/file_io.h"
#include "lib/jxl/color_encoding_internal.h"
namespace jxl {
namespace extras {
Status ApplyColorHints(const ColorHints& color_hints,
const bool color_already_set, const bool is_gray,
CodecInOut* io) {
if (color_already_set) {
return color_hints.Foreach(
[](const std::string& key, const std::string& /*value*/) {
JXL_WARNING("Decoder ignoring %s hint", key.c_str());
return true;
});
}
bool got_color_space = false;
JXL_RETURN_IF_ERROR(color_hints.Foreach(
[is_gray, io, &got_color_space](const std::string& key,
const std::string& value) -> Status {
ColorEncoding* c_original = &io->metadata.m.color_encoding;
if (key == "color_space") {
JxlColorEncoding c_original_external;
if (!ParseDescription(value, &c_original_external) ||
!ConvertExternalToInternalColorEncoding(c_original_external,
c_original) ||
!c_original->CreateICC()) {
return JXL_FAILURE("Failed to apply color_space");
}
if (is_gray != io->metadata.m.color_encoding.IsGray()) {
return JXL_FAILURE("mismatch between file and color_space hint");
}
got_color_space = true;
} else if (key == "icc_pathname") {
PaddedBytes icc;
JXL_RETURN_IF_ERROR(ReadFile(value, &icc));
JXL_RETURN_IF_ERROR(c_original->SetICC(std::move(icc)));
got_color_space = true;
} else {
JXL_WARNING("Ignoring %s hint", key.c_str());
}
return true;
}));
if (!got_color_space) {
JXL_WARNING("No color_space/icc_pathname given, assuming sRGB");
JXL_RETURN_IF_ERROR(io->metadata.m.color_encoding.SetSRGB(
is_gray ? ColorSpace::kGray : ColorSpace::kRGB));
}
return true;
}
} // namespace extras
} // namespace jxl

View file

@ -0,0 +1,73 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_EXTRAS_COLOR_HINTS_H_
#define LIB_EXTRAS_COLOR_HINTS_H_
// Not all the formats implemented in the extras lib support bundling color
// information into the file, and those that support it may not have it.
// To allow attaching color information to those file formats the caller can
// define these color hints.
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
#include "lib/jxl/base/status.h"
#include "lib/jxl/codec_in_out.h"
namespace jxl {
class ColorHints {
public:
// key=color_space, value=Description(c/pp): specify the ColorEncoding of
// the pixels for decoding. Otherwise, if the codec did not obtain an ICC
// profile from the image, assume sRGB.
//
// Strings are taken from the command line, so avoid spaces for convenience.
void Add(const std::string& key, const std::string& value) {
kv_.emplace_back(key, value);
}
// Calls `func(key, value)` for each key/value in the order they were added,
// returning false immediately if `func` returns false.
template <class Func>
Status Foreach(const Func& func) const {
for (const KeyValue& kv : kv_) {
Status ok = func(kv.key, kv.value);
if (!ok) {
return JXL_FAILURE("ColorHints::Foreach returned false");
}
}
return true;
}
private:
// Splitting into key/value avoids parsing in each codec.
struct KeyValue {
KeyValue(std::string key, std::string value)
: key(std::move(key)), value(std::move(value)) {}
std::string key;
std::string value;
};
std::vector<KeyValue> kv_;
};
namespace extras {
// Apply the color hints to the decoded image in CodecInOut if any.
// color_already_set tells whether the color encoding was already set, in which
// case the hints are ignored if any hint is passed.
Status ApplyColorHints(const ColorHints& color_hints, bool color_already_set,
bool is_gray, CodecInOut* io);
} // namespace extras
} // namespace jxl
#endif // LIB_EXTRAS_COLOR_HINTS_H_

View file

@ -3,7 +3,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "lib/jxl/base/time.h"
#include "lib/extras/time.h"
#include <stdint.h>
#include <stdio.h>

View file

@ -3,8 +3,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#ifndef LIB_JXL_BASE_TIME_H_
#define LIB_JXL_BASE_TIME_H_
#ifndef LIB_EXTRAS_TIME_H_
#define LIB_EXTRAS_TIME_H_
// OS-specific function for timing.
@ -16,4 +16,4 @@ double Now();
} // namespace jxl
#endif // LIB_JXL_BASE_TIME_H_
#endif // LIB_EXTRAS_TIME_H_

View file

@ -4,7 +4,9 @@
* license that can be found in the LICENSE file.
*/
/** @file butteraugli.h
/** @addtogroup libjxl_butteraugli
* @{
* @file butteraugli.h
* @brief Butteraugli API for JPEG XL.
*/
@ -154,3 +156,5 @@ JXL_EXPORT void JxlButteraugliResultGetDistmap(
#endif
#endif /* JXL_BUTTERAUGLI_H_ */
/** @}*/

View file

@ -3,6 +3,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/// @addtogroup libjxl_butteraugli
/// @{
///
/// @file butteraugli_cxx.h
/// @brief C++ header-only helper for @ref butteraugli.h.
///
@ -53,3 +56,5 @@ typedef std::unique_ptr<JxlButteraugliResult, JxlButteraugliResultDestroyStruct>
JxlButteraugliResultPtr;
#endif // JXL_BUTTERAUGLI_CXX_H_
/// @}

View file

@ -4,7 +4,9 @@
* license that can be found in the LICENSE file.
*/
/** @file codestream_header.h
/** @addtogroup libjxl_common
* @{
* @file codestream_header.h
* @brief Definitions of structs and enums for the metadata from the JPEG XL
* codestream headers (signature, metadata, preview dimensions, ...), excluding
* color encoding which is in color_encoding.h.
@ -92,7 +94,7 @@ typedef struct {
/** Basic image information. This information is available from the file
* signature and first part of the codestream header.
*/
typedef struct JxlBasicInfo {
typedef struct {
/* TODO(lode): need additional fields for (transcoded) JPEG? For reusable
* fields orientation must be read from Exif APP1. For has_icc_profile: must
* look up where ICC profile is guaranteed to be in a JPEG file to be able to
@ -203,15 +205,20 @@ typedef struct JxlBasicInfo {
uint32_t num_extra_channels;
/** Bit depth of the encoded alpha channel, or 0 if there is no alpha channel.
* If present, matches the alpha_bits value of the JxlExtraChannelInfo
* associated with this alpha channel.
*/
uint32_t alpha_bits;
/** Alpha channel floating point exponent bits, or 0 if they are unsigned
* integer.
/** Alpha channel floating point exponent bits, or 0 if they are unsigned. If
* present, matches the alpha_bits value of the JxlExtraChannelInfo associated
* with this alpha channel. integer.
*/
uint32_t alpha_exponent_bits;
/** Whether the alpha channel is premultiplied
/** Whether the alpha channel is premultiplied. Only used if there is a main
* alpha channel. Matches the alpha_premultiplied value of the
* JxlExtraChannelInfo associated with this alpha channel.
*/
JXL_BOOL alpha_premultiplied;
@ -224,6 +231,11 @@ typedef struct JxlBasicInfo {
* used if have_animation is JXL_TRUE.
*/
JxlAnimationHeader animation;
/** Padding for forwards-compatibility, in case more fields are exposed
* in a future version of the library.
*/
uint8_t padding[108];
} JxlBasicInfo;
/** Information for a single extra channel.
@ -257,7 +269,7 @@ typedef struct {
/** Whether alpha channel uses premultiplied alpha. Only applicable if
* type is JXL_CHANNEL_ALPHA.
*/
JXL_BOOL alpha_associated;
JXL_BOOL alpha_premultiplied;
/** Spot color of the current spot channel in linear RGBA. Only applicable if
* type is JXL_CHANNEL_SPOT_COLOR.
@ -309,3 +321,5 @@ typedef struct {
#endif
#endif /* JXL_CODESTREAM_HEADER_H_ */
/** @}*/

View file

@ -4,7 +4,9 @@
* license that can be found in the LICENSE file.
*/
/** @file color_encoding.h
/** @addtogroup libjxl_common
* @{
* @file color_encoding.h
* @brief Color Encoding definitions used by JPEG XL.
* All CIE units are for the standard 1931 2 degree observer.
*/
@ -127,7 +129,7 @@ typedef struct {
/** Numerical blue primary values in CIE xy space. */
double primaries_blue_xy[2];
/** Transfer function is have_gamma is 0 */
/** Transfer function if have_gamma is 0 */
JxlTransferFunction transfer_function;
/** Gamma value used when transfer_function is JXL_TRANSFER_FUNCTION_GAMMA
@ -138,27 +140,10 @@ typedef struct {
JxlRenderingIntent rendering_intent;
} JxlColorEncoding;
/** Color transform used for the XYB encoding. This affects how the internal
* XYB color format is converted, and is not needed unless XYB color is used.
*/
typedef struct {
/** Inverse opsin matrix.
*/
float opsin_inv_matrix[3][3];
/** Opsin bias for opsin matrix. This affects how the internal XYB color
* format is converted, and is not needed unless XYB color is used.
*/
float opsin_biases[3];
/** Quantization bias for opsin matrix. This affects how the internal XYB
* color format is converted, and is not needed unless XYB color is used.
*/
float quant_biases[3];
} JxlInverseOpsinMatrix;
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* JXL_COLOR_ENCODING_H_ */
/** @}*/

View file

@ -4,7 +4,9 @@
* license that can be found in the LICENSE file.
*/
/** @file decode.h
/** @addtogroup libjxl_decoder
* @{
* @file decode.h
* @brief Decoding API for JPEG XL.
*/
@ -121,7 +123,7 @@ typedef enum {
*/
JXL_DEC_SUCCESS = 0,
/** An error occured, for example invalid input file or out of memory.
/** An error occurred, for example invalid input file or out of memory.
* TODO(lode): add function to get error information from decoder.
*/
JXL_DEC_ERROR = 1,
@ -150,6 +152,8 @@ typedef enum {
* requested and it is possible to decode a DC image from the codestream and
* the DC out buffer was not yet set. This event re-occurs for new frames
* if there are multiple animation frames.
* DEPRECATED: the DC feature in this form will be removed. You can use
* JxlDecoderFlushImage for progressive rendering.
*/
JXL_DEC_NEED_DC_OUT_BUFFER = 4,
@ -160,14 +164,19 @@ typedef enum {
*/
JXL_DEC_NEED_IMAGE_OUT_BUFFER = 5,
/** Informative event by JxlDecoderProcessInput: JPEG reconstruction buffer is
* too small for reconstructed JPEG codestream to fit.
* JxlDecoderSetJPEGBuffer must be called again to make room for remaining
* bytes. This event may occur multiple times after
* JXL_DEC_JPEG_RECONSTRUCTION
/** The JPEG reconstruction buffer is too small for reconstructed JPEG
* codestream to fit. JxlDecoderSetJPEGBuffer must be called again to make
* room for remaining bytes. This event may occur multiple times after
* JXL_DEC_JPEG_RECONSTRUCTION.
*/
JXL_DEC_JPEG_NEED_MORE_OUTPUT = 6,
/** The box contents output buffer is too small. JxlDecoderSetBoxBuffer must
* be called again to make room for remaining bytes. This event may occur
* multiple times after JXL_DEC_BOX.
*/
JXL_DEC_BOX_NEED_MORE_OUTPUT = 7,
/** Informative event by JxlDecoderProcessInput: basic information such as
* image dimensions and extra channels. This event occurs max once per image.
*/
@ -214,6 +223,8 @@ typedef enum {
* status only indicates we're past this point in the codestream. This event
* occurs max once per frame and always later than JXL_DEC_FRAME_HEADER
* and other header events and earlier than full resolution pixel data.
* DEPRECATED: the DC feature in this form will be removed. You can use
* JxlDecoderFlushImage for progressive rendering.
*/
JXL_DEC_DC_IMAGE = 0x800,
@ -234,8 +245,70 @@ typedef enum {
* image and always before JXL_DEC_FULL_IMAGE.
*/
JXL_DEC_JPEG_RECONSTRUCTION = 0x2000,
/** Informative event by JxlDecoderProcessInput: The header of a box of the
* container format (BMFF) is decoded. The following API functions related to
* boxes can be used after this event:
* @see JxlDecoderSetBoxBuffer and JxlDecoderReleaseBoxBuffer: set and release
* a buffer to get the box data.
* @see JxlDecoderGetBoxType get the 4-character box typename.
* @see JxlDecoderGetBoxSizeRaw get the size of the box as it appears in the
* container file, not decompressed.
* @see JxlDecoderSetDecompressBoxes to configure whether to get the box
* data decompressed, or possibly compressed.
*
* Boxes can be compressed. This is so when their box type is "brob". In that
* case, they have an underlying decompressed box type and decompressed data.
* Use JxlDecoderSetDecompressBoxes to configure which data to get,
* decompressing them requires Brotli. JxlDecoderGetBoxType has a flag to
* get the compressed box type, which can be "brob", or the decompressed box
* type. If a box is not compressed (its compressed type is not "brob"), then
* you get the same decompressed box type and data no matter what setting is
* configured.
*
* The buffer set with JxlDecoderSetBoxBuffer must be set again for each next
* box that you want to get, or can be left unset to skip outputting this box.
* The output buffer contains the full box data when the next JXL_DEC_BOX
* event or JXL_DEC_SUCCESS occurs. JXL_DEC_BOX occurs for all boxes,
* including non-metadata boxes such as the signature box or codestream boxes.
* To check whether the box is a metadata type for respectively EXIF, XMP or
* JUMBF, use JxlDecoderGetBoxType and check for types "Exif", "xml " and
* "jumb" respectively.
*/
JXL_DEC_BOX = 0x4000,
} JxlDecoderStatus;
/** Rewinds decoder to the beginning. The same input must be given again from
* the beginning of the file and the decoder will emit events from the beginning
* again. When rewinding (as opposed to JxlDecoderReset), the decoder can keep
* state about the image, which it can use to skip to a requested frame more
* efficiently with JxlDecoderSkipFrames. Settings such as parallel runner or
* subscribed events are kept. After rewind, JxlDecoderSubscribeEvents can be
* used again, and it is feasible to leave out events that were already handled
* before, such as JXL_DEC_BASIC_INFO and JXL_DEC_COLOR_ENCODING, since they
* will provide the same information as before.
* @param dec decoder object
*/
JXL_EXPORT void JxlDecoderRewind(JxlDecoder* dec);
/** Makes the decoder skip the next `amount` frames. It still needs to process
* the input, but will not output the frame events. It can be more efficient
* when skipping frames, and even more so when using this after
* JxlDecoderRewind. If the decoder is already processing a frame (could
* have emitted JXL_DEC_FRAME but not yet JXL_DEC_FULL_IMAGE), it starts
* skipping from the next frame. If the amount is larger than the amount of
* frames remaining in the image, all remaining frames are skipped. Calling this
* function multiple times adds the amount to skip to the already existing
* amount.
* A frame here is defined as a frame that without skipping emits events such as
* JXL_DEC_FRAME and JXL_FULL_IMAGE, frames that are internal to the file format
* but are not rendered as part of an animation, or are not the final still
* frame of a still image, are not counted.
* @param dec decoder object
* @param amount the amount of frames to skip
*/
JXL_EXPORT void JxlDecoderSkipFrames(JxlDecoder* dec, size_t amount);
/**
* Get the default pixel format for this decoder.
*
@ -319,6 +392,8 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSubscribeEvents(JxlDecoder* dec,
* By default, this option is disabled, and the decoder automatically corrects
* the orientation.
*
* This function must be called at the beginning, before decoding is performed.
*
* @see JxlBasicInfo for the orientation field, and @see JxlOrientation for the
* possible values.
*
@ -347,7 +422,15 @@ JxlDecoderSetKeepOrientation(JxlDecoder* dec, JXL_BOOL keep_orientation);
* requires more JxlDecoderProcessInput calls to continue.
*
* @param dec decoder object
* @return JXL_DEC_SUCCESS when decoding finished and all events handled.
* @return JXL_DEC_SUCCESS when decoding finished and all events handled. If you
* still have more unprocessed input data anyway, then you can still continue
* by using JxlDecoderSetInput and calling JxlDecoderProcessInput again, similar
* to handling JXL_DEC_NEED_MORE_INPUT. JXL_DEC_SUCCESS can occur instead of
* JXL_DEC_NEED_MORE_INPUT when, for example, the input data ended right at
* the boundary of a box of the container format, all essential codestream boxes
* were already decoded, but extra metadata boxes are still present in the next
* data. JxlDecoderProcessInput cannot return success if all codestream boxes
* have not been seen yet.
* @return JXL_DEC_ERROR when decoding failed, e.g. invalid codestream.
* TODO(lode) document the input data mechanism
* @return JXL_DEC_NEED_MORE_INPUT more input data is necessary.
@ -374,7 +457,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec);
* @param data pointer to next bytes to read from
* @param size amount of bytes available starting from data
* @return JXL_DEC_ERROR if input was already set without releasing,
* JXL_DEC_SUCCESS otherwise
* JXL_DEC_SUCCESS otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetInput(JxlDecoder* dec,
const uint8_t* data,
@ -496,7 +579,7 @@ typedef enum {
* check whether the information is available through the return value.
* @return JXL_DEC_SUCCESS if the data is available and returned,
* JXL_DEC_NEED_MORE_INPUT if not yet available, JXL_DEC_ERROR in case
* the encuded structured color profile does not exist in the codestream.
* the encoded structured color profile does not exist in the codestream.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetColorAsEncodedProfile(
const JxlDecoder* dec, const JxlPixelFormat* format,
@ -550,6 +633,47 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetColorAsICCProfile(
const JxlDecoder* dec, const JxlPixelFormat* format,
JxlColorProfileTarget target, uint8_t* icc_profile, size_t size);
/** Sets the color profile to use for JXL_COLOR_PROFILE_TARGET_DATA for the
* special case when the decoder has a choice. This only has effect for a JXL
* image where uses_original_profile is false, and the original color profile is
* encoded as an ICC color profile rather than a JxlColorEncoding with known
* enum values. In most other cases (uses uses_original_profile is true, or the
* color profile is already given as a JxlColorEncoding), this setting is
* ignored and the decoder uses a profile related to the image.
* No matter what, the JXL_COLOR_PROFILE_TARGET_DATA must still be queried to
* know the actual data format of the decoded pixels after decoding.
*
* The intended use case of this function is for cases where you are using
* a color management system to parse the original ICC color profile
* (JXL_COLOR_PROFILE_TARGET_ORIGINAL), from this you know that the ICC
* profile represents one of the color profiles supported by JxlColorEncoding
* (such as sRGB, PQ or HLG): in that case it is beneficial (but not necessary)
* to use JxlDecoderSetPreferredColorProfile to match the parsed profile. The
* JXL decoder has no color management system built in, but can convert XYB
* color to any of the ones supported by JxlColorEncoding.
*
* Can only be set after the JXL_DEC_COLOR_ENCODING event occurred and before
* any other event occurred, and can affect the result of
* JXL_COLOR_PROFILE_TARGET_DATA (but not of JXL_COLOR_PROFILE_TARGET_ORIGINAL),
* so should be used after getting JXL_COLOR_PROFILE_TARGET_ORIGINAL but before
* getting JXL_COLOR_PROFILE_TARGET_DATA. The color_encoding must be grayscale
* if num_color_channels from the basic info is 1, RGB if num_color_channels
* from the basic info is 3.
*
* If JxlDecoderSetPreferredColorProfile is not used, then for images for which
* uses_original_profile is false and with ICC color profile, the decoder will
* choose linear sRGB for color images, linear grayscale for grayscale images.
* This function only sets a preference, since for other images the decoder has
* no choice what color profile to use, it is determined by the image.
*
* @param dec decoder object
* @param color_encoding the default color encoding to set
* @return JXL_DEC_SUCCESS if the preference was set successfully, JXL_DEC_ERROR
* otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetPreferredColorProfile(
JxlDecoder* dec, const JxlColorEncoding* color_encoding);
/**
* Returns the minimum size in bytes of the preview image output pixel buffer
* for the given format. This is the buffer for JxlDecoderSetPreviewOutBuffer.
@ -598,13 +722,12 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameHeader(const JxlDecoder* dec,
JxlFrameHeader* header);
/**
* Outputs name for the current frame. The buffer
* for name must have at least name_length + 1 bytes allocated, gotten from
* the associated JxlFrameHeader.
* Outputs name for the current frame. The buffer for name must have at least
* name_length + 1 bytes allocated, gotten from the associated JxlFrameHeader.
*
* @param dec decoder object
* @param name buffer to copy the name into
* @param size size of the name buffer in bytes, includig zero termination
* @param size size of the name buffer in bytes, including zero termination
* character, so this must be at least JxlFrameHeader.name_length + 1.
* @return JXL_DEC_SUCCESS if the value is available,
* JXL_DEC_NEED_MORE_INPUT if not yet available, JXL_DEC_ERROR in case
@ -623,8 +746,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderGetFrameName(const JxlDecoder* dec,
* @param size output value, buffer size in bytes
* @return JXL_DEC_SUCCESS on success, JXL_DEC_ERROR on error, such as
* information not available yet.
*
* DEPRECATED: the DC feature in this form will be removed. You can use
* JxlDecoderFlushImage for progressive rendering.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderDCOutBufferSize(
JXL_EXPORT JXL_DEPRECATED JxlDecoderStatus JxlDecoderDCOutBufferSize(
const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size);
/**
@ -641,8 +767,11 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderDCOutBufferSize(
* @param size size of buffer in bytes
* @return JXL_DEC_SUCCESS on success, JXL_DEC_ERROR on error, such as
* size too small.
*
* DEPRECATED: the DC feature in this form will be removed. You can use
* JxlDecoderFlushImage for progressive rendering.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetDCOutBuffer(
JXL_EXPORT JXL_DEPRECATED JxlDecoderStatus JxlDecoderSetDCOutBuffer(
JxlDecoder* dec, const JxlPixelFormat* format, void* buffer, size_t size);
/**
@ -659,40 +788,6 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetDCOutBuffer(
JXL_EXPORT JxlDecoderStatus JxlDecoderImageOutBufferSize(
const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size);
/**
* Sets output buffer for reconstructed JPEG codestream.
*
* The data is owned by the caller
* and may be used by the decoder until JxlDecoderReleaseJPEGBuffer is called or
* the decoder is destroyed or reset so must be kept alive until then.
*
* @param dec decoder object
* @param data pointer to next bytes to write to
* @param size amount of bytes available starting from data
* @return JXL_DEC_ERROR if input was already set without releasing,
* JXL_DEC_SUCCESS otherwise
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec,
uint8_t* data, size_t size);
/**
* Releases buffer which was provided with JxlDecoderSetJPEGBuffer.
*
* Calling JxlDecoderReleaseJPEGBuffer is required whenever
* a buffer is already set and a new buffer needs to be added with
* JxlDecoderSetJPEGBuffer, but is not required before JxlDecoderDestroy or
* JxlDecoderReset.
*
* Calling JxlDecoderReleaseJPEGBuffer when no input is set is
* not an error and returns 0.
*
* @param dec decoder object
* @return the amount of bytes the decoder has not yet written to of the data
* set by JxlDecoderSetJPEGBuffer, or 0 if no buffer is set or
* JxlDecoderReleaseJPEGBuffer was already called.
*/
JXL_EXPORT size_t JxlDecoderReleaseJPEGBuffer(JxlDecoder* dec);
/**
* Sets the buffer to write the full resolution image to. This can be set when
* the JXL_DEC_FRAME event occurs, must be set when the
@ -716,7 +811,7 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderSetImageOutBuffer(
* Callback function type for JxlDecoderSetImageOutCallback. @see
* JxlDecoderSetImageOutCallback for usage.
*
* The callback bay be called simultaneously by different threads when using a
* The callback may be called simultaneously by different threads when using a
* threaded parallel runner, on different pixels.
*
* @param opaque optional user data, as given to JxlDecoderSetImageOutCallback.
@ -744,7 +839,7 @@ typedef void (*JxlImageOutCallback)(void* opaque, size_t x, size_t y,
* data, 1 pixel high, xsize pixels wide, called a scanline. The xsize here is
* not the same as the full image width, the scanline may be a partial section,
* and xsize may differ between calls. The user can then process and/or copy the
* partial scanline to an image buffer. The callback bay be called
* partial scanline to an image buffer. The callback may be called
* simultaneously by different threads when using a threaded parallel runner, on
* different pixels.
*
@ -777,7 +872,192 @@ JXL_EXPORT JxlDecoderStatus
JxlDecoderSetImageOutCallback(JxlDecoder* dec, const JxlPixelFormat* format,
JxlImageOutCallback callback, void* opaque);
/* TODO(lode): add way to output extra channels */
/**
* Returns the minimum size in bytes of an extra channel pixel buffer for the
* given format. This is the buffer for JxlDecoderSetExtraChannelBuffer.
* Requires the basic image information is available in the decoder.
*
* @param dec decoder object
* @param format format of the pixels. The num_channels value is ignored and is
* always treated to be 1.
* @param size output value, buffer size in bytes
* @param index which extra channel to get, matching the index used in @see
* JxlDecoderGetExtraChannelInfo. Must be smaller than num_extra_channels in the
* associated JxlBasicInfo.
* @return JXL_DEC_SUCCESS on success, JXL_DEC_ERROR on error, such as
* information not available yet or invalid index.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderExtraChannelBufferSize(
const JxlDecoder* dec, const JxlPixelFormat* format, size_t* size,
uint32_t index);
/**
* Sets the buffer to write an extra channel to. This can be set when
* the JXL_DEC_FRAME or JXL_DEC_NEED_IMAGE_OUT_BUFFER event occurs, and applies
* only for the current frame. The size of the buffer must be at least as large
* as given by JxlDecoderExtraChannelBufferSize. The buffer follows the format
* described by JxlPixelFormat, but where num_channels is 1. The buffer is owned
* by the caller. The amount of extra channels is given by the
* num_extra_channels field in the associated JxlBasicInfo, and the information
* of individual extra channels can be queried with @see
* JxlDecoderGetExtraChannelInfo. To get multiple extra channels, this function
* must be called multiple times, once for each wanted index. Not all images
* have extra channels. The alpha channel is an extra channel and can be gotten
* as part of the color channels when using an RGBA pixel buffer with
* JxlDecoderSetImageOutBuffer, but additionally also can be gotten separately
* as extra channel. The color channels themselves cannot be gotten this way.
*
*
* @param dec decoder object
* @param format format of the pixels. Object owned by user and its contents
* are copied internally. The num_channels value is ignored and is always
* treated to be 1.
* @param buffer buffer type to output the pixel data to
* @param size size of buffer in bytes
* @param index which extra channel to get, matching the index used in @see
* JxlDecoderGetExtraChannelInfo. Must be smaller than num_extra_channels in the
* associated JxlBasicInfo.
* @return JXL_DEC_SUCCESS on success, JXL_DEC_ERROR on error, such as
* size too small or invalid index.
*/
JXL_EXPORT JxlDecoderStatus
JxlDecoderSetExtraChannelBuffer(JxlDecoder* dec, const JxlPixelFormat* format,
void* buffer, size_t size, uint32_t index);
/**
* Sets output buffer for reconstructed JPEG codestream.
*
* The data is owned by the caller and may be used by the decoder until
* JxlDecoderReleaseJPEGBuffer is called or the decoder is destroyed or reset so
* must be kept alive until then.
*
* If a JPEG buffer was set before and released with
* JxlDecoderReleaseJPEGBuffer, bytes that the decoder has already output should
* not be included, only the remaining bytes output must be set.
*
* @param dec decoder object
* @param data pointer to next bytes to write to
* @param size amount of bytes available starting from data
* @return JXL_DEC_ERROR if output buffer was already set and
* JxlDecoderReleaseJPEGBuffer was not called on it, JXL_DEC_SUCCESS otherwise
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec,
uint8_t* data, size_t size);
/**
* Releases buffer which was provided with JxlDecoderSetJPEGBuffer.
*
* Calling JxlDecoderReleaseJPEGBuffer is required whenever
* a buffer is already set and a new buffer needs to be added with
* JxlDecoderSetJPEGBuffer, but is not required before JxlDecoderDestroy or
* JxlDecoderReset.
*
* Calling JxlDecoderReleaseJPEGBuffer when no buffer is set is
* not an error and returns 0.
*
* @param dec decoder object
* @return the amount of bytes the decoder has not yet written to of the data
* set by JxlDecoderSetJPEGBuffer, or 0 if no buffer is set or
* JxlDecoderReleaseJPEGBuffer was already called.
*/
JXL_EXPORT size_t JxlDecoderReleaseJPEGBuffer(JxlDecoder* dec);
/**
* Sets output buffer for box output codestream.
*
* The data is owned by the caller and may be used by the decoder until
* JxlDecoderReleaseBoxBuffer is called or the decoder is destroyed or reset so
* must be kept alive until then.
*
* If for the current box a box buffer was set before and released with
* JxlDecoderReleaseBoxBuffer, bytes that the decoder has already output should
* not be included, only the remaining bytes output must be set.
*
* @param dec decoder object
* @param data pointer to next bytes to write to
* @param size amount of bytes available starting from data
* @return JXL_DEC_ERROR if output buffer was already set and
* JxlDecoderReleaseBoxBuffer was not called on it, JXL_DEC_SUCCESS otherwise
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetBoxBuffer(JxlDecoder* dec,
uint8_t* data, size_t size);
/**
* Releases buffer which was provided with JxlDecoderSetBoxBuffer.
*
* Calling JxlDecoderReleaseBoxBuffer is required whenever
* a buffer is already set and a new buffer needs to be added with
* JxlDecoderSetBoxBuffer, but is not required before JxlDecoderDestroy or
* JxlDecoderReset.
*
* Calling JxlDecoderReleaseBoxBuffer when no buffer is set is
* not an error and returns 0.
*
* @param dec decoder object
* @return the amount of bytes the decoder has not yet written to of the data
* set by JxlDecoderSetBoxBuffer, or 0 if no buffer is set or
* JxlDecoderReleaseBoxBuffer was already called.
*/
JXL_EXPORT size_t JxlDecoderReleaseBoxBuffer(JxlDecoder* dec);
/**
* Configures whether to get boxes in raw mode or in decompressed mode. In raw
* mode, boxes are output as their bytes appear in the container file, which may
* be decompressed, or compressed if their type is "brob". In decompressed mode,
* "brob" boxes are decompressed with Brotli before outputting them. The size of
* the decompressed stream is not known before the decompression has already
* finished.
*
* The default mode is raw. This setting can only be changed before decoding, or
* directly after a JXL_DEC_BOX event, and is remembered until the decoder is
* reset or destroyed.
*
* Enabling decompressed mode requires Brotli support from the library.
*
* @param dec decoder object
* @param decompress JXL_TRUE to transparently decompress, JXL_FALSE to get
* boxes in raw mode.
* @return JXL_DEC_ERROR if decompressed mode is set and Brotli is not
* available, JXL_DEC_SUCCESS otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderSetDecompressBoxes(JxlDecoder* dec,
JXL_BOOL decompress);
/**
* Outputs the type of the current box, after a JXL_DEC_BOX event occured, as 4
* characters without null termination character. In case of a compressed "brob"
* box, this will return "brob" if the decompressed argument is JXL_FALSE, or
* the underlying box type if the decompressed argument is JXL_TRUE.
*
* @param dec decoder object
* @param type buffer to copy the type into
* @param decompressed which box type to get: JXL_TRUE to get the raw box type,
* which can be "brob", JXL_FALSE, get the underlying box type.
* @return JXL_DEC_SUCCESS if the value is available, JXL_DEC_ERROR if not, for
* example the JXL file does not use the container format.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxType(JxlDecoder* dec,
JxlBoxType* type,
JXL_BOOL decompressed);
/**
* Returns the size of a box as it appears in the container file, after the
* JXL_DEC_BOX event. For a non-compressed box, this is the size of the
* contents, excluding the 4 bytes indicating the box type. For a compressed
* "brob" box, this is the size of the compressed box contents plus the
* additional 4 byte indicating the underlying box type, but excluding the 4
* bytes indicating "brob". This function gives the size of the data that will
* be written in the output buffer when getting boxes in the default raw
* compressed mode. When JxlDecoderSetDecompressBoxes is enabled, the return
* value of function does not change, and the decompressed size is not known
* before it has already been decompressed and output.
*
* @param dec decoder object
* @param size raw size of the box in bytes
* @return JXL_DEC_ERROR if no box size is available, JXL_DEC_SUCCESS otherwise.
*/
JXL_EXPORT JxlDecoderStatus JxlDecoderGetBoxSizeRaw(const JxlDecoder* dec,
uint64_t* size);
/**
* Outputs progressive step towards the decoded image so far when only partial
@ -785,8 +1065,8 @@ JxlDecoderSetImageOutCallback(JxlDecoder* dec, const JxlPixelFormat* format,
* JxlDecoderSetImageOutBuffer will contain partial image data.
*
* Can be called when JxlDecoderProcessInput returns JXL_DEC_NEED_MORE_INPUT,
* after the JXL_DEC_FRAME event already occured and before the
* JXL_DEC_FULL_IMAGE event occured for a frame.
* after the JXL_DEC_FRAME event already occurred and before the
* JXL_DEC_FULL_IMAGE event occurred for a frame.
*
* @param dec decoder object
* @return JXL_DEC_SUCCESS if image data was flushed to the output buffer, or
@ -802,3 +1082,5 @@ JXL_EXPORT JxlDecoderStatus JxlDecoderFlushImage(JxlDecoder* dec);
#endif
#endif /* JXL_DECODE_H_ */
/** @}*/

View file

@ -3,6 +3,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/// @addtogroup libjxl_decoder
/// @{
///
/// @file decode_cxx.h
/// @brief C++ header-only helper for @ref decode.h.
///
@ -50,3 +53,5 @@ static inline JxlDecoderPtr JxlDecoderMake(
}
#endif // JXL_DECODE_CXX_H_
/// @}

View file

@ -4,7 +4,9 @@
* license that can be found in the LICENSE file.
*/
/** @file encode.h
/** @addtogroup libjxl_encoder
* @{
* @file encode.h
* @brief Encoding API for JPEG XL.
*/
@ -142,6 +144,14 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderProcessOutput(JxlEncoder* enc,
/**
* Sets the buffer to read JPEG encoded bytes from for the next frame to encode.
*
* If JxlEncoderSetBasicInfo has not yet been called, calling
* JxlEncoderAddJPEGFrame will implicitly call it with the parameters of the
* added JPEG frame.
*
* If JxlEncoderSetColorEncoding or JxlEncoderSetICCProfile has not yet been
* called, calling JxlEncoderAddJPEGFrame will implicitly call it with the
* parameters of the added JPEG frame.
*
* If the encoder is set to store JPEG reconstruction metadata using @ref
* JxlEncoderStoreJPEGMetadata and a single JPEG frame is added, it will be
* possible to losslessly reconstruct the JPEG codestream.
@ -157,19 +167,20 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderAddJPEGFrame(
/**
* Sets the buffer to read pixels from for the next image to encode. Must call
* JxlEncoderSetDimensions before JxlEncoderAddImageFrame.
* JxlEncoderSetBasicInfo before JxlEncoderAddImageFrame.
*
* Currently only some pixel formats are supported:
* - JXL_TYPE_UINT8
* - JXL_TYPE_UINT16
* - JXL_TYPE_FLOAT16, with nominal range 0..1
* - JXL_TYPE_FLOAT, with nominal range 0..1
*
* The color profile of the pixels depends on the value of uses_original_profile
* in the JxlBasicInfo. If true, the pixels are assumed to be encoded in the
* original profile that is set with JxlEncoderSetColorEncoding or
* JxlEncoderSetICCProfile. If false, the pixels are assumed to be nonlinear
* sRGB for integer data types (JXL_TYPE_UINT8 and JXL_TYPE_UINT16), and linear
* sRGB for floating point data types (JXL_TYPE_FLOAT).
* sRGB for integer data types (JXL_TYPE_UINT8, JXL_TYPE_UINT16), and linear
* sRGB for floating point data types (JXL_TYPE_FLOAT16, JXL_TYPE_FLOAT).
*
* @param options set of encoder options to use when encoding the frame.
* @param pixel_format format for pixels. Object owned by the caller and its
@ -225,6 +236,17 @@ JXL_EXPORT JxlEncoderStatus JxlEncoderSetICCProfile(JxlEncoder* enc,
const uint8_t* icc_profile,
size_t size);
/**
* Initializes a JxlBasicInfo struct to default values.
* For forwards-compatibility, this function has to be called before values
* are assigned to the struct fields.
* The default values correspond to an 8-bit RGB image, no alpha or any
* other extra channels.
*
* @param info global image metadata. Object owned by the caller.
*/
JXL_EXPORT void JxlEncoderInitBasicInfo(JxlBasicInfo* info);
/**
* Sets the global metadata of the image encoded by this encoder.
*
@ -298,8 +320,9 @@ JxlEncoderOptionsSetDecodingSpeed(JxlEncoderOptions* options, int tier);
/**
* Sets encoder effort/speed level without affecting decoding speed. Valid
* values are, from faster to slower speed: 3:falcon 4:cheetah 5:hare 6:wombat
* 7:squirrel 8:kitten 9:tortoise Default: squirrel (7).
* values are, from faster to slower speed: 1:lightning 2:thunder 3:falcon
* 4:cheetah 5:hare 6:wombat 7:squirrel 8:kitten 9:tortoise.
* Default: squirrel (7).
*
* @param options set of encoder options to update with the new mode.
* @param effort the effort value to set.
@ -369,3 +392,5 @@ JXL_EXPORT void JxlColorEncodingSetToLinearSRGB(
#endif
#endif /* JXL_ENCODE_H_ */
/** @}*/

View file

@ -3,6 +3,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/// @addtogroup libjxl_encoder
///@{
///
/// @file encode_cxx.h
/// @brief C++ header-only helper for @ref encode.h.
///
@ -50,3 +53,5 @@ static inline JxlEncoderPtr JxlEncoderMake(
}
#endif // JXL_ENCODE_CXX_H_
/// @}

View file

@ -4,7 +4,9 @@
* license that can be found in the LICENSE file.
*/
/** @file memory_manager.h
/** @addtogroup libjxl_common
* @{
* @file memory_manager.h
* @brief Abstraction functions used by JPEG XL to allocate memory.
*/
@ -65,3 +67,5 @@ typedef struct JxlMemoryManagerStruct {
#endif
#endif /* JXL_MEMORY_MANAGER_H_ */
/** @}*/

View file

@ -4,6 +4,9 @@
* license that can be found in the LICENSE file.
*/
/** @addtogroup libjxl_common
* @{
*/
/**
* @file parallel_runner.h
*/
@ -149,3 +152,5 @@ typedef JxlParallelRetCode (*JxlParallelRunner)(
#endif
#endif /* JXL_PARALLEL_RUNNER_H_ */
/** @}*/

View file

@ -0,0 +1,79 @@
/* Copyright (c) the JPEG XL Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
/** @addtogroup libjxl_threads
* @{
* @file resizable_parallel_runner.h
* @brief implementation using std::thread of a resizeable ::JxlParallelRunner.
*/
/** Implementation of JxlParallelRunner than can be used to enable
* multithreading when using the JPEG XL library. This uses std::thread
* internally and related synchronization functions. The number of threads
* created can be changed after creation of the thread pool; the threads
* (including the main thread) are re-used for every
* ResizableParallelRunner::Runner call. Only one concurrent
* JxlResizableParallelRunner call per instance is allowed at a time.
*
* This is a scalable, lower-overhead thread pool runner, especially suitable
* for data-parallel computations in the fork-join model, where clients need to
* know when all tasks have completed.
*
* Compared to the implementation in @ref thread_parallel_runner.h, this
* implementation is tuned for execution on lower-powered systems, including
* for example ARM CPUs with big.LITTLE computation models.
*/
#ifndef JXL_RESIZABLE_PARALLEL_RUNNER_H_
#define JXL_RESIZABLE_PARALLEL_RUNNER_H_
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "jxl/jxl_threads_export.h"
#include "jxl/memory_manager.h"
#include "jxl/parallel_runner.h"
#if defined(__cplusplus) || defined(c_plusplus)
extern "C" {
#endif
/** Parallel runner internally using std::thread. Use as JxlParallelRunner.
*/
JXL_THREADS_EXPORT JxlParallelRetCode JxlResizableParallelRunner(
void* runner_opaque, void* jpegxl_opaque, JxlParallelRunInit init,
JxlParallelRunFunction func, uint32_t start_range, uint32_t end_range);
/** Creates the runner for JxlResizableParallelRunner. Use as the opaque
* runner. The runner will execute tasks on the calling thread until
* @ref JxlResizableParallelRunnerSetThreads is called.
*/
JXL_THREADS_EXPORT void* JxlResizableParallelRunnerCreate(
const JxlMemoryManager* memory_manager);
/** Changes the number of threads for JxlResizableParallelRunner.
*/
JXL_THREADS_EXPORT void JxlResizableParallelRunnerSetThreads(
void* runner_opaque, size_t num_threads);
/** Suggests a number of threads to use for an image of given size.
*/
JXL_THREADS_EXPORT uint32_t
JxlResizableParallelRunnerSuggestThreads(uint64_t xsize, uint64_t ysize);
/** Destroys the runner created by JxlResizableParallelRunnerCreate.
*/
JXL_THREADS_EXPORT void JxlResizableParallelRunnerDestroy(void* runner_opaque);
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* JXL_RESIZABLE_PARALLEL_RUNNER_H_ */
/** @}*/

View file

@ -0,0 +1,64 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/// @addtogroup libjxl_threads
/// @{
///
/// @file resizable_parallel_runner_cxx.h
/// @ingroup libjxl_threads
/// @brief C++ header-only helper for @ref resizable_parallel_runner.h.
///
/// There's no binary library associated with the header since this is a header
/// only library.
#ifndef JXL_RESIZABLE_PARALLEL_RUNNER_CXX_H_
#define JXL_RESIZABLE_PARALLEL_RUNNER_CXX_H_
#include <memory>
#include "jxl/resizable_parallel_runner.h"
#if !(defined(__cplusplus) || defined(c_plusplus))
#error \
"This a C++ only header. Use jxl/jxl_resizable_parallel_runner.h from C" \
"sources."
#endif
/// Struct to call JxlResizableParallelRunnerDestroy from the
/// JxlResizableParallelRunnerPtr unique_ptr.
struct JxlResizableParallelRunnerDestroyStruct {
/// Calls @ref JxlResizableParallelRunnerDestroy() on the passed runner.
void operator()(void* runner) { JxlResizableParallelRunnerDestroy(runner); }
};
/// std::unique_ptr<> type that calls JxlResizableParallelRunnerDestroy() when
/// releasing the runner.
///
/// Use this helper type from C++ sources to ensure the runner is destroyed and
/// their internal resources released.
typedef std::unique_ptr<void, JxlResizableParallelRunnerDestroyStruct>
JxlResizableParallelRunnerPtr;
/// Creates an instance of JxlResizableParallelRunner into a
/// JxlResizableParallelRunnerPtr and initializes it.
///
/// This function returns a unique_ptr that will call
/// JxlResizableParallelRunnerDestroy() when releasing the pointer. See @ref
/// JxlResizableParallelRunnerCreate for details on the instance creation.
///
/// @param memory_manager custom allocator function. It may be NULL. The memory
/// manager will be copied internally.
/// @return a @c NULL JxlResizableParallelRunnerPtr if the instance can not be
/// allocated or initialized
/// @return initialized JxlResizableParallelRunnerPtr instance otherwise.
static inline JxlResizableParallelRunnerPtr JxlResizableParallelRunnerMake(
const JxlMemoryManager* memory_manager) {
return JxlResizableParallelRunnerPtr(
JxlResizableParallelRunnerCreate(memory_manager));
}
#endif // JXL_RESIZABLE_PARALLEL_RUNNER_CXX_H_
/// @}

View file

@ -4,7 +4,9 @@
* license that can be found in the LICENSE file.
*/
/** @file thread_parallel_runner.h
/** @addtogroup libjxl_threads
* @{
* @file thread_parallel_runner.h
* @brief implementation using std::thread of a ::JxlParallelRunner.
*/
@ -67,3 +69,5 @@ JXL_THREADS_EXPORT size_t JxlThreadParallelRunnerDefaultNumWorkerThreads();
#endif
#endif /* JXL_THREAD_PARALLEL_RUNNER_H_ */
/** @}*/

View file

@ -3,6 +3,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/// @addtogroup libjxl_threads
/// @{
///
/// @file thread_parallel_runner_cxx.h
/// @brief C++ header-only helper for @ref thread_parallel_runner.h.
///
@ -57,3 +60,5 @@ static inline JxlThreadParallelRunnerPtr JxlThreadParallelRunnerMake(
}
#endif // JXL_THREAD_PARALLEL_RUNNER_CXX_H_
/// @}

View file

@ -4,7 +4,9 @@
* license that can be found in the LICENSE file.
*/
/** @file types.h
/** @addtogroup libjxl_common
* @{
* @file types.h
* @brief Data types for the JPEG XL API, for both encoding and decoding.
*/
@ -84,7 +86,7 @@ typedef enum {
*/
typedef struct {
/** Amount of channels available in a pixel buffer.
* 1: single-channel data, e.g. grayscale
* 1: single-channel data, e.g. grayscale or a single extra channel
* 2: single-channel + alpha
* 3: trichromatic, e.g. RGB
* 4: trichromatic + alpha
@ -109,8 +111,14 @@ typedef struct {
size_t align;
} JxlPixelFormat;
/** Data type holding the 4-character type name of an ISOBMFF box.
*/
typedef char JxlBoxType[4];
#if defined(__cplusplus) || defined(c_plusplus)
}
#endif
#endif /* JXL_TYPES_H_ */
/** @}*/

View file

@ -43,8 +43,6 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/base/status.cc
jxl/base/status.h
jxl/base/thread_pool_internal.h
jxl/base/time.cc
jxl/base/time.h
jxl/blending.cc
jxl/blending.h
jxl/chroma_from_luma.cc
@ -71,6 +69,7 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/dec_ans.cc
jxl/dec_ans.h
jxl/dec_bit_reader.h
jxl/dec_cache.cc
jxl/dec_cache.h
jxl/dec_context_map.cc
jxl/dec_context_map.h
@ -93,6 +92,7 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/dec_patch_dictionary.h
jxl/dec_reconstruct.cc
jxl/dec_reconstruct.h
jxl/dec_render_pipeline.h
jxl/dec_transforms-inl.h
jxl/dec_upsample.cc
jxl/dec_upsample.h
@ -100,6 +100,8 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/dec_xyb.cc
jxl/dec_xyb.h
jxl/decode.cc
jxl/decode_to_jpeg.cc
jxl/decode_to_jpeg.h
jxl/enc_bit_writer.cc
jxl/enc_bit_writer.h
jxl/entropy_coder.cc
@ -158,10 +160,11 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/modular/modular_image.cc
jxl/modular/modular_image.h
jxl/modular/options.h
jxl/modular/transform/near-lossless.h
jxl/modular/transform/palette.h
jxl/modular/transform/rct.cc
jxl/modular/transform/rct.h
jxl/modular/transform/squeeze.cc
jxl/modular/transform/squeeze.h
jxl/modular/transform/subtractgreen.h
jxl/modular/transform/transform.cc
jxl/modular/transform/transform.h
jxl/noise.h
@ -177,6 +180,7 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
jxl/quantizer.cc
jxl/quantizer.h
jxl/rational_polynomial-inl.h
jxl/sanitizers.h
jxl/splines.cc
jxl/splines.h
jxl/toc.cc
@ -245,6 +249,7 @@ set(JPEGXL_INTERNAL_SOURCES_ENC
jxl/enc_icc_codec.h
jxl/enc_image_bundle.cc
jxl/enc_image_bundle.h
jxl/enc_jxl_skcms.h
jxl/enc_modular.cc
jxl/enc_modular.h
jxl/enc_noise.cc
@ -252,6 +257,8 @@ set(JPEGXL_INTERNAL_SOURCES_ENC
jxl/enc_params.h
jxl/enc_patch_dictionary.cc
jxl/enc_patch_dictionary.h
jxl/enc_photon_noise.cc
jxl/enc_photon_noise.h
jxl/enc_quant_weights.cc
jxl/enc_quant_weights.h
jxl/enc_splines.cc
@ -276,10 +283,20 @@ set(JPEGXL_INTERNAL_SOURCES_ENC
jxl/jpeg/enc_jpeg_huffman_decode.cc
jxl/jpeg/enc_jpeg_huffman_decode.h
jxl/linalg.cc
jxl/modular/encoding/enc_debug_tree.cc
jxl/modular/encoding/enc_debug_tree.h
jxl/modular/encoding/enc_encoding.cc
jxl/modular/encoding/enc_encoding.h
jxl/modular/encoding/enc_ma.cc
jxl/modular/encoding/enc_ma.h
jxl/modular/transform/enc_palette.cc
jxl/modular/transform/enc_palette.h
jxl/modular/transform/enc_rct.cc
jxl/modular/transform/enc_rct.h
jxl/modular/transform/enc_squeeze.cc
jxl/modular/transform/enc_squeeze.h
jxl/modular/transform/enc_transform.cc
jxl/modular/transform/enc_transform.h
jxl/optimize.cc
jxl/optimize.h
jxl/progressive_split.cc
@ -313,11 +330,20 @@ endfunction()
if (JPEGXL_ENABLE_SKCMS)
list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_SKCMS=1)
list(APPEND JPEGXL_INTERNAL_LIBS skcms)
if (JPEGXL_BUNDLE_SKCMS)
list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_BUNDLE_SKCMS=1)
# skcms objects are later added to JPEGXL_INTERNAL_OBJECTS
else ()
list(APPEND JPEGXL_INTERNAL_LIBS skcms)
endif ()
else ()
list(APPEND JPEGXL_INTERNAL_LIBS lcms2)
endif ()
if (NOT JPEGXL_ENABLE_TRANSCODE_JPEG)
list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_TRANSCODE_JPEG=0)
endif ()
set(OBJ_COMPILE_DEFINITIONS
JPEGXL_MAJOR_VERSION=${JPEGXL_MAJOR_VERSION}
JPEGXL_MINOR_VERSION=${JPEGXL_MINOR_VERSION}
@ -369,23 +395,14 @@ if (JPEGXL_ENABLE_SKCMS)
target_include_directories(jxl_enc-obj PRIVATE
$<TARGET_PROPERTY:skcms,INCLUDE_DIRECTORIES>
)
target_include_directories(jxl_dec-obj PRIVATE
$<TARGET_PROPERTY:skcms,INCLUDE_DIRECTORIES>
)
else ()
target_include_directories(jxl_enc-obj PRIVATE
$<TARGET_PROPERTY:lcms2,INCLUDE_DIRECTORIES>
)
target_include_directories(jxl_dec-obj PRIVATE
$<TARGET_PROPERTY:lcms2,INCLUDE_DIRECTORIES>
)
endif ()
# Headers for exporting/importing public headers
include(GenerateExportHeader)
# TODO(deymo): Add these visibility properties to the static dependencies of
# jxl_{dec,enc}-obj since those are currently compiled with the default
# visibility.
set_target_properties(jxl_dec-obj PROPERTIES
CXX_VISIBILITY_PRESET hidden
VISIBILITY_INLINES_HIDDEN 1
@ -407,8 +424,6 @@ target_include_directories(jxl_enc-obj PUBLIC
# Private static library. This exposes all the internal functions and is used
# for tests.
# TODO(lode): this library is missing symbols, more encoder-only code needs to
# be moved to JPEGXL_INTERNAL_SOURCES_ENC before this works
add_library(jxl_dec-static STATIC
$<TARGET_OBJECTS:jxl_dec-obj>
)
@ -419,14 +434,20 @@ target_include_directories(jxl_dec-static PUBLIC
"${CMAKE_CURRENT_SOURCE_DIR}/include"
"${CMAKE_CURRENT_BINARY_DIR}/include")
# The list of objects in the static and shared libraries.
set(JPEGXL_INTERNAL_OBJECTS
$<TARGET_OBJECTS:jxl_enc-obj>
$<TARGET_OBJECTS:jxl_dec-obj>
)
if (JPEGXL_ENABLE_SKCMS AND JPEGXL_BUNDLE_SKCMS)
list(APPEND JPEGXL_INTERNAL_OBJECTS $<TARGET_OBJECTS:skcms-obj>)
endif()
# Private static library. This exposes all the internal functions and is used
# for tests.
# TODO(lode): once the source files are correctly split so that it is possible
# to do, remove $<TARGET_OBJECTS:jxl_dec-obj> here and depend on jxl_dec-static
add_library(jxl-static STATIC
$<TARGET_OBJECTS:jxl_enc-obj>
$<TARGET_OBJECTS:jxl_dec-obj>
)
add_library(jxl-static STATIC ${JPEGXL_INTERNAL_OBJECTS})
target_link_libraries(jxl-static
PUBLIC ${JPEGXL_COVERAGE_FLAGS} ${JPEGXL_INTERNAL_LIBS} hwy)
target_include_directories(jxl-static PUBLIC
@ -473,12 +494,10 @@ install(TARGETS jxl-static DESTINATION ${CMAKE_INSTALL_LIBDIR})
install(TARGETS jxl_dec-static DESTINATION ${CMAKE_INSTALL_LIBDIR})
if (((NOT DEFINED "${TARGET_SUPPORTS_SHARED_LIBS}") OR
TARGET_SUPPORTS_SHARED_LIBS) AND NOT JPEGXL_STATIC)
TARGET_SUPPORTS_SHARED_LIBS) AND NOT JPEGXL_STATIC AND BUILD_SHARED_LIBS)
# Public shared library.
add_library(jxl SHARED
$<TARGET_OBJECTS:jxl_dec-obj>
$<TARGET_OBJECTS:jxl_enc-obj>)
add_library(jxl SHARED ${JPEGXL_INTERNAL_OBJECTS})
strip_static(JPEGXL_INTERNAL_SHARED_LIBS JPEGXL_INTERNAL_LIBS)
target_link_libraries(jxl PUBLIC ${JPEGXL_COVERAGE_FLAGS})
target_link_libraries(jxl PRIVATE ${JPEGXL_INTERNAL_SHARED_LIBS})
@ -507,6 +526,13 @@ set_target_properties(jxl_dec PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
# Check whether the linker support excluding libs
set(LINKER_EXCLUDE_LIBS_FLAG "-Wl,--exclude-libs=ALL")
include(CheckCSourceCompiles)
list(APPEND CMAKE_EXE_LINKER_FLAGS ${LINKER_EXCLUDE_LIBS_FLAG})
check_c_source_compiles("int main(){return 0;}" LINKER_SUPPORT_EXCLUDE_LIBS)
list(REMOVE_ITEM CMAKE_EXE_LINKER_FLAGS ${LINKER_EXCLUDE_LIBS_FLAG})
# Add a jxl.version file as a version script to tag symbols with the
# appropriate version number. This script is also used to limit what's exposed
# in the shared library from the static dependencies bundled here.
@ -522,6 +548,13 @@ foreach(target IN ITEMS jxl jxl_dec)
set_property(TARGET ${target} APPEND_STRING PROPERTY
LINK_FLAGS " -Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/jxl/jxl.version")
endif() # APPLE
# This hides the default visibility symbols from static libraries bundled into
# the shared library. In particular this prevents exposing symbols from hwy
# and skcms in the shared library.
if(${LINKER_SUPPORT_EXCLUDE_LIBS})
set_property(TARGET ${target} APPEND_STRING PROPERTY
LINK_FLAGS " ${LINKER_EXCLUDE_LIBS_FLAG}")
endif()
endforeach()
# Only install libjxl shared library. The libjxl_dec is not installed since it
@ -529,16 +562,19 @@ endforeach()
# both.
install(TARGETS jxl
DESTINATION ${CMAKE_INSTALL_LIBDIR})
else()
add_library(jxl ALIAS jxl-static)
add_library(jxl_dec ALIAS jxl_dec-static)
endif() # TARGET_SUPPORTS_SHARED_LIBS AND NOT JPEGXL_STATIC AND
# BUILD_SHARED_LIBS
# Add a pkg-config file for libjxl.
set(JPEGXL_LIBRARY_REQUIRES
"libhwy libbrotlicommon libbrotlienc libbrotlidec")
if(NOT JPEGXL_ENABLE_SKCMS)
set(JPEGXL_LIBRARY_REQUIRES "${JPEGXL_LIBRARY_REQUIRES} lcms2")
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/jxl/libjxl.pc.in"
"libjxl.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libjxl.pc"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
else()
add_library(jxl ALIAS jxl-static)
add_library(jxl_dec ALIAS jxl_dec-static)
endif() # TARGET_SUPPORTS_SHARED_LIBS AND NOT JPEGXL_STATIC

View file

@ -134,10 +134,10 @@ void EnsureUnchanged(const float background, const float foreground,
jxl::PassesDecoderState state;
JXL_CHECK(
jxl::InitializePassesSharedState(frame_header, &state.shared_storage));
state.Init();
JXL_CHECK(state.Init());
state.InitForAC(/*pool=*/nullptr);
state.filter_weights.Init(lf, frame_dim);
JXL_CHECK(state.filter_weights.Init(lf, frame_dim));
FillImage(-0.5f, &state.filter_weights.sigma);
for (size_t idx_image = 0; idx_image < images.size(); ++idx_image) {
@ -152,7 +152,7 @@ void EnsureUnchanged(const float background, const float foreground,
// input image.
JXL_CHECK(FinalizeFrameDecoding(&out, &state, /*pool=*/nullptr,
/*force_fir=*/true,
/*skip_blending=*/true));
/*skip_blending=*/true, /*move_ec=*/true));
#if JXL_HIGH_PRECISION
VerifyRelativeError(in, *out.color(), 1E-3, 1E-4);

View file

@ -79,12 +79,12 @@ struct AliasTable {
// Dividing `value` by `entry_size` determines `i`, the entry which is
// responsible for the input. If the remainder is below `cutoff`, then the
// mapped symbol is `i`; since `offsets[0]` stores the number of occurences of
// `i` "before" the start of this entry, the offset of the input will be
// mapped symbol is `i`; since `offsets[0]` stores the number of occurrences
// of `i` "before" the start of this entry, the offset of the input will be
// `offsets[0] + remainder`. If the remainder is above cutoff, the mapped
// symbol is `right_value`; since `offsets[1]` stores the number of occurences
// of `right_value` "before" this entry, minus the `cutoff` value, the input
// offset is then `remainder + offsets[1]`.
// symbol is `right_value`; since `offsets[1]` stores the number of
// occurrences of `right_value` "before" this entry, minus the `cutoff` value,
// the input offset is then `remainder + offsets[1]`.
static JXL_INLINE Symbol Lookup(const Entry* JXL_RESTRICT table, size_t value,
size_t log_entry_size,
size_t entry_size_minus_1) {

View file

@ -25,7 +25,7 @@ void VerifyAliasDistribution(const std::vector<int>& distribution,
offsets[s.value].push_back(s.offset);
}
for (uint32_t i = 0; i < distribution.size(); i++) {
ASSERT_EQ(distribution[i], offsets[i].size());
ASSERT_EQ(static_cast<size_t>(distribution[i]), offsets[i].size());
std::sort(offsets[i].begin(), offsets[i].end());
for (uint32_t j = 0; j < offsets[i].size(); j++) {
ASSERT_EQ(offsets[i][j], j);

View file

@ -111,7 +111,7 @@ void RoundtripRandomUnbalancedStream(int alphabet_size) {
constexpr int kNumHistograms = 3;
constexpr int kPrecision = 1 << 10;
std::mt19937_64 rng;
for (int i = 0; i < 100; i++) {
for (size_t i = 0; i < kReps; i++) {
std::vector<int> distributions[kNumHistograms];
for (int j = 0; j < kNumHistograms; j++) {
distributions[j].resize(kPrecision);

View file

@ -148,8 +148,7 @@ struct AuxOut {
layers[i].Assimilate(victim.layers[i]);
}
num_blocks += victim.num_blocks;
num_dct2_blocks += victim.num_dct2_blocks;
num_dct4_blocks += victim.num_dct4_blocks;
num_small_blocks += victim.num_small_blocks;
num_dct4x8_blocks += victim.num_dct4x8_blocks;
num_afv_blocks += victim.num_afv_blocks;
num_dct8_blocks += victim.num_dct8_blocks;
@ -158,6 +157,8 @@ struct AuxOut {
num_dct16_blocks += victim.num_dct16_blocks;
num_dct16x32_blocks += victim.num_dct16x32_blocks;
num_dct32_blocks += victim.num_dct32_blocks;
num_dct32x64_blocks += victim.num_dct32x64_blocks;
num_dct64_blocks += victim.num_dct64_blocks;
num_butteraugli_iters += victim.num_butteraugli_iters;
for (size_t i = 0; i < dc_pred_usage.size(); ++i) {
dc_pred_usage[i] += victim.dc_pred_usage[i];
@ -270,8 +271,7 @@ struct AuxOut {
size_t num_blocks = 0;
// Number of blocks that use larger DCT (set by ac_strategy).
size_t num_dct2_blocks = 0;
size_t num_dct4_blocks = 0;
size_t num_small_blocks = 0;
size_t num_dct4x8_blocks = 0;
size_t num_afv_blocks = 0;
size_t num_dct8_blocks = 0;
@ -280,9 +280,11 @@ struct AuxOut {
size_t num_dct16_blocks = 0;
size_t num_dct16x32_blocks = 0;
size_t num_dct32_blocks = 0;
size_t num_dct32x64_blocks = 0;
size_t num_dct64_blocks = 0;
std::array<uint32_t, 8> dc_pred_usage = {0};
std::array<uint32_t, 8> dc_pred_usage_xb = {0};
std::array<uint32_t, 8> dc_pred_usage = {{0}};
std::array<uint32_t, 8> dc_pred_usage_xb = {{0}};
int num_butteraugli_iters = 0;

View file

@ -46,9 +46,9 @@ constexpr size_t CacheAligned::kAlignment;
constexpr size_t CacheAligned::kAlias;
void CacheAligned::PrintStats() {
printf("Allocations: %zu (max bytes in use: %E)\n",
size_t(num_allocations.load(std::memory_order_relaxed)),
double(max_bytes_in_use.load(std::memory_order_relaxed)));
fprintf(stderr, "Allocations: %zu (max bytes in use: %E)\n",
size_t(num_allocations.load(std::memory_order_relaxed)),
double(max_bytes_in_use.load(std::memory_order_relaxed)));
}
size_t CacheAligned::NextOffset() {

View file

@ -129,6 +129,16 @@
#define JXL_MUST_USE_RESULT
#endif
// Disable certain -fsanitize flags for functions that are expected to include
// things like unsigned integer overflow. For example use in the function
// declaration JXL_NO_SANITIZE("unsigned-integer-overflow") to silence unsigned
// integer overflow ubsan messages.
#if JXL_COMPILER_CLANG && JXL_HAVE_ATTRIBUTE(no_sanitize)
#define JXL_NO_SANITIZE(X) __attribute__((no_sanitize(X)))
#else
#define JXL_NO_SANITIZE(X)
#endif
#if JXL_HAVE_ATTRIBUTE(__format__)
#define JXL_FORMAT(idx_fmt, idx_arg) \
__attribute__((__format__(__printf__, idx_fmt, idx_arg)))

View file

@ -5,14 +5,6 @@
#include "lib/jxl/base/data_parallel.h"
#define DATA_PARALLEL_TRACE 0
#if DATA_PARALLEL_TRACE
#include <stdlib.h>
#include "lib/jxl/base/time.h"
#endif // DATA_PARALLEL_TRACE
namespace jxl {
// static
@ -28,16 +20,4 @@ JxlParallelRetCode ThreadPool::SequentialRunnerStatic(
return 0;
}
#if DATA_PARALLEL_TRACE
void TraceRunBegin(const char* /*caller*/, double* t0) { *t0 = Now(); }
void TraceRunEnd(const char* caller, double t0) {
const double elapsed = Now() - t0;
fprintf(stderr, "%27s: %5.1f ms\n", caller, elapsed * 1E3);
}
#else
void TraceRunBegin(const char* /*caller*/, double* /*t0*/) {}
void TraceRunEnd(const char* /*caller*/, double /*t0*/) {}
#endif
} // namespace jxl

View file

@ -104,58 +104,21 @@ class ThreadPool {
void* const runner_opaque_;
};
void TraceRunBegin(const char* caller, double* t0);
void TraceRunEnd(const char* caller, double t0);
// TODO(deymo): Convert the return value to a Status when not using SkipInit.
template <class InitFunc, class DataFunc>
bool RunOnPool(ThreadPool* pool, const uint32_t begin, const uint32_t end,
const InitFunc& init_func, const DataFunc& data_func,
const char* caller) {
Status ret = true;
double t0;
TraceRunBegin(caller, &t0);
if (pool == nullptr) {
ThreadPool default_pool(nullptr, nullptr);
ret = default_pool.Run(begin, end, init_func, data_func, caller);
} else {
ret = pool->Run(begin, end, init_func, data_func, caller);
}
TraceRunEnd(caller, t0);
return ret;
}
// Accelerates multiple unsigned 32-bit divisions with the same divisor by
// precomputing a multiplier. This is useful for splitting a contiguous range of
// indices (the task index) into 2D indices. Exhaustively tested on dividends
// up to 4M with non-power of two divisors up to 2K.
class Divider {
public:
// "d" is the divisor (what to divide by).
explicit Divider(const uint32_t d) : shift_(FloorLog2Nonzero(d)) {
// Power of two divisors (including 1) are not supported because it is more
// efficient to special-case them at a higher level.
JXL_ASSERT((d & (d - 1)) != 0);
// ceil_log2 = floor_log2 + 1 because we ruled out powers of two above.
const uint64_t next_pow2 = 1ULL << (shift_ + 1);
mul_ = ((next_pow2 - d) << 32) / d + 1;
}
// "n" is the numerator (what is being divided).
inline uint32_t operator()(const uint32_t n) const {
// Algorithm from "Division by Invariant Integers using Multiplication".
// Its "sh1" is hardcoded to 1 because we don't need to handle d=1.
const uint32_t hi = (uint64_t(mul_) * n) >> 32;
return (hi + ((n - hi) >> 1)) >> shift_;
}
private:
uint32_t mul_;
const int shift_;
};
} // namespace jxl
#endif // LIB_JXL_BASE_DATA_PARALLEL_H_

View file

@ -11,7 +11,9 @@
#include <stdio.h>
#include <sys/stat.h>
#include <list>
#include <string>
#include <vector>
#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/padded_bytes.h"
@ -35,10 +37,25 @@ class FileWrapper {
FileWrapper& operator=(const FileWrapper& other) = delete;
explicit FileWrapper(const std::string& pathname, const char* mode)
: file_(fopen(pathname.c_str(), mode)) {}
: file_(pathname == "-" ? (mode[0] == 'r' ? stdin : stdout)
: fopen(pathname.c_str(), mode)),
close_on_delete_(pathname != "-") {
#ifdef _WIN32
struct __stat64 s = {};
const int err = _stat64(pathname.c_str(), &s);
const bool is_file = (s.st_mode & S_IFREG) != 0;
#else
struct stat s = {};
const int err = stat(pathname.c_str(), &s);
const bool is_file = S_ISREG(s.st_mode);
#endif
if (err == 0 && is_file) {
size_ = s.st_size;
}
}
~FileWrapper() {
if (file_ != nullptr) {
if (file_ != nullptr && close_on_delete_) {
const int err = fclose(file_);
JXL_CHECK(err == 0);
}
@ -48,8 +65,12 @@ class FileWrapper {
// NOLINTNEXTLINE(google-explicit-constructor)
operator FILE*() const { return file_; }
int64_t size() { return size_; }
private:
FILE* const file_;
bool close_on_delete_ = true;
int64_t size_ = -1;
};
template <typename ContainerType>
@ -58,34 +79,52 @@ static inline Status ReadFile(const std::string& pathname,
FileWrapper f(pathname, "rb");
if (f == nullptr) return JXL_FAILURE("Failed to open file for reading");
// Ensure it is a regular file
#ifdef _WIN32
struct __stat64 s = {};
const int err = _stat64(pathname.c_str(), &s);
const bool is_file = (s.st_mode & S_IFREG) != 0;
#else
struct stat s = {};
const int err = stat(pathname.c_str(), &s);
const bool is_file = S_ISREG(s.st_mode);
#endif
if (err != 0) return JXL_FAILURE("Failed to obtain file status");
if (!is_file) return JXL_FAILURE("Not a file");
// Get size of file in bytes
const int64_t size = s.st_size;
if (size <= 0) return JXL_FAILURE("Empty or invalid file size");
bytes->resize(static_cast<size_t>(size));
const int64_t size = f.size();
if (size < 0) {
// Size is unknown, loop reading chunks until EOF.
bytes->clear();
std::list<std::vector<uint8_t>> chunks;
size_t pos = 0;
while (pos < bytes->size()) {
// Needed in case ContainerType is std::string, whose data() is const.
char* bytes_writable = reinterpret_cast<char*>(&(*bytes)[0]);
const size_t bytes_read =
fread(bytes_writable + pos, 1, bytes->size() - pos, f);
if (bytes_read == 0) return JXL_FAILURE("Failed to read");
pos += bytes_read;
size_t total_size = 0;
while (true) {
std::vector<uint8_t> chunk(16 * 1024);
const size_t bytes_read = fread(chunk.data(), 1, chunk.size(), f);
if (ferror(f) || bytes_read > chunk.size()) {
return JXL_FAILURE("Error reading %s", pathname.c_str());
}
chunk.resize(bytes_read);
total_size += bytes_read;
if (bytes_read != 0) {
chunks.emplace_back(std::move(chunk));
}
if (feof(f)) {
break;
}
}
bytes->resize(total_size);
size_t pos = 0;
for (const auto& chunk : chunks) {
// Needed in case ContainerType is std::string, whose data() is const.
char* bytes_writable = reinterpret_cast<char*>(&(*bytes)[0]);
memcpy(bytes_writable + pos, chunk.data(), chunk.size());
pos += chunk.size();
}
} else {
// Size is known, read the file directly.
bytes->resize(static_cast<size_t>(size));
size_t pos = 0;
while (pos < bytes->size()) {
// Needed in case ContainerType is std::string, whose data() is const.
char* bytes_writable = reinterpret_cast<char*>(&(*bytes)[0]);
const size_t bytes_read =
fread(bytes_writable + pos, 1, bytes->size() - pos, f);
if (bytes_read == 0) return JXL_FAILURE("Failed to read");
pos += bytes_read;
}
JXL_ASSERT(pos == bytes->size());
}
JXL_ASSERT(pos == bytes->size());
return true;
}

View file

@ -11,8 +11,9 @@
#include <string>
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
#include "lib/jxl/sanitizers.h"
#if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER
#include "sanitizer/common_interface_defs.h" // __sanitizer_print_stack_trace
#endif // defined(*_SANITIZER)
@ -27,13 +28,12 @@ bool Debug(const char* format, ...) {
}
bool Abort() {
#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \
defined(THREAD_SANITIZER)
#if JXL_ADDRESS_SANITIZER || JXL_MEMORY_SANITIZER || JXL_THREAD_SANITIZER
// If compiled with any sanitizer print a stack trace. This call doesn't crash
// the program, instead the trap below will crash it also allowing gdb to
// break there.
__sanitizer_print_stack_trace();
#endif // defined(*_SANITIZER)
#endif // *_SANITIZER)
#if JXL_COMPILER_MSVC
__debugbreak();

View file

@ -60,6 +60,12 @@ namespace jxl {
#define JXL_DEBUG_V_LEVEL 0
#endif // JXL_DEBUG_V_LEVEL
// Pass -DJXL_DEBUG_ON_ABORT=0 to disable the debug messages on JXL_ASSERT,
// JXL_CHECK and JXL_ABORT.
#ifndef JXL_DEBUG_ON_ABORT
#define JXL_DEBUG_ON_ABORT 1
#endif // JXL_DEBUG_ON_ABORT
// Print a debug message on standard error. You should use the JXL_DEBUG macro
// instead of calling Debug directly. This function returns false, so it can be
// used as a return value in JXL_FAILURE.
@ -110,19 +116,19 @@ bool Debug(const char* format, ...);
JXL_NORETURN bool Abort();
// Exits the program after printing file/line plus a formatted string.
#define JXL_ABORT(format, ...) \
(::jxl::Debug(("%s:%d: JXL_ABORT: " format "\n"), __FILE__, __LINE__, \
##__VA_ARGS__), \
#define JXL_ABORT(format, ...) \
((JXL_DEBUG_ON_ABORT) && ::jxl::Debug(("%s:%d: JXL_ABORT: " format "\n"), \
__FILE__, __LINE__, ##__VA_ARGS__), \
::jxl::Abort())
// Does not guarantee running the code, use only for debug mode checks.
#if JXL_ENABLE_ASSERT
#define JXL_ASSERT(condition) \
do { \
if (!(condition)) { \
JXL_DEBUG(true, "JXL_ASSERT: %s", #condition); \
::jxl::Abort(); \
} \
#define JXL_ASSERT(condition) \
do { \
if (!(condition)) { \
JXL_DEBUG(JXL_DEBUG_ON_ABORT, "JXL_ASSERT: %s", #condition); \
::jxl::Abort(); \
} \
} while (0)
#else
#define JXL_ASSERT(condition) \
@ -132,6 +138,7 @@ JXL_NORETURN bool Abort();
// Define JXL_IS_DEBUG_BUILD that denotes asan, msan and other debug builds,
// but not opt or release.
#ifndef JXL_IS_DEBUG_BUILD
#if !defined(NDEBUG) || defined(ADDRESS_SANITIZER) || \
defined(MEMORY_SANITIZER) || defined(THREAD_SANITIZER) || \
defined(__clang_analyzer__)
@ -139,18 +146,19 @@ JXL_NORETURN bool Abort();
#else
#define JXL_IS_DEBUG_BUILD 0
#endif
#endif // JXL_IS_DEBUG_BUILD
// Same as above, but only runs in debug builds (builds where NDEBUG is not
// defined). This is useful for slower asserts that we want to run more rarely
// than usual. These will run on asan, msan and other debug builds, but not in
// opt or release.
#if JXL_IS_DEBUG_BUILD
#define JXL_DASSERT(condition) \
do { \
if (!(condition)) { \
JXL_DEBUG(true, "JXL_DASSERT: %s", #condition); \
::jxl::Abort(); \
} \
#define JXL_DASSERT(condition) \
do { \
if (!(condition)) { \
JXL_DEBUG(JXL_DEBUG_ON_ABORT, "JXL_DASSERT: %s", #condition); \
::jxl::Abort(); \
} \
} while (0)
#else
#define JXL_DASSERT(condition) \
@ -160,12 +168,12 @@ JXL_NORETURN bool Abort();
// Always runs the condition, so can be used for non-debug calls.
#if JXL_ENABLE_CHECK
#define JXL_CHECK(condition) \
do { \
if (!(condition)) { \
JXL_DEBUG(true, "JXL_CHECK: %s", #condition); \
::jxl::Abort(); \
} \
#define JXL_CHECK(condition) \
do { \
if (!(condition)) { \
JXL_DEBUG(JXL_DEBUG_ON_ABORT, "JXL_CHECK: %s", #condition); \
::jxl::Abort(); \
} \
} while (0)
#else
#define JXL_CHECK(condition) \

View file

@ -31,12 +31,12 @@ TEST(BitReaderTest, ExtendsWithZeroes) {
BitReader br(Span<const uint8_t>(data.data(), n_bytes));
// Read all the bits
for (size_t i = 0; i < n_bytes * kBitsPerByte; i++) {
ASSERT_EQ(br.ReadBits(1), 1) << "n_bytes=" << n_bytes << " i=" << i;
ASSERT_EQ(br.ReadBits(1), 1u) << "n_bytes=" << n_bytes << " i=" << i;
}
// PEEK more than the declared size - all will be zero. Cannot consume.
for (size_t i = 0; i < BitReader::kMaxBitsPerCall; i++) {
ASSERT_EQ(br.PeekBits(i), 0)
ASSERT_EQ(br.PeekBits(i), 0u)
<< "size=" << size << "n_bytes=" << n_bytes << " i=" << i;
}
@ -116,20 +116,21 @@ TEST(BitReaderTest, TestSkip) {
BitReader reader2(writer.GetSpan());
// Verify initial 1-bits
for (int i = 0; i < task; ++i) {
EXPECT_EQ(1, reader1.ReadBits(1));
EXPECT_EQ(1, reader2.ReadBits(1));
EXPECT_EQ(1u, reader1.ReadBits(1));
EXPECT_EQ(1u, reader2.ReadBits(1));
}
// SkipBits or manually read "skip" bits
reader1.SkipBits(skip);
for (size_t i = 0; i < skip; ++i) {
EXPECT_EQ(0, reader2.ReadBits(1)) << " skip=" << skip << " i=" << i;
EXPECT_EQ(0u, reader2.ReadBits(1))
<< " skip=" << skip << " i=" << i;
}
EXPECT_EQ(reader1.TotalBitsConsumed(), reader2.TotalBitsConsumed());
// Ensure both readers see the terminator bits.
EXPECT_EQ(5, reader1.ReadBits(3));
EXPECT_EQ(5, reader2.ReadBits(3));
EXPECT_EQ(5u, reader1.ReadBits(3));
EXPECT_EQ(5u, reader2.ReadBits(3));
EXPECT_TRUE(reader1.Close());
EXPECT_TRUE(reader2.Close());
@ -158,8 +159,8 @@ TEST(BitReaderTest, TestOrder) {
writer.ZeroPadToByte();
ReclaimAndCharge(&writer, &allotment, 0, nullptr);
BitReader reader(writer.GetSpan());
EXPECT_EQ(0x1F, reader.ReadFixedBits<8>());
EXPECT_EQ(0xFC, reader.ReadFixedBits<8>());
EXPECT_EQ(0x1Fu, reader.ReadFixedBits<8>());
EXPECT_EQ(0xFCu, reader.ReadFixedBits<8>());
EXPECT_TRUE(reader.Close());
}
@ -173,8 +174,8 @@ TEST(BitReaderTest, TestOrder) {
writer.ZeroPadToByte();
ReclaimAndCharge(&writer, &allotment, 0, nullptr);
BitReader reader(writer.GetSpan());
EXPECT_EQ(0xF8, reader.ReadFixedBits<8>());
EXPECT_EQ(0x3F, reader.ReadFixedBits<8>());
EXPECT_EQ(0xF8u, reader.ReadFixedBits<8>());
EXPECT_EQ(0x3Fu, reader.ReadFixedBits<8>());
EXPECT_TRUE(reader.Close());
}
@ -187,8 +188,8 @@ TEST(BitReaderTest, TestOrder) {
writer.ZeroPadToByte();
ReclaimAndCharge(&writer, &allotment, 0, nullptr);
BitReader reader(writer.GetSpan());
EXPECT_EQ(0x3F, reader.ReadFixedBits<8>());
EXPECT_EQ(0xF8, reader.ReadFixedBits<8>());
EXPECT_EQ(0x3Fu, reader.ReadFixedBits<8>());
EXPECT_EQ(0xF8u, reader.ReadFixedBits<8>());
EXPECT_TRUE(reader.Close());
}
@ -204,8 +205,8 @@ TEST(BitReaderTest, TestOrder) {
writer.ZeroPadToByte();
ReclaimAndCharge(&writer, &allotment, 0, nullptr);
BitReader reader(writer.GetSpan());
EXPECT_EQ(0xBD, reader.ReadFixedBits<8>());
EXPECT_EQ(0x8D, reader.ReadFixedBits<8>());
EXPECT_EQ(0xBDu, reader.ReadFixedBits<8>());
EXPECT_EQ(0x8Du, reader.ReadFixedBits<8>());
EXPECT_TRUE(reader.Close());
}
}
@ -215,21 +216,21 @@ TEST(BitReaderTest, TotalCountersTest) {
BitReader reader(Span<const uint8_t>(buf, sizeof(buf)));
EXPECT_EQ(sizeof(buf), reader.TotalBytes());
EXPECT_EQ(0, reader.TotalBitsConsumed());
EXPECT_EQ(0u, reader.TotalBitsConsumed());
reader.ReadFixedBits<1>();
EXPECT_EQ(1, reader.TotalBitsConsumed());
EXPECT_EQ(1u, reader.TotalBitsConsumed());
reader.ReadFixedBits<10>();
EXPECT_EQ(11, reader.TotalBitsConsumed());
EXPECT_EQ(11u, reader.TotalBitsConsumed());
reader.ReadFixedBits<4>();
EXPECT_EQ(15, reader.TotalBitsConsumed());
EXPECT_EQ(15u, reader.TotalBitsConsumed());
reader.ReadFixedBits<1>();
EXPECT_EQ(16, reader.TotalBitsConsumed());
EXPECT_EQ(16u, reader.TotalBitsConsumed());
reader.ReadFixedBits<16>();
EXPECT_EQ(32, reader.TotalBitsConsumed());
EXPECT_EQ(32u, reader.TotalBitsConsumed());
EXPECT_TRUE(reader.Close());
}
@ -240,18 +241,18 @@ TEST(BitReaderTest, MoveTest) {
{
BitReader reader1(Span<const uint8_t>(buf, sizeof(buf)));
EXPECT_EQ(0, reader1.TotalBitsConsumed());
EXPECT_EQ(0u, reader1.TotalBitsConsumed());
reader1.ReadFixedBits<16>();
EXPECT_EQ(16, reader1.TotalBitsConsumed());
EXPECT_EQ(16u, reader1.TotalBitsConsumed());
reader2 = std::move(reader1);
// From this point reader1 is invalid, but can continue to access reader2
// and we don't need to call Close() on reader1.
}
EXPECT_EQ(16, reader2.TotalBitsConsumed());
EXPECT_EQ(16u, reader2.TotalBitsConsumed());
EXPECT_EQ(3U, reader2.ReadFixedBits<8>());
EXPECT_EQ(24, reader2.TotalBitsConsumed());
EXPECT_EQ(24u, reader2.TotalBitsConsumed());
EXPECT_TRUE(reader2.Close());
}

Some files were not shown because too many files have changed in this diff Show more