mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 06:43:32 +00:00
Bug 1800284 - Update libjxl r=tnikkel
Differential Revision: https://phabricator.services.mozilla.com/D162008
This commit is contained in:
parent
2707c9dabc
commit
26ed19454c
@ -9,8 +9,6 @@
|
||||
|
||||
#define JXL_EXPORT
|
||||
|
||||
// TODO: go back to [[deprecated]]
|
||||
// https://github.com/libjxl/libjxl/issues/1388
|
||||
#define JXL_DEPRECATED __attribute__((__deprecated__))
|
||||
#define JXL_DEPRECATED [[deprecated]]
|
||||
|
||||
#endif /* JXL_EXPORT_H */
|
||||
|
@ -20,7 +20,6 @@ SOURCES += [
|
||||
"/third_party/jpeg-xl/lib/jxl/base/padded_bytes.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/base/random.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/blending.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/box_content_decoder.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/chroma_from_luma.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/coeff_order.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/color_encoding_internal.cc",
|
||||
@ -45,7 +44,6 @@ SOURCES += [
|
||||
"/third_party/jpeg-xl/lib/jxl/dec_patch_dictionary.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",
|
||||
@ -60,9 +58,6 @@ SOURCES += [
|
||||
"/third_party/jpeg-xl/lib/jxl/image.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/image_bundle.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/image_metadata.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/jpeg/dec_jpeg_data_writer.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/loop_filter.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/luminance.cc",
|
||||
"/third_party/jpeg-xl/lib/jxl/memory_manager_internal.cc",
|
||||
@ -123,6 +118,9 @@ EXPORTS.jxl += [
|
||||
"/third_party/jpeg-xl/lib/include/jxl/types.h",
|
||||
]
|
||||
|
||||
DEFINES["JPEGXL_ENABLE_BOXES"] = "0"
|
||||
DEFINES["JPEGXL_ENABLE_TRANSCODE_JPEG"] = "0"
|
||||
|
||||
FINAL_LIBRARY = "gkmedias"
|
||||
|
||||
# We allow warnings for third-party code that can be updated from upstream.
|
||||
|
@ -10,9 +10,9 @@ origin:
|
||||
|
||||
url: https://github.com/libjxl/libjxl
|
||||
|
||||
release: 99b0721251b51028927c56a81657cf76de7ea320 (2022-10-20T11:29:24Z).
|
||||
release: afa493d9c7c8b47b6ce709180a74a49085291776 (2022-11-12T22:27:21Z).
|
||||
|
||||
revision: 99b0721251b51028927c56a81657cf76de7ea320
|
||||
revision: afa493d9c7c8b47b6ce709180a74a49085291776
|
||||
|
||||
license: Apache-2.0
|
||||
|
||||
|
@ -68,6 +68,16 @@ jobs:
|
||||
-DJPEGXL_ENABLE_TRANSCODE_JPEG=OFF
|
||||
-DJPEGXL_ENABLE_PLUGINS=OFF
|
||||
-DJPEGXL_ENABLE_VIEWERS=OFF
|
||||
# Build optimized for binary size, all features not needed for
|
||||
# reconstructing pixels is disabled.
|
||||
- name: release:minimal
|
||||
mode: release
|
||||
cxxflags: -DJXL_DEBUG_ON_ABORT=0
|
||||
cmake_args: >-
|
||||
-DJPEGXL_ENABLE_TRANSCODE_JPEG=OFF
|
||||
-DJPEGXL_ENABLE_BOXES=OFF
|
||||
-DJPEGXL_ENABLE_PLUGINS=OFF
|
||||
-DJPEGXL_ENABLE_VIEWERS=OFF
|
||||
# Builds with gcc in release mode
|
||||
- name: release:gcc8
|
||||
mode: release
|
||||
@ -394,7 +404,7 @@ jobs:
|
||||
|
||||
- name: Set EMSDK node version
|
||||
run: |
|
||||
echo "NODE_JS='$(cat $HOME/.base_node_path)'" >> $EM_CONFIG
|
||||
echo "NODE_JS='$(cat $HOME/.base_node_path)'" >> $EMSDK/.emscripten
|
||||
emsdk construct_env
|
||||
|
||||
# TODO(deymo): Build and install other dependencies like libpng, libjpeg,
|
||||
|
2
third_party/jpeg-xl/AUTHORS
vendored
2
third_party/jpeg-xl/AUTHORS
vendored
@ -29,6 +29,7 @@ David Burnett <vargolsoft@gmail.com>
|
||||
Dirk Lemstra <dirk@lemstra.org>
|
||||
Don Olmstead <don.j.olmstead@gmail.com>
|
||||
Even Rouault <even.rouault@spatialys.com>
|
||||
Fred Brennan <copypaste@kittens.ph>
|
||||
Heiko Becker <heirecka@exherbo.org>
|
||||
Jon Sneyers <jon@cloudinary.com>
|
||||
Kai Hollberg <Schweinepriester@users.noreply.github.com>
|
||||
@ -43,6 +44,7 @@ Mathieu Malaterre <mathieu.malaterre@gmail.com>
|
||||
Mikk Leini <mikk.leini@krakul.eu>
|
||||
Misaki Kasumi <misakikasumi@outlook.com>
|
||||
Nicholas Hayes <0xC0000054@users.noreply.github.com>
|
||||
Nigel Tao <nigeltao@golang.org>
|
||||
Petr Diblík
|
||||
Pieter Wuille
|
||||
roland-rollo
|
||||
|
4
third_party/jpeg-xl/CMakeLists.txt
vendored
4
third_party/jpeg-xl/CMakeLists.txt
vendored
@ -136,6 +136,10 @@ 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_ENABLE_BOXES true CACHE BOOL
|
||||
"Builds in support for decoding boxes in JXL files,\
|
||||
disabling it makes the decoder reject JXL_DEC_BOX 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
|
||||
|
2
third_party/jpeg-xl/ci.sh
vendored
2
third_party/jpeg-xl/ci.sh
vendored
@ -576,7 +576,7 @@ cmd_test() {
|
||||
(cd "${BUILD_DIR}"
|
||||
export UBSAN_OPTIONS=print_stacktrace=1
|
||||
[[ "${TEST_STACK_LIMIT}" == "none" ]] || ulimit -s "${TEST_STACK_LIMIT}"
|
||||
ctest -j $(nproc --all || echo 1) --output-on-failure "$@")
|
||||
ctest -j $(nproc --all || echo 1) ${TEST_SELECTOR} --output-on-failure "$@")
|
||||
}
|
||||
|
||||
cmd_gbench() {
|
||||
|
2
third_party/jpeg-xl/deps.sh
vendored
2
third_party/jpeg-xl/deps.sh
vendored
@ -15,7 +15,7 @@ MYDIR=$(dirname $(realpath "$0"))
|
||||
# update a git submodule.
|
||||
THIRD_PARTY_BROTLI="35ef5c554d888bef217d449346067de05e269b30"
|
||||
THIRD_PARTY_HIGHWAY="22e3d7276f4157d4a47586ba9fd91dd6303f441a"
|
||||
THIRD_PARTY_SKCMS="64374756e03700d649f897dbd98c95e78c30c7da"
|
||||
THIRD_PARTY_SKCMS="b25b07b4b07990811de121c0356155b2ba0f4318"
|
||||
THIRD_PARTY_SJPEG="868ab558fad70fcbe8863ba4e85179eeb81cc840"
|
||||
THIRD_PARTY_ZLIB="cacf7f1d4e3d44d871b605da3b647f07d718623f"
|
||||
THIRD_PARTY_LIBPNG="a40189cf881e9f0db80511c382292a5604c3c3d1"
|
||||
|
@ -20,6 +20,8 @@
|
||||
#error "system not known to be little endian"
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
struct BitWriter {
|
||||
void Allocate(size_t maximum_bit_size) {
|
||||
assert(data == nullptr);
|
||||
@ -143,10 +145,10 @@ struct PrefixCode {
|
||||
static void ComputeCodeLengths(uint64_t* freqs, size_t n, size_t limit,
|
||||
uint8_t* nbits) {
|
||||
if (n <= 1) return;
|
||||
assert(n <= (1 << limit));
|
||||
assert(n <= (1u << limit));
|
||||
assert(n <= 32);
|
||||
int parent[64] = {};
|
||||
int height[64] = {};
|
||||
unsigned int parent[64] = {};
|
||||
unsigned int height[64] = {};
|
||||
using QElem = std::pair<uint64_t, size_t>;
|
||||
std::priority_queue<QElem, std::vector<QElem>, std::greater<QElem>> q;
|
||||
// Standard Huffman code construction. On failure (i.e. if going beyond the
|
||||
@ -1088,8 +1090,8 @@ void PrepareDCGlobalPalette(bool is_single_group, size_t width, size_t height,
|
||||
encoder.code = &code;
|
||||
int16_t p[4][32 + 1024] = {};
|
||||
uint8_t prgba[4];
|
||||
int i = 0;
|
||||
int have_zero = 0;
|
||||
size_t i = 0;
|
||||
size_t have_zero = 0;
|
||||
if (palette[pcolors_real - 1] == 0) have_zero = 1;
|
||||
for (; i < pcolors; i++) {
|
||||
if (i < pcolors_real) {
|
||||
@ -1184,7 +1186,7 @@ size_t LLEnc(const unsigned char* rgba, size_t width, size_t stride,
|
||||
if (palette[0] == 1) palette[0] = 0;
|
||||
bool have_color = false;
|
||||
uint8_t minG = 255, maxG = 0;
|
||||
for (int k = 0; k < kHashSize; k++) {
|
||||
for (uint32_t k = 0; k < kHashSize; k++) {
|
||||
if (palette[k] == 0) continue;
|
||||
uint8_t p[4];
|
||||
memcpy(p, &palette[k], 4);
|
||||
@ -1297,7 +1299,9 @@ size_t LLEnc(const unsigned char* rgba, size_t width, size_t stride,
|
||||
PrepareDCGlobalPalette(onegroup, width, height, hcode, palette, pcolors,
|
||||
&group_data[0][0]);
|
||||
}
|
||||
#ifdef _OPENMP
|
||||
#pragma omp parallel for
|
||||
#endif
|
||||
for (size_t g = 0; g < num_groups_y * num_groups_x; g++) {
|
||||
size_t xg = g % num_groups_x;
|
||||
size_t yg = g / num_groups_x;
|
||||
@ -1324,9 +1328,16 @@ size_t LLEnc(const unsigned char* rgba, size_t width, size_t stride,
|
||||
return writer.bytes_written;
|
||||
}
|
||||
|
||||
size_t FastLosslessEncode(const unsigned char* rgba, size_t width,
|
||||
size_t stride, size_t height, size_t nb_chans,
|
||||
size_t bitdepth, int effort, unsigned char** output) {
|
||||
} // namespace
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
size_t JxlFastLosslessEncode(const unsigned char* rgba, size_t width,
|
||||
size_t stride, size_t height, size_t nb_chans,
|
||||
size_t bitdepth, int effort,
|
||||
unsigned char** output) {
|
||||
assert(bitdepth <= 12);
|
||||
assert(bitdepth > 0);
|
||||
assert(nb_chans <= 4);
|
||||
@ -1360,3 +1371,7 @@ size_t FastLosslessEncode(const unsigned char* rgba, size_t width,
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
@ -7,8 +7,17 @@
|
||||
#define FAST_LOSSLESS_H
|
||||
#include <stdlib.h>
|
||||
|
||||
size_t FastLosslessEncode(const unsigned char* rgba, size_t width,
|
||||
size_t row_stride, size_t height, size_t nb_chans,
|
||||
size_t bitdepth, int effort, unsigned char** output);
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
size_t JxlFastLosslessEncode(const unsigned char* rgba, size_t width,
|
||||
size_t row_stride, size_t height, size_t nb_chans,
|
||||
size_t bitdepth, int effort,
|
||||
unsigned char** output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
@ -51,8 +51,8 @@ int main(int argc, char** argv) {
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
for (size_t _ = 0; _ < num_reps; _++) {
|
||||
free(encoded);
|
||||
encoded_size = FastLosslessEncode(png, width, stride, height, nb_chans,
|
||||
bitdepth, effort, &encoded);
|
||||
encoded_size = JxlFastLosslessEncode(png, width, stride, height, nb_chans,
|
||||
bitdepth, effort, &encoded);
|
||||
}
|
||||
auto stop = std::chrono::high_resolution_clock::now();
|
||||
if (num_reps > 1) {
|
||||
|
5
third_party/jpeg-xl/lib/extras/codec.cc
vendored
5
third_party/jpeg-xl/lib/extras/codec.cc
vendored
@ -102,7 +102,7 @@ Status Encode(const CodecInOut& io, const extras::Codec codec,
|
||||
encoder = extras::GetPPMEncoder();
|
||||
} else {
|
||||
format.data_type = JXL_TYPE_FLOAT;
|
||||
format.endianness = JXL_NATIVE_ENDIAN;
|
||||
format.endianness = JXL_LITTLE_ENDIAN;
|
||||
encoder = extras::GetPFMEncoder();
|
||||
}
|
||||
break;
|
||||
@ -131,6 +131,9 @@ Status Encode(const CodecInOut& io, const extras::Codec codec,
|
||||
JXL_RETURN_IF_ERROR(
|
||||
ConvertCodecInOutToPackedPixelFile(io, format, c_desired, pool, &ppf));
|
||||
ppf.info.bits_per_sample = bits_per_sample;
|
||||
if (format.data_type == JXL_TYPE_FLOAT) {
|
||||
ppf.info.exponent_bits_per_sample = 8;
|
||||
}
|
||||
extras::EncodedImage encoded_image;
|
||||
JXL_RETURN_IF_ERROR(encoder->Encode(ppf, &encoded_image, pool));
|
||||
JXL_ASSERT(encoded_image.bitstreams.size() == 1);
|
||||
|
4
third_party/jpeg-xl/lib/extras/dec/jxl.cc
vendored
4
third_party/jpeg-xl/lib/extras/dec/jxl.cc
vendored
@ -283,6 +283,10 @@ bool DecodeImageJXL(const uint8_t* bytes, size_t bytes_size,
|
||||
// color space.
|
||||
ppf->info.num_color_channels = num_color_channels;
|
||||
}
|
||||
if (dparams.output_bitdepth.type == JXL_BIT_DEPTH_CUSTOM) {
|
||||
// Select format based on custom bits per sample.
|
||||
ppf->info.bits_per_sample = dparams.output_bitdepth.bits_per_sample;
|
||||
}
|
||||
// Select format according to accepted formats.
|
||||
if (!jxl::extras::SelectFormat(accepted_formats, ppf->info, &format)) {
|
||||
fprintf(stderr, "SelectFormat failed\n");
|
||||
|
322
third_party/jpeg-xl/lib/extras/dec_group_jpeg.cc
vendored
322
third_party/jpeg-xl/lib/extras/dec_group_jpeg.cc
vendored
@ -12,28 +12,43 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#ifdef MEMORY_SANITIZER
|
||||
#define JXL_MEMORY_SANITIZER 1
|
||||
#elif defined(__has_feature)
|
||||
#if __has_feature(memory_sanitizer)
|
||||
#define JXL_MEMORY_SANITIZER 1
|
||||
#else
|
||||
#define JXL_MEMORY_SANITIZER 0
|
||||
#endif
|
||||
#else
|
||||
#define JXL_MEMORY_SANITIZER 0
|
||||
#endif
|
||||
|
||||
#if JXL_MEMORY_SANITIZER
|
||||
#include "sanitizer/msan_interface.h"
|
||||
#endif
|
||||
|
||||
#undef HWY_TARGET_INCLUDE
|
||||
#define HWY_TARGET_INCLUDE "lib/extras/dec_group_jpeg.cc"
|
||||
#include <hwy/foreach_target.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/dct_scales.h"
|
||||
#include "lib/jxl/dec_transforms-inl.h"
|
||||
#include "lib/jxl/sanitizers.h"
|
||||
#include "lib/jxl/simd_util-inl.h"
|
||||
|
||||
HWY_BEFORE_NAMESPACE();
|
||||
namespace jxl {
|
||||
namespace HWY_NAMESPACE {
|
||||
|
||||
// These templates are not found via ADL.
|
||||
using hwy::HWY_NAMESPACE::Abs;
|
||||
using hwy::HWY_NAMESPACE::Add;
|
||||
using hwy::HWY_NAMESPACE::Clamp;
|
||||
using hwy::HWY_NAMESPACE::Gt;
|
||||
using hwy::HWY_NAMESPACE::IfThenElseZero;
|
||||
using hwy::HWY_NAMESPACE::Mul;
|
||||
using hwy::HWY_NAMESPACE::MulAdd;
|
||||
using hwy::HWY_NAMESPACE::NearestInt;
|
||||
using hwy::HWY_NAMESPACE::NegMulAdd;
|
||||
using hwy::HWY_NAMESPACE::Rebind;
|
||||
using hwy::HWY_NAMESPACE::Sub;
|
||||
using hwy::HWY_NAMESPACE::Vec;
|
||||
using hwy::HWY_NAMESPACE::Xor;
|
||||
|
||||
@ -42,8 +57,11 @@ using DI = HWY_FULL(int32_t);
|
||||
constexpr D d;
|
||||
constexpr DI di;
|
||||
|
||||
void GatherBlockStats(const int16_t* coeffs, const size_t coeffs_size,
|
||||
int32_t* JXL_RESTRICT nonzeros,
|
||||
using D8 = HWY_CAPPED(float, 8);
|
||||
constexpr D8 d8;
|
||||
|
||||
void GatherBlockStats(const int16_t* JXL_RESTRICT coeffs,
|
||||
const size_t coeffs_size, int32_t* JXL_RESTRICT nonzeros,
|
||||
int32_t* JXL_RESTRICT sumabs) {
|
||||
for (size_t i = 0; i < coeffs_size; i += Lanes(d)) {
|
||||
size_t k = i % kDCTBlockSize;
|
||||
@ -76,16 +94,274 @@ void DequantBlock(const int16_t* JXL_RESTRICT qblock,
|
||||
}
|
||||
}
|
||||
|
||||
void DecodeJpegBlock(const int16_t* qblock, const float* JXL_RESTRICT dequant,
|
||||
#if HWY_CAP_GE256
|
||||
JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from,
|
||||
float* JXL_RESTRICT to) {
|
||||
const D8 d;
|
||||
auto i0 = Load(d, from);
|
||||
auto i1 = Load(d, from + 1 * 8);
|
||||
auto i2 = Load(d, from + 2 * 8);
|
||||
auto i3 = Load(d, from + 3 * 8);
|
||||
auto i4 = Load(d, from + 4 * 8);
|
||||
auto i5 = Load(d, from + 5 * 8);
|
||||
auto i6 = Load(d, from + 6 * 8);
|
||||
auto i7 = Load(d, from + 7 * 8);
|
||||
|
||||
const auto q0 = InterleaveLower(d, i0, i2);
|
||||
const auto q1 = InterleaveLower(d, i1, i3);
|
||||
const auto q2 = InterleaveUpper(d, i0, i2);
|
||||
const auto q3 = InterleaveUpper(d, i1, i3);
|
||||
const auto q4 = InterleaveLower(d, i4, i6);
|
||||
const auto q5 = InterleaveLower(d, i5, i7);
|
||||
const auto q6 = InterleaveUpper(d, i4, i6);
|
||||
const auto q7 = InterleaveUpper(d, i5, i7);
|
||||
|
||||
const auto r0 = InterleaveLower(d, q0, q1);
|
||||
const auto r1 = InterleaveUpper(d, q0, q1);
|
||||
const auto r2 = InterleaveLower(d, q2, q3);
|
||||
const auto r3 = InterleaveUpper(d, q2, q3);
|
||||
const auto r4 = InterleaveLower(d, q4, q5);
|
||||
const auto r5 = InterleaveUpper(d, q4, q5);
|
||||
const auto r6 = InterleaveLower(d, q6, q7);
|
||||
const auto r7 = InterleaveUpper(d, q6, q7);
|
||||
|
||||
i0 = ConcatLowerLower(d, r4, r0);
|
||||
i1 = ConcatLowerLower(d, r5, r1);
|
||||
i2 = ConcatLowerLower(d, r6, r2);
|
||||
i3 = ConcatLowerLower(d, r7, r3);
|
||||
i4 = ConcatUpperUpper(d, r4, r0);
|
||||
i5 = ConcatUpperUpper(d, r5, r1);
|
||||
i6 = ConcatUpperUpper(d, r6, r2);
|
||||
i7 = ConcatUpperUpper(d, r7, r3);
|
||||
|
||||
Store(i0, d, to);
|
||||
Store(i1, d, to + 1 * 8);
|
||||
Store(i2, d, to + 2 * 8);
|
||||
Store(i3, d, to + 3 * 8);
|
||||
Store(i4, d, to + 4 * 8);
|
||||
Store(i5, d, to + 5 * 8);
|
||||
Store(i6, d, to + 6 * 8);
|
||||
Store(i7, d, to + 7 * 8);
|
||||
}
|
||||
#elif HWY_TARGET != HWY_SCALAR
|
||||
JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from,
|
||||
float* JXL_RESTRICT to) {
|
||||
const HWY_CAPPED(float, 4) d;
|
||||
for (size_t n = 0; n < 8; n += 4) {
|
||||
for (size_t m = 0; m < 8; m += 4) {
|
||||
auto p0 = Load(d, from + n * 8 + m);
|
||||
auto p1 = Load(d, from + (n + 1) * 8 + m);
|
||||
auto p2 = Load(d, from + (n + 2) * 8 + m);
|
||||
auto p3 = Load(d, from + (n + 3) * 8 + m);
|
||||
const auto q0 = InterleaveLower(d, p0, p2);
|
||||
const auto q1 = InterleaveLower(d, p1, p3);
|
||||
const auto q2 = InterleaveUpper(d, p0, p2);
|
||||
const auto q3 = InterleaveUpper(d, p1, p3);
|
||||
|
||||
const auto r0 = InterleaveLower(d, q0, q1);
|
||||
const auto r1 = InterleaveUpper(d, q0, q1);
|
||||
const auto r2 = InterleaveLower(d, q2, q3);
|
||||
const auto r3 = InterleaveUpper(d, q2, q3);
|
||||
Store(r0, d, to + m * 8 + n);
|
||||
Store(r1, d, to + (1 + m) * 8 + n);
|
||||
Store(r2, d, to + (2 + m) * 8 + n);
|
||||
Store(r3, d, to + (3 + m) * 8 + n);
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
JXL_INLINE void Transpose8x8Block(const float* JXL_RESTRICT from,
|
||||
float* JXL_RESTRICT to) {
|
||||
for (size_t n = 0; n < 8; ++n) {
|
||||
for (size_t m = 0; m < 8; ++m) {
|
||||
to[8 * n + m] = from[8 * m + n];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <size_t N>
|
||||
void ForwardEvenOdd(const float* JXL_RESTRICT ain, size_t ain_stride,
|
||||
float* JXL_RESTRICT aout) {
|
||||
for (size_t i = 0; i < N / 2; i++) {
|
||||
auto in1 = LoadU(d8, ain + 2 * i * ain_stride);
|
||||
Store(in1, d8, aout + i * 8);
|
||||
}
|
||||
for (size_t i = N / 2; i < N; i++) {
|
||||
auto in1 = LoadU(d8, ain + (2 * (i - N / 2) + 1) * ain_stride);
|
||||
Store(in1, d8, aout + i * 8);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
void BTranspose(float* JXL_RESTRICT coeff) {
|
||||
for (size_t i = N - 1; i > 0; i--) {
|
||||
auto in1 = Load(d8, coeff + i * 8);
|
||||
auto in2 = Load(d8, coeff + (i - 1) * 8);
|
||||
Store(Add(in1, in2), d8, coeff + i * 8);
|
||||
}
|
||||
constexpr float kSqrt2 = 1.41421356237f;
|
||||
auto sqrt2 = Set(d8, kSqrt2);
|
||||
auto in1 = Load(d8, coeff);
|
||||
Store(Mul(in1, sqrt2), d8, coeff);
|
||||
}
|
||||
|
||||
// Constants for DCT implementation. Generated by the following snippet:
|
||||
// for i in range(N // 2):
|
||||
// print(1.0 / (2 * math.cos((i + 0.5) * math.pi / N)), end=", ")
|
||||
template <size_t N>
|
||||
struct WcMultipliers;
|
||||
|
||||
template <>
|
||||
struct WcMultipliers<4> {
|
||||
static constexpr float kMultipliers[] = {
|
||||
0.541196100146197,
|
||||
1.3065629648763764,
|
||||
};
|
||||
};
|
||||
|
||||
template <>
|
||||
struct WcMultipliers<8> {
|
||||
static constexpr float kMultipliers[] = {
|
||||
0.5097955791041592,
|
||||
0.6013448869350453,
|
||||
0.8999762231364156,
|
||||
2.5629154477415055,
|
||||
};
|
||||
};
|
||||
|
||||
constexpr float WcMultipliers<4>::kMultipliers[];
|
||||
constexpr float WcMultipliers<8>::kMultipliers[];
|
||||
|
||||
template <size_t N>
|
||||
void MultiplyAndAdd(const float* JXL_RESTRICT coeff, float* JXL_RESTRICT out,
|
||||
size_t out_stride) {
|
||||
for (size_t i = 0; i < N / 2; i++) {
|
||||
auto mul = Set(d8, WcMultipliers<N>::kMultipliers[i]);
|
||||
auto in1 = Load(d8, coeff + i * 8);
|
||||
auto in2 = Load(d8, coeff + (N / 2 + i) * 8);
|
||||
auto out1 = MulAdd(mul, in2, in1);
|
||||
auto out2 = NegMulAdd(mul, in2, in1);
|
||||
StoreU(out1, d8, out + i * out_stride);
|
||||
StoreU(out2, d8, out + (N - i - 1) * out_stride);
|
||||
}
|
||||
}
|
||||
|
||||
template <size_t N>
|
||||
struct IDCT1DImpl;
|
||||
|
||||
template <>
|
||||
struct IDCT1DImpl<1> {
|
||||
JXL_INLINE void operator()(const float* from, size_t from_stride, float* to,
|
||||
size_t to_stride) {
|
||||
StoreU(LoadU(d8, from), d8, to);
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct IDCT1DImpl<2> {
|
||||
JXL_INLINE void operator()(const float* from, size_t from_stride, float* to,
|
||||
size_t to_stride) {
|
||||
JXL_DASSERT(from_stride >= 8);
|
||||
JXL_DASSERT(to_stride >= 8);
|
||||
auto in1 = LoadU(d8, from);
|
||||
auto in2 = LoadU(d8, from + from_stride);
|
||||
StoreU(Add(in1, in2), d8, to);
|
||||
StoreU(Sub(in1, in2), d8, to + to_stride);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
struct IDCT1DImpl {
|
||||
void operator()(const float* from, size_t from_stride, float* to,
|
||||
size_t to_stride) {
|
||||
JXL_DASSERT(from_stride >= 8);
|
||||
JXL_DASSERT(to_stride >= 8);
|
||||
HWY_ALIGN float tmp[64];
|
||||
ForwardEvenOdd<N>(from, from_stride, tmp);
|
||||
IDCT1DImpl<N / 2>()(tmp, 8, tmp, 8);
|
||||
BTranspose<N / 2>(tmp + N * 4);
|
||||
IDCT1DImpl<N / 2>()(tmp + N * 4, 8, tmp + N * 4, 8);
|
||||
MultiplyAndAdd<N>(tmp, to, to_stride);
|
||||
}
|
||||
};
|
||||
|
||||
template <size_t N>
|
||||
void IDCT1D(float* JXL_RESTRICT from, float* JXL_RESTRICT output,
|
||||
size_t output_stride) {
|
||||
for (size_t i = 0; i < 8; i += Lanes(d8)) {
|
||||
IDCT1DImpl<N>()(from + i, 8, output + i, output_stride);
|
||||
}
|
||||
}
|
||||
|
||||
void ComputeScaledIDCT(float* JXL_RESTRICT block0, float* JXL_RESTRICT block1,
|
||||
float* JXL_RESTRICT output, size_t output_stride) {
|
||||
Transpose8x8Block(block0, block1);
|
||||
IDCT1D<8>(block1, block0, 8);
|
||||
Transpose8x8Block(block0, block1);
|
||||
IDCT1D<8>(block1, output, output_stride);
|
||||
}
|
||||
|
||||
void DecodeJpegBlock(const int16_t* JXL_RESTRICT qblock,
|
||||
const float* JXL_RESTRICT dequant,
|
||||
const float* JXL_RESTRICT biases,
|
||||
float* JXL_RESTRICT scratch_space,
|
||||
float* JXL_RESTRICT output, size_t output_stride) {
|
||||
HWY_ALIGN float* const block = scratch_space + kDCTBlockSize;
|
||||
DequantBlock(qblock, dequant, biases, scratch_space);
|
||||
// JPEG XL transposes the DCT, JPEG doesn't.
|
||||
Transpose<8, 8>::Run(DCTFrom(scratch_space, 8), DCTTo(block, 8));
|
||||
TransformToPixels(AcStrategy::DCT, block, output, output_stride,
|
||||
scratch_space);
|
||||
float* JXL_RESTRICT block0 = scratch_space;
|
||||
float* JXL_RESTRICT block1 = scratch_space + kDCTBlockSize;
|
||||
DequantBlock(qblock, dequant, biases, block0);
|
||||
ComputeScaledIDCT(block0, block1, output, output_stride);
|
||||
}
|
||||
|
||||
#if HWY_CAP_GE512
|
||||
using hwy::HWY_NAMESPACE::Half;
|
||||
using hwy::HWY_NAMESPACE::Vec;
|
||||
template <size_t i, class DF, class V>
|
||||
HWY_INLINE Vec<Half<Half<DF>>> Quarter(const DF df, V v) {
|
||||
using HF = Half<DF>;
|
||||
using HHF = Half<HF>;
|
||||
auto half = i >= 2 ? UpperHalf(HF(), v) : LowerHalf(HF(), v);
|
||||
return i & 1 ? UpperHalf(HHF(), half) : LowerHalf(HHF(), half);
|
||||
}
|
||||
|
||||
template <class DF, class V>
|
||||
HWY_INLINE Vec<DF> Concat4(const DF df, V v0, V v1, V v2, V v3) {
|
||||
using HF = Half<DF>;
|
||||
return Combine(DF(), Combine(HF(), v3, v2), Combine(HF(), v1, v0));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// Stores v0[0], v1[0], v0[1], v1[1], ... to mem, in this order. Mem must be
|
||||
// aligned.
|
||||
template <class DF, class V, typename T>
|
||||
void StoreInterleaved(const DF df, V v0, V v1, T* mem) {
|
||||
static_assert(sizeof(T) == 4, "only use StoreInterleaved for 4-byte types");
|
||||
#if HWY_TARGET == HWY_SCALAR
|
||||
Store(v0, df, mem);
|
||||
Store(v1, df, mem + 1);
|
||||
#elif !HWY_CAP_GE256
|
||||
Store(InterleaveLower(df, v0, v1), df, mem);
|
||||
Store(InterleaveUpper(df, v0, v1), df, mem + Lanes(df));
|
||||
#else
|
||||
if (!HWY_CAP_GE512 || Lanes(df) == 8) {
|
||||
auto t0 = InterleaveLower(df, v0, v1);
|
||||
auto t1 = InterleaveUpper(df, v0, v1);
|
||||
Store(ConcatLowerLower(df, t1, t0), df, mem);
|
||||
Store(ConcatUpperUpper(df, t1, t0), df, mem + Lanes(df));
|
||||
} else {
|
||||
#if HWY_CAP_GE512
|
||||
auto t0 = InterleaveLower(df, v0, v1);
|
||||
auto t1 = InterleaveUpper(df, v0, v1);
|
||||
Store(Concat4(df, Quarter<0>(df, t0), Quarter<0>(df, t1),
|
||||
Quarter<1>(df, t0), Quarter<1>(df, t1)),
|
||||
df, mem);
|
||||
Store(Concat4(df, Quarter<2>(df, t0), Quarter<2>(df, t1),
|
||||
Quarter<3>(df, t0), Quarter<3>(df, t1)),
|
||||
df, mem + Lanes(df));
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Upsample2Horizontal(float* JXL_RESTRICT row_in,
|
||||
@ -165,10 +441,12 @@ void StoreUnsignedRow(float* JXL_RESTRICT input[3], size_t x0, size_t len,
|
||||
auto one = Set(d, 1.0f);
|
||||
auto mul = Set(d, (1u << (sizeof(T) * 8)) - 1);
|
||||
const Rebind<T, decltype(d)> du;
|
||||
#if JXL_MEMORY_SANITIZER
|
||||
const size_t padding = RoundUpTo(len, Lanes(d)) - len;
|
||||
for (size_t c = 0; c < num_channels; ++c) {
|
||||
msan::UnpoisonMemory(input[c] + x0 + len, sizeof(input[c][0]) * padding);
|
||||
__msan_unpoison(input[c] + x0 + len, sizeof(input[c][0]) * padding);
|
||||
}
|
||||
#endif
|
||||
if (num_channels == 1) {
|
||||
for (size_t i = 0; i < len; i += Lanes(d)) {
|
||||
auto v0 = Mul(Clamp(zero, Load(d, &input[0][x0 + i]), one), mul);
|
||||
@ -184,8 +462,10 @@ void StoreUnsignedRow(float* JXL_RESTRICT input[3], size_t x0, size_t len,
|
||||
DemoteTo(du, NearestInt(v2)), du, &output[3 * i]);
|
||||
}
|
||||
}
|
||||
msan::PoisonMemory(output + num_channels * len,
|
||||
sizeof(output[0]) * num_channels * padding);
|
||||
#if JXL_MEMORY_SANITIZER
|
||||
__msan_poison(output + num_channels * len,
|
||||
sizeof(output[0]) * num_channels * padding);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WriteToPackedImage(float* JXL_RESTRICT rows[3], size_t x0, size_t y0,
|
||||
@ -228,14 +508,14 @@ HWY_EXPORT(WriteToPackedImage);
|
||||
|
||||
namespace extras {
|
||||
|
||||
void GatherBlockStats(const int16_t* coeffs, const size_t coeffs_size,
|
||||
int32_t* JXL_RESTRICT nonzeros,
|
||||
void GatherBlockStats(const int16_t* JXL_RESTRICT coeffs,
|
||||
const size_t coeffs_size, int32_t* JXL_RESTRICT nonzeros,
|
||||
int32_t* JXL_RESTRICT sumabs) {
|
||||
return HWY_DYNAMIC_DISPATCH(GatherBlockStats)(coeffs, coeffs_size, nonzeros,
|
||||
sumabs);
|
||||
}
|
||||
|
||||
void DecodeJpegBlock(const int16_t* qblock,
|
||||
void DecodeJpegBlock(const int16_t* JXL_RESTRICT qblock,
|
||||
const float* JXL_RESTRICT dequant_matrices,
|
||||
const float* JXL_RESTRICT biases,
|
||||
float* JXL_RESTRICT scratch_space,
|
||||
|
@ -12,17 +12,16 @@
|
||||
#include <vector>
|
||||
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/image.h"
|
||||
#include "lib/jxl/base/compiler_specific.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
void GatherBlockStats(const int16_t* coeffs, const size_t coeffs_size,
|
||||
int32_t* JXL_RESTRICT nonzeros,
|
||||
void GatherBlockStats(const int16_t* JXL_RESTRICT coeffs,
|
||||
const size_t coeffs_size, int32_t* JXL_RESTRICT nonzeros,
|
||||
int32_t* JXL_RESTRICT sumabs);
|
||||
|
||||
void DecodeJpegBlock(const int16_t* qblock,
|
||||
void DecodeJpegBlock(const int16_t* JXL_RESTRICT qblock,
|
||||
const float* JXL_RESTRICT dequant_matrices,
|
||||
const float* JXL_RESTRICT biases,
|
||||
float* JXL_RESTRICT scratch_space,
|
||||
|
1703
third_party/jpeg-xl/lib/extras/decode_jpeg.cc
vendored
1703
third_party/jpeg-xl/lib/extras/decode_jpeg.cc
vendored
File diff suppressed because it is too large
Load Diff
250
third_party/jpeg-xl/lib/extras/decode_jpeg.h
vendored
250
third_party/jpeg-xl/lib/extras/decode_jpeg.h
vendored
@ -8,14 +8,264 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
|
||||
#include "hwy/aligned_allocator.h"
|
||||
#include "lib/extras/packed_image.h"
|
||||
#include "lib/jxl/base/data_parallel.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#include "lib/jxl/image.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
|
||||
constexpr int kMaxComponents = 4;
|
||||
|
||||
typedef int16_t coeff_t;
|
||||
|
||||
// Represents one component of a jpeg file.
|
||||
struct JPEGComponent {
|
||||
JPEGComponent()
|
||||
: id(0),
|
||||
h_samp_factor(1),
|
||||
v_samp_factor(1),
|
||||
quant_idx(0),
|
||||
width_in_blocks(0),
|
||||
height_in_blocks(0) {}
|
||||
|
||||
// One-byte id of the component.
|
||||
uint32_t id;
|
||||
// Horizontal and vertical sampling factors.
|
||||
// In interleaved mode, each minimal coded unit (MCU) has
|
||||
// h_samp_factor x v_samp_factor DCT blocks from this component.
|
||||
int h_samp_factor;
|
||||
int v_samp_factor;
|
||||
// The index of the quantization table used for this component.
|
||||
uint32_t quant_idx;
|
||||
// The dimensions of the component measured in 8x8 blocks.
|
||||
uint32_t width_in_blocks;
|
||||
uint32_t height_in_blocks;
|
||||
// The DCT coefficients of this component, laid out block-by-block, divided
|
||||
// through the quantization matrix values.
|
||||
hwy::AlignedFreeUniquePtr<coeff_t[]> coeffs;
|
||||
};
|
||||
|
||||
struct HuffmanTableEntry {
|
||||
// Initialize the value to an invalid symbol so that we can recognize it
|
||||
// when reading the bit stream using a Huffman code with space > 0.
|
||||
HuffmanTableEntry() : bits(0), value(0xffff) {}
|
||||
|
||||
uint8_t bits; // number of bits used for this symbol
|
||||
uint16_t value; // symbol value or table offset
|
||||
};
|
||||
|
||||
// Quantization values for an 8x8 pixel block.
|
||||
struct JPEGQuantTable {
|
||||
std::array<int32_t, kDCTBlockSize> values;
|
||||
// The index of this quantization table as it was parsed from the input JPEG.
|
||||
// Each DQT marker segment contains an 'index' field, and we save this index
|
||||
// here. Valid values are 0 to 3.
|
||||
uint32_t index = 0;
|
||||
};
|
||||
|
||||
// Huffman table indexes and MCU dimensions used for one component of one scan.
|
||||
struct JPEGComponentScanInfo {
|
||||
uint32_t comp_idx;
|
||||
uint32_t dc_tbl_idx;
|
||||
uint32_t ac_tbl_idx;
|
||||
uint32_t mcu_ysize_blocks;
|
||||
uint32_t mcu_xsize_blocks;
|
||||
};
|
||||
|
||||
// Contains information that is used in one scan.
|
||||
struct JPEGScanInfo {
|
||||
// Parameters used for progressive scans (named the same way as in the spec):
|
||||
// Ss : Start of spectral band in zig-zag sequence.
|
||||
// Se : End of spectral band in zig-zag sequence.
|
||||
// Ah : Successive approximation bit position, high.
|
||||
// Al : Successive approximation bit position, low.
|
||||
uint32_t Ss;
|
||||
uint32_t Se;
|
||||
uint32_t Ah;
|
||||
uint32_t Al;
|
||||
uint32_t num_components = 0;
|
||||
std::array<JPEGComponentScanInfo, kMaxComponents> components;
|
||||
size_t MCU_rows;
|
||||
size_t MCU_cols;
|
||||
};
|
||||
|
||||
// State of the decoder that has to be saved before decoding one MCU in case
|
||||
// we run out of the bitstream.
|
||||
struct MCUCodingState {
|
||||
coeff_t last_dc_coeff[kMaxComponents];
|
||||
int eobrun;
|
||||
std::vector<coeff_t> coeffs;
|
||||
};
|
||||
|
||||
// Streaming JPEG decoding object.
|
||||
class JpegDecoder {
|
||||
public:
|
||||
enum class Status {
|
||||
kSuccess,
|
||||
kNeedMoreInput,
|
||||
kError,
|
||||
};
|
||||
|
||||
// Sets the next chunk of input. It must be called before the first call to
|
||||
// ReadHeaders() and every time a reder function returns
|
||||
// Status::kNeedMoreInput.
|
||||
Status SetInput(const uint8_t* data, size_t len);
|
||||
|
||||
// Sets the output image. Must be called between ReadHeaders() and
|
||||
// ReadScanLines(). The provided image must have the dimensions and number of
|
||||
// channels as the underlying JPEG bitstream.
|
||||
Status SetOutput(PackedImage* image);
|
||||
|
||||
// Reads the header markers up to and including SOF marker. After this returns
|
||||
// kSuccess, the image attribute accessors can be called.
|
||||
Status ReadHeaders();
|
||||
|
||||
// Reads the bitstream after the SOF marker, and fills in at most
|
||||
// max_output_rows scan lines of the provided image. Set *num_output_rows to
|
||||
// the actual number of lines produced.
|
||||
Status ReadScanLines(size_t* num_output_rows, size_t max_output_rows);
|
||||
|
||||
// Image attribute accessors, can be called after ReadHeaders() returns
|
||||
// kSuccess.
|
||||
size_t xsize() const { return xsize_; }
|
||||
size_t ysize() const { return ysize_; }
|
||||
size_t num_channels() const { return components_.size(); }
|
||||
const std::vector<uint8_t>& icc_profile() const { return icc_profile_; }
|
||||
|
||||
private:
|
||||
enum class State {
|
||||
kStart,
|
||||
kProcessMarkers,
|
||||
kScan,
|
||||
kRender,
|
||||
kEnd,
|
||||
};
|
||||
State state_ = State::kStart;
|
||||
|
||||
//
|
||||
// Input handling state.
|
||||
//
|
||||
const uint8_t* next_in_ = nullptr;
|
||||
size_t avail_in_ = 0;
|
||||
// Codestream input data is copied here temporarily when the decoder needs
|
||||
// more input bytes to process the next part of the stream.
|
||||
std::vector<uint8_t> codestream_copy_;
|
||||
// Number of bytes at the end of codestream_copy_ that were not yet consumed
|
||||
// by calling AdvanceInput().
|
||||
size_t codestream_unconsumed_ = 0;
|
||||
// Position in the codestream_copy_ vector that the decoder already finished
|
||||
// processing.
|
||||
size_t codestream_pos_ = 0;
|
||||
// Number of bits after codestream_pos_ that were already processed.
|
||||
size_t codestream_bits_ahead_ = 0;
|
||||
|
||||
//
|
||||
// Marker data processing state.
|
||||
//
|
||||
bool found_soi_ = false;
|
||||
bool found_app0_ = false;
|
||||
bool found_dri_ = false;
|
||||
bool found_sof_ = false;
|
||||
bool found_eoi_ = false;
|
||||
size_t xsize_ = 0;
|
||||
size_t ysize_ = 0;
|
||||
bool is_ycbcr_ = true;
|
||||
size_t icc_index_ = 0;
|
||||
size_t icc_total_ = 0;
|
||||
std::vector<uint8_t> icc_profile_;
|
||||
size_t restart_interval_ = 0;
|
||||
std::vector<JPEGQuantTable> quant_;
|
||||
std::vector<JPEGComponent> components_;
|
||||
std::vector<HuffmanTableEntry> dc_huff_lut_;
|
||||
std::vector<HuffmanTableEntry> ac_huff_lut_;
|
||||
uint8_t huff_slot_defined_[256] = {};
|
||||
|
||||
// Fields defined by SOF marker.
|
||||
bool is_progressive_;
|
||||
int max_h_samp_;
|
||||
int max_v_samp_;
|
||||
size_t iMCU_rows_;
|
||||
size_t iMCU_cols_;
|
||||
size_t iMCU_width_;
|
||||
size_t iMCU_height_;
|
||||
|
||||
// Initialized at strat of frame.
|
||||
uint16_t scan_progression_[kMaxComponents][kDCTBlockSize];
|
||||
|
||||
//
|
||||
// Per scan state.
|
||||
//
|
||||
JPEGScanInfo scan_info_;
|
||||
size_t scan_mcu_row_;
|
||||
size_t scan_mcu_col_;
|
||||
coeff_t last_dc_coeff_[kMaxComponents];
|
||||
int eobrun_;
|
||||
int restarts_to_go_;
|
||||
int next_restart_marker_;
|
||||
|
||||
MCUCodingState mcu_;
|
||||
|
||||
//
|
||||
// Rendering state.
|
||||
//
|
||||
PackedImage* output_;
|
||||
|
||||
Image3F MCU_row_buf_;
|
||||
size_t MCU_buf_current_row_;
|
||||
size_t MCU_buf_ready_rows_;
|
||||
|
||||
size_t output_row_;
|
||||
size_t output_mcu_row_;
|
||||
size_t output_ci_;
|
||||
|
||||
// Temporary buffers for vertically upsampled chroma components. We keep a
|
||||
// ringbuffer of 3 * kBlockDim rows so that we have access for previous and
|
||||
// next rows.
|
||||
std::vector<ImageF> chroma_;
|
||||
// In the rendering order, vertically upsampled chroma components come first.
|
||||
std::vector<size_t> component_order_;
|
||||
hwy::AlignedFreeUniquePtr<float[]> idct_scratch_;
|
||||
hwy::AlignedFreeUniquePtr<float[]> upsample_scratch_;
|
||||
hwy::AlignedFreeUniquePtr<uint8_t[]> output_scratch_;
|
||||
|
||||
hwy::AlignedFreeUniquePtr<float[]> dequant_;
|
||||
// Per channel and per frequency statistics about the number of nonzeros and
|
||||
// the sum of coefficient absolute values, used in dequantization bias
|
||||
// computation.
|
||||
hwy::AlignedFreeUniquePtr<int[]> nonzeros_;
|
||||
hwy::AlignedFreeUniquePtr<int[]> sumabs_;
|
||||
std::vector<size_t> num_processed_blocks_;
|
||||
hwy::AlignedFreeUniquePtr<float[]> biases_;
|
||||
|
||||
void AdvanceInput(size_t size);
|
||||
void AdvanceCodestream(size_t size);
|
||||
Status RequestMoreInput();
|
||||
Status GetCodestreamInput(const uint8_t** data, size_t* len);
|
||||
|
||||
Status ProcessMarker(const uint8_t* data, size_t len, size_t* pos);
|
||||
Status ProcessSOF(const uint8_t* data, size_t len);
|
||||
Status ProcessSOS(const uint8_t* data, size_t len);
|
||||
Status ProcessDHT(const uint8_t* data, size_t len);
|
||||
Status ProcessDQT(const uint8_t* data, size_t len);
|
||||
Status ProcessDRI(const uint8_t* data, size_t len);
|
||||
Status ProcessAPP(const uint8_t* data, size_t len);
|
||||
Status ProcessCOM(const uint8_t* data, size_t len);
|
||||
|
||||
Status ProcessScan(const uint8_t* data, size_t len, size_t* pos);
|
||||
|
||||
void SaveMCUCodingState();
|
||||
void RestoreMCUCodingState();
|
||||
|
||||
void PrepareForOutput();
|
||||
void ProcessOutput(size_t* num_output_rows, size_t max_output_rows);
|
||||
};
|
||||
|
||||
Status DecodeJpeg(const std::vector<uint8_t>& compressed,
|
||||
JxlDataType output_data_type, ThreadPool* pool,
|
||||
PackedPixelFile* ppf);
|
||||
|
190
third_party/jpeg-xl/lib/extras/decode_jpeg_test.cc
vendored
Normal file
190
third_party/jpeg-xl/lib/extras/decode_jpeg_test.cc
vendored
Normal file
@ -0,0 +1,190 @@
|
||||
// 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/decode_jpeg.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if JPEGXL_ENABLE_JPEG
|
||||
#include "lib/extras/dec/jpg.h"
|
||||
#endif
|
||||
#include "lib/jxl/test_utils.h"
|
||||
#include "lib/jxl/testdata.h"
|
||||
|
||||
namespace jxl {
|
||||
namespace extras {
|
||||
namespace {
|
||||
|
||||
using test::DistanceRMS;
|
||||
|
||||
struct TestConfig {
|
||||
std::string fn;
|
||||
std::string fn_desc;
|
||||
size_t chunk_size;
|
||||
size_t max_output_lines;
|
||||
};
|
||||
|
||||
class DecodeJpegTestParam : public ::testing::TestWithParam<TestConfig> {};
|
||||
|
||||
TEST_P(DecodeJpegTestParam, Streaming) {
|
||||
TestConfig config = GetParam();
|
||||
const PaddedBytes compressed = ReadTestData(config.fn.c_str());
|
||||
|
||||
#if JPEGXL_ENABLE_JPEG
|
||||
PackedPixelFile ppf_libjpeg;
|
||||
EXPECT_TRUE(
|
||||
DecodeImageJPG(Span<const uint8_t>(compressed.data(), compressed.size()),
|
||||
ColorHints(), SizeConstraints(), &ppf_libjpeg));
|
||||
ASSERT_EQ(1, ppf_libjpeg.frames.size());
|
||||
#endif
|
||||
|
||||
JpegDecoder dec;
|
||||
|
||||
size_t chunk_size = config.chunk_size;
|
||||
if (chunk_size == 0) chunk_size = compressed.size();
|
||||
size_t pos = std::min(chunk_size, compressed.size());
|
||||
ASSERT_EQ(JpegDecoder::Status::kSuccess,
|
||||
dec.SetInput(compressed.data(), pos));
|
||||
|
||||
JpegDecoder::Status status;
|
||||
for (;;) {
|
||||
status = dec.ReadHeaders();
|
||||
if (status == JpegDecoder::Status::kNeedMoreInput) {
|
||||
ASSERT_LT(pos, compressed.size());
|
||||
size_t len = std::min(chunk_size, compressed.size() - pos);
|
||||
ASSERT_EQ(JpegDecoder::Status::kSuccess,
|
||||
dec.SetInput(compressed.data() + pos, len));
|
||||
pos += len;
|
||||
continue;
|
||||
}
|
||||
ASSERT_EQ(status, JpegDecoder::Status::kSuccess);
|
||||
break;
|
||||
}
|
||||
|
||||
#if JPEGXL_ENABLE_JPEG
|
||||
EXPECT_EQ(ppf_libjpeg.info.xsize, dec.xsize());
|
||||
EXPECT_EQ(ppf_libjpeg.info.ysize, dec.ysize());
|
||||
EXPECT_EQ(ppf_libjpeg.info.num_color_channels, dec.num_channels());
|
||||
#endif
|
||||
|
||||
JxlPixelFormat format = {static_cast<uint32_t>(dec.num_channels()),
|
||||
JXL_TYPE_UINT8, JXL_BIG_ENDIAN, 0};
|
||||
PackedImage output(dec.xsize(), dec.ysize(), format);
|
||||
ASSERT_EQ(JpegDecoder::Status::kSuccess, dec.SetOutput(&output));
|
||||
|
||||
size_t max_output_lines = config.max_output_lines;
|
||||
if (max_output_lines == 0) max_output_lines = dec.ysize();
|
||||
|
||||
size_t total_output_lines = 0;
|
||||
while (total_output_lines < dec.ysize()) {
|
||||
size_t num_output_lines = 0;
|
||||
status = dec.ReadScanLines(&num_output_lines, max_output_lines);
|
||||
total_output_lines += num_output_lines;
|
||||
if (status == JpegDecoder::Status::kNeedMoreInput) {
|
||||
ASSERT_LT(pos, compressed.size());
|
||||
size_t len = std::min(chunk_size, compressed.size() - pos);
|
||||
ASSERT_EQ(JpegDecoder::Status::kSuccess,
|
||||
dec.SetInput(compressed.data() + pos, len));
|
||||
pos += len;
|
||||
continue;
|
||||
}
|
||||
ASSERT_EQ(status, JpegDecoder::Status::kSuccess);
|
||||
if (total_output_lines < dec.ysize()) {
|
||||
EXPECT_EQ(num_output_lines, max_output_lines);
|
||||
}
|
||||
}
|
||||
|
||||
#if JPEGXL_ENABLE_JPEG
|
||||
const PackedImage& output_libjpeg = ppf_libjpeg.frames[0].color;
|
||||
ASSERT_EQ(output.xsize, output_libjpeg.xsize);
|
||||
ASSERT_EQ(output.ysize, output_libjpeg.ysize);
|
||||
EXPECT_LE(
|
||||
DistanceRMS(reinterpret_cast<const uint8_t*>(output.pixels()),
|
||||
reinterpret_cast<const uint8_t*>(output_libjpeg.pixels()),
|
||||
output.xsize, output.ysize, output.format),
|
||||
0.0075);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<TestConfig> GenerateTests() {
|
||||
std::vector<TestConfig> all_tests;
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> testfiles({
|
||||
{"jxl/flower/flower.png.im_q85_444.jpg", "Q85YUV444"},
|
||||
{"jxl/flower/flower.png.im_q85_420.jpg", "Q85YUV420"},
|
||||
{"jxl/flower/flower.png.im_q85_420_progr.jpg", "Q85YUV420PROGR"},
|
||||
{"jxl/flower/flower.png.im_q85_420_R13B.jpg", "Q85YUV420R13B"},
|
||||
});
|
||||
for (const auto& it : testfiles) {
|
||||
for (size_t chunk_size : {0, 1, 64, 65536}) {
|
||||
for (size_t max_output_lines : {0, 1, 8, 16}) {
|
||||
TestConfig config;
|
||||
config.fn = it.first;
|
||||
config.fn_desc = it.second;
|
||||
config.chunk_size = chunk_size;
|
||||
config.max_output_lines = max_output_lines;
|
||||
all_tests.push_back(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
std::vector<std::pair<std::string, std::string>> testfiles({
|
||||
{"jxl/flower/flower.png.im_q85_422.jpg", "Q85YUV422"},
|
||||
{"jxl/flower/flower.png.im_q85_440.jpg", "Q85YUV440"},
|
||||
{"jxl/flower/flower.png.im_q85_444_1x2.jpg", "Q85YUV444_1x2"},
|
||||
{"jxl/flower/flower.png.im_q85_asymmetric.jpg", "Q85Asymmetric"},
|
||||
{"jxl/flower/flower.png.im_q85_gray.jpg", "Q85Gray"},
|
||||
{"jxl/flower/flower.png.im_q85_luma_subsample.jpg", "Q85LumaSubsample"},
|
||||
{"jxl/flower/flower.png.im_q85_rgb.jpg", "Q85RGB"},
|
||||
{"jxl/flower/flower.png.im_q85_rgb_subsample_blue.jpg",
|
||||
"Q85RGBSubsampleBlue"},
|
||||
});
|
||||
for (const auto& it : testfiles) {
|
||||
for (size_t chunk_size : {0, 64}) {
|
||||
for (size_t max_output_lines : {0, 16}) {
|
||||
TestConfig config;
|
||||
config.fn = it.first;
|
||||
config.fn_desc = it.second;
|
||||
config.chunk_size = chunk_size;
|
||||
config.max_output_lines = max_output_lines;
|
||||
all_tests.push_back(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return all_tests;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const TestConfig& c) {
|
||||
os << c.fn_desc;
|
||||
if (c.chunk_size == 0) {
|
||||
os << "CompleteInput";
|
||||
} else {
|
||||
os << "InputChunks" << c.chunk_size;
|
||||
}
|
||||
if (c.max_output_lines == 0) {
|
||||
os << "CompleteOutput";
|
||||
} else {
|
||||
os << "OutputLines" << c.max_output_lines;
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string TestDescription(
|
||||
const testing::TestParamInfo<DecodeJpegTestParam::ParamType>& info) {
|
||||
std::stringstream name;
|
||||
name << info.param;
|
||||
return name.str();
|
||||
}
|
||||
|
||||
JXL_GTEST_INSTANTIATE_TEST_SUITE_P(DecodeJpegTest, DecodeJpegTestParam,
|
||||
testing::ValuesIn(GenerateTests()),
|
||||
TestDescription);
|
||||
|
||||
} // namespace
|
||||
} // namespace extras
|
||||
} // namespace jxl
|
436
third_party/jpeg-xl/lib/extras/encode_jpeg.cc
vendored
436
third_party/jpeg-xl/lib/extras/encode_jpeg.cc
vendored
@ -15,7 +15,6 @@
|
||||
#include "lib/jxl/enc_adaptive_quantization.h"
|
||||
#include "lib/jxl/enc_cluster.h"
|
||||
#include "lib/jxl/enc_color_management.h"
|
||||
#include "lib/jxl/enc_quant_weights.h"
|
||||
#include "lib/jxl/enc_transforms.h"
|
||||
#include "lib/jxl/enc_xyb.h"
|
||||
#include "lib/jxl/huffman_tree.h"
|
||||
@ -117,36 +116,218 @@ std::vector<uint8_t> CreateXybICCAppMarker() {
|
||||
return icc_marker;
|
||||
}
|
||||
|
||||
static constexpr float kBaseQuantMatrix[] = {
|
||||
// c = 0
|
||||
0.010745695802f,
|
||||
0.014724285860f,
|
||||
0.016765073259f,
|
||||
0.015352546818f,
|
||||
0.016849715608f,
|
||||
0.017505664513f,
|
||||
0.019171796023f,
|
||||
0.026983627671f,
|
||||
0.014724285860f,
|
||||
0.016005879113f,
|
||||
0.014807802023f,
|
||||
0.015257294568f,
|
||||
0.016239266522f,
|
||||
0.017754112611f,
|
||||
0.021007430943f,
|
||||
0.024258001854f,
|
||||
0.016765073259f,
|
||||
0.014807802023f,
|
||||
0.016266879484f,
|
||||
0.014202573480f,
|
||||
0.016155362246f,
|
||||
0.018324768181f,
|
||||
0.018883664957f,
|
||||
0.024261275157f,
|
||||
0.015352546818f,
|
||||
0.015257294568f,
|
||||
0.014202573480f,
|
||||
0.014974020066f,
|
||||
0.018844302744f,
|
||||
0.019286162437f,
|
||||
0.023009874591f,
|
||||
0.023277331489f,
|
||||
0.016849715608f,
|
||||
0.016239266522f,
|
||||
0.016155362246f,
|
||||
0.018844302744f,
|
||||
0.019491371738f,
|
||||
0.030153905190f,
|
||||
0.032131952026f,
|
||||
0.047015070993f,
|
||||
0.017505664513f,
|
||||
0.017754112611f,
|
||||
0.018324768181f,
|
||||
0.019286162437f,
|
||||
0.030153905190f,
|
||||
0.035875428738f,
|
||||
0.025324149774f,
|
||||
0.046037739693f,
|
||||
0.019171796023f,
|
||||
0.021007430943f,
|
||||
0.018883664957f,
|
||||
0.023009874591f,
|
||||
0.032131952026f,
|
||||
0.025324149774f,
|
||||
0.025619236945f,
|
||||
0.049740249957f,
|
||||
0.026983627671f,
|
||||
0.024258001854f,
|
||||
0.024261275157f,
|
||||
0.023277331489f,
|
||||
0.047015070993f,
|
||||
0.046037739693f,
|
||||
0.049740249957f,
|
||||
0.029683058303f,
|
||||
// c = 1
|
||||
0.002310547025f,
|
||||
0.002391506241f,
|
||||
0.002592377991f,
|
||||
0.002907631930f,
|
||||
0.003590107614f,
|
||||
0.003647418579f,
|
||||
0.003946607583f,
|
||||
0.004580867024f,
|
||||
0.002391506241f,
|
||||
0.002565945978f,
|
||||
0.002676532241f,
|
||||
0.003167916799f,
|
||||
0.003592423110f,
|
||||
0.003581864537f,
|
||||
0.004168190188f,
|
||||
0.004711190832f,
|
||||
0.002592377991f,
|
||||
0.002676532241f,
|
||||
0.002899922325f,
|
||||
0.003221508920f,
|
||||
0.003597377805f,
|
||||
0.004015001363f,
|
||||
0.004164168214f,
|
||||
0.004536180462f,
|
||||
0.002907631930f,
|
||||
0.003167916799f,
|
||||
0.003221508920f,
|
||||
0.003421333194f,
|
||||
0.003843692347f,
|
||||
0.004011729362f,
|
||||
0.004486022354f,
|
||||
0.005037524314f,
|
||||
0.003590107614f,
|
||||
0.003592423110f,
|
||||
0.003597377805f,
|
||||
0.003843692347f,
|
||||
0.003991982168f,
|
||||
0.004561113887f,
|
||||
0.005683994831f,
|
||||
0.005587879717f,
|
||||
0.003647418579f,
|
||||
0.003581864537f,
|
||||
0.004015001363f,
|
||||
0.004011729362f,
|
||||
0.004561113887f,
|
||||
0.004711190832f,
|
||||
0.005279489671f,
|
||||
0.005645298559f,
|
||||
0.003946607583f,
|
||||
0.004168190188f,
|
||||
0.004164168214f,
|
||||
0.004486022354f,
|
||||
0.005683994831f,
|
||||
0.005279489671f,
|
||||
0.005269099460f,
|
||||
0.005234577959f,
|
||||
0.004580867024f,
|
||||
0.004711190832f,
|
||||
0.004536180462f,
|
||||
0.005037524314f,
|
||||
0.005587879717f,
|
||||
0.005645298559f,
|
||||
0.005234577959f,
|
||||
0.005138602544f,
|
||||
// c = 2
|
||||
0.004694191704f,
|
||||
0.007478405841f,
|
||||
0.009119519544f,
|
||||
0.010846788859f,
|
||||
0.012040055008f,
|
||||
0.014283609506f,
|
||||
0.020805819128f,
|
||||
0.041346026531f,
|
||||
0.007478405841f,
|
||||
0.008473337032f,
|
||||
0.008457467755f,
|
||||
0.011507290737f,
|
||||
0.012282006381f,
|
||||
0.011077942494f,
|
||||
0.019589180487f,
|
||||
0.030348661601f,
|
||||
0.009119519544f,
|
||||
0.008457467755f,
|
||||
0.012692131754f,
|
||||
0.010360988009f,
|
||||
0.011883779193f,
|
||||
0.021216622915f,
|
||||
0.019468523508f,
|
||||
0.022375231013f,
|
||||
0.010846788859f,
|
||||
0.011507290737f,
|
||||
0.010360988009f,
|
||||
0.015688875916f,
|
||||
0.019428087454f,
|
||||
0.018982414995f,
|
||||
0.030218311113f,
|
||||
0.025108166811f,
|
||||
0.012040055008f,
|
||||
0.012282006381f,
|
||||
0.011883779193f,
|
||||
0.019428087454f,
|
||||
0.019908111501f,
|
||||
0.019428676375f,
|
||||
0.026540699320f,
|
||||
0.032446303017f,
|
||||
0.014283609506f,
|
||||
0.011077942494f,
|
||||
0.021216622915f,
|
||||
0.018982414995f,
|
||||
0.019428676375f,
|
||||
0.025654942665f,
|
||||
0.030689090332f,
|
||||
0.036234971093f,
|
||||
0.020805819128f,
|
||||
0.019589180487f,
|
||||
0.019468523508f,
|
||||
0.030218311113f,
|
||||
0.026540699320f,
|
||||
0.030689090332f,
|
||||
0.035378000966f,
|
||||
0.041109510150f,
|
||||
0.041346026531f,
|
||||
0.030348661601f,
|
||||
0.022375231013f,
|
||||
0.025108166811f,
|
||||
0.032446303017f,
|
||||
0.036234971093f,
|
||||
0.041109510150f,
|
||||
0.047241950370f,
|
||||
};
|
||||
|
||||
void AddJpegQuantMatrices(const ImageF& qf, float dc_quant, float global_scale,
|
||||
std::vector<jpeg::JPEGQuantTable>* quant_tables,
|
||||
float* qm) {
|
||||
// Create a custom JPEG XL dequant matrix. The quantization weight parameters
|
||||
// were determined with manual tweaking.
|
||||
DequantMatrices dequant;
|
||||
std::vector<QuantEncoding> encodings(DequantMatrices::kNum,
|
||||
QuantEncoding::Library(0));
|
||||
encodings[0] = QuantEncoding::DCT(
|
||||
DctQuantWeightParams({{{{2000.0, -0.5, -0.5, -0.5, -0.5, -0.5}},
|
||||
{{500.0, -0.1, -0.1, -0.2, -0.2, -0.2}},
|
||||
{{200.0, -1.0, -0.5, -0.5, -0.5, -0.5}}}},
|
||||
6));
|
||||
dequant.SetEncodings(encodings);
|
||||
JXL_CHECK(dequant.EnsureComputed(1));
|
||||
memcpy(qm, dequant.Matrix(0, 0), 3 * kDCTBlockSize * sizeof(qm[0]));
|
||||
// Set custom DC quant weights.
|
||||
const float inv_dc_quant[3] = {3200.0f, 512.0f, 320.0f};
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
qm[c * kDCTBlockSize] = 1.0f / (inv_dc_quant[c] * dc_quant);
|
||||
}
|
||||
|
||||
// Scale the quant matrix based on the scaled XYB scales and the quant field.
|
||||
// Scale the base quant matrix based on the scaled XYB scales and the quant
|
||||
// field.
|
||||
float qfmin, qfmax;
|
||||
ImageMinMax(qf, &qfmin, &qfmax);
|
||||
for (size_t c = 0; c < 3; c++) {
|
||||
const float scale = kScaledXYBScale[c] * global_scale;
|
||||
qm[c * kDCTBlockSize] *= scale;
|
||||
for (size_t j = 1; j < kDCTBlockSize; j++) {
|
||||
qm[c * kDCTBlockSize + j] *= scale / qfmax;
|
||||
const float dc_scale = global_scale / dc_quant;
|
||||
const float ac_scale = global_scale / qfmax;
|
||||
for (size_t c = 0, ix = 0; c < 3; c++) {
|
||||
qm[ix] = dc_scale * kBaseQuantMatrix[ix];
|
||||
ix++;
|
||||
for (size_t j = 1; j < kDCTBlockSize; j++, ix++) {
|
||||
qm[ix] = ac_scale * kBaseQuantMatrix[ix];
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,9 +374,34 @@ void AddJpegScanInfos(const std::vector<ProgressiveScan>& scans,
|
||||
}
|
||||
}
|
||||
|
||||
float HistogramCost(const Histogram& histo) {
|
||||
std::vector<uint32_t> counts(jpeg::kJpegHuffmanAlphabetSize + 1);
|
||||
std::vector<uint8_t> depths(jpeg::kJpegHuffmanAlphabetSize + 1);
|
||||
for (size_t i = 0; i < jpeg::kJpegHuffmanAlphabetSize; ++i) {
|
||||
counts[i] = histo.data_[i];
|
||||
}
|
||||
counts[jpeg::kJpegHuffmanAlphabetSize] = 1;
|
||||
CreateHuffmanTree(counts.data(), counts.size(),
|
||||
jpeg::kJpegHuffmanMaxBitLength, &depths[0]);
|
||||
size_t header_bits = (1 + jpeg::kJpegHuffmanMaxBitLength) * 8;
|
||||
size_t data_bits = 0;
|
||||
for (size_t i = 0; i < jpeg::kJpegHuffmanAlphabetSize; ++i) {
|
||||
if (depths[i] > 0) {
|
||||
header_bits += 8;
|
||||
data_bits += counts[i] * depths[i];
|
||||
}
|
||||
}
|
||||
return header_bits + data_bits;
|
||||
}
|
||||
|
||||
struct JpegClusteredHistograms {
|
||||
std::vector<Histogram> histograms;
|
||||
std::vector<uint32_t> histogram_indexes;
|
||||
std::vector<uint32_t> slot_ids;
|
||||
};
|
||||
|
||||
void ClusterJpegHistograms(const std::vector<jpeg::HuffmanCodeTable>& jpeg_in,
|
||||
size_t max_histograms, std::vector<Histogram>* out,
|
||||
std::vector<uint32_t>* histogram_indexes) {
|
||||
JpegClusteredHistograms* clusters) {
|
||||
std::vector<Histogram> histograms;
|
||||
for (const auto& t : jpeg_in) {
|
||||
Histogram histo;
|
||||
@ -206,17 +412,70 @@ void ClusterJpegHistograms(const std::vector<jpeg::HuffmanCodeTable>& jpeg_in,
|
||||
}
|
||||
histograms.push_back(histo);
|
||||
}
|
||||
// TODO(szabadka): Use a JPEG-specific version of the clustering algorithm.
|
||||
HistogramParams params;
|
||||
ClusterHistograms(params, histograms, max_histograms, out, histogram_indexes);
|
||||
clusters->histogram_indexes.resize(histograms.size());
|
||||
std::vector<uint32_t> slot_histograms;
|
||||
std::vector<float> slot_costs;
|
||||
for (size_t i = 0; i < histograms.size(); ++i) {
|
||||
const Histogram& cur = histograms[i];
|
||||
if (cur.total_count_ == 0) {
|
||||
continue;
|
||||
}
|
||||
float best_cost = HistogramCost(cur);
|
||||
size_t best_slot = slot_histograms.size();
|
||||
for (size_t j = 0; j < slot_histograms.size(); ++j) {
|
||||
size_t prev_idx = slot_histograms[j];
|
||||
const Histogram& prev = clusters->histograms[prev_idx];
|
||||
Histogram combined;
|
||||
combined.AddHistogram(prev);
|
||||
combined.AddHistogram(cur);
|
||||
float combined_cost = HistogramCost(combined);
|
||||
float cost = combined_cost - slot_costs[j];
|
||||
if (cost < best_cost) {
|
||||
best_cost = cost;
|
||||
best_slot = j;
|
||||
}
|
||||
}
|
||||
if (best_slot == slot_histograms.size()) {
|
||||
// Create new histogram.
|
||||
size_t histogram_index = clusters->histograms.size();
|
||||
clusters->histograms.push_back(cur);
|
||||
clusters->histogram_indexes[i] = histogram_index;
|
||||
if (best_slot < 4) {
|
||||
// We have a free slot, so we put the new histogram there.
|
||||
slot_histograms.push_back(histogram_index);
|
||||
slot_costs.push_back(best_cost);
|
||||
} else {
|
||||
// TODO(szabadka) Find the best histogram to replce.
|
||||
best_slot = (clusters->slot_ids.back() + 1) % 4;
|
||||
}
|
||||
slot_histograms[best_slot] = histogram_index;
|
||||
slot_costs[best_slot] = best_cost;
|
||||
clusters->slot_ids.push_back(best_slot);
|
||||
} else {
|
||||
// Merge this histogram with a previous one.
|
||||
size_t histogram_index = slot_histograms[best_slot];
|
||||
clusters->histograms[histogram_index].AddHistogram(cur);
|
||||
clusters->histogram_indexes[i] = histogram_index;
|
||||
JXL_ASSERT(clusters->slot_ids[histogram_index] == best_slot);
|
||||
slot_costs[best_slot] += best_cost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BuildJpegHuffmanCode(const uint8_t* depth, jpeg::JPEGHuffmanCode* huff) {
|
||||
void BuildJpegHuffmanCode(const Histogram& histo, jpeg::JPEGHuffmanCode* huff) {
|
||||
std::vector<uint32_t> counts(jpeg::kJpegHuffmanAlphabetSize + 1);
|
||||
std::vector<uint8_t> depths(jpeg::kJpegHuffmanAlphabetSize + 1);
|
||||
for (size_t j = 0; j < jpeg::kJpegHuffmanAlphabetSize; ++j) {
|
||||
counts[j] = histo.data_[j];
|
||||
}
|
||||
counts[jpeg::kJpegHuffmanAlphabetSize] = 1;
|
||||
CreateHuffmanTree(counts.data(), counts.size(),
|
||||
jpeg::kJpegHuffmanMaxBitLength, &depths[0]);
|
||||
std::fill(std::begin(huff->counts), std::end(huff->counts), 0);
|
||||
std::fill(std::begin(huff->values), std::end(huff->values), 0);
|
||||
for (size_t i = 0; i <= jpeg::kJpegHuffmanAlphabetSize; ++i) {
|
||||
if (depth[i] > 0) {
|
||||
++huff->counts[depth[i]];
|
||||
if (depths[i] > 0) {
|
||||
++huff->counts[depths[i]];
|
||||
}
|
||||
}
|
||||
int offset[jpeg::kJpegHuffmanMaxBitLength + 1] = {0};
|
||||
@ -224,29 +483,33 @@ void BuildJpegHuffmanCode(const uint8_t* depth, jpeg::JPEGHuffmanCode* huff) {
|
||||
offset[i] = offset[i - 1] + huff->counts[i - 1];
|
||||
}
|
||||
for (size_t i = 0; i <= jpeg::kJpegHuffmanAlphabetSize; ++i) {
|
||||
if (depth[i] > 0) {
|
||||
huff->values[offset[depth[i]]++] = i;
|
||||
if (depths[i] > 0) {
|
||||
huff->values[offset[depths[i]]++] = i;
|
||||
}
|
||||
}
|
||||
huff->is_last = false;
|
||||
}
|
||||
|
||||
void AddJpegHuffmanCodes(std::vector<Histogram>& histograms,
|
||||
size_t slot_id_offset,
|
||||
std::vector<jpeg::JPEGHuffmanCode>* huff_codes) {
|
||||
for (size_t i = 0; i < histograms.size(); ++i) {
|
||||
jpeg::JPEGHuffmanCode huff_code;
|
||||
huff_code.slot_id = slot_id_offset + i;
|
||||
std::vector<uint32_t> counts(jpeg::kJpegHuffmanAlphabetSize + 1);
|
||||
std::vector<uint8_t> depths(jpeg::kJpegHuffmanAlphabetSize + 1);
|
||||
for (size_t j = 0; j < jpeg::kJpegHuffmanAlphabetSize; ++j) {
|
||||
counts[j] = histograms[i].data_[j];
|
||||
}
|
||||
counts[jpeg::kJpegHuffmanAlphabetSize] = 1;
|
||||
CreateHuffmanTree(counts.data(), counts.size(),
|
||||
jpeg::kJpegHuffmanMaxBitLength, &depths[0]);
|
||||
BuildJpegHuffmanCode(&depths[0], &huff_code);
|
||||
huff_codes->emplace_back(std::move(huff_code));
|
||||
void AddJpegHuffmanCode(const Histogram& histogram, size_t slot_id,
|
||||
std::vector<jpeg::JPEGHuffmanCode>* huff_codes) {
|
||||
jpeg::JPEGHuffmanCode huff_code;
|
||||
huff_code.slot_id = slot_id;
|
||||
BuildJpegHuffmanCode(histogram, &huff_code);
|
||||
huff_codes->emplace_back(std::move(huff_code));
|
||||
}
|
||||
|
||||
void SetJpegHuffmanCode(const JpegClusteredHistograms& clusters,
|
||||
size_t histogram_id, size_t slot_id_offset,
|
||||
std::vector<uint32_t>& slot_histograms,
|
||||
uint32_t* slot_id,
|
||||
std::vector<jpeg::JPEGHuffmanCode>* huff_codes) {
|
||||
JXL_ASSERT(histogram_id < clusters.histogram_indexes.size());
|
||||
uint32_t histogram_index = clusters.histogram_indexes[histogram_id];
|
||||
*slot_id = clusters.slot_ids[histogram_index];
|
||||
if (slot_histograms[*slot_id] != histogram_index) {
|
||||
AddJpegHuffmanCode(clusters.histograms[histogram_index],
|
||||
slot_id_offset + *slot_id, huff_codes);
|
||||
slot_histograms[*slot_id] = histogram_index;
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,11 +554,8 @@ void FillJPEGData(const Image3F& opsin, const ImageF& qf, float dc_quant,
|
||||
// SOS
|
||||
std::vector<ProgressiveScan> progressive_mode = {
|
||||
// DC
|
||||
{0, 0, 0, 0, !subsample_blue},
|
||||
// AC 1 - highest bits
|
||||
{1, 63, 0, 1, false},
|
||||
// AC 2 - lowest bit
|
||||
{1, 63, 1, 0, false},
|
||||
{0, 0, 0, 0, !subsample_blue}, {1, 2, 0, 0, false}, {3, 63, 0, 2, false},
|
||||
{3, 63, 2, 1, false}, {3, 63, 1, 0, false},
|
||||
};
|
||||
AddJpegScanInfos(progressive_mode, &out->scan_info);
|
||||
for (size_t i = 0; i < out->scan_info.size(); i++) {
|
||||
@ -309,30 +569,58 @@ void FillJPEGData(const Image3F& opsin, const ImageF& qf, float dc_quant,
|
||||
jpeg::SerializationState ss;
|
||||
JXL_CHECK(jpeg::ProcessJpeg(*out, &ss));
|
||||
|
||||
// Build DC Huffman codes and add them to DHT segment.
|
||||
std::vector<Histogram> dc_histo;
|
||||
std::vector<uint32_t> dc_tbl_indexes;
|
||||
ClusterJpegHistograms(ss.dc_huff_table, 4, &dc_histo, &dc_tbl_indexes);
|
||||
AddJpegHuffmanCodes(dc_histo, 0, &out->huffman_code);
|
||||
// Cluster DC histograms.
|
||||
JpegClusteredHistograms dc_clusters;
|
||||
ClusterJpegHistograms(ss.dc_huff_table, &dc_clusters);
|
||||
|
||||
// Build AC Huffman codes and add them to DHT segment.
|
||||
std::vector<Histogram> ac_histo;
|
||||
std::vector<uint32_t> ac_tbl_indexes;
|
||||
ClusterJpegHistograms(ss.ac_huff_table, 4, &ac_histo, &ac_tbl_indexes);
|
||||
AddJpegHuffmanCodes(ac_histo, 0x10, &out->huffman_code);
|
||||
// Cluster AC histograms.
|
||||
JpegClusteredHistograms ac_clusters;
|
||||
ClusterJpegHistograms(ss.ac_huff_table, &ac_clusters);
|
||||
|
||||
// Rebuild marker_order because we may want to emit Huffman codes in between
|
||||
// scans.
|
||||
out->marker_order.resize(out->marker_order.size() - 2 -
|
||||
out->scan_info.size());
|
||||
|
||||
// Add the first 4 DC and AC histograms in the first DHT segment.
|
||||
out->marker_order.emplace_back(0xc4); // DHT
|
||||
std::vector<uint32_t> dc_slot_histograms;
|
||||
std::vector<uint32_t> ac_slot_histograms;
|
||||
for (size_t i = 0; i < dc_clusters.histograms.size(); ++i) {
|
||||
if (i >= 4) break;
|
||||
JXL_ASSERT(dc_clusters.slot_ids[i] == i);
|
||||
AddJpegHuffmanCode(dc_clusters.histograms[i], i, &out->huffman_code);
|
||||
dc_slot_histograms.push_back(i);
|
||||
}
|
||||
for (size_t i = 0; i < ac_clusters.histograms.size(); ++i) {
|
||||
if (i >= 4) break;
|
||||
JXL_ASSERT(ac_clusters.slot_ids[i] == i);
|
||||
AddJpegHuffmanCode(ac_clusters.histograms[i], 0x10 + i, &out->huffman_code);
|
||||
ac_slot_histograms.push_back(i);
|
||||
}
|
||||
out->huffman_code.back().is_last = true;
|
||||
|
||||
// Set the Huffman table indexes in the scan_infos.
|
||||
size_t histo_idx = 0;
|
||||
// Set the Huffman table indexes in the scan_infos and emit additional DHT
|
||||
// segments if necessary.
|
||||
size_t histogram_id = 0;
|
||||
for (auto& si : out->scan_info) {
|
||||
size_t num_huff_codes = out->huffman_code.size();
|
||||
for (size_t c = 0; c < si.num_components; ++c) {
|
||||
JXL_ASSERT(histo_idx < dc_tbl_indexes.size());
|
||||
JXL_ASSERT(histo_idx < ac_tbl_indexes.size());
|
||||
si.components[c].dc_tbl_idx = dc_tbl_indexes[histo_idx];
|
||||
si.components[c].ac_tbl_idx = ac_tbl_indexes[histo_idx];
|
||||
++histo_idx;
|
||||
SetJpegHuffmanCode(dc_clusters, histogram_id, 0, dc_slot_histograms,
|
||||
&si.components[c].dc_tbl_idx, &out->huffman_code);
|
||||
SetJpegHuffmanCode(ac_clusters, histogram_id, 0x10, ac_slot_histograms,
|
||||
&si.components[c].ac_tbl_idx, &out->huffman_code);
|
||||
++histogram_id;
|
||||
}
|
||||
// If we updated a slot histogram in this scan, create an additional DHT
|
||||
// segment.
|
||||
if (out->huffman_code.size() > num_huff_codes) {
|
||||
out->marker_order.emplace_back(0xc4); // DHT
|
||||
out->huffman_code.back().is_last = true;
|
||||
}
|
||||
out->marker_order.emplace_back(0xda); // SOS
|
||||
}
|
||||
out->marker_order.emplace_back(0xd9); // EOI
|
||||
}
|
||||
|
||||
size_t JpegSize(const jpeg::JPEGData& jpeg_data) {
|
||||
|
@ -71,15 +71,6 @@ typedef struct {
|
||||
uint32_t ysize;
|
||||
} JxlPreviewHeader;
|
||||
|
||||
/** The intrinsic size header */
|
||||
typedef struct {
|
||||
/** Intrinsic width in pixels */
|
||||
uint32_t xsize;
|
||||
|
||||
/** Intrinsic height in pixels */
|
||||
uint32_t ysize;
|
||||
} JxlIntrinsicSizeHeader;
|
||||
|
||||
/** The codestream animation header, optionally present in the beginning of
|
||||
* the codestream, and if it is it applies to all animation frames, unlike
|
||||
* JxlFrameHeader which applies to an individual frame.
|
||||
|
1
third_party/jpeg-xl/lib/include/jxl/types.h
vendored
1
third_party/jpeg-xl/lib/include/jxl/types.h
vendored
@ -81,7 +81,6 @@ typedef enum {
|
||||
* for pixels. This is not necessarily the same as the data type encoded in the
|
||||
* codestream. The channels are interleaved per pixel. The pixels are
|
||||
* organized row by row, left to right, top to bottom.
|
||||
* TODO(lode): implement padding / alignment (row stride)
|
||||
* TODO(lode): support different channel orders if needed (RGB, BGR, ...)
|
||||
*/
|
||||
typedef struct {
|
||||
|
52
third_party/jpeg-xl/lib/jxl.cmake
vendored
52
third_party/jpeg-xl/lib/jxl.cmake
vendored
@ -46,8 +46,6 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
||||
jxl/base/thread_pool_internal.h
|
||||
jxl/blending.cc
|
||||
jxl/blending.h
|
||||
jxl/box_content_decoder.cc
|
||||
jxl/box_content_decoder.h
|
||||
jxl/chroma_from_luma.cc
|
||||
jxl/chroma_from_luma.h
|
||||
jxl/codec_in_out.h
|
||||
@ -102,8 +100,6 @@ 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
|
||||
@ -143,14 +139,6 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
||||
jxl/image_metadata.cc
|
||||
jxl/image_metadata.h
|
||||
jxl/image_ops.h
|
||||
jxl/jpeg/dec_jpeg_data.cc
|
||||
jxl/jpeg/dec_jpeg_data.h
|
||||
jxl/jpeg/dec_jpeg_data_writer.cc
|
||||
jxl/jpeg/dec_jpeg_data_writer.h
|
||||
jxl/jpeg/dec_jpeg_output_chunk.h
|
||||
jxl/jpeg/dec_jpeg_serialization_state.h
|
||||
jxl/jpeg/jpeg_data.cc
|
||||
jxl/jpeg/jpeg_data.h
|
||||
jxl/jxl_inspection.h
|
||||
jxl/lehmer_code.h
|
||||
jxl/linalg.h
|
||||
@ -238,6 +226,30 @@ set(JPEGXL_INTERNAL_SOURCES_DEC
|
||||
jxl/xorshift128plus-inl.h
|
||||
)
|
||||
|
||||
if (JPEGXL_ENABLE_TRANSCODE_JPEG OR JPEGXL_ENABLE_TOOLS OR JPEGXL_ENABLE_DEVTOOLS OR JPEGXL_ENABLE_BOXES)
|
||||
list(APPEND JPEGXL_INTERNAL_SOURCES_DEC
|
||||
jxl/decode_to_jpeg.cc
|
||||
jxl/decode_to_jpeg.h
|
||||
jxl/box_content_decoder.cc
|
||||
jxl/box_content_decoder.h
|
||||
)
|
||||
endif()
|
||||
|
||||
if (JPEGXL_ENABLE_TRANSCODE_JPEG OR JPEGXL_ENABLE_TOOLS OR JPEGXL_ENABLE_DEVTOOLS)
|
||||
list(APPEND JPEGXL_INTERNAL_SOURCES_DEC
|
||||
jxl/decode_to_jpeg.cc
|
||||
jxl/decode_to_jpeg.h
|
||||
jxl/jpeg/dec_jpeg_data.cc
|
||||
jxl/jpeg/dec_jpeg_data.h
|
||||
jxl/jpeg/dec_jpeg_data_writer.cc
|
||||
jxl/jpeg/dec_jpeg_data_writer.h
|
||||
jxl/jpeg/dec_jpeg_output_chunk.h
|
||||
jxl/jpeg/dec_jpeg_serialization_state.h
|
||||
jxl/jpeg/jpeg_data.cc
|
||||
jxl/jpeg/jpeg_data.h
|
||||
)
|
||||
endif()
|
||||
|
||||
# List of source files only needed by the encoder or by tools (including
|
||||
# decoding tools), but not by the decoder library.
|
||||
set(JPEGXL_INTERNAL_SOURCES_ENC
|
||||
@ -349,13 +361,15 @@ set(JPEGXL_INTERNAL_SOURCES_ENC
|
||||
)
|
||||
|
||||
set(JPEGXL_DEC_INTERNAL_LIBS
|
||||
brotlidec-static
|
||||
brotlicommon-static
|
||||
hwy
|
||||
Threads::Threads
|
||||
${ATOMICS_LIBRARIES}
|
||||
)
|
||||
|
||||
if (JPEGXL_ENABLE_TRANSCODE_JPEG OR JPEGXL_ENABLE_BOXES)
|
||||
list(APPEND JPEGXL_DEC_INTERNAL_LIBS brotlidec-static brotlicommon-static)
|
||||
endif()
|
||||
|
||||
if(JPEGXL_ENABLE_PROFILER)
|
||||
list(APPEND JPEGXL_DEC_INTERNAL_LIBS jxl_profiler)
|
||||
endif()
|
||||
@ -386,10 +400,18 @@ else ()
|
||||
list(APPEND JPEGXL_INTERNAL_LIBS lcms2)
|
||||
endif ()
|
||||
|
||||
if (NOT JPEGXL_ENABLE_TRANSCODE_JPEG)
|
||||
if (JPEGXL_ENABLE_TRANSCODE_JPEG)
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_TRANSCODE_JPEG=1)
|
||||
else()
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_TRANSCODE_JPEG=0)
|
||||
endif ()
|
||||
|
||||
if (JPEGXL_ENABLE_BOXES)
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_BOXES=1)
|
||||
else()
|
||||
list(APPEND JPEGXL_INTERNAL_FLAGS -DJPEGXL_ENABLE_BOXES=0)
|
||||
endif ()
|
||||
|
||||
set(OBJ_COMPILE_DEFINITIONS
|
||||
JPEGXL_MAJOR_VERSION=${JPEGXL_MAJOR_VERSION}
|
||||
JPEGXL_MINOR_VERSION=${JPEGXL_MINOR_VERSION}
|
||||
|
5
third_party/jpeg-xl/lib/jxl/common.h
vendored
5
third_party/jpeg-xl/lib/jxl/common.h
vendored
@ -28,6 +28,11 @@
|
||||
#define JPEGXL_ENABLE_TRANSCODE_JPEG 1
|
||||
#endif // JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
|
||||
// Macro that defines whether support for decoding boxes is enabled.
|
||||
#ifndef JPEGXL_ENABLE_BOXES
|
||||
#define JPEGXL_ENABLE_BOXES 1
|
||||
#endif // JPEGXL_ENABLE_BOXES
|
||||
|
||||
namespace jxl {
|
||||
// Some enums and typedefs used by more than one header file.
|
||||
|
||||
|
70
third_party/jpeg-xl/lib/jxl/decode.cc
vendored
70
third_party/jpeg-xl/lib/jxl/decode.cc
vendored
@ -9,11 +9,15 @@
|
||||
#include "lib/jxl/base/byte_order.h"
|
||||
#include "lib/jxl/base/span.h"
|
||||
#include "lib/jxl/base/status.h"
|
||||
#if JPEGXL_ENABLE_BOXES || JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
#include "lib/jxl/box_content_decoder.h"
|
||||
#endif
|
||||
#include "lib/jxl/dec_external_image.h"
|
||||
#include "lib/jxl/dec_frame.h"
|
||||
#include "lib/jxl/dec_modular.h"
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
#include "lib/jxl/decode_to_jpeg.h"
|
||||
#endif
|
||||
#include "lib/jxl/fields.h"
|
||||
#include "lib/jxl/frame_header.h"
|
||||
#include "lib/jxl/headers.h"
|
||||
@ -507,8 +511,11 @@ struct JxlDecoderStruct {
|
||||
|
||||
BoxStage box_stage;
|
||||
|
||||
jxl::JxlToJpegDecoder jpeg_decoder;
|
||||
#if JPEGXL_ENABLE_BOXES
|
||||
jxl::JxlBoxContentDecoder box_content_decoder;
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
jxl::JxlToJpegDecoder jpeg_decoder;
|
||||
// Decodes Exif or XMP metadata for JPEG reconstruction
|
||||
jxl::JxlBoxContentDecoder metadata_decoder;
|
||||
std::vector<uint8_t> exif_metadata;
|
||||
@ -529,6 +536,7 @@ struct JxlDecoderStruct {
|
||||
if (store_xmp < 2 && recon_xmp_size > 0) return true;
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Statistics which CodecInOut can keep
|
||||
uint64_t dec_pixels;
|
||||
@ -695,6 +703,8 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
|
||||
dec->box_out_buffer_size = 0;
|
||||
dec->box_out_buffer_begin = 0;
|
||||
dec->box_out_buffer_pos = 0;
|
||||
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
dec->exif_metadata.clear();
|
||||
dec->xmp_metadata.clear();
|
||||
dec->store_exif = 0;
|
||||
@ -703,6 +713,7 @@ void JxlDecoderRewindDecodingState(JxlDecoder* dec) {
|
||||
dec->recon_exif_size = 0;
|
||||
dec->recon_xmp_size = 0;
|
||||
dec->recon_output_jpeg = JpegReconStage::kNone;
|
||||
#endif
|
||||
|
||||
dec->events_wanted = 0;
|
||||
dec->basic_info_size_hint = InitialBasicInfoSizeHint();
|
||||
@ -1210,6 +1221,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) {
|
||||
break;
|
||||
}
|
||||
if (dec->frame_stage == FrameStage::kHeader) {
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
if (dec->recon_output_jpeg == JpegReconStage::kSettingMetadata ||
|
||||
dec->recon_output_jpeg == JpegReconStage::kOutputting) {
|
||||
// The image bundle contains the JPEG reconstruction frame, but the
|
||||
@ -1219,14 +1231,16 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) {
|
||||
return JXL_API_ERROR(
|
||||
"cannot decode a next frame after JPEG reconstruction frame");
|
||||
}
|
||||
#endif
|
||||
if (!dec->ib) {
|
||||
dec->ib.reset(new jxl::ImageBundle(&dec->image_metadata));
|
||||
}
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
// If JPEG reconstruction is wanted and possible, set the jpeg_data of
|
||||
// the ImageBundle.
|
||||
if (!dec->jpeg_decoder.SetImageBundleJpegData(dec->ib.get()))
|
||||
return JXL_DEC_ERROR;
|
||||
|
||||
#endif
|
||||
dec->frame_dec.reset(new FrameDecoder(
|
||||
dec->passes_state.get(), dec->metadata, dec->thread_pool.get(),
|
||||
/*use_slow_rendering_pipeline=*/false));
|
||||
@ -1382,8 +1396,11 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) {
|
||||
if (dec->preview_frame) {
|
||||
return JXL_DEC_NEED_PREVIEW_OUT_BUFFER;
|
||||
}
|
||||
if ((!dec->jpeg_decoder.IsOutputSet() ||
|
||||
if (
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
(!dec->jpeg_decoder.IsOutputSet() ||
|
||||
dec->ib->jpeg_data == nullptr) &&
|
||||
#endif
|
||||
dec->is_last_of_still && !dec->skipping_frame) {
|
||||
// TODO(lode): remove the dec->is_last_of_still condition if the
|
||||
// frame decoder needs the image buffer as working space for decoding
|
||||
@ -1458,7 +1475,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) {
|
||||
if (!dec->frame_dec->FinalizeFrame()) {
|
||||
return JXL_API_ERROR("decoding frame failed");
|
||||
}
|
||||
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
// If jpeg output was requested, we merely return the JXL_DEC_FULL_IMAGE
|
||||
// status without outputting pixels.
|
||||
if (dec->jpeg_decoder.IsOutputSet() && dec->ib->jpeg_data != nullptr) {
|
||||
@ -1466,7 +1483,7 @@ JxlDecoderStatus JxlDecoderProcessCodestream(JxlDecoder* dec) {
|
||||
dec->recon_output_jpeg = JpegReconStage::kSettingMetadata;
|
||||
return JXL_DEC_FULL_IMAGE;
|
||||
}
|
||||
|
||||
#endif
|
||||
if (dec->preview_frame || dec->is_last_of_still) {
|
||||
dec->image_out_buffer_set = false;
|
||||
dec->extra_channel_output.clear();
|
||||
@ -1523,6 +1540,7 @@ void JxlDecoderCloseInput(JxlDecoder* dec) { dec->input_closed = true; }
|
||||
|
||||
JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec, uint8_t* data,
|
||||
size_t size) {
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
// JPEG reconstruction buffer can only set and updated before or during the
|
||||
// first frame, the reconstruction box refers to the first frame and in
|
||||
// theory multi-frame images should not be used with a jbrd box.
|
||||
@ -1533,10 +1551,17 @@ JxlDecoderStatus JxlDecoderSetJPEGBuffer(JxlDecoder* dec, uint8_t* data,
|
||||
return JXL_API_ERROR("Already set JPEG buffer");
|
||||
}
|
||||
return dec->jpeg_decoder.SetOutputBuffer(data, size);
|
||||
#else
|
||||
return JXL_API_ERROR("JPEG reconstruction is not supported.");
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t JxlDecoderReleaseJPEGBuffer(JxlDecoder* dec) {
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
return dec->jpeg_decoder.ReleaseOutputBuffer();
|
||||
#else
|
||||
return JXL_API_ERROR("JPEG reconstruction is not supported.");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Parses the header of the box, outputting the 4-character type and the box
|
||||
@ -1592,6 +1617,7 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
if (dec->box_stage != BoxStage::kHeader) {
|
||||
dec->AdvanceInput(dec->header_size);
|
||||
dec->header_size = 0;
|
||||
#if JPEGXL_ENABLE_BOXES
|
||||
if ((dec->events_wanted & JXL_DEC_BOX) &&
|
||||
dec->box_out_buffer_set_current_box) {
|
||||
uint8_t* next_out = dec->box_out_buffer + dec->box_out_buffer_pos;
|
||||
@ -1612,7 +1638,8 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
return box_result;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
if (dec->store_exif == 1 || dec->store_xmp == 1) {
|
||||
std::vector<uint8_t>& metadata =
|
||||
(dec->store_exif == 1) ? dec->exif_metadata : dec->xmp_metadata;
|
||||
@ -1650,8 +1677,9 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
if (dec->recon_output_jpeg == JpegReconStage::kSettingMetadata &&
|
||||
!dec->JbrdNeedMoreBoxes()) {
|
||||
jxl::jpeg::JPEGData* jpeg_data = dec->ib->jpeg_data.get();
|
||||
@ -1682,6 +1710,7 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
return JXL_DEC_FULL_IMAGE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (dec->box_stage == BoxStage::kHeader) {
|
||||
if (!dec->have_container) {
|
||||
@ -1696,9 +1725,11 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
// Not yet seen (all) codestream boxes.
|
||||
return JXL_DEC_NEED_MORE_INPUT;
|
||||
}
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
if (dec->JbrdNeedMoreBoxes()) {
|
||||
return JXL_DEC_NEED_MORE_INPUT;
|
||||
}
|
||||
#endif
|
||||
if (dec->input_closed) {
|
||||
return JXL_DEC_SUCCESS;
|
||||
}
|
||||
@ -1719,7 +1750,10 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
bool boxed_codestream_done =
|
||||
((dec->events_wanted & JXL_DEC_BOX) &&
|
||||
dec->stage == DecoderStage::kCodestreamFinished &&
|
||||
dec->last_codestream_seen && !dec->JbrdNeedMoreBoxes());
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
!dec->JbrdNeedMoreBoxes() &&
|
||||
#endif
|
||||
dec->last_codestream_seen);
|
||||
if (boxed_codestream_done && dec->avail_in >= 2 &&
|
||||
dec->next_in[0] == 0xff &&
|
||||
dec->next_in[1] == jxl::kCodestreamMarker) {
|
||||
@ -1774,7 +1808,7 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
dec->box_contents_unbounded ? 0 : (box_size - header_size);
|
||||
dec->box_size = box_size;
|
||||
dec->header_size = header_size;
|
||||
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
if (dec->orig_events_wanted & JXL_DEC_JPEG_RECONSTRUCTION) {
|
||||
// Initiate storing of Exif or XMP data for JPEG reconstruction
|
||||
if (dec->store_exif == 0 &&
|
||||
@ -1788,19 +1822,22 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
dec->recon_out_buffer_pos = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_BOXES
|
||||
if (dec->events_wanted & JXL_DEC_BOX) {
|
||||
bool decompress =
|
||||
dec->decompress_boxes && memcmp(dec->box_type, "brob", 4) == 0;
|
||||
dec->box_content_decoder.StartBox(
|
||||
decompress, dec->box_contents_unbounded, dec->box_contents_size);
|
||||
}
|
||||
#endif
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
if (dec->store_exif == 1 || dec->store_xmp == 1) {
|
||||
bool brob = memcmp(dec->box_type, "brob", 4) == 0;
|
||||
dec->metadata_decoder.StartBox(brob, dec->box_contents_unbounded,
|
||||
dec->box_contents_size);
|
||||
}
|
||||
|
||||
#endif
|
||||
if (memcmp(dec->box_type, "ftyp", 4) == 0) {
|
||||
dec->box_stage = BoxStage::kFtyp;
|
||||
} else if (memcmp(dec->box_type, "jxlc", 4) == 0) {
|
||||
@ -1811,6 +1848,7 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
dec->box_stage = BoxStage::kCodestream;
|
||||
} else if (memcmp(dec->box_type, "jxlp", 4) == 0) {
|
||||
dec->box_stage = BoxStage::kPartialCodestream;
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
} else if ((dec->orig_events_wanted & JXL_DEC_JPEG_RECONSTRUCTION) &&
|
||||
memcmp(dec->box_type, "jbrd", 4) == 0) {
|
||||
if (!(dec->events_wanted & JXL_DEC_JPEG_RECONSTRUCTION)) {
|
||||
@ -1818,6 +1856,7 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
"multiple JPEG reconstruction boxes not supported");
|
||||
}
|
||||
dec->box_stage = BoxStage::kJpegRecon;
|
||||
#endif
|
||||
} else {
|
||||
dec->box_stage = BoxStage::kSkip;
|
||||
}
|
||||
@ -1856,11 +1895,13 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
dec->box_stage = BoxStage::kCodestream;
|
||||
} else if (dec->box_stage == BoxStage::kCodestream) {
|
||||
JxlDecoderStatus status = jxl::JxlDecoderProcessCodestream(dec);
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
if (status == JXL_DEC_FULL_IMAGE) {
|
||||
if (dec->recon_output_jpeg != JpegReconStage::kNone) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
if (dec->file_pos == dec->box_contents_end &&
|
||||
!dec->box_contents_unbounded) {
|
||||
@ -1870,10 +1911,12 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
}
|
||||
|
||||
if (status == JXL_DEC_SUCCESS) {
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
if (dec->JbrdNeedMoreBoxes()) {
|
||||
dec->box_stage = BoxStage::kSkip;
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (dec->box_contents_unbounded) {
|
||||
// Last box reached and codestream done, nothing more to do.
|
||||
break;
|
||||
@ -1885,6 +1928,7 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
}
|
||||
}
|
||||
return status;
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
} else if (dec->box_stage == BoxStage::kJpegRecon) {
|
||||
if (!dec->jpeg_decoder.IsParsingBox()) {
|
||||
// This is a new JPEG reconstruction metadata box.
|
||||
@ -1933,6 +1977,7 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
// If anything else, return the result.
|
||||
return recon_result;
|
||||
}
|
||||
#endif
|
||||
} else if (dec->box_stage == BoxStage::kSkip) {
|
||||
if (dec->box_contents_unbounded) {
|
||||
if (dec->input_closed) {
|
||||
@ -1968,7 +2013,6 @@ static JxlDecoderStatus HandleBoxes(JxlDecoder* dec) {
|
||||
JXL_DASSERT(false); // unknown box stage
|
||||
}
|
||||
}
|
||||
|
||||
return JXL_DEC_SUCCESS;
|
||||
}
|
||||
|
||||
@ -2013,9 +2057,11 @@ JxlDecoderStatus JxlDecoderProcessInput(JxlDecoder* dec) {
|
||||
if (dec->CanUseMoreCodestreamInput()) {
|
||||
return JXL_API_ERROR("codestream never finished");
|
||||
}
|
||||
#if JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
if (dec->JbrdNeedMoreBoxes()) {
|
||||
return JXL_API_ERROR("missing metadata boxes for jpeg reconstruction");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return status;
|
||||
|
6
third_party/jpeg-xl/lib/jxl/decode_test.cc
vendored
6
third_party/jpeg-xl/lib/jxl/decode_test.cc
vendored
@ -4967,7 +4967,7 @@ TEST(DecodeTest, ExtentedBoxSizeTest) {
|
||||
JxlDecoderDestroy(dec);
|
||||
}
|
||||
|
||||
TEST(DecodeTest, BoxTest) {
|
||||
TEST(DecodeTest, JXL_BOXES_TEST(BoxTest)) {
|
||||
size_t xsize = 1, ysize = 1;
|
||||
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
|
||||
jxl::TestCodestreamParams params;
|
||||
@ -5040,7 +5040,7 @@ TEST(DecodeTest, BoxTest) {
|
||||
JxlDecoderDestroy(dec);
|
||||
}
|
||||
|
||||
TEST(DecodeTest, ExifBrobBoxTest) {
|
||||
TEST(DecodeTest, JXL_BOXES_TEST(ExifBrobBoxTest)) {
|
||||
size_t xsize = 1, ysize = 1;
|
||||
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
|
||||
jxl::TestCodestreamParams params;
|
||||
@ -5222,7 +5222,7 @@ TEST(DecodeTest, ExifBrobBoxTest) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(DecodeTest, PartialCodestreamBoxTest) {
|
||||
TEST(DecodeTest, JXL_BOXES_TEST(PartialCodestreamBoxTest)) {
|
||||
size_t xsize = 23, ysize = 81;
|
||||
std::vector<uint8_t> pixels = jxl::test::GetSomeTestImage(xsize, ysize, 4, 0);
|
||||
JxlPixelFormat format_orig = {4, JXL_TYPE_UINT16, JXL_BIG_ENDIAN, 0};
|
||||
|
@ -840,6 +840,35 @@ Status ApplyHlgOotf(JxlCms* t, float* JXL_RESTRICT buf, size_t xsize,
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ApplyCICP(const uint8_t color_primaries,
|
||||
const uint8_t transfer_characteristics,
|
||||
const uint8_t matrix_coefficients, const uint8_t full_range,
|
||||
ColorEncoding* JXL_RESTRICT c) {
|
||||
if (matrix_coefficients != 0) return false;
|
||||
if (full_range != 1) return false;
|
||||
|
||||
const auto primaries = static_cast<Primaries>(color_primaries);
|
||||
const auto tf = static_cast<TransferFunction>(transfer_characteristics);
|
||||
if (!EnumValid(tf) || tf == TransferFunction::kUnknown) return false;
|
||||
if (!(EnumValid(primaries) || color_primaries == 12) ||
|
||||
primaries == Primaries::kCustom) {
|
||||
return false;
|
||||
}
|
||||
c->SetColorSpace(ColorSpace::kRGB);
|
||||
c->tf.SetTransferFunction(tf);
|
||||
if (primaries == Primaries::kP3) {
|
||||
c->white_point = WhitePoint::kDCI;
|
||||
c->primaries = Primaries::kP3;
|
||||
} else if (color_primaries == 12) {
|
||||
c->white_point = WhitePoint::kD65;
|
||||
c->primaries = Primaries::kP3;
|
||||
} else {
|
||||
c->white_point = WhitePoint::kD65;
|
||||
c->primaries = primaries;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status ColorEncoding::SetFieldsFromICC() {
|
||||
@ -864,6 +893,15 @@ Status ColorEncoding::SetFieldsFromICC() {
|
||||
icc_[66] != 0) {
|
||||
return JXL_FAILURE("Invalid rendering intent %u\n", rendering_intent32);
|
||||
}
|
||||
// ICC and RenderingIntent have the same values (0..3).
|
||||
rendering_intent = static_cast<RenderingIntent>(rendering_intent32);
|
||||
|
||||
if (profile.has_CICP && ApplyCICP(profile.CICP.color_primaries,
|
||||
profile.CICP.transfer_characteristics,
|
||||
profile.CICP.matrix_coefficients,
|
||||
profile.CICP.video_full_range_flag, this)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
SetColorSpace(ColorSpaceFromProfile(profile));
|
||||
cmyk_ = (profile.data_color_space == skcms_Signature_CMYK);
|
||||
@ -877,8 +915,6 @@ Status ColorEncoding::SetFieldsFromICC() {
|
||||
|
||||
// Relies on color_space/white point/primaries being set already.
|
||||
DetectTransferFunction(profile, this);
|
||||
// ICC and RenderingIntent have the same values (0..3).
|
||||
rendering_intent = static_cast<RenderingIntent>(rendering_intent32);
|
||||
#else // JPEGXL_ENABLE_SKCMS
|
||||
|
||||
const cmsContext context = GetContext();
|
||||
@ -886,6 +922,17 @@ Status ColorEncoding::SetFieldsFromICC() {
|
||||
Profile profile;
|
||||
JXL_RETURN_IF_ERROR(DecodeProfile(context, icc_, &profile));
|
||||
|
||||
static constexpr size_t kCICPSize = 12;
|
||||
static constexpr auto kCICPSignature =
|
||||
static_cast<cmsTagSignature>(0x63696370);
|
||||
uint8_t cicp_buffer[kCICPSize];
|
||||
if (cmsReadRawTag(profile.get(), kCICPSignature, cicp_buffer, kCICPSize) ==
|
||||
kCICPSize &&
|
||||
ApplyCICP(cicp_buffer[8], cicp_buffer[9], cicp_buffer[10],
|
||||
cicp_buffer[11], this)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const cmsUInt32Number rendering_intent32 =
|
||||
cmsGetHeaderRenderingIntent(profile.get());
|
||||
if (rendering_intent32 > 3) {
|
||||
@ -916,23 +963,18 @@ Status ColorEncoding::SetFieldsFromICC() {
|
||||
|
||||
void ColorEncoding::DecideIfWantICC() {
|
||||
PaddedBytes icc_new;
|
||||
bool equivalent;
|
||||
#if JPEGXL_ENABLE_SKCMS
|
||||
skcms_ICCProfile profile;
|
||||
if (!DecodeProfile(ICC().data(), ICC().size(), &profile)) return;
|
||||
if (!MaybeCreateProfile(*this, &icc_new)) return;
|
||||
equivalent = ProfileEquivalentToICC(profile, icc_new);
|
||||
#else // JPEGXL_ENABLE_SKCMS
|
||||
const cmsContext context = GetContext();
|
||||
Profile profile;
|
||||
if (!DecodeProfile(context, ICC(), &profile)) return;
|
||||
if (cmsGetColorSpace(profile.get()) == cmsSigCmykData) return;
|
||||
if (!MaybeCreateProfile(*this, &icc_new)) return;
|
||||
equivalent = ProfileEquivalentToICC(context, profile, icc_new, *this);
|
||||
#endif // JPEGXL_ENABLE_SKCMS
|
||||
|
||||
// Successfully created a profile => reconstruction should be equivalent.
|
||||
JXL_ASSERT(equivalent);
|
||||
want_icc_ = false;
|
||||
}
|
||||
|
||||
|
4
third_party/jpeg-xl/lib/jxl/enc_modular.cc
vendored
4
third_party/jpeg-xl/lib/jxl/enc_modular.cc
vendored
@ -42,6 +42,8 @@
|
||||
namespace jxl {
|
||||
|
||||
namespace {
|
||||
constexpr bool kPrintTree = false;
|
||||
|
||||
// Squeeze default quantization factors
|
||||
// these quantization factors are for -Q 50 (other qualities simply scale the
|
||||
// factors; things are rounded down and obviously cannot get below 1)
|
||||
@ -1090,7 +1092,7 @@ Status ModularFrameEncoder::PrepareEncoding(const FrameHeader& frame_header,
|
||||
JXL_ASSERT(tree_.size() == decoded_tree.size());
|
||||
tree_ = std::move(decoded_tree);
|
||||
|
||||
if (WantDebugOutput(aux_out)) {
|
||||
if (kPrintTree && WantDebugOutput(aux_out)) {
|
||||
if (frame_header.dc_level > 0) {
|
||||
PrintTree(tree_, aux_out->debug_prefix + "/dc_frame_level" +
|
||||
std::to_string(frame_header.dc_level) + "_tree");
|
||||
|
22
third_party/jpeg-xl/lib/jxl/encode.cc
vendored
22
third_party/jpeg-xl/lib/jxl/encode.cc
vendored
@ -762,6 +762,18 @@ JxlEncoderStatus JxlEncoderSetBasicInfo(JxlEncoder* enc,
|
||||
enc->metadata.m.modular_16_bit_buffer_sufficient =
|
||||
(!info->uses_original_profile || info->bits_per_sample <= 12) &&
|
||||
info->alpha_bits <= 12;
|
||||
if ((info->intrinsic_xsize > 0 || info->intrinsic_ysize > 0) &&
|
||||
(info->intrinsic_xsize != info->xsize ||
|
||||
info->intrinsic_ysize != info->ysize)) {
|
||||
if (info->intrinsic_xsize > (1ull << 30ull) ||
|
||||
info->intrinsic_ysize > (1ull << 30ull) ||
|
||||
!enc->metadata.m.intrinsic_size.Set(info->intrinsic_xsize,
|
||||
info->intrinsic_ysize)) {
|
||||
return JXL_API_ERROR(enc, JXL_ENC_ERR_API_USAGE,
|
||||
"Invalid intrinsic dimensions");
|
||||
}
|
||||
enc->metadata.m.have_intrinsic_size = true;
|
||||
}
|
||||
|
||||
// The number of extra channels includes the alpha channel, so for example and
|
||||
// RGBA with no other extra channels, has exactly num_extra_channels == 1
|
||||
@ -939,8 +951,9 @@ JxlEncoderStatus JxlEncoderSetFrameLossless(
|
||||
JxlEncoderFrameSettings* frame_settings, const JXL_BOOL lossless) {
|
||||
if (lossless && frame_settings->enc->basic_info_set &&
|
||||
frame_settings->enc->metadata.m.xyb_encoded) {
|
||||
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_API_USAGE,
|
||||
"Set use_original_profile=true for lossless encoding");
|
||||
return JXL_API_ERROR(
|
||||
frame_settings->enc, JXL_ENC_ERR_API_USAGE,
|
||||
"Set uses_original_profile=true for lossless encoding");
|
||||
}
|
||||
frame_settings->values.lossless = lossless;
|
||||
return JXL_ENC_SUCCESS;
|
||||
@ -1691,8 +1704,9 @@ JxlEncoderStatus JxlEncoderAddImageFrame(
|
||||
}
|
||||
if (frame_settings->values.lossless &&
|
||||
frame_settings->enc->metadata.m.xyb_encoded) {
|
||||
return JXL_API_ERROR(frame_settings->enc, JXL_ENC_ERR_API_USAGE,
|
||||
"Set use_original_profile=true for lossless encoding");
|
||||
return JXL_API_ERROR(
|
||||
frame_settings->enc, JXL_ENC_ERR_API_USAGE,
|
||||
"Set uses_original_profile=true for lossless encoding");
|
||||
}
|
||||
queued_frame->option_values.cparams.level =
|
||||
frame_settings->enc->codestream_level;
|
||||
|
6
third_party/jpeg-xl/lib/jxl/encode_test.cc
vendored
6
third_party/jpeg-xl/lib/jxl/encode_test.cc
vendored
@ -890,6 +890,8 @@ TEST(EncodeTest, BasicInfoTest) {
|
||||
basic_info.min_nits = 5.0;
|
||||
basic_info.linear_below = 12.7;
|
||||
basic_info.orientation = JXL_ORIENT_ROTATE_90_CW;
|
||||
basic_info.intrinsic_xsize = 88;
|
||||
basic_info.intrinsic_ysize = 99;
|
||||
basic_info.animation.tps_numerator = 55;
|
||||
basic_info.animation.tps_denominator = 77;
|
||||
basic_info.animation.num_loops = 10;
|
||||
@ -945,6 +947,8 @@ TEST(EncodeTest, BasicInfoTest) {
|
||||
EXPECT_EQ(basic_info.uses_original_profile,
|
||||
basic_info2.uses_original_profile);
|
||||
EXPECT_EQ(basic_info.orientation, basic_info2.orientation);
|
||||
EXPECT_EQ(basic_info.intrinsic_xsize, basic_info2.intrinsic_xsize);
|
||||
EXPECT_EQ(basic_info.intrinsic_ysize, basic_info2.intrinsic_ysize);
|
||||
EXPECT_EQ(basic_info.num_color_channels, basic_info2.num_color_channels);
|
||||
// TODO(lode): also test num_extra_channels, but currently there may be a
|
||||
// mismatch between 0 and 1 if there is alpha, until encoder support for
|
||||
@ -1167,7 +1171,7 @@ TEST(EncodeTest, CroppedFrameTest) {
|
||||
EXPECT_EQ(true, seen_frame);
|
||||
}
|
||||
|
||||
TEST(EncodeTest, BoxTest) {
|
||||
TEST(EncodeTest, JXL_BOXES_TEST(BoxTest)) {
|
||||
// Test with uncompressed boxes and with brob boxes
|
||||
for (int compress_box = 0; compress_box <= 1; ++compress_box) {
|
||||
// Tests adding two metadata boxes with the encoder: an exif box before the
|
||||
|
2
third_party/jpeg-xl/lib/jxl/fast_dct-inl.h
vendored
2
third_party/jpeg-xl/lib/jxl/fast_dct-inl.h
vendored
@ -10,6 +10,8 @@
|
||||
#define LIB_JXL_FAST_DCT_INL_H_
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include <hwy/aligned_allocator.h>
|
||||
#include <hwy/highway.h>
|
||||
|
||||
|
@ -1022,6 +1022,9 @@ Status WriteJpegInternal(const JPEGData& jpg, const JPEGOutput& out,
|
||||
|
||||
case SerializationState::DONE:
|
||||
JXL_ASSERT(ss->output_queue.empty());
|
||||
if (ss->pad_bits != nullptr && ss->pad_bits != ss->pad_bits_end) {
|
||||
return JXL_FAILURE("Invalid number of padding bits.");
|
||||
}
|
||||
return true;
|
||||
|
||||
case SerializationState::ERROR:
|
||||
|
32
third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc
vendored
32
third_party/jpeg-xl/lib/jxl/jpeg/jpeg_data.cc
vendored
@ -288,8 +288,6 @@ Status JPEGData::VisitFields(Visitor* visitor) {
|
||||
JXL_RETURN_IF_ERROR(visitor->Bits(16, 0, &restart_interval));
|
||||
}
|
||||
|
||||
uint64_t padding_spot_limit = scan_info.size();
|
||||
|
||||
for (auto& scan : scan_info) {
|
||||
uint32_t num_reset_points = scan.reset_points.size();
|
||||
JXL_RETURN_IF_ERROR(visitor->U32(Val(0), BitsOffset(2, 1), BitsOffset(4, 4),
|
||||
@ -349,13 +347,6 @@ Status JPEGData::VisitFields(Visitor* visitor) {
|
||||
}
|
||||
last_block_idx = block_idx;
|
||||
}
|
||||
|
||||
if (restart_interval > 0) {
|
||||
int MCUs_per_row = 0;
|
||||
int MCU_rows = 0;
|
||||
CalculateMcuSize(scan, &MCUs_per_row, &MCU_rows);
|
||||
padding_spot_limit += DivCeil(MCU_rows * MCUs_per_row, restart_interval);
|
||||
}
|
||||
}
|
||||
std::vector<uint32_t> inter_marker_data_sizes;
|
||||
inter_marker_data_sizes.reserve(info.num_intermarker);
|
||||
@ -377,18 +368,19 @@ Status JPEGData::VisitFields(Visitor* visitor) {
|
||||
if (has_zero_padding_bit) {
|
||||
uint32_t nbit = padding_bits.size();
|
||||
JXL_RETURN_IF_ERROR(visitor->Bits(24, 0, &nbit));
|
||||
if (nbit > 7 * padding_spot_limit) {
|
||||
return JXL_FAILURE("Number of padding bits does not correspond to image");
|
||||
}
|
||||
// TODO(eustas): check that that much bits of input are available.
|
||||
if (visitor->IsReading()) {
|
||||
padding_bits.resize(nbit);
|
||||
}
|
||||
// TODO(eustas): read in (8-64?) bit groups to reduce overhead.
|
||||
for (uint8_t& bit : padding_bits) {
|
||||
bool bbit = bit;
|
||||
JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit));
|
||||
bit = bbit;
|
||||
padding_bits.reserve(std::min<uint32_t>(1024u, nbit));
|
||||
for (uint32_t i = 0; i < nbit; i++) {
|
||||
bool bbit = false;
|
||||
JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit));
|
||||
padding_bits.push_back(bbit);
|
||||
}
|
||||
} else {
|
||||
for (uint8_t& bit : padding_bits) {
|
||||
bool bbit = bit;
|
||||
JXL_RETURN_IF_ERROR(visitor->Bool(false, &bbit));
|
||||
bit = bbit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
6
third_party/jpeg-xl/lib/jxl/test_utils.h
vendored
6
third_party/jpeg-xl/lib/jxl/test_utils.h
vendored
@ -50,6 +50,12 @@
|
||||
#define JXL_TRANSCODE_JPEG_TEST(X) DISABLED_##X
|
||||
#endif // JPEGXL_ENABLE_TRANSCODE_JPEG
|
||||
|
||||
#if JPEGXL_ENABLE_BOXES
|
||||
#define JXL_BOXES_TEST(X) X
|
||||
#else
|
||||
#define JXL_BOXES_TEST(X) DISABLED_##X
|
||||
#endif // JPEGXL_ENABLE_BOXES
|
||||
|
||||
#ifdef THREAD_SANITIZER
|
||||
#define JXL_TSAN_SLOW_TEST(X) DISABLED_##X
|
||||
#else
|
||||
|
1
third_party/jpeg-xl/lib/jxl_tests.cmake
vendored
1
third_party/jpeg-xl/lib/jxl_tests.cmake
vendored
@ -5,6 +5,7 @@ set(TEST_FILES
|
||||
extras/codec_test.cc
|
||||
extras/dec/color_description_test.cc
|
||||
extras/dec/pgx_test.cc
|
||||
extras/decode_jpeg_test.cc
|
||||
jxl/ac_strategy_test.cc
|
||||
jxl/alpha_test.cc
|
||||
jxl/ans_common_test.cc
|
||||
|
110
third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc
vendored
110
third_party/jpeg-xl/plugins/gimp/file-jxl-load.cc
vendored
@ -16,6 +16,11 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
|
||||
GimpColorProfile *profile_icc = nullptr;
|
||||
GimpColorProfile *profile_int = nullptr;
|
||||
bool is_linear = false;
|
||||
unsigned long xsize = 0, ysize = 0;
|
||||
long crop_x0 = 0, crop_y0 = 0;
|
||||
size_t layer_idx = 0;
|
||||
uint32_t frame_duration = 0;
|
||||
double tps_denom = 1.f, tps_numer = 1.f;
|
||||
|
||||
gint32 layer;
|
||||
|
||||
@ -28,6 +33,10 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
|
||||
GimpPrecision precision = GIMP_PRECISION_U16_GAMMA;
|
||||
JxlBasicInfo info = {};
|
||||
JxlPixelFormat format = {};
|
||||
JxlAnimationHeader animation = {};
|
||||
JxlBlendMode blend_mode = JXL_BLEND_BLEND;
|
||||
char *frame_name = nullptr; // will be realloced
|
||||
size_t frame_name_len = 0;
|
||||
|
||||
format.num_channels = 4;
|
||||
format.data_type = JXL_TYPE_FLOAT;
|
||||
@ -53,9 +62,9 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
|
||||
|
||||
auto dec = JxlDecoderMake(nullptr);
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
|
||||
JXL_DEC_COLOR_ENCODING |
|
||||
JXL_DEC_FULL_IMAGE)) {
|
||||
JxlDecoderSubscribeEvents(dec.get(),
|
||||
JXL_DEC_BASIC_INFO | JXL_DEC_COLOR_ENCODING |
|
||||
JXL_DEC_FULL_IMAGE | JXL_DEC_FRAME)) {
|
||||
g_printerr(LOAD_PROC " Error: JxlDecoderSubscribeEvents failed\n");
|
||||
return false;
|
||||
}
|
||||
@ -66,6 +75,12 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
|
||||
g_printerr(LOAD_PROC " Error: JxlDecoderSetParallelRunner failed\n");
|
||||
return false;
|
||||
}
|
||||
// TODO: make this work with coalescing set to false, while handling frames
|
||||
// with duration 0 and references to earlier frames correctly.
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetCoalescing(dec.get(), JXL_TRUE)) {
|
||||
g_printerr(LOAD_PROC " Error: JxlDecoderSetCoalescing failed\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
// grand decode loop...
|
||||
JxlDecoderSetInput(dec.get(), compressed.data(), compressed.size());
|
||||
@ -81,9 +96,16 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
xsize = info.xsize;
|
||||
ysize = info.ysize;
|
||||
if (info.have_animation) {
|
||||
animation = info.animation;
|
||||
tps_denom = animation.tps_denominator;
|
||||
tps_numer = animation.tps_numerator;
|
||||
}
|
||||
|
||||
JxlResizableParallelRunnerSetThreads(
|
||||
runner.get(),
|
||||
JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
|
||||
runner.get(), JxlResizableParallelRunnerSuggestThreads(xsize, ysize));
|
||||
} else if (status == JXL_DEC_COLOR_ENCODING) {
|
||||
// check for ICC profile
|
||||
size_t icc_size = 0;
|
||||
@ -279,11 +301,11 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
|
||||
|
||||
// create new image
|
||||
if (is_linear) {
|
||||
*image_id = gimp_image_new_with_precision(
|
||||
info.xsize, info.ysize, image_type, GIMP_PRECISION_FLOAT_LINEAR);
|
||||
*image_id = gimp_image_new_with_precision(xsize, ysize, image_type,
|
||||
GIMP_PRECISION_FLOAT_LINEAR);
|
||||
} else {
|
||||
*image_id = gimp_image_new_with_precision(
|
||||
info.xsize, info.ysize, image_type, GIMP_PRECISION_FLOAT_GAMMA);
|
||||
*image_id = gimp_image_new_with_precision(xsize, ysize, image_type,
|
||||
GIMP_PRECISION_FLOAT_GAMMA);
|
||||
}
|
||||
|
||||
if (profile_int) {
|
||||
@ -306,10 +328,38 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
|
||||
g_printerr(LOAD_PROC " Error: JxlDecoderSetImageOutBuffer failed\n");
|
||||
return false;
|
||||
}
|
||||
} else if (status == JXL_DEC_FULL_IMAGE || status == JXL_DEC_FRAME) {
|
||||
} else if (status == JXL_DEC_FULL_IMAGE) {
|
||||
// create and insert layer
|
||||
layer = gimp_layer_new(*image_id, "Background", info.xsize, info.ysize,
|
||||
layer_type, /*opacity=*/100,
|
||||
gchar *layer_name;
|
||||
if (layer_idx == 0 && !info.have_animation) {
|
||||
layer_name = g_strdup_printf("Background");
|
||||
} else {
|
||||
const GString *blend_null_flag = g_string_new("");
|
||||
const GString *blend_replace_flag = g_string_new(" (replace)");
|
||||
const GString *blend_combine_flag = g_string_new(" (combine)");
|
||||
GString *blend;
|
||||
if (blend_mode == JXL_BLEND_REPLACE) {
|
||||
blend = (GString *)blend_replace_flag;
|
||||
} else if (blend_mode == JXL_BLEND_BLEND) {
|
||||
blend = (GString *)blend_combine_flag;
|
||||
} else {
|
||||
blend = (GString *)blend_null_flag;
|
||||
}
|
||||
char *temp_frame_name = nullptr;
|
||||
bool must_free_frame_name = false;
|
||||
if (frame_name_len == 0) {
|
||||
temp_frame_name = g_strdup_printf("Frame %lu", layer_idx + 1);
|
||||
must_free_frame_name = true;
|
||||
} else {
|
||||
temp_frame_name = frame_name;
|
||||
}
|
||||
double fduration = frame_duration * 1000.f * tps_denom / tps_numer;
|
||||
layer_name = g_strdup_printf("%s (%.15gms)%s", temp_frame_name,
|
||||
fduration, blend->str);
|
||||
if (must_free_frame_name) free(temp_frame_name);
|
||||
}
|
||||
layer = gimp_layer_new(*image_id, layer_name, xsize, ysize, layer_type,
|
||||
/*opacity=*/100,
|
||||
gimp_image_get_default_new_layer_mode(*image_id));
|
||||
|
||||
gimp_image_insert_layer(*image_id, layer, /*parent_id=*/-1,
|
||||
@ -333,12 +383,42 @@ bool LoadJpegXlImage(const gchar *const filename, gint32 *const image_id) {
|
||||
const Babl *source_format = babl_format(babl_format_str.c_str());
|
||||
|
||||
babl_process(babl_fish(source_format, destination_format),
|
||||
pixels_buffer_1, pixels_buffer_2, info.xsize * info.ysize);
|
||||
pixels_buffer_1, pixels_buffer_2, xsize * ysize);
|
||||
|
||||
gegl_buffer_set(buffer, GEGL_RECTANGLE(0, 0, info.xsize, info.ysize), 0,
|
||||
nullptr, pixels_buffer_2, GEGL_AUTO_ROWSTRIDE);
|
||||
gegl_buffer_set(buffer, GEGL_RECTANGLE(0, 0, xsize, ysize), 0, nullptr,
|
||||
pixels_buffer_2, GEGL_AUTO_ROWSTRIDE);
|
||||
gimp_item_transform_translate(layer, crop_x0, crop_y0);
|
||||
|
||||
g_clear_object(&buffer);
|
||||
g_free(layer_name);
|
||||
layer_idx++;
|
||||
} else if (status == JXL_DEC_FRAME) {
|
||||
JxlFrameHeader frame_header;
|
||||
if (JxlDecoderGetFrameHeader(dec.get(), &frame_header) !=
|
||||
JXL_DEC_SUCCESS) {
|
||||
g_printerr(LOAD_PROC " Error: JxlDecoderSetImageOutBuffer failed\n");
|
||||
return false;
|
||||
}
|
||||
xsize = frame_header.layer_info.xsize;
|
||||
ysize = frame_header.layer_info.ysize;
|
||||
crop_x0 = frame_header.layer_info.crop_x0;
|
||||
crop_y0 = frame_header.layer_info.crop_y0;
|
||||
frame_duration = frame_header.duration;
|
||||
blend_mode = frame_header.layer_info.blend_info.blendmode;
|
||||
if (blend_mode != JXL_BLEND_BLEND && blend_mode != JXL_BLEND_REPLACE) {
|
||||
g_printerr(
|
||||
LOAD_PROC
|
||||
" Warning: JxlDecoderGetFrameHeader: Unhandled blend mode: %d\n",
|
||||
blend_mode);
|
||||
}
|
||||
if ((frame_name_len = frame_header.name_length) > 0) {
|
||||
frame_name = (char *)realloc(frame_name, frame_name_len);
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderGetFrameName(dec.get(), frame_name, frame_name_len)) {
|
||||
g_printerr(LOAD_PROC "Error: JxlDecoderGetFrameName failed");
|
||||
return false;
|
||||
};
|
||||
}
|
||||
} else if (status == JXL_DEC_SUCCESS) {
|
||||
// All decoding successfully finished.
|
||||
// It's not required to call JxlDecoderReleaseInput(dec.get())
|
||||
|
Loading…
Reference in New Issue
Block a user