Merge pull request #6367 from xiota/jxl-import
Add ability to import JXL images
This commit is contained in:
@@ -45,6 +45,9 @@ endif()
|
||||
if(EXIV2_INCLUDE_DIRS)
|
||||
include_directories("${EXIV2_INCLUDE_DIRS}")
|
||||
endif()
|
||||
if(JXL_INCLUDE_DIRS)
|
||||
include_directories("${JXL_INCLUDE_DIRS}")
|
||||
endif()
|
||||
|
||||
link_directories(
|
||||
"${EXPAT_LIBRARY_DIRS}"
|
||||
@@ -246,6 +249,7 @@ target_link_libraries(rtengine
|
||||
${RSVG_LIBRARIES}
|
||||
${KLT_LIBRARIES}
|
||||
${EXIV2_LIBRARIES}
|
||||
${JXL_LIBRARIES}
|
||||
)
|
||||
|
||||
if(OpenMP_FOUND)
|
||||
|
||||
@@ -25,6 +25,11 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#ifdef LIBJXL
|
||||
#include "jxl/decode_cxx.h"
|
||||
#include "jxl/resizable_parallel_runner_cxx.h"
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <glib/gstdio.h>
|
||||
#include <png.h>
|
||||
@@ -823,6 +828,171 @@ int ImageIO::loadTIFF (const Glib::ustring &fname)
|
||||
return IMIO_SUCCESS;
|
||||
}
|
||||
|
||||
#ifdef LIBJXL
|
||||
#define _PROFILE_ JXL_COLOR_PROFILE_TARGET_ORIGINAL
|
||||
// adapted from libjxl
|
||||
int ImageIO::loadJXL(const Glib::ustring &fname)
|
||||
{
|
||||
if (pl) {
|
||||
pl->setProgressStr("PROGRESSBAR_LOADJXL");
|
||||
pl->setProgress(0.0);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> icc_profile;
|
||||
std::vector<std::uint8_t> buffer;
|
||||
std::size_t buffer_size = 0;
|
||||
|
||||
JxlBasicInfo info = {};
|
||||
JxlPixelFormat format = {};
|
||||
|
||||
format.num_channels = 3;
|
||||
format.data_type = JXL_TYPE_FLOAT;
|
||||
format.endianness = JXL_NATIVE_ENDIAN;
|
||||
format.align = 0;
|
||||
|
||||
std::vector<std::uint8_t> const compressed = getFileData(fname);
|
||||
|
||||
if (compressed.empty()) {
|
||||
std::cerr << "Error: loadJXL failed to get data from file" << std::endl;
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
|
||||
// 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)) {
|
||||
std::cerr << "Error: JxlDecoderSubscribeEvents failed" << std::endl;
|
||||
return IMIO_HEADERERROR;
|
||||
}
|
||||
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderSetParallelRunner(dec.get(), JxlResizableParallelRunner,
|
||||
runner.get())) {
|
||||
std::cerr << "Error: JxlDecoderSetParallelRunner failed" << std::endl;
|
||||
return IMIO_HEADERERROR;
|
||||
}
|
||||
|
||||
// grand decode loop...
|
||||
JxlDecoderSetInput(dec.get(), compressed.data(), compressed.size());
|
||||
|
||||
while (true) {
|
||||
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get());
|
||||
|
||||
if (status == JXL_DEC_BASIC_INFO) {
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &info)) {
|
||||
std::cerr << "Error: JxlDecoderGetBasicInfo failed" << std::endl;
|
||||
return IMIO_HEADERERROR;
|
||||
}
|
||||
|
||||
JxlResizableParallelRunnerSetThreads(
|
||||
runner.get(),
|
||||
JxlResizableParallelRunnerSuggestThreads(info.xsize, info.ysize));
|
||||
} else if (status == JXL_DEC_COLOR_ENCODING) {
|
||||
// check for ICC profile
|
||||
deleteLoadedProfileData();
|
||||
embProfile = nullptr;
|
||||
std::size_t icc_size = 0;
|
||||
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
|
||||
JxlDecoderGetICCProfileSize(dec.get(), &format, _PROFILE_, &icc_size)
|
||||
#else
|
||||
JxlDecoderGetICCProfileSize(dec.get(), _PROFILE_, &icc_size)
|
||||
#endif
|
||||
) {
|
||||
std::cerr << "Warning: JxlDecoderGetICCProfileSize failed" << std::endl;
|
||||
}
|
||||
|
||||
if (icc_size > 0) {
|
||||
icc_profile.resize(icc_size);
|
||||
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
#if JPEGXL_NUMERIC_VERSION < JPEGXL_COMPUTE_NUMERIC_VERSION(0, 9, 0)
|
||||
JxlDecoderGetColorAsICCProfile(
|
||||
dec.get(), &format, _PROFILE_,
|
||||
icc_profile.data(), icc_profile.size())
|
||||
#else
|
||||
JxlDecoderGetColorAsICCProfile(
|
||||
dec.get(), _PROFILE_,
|
||||
icc_profile.data(), icc_profile.size())
|
||||
#endif
|
||||
) {
|
||||
std::cerr << "Warning: JxlDecoderGetColorAsICCProfile failed" << std::endl;
|
||||
} else {
|
||||
embProfile = cmsOpenProfileFromMem(icc_profile.data(),
|
||||
icc_profile.size());
|
||||
}
|
||||
} else {
|
||||
std::cerr << "Warning: Empty ICC data." << std::endl;
|
||||
}
|
||||
} else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) {
|
||||
// Note: If assert is triggered, change to assignment.
|
||||
// We want maximum bit depth from the decoder,
|
||||
// regardless of the original encoding intent.
|
||||
assert(format.data_type == JXL_TYPE_FLOAT);
|
||||
|
||||
if (JXL_DEC_SUCCESS !=
|
||||
JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) {
|
||||
std::cerr << "Error: JxlDecoderImageOutBufferSize failed" << std::endl;
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
|
||||
buffer.resize(buffer_size);
|
||||
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, buffer.data(), buffer.size())) {
|
||||
std::cerr << "Error: JxlDecoderSetImageOutBuffer failed" << std::endl;
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
} else if (status == JXL_DEC_FULL_IMAGE ||
|
||||
status == JXL_DEC_FRAME) {
|
||||
// Nothing to do. If the image is an animation, more full frames
|
||||
// may be decoded. This example only keeps the first one.
|
||||
break;
|
||||
} else if (status == JXL_DEC_SUCCESS) {
|
||||
// Decoding complete. Decoder will be released automatically.
|
||||
break;
|
||||
} else if (status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
std::cerr << "Error: Decoder needs more input data" << std::endl;
|
||||
return IMIO_READERROR;
|
||||
} else if (status == JXL_DEC_ERROR) {
|
||||
std::cerr << "Error: Decoder error" << std::endl;
|
||||
return IMIO_READERROR;
|
||||
} else {
|
||||
std::cerr << "Error: Unknown decoder status" << std::endl;
|
||||
return IMIO_READERROR;
|
||||
}
|
||||
} // end grand decode loop
|
||||
|
||||
std::size_t width = info.xsize;
|
||||
std::size_t height = info.ysize;
|
||||
|
||||
allocate(width, height);
|
||||
|
||||
std::size_t line_length = width * 3 * 4;
|
||||
|
||||
for (std::size_t row = 0; row < height; ++row) {
|
||||
setScanline(row, buffer.data() + (row * line_length), 32);
|
||||
|
||||
if (pl && !(row % 100)) {
|
||||
pl->setProgress((double)(row + 1) / height);
|
||||
}
|
||||
}
|
||||
|
||||
if (pl) {
|
||||
pl->setProgressStr("PROGRESSBAR_READY");
|
||||
pl->setProgress(1.0);
|
||||
}
|
||||
|
||||
return IMIO_SUCCESS;
|
||||
}
|
||||
#undef _PROFILE_
|
||||
#endif // LIBJXL
|
||||
|
||||
int ImageIO::loadPPMFromMemory(const char* buffer, int width, int height, bool swap, int bps)
|
||||
{
|
||||
allocate (width, height);
|
||||
@@ -1311,6 +1481,10 @@ int ImageIO::load (const Glib::ustring &fname)
|
||||
return loadPNG (fname);
|
||||
} else if (hasJpegExtension(fname)) {
|
||||
return loadJPEG (fname);
|
||||
#ifdef LIBJXL
|
||||
} else if (hasJxlExtension(fname)) {
|
||||
return loadJXL(fname);
|
||||
#endif
|
||||
} else if (hasTiffExtension(fname)) {
|
||||
return loadTIFF (fname);
|
||||
} else {
|
||||
|
||||
@@ -90,6 +90,10 @@ public:
|
||||
int load (const Glib::ustring &fname);
|
||||
int save (const Glib::ustring &fname) const;
|
||||
|
||||
#ifdef LIBJXL
|
||||
int loadJXL (const Glib::ustring &fname);
|
||||
#endif
|
||||
|
||||
int loadPNG (const Glib::ustring &fname);
|
||||
int loadJPEG (const Glib::ustring &fname);
|
||||
int loadTIFF (const Glib::ustring &fname);
|
||||
|
||||
@@ -90,6 +90,12 @@ void StdImageSource::getSampleFormat (const Glib::ustring &fname, IIOSampleForma
|
||||
if (result == IMIO_SUCCESS) {
|
||||
return;
|
||||
}
|
||||
#ifdef LIBJXL
|
||||
} else if (hasJxlExtension(fname)) {
|
||||
sFormat = IIOSF_FLOAT32;
|
||||
sArrangement = IIOSA_CHUNKY;
|
||||
return;
|
||||
#endif
|
||||
} else if (hasTiffExtension(fname)) {
|
||||
int result = ImageIO::getTIFFSampleFormat (fname, sFormat, sArrangement);
|
||||
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <glibmm/convert.h>
|
||||
#include "rt_math.h"
|
||||
|
||||
#include "utils.h"
|
||||
@@ -222,6 +224,23 @@ void vflip(unsigned char* img, int w, int h)
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> getFileData(const Glib::ustring &filename)
|
||||
{
|
||||
try {
|
||||
const std::string fn = Glib::filename_from_utf8(filename);
|
||||
std::ifstream instream(fn, std::ios::in | std::ios::binary);
|
||||
|
||||
std::vector<std::uint8_t> contents(
|
||||
(std::istreambuf_iterator<char>(instream)),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
||||
instream.close();
|
||||
return contents;
|
||||
} catch (...) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Glib::ustring getFileExtension(const Glib::ustring& filename)
|
||||
{
|
||||
const Glib::ustring::size_type lastdot_pos = filename.find_last_of('.');
|
||||
@@ -237,6 +256,13 @@ bool hasJpegExtension(const Glib::ustring& filename)
|
||||
return extension == "jpg" || extension == "jpeg";
|
||||
}
|
||||
|
||||
#ifdef LIBJXL
|
||||
bool hasJxlExtension(const Glib::ustring& filename)
|
||||
{
|
||||
return getFileExtension(filename) == "jxl";
|
||||
}
|
||||
#endif
|
||||
|
||||
bool hasTiffExtension(const Glib::ustring& filename)
|
||||
{
|
||||
const Glib::ustring extension = getFileExtension(filename);
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <type_traits>
|
||||
#include <glibmm/ustring.h>
|
||||
|
||||
@@ -43,6 +45,8 @@ constexpr typename std::underlying_type<ENUM>::type toUnderlying(ENUM value)
|
||||
return static_cast<typename std::underlying_type<ENUM>::type>(value);
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> getFileData(const Glib::ustring &filename);
|
||||
|
||||
// Return lower case extension without the "." or "" if the given name contains no "."
|
||||
Glib::ustring getFileExtension(const Glib::ustring& filename);
|
||||
// Return true if file has .jpeg or .jpg extension (ignoring case)
|
||||
@@ -52,6 +56,11 @@ bool hasTiffExtension(const Glib::ustring& filename);
|
||||
// Return true if file has .png extension (ignoring case)
|
||||
bool hasPngExtension(const Glib::ustring& filename);
|
||||
|
||||
#ifdef LIBJXL
|
||||
// Return true if file has .jxl extension (ignoring case)
|
||||
bool hasJxlExtension(const Glib::ustring& filename);
|
||||
#endif
|
||||
|
||||
void swab(const void* from, void* to, ssize_t n);
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user