Merge pull request #2918 from dreampiggy/feature_transformer_CIImage

Support all transformer method on CIImage based UIImage/NSImage
This commit is contained in:
DreamPiggy 2020-01-03 20:09:06 +08:00 committed by GitHub
commit fafed705d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 318 additions and 62 deletions

View File

@ -19,6 +19,10 @@
The underlying Core Graphics image object. This will actually use `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; @property (nonatomic, readonly, nullable) CGImageRef CGImage;
/**
The underlying Core Image data. This will actually use `bestRepresentationForRect` with the image size to find the `NSCIImageRep`.
*/
@property (nonatomic, readonly, nullable) CIImage *CIImage;
/** /**
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. 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.
*/ */
@ -38,6 +42,16 @@ The underlying Core Graphics image object. This will actually use `CGImageForPro
*/ */
- (nonnull instancetype)initWithCGImage:(nonnull CGImageRef)cgImage scale:(CGFloat)scale orientation:(CGImagePropertyOrientation)orientation; - (nonnull instancetype)initWithCGImage:(nonnull CGImageRef)cgImage scale:(CGFloat)scale orientation:(CGImagePropertyOrientation)orientation;
/**
Initializes and returns an image object with the specified Core Image object. The representation is `NSCIImageRep`.
@param ciImage A Core Image image object
@param scale The image scale factor
@param orientation The orientation of the image data
@return The image object
*/
- (nonnull instancetype)initWithCIImage:(nonnull CIImage *)ciImage scale:(CGFloat)scale orientation:(CGImagePropertyOrientation)orientation;
/** /**
Returns an image object with the scale factor. The representation is created from the image data. 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 use `backingScaleFactor` as scale factor. @note The difference between these this and `initWithData:` is that `initWithData:` will always use `backingScaleFactor` as scale factor.

View File

@ -20,6 +20,15 @@
return cgImage; return cgImage;
} }
- (nullable CIImage *)CIImage {
NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);
NSImageRep *imageRep = [self bestRepresentationForRect:imageRect context:nil hints:nil];
if (![imageRep isKindOfClass:NSCIImageRep.class]) {
return nil;
}
return ((NSCIImageRep *)imageRep).CIImage;
}
- (CGFloat)scale { - (CGFloat)scale {
CGFloat scale = 1; CGFloat scale = 1;
NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height); NSRect imageRect = NSMakeRect(0, 0, self.size.width, self.size.height);
@ -65,6 +74,28 @@
return self; return self;
} }
- (instancetype)initWithCIImage:(nonnull CIImage *)ciImage scale:(CGFloat)scale orientation:(CGImagePropertyOrientation)orientation {
NSCIImageRep *imageRep;
if (orientation != kCGImagePropertyOrientationUp) {
CIImage *rotatedCIImage = [ciImage imageByApplyingOrientation:orientation];
imageRep = [[NSCIImageRep alloc] initWithCIImage:rotatedCIImage];
} else {
imageRep = [[NSCIImageRep alloc] initWithCIImage:ciImage];
}
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;
}
- (instancetype)initWithData:(nonnull NSData *)data scale:(CGFloat)scale { - (instancetype)initWithData:(nonnull NSData *)data scale:(CGFloat)scale {
NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithData:data]; NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithData:data];
if (!imageRep) { if (!imageRep) {

View File

@ -164,6 +164,21 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
return [UIColor colorWithRed:r green:g blue:b alpha:a]; return [UIColor colorWithRed:r green:g blue:b alpha:a];
} }
#if SD_UIKIT || SD_MAC
// Core Image Support
static inline CGImageRef _Nullable SDCGImageFromCIImage(CIImage * _Nonnull ciImage) {
CGImageRef imageRef = NULL;
if (@available(iOS 10, macOS 10.12, tvOS 10, *)) {
imageRef = ciImage.CGImage;
}
if (!imageRef) {
CIContext *context = [CIContext context];
imageRef = [context createCGImage:ciImage fromRect:ciImage.extent];
}
return imageRef;
}
#endif
@implementation UIImage (Transform) @implementation UIImage (Transform)
- (void)sd_drawInRect:(CGRect)rect context:(CGContextRef)context scaleMode:(SDImageScaleMode)scaleMode clipsToBounds:(BOOL)clips { - (void)sd_drawInRect:(CGRect)rect context:(CGContextRef)context scaleMode:(SDImageScaleMode)scaleMode clipsToBounds:(BOOL)clips {
@ -194,27 +209,45 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
} }
- (nullable UIImage *)sd_croppedImageWithRect:(CGRect)rect { - (nullable UIImage *)sd_croppedImageWithRect:(CGRect)rect {
if (!self.CGImage) return nil;
rect.origin.x *= self.scale; rect.origin.x *= self.scale;
rect.origin.y *= self.scale; rect.origin.y *= self.scale;
rect.size.width *= self.scale; rect.size.width *= self.scale;
rect.size.height *= self.scale; rect.size.height *= self.scale;
if (rect.size.width <= 0 || rect.size.height <= 0) return nil; if (rect.size.width <= 0 || rect.size.height <= 0) return nil;
CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
#if SD_UIKIT || SD_MAC
// CIImage shortcut
if (self.CIImage) {
CGRect croppingRect = CGRectMake(rect.origin.x, self.size.height - CGRectGetMaxY(rect), rect.size.width, rect.size.height);
CIImage *ciImage = [self.CIImage imageByCroppingToRect:croppingRect];
#if SD_UIKIT
UIImage *image = [UIImage imageWithCIImage:ciImage scale:self.scale orientation:self.imageOrientation];
#else
UIImage *image = [[UIImage alloc] initWithCIImage:ciImage scale:self.scale orientation:kCGImagePropertyOrientationUp];
#endif
return image;
}
#endif
CGImageRef imageRef = self.CGImage;
if (!imageRef) { if (!imageRef) {
return nil; return nil;
} }
CGImageRef croppedImageRef = CGImageCreateWithImageInRect(imageRef, rect);
if (!croppedImageRef) {
return nil;
}
#if SD_UIKIT || SD_WATCH #if SD_UIKIT || SD_WATCH
UIImage *image = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation]; UIImage *image = [UIImage imageWithCGImage:croppedImageRef scale:self.scale orientation:self.imageOrientation];
#else #else
UIImage *image = [[UIImage alloc] initWithCGImage:imageRef scale:self.scale orientation:kCGImagePropertyOrientationUp]; UIImage *image = [[UIImage alloc] initWithCGImage:croppedImageRef scale:self.scale orientation:kCGImagePropertyOrientationUp];
#endif #endif
CGImageRelease(imageRef); CGImageRelease(croppedImageRef);
return image; return image;
} }
- (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;
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init]; SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init];
format.scale = self.scale; format.scale = self.scale;
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:self.size format:format]; SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:self.size format:format];
@ -256,12 +289,33 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
} }
- (nullable UIImage *)sd_rotatedImageWithAngle:(CGFloat)angle fitSize:(BOOL)fitSize { - (nullable UIImage *)sd_rotatedImageWithAngle:(CGFloat)angle fitSize:(BOOL)fitSize {
if (!self.CGImage) return nil;
size_t width = self.size.width; size_t width = self.size.width;
size_t height = self.size.height; 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);
#if SD_UIKIT || SD_MAC
// CIImage shortcut
if (self.CIImage) {
CIImage *ciImage = self.CIImage;
if (fitSize) {
CGAffineTransform transform = CGAffineTransformMakeRotation(angle);
ciImage = [ciImage imageByApplyingTransform:transform];
} else {
CIFilter *filter = [CIFilter filterWithName:@"CIStraightenFilter"];
[filter setValue:ciImage forKey:kCIInputImageKey];
[filter setValue:@(angle) forKey:kCIInputAngleKey];
ciImage = filter.outputImage;
}
#if SD_UIKIT || SD_WATCH
UIImage *image = [UIImage imageWithCIImage:ciImage scale:self.scale orientation:self.imageOrientation];
#else
UIImage *image = [[UIImage alloc] initWithCIImage:ciImage scale:self.scale orientation:kCGImagePropertyOrientationUp];
#endif
return image;
}
#endif
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init]; SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init];
format.scale = self.scale; format.scale = self.scale;
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:newRect.size format:format]; SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:newRect.size format:format];
@ -283,10 +337,32 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
} }
- (nullable UIImage *)sd_flippedImageWithHorizontal:(BOOL)horizontal vertical:(BOOL)vertical { - (nullable UIImage *)sd_flippedImageWithHorizontal:(BOOL)horizontal vertical:(BOOL)vertical {
if (!self.CGImage) return nil;
size_t width = self.size.width; size_t width = self.size.width;
size_t height = self.size.height; size_t height = self.size.height;
#if SD_UIKIT || SD_MAC
// CIImage shortcut
if (self.CIImage) {
CGAffineTransform transform = CGAffineTransformIdentity;
// Use UIKit coordinate system
if (horizontal) {
CGAffineTransform flipHorizontal = CGAffineTransformMake(-1, 0, 0, 1, width, 0);
transform = CGAffineTransformConcat(transform, flipHorizontal);
}
if (vertical) {
CGAffineTransform flipVertical = CGAffineTransformMake(1, 0, 0, -1, 0, height);
transform = CGAffineTransformConcat(transform, flipVertical);
}
CIImage *ciImage = [self.CIImage imageByApplyingTransform:transform];
#if SD_UIKIT
UIImage *image = [UIImage imageWithCIImage:ciImage scale:self.scale orientation:self.imageOrientation];
#else
UIImage *image = [[UIImage alloc] initWithCIImage:ciImage scale:self.scale orientation:kCGImagePropertyOrientationUp];
#endif
return image;
}
#endif
SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init]; SDGraphicsImageRendererFormat *format = [[SDGraphicsImageRendererFormat alloc] init];
format.scale = self.scale; format.scale = self.scale;
SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:self.size format:format]; SDGraphicsImageRenderer *renderer = [[SDGraphicsImageRenderer alloc] initWithSize:self.size format:format];
@ -308,18 +384,30 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
#pragma mark - Image Blending #pragma mark - Image Blending
- (nullable UIImage *)sd_tintedImageWithColor:(nonnull UIColor *)tintColor { - (nullable UIImage *)sd_tintedImageWithColor:(nonnull UIColor *)tintColor {
if (!self.CGImage) return nil;
if (!tintColor.CGColor) return nil;
BOOL hasTint = CGColorGetAlpha(tintColor.CGColor) > __FLT_EPSILON__; BOOL hasTint = CGColorGetAlpha(tintColor.CGColor) > __FLT_EPSILON__;
if (!hasTint) { if (!hasTint) {
#if SD_UIKIT || SD_WATCH return self;
return [UIImage imageWithCGImage:self.CGImage scale:self.scale orientation:self.imageOrientation];
#else
return [[UIImage alloc] initWithCGImage:self.CGImage scale:self.scale orientation:kCGImagePropertyOrientationUp];
#endif
} }
#if SD_UIKIT || SD_MAC
// CIImage shortcut
if (self.CIImage) {
CIImage *ciImage = self.CIImage;
CIImage *colorImage = [CIImage imageWithColor:[[CIColor alloc] initWithColor:tintColor]];
colorImage = [colorImage imageByCroppingToRect:ciImage.extent];
CIFilter *filter = [CIFilter filterWithName:@"CISourceAtopCompositing"];
[filter setValue:colorImage forKey:kCIInputImageKey];
[filter setValue:ciImage forKey:kCIInputBackgroundImageKey];
ciImage = filter.outputImage;
#if SD_UIKIT
UIImage *image = [UIImage imageWithCIImage:ciImage scale:self.scale orientation:self.imageOrientation];
#else
UIImage *image = [[UIImage alloc] initWithCIImage:ciImage scale:self.scale orientation:kCGImagePropertyOrientationUp];
#endif
return image;
}
#endif
CGSize size = self.size; CGSize size = self.size;
CGRect rect = { CGPointZero, size }; CGRect rect = { CGPointZero, size };
CGFloat scale = self.scale; CGFloat scale = self.scale;
@ -340,10 +428,16 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
} }
- (nullable UIColor *)sd_colorAtPoint:(CGPoint)point { - (nullable UIColor *)sd_colorAtPoint:(CGPoint)point {
if (!self) { CGImageRef imageRef = NULL;
return nil; // CIImage compatible
#if SD_UIKIT || SD_MAC
if (self.CIImage) {
imageRef = SDCGImageFromCIImage(self.CIImage);
}
#endif
if (!imageRef) {
imageRef = self.CGImage;
} }
CGImageRef imageRef = self.CGImage;
if (!imageRef) { if (!imageRef) {
return nil; return nil;
} }
@ -384,10 +478,16 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
} }
- (nullable NSArray<UIColor *> *)sd_colorsWithRect:(CGRect)rect { - (nullable NSArray<UIColor *> *)sd_colorsWithRect:(CGRect)rect {
if (!self) { CGImageRef imageRef = NULL;
return nil; // CIImage compatible
#if SD_UIKIT || SD_MAC
if (self.CIImage) {
imageRef = SDCGImageFromCIImage(self.CIImage);
}
#endif
if (!imageRef) {
imageRef = self.CGImage;
} }
CGImageRef imageRef = self.CGImage;
if (!imageRef) { if (!imageRef) {
return nil; return nil;
} }
@ -451,18 +551,41 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
if (self.size.width < 1 || self.size.height < 1) { if (self.size.width < 1 || self.size.height < 1) {
return nil; return nil;
} }
if (!self.CGImage) {
return nil;
}
BOOL hasBlur = blurRadius > __FLT_EPSILON__; BOOL hasBlur = blurRadius > __FLT_EPSILON__;
if (!hasBlur) { if (!hasBlur) {
return self; return self;
} }
#if SD_UIKIT || SD_MAC
if (self.CIImage) {
CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"];
[filter setValue:self.CIImage forKey:kCIInputImageKey];
// Blur Radius use pixel count
[filter setValue:@(blurRadius / 2) forKey:kCIInputRadiusKey];
CIImage *ciImage = filter.outputImage;
ciImage = [ciImage imageByCroppingToRect:CGRectMake(0, 0, self.size.width, self.size.height)];
#if SD_UIKIT
UIImage *image = [UIImage imageWithCIImage:ciImage scale:self.scale orientation:self.imageOrientation];
#else
UIImage *image = [[UIImage alloc] initWithCIImage:ciImage scale:self.scale orientation:kCGImagePropertyOrientationUp];
#endif
return image;
}
#endif
CGFloat scale = self.scale; CGFloat scale = self.scale;
CGImageRef imageRef = self.CGImage; CGImageRef imageRef = self.CGImage;
//convert to BGRA if it isn't
if (CGImageGetBitsPerPixel(imageRef) != 32 ||
CGImageGetBitsPerComponent(imageRef) != 8 ||
!((CGImageGetBitmapInfo(imageRef) & kCGBitmapAlphaInfoMask))) {
SDGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
[self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)];
imageRef = SDGraphicsGetImageFromCurrentImageContext().CGImage;
SDGraphicsEndImageContext();
}
vImage_Buffer effect = {}, scratch = {}; vImage_Buffer effect = {}, scratch = {};
vImage_Buffer *input = NULL, *output = NULL; vImage_Buffer *input = NULL, *output = NULL;
@ -477,7 +600,7 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
}; };
vImage_Error err; vImage_Error err;
err = vImageBuffer_InitWithCGImage(&effect, &format, NULL, imageRef, kvImagePrintDiagnosticsToConsole); err = vImageBuffer_InitWithCGImage(&effect, &format, NULL, imageRef, kvImageNoFlags);
if (err != kvImageNoError) { if (err != kvImageNoError) {
NSLog(@"UIImage+Transform error: vImageBuffer_InitWithCGImage returned error code %zi for inputImage: %@", err, self); NSLog(@"UIImage+Transform error: vImageBuffer_InitWithCGImage returned error code %zi for inputImage: %@", err, self);
return nil; return nil;
@ -542,12 +665,19 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
#if SD_UIKIT || SD_MAC #if SD_UIKIT || SD_MAC
- (nullable UIImage *)sd_filteredImageWithFilter:(nonnull CIFilter *)filter { - (nullable UIImage *)sd_filteredImageWithFilter:(nonnull CIFilter *)filter {
if (!self.CGImage) return nil; CIImage *inputImage;
if (self.CIImage) {
CIContext *context = [CIContext context]; inputImage = self.CIImage;
CIImage *inputImage = [CIImage imageWithCGImage:self.CGImage]; } else {
CGImageRef imageRef = self.CGImage;
if (!imageRef) {
return nil;
}
inputImage = [CIImage imageWithCGImage:imageRef];
}
if (!inputImage) return nil; if (!inputImage) return nil;
CIContext *context = [CIContext context];
[filter setValue:inputImage forKey:kCIInputImageKey]; [filter setValue:inputImage forKey:kCIInputImageKey];
CIImage *outputImage = filter.outputImage; CIImage *outputImage = filter.outputImage;
if (!outputImage) return nil; if (!outputImage) return nil;

View File

@ -13,7 +13,8 @@
@interface SDImageTransformerTests : SDTestCase @interface SDImageTransformerTests : SDTestCase
@property (nonatomic, strong) UIImage *testImage; @property (nonatomic, strong) UIImage *testImageCG;
@property (nonatomic, strong) UIImage *testImageCI;
@end @end
@ -22,21 +23,37 @@
#pragma mark - UIImage+Transform #pragma mark - UIImage+Transform
// UIImage+Transform test is hard to write because it's more about visual effect. Current it's tied to the `TestImage.png`, please keep that image or write new test with new image // UIImage+Transform test is hard to write because it's more about visual effect. Current it's tied to the `TestImage.png`, please keep that image or write new test with new image
- (void)test01UIImageTransformResize { - (void)test01UIImageTransformResizeCG {
[self test01UIImageTransformResizeWithImage:self.testImageCG];
}
- (void)test01UIImageTransformResizeCI {
[self test01UIImageTransformResizeWithImage:self.testImageCI];
}
- (void)test01UIImageTransformResizeWithImage:(UIImage *)testImage {
CGSize scaleDownSize = CGSizeMake(200, 100); CGSize scaleDownSize = CGSizeMake(200, 100);
UIImage *scaledDownImage = [self.testImage sd_resizedImageWithSize:scaleDownSize scaleMode:SDImageScaleModeFill]; UIImage *scaledDownImage = [testImage sd_resizedImageWithSize:scaleDownSize scaleMode:SDImageScaleModeFill];
expect(CGSizeEqualToSize(scaledDownImage.size, scaleDownSize)).beTruthy(); expect(CGSizeEqualToSize(scaledDownImage.size, scaleDownSize)).beTruthy();
CGSize scaleUpSize = CGSizeMake(2000, 1000); CGSize scaleUpSize = CGSizeMake(2000, 1000);
UIImage *scaledUpImage = [self.testImage sd_resizedImageWithSize:scaleUpSize scaleMode:SDImageScaleModeAspectFit]; UIImage *scaledUpImage = [testImage sd_resizedImageWithSize:scaleUpSize scaleMode:SDImageScaleModeAspectFit];
expect(CGSizeEqualToSize(scaledUpImage.size, scaleUpSize)).beTruthy(); expect(CGSizeEqualToSize(scaledUpImage.size, scaleUpSize)).beTruthy();
// Check image not inversion // Check image not inversion
UIColor *topCenterColor = [scaledUpImage sd_colorAtPoint:CGPointMake(1000, 50)]; UIColor *topCenterColor = [scaledUpImage sd_colorAtPoint:CGPointMake(1000, 50)];
expect([topCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy(); expect([topCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy();
} }
- (void)test02UIImageTransformCrop { - (void)test02UIImageTransformCropCG {
[self test02UIImageTransformCropWithImage:self.testImageCG];
}
- (void)test02UIImageTransformCropCI {
[self test02UIImageTransformCropWithImage:self.testImageCI];
}
- (void)test02UIImageTransformCropWithImage:(UIImage *)testImage {
CGRect rect = CGRectMake(50, 10, 200, 200); CGRect rect = CGRectMake(50, 10, 200, 200);
UIImage *croppedImage = [self.testImage sd_croppedImageWithRect:rect]; UIImage *croppedImage = [testImage sd_croppedImageWithRect:rect];
expect(CGSizeEqualToSize(croppedImage.size, CGSizeMake(200, 200))).beTruthy(); expect(CGSizeEqualToSize(croppedImage.size, CGSizeMake(200, 200))).beTruthy();
UIColor *startColor = [croppedImage sd_colorAtPoint:CGPointZero]; UIColor *startColor = [croppedImage sd_colorAtPoint:CGPointZero];
expect([startColor.sd_hexString isEqualToString:[UIColor clearColor].sd_hexString]).beTruthy(); expect([startColor.sd_hexString isEqualToString:[UIColor clearColor].sd_hexString]).beTruthy();
@ -45,7 +62,15 @@
expect([topCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy(); expect([topCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy();
} }
- (void)test03UIImageTransformRoundedCorner { - (void)test03UIImageTransformRoundedCornerCG {
[self test03UIImageTransformRoundedCornerWithImage:self.testImageCG];
}
- (void)test03UIImageTransformRoundedCornerCI {
[self test03UIImageTransformRoundedCornerWithImage:self.testImageCI];
}
- (void)test03UIImageTransformRoundedCornerWithImage:(UIImage *)testImage {
CGFloat radius = 50; CGFloat radius = 50;
#if SD_UIKIT #if SD_UIKIT
SDRectCorner corners = UIRectCornerAllCorners; SDRectCorner corners = UIRectCornerAllCorners;
@ -54,7 +79,7 @@
#endif #endif
CGFloat borderWidth = 1; CGFloat borderWidth = 1;
UIColor *borderColor = [UIColor blackColor]; UIColor *borderColor = [UIColor blackColor];
UIImage *roundedCornerImage = [self.testImage sd_roundedCornerImageWithRadius:radius corners:corners borderWidth:borderWidth borderColor:borderColor]; UIImage *roundedCornerImage = [testImage sd_roundedCornerImageWithRadius:radius corners:corners borderWidth:borderWidth borderColor:borderColor];
expect(CGSizeEqualToSize(roundedCornerImage.size, CGSizeMake(300, 300))).beTruthy(); expect(CGSizeEqualToSize(roundedCornerImage.size, CGSizeMake(300, 300))).beTruthy();
UIColor *startColor = [roundedCornerImage sd_colorAtPoint:CGPointZero]; UIColor *startColor = [roundedCornerImage sd_colorAtPoint:CGPointZero];
expect([startColor.sd_hexString isEqualToString:[UIColor clearColor].sd_hexString]).beTruthy(); expect([startColor.sd_hexString isEqualToString:[UIColor clearColor].sd_hexString]).beTruthy();
@ -66,25 +91,42 @@
expect([topCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy(); expect([topCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy();
} }
- (void)test04UIImageTransformRotate { - (void)test04UIImageTransformRotateCG {
[self test04UIImageTransformRotateWithImage:self.testImageCG];
}
- (void)test04UIImageTransformRotateCI {
[self test04UIImageTransformRotateWithImage:self.testImageCI];
}
- (void)test04UIImageTransformRotateWithImage:(UIImage *)testImage {
CGFloat angle = M_PI_4; CGFloat angle = M_PI_4;
UIImage *rotatedImage = [self.testImage sd_rotatedImageWithAngle:angle fitSize:NO]; UIImage *rotatedImage = [testImage sd_rotatedImageWithAngle:angle fitSize:NO];
// Not fit size and no change // Not fit size and no change
expect(CGSizeEqualToSize(rotatedImage.size, self.testImage.size)).beTruthy(); expect(CGSizeEqualToSize(rotatedImage.size, testImage.size)).beTruthy();
// Fit size, may change size // Fit size, may change size
rotatedImage = [self.testImage sd_rotatedImageWithAngle:angle fitSize:YES]; rotatedImage = [testImage sd_rotatedImageWithAngle:angle fitSize:YES];
CGSize rotatedSize = CGSizeMake(ceil(300 * 1.414), ceil(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(rotatedImage.size.width - rotatedSize.width <= 1).beTruthy();
expect(rotatedImage.size.height - rotatedSize.height <= 1).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)];
expect([leftCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy(); expect([leftCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy();
} }
- (void)test05UIImageTransformFlip { - (void)test05UIImageTransformFlipCG {
[self test05UIImageTransformFlipWithImage:self.testImageCG];
}
- (void)test05UIImageTransformFlipCI {
[self test05UIImageTransformFlipWithImage:self.testImageCI];
}
- (void)test05UIImageTransformFlipWithImage:(UIImage *)testImage {
BOOL horizontal = YES; BOOL horizontal = YES;
BOOL vertical = YES; BOOL vertical = YES;
UIImage *flippedImage = [self.testImage sd_flippedImageWithHorizontal:horizontal vertical:vertical]; UIImage *flippedImage = [testImage sd_flippedImageWithHorizontal:horizontal vertical:vertical];
expect(CGSizeEqualToSize(flippedImage.size, self.testImage.size)).beTruthy(); expect(CGSizeEqualToSize(flippedImage.size, testImage.size)).beTruthy();
// Test pixel colors method here // Test pixel colors method here
UIColor *checkColor = [flippedImage sd_colorAtPoint:CGPointMake(75, 75)]; UIColor *checkColor = [flippedImage sd_colorAtPoint:CGPointMake(75, 75)];
expect(checkColor); expect(checkColor);
@ -98,10 +140,18 @@
expect([bottomCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy(); expect([bottomCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy();
} }
- (void)test06UIImageTransformTint { - (void)test06UIImageTransformTintCG {
[self test06UIImageTransformTintWithImage:self.testImageCG];
}
- (void)test06UIImageTransformTintCI {
[self test06UIImageTransformTintWithImage:self.testImageCI];
}
- (void)test06UIImageTransformTintWithImage:(UIImage *)testImage {
UIColor *tintColor = [UIColor blackColor]; UIColor *tintColor = [UIColor blackColor];
UIImage *tintedImage = [self.testImage sd_tintedImageWithColor:tintColor]; UIImage *tintedImage = [testImage sd_tintedImageWithColor:tintColor];
expect(CGSizeEqualToSize(tintedImage.size, self.testImage.size)).beTruthy(); expect(CGSizeEqualToSize(tintedImage.size, testImage.size)).beTruthy();
// Check center color, should keep clear // Check center color, should keep clear
UIColor *centerColor = [tintedImage sd_colorAtPoint:CGPointMake(150, 150)]; UIColor *centerColor = [tintedImage sd_colorAtPoint:CGPointMake(150, 150)];
expect([centerColor.sd_hexString isEqualToString:[UIColor clearColor].sd_hexString]); expect([centerColor.sd_hexString isEqualToString:[UIColor clearColor].sd_hexString]);
@ -113,10 +163,18 @@
expect([topCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy(); expect([topCenterColor.sd_hexString isEqualToString:[UIColor blackColor].sd_hexString]).beTruthy();
} }
- (void)test07UIImageTransformBlur { - (void)test07UIImageTransformBlurCG {
[self test07UIImageTransformBlurWithImage:self.testImageCG];
}
- (void)test07UIImageTransformBlurCI {
[self test07UIImageTransformBlurWithImage:self.testImageCI];
}
- (void)test07UIImageTransformBlurWithImage:(UIImage *)testImage {
CGFloat radius = 50; CGFloat radius = 50;
UIImage *blurredImage = [self.testImage sd_blurredImageWithRadius:radius]; UIImage *blurredImage = [testImage sd_blurredImageWithRadius:radius];
expect(CGSizeEqualToSize(blurredImage.size, self.testImage.size)).beTruthy(); expect(CGSizeEqualToSize(blurredImage.size, testImage.size)).beTruthy();
// Check left color, should be blurred // Check left color, should be blurred
UIColor *leftColor = [blurredImage sd_colorAtPoint:CGPointMake(80, 150)]; UIColor *leftColor = [blurredImage sd_colorAtPoint:CGPointMake(80, 150)];
// Hard-code from the output // Hard-code from the output
@ -124,14 +182,23 @@
expect([leftColor.sd_hexString isEqualToString:expectedColor.sd_hexString]); expect([leftColor.sd_hexString isEqualToString:expectedColor.sd_hexString]);
// Check rounded corner operation not inversion the image // Check rounded corner operation not inversion the image
UIColor *topCenterColor = [blurredImage sd_colorAtPoint:CGPointMake(150, 20)]; UIColor *topCenterColor = [blurredImage sd_colorAtPoint:CGPointMake(150, 20)];
expect([topCenterColor.sd_hexString isEqualToString:@"#9a430d06"]).beTruthy(); UIColor *bottomCenterColor = [blurredImage sd_colorAtPoint:CGPointMake(150, 280)];
expect([topCenterColor.sd_hexString isEqualToString:bottomCenterColor.sd_hexString]).beFalsy();
} }
- (void)test08UIImageTransformFilter { - (void)test08UIImageTransformFilterCG {
[self test08UIImageTransformFilterWithImage:self.testImageCG];
}
- (void)test08UIImageTransformFilterCI {
[self test08UIImageTransformFilterWithImage:self.testImageCI];
}
- (void)test08UIImageTransformFilterWithImage:(UIImage *)testImage {
// Invert color filter // Invert color filter
CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"]; CIFilter *filter = [CIFilter filterWithName:@"CIColorInvert"];
UIImage *filteredImage = [self.testImage sd_filteredImageWithFilter:filter]; UIImage *filteredImage = [testImage sd_filteredImageWithFilter:filter];
expect(CGSizeEqualToSize(filteredImage.size, self.testImage.size)).beTruthy(); expect(CGSizeEqualToSize(filteredImage.size, testImage.size)).beTruthy();
// Check left color, should be inverted // Check left color, should be inverted
UIColor *leftColor = [filteredImage sd_colorAtPoint:CGPointMake(80, 150)]; UIColor *leftColor = [filteredImage sd_colorAtPoint:CGPointMake(80, 150)];
// Hard-code from the output // Hard-code from the output
@ -198,7 +265,7 @@
NSString *transformerKey = [transformerKeys componentsJoinedByString:@"-"]; // SDImageTransformerKeySeparator NSString *transformerKey = [transformerKeys componentsJoinedByString:@"-"]; // SDImageTransformerKeySeparator
expect([pipelineTransformer.transformerKey isEqualToString:transformerKey]).beTruthy(); expect([pipelineTransformer.transformerKey isEqualToString:transformerKey]).beTruthy();
UIImage *transformedImage = [pipelineTransformer transformedImageWithImage:self.testImage forKey:@"Test"]; UIImage *transformedImage = [pipelineTransformer transformedImageWithImage:self.testImageCG forKey:@"Test"];
expect(transformedImage).notTo.beNil(); expect(transformedImage).notTo.beNil();
expect(CGSizeEqualToSize(transformedImage.size, cropRect.size)).beTruthy(); expect(CGSizeEqualToSize(transformedImage.size, cropRect.size)).beTruthy();
} }
@ -239,6 +306,8 @@
expect(SDTransformedKeyForKey(key, transformerKey)).equal(@"ftp://root:password@foo.com/image-SDImageFlippingTransformer(1,0).png"); expect(SDTransformedKeyForKey(key, transformerKey)).equal(@"ftp://root:password@foo.com/image-SDImageFlippingTransformer(1,0).png");
} }
#pragma mark - Coder Helper
- (void)test20CGImageCreateDecodedWithOrientation { - (void)test20CGImageCreateDecodedWithOrientation {
// Test EXIF orientation tag, you can open this image with `Preview.app`, open inspector (Command+I) and rotate (Command+L/R) to check // Test EXIF orientation tag, you can open this image with `Preview.app`, open inspector (Command+I) and rotate (Command+L/R) to check
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self testPNGPathForName:@"TestEXIF"]]; UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self testPNGPathForName:@"TestEXIF"]];
@ -331,11 +400,23 @@
#pragma mark - Helper #pragma mark - Helper
- (UIImage *)testImage { - (UIImage *)testImageCG {
if (!_testImage) { if (!_testImageCG) {
_testImage = [[UIImage alloc] initWithContentsOfFile:[self testPNGPathForName:@"TestImage"]]; _testImageCG = [[UIImage alloc] initWithContentsOfFile:[self testPNGPathForName:@"TestImage"]];
} }
return _testImage; return _testImageCG;
}
- (UIImage *)testImageCI {
if (!_testImageCI) {
CIImage *ciImage = [[CIImage alloc] initWithContentsOfURL:[NSURL fileURLWithPath:[self testPNGPathForName:@"TestImage"]]];
#if SD_UIKIT
_testImageCI = [[UIImage alloc] initWithCIImage:ciImage scale:1 orientation:UIImageOrientationUp];
#else
_testImageCI = [[UIImage alloc] initWithCIImage:ciImage scale:1 orientation:kCGImagePropertyOrientationUp];
#endif
}
return _testImageCI;
} }
- (NSString *)testPNGPathForName:(NSString *)name { - (NSString *)testPNGPathForName:(NSString *)name {