diff --git a/SDWebImageWebPCoder.podspec b/SDWebImageWebPCoder.podspec
index a548240..f4214cd 100644
--- a/SDWebImageWebPCoder.podspec
+++ b/SDWebImageWebPCoder.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'SDWebImageWebPCoder'
- s.version = '0.14.5'
+ s.version = '0.15.0'
s.summary = 'WebP decoder/encoder for SDWebImage coder plugin.'
s.description = <<-DESC
diff --git a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m
index 7f79cb6..8e51a9e 100644
--- a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m
+++ b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m
@@ -174,7 +174,7 @@ @implementation SDImageWebPCoder {
NSUInteger _currentBlendIndex;
BOOL _preserveAspectRatio;
CGSize _thumbnailSize;
- BOOL _limitBytes;
+ NSUInteger _limitBytes;
}
- (void)dealloc {
@@ -818,7 +818,7 @@ - (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef
maxFileSize:(NSUInteger)maxFileSize
options:(nullable SDImageCoderOptions *)options
{
- NSData *webpData;
+ NSData *webpData = nil;
if (!imageRef) {
return nil;
}
@@ -950,16 +950,55 @@ - (nullable NSData *)sd_encodedWebpDataWithImage:(nullable CGImageRef)imageRef
result = WebPEncode(&config, &picture);
WebPPictureFree(&picture);
free(dest.data);
-
+
if (result) {
- // success
- webpData = [NSData dataWithBytes:writer.mem length:writer.size];
+ // Add ICC profile if present
+ // See: https://developers.google.com/speed/webp/docs/riff_container#color_profile
+ // Skip ICC profile when maxFileSize is set, as meeting the size limit takes priority
+ CFDataRef iccData = NULL;
+ if (colorSpace && maxFileSize == 0) {
+ if (@available(iOS 10, tvOS 10, macOS 10.12, watchOS 3, *)) {
+ iccData = CGColorSpaceCopyICCData(colorSpace);
+ }
+ }
+
+ if (iccData && CFDataGetLength(iccData) > 0) {
+ // Use WebPMux to add ICCP chunk
+ // This automatically converts Simple Format to Extended Format (VP8X)
+ WebPMux *mux = WebPMuxNew();
+ if (mux) {
+ WebPData webp_input = {
+ .bytes = writer.mem,
+ .size = writer.size
+ };
+
+ if (WebPMuxSetImage(mux, &webp_input, 0) == WEBP_MUX_OK) {
+ WebPData icc_chunk = {
+ .bytes = CFDataGetBytePtr(iccData),
+ .size = CFDataGetLength(iccData)
+ };
+
+ if (WebPMuxSetChunk(mux, "ICCP", &icc_chunk, 0) == WEBP_MUX_OK) {
+ WebPData output;
+ if (WebPMuxAssemble(mux, &output) == WEBP_MUX_OK) {
+ webpData = [NSData dataWithBytes:output.bytes length:output.size];
+ WebPDataClear(&output);
+ }
+ }
+ }
+ WebPMuxDelete(mux);
+ }
+ CFRelease(iccData);
+ }
+
+ if (!webpData) {
+ webpData = [NSData dataWithBytes:writer.mem length:writer.size];
+ }
} else {
- // failed
webpData = nil;
}
WebPMemoryWriterClear(&writer);
-
+
return webpData;
}
diff --git a/SDWebImageWebPCoder/Module/Info.plist b/SDWebImageWebPCoder/Module/Info.plist
index 03f556b..76aa9bb 100644
--- a/SDWebImageWebPCoder/Module/Info.plist
+++ b/SDWebImageWebPCoder/Module/Info.plist
@@ -15,9 +15,9 @@
CFBundlePackageType
FMWK
CFBundleShortVersionString
- 0.14.5
+ 0.15.0
CFBundleVersion
- 0.14.5
+ 0.15.0
NSPrincipalClass
diff --git a/Tests/Images/TestDisplayP3.png b/Tests/Images/TestDisplayP3.png
new file mode 100644
index 0000000..55bad7f
Binary files /dev/null and b/Tests/Images/TestDisplayP3.png differ
diff --git a/Tests/SDWebImageWebPCoderTests.m b/Tests/SDWebImageWebPCoderTests.m
index 89a89a8..f89bcfa 100644
--- a/Tests/SDWebImageWebPCoderTests.m
+++ b/Tests/SDWebImageWebPCoderTests.m
@@ -381,7 +381,7 @@ - (void)testWebPEncodingWithICCProfile {
NSString *jpegPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TestColorspaceBefore" ofType:@"jpeg"];
NSData *jpegData = [NSData dataWithContentsOfFile:jpegPath];
UIImage *jpegImage = [[UIImage alloc] initWithData:jpegData];
-
+
NSData *webpData = [[SDImageWebPCoder sharedCoder] encodedDataWithImage:jpegImage format:SDImageFormatWebP options:nil];
// Re-decode to pick color
UIImage *webpImage = [[SDImageWebPCoder sharedCoder] decodedImageWithData:webpData options:nil];
@@ -404,6 +404,74 @@ - (void)testWebPEncodingWithICCProfile {
#endif
}
+- (void)testWebPEncodingEmbedICCProfile {
+ // Test that ICC profile is embedded in WebP
+ NSString *jpegPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TestColorspaceBefore" ofType:@"jpeg"];
+ NSData *jpegData = [NSData dataWithContentsOfFile:jpegPath];
+ UIImage *jpegImage = [[UIImage alloc] initWithData:jpegData];
+ expect(jpegImage).notTo.beNil();
+
+ NSData *webpData = [[SDImageWebPCoder sharedCoder] encodedDataWithImage:jpegImage format:SDImageFormatWebP options:nil];
+ expect(webpData).notTo.beNil();
+
+ // Check for ICCP chunk
+ WebPData webp_data;
+ WebPDataInit(&webp_data);
+ webp_data.bytes = webpData.bytes;
+ webp_data.size = webpData.length;
+
+ WebPDemuxer *demuxer = WebPDemux(&webp_data);
+ expect(demuxer).notTo.beNil();
+
+ uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
+ expect(flags & ICCP_FLAG).notTo.equal(0);
+
+ WebPChunkIterator chunk_iter;
+ int result = WebPDemuxGetChunk(demuxer, "ICCP", 1, &chunk_iter);
+ expect(result).notTo.equal(0);
+ expect(chunk_iter.chunk.size).to.beGreaterThan(0);
+
+ WebPDemuxReleaseChunkIterator(&chunk_iter);
+ WebPDemuxDelete(demuxer);
+}
+
+- (void)testWebPEncodingDisplayP3 {
+ // Test Display P3 wide color gamut encoding with ICC profile
+ NSString *pngPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"TestDisplayP3" ofType:@"png"];
+ if (!pngPath) {
+ return;
+ }
+
+ NSData *pngData = [NSData dataWithContentsOfFile:pngPath];
+ UIImage *p3Image = [[UIImage alloc] initWithData:pngData];
+ expect(p3Image).notTo.beNil();
+
+ NSData *webpData = [[SDImageWebPCoder sharedCoder] encodedDataWithImage:p3Image
+ format:SDImageFormatWebP
+ options:@{SDImageCoderEncodeCompressionQuality: @0.95}];
+ expect(webpData).notTo.beNil();
+
+ // Check for ICCP chunk
+ WebPData webp_data;
+ WebPDataInit(&webp_data);
+ webp_data.bytes = webpData.bytes;
+ webp_data.size = webpData.length;
+
+ WebPDemuxer *demuxer = WebPDemux(&webp_data);
+ expect(demuxer).notTo.beNil();
+
+ uint32_t flags = WebPDemuxGetI(demuxer, WEBP_FF_FORMAT_FLAGS);
+ expect(flags & ICCP_FLAG).notTo.equal(0);
+
+ WebPChunkIterator chunk_iter;
+ int result = WebPDemuxGetChunk(demuxer, "ICCP", 1, &chunk_iter);
+ expect(result).notTo.equal(0);
+ expect(chunk_iter.chunk.size).to.beGreaterThan(0);
+
+ WebPDemuxReleaseChunkIterator(&chunk_iter);
+ WebPDemuxDelete(demuxer);
+}
+
@end
@implementation SDWebImageWebPCoderTests (Helpers)
diff --git a/Tests/SDWebImageWebPCoderTests.xcodeproj/project.pbxproj b/Tests/SDWebImageWebPCoderTests.xcodeproj/project.pbxproj
index 5626066..e09834c 100644
--- a/Tests/SDWebImageWebPCoderTests.xcodeproj/project.pbxproj
+++ b/Tests/SDWebImageWebPCoderTests.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
archiveVersion = 1;
classes = {
};
- objectVersion = 51;
+ objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
@@ -23,6 +23,8 @@
808C918E213FD131004B0F7C /* SDWebImageWebPCoderTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 808C918D213FD131004B0F7C /* SDWebImageWebPCoderTests.m */; };
808C919C213FD2B2004B0F7C /* TestImageStatic.webp in Resources */ = {isa = PBXBuildFile; fileRef = 808C919A213FD2B2004B0F7C /* TestImageStatic.webp */; };
808C919D213FD2B2004B0F7C /* TestImageAnimated.webp in Resources */ = {isa = PBXBuildFile; fileRef = 808C919B213FD2B2004B0F7C /* TestImageAnimated.webp */; };
+ B359E6032EB3ACBE0064933A /* TestDisplayP3.png in Resources */ = {isa = PBXBuildFile; fileRef = B359E6022EB3ACBE0064933A /* TestDisplayP3.png */; };
+ B359E6042EB3ACBE0064933A /* TestDisplayP3.png in Resources */ = {isa = PBXBuildFile; fileRef = B359E6022EB3ACBE0064933A /* TestDisplayP3.png */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -40,6 +42,7 @@
808C918F213FD131004B0F7C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
808C919A213FD2B2004B0F7C /* TestImageStatic.webp */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestImageStatic.webp; sourceTree = ""; };
808C919B213FD2B2004B0F7C /* TestImageAnimated.webp */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestImageAnimated.webp; sourceTree = ""; };
+ B359E6022EB3ACBE0064933A /* TestDisplayP3.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TestDisplayP3.png; sourceTree = ""; };
D92E6791BF088D1A101E670E /* Pods-SDWebImageWebPCoderTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageWebPCoderTests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-SDWebImageWebPCoderTests/Pods-SDWebImageWebPCoderTests.release.xcconfig"; sourceTree = ""; };
F121CFAEBEFA209D335C5C6D /* Pods-SDWebImageWebPCoderTests-macOS.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageWebPCoderTests-macOS.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-SDWebImageWebPCoderTests-macOS/Pods-SDWebImageWebPCoderTests-macOS.debug.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
@@ -107,6 +110,7 @@
808C9199213FD2B2004B0F7C /* Images */ = {
isa = PBXGroup;
children = (
+ B359E6022EB3ACBE0064933A /* TestDisplayP3.png */,
32B4C7912AFB959E003A4BC7 /* TestColorspaceBefore.jpeg */,
326420302A5D53E300EE3E46 /* TestColorspaceStatic.webp */,
325E268D25C82BE1000B807B /* TestImageGrayscale.jpg */,
@@ -211,6 +215,7 @@
32B4C78C2AFB954C003A4BC7 /* TestColorspaceStatic.webp in Resources */,
32B4C78D2AFB954C003A4BC7 /* TestImageBlendAnimated.webp in Resources */,
32B4C7932AFB959E003A4BC7 /* TestColorspaceBefore.jpeg in Resources */,
+ B359E6042EB3ACBE0064933A /* TestDisplayP3.png in Resources */,
32B4C78B2AFB954C003A4BC7 /* TestImageGrayscale.jpg in Resources */,
32B4C78F2AFB954C003A4BC7 /* TestImageStatic.webp in Resources */,
);
@@ -224,6 +229,7 @@
808C919D213FD2B2004B0F7C /* TestImageAnimated.webp in Resources */,
808C919C213FD2B2004B0F7C /* TestImageStatic.webp in Resources */,
32B4C7922AFB959E003A4BC7 /* TestColorspaceBefore.jpeg in Resources */,
+ B359E6032EB3ACBE0064933A /* TestDisplayP3.png in Resources */,
326420312A5D53E300EE3E46 /* TestColorspaceStatic.webp in Resources */,
325E268E25C82BE1000B807B /* TestImageGrayscale.jpg in Resources */,
);
@@ -262,10 +268,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-SDWebImageWebPCoderTests/Pods-SDWebImageWebPCoderTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
+ inputPaths = (
+ );
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-SDWebImageWebPCoderTests/Pods-SDWebImageWebPCoderTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
+ outputPaths = (
+ );
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageWebPCoderTests/Pods-SDWebImageWebPCoderTests-frameworks.sh\"\n";
@@ -297,10 +307,14 @@
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-SDWebImageWebPCoderTests-macOS/Pods-SDWebImageWebPCoderTests-macOS-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
+ inputPaths = (
+ );
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-SDWebImageWebPCoderTests-macOS/Pods-SDWebImageWebPCoderTests-macOS-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
+ outputPaths = (
+ );
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SDWebImageWebPCoderTests-macOS/Pods-SDWebImageWebPCoderTests-macOS-frameworks.sh\"\n";