Skip to content

Conversation

@nickv2002
Copy link

@nickv2002 nickv2002 commented Jan 11, 2026

Add Qt Image Plugins for Modern Image Format Support (Docker)

Summary

This PR adds support for modern image formats (AVIF, JXL, HEIC/HEIF) to the YACReaderLibraryServer Docker image using Qt image plugins. No source code changes are required - this follows YACReader's existing plugin architecture as suggested in #498.

Motivation

Modern image formats like AVIF and JPEG XL offer significant advantages:

  • Better compression: 30-50% smaller file sizes vs JPEG at same quality
  • Higher quality: Better detail preservation at lower bitrates
  • Modern features: HDR, lossless compression, progressive decoding
  • Growing adoption: Increasingly used in digital comics and manga

Users with comics containing these formats currently cannot view them in YACReader, despite the formats becoming more common.

Implementation

This PR follows the maintainer's guidance from #498 to use Qt's dynamic plugin loading system rather than adding custom decoders to the codebase.

Docker Multi-Stage Build (8 Stages)

The updated Dockerfile uses an optimized multi-stage architecture with a shared base layer for maximum build caching and parallelization:

Stage 0-1: Base Layers

  • base - Common Ubuntu noble base image
  • plugin-base - Shared build dependencies (cmake, git, qt6-base-dev, etc.) inherited by all plugin builders

Stage 2-5: Independent Plugin Builders (all FROM plugin-base, build in parallel)

  • sevenzip-builder - Builds 7z.so with RAR support
  • heic-plugin-builder - Builds qt-heic-image-plugin
  • jxl-plugin-builder - Builds libjxl 0.10.2 + qt-jpegxl-image-plugin (merged into one stage)
  • avif-plugin-builder - Builds qt-avif-image-plugin

Stage 6-7: Assembly

  • yacreader-builder - Compiles YACReaderLibraryServer, collects plugins from stages 2-5
  • runtime - Final minimal image with only runtime dependencies

Key Optimizations:

  • Shared plugin-base stage: Common dependencies installed once, reused by all builders
  • Maximum parallelization: Stages 2, 3, 4, 5 build simultaneously
  • Merged libjxl stages: libjxl build + JXL plugin build in single stage (no intermediate copy)
  • Better caching: Changes to one plugin don't invalidate others
  • Smaller layers: No duplicate dependency installations

Qt Image Plugins Used

All plugins are from @novomesk's excellent collection:

Plugin Formats Library Version Stage
qt-avif-image-plugin .avif, .avifs libavif16 (system) 1.0.4 5
qt-jpegxl-image-plugin .jxl libjxl (custom build) 0.10.2 4
qt-heic-image-plugin .heic, .heif libheif1 (system) 1.17.6 3

Note on libjxl: Ubuntu noble ships libjxl 0.7.0, but qt-jpegxl-image-plugin requires 0.10.0+ for JxlEncoderDistanceFromQuality. We build 0.10.2 from source and merge it with the plugin build in a single optimized stage (stage 4).

How It Works

  1. Qt plugins are installed to /usr/lib/x86_64-linux-gnu/qt6/plugins/imageformats/
  2. YACReaderLibraryServer automatically discovers and loads them at startup
  3. Qt's QImageReader transparently uses plugins for supported formats
  4. No code changes needed - Qt handles everything

Testing

Prerequisites

# Pull the updated Docker image
docker pull yacreader/yacreader-library-server:latest

Verify Format Support

# Check that plugins are installed
docker run --rm yacreader/yacreader-library-server:latest \
  ls /usr/lib/x86_64-linux-gnu/qt6/plugins/imageformats/

# Should show:
# libqavif6.so (AVIF)
# libqjpegxl6.so (JXL)  
# kimg_heif6.so (HEIC/HEIF)

Test with Real Comics

# Run server with comics directory
docker run -d \
  -p 8080:8080 \
  -v /path/to/comics:/comics \
  --name yacreader \
  yacreader/yacreader-library-server:latest

