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:
DreamPiggy 2019-04-25 22:05:04 +08:00 committed by GitHub
commit 8dbc9ae8f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 122 additions and 22 deletions

View File

@ -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);

View File

@ -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:

View File

@ -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

View File

@ -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