-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* WIP: preparation for JPEG XL support * jxl: add loading support * update jxl abstract * add support for saving jxl (lossless not working) * everything works except setting lossless explicitly * remove unused header * fix wrong quality logic * remove debugging statements * fix lossless encoding * improve support for grayscale images * use JXL instead of JPEGXL everywhere * oops do not make libjxl a requirement * update years * silence some warnings * simplify loader fast path logic * allow python to save jxl and webp * update error message with supported formats * Allow setting image quality in Python The setting is ignored where it does not make sense. * round quality in JPEG saver * improve error message in CMake * add jxl support to imglab * add Davis's suggestion Co-authored-by: Davis E. King <davis685@gmail.com> * Apply suggestions from code review Co-authored-by: Davis E. King <davis685@gmail.com> * make sure grayscale is 8 bit * update abstract: JPEG XL can store grayscale images * add more methods to query basic info from JXL * documentation formatting * Apply Davis' suggestions --------- Co-authored-by: Davis E. King <davis685@gmail.com>
- Loading branch information
Showing
20 changed files
with
1,156 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#============================================================================= | ||
# Find JPEG XL library | ||
#============================================================================= | ||
# Find the native JPEG XL headers and libraries. | ||
# | ||
# JXL_INCLUDE_DIRS - where to find jxl/decode_cxx.h, etc. | ||
# JXL_LIBRARIES - List of libraries when using jxl. | ||
# JXL_FOUND - True if jxl is found. | ||
#============================================================================= | ||
|
||
# Look for the header file. | ||
|
||
message(STATUS "Searching for JPEG XL") | ||
find_package(PkgConfig) | ||
if (PkgConfig_FOUND) | ||
pkg_check_modules(JXL IMPORTED_TARGET libjxl libjxl_cms libjxl_threads) | ||
if (JXL_FOUND) | ||
message(STATUS "Found libjxl via pkg-config in `${JXL_LIBRARY_DIRS}`") | ||
else() | ||
message(" *****************************************************************************") | ||
message(" *** No JPEG XL libraries found. ***") | ||
message(" *** On Ubuntu 23.04 and newer you can install them by executing ***") | ||
message(" *** sudo apt install libjxl-dev ***") | ||
message(" *** ***") | ||
message(" *** Otherwise, you can find precompiled packages here: ***") | ||
message(" *** https://github.com/libjxl/libjxl/releases ***") | ||
message(" *****************************************************************************") | ||
endif() | ||
else() | ||
message(STATUS "PkgConfig could not be found, JPEG XL support won't be available") | ||
set(JXL_FOUND 0) | ||
endif() | ||
|
||
if(JXL_FOUND) | ||
set(JXL_TEST_CMAKE_FLAGS | ||
"-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" | ||
"-DCMAKE_INCLUDE_PATH=${CMAKE_INCLUDE_PATH}" | ||
"-DCMAKE_LIBRARY_PATH=${CMAKE_LIBRARY_PATH}") | ||
|
||
try_compile(test_for_libjxl_worked | ||
${PROJECT_BINARY_DIR}/test_for_libjxl_build | ||
${CMAKE_CURRENT_LIST_DIR}/test_for_libjxl | ||
test_if_libjxl_is_broken | ||
CMAKE_FLAGS "${JXL_TEST_CMAKE_FLAGS}") | ||
|
||
if(NOT test_for_libjxl_worked) | ||
set(JXL_FOUND 0) | ||
message (STATUS "System copy of libjxl is either too old or broken. Will disable JPEG XL support.") | ||
endif() | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
|
||
cmake_minimum_required(VERSION 3.8.0) | ||
project(test_if_libjxl_is_broken) | ||
|
||
include_directories(${JXL_INCLUDE_DIR}) | ||
add_executable(libjxl_test libjxl_test.cpp) | ||
target_link_libraries(libjxl_test ${JXL_LIBRARY}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// Copyright (C) 2023 Davis E. King (davis@dlib.net), Adrià Arrufat | ||
// License: Boost Software License See LICENSE.txt for the full license. | ||
|
||
#include <jxl/encode_cxx.h> | ||
#include <jxl/decode_cxx.h> | ||
#include <jxl/resizable_parallel_runner_cxx.h> | ||
#include <iostream> | ||
#include <memory> | ||
|
||
// This code doesn't really make a lot of sense. It's just calling all the libjpeg functions to make | ||
// sure they can be compiled and linked. | ||
|
||
int main() | ||
{ | ||
std::cerr << "This program is just for build system testing. Don't actually run it." << std::endl; | ||
std::abort(); | ||
auto enc = JxlEncoderMake(nullptr); | ||
auto dec = JxlDecoderMake(nullptr); | ||
auto runner = JxlResizableParallelRunnerMake(nullptr); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
// Copyright (C) 2024 Davis E. King (davis@dlib.net), Adrià Arrufat | ||
// License: Boost Software License See LICENSE.txt for the full license. | ||
#ifndef DLIB_JXL_LOADER_CPp_ | ||
#define DLIB_JXL_LOADER_CPp_ | ||
|
||
// only do anything with this file if DLIB_JXL_SUPPORT is defined | ||
#ifdef DLIB_JXL_SUPPORT | ||
#include "jxl_loader.h" | ||
#include <jxl/decode_cxx.h> | ||
#include <jxl/resizable_parallel_runner_cxx.h> | ||
#include <fstream> | ||
|
||
namespace dlib | ||
{ | ||
|
||
static std::vector<unsigned char> load_contents(const std::string& filename) | ||
{ | ||
std::ifstream stream(filename, std::ios::binary); | ||
stream.exceptions(std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit); | ||
std::vector<unsigned char> buffer; | ||
vectorstream temp(buffer); | ||
temp << stream.rdbuf(); | ||
return buffer; | ||
} | ||
|
||
// ---------------------------------------------------------------------------------------- | ||
|
||
jxl_loader:: | ||
jxl_loader(const char* filename) : height(0), width(0) | ||
{ | ||
data = load_contents(filename); | ||
get_info(); | ||
} | ||
|
||
// ---------------------------------------------------------------------------------------- | ||
|
||
jxl_loader:: | ||
jxl_loader(const std::string& filename) : height(0), width(0) | ||
{ | ||
data = load_contents(filename); | ||
get_info(); | ||
} | ||
|
||
// ---------------------------------------------------------------------------------------- | ||
|
||
jxl_loader:: | ||
jxl_loader(const dlib::file& f) : height(0), width(0) | ||
{ | ||
data = load_contents(f.full_name()); | ||
get_info(); | ||
} | ||
|
||
// ---------------------------------------------------------------------------------------- | ||
|
||
jxl_loader:: | ||
jxl_loader(const unsigned char* imgbuffer, size_t imgbuffersize) : height(0), width(0) | ||
{ | ||
data.resize(imgbuffersize); | ||
memcpy(data.data(), imgbuffer, imgbuffersize); | ||
get_info(); | ||
} | ||
|
||
// ---------------------------------------------------------------------------------------- | ||
|
||
bool jxl_loader::is_gray() const { return depth == 1; } | ||
bool jxl_loader::is_graya() const { return depth == 2; }; | ||
bool jxl_loader::is_rgb() const { return depth == 3; } | ||
bool jxl_loader::is_rgba() const { return depth == 4; } | ||
unsigned int jxl_loader::bit_depth() const { return bits_per_sample; }; | ||
long jxl_loader::nr() const { return static_cast<long>(height); }; | ||
long jxl_loader::nc() const { return static_cast<long>(width); }; | ||
|
||
// ---------------------------------------------------------------------------------------- | ||
|
||
void jxl_loader::get_info() | ||
{ | ||
JxlSignature signature = JxlSignatureCheck(data.data(), data.size()); | ||
if (signature != JXL_SIG_CODESTREAM && signature != JXL_SIG_CONTAINER) | ||
{ | ||
throw image_load_error("jxl_loader: JxlSignatureCheck failed"); | ||
} | ||
|
||
auto dec = JxlDecoderMake(nullptr); | ||
if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_BASIC_INFO)) | ||
{ | ||
throw image_load_error("jxl_loader: JxlDecoderSubscribeEvents failed"); | ||
} | ||
|
||
JxlDecoderSetInput(dec.get(), data.data(), data.size()); | ||
JxlDecoderCloseInput(dec.get()); | ||
if (JXL_DEC_BASIC_INFO != JxlDecoderProcessInput(dec.get())) { | ||
throw image_load_error("jxl_loader: JxlDecoderProcessInput failed"); | ||
} | ||
|
||
JxlBasicInfo basic_info; | ||
if (JXL_DEC_SUCCESS != JxlDecoderGetBasicInfo(dec.get(), &basic_info)) | ||
{ | ||
throw image_load_error("jxl_loader: JxlDecoderGetBasicInfo failed"); | ||
} | ||
width = basic_info.xsize; | ||
height = basic_info.ysize; | ||
depth = basic_info.num_color_channels + basic_info.num_extra_channels; | ||
bits_per_sample = basic_info.bits_per_sample; | ||
} | ||
// ---------------------------------------------------------------------------------------- | ||
|
||
void jxl_loader::decode(unsigned char* out, const size_t out_size) const | ||
{ | ||
auto runner = JxlResizableParallelRunnerMake(nullptr); | ||
auto dec = JxlDecoderMake(nullptr); | ||
if (JXL_DEC_SUCCESS != JxlDecoderSubscribeEvents(dec.get(), JXL_DEC_FULL_IMAGE)) | ||
{ | ||
throw image_load_error("jxl_loader: JxlDecoderSubscribeEvents failed"); | ||
} | ||
|
||
if (JXL_DEC_SUCCESS != JxlDecoderSetParallelRunner(dec.get(), JxlResizableParallelRunner, runner.get())) | ||
{ | ||
throw image_load_error("jxl_loader: JxlDecoderSetParallelRunner failed"); | ||
} | ||
|
||
if (JXL_DEC_SUCCESS != JxlDecoderSetInput(dec.get(), data.data(), data.size())) | ||
{ | ||
throw image_load_error("jxl_loader: JxlDecoderSetInput failed"); | ||
} | ||
JxlDecoderCloseInput(dec.get()); | ||
|
||
JxlPixelFormat format = { | ||
.num_channels = depth, | ||
.data_type = JXL_TYPE_UINT8, | ||
.endianness = JXL_NATIVE_ENDIAN, | ||
.align=0 | ||
}; | ||
for (;;) | ||
{ | ||
JxlDecoderStatus status = JxlDecoderProcessInput(dec.get()); | ||
if (status == JXL_DEC_ERROR) | ||
{ | ||
throw image_load_error("jxl_loader: JxlDecoderProcessInput failed"); | ||
} | ||
else if (status == JXL_DEC_NEED_MORE_INPUT) | ||
{ | ||
throw image_load_error("jxl_loader: Error, expected more input"); | ||
} | ||
else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) | ||
{ | ||
JxlResizableParallelRunnerSetThreads(runner.get(), JxlResizableParallelRunnerSuggestThreads(width, height)); | ||
size_t buffer_size; | ||
if (JXL_DEC_SUCCESS != JxlDecoderImageOutBufferSize(dec.get(), &format, &buffer_size)) | ||
{ | ||
throw image_load_error("jxl_loader: JxlDecoderImageOutBufferSize failed"); | ||
} | ||
if (buffer_size != width * height * depth) | ||
{ | ||
throw image_load_error("jxl_loader: invalid output buffer size"); | ||
} | ||
if (JXL_DEC_SUCCESS != JxlDecoderSetImageOutBuffer(dec.get(), &format, out, out_size)) | ||
{ | ||
throw image_load_error("jxl_loader: JxlDecoderSetImageOutBuffer failed"); | ||
} | ||
} | ||
else if (status == JXL_DEC_FULL_IMAGE) | ||
{ | ||
// If the image is an animation, more full frames may be decoded. | ||
// This loader only decodes the first one. | ||
return; | ||
} | ||
else if (status == JXL_DEC_SUCCESS) | ||
{ | ||
return; | ||
} | ||
else | ||
{ | ||
throw image_load_error("jxl_loder: Unknown decoder status"); | ||
} | ||
} | ||
} | ||
} | ||
|
||
#endif // DLIB_JXL_SUPPORT | ||
#endif // DLIB_JXL_LOADER_CPp_ |
Oops, something went wrong.