summaryrefslogtreecommitdiffstats
path: root/third_party/jpeg-xl/examples
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/jpeg-xl/examples')
-rw-r--r--third_party/jpeg-xl/examples/CMakeLists.txt56
-rw-r--r--third_party/jpeg-xl/examples/decode_exif_metadata.cc172
-rw-r--r--third_party/jpeg-xl/examples/decode_oneshot.cc251
-rw-r--r--third_party/jpeg-xl/examples/decode_progressive.cc245
-rw-r--r--third_party/jpeg-xl/examples/encode_oneshot.cc276
-rw-r--r--third_party/jpeg-xl/examples/examples.cmake11
6 files changed, 1011 insertions, 0 deletions
diff --git a/third_party/jpeg-xl/examples/CMakeLists.txt b/third_party/jpeg-xl/examples/CMakeLists.txt
new file mode 100644
index 0000000000..88dc27c49f
--- /dev/null
+++ b/third_party/jpeg-xl/examples/CMakeLists.txt
@@ -0,0 +1,56 @@
+# Copyright (c) the JPEG XL Project Authors. All rights reserved.
+#
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Example project using libjxl.
+
+cmake_minimum_required(VERSION 3.10)
+
+project(SAMPLE_LIBJXL LANGUAGES C CXX)
+
+# Use pkg-config to find libjxl.
+find_package(PkgConfig)
+pkg_check_modules(Jxl REQUIRED IMPORTED_TARGET libjxl)
+pkg_check_modules(JxlThreads REQUIRED IMPORTED_TARGET libjxl_threads)
+
+# Build the example encoder/decoder binaries using the default shared libraries
+# installed.
+add_executable(decode_oneshot decode_oneshot.cc)
+target_link_libraries(decode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)
+
+add_executable(decode_progressive decode_progressive.cc)
+target_link_libraries(decode_progressive PkgConfig::Jxl PkgConfig::JxlThreads)
+
+add_executable(encode_oneshot encode_oneshot.cc)
+target_link_libraries(encode_oneshot PkgConfig::Jxl PkgConfig::JxlThreads)
+
+
+# Building a static binary with the static libjxl dependencies. How to load
+# static library configs from pkg-config and how to build static binaries
+# depends on the platform, and building static binaries in general has problems.
+# If you don't need static binaries you can remove this section.
+add_library(StaticJxl INTERFACE IMPORTED GLOBAL)
+set_target_properties(StaticJxl PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${Jxl_STATIC_INCLUDE_DIR}"
+ INTERFACE_COMPILE_OPTIONS "${Jxl_STATIC_CFLAGS_OTHER}"
+ INTERFACE_LINK_LIBRARIES "${Jxl_STATIC_LDFLAGS}"
+)
+add_library(StaticJxlThreads INTERFACE IMPORTED GLOBAL)
+set_target_properties(StaticJxlThreads PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "${JxlThreads_STATIC_INCLUDE_DIR}"
+ INTERFACE_COMPILE_OPTIONS "${JxlThreads_STATIC_CFLAGS_OTHER}"
+ # libgcc uses weak symbols for pthread which means that -lpthread is not
+ # linked when compiling a static binary. This is a platform-specific fix for
+ # that.
+ INTERFACE_LINK_LIBRARIES
+ "${JxlThreads_STATIC_LDFLAGS} -Wl,--whole-archive -lpthread -Wl,--no-whole-archive"
+)
+
+add_executable(decode_oneshot_static decode_oneshot.cc)
+target_link_libraries(decode_oneshot_static
+ -static StaticJxl StaticJxlThreads)
+
+add_executable(encode_oneshot_static encode_oneshot.cc)
+target_link_libraries(encode_oneshot_static
+ -static StaticJxl StaticJxlThreads)
diff --git a/third_party/jpeg-xl/examples/decode_exif_metadata.cc b/third_party/jpeg-xl/examples/decode_exif_metadata.cc
new file mode 100644
index 0000000000..97b0e52703
--- /dev/null
+++ b/third_party/jpeg-xl/examples/decode_exif_metadata.cc
@@ -0,0 +1,172 @@
+// Copyright (c) the JPEG XL Project Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This C++ example decodes a JPEG XL image in one shot (all input bytes
+// available at once). The example outputs the pixels and color information to a
+// floating point image and an ICC profile on disk.
+
+#include <jxl/decode.h>
+#include <jxl/decode_cxx.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <vector>
+
+bool DecodeJpegXlExif(const uint8_t* jxl, size_t size,
+ std::vector<uint8_t>* exif) {
+ auto dec = JxlDecoderMake(nullptr);
+
+ // We're only interested in the Exif boxes in this example, so don't
+ // subscribe to events related to pixel data.
+ if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BOX)) {
+ fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
+ return false;
+ }
+ bool support_decompression = true;
+ if (JXL_DEC_SUCCESS != JxlDecoderSetDecompressBoxes(dec.get(), JXL_TRUE)) {
+ fprintf(stderr,
+ "NOTE: decompressing brob boxes not supported with the currently "
+ "used jxl library.\n");
+ support_decompression = false;
+ }
+
+ JxlDecoderSetInput(dec.get(), jxl, size);
+ JxlDecoderCloseInput(dec.get());
+
+ const constexpr size_t kChunkSize = 65536;
+ size_t output_pos = 0;
+
+ for (;;) {
+ JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
+ if (status == JXL_DEC_ERROR) {
+ fprintf(stderr, "Decoder error\n");
+ return false;
+ } else if (status == JXL_DEC_NEED_MORE_INPUT) {
+ fprintf(stderr, "Error, already provided all input\n");
+ return false;
+ } else if (status == JXL_DEC_BOX) {
+ if (!exif->empty()) {
+ size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get());
+ exif->resize(exif->size() - remaining);
+ // No need to wait for JXL_DEC_SUCCESS or decode other boxes.
+ return true;
+ }
+ JxlBoxType type;
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderGetBoxType(dec.get(), type, support_decompression)) {
+ fprintf(stderr, "Error, failed to get box type\n");
+ return false;
+ }
+ if (!memcmp(type, "Exif", 4)) {
+ exif->resize(kChunkSize);
+ JxlDecoderSetBoxBuffer(dec.get(), exif->data(), exif->size());
+ }
+ } else if (status == JXL_DEC_BOX_NEED_MORE_OUTPUT) {
+ size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get());
+ output_pos += kChunkSize - remaining;
+ exif->resize(exif->size() + kChunkSize);
+ JxlDecoderSetBoxBuffer(dec.get(), exif->data() + output_pos,
+ exif->size() - output_pos);
+ } else if (status == JXL_DEC_SUCCESS) {
+ if (!exif->empty()) {
+ size_t remaining = JxlDecoderReleaseBoxBuffer(dec.get());
+ exif->resize(exif->size() - remaining);
+ return true;
+ }
+ return true;
+ } else {
+ fprintf(stderr, "Unknown decoder status\n");
+ return false;
+ }
+ }
+}
+
+bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
+ FILE* file = fopen(filename, "rb");
+ if (!file) {
+ return false;
+ }
+
+ if (fseek(file, 0, SEEK_END) != 0) {
+ fclose(file);
+ return false;
+ }
+
+ long size = ftell(file);
+ // Avoid invalid file or directory.
+ if (size >= LONG_MAX || size < 0) {
+ fclose(file);
+ return false;
+ }
+
+ if (fseek(file, 0, SEEK_SET) != 0) {
+ fclose(file);
+ return false;
+ }
+
+ out->resize(size);
+ size_t readsize = fread(out->data(), 1, size, file);
+ if (fclose(file) != 0) {
+ return false;
+ }
+
+ return readsize == static_cast<size_t>(size);
+}
+
+bool WriteFile(const char* filename, const uint8_t* data, size_t size) {
+ FILE* file = fopen(filename, "wb");
+ if (!file) {
+ fprintf(stderr, "Could not open %s for writing", filename);
+ return false;
+ }
+ fwrite(data, 1, size, file);
+ if (fclose(file) != 0) {
+ return false;
+ }
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc != 3) {
+ fprintf(stderr,
+ "Usage: %s <jxl> <exif>\n"
+ "Where:\n"
+ " jxl = input JPEG XL image filename\n"
+ " exif = output exif filename\n"
+ "Output files will be overwritten.\n",
+ argv[0]);
+ return 1;
+ }
+
+ const char* jxl_filename = argv[1];
+ const char* exif_filename = argv[2];
+
+ std::vector<uint8_t> jxl;
+ if (!LoadFile(jxl_filename, &jxl)) {
+ fprintf(stderr, "couldn't load %s\n", jxl_filename);
+ return 1;
+ }
+
+ std::vector<uint8_t> exif;
+ if (!DecodeJpegXlExif(jxl.data(), jxl.size(), &exif)) {
+ fprintf(stderr, "Error while decoding the jxl file\n");
+ return 1;
+ }
+ if (exif.empty()) {
+ printf("No exif data present in this image\n");
+ } else {
+ // TODO(lode): the exif box data contains the 4-byte TIFF header at the
+ // beginning, check whether this is desired to be part of the output, or
+ // should be removed.
+ if (!WriteFile(exif_filename, exif.data(), exif.size())) {
+ fprintf(stderr, "Error while writing the exif file\n");
+ return 1;
+ }
+ printf("Successfully wrote %s\n", exif_filename);
+ }
+ return 0;
+}
diff --git a/third_party/jpeg-xl/examples/decode_oneshot.cc b/third_party/jpeg-xl/examples/decode_oneshot.cc
new file mode 100644
index 0000000000..8cf9d4f3a6
--- /dev/null
+++ b/third_party/jpeg-xl/examples/decode_oneshot.cc
@@ -0,0 +1,251 @@
+// Copyright (c) the JPEG XL Project Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This C++ example decodes a JPEG XL image in one shot (all input bytes
+// available at once). The example outputs the pixels and color information to a
+// floating point image and an ICC profile on disk.
+
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include <inttypes.h>
+#include <jxl/decode.h>
+#include <jxl/decode_cxx.h>
+#include <jxl/resizable_parallel_runner.h>
+#include <jxl/resizable_parallel_runner_cxx.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <vector>
+
+/** Decodes JPEG XL image to floating point pixels and ICC Profile. Pixel are
+ * stored as floating point, as interleaved RGBA (4 floating point values per
+ * pixel), line per line from top to bottom. Pixel values have nominal range
+ * 0..1 but may go beyond this range for HDR or wide gamut. The ICC profile
+ * describes the color format of the pixel data.
+ */
+bool DecodeJpegXlOneShot(const uint8_t* jxl, size_t size,
+ std::vector<float>* pixels, size_t* xsize,
+ size_t* ysize, std::vector<uint8_t>* icc_profile) {
+ // Multi-threaded parallel runner.
+ auto runner = JxlResizableParallelRunnerMake(nullptr);
+
+ auto dec = JxlDecoderMake(nullptr);
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
+ JXL_DEC_COLOR_ENCODING |
+ JXL_DEC_FULL_IMAGE)) {
+ fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
+ return false;
+ }
+
+ if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(),
+ JxlResizableParallelRunner,
+ runner.get())) {
+ fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
+ return false;
+ }
+
+ JxlBasicInfo info;
+ JxlPixelFormat format = {4, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
+
+ JxlDecoderSetInput(dec.get(), jxl, size);
+ JxlDecoderCloseInput(dec.get());
+
+ for (;;) {
+ JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
+
+ if (status == JXL_DEC_ERROR) {
+ fprintf(stderr, "Decoder error\n");
+ return false;
+ } else if (status == JXL_DEC_NEED_MORE_INPUT) {
+ fprintf(stderr, "Error, already provided all input\n");
+ return false;
+ } else if (status == JXL_DEC_BASIC_INFO) {
+ if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
+ fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
+ return false;
+ }
+ *xsize = info.xsize;
+ *ysize = info.ysize;
+ JxlResizableParallelRunnerSetThreads(
+ runner.get(),
+ JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
+ } else if (status == JXL_DEC_COLOR_ENCODING) {
+ // Get the ICC color profile of the pixel data
+ size_t icc_size;
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderGetICCProfileSize(
+ dec.get(), &format, JXL_COLOR_PROFILE_TARGET_DATA, &icc_size)) {
+ fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
+ return false;
+ }
+ icc_profile->resize(icc_size);
+ if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
+ dec.get(), &format,
+ JXL_COLOR_PROFILE_TARGET_DATA,
+ icc_profile->data(), icc_profile->size())) {
+ fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
+ return false;
+ }
+ } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
+ size_t buffer_size;
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
+ fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
+ return false;
+ }
+ if (buffer_size != *xsize * *ysize * 16) {
+ fprintf(stderr, "Invalid out buffer size %" PRIu64 " %" PRIu64 "\n",
+ static_cast<uint64_t>(buffer_size),
+ static_cast<uint64_t>(*xsize * *ysize * 16));
+ return false;
+ }
+ pixels->resize(*xsize * *ysize * 4);
+ void* pixels_buffer = (void*)pixels->data();
+ size_t pixels_buffer_size = pixels->size() * sizeof(float);
+ if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
+ pixels_buffer,
+ pixels_buffer_size)) {
+ fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
+ return false;
+ }
+ } else if (status == JXL_DEC_FULL_IMAGE) {
+ // Nothing to do. Do not yet return. If the image is an animation, more
+ // full frames may be decoded. This example only keeps the last one.
+ } else if (status == JXL_DEC_SUCCESS) {
+ // All decoding successfully finished.
+ // It's not required to call JxlDecoderReleaseInput(dec.get()) here since
+ // the decoder will be destroyed.
+ return true;
+ } else {
+ fprintf(stderr, "Unknown decoder status\n");
+ return false;
+ }
+ }
+}
+
+/** Writes to .pfm file (Portable FloatMap). Gimp, tev viewer and ImageMagick
+ * support viewing this format.
+ * The input pixels are given as 32-bit floating point with 4-channel RGBA.
+ * The alpha channel will not be written since .pfm does not support it.
+ */
+bool WritePFM(const char* filename, const float* pixels, size_t xsize,
+ size_t ysize) {
+ FILE* file = fopen(filename, "wb");
+ if (!file) {
+ fprintf(stderr, "Could not open %s for writing", filename);
+ return false;
+ }
+ uint32_t endian_test = 1;
+ uint8_t little_endian[4];
+ memcpy(little_endian, &endian_test, 4);
+
+ fprintf(file, "PF\n%d %d\n%s\n", (int)xsize, (int)ysize,
+ little_endian[0] ? "-1.0" : "1.0");
+ for (int y = ysize - 1; y >= 0; y--) {
+ for (size_t x = 0; x < xsize; x++) {
+ for (size_t c = 0; c < 3; c++) {
+ const float* f = &pixels[(y * xsize + x) * 4 + c];
+ fwrite(f, 4, 1, file);
+ }
+ }
+ }
+ if (fclose(file) != 0) {
+ return false;
+ }
+ return true;
+}
+
+bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
+ FILE* file = fopen(filename, "rb");
+ if (!file) {
+ return false;
+ }
+
+ if (fseek(file, 0, SEEK_END) != 0) {
+ fclose(file);
+ return false;
+ }
+
+ long size = ftell(file);
+ // Avoid invalid file or directory.
+ if (size >= LONG_MAX || size < 0) {
+ fclose(file);
+ return false;
+ }
+
+ if (fseek(file, 0, SEEK_SET) != 0) {
+ fclose(file);
+ return false;
+ }
+
+ out->resize(size);
+ size_t readsize = fread(out->data(), 1, size, file);
+ if (fclose(file) != 0) {
+ return false;
+ }
+
+ return readsize == static_cast<size_t>(size);
+}
+
+bool WriteFile(const char* filename, const uint8_t* data, size_t size) {
+ FILE* file = fopen(filename, "wb");
+ if (!file) {
+ fprintf(stderr, "Could not open %s for writing", filename);
+ return false;
+ }
+ fwrite(data, 1, size, file);
+ if (fclose(file) != 0) {
+ return false;
+ }
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc != 4) {
+ fprintf(stderr,
+ "Usage: %s <jxl> <pfm> <icc>\n"
+ "Where:\n"
+ " jxl = input JPEG XL image filename\n"
+ " pfm = output Portable FloatMap image filename\n"
+ " icc = output ICC color profile filename\n"
+ "Output files will be overwritten.\n",
+ argv[0]);
+ return 1;
+ }
+
+ const char* jxl_filename = argv[1];
+ const char* pfm_filename = argv[2];
+ const char* icc_filename = argv[3];
+
+ std::vector<uint8_t> jxl;
+ if (!LoadFile(jxl_filename, &jxl)) {
+ fprintf(stderr, "couldn't load %s\n", jxl_filename);
+ return 1;
+ }
+
+ std::vector<float> pixels;
+ std::vector<uint8_t> icc_profile;
+ size_t xsize = 0, ysize = 0;
+ if (!DecodeJpegXlOneShot(jxl.data(), jxl.size(), &pixels, &xsize, &ysize,
+ &icc_profile)) {
+ fprintf(stderr, "Error while decoding the jxl file\n");
+ return 1;
+ }
+ if (!WritePFM(pfm_filename, pixels.data(), xsize, ysize)) {
+ fprintf(stderr, "Error while writing the PFM image file\n");
+ return 1;
+ }
+ if (!WriteFile(icc_filename, icc_profile.data(), icc_profile.size())) {
+ fprintf(stderr, "Error while writing the ICC profile file\n");
+ return 1;
+ }
+ printf("Successfully wrote %s and %s\n", pfm_filename, icc_filename);
+ return 0;
+}
diff --git a/third_party/jpeg-xl/examples/decode_progressive.cc b/third_party/jpeg-xl/examples/decode_progressive.cc
new file mode 100644
index 0000000000..0d035121e4
--- /dev/null
+++ b/third_party/jpeg-xl/examples/decode_progressive.cc
@@ -0,0 +1,245 @@
+// Copyright (c) the JPEG XL Project Authors. All rights reserved.
+//
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This C++ example decodes a JPEG XL image progressively (input bytes are
+// passed in chunks). The example outputs the intermediate steps to PAM files.
+
+#ifndef __STDC_FORMAT_MACROS
+#define __STDC_FORMAT_MACROS
+#endif
+
+#include <inttypes.h>
+#include <jxl/decode.h>
+#include <jxl/decode_cxx.h>
+#include <jxl/resizable_parallel_runner.h>
+#include <jxl/resizable_parallel_runner_cxx.h>
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <vector>
+
+bool WritePAM(const char* filename, const uint8_t* buffer, size_t w, size_t h) {
+ FILE* fp = fopen(filename, "wb");
+ if (!fp) {
+ fprintf(stderr, "Could not open %s for writing", filename);
+ return false;
+ }
+ fprintf(fp,
+ "P7\nWIDTH %" PRIu64 "\nHEIGHT %" PRIu64
+ "\nDEPTH 4\nMAXVAL 255\nTUPLTYPE "
+ "RGB_ALPHA\nENDHDR\n",
+ static_cast<uint64_t>(w), static_cast<uint64_t>(h));
+ size_t num_bytes = w * h * 4;
+ if (fwrite(buffer, 1, num_bytes, fp) != num_bytes) {
+ fclose(fp);
+ return false;
+ };
+ if (fclose(fp) != 0) {
+ return false;
+ }
+ return true;
+}
+
+/** Decodes JPEG XL image to 8-bit integer RGBA pixels and an ICC Profile, in a
+ * progressive way, saving the intermediate steps.
+ */
+bool DecodeJpegXlProgressive(const uint8_t* jxl, size_t size,
+ const char* filename, size_t chunksize) {
+ std::vector<uint8_t> pixels;
+ std::vector<uint8_t> icc_profile;
+ size_t xsize = 0, ysize = 0;
+
+ // Multi-threaded parallel runner.
+ auto runner = JxlResizableParallelRunnerMake(nullptr);
+
+ auto dec = JxlDecoderMake(nullptr);
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO |
+ JXL_DEC_COLOR_ENCODING |
+ JXL_DEC_FULL_IMAGE)) {
+ fprintf(stderr, "JxlDecoderSubscribeEvents failed\n");
+ return false;
+ }
+
+ if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(),
+ JxlResizableParallelRunner,
+ runner.get())) {
+ fprintf(stderr, "JxlDecoderSetParallelRunner failed\n");
+ return false;
+ }
+
+ JxlBasicInfo info;
+ JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0};
+
+ size_t seen = 0;
+ JxlDecoderSetInput(dec.get(), jxl, chunksize);
+ size_t remaining = chunksize;
+
+ for (;;) {
+ JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
+
+ if (status == JXL_DEC_ERROR) {
+ fprintf(stderr, "Decoder error\n");
+ return false;
+ } else if (status == JXL_DEC_NEED_MORE_INPUT || status == JXL_DEC_SUCCESS ||
+ status == JXL_DEC_FULL_IMAGE) {
+ seen += remaining - JxlDecoderReleaseInput(dec.get());
+ printf("Flushing after %" PRIu64 " bytes\n", static_cast<uint64_t>(seen));
+ if (status == JXL_DEC_NEED_MORE_INPUT &&
+ JXL_DEC_SUCCESS != JxlDecoderFlushImage(dec.get())) {
+ printf("flush error (no preview yet)\n");
+ } else {
+ char fname[1024];
+ if (snprintf(fname, 1024, "%s-%" PRIu64 ".pam", filename,
+ static_cast<uint64_t>(seen)) >= 1024) {
+ fprintf(stderr, "Filename too long\n");
+ return false;
+ };
+ if (!WritePAM(fname, pixels.data(), xsize, ysize)) {
+ fprintf(stderr, "Error writing progressive output\n");
+ }
+ }
+ remaining = size - seen;
+ if (remaining > chunksize) remaining = chunksize;
+ if (remaining == 0) {
+ if (status == JXL_DEC_NEED_MORE_INPUT) {
+ fprintf(stderr, "Error, already provided all input\n");
+ return false;
+ } else {
+ return true;
+ }
+ }
+ JxlDecoderSetInput(dec.get(), jxl + seen, remaining);
+ } else if (status == JXL_DEC_BASIC_INFO) {
+ if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
+ fprintf(stderr, "JxlDecoderGetBasicInfo failed\n");
+ return false;
+ }
+ xsize = info.xsize;
+ ysize = info.ysize;
+ JxlResizableParallelRunnerSetThreads(
+ runner.get(),
+ JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
+ } else if (status == JXL_DEC_COLOR_ENCODING) {
+ // Get the ICC color profile of the pixel data
+ size_t icc_size;
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderGetICCProfileSize(dec.get(), &format,
+ JXL_COLOR_PROFILE_TARGET_ORIGINAL,
+ &icc_size)) {
+ fprintf(stderr, "JxlDecoderGetICCProfileSize failed\n");
+ return false;
+ }
+ icc_profile.resize(icc_size);
+ if (JXL_DEC_SUCCESS != JxlDecoderGetColorAsICCProfile(
+ dec.get(), &format,
+ JXL_COLOR_PROFILE_TARGET_ORIGINAL,
+ icc_profile.data(), icc_profile.size())) {
+ fprintf(stderr, "JxlDecoderGetColorAsICCProfile failed\n");
+ return false;
+ }
+ } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
+ size_t buffer_size;
+ if (JXL_DEC_SUCCESS !=
+ JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
+ fprintf(stderr, "JxlDecoderImageOutBufferSize failed\n");
+ return false;
+ }
+ if (buffer_size != xsize * ysize * 4) {
+ fprintf(stderr, "Invalid out buffer size %" PRIu64 " != %" PRIu64 "\n",
+ static_cast<uint64_t>(buffer_size),
+ static_cast<uint64_t>(xsize * ysize * 4));
+ return false;
+ }
+ pixels.resize(xsize * ysize * 4);
+ void* pixels_buffer = (void*)pixels.data();
+ size_t pixels_buffer_size = pixels.size() * sizeof(float);
+ if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format,
+ pixels_buffer,
+ pixels_buffer_size)) {
+ fprintf(stderr, "JxlDecoderSetImageOutBuffer failed\n");
+ return false;
+ }
+ } else {
+ fprintf(stderr, "Unknown decoder status\n");
+ return false;
+ }
+ }
+}
+
+bool LoadFile(const char* filename, std::vector<uint8_t>* out) {
+ FILE* file = fopen(filename, "rb");
+ if (!file) {
+ return false;
+ }
+
+ if (fseek(file, 0, SEEK_END) != 0) {
+ fclose(file);
+ return false;
+ }
+
+ long size = ftell(file);
+ // Avoid invalid file or directory.
+ if (size >= LONG_MAX || size < 0) {
+ fclose(file);
+ return false;
+ }
+
+ if (fseek(file, 0, SEEK_SET) != 0) {
+ fclose(file);
+ return false;
+ }
+
+ out->resize(size);
+ size_t readsize = fread(out->data(), 1, size, file);
+ if (fclose(file) != 0) {
+ return false;
+ }
+
+ return readsize == static_cast<size_t>(size);
+}
+
+int main(int argc, char* argv[]) {
+ if (argc < 3) {
+ fprintf(
+ stderr,
+ "Usage: %s <jxl> <basename> [chunksize]\n"
+ "Where:\n"
+ " jxl = input JPEG XL image filename\n"
+ " basename = prefix of output filenames\n"
+ " chunksize = loads chunksize bytes at a time and writes\n"
+ " intermediate results to basename-[bytes loaded].pam\n"
+ "Output files will be overwritten.\n",
+ argv[0]);
+ return 1;
+ }
+
+ const char* jxl_filename = argv[1];
+ const char* png_filename = argv[2];
+
+ std::vector<uint8_t> jxl;
+ if (!LoadFile(jxl_filename, &jxl)) {
+ fprintf(stderr, "couldn't load %s\n", jxl_filename);
+ return 1;
+ }
+ size_t chunksize = jxl.size();
+ if (argc > 3) {
+ long cs = atol(argv[3]);
+ if (cs < 100) {
+ fprintf(stderr, "Chunk size is too low, try at least 100 bytes\n");
+ return 1;
+ }
+ chunksize = cs;
+ }
+
+ if (!DecodeJpegXlProgressive(jxl.data(), jxl.size(), png_filename,
+ chunksize)) {
+ fprintf(stderr, "Error while decoding the jxl file\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/third_party/jpeg-xl/examples/encode_oneshot.cc b/third_party/jpeg-xl/examples/encode_oneshot.cc
new file mode 100644
index 0000000000..49b360ce3b
--- /dev/null
+++ b/third_party/jpeg-xl/examples/encode_oneshot.cc
@@ -0,0 +1,276 @@
+// 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 example encodes a file containing a floating point image to another
+// file containing JPEG XL image with a single frame.
+
+#include <jxl/encode.h>
+#include <jxl/encode_cxx.h>
+#include <jxl/thread_parallel_runner.h>
+#include <jxl/thread_parallel_runner_cxx.h>
+#include <limits.h>
+#include <string.h>
+
+#include <sstream>
+#include <string>
+#include <vector>
+
+/**
+ * Reads from .pfm file (Portable FloatMap)
+ *
+ * @param filename name of the file to read
+ * @param pixels vector to fill with loaded pixels as 32-bit floating point with
+ * 3-channel RGB
+ * @param xsize set to width of loaded image
+ * @param ysize set to height of loaded image
+ */
+bool ReadPFM(const char* filename, std::vector<float>* pixels, uint32_t* xsize,
+ uint32_t* ysize) {
+ FILE* file = fopen(filename, "rb");
+ if (!file) {
+ fprintf(stderr, "Could not open %s for reading.\n", filename);
+ return false;
+ }
+ uint32_t endian_test = 1;
+ uint8_t little_endian[4];
+ memcpy(little_endian, &endian_test, 4);
+
+ if (fseek(file, 0, SEEK_END) != 0) {
+ fclose(file);
+ return false;
+ }
+
+ long size = ftell(file);
+ // Avoid invalid file or directory.
+ if (size >= LONG_MAX || size < 0) {
+ fclose(file);
+ return false;
+ }
+
+ if (fseek(file, 0, SEEK_SET) != 0) {
+ fclose(file);
+ return false;
+ }
+
+ std::vector<char> data;
+ data.resize(size);
+
+ size_t readsize = fread(data.data(), 1, size, file);
+ if ((long)readsize != size) {
+ return false;
+ }
+ if (fclose(file) != 0) {
+ return false;
+ }
+
+ std::stringstream datastream;
+ std::string datastream_content(data.data(), data.size());
+ datastream.str(datastream_content);
+
+ std::string pf_token;
+ getline(datastream, pf_token, '\n');
+ if (pf_token != "PF") {
+ fprintf(stderr,
+ "%s doesn't seem to be a 3 channel Portable FloatMap file (missing "
+ "'PF\\n' "
+ "bytes).\n",
+ filename);
+ return false;
+ }
+
+ std::string xsize_token;
+ getline(datastream, xsize_token, ' ');
+ *xsize = std::stoi(xsize_token);
+
+ std::string ysize_token;
+ getline(datastream, ysize_token, '\n');
+ *ysize = std::stoi(ysize_token);
+
+ std::string endianness_token;
+ getline(datastream, endianness_token, '\n');
+ bool input_little_endian;
+ if (endianness_token == "1.0") {
+ input_little_endian = false;
+ } else if (endianness_token == "-1.0") {
+ input_little_endian = true;
+ } else {
+ fprintf(stderr,
+ "%s doesn't seem to be a Portable FloatMap file (endianness token "
+ "isn't '1.0' or '-1.0').\n",
+ filename);
+ return false;
+ }
+
+ size_t offset = pf_token.size() + 1 + xsize_token.size() + 1 +
+ ysize_token.size() + 1 + endianness_token.size() + 1;
+
+ if (data.size() != *ysize * *xsize * 3 * 4 + offset) {
+ fprintf(stderr,
+ "%s doesn't seem to be a Portable FloatMap file (pixel data bytes "
+ "are %d, but expected %d * %d * 3 * 4 + %d (%d).\n",
+ filename, (int)data.size(), (int)*ysize, (int)*xsize, (int)offset,
+ (int)(*ysize * *xsize * 3 * 4 + offset));
+ return false;
+ }
+
+ if (!!little_endian[0] != input_little_endian) {
+ fprintf(stderr,
+ "%s has a different endianness than we do, conversion is not "
+ "supported.\n",
+ filename);
+ return false;
+ }
+
+ pixels->resize(*ysize * *xsize * 3);
+
+ for (int y = *ysize - 1; y >= 0; y--) {
+ for (int x = 0; x < (int)*xsize; x++) {
+ for (int c = 0; c < 3; c++) {
+ memcpy(pixels->data() + (y * *xsize + x) * 3 + c, data.data() + offset,
+ sizeof(float));
+ offset += sizeof(float);
+ }
+ }
+ }
+
+ return true;
+}
+
+/**
+ * Compresses the provided pixels.
+ *
+ * @param pixels input pixels
+ * @param xsize width of the input image
+ * @param ysize height of the input image
+ * @param compressed will be populated with the compressed bytes
+ */
+bool EncodeJxlOneshot(const std::vector<float>& pixels, const uint32_t xsize,
+ const uint32_t ysize, std::vector<uint8_t>* compressed) {
+ auto enc = JxlEncoderMake(/*memory_manager=*/nullptr);
+ auto runner = JxlThreadParallelRunnerMake(
+ /*memory_manager=*/nullptr,
+ JxlThreadParallelRunnerDefaultNumWorkerThreads());
+ if (JXL_ENC_SUCCESS != JxlEncoderSetParallelRunner(enc.get(),
+ JxlThreadParallelRunner,
+ runner.get())) {
+ fprintf(stderr, "JxlEncoderSetParallelRunner failed\n");
+ return false;
+ }
+
+ JxlPixelFormat pixel_format = {3, JXL_TYPE_FLOAT, JXL_NATIVE_ENDIAN, 0};
+
+ JxlBasicInfo basic_info;
+ JxlEncoderInitBasicInfo(&basic_info);
+ basic_info.xsize = xsize;
+ basic_info.ysize = ysize;
+ basic_info.bits_per_sample = 32;
+ basic_info.exponent_bits_per_sample = 8;
+ basic_info.uses_original_profile = JXL_FALSE;
+ if (JXL_ENC_SUCCESS != JxlEncoderSetBasicInfo(enc.get(), &basic_info)) {
+ fprintf(stderr, "JxlEncoderSetBasicInfo failed\n");
+ return false;
+ }
+
+ JxlColorEncoding color_encoding = {};
+ JxlColorEncodingSetToSRGB(&color_encoding,
+ /*is_gray=*/pixel_format.num_channels < 3);
+ if (JXL_ENC_SUCCESS !=
+ JxlEncoderSetColorEncoding(enc.get(), &color_encoding)) {
+ fprintf(stderr, "JxlEncoderSetColorEncoding failed\n");
+ return false;
+ }
+
+ JxlEncoderFrameSettings* frame_settings =
+ JxlEncoderFrameSettingsCreate(enc.get(), nullptr);
+
+ if (JXL_ENC_SUCCESS !=
+ JxlEncoderAddImageFrame(frame_settings, &pixel_format,
+ (void*)pixels.data(),
+ sizeof(float) * pixels.size())) {
+ fprintf(stderr, "JxlEncoderAddImageFrame failed\n");
+ return false;
+ }
+ JxlEncoderCloseInput(enc.get());
+
+ compressed->resize(64);
+ uint8_t* next_out = compressed->data();
+ size_t avail_out = compressed->size() - (next_out - compressed->data());
+ JxlEncoderStatus process_result = JXL_ENC_NEED_MORE_OUTPUT;
+ while (process_result == JXL_ENC_NEED_MORE_OUTPUT) {
+ process_result = JxlEncoderProcessOutput(enc.get(), &next_out, &avail_out);
+ if (process_result == JXL_ENC_NEED_MORE_OUTPUT) {
+ size_t offset = next_out - compressed->data();
+ compressed->resize(compressed->size() * 2);
+ next_out = compressed->data() + offset;
+ avail_out = compressed->size() - offset;
+ }
+ }
+ compressed->resize(next_out - compressed->data());
+ if (JXL_ENC_SUCCESS != process_result) {
+ fprintf(stderr, "JxlEncoderProcessOutput failed\n");
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * Writes bytes to file.
+ */
+bool WriteFile(const std::vector<uint8_t>& bytes, const char* filename) {
+ FILE* file = fopen(filename, "wb");
+ if (!file) {
+ fprintf(stderr, "Could not open %s for writing\n", filename);
+ return false;
+ }
+ if (fwrite(bytes.data(), sizeof(uint8_t), bytes.size(), file) !=
+ bytes.size()) {
+ fprintf(stderr, "Could not write bytes to %s\n", filename);
+ fclose(file);
+ return false;
+ }
+ if (fclose(file) != 0) {
+ fprintf(stderr, "Could not close %s\n", filename);
+ return false;
+ }
+ return true;
+}
+
+int main(int argc, char* argv[]) {
+ if (argc != 3) {
+ fprintf(stderr,
+ "Usage: %s <pfm> <jxl>\n"
+ "Where:\n"
+ " pfm = input Portable FloatMap image filename\n"
+ " jxl = output JPEG XL image filename\n"
+ "Output files will be overwritten.\n",
+ argv[0]);
+ return 1;
+ }
+
+ const char* pfm_filename = argv[1];
+ const char* jxl_filename = argv[2];
+
+ std::vector<float> pixels;
+ uint32_t xsize;
+ uint32_t ysize;
+ if (!ReadPFM(pfm_filename, &pixels, &xsize, &ysize)) {
+ fprintf(stderr, "Couldn't load %s\n", pfm_filename);
+ return 2;
+ }
+
+ std::vector<uint8_t> compressed;
+ if (!EncodeJxlOneshot(pixels, xsize, ysize, &compressed)) {
+ fprintf(stderr, "Couldn't encode jxl\n");
+ return 3;
+ }
+
+ if (!WriteFile(compressed, jxl_filename)) {
+ fprintf(stderr, "Couldn't write jxl file\n");
+ return 4;
+ }
+
+ return 0;
+}
diff --git a/third_party/jpeg-xl/examples/examples.cmake b/third_party/jpeg-xl/examples/examples.cmake
new file mode 100644
index 0000000000..fd159578bc
--- /dev/null
+++ b/third_party/jpeg-xl/examples/examples.cmake
@@ -0,0 +1,11 @@
+# Copyright (c) the JPEG XL Project Authors. All rights reserved.
+#
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+add_executable(decode_oneshot ${CMAKE_CURRENT_LIST_DIR}/decode_oneshot.cc)
+target_link_libraries(decode_oneshot jxl_dec jxl_threads)
+add_executable(decode_progressive ${CMAKE_CURRENT_LIST_DIR}/decode_progressive.cc)
+target_link_libraries(decode_progressive jxl_dec jxl_threads)
+add_executable(encode_oneshot ${CMAKE_CURRENT_LIST_DIR}/encode_oneshot.cc)
+target_link_libraries(encode_oneshot jxl jxl_threads)