Skip to content

Initial JPEG XL decoder #755

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

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
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 .ci/Brewfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,7 @@ brew 'cmake'
brew 'git'
brew 'libomp'
brew 'jpeg-turbo'
brew 'jpeg-xl'
brew 'ninja'
brew 'pkgconf'
brew 'pugixml'
2 changes: 1 addition & 1 deletion .ci/oss-fuzz.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ cd build
cmake \
-DBINARY_PACKAGE_BUILD=ON -DWITH_OPENMP=$WITH_OPENMP \
-DUSE_BUNDLED_LLVMOPENMP=ON -DALLOW_DOWNLOADING_LLVMOPENMP=ON \
-DWITH_PUGIXML=OFF -DUSE_XMLLINT=OFF -DWITH_JPEG=OFF -DWITH_ZLIB=OFF \
-DWITH_PUGIXML=OFF -DUSE_XMLLINT=OFF -DWITH_JPEG=OFF -DWITH_JXL=OFF -DWITH_ZLIB=OFF \
-DBUILD_TESTING=OFF -DBUILD_TOOLS=OFF -DBUILD_BENCHMARKING=OFF \
-DCMAKE_BUILD_TYPE=FUZZ -DBUILD_FUZZERS=ON \
-DLIB_FUZZING_ENGINE:STRING="$LIB_FUZZING_ENGINE" \
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/CI-linux.yml
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,11 @@ jobs:
googletest \
binutils-gold \
libjpeg-dev \
libjxl-dev \
libpugixml-dev \
libxml2-utils \
ninja-build \
pkgconf \
zlib1g-dev
if [ "$COMPILER_FAMILY" = "GNU" ]; then
eatmydata apt install g++-${{ inputs.compiler-version }} \
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/CI-windows-msys2.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ jobs:
libxml2:p
pugixml:p
libjpeg-turbo:p
libjxl:p
zlib:p
- name: Install Additional Dependencies (Coverage)
if: inputs.flavor == 'Coverage'
Expand Down
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ else()
set(ALLOW_DOWNLOADING_PUGIXML OFF CACHE BOOL "If pugixml src tree is not found in location specified by PUGIXML_PATH, do fetch the archive from internet" FORCE)
endif()
option(WITH_JPEG "Enable JPEG support for DNG Lossy JPEG support" ON)
option(WITH_JXL "Enable JPEG XL support for DNG JPEG XL support" ON)
option(WITH_ZLIB "Enable ZLIB support for DNG deflate support" ON)
if(WITH_ZLIB)
option(USE_BUNDLED_ZLIB "Build and use zlib in-tree" OFF)
Expand Down
33 changes: 33 additions & 0 deletions cmake/Modules/FindJXL.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Find libjxl
# Will define:
# - JXL_FOUND
# - JXL_INCLUDE_DIRS directory to include for libjxl headers
# - JXL_LIBRARIES libraries to link to
# - JXL_VERSION

find_package(PkgConfig QUIET REQUIRED)
pkg_check_modules(JXL_PKGCONF QUIET libjxl)

if(JXL_PKGCONF_VERSION)
set(JXL_VERSION ${JXL_PKGCONF_VERSION})
endif()

find_path(JXL_INCLUDE_DIR
NAMES jxl/decode.h
HINTS ${JXL_PKGCONF_INCLUDE_DIRS})
mark_as_advanced(JXL_INCLUDE_DIR)

find_library(JXL_LIBRARY
NAMES jxl
HINTS ${JXL_PKGCONF_LIBRARY_DIRS})
mark_as_advanced(JXL_LIBRARY)

include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(JXL
REQUIRED_VARS JXL_LIBRARY JXL_INCLUDE_DIR
VERSION_VAR JXL_VERSION)

if(JXL_FOUND)
set(JXL_INCLUDE_DIRS ${JXL_INCLUDE_DIR})
set(JXL_LIBRARIES ${JXL_LIBRARY})
endif()
27 changes: 27 additions & 0 deletions cmake/src-dependencies.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,33 @@ else()
endif()
add_feature_info("Lossy JPEG decoding" HAVE_JPEG "used for DNG Lossy JPEG compression decoding")

unset(HAVE_JXL)
if(WITH_JXL)
message(STATUS "Looking for JPEG XL")
find_package(JXL)
if(NOT JXL_FOUND)
message(SEND_ERROR "Did not find JPEG XL! Either make it find JPEG XL, or pass -DWITH_JXL=OFF to disable JPEG XL.")
else()
message(STATUS "Looking for JPEG XL - found")
set(HAVE_JXL 1)

