Merge pull request #2708 from dreampiggy/bugfix_CGImage_create_orientation_wrong_ratio
Fix that `CGImageCreateDecoded:orientation:` use the wrong aspect ratio when orientation is left/leftMirrored/right/rightMirrored
This commit is contained in:
commit
8dbc9ae8f4
|
@ -239,36 +239,40 @@ static const CGFloat kDestSeemOverlap = 2.0f; // the numbers of pixels to over
|
|||
size_t width = CGImageGetWidth(cgImage);
|
||||
size_t height = CGImageGetHeight(cgImage);
|
||||
if (width == 0 || height == 0) return NULL;
|
||||
BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
|
||||
// iOS prefer BGRA8888 (premultiplied) or BGRX8888 bitmapInfo for screen rendering, which is same as `UIGraphicsBeginImageContext()` or `- [CALayer drawInContext:]`
|
||||
// Though 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
|
||||
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
|
||||
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
|
||||
CGContextRef context = CGBitmapContextCreate(NULL, width, height, 8, 0, [self colorSpaceGetDeviceRGB], bitmapInfo);
|
||||
if (!context) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Apply transform
|
||||
CGAffineTransform transform = SDCGContextTransformFromOrientation(orientation, CGSizeMake(width, height));
|
||||
CGRect rect;
|
||||
size_t newWidth;
|
||||
size_t newHeight;
|
||||
switch (orientation) {
|
||||
case kCGImagePropertyOrientationLeft:
|
||||
case kCGImagePropertyOrientationLeftMirrored:
|
||||
case kCGImagePropertyOrientationRight:
|
||||
case kCGImagePropertyOrientationRightMirrored: {
|
||||
// These orientation should swap width & height
|
||||
rect = CGRectMake(0, 0, height, width);
|
||||
newWidth = height;
|
||||
newHeight = width;
|
||||
}
|
||||
break;
|
||||
default: {
|
||||
rect = CGRectMake(0, 0, width, height);
|
||||
newWidth = width;
|
||||
newHeight = height;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
BOOL hasAlpha = [self CGImageContainsAlpha:cgImage];
|
||||
// iOS prefer BGRA8888 (premultiplied) or BGRX8888 bitmapInfo for screen rendering, which is same as `UIGraphicsBeginImageContext()` or `- [CALayer drawInContext:]`
|
||||
// Though 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
|
||||
CGBitmapInfo bitmapInfo = kCGBitmapByteOrder32Host;
|
||||
bitmapInfo |= hasAlpha ? kCGImageAlphaPremultipliedFirst : kCGImageAlphaNoneSkipFirst;
|
||||
CGContextRef context = CGBitmapContextCreate(NULL, newWidth, newHeight, 8, 0, [self colorSpaceGetDeviceRGB], bitmapInfo);
|
||||
if (!context) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Apply transform
|
||||
CGAffineTransform transform = SDCGContextTransformFromOrientation(orientation, CGSizeMake(newWidth, newHeight));
|
||||
CGContextConcatCTM(context, transform);
|
||||
CGContextDrawImage(context, rect, cgImage);
|
||||
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage); // The rect is bounding box of CGImage, don't swap width & height
|
||||
CGImageRef newImageRef = CGBitmapContextCreateImage(context);
|
||||
CGContextRelease(context);
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
|
|||
// Get alpha info, byteOrder info
|
||||
CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
|
||||
CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
|
||||
CGFloat r = 0, g = 0, b = 0, a = 255.0;
|
||||
CGFloat r = 0, g = 0, b = 0, a = 1;
|
||||
|
||||
BOOL byteOrderNormal = NO;
|
||||
switch (byteOrderInfo) {
|
||||
|
@ -153,7 +153,7 @@ static inline UIColor * SDGetColorFromPixel(Pixel_8888 pixel, CGBitmapInfo bitma
|
|||
break;
|
||||
case kCGImageAlphaOnly: {
|
||||
// A
|
||||
a = pixel[0];
|
||||
a = pixel[0] / 255.0;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
3226ECBC20754F7700FAFACF /* SDWebImageTestDownloadOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = 3226ECBA20754F7700FAFACF /* SDWebImageTestDownloadOperation.m */; };
|
||||
323B8E1F20862322008952BE /* SDWebImageTestLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 323B8E1E20862322008952BE /* SDWebImageTestLoader.m */; };
|
||||
323B8E2020862322008952BE /* SDWebImageTestLoader.m in Sources */ = {isa = PBXBuildFile; fileRef = 323B8E1E20862322008952BE /* SDWebImageTestLoader.m */; };
|
||||
324047442271956F007C53E1 /* TestEXIF.png in Resources */ = {isa = PBXBuildFile; fileRef = 324047432271956F007C53E1 /* TestEXIF.png */; };
|
||||
324047452271956F007C53E1 /* TestEXIF.png in Resources */ = {isa = PBXBuildFile; fileRef = 324047432271956F007C53E1 /* TestEXIF.png */; };
|
||||
3254C32020641077008D1022 /* SDImageTransformerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3254C31F20641077008D1022 /* SDImageTransformerTests.m */; };
|
||||
3254C32120641077008D1022 /* SDImageTransformerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3254C31F20641077008D1022 /* SDImageTransformerTests.m */; };
|
||||
3264FF2F205D42CB00F6BD48 /* SDWebImageTestTransformer.m in Sources */ = {isa = PBXBuildFile; fileRef = 3264FF2E205D42CB00F6BD48 /* SDWebImageTestTransformer.m */; };
|
||||
|
@ -79,6 +81,7 @@
|
|||
3226ECBA20754F7700FAFACF /* SDWebImageTestDownloadOperation.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestDownloadOperation.m; sourceTree = "<group>"; };
|
||||
323B8E1D20862322008952BE /* SDWebImageTestLoader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestLoader.h; sourceTree = "<group>"; };
|
||||
323B8E1E20862322008952BE /* SDWebImageTestLoader.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestLoader.m; sourceTree = "<group>"; };
|
||||
324047432271956F007C53E1 /* TestEXIF.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TestEXIF.png; sourceTree = "<group>"; };
|
||||
3254C31F20641077008D1022 /* SDImageTransformerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDImageTransformerTests.m; sourceTree = "<group>"; };
|
||||
3264FF2D205D42CB00F6BD48 /* SDWebImageTestTransformer.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDWebImageTestTransformer.h; sourceTree = "<group>"; };
|
||||
3264FF2E205D42CB00F6BD48 /* SDWebImageTestTransformer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SDWebImageTestTransformer.m; sourceTree = "<group>"; };
|
||||
|
@ -170,6 +173,7 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
433BBBBA1D7EFA8B0086B6E9 /* MonochromeTestImage.jpg */,
|
||||
324047432271956F007C53E1 /* TestEXIF.png */,
|
||||
433BBBB61D7EF8200086B6E9 /* TestImage.gif */,
|
||||
5F7F38AC1AE2A77A00B0E330 /* TestImage.jpg */,
|
||||
43828A441DA67F9900000E62 /* TestImageLarge.jpg */,
|
||||
|
@ -352,6 +356,7 @@
|
|||
327054E3206CEFF3006EA328 /* TestImageAnimated.apng in Resources */,
|
||||
32B99EA3203B31360017FD66 /* TestImage.gif in Resources */,
|
||||
328BAF2C2240C08E00FC70DD /* Test-Release.xcconfig in Resources */,
|
||||
324047452271956F007C53E1 /* TestEXIF.png in Resources */,
|
||||
328BAF2E2240C08E00FC70DD /* Test-Debug.xcconfig in Resources */,
|
||||
32B99EA4203B31360017FD66 /* TestImage.jpg in Resources */,
|
||||
32B99EA6203B31360017FD66 /* TestImage.png in Resources */,
|
||||
|
@ -379,6 +384,7 @@
|
|||
433BBBB91D7EF8260086B6E9 /* TestImage.png in Resources */,
|
||||
327054E2206CEFF3006EA328 /* TestImageAnimated.apng in Resources */,
|
||||
433BBBBB1D7EFA8B0086B6E9 /* MonochromeTestImage.jpg in Resources */,
|
||||
324047442271956F007C53E1 /* TestEXIF.png in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
|
@ -208,18 +208,108 @@
|
|||
expect(SDTransformedKeyForKey(key, transformerKey)).equal(@"ftp://root:password@foo.com/image-SDImageFlippingTransformer(1,0).png");
|
||||
}
|
||||
|
||||
- (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
|
||||
UIImage *image = [[UIImage alloc] initWithContentsOfFile:[self testPNGPathForName:@"TestEXIF"]];
|
||||
CGImageRef originalCGImage = image.CGImage;
|
||||
expect(image).notTo.beNil();
|
||||
|
||||
// Check the longest side of "F" point color
|
||||
UIColor *pointColor = [UIColor colorWithRed:0 green:0 blue:0 alpha:1];
|
||||
|
||||
CGImageRef upCGImage = [SDImageCoderHelper CGImageCreateDecoded:originalCGImage orientation:kCGImagePropertyOrientationUp];
|
||||
#if SD_UIKIT
|
||||
UIImage *upImage = [[UIImage alloc] initWithCGImage:upCGImage];
|
||||
#else
|
||||
UIImage *upImage = [[UIImage alloc] initWithCGImage:upCGImage size:NSZeroSize];
|
||||
#endif
|
||||
expect([[upImage sd_colorAtPoint:CGPointMake(40, 160)].sd_hexString isEqualToString:pointColor.sd_hexString]).beTruthy();
|
||||
expect(upImage.size).equal(CGSizeMake(150, 200));
|
||||
CGImageRelease(upCGImage);
|
||||
|
||||
CGImageRef upMirroredCGImage = [SDImageCoderHelper CGImageCreateDecoded:originalCGImage orientation:kCGImagePropertyOrientationUpMirrored];
|
||||
#if SD_UIKIT
|
||||
UIImage *upMirroredImage = [[UIImage alloc] initWithCGImage:upMirroredCGImage];
|
||||
#else
|
||||
UIImage *upMirroredImage = [[UIImage alloc] initWithCGImage:upMirroredCGImage size:NSZeroSize];
|
||||
#endif
|
||||
expect([[upMirroredImage sd_colorAtPoint:CGPointMake(110, 160)].sd_hexString isEqualToString:pointColor.sd_hexString]).beTruthy();
|
||||
expect(upMirroredImage.size).equal(CGSizeMake(150, 200));
|
||||
CGImageRelease(upMirroredCGImage);
|
||||
|
||||
CGImageRef downCGImage = [SDImageCoderHelper CGImageCreateDecoded:originalCGImage orientation:kCGImagePropertyOrientationDown];
|
||||
#if SD_UIKIT
|
||||
UIImage *downImage = [[UIImage alloc] initWithCGImage:downCGImage];
|
||||
#else
|
||||
UIImage *downImage = [[UIImage alloc] initWithCGImage:downCGImage size:NSZeroSize];
|
||||
#endif
|
||||
expect([[downImage sd_colorAtPoint:CGPointMake(110, 30)].sd_hexString isEqualToString:pointColor.sd_hexString]).beTruthy();
|
||||
expect(downImage.size).equal(CGSizeMake(150, 200));
|
||||
CGImageRelease(downCGImage);
|
||||
|
||||
CGImageRef downMirrorerdCGImage = [SDImageCoderHelper CGImageCreateDecoded:originalCGImage orientation:kCGImagePropertyOrientationDownMirrored];
|
||||
#if SD_UIKIT
|
||||
UIImage *downMirroredImage = [[UIImage alloc] initWithCGImage:downMirrorerdCGImage];
|
||||
#else
|
||||
UIImage *downMirroredImage = [[UIImage alloc] initWithCGImage:downMirrorerdCGImage size:NSZeroSize];
|
||||
#endif
|
||||
expect([[downMirroredImage sd_colorAtPoint:CGPointMake(40, 30)].sd_hexString isEqualToString:pointColor.sd_hexString]).beTruthy();
|
||||
expect(downMirroredImage.size).equal(CGSizeMake(150, 200));
|
||||
CGImageRelease(downMirrorerdCGImage);
|
||||
|
||||
CGImageRef leftMirroredCGImage = [SDImageCoderHelper CGImageCreateDecoded:originalCGImage orientation:kCGImagePropertyOrientationLeftMirrored];
|
||||
#if SD_UIKIT
|
||||
UIImage *leftMirroredImage = [[UIImage alloc] initWithCGImage:leftMirroredCGImage];
|
||||
#else
|
||||
UIImage *leftMirroredImage = [[UIImage alloc] initWithCGImage:leftMirroredCGImage size:NSZeroSize];
|
||||
#endif
|
||||
expect([[leftMirroredImage sd_colorAtPoint:CGPointMake(160, 40)].sd_hexString isEqualToString:pointColor.sd_hexString]).beTruthy();
|
||||
expect(leftMirroredImage.size).equal(CGSizeMake(200, 150));
|
||||
CGImageRelease(leftMirroredCGImage);
|
||||
|
||||
CGImageRef rightCGImage = [SDImageCoderHelper CGImageCreateDecoded:originalCGImage orientation:kCGImagePropertyOrientationRight];
|
||||
#if SD_UIKIT
|
||||
UIImage *rightImage = [[UIImage alloc] initWithCGImage:rightCGImage];
|
||||
#else
|
||||
UIImage *rightImage = [[UIImage alloc] initWithCGImage:rightCGImage size:NSZeroSize];
|
||||
#endif
|
||||
expect([[rightImage sd_colorAtPoint:CGPointMake(30, 40)].sd_hexString isEqualToString:pointColor.sd_hexString]).beTruthy();
|
||||
expect(rightImage.size).equal(CGSizeMake(200, 150));
|
||||
CGImageRelease(rightCGImage);
|
||||
|
||||
CGImageRef rightMirroredCGImage = [SDImageCoderHelper CGImageCreateDecoded:originalCGImage orientation:kCGImagePropertyOrientationRightMirrored];
|
||||
#if SD_UIKIT
|
||||
UIImage *rightMirroredImage = [[UIImage alloc] initWithCGImage:rightMirroredCGImage];
|
||||
#else
|
||||
UIImage *rightMirroredImage = [[UIImage alloc] initWithCGImage:rightMirroredCGImage size:NSZeroSize];
|
||||
#endif
|
||||
expect([[rightMirroredImage sd_colorAtPoint:CGPointMake(30, 110)].sd_hexString isEqualToString:pointColor.sd_hexString]).beTruthy();
|
||||
expect(rightMirroredImage.size).equal(CGSizeMake(200, 150));
|
||||
CGImageRelease(rightMirroredCGImage);
|
||||
|
||||
CGImageRef leftCGImage = [SDImageCoderHelper CGImageCreateDecoded:originalCGImage orientation:kCGImagePropertyOrientationLeft];
|
||||
#if SD_UIKIT
|
||||
UIImage *leftImage = [[UIImage alloc] initWithCGImage:leftCGImage];
|
||||
#else
|
||||
UIImage *leftImage = [[UIImage alloc] initWithCGImage:leftCGImage size:NSZeroSize];
|
||||
#endif
|
||||
expect([[leftImage sd_colorAtPoint:CGPointMake(160, 110)].sd_hexString isEqualToString:pointColor.sd_hexString]).beTruthy();
|
||||
expect(leftImage.size).equal(CGSizeMake(200, 150));
|
||||
CGImageRelease(leftCGImage);
|
||||
}
|
||||
|
||||
#pragma mark - Helper
|
||||
|
||||
- (UIImage *)testImage {
|
||||
if (!_testImage) {
|
||||
_testImage = [[UIImage alloc] initWithContentsOfFile:[self testPNGPath]];
|
||||
_testImage = [[UIImage alloc] initWithContentsOfFile:[self testPNGPathForName:@"TestImage"]];
|
||||
}
|
||||
return _testImage;
|
||||
}
|
||||
|
||||
- (NSString *)testPNGPath {
|
||||
- (NSString *)testPNGPathForName:(NSString *)name {
|
||||
NSBundle *testBundle = [NSBundle bundleForClass:[self class]];
|
||||
return [testBundle pathForResource:@"TestImage" ofType:@"png"];
|
||||
return [testBundle pathForResource:name ofType:@"png"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
Loading…
Reference in New Issue