Skip to content

Conversation

@KYHyeon
Copy link
Contributor

@KYHyeon KYHyeon commented Oct 30, 2025

Add ICC Profile Embedding Support for WebP Encoding

Fixes #119

Problem

When encoding images with wide color gamut (Display P3, Adobe RGB) to WebP, the ICC color profile was not embedded in the output file. This caused colors to appear washed out on Android devices and web browsers that assume sRGB.

Before

00000000  52 49 46 46 c2 26 00 00  57 45 42 50 56 50 38 20  |RIFF.&..WEBPVP8 |
                                                      ^^^^
                                                      VP8 (Simple Format)
→ No VP8X, no ICCP chunk
→ Display P3 colors interpreted as sRGB → washed out

After

00000000  52 49 46 46 f4 28 00 00  57 45 42 50 56 50 38 58  |RIFF.(..WEBPVP8X|
                                                      ^^^^
00000010  0a 00 00 00 20 00 00 00  e7 03 00 e7 03 00 49 43  |.... .........IC|
                    ^^                                ^^^^
                    0x20 (ICCP_FLAG)                  ICCP chunk
00000020  43 50 18 02 00 00 ...                             |CP........

→ VP8X Extended Format with ICCP_FLAG (0x20)
→ ICCP chunk contains Display P3 profile (536 bytes)
→ Colors display correctly on all platforms

Implementation

Key points:

  1. Uses WebPMux API to embed ICCP chunk following libwebp examples
  2. Performance optimized with copy_data=0 - avoids redundant 10MB+ memory copies
  3. Skips ICC when maxFileSize is set - size limit takes priority over color accuracy
  4. iOS 10+ runtime check for CGColorSpaceCopyICCData (iOS 9 deployment target)

Test Results

sRGB: 3,144 bytes ICC embedded ✅
Display P3: 536 bytes ICC embedded ✅
All tests: 20/20 passed ✅

Verified with hexdump and WebPDemux that ICCP chunk is correctly present.

ScreenShot

Summary by CodeRabbit

  • New Features

    • WebP image encoding now supports embedding ICC color profiles to ensure accurate color representation during encoding and display.
  • Tests

    • Added tests verifying ICC profile embedding functionality in WebP-encoded images.
    • Added test coverage for Display P3 color space during WebP encoding.
  • Chores

    • Updated project configuration files and test asset structure.

- Extract ICC profile from input color space using CGColorSpaceCopyICCData
- Use WebPMux API to embed ICCP chunk in WebP container with copy_data=1
- Automatically convert Simple Format (VP8) to Extended Format (VP8X)
- Add iOS 10+ availability check for CGColorSpaceCopyICCData
- Skip ICC profile when maxFileSize is set (size limit takes priority)
- Add comprehensive test (testWebPEncodingEmbedICCProfile)

This fixes color reproduction issues with wide color gamut images
(Display P3, Adobe RGB, etc.) on platforms that assume sRGB when
ICC profile is missing.

Fixes SDWebImage#119
- Add TestDisplayP3.png test image with Display P3 color space
- Add testWebPEncodingDisplayP3 to verify wide color gamut support
- Verify ICC profile embedding works correctly for Display P3 images (536 bytes)

This test demonstrates that ICC profile embedding preserves wide color
gamut information, preventing washed-out colors on Android and web browsers.
- Use copy_data=0 in WebPMuxSetImage and WebPMuxSetChunk to avoid unnecessary memory copies
- Ensure writer.mem and iccData remain valid until WebPMuxAssemble completes
- Release iccData only after WebPMuxAssemble finishes
- Clear writer memory as the final step

This reduces memory operations by 50% for images with ICC profiles.

Performance improvement:
- Before: Image data copied twice (SetImage copy + Assemble)
- After: Image data copied once (Assemble only)
- Benefit: ~50% reduction in memory operations for 1MB+ images
@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 2025

Walkthrough

This pull request implements ICC color profile embedding in WebP encoding. When encoding images with color spaces like Display P3, the code now extracts the ICC profile and embeds it in the output WebP file using WebPMux API, with fallback to original encoding if muxing fails, accompanied by comprehensive tests.

Changes

Cohort / File(s) Summary
ICC Profile Embedding
SDWebImageWebPCoder/Classes/SDImageWebPCoder.m
Initialize webpData to nil; extract ICC profile data from color space when available and maxFileSize == 0; create WebP mux, insert ICCP chunk, and assemble output; free ICC data and mux resources; fall back to original writer output if muxing unavailable.
Test Coverage
Tests/SDWebImageWebPCoderTests.m
Add testWebPEncodingEmbedICCProfile to verify ICCP chunk presence in WebP output from JPEG encoding; add testWebPEncodingDisplayP3 to test Display P3 PNG encoding with ICCP verification; minor whitespace adjustment in existing test.
Build Configuration
Tests/SDWebImageWebPCoderTests.xcodeproj/project.pbxproj
Bump objectVersion from 51 to 54; add TestDisplayP3.png resource file with PBXFileReference, PBXBuildFile, and group/build phase entries; update Pods embed framework phases with empty inputPaths and outputPaths.

Sequence Diagram

sequenceDiagram
    participant Encoder as WebP Encoder
    participant ColorSpace as ColorSpace
    participant Writer as Memory Writer
    participant Mux as WebP Mux
    participant Output as Output Data

    Note over Encoder: Old Flow (No ICC)
    Encoder->>Writer: Encode image (WebPEncode)
    Writer-->>Encoder: Encoded data

    Note over Encoder: New Flow (With ICC)
    Encoder->>ColorSpace: Extract ICC profile
    ColorSpace-->>Encoder: ICC data (if available)
    Encoder->>Writer: Encode image (WebPEncode)
    Writer-->>Encoder: Encoded data
    
    alt ICC data exists and maxFileSize == 0
        Encoder->>Mux: Create WebPMux
        Encoder->>Mux: SetImage (creates VP8X)
        Encoder->>Mux: SetChunk ICCP
        Encoder->>Mux: Assemble with ICCP
        Mux-->>Output: WebP with ICC profile
    else Fallback
        Encoder-->>Output: Original encoded data
    end
    
    rect rgb(200, 220, 255)
    Note over Encoder,Output: New: ICC profile embedded in ICCP chunk
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Main implementation file: ICC profile extraction and WebP mux integration introduce moderate complexity around error handling, resource management (ICC data and mux cleanup), and the conditional embedding logic. Requires verification of correct WebPMux API usage and edge cases (nil color space, profile unavailability).
  • Test file: Two new test methods follow similar patterns (encode → demux → verify ICCP chunk), reducing per-test review effort but requiring validation that test assets and assertions are correct.
  • Project configuration: Xcode project file changes are lower-risk (version bump, resource addition) but should verify TestDisplayP3.png is properly wired into build phases and no duplicate entries cause issues.

Areas requiring extra attention:

  • Verify ICC profile is extracted only when maxFileSize == 0 (inline with the conditional logic)
  • Confirm proper cleanup of WebPMux and ICC data to prevent memory leaks
  • Validate fallback behavior when muxing fails (returns original encoder output, not nil)
  • Ensure new test assertions correctly verify ICCP chunk presence and size

Poem

🐰 Colors bright in Display P3,
Now embedded for all to see!
ICC profiles take their place,
In WebP's extended space. ✨
No more shifting hue by hue,
Wide gamuts preserved—vibrant and true! 🎨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Add ICC Profile Embedding Support for WebP Encoding" is concise, specific, and directly summarizes the main change implemented in the PR. It clearly communicates that the primary objective is adding ICC profile embedding functionality to the WebP encoder, which aligns with the code changes in SDImageWebPCoder.m that add ICCP chunk insertion via WebPMux API. The title is neither vague nor generic, and it accurately reflects the core functionality being introduced.
Linked Issues Check ✅ Passed The PR implementation addresses all primary objectives from Issue #119. The code changes to SDImageWebPCoder.m implement ICC profile extraction using CGColorSpaceCopyICCData [#119], create a WebPMux to embed an ICCP chunk [#119], set the ICCP_FLAG in the VP8X chunk for both RGB and RGBA formats [#119], and respect the maxFileSize constraint by skipping ICC embedding when a file-size limit is set [#119]. The test additions validate ICCP chunk presence and correctness in the resulting WebP files for both sRGB and Display P3 color spaces [#119], confirming the feature works as specified. The implementation maintains existing vImage pixel conversion behavior while adding the metadata preservation layer [#119].
Out of Scope Changes Check ✅ Passed The pull request changes are all related to the ICC profile embedding feature. The core implementation in SDImageWebPCoder.m adds ICC extraction and WebPMux-based ICCP embedding as required by Issue #119. The test methods validate the ICC profile embedding functionality, and the project file updates add the necessary TestDisplayP3.png resource asset for the new tests. While the project file also includes an objectVersion bump and some Pods configuration adjustments, these are incidental changes typically caused by Xcode regenerating project metadata and are necessary for proper project structure when adding new test resources.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f534cfe and aa92e07.

⛔ Files ignored due to path filters (1)
  • Tests/Images/TestDisplayP3.png is excluded by !**/*.png
📒 Files selected for processing (3)
  • SDWebImageWebPCoder/Classes/SDImageWebPCoder.m (2 hunks)
  • Tests/SDWebImageWebPCoderTests.m (2 hunks)
  • Tests/SDWebImageWebPCoderTests.xcodeproj/project.pbxproj (8 hunks)
🔇 Additional comments (4)
Tests/SDWebImageWebPCoderTests.xcodeproj/project.pbxproj (1)

26-233: DisplayP3 asset wiring looks good

The new TestDisplayP3.png resource is correctly referenced for both iOS and macOS test targets, so the new fixture will be available everywhere it’s needed.

Tests/SDWebImageWebPCoderTests.m (1)

407-473: Appreciate the ICCP demux coverage

Great to see the tests verifying both sRGB and Display P3 flows all the way down to the ICCP chunk; that gives strong signal that muxing the profile works end-to-end.

SDWebImageWebPCoder/Classes/SDImageWebPCoder.m (2)

821-821: Good defensive init

Explicitly zeroing webpData up front avoids ever reading stale stack contents if the later mux path bails early.


955-997: ICC mux integration LGTM

The availability/maxFileSize gating plus the copy-data=0 mux path covers the profile embedding without extra copies, and the fallback to writer.mem keeps the legacy path intact—nice balance of perf and safety.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dreampiggy
Copy link
Contributor

dreampiggy commented Nov 3, 2025

LGTM. Will release a version with this PR


Released in https://github.com/SDWebImage/SDWebImageWebPCoder/releases/tag/0.15.0

@dreampiggy dreampiggy merged commit d15b424 into SDWebImage:master Nov 3, 2025
1 of 3 checks passed
@lucasfischer
Copy link

Thank you so much for taking a stab at fixing this issue.

The new 0.15.0 release doesn't compile for me with the following errors:

/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:79:27: error: unknown type name 'SharpYuvTransferFunctionType'
   79 |                           SharpYuvTransferFunctionType transfer_type) {
      |                           ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:91:33: error: unknown type name 'SharpYuvTransferFunctionType'
   91 |                                 SharpYuvTransferFunctionType transfer_type) {
      |                                 ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:108:26: error: unknown type name 'SharpYuvTransferFunctionType'
  108 |                          SharpYuvTransferFunctionType transfer_type) {
      |                          ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:310:29: error: unknown type name 'SharpYuvTransferFunctionType'
  310 |                             SharpYuvTransferFunctionType transfer_type) {
      |                             ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:487:3: error: use of undeclared identifier 'SharpYuvOptions'
  487 |   SharpYuvOptions options;
      |   ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:488:3: error: use of undeclared identifier 'options'
  488 |   options.yuv_matrix = yuv_matrix;
      |   ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:489:3: error: use of undeclared identifier 'options'
  489 |   options.transfer_type = kSharpYuvTransferFunctionSrgb;
      |   ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:489:27: error: use of undeclared identifier 'kSharpYuvTransferFunctionSrgb'
  489 |   options.transfer_type = kSharpYuvTransferFunctionSrgb;
      |                           ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:490:10: error: call to undeclared function 'SharpYuvConvertWithOptions'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]
  490 |   return SharpYuvConvertWithOptions(
      |          ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:492:72: error: use of undeclared identifier 'options'
  492 |       u_ptr, u_stride, v_ptr, v_stride, yuv_bit_depth, width, height, &options);
      |                                                                        ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:496:33: error: unknown type name 'SharpYuvOptions'
  496 |                                 SharpYuvOptions* options, int version) {
      |                                 ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:506:28: error: use of undeclared identifier 'kSharpYuvTransferFunctionSrgb'
  506 |   options->transfer_type = kSharpYuvTransferFunctionSrgb;
      |                            ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:515:50: error: unknown type name 'SharpYuvOptions'
  515 |                                int height, const SharpYuvOptions* options) {
      |                                                  ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:517:3: error: use of undeclared identifier 'SharpYuvTransferFunctionType'
  517 |   SharpYuvTransferFunctionType transfer_type = options->transfer_type;
      |   ^
/Users/lucas/Library/Developer/Xcode/DerivedData/Project-abkdgniwyfxzcxbgrfotiaonvaki/SourcePackages/checkouts/libwebp-Xcode/libwebp/sharpyuv/sharpyuv.c:572:38: error: use of undeclared identifier 'transfer_type'
  572 |       width, height, &scaled_matrix, transfer_type);
      |                                      ^
15 errors generated.

Help would be appreciated.

@dreampiggy
Copy link
Contributor

@lucasfischer @KYHyeon

Maybe you need to upgrade libwebp's min dependency. Means we use API which requires some high version libwebp.

You can use cocoapods or SPM package update to do so. I could not rember which of these API was introduced in libwebp. Maybe @KYHyeon can have a check (so that we can edit the Package.swift/podspec to limit a minimum libwebp version to avoid user who still use the old version)

@dreampiggy
Copy link
Contributor

Currently you can just use the latest 1.5.0 libwebp, see https://github.com/SDWebImage/libwebp-Xcode/releases

@dreampiggy
Copy link
Contributor

dreampiggy commented Nov 3, 2025

Read again about sharpyuv: https://github.com/SDWebImage/libwebp-Xcode?tab=readme-ov-file#about-sharpyuv

Maybe at least v1.3.0

@KYHyeon
Copy link
Contributor Author

KYHyeon commented Nov 3, 2025

It seems that SharpYuvOptions and SharpYuvTransferFunctionType were introduced in libwebp 1.3.0.

https://chromium.googlesource.com/webm/libwebp/%2B/25d94f473b10882b8bee9288d00539001b692042%5E%21

@lucasfischer
Copy link

Thank you for the quick help.

My Package.resolved says I am using https://github.com/SDWebImage/libwebp-Xcode.git 1.5.0, but I am still getting this error. Locally and in Xcode Cloud. Am I holding this wrong?

@lucasfischer
Copy link

It didn't work with libwebp-Xcode 1.5.0.

I now added a new entry to my Package.swift in which I explicitly pin libwebp-Xcode to 1.3.2, which seems to be the only version that properly works.

 .package(url: "https://github.com/SDWebImage/libwebp-Xcode.git", exact: "1.3.2"),

Previously I didn't have to add the libwebp-Xcode package do my dependencies because SDWebImageWebPCoder was depending on it with a compatible version.

IMO this should get fixed in this project's Package.json file. Should I submit a PR for this?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ICC Profile is not embedded when encoding to WebP

3 participants