Merge pull request #2988 from dreampiggy/encoding_options_embed_thumbnail
Feature: Encoding options supports embed thumbnail (works for JPEG/HEIF/AVIF)
This commit is contained in:
commit
ca970aeace
|
@ -79,6 +79,14 @@ FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeMaxPixelSi
|
||||||
*/
|
*/
|
||||||
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeMaxFileSize;
|
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeMaxFileSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
A Boolean value indicating the encoding format should contains a thumbnail image into the output data. Only some of image format (like JPEG/HEIF/AVIF) support this behavior. The embed thumbnail will be used during next time thumbnail decoding (provided `.thumbnailPixelSize`), which is faster than full image thumbnail decoding. (NSNumber)
|
||||||
|
Defaults to NO, which does not embed any thumbnail.
|
||||||
|
@note The thumbnail image's pixel size is not defined, the encoder can choose the proper pixel size which is suitable for encoding quality.
|
||||||
|
@note works for `SDImageCoder`
|
||||||
|
*/
|
||||||
|
FOUNDATION_EXPORT SDImageCoderOption _Nonnull const SDImageCoderEncodeEmbedThumbnail;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A SDWebImageContext object which hold the original context options from top-level API. (SDWebImageContext)
|
A SDWebImageContext object which hold the original context options from top-level API. (SDWebImageContext)
|
||||||
This option is ignored for all built-in coders and take no effect.
|
This option is ignored for all built-in coders and take no effect.
|
||||||
|
|
|
@ -18,5 +18,6 @@ SDImageCoderOption const SDImageCoderEncodeCompressionQuality = @"encodeCompress
|
||||||
SDImageCoderOption const SDImageCoderEncodeBackgroundColor = @"encodeBackgroundColor";
|
SDImageCoderOption const SDImageCoderEncodeBackgroundColor = @"encodeBackgroundColor";
|
||||||
SDImageCoderOption const SDImageCoderEncodeMaxPixelSize = @"encodeMaxPixelSize";
|
SDImageCoderOption const SDImageCoderEncodeMaxPixelSize = @"encodeMaxPixelSize";
|
||||||
SDImageCoderOption const SDImageCoderEncodeMaxFileSize = @"encodeMaxFileSize";
|
SDImageCoderOption const SDImageCoderEncodeMaxFileSize = @"encodeMaxFileSize";
|
||||||
|
SDImageCoderOption const SDImageCoderEncodeEmbedThumbnail = @"encodeEmbedThumbnail";
|
||||||
|
|
||||||
SDImageCoderOption const SDImageCoderWebImageContext = @"webImageContext";
|
SDImageCoderOption const SDImageCoderWebImageContext = @"webImageContext";
|
||||||
|
|
|
@ -484,6 +484,11 @@ static NSString * kSDCGImageDestinationRequestedFileSize = @"kCGImageDestination
|
||||||
// Remove the quality if we have file size limit
|
// Remove the quality if we have file size limit
|
||||||
properties[(__bridge NSString *)kCGImageDestinationLossyCompressionQuality] = nil;
|
properties[(__bridge NSString *)kCGImageDestinationLossyCompressionQuality] = nil;
|
||||||
}
|
}
|
||||||
|
BOOL embedThumbnail = NO;
|
||||||
|
if (options[SDImageCoderEncodeEmbedThumbnail]) {
|
||||||
|
embedThumbnail = [options[SDImageCoderEncodeEmbedThumbnail] boolValue];
|
||||||
|
}
|
||||||
|
properties[(__bridge NSString *)kCGImageDestinationEmbedThumbnail] = @(embedThumbnail);
|
||||||
|
|
||||||
BOOL encodeFirstFrame = [options[SDImageCoderEncodeFirstFrameOnly] boolValue];
|
BOOL encodeFirstFrame = [options[SDImageCoderEncodeFirstFrameOnly] boolValue];
|
||||||
if (encodeFirstFrame || frames.count == 0) {
|
if (encodeFirstFrame || frames.count == 0) {
|
||||||
|
|
|
@ -294,6 +294,11 @@ static NSString * kSDCGImageDestinationRequestedFileSize = @"kCGImageDestination
|
||||||
// Remove the quality if we have file size limit
|
// Remove the quality if we have file size limit
|
||||||
properties[(__bridge NSString *)kCGImageDestinationLossyCompressionQuality] = nil;
|
properties[(__bridge NSString *)kCGImageDestinationLossyCompressionQuality] = nil;
|
||||||
}
|
}
|
||||||
|
BOOL embedThumbnail = NO;
|
||||||
|
if (options[SDImageCoderEncodeEmbedThumbnail]) {
|
||||||
|
embedThumbnail = [options[SDImageCoderEncodeEmbedThumbnail] boolValue];
|
||||||
|
}
|
||||||
|
properties[(__bridge NSString *)kCGImageDestinationEmbedThumbnail] = @(embedThumbnail);
|
||||||
|
|
||||||
// Add your image to the destination.
|
// Add your image to the destination.
|
||||||
CGImageDestinationAddImage(imageDestination, imageRef, (__bridge CFDictionaryRef)properties);
|
CGImageDestinationAddImage(imageDestination, imageRef, (__bridge CFDictionaryRef)properties);
|
||||||
|
|
|
@ -231,6 +231,52 @@
|
||||||
isVectorImage:YES];
|
isVectorImage:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)test18ThatImageIOAnimatedCoderAbstractClass {
|
||||||
|
SDImageIOAnimatedCoder *coder = [[SDImageIOAnimatedCoder alloc] init];
|
||||||
|
@try {
|
||||||
|
[coder canEncodeToFormat:SDImageFormatPNG];
|
||||||
|
XCTFail("Should throw exception");
|
||||||
|
} @catch (NSException *exception) {
|
||||||
|
expect(exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)test19ThatEmbedThumbnailHEICWorks {
|
||||||
|
if (@available(iOS 11, macOS 10.13, *)) {
|
||||||
|
// The input HEIC does not contains any embed thumbnail
|
||||||
|
NSURL *heicURL = [[NSBundle bundleForClass:[self class]] URLForResource:@"TestImage" withExtension:@"heic"];
|
||||||
|
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)heicURL, nil);
|
||||||
|
expect(source).notTo.beNil();
|
||||||
|
NSArray *thumbnailImages = [self thumbnailImagesFromImageSource:source];
|
||||||
|
expect(thumbnailImages.count).equal(0);
|
||||||
|
|
||||||
|
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, 0, nil);
|
||||||
|
#if SD_UIKIT
|
||||||
|
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:1 orientation: UIImageOrientationUp];
|
||||||
|
#else
|
||||||
|
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:1 orientation:kCGImagePropertyOrientationUp];
|
||||||
|
#endif
|
||||||
|
CGImageRelease(imageRef);
|
||||||
|
// Encode with embed thumbnail
|
||||||
|
NSData *encodedData = [SDImageIOCoder.sharedCoder encodedDataWithImage:image format:SDImageFormatHEIC options:@{SDImageCoderEncodeEmbedThumbnail : @(YES)}];
|
||||||
|
|
||||||
|
// The new HEIC contains one embed thumbnail
|
||||||
|
CGImageSourceRef source2 = CGImageSourceCreateWithData((__bridge CFDataRef)encodedData, nil);
|
||||||
|
expect(source2).notTo.beNil();
|
||||||
|
NSArray *thumbnailImages2 = [self thumbnailImagesFromImageSource:source2];
|
||||||
|
expect(thumbnailImages2.count).equal(1);
|
||||||
|
|
||||||
|
// Currently ImageIO has no control to custom embed thumbnail pixel size, just check the behavior :)
|
||||||
|
NSDictionary *thumbnailImageInfo = thumbnailImages2.firstObject;
|
||||||
|
NSUInteger thumbnailWidth = [thumbnailImageInfo[(__bridge NSString *)kCGImagePropertyWidth] unsignedIntegerValue];
|
||||||
|
NSUInteger thumbnailHeight = [thumbnailImageInfo[(__bridge NSString *)kCGImagePropertyHeight] unsignedIntegerValue];
|
||||||
|
expect(thumbnailWidth).equal(320);
|
||||||
|
expect(thumbnailHeight).equal(212);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark - Utils
|
||||||
|
|
||||||
- (void)verifyCoder:(id<SDImageCoder>)coder
|
- (void)verifyCoder:(id<SDImageCoder>)coder
|
||||||
withLocalImageURL:(NSURL *)imageUrl
|
withLocalImageURL:(NSURL *)imageUrl
|
||||||
supportsEncoding:(BOOL)supportsEncoding
|
supportsEncoding:(BOOL)supportsEncoding
|
||||||
|
@ -353,14 +399,14 @@ withLocalImageURL:(NSURL *)imageUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)test16ThatImageIOAnimatedCoderAbstractClass {
|
- (NSArray *)thumbnailImagesFromImageSource:(CGImageSourceRef)source API_AVAILABLE(ios(11.0), tvos(11.0), macos(13.0)) {
|
||||||
SDImageIOAnimatedCoder *coder = [[SDImageIOAnimatedCoder alloc] init];
|
NSDictionary *properties = (__bridge_transfer NSDictionary *)CGImageSourceCopyProperties(source, nil);
|
||||||
@try {
|
NSDictionary *fileProperties = properties[(__bridge NSString *)kCGImagePropertyFileContentsDictionary];
|
||||||
[coder canEncodeToFormat:SDImageFormatPNG];
|
NSArray *imagesProperties = fileProperties[(__bridge NSString *)kCGImagePropertyImages];
|
||||||
XCTFail("Should throw exception");
|
NSDictionary *imageProperties = imagesProperties.firstObject;
|
||||||
} @catch (NSException *exception) {
|
NSArray *thumbnailImages = imageProperties[(__bridge NSString *)kCGImagePropertyThumbnailImages];
|
||||||
expect(exception);
|
|
||||||
}
|
return thumbnailImages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in New Issue