Skip to content
Open
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
2 changes: 2 additions & 0 deletions .github/workflows/build_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -366,12 +366,14 @@ jobs:
run: |
set -x
sudo cmake --build build -- install
if [[ "${RUNNER_OS}" == "Linux" ]]; then sudo ldconfig; fi
# GCC does not know how to refer to shared libraries properly
export CC=clang
export CXX=clang++
cmake -Bbuild-example -Hexamples -G Ninja
cmake --build build-example
# Test that the built binaries run.
build-example/gain_map_api_roundtrip
echo -e -n "PF\n1 1\n-1.0\n\0\0\x80\x3f\0\0\x80\x3f\0\0\x80\x3f" > test.pfm
build-example/encode_oneshot test.pfm test.jxl
build-example/decode_oneshot test.jxl dec.pfm dec.icc
Expand Down
5 changes: 5 additions & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ project(SAMPLE_LIBJXL LANGUAGES C CXX)
# Use pkg-config to find libjxl.
find_package(PkgConfig)
pkg_check_modules(Jxl REQUIRED IMPORTED_TARGET libjxl libjxl_cms libjxl_threads)
pkg_check_modules(JxlOnly REQUIRED IMPORTED_TARGET libjxl)

# Build the example encoder/decoder binaries using the default shared libraries
# installed.
Expand All @@ -26,3 +27,7 @@ target_link_libraries(decode_progressive PkgConfig::Jxl)

add_executable(encode_oneshot encode_oneshot.cc)
target_link_libraries(encode_oneshot PkgConfig::Jxl)

# Exercise the public gain-map and ICC helpers through libjxl alone.
add_executable(gain_map_api_roundtrip gain_map_api_roundtrip.c)
target_link_libraries(gain_map_api_roundtrip PkgConfig::JxlOnly)
139 changes: 139 additions & 0 deletions examples/gain_map_api_roundtrip.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/* Copyright (c) the JPEG XL Project Authors. All rights reserved.
*
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*
* This is a linkage and serialization smoke test with synthetic payloads. It
* does not decode image data or use a valid color profile.
*/

#include <jxl/color_encoding.h>
#include <jxl/compressed_icc.h>
#include <jxl/encode.h>
#include <jxl/gain_map.h>
#include <jxl/memory_manager.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static void* Allocate(void* opaque, size_t size) {
(void)opaque;
return malloc(size);
}

static void Release(void* opaque, void* address) {
(void)opaque;
free(address);
}

