Skip to content

Commit 41dc9be

Browse files
committed
Added animationTransformer on SDAnimatedImageView
This allows the animated image to apply post-transform Currently we don't change the old `SDWebImageTransformAnimatedImage` behavior. This feature is opt-in
1 parent 0b10fcb commit 41dc9be

File tree

5 files changed

+124
-2
lines changed

5 files changed

+124
-2
lines changed

Examples/SDWebImage Demo/DetailViewController.m

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
@interface DetailViewController ()
1313

1414
@property (strong, nonatomic) IBOutlet SDAnimatedImageView *imageView;
15+
@property (assign) BOOL tintApplied;
1516

1617
@end
1718

@@ -37,6 +38,39 @@ - (void)viewDidLoad {
3738
style:UIBarButtonItemStylePlain
3839
target:self
3940
action:@selector(toggleAnimation:)];
41+
// Add a secret title click action to apply tint color
42+
UIButton *button = [UIButton buttonWithType:UIButtonTypeSystem];
43+
[button addTarget:self
44+
action:@selector(toggleTint:)
45+
forControlEvents:UIControlEventTouchUpInside];
46+
[button setTitle:@"Tint" forState:UIControlStateNormal];
47+
self.navigationItem.titleView = button;
48+
}
49+
50+
- (void)toggleTint:(UIResponder *)sender {
51+
// tint for non-opaque animation
52+
if (!self.imageView.isAnimating) {
53+
return;
54+
}
55+
SDAnimatedImage *animatedImage = (SDAnimatedImage *)self.imageView.image;
56+
if (animatedImage.sd_imageFormat == SDImageFormatGIF) {
57+
// GIF is opaque
58+
return;
59+
}
60+
BOOL containsAlpha = [SDImageCoderHelper CGImageContainsAlpha:animatedImage.CGImage];
61+
if (!containsAlpha) {
62+
return;
63+
}
64+
if (self.tintApplied) {
65+
self.imageView.animationTransformer = nil;
66+
} else {
67+
self.imageView.animationTransformer = [SDImageTintTransformer transformerWithColor:UIColor.blackColor];
68+
}
69+
self.tintApplied = !self.tintApplied;
70+
// refresh
71+
UIImage *image = self.imageView.image;
72+
self.imageView.image = nil;
73+
self.imageView.image = image;
4074
}
4175

4276
- (void)toggleAnimation:(UIResponder *)sender {

SDWebImage/Core/SDAnimatedImageView.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
#import "SDAnimatedImage.h"
1414
#import "SDAnimatedImagePlayer.h"
15+
#import "SDImageTransformer.h"
1516

1617
/**
1718
A drop-in replacement for UIImageView/NSImageView, you can use this for animated image rendering.
@@ -28,6 +29,19 @@ NS_SWIFT_UI_ACTOR
2829
*/
2930
@property (nonatomic, strong, readonly, nullable) SDAnimatedImagePlayer *player;
3031

32+
/**
33+
The transformer for each decoded animated image frame.
34+
We supports post-transform on animated image frame from version 5.20.
35+
When you configure the transformer on `SDAnimatedImageView` and animation is playing, the `transformedImageWithImage:forKey:` will be called just after the frame is decoded. (The `key` arg is always empty for backward-compatible)
36+
37+
Example to tint the animated image with alpha channel into template:
38+
* @code
39+
imageView.animationTransformer = [SDImageTintTransformer transformerWithColor:UIColor.blackColor];
40+
* @endcode
41+
@note The `transformerKey` property is used to ensure the buffer cache available. So make sure it's correct value match the actual logic on transformer.
42+
*/
43+
@property (nonatomic, strong, nullable) id<SDImageTransformer> animationTransformer;
44+
3145
/**
3246
Current display frame image. This value is KVO Compliance.
3347
*/

SDWebImage/Core/SDAnimatedImageView.m

Lines changed: 72 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,70 @@
1515
#import "SDInternalMacros.h"
1616
#import "objc/runtime.h"
1717

18+
// A wrapper to implements the transformer on animated image, like tint color
19+
@interface SDAnimatedImageFrameProvider : NSObject <SDAnimatedImageProvider>
20+
@property (nonatomic, strong) id<SDAnimatedImageProvider> provider;
21+
@property (nonatomic, strong) id<SDImageTransformer> transformer;
22+
@end
23+
24+
@implementation SDAnimatedImageFrameProvider
25+
26+
- (instancetype)initWithProvider:(id<SDAnimatedImageProvider>)provider transformer:(id<SDImageTransformer>)transformer {
27+
self = [super init];
28+
if (self) {
29+
_provider = provider;
30+
_transformer = transformer;
31+
}
32+
return self;
33+
}
34+
35+
- (NSUInteger)hash {
36+
NSUInteger prime = 31;
37+
NSUInteger result = 1;
38+
NSUInteger providerHash = self.provider.hash;
39+
NSUInteger transformerHash = self.transformer.transformerKey.hash;
40+
result = prime * result + providerHash;
41+
result = prime * result + transformerHash;
42+
return result;
43+
}
44+
45+
- (BOOL)isEqual:(id)object {
46+
if (nil == object) {
47+
return NO;
48+
}
49+
if (self == object) {
50+
return YES;
51+
}
52+
if (![object isKindOfClass:[self class]]) {
53+
return NO;
54+
}
55+
return self.provider == [object provider]
56+
&& [self.transformer.transformerKey isEqualToString:[object transformer].transformerKey];
57+
}
58+
59+
- (NSData *)animatedImageData {
60+
return self.provider.animatedImageData;
61+
}
62+
63+
- (NSUInteger)animatedImageFrameCount {
64+
return self.provider.animatedImageFrameCount;
65+
}
66+
67+
- (NSUInteger)animatedImageLoopCount {
68+
return self.provider.animatedImageLoopCount;
69+
}
70+
71+
- (NSTimeInterval)animatedImageDurationAtIndex:(NSUInteger)index {
72+
return [self.provider animatedImageDurationAtIndex:index];
73+
}
74+
75+
- (UIImage *)animatedImageFrameAtIndex:(NSUInteger)index {
76+
UIImage *frame = [self.provider animatedImageFrameAtIndex:index];
77+
return [self.transformer transformedImageWithImage:frame forKey:@""];
78+
}
79+
80+
@end
81+
1882
@interface UIImageView () <CALayerDelegate>
1983
@end
2084

@@ -139,7 +203,14 @@ - (void)setImage:(UIImage *)image
139203
provider = (id<SDAnimatedImage>)image;
140204
}
141205
// Create animated player
142-
self.player = [SDAnimatedImagePlayer playerWithProvider:provider];
206+
if (self.animationTransformer) {
207+
// Check if post-transform animation available
208+
provider = [[SDAnimatedImageFrameProvider alloc] initWithProvider:provider transformer:self.animationTransformer];
209+
self.player = [SDAnimatedImagePlayer playerWithProvider:provider];
210+
} else {
211+
// Normal animation without post-transform
212+
self.player = [SDAnimatedImagePlayer playerWithProvider:provider];
213+
}
143214
} else {
144215
// Update Frame Count
145216
self.player.totalFrameCount = [(id<SDAnimatedImage>)image animatedImageFrameCount];

SDWebImage/Core/SDImageTransformer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ FOUNDATION_EXPORT NSString * _Nullable SDThumbnailedKeyForKey(NSString * _Nullab
3131
/**
3232
A transformer protocol to transform the image load from cache or from download.
3333
You can provide transformer to cache and manager (Through the `transformer` property or context option `SDWebImageContextImageTransformer`).
34+
From v5.20, the transformer class also can be used on animated image frame post-transform logic, see `SDAnimatedImageView`.
3435
3536
@note The transform process is called from a global queue in order to not to block the main queue.
3637
*/
@@ -50,6 +51,7 @@ FOUNDATION_EXPORT NSString * _Nullable SDThumbnailedKeyForKey(NSString * _Nullab
5051
@required
5152
/**
5253
For each transformer, it must contains its cache key to used to store the image cache or query from the cache. This key will be appened after the original cache key generated by URL or from user.
54+
Which means, the cache should match what your transformer logic do. The same `input image` + `transformer key`, should always generate the same `output image`.
5355
5456
@return The cache key to appended after the original cache key. Should not be nil.
5557
*/

SDWebImage/Private/SDImageFramePool.m

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ + (NSMapTable *)providerFramePoolMap {
3131
static NSMapTable *providerFramePoolMap;
3232
static dispatch_once_t onceToken;
3333
dispatch_once(&onceToken, ^{
34-
providerFramePoolMap = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality valueOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality];
34+
// Key use `hash` && `isEqual:`
35+
providerFramePoolMap = [NSMapTable mapTableWithKeyOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPersonality valueOptions:NSPointerFunctionsStrongMemory | NSPointerFunctionsObjectPointerPersonality];
3536
});
3637
return providerFramePoolMap;
3738
}

0 commit comments

Comments
 (0)