mirror of
https://github.com/SDWebImage/SDWebImage.git
synced 2026-04-07 19:27:40 +00:00
Added encodeWithFrames API for animation encoding in custom coder, better for usage
The exist API need wrap the frames into a new image and extract, consume RAM and CPU
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "SDWebImageCompat.h"
|
||||
#import "NSData+ImageContentType.h"
|
||||
#import "SDImageFrame.h"
|
||||
|
||||
typedef NSString * SDImageCoderOption NS_STRING_ENUM;
|
||||
typedef NSDictionary<SDImageCoderOption, id> SDImageCoderOptions;
|
||||
@@ -171,7 +172,8 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderWebImageContext
|
||||
|
||||
/**
|
||||
Encode the image to image data.
|
||||
@note This protocol may supports encode animated image frames. You can use `+[SDImageCoderHelper framesFromAnimatedImage:]` to assemble an animated image with frames.
|
||||
@note This protocol may supports encode animated image frames. You can use `+[SDImageCoderHelper framesFromAnimatedImage:]` to assemble an animated image with frames. But this consume time is not always reversible. In 5.15.0, we introduce `encodedDataWithFrames` API for better animated image encoding. Use that instead.
|
||||
@note Which means, this just forward to `encodedDataWithFrames([SDImageFrame(image: image, duration: 0], image.sd_imageLoopCount))`
|
||||
|
||||
@param image The image to be encoded
|
||||
@param format The image format to encode, you should note `SDImageFormatUndefined` format is also possible
|
||||
@@ -182,6 +184,21 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderWebImageContext
|
||||
format:(SDImageFormat)format
|
||||
options:(nullable SDImageCoderOptions *)options;
|
||||
|
||||
#pragma mark - Animated Encoding
|
||||
@optional
|
||||
/**
|
||||
Encode the animated image frames to image data.
|
||||
|
||||
@param frames The animated image frames to be encoded, should be at least 1 element, or it will fallback to static image encode.
|
||||
@param loopCount The final animated image loop count. 0 means infinity loop. This config ignore each frame's `sd_imageLoopCount`
|
||||
@param format The image format to encode, you should note `SDImageFormatUndefined` format is also possible
|
||||
@param options A dictionary containing any encoding options. Pass @{SDImageCoderEncodeCompressionQuality: @(1)} to specify compression quality.
|
||||
@return The encoded image data
|
||||
*/
|
||||
- (nullable NSData *)encodedDataWithFrames:(nonnull NSArray<SDImageFrame *>*)frames
|
||||
loopCount:(NSUInteger)loopCount
|
||||
format:(SDImageFormat)format
|
||||
options:(nullable SDImageCoderOptions *)options;
|
||||
@end
|
||||
|
||||
#pragma mark - Progressive Coder
|
||||
|
||||
@@ -127,4 +127,19 @@
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSData *)encodedDataWithFrames:(NSArray<SDImageFrame *> *)frames loopCount:(NSUInteger)loopCount format:(SDImageFormat)format options:(SDImageCoderOptions *)options {
|
||||
if (!frames || frames.count < 1) {
|
||||
return nil;
|
||||
}
|
||||
NSArray<id<SDImageCoder>> *coders = self.coders;
|
||||
for (id<SDImageCoder> coder in coders.reverseObjectEnumerator) {
|
||||
if ([coder canEncodeToFormat:format]) {
|
||||
if ([coder respondsToSelector:@selector(encodedDataWithFrames:loopCount:format:options:)]) {
|
||||
return [coder encodedDataWithFrames:frames loopCount:loopCount format:format options:options];
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -24,6 +24,11 @@
|
||||
*/
|
||||
@property (nonatomic, readonly, assign) NSTimeInterval duration;
|
||||
|
||||
/// Create a frame instance with specify image and duration
|
||||
/// @param image current frame's image
|
||||
/// @param duration current frame's duration
|
||||
- (nonnull instancetype)initWithImage:(nonnull UIImage *)image duration:(NSTimeInterval)duration;
|
||||
|
||||
/**
|
||||
Create a frame instance with specify image and duration
|
||||
|
||||
@@ -31,6 +36,9 @@
|
||||
@param duration current frame's duration
|
||||
@return frame instance
|
||||
*/
|
||||
+ (instancetype _Nonnull)frameWithImage:(UIImage * _Nonnull)image duration:(NSTimeInterval)duration;
|
||||
+ (nonnull instancetype)frameWithImage:(nonnull UIImage *)image duration:(NSTimeInterval)duration;
|
||||
|
||||
- (nonnull instancetype)init NS_UNAVAILABLE;
|
||||
+ (nonnull instancetype)new NS_UNAVAILABLE;
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,11 +17,17 @@
|
||||
|
||||
@implementation SDImageFrame
|
||||
|
||||
- (instancetype)initWithImage:(UIImage *)image duration:(NSTimeInterval)duration {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_image = image;
|
||||
_duration = duration;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
+ (instancetype)frameWithImage:(UIImage *)image duration:(NSTimeInterval)duration {
|
||||
SDImageFrame *frame = [[SDImageFrame alloc] init];
|
||||
frame.image = image;
|
||||
frame.duration = duration;
|
||||
|
||||
SDImageFrame *frame = [[SDImageFrame alloc] initWithImage:image duration:duration];
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
@@ -552,6 +552,23 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
|
||||
}
|
||||
|
||||
- (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format options:(nullable SDImageCoderOptions *)options {
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
if (format != self.class.imageFormat) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSArray<SDImageFrame *> *frames = [SDImageCoderHelper framesFromAnimatedImage:image];
|
||||
if (!frames || frames.count == 0) {
|
||||
SDImageFrame *frame = [SDImageFrame frameWithImage:image duration:0];
|
||||
frames = @[frame];
|
||||
}
|
||||
return [self encodedDataWithFrames:frames loopCount:image.sd_imageLoopCount format:format options:options];
|
||||
}
|
||||
|
||||
- (NSData *)encodedDataWithFrames:(NSArray<SDImageFrame *> *)frames loopCount:(NSUInteger)loopCount format:(SDImageFormat)format options:(SDImageCoderOptions *)options {
|
||||
UIImage *image = frames.firstObject.image; // Primary image
|
||||
if (!image) {
|
||||
return nil;
|
||||
}
|
||||
@@ -561,13 +578,8 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
if (format != self.class.imageFormat) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
NSMutableData *imageData = [NSMutableData data];
|
||||
CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format];
|
||||
NSArray<SDImageFrame *> *frames = [SDImageCoderHelper framesFromAnimatedImage:image];
|
||||
|
||||
// Create an image destination. Animated Image does not support EXIF image orientation TODO
|
||||
// The `CGImageDestinationCreateWithData` will log a warning when count is 0, use 1 instead.
|
||||
@@ -630,12 +642,11 @@ static CGImageRef __nullable SDCGImageCreateCopy(CGImageRef cg_nullable image) {
|
||||
properties[(__bridge NSString *)kCGImageDestinationEmbedThumbnail] = @(embedThumbnail);
|
||||
|
||||
BOOL encodeFirstFrame = [options[SDImageCoderEncodeFirstFrameOnly] boolValue];
|
||||
if (encodeFirstFrame || frames.count == 0) {
|
||||
if (encodeFirstFrame || frames.count <= 1) {
|
||||
// for static single images
|
||||
CGImageDestinationAddImage(imageDestination, imageRef, (__bridge CFDictionaryRef)properties);
|
||||
} else {
|
||||
// for animated images
|
||||
NSUInteger loopCount = image.sd_imageLoopCount;
|
||||
NSDictionary *containerProperties = @{
|
||||
self.class.dictionaryProperty: @{self.class.loopCountProperty : @(loopCount)}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user