From ee6f99f204d878b01be5b17fd0d3a0f47c69d005 Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 31 Oct 2018 14:40:10 +0800 Subject: [PATCH 1/2] Fix that the graphics helper method will return nil when scale = 0, match the UIKit behavior --- SDWebImage/UIImage+Transform.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SDWebImage/UIImage+Transform.m b/SDWebImage/UIImage+Transform.m index ff95ea45..a397924a 100644 --- a/SDWebImage/UIImage+Transform.m +++ b/SDWebImage/UIImage+Transform.m @@ -18,6 +18,10 @@ static void *kNSGraphicsContextScaleFactorKey; static CGContextRef SDCGContextCreateBitmapContext(CGSize size, BOOL opaque, CGFloat scale) { + if (scale == 0) { + // Match `UIGraphicsBeginImageContextWithOptions`, reset to the scale factor of the device’s main screen if scale is 0. + scale = [NSScreen mainScreen].backingScaleFactor; + } size_t width = ceil(size.width * scale); size_t height = ceil(size.height * scale); if (width < 1 || height < 1) return NULL; @@ -30,10 +34,6 @@ static CGContextRef SDCGContextCreateBitmapContext(CGSize size, BOOL opaque, CGF if (!context) { return NULL; } - if (scale == 0) { - // Match `UIGraphicsBeginImageContextWithOptions`, reset to the scale factor of the device’s main screen if scale is 0. - scale = [NSScreen mainScreen].backingScaleFactor; - } CGContextScaleCTM(context, scale, scale); return context; From 025c5f833b7a73076562df9c839333c0cdb2d1ee Mon Sep 17 00:00:00 2001 From: DreamPiggy Date: Wed, 31 Oct 2018 15:13:31 +0800 Subject: [PATCH 2/2] Expose the all image graphics methods to public method, make it easy for coder plugin user to reuse it instead of duplicate code --- SDWebImage.xcodeproj/project.pbxproj | 16 ++++ SDWebImage/SDImageGraphics.h | 21 ++++++ SDWebImage/SDImageGraphics.m | 105 +++++++++++++++++++++++++++ SDWebImage/UIImage+Transform.m | 88 +--------------------- WebImage/SDWebImage.h | 1 + 5 files changed, 144 insertions(+), 87 deletions(-) create mode 100644 SDWebImage/SDImageGraphics.h create mode 100644 SDWebImage/SDImageGraphics.m diff --git a/SDWebImage.xcodeproj/project.pbxproj b/SDWebImage.xcodeproj/project.pbxproj index c2186d66..2b7a7135 100644 --- a/SDWebImage.xcodeproj/project.pbxproj +++ b/SDWebImage.xcodeproj/project.pbxproj @@ -57,6 +57,12 @@ 325312CA200F09910046BF1E /* SDWebImageTransition.h in Headers */ = {isa = PBXBuildFile; fileRef = 325312C6200F09910046BF1E /* SDWebImageTransition.h */; settings = {ATTRIBUTES = (Public, ); }; }; 325312CE200F09910046BF1E /* SDWebImageTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 325312C7200F09910046BF1E /* SDWebImageTransition.m */; }; 325312D0200F09910046BF1E /* SDWebImageTransition.m in Sources */ = {isa = PBXBuildFile; fileRef = 325312C7200F09910046BF1E /* SDWebImageTransition.m */; }; + 3257EAF921898AED0097B271 /* SDImageGraphics.h in Headers */ = {isa = PBXBuildFile; fileRef = 3257EAF721898AED0097B271 /* SDImageGraphics.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3257EAFA21898AED0097B271 /* SDImageGraphics.h in Headers */ = {isa = PBXBuildFile; fileRef = 3257EAF721898AED0097B271 /* SDImageGraphics.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3257EAFB21898AED0097B271 /* SDImageGraphics.h in Headers */ = {isa = PBXBuildFile; fileRef = 3257EAF721898AED0097B271 /* SDImageGraphics.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3257EAFC21898AED0097B271 /* SDImageGraphics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3257EAF821898AED0097B271 /* SDImageGraphics.m */; }; + 3257EAFD21898AED0097B271 /* SDImageGraphics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3257EAF821898AED0097B271 /* SDImageGraphics.m */; }; + 3257EAFE21898AED0097B271 /* SDImageGraphics.m in Sources */ = {isa = PBXBuildFile; fileRef = 3257EAF821898AED0097B271 /* SDImageGraphics.m */; }; 327054D4206CD8B3006EA328 /* SDImageAPNGCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 327054D2206CD8B3006EA328 /* SDImageAPNGCoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 327054D6206CD8B3006EA328 /* SDImageAPNGCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 327054D2206CD8B3006EA328 /* SDImageAPNGCoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 327054DA206CD8B3006EA328 /* SDImageAPNGCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 327054D3206CD8B3006EA328 /* SDImageAPNGCoder.m */; }; @@ -243,6 +249,8 @@ 324DF4B3200A14DC008A84CC /* SDWebImageDefine.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageDefine.m; sourceTree = ""; }; 325312C6200F09910046BF1E /* SDWebImageTransition.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTransition.h; sourceTree = ""; }; 325312C7200F09910046BF1E /* SDWebImageTransition.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTransition.m; sourceTree = ""; }; + 3257EAF721898AED0097B271 /* SDImageGraphics.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDImageGraphics.h; sourceTree = ""; }; + 3257EAF821898AED0097B271 /* SDImageGraphics.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDImageGraphics.m; sourceTree = ""; }; 327054D2206CD8B3006EA328 /* SDImageAPNGCoder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDImageAPNGCoder.h; sourceTree = ""; }; 327054D3206CD8B3006EA328 /* SDImageAPNGCoder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDImageAPNGCoder.m; sourceTree = ""; }; 328BB69A2081FED200760D6C /* SDWebImageCacheKeyFilter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageCacheKeyFilter.h; sourceTree = ""; }; @@ -369,6 +377,8 @@ 3290FA031FA478AF0047D20C /* SDImageFrame.m */, 32CF1C051FA496B000004BD1 /* SDImageCoderHelper.h */, 32CF1C061FA496B000004BD1 /* SDImageCoderHelper.m */, + 3257EAF721898AED0097B271 /* SDImageGraphics.h */, + 3257EAF821898AED0097B271 /* SDImageGraphics.m */, ); name = Decoder; sourceTree = ""; @@ -603,6 +613,7 @@ files = ( 32D122202080B2EB003685A3 /* SDImageCacheDefine.h in Headers */, 32B9B539206ED4230026769D /* SDWebImageDownloaderConfig.h in Headers */, + 3257EAFA21898AED0097B271 /* SDImageGraphics.h in Headers */, 328BB6AC2081FEE500760D6C /* SDWebImageCacheSerializer.h in Headers */, 321B378F2083290E00C0EA77 /* SDImageLoadersManager.h in Headers */, 329A185B1FFF5DFD008C9A2F /* UIImage+Metadata.h in Headers */, @@ -658,6 +669,7 @@ files = ( 32CF1C071FA496B000004BD1 /* SDImageCoderHelper.h in Headers */, 32F7C0842030719600873181 /* UIImage+Transform.h in Headers */, + 3257EAF921898AED0097B271 /* SDImageGraphics.h in Headers */, 53761316155AD0D5005750A4 /* SDImageCache.h in Headers */, 325312C8200F09910046BF1E /* SDWebImageTransition.h in Headers */, 32C0FDE12013426C001B8F2D /* SDWebImageIndicator.h in Headers */, @@ -711,6 +723,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + 3257EAFB21898AED0097B271 /* SDImageGraphics.h in Headers */, 806BE07E2142C65200E02143 /* SDWebImageMapKit.h in Headers */, 80B6DFCD2142B71600BCB334 /* MKAnnotationView+WebCache.h in Headers */, ); @@ -846,6 +859,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3257EAFD21898AED0097B271 /* SDImageGraphics.m in Sources */, 3290FA0C1FA478AF0047D20C /* SDImageFrame.m in Sources */, 321E60C61F38E91700405457 /* UIImage+ForceDecode.m in Sources */, 328BB6A42081FED200760D6C /* SDWebImageCacheKeyFilter.m in Sources */, @@ -899,6 +913,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 3257EAFC21898AED0097B271 /* SDImageGraphics.m in Sources */, 3290FA0A1FA478AF0047D20C /* SDImageFrame.m in Sources */, 321E60C41F38E91700405457 /* UIImage+ForceDecode.m in Sources */, 328BB6A22081FED200760D6C /* SDWebImageCacheKeyFilter.m in Sources */, @@ -953,6 +968,7 @@ buildActionMask = 2147483647; files = ( 80B6DFA72142B71600BCB334 /* MKAnnotationView+WebCache.m in Sources */, + 3257EAFE21898AED0097B271 /* SDImageGraphics.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SDWebImage/SDImageGraphics.h b/SDWebImage/SDImageGraphics.h new file mode 100644 index 00000000..6a41e9a2 --- /dev/null +++ b/SDWebImage/SDImageGraphics.h @@ -0,0 +1,21 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDWebImageCompat.h" +#import + +/** + These following graphics context method are provided to easily write cross-platform(AppKit/UIKit) code. + For UIKit, these methods just call the same method in `UIGraphics.h`. See the documentation for usage. + For AppKit, these methods use `NSGraphicsContext` to create image context and match the behavior like UIKit. + */ +FOUNDATION_EXPORT CGContextRef __nullable SDGraphicsGetCurrentContext(void) CF_RETURNS_NOT_RETAINED; +FOUNDATION_EXPORT void SDGraphicsBeginImageContext(CGSize size); +FOUNDATION_EXPORT void SDGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale); +FOUNDATION_EXPORT void SDGraphicsEndImageContext(void); +FOUNDATION_EXPORT UIImage * __nullable SDGraphicsGetImageFromCurrentImageContext(void); diff --git a/SDWebImage/SDImageGraphics.m b/SDWebImage/SDImageGraphics.m new file mode 100644 index 00000000..683780ee --- /dev/null +++ b/SDWebImage/SDImageGraphics.m @@ -0,0 +1,105 @@ +/* + * This file is part of the SDWebImage package. + * (c) Olivier Poitrey + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +#import "SDImageGraphics.h" +#import "NSImage+Compatibility.h" +#import "objc/runtime.h" + +#if SD_MAC +static void *kNSGraphicsContextScaleFactorKey; + +static CGContextRef SDCGContextCreateBitmapContext(CGSize size, BOOL opaque, CGFloat scale) { + if (scale == 0) { + // Match `UIGraphicsBeginImageContextWithOptions`, reset to the scale factor of the device’s main screen if scale is 0. + scale = [NSScreen mainScreen].backingScaleFactor; + } + size_t width = ceil(size.width * scale); + size_t height = ceil(size.height * scale); + if (width < 1 || height < 1) return NULL; + + //pre-multiplied BGRA for non-opaque, BGRX for opaque, 8-bits per component, as Apple's doc + CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); + CGImageAlphaInfo alphaInfo = kCGBitmapByteOrder32Host | (opaque ? kCGImageAlphaNoneSkipFirst : kCGImageAlphaPremultipliedFirst); + CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, space, kCGBitmapByteOrderDefault | alphaInfo); + CGColorSpaceRelease(space); + if (!context) { + return NULL; + } + CGContextScaleCTM(context, scale, scale); + + return context; +} +#endif + +CGContextRef SDGraphicsGetCurrentContext(void) { +#if SD_UIKIT || SD_WATCH + return UIGraphicsGetCurrentContext(); +#else + return NSGraphicsContext.currentContext.CGContext; +#endif +} + +void SDGraphicsBeginImageContext(CGSize size) { +#if SD_UIKIT || SD_WATCH + SDGraphicsBeginImageContext(size); +#else + SDGraphicsBeginImageContextWithOptions(size, NO, 1.0); +#endif +} + +void SDGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale) { +#if SD_UIKIT || SD_WATCH + UIGraphicsBeginImageContextWithOptions(size, opaque, scale); +#else + CGContextRef context = SDCGContextCreateBitmapContext(size, opaque, scale); + if (!context) { + return; + } + NSGraphicsContext *graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]; + objc_setAssociatedObject(graphicsContext, &kNSGraphicsContextScaleFactorKey, @(scale), OBJC_ASSOCIATION_RETAIN); + CGContextRelease(context); + [NSGraphicsContext saveGraphicsState]; + NSGraphicsContext.currentContext = graphicsContext; +#endif +} + +void SDGraphicsEndImageContext(void) { +#if SD_UIKIT || SD_WATCH + UIGraphicsEndImageContext(); +#else + [NSGraphicsContext restoreGraphicsState]; +#endif +} + +UIImage * SDGraphicsGetImageFromCurrentImageContext(void) { +#if SD_UIKIT || SD_WATCH + return UIGraphicsGetImageFromCurrentImageContext(); +#else + NSGraphicsContext *context = NSGraphicsContext.currentContext; + CGContextRef contextRef = context.CGContext; + if (!contextRef) { + return nil; + } + CGImageRef imageRef = CGBitmapContextCreateImage(contextRef); + if (!imageRef) { + return nil; + } + CGFloat scale = 0; + NSNumber *scaleFactor = objc_getAssociatedObject(context, &kNSGraphicsContextScaleFactorKey); + if ([scaleFactor isKindOfClass:[NSNumber class]]) { + scale = scaleFactor.doubleValue; + } + if (!scale) { + // reset to the scale factor of the device’s main screen if scale is 0. + scale = [NSScreen mainScreen].backingScaleFactor; + } + NSImage *image = [[NSImage alloc] initWithCGImage:imageRef scale:scale orientation:kCGImagePropertyOrientationUp]; + CGImageRelease(imageRef); + return image; +#endif +} diff --git a/SDWebImage/UIImage+Transform.m b/SDWebImage/UIImage+Transform.m index a397924a..6d844ec9 100644 --- a/SDWebImage/UIImage+Transform.m +++ b/SDWebImage/UIImage+Transform.m @@ -8,98 +8,12 @@ #import "UIImage+Transform.h" #import "NSImage+Compatibility.h" +#import "SDImageGraphics.h" #import #if SD_UIKIT || SD_MAC #import -#import "objc/runtime.h" #endif -#if SD_MAC -static void *kNSGraphicsContextScaleFactorKey; - -static CGContextRef SDCGContextCreateBitmapContext(CGSize size, BOOL opaque, CGFloat scale) { - if (scale == 0) { - // Match `UIGraphicsBeginImageContextWithOptions`, reset to the scale factor of the device’s main screen if scale is 0. - scale = [NSScreen mainScreen].backingScaleFactor; - } - size_t width = ceil(size.width * scale); - size_t height = ceil(size.height * scale); - if (width < 1 || height < 1) return NULL; - - //pre-multiplied BGRA for non-opaque, BGRX for opaque, 8-bits per component, as Apple's doc - CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); - CGImageAlphaInfo alphaInfo = kCGBitmapByteOrder32Host | (opaque ? kCGImageAlphaNoneSkipFirst : kCGImageAlphaPremultipliedFirst); - CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, space, kCGBitmapByteOrderDefault | alphaInfo); - CGColorSpaceRelease(space); - if (!context) { - return NULL; - } - CGContextScaleCTM(context, scale, scale); - - return context; -} -#endif - -static void SDGraphicsBeginImageContextWithOptions(CGSize size, BOOL opaque, CGFloat scale) { -#if SD_UIKIT || SD_WATCH - UIGraphicsBeginImageContextWithOptions(size, opaque, scale); -#else - CGContextRef context = SDCGContextCreateBitmapContext(size, opaque, scale); - if (!context) { - return; - } - NSGraphicsContext *graphicsContext = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO]; - objc_setAssociatedObject(graphicsContext, &kNSGraphicsContextScaleFactorKey, @(scale), OBJC_ASSOCIATION_RETAIN); - CGContextRelease(context); - [NSGraphicsContext saveGraphicsState]; - NSGraphicsContext.currentContext = graphicsContext; -#endif -} - -static CGContextRef SDGraphicsGetCurrentContext(void) { -#if SD_UIKIT || SD_WATCH - return UIGraphicsGetCurrentContext(); -#else - return NSGraphicsContext.currentContext.CGContext; -#endif -} - -static void SDGraphicsEndImageContext(void) { -#if SD_UIKIT || SD_WATCH - UIGraphicsEndImageContext(); -#else - [NSGraphicsContext restoreGraphicsState]; -#endif -} - -static UIImage * SDGraphicsGetImageFromCurrentImageContext(void) { -#if SD_UIKIT || SD_WATCH - return UIGraphicsGetImageFromCurrentImageContext(); -#else - NSGraphicsContext *context = NSGraphicsContext.currentContext; - CGContextRef contextRef = context.CGContext; - if (!contextRef) { - return nil; - } - CGImageRef imageRef = CGBitmapContextCreateImage(contextRef); - if (!imageRef) { - return nil; - } - CGFloat scale = 0; - NSNumber *scaleFactor = objc_getAssociatedObject(context, &kNSGraphicsContextScaleFactorKey); - if ([scaleFactor isKindOfClass:[NSNumber class]]) { - scale = scaleFactor.doubleValue; - } - if (!scale) { - // reset to the scale factor of the device’s main screen if scale is 0. - scale = [NSScreen mainScreen].backingScaleFactor; - } - NSImage *image = [[NSImage alloc] initWithCGImage:imageRef scale:scale orientation:kCGImagePropertyOrientationUp]; - CGImageRelease(imageRef); - return image; -#endif -} - static inline CGRect SDCGRectFitWithScaleMode(CGRect rect, CGSize size, SDImageScaleMode scaleMode) { rect = CGRectStandardize(rect); size.width = size.width < 0 ? -size.width : size.width; diff --git a/WebImage/SDWebImage.h b/WebImage/SDWebImage.h index ce50a1f3..49eeae80 100644 --- a/WebImage/SDWebImage.h +++ b/WebImage/SDWebImage.h @@ -59,6 +59,7 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[]; #import #import #import +#import #import #import #import