Refactor API. Change that `imageRef` arg description to `CGImage` to match the common design pattern and make it clear to Swift user

This commit is contained in:
DreamPiggy 2018-04-11 08:16:39 +08:00
parent 7d50d61b37
commit cebf72d6d5
9 changed files with 47 additions and 81 deletions

View File

@ -15,11 +15,11 @@
@interface NSImage (Additions)
/**
The underlying Core Graphics image object. This will actually `CGImageForProposedRect` with the image size.
The underlying Core Graphics image object. This will actually use `CGImageForProposedRect` with the image size.
*/
@property (nonatomic, readonly, nullable) CGImageRef CGImage;
/**
The scale factor of the image. This wil actually use bitmap representation's size and pixel size to calculate the scale factor. If failed, use the default value 1.0. Should be greater than or equal to 1.0.
The scale factor of the image. This wil actually use `bestRepresentationForRect` with image size and pixel size to calculate the scale factor. If failed, use the default value 1.0. Should be greater than or equal to 1.0.
*/
@property (nonatomic, readonly) CGFloat scale;
@ -27,8 +27,8 @@ The underlying Core Graphics image object. This will actually `CGImageForPropose
/**
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 create a `NSCGImageSnapshotRep` but not `NSBitmapImageRep` instance. And it will always `backingScaleFactor` as scale factor.
@note 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`.
@note The difference between this and `initWithCGImage:size` is that `initWithCGImage:size` will create a `NSCGImageSnapshotRep` but not `NSBitmapImageRep` instance. And it will always use `backingScaleFactor` as scale factor.
@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
@param scale The image scale factor
@ -39,7 +39,7 @@ The underlying Core Graphics image object. This will actually `CGImageForPropose
/**
Returns an image object with the scale factor. The representation is created from the image data.
@note The difference between these this and `initWithData:` is that `initWithData:` will always `backingScaleFactor` as scale factor.
@note The difference between these this and `initWithData:` is that `initWithData:` will always use `backingScaleFactor` as scale factor.
@param data The image data
@param scale The image scale factor
@ -53,8 +53,6 @@ The underlying Core Graphics image object. This will actually `CGImageForPropose
// These methods' function is the same as `NSImage`'s function. For `NSBitmapImageRep`.
@property (nonatomic, readonly) CGFloat scale;
- (nonnull instancetype)initWithCGImage:(nonnull CGImageRef)cgImage scale:(CGFloat)scale orientation:(CGImagePropertyOrientation)orientation;
- (nullable instancetype)initWithData:(nonnull NSData *)data scale:(CGFloat)scale;

View File

