diff options
-rw-r--r-- | CHANGELOG.md | 4 | ||||
-rw-r--r-- | README.md | 12 | ||||
-rw-r--r-- | jxl.c | 150 | ||||
-rw-r--r-- | jxl.h | 6 | ||||
-rw-r--r-- | main.c | 7 | ||||
-rw-r--r-- | meson.build | 18 | ||||
-rw-r--r-- | meson_options.txt | 1 |
7 files changed, 192 insertions, 6 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 30dce6b..b91242c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ ### Added * Nanosvg updated to 93ce879dc4c04a3ef1758428ec80083c38610b1f +* JPEG XL support ([#14][14]) ### Changed @@ -20,11 +21,14 @@ * Compilation error on musl libc ([#11][11]). [11]: https://codeberg.org/dnkl/wbg/issues/11 +[14]: https://codeberg.org/dnkl/wbg/pulls/14 ### Security ### Contributors +* Leonardo Hernández Hernández + ## 1.2.0 @@ -21,9 +21,13 @@ wallpaper _may_ be added in the future. * libpng (optional) * libjpeg (optional) * libwebp (optional) +* libjxl (optional) +* libjxl_threads (optional) -Note that if SVG support is disabled at least one of _libpng_, _libjpeg_ and -_libwebp_ is required. +Note that if SVG support is disabled at least one of _libpng_, _libjpeg_, +_libwebp_ and _libjxl_ is required. + +_libjxl\_threads_ is recommended for better performance decoding JPEG XL images ### Compile time @@ -41,10 +45,10 @@ ninja -C build sudo ninja -C build install ``` -By default, PNG, JPEG and WebP support is auto-detected. You can force +By default, PNG, JPEG, JPEG XL and WebP support is auto-detected. You can force disable/enable them with the meson command line options `-Dpng=disabled|enabled`, `-Djpeg=disabled|enabled` and -`-Dwebp=disabled|enabled`. +`-Dwebp=disabled|enabled` `-Djxl=disabled|enabled`. SVG support is enabled by default (as it does not require additional dependencies). You can disable it with the meson command line option `-Dsvg=false` @@ -0,0 +1,150 @@ +#include "jxl.h" +#include <stdlib.h> +#include <stdio.h> + +#include <jxl/decode.h> +#include <jxl/codestream_header.h> +#if defined(WBG_HAVE_JXL_THREADS) + #include <jxl/resizable_parallel_runner.h> +#endif + +#define LOG_MODULE "jxl" +#define LOG_ENABLE_DBG 0 +#include "log.h" +#include "stride.h" + +pixman_image_t * +jxl_load(FILE *fp, const char *path) +{ + pixman_image_t *pix = NULL; + pixman_format_code_t format = PIXMAN_x8b8g8r8; + bool ok = false; + uint8_t *file_data = NULL; + uint8_t *image = NULL; + size_t file_size, image_size; + int width, height, stride; + +#if defined(WBG_HAVE_JXL_THREADS) + JxlParallelRunner *runner = NULL; +#endif + JxlDecoder *decoder = NULL; + JxlBasicInfo info = {0}; + JxlPixelFormat jxl_format = { + .num_channels = 4, + .data_type = JXL_TYPE_UINT8, + .endianness = JXL_LITTLE_ENDIAN, + .align = 0 + }; + + if (fseek(fp, 0, SEEK_END) < 0) { + LOG_ERRNO("%s: failed to seek to end of file", path); + return NULL; + } + file_size = ftell(fp); + if (fseek(fp, 0, SEEK_SET) < 0) { + LOG_ERRNO("%s: failed to seek to beginning of file", path); + return NULL; + } + + if (!(file_data = malloc(file_size))) + goto err; + clearerr(fp); + if (fread(file_data, sizeof(*file_data), file_size, fp) != file_size + && ferror(fp)) { + LOG_ERRNO("%s: failed to read", path); + goto err; + } + + if (JxlSignatureCheck(file_data, file_size) == JXL_SIG_INVALID) { + LOG_DBG("%s: not a jpegxl image", path); + goto err; + } + + if (!(decoder = JxlDecoderCreate(NULL))) + goto err; +#if defined(WBG_HAVE_JXL_THREADS) + if (!(runner = JxlResizableParallelRunnerCreate(NULL))) + goto err; + JxlDecoderSetParallelRunner(decoder, JxlResizableParallelRunner, runner); +#endif + + /* pixman expects premultiplied alpha */ + JxlDecoderSetUnpremultiplyAlpha(decoder, JXL_FALSE); + + JxlDecoderSubscribeEvents(decoder, JXL_DEC_BASIC_INFO | JXL_DEC_FULL_IMAGE); + + JxlDecoderSetInput(decoder, file_data, file_size); + JxlDecoderCloseInput(decoder); + + while (1) { + JxlDecoderStatus status = JxlDecoderProcessInput(decoder); + + if (status == JXL_DEC_ERROR) { + LOG_ERR("%s: decoder error", path); + goto err; + } else if (status == JXL_DEC_NEED_MORE_INPUT) { + LOG_ERR("%s: decoder requires more input but already provided all input", path); + goto err; + } else if (status == JXL_DEC_BASIC_INFO) { + if (JxlDecoderGetBasicInfo(decoder, &info) + != JXL_DEC_SUCCESS) { + LOG_ERR("%s: failed to get basic info", path); + } + + width = info.xsize; + height = info.ysize; + stride = stride_for_format_and_width(format, width); + image_size = height * stride; + + LOG_DBG("%s: %dx%d@%hhubpp, %d channels, %d alpha bits", path, width, height, + info.bits_per_sample, info.num_color_channels, info.alpha_bits); + + if (!(image = malloc(image_size))) + goto err; + +#if defined(WBG_HAVE_JXL_THREADS) + JxlResizableParallelRunnerSetThreads(runner, + JxlResizableParallelRunnerSuggestThreads(width, height)); +#endif + } else if (status == JXL_DEC_NEED_IMAGE_OUT_BUFFER) { + size_t min_size; + if (JxlDecoderImageOutBufferSize(decoder, &jxl_format, &min_size) + != JXL_DEC_SUCCESS) { + LOG_ERR("%s: failed to get the minimum size of the output buffer", path); + goto err; + } + + if (min_size > image_size) { + LOG_ERR("minimum size [%zu] is greater than the expected size [%zu]", + min_size, image_size); + goto err; + } else if (min_size < image_size) { + LOG_WARN("minimum size [%zu] is less than the expected size [%zu]", + min_size, image_size); + } + + if (JxlDecoderSetImageOutBuffer(decoder, &jxl_format, image, + image_size) != JXL_DEC_SUCCESS) { + LOG_ERR("%s: failed to set output buffer", path); + goto err; + } + } else { + break; + } + } + + pix = pixman_image_create_bits_no_clear(format, width, height, + (uint32_t *)image, stride); + ok = pix != NULL; + +err: + if (!ok) + free(image); + free(file_data); +#if defined(WBG_HAVE_JXL_THREADS) + JxlResizableParallelRunnerDestroy(runner); +#endif + JxlDecoderDestroy(decoder); + + return pix; +} @@ -0,0 +1,6 @@ +#pragma once + +#include <stdio.h> +#include <pixman.h> + +pixman_image_t *jxl_load(FILE *fp, const char *path); @@ -36,6 +36,9 @@ #if defined(WBG_HAVE_SVG) #include "svg.h" #endif +#if defined(WBG_HAVE_JXL) + #include "jxl.h" +#endif /* Top-level globals */ static struct wl_display *display; @@ -419,6 +422,10 @@ main(int argc, const char *const *argv) if (image == NULL) image = svg_load(fp, image_path); #endif +#if defined(WBG_HAVE_JXL) + if (image == NULL) + image = jxl_load(fp, image_path); +#endif if (image == NULL) { LOG_ERR("%s: failed to load", image_path); fclose(fp); diff --git a/meson.build b/meson.build index eaac74a..ea310ca 100644 --- a/meson.build +++ b/meson.build @@ -51,6 +51,8 @@ pixman = dependency('pixman-1') png = dependency('libpng', required: get_option('png')) jpg = dependency('libjpeg', required: get_option('jpeg')) webp = dependency('libwebp', required: get_option('webp')) +jxl = dependency('libjxl', required: get_option('jxl')) +jxl_threads = dependency('libjxl_threads', required: false) svg = declare_dependency( sources: ['nanosvg.c', '3rd-party/nanosvg/src/nanosvg.h', 'nanosvgrast.c', '3rd-party/nanosvg/src/nanosvgrast.h'], @@ -59,7 +61,7 @@ svg = declare_dependency( ) have_svg = get_option('svg') -if not png.found() and not jpg.found() and not webp.found() and not have_svg +if not png.found() and not jpg.found() and not jxl.found() and not webp.found() and not have_svg error('you must enable at least one image format') endif @@ -75,6 +77,14 @@ endif if have_svg add_project_arguments('-DWBG_HAVE_SVG=1', language:'c') endif +if jxl.found() + add_project_arguments('-DWBG_HAVE_JXL=1', language:'c') + if jxl_threads.found() + add_project_arguments('-DWBG_HAVE_JXL_THREADS=1', language:'c') + endif +else + jxl_threads = dependency('', required: false) +endif wayland_protocols = dependency('wayland-protocols') wayland_client = dependency('wayland-client') @@ -121,6 +131,9 @@ endif if jpg.found() image_format_sources += ['jpg.c', 'jpg.h'] endif +if jxl.found() + image_format_sources += ['jxl.c', 'jxl.h'] +endif if webp.found() image_format_sources += ['webp.c', 'webp.h'] endif @@ -136,13 +149,14 @@ executable( 'stride.h', image_format_sources, wl_proto_src + wl_proto_headers, version, - dependencies: [pixman, png, jpg, webp, svg, wayland_client, tllist], + dependencies: [pixman, png, jpg, jxl, jxl_threads, webp, svg, wayland_client, tllist], install: true) summary( { 'PNG support': png.found(), 'JPEG support': jpg.found(), + 'JPEG XL support': jxl.found(), 'WebP support': webp.found(), 'SVG support': have_svg }, diff --git a/meson_options.txt b/meson_options.txt index adc2212..b9aae7f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,4 +1,5 @@ option('png', type: 'feature') option('jpeg', type: 'feature') +option('jxl', type: 'feature') option('webp', type: 'feature') option('svg', type: 'boolean') |