这是indexloc提供的服务,不要输入任何密码
Skip to content

Compressed ICC #3628

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions lib/extras/compressed_icc.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) the JPEG XL Project Authors. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

#include <jxl/compressed_icc.h>

#include "lib/jxl/enc_icc_codec.h"
#include "lib/jxl/icc_codec.h"

JXL_BOOL JxlICCProfileEncode(JxlMemoryManager* memory_manager,
const uint8_t* icc, size_t icc_size,
uint8_t** compressed_icc,
size_t* compressed_icc_size) {
jxl::BitWriter writer(memory_manager);
JXL_RETURN_IF_ERROR(jxl::WriteICC(jxl::Span<const uint8_t>(icc, icc_size),
&writer, 0, nullptr));
writer.ZeroPadToByte();
*compressed_icc_size = writer.GetSpan().size();
*compressed_icc = static_cast<uint8_t*>(
memory_manager->alloc(memory_manager->opaque, *compressed_icc_size));
memcpy(*compressed_icc, writer.GetSpan().data(), *compressed_icc_size);
return JXL_TRUE;
}

JXL_BOOL JxlICCProfileDecode(JxlMemoryManager* memory_manager,
const uint8_t* compressed_icc,
size_t compressed_icc_size, uint8_t** icc,
size_t* icc_size) {
jxl::ICCReader icc_reader(memory_manager);
jxl::PaddedBytes decompressed(memory_manager);
jxl::BitReader bit_reader(
jxl::Span<const uint8_t>(compressed_icc, compressed_icc_size));
JXL_RETURN_IF_ERROR(icc_reader.Init(&bit_reader, /*output_limit=*/0));
JXL_RETURN_IF_ERROR(icc_reader.Process(&bit_reader, &decompressed));
JXL_RETURN_IF_ERROR(bit_reader.Close());
*icc_size = decompressed.size();
*icc = static_cast<uint8_t*>(
memory_manager->alloc(memory_manager->opaque, *icc_size));
memcpy(*icc, decompressed.data(), *icc_size);
return JXL_TRUE;
}
40 changes: 40 additions & 0 deletions lib/extras/compressed_icc_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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 "jxl/compressed_icc.h"

#include "lib/jxl/test_memory_manager.h"
#include "lib/jxl/test_utils.h"
#include "lib/jxl/testing.h"

