Skip to content

feat(image): add JPEG XL (jxl) image filter backed by libvips#36224

Open
wezell wants to merge 2 commits into
mainfrom
issue-36223-jpegxl-image-filter
Open

feat(image): add JPEG XL (jxl) image filter backed by libvips#36224
wezell wants to merge 2 commits into
mainfrom
issue-36223-jpegxl-image-filter

Conversation

@wezell

@wezell wezell commented Jun 18, 2026

Copy link
Copy Markdown
Member

What

Adds a JPEG XL (.jxl) image filter backed by libvips, mirroring the existing AVIF filter. JPEG XL is a modern format the legacy pure-JVM (AWT/TwelveMonkeys) engine cannot produce; libvips encodes it via its libjxl delegate.

URL contract: filter=jxl&jxl_q=75 (also aliased as filter=jpegxl), with optional jxl_lossless and jxl_effort (1–9).

How

  • VipsJpegXlImageFilter — new filter extending VipsImageFilter, encoding to .jxl with Q (quality, default 75), lossless (default off), and effort (default 7). Modeled directly on VipsAvifImageFilter.
  • VipsImageFilterApiImpl — registers the filter under jxl and jpegxl in the "libvips-only capabilities with no legacy equivalent" block, next to avif/smartcrop.
  • VipsParityTest — adds a test that validates the JPEG XL output signature (raw codestream FF 0A or JXL ISO-BMFF box) and skips cleanly when the host libvips lacks the libjxl delegate.

Only available when libvips is enabled

ImageEngine.resolve() returns the libvips engine only when IMAGE_API_USE_LIBVIPS=true and native libvips is available. The jxl/jpegxl keys live solely in VipsImageFilterApiImpl — the legacy ImageFilterApiImpl has no knowledge of them, so when libvips is disabled the filter simply doesn't resolve (no error). Because JPEG XL has no pure-JVM equivalent, no VipsLegacyFilters fallback entry is added (same approach as avif).

Testing

  • ./mvnw compile -pl :dotcms-core passes.
  • VipsParityTest#jxl_encoder_produces_valid_jxl validates the output; it Assume-skips on hosts whose libvips lacks libjxl, keeping CI green.

Fixes #36223

🤖 Generated with Claude Code

Adds a libvips-only JPEG XL encoder filter, mirroring the existing AVIF
filter. Encodes via libvips' libjxl delegate (inferred from the .jxl
extension) with q/lossless/effort parameters under the jxl_ prefix.

Registered under both "jxl" and "jpegxl" keys in VipsImageFilterApiImpl,
so it is only reachable when the libvips engine is active
(IMAGE_API_USE_LIBVIPS=true and native libvips available, via
ImageEngine.resolve()). The legacy engine has no knowledge of these keys,
so the filter is simply unavailable when libvips is disabled. No
VipsLegacyFilters fallback is wired since JPEG XL has no pure-JVM
equivalent.

Adds a parity test validating the JPEG XL output signature that skips
cleanly when the host libvips lacks the libjxl delegate.

Fixes #36223

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@claude

claude Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Claude finished @wezell's task in 1m 3s —— View job


Rollback Safety Analysis

  • Read rollback-unsafe categories reference
  • Get full PR diff
  • Analyze diff against unsafe categories
  • Apply appropriate label

Result: ✅ Safe To Rollback

The changes in this PR are purely additive and do not match any rollback-unsafe category:

Category Status
C-1 Structural Data Model Change ✅ No DB changes
C-2 Elasticsearch Mapping Change ✅ No ES changes
C-3 Content JSON Model Version Bump ✅ No model version changes
C-4 DROP TABLE / DROP Column ✅ No destructive DDL
H-1 One-Way Data Migration ✅ No data transformation
H-2 RENAME TABLE / RENAME COLUMN ✅ No renames
H-3 PK Restructuring ✅ No PK changes
H-4 New ContentType Field Type ✅ Not a content field type
H-5 Storage Provider Change ✅ No storage provider changes
M-2 Push Publishing Bundle Format ✅ No bundle format changes
M-3 REST / GraphQL API Contract ✅ No API contract changes
M-4 OSGi Interface Change ✅ No OSGi interface changes

What this PR adds:

  • VipsJpegXlImageFilter.java — a new image filter class (additive)
  • Two new entries in the VipsImageFilterApiImpl in-memory filter map (jxl, jpegxl) — additive, libvips-only; the legacy engine has no knowledge of these keys
  • A new parity test that skips gracefully when libjxl is absent

Rolling back to N-1 simply means neither key resolves in VipsImageFilterApiImpl, which is the same state that existed before N. No data is written to the database, no ES mappings are altered, and no existing contracts change.

@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — deepseek.v3.2

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/image/vips/VipsJpegXlImageFilter.java:29 — The parameter key for lossless is constructed using getPrefix(), but getPrefix() is not defined in this class or its parent VipsImageFilter. This will cause a NullPointerException when checking for the lossless parameter. The key should likely be just "lossless" without a prefix, consistent with how "q" and "effort" are retrieved.

[🟡 Medium] dotCMS/src/main/java/com/dotmarketing/image/vips/VipsJpegXlImageFilter.java:33 — The VipsOption.Int and VipsOption.Boolean calls may throw runtime exceptions if the underlying libvips library does not support these options for the JPEG XL format. While the test attempts to detect support, the production code lacks a graceful fallback or clear error handling, which could cause image processing to fail abruptly.

[🟡 Medium] dotCMS/src/test/java/com/dotmarketing/image/vips/VipsParityTest.java:340 — The test helper jxlEncodeSupported() catches all Exception types, which could mask unrelated errors (e.g., I/O problems) and incorrectly report that JPEG XL encoding is supported. It should catch a more specific exception, such as one thrown by the libvips bindings when the delegate is missing.


Run: #27732344799 · tokens: in: 1915 · out: 319 · total: 2234

@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

🤖 Bedrock Review — us.deepseek.r1-v1:0

New Issues

  • 🔴 Critical: dotCMS/src/main/java/com/dotmarketing/image/vips/VipsJpegXlImageFilter.java:32 — Parameter check uses parameters.get() null check instead of containsKey(), failing to detect presence-only flags like ?lossless. This breaks lossless encoding when using URL params without values.
  • 🔴 Critical: dotCMS/src/main/java/com/dotmarketing/image/vips/VipsJpegXlImageFilter.java:19-23 — Javadoc documents parameters without filter prefix (e.g. q), but code requires prefixed names (e.g. jxl_q). This API contract mismatch causes silent parameter ignoring.
  • 🟡 Medium: dotCMS/src/test/java/com/dotmarketing/image/vips/VipsParityTest.java:360 — Test lacks coverage for lossless and effort parameters. Meaningful new functionality untested.

Run: #28064471931 · tokens: in: 2569 · out: 2970 · total: 5539

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI: Safe To Rollback Area : Backend PR changes Java/Maven backend code

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

Add JPEG XL (jxl) image filter backed by libvips

1 participant