# Create library and scan
docker exec yacreader YACReaderLibraryServer create-library "test" /comics

# Access web UI at http://localhost:8080
# Comics with AVIF/JXL/HEIC images should display correctly

Sample Test Files

Create test CBZ files to verify:

# Download sample images (or use your own)
# AVIF: https://github.com/AOMediaCodec/av1-avif/tree/master/testFiles
# JXL: https://github.com/libjxl/libjxl/tree/main/testdata  
# HEIC: iOS screenshots or HEIC photos

# Create test archives
zip test_avif.cbz sample.avif
zip test_jxl.cbz sample.jxl
zip test_heic.cbz sample.heic

# Place in comics directory and scan

Build Performance

Recent Optimizations (Latest Commit)

The Dockerfile was further optimized to reduce build times and improve caching:

  • Shared plugin-base layer: Eliminates duplicate dependency installations across plugin builders
  • Merged libjxl stages: Combines libjxl compilation and JXL plugin build, saving ~5-10s
  • Better parallelization: All 4 plugin stages (2-5) now build simultaneously from the same base
  • Cleaner layer structure: Reduced total lines from ~289 to ~243 while adding functionality

Image Size

  • Runtime image: ~450-460MB
  • Plugin overhead: ~6MB total for all three formats

Build Time (First Build)

  • Stage 0 (base): <5s (cached from registry)
  • Stage 1 (plugin-base): ~30s (common dependencies)
  • Stage 2-5 (build in parallel):
    • sevenzip-builder: ~30s
    • heic-plugin-builder: ~10s
    • jxl-plugin-builder: ~60s (libjxl + plugin merged)
    • avif-plugin-builder: ~6s
  • Stage 6 (yacreader-builder): ~2-3min (waits for stages 2-5)
  • Stage 7 (runtime): ~1min (waits for stage 6)
  • Total: ~4-5 minutes (stages 2-5 run concurrently)

Cached Rebuild

  • Unchanged stages: <1s each (cached)
  • Only changed stages rebuild

Compatibility

Backwards Compatibility

Fully backwards compatible

  • No changes to existing functionality
  • Traditional formats (JPEG, PNG, WebP) work as before
  • Additive change only

Platform Support

  • Linux Docker: ✅ Tested (Ubuntu noble base)
  • YACReaderLibraryServer: ✅ Tested with sample comics
  • iOS/Web clients: ✅ Work with native AVIF/JXL bytes

Client Support

  • AVIF: iOS 16+, Safari 16+, Chrome 85+, Firefox 93+
  • JXL: iOS 18+, Chrome soon
  • HEIC/HEIF: iOS/macOS native, limited elsewhere

Server serves native bytes - clients decode using their capabilities.

Changes

Modified Files

  • docker/Dockerfile - Complete rewrite with multi-stage architecture

No Source Code Changes

  • Zero modifications to YACReader C++ code
  • Zero changes to .pro build files
  • Pure plugin-based approach

Dependencies

New Runtime Dependencies

All dependencies are open source with permissive licenses:

  • libavif16 (BSD-2-Clause) - AVIF codec
  • libjxl 0.10.2 (BSD-3-Clause) - JPEG XL codec
  • libheif1 (LGPL-3.0) - HEIC/HEIF codec

Plugin Licenses

  • qt-avif-image-plugin: BSD-2-Clause
  • qt-jpegxl-image-plugin: BSD-3-Clause
  • qt-heic-image-plugin: LGPL-3.0

Future Considerations

When libjxl 0.10+ Reaches Ubuntu Repos

Once Ubuntu ships libjxl 0.10.0+:

  • Simplify Stage 3 to use apt-get install libjxl-dev
  • Remove custom build stage
  • Faster builds

Additional Formats

More Qt image plugins available:

  • WebP2: When spec finalizes
  • QOI: Quite OK Image format
  • FLIF: Free Lossless Image Format

