CDN optimization: encoder upgrades + format-selection safety guards#2
Open
adityaverm-a wants to merge 6 commits into
Open
CDN optimization: encoder upgrades + format-selection safety guards#2adityaverm-a wants to merge 6 commits into
adityaverm-a wants to merge 6 commits into
Conversation
Enable mozjpeg encoder for all JPEG transformations in edit-applicator.ts.
This reduces JPEG file size by 10-15% at comparable visual quality compared
to Sharp's default encoder, bringing DIT output in line with Cloudinary's
compression performance.
- Pass { mozjpeg: true } when converting to JPEG format
- Add test coverage to verify mozjpeg option is applied
- Achieves expected 10-15% file size reduction for JPEG output
Fixes: CloudFront DIT CDN Migration - Enable mozjpeg optimization for JPEG output
Co-authored-by: Cursor <cursoragent@cursor.com>
Enable palette quantization with maximum compression and adaptive filtering for PNG output in the Sharp format step to reduce transformed PNG payload size. Update tests to assert the new PNG output options are applied. Co-authored-by: Cursor <cursoragent@cursor.com>
Translate generic quality input into Sharp GIF options (colours, effort, inter-frame and inter-palette error) so GIF output uses real compression controls instead of an unsupported quality field. Add tests for mapping behavior and bounds clamping. Co-authored-by: Cursor <cursoragent@cursor.com>
Two-layer alpha guard so transparent PNG/WebP icons are never flattened to JPEG. 1. auto-optimizer: when the selected auto format is jpeg and the source content-type is image/png or image/webp, fall back to the next non-jpeg accepted format from FORMAT_PRIORITY; if none, return [] so the source format passes through. 2. image-processor: after sharp(buffer).metadata(), if hasAlpha is true, rewrite any format=jpeg transformation to webp (when image/webp is in dit-accept) else png. Catches URL-specified jpeg and origins whose Content-Type didn't disclose alpha. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Guard against re-encoding jpeg/webp sources into a lossless container (png, tiff). Lossless can't recover quality the source already discarded, so it only inflates file size — observed up to +307% on jpeg → png. In getFormatOptimizations(), if the source content-type is in LOSSY_SOURCE_CONTENT_TYPES (image/jpeg, image/jpg, image/webp) and the selected format is in LOSSLESS_TARGET_FORMATS (png, tiff), fall back to the next accepted non-lossless format. The fallback search keeps the alpha guard's no-jpeg rule in effect for alpha-suspect sources so the two guards compose. If no safe fallback is accepted, return [] so the source format passes through unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Animated WebP can drop quality significantly without perceptual loss — motion masking lets the eye tolerate compression artifacts between rapidly changing frames. DIT previously emitted animated WebP at the same Q80 as static content; on the 304 GIF→WebP rows shared with Cloudinary, DIT was larger on 100% of rows, average +36%. Thread the existing isAnimation flag (metadata.pages > 1, already computed in applyEdits) into applyFormat and cap webp output quality at ANIMATED_WEBP_QUALITY_CAP=50 when emitting animated content. A caller-supplied lower quality is honored as-is. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR improves DIT’s CDN image optimization by (1) upgrading Sharp encoder output options for several formats and (2) adding safety guards to prevent visually broken or size-inflating auto format selections (notably alpha → JPEG and lossy → lossless re-encodes).
Changes:
- Add auto-format-selection guards to avoid transparency loss (alpha → JPEG) and lossy → lossless size bloat (JPEG/WebP → PNG/TIFF).
- Enhance encoder configuration in the transformation engine (mozjpeg for JPEG, PNG compression settings, GIF quality mapping, and animated WebP quality capping).
- Expand unit tests to cover the new guards and encoder behaviors.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| source/container/src/services/transformation-resolver/auto-optimization/auto-optimizer.ts | Adds pre-fetch format-selection guards for alpha safety and lossy→lossless avoidance. |
| source/container/src/services/transformation-resolver/auto-optimization/auto-optimizer.test.ts | Adds/updates tests validating the new format-selection guard behavior and guard composition. |
| source/container/src/services/image-processing/transformation-engine/edit-applicator.ts | Upgrades Sharp output options across formats and caps animated WebP quality. |
| source/container/src/services/image-processing/transformation-engine/edit-applicator.test.ts | Adds tests validating new encoder options and animated WebP cap behavior. |
| source/container/src/services/image-processing/image-processor.service.ts | Adds post-fetch (metadata-based) alpha→JPEG override for defense in depth. |
| source/container/src/services/image-processing/image-processor.service.test.ts | Adds tests for the post-fetch alpha override logic. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+82
to
+83
| const accept = req.header("dit-accept") || ""; | ||
| console.log("Accept header found as: ", req.header("dit-accept")); |
Comment on lines
144
to
146
| function getQualityOptimizations(req: Request, qualityConfig: any): Transformation[] { | ||
| console.log('getQuality: ', qualityConfig) | ||
| console.log("getQuality: ", qualityConfig); | ||
| if (!qualityConfig || !Array.isArray(qualityConfig) || qualityConfig.length === 0) { |
| }) | ||
| ); | ||
|
|
||
| const isExpectedToBeAnimated = imageRequest.sourceImageContentType == "image/gif"; |
Comment on lines
+116
to
+117
| const accept = imageRequest.clientHeaders?.["dit-accept"] || ""; | ||
| const replacement = accept.includes("image/webp") ? "webp" : "png"; |
Comment on lines
+82
to
+83
| const accept = req.header("dit-accept") || ""; | ||
| console.log("Accept header found as: ", req.header("dit-accept")); |
Comment on lines
+116
to
+117
| const accept = imageRequest.clientHeaders?.["dit-accept"] || ""; | ||
| const replacement = accept.includes("image/webp") ? "webp" : "png"; |
Comment on lines
+82
to
+83
| const accept = req.header("dit-accept") || ""; | ||
| console.log("Accept header found as: ", req.header("dit-accept")); |
Comment on lines
144
to
146
| function getQualityOptimizations(req: Request, qualityConfig: any): Transformation[] { | ||
| console.log('getQuality: ', qualityConfig) | ||
| console.log("getQuality: ", qualityConfig); | ||
| if (!qualityConfig || !Array.isArray(qualityConfig) || qualityConfig.length === 0) { |
| }) | ||
| ); | ||
|
|
||
| const isExpectedToBeAnimated = imageRequest.sourceImageContentType == "image/gif"; |
Comment on lines
+116
to
+117
| const accept = imageRequest.clientHeaders?.["dit-accept"] || ""; | ||
| const replacement = accept.includes("image/webp") ? "webp" : "png"; |
6 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Six commits bundling per-format encoder upgrades (Sharp output options) and two safety guards in the auto-format-selection pipeline. Targets the bandwidth/quality gaps with Cloudinary documented in the internal CDN optimization tracker.
Issue tracker: DIT Migration Issues — Consolidated Log of Image Format Problems and Proposed Solutions
Encoder upgrades — straightforward Sharp option enablement:
7d72a5c): enable mozjpeg for JPEG output (~10–15% smaller files)c5bac5f): PNG output —palette: true,compressionLevel: 9,adaptiveFiltering: true6b9cfb4): map genericqualityto Sharp GIF options (colours,effort,interFrameMaxError,interPaletteMaxError)52bfe6d): cap animated WebP at Q50 (motion masking) — closes the +36% gap vs Cloudinary on the 304 GIF→WebP rowsFormat-selection safety guards — block visually-broken or size-broken auto-conversions:
812b18c): block transparent PNG/WebP → JPEG auto-selection (drops alpha → broken icons). Two layers: pre-fetch guard inauto-optimizerplus post-fetch override inimage-processorafter Sharp metadata.2159153): block lossy → lossless re-encode (JPEG/WebP → PNG/TIFF), which only inflates size — average +164% on JPEG sweep, +307% onvnd.ms-photo. DIT-side half; the CF Function piece (restrictingdit-acceptnormalization to modern formats) is owned by DevOps/AWS and remains open.Guards compose: an alpha-suspect, lossy source (WebP) falls through both filters; the lossy fallback search honors the alpha guard's no-jpeg rule.
Test plan
auto-optimizer.test.ts— 31/31 passing (12 new across alpha + lossy guards, including their composition for WebP sources)edit-applicator.test.ts— 34/34 passing (4 new for the animated WebP quality cap; existing PNG/GIF/JPEG/HEIF cases preserved)image-processor.service.test.ts— 22/22 passing (3 new for the post-fetch alpha override on transparent PNG with varyingdit-accept)metricType: imageTransformationand newoperation: alpha_jpeg_overridelog lines for unexpected volume🤖 Generated with Claude Code