Added `sd_imageFrameCount` convenient API for UIAinmatedImage/NSBitmapImageRep

Fix one issue when input UIAnimatedImage contains only 1 image
This commit is contained in:
DreamPiggy 2021-05-25 15:38:04 +08:00
parent fd326e3d51
commit 4ae33983e0
6 changed files with 56 additions and 11 deletions

View File

@ -135,7 +135,6 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
avgDuration = 0.1; // if it's a animated image but no duration, set it to default 100ms (this do not have that 10ms limit like GIF or WebP to allow custom coder provide the limit)
}
__block NSUInteger index = 0;
__block NSUInteger repeatCount = 1;
__block UIImage *previousImage = animatedImages.firstObject;
[animatedImages enumerateObjectsUsingBlock:^(UIImage * _Nonnull image, NSUInteger idx, BOOL * _Nonnull stop) {
@ -149,15 +148,12 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
SDImageFrame *frame = [SDImageFrame frameWithImage:previousImage duration:avgDuration * repeatCount];
[frames addObject:frame];
repeatCount = 1;
index++;
}
previousImage = image;
// last one
if (idx == frameCount - 1) {
SDImageFrame *frame = [SDImageFrame frameWithImage:previousImage duration:avgDuration * repeatCount];
[frames addObject:frame];
}
}];
// last one
SDImageFrame *frame = [SDImageFrame frameWithImage:previousImage duration:avgDuration * repeatCount];
[frames addObject:frame];
#else

View File

@ -20,12 +20,23 @@
* For animated image format, 0 means infinite looping.
* Note that because of the limitations of categories this property can get out of sync if you create another instance with CGImage or other methods.
* AppKit:
* NSImage currently only support animated via GIF imageRep unlike UIImage.
* The getter of this property will get the loop count from GIF imageRep
* The setter of this property will set the loop count from GIF imageRep
* NSImage currently only support animated via `NSBitmapImageRep`(GIF) or `SDAnimatedImageRep`(APNG/GIF/WebP) unlike UIImage.
* The getter of this property will get the loop count from animated imageRep
* The setter of this property will set the loop count from animated imageRep
*/
@property (nonatomic, assign) NSUInteger sd_imageLoopCount;
/**
* UIKit:
* Returns the `images`'s count by unapply the patch for the different frame durations. Which matches the real visible frame count when displaying on UIImageView.
* See more in `SDImageCoderHelper.animatedImageWithFrames`.
* Returns 1 for static image.
* AppKit:
* Returns the underlaying `NSBitmapImageRep` or `SDAnimatedImageRep` frame count.
* Returns 1 for static image.
*/
@property (nonatomic, assign, readonly) NSUInteger sd_imageFrameCount;
/**
* UIKit:
* Check the `images` array property.

View File

@ -29,6 +29,27 @@
objc_setAssociatedObject(self, @selector(sd_imageLoopCount), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSUInteger)sd_imageFrameCount {
NSArray<UIImage *> *animatedImages = self.images;
if (!animatedImages || animatedImages.count <= 1) {
return 1;
}
__block NSUInteger frameCount = 1;
__block UIImage *previousImage = animatedImages.firstObject;
[animatedImages enumerateObjectsUsingBlock:^(UIImage * _Nonnull image, NSUInteger idx, BOOL * _Nonnull stop) {
// ignore first
if (idx == 0) {
return;
}
if (![image isEqual:previousImage]) {
frameCount++;
}
previousImage = image;
}];
return frameCount;
}
- (BOOL)sd_isAnimated {
return (self.images != nil);
}
@ -87,6 +108,19 @@
}
}
- (NSUInteger)sd_imageFrameCount {
NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);
NSImageRep *imageRep = [self bestRepresentationForRect:imageRect context:nil hints:nil];
NSBitmapImageRep *bitmapImageRep;
if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
bitmapImageRep = (NSBitmapImageRep *)imageRep;
}
if (bitmapImageRep) {
return [[bitmapImageRep valueForProperty:NSImageFrameCount] unsignedIntegerValue];
}
return 1;
}
- (BOOL)sd_isAnimated {
BOOL isAnimated = NO;
NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);

View File

@ -770,7 +770,10 @@ static BOOL _isCalled;
[[SDImageCodersManager sharedManager] addCoder:[SDImageAWebPCoder sharedCoder]];
UIImage *image = [UIImage sd_imageWithData:[NSData dataWithContentsOfFile:[self testMemotyCostImagePath]]];
NSUInteger cost = [image sd_memoryCost];
#if SD_UIKIT
expect(image.images.count).equal(5333);
#endif
expect(image.sd_imageFrameCount).equal(16);
expect(image.scale).equal(1);
expect(cost).equal(16 * image.size.width * image.size.height * 4);
[[SDImageCodersManager sharedManager] removeCoder:[SDImageAWebPCoder sharedCoder]];

View File

@ -59,6 +59,7 @@
image = [UIImage sd_imageWithGIFData:data];
expect(image).notTo.beNil();
expect(image.sd_isAnimated).beTruthy();
expect(image.sd_imageFrameCount).equal(5);
}
#pragma mark - Helper

View File

@ -435,7 +435,7 @@ withLocalImageURL:(NSURL *)imageUrl
}
}
- (NSArray *)thumbnailImagesFromImageSource:(CGImageSourceRef)source API_AVAILABLE(ios(11.0), tvos(11.0), macos(13.0)) {
- (NSArray *)thumbnailImagesFromImageSource:(CGImageSourceRef)source API_AVAILABLE(ios(11.0), tvos(11.0), macos(10.13)) {
NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(source, nil);
NSDictionary *fileProperties = properties[(__bridge NSString *)kCGImagePropertyFileContentsDictionary];
NSArray *imagesProperties = fileProperties[(__bridge NSString *)kCGImagePropertyImages];