diff --git a/SDWebImageWebPCoder.xcodeproj/project.pbxproj b/SDWebImageWebPCoder.xcodeproj/project.pbxproj index 3149cd9..0a8d532 100644 --- a/SDWebImageWebPCoder.xcodeproj/project.pbxproj +++ b/SDWebImageWebPCoder.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 0EF5B6264833B7BC20894578 /* Pods_SDWebImageWebPCoderTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 46F21AD7D1692EBAC4D0FF33 /* Pods_SDWebImageWebPCoderTests.framework */; }; + 3219F3B2228B0453003822A6 /* TestImageBlendAnimated.webp in Resources */ = {isa = PBXBuildFile; fileRef = 3219F3B1228B0453003822A6 /* TestImageBlendAnimated.webp */; }; 806E77B32136A2E900A316D2 /* UIImage+WebP.m in Sources */ = {isa = PBXBuildFile; fileRef = 806E77AA2136A2E900A316D2 /* UIImage+WebP.m */; }; 806E77B42136A2E900A316D2 /* SDImageWebPCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 806E77AB2136A2E900A316D2 /* SDImageWebPCoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 806E77B62136A2E900A316D2 /* UIImage+WebP.h in Headers */ = {isa = PBXBuildFile; fileRef = 806E77AD2136A2E900A316D2 /* UIImage+WebP.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -44,6 +45,7 @@ /* Begin PBXFileReference section */ 28D8AA3D3015E075692FD3E3 /* Pods-SDWebImageWebPCoderTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SDWebImageWebPCoderTests.debug.xcconfig"; path = "SDWebImageWebPCoderTests/Pods/Target Support Files/Pods-SDWebImageWebPCoderTests/Pods-SDWebImageWebPCoderTests.debug.xcconfig"; sourceTree = ""; }; 3217BE7B220547EB003D0310 /* SDWebImageWebPCoder.modulemap */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = "sourcecode.module-map"; path = SDWebImageWebPCoder.modulemap; sourceTree = ""; }; + 3219F3B1228B0453003822A6 /* TestImageBlendAnimated.webp */ = {isa = PBXFileReference; lastKnownFileType = file; path = TestImageBlendAnimated.webp; sourceTree = ""; }; 46F21AD7D1692EBAC4D0FF33 /* Pods_SDWebImageWebPCoderTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SDWebImageWebPCoderTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 806E779D2136A1C000A316D2 /* SDWebImageWebPCoder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SDWebImageWebPCoder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 806E77AA2136A2E900A316D2 /* UIImage+WebP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+WebP.m"; sourceTree = ""; }; @@ -205,6 +207,7 @@ children = ( 808C919A213FD2B2004B0F7C /* TestImageStatic.webp */, 808C919B213FD2B2004B0F7C /* TestImageAnimated.webp */, + 3219F3B1228B0453003822A6 /* TestImageBlendAnimated.webp */, ); path = Images; sourceTree = ""; @@ -453,6 +456,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3219F3B2228B0453003822A6 /* TestImageBlendAnimated.webp in Resources */, 808C919D213FD2B2004B0F7C /* TestImageAnimated.webp in Resources */, 808C919C213FD2B2004B0F7C /* TestImageStatic.webp in Resources */, ); @@ -620,8 +624,14 @@ "DEBUG=1", "$(inherited)", ); - "GCC_PREPROCESSOR_DEFINITIONS[sdk=watchos*]" = "WEBP_USE_INTRINSICS=1 $(inherited)"; - "GCC_PREPROCESSOR_DEFINITIONS[sdk=watchsimulator*]" = "WEBP_USE_INTRINSICS=1 $(inherited)"; + "GCC_PREPROCESSOR_DEFINITIONS[sdk=watchos*]" = ( + "WEBP_USE_INTRINSICS=1", + "$(inherited)", + ); + "GCC_PREPROCESSOR_DEFINITIONS[sdk=watchsimulator*]" = ( + "WEBP_USE_INTRINSICS=1", + "$(inherited)", + ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; @@ -689,8 +699,14 @@ GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_PREPROCESSOR_DEFINITIONS = "$(inherited)"; - "GCC_PREPROCESSOR_DEFINITIONS[sdk=watchos*]" = "WEBP_USE_INTRINSICS=1 $(inherited)"; - "GCC_PREPROCESSOR_DEFINITIONS[sdk=watchsimulator*]" = "WEBP_USE_INTRINSICS=1 $(inherited)"; + "GCC_PREPROCESSOR_DEFINITIONS[sdk=watchos*]" = ( + "WEBP_USE_INTRINSICS=1", + "$(inherited)", + ); + "GCC_PREPROCESSOR_DEFINITIONS[sdk=watchsimulator*]" = ( + "WEBP_USE_INTRINSICS=1", + "$(inherited)", + ); GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; GCC_WARN_UNDECLARED_SELECTOR = YES; diff --git a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m index b1fb2fc..7a11965 100644 --- a/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m +++ b/SDWebImageWebPCoder/Classes/SDImageWebPCoder.m @@ -46,8 +46,8 @@ @property (nonatomic, assign) NSUInteger offsetY; // Frame origin.y in canvas (left-bottom based) @property (nonatomic, assign) BOOL hasAlpha; // Whether frame contains alpha @property (nonatomic, assign) BOOL isFullSize; // Whether frame size is equal to canvas size -@property (nonatomic, assign) WebPMuxAnimBlend blend; // Frame dispose method -@property (nonatomic, assign) WebPMuxAnimDispose dispose; // Frame blend operation +@property (nonatomic, assign) BOOL shouldBlend; // Frame dispose method +@property (nonatomic, assign) BOOL shouldDispose; // Frame blend operation @property (nonatomic, assign) NSUInteger blendFromIndex; // The nearest previous frame index which blend mode is WEBP_MUX_BLEND @end @@ -719,8 +719,8 @@ static void FreeImageData(void *info, const void *data, size_t size) { frame.width = iter.width; frame.height = iter.height; frame.hasAlpha = iter.has_alpha; - frame.dispose = iter.dispose_method; - frame.blend = iter.blend_method; + frame.shouldDispose = iter.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND; + frame.shouldBlend = iter.blend_method == WEBP_MUX_BLEND; frame.offsetX = iter.x_offset; frame.offsetY = canvasHeight - iter.y_offset - iter.height; @@ -728,11 +728,11 @@ static void FreeImageData(void *info, const void *data, size_t size) { BOOL offsetIsZero = (iter.x_offset == 0 && iter.y_offset == 0); frame.isFullSize = (sizeEqualsToCanvas && offsetIsZero); - if ((!frame.blend || !frame.hasAlpha) && frame.isFullSize) { + if ((!frame.shouldBlend || !frame.hasAlpha) && frame.isFullSize) { lastBlendIndex = iterIndex; frame.blendFromIndex = iterIndex; } else { - if (frame.dispose && frame.isFullSize) { + if (frame.shouldDispose && frame.isFullSize) { frame.blendFromIndex = lastBlendIndex; lastBlendIndex = iterIndex + 1; } else { diff --git a/SDWebImageWebPCoderTests/Images/TestImageBlendAnimated.webp b/SDWebImageWebPCoderTests/Images/TestImageBlendAnimated.webp new file mode 100644 index 0000000..7a1d3fe Binary files /dev/null and b/SDWebImageWebPCoderTests/Images/TestImageBlendAnimated.webp differ diff --git a/SDWebImageWebPCoderTests/SDWebImageWebPCoderTests.m b/SDWebImageWebPCoderTests/SDWebImageWebPCoderTests.m index 8a71857..c20e24e 100644 --- a/SDWebImageWebPCoderTests/SDWebImageWebPCoderTests.m +++ b/SDWebImageWebPCoderTests/SDWebImageWebPCoderTests.m @@ -28,6 +28,11 @@ const int64_t kAsyncTestTimeout = 5; @property (nonatomic, assign) BOOL isProgressive; @end +@interface SDWebPCoderFrame : NSObject +@property (nonatomic, assign) NSUInteger index; // Frame index (zero based) +@property (nonatomic, assign) NSUInteger blendFromIndex; // The nearest previous frame index which blend mode is WEBP_MUX_BLEND +@end + @implementation SDWebImageWebPCoderTests + (void)setUp { @@ -126,6 +131,47 @@ const int64_t kAsyncTestTimeout = 5; [self waitForExpectationsWithTimeout:kAsyncTestTimeout handler:nil]; } +- (void)test33AnimatedImageBlendMethod { + // Test the optimization for blend and disposal method works without problem + NSURL *animatedWebPURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"TestImageBlendAnimated" withExtension:@"webp"]; + NSData *data = [NSData dataWithContentsOfURL:animatedWebPURL]; + SDImageWebPCoder *coder = [[SDImageWebPCoder alloc] initWithAnimatedImageData:data options:nil]; + XCTAssertNotNil(coder); + /** + This WebP image frames info is below: + Canvas size: 400 x 400 + Features present: animation transparency + Background color : 0xFF000000 Loop Count : 0 + Number of frames: 12 + No.: width height alpha x_offset y_offset duration dispose blend image_size compression + 1: 400 400 no 0 0 70 none no 5178 lossless + 2: 400 400 yes 0 0 70 none yes 1386 lossless + 3: 400 400 yes 0 0 70 none yes 1472 lossless + 4: 400 394 yes 0 6 70 none yes 3212 lossless + 5: 371 394 yes 0 6 70 none yes 1888 lossless + 6: 394 382 yes 6 6 70 none yes 3346 lossless + 7: 400 388 yes 0 0 70 none yes 3786 lossless + 8: 394 383 yes 0 0 70 none yes 1858 lossless + 9: 394 394 yes 0 6 70 none yes 3794 lossless + 10: 372 394 yes 22 6 70 none yes 3458 lossless + 11: 400 400 no 0 0 70 none no 5270 lossless + 12: 320 382 yes 0 6 70 none yes 2506 lossless + */ + NSArray *frames = [coder valueForKey:@"_frames"]; + XCTAssertEqual(frames.count, 12); + for (SDWebPCoderFrame *frame in frames) { + switch (frame.index) { + // frame: 11 blend == no, means clear the canvas + case 10: + case 11: + XCTAssertEqual(frame.blendFromIndex, 10); + break; + default: + XCTAssertEqual(frame.blendFromIndex, 0); + break; + } + } +} @end