if(NOT TARGET JXL::jxl)
add_library(JXL::jxl INTERFACE IMPORTED)
set_property(TARGET JXL::jxl PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${JXL_INCLUDE_DIRS}")
set_property(TARGET JXL::jxl PROPERTY INTERFACE_LINK_LIBRARIES "${JXL_LIBRARIES}")
endif()

target_link_libraries(rawspeed PRIVATE JXL::jxl)
set_package_properties(JXL PROPERTIES
TYPE RECOMMENDED
DESCRIPTION "library for handling the JPEG XL image data format, implements a JPEG XL codec"
PURPOSE "Used for decoding DNG JPEG XL compression")
endif()
else()
message(STATUS "JPEG XL is disabled, DNG JPEG XL support won't be available.")
endif()
add_feature_info("JPEG XL decoding" HAVE_JXL "used for DNG JPEG XL compression decoding")

unset(HAVE_ZLIB)
if (WITH_ZLIB)
message(STATUS "Looking for ZLIB")
Expand Down
2 changes: 2 additions & 0 deletions src/config.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ static_assert(RAWSPEED_LARGEPAGESIZE >= RAWSPEED_PAGESIZE,
#cmakedefine HAVE_JPEG
#cmakedefine HAVE_JPEG_MEM_SRC

#cmakedefine HAVE_JXL

#cmakedefine HAVE_CXX_THREAD_LOCAL
#cmakedefine HAVE_GCC_THREAD_LOCAL

Expand Down
27 changes: 24 additions & 3 deletions src/librawspeed/decoders/DngDecoder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
#include <vector>

using std::map;
using std::pair;
using std::vector;

namespace rawspeed {
Expand Down Expand Up @@ -119,6 +120,9 @@ void DngDecoder::dropUnsuportedChunks(std::vector<const TiffIFD*>* data) {
case 9: // VC-5 as used by GoPro
#ifdef HAVE_JPEG
case 0x884c: // lossy JPEG
#endif
#ifdef HAVE_JXL
case 0xcd42: // JPEG XL
#endif
// no change, if supported, then is still supported.
break;
Expand All @@ -140,6 +144,15 @@ void DngDecoder::dropUnsuportedChunks(std::vector<const TiffIFD*>* data) {
"chunk, but the jpeg support was "
"disabled at build!");
[[clang::fallthrough]];
#endif
#ifndef HAVE_JXL
case 0xcd42: // JPEG XL
#pragma message \
"JPEG XL is not present! JPEG XL compression will not be supported!"
writeLog(DEBUG_PRIO::WARNING, "DNG Decoder: found JPEG XL encoded "
"chunk, but the jpeg xl support was "
"disabled at build!");
[[clang::fallthrough]];
#endif
default:
supported = false;
Expand Down Expand Up @@ -362,16 +375,24 @@ void DngDecoder::decodeData(const TiffIFD* raw, uint32_t sample_format) const {
if (compression == 8 && sample_format != 3) {
ThrowRDE("Only float format is supported for "
"deflate-compressed data.");
} else if ((compression == 7 || compression == 0x884c) &&
} else if ((compression == 7 || compression == 0x884c ||
compression == 0xcd42) &&
sample_format != 1) {
ThrowRDE("Only 16 bit unsigned data supported for "
"JPEG-compressed data.");
"JPEG or JPEG XL compressed data.");
}

uint32_t predictor = ~0U;
if (raw->hasEntry(TiffTag::PREDICTOR))
predictor = raw->getEntry(TiffTag::PREDICTOR)->getU32();

pair<uint32_t, uint32_t> interleave{1U, 1U};
if (raw->hasEntry(TiffTag::ROWINTERLEAVEFACTOR))
interleave.first = raw->getEntry(TiffTag::ROWINTERLEAVEFACTOR)->getU32();
if (raw->hasEntry(TiffTag::COLUMNINTERLEAVEFACTOR))
interleave.second =
raw->getEntry(TiffTag::COLUMNINTERLEAVEFACTOR)->getU32();

if (mRaw->getDataType() == RawImageType::UINT16) {
// Default white level is (2 ** BitsPerSample) - 1
mRaw->whitePoint = implicit_cast<int>((1UL << *bps) - 1UL);
Expand Down Expand Up @@ -401,7 +422,7 @@ void DngDecoder::decodeData(const TiffIFD* raw, uint32_t sample_format) const {
}

AbstractDngDecompressor slices(mRaw, getTilingDescription(raw), compression,
mFixLjpeg, *bps, predictor);
mFixLjpeg, *bps, predictor, interleave);

slices.slices.reserve(slices.dsc.numTiles);

Expand Down
36 changes: 36 additions & 0 deletions src/librawspeed/decompressors/AbstractDngDecompressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
#include "decompressors/JpegDecompressor.h"
#endif

#ifdef HAVE_JXL
#include "decompressors/JpegXLDecompressor.h"
#endif

namespace rawspeed {

template <> void AbstractDngDecompressor::decompressThread<1>() const noexcept {
Expand Down Expand Up @@ -201,6 +205,30 @@ void AbstractDngDecompressor::decompressThread<0x884c>() const noexcept {
}
#endif

#ifdef HAVE_JXL
template <>
void AbstractDngDecompressor::decompressThread<0xcd42>() const noexcept {
#ifdef HAVE_OPENMP
#pragma omp for schedule(static)
#endif
for (const auto& e :
Array1DRef(slices.data(), implicit_cast<int>(slices.size()))) {
try {
JpegXLDecompressor j(e.bs.peekBuffer(e.bs.getRemainSize()), mRaw,
mInterleave);
j.decode(e.offX, e.offY);
} catch (const RawDecoderException& err) {
mRaw->setError(err.what());
} catch (const IOException& err) {
mRaw->setError(err.what());
} catch (...) {
// We should not get any other exception type here.
__builtin_unreachable();
}
}
}
#endif

void AbstractDngDecompressor::decompressThread() const noexcept {
invariant(mRaw->dim.x > 0);
invariant(mRaw->dim.y > 0);
Expand Down Expand Up @@ -232,6 +260,14 @@ void AbstractDngDecompressor::decompressThread() const noexcept {
#else
#pragma message "JPEG is not present! Lossy JPEG DNG will not be supported!"
mRaw->setError("jpeg support is disabled.");
#endif
} else if (compression == 0xcd42) {
/* Lossy DNG */
#ifdef HAVE_JXL
decompressThread<0xcd42>();
#else
#pragma message "JPEG XL is not present! JPEG XL DNG will not be supported!"
mRaw->setError("jpeg xl support is disabled.");
#endif
} else {
mRaw->setError("AbstractDngDecompressor: Unknown compression");
Expand Down
7 changes: 5 additions & 2 deletions src/librawspeed/decompressors/AbstractDngDecompressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,11 @@ class AbstractDngDecompressor final : public AbstractDecompressor {
public:
AbstractDngDecompressor(RawImage img, const DngTilingDescription& dsc_,
int compression_, bool mFixLjpeg_, uint32_t mBps_,
uint32_t mPredictor_)
uint32_t mPredictor_,
std::pair<uint32_t, uint32_t> mInterleave_)
: mRaw(std::move(img)), dsc(dsc_), compression(compression_),
mFixLjpeg(mFixLjpeg_), mBps(mBps_), mPredictor(mPredictor_) {}
mFixLjpeg(mFixLjpeg_), mBps(mBps_), mPredictor(mPredictor_),
mInterleave(std::move(mInterleave_)) {}

void decompress() const;

Expand All @@ -148,6 +150,7 @@ class AbstractDngDecompressor final : public AbstractDecompressor {
const bool mFixLjpeg = false;
const uint32_t mBps;
const uint32_t mPredictor;
const std::pair<uint32_t, uint32_t> mInterleave;
};

} // namespace rawspeed
6 changes: 6 additions & 0 deletions src/librawspeed/decompressors/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ FILE(GLOB SOURCES
"JpegDecompressor.cpp"
"JpegDecompressor.h"
"JpegMarkers.h"
"JpegXLDecompressor.cpp"
"JpegXLDecompressor.h"
"KodakDecompressor.cpp"
"KodakDecompressor.h"
"LJpegDecoder.cpp"
Expand Down Expand Up @@ -82,4 +84,8 @@ if(WITH_JPEG AND TARGET JPEG::JPEG)
target_link_libraries(rawspeed_decompressors PUBLIC JPEG::JPEG)
endif()

if(WITH_JXL AND TARGET JXL::jxl)
target_link_libraries(rawspeed_decompressors PUBLIC JXL::jxl)
endif()

target_link_libraries(rawspeed PRIVATE rawspeed_decompressors)
Loading
Loading