Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ public class VipsImageFilterApiImpl implements ImageFilterAPI {
// libvips-only capabilities with no legacy equivalent
.put("smartcrop", VipsSmartCropImageFilter.class)
.put("avif", VipsAvifImageFilter.class)
.put("jxl", VipsJpegXlImageFilter.class)
.put("jpegxl", VipsJpegXlImageFilter.class)
.build();

public VipsImageFilterApiImpl() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.dotmarketing.image.vips;

import app.photofox.vipsffm.VImage;
import app.photofox.vipsffm.VipsOption;
import java.io.File;
import java.util.Map;

/**
* JPEG XL encoder — a modern format the legacy engine cannot produce. Encodes via libvips' libjxl
* delegate, typically smaller than JPEG at equal quality with support for lossless re-compression of
* existing JPEGs.
*
* <p>URL contract: {@code filter=jxl&jxl_q=75}. {@code q} is 0-100 quality; {@code lossless=true}
* switches to mathematically lossless encoding; {@code effort} (1-9) trades encode speed for size.
* Requires the host libvips to be built with libjxl; if absent the request fails (no legacy
* fallback — JPEG XL is libvips-only).</p>
*/
public class VipsJpegXlImageFilter extends VipsImageFilter {

@Override
public String[] getAcceptedParameters() {
return new String[] {
"q (int) 0-100 quality",
"lossless (present) lossless encode",
"effort (int) 1-9 encoder effort/speed tradeoff"
};
}

@Override
protected void transform(final File in, final File out, final Map<String, String[]> parameters) {
final int quality = intParam(parameters, "q", 75);
final boolean lossless = parameters.get(getPrefix() + "lossless") != null;
final int effort = intParam(parameters, "effort", 7);
VipsManager.run(arena -> {
final VImage src = VipsManager.load(arena, in);
src.writeToFile(out.getAbsolutePath(),
VipsOption.Int("Q", quality),
VipsOption.Boolean("lossless", lossless),
VipsOption.Int("effort", effort));
});
}

@Override
public File getResultsFile(final File file, final Map<String, String[]> parameters) {
return getResultsFile(file, parameters, "jxl");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,37 @@ public void avif_encoder_produces_valid_avif() throws Exception {
assertTrue("expected avif brand, got '" + brand + "'", brand.startsWith("avi"));
}

/** JPEG XL encode requires the host libvips to be built with the libjxl delegate. */
private boolean jxlEncodeSupported() {
try {
final File probeIn = image("test.png");
final File probeOut = tempOut("jxl");
new VipsJpegXlImageFilter().transform(probeIn, probeOut, params("jxl_q", "75"));
return probeOut.exists() && probeOut.length() > 50;
} catch (Exception e) {
return false;
}
}

@Test
public void jxl_encoder_produces_valid_jxl() throws Exception {
Assume.assumeTrue("host libvips lacks a JPEG XL encoder (libjxl)", jxlEncodeSupported());
final File in = image("test.png");
final File out = tempOut("jxl");
new VipsJpegXlImageFilter().transform(in, out, params("jxl_q", "75"));
assertTrue("jxl written", out.exists() && out.length() > 50);
// JPEG XL signatures: the raw codestream starts with FF 0A; the ISO-BMFF container starts
// with a 'JXL ' box (00 00 00 0C 4A 58 4C 20).
final byte[] head = new byte[12];
try (java.io.InputStream is = new java.io.FileInputStream(out)) {
assertEquals(12, is.read(head));
}
final boolean rawCodestream = (head[0] & 0xFF) == 0xFF && (head[1] & 0xFF) == 0x0A;
final boolean container = (head[4] & 0xFF) == 0x4A && (head[5] & 0xFF) == 0x58
&& (head[6] & 0xFF) == 0x4C && (head[7] & 0xFF) == 0x20;
assertTrue("expected a JPEG XL signature", rawCodestream || container);
}

@Test
public void smartcrop_produces_exact_box_from_salient_region() throws Exception {
final File in = image("test.jpg");
Expand Down
Loading