Same pattern: build plugin, copy to imageformats/, done.

References

Checklist

  • Docker image builds successfully
  • Multi-stage build optimized for caching
  • No source code modifications
  • Follows YACReader's plugin architecture (Add AVIF and JPEG XL image format support #498 guidance)
  • Backwards compatible
  • All plugins from trusted sources (@novomesk)
  • Tested with sample AVIF/JXL/HEIC comics
  • Documentation complete

Note: This PR addresses #498's feedback by using Qt plugins instead of custom decoders. The approach is cleaner, more maintainable, and aligns with YACReader's existing architecture. No source code changes means no risk to existing functionality.

- Add multi-stage Docker build with 7 optimized stages
- Build and install Qt image plugins: qt-avif-image-plugin, qt-jpegxl-image-plugin, qt-heic-image-plugin
- Build libjxl 0.10.2 from source (required version not in Ubuntu noble)
- Use system packages for AVIF (libavif16) and HEIC (libheif1)
- No YACReader source code changes - uses Qt's dynamic plugin loading
- Improves build caching and reduces rebuild times

Enables support for modern image formats without code modifications,
following YACReader's Qt plugin architecture as suggested in YACReader#498
@nickv2002
Copy link
Author

nickv2002 commented Jan 11, 2026

BTW: I didn't port these updates over to the docker/Dockerfile.aarch64 as that's not the arch I use for my Docker server but if you're happy with these changes I can do that port. Thanks!

@nickv2002
Copy link
Author

nickv2002 commented Jan 18, 2026

@luisangelsm I'd love to get this merged so that this version it was available on DockerHub. What can I do to help make that happen?

@selmf
Copy link
Member

selmf commented Jan 20, 2026

@luisangelsm I'd love to get this merged so that this version it was available on DockerHub. What can I do to help make that happen?

To make this happen, it needs to be negligible in terms of added maintenance and support workload for @luisangelsm.

The best way to achieve this is to simply add kimageformat as a dependency (widely available on Ubuntu and other Linux distros) instead of introducing build processes and dependencies on non-packaged plugins and libraries.

As a general rule of thumb, YACReader only supports image formats supported by Qt itself. Adding support for additional formats is out of scope.

We still support adding these as a community effort or where doing so is a no-brainer, but supplying the necessary plugins for all variants of YACReader is not something that is feasible with the limited resources.

That means your options are:

  1. Fork and publish your own version of the docker image
  2. Minimal non-invasive kimageformats based version of the docker image with clear documentation that the extra formats supported are not guaranteed
  3. Consider joining the YACReader team and supplying (optional?) inclusion for emerging image formats

I can help getting this cleaned up and into a good shape, but the final decision rests with @luisangelsm .

PS: If possible, please keep the usage of AI/LLM at a minimum

@nickv2002
Copy link
Author

nickv2002 commented Jan 21, 2026

In this PR I directly followed the instructions given by @luisangelsm :

novomesk has a bunch of plugins available: https://github.com/novomesk

@selmf you said:

The best way to achieve [additional format support] is to simply add kimageformat as a dependency (widely available on Ubuntu and other Linux distros).

Those are different instructions.

I can help getting this cleaned up and into a good shape, but the final decision rests with @luisangelsm .

Can you clarify what you want to cleaned up? Are you just asking to switch from novomesk/qt_* to kimageformats?

For whatever it's worth, I've been using this image to host my library for the iOS app since creating this PR and I've added/read a number of AVIF/JXL comics without issue.


Stray comments:

documentation that the extra formats supported are not guaranteed

That's fine by me, I agree regular old JPG/PNG should be the target of your limited resources.

Consider joining the YACReader team and supplying (optional?) inclusion for emerging image formats

Sorry, I already help maintain and publish Simple Comic so one OSS comic tool is more than enough 😆

If possible, please keep the usage of AI/LLM at a minimum

I write lots of Dockerfiles for my day job, but I don't know much about QT, so that part was shored up by LLMs in this PR.