int main(void) {
/* The ICC codec preserves bytes; these are not a valid color profile. */
static const uint8_t icc_data[] = {
0x00, 0x00, 0x00, 0x3c, 's', 'y', 'n', 't', 'h', 'e', 't', 'i',
'c', '-', 'I', 'C', 'C', '-', 't', 'e', 's', 't', '-', 'd',
'a', 't', 'a', '-', 'f', 'o', 'r', '-', 'l', 'i', 'b', 'j',
'x', 'l', 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
};
static const uint8_t metadata[] = {0x00, 0x01, 0x02, 0x03, 0x7f, 0xff};
static const uint8_t gain_map[] = {0xff, 0x00, 0x80, 0x40, 0x20};

JxlMemoryManager memory_manager = {NULL, Allocate, Release};
uint8_t* compressed_icc = NULL;
size_t compressed_icc_size = 0;
uint8_t* decoded_icc = NULL;
size_t decoded_icc_size = 0;
uint8_t* bundle_buffer = NULL;
size_t bundle_size = 0;
size_t bytes_written = 0;
size_t bytes_read = 0;
int result = EXIT_FAILURE;
JxlGainMapBundle input_bundle = {0};
JxlGainMapBundle output_bundle = {0};

if (!JxlICCProfileEncode(&memory_manager, icc_data, sizeof(icc_data),
&compressed_icc, &compressed_icc_size)) {
fprintf(stderr, "JxlICCProfileEncode failed\n");
goto cleanup;
}
if (compressed_icc_size > UINT32_MAX) {
fprintf(stderr, "compressed ICC data does not fit in a gain map bundle\n");
goto cleanup;
}

input_bundle.jhgm_version = 0;
input_bundle.gain_map_metadata_size = sizeof(metadata);
input_bundle.gain_map_metadata = metadata;
input_bundle.has_color_encoding = JXL_TRUE;
JxlColorEncodingSetToLinearSRGB(&input_bundle.color_encoding, JXL_FALSE);
input_bundle.alt_icc_size = (uint32_t)compressed_icc_size;
input_bundle.alt_icc = compressed_icc;
input_bundle.gain_map_size = sizeof(gain_map);
input_bundle.gain_map = gain_map;

if (!JxlGainMapGetBundleSize(&input_bundle, &bundle_size)) {
fprintf(stderr, "JxlGainMapGetBundleSize failed\n");
goto cleanup;
}
bundle_buffer = (uint8_t*)malloc(bundle_size);
if (bundle_buffer == NULL) {
fprintf(stderr, "allocating gain map bundle failed\n");
goto cleanup;
}
if (!JxlGainMapWriteBundle(&input_bundle, bundle_buffer, bundle_size,
&bytes_written)) {
fprintf(stderr, "JxlGainMapWriteBundle failed\n");
goto cleanup;
}
if (bytes_written != bundle_size) {
fprintf(stderr, "JxlGainMapWriteBundle wrote an unexpected size\n");
goto cleanup;
}
if (!JxlGainMapReadBundle(&output_bundle, bundle_buffer, bundle_size,
&bytes_read)) {
fprintf(stderr, "JxlGainMapReadBundle failed\n");
goto cleanup;
}
if (bytes_read != bundle_size || output_bundle.jhgm_version != 0 ||
output_bundle.gain_map_metadata_size != sizeof(metadata) ||
memcmp(output_bundle.gain_map_metadata, metadata, sizeof(metadata)) !=
0 ||
!output_bundle.has_color_encoding ||
output_bundle.color_encoding.color_space !=
input_bundle.color_encoding.color_space ||
output_bundle.color_encoding.white_point !=
input_bundle.color_encoding.white_point ||
output_bundle.color_encoding.primaries !=
input_bundle.color_encoding.primaries ||
output_bundle.color_encoding.transfer_function !=
input_bundle.color_encoding.transfer_function ||
output_bundle.color_encoding.rendering_intent !=
input_bundle.color_encoding.rendering_intent ||
output_bundle.alt_icc_size != compressed_icc_size ||
memcmp(output_bundle.alt_icc, compressed_icc, compressed_icc_size) != 0 ||
output_bundle.gain_map_size != sizeof(gain_map) ||
memcmp(output_bundle.gain_map, gain_map, sizeof(gain_map)) != 0) {
fprintf(stderr, "gain map bundle roundtrip mismatch\n");
goto cleanup;
}
if (!JxlICCProfileDecode(&memory_manager, output_bundle.alt_icc,
output_bundle.alt_icc_size, &decoded_icc,
&decoded_icc_size)) {
fprintf(stderr, "ICC byte-stream decode failed\n");
goto cleanup;
}
if (decoded_icc_size != sizeof(icc_data) ||
memcmp(decoded_icc, icc_data, sizeof(icc_data)) != 0) {
fprintf(stderr, "ICC byte-stream roundtrip mismatch\n");
goto cleanup;
}

result = EXIT_SUCCESS;

cleanup:
memory_manager.free(memory_manager.opaque, decoded_icc);
memory_manager.free(memory_manager.opaque, compressed_icc);
free(bundle_buffer);
return result;
}
89 changes: 89 additions & 0 deletions lib/gain_map_public_api_decode_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/* 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 <jxl/gain_map.h>
#include <jxl/memory_manager.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* These fixed bytes were generated once from synthetic data with the public
* bundle and ICC helpers. This test calls only their decoder counterparts.
* The ICC payload is opaque test data, not a color profile. */
static const uint8_t kSyntheticIccBytes[] = {
0x00, 0x00, 0x00, 0x3c, 0x73, 0x79, 0x6e, 0x74, 0x68, 0x65, 0x74, 0x69,
0x63, 0x2d, 0x49, 0x43, 0x43, 0x2d, 0x74, 0x65, 0x73, 0x74, 0x2d, 0x64,
0x61, 0x74, 0x61, 0x2d, 0x66, 0x6f, 0x72, 0x2d, 0x6c, 0x69, 0x62, 0x6a,
0x78, 0x6c, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
};

static const uint8_t kGainMapBundleBytes[] = {
0x00, 0x00, 0x03, 0x10, 0x20, 0x30, 0x03, 0x50, 0xb4, 0x00, 0x00,
0x00, 0x00, 0x46, 0xb6, 0xc8, 0xf1, 0x7a, 0xd6, 0xe4, 0x3e, 0xc0,
0x80, 0xcd, 0x5a, 0x55, 0x93, 0xa8, 0xbb, 0x5f, 0x4b, 0x9b, 0x5b,
0x11, 0x11, 0x49, 0x49, 0xc4, 0x55, 0x10, 0xa5, 0x22, 0xeb, 0x7e,
0x5f, 0xb4, 0xe0, 0x8f, 0x75, 0xae, 0x5e, 0x85, 0x61, 0xc9, 0x47,
0x84, 0x8a, 0x88, 0x62, 0xbc, 0x26, 0x0a, 0x93, 0x08, 0xea, 0x9b,
0x6c, 0x19, 0x9b, 0xb6, 0xeb, 0x87, 0x71, 0x32, 0xf3, 0xb2, 0x6e,
0xfb, 0x61, 0x39, 0xb6, 0x7b, 0xf2, 0x00, 0xff, 0x00, 0x80,
};

static void* Allocate(void* opaque, size_t size) {
(void)opaque;
return malloc(size);
}

static void Release(void* opaque, void* address) {
(void)opaque;
free(address);
}

