Skip to content

Commit

Permalink
Merge branch 'develop' into feature/62-ngdm-web-optimized-delivery-re…
Browse files Browse the repository at this point in the history
…lative-cropping
  • Loading branch information
stefanseifert committed Aug 23, 2024
2 parents 2b8be8b + 6832fb4 commit 5229e4a
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 43 deletions.
4 changes: 4 additions & 0 deletions changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
Dynamic Media with OpenAPI: Do not enable support for remote assets by default. Since general availability the related configuration services not longer protected by a feature flag, so the feature has to be enabled explicitly via OSGi configuration.<br/>
<b>Breaking change:</b> You can enable support for remote assets by setting "Remote Assets" to true in the "wcm.io Media Handler Dynamic Media with OpenAPI Support" OSGi configuration.
]]></action>
<action type="update" dev="sseifert" issue="65"><![CDATA[
Dynamic Media: Control image quality for lossy output image for each media request by appending "qlt" URL parameter, defaulting to the default image quality configured in the media handler configuration class.<br/>
<b>Breaking change:</b> You can disable this behavior by setting "Set Image Quality" to false in the "wcm.io Media Handler Dynamic Media Support" OSGi configuration. If disabled, the default image quality setting configured in Dynamic Media is used for all images.
]]></action>
<action type="update" dev="sseifert">
Eliminate dependency to Commons Lang 2.
</action>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@
* limitations under the License.
* #L%
*/
package io.wcm.handler.mediasource.ngdm.impl;
package io.wcm.handler.media.impl;

import org.jetbrains.annotations.NotNull;

import io.wcm.handler.media.MediaArgs;
import io.wcm.handler.media.spi.MediaHandlerConfig;