If there is a more QT-native way to do this within the constraints of a Dockerfile let me know.

If YACReader's position is that LLM-generated code is not welcome here, that should be made clear in the contribution guidelines

@luisangelsm
Copy link
Member

The main problem is that adding plugins compilation is adding another dependency that I would need to take care of in the future, if compiling a plugin starts to fail it would block the whole CI/CD pipeline.

Is not that AI generated content is not welcome, but the more verbose the PR is the more time requires to be reviewed because I need to fully understand everything if I want to be able to maintain it in the future. If installing kimageformats works and it's just adding a package as a dependency (or just a few) it will be way easier for me to accept and merge the PR. That's why I didn't comment on the PR, because I hadn't time to read it completely (I just took a look, I saw so many changes in the Docker file and that was it).

And now that we are talking about the contribution guidelines, the very first thing I say there is If you are interested in contributing to the project the first step should be to contact me.... Not because I am picky or anything, but it's the best way to make sure the contributor doesn't waste any time.

Anyway, I truly appreciate the interest in the project and I always encourage people to join YACReader and participate in any way they can, not only coding, UI/UX design, writing user guides, testing...

@luisangelsm
Copy link
Member

BTW, there are two Docker files Dockerfile and Dockerfile.aarch64. If you are willing to give the kformats a go, please update both files.

I had some troubles setting up the arm version with Qt6, I am planning to drop Qt5 support, I would really appreciate if you can help with that.

@selmf
Copy link
Member

selmf commented Jan 25, 2026

...

Those are different instructions.

Sorry if this is confusing. novomesk/qt_* is great as a drop-in for Windows but on Linux kimageformats is widely available and can easily be deployed.

I can help getting this cleaned up and into a good shape, but the final decision rests with @luisangelsm .

Can you clarify what you want to cleaned up? Are you just asking to switch from novomesk/qt_* to kimageformats?

Kimageformats installed as a package via apt-get with a minimal diff for the dockerfile would be the goal. As a bonus we could also clean up the dependencies - build-essential already pulls stuff like g++ and gcc.

If possible, please keep the usage of AI/LLM at a minimum

I write lots of Dockerfiles for my day job, but I don't know much about QT, so that part was shored up by LLMs in this PR.

If there is a more QT-native way to do this within the constraints of a Dockerfile let me know.

If YACReader's position is that LLM-generated code is not welcome here, that should be made clear in the contribution guidelines

As @luisangelsm said we are not anti LLM luddites, we just don't like some of its quirks. Sorry if that came along a little harsh.

You're welcome to use the tools you are productive with. I personally like to refine LLM generated code by using "KISS" and "compact/concise" as part of the prompt or asking it explicitly to summarize or distill info drops into short sections of text. That is my way of "keeping it to a minimum" :)

nickv2002 added a commit to nickv2002/yacreader that referenced this pull request Jan 26, 2026
- Replace 3 separate image plugin builders (AVIF, JXL, HEIC) with single kimageformats build
- Reduce Dockerfile from 8 stages to 6 stages (25% reduction)
- Build kimageformats 6.0.0 from KDE frameworks (patched for Qt 6.4 compatibility)
- Support AVIF (avif, avifs) and JPEG XL (jxl) formats
- Add 30+ bonus image formats (PSD, QOI, XCF, HDR, etc.)
- Simplify build dependencies and maintenance
- Note: HEIC/HEIF support not included (kimageformats 6.0.0 limitation)

This provides a minimal diff alternative to PR YACReader#499 as requested by maintainers,
using well-maintained KDE framework instead of multiple custom plugin builds.
nickv2002 added a commit to nickv2002/yacreader that referenced this pull request Jan 26, 2026
- Step-by-step testing instructions
- Expected outputs and success criteria
- Known limitations (HEIC/HEIF not supported)
- Troubleshooting section
- Comparison with original PR YACReader#499 approach
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants