-
Notifications
You must be signed in to change notification settings - Fork 19
Remove all previous hack code. Create a SDWebImageFLCoder to decode just FLAnimatedImage for GIF. #2
Remove all previous hack code. Create a SDWebImageFLCoder to decode just FLAnimatedImage for GIF. #2
Changes from all commits
8621a7d
28426d1
10e01c8
32dae6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* This file is part of the SDWebImage package. | ||
* (c) Olivier Poitrey <rs@dailymotion.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
#import <SDWebImage/SDWebImage.h> | ||
|
||
// A coder which decode the GIF image, into `FLAnimatedImage` representation and bind the associated object. See `UIImage+SDWebImageFLPlugin` for more detailed information. | ||
// When you want to use `FLAnimatedImageView` to load image, be sure to add this coder before `SDImageGIFCoder`, to ensure this coder been processed before `SDImageGIFCoder` | ||
|
||
@interface SDWebImageFLCoder : NSObject <SDImageCoder> | ||
|
||
@property (nonatomic, class, readonly, nonnull) SDWebImageFLCoder *sharedCoder; | ||
|
||
@end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
* This file is part of the SDWebImage package. | ||
* (c) Olivier Poitrey <rs@dailymotion.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
#import "SDWebImageFLCoder.h" | ||
#import "FLAnimatedImageView+WebCache.h" | ||
|
||
@implementation SDWebImageFLCoder | ||
|
||
+ (SDWebImageFLCoder *)sharedCoder { | ||
static SDWebImageFLCoder *coder; | ||
static dispatch_once_t onceToken; | ||
dispatch_once(&onceToken, ^{ | ||
coder = [[SDWebImageFLCoder alloc] init]; | ||
}); | ||
return coder; | ||
} | ||
|
||
- (BOOL)canDecodeFromData:(NSData *)data { | ||
return ([NSData sd_imageFormatForImageData:data] == SDImageFormatGIF); | ||
} | ||
|
||
- (UIImage *)decodedImageWithData:(NSData *)data options:(SDImageCoderOptions *)options { | ||
SDWebImageContext *context = options[SDImageCoderWebImageContext]; | ||
NSString *operationKey = context[SDWebImageContextSetImageOperationKey]; | ||
Class imageViewClass = NSClassFromString(operationKey); | ||
|
||
// Check if image request come from `FLAnimatedImageView` | ||
if (imageViewClass && [imageViewClass isSubclassOfClass:FLAnimatedImageView.class]) { | ||
// Parse args | ||
BOOL predrawingEnabled = YES; | ||
if (context[SDWebImageContextPredrawingEnabled]) { | ||
predrawingEnabled = [context[SDWebImageContextPredrawingEnabled] boolValue]; | ||
} | ||
NSUInteger optimalFrameCacheSize = 0; | ||
if (context[SDWebImageContextOptimalFrameCacheSize]) { | ||
optimalFrameCacheSize = [context[SDWebImageContextOptimalFrameCacheSize] unsignedIntegerValue]; | ||
} | ||
// Create FLAnimatedImage | ||
FLAnimatedImage *animatedImage = [[FLAnimatedImage alloc] initWithAnimatedGIFData:data optimalFrameCacheSize:optimalFrameCacheSize predrawingEnabled:predrawingEnabled]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the meaning ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't support subclass.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops, this line don't needs to consider subclass. Just this line. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zhongwuzw Added one commit to support subclass. Anyway, most of users does not use subclass of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it's true, normally, maybe only big companies would do some encapsulation, they maybe would use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I'm sensitive to these things. The |
||
if (!animatedImage) { | ||
return nil; | ||
} | ||
|
||
return [UIImage sd_imageWithFLAnimatedImage:animatedImage]; | ||
} else { | ||
UIImage *image; | ||
NSArray<id<SDImageCoder>> *coders = [SDImageCodersManager sharedManager].coders; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dreampiggy TBO, I don't much like handle like this, it's a little bit non-decouple very well, my opinion is if There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zhongwuzw The image coder protocol, it's not designed for The For example, during image decoding, you are trying to parse the information from binary data for target image format (EXIF, etc), but it failed. These cases, should not call another coder to process the samething. Because it's a internal error happend for that coder, but not image foramt. However, the So, I still propose to keep these two protocol seperate. The There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dreampiggy 👍 Yeah, I know your opinion. Maybe most of the user would not use multiple coder for each image format. e.x. we have My opinion for At last. It's also good to keep the current implementation. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dreampiggy I suddenly realized that maybe There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zhongwuzw No. ImageIO is just a Core Foundation wrapper for the C++ framework IIOReadPlugin && IIOWritePlugin. It actually use the same design from SDWebImage's coder. They still need to enumerate each plugins. IIOReadPlugin use a array ( The good point it's that Apple can hard-coded all the plugins into single files because they are an entire framework, but we can't. We open the posibility to let other third-party developers to provide a coder plugin. So this is why we can't do the same thing to hold a central check of UTType and choose which coder should been used. If you're interested in the detailed implementation, You can use Hopper to deassemble ImageIO.framework. Here is just some snippet from my previous experience. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For example, they register all plugins during int __ZN17IIO_ReaderHandler15buildPluginListEv() {
r13 = rdi;
if (*_gIIODebugFlagsInitializer != 0xffffffffffffffff) {
dispatch_once(_gIIODebugFlagsInitializer, ^ {/* block implemented at ____ZN17IIO_ReaderHandler15buildPluginListEv_block_invoke */ } });
}
r14 = 0x0;
var_38 = r14;
var_34 = r14;
if (dyld_process_is_restricted() == 0x0) {
r14 = getenv("RAWCAMERA_BUNDLE_PATH");
}
if ((*(int8_t *)0x539f42 & 0x4) == 0x0) {
if (r14 != 0x0) {
r15 = dlopen(r14, 0x1);
if (r15 == 0x0) {
r15 = dlopen("/System/Library/CoreServices/RawCamera.bundle/RawCamera", 0x1);
if (r15 != 0x0) {
rcx = dlsym(r15, "GetRawPluginsInfo");
if (rcx != 0x0) {
rax = (rcx)(&var_38, &var_34);
}
else {
rax = 0x0;
}
var_40 = rax;
*_gReadMakerNoteProps = dlsym(r15, "ReadMakerNoteProps");
}
else {
var_40 = 0x0;
}
}
else {
rcx = dlsym(r15, "GetRawPluginsInfo");
if (rcx != 0x0) {
rax = (rcx)(&var_38, &var_34);
}
else {
rax = 0x0;
}
var_40 = rax;
*_gReadMakerNoteProps = dlsym(r15, "ReadMakerNoteProps");
}
}
else {
r15 = dlopen("/System/Library/CoreServices/RawCamera.bundle/RawCamera", 0x1);
if (r15 != 0x0) {
rcx = dlsym(r15, "GetRawPluginsInfo");
if (rcx != 0x0) {
rax = (rcx)(&var_38, &var_34);
}
else {
rax = 0x0;
}
var_40 = rax;
*_gReadMakerNoteProps = dlsym(r15, "ReadMakerNoteProps");
}
else {
var_40 = 0x0;
}
}
}
else {
var_40 = 0x0;
}
if (*IIO_ReaderHandler::UseAppleJPEG()::appleJPEGCheck != 0xffffffffffffffff) {
dispatch_once(IIO_ReaderHandler::UseAppleJPEG()::appleJPEGCheck, ^ {/* block implemented at ____ZN17IIO_ReaderHandler12UseAppleJPEGEv_block_invoke */ } });
}
if (*(int8_t *)IIO_ReaderHandler::UseAppleJPEG()::gUseAppleJPEGPlugin != 0x0) {
rax = CreateReader_AppleJPEG();
}
else {
rax = CreateReader_JPEG();
}
var_30 = rax;
if (rax != 0x0) {
rcx = *(r13 + 0x18);
if (rcx != *(r13 + 0x20)) {
*rcx = rax;
*(r13 + 0x18) = *(r13 + 0x18) + 0x8;
}
else {
void std::__1::vector<IIO_Reader*, std::__1::allocator<IIO_Reader*> >::__push_back_slow_path<IIO_Reader* const>(r13 + 0x10);
}
}
rax = CreateReader_PNG();
var_30 = rax;
if (rax != 0x0) {
rcx = *(r13 + 0x18);
if (rcx != *(r13 + 0x20)) {
*rcx = rax;
*(r13 + 0x18) = *(r13 + 0x18) + 0x8;
}
else {
void std::__1::vector<IIO_Reader*, std::__1::allocator<IIO_Reader*> >::__push_back_slow_path<IIO_Reader* const>(r13 + 0x10);
}
}
rax = CreateReader_GIF();
var_30 = rax;
if (rax != 0x0) {
rcx = *(r13 + 0x18);
if (rcx != *(r13 + 0x20)) {
*rcx = rax;
*(r13 + 0x18) = *(r13 + 0x18) + 0x8;
}
else {
void std::__1::vector<IIO_Reader*, std::__1::allocator<IIO_Reader*> >::__push_back_slow_path<IIO_Reader* const>(r13 + 0x10);
}
}
r12 = var_34;
if (r12 > 0x0) {
r15 = r13 + 0x10;
rbx = 0x0;
do {
rax = operator new(0x60);
rcx = *(var_40 + rbx * 0x8);
xmm0 = intrinsic_movups(xmm0, *(int128_t *)rcx);
rdx = *(rcx + 0x10);
rsi = *(rcx + 0x60);
*(int128_t *)(rax + 0x8) = intrinsic_movups(*(int128_t *)(rax + 0x8), xmm0);
*(rax + 0x18) = rdx;
*(rax + 0x20) = rsi;
*(int8_t *)(rax + 0x28) = 0x1;
*rax = 0x527c18;
xmm0 = intrinsic_movups(xmm0, *(int128_t *)(rcx + 0x18));
*(int128_t *)(rax + 0x30) = intrinsic_movups(*(int128_t *)(rax + 0x30), xmm0);
xmm0 = intrinsic_movups(xmm0, *(int128_t *)(rcx + 0x28));
*(int128_t *)(rax + 0x40) = intrinsic_movups(*(int128_t *)(rax + 0x40), xmm0);
xmm0 = intrinsic_movups(xmm0, *(int128_t *)(rcx + 0x38));
*(int128_t *)(rax + 0x50) = intrinsic_movups(*(int128_t *)(rax + 0x50), xmm0);
var_30 = rax;
rcx = *(r13 + 0x18);
if (rcx != *(r13 + 0x20)) {
*rcx = rax;
*(r13 + 0x18) = *(r13 + 0x18) + 0x8;
}
else {
void std::__1::vector<IIO_Reader*, std::__1::allocator<IIO_Reader*> >::__push_back_slow_path<IIO_Reader* const>(r15);
r12 = var_34;
}
rbx = rbx + 0x1;
} while (rbx < sign_extend_64(r12));
}
rax = CreateReader_TIFF();
var_30 = rax;
if (rax != 0x0) {
rcx = *(r13 + 0x18);
if (rcx != *(r13 + 0x20)) {
*rcx = rax;
*(r13 + 0x18) = *(r13 + 0x18) + 0x8;
}
else {
void std::__1::vector<IIO_Reader*, std::__1::allocator<IIO_Reader*> >::__push_back_slow_path<IIO_Reader* const>(r13 + 0x10);
}
}
rax = CreateReader_JP2();
var_30 = rax;
if (rax != 0x0) {
rcx = *(r13 + 0x18);
if (rcx != *(r13 + 0x20)) {
*rcx = rax;
*(r13 + 0x18) = *(r13 + 0x18) + 0x8;
}
else {
void std::__1::vector<IIO_Reader*, std::__1::allocator<IIO_Reader*> >::__push_back_slow_path<IIO_Reader* const>(r13 + 0x10);
}
}
// many lines of add plugin
// many lines of add plugin
// many lines of add plugin
rax = *(r13 + 0x10);
rcx = *(r13 + 0x18);
if (rax != rcx) {
rdx = *(r13 + 0x8);
do {
rsi = rdx;
rdx = *rax;
rdx = *(rdx + 0x18);
CMP(rsi, rdx);
asm { cmova rdx, rsi };
rax = rax + 0x8;
} while (rcx != rax);
*(r13 + 0x8) = rdx;
}
return rax;
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For example, they do for-loop to check first 16-bytes of File Signature (See above), to determine which plugin should be binded for current CGImageSource during There are also some other check based on the UTType string in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dreampiggy Maybe you are more familiar than me at I use
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @zhongwuzw ...Paste many deassemble code here seems no use, and here on GitHub, maybe it's not a good place to talk about it. You can just use Hopper, search ImageIO.framework with that symbols. Each plugins, in During I don't know why you care about the loop. It just use a And anyway, it's just a personal experience, and deassemble code may not been considered a legal way of understanding the internal behavior for their framework. If you're interested in, just do yourself. Don't need to append more un-related things under this issue. int __ZN17IIO_ReaderHandler15readerForUTTypeEPK10__CFString(void * arg0) {
r15 = rsi;
r12 = arg0;
rbx = *(r12 + 0x10);
if (rbx == *(r12 + 0x18)) goto loc_10cc0e;
loc_10cbe5:
r14 = 0x0;
goto loc_10cbe8;
loc_10cbe8:
if (CFStringCompare(**(*rbx + 0x8), r15, 0x0) == 0x0) goto loc_10cc13;
loc_10cc01:
rbx = rbx + 0x8;
if (rbx != *(r12 + 0x18)) goto loc_10cbe8;
loc_10cc16:
rax = r14;
return rax;
loc_10cc13:
r14 = *rbx;
goto loc_10cc16;
loc_10cc0e:
r14 = 0x0;
goto loc_10cc16;
}
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dreampiggy Yeah, final words 😂 ,thanks for your comments, I looked the disassembling code already, you said system would enumerate all plugins to try to decode each image data. I think it's not , maybe from your recent comment, it's exactly what I want, it has the bind mechanism, just like a Dictionary, one-to-one relationship, TBO, I don't much care the performance of Fine, I already learn too much. Thanks again ! ❤️ |
||
for (id<SDImageCoder> coder in coders.reverseObjectEnumerator) { | ||
if (coder == self) { | ||
continue; | ||
} | ||
if ([coder canDecodeFromData:data]) { | ||
image = [coder decodedImageWithData:data options:options]; | ||
break; | ||
} | ||
} | ||
|
||
return image; | ||
} | ||
} | ||
|
||
- (BOOL)canEncodeToFormat:(SDImageFormat)format { | ||
return NO; | ||
} | ||
|
||
- (NSData *)encodedDataWithImage:(UIImage *)image format:(SDImageFormat)format options:(SDImageCoderOptions *)options { | ||
return nil; | ||
} | ||
|
||
@end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some thoughts after I looked coder mechanism.
The coder mechanism is sufficient currently? Maybe we can add the url parameter and a context option which can specify the coder class per image.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zhongwuzw You can pass anything into context, and get the context back. This is a context pattern (The context arg pass from really top-level API to the bottom). For example, pass the url during
sd_setImageWithURL:
Actually it's a bad design. A image coder, it's designed to solve the image decoding task, but not any business logic related to things other than decoding. However, we leave it as a hackable space, to enable some advanced user use this to do extra logic.
All built-in coders, should not touch
SDWebImageContext
during image decoding, if new option is requsted, updateSDImageCoderOptions
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dreampiggy Emm, I mean bring the context ahead to
- (BOOL)canDecodeFromData:(NSData *)data
, which can determine wether or not can handle image in advance.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@zhongwuzw That too much. Your image coder should only judge the image format and choose to response it or not. Like Image/IO using UTType to determine which underneath plugin (AppleJPEGPlugin ? PNGPlugin ?) should use. So it should be been passed with the
SDImageCoderOptions
arg.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dreampiggy fine, no big difference.