Skip to content

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

Open
wezell wants to merge 3 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 3 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 all unsafe categories
  • Apply appropriate label

Result: ✅ Safe to Roll Back

The label AI: Safe To Rollback has been applied.

Analysis

All three changed files were evaluated against every rollback-unsafe category:

File Change Category check
VipsJpegXlImageFilter.java New class added No DB, ES, or API contract changes
VipsImageFilterApiImpl.java Registers jxl/jpegxl keys Purely additive in-memory map; no persistence
VipsParityTest.java New test with Assume skip Test-only change

Category-by-category verdict:

  • C-1 through C-4 (CRITICAL): No database schema changes of any kind — no runonce tasks, no DDL, no contentlet_as_json model version bump, no DROP statements.
  • H-1 through H-8 (HIGH): No data migrations, no renames, no PK changes, no new content-type field types, no storage provider changes, no VTL viewtool contract changes.
  • M-1 through M-4 (MEDIUM): No column type changes, no bundle format changes, no REST/GraphQL contract additions that clients depend on, no OSGi interface changes.

Why rollback is safe: The jxl/jpegxl filter keys exist only in VipsImageFilterApiImpl's in-memory map. N-1 simply has no knowledge of these keys — a request for filter=jxl on N-1 will return a "filter not found" result (same as before N was deployed). No data is written to the database, no ES mappings change, and no persistent state is created by this feature. Rolling back to N-1 cleanly removes the feature with zero side effects.

@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

@github-actions

github-actions Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

🤖 dotBot Review (Bedrock)

Reviewed 3 file(s); 3 candidate(s) → 2 confirmed, 0 uncertain (unverified, kept for review).

Confirmed findings

  • 🟡 Medium dotCMS/src/main/java/com/dotmarketing/image/vips/VipsJpegXlImageFilter.java:30 — Missing validation for effort parameter range (1-9)
    The code retrieves 'jxl_effort' as an integer with a default of 7 but does not validate/clamp the value to the required 1-9 range. Other filters like VipsAvifImageFilter use Math.min/max to enforce parameter ranges (e.g. avif_speed 0-8). Without validation, invalid values could cause libvips errors or degraded performance.
  • 🟡 Medium dotCMS/src/main/java/com/dotmarketing/image/vips/VipsJpegXlImageFilter.java:28 — Missing validation/clamping for jxl_q parameter
    The code retrieves the 'jxl_q' parameter but does not validate or clamp it to the 0-100 range. While libvips may clamp internally, explicit validation ensures API contract adherence and prevents invalid values from propagating. Similar filters (like AVIF) show the same pattern, but this doesn't negate the need for proper input validation per the documented parameters.

us.deepseek.r1-v1:0 · Run: #28270482325 · tokens: in: 15734 · out: 5310 · total: 21044 · calls: 6 · est. ~$0.050

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