int main(void) {
JxlMemoryManager memory_manager = {NULL, Allocate, Release};
JxlGainMapBundle bundle = {0};
uint8_t* icc_bytes = NULL;
size_t icc_size = 0;
size_t bytes_read = 0;
int result = EXIT_FAILURE;

if (!JxlGainMapReadBundle(&bundle, kGainMapBundleBytes,
sizeof(kGainMapBundleBytes), &bytes_read)) {
fprintf(stderr, "JxlGainMapReadBundle failed\n");
goto cleanup;
}
if (bytes_read != sizeof(kGainMapBundleBytes) || bundle.jhgm_version != 0 ||
bundle.gain_map_metadata_size != 3 ||
memcmp(bundle.gain_map_metadata, "\x10\x20\x30", 3) != 0 ||
!bundle.has_color_encoding ||
bundle.color_encoding.color_space != JXL_COLOR_SPACE_RGB ||
bundle.color_encoding.white_point != JXL_WHITE_POINT_D65 ||
bundle.color_encoding.primaries != JXL_PRIMARIES_SRGB ||
bundle.color_encoding.transfer_function != JXL_TRANSFER_FUNCTION_LINEAR ||
bundle.gain_map_size != 3 ||
memcmp(bundle.gain_map, "\xff\x00\x80", 3) != 0) {
fprintf(stderr, "gain map reader returned unexpected data\n");
goto cleanup;
}
if (!JxlICCProfileDecode(&memory_manager, bundle.alt_icc, bundle.alt_icc_size,
&icc_bytes, &icc_size)) {
fprintf(stderr, "JxlICCProfileDecode failed\n");
goto cleanup;
}
if (icc_size != sizeof(kSyntheticIccBytes) ||
memcmp(icc_bytes, kSyntheticIccBytes, sizeof(kSyntheticIccBytes)) != 0) {
fprintf(stderr, "ICC decoder returned unexpected bytes\n");
goto cleanup;
}

result = EXIT_SUCCESS;

cleanup:
memory_manager.free(memory_manager.opaque, icc_bytes);
return result;
}
26 changes: 0 additions & 26 deletions lib/extras/compressed_icc.cc → lib/jxl/compressed_icc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,35 +14,9 @@
#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/dec_bit_reader.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/enc_icc_codec.h"
#include "lib/jxl/icc_codec.h"
#include "lib/jxl/memory_manager_internal.h"

JXL_BOOL JxlICCProfileEncode(const JxlMemoryManager* memory_manager,
const uint8_t* icc, size_t icc_size,
uint8_t** compressed_icc,
size_t* compressed_icc_size) {
JxlMemoryManager local_memory_manager;
if (!jxl::MemoryManagerInit(&local_memory_manager, memory_manager)) {
return JXL_FALSE;
}
jxl::BitWriter writer(&local_memory_manager);
JXL_RETURN_IF_ERROR(jxl::WriteICC(jxl::Span<const uint8_t>(icc, icc_size),
&writer, jxl::LayerType::Header, nullptr));
writer.ZeroPadToByte();
jxl::Bytes bytes = writer.GetSpan();
*compressed_icc_size = bytes.size();
*compressed_icc = static_cast<uint8_t*>(
jxl::MemoryManagerAlloc(&local_memory_manager, *compressed_icc_size));
if (*compressed_icc == nullptr) {
return JXL_FALSE;
}
memcpy(*compressed_icc, bytes.data(), bytes.size());
return JXL_TRUE;
}

JXL_BOOL JxlICCProfileDecode(const JxlMemoryManager* memory_manager,
const uint8_t* compressed_icc,
size_t compressed_icc_size, uint8_t** icc,
Expand Down
42 changes: 42 additions & 0 deletions lib/jxl/enc_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 <jxl/memory_manager.h>
#include <jxl/types.h>

#include <cstddef>
#include <cstdint>
#include <cstring>

#include "lib/jxl/base/span.h"
#include "lib/jxl/base/status.h"
#include "lib/jxl/enc_aux_out.h"
#include "lib/jxl/enc_bit_writer.h"
#include "lib/jxl/enc_icc_codec.h"
#include "lib/jxl/memory_manager_internal.h"

JXL_BOOL JxlICCProfileEncode(const JxlMemoryManager* memory_manager,
const uint8_t* icc, size_t icc_size,
uint8_t** compressed_icc,
size_t* compressed_icc_size) {
JxlMemoryManager local_memory_manager;
if (!jxl::MemoryManagerInit(&local_memory_manager, memory_manager)) {
return JXL_FALSE;
}
jxl::BitWriter writer(&local_memory_manager);
JXL_RETURN_IF_ERROR(jxl::WriteICC(jxl::Span<const uint8_t>(icc, icc_size),
&writer, jxl::LayerType::Header, nullptr));
writer.ZeroPadToByte();
jxl::Bytes bytes = writer.GetSpan();
*compressed_icc_size = bytes.size();
*compressed_icc = static_cast<uint8_t*>(
jxl::MemoryManagerAlloc(&local_memory_manager, *compressed_icc_size));
if (*compressed_icc == nullptr) {
return JXL_FALSE;
}
memcpy(*compressed_icc, bytes.data(), bytes.size());
return JXL_TRUE;
}
Loading
Loading