From 44a3bd719c739cd9f05e89418c1bb4cba06eb703 Mon Sep 17 00:00:00 2001 From: Cacodemon345 Date: Wed, 23 Aug 2023 01:34:19 +0600 Subject: [PATCH] JPEG XL implementation --- CMakeLists.txt | 16 +- src/CMakeLists.txt | 11 + src/common/textures/formats/jpegxltexture.cpp | 221 ++++++++++++++++++ src/common/textures/image.cpp | 6 + vcpkg.json | 11 + 5 files changed, 264 insertions(+), 1 deletion(-) create mode 100644 src/common/textures/formats/jpegxltexture.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d59560964e..aa5dd27005 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,10 @@ if (OPENAL_SOFT_VCPKG) list(APPEND VCPKG_MANIFEST_FEATURES "vcpkg-openal-soft") endif() +if (NOT DEFINED JPEG_XL OR JPEG_XL) + list(APPEND VCPKG_MANIFEST_FEATURES "vcpkg-libjxl") +endif() + if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR (NOT CMAKE_SYSTEM_NAME AND CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")) # Force static triplet on Windows set(VCPKG_TARGET_TRIPLET "x64-windows-static") @@ -43,6 +47,10 @@ if (NOT VCPKG_TOOLCHAIN) set(VCPKG_MANIFEST_FEATURES) endif() +option(JPEG_XL "Enable JPEG XL support" ON) + +include ( FindPkgConfig ) + set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -219,7 +227,9 @@ find_package( VPX ) find_package( ZLIB ) find_package( WebP ) if (NOT WebP_FOUND) - include(FindPkgConfig) + if (NOT PKG_CONFIG_FOUND) + message(SEND_ERROR "pkgconfig not found") + endif() pkg_check_modules(libwebp IMPORTED_TARGET libwebp) if (NOT TARGET PkgConfig::libwebp) message(SEND_ERROR "libwebp not found") @@ -232,6 +242,10 @@ if (NOT WebP_FOUND) add_library(WebP::libwebpmux ALIAS PkgConfig::libwebpmux) endif() +if (PKG_CONFIG_FOUND AND JPEG_XL) + pkg_check_modules( libjxl IMPORTED_TARGET libjxl ) +endif() + include( TargetArch ) target_architecture(TARGET_ARCHITECTURE) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 35587dfa87..3d174d7b25 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -320,6 +320,11 @@ add_custom_target( revision_check ALL # required libraries set( PROJECT_LIBRARIES ${PROJECT_LIBRARIES} "${ZLIB_LIBRARIES}" "${JPEG_LIBRARIES}" "${BZIP2_LIBRARIES}" "${CMAKE_DL_LIBS}" "${DRPC_LIBRARIES}") + +if (TARGET PkgConfig::libjxl) + list( APPEND PROJECT_LIBRARIES PkgConfig::libjxl ) +endif() + if (HAVE_VULKAN) list( APPEND PROJECT_LIBRARIES "zvulkan" ) endif() @@ -1240,6 +1245,12 @@ set_source_files_properties( common/engine/sc_man.cpp PROPERTIES OBJECT_DEPENDS set_source_files_properties( ${NOT_COMPILED_SOURCE_FILES} PROPERTIES HEADER_FILE_ONLY TRUE ) set_source_files_properties( ${ZDOOM_NONPCH_SOURCES} common/textures/hires/hqresize.cpp PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE ) +if (TARGET PkgConfig::libjxl) + target_compile_definitions(zdoom PRIVATE -DHAVE_JPEGXL=1) + target_sources(zdoom PRIVATE common/textures/formats/jpegxltexture.cpp) +else() + message( STATUS "Compiling without JPEG XL support" ) +endif() if(${CMAKE_SYSTEM_NAME} STREQUAL "SunOS") # [BL] Solaris requires these to be explicitly linked. diff --git a/src/common/textures/formats/jpegxltexture.cpp b/src/common/textures/formats/jpegxltexture.cpp new file mode 100644 index 0000000000..914922743f --- /dev/null +++ b/src/common/textures/formats/jpegxltexture.cpp @@ -0,0 +1,221 @@ +/* +** jpegxltexture.cpp +** Texture class for JPEG XL images +** +**--------------------------------------------------------------------------- +** Copyright 2023 Cacodemon345 +** Copyright 2022 Dominic Szablewski +** All rights reserved. +** +** Permission is hereby granted, free of charge, to any person obtaining a copy +** of this software and associated documentation files (the "Software"), to deal +** in the Software without restriction, including without limitation the rights +** to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +** copies of the Software, and to permit persons to whom the Software is +** furnished to do so, subject to the following conditions: +** +** The above copyright notice and this permission notice shall be included in all +** copies or substantial portions of the Software. +** +** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +** IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +** AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +** OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +** SOFTWARE. +**--------------------------------------------------------------------------- +** +** +*/ + +#include + +#include "files.h" +#include "filesystem.h" +#include "bitmap.h" +#include "imagehelpers.h" +#include "image.h" + +class FJPEGXLTexture : public FImageSource +{ + +public: + FJPEGXLTexture (int lumpnum, int w, int h, int xoffset, int yoffset, bool transparent = true); + PalettedPixels CreatePalettedPixels(int conversion) override; + int CopyPixels(FBitmap *bmp, int conversion) override; +}; + +FImageSource *JPEGXLImage_TryCreate(FileReader & file, int lumpnum) +{ + JxlBasicInfo info; + file.Seek(0, FileReader::SeekSet); + auto array = file.Read(); + int width = 0, height = 0, xoffset = 0, yoffset = 0; + // TODO: Revisit this again when the box decoding API is well understood. + (void)xoffset; + (void)yoffset; + + auto jxlDecoder = JxlDecoderMake(nullptr); + if (!jxlDecoder) + { + return nullptr; + } + + auto jxlDecoderPtr = jxlDecoder.get(); + if (JxlDecoderSubscribeEvents(jxlDecoderPtr, JXL_DEC_BASIC_INFO) != JXL_DEC_SUCCESS) + { + return nullptr; + } + JxlDecoderSetInput(jxlDecoderPtr, array.Data(), array.Size()); + JxlDecoderCloseInput(jxlDecoderPtr); + + for (;;) + { + JxlDecoderStatus status = JxlDecoderProcessInput(jxlDecoderPtr); + + switch (status) + { + case JXL_DEC_ERROR: + return nullptr; + + case JXL_DEC_SUCCESS: + case JXL_DEC_BASIC_INFO: + { + if (JxlDecoderGetBasicInfo(jxlDecoderPtr, &info) != JXL_DEC_SUCCESS) + return nullptr; + + width = info.xsize; + height = info.ysize; + if (width && height) + return new FJPEGXLTexture(lumpnum, width, height, xoffset, yoffset, !!info.alpha_bits); + else + return nullptr; + break; + } + } + } + + return nullptr; +} + +FJPEGXLTexture::FJPEGXLTexture (int lumpnum, int w, int h, int xoffset, int yoffset, bool transparent) + : FImageSource(lumpnum) +{ + Width = w; + Height = h; + LeftOffset = xoffset; + TopOffset = yoffset; + if (!transparent) + bMasked = bTranslucent = false; +} + +PalettedPixels FJPEGXLTexture::CreatePalettedPixels(int conversion) +{ + FBitmap bitmap; + bitmap.Create(Width, Height); + CopyPixels(&bitmap, conversion); + const uint8_t *data = bitmap.GetPixels(); + + uint8_t *dest_p; + int dest_adv = Height; + int dest_rew = Width * Height - 1; + + PalettedPixels Pixels(Width * Height); + dest_p = Pixels.Data(); + + bool doalpha = conversion == luminance; + // Convert the source image from row-major to column-major format and remap it + for (int y = Height; y != 0; --y) + { + for (int x = Width; x != 0; --x) + { + int b = *data++; + int g = *data++; + int r = *data++; + int a = *data++; + if (a < 128) + *dest_p = 0; + else + *dest_p = ImageHelpers::RGBToPalette(doalpha, r, g, b); + dest_p += dest_adv; + } + dest_p -= dest_rew; + } + return Pixels; +} + +int FJPEGXLTexture::CopyPixels(FBitmap *bmp, int conversion) +{ + JxlBasicInfo info; + int width = 0, height = 0; + uint8_t* pixels = nullptr; + JxlPixelFormat format = {4, JXL_TYPE_UINT8, JXL_NATIVE_ENDIAN, 0}; + auto jxlDecoder = JxlDecoderMake(nullptr); + if (!jxlDecoder) + { + return 0; + } + + auto jxlDecoderPtr = jxlDecoder.get(); + if (JxlDecoderSubscribeEvents(jxlDecoderPtr, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE) != JXL_DEC_SUCCESS) + { + return 0; + } + + auto lump = fileSystem.OpenFileReader(SourceLump); + auto array = lump.Read(); + JxlDecoderSetInput(jxlDecoderPtr, array.Data(), array.Size()); + JxlDecoderCloseInput(jxlDecoderPtr); + + for (;;) + { + JxlDecoderStatus status = JxlDecoderProcessInput(jxlDecoderPtr); + + switch (status) + { + case JXL_DEC_ERROR: + return 0; + + case JXL_DEC_BASIC_INFO: + { + if (JxlDecoderGetBasicInfo(jxlDecoderPtr, &info) != JXL_DEC_SUCCESS) + return 0; + + width = info.xsize; + height = info.ysize; + if (width != Width && height != Height) + return 0; + break; + } + + case JXL_DEC_NEED_IMAGE_OUT_BUFFER: + { + size_t bufferSize = 0; + if (JxlDecoderImageOutBufferSize(jxlDecoderPtr, &format, &bufferSize) != JXL_DEC_SUCCESS) + return 0; + + if (bufferSize != (Width * Height * 4)) + return 0; + + pixels = new uint8_t[Width * Height * 4]; + + if (JxlDecoderSetImageOutBuffer(jxlDecoderPtr, &format, pixels, bufferSize) != JXL_DEC_SUCCESS) + return 0; + break; + } + + case JXL_DEC_FULL_IMAGE: + { + break; + } + + case JXL_DEC_SUCCESS: + { + bmp->CopyPixelDataRGB(0, 0, pixels, Width, Height, 4, Width * 4, 0, CF_RGBA); + + return bMasked ? -1 : 0; + } + } + } +} \ No newline at end of file diff --git a/src/common/textures/image.cpp b/src/common/textures/image.cpp index dcd1f28d3a..bd6b9d2852 100644 --- a/src/common/textures/image.cpp +++ b/src/common/textures/image.cpp @@ -324,6 +324,9 @@ FImageSource *TGAImage_TryCreate(FileReader &, int lumpnum); FImageSource *StbImage_TryCreate(FileReader &, int lumpnum); FImageSource *QOIImage_TryCreate(FileReader &, int lumpnum); FImageSource *WebPImage_TryCreate(FileReader &, int lumpnum); +#ifdef HAVE_JPEGXL +FImageSource *JPEGXLImage_TryCreate(FileReader &, int lumpnum); +#endif FImageSource *AnmImage_TryCreate(FileReader &, int lumpnum); FImageSource *RawPageImage_TryCreate(FileReader &, int lumpnum); FImageSource *FlatImage_TryCreate(FileReader &, int lumpnum); @@ -346,6 +349,9 @@ FImageSource * FImageSource::GetImage(int lumpnum, bool isflat) { StbImage_TryCreate, false }, { QOIImage_TryCreate, false }, { WebPImage_TryCreate, false }, +#ifdef HAVE_JPEGXL + { JPEGXLImage_TryCreate, false }, +#endif { TGAImage_TryCreate, false }, { AnmImage_TryCreate, false }, { StartupPageImage_TryCreate, false }, diff --git a/vcpkg.json b/vcpkg.json index d89c12d124..998fceab7c 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -24,6 +24,17 @@ "platform": "!windows | (windows & static & staticcrt)" } ] + }, + "vcpkg-libjxl": + { + "description": "Use libjxl provided by vcpkg.", + "dependencies": [ + { + "name": "libjxl", + "default-features": false, + "platform": "!windows | (windows & static & staticcrt)" + } + ] } }, "dependencies": [