Skip to content

Commit

Permalink
Added Tolerance on individual pixel level (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
JerryTheIntern authored and alanzeino committed Jan 10, 2019
1 parent 8c5aaff commit 8c5bdf3
Show file tree
Hide file tree
Showing 10 changed files with 343 additions and 54 deletions.
6 changes: 6 additions & 0 deletions FBSnapshotTestCase.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
1335641B1B59C3F500A4E4BF /* UIImage+Snapshot.m in Sources */ = {isa = PBXBuildFile; fileRef = 133564151B59C3F500A4E4BF /* UIImage+Snapshot.m */; };
13CBB39D1AEE013900B6ADBA /* FBSnapshotTestCasePlatform.h in Headers */ = {isa = PBXBuildFile; fileRef = 13CBB39B1AEE013900B6ADBA /* FBSnapshotTestCasePlatform.h */; settings = {ATTRIBUTES = (Public, ); }; };
13CBB39E1AEE013900B6ADBA /* FBSnapshotTestCasePlatform.m in Sources */ = {isa = PBXBuildFile; fileRef = 13CBB39C1AEE013900B6ADBA /* FBSnapshotTestCasePlatform.m */; };
42F2B74420C0D7A400ABED24 /* rect_shade.png in Resources */ = {isa = PBXBuildFile; fileRef = 42F2B74320C0D7A400ABED24 /* rect_shade.png */; };
42F2B74520C0D7A400ABED24 /* rect_shade.png in Resources */ = {isa = PBXBuildFile; fileRef = 42F2B74320C0D7A400ABED24 /* rect_shade.png */; };
827137841C63AB7000354E42 /* FBSnapshotTestCase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8271377A1C63AB6F00354E42 /* FBSnapshotTestCase.framework */; };
827137911C63ABE900354E42 /* UIImage+Compare.h in Headers */ = {isa = PBXBuildFile; fileRef = 133564101B59C3F500A4E4BF /* UIImage+Compare.h */; };
827137921C63ABF000354E42 /* UIImage+Diff.h in Headers */ = {isa = PBXBuildFile; fileRef = 133564121B59C3F500A4E4BF /* UIImage+Diff.h */; };
Expand Down Expand Up @@ -75,6 +77,7 @@
133564151B59C3F500A4E4BF /* UIImage+Snapshot.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Snapshot.m"; sourceTree = "<group>"; };
13CBB39B1AEE013900B6ADBA /* FBSnapshotTestCasePlatform.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FBSnapshotTestCasePlatform.h; sourceTree = "<group>"; };
13CBB39C1AEE013900B6ADBA /* FBSnapshotTestCasePlatform.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FBSnapshotTestCasePlatform.m; sourceTree = "<group>"; };
42F2B74320C0D7A400ABED24 /* rect_shade.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rect_shade.png; sourceTree = "<group>"; };
8271377A1C63AB6F00354E42 /* FBSnapshotTestCase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSnapshotTestCase.framework; sourceTree = BUILT_PRODUCTS_DIR; };
827137831C63AB7000354E42 /* FBSnapshotTestCase tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "FBSnapshotTestCase tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
B31987F01AB782D000B0A900 /* FBSnapshotTestCase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = FBSnapshotTestCase.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -191,6 +194,7 @@
B31987FF1AB782D100B0A900 /* Tests */ = {
isa = PBXGroup;
children = (
42F2B74320C0D7A400ABED24 /* rect_shade.png */,
B76C68271C6BD68100586E5B /* rect.png */,
E5C2CD611B1F399A00669887 /* square_with_pixel.png */,
B32447D91AB78B5E00B1D6FF /* square_with_text.png */,
Expand Down Expand Up @@ -379,6 +383,7 @@
827137A21C63AC0D00354E42 /* square-copy.png in Resources */,
827137A31C63AC0D00354E42 /* square.png in Resources */,
827137A11C63AC0900354E42 /* square_with_text.png in Resources */,
42F2B74520C0D7A400ABED24 /* rect_shade.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -398,6 +403,7 @@
E5C2CD621B1F399A00669887 /* square_with_pixel.png in Resources */,
B32447DE1AB78B5E00B1D6FF /* square.png in Resources */,
B32447DD1AB78B5E00B1D6FF /* square-copy.png in Resources */,
42F2B74420C0D7A400ABED24 /* rect_shade.png in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
2 changes: 1 addition & 1 deletion FBSnapshotTestCase/Categories/UIImage+Compare.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ NS_ASSUME_NONNULL_BEGIN

@interface UIImage (Compare)

- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance;
- (BOOL)fb_compareWithImage:(UIImage *)image pixelTolerance:(CGFloat)pixelTolerance tolerance:(CGFloat)tolerance;

@end

Expand Down
96 changes: 72 additions & 24 deletions FBSnapshotTestCase/Categories/UIImage+Compare.m
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

@implementation UIImage (Compare)

- (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
- (BOOL)fb_compareWithImage:(UIImage *)image pixelTolerance:(CGFloat)pixelTolerance tolerance:(CGFloat)tolerance
{
NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size.");

Expand Down Expand Up @@ -93,34 +93,20 @@ - (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
CGContextRelease(imageContext);

BOOL imageEqual = YES;
FBComparePixel *p1 = referenceImagePixels;
FBComparePixel *p2 = imagePixels;

// Do a fast compare if we can
if (tolerance == 0) {
if (tolerance == 0 && pixelTolerance == 0) {
imageEqual = (memcmp(referenceImagePixels, imagePixels, referenceImageSizeBytes) == 0);
} else {
const NSUInteger pixelCount = referenceImageSize.width * referenceImageSize.height;
// Go through each pixel in turn and see if it is different
const NSInteger pixelCount = referenceImageSize.width * referenceImageSize.height;

FBComparePixel *p1 = referenceImagePixels;
FBComparePixel *p2 = imagePixels;

NSInteger numDiffPixels = 0;
for (int n = 0; n < pixelCount; ++n) {
// If this pixel is different, increment the pixel diff count and see
// if we have hit our limit.
if (p1->raw != p2->raw) {
numDiffPixels++;

CGFloat percent = (CGFloat)numDiffPixels / pixelCount;
if (percent > tolerance) {
imageEqual = NO;
break;
}
}

p1++;
p2++;
}
imageEqual = [self _compareAllPixelsWithPixelTolerance:pixelTolerance
tolerance:tolerance
pixelCount:pixelCount
referencePixels:p1
imagePixels:p2];
}

free(referenceImagePixels);
Expand All @@ -129,4 +115,66 @@ - (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance
return imageEqual;
}

- (BOOL)_comparePixelWithPixelTolerance:(CGFloat)pixelTolerance
referencePixel:(FBComparePixel *)referencePixel
imagePixel:(FBComparePixel *)imagePixel
{
if (referencePixel->raw == imagePixel->raw) {
return YES;
} else if (pixelTolerance == 0) {
return NO;
}

CGFloat redPercentDiff = [self _calculatePercentDifferenceForReferencePixelComponent:referencePixel->pixels.red
imagePixelComponent:imagePixel->pixels.red];
CGFloat greenPercentDiff = [self _calculatePercentDifferenceForReferencePixelComponent:referencePixel->pixels.green
imagePixelComponent:imagePixel->pixels.green];
CGFloat bluePercentDiff = [self _calculatePercentDifferenceForReferencePixelComponent:referencePixel->pixels.blue
imagePixelComponent:imagePixel->pixels.blue];
CGFloat alphaPercentDiff = [self _calculatePercentDifferenceForReferencePixelComponent:referencePixel->pixels.alpha
imagePixelComponent:imagePixel->pixels.alpha];

BOOL anyDifferencesFound = (redPercentDiff > pixelTolerance ||
greenPercentDiff > pixelTolerance ||
bluePercentDiff > pixelTolerance ||
alphaPercentDiff > pixelTolerance);

return !anyDifferencesFound;
}

- (CGFloat)_calculatePercentDifferenceForReferencePixelComponent:(char)p1
imagePixelComponent:(char)p2
{
NSInteger referencePixelComponent = (unsigned char)p1;
NSInteger imagePixelComponent = (unsigned char)p2;
NSUInteger componentDifference = ABS(referencePixelComponent - imagePixelComponent);
return (CGFloat)componentDifference / 256;
}

- (BOOL)_compareAllPixelsWithPixelTolerance:(CGFloat)pixelTolerance
tolerance:(CGFloat)tolerance
pixelCount:(NSUInteger)pixelCount
referencePixels:(FBComparePixel *)referencePixel
imagePixels:(FBComparePixel *)imagePixel
{
NSUInteger numDiffPixels = 0;
for (NSUInteger n = 0; n < pixelCount; ++n) {
// If this pixel is different, increment the pixel diff count and see
// if we have hit our limit.
BOOL isIdenticalPixel = [self _comparePixelWithPixelTolerance:pixelTolerance referencePixel:referencePixel imagePixel:imagePixel];
if (!isIdenticalPixel) {
numDiffPixels++;

CGFloat percent = (CGFloat)numDiffPixels / (CGFloat)pixelCount;
if (percent > tolerance) {
return NO;
}
}

referencePixel++;
imagePixel++;
}
return YES;
}

@end

0 comments on commit 8c5bdf3

Please sign in to comment.