From af8607be80c7284012fb94b7e01e50639593fd7e Mon Sep 17 00:00:00 2001 From: Owen Thomas Date: Fri, 11 Dec 2020 01:07:08 -0800 Subject: [PATCH 1/2] Allow comparison of optimized images with unoptimized Optimized images may contain the same image, but fewer color components and therefore fewer bits per row. This causes the current image comparison to fail even though the image is otherwise identical. --- .../Categories/UIImage+Compare.m | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/iOSSnapshotTestCaseCore/Categories/UIImage+Compare.m b/src/iOSSnapshotTestCaseCore/Categories/UIImage+Compare.m index cdd97da..f75d0c1 100644 --- a/src/iOSSnapshotTestCaseCore/Categories/UIImage+Compare.m +++ b/src/iOSSnapshotTestCaseCore/Categories/UIImage+Compare.m @@ -51,12 +51,13 @@ @implementation UIImage (Compare) - (BOOL)fb_compareWithImage:(UIImage *)image perPixelTolerance:(CGFloat)perPixelTolerance overallTolerance:(CGFloat)overallTolerance { CGSize referenceImageSize = CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage)); - CGSize imageSize = CGSizeMake(CGImageGetWidth(image.CGImage), CGImageGetHeight(image.CGImage)); - NSAssert(CGSizeEqualToSize(referenceImageSize, imageSize), @"Images must be same size."); + NSAssert(CGSizeEqualToSize(CGSizeMake(CGImageGetWidth(self.CGImage), CGImageGetHeight(self.CGImage)), referenceImageSize), + @"Images must be same size."); - // The images have the equal size, so we could use the smallest amount of bytes because of byte padding - size_t minBytesPerRow = MIN(CGImageGetBytesPerRow(self.CGImage), CGImageGetBytesPerRow(image.CGImage)); - size_t referenceImageSizeBytes = referenceImageSize.height * minBytesPerRow; + // Use larger of the two in case one has been optimized (fewer bits per component and/or bytes per row) + size_t bytesPerRow = MAX(CGImageGetBytesPerRow(image.CGImage), CGImageGetBytesPerRow(self.CGImage)); + size_t bitsPerComponent = MAX(CGImageGetBitsPerComponent(image.CGImage), CGImageGetBitsPerComponent(self.CGImage)); + size_t referenceImageSizeBytes = referenceImageSize.height * bytesPerRow; void *referenceImagePixels = calloc(1, referenceImageSizeBytes); void *imagePixels = calloc(1, referenceImageSizeBytes); @@ -69,16 +70,16 @@ - (BOOL)fb_compareWithImage:(UIImage *)image perPixelTolerance:(CGFloat)perPixel CGContextRef referenceImageContext = CGBitmapContextCreate(referenceImagePixels, referenceImageSize.width, referenceImageSize.height, - CGImageGetBitsPerComponent(self.CGImage), - minBytesPerRow, - CGImageGetColorSpace(self.CGImage), + bitsPerComponent, + bytesPerRow, + CGColorSpaceCreateDeviceRGB(), (CGBitmapInfo)kCGImageAlphaPremultipliedLast); CGContextRef imageContext = CGBitmapContextCreate(imagePixels, - imageSize.width, - imageSize.height, - CGImageGetBitsPerComponent(image.CGImage), - minBytesPerRow, - CGImageGetColorSpace(image.CGImage), + referenceImageSize.width, + referenceImageSize.height, + bitsPerComponent, + bytesPerRow, + CGColorSpaceCreateDeviceRGB(), (CGBitmapInfo)kCGImageAlphaPremultipliedLast); if (!referenceImageContext || !imageContext) { @@ -90,7 +91,7 @@ - (BOOL)fb_compareWithImage:(UIImage *)image perPixelTolerance:(CGFloat)perPixel } CGContextDrawImage(referenceImageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), self.CGImage); - CGContextDrawImage(imageContext, CGRectMake(0, 0, imageSize.width, imageSize.height), image.CGImage); + CGContextDrawImage(imageContext, CGRectMake(0, 0, referenceImageSize.width, referenceImageSize.height), image.CGImage); CGContextRelease(referenceImageContext); CGContextRelease(imageContext); From 8e9c24f17590013fce7612a2a7b8a78faec63e32 Mon Sep 17 00:00:00 2001 From: Owen Thomas Date: Fri, 22 Oct 2021 13:32:13 -0700 Subject: [PATCH 2/2] Add tests of crushed PNGs --- .../FBSnapshotControllerTests.m | 31 ++++++++++++++++++ .../Resources/rect_crushed.png | Bin 0 -> 3180 bytes 2 files changed, 31 insertions(+) create mode 100644 src/iOSSnapshotTestCaseTests/Resources/rect_crushed.png diff --git a/src/iOSSnapshotTestCaseTests/FBSnapshotControllerTests.m b/src/iOSSnapshotTestCaseTests/FBSnapshotControllerTests.m index 9331038..4031be9 100644 --- a/src/iOSSnapshotTestCaseTests/FBSnapshotControllerTests.m +++ b/src/iOSSnapshotTestCaseTests/FBSnapshotControllerTests.m @@ -196,6 +196,37 @@ - (void)testCompareReferenceImageWithLowPixelToleranceShouldMatch XCTAssertNil(error); } +- (void)testCompareReferenceImageToGrayscaleCrushedImageShouldBeEqual +{ + UIImage *referenceImage = [self _bundledImageNamed:@"rect" type:@"png"]; + XCTAssertNotNil(referenceImage); + + // rect_crushed was made by pngcrush version 1.8.13 with default options as shown below. Reduced file size by 67% + // FBSnapshotTestCaseTests$ pngcrush rect.png rect_crushed.png + UIImage *testImage = [self _bundledImageNamed:@"rect_crushed" type:@"png"]; + XCTAssertNotNil(testImage); + + id testClass = nil; + FBSnapshotTestController *controller = [[FBSnapshotTestController alloc] initWithTestClass:testClass]; + NSError *error = nil; + XCTAssertTrue([controller compareReferenceImage:referenceImage toImage:testImage overallTolerance:0 error:&error]); + XCTAssertNil(error); +} + +- (void)testCompareGrayscaleCrushedImageToReferenceImageShouldBeEqual +{ + UIImage *referenceImage = [self _bundledImageNamed:@"rect_crushed" type:@"png"]; + XCTAssertNotNil(referenceImage); + UIImage *testImage = [self _bundledImageNamed:@"rect" type:@"png"]; + XCTAssertNotNil(testImage); + + id testClass = nil; + FBSnapshotTestController *controller = [[FBSnapshotTestController alloc] initWithTestClass:testClass]; + NSError *error = nil; + XCTAssertTrue([controller compareReferenceImage:referenceImage toImage:testImage overallTolerance:0 error:&error]); + XCTAssertNil(error); +} + #pragma mark - Private helper methods - (UIImage *)_bundledImageNamed:(NSString *)name type:(NSString *)type diff --git a/src/iOSSnapshotTestCaseTests/Resources/rect_crushed.png b/src/iOSSnapshotTestCaseTests/Resources/rect_crushed.png new file mode 100644 index 0000000000000000000000000000000000000000..dbcf23074e38acf652c4cb07bf578c8523e04680 GIT binary patch literal 3180 zcmeHJJ*?wI5Z(vP@z6DZ1VR*64iE@A{z>dh7RTsa{J2DryO7JFTz8xGCb4k5#&%xv zs7{FHDnx;V&~;EB6jYQ@fdmp25>#}6M2$qn_$P^zIH!l6goH#C+v}P6zS-HC9q--l zwW}8opFTtgU2M0k9z1Qhx)Pry z*;HaKn=&s70jCtYgSG8+R?v-UE{LDFn@$UnVRU=~p=V23hz$FuK9hPZWTKv(K?$&yk?Gi1c;kK)o8l1#Hi(G_2KLk`){mM9P*}?I&@(Fd`mh*@`=7Uh$MY2jB05 z(4?|yz(~aG<-{`SHq4OX4(K}nsZ`Jj@y5(HrDbqS!FF`(^4QE?y|2hvt7BC?Z}w+ucrTq;0sp+f&XK*$#ZYeKP8Eejsx2 zO={dsnv(?$>On0PCYZO};^@G+(IyeuWZ5l`m}SFun}^%Z==4l1Ys)Q;Bpx30Id{+Y z?OBDEfpUDSLk~{Q2_0@Tvtmd^;tZ9RP3~Bmbn{dShi3B-YG`=?HxEDj;myzCOs==B zqyFIQKfY5A5W4WN@Z&3Ay!Oj;PY5snS^A*$>|=MWg2y+XeLDQAdvN`spMH*y_3h(p K*8MBD-~Ai-(2yhm literal 0 HcmV?d00001