Merge pull request #2907 from dreampiggy/performance_UIGraphicsImageRenderer
Performance - Using UIGraphicsImageRenderer on iOS 10+, save memory when image bitmap is RGB(-25%) or Grayscale(-75%)
This commit is contained in:
commit
69a62ff39d
|
@ -52,6 +52,9 @@
|
||||||
3244062C2296C5F400A36084 /* SDWebImageOptionsProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 324406292296C5F400A36084 /* SDWebImageOptionsProcessor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
3244062C2296C5F400A36084 /* SDWebImageOptionsProcessor.h in Headers */ = {isa = PBXBuildFile; fileRef = 324406292296C5F400A36084 /* SDWebImageOptionsProcessor.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
3244062D2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3244062A2296C5F400A36084 /* SDWebImageOptionsProcessor.m */; };
|
3244062D2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3244062A2296C5F400A36084 /* SDWebImageOptionsProcessor.m */; };
|
||||||
3244062E2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3244062A2296C5F400A36084 /* SDWebImageOptionsProcessor.m */; };
|
3244062E2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */ = {isa = PBXBuildFile; fileRef = 3244062A2296C5F400A36084 /* SDWebImageOptionsProcessor.m */; };
|
||||||
|
3246A70323A567AC00FBEA10 /* SDGraphicsImageRenderer.h in Headers */ = {isa = PBXBuildFile; fileRef = 3246A70123A567AC00FBEA10 /* SDGraphicsImageRenderer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
3246A70423A567AC00FBEA10 /* SDGraphicsImageRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3246A70223A567AC00FBEA10 /* SDGraphicsImageRenderer.m */; };
|
||||||
|
3246A70523A567AC00FBEA10 /* SDGraphicsImageRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3246A70223A567AC00FBEA10 /* SDGraphicsImageRenderer.m */; };
|
||||||
3248475D201775F600AF9E5A /* SDAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32484757201775F600AF9E5A /* SDAnimatedImageView.m */; };
|
3248475D201775F600AF9E5A /* SDAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32484757201775F600AF9E5A /* SDAnimatedImageView.m */; };
|
||||||
3248475F201775F600AF9E5A /* SDAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32484757201775F600AF9E5A /* SDAnimatedImageView.m */; };
|
3248475F201775F600AF9E5A /* SDAnimatedImageView.m in Sources */ = {isa = PBXBuildFile; fileRef = 32484757201775F600AF9E5A /* SDAnimatedImageView.m */; };
|
||||||
32484765201775F600AF9E5A /* SDAnimatedImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32484758201775F600AF9E5A /* SDAnimatedImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
32484765201775F600AF9E5A /* SDAnimatedImageView+WebCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 32484758201775F600AF9E5A /* SDAnimatedImageView+WebCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
|
@ -121,6 +124,7 @@
|
||||||
328BB6CF2082581100760D6C /* SDMemoryCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BF2082581100760D6C /* SDMemoryCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
328BB6CF2082581100760D6C /* SDMemoryCache.h in Headers */ = {isa = PBXBuildFile; fileRef = 328BB6BF2082581100760D6C /* SDMemoryCache.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
328BB6D32082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
|
328BB6D32082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
|
||||||
328BB6D52082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
|
328BB6D52082581100760D6C /* SDMemoryCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 328BB6C02082581100760D6C /* SDMemoryCache.m */; };
|
||||||
|
328E9DE523A61DD30051C893 /* SDGraphicsImageRenderer.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3246A70123A567AC00FBEA10 /* SDGraphicsImageRenderer.h */; };
|
||||||
3290FA061FA478AF0047D20C /* SDImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 3290FA021FA478AF0047D20C /* SDImageFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
3290FA061FA478AF0047D20C /* SDImageFrame.h in Headers */ = {isa = PBXBuildFile; fileRef = 3290FA021FA478AF0047D20C /* SDImageFrame.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||||
3290FA0A1FA478AF0047D20C /* SDImageFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 3290FA031FA478AF0047D20C /* SDImageFrame.m */; };
|
3290FA0A1FA478AF0047D20C /* SDImageFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 3290FA031FA478AF0047D20C /* SDImageFrame.m */; };
|
||||||
3290FA0C1FA478AF0047D20C /* SDImageFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 3290FA031FA478AF0047D20C /* SDImageFrame.m */; };
|
3290FA0C1FA478AF0047D20C /* SDImageFrame.m in Sources */ = {isa = PBXBuildFile; fileRef = 3290FA031FA478AF0047D20C /* SDImageFrame.m */; };
|
||||||
|
@ -308,6 +312,7 @@
|
||||||
dstPath = include/SDWebImage;
|
dstPath = include/SDWebImage;
|
||||||
dstSubfolderSpec = 16;
|
dstSubfolderSpec = 16;
|
||||||
files = (
|
files = (
|
||||||
|
328E9DE523A61DD30051C893 /* SDGraphicsImageRenderer.h in Copy Headers */,
|
||||||
325F7CCD2389467800AEDFCC /* UIImage+ExtendedCacheData.h in Copy Headers */,
|
325F7CCD2389467800AEDFCC /* UIImage+ExtendedCacheData.h in Copy Headers */,
|
||||||
326E2F36236F1E30006F847F /* SDAnimatedImagePlayer.h in Copy Headers */,
|
326E2F36236F1E30006F847F /* SDAnimatedImagePlayer.h in Copy Headers */,
|
||||||
3250C9F12355E3DF0093A896 /* SDWebImageDownloaderDecryptor.h in Copy Headers */,
|
3250C9F12355E3DF0093A896 /* SDWebImageDownloaderDecryptor.h in Copy Headers */,
|
||||||
|
@ -394,6 +399,8 @@
|
||||||
3240BB6723968FE6003BA07D /* SDAssociatedObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDAssociatedObject.m; sourceTree = "<group>"; };
|
3240BB6723968FE6003BA07D /* SDAssociatedObject.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SDAssociatedObject.m; sourceTree = "<group>"; };
|
||||||
324406292296C5F400A36084 /* SDWebImageOptionsProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageOptionsProcessor.h; path = Core/SDWebImageOptionsProcessor.h; sourceTree = "<group>"; };
|
324406292296C5F400A36084 /* SDWebImageOptionsProcessor.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDWebImageOptionsProcessor.h; path = Core/SDWebImageOptionsProcessor.h; sourceTree = "<group>"; };
|
||||||
3244062A2296C5F400A36084 /* SDWebImageOptionsProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageOptionsProcessor.m; path = Core/SDWebImageOptionsProcessor.m; sourceTree = "<group>"; };
|
3244062A2296C5F400A36084 /* SDWebImageOptionsProcessor.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDWebImageOptionsProcessor.m; path = Core/SDWebImageOptionsProcessor.m; sourceTree = "<group>"; };
|
||||||
|
3246A70123A567AC00FBEA10 /* SDGraphicsImageRenderer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDGraphicsImageRenderer.h; path = Core/SDGraphicsImageRenderer.h; sourceTree = "<group>"; };
|
||||||
|
3246A70223A567AC00FBEA10 /* SDGraphicsImageRenderer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDGraphicsImageRenderer.m; path = Core/SDGraphicsImageRenderer.m; sourceTree = "<group>"; };
|
||||||
32484757201775F600AF9E5A /* SDAnimatedImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDAnimatedImageView.m; path = Core/SDAnimatedImageView.m; sourceTree = "<group>"; };
|
32484757201775F600AF9E5A /* SDAnimatedImageView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDAnimatedImageView.m; path = Core/SDAnimatedImageView.m; sourceTree = "<group>"; };
|
||||||
32484758201775F600AF9E5A /* SDAnimatedImageView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SDAnimatedImageView+WebCache.h"; path = "Core/SDAnimatedImageView+WebCache.h"; sourceTree = "<group>"; };
|
32484758201775F600AF9E5A /* SDAnimatedImageView+WebCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "SDAnimatedImageView+WebCache.h"; path = "Core/SDAnimatedImageView+WebCache.h"; sourceTree = "<group>"; };
|
||||||
32484759201775F600AF9E5A /* SDAnimatedImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDAnimatedImageView.h; path = Core/SDAnimatedImageView.h; sourceTree = "<group>"; };
|
32484759201775F600AF9E5A /* SDAnimatedImageView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SDAnimatedImageView.h; path = Core/SDAnimatedImageView.h; sourceTree = "<group>"; };
|
||||||
|
@ -577,6 +584,8 @@
|
||||||
32CF1C061FA496B000004BD1 /* SDImageCoderHelper.m */,
|
32CF1C061FA496B000004BD1 /* SDImageCoderHelper.m */,
|
||||||
3257EAF721898AED0097B271 /* SDImageGraphics.h */,
|
3257EAF721898AED0097B271 /* SDImageGraphics.h */,
|
||||||
3257EAF821898AED0097B271 /* SDImageGraphics.m */,
|
3257EAF821898AED0097B271 /* SDImageGraphics.m */,
|
||||||
|
3246A70123A567AC00FBEA10 /* SDGraphicsImageRenderer.h */,
|
||||||
|
3246A70223A567AC00FBEA10 /* SDGraphicsImageRenderer.m */,
|
||||||
);
|
);
|
||||||
name = Decoder;
|
name = Decoder;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -905,6 +914,7 @@
|
||||||
4A2CAE1D1AB4BB6800B6BC39 /* SDWebImageDownloaderOperation.h in Headers */,
|
4A2CAE1D1AB4BB6800B6BC39 /* SDWebImageDownloaderOperation.h in Headers */,
|
||||||
4A2CAE2B1AB4BB7500B6BC39 /* UIButton+WebCache.h in Headers */,
|
4A2CAE2B1AB4BB7500B6BC39 /* UIButton+WebCache.h in Headers */,
|
||||||
4A2CAE251AB4BB7000B6BC39 /* SDWebImagePrefetcher.h in Headers */,
|
4A2CAE251AB4BB7000B6BC39 /* SDWebImagePrefetcher.h in Headers */,
|
||||||
|
3246A70323A567AC00FBEA10 /* SDGraphicsImageRenderer.h in Headers */,
|
||||||
328BB6CF2082581100760D6C /* SDMemoryCache.h in Headers */,
|
328BB6CF2082581100760D6C /* SDMemoryCache.h in Headers */,
|
||||||
325C460F223394D8004CAE11 /* SDImageCachesManagerOperation.h in Headers */,
|
325C460F223394D8004CAE11 /* SDImageCachesManagerOperation.h in Headers */,
|
||||||
321E60881F38E8C800405457 /* SDImageCoder.h in Headers */,
|
321E60881F38E8C800405457 /* SDImageCoder.h in Headers */,
|
||||||
|
@ -1125,6 +1135,7 @@
|
||||||
3290FA0C1FA478AF0047D20C /* SDImageFrame.m in Sources */,
|
3290FA0C1FA478AF0047D20C /* SDImageFrame.m in Sources */,
|
||||||
325C46232233A02E004CAE11 /* UIColor+HexString.m in Sources */,
|
325C46232233A02E004CAE11 /* UIColor+HexString.m in Sources */,
|
||||||
325F7CCB238942AB00AEDFCC /* UIImage+ExtendedCacheData.m in Sources */,
|
325F7CCB238942AB00AEDFCC /* UIImage+ExtendedCacheData.m in Sources */,
|
||||||
|
3246A70523A567AC00FBEA10 /* SDGraphicsImageRenderer.m in Sources */,
|
||||||
321E60C61F38E91700405457 /* UIImage+ForceDecode.m in Sources */,
|
321E60C61F38E91700405457 /* UIImage+ForceDecode.m in Sources */,
|
||||||
3244062E2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */,
|
3244062E2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */,
|
||||||
3250C9F02355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */,
|
3250C9F02355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */,
|
||||||
|
@ -1198,6 +1209,7 @@
|
||||||
3290FA0A1FA478AF0047D20C /* SDImageFrame.m in Sources */,
|
3290FA0A1FA478AF0047D20C /* SDImageFrame.m in Sources */,
|
||||||
325C46222233A02E004CAE11 /* UIColor+HexString.m in Sources */,
|
325C46222233A02E004CAE11 /* UIColor+HexString.m in Sources */,
|
||||||
321E60C41F38E91700405457 /* UIImage+ForceDecode.m in Sources */,
|
321E60C41F38E91700405457 /* UIImage+ForceDecode.m in Sources */,
|
||||||
|
3246A70423A567AC00FBEA10 /* SDGraphicsImageRenderer.m in Sources */,
|
||||||
3244062D2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */,
|
3244062D2296C5F400A36084 /* SDWebImageOptionsProcessor.m in Sources */,
|
||||||
3250C9EF2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */,
|
3250C9EF2355D9DA0093A896 /* SDWebImageDownloaderDecryptor.m in Sources */,
|
||||||
3240BB6523968FA1003BA07D /* SDFileAttributeHelper.m in Sources */,
|
3240BB6523968FA1003BA07D /* SDFileAttributeHelper.m in Sources */,
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* 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 "SDWebImageCompat.h"
|
||||||
|
|
||||||
|
/**
|
||||||
|
These following class are provided to use `UIGraphicsImageRenderer` with polyfill, which allows write cross-platform(AppKit/UIKit) code and avoid runtime version check.
|
||||||
|
Compared to `UIGraphicsBeginImageContext`, `UIGraphicsImageRenderer` use dynamic bitmap from your draw code to generate CGContext, not always use ARGB8888, which is more performant on RAM usage.
|
||||||
|
Which means, if you draw CGImage/CIImage which contains grayscale only, the underlaying bitmap context use grayscale, it's managed by system and not a fixed type. (actually, the `kCGContextTypeAutomatic`)
|
||||||
|
For usage, See more in Apple's documentation: https://developer.apple.com/documentation/uikit/uigraphicsimagerenderer
|
||||||
|
For UIKit on iOS/tvOS 10+, these method just use the same `UIGraphicsImageRenderer` API.
|
||||||
|
For others (macOS/watchOS or iOS/tvOS 10-), these method use the `SDImageGraphics.h` to implements the same behavior (but without dynamic bitmap support)
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef void (^SDGraphicsImageDrawingActions)(CGContextRef _Nonnull context);
|
||||||
|
typedef NS_ENUM(NSInteger, SDGraphicsImageRendererFormatRange) {
|
||||||
|
SDGraphicsImageRendererFormatRangeUnspecified = -1,
|
||||||
|
SDGraphicsImageRendererFormatRangeAutomatic = 0,
|
||||||
|
SDGraphicsImageRendererFormatRangeExtended,
|
||||||
|
SDGraphicsImageRendererFormatRangeStandard
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A set of drawing attributes that represent the configuration of an image renderer context.
|
||||||
|
@interface SDGraphicsImageRendererFormat : NSObject
|
||||||
|
|
||||||
|
/// The display scale of the image renderer context.
|
||||||
|
/// The default value is equal to the scale of the main screen.
|
||||||
|
@property (nonatomic) CGFloat scale;
|
||||||
|
|
||||||
|
/// A Boolean value indicating whether the underlying Core Graphics context has an alpha channel.
|
||||||
|
/// The default value is NO.
|
||||||
|
@property (nonatomic) BOOL opaque;
|
||||||
|
|
||||||
|
/// Specifying whether the bitmap context should use extended color.
|
||||||
|
/// For iOS 12+, the value is from system `preferredRange` property
|
||||||
|
/// For iOS 10-11, the value is from system `prefersExtendedRange` property
|
||||||
|
/// For iOS 9-, the value is `.standard`
|
||||||
|
@property (nonatomic) SDGraphicsImageRendererFormatRange preferredRange;
|
||||||
|
|
||||||
|
/// Init the default format. See each properties's default value.
|
||||||
|
- (nonnull instancetype)init;
|
||||||
|
|
||||||
|
/// Returns a new format best suited for the main screen’s current configuration.
|
||||||
|
+ (nonnull instancetype)preferredFormat;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
/// A graphics renderer for creating Core Graphics-backed images.
|
||||||
|
@interface SDGraphicsImageRenderer : NSObject
|
||||||
|
|
||||||
|
/// Creates an image renderer for drawing images of a given size.
|
||||||
|
/// @param size The size of images output from the renderer, specified in points.
|
||||||
|
/// @return An initialized image renderer.
|
||||||
|
- (nonnull instancetype)initWithSize:(CGSize)size;
|
||||||
|
|
||||||
|
/// Creates a new image renderer with a given size and format.
|
||||||
|
/// @param size The size of images output from the renderer, specified in points.
|
||||||
|
/// @param format A SDGraphicsImageRendererFormat object that encapsulates the format used to create the renderer context.
|
||||||
|
/// @return An initialized image renderer.
|
||||||
|
- (nonnull instancetype)initWithSize:(CGSize)size format:(nonnull SDGraphicsImageRendererFormat *)format;
|
||||||
|
|
||||||
|
/// Creates an image by following a set of drawing instructions.
|
||||||
|
/// @param actions A SDGraphicsImageDrawingActions block that, when invoked by the renderer, executes a set of drawing instructions to create the output image.
|
||||||
|
/// @note You should not retain or use the context outside the block, it's non-escaping.
|
||||||
|
/// @return A UIImage object created by the supplied drawing actions.
|
||||||
|
- (nonnull UIImage *)imageWithActions:(nonnull NS_NOESCAPE SDGraphicsImageDrawingActions)actions;
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,241 @@
|
||||||
|
/*
|
||||||
|
* 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 "SDGraphicsImageRenderer.h"
|
||||||
|
#import "SDImageGraphics.h"
|
||||||
|
|
||||||
|
@interface SDGraphicsImageRendererFormat ()
|
||||||
|
#if SD_UIKIT
|
||||||
|
@property (nonatomic, strong) UIGraphicsImageRendererFormat *uiformat API_AVAILABLE(ios(10.0), tvos(10.0));
|
||||||
|
#endif
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SDGraphicsImageRendererFormat
|
||||||
|
@synthesize scale = _scale;
|
||||||
|
@synthesize opaque = _opaque;
|
||||||
|
@synthesize preferredRange = _preferredRange;
|
||||||
|
|
||||||
|
#pragma mark - Property
|
||||||
|
- (CGFloat)scale {
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.10, *)) {
|
||||||
|
return self.uiformat.scale;
|
||||||
|
} else {
|
||||||
|
return _scale;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return _scale;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setScale:(CGFloat)scale {
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.10, *)) {
|
||||||
|
self.uiformat.scale = scale;
|
||||||
|
} else {
|
||||||
|
_scale = scale;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
_scale = scale;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)opaque {
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.10, *)) {
|
||||||
|
return self.uiformat.opaque;
|
||||||
|
} else {
|
||||||
|
return _opaque;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return _opaque;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setOpaque:(BOOL)opaque {
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.10, *)) {
|
||||||
|
self.uiformat.opaque = opaque;
|
||||||
|
} else {
|
||||||
|
_opaque = opaque;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
_opaque = opaque;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
- (SDGraphicsImageRendererFormatRange)preferredRange {
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.10, *)) {
|
||||||
|
if (@available(iOS 12.0, tvOS 12.0, *)) {
|
||||||
|
return (SDGraphicsImageRendererFormatRange)self.uiformat.preferredRange;
|
||||||
|
} else {
|
||||||
|
BOOL prefersExtendedRange = self.uiformat.prefersExtendedRange;
|
||||||
|
if (prefersExtendedRange) {
|
||||||
|
return SDGraphicsImageRendererFormatRangeExtended;
|
||||||
|
} else {
|
||||||
|
return SDGraphicsImageRendererFormatRangeStandard;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return _preferredRange;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
return _preferredRange;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)setPreferredRange:(SDGraphicsImageRendererFormatRange)preferredRange {
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.10, *)) {
|
||||||
|
if (@available(iOS 12.0, tvOS 12.0, *)) {
|
||||||
|
self.uiformat.preferredRange = (UIGraphicsImageRendererFormatRange)preferredRange;
|
||||||
|
} else {
|
||||||
|
switch (preferredRange) {
|
||||||
|
case SDGraphicsImageRendererFormatRangeExtended:
|
||||||
|
self.uiformat.prefersExtendedRange = YES;
|
||||||
|
break;
|
||||||
|
case SDGraphicsImageRendererFormatRangeStandard:
|
||||||
|
self.uiformat.prefersExtendedRange = NO;
|
||||||
|
default:
|
||||||
|
// Automatic means default
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_preferredRange = preferredRange;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
_preferredRange = preferredRange;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)init {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.10, *)) {
|
||||||
|
UIGraphicsImageRendererFormat *uiformat = [[UIGraphicsImageRendererFormat alloc] init];
|
||||||
|
self.uiformat = uiformat;
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
#if SD_WATCH
|
||||||
|
CGFloat screenScale = [WKInterfaceDevice currentDevice].screenScale;
|
||||||
|
#elif SD_UIKIT
|
||||||
|
CGFloat screenScale = [UIScreen mainScreen].scale;
|
||||||
|
#elif SD_MAC
|
||||||
|
CGFloat screenScale = [NSScreen mainScreen].backingScaleFactor;
|
||||||
|
#endif
|
||||||
|
self.scale = screenScale;
|
||||||
|
self.opaque = NO;
|
||||||
|
self.preferredRange = SDGraphicsImageRendererFormatRangeStandard;
|
||||||
|
#if SD_UIKIT
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wunguarded-availability"
|
||||||
|
- (instancetype)initForMainScreen {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.0, *)) {
|
||||||
|
UIGraphicsImageRendererFormat *uiformat;
|
||||||
|
// iOS 11.0.0 GM does have `preferredFormat`, but iOS 11 betas did not (argh!)
|
||||||
|
if ([UIGraphicsImageRenderer respondsToSelector:@selector(preferredFormat)]) {
|
||||||
|
uiformat = [UIGraphicsImageRendererFormat preferredFormat];
|
||||||
|
} else {
|
||||||
|
uiformat = [UIGraphicsImageRendererFormat defaultFormat];
|
||||||
|
}
|
||||||
|
self.uiformat = uiformat;
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
#if SD_WATCH
|
||||||
|
CGFloat screenScale = [WKInterfaceDevice currentDevice].screenScale;
|
||||||
|
#elif SD_UIKIT
|
||||||
|
CGFloat screenScale = [UIScreen mainScreen].scale;
|
||||||
|
#elif SD_MAC
|
||||||
|
CGFloat screenScale = [NSScreen mainScreen].backingScaleFactor;
|
||||||
|
#endif
|
||||||
|
self.scale = screenScale;
|
||||||
|
self.opaque = NO;
|
||||||
|
self.preferredRange = SDGraphicsImageRendererFormatRangeStandard;
|
||||||
|
#if SD_UIKIT
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
+ (instancetype)preferredFormat {
|
||||||
|
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] initForMainScreen];
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@interface SDGraphicsImageRenderer ()
|
||||||
|
@property (nonatomic, assign) CGSize size;
|
||||||
|
@property (nonatomic, strong) SDGraphicsImageRendererFormat *format;
|
||||||
|
#if SD_UIKIT
|
||||||
|
@property (nonatomic, strong) UIGraphicsImageRenderer *uirenderer API_AVAILABLE(ios(10.0), tvos(10.0));
|
||||||
|
#endif
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation SDGraphicsImageRenderer
|
||||||
|
|
||||||
|
- (instancetype)initWithSize:(CGSize)size {
|
||||||
|
return [self initWithSize:size format:SDGraphicsImageRendererFormat.preferredFormat];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (instancetype)initWithSize:(CGSize)size format:(SDGraphicsImageRendererFormat *)format {
|
||||||
|
NSParameterAssert(format);
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
self.size = size;
|
||||||
|
self.format = format;
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.0, *)) {
|
||||||
|
UIGraphicsImageRendererFormat *uiformat = format.uiformat;
|
||||||
|
self.uirenderer = [[UIGraphicsImageRenderer alloc] initWithSize:size format:uiformat];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (UIImage *)imageWithActions:(NS_NOESCAPE SDGraphicsImageDrawingActions)actions {
|
||||||
|
NSParameterAssert(actions);
|
||||||
|
#if SD_UIKIT
|
||||||
|
if (@available(iOS 10.0, tvOS 10.0, *)) {
|
||||||
|
UIGraphicsImageDrawingActions uiactions = ^(UIGraphicsImageRendererContext *rendererContext) {
|
||||||
|
if (actions) {
|
||||||
|
actions(rendererContext.CGContext);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return [self.uirenderer imageWithActions:uiactions];
|
||||||
|
} else {
|
||||||
|
#endif
|
||||||
|
SDGraphicsBeginImageContextWithOptions(self.size, self.format.opaque, self.format.scale);
|
||||||
|
CGContextRef context = SDGraphicsGetCurrentContext();
|
||||||
|
if (actions) {
|
||||||
|
actions(context);
|
||||||
|
}
|
||||||
|
UIImage *image = SDGraphicsGetImageFromCurrentImageContext();
|
||||||
|
SDGraphicsEndImageContext();
|
||||||
|
return image;
|
||||||
|
#if SD_UIKIT
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -13,6 +13,7 @@
|
||||||
These following graphics context method are provided to easily write cross-platform(AppKit/UIKit) code.
|
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 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.
|
For AppKit, these methods use `NSGraphicsContext` to create image context and match the behavior like UIKit.
|
||||||
|
@note If you don't care bitmap format (ARGB8888) and just draw image, use `SDGraphicsImageRenderer` instead. It's more performant on RAM usage.`
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// Returns the current graphics context.
|
/// Returns the current graphics context.
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#import "UIImage+Transform.h"
|
#import "UIImage+Transform.h"
|
||||||
#import "NSImage+Compatibility.h"
|
#import "NSImage+Compatibility.h"
|
||||||
#import "SDImageGraphics.h"
|
#import "SDImageGraphics.h"
|
||||||
|
#import "SDGraphicsImageRenderer.h"
|
||||||
#import "NSBezierPath+RoundedCorners.h"
|
#import "NSBezierPath+RoundedCorners.h"
|
||||||
#import <Accelerate/Accelerate.h>
|
#import <Accelerate/Accelerate.h>
|
||||||
#if SD_UIKIT || SD_MAC
|
#if SD_UIKIT || SD_MAC
|
||||||
|
@ -165,11 +166,10 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
|
||||||
|
|
||||||
@implementation UIImage (Transform)
|
@implementation UIImage (Transform)
|
||||||
|
|
||||||
- (void)sd_drawInRect:(CGRect)rect withScaleMode:(SDImageScaleMode)scaleMode clipsToBounds:(BOOL)clips {
|
- (void)sd_drawInRect:(CGRect)rect context:(CGContextRef)context scaleMode:(SDImageScaleMode)scaleMode clipsToBounds:(BOOL)clips {
|
||||||
CGRect drawRect = SDCGRectFitWithScaleMode(rect, self.size, scaleMode);
|
CGRect drawRect = SDCGRectFitWithScaleMode(rect, self.size, scaleMode);
|
||||||
if (drawRect.size.width == 0 || drawRect.size.height == 0) return;
|
if (drawRect.size.width == 0 || drawRect.size.height == 0) return;
|
||||||
if (clips) {
|
if (clips) {
|
||||||
CGContextRef context = SDGraphicsGetCurrentContext();
|
|
||||||
if (context) {
|
if (context) {
|
||||||
CGContextSaveGState(context);
|
CGContextSaveGState(context);
|
||||||
CGContextAddRect(context, rect);
|
CGContextAddRect(context, rect);
|
||||||
|
@ -184,10 +184,12 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
|
||||||
|
|
||||||
- (nullable UIImage *)sd_resizedImageWithSize:(CGSize)size scaleMode:(SDImageScaleMode)scaleMode {
|
- (nullable UIImage *)sd_resizedImageWithSize:(CGSize)size scaleMode:(SDImageScaleMode)scaleMode {
|
||||||
if (size.width <= 0 || size.height <= 0) return nil;
|
if (size.width <= 0 || size.height <= 0) return nil;
|
||||||
SDGraphicsBeginImageContextWithOptions(size, NO, self.scale);
|
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init];
|
||||||
[self sd_drawInRect:CGRectMake(0, 0, size.width, size.height) withScaleMode:scaleMode clipsToBounds:NO];
|
format.scale = self.scale;
|
||||||
UIImage *image = SDGraphicsGetImageFromCurrentImageContext();
|
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:size format:format];
|
||||||
SDGraphicsEndImageContext();
|
UIImage *image = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
||||||
|
[self sd_drawInRect:CGRectMake(0, 0, size.width, size.height) context:context scaleMode:scaleMode clipsToBounds:NO];
|
||||||
|
}];
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,116 +215,94 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
|
||||||
|
|
||||||
- (nullable UIImage *)sd_roundedCornerImageWithRadius:(CGFloat)cornerRadius corners:(SDRectCorner)corners borderWidth:(CGFloat)borderWidth borderColor:(nullable UIColor *)borderColor {
|
- (nullable UIImage *)sd_roundedCornerImageWithRadius:(CGFloat)cornerRadius corners:(SDRectCorner)corners borderWidth:(CGFloat)borderWidth borderColor:(nullable UIColor *)borderColor {
|
||||||
if (!self.CGImage) return nil;
|
if (!self.CGImage) return nil;
|
||||||
SDGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
|
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init];
|
||||||
CGContextRef context = SDGraphicsGetCurrentContext();
|
format.scale = self.scale;
|
||||||
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
|
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:self.size format:format];
|
||||||
|
UIImage *image = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
||||||
CGFloat minSize = MIN(self.size.width, self.size.height);
|
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
|
||||||
if (borderWidth < minSize / 2) {
|
|
||||||
#if SD_UIKIT || SD_WATCH
|
|
||||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
|
|
||||||
#else
|
|
||||||
NSBezierPath *path = [NSBezierPath sd_bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadius:cornerRadius];
|
|
||||||
#endif
|
|
||||||
[path closePath];
|
|
||||||
|
|
||||||
CGContextSaveGState(context);
|
CGFloat minSize = MIN(self.size.width, self.size.height);
|
||||||
[path addClip];
|
if (borderWidth < minSize / 2) {
|
||||||
[self drawInRect:rect];
|
|
||||||
CGContextRestoreGState(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (borderColor && borderWidth < minSize / 2 && borderWidth > 0) {
|
|
||||||
CGFloat strokeInset = (floor(borderWidth * self.scale) + 0.5) / self.scale;
|
|
||||||
CGRect strokeRect = CGRectInset(rect, strokeInset, strokeInset);
|
|
||||||
CGFloat strokeRadius = cornerRadius > self.scale / 2 ? cornerRadius - self.scale / 2 : 0;
|
|
||||||
#if SD_UIKIT || SD_WATCH
|
#if SD_UIKIT || SD_WATCH
|
||||||
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius, strokeRadius)];
|
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
|
||||||
#else
|
#else
|
||||||
NSBezierPath *path = [NSBezierPath sd_bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadius:strokeRadius];
|
NSBezierPath *path = [NSBezierPath sd_bezierPathWithRoundedRect:CGRectInset(rect, borderWidth, borderWidth) byRoundingCorners:corners cornerRadius:cornerRadius];
|
||||||
#endif
|
#endif
|
||||||
[path closePath];
|
[path closePath];
|
||||||
|
|
||||||
|
CGContextSaveGState(context);
|
||||||
|
[path addClip];
|
||||||
|
[self drawInRect:rect];
|
||||||
|
CGContextRestoreGState(context);
|
||||||
|
}
|
||||||
|
|
||||||
path.lineWidth = borderWidth;
|
if (borderColor && borderWidth < minSize / 2 && borderWidth > 0) {
|
||||||
[borderColor setStroke];
|
CGFloat strokeInset = (floor(borderWidth * self.scale) + 0.5) / self.scale;
|
||||||
[path stroke];
|
CGRect strokeRect = CGRectInset(rect, strokeInset, strokeInset);
|
||||||
}
|
CGFloat strokeRadius = cornerRadius > self.scale / 2 ? cornerRadius - self.scale / 2 : 0;
|
||||||
|
#if SD_UIKIT || SD_WATCH
|
||||||
UIImage *image = SDGraphicsGetImageFromCurrentImageContext();
|
UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius, strokeRadius)];
|
||||||
SDGraphicsEndImageContext();
|
#else
|
||||||
|
NSBezierPath *path = [NSBezierPath sd_bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadius:strokeRadius];
|
||||||
|
#endif
|
||||||
|
[path closePath];
|
||||||
|
|
||||||
|
path.lineWidth = borderWidth;
|
||||||
|
[borderColor setStroke];
|
||||||
|
[path stroke];
|
||||||
|
}
|
||||||
|
}];
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (nullable UIImage *)sd_rotatedImageWithAngle:(CGFloat)angle fitSize:(BOOL)fitSize {
|
- (nullable UIImage *)sd_rotatedImageWithAngle:(CGFloat)angle fitSize:(BOOL)fitSize {
|
||||||
if (!self.CGImage) return nil;
|
if (!self.CGImage) return nil;
|
||||||
size_t width = (size_t)CGImageGetWidth(self.CGImage);
|
size_t width = self.size.width;
|
||||||
size_t height = (size_t)CGImageGetHeight(self.CGImage);
|
size_t height = self.size.height;
|
||||||
CGRect newRect = CGRectApplyAffineTransform(CGRectMake(0., 0., width, height),
|
CGRect newRect = CGRectApplyAffineTransform(CGRectMake(0, 0, width, height),
|
||||||
fitSize ? CGAffineTransformMakeRotation(angle) : CGAffineTransformIdentity);
|
fitSize ? CGAffineTransformMakeRotation(angle) : CGAffineTransformIdentity);
|
||||||
|
|
||||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init];
|
||||||
CGContextRef context = CGBitmapContextCreate(NULL,
|
format.scale = self.scale;
|
||||||
(size_t)newRect.size.width,
|
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:newRect.size format:format];
|
||||||
(size_t)newRect.size.height,
|
UIImage *image = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
||||||
8,
|
CGContextSetShouldAntialias(context, true);
|
||||||
(size_t)newRect.size.width * 4,
|
CGContextSetAllowsAntialiasing(context, true);
|
||||||
colorSpace,
|
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
|
||||||
kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
|
CGContextTranslateCTM(context, +(newRect.size.width * 0.5), +(newRect.size.height * 0.5));
|
||||||
CGColorSpaceRelease(colorSpace);
|
#if SD_UIKIT
|
||||||
if (!context) return nil;
|
// Use UIKit coordinate system counterclockwise (⟲)
|
||||||
|
CGContextRotateCTM(context, -angle);
|
||||||
CGContextSetShouldAntialias(context, true);
|
|
||||||
CGContextSetAllowsAntialiasing(context, true);
|
|
||||||
CGContextSetInterpolationQuality(context, kCGInterpolationHigh);
|
|
||||||
|
|
||||||
CGContextTranslateCTM(context, +(newRect.size.width * 0.5), +(newRect.size.height * 0.5));
|
|
||||||
CGContextRotateCTM(context, angle);
|
|
||||||
|
|
||||||
CGContextDrawImage(context, CGRectMake(-(width * 0.5), -(height * 0.5), width, height), self.CGImage);
|
|
||||||
CGImageRef imgRef = CGBitmapContextCreateImage(context);
|
|
||||||
#if SD_UIKIT || SD_WATCH
|
|
||||||
UIImage *img = [UIImage imageWithCGImage:imgRef scale:self.scale orientation:self.imageOrientation];
|
|
||||||
#else
|
#else
|
||||||
UIImage *img = [[UIImage alloc] initWithCGImage:imgRef scale:self.scale orientation:kCGImagePropertyOrientationUp];
|
CGContextRotateCTM(context, angle);
|
||||||
#endif
|
#endif
|
||||||
CGImageRelease(imgRef);
|
|
||||||
CGContextRelease(context);
|
[self drawInRect:CGRectMake(-(width * 0.5), -(height * 0.5), width, height)];
|
||||||
return img;
|
}];
|
||||||
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (nullable UIImage *)sd_flippedImageWithHorizontal:(BOOL)horizontal vertical:(BOOL)vertical {
|
- (nullable UIImage *)sd_flippedImageWithHorizontal:(BOOL)horizontal vertical:(BOOL)vertical {
|
||||||
if (!self.CGImage) return nil;
|
if (!self.CGImage) return nil;
|
||||||
size_t width = (size_t)CGImageGetWidth(self.CGImage);
|
size_t width = self.size.width;
|
||||||
size_t height = (size_t)CGImageGetHeight(self.CGImage);
|
size_t height = self.size.height;
|
||||||
size_t bytesPerRow = width * 4;
|
|
||||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
|
||||||
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrderDefault | kCGImageAlphaPremultipliedFirst);
|
|
||||||
CGColorSpaceRelease(colorSpace);
|
|
||||||
if (!context) return nil;
|
|
||||||
|
|
||||||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.CGImage);
|
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init];
|
||||||
UInt8 *data = (UInt8 *)CGBitmapContextGetData(context);
|
format.scale = self.scale;
|
||||||
if (!data) {
|
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:self.size format:format];
|
||||||
CGContextRelease(context);
|
UIImage *image = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
||||||
return nil;
|
// Use UIKit coordinate system
|
||||||
}
|
if (horizontal) {
|
||||||
vImage_Buffer src = { data, height, width, bytesPerRow };
|
CGAffineTransform flipHorizontal = CGAffineTransformMake(-1, 0, 0, 1, width, 0);
|
||||||
vImage_Buffer dest = { data, height, width, bytesPerRow };
|
CGContextConcatCTM(context, flipHorizontal);
|
||||||
if (vertical) {
|
}
|
||||||
vImageVerticalReflect_ARGB8888(&src, &dest, kvImageBackgroundColorFill);
|
if (vertical) {
|
||||||
}
|
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, height);
|
||||||
if (horizontal) {
|
CGContextConcatCTM(context, flipVertical);
|
||||||
vImageHorizontalReflect_ARGB8888(&src, &dest, kvImageBackgroundColorFill);
|
}
|
||||||
}
|
[self drawInRect:CGRectMake(0, 0, width, height)];
|
||||||
CGImageRef imgRef = CGBitmapContextCreateImage(context);
|
}];
|
||||||
CGContextRelease(context);
|
return image;
|
||||||
#if SD_UIKIT || SD_WATCH
|
|
||||||
UIImage *img = [UIImage imageWithCGImage:imgRef scale:self.scale orientation:self.imageOrientation];
|
|
||||||
#else
|
|
||||||
UIImage *img = [[UIImage alloc] initWithCGImage:imgRef scale:self.scale orientation:kCGImagePropertyOrientationUp];
|
|
||||||
#endif
|
|
||||||
CGImageRelease(imgRef);
|
|
||||||
return img;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark - Image Blending
|
#pragma mark - Image Blending
|
||||||
|
@ -347,15 +327,15 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
|
||||||
// blend mode, see https://en.wikipedia.org/wiki/Alpha_compositing
|
// blend mode, see https://en.wikipedia.org/wiki/Alpha_compositing
|
||||||
CGBlendMode blendMode = kCGBlendModeSourceAtop;
|
CGBlendMode blendMode = kCGBlendModeSourceAtop;
|
||||||
|
|
||||||
SDGraphicsBeginImageContextWithOptions(size, NO, scale);
|
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init];
|
||||||
CGContextRef context = SDGraphicsGetCurrentContext();
|
format.scale = scale;
|
||||||
[self drawInRect:rect];
|
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:size format:format];
|
||||||
CGContextSetBlendMode(context, blendMode);
|
UIImage *image = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
||||||
CGContextSetFillColorWithColor(context, tintColor.CGColor);
|
[self drawInRect:rect];
|
||||||
CGContextFillRect(context, rect);
|
CGContextSetBlendMode(context, blendMode);
|
||||||
UIImage *image = SDGraphicsGetImageFromCurrentImageContext();
|
CGContextSetFillColorWithColor(context, tintColor.CGColor);
|
||||||
SDGraphicsEndImageContext();
|
CGContextFillRect(context, rect);
|
||||||
|
}];
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
expect(CGSizeEqualToSize(rotatedImage.size, self.testImage.size)).beTruthy();
|
expect(CGSizeEqualToSize(rotatedImage.size, self.testImage.size)).beTruthy();
|
||||||
// Fit size, may change size
|
// Fit size, may change size
|
||||||
rotatedImage = [self.testImage sd_rotatedImageWithAngle:angle fitSize:YES];
|
rotatedImage = [self.testImage sd_rotatedImageWithAngle:angle fitSize:YES];
|
||||||
CGSize rotatedSize = CGSizeMake(floor(300 * 1.414), floor(300 * 1.414)); // 45º, square length * sqrt(2)
|
CGSize rotatedSize = CGSizeMake(ceil(300 * 1.414), ceil(300 * 1.414)); // 45º, square length * sqrt(2)
|
||||||
expect(CGSizeEqualToSize(rotatedImage.size, rotatedSize)).beTruthy();
|
expect(CGSizeEqualToSize(rotatedImage.size, rotatedSize)).beTruthy();
|
||||||
// Check image not inversion
|
// Check image not inversion
|
||||||
UIColor *leftCenterColor = [rotatedImage sd_colorAtPoint:CGPointMake(60, 175)];
|
UIColor *leftCenterColor = [rotatedImage sd_colorAtPoint:CGPointMake(60, 175)];
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#import "SDDisplayLink.h"
|
#import "SDDisplayLink.h"
|
||||||
#import "SDInternalMacros.h"
|
#import "SDInternalMacros.h"
|
||||||
#import "SDFileAttributeHelper.h"
|
#import "SDFileAttributeHelper.h"
|
||||||
|
#import "UIColor+HexString.h"
|
||||||
|
|
||||||
@interface SDUtilsTests : SDTestCase
|
@interface SDUtilsTests : SDTestCase
|
||||||
|
|
||||||
|
@ -107,6 +108,32 @@
|
||||||
expect(hasAttr).beFalsy();
|
expect(hasAttr).beFalsy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testSDGraphicsImageRenderer {
|
||||||
|
// Main Screen
|
||||||
|
SDGraphicsImageRendererFormat *format = SDGraphicsImageRendererFormat.preferredFormat;
|
||||||
|
#if SD_UIKIT
|
||||||
|
CGFloat screenScale = [UIScreen mainScreen].scale;
|
||||||
|
#elif SD_MAC
|
||||||
|
CGFloat screenScale = [NSScreen mainScreen].backingScaleFactor;
|
||||||
|
#endif
|
||||||
|
expect(format.scale).equal(screenScale);
|
||||||
|
expect(format.opaque).beFalsy();
|
||||||
|
#if SD_UIKIT
|
||||||
|
expect(format.preferredRange).equal(SDGraphicsImageRendererFormatRangeAutomatic);
|
||||||
|
#elif SD_MAC
|
||||||
|
expect(format.preferredRange).equal(SDGraphicsImageRendererFormatRangeStandard);
|
||||||
|
#endif
|
||||||
|
CGSize size = CGSizeMake(100, 100);
|
||||||
|
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:size format:format];
|
||||||
|
UIColor *color = UIColor.redColor;
|
||||||
|
UIImage *image = [renderer imageWithActions:^(CGContextRef _Nonnull context) {
|
||||||
|
[color setFill];
|
||||||
|
CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
|
||||||
|
}];
|
||||||
|
expect(image.scale).equal(format.scale);
|
||||||
|
expect([[image sd_colorAtPoint:CGPointMake(50, 50)].sd_hexString isEqualToString:color.sd_hexString]).beTruthy();
|
||||||
|
}
|
||||||
|
|
||||||
- (void)testSDScaledImageForKey {
|
- (void)testSDScaledImageForKey {
|
||||||
// Test nil
|
// Test nil
|
||||||
expect(SDScaledImageForKey(nil, nil)).beNil();
|
expect(SDScaledImageForKey(nil, nil)).beNil();
|
||||||
|
|
|
@ -65,6 +65,7 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[];
|
||||||
#import <SDWebImage/SDImageFrame.h>
|
#import <SDWebImage/SDImageFrame.h>
|
||||||
#import <SDWebImage/SDImageCoderHelper.h>
|
#import <SDWebImage/SDImageCoderHelper.h>
|
||||||
#import <SDWebImage/SDImageGraphics.h>
|
#import <SDWebImage/SDImageGraphics.h>
|
||||||
|
#import <SDWebImage/SDGraphicsImageRenderer.h>
|
||||||
#import <SDWebImage/UIImage+GIF.h>
|
#import <SDWebImage/UIImage+GIF.h>
|
||||||
#import <SDWebImage/UIImage+ForceDecode.h>
|
#import <SDWebImage/UIImage+ForceDecode.h>
|
||||||
#import <SDWebImage/NSData+ImageContentType.h>
|
#import <SDWebImage/NSData+ImageContentType.h>
|
||||||
|
|
Loading…
Reference in New Issue