diff --git a/SDWebImage/NSImage+Additions.h b/SDWebImage/NSImage+Additions.h index b7fdcd64..6ae7e4cf 100644 --- a/SDWebImage/NSImage+Additions.h +++ b/SDWebImage/NSImage+Additions.h @@ -27,7 +27,7 @@ The underlying Core Graphics image object. This will actually use `CGImageForPro /** Returns an image object with the scale factor and orientation. The representation is created from the Core Graphics image object. - @note The difference between this and `initWithCGImage:size` is that `initWithCGImage:size` will use `backingScaleFactor` as scale factor if you specify `NSZeroSize` and does not support orientation. + @note The difference between this and `initWithCGImage:size` is that `initWithCGImage:size` will actually create a `NSCGImageSnapshotRep` representation and always use `backingScaleFactor` as scale factor. So we should avoid it and use `NSBitmapImageRep` with `initWithCGImage:` instead. @note The difference between this and UIKit's `UIImage` equivalent method is the way to process orientation. If the provided image orientation is not equal to Up orientation, this method will firstly rotate the CGImage to the correct orientation to work compatible with `NSImageView`. However, UIKit will not actually rotate CGImage and just store it as `imageOrientation` property. @param cgImage A Core Graphics image object diff --git a/SDWebImage/NSImage+Additions.m b/SDWebImage/NSImage+Additions.m index dc68fdca..7cfb99ad 100644 --- a/SDWebImage/NSImage+Additions.m +++ b/SDWebImage/NSImage+Additions.m @@ -41,20 +41,26 @@ } - (instancetype)initWithCGImage:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(CGImagePropertyOrientation)orientation { - if (scale < 1) { - scale = 1; - } - CGFloat pixelWidth = CGImageGetWidth(cgImage); - CGFloat pixelHeight = CGImageGetHeight(cgImage); - NSSize size = NSMakeSize(pixelWidth / scale, pixelHeight / scale); + NSBitmapImageRep *imageRep; if (orientation != kCGImagePropertyOrientationUp) { // AppKit design is different from UIKit. Where CGImage based image rep does not respect to any orientation. Only data based image rep which contains the EXIF metadata can automatically detect orientation. // This should be nonnull, until the memory is exhausted cause `CGBitmapContextCreate` failed. - cgImage = [SDWebImageCoderHelper CGImageCreateDecoded:cgImage orientation:orientation]; - self = [self initWithCGImage:cgImage size:size]; - CGImageRelease(cgImage); + CGImageRef rotatedCGImage = [SDWebImageCoderHelper CGImageCreateDecoded:cgImage orientation:orientation]; + imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage]; + CGImageRelease(rotatedCGImage); } else { - self = [self initWithCGImage:cgImage size:size]; + imageRep = [[NSBitmapImageRep alloc] initWithCGImage:cgImage]; + } + if (scale < 1) { + scale = 1; + } + CGFloat pixelWidth = imageRep.pixelsWide; + CGFloat pixelHeight = imageRep.pixelsHigh; + NSSize size = NSMakeSize(pixelWidth / scale, pixelHeight / scale); + self = [self initWithSize:size]; + if (self) { + imageRep.size = size; + [self addRepresentation:imageRep]; } return self; } diff --git a/SDWebImage/SDAnimatedImage.h b/SDWebImage/SDAnimatedImage.h index 00f2fb7f..bb452669 100644 --- a/SDWebImage/SDAnimatedImage.h +++ b/SDWebImage/SDAnimatedImage.h @@ -69,7 +69,8 @@ @property (nonatomic, assign, readonly) SDImageFormat animatedImageFormat; /** - Current animated image data, you can use this instead of CGImage to create another instance + Current animated image data, you can use this instead of CGImage to create another instance. + If the current image is not animated image, this value is nil. */ @property (nonatomic, copy, readonly, nullable) NSData *animatedImageData; @@ -77,7 +78,7 @@ The scale factor of the image. @note For UIKit, this just call super instead. - @note For AppKit, `NSImage` can contains multiple image representations with different scales. However, this class does not do that from the design. We processs the scale like UIKit and store it as a extra information for correctlly rendering in `SDAnimatedImageView`. + @note For AppKit, `NSImage` can contains multiple image representations with different scales. However, this class does not do that from the design. We processs the scale like UIKit. This wil actually be calculated from image size and pixel size. */ @property (nonatomic, readonly) CGFloat scale; diff --git a/SDWebImage/SDAnimatedImage.m b/SDWebImage/SDAnimatedImage.m index 74e16784..34d88051 100644 --- a/SDWebImage/SDAnimatedImage.m +++ b/SDWebImage/SDAnimatedImage.m @@ -205,9 +205,7 @@ static NSArray *SDBundlePreferredScales() { @end @implementation SDAnimatedImage -#if SD_UIKIT || SD_WATCH @dynamic scale; // call super -#endif #pragma mark - UIImage override method + (instancetype)imageNamed:(NSString *)name { @@ -316,9 +314,6 @@ static NSArray *SDBundlePreferredScales() { #endif if (self) { _coder = animatedCoder; -#if SD_MAC - _scale = scale; -#endif NSData *data = [animatedCoder animatedImageData]; SDImageFormat format = [NSData sd_imageFormatForImageData:data]; _animatedImageFormat = format; @@ -350,24 +345,37 @@ static NSArray *SDBundlePreferredScales() { #pragma mark - NSSecureCoding - (instancetype)initWithCoder:(NSCoder *)aDecoder { - NSNumber *scale = [aDecoder decodeObjectOfClass:[NSNumber class] forKey:NSStringFromSelector(@selector(scale))]; - NSData *animatedImageData = [aDecoder decodeObjectOfClass:[NSData class] forKey:NSStringFromSelector(@selector(animatedImageData))]; - if (animatedImageData) { -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wobjc-designated-initializers" - return [self initWithData:animatedImageData scale:scale.doubleValue]; -#pragma clang diagnostic pop - } else { - return [super initWithCoder:aDecoder]; + self = [super initWithCoder:aDecoder]; + if (self) { + NSData *animatedImageData = [aDecoder decodeObjectOfClass:[NSData class] forKey:NSStringFromSelector(@selector(animatedImageData))]; + CGFloat scale = self.scale; + if (!animatedImageData) { + return self; + } + id animatedCoder = nil; + for (idcoder in [SDWebImageCodersManager sharedManager].coders) { + if ([coder conformsToProtocol:@protocol(SDWebImageAnimatedCoder)]) { + if ([coder canDecodeFromData:animatedImageData]) { + animatedCoder = [[[coder class] alloc] initWithAnimatedImageData:animatedImageData options:@{SDWebImageCoderDecodeScaleFactor : @(scale)}]; + break; + } + } + } + if (!animatedCoder) { + return self; + } + _coder = animatedCoder; + SDImageFormat format = [NSData sd_imageFormatForImageData:animatedImageData]; + _animatedImageFormat = format; } + return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { - if (self.animatedImageData) { - [aCoder encodeObject:self.animatedImageData forKey:NSStringFromSelector(@selector(animatedImageData))]; - [aCoder encodeObject:@(self.scale) forKey:NSStringFromSelector(@selector(scale))]; - } else { - [super encodeWithCoder:aCoder]; + [super encodeWithCoder:aCoder]; + NSData *animatedImageData = self.animatedImageData; + if (animatedImageData) { + [aCoder encodeObject:animatedImageData forKey:NSStringFromSelector(@selector(animatedImageData))]; } } diff --git a/SDWebImage/SDWebImageGIFCoder.m b/SDWebImage/SDWebImageGIFCoder.m index 3db33284..c44af183 100644 --- a/SDWebImage/SDWebImageGIFCoder.m +++ b/SDWebImage/SDWebImageGIFCoder.m @@ -99,7 +99,7 @@ BOOL decodeFirstFrame = [options[SDWebImageCoderDecodeFirstFrameOnly] boolValue]; if (decodeFirstFrame || count <= 1) { - animatedImage = [[UIImage alloc] initWithData:data]; + animatedImage = [[UIImage alloc] initWithData:data scale:scale]; } else { NSMutableArray *frames = [NSMutableArray array];