namespace jxl {
namespace {

TEST(CompressedIccTest, Roundtrip) {
JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
uint8_t* compressed_icc;
size_t compressed_icc_size;
const IccBytes icc = jxl::test::GetIccTestProfile();
ASSERT_TRUE(JxlICCProfileEncode(memory_manager, icc.data(), icc.size(),
&compressed_icc, &compressed_icc_size));

EXPECT_LT(compressed_icc_size, icc.size());

uint8_t* decompressed_icc;
size_t decompressed_icc_size;
ASSERT_TRUE(JxlICCProfileDecode(memory_manager, compressed_icc,
compressed_icc_size, &decompressed_icc,
&decompressed_icc_size));

ASSERT_EQ(decompressed_icc_size, icc.size());

EXPECT_EQ(0, memcmp(decompressed_icc, icc.data(), decompressed_icc_size));

memory_manager->free(memory_manager->opaque, compressed_icc);
memory_manager->free(memory_manager->opaque, decompressed_icc);
}

} // namespace
} // namespace jxl
5 changes: 1 addition & 4 deletions lib/extras/gain_map_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ std::vector<uint8_t> GoldenTestGainMap(bool has_icc, bool has_color_encoding) {

std::vector<uint8_t> icc_size = {0x00, 0x00, 0x00, 0x00};
if (has_icc) {
icc_size = {0x00, 0x00, 0x01, 0x7A}; // 378 in decimal
icc_size = {0x00, 0x00, 0x00, 0x88}; // 136 in decimal
}
std::vector<uint8_t> icc_data = jxl::test::GetCompressedIccTestProfile();
std::string second_placeholder =
Expand Down Expand Up @@ -133,9 +133,6 @@ TEST_P(GainMapTest, GainMapRoundtrip) {
ASSERT_TRUE(JxlGainMapWriteBundle(&orig_bundle, buffer.data(), buffer.size(),
&bytes_written));
EXPECT_EQ(bytes_written, bundle_size);
std::ofstream dump("/tmp/gainmap.bin", std::ios::out);
dump.write(reinterpret_cast<const char*>(buffer.data()), buffer.size());
dump.close();
EXPECT_EQ(buffer[0], orig_bundle.jhgm_version);
EXPECT_EQ(buffer.size(), golden_gain_map.size());
EXPECT_TRUE(
Expand Down
75 changes: 75 additions & 0 deletions lib/include/jxl/compressed_icc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/* Copyright (c) the JPEG XL Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/

/** @addtogroup libjxl_metadata
* @{
* @file compressed_icc.h
* @brief Utility functions to compress and decompress ICC streams.
*/

#ifndef JXL_COMPRESSED_ICC_H_
#define JXL_COMPRESSED_ICC_H_

#include <jxl/jxl_export.h>
#include <jxl/memory_manager.h>
#include <jxl/types.h>

#ifndef __cplusplus
extern "C" {
#endif

/**
* Allocates a buffer using the memory manager, fills it with a compressed
* representation of an ICC profile, returns the result through @c output_buffer
* and indicates its size through @c output_size.
*
* The result must be freed using the memory manager once it is not of any more
* use.
*
* @param[in] memory_manager Pointer to a JxlMemoryManager.
* @param[in] icc Pointer to a buffer containing the uncompressed ICC profile.
* @param[in] icc_size Size of the buffer containing the ICC profile.
* @param[out] compressed_icc Will be set to a pointer to the buffer containing
* the result.
* @param[out] compressed_icc_size Will be set to the size of the buffer
* containing the result.
* @return Whether compressing the profile was successful.
*/
JXL_EXPORT JXL_BOOL JxlICCProfileEncode(JxlMemoryManager* memory_manager,
const uint8_t* icc, size_t icc_size,
uint8_t** compressed_icc,
size_t* compressed_icc_size);

/**
* Allocates a buffer using the memory manager, fills it with the decompressed
* version of the ICC profile in @c compressed_icc, returns the result through
* @c output_buffer and indicates its size through @c output_size.
*
* The result must be freed using the memory manager once it is not of any more
* use.
*
* @param[in] memory_manager Pointer to a JxlMemoryManager.
* @param[in] compressed_icc Pointer to a buffer containing the compressed ICC
* profile.
* @param[in] compressed_icc_size Size of the buffer containing the compressed
* ICC profile.
* @param[out] icc Will be set to a pointer to the buffer containing the result.
* @param[out] icc_size Will be set to the size of the buffer containing the
* result.
* @return Whether decompressing the profile was successful.
*/
JXL_EXPORT JXL_BOOL JxlICCProfileDecode(JxlMemoryManager* memory_manager,
const uint8_t* compressed_icc,
size_t compressed_icc_size,
uint8_t** icc, size_t* icc_size);

#ifndef __cplusplus
}
#endif

#endif /* JXL_COMPRESSED_ICC_H_ */

/** @} */
3 changes: 2 additions & 1 deletion lib/jxl/decode_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,8 @@ std::vector<uint8_t> GetTestHeader(size_t xsize, size_t ysize,

if (!icc_profile.empty()) {
EXPECT_TRUE(metadata.m.color_encoding.WantICC());
EXPECT_TRUE(jxl::WriteICC(icc_profile, &writer, 0, nullptr));
EXPECT_TRUE(jxl::WriteICC(jxl::Span<const uint8_t>(icc_profile), &writer, 0,
nullptr));
}

writer.ZeroPadToByte();
Expand Down
2 changes: 1 addition & 1 deletion lib/jxl/enc_icc_codec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ Status PredictICC(const uint8_t* icc, size_t size, PaddedBytes* result) {
return true;
}

Status WriteICC(const IccBytes& icc, BitWriter* JXL_RESTRICT writer,
Status WriteICC(const Span<const uint8_t> icc, BitWriter* JXL_RESTRICT writer,
size_t layer, AuxOut* JXL_RESTRICT aux_out) {
if (icc.empty()) return JXL_FAILURE("ICC must be non-empty");
JxlMemoryManager* memory_manager = writer->memory_manager();
Expand Down
4 changes: 2 additions & 2 deletions lib/jxl/enc_icc_codec.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
#include <vector>

#include "lib/jxl/base/compiler_specific.h"
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/enc_bit_writer.h"

namespace jxl {

struct AuxOut;
class PaddedBytes;

// Should still be called if `icc.empty()` - if so, writes only 1 bit.
Status WriteICC(const std::vector<uint8_t>& icc, BitWriter* JXL_RESTRICT writer,
Status WriteICC(Span<const uint8_t> icc, BitWriter* JXL_RESTRICT writer,
size_t layer, AuxOut* JXL_RESTRICT aux_out);

} // namespace jxl
Expand Down
5 changes: 3 additions & 2 deletions lib/jxl/encode.cc
Original file line number Diff line number Diff line change
Expand Up @@ -740,8 +740,9 @@ jxl::Status JxlEncoderStruct::ProcessOneEnqueuedInput() {
}
// Only send ICC (at least several hundred bytes) if fields aren't enough.
if (metadata.m.color_encoding.WantICC()) {
if (!jxl::WriteICC(metadata.m.color_encoding.ICC(), &writer,
jxl::kLayerHeader, aux_out)) {
if (!jxl::WriteICC(
jxl::Span<const uint8_t>(metadata.m.color_encoding.ICC()),
&writer, jxl::kLayerHeader, aux_out)) {
return JXL_API_ERROR(this, JXL_ENC_ERR_GENERIC,
"Failed to write ICC profile");
}
Expand Down
2 changes: 1 addition & 1 deletion lib/jxl/icc_codec_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace {
void TestProfile(const IccBytes& icc) {
JxlMemoryManager* memory_manager = jxl::test::MemoryManager();
BitWriter writer{memory_manager};
ASSERT_TRUE(WriteICC(icc, &writer, 0, nullptr));
ASSERT_TRUE(WriteICC(Span<const uint8_t>(icc), &writer, 0, nullptr));
writer.ZeroPadToByte();
std::vector<uint8_t> dec;
BitReader reader(writer.GetSpan());
Expand Down
75 changes: 9 additions & 66 deletions lib/jxl/test_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,73 +62,15 @@ std::string GetTestDataPath(const std::string& filename) {
#endif

jxl::IccBytes GetIccTestProfile() {
const uint8_t* profile = reinterpret_cast<const uint8_t*>(
"\0\0\3\200lcms\0040\0\0mntrRGB XYZ "
"\a\344\0\a\0\27\0\21\0$"
"\0\37acspAPPL\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\366"
"\326\0\1\0\0\0\0\323-lcms\372c\207\36\227\200{"
"\2\232s\255\327\340\0\n\26\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
"\0\0\0\0\0\0\0\0\rdesc\0\0\1 "
"\0\0\0Bcprt\0\0\1d\0\0\1\0wtpt\0\0\2d\0\0\0\24chad\0\0\2x\0\0\0,"
"bXYZ\0\0\2\244\0\0\0\24gXYZ\0\0\2\270\0\0\0\24rXYZ\0\0\2\314\0\0\0\24rTR"
"C\0\0\2\340\0\0\0 gTRC\0\0\2\340\0\0\0 bTRC\0\0\2\340\0\0\0 "
"chrm\0\0\3\0\0\0\0$dmnd\0\0\3$\0\0\0("
"dmdd\0\0\3L\0\0\0002mluc\0\0\0\0\0\0\0\1\0\0\0\fenUS\0\0\0&"
"\0\0\0\34\0R\0G\0B\0_\0D\0006\0005\0_\0S\0R\0G\0_\0R\0e\0l\0_"
"\0L\0i\0n\0\0mluc\0\0\0\0\0\0\0\1\0\0\0\fenUS\0\0\0\344\0\0\0\34\0C\0o\0"
"p\0y\0r\0i\0g\0h\0t\0 \0002\0000\0001\08\0 \0G\0o\0o\0g\0l\0e\0 "
"\0L\0L\0C\0,\0 \0C\0C\0-\0B\0Y\0-\0S\0A\0 \0003\0.\0000\0 "
"\0U\0n\0p\0o\0r\0t\0e\0d\0 "
"\0l\0i\0c\0e\0n\0s\0e\0(\0h\0t\0t\0p\0s\0:\0/\0/"
"\0c\0r\0e\0a\0t\0i\0v\0e\0c\0o\0m\0m\0o\0n\0s\0.\0o\0r\0g\0/"
"\0l\0i\0c\0e\0n\0s\0e\0s\0/\0b\0y\0-\0s\0a\0/\0003\0.\0000\0/"
"\0l\0e\0g\0a\0l\0c\0o\0d\0e\0)XYZ "
"\0\0\0\0\0\0\366\326\0\1\0\0\0\0\323-"
"sf32\0\0\0\0\0\1\fB\0\0\5\336\377\377\363%"
"\0\0\a\223\0\0\375\220\377\377\373\241\377\377\375\242\0\0\3\334\0\0\300"
"nXYZ \0\0\0\0\0\0o\240\0\08\365\0\0\3\220XYZ "
"\0\0\0\0\0\0$\237\0\0\17\204\0\0\266\304XYZ "
"\0\0\0\0\0\0b\227\0\0\267\207\0\0\30\331para\0\0\0\0\0\3\0\0\0\1\0\0\0\1"
"\0\0\0\0\0\0\0\1\0\0\0\0\0\0chrm\0\0\0\0\0\3\0\0\0\0\243\327\0\0T|"
"\0\0L\315\0\0\231\232\0\0&"
"g\0\0\17\\mluc\0\0\0\0\0\0\0\1\0\0\0\fenUS\0\0\0\f\0\0\0\34\0G\0o\0o\0g"
"\0l\0emluc\0\0\0\0\0\0\0\1\0\0\0\fenUS\0\0\0\26\0\0\0\34\0I\0m\0a\0g\0e"
"\0 \0c\0o\0d\0e\0c\0\0");
size_t profile_size = 896;
jxl::IccBytes icc_profile;
icc_profile.assign(profile, profile + profile_size);
return icc_profile;
return ReadTestData("external/Compact-ICC-Profiles/profiles/scRGB-v2.icc");
}

std::vector<uint8_t> GetCompressedIccTestProfile() {
const uint8_t* raw_icc_data = reinterpret_cast<const uint8_t*>(
"\x1f\x8b\x01\x33\x38\x18\x00\x30\x20\x8c"
"\xe6\x81\x59\x00\x64\x69\x2c\x50\x80\xfc\xbc\x8e\xd6\xf7\x84\x66"
"\x0c\x46\x68\x8e\xc9\x1e\x35\xb7\xe6\x79\x0a\x38\x0f\x2d\x0b\x15"
"\x94\x56\x90\x28\x39\x09\x48\x27\x1f\xc3\x2a\x85\xb3\x82\x01\x46"
"\x86\x28\x19\xe4\x64\x24\x3d\x69\x74\xa4\x9e\x24\x3e\x4a\x6d\x31"
"\xa4\x54\x2a\x35\xc5\xf0\x9e\x34\xa0\x27\x8d\x8a\x04\xb0\xec\x8e"
"\xdb\xee\xcc\x40\x5e\x71\x96\xcc\x99\x3e\x3a\x18\x42\x3f\xc0\x06"
"\x5c\x04\xaf\x79\xdf\xa3\x7e\x47\x0f\x0f\xbd\x08\xd8\x3d\xa9\xd9"
"\xf9\xdd\x3e\x57\x30\xa5\x36\x7e\xcc\x96\x57\xfa\x11\x41\x71\xdd"
"\x1b\x8d\xa1\x79\xa5\x5c\xe4\x3e\xb4\xde\xde\xdf\x9c\xe4\xee\x4f"
"\x28\xf8\x3e\x4c\xe2\xfa\x36\xfb\x3f\x13\x97\x1a\xc9\x34\xef\xc0"
"\x17\x9a\x15\x92\x03\x4b\x83\xd5\x62\xf3\xc4\x20\xc7\xf3\x1c\x4c"
"\x0d\xc2\xe1\x8c\x39\xc8\x64\xdc\xc8\xa5\x7b\x93\x18\xec\xec\xc5"
"\xe0\x0a\x2f\xf0\x95\x12\x05\x0d\x60\x92\xa1\xf0\x0e\x65\x80\xa5"
"\x52\xa1\xf3\x94\x3f\x6f\xc7\x0a\x45\x94\xc8\x1a\xc5\xf0\x34\xcd"
"\xe3\x1d\x9b\xaf\x70\xfe\x8f\x19\x1d\x1f\x69\xba\x1d\xc2\xdf\xd9"
"\x0b\x1f\xa6\x38\x02\x66\x78\x88\x72\x84\x76\xad\x04\x80\xd3\x69"
"\x44\x71\x05\x71\xd2\xeb\xdf\xbf\xf3\x29\x70\x76\x02\xf6\x85\xf8"
"\xf7\xef\xde\x90\x7f\xff\xf6\x15\x41\x96\x0b\x02\xd7\x15\xfb\xbe"
"\x81\x18\x6c\x1d\xb2\x10\x18\xe2\x07\xea\x12\x40\x9b\x44\x58\xf1"
"\x75\x85\x37\x0f\xd0\x68\x96\x7c\x39\x85\xf8\xea\xf7\x62\x47\xb0"
"\x42\xeb\x43\x06\x70\xe4\x15\x96\x2a\x8b\x65\x3e\x4d\x98\x51\x03"
"\x63\xf6\x14\xf5\xe5\xe0\x7a\x0e\xdf\x3e\x1b\x45\x9a\xef\x87\xe1"
"\x3f\xcf\x69\x5c\x43\xda\x68\xde\x84\x26\x38\x6a\xf0\x35\xcb\x08");
std::vector<uint8_t> icc_data;
icc_data.assign(raw_icc_data, raw_icc_data + 378);
return icc_data;
BitWriter writer(MemoryManager());
const IccBytes icc = GetIccTestProfile();
JXL_CHECK(WriteICC(Span<const uint8_t>(icc), &writer, 0, nullptr));
writer.ZeroPadToByte();
return std::vector<uint8_t>(writer.GetSpan().begin(), writer.GetSpan().end());
}

std::vector<uint8_t> ReadTestData(const std::string& filename) {
Expand Down Expand Up @@ -879,8 +821,9 @@ Status EncodeFile(const CompressParams& params, const CodecInOut* io,

// Only send ICC (at least several hundred bytes) if fields aren't enough.
if (metadata->m.color_encoding.WantICC()) {
JXL_RETURN_IF_ERROR(WriteICC(metadata->m.color_encoding.ICC(), &writer,
kLayerHeader, /* aux_out */ nullptr));
JXL_RETURN_IF_ERROR(
WriteICC(Span<const uint8_t>(metadata->m.color_encoding.ICC()), &writer,
kLayerHeader, /* aux_out */ nullptr));
}

if (metadata->m.have_preview) {
Expand Down
3 changes: 3 additions & 0 deletions lib/jxl_lists.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ libjxl_extras_sources = [
"extras/alpha_blend.h",
"extras/common.cc",
"extras/common.h",
"extras/compressed_icc.cc",
"extras/dec/color_description.cc",
"extras/dec/color_description.h",
"extras/dec/color_hints.cc",
Expand Down Expand Up @@ -558,6 +559,7 @@ libjxl_public_headers = [
"include/jxl/cms_interface.h",
"include/jxl/codestream_header.h",
"include/jxl/color_encoding.h",
"include/jxl/compressed_icc.h",
"include/jxl/decode.h",
"include/jxl/decode_cxx.h",
"include/jxl/encode.h",
Expand Down Expand Up @@ -587,6 +589,7 @@ libjxl_testlib_files = [

libjxl_tests = [
"extras/codec_test.cc",
"extras/compressed_icc_test.cc",
"extras/dec/color_description_test.cc",
"extras/dec/pgx_test.cc",
"extras/gain_map_test.cc",
Expand Down
3 changes: 3 additions & 0 deletions lib/jxl_lists.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,7 @@ set(JPEGXL_INTERNAL_EXTRAS_SOURCES
extras/alpha_blend.h
extras/common.cc
extras/common.h
extras/compressed_icc.cc
extras/dec/color_description.cc
extras/dec/color_description.h
extras/dec/color_hints.cc
Expand Down Expand Up @@ -547,6 +548,7 @@ set(JPEGXL_INTERNAL_PUBLIC_HEADERS
include/jxl/cms_interface.h
include/jxl/codestream_header.h
include/jxl/color_encoding.h
include/jxl/compressed_icc.h
include/jxl/decode.h
include/jxl/decode_cxx.h
include/jxl/encode.h
Expand Down Expand Up @@ -576,6 +578,7 @@ set(JPEGXL_INTERNAL_TESTLIB_FILES

set(JPEGXL_INTERNAL_TESTS
extras/codec_test.cc
extras/compressed_icc_test.cc
extras/dec/color_description_test.cc
extras/dec/pgx_test.cc
extras/gain_map_test.cc
Expand Down
Loading