@ -25,12 +25,17 @@
CGFloat scale = 1;
NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);
NSImageRep *imageRep = [self bestRepresentationForRect:imageRect context:nil hints:nil];
NSBitmapImageRep *bitmapImageRep;
if ([imageRep isKindOfClass:[NSBitmapImageRep class]]) {
bitmapImageRep = (NSBitmapImageRep *)imageRep;
}
if (bitmapImageRep) {
return bitmapImageRep.scale;
CGFloat width = imageRep.size.width;
CGFloat height = imageRep.size.height;
NSUInteger pixelWidth = imageRep.pixelsWide;
NSUInteger pixelHeight = imageRep.pixelsHigh;
if (width > 0 && height > 0) {
CGFloat widthScale = pixelWidth / width;
CGFloat heightScale = pixelHeight / height;
if (widthScale == heightScale && widthScale >= 1) {
// Protect for image object which custom the size.
scale = widthScale;
}
}
return scale;
@ -71,42 +76,13 @@
@end
@interface NSBitmapImageRep ()
@property (nonatomic, assign, readonly, nullable) CGImageSourceRef imageSource;
@end
@implementation NSBitmapImageRep (Additions)
- (CGImageSourceRef)imageSource {
if (_tiffData) {
return (__bridge CGImageSourceRef)(_tiffData);
}
return NULL;
}
- (CGFloat)scale {
CGFloat scale = 1;
CGFloat width = self.size.width;
CGFloat height = self.size.height;
NSUInteger pixelWidth = self.pixelsWide;
NSUInteger pixelHeight = self.pixelsHigh;
if (width > 0 && height > 0) {
CGFloat widthScale = pixelWidth / width;
CGFloat heightScale = pixelHeight / height;
if (widthScale == heightScale && widthScale >= 1) {
// Protect for image object which custom the size.
scale = widthScale;
}
}
return scale;
}
- (instancetype)initWithCGImage:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(CGImagePropertyOrientation)orientation {
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 imageRefCreateDecoded:cgImage orientation:orientation];
cgImage = [SDWebImageCoderHelper CGImageCreateDecoded:cgImage orientation:orientation];
self = [self initWithCGImage:cgImage];
CGImageRelease(cgImage);
} else {

View File

@ -297,7 +297,7 @@ FOUNDATION_STATIC_INLINE NSUInteger SDCacheCostForImage(UIImage *image) {
if (!data && image) {
// If we do not have any data to detect image format, check whether it contains alpha channel to use PNG or JPEG format
SDImageFormat format;
if ([SDWebImageCoderHelper imageRefContainsAlpha:image.CGImage]) {
if ([SDWebImageCoderHelper CGImageContainsAlpha:image.CGImage]) {
format = SDImageFormatPNG;
} else {
format = SDImageFormatJPEG;

View File

@ -396,7 +396,7 @@ const CFStringRef kCGImagePropertyAPNGUnclampedDelayTime = (__bridge CFStringRef
return nil;
}
// Image/IO create CGImage does not decompressed, so we do this because this is called background queue, this can avoid main queue block when rendering(especially when one more imageViews use the same image instance)
CGImageRef newImageRef = [SDWebImageCoderHelper imageRefCreateDecoded:imageRef];
CGImageRef newImageRef = [SDWebImageCoderHelper CGImageCreateDecoded:imageRef];
if (!newImageRef) {
newImageRef = imageRef;
} else {

View File

@ -33,7 +33,7 @@ FOUNDATION_EXPORT SDWebImageCoderOption _Nonnull const SDWebImageCoderDecodeScal
*/
FOUNDATION_EXPORT SDWebImageCoderOption _Nonnull const SDWebImageCoderEncodeFirstFrameOnly;
/**
A double value between 0.0-1.0 indicating the encode compression quality to produce the image data. If not provide, use 1.0. (NSNumber)
A double value between 0.0-1.0 indicating the encode compression quality to produce the image data. 1.0 resulting in no compression and 0.0 resulting in the maximum compression possible. If not provide, use 1.0. (NSNumber)
@note works for `SDWebImageCoder`
*/
FOUNDATION_EXPORT SDWebImageCoderOption _Nonnull const SDWebImageCoderEncodeCompressionQuality;

View File

@ -33,7 +33,7 @@
+ (NSArray<SDWebImageFrame *> * _Nullable)framesFromAnimatedImage:(UIImage * _Nullable)animatedImage NS_SWIFT_NAME(frames(from:));
/**
Return the shared device-dependent RGB color space.
Return the shared device-dependent RGB color space. This follows The Get Rule.
On iOS, it's created with deviceRGB (if available, use sRGB).
On macOS, it's from the screen colorspace (if failed, use deviceRGB)
Because it's shared, you should not retain or release this object.
@ -42,44 +42,36 @@
*/
+ (CGColorSpaceRef _Nonnull)colorSpaceGetDeviceRGB CF_RETURNS_NOT_RETAINED;
/**
Retuen the color space of the CGImage
@param imageRef The CGImage
@return The color space of CGImage, or if not supported, return the device-dependent RGB color space
*/
+ (CGColorSpaceRef _Nonnull)imageRefGetColorSpace:(_Nonnull CGImageRef)imageRef CF_RETURNS_NOT_RETAINED;
/**
Check whether CGImage contains alpha channel.
@param imageRef The CGImage
@param cgImage The CGImage
@return Return YES if CGImage contains alpha channel, otherwise return NO
*/
+ (BOOL)imageRefContainsAlpha:(_Nonnull CGImageRef)imageRef;
+ (BOOL)CGImageContainsAlpha:(_Nonnull CGImageRef)cgImage;
/**
Create a decoded CGImage by the provided CGImage. This follows The Create Rule and you are response to call release after usage.
It will detect whether image contains alpha channel, then create a new bitmap context with the same size of image, and draw it. This can ensure that the image do not need extra decoding after been set to the imageView.
@note This actually call `imageRefCreateDecoded:orientation` with the Up orientation.
@note This actually call `CGImageCreateDecoded:orientation:` with the Up orientation.
@param imageRef The CGImage
@param cgImage The CGImage
@return A new created decoded image
*/
+ (CGImageRef _Nullable)imageRefCreateDecoded:(_Nonnull CGImageRef)imageRef CF_RETURNS_RETAINED;
+ (CGImageRef _Nullable)CGImageCreateDecoded:(_Nonnull CGImageRef)cgImage CF_RETURNS_RETAINED;
/**
Create a decoded CGImage by the provided CGImage and orientation. This follows The Create Rule and you are response to call release after usage.
It will detect whether image contains alpha channel, then create a new bitmap context with the same size of image, and draw it. This can ensure that the image do not need extra decoding after been set to the imageView.
@param imageRef The CGImage
@param orientation The image orientation.
@param cgImage The CGImage
@param orientation The EXIF image orientation.
@return A new created decoded image
*/
+ (CGImageRef _Nullable)imageRefCreateDecoded:(_Nonnull CGImageRef)imageRef orientation:(CGImagePropertyOrientation)orientation CF_RETURNS_RETAINED;
+ (CGImageRef _Nullable)CGImageCreateDecoded:(_Nonnull CGImageRef)cgImage orientation:(CGImagePropertyOrientation)orientation CF_RETURNS_RETAINED;
/**
Return the decoded image by the provided image. This one unlike `imageRefCreateDecoded:`, will not decode the image which contains alpha channel or animated image
Return the decoded image by the provided image. This one unlike `CGImageCreateDecoded:`, will not decode the image which contains alpha channel or animated image
@param image The image to be decoded
@return The decoded image
*/

View File

@ -236,29 +236,29 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
return colorspaceRef;
}
+ (BOOL)imageRefContainsAlpha:(CGImageRef)imageRef {
if (!imageRef) {
+ (BOOL)CGImageContainsAlpha:(CGImageRef)cgImage {
if (!cgImage) {
return NO;
}
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imageRef);
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage);
BOOL hasAlpha = !(alphaInfo == kCGImageAlphaNone ||
alphaInfo == kCGImageAlphaNoneSkipFirst ||
alphaInfo == kCGImageAlphaNoneSkipLast);
return hasAlpha;
}
+ (CGImageRef)imageRefCreateDecoded:(CGImageRef)imageRef {
return [self imageRefCreateDecoded:imageRef orientation:kCGImagePropertyOrientationUp];
+ (CGImageRef)CGImageCreateDecoded:(CGImageRef)cgImage {
return [self CGImageCreateDecoded:cgImage orientation:kCGImagePropertyOrientationUp];
}
+ (CGImageRef)imageRefCreateDecoded:(CGImageRef)imageRef orientation:(CGImagePropertyOrientation)orientation {
if (!imageRef) {
+ (CGImageRef)CGImageCreateDecoded:(CGImageRef)cgImage orientation:(CGImagePropertyOrientation)orientation {
if (!cgImage) {
return NULL;
}
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
if (width == 0 || height == 0) return NULL;
BOOL hasAlpha = [self imageRefContainsAlpha:imageRef];
BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
// iOS prefer BGRA8888 (premultiplied) or BGRX8888 bitmapInfo for screen rendering, which is same as `UIGraphicsBeginImageContext()` or `- [CALayer drawInContext:]`
// Through you can use any supported bitmapInfo (see: https://developer.apple.com/library/content/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB ) and let Core Graphics reorder it when you call `CGContextDrawImage`
// But since our build-in coders use this bitmapInfo, this can have a little performance benefit
@ -287,7 +287,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
break;
}
CGContextConcatCTM(context, transform);
CGContextDrawImage(context, rect, imageRef);
CGContextDrawImage(context, rect, cgImage);
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
CGContextRelease(context);
@ -302,7 +302,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
return image;
}
CGImageRef imageRef = [self imageRefCreateDecoded:image.CGImage];
CGImageRef imageRef = [self CGImageCreateDecoded:image.CGImage];
if (!imageRef) {
return image;
}
@ -529,7 +529,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
return NO;
}
CGImageRef imageRef = image.CGImage;
BOOL hasAlpha = [self imageRefContainsAlpha:imageRef];
BOOL hasAlpha = [self CGImageContainsAlpha:imageRef];
// do not decode images with alpha
if (hasAlpha) {
return NO;
@ -570,7 +570,7 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
}
#endif
static CGAffineTransform SDCGContextTransformFromOrientation(CGImagePropertyOrientation orientation, CGSize size) {
static inline CGAffineTransform SDCGContextTransformFromOrientation(CGImagePropertyOrientation orientation, CGSize size) {
// Inspiration from @libfeihu
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.

View File

@ -388,7 +388,7 @@
return nil;
}
// Image/IO create CGImage does not decode, so we do this because this is called background queue, this can avoid main queue block when rendering(especially when one more imageViews use the same image instance)
CGImageRef newImageRef = [SDWebImageCoderHelper imageRefCreateDecoded:imageRef];
CGImageRef newImageRef = [SDWebImageCoderHelper CGImageCreateDecoded:imageRef];
if (!newImageRef) {
newImageRef = imageRef;
} else {

View File

@ -211,7 +211,7 @@
}
if (format == SDImageFormatUndefined) {
BOOL hasAlpha = [SDWebImageCoderHelper imageRefContainsAlpha:image.CGImage];
BOOL hasAlpha = [SDWebImageCoderHelper CGImageContainsAlpha:image.CGImage];
if (hasAlpha) {
format = SDImageFormatPNG;
} else {