/**
* Sanitizes SEO names for usage in context of Next Gen. Dynamic Media
* Gets image quality for current media request, with fallback to default quality.
*/
public final class ImageQualityPercentage {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@

import io.wcm.handler.media.Dimension;
import io.wcm.handler.media.MediaArgs;
import io.wcm.handler.media.impl.ImageQualityPercentage;
import io.wcm.handler.media.spi.MediaHandlerConfig;
import io.wcm.handler.mediasource.dam.impl.dynamicmedia.DynamicMediaSupportService;
import io.wcm.handler.mediasource.dam.impl.dynamicmedia.ImageProfile;
import io.wcm.handler.mediasource.dam.impl.dynamicmedia.NamedDimension;
import io.wcm.handler.mediasource.dam.impl.weboptimized.WebOptimizedImageDeliveryParams;
import io.wcm.handler.mediasource.dam.impl.weboptimized.WebOptimizedImageDeliveryService;
import io.wcm.handler.mediasource.ngdm.impl.ImageQualityPercentage;

/**
* Context objects require in DAM support implementation.
Expand Down Expand Up @@ -163,6 +163,14 @@ public boolean isDynamicMediaValidateSmartCropRenditionSizes() {
return dynamicMediaSupportService.isValidateSmartCropRenditionSizes();
}

/**
* @return Whether to control image quality for lossy output formats for each media request via 'qlt' URL parameter
* (instead of relying on default setting within Dynamic Media).
*/
public boolean isDynamicMediaSetImageQuality() {
return dynamicMediaSupportService.isSetImageQuality();
}

/**
* @return Dynamic media reply image size limit
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.wcm.handler.media.CropDimension;
import io.wcm.handler.media.Dimension;
import io.wcm.handler.media.format.Ratio;
import io.wcm.handler.media.impl.ImageQualityPercentage;
import io.wcm.handler.mediasource.dam.impl.DamContext;
import io.wcm.wcm.commons.contenttype.ContentType;

Expand Down Expand Up @@ -126,15 +127,8 @@ private DynamicMediaPath() {
logResult(damContext, "<too small for " + width + "x" + height + ">");
return null;
}
result.append("%3A").append(smartCropDef.getName()).append("?")
.append("wid=").append(dimension.getWidth()).append("&")
.append("hei=").append(dimension.getHeight()).append("&")
// cropping/width/height is pre-calculated to fit with original ratio, make sure there are no 1px background lines visible
.append("fit=stretch");
if (isPNG(damContext)) {
// if original image is PNG image, make sure alpha channel is preserved
result.append("&fmt=png-alpha");
}
result.append("%3A").append(smartCropDef.getName()).append("?");
appendWidthHeigtFormatQuality(result, dimension, damContext);
logResult(damContext, result);
return result.toString();
}
Expand All @@ -147,6 +141,12 @@ private DynamicMediaPath() {
if (rotation != null) {
result.append("rotate=").append(rotation).append("&");
}
appendWidthHeigtFormatQuality(result, dimension, damContext);
logResult(damContext, result);
return result.toString();
}

private static void appendWidthHeigtFormatQuality(@NotNull StringBuilder result, @NotNull Dimension dimension, @NotNull DamContext damContext) {
result.append("wid=").append(dimension.getWidth()).append("&")
.append("hei=").append(dimension.getHeight()).append("&")
// cropping/width/height is pre-calculated to fit with original ratio, make sure there are no 1px background lines visible
Expand All @@ -155,8 +155,10 @@ private DynamicMediaPath() {
// if original image is PNG image, make sure alpha channel is preserved
result.append("&fmt=png-alpha");
}
logResult(damContext, result);
return result.toString();
else if (damContext.isDynamicMediaSetImageQuality()) {
// it not PNG lossy format is used, apply image quality setting
result.append("&qlt=").append(ImageQualityPercentage.getAsInteger(damContext.getMediaArgs(), damContext.getMediaHandlerConfig()));
}
}

private static void logResult(@NotNull DamContext damContext, @NotNull CharSequence result) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ public interface DynamicMediaSupportService {
@NotNull
Dimension getImageSizeLimit();

/**
* @return Whether to control image quality for lossy output formats for each media request via 'qlt' URL parameter
* (instead of relying on default setting within Dynamic Media).
*/
boolean isSetImageQuality();

/**
* Get image profile.
* @param profilePath Full profile path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ public class DynamicMediaSupportServiceImpl implements DynamicMediaSupportServic
description = "The configured height value for 'Reply Image Size Limit'.")
long imageSizeLimitHeight() default 2000;

@AttributeDefinition(
name = "Set Image Quality",
description = "Control image quality for lossy output formats for each media request via 'qlt' URL parameter (instead of relying on default setting within Dynamic Media).")
boolean setImageQuality() default true;

}

@Reference
Expand All @@ -118,6 +123,7 @@ public class DynamicMediaSupportServiceImpl implements DynamicMediaSupportServic
private boolean disableAemFallback;
private boolean validateSmartCropRenditionSizes;
private Dimension imageSizeLimit;
private boolean setImageQuality;

private static final String SERVICEUSER_SUBSERVICE = "dynamic-media-support";
private static final Pattern DAM_PATH_PATTERN = Pattern.compile("^/content/dam(/.*)?$");
Expand All @@ -132,6 +138,7 @@ private void activate(Config config) {
this.disableAemFallback = config.disableAemFallback();
this.validateSmartCropRenditionSizes = config.validateSmartCropRenditionSizes();
this.imageSizeLimit = new Dimension(config.imageSizeLimitWidth(), config.imageSizeLimitHeight());
this.setImageQuality = config.setImageQuality();

if (this.enabled) {
log.info("DynamicMediaSupport: enabled={}, capabilityEnabled={}, capabilityDetection={}, "
Expand Down Expand Up @@ -174,6 +181,11 @@ public boolean isValidateSmartCropRenditionSizes() {
return this.imageSizeLimit;
}

@Override
public boolean isSetImageQuality() {
return setImageQuality;
}

@Override
public @Nullable ImageProfile getImageProfile(@NotNull String profilePath) {
try (ResourceResolver resourceResolver = resourceResolverFactory
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
import io.wcm.handler.media.UriTemplate;
import io.wcm.handler.media.UriTemplateType;
import io.wcm.handler.media.format.MediaFormat;
import io.wcm.handler.mediasource.ngdm.impl.ImageQualityPercentage;
import io.wcm.handler.media.impl.ImageQualityPercentage;
import io.wcm.handler.mediasource.ngdm.impl.MediaArgsDimension;
import io.wcm.handler.mediasource.ngdm.impl.NextGenDynamicMediaBinaryUrlBuilder;
import io.wcm.handler.mediasource.ngdm.impl.NextGenDynamicMediaContext;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import io.wcm.handler.media.MediaNameConstants;
import io.wcm.handler.media.UriTemplate;
import io.wcm.handler.media.UriTemplateType;
import io.wcm.handler.mediasource.ngdm.impl.ImageQualityPercentage;
import io.wcm.handler.media.impl.ImageQualityPercentage;
import io.wcm.handler.mediasource.ngdm.impl.MediaArgsDimension;
import io.wcm.handler.mediasource.ngdm.impl.NextGenDynamicMediaContext;
import io.wcm.handler.mediasource.ngdm.impl.NextGenDynamicMediaImageDeliveryParams;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
* limitations under the License.
* #L%
*/
package io.wcm.handler.mediasource.ngdm.impl;
package io.wcm.handler.media.impl;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ void testValidSmartCroppedRenditionAndWidths() {

List<Rendition> renditions = List.copyOf(media.getRenditions());
assertEquals(2, renditions.size());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=80&hei=60&fit=stretch", renditions.get(0).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=40&hei=30&fit=stretch", renditions.get(1).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=80&hei=60&fit=stretch&qlt=85", renditions.get(0).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=40&hei=30&fit=stretch&qlt=85", renditions.get(1).getUrl());
}

@Test
Expand All @@ -118,9 +118,9 @@ void testValidSmartCroppedRenditionAndWidths_DisableValidateSmartCropRenditionSi

List<Rendition> renditions = List.copyOf(media.getRenditions());
assertEquals(3, renditions.size());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=100&hei=75&fit=stretch", renditions.get(0).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=80&hei=60&fit=stretch", renditions.get(1).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=40&hei=30&fit=stretch", renditions.get(2).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=100&hei=75&fit=stretch&qlt=85", renditions.get(0).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=80&hei=60&fit=stretch&qlt=85", renditions.get(1).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=40&hei=30&fit=stretch&qlt=85", renditions.get(2).getUrl());
}

@Test
Expand All @@ -144,7 +144,7 @@ void testValidSmartCroppedRendition_OnlyRatio() {

List<Rendition> renditions = List.copyOf(media.getRenditions());
assertEquals(1, renditions.size());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=80&hei=60&fit=stretch", renditions.get(0).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A4-3?wid=80&hei=60&fit=stretch&qlt=85", renditions.get(0).getUrl());
}

@Test
Expand All @@ -154,7 +154,7 @@ void testValidSmartCroppedRenditionOnlyRatio_MatchingOriginalRatio() {

List<Rendition> renditions = List.copyOf(media.getRenditions());
assertEquals(1, renditions.size());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A16-10?wid=120&hei=75&fit=stretch", renditions.get(0).getUrl());
assertEquals("https://dummy.scene7.com/is/image/DummyFolder/test%3A16-10?wid=120&hei=75&fit=stretch&qlt=85", renditions.get(0).getUrl());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ boolean isCreateAssetWithDynamicMediaMetadata() {
void testAsset_JPEG_Original() {
Asset asset = createSampleAsset("/filetype/sample.jpg", ContentType.JPEG);
buildAssertMedia(asset, 100, 50,
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?wid=100&hei=50&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?wid=100&hei=50&fit=stretch&qlt=85",
ContentType.JPEG);
}

Expand All @@ -74,7 +74,7 @@ void testAsset_JPEG_Original_ContentDisposition() {
void testAsset_JPEG_Rescale() {
Asset asset = createSampleAsset("/filetype/sample.jpg", ContentType.JPEG);
buildAssertMedia_Rescale(asset, 80, 40,
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?wid=80&hei=40&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?wid=80&hei=40&fit=stretch&qlt=85",
ContentType.JPEG);
}

Expand All @@ -83,7 +83,7 @@ void testAsset_JPEG_Rescale() {
void testAsset_JPEG_AutoCrop() {
Asset asset = createSampleAsset("/filetype/sample.jpg", ContentType.JPEG);
buildAssertMedia_AutoCrop(asset, 50, 50,
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?crop=25,0,50,50&wid=50&hei=50&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?crop=25,0,50,50&wid=50&hei=50&fit=stretch&qlt=85",
ContentType.JPEG);
}

Expand All @@ -92,7 +92,7 @@ void testAsset_JPEG_AutoCrop() {
void testAsset_JPEG_AutoCrop_ImageQuality() {
Asset asset = createSampleAsset("/filetype/sample.jpg", ContentType.JPEG);
buildAssertMedia_AutoCrop(asset, 50, 50,
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?crop=25,0,50,50&wid=50&hei=50&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?crop=25,0,50,50&wid=50&hei=50&fit=stretch&qlt=60",
ContentType.JPEG, 0.6d);
}

Expand All @@ -102,7 +102,7 @@ void testAsset_JPEG_CropWithExplicitRendition() {
Asset asset = createSampleAsset("/filetype/sample.jpg", ContentType.JPEG);
context.create().assetRendition(asset, "square.jpg", 50, 50, ContentType.JPEG);
buildAssertMedia_AutoCrop(asset, 50, 50,
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?crop=25,0,50,50&wid=50&hei=50&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.jpg?crop=25,0,50,50&wid=50&hei=50&fit=stretch&qlt=85",
ContentType.JPEG);
}

Expand Down Expand Up @@ -148,7 +148,7 @@ void testAsset_GIF_Original() {
void testAsset_GIF_Rescale() {
Asset asset = createSampleAsset("/filetype/sample.gif", ContentType.GIF);
buildAssertMedia_Rescale(asset, 80, 40,
"https://dummy.scene7.com/is/image/DummyFolder/sample.gif?wid=80&hei=40&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.gif?wid=80&hei=40&fit=stretch&qlt=85",
ContentType.JPEG);
}

Expand All @@ -157,7 +157,7 @@ void testAsset_GIF_Rescale() {
void testAsset_GIF_AutoCrop() {
Asset asset = createSampleAsset("/filetype/sample.gif", ContentType.GIF);
buildAssertMedia_AutoCrop(asset, 50, 50,
"https://dummy.scene7.com/is/image/DummyFolder/sample.gif?crop=25,0,50,50&wid=50&hei=50&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.gif?crop=25,0,50,50&wid=50&hei=50&fit=stretch&qlt=85",
ContentType.JPEG);
}

Expand Down Expand Up @@ -235,7 +235,7 @@ void testFileUpload_PNG_AutoCrop() {
void testAsset_TIFF_Original() {
Asset asset = createSampleAsset("/filetype/sample.tif", ContentType.TIFF);
buildAssertMedia(asset, 100, 50,
"https://dummy.scene7.com/is/image/DummyFolder/sample.tif?wid=100&hei=50&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.tif?wid=100&hei=50&fit=stretch&qlt=85",
ContentType.JPEG);
}

Expand All @@ -253,7 +253,7 @@ void testAsset_TIFF_Original_ContentDisposition() {
void testAsset_TIFF_Rescale() {
Asset asset = createSampleAsset("/filetype/sample.tif", ContentType.TIFF);
buildAssertMedia_Rescale(asset, 80, 40,
"https://dummy.scene7.com/is/image/DummyFolder/sample.tif?wid=80&hei=40&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.tif?wid=80&hei=40&fit=stretch&qlt=85",
ContentType.JPEG);
}

Expand All @@ -262,7 +262,7 @@ void testAsset_TIFF_Rescale() {
void testAsset_TIFF_AutoCrop() {
Asset asset = createSampleAsset("/filetype/sample.tif", ContentType.TIFF);
buildAssertMedia_AutoCrop(asset, 50, 50,
"https://dummy.scene7.com/is/image/DummyFolder/sample.tif?crop=25,0,50,50&wid=50&hei=50&fit=stretch",
"https://dummy.scene7.com/is/image/DummyFolder/sample.tif?crop=25,0,50,50&wid=50&hei=50&fit=stretch&qlt=85",
ContentType.JPEG);
}

Expand Down
Loading

0 comments on commit 5229e4a

Please sign in to comment.