diff --git a/FBSnapshotTestCase/Categories/UIImage+Compare.m b/FBSnapshotTestCase/Categories/UIImage+Compare.m index c997f57..a2b20b7 100644 --- a/FBSnapshotTestCase/Categories/UIImage+Compare.m +++ b/FBSnapshotTestCase/Categories/UIImage+Compare.m @@ -46,11 +46,11 @@ @implementation UIImage (Compare) - (BOOL)fb_compareWithImage:(UIImage *)image tolerance:(CGFloat)tolerance { - NSAssert(CGSizeEqualToSize(self.size, image.size), @"Images must be same size."); - 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."); + // 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; diff --git a/FBSnapshotTestCase/Categories/UIImage+Diff.m b/FBSnapshotTestCase/Categories/UIImage+Diff.m index ebb72fe..c72ba3b 100644 --- a/FBSnapshotTestCase/Categories/UIImage+Diff.m +++ b/FBSnapshotTestCase/Categories/UIImage+Diff.m @@ -38,7 +38,7 @@ - (UIImage *)fb_diffWithImage:(UIImage *)image return nil; } CGSize imageSize = CGSizeMake(MAX(self.size.width, image.size.width), MAX(self.size.height, image.size.height)); - UIGraphicsBeginImageContextWithOptions(imageSize, YES, 0); + UIGraphicsBeginImageContextWithOptions(imageSize, YES, self.scale); CGContextRef context = UIGraphicsGetCurrentContext(); [self drawInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; CGContextSetAlpha(context, 0.5); diff --git a/FBSnapshotTestCase/Categories/UIImage+Snapshot.h b/FBSnapshotTestCase/Categories/UIImage+Snapshot.h index b0d5b26..e345b06 100644 --- a/FBSnapshotTestCase/Categories/UIImage+Snapshot.h +++ b/FBSnapshotTestCase/Categories/UIImage+Snapshot.h @@ -13,12 +13,12 @@ @interface UIImage (Snapshot) /// Uses renderInContext: to get a snapshot of the layer. -+ (UIImage *)fb_imageForLayer:(CALayer *)layer; ++ (UIImage *)fb_imageForLayer:(CALayer *)layer scale:(CGFloat)scale; /// Uses renderInContext: to get a snapshot of the view layer. -+ (UIImage *)fb_imageForViewLayer:(UIView *)view; ++ (UIImage *)fb_imageForViewLayer:(UIView *)view scale:(CGFloat)scale; /// Uses drawViewHierarchyInRect: to get a snapshot of the view and adds the view into a window if needed. -+ (UIImage *)fb_imageForView:(UIView *)view; ++ (UIImage *)fb_imageForView:(UIView *)view scale:(CGFloat)scale; @end diff --git a/FBSnapshotTestCase/Categories/UIImage+Snapshot.m b/FBSnapshotTestCase/Categories/UIImage+Snapshot.m index 2f3a4c3..4f67e86 100644 --- a/FBSnapshotTestCase/Categories/UIImage+Snapshot.m +++ b/FBSnapshotTestCase/Categories/UIImage+Snapshot.m @@ -13,13 +13,13 @@ @implementation UIImage (Snapshot) -+ (UIImage *)fb_imageForLayer:(CALayer *)layer ++ (UIImage *)fb_imageForLayer:(CALayer *)layer scale:(CGFloat)scale { CGRect bounds = layer.bounds; NSAssert1(CGRectGetWidth(bounds), @"Zero width for layer %@", layer); NSAssert1(CGRectGetHeight(bounds), @"Zero height for layer %@", layer); - UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); + UIGraphicsBeginImageContextWithOptions(bounds.size, NO, scale); CGContextRef context = UIGraphicsGetCurrentContext(); NSAssert1(context, @"Could not generate context for layer %@", layer); CGContextSaveGState(context); @@ -32,13 +32,13 @@ + (UIImage *)fb_imageForLayer:(CALayer *)layer return snapshot; } -+ (UIImage *)fb_imageForViewLayer:(UIView *)view ++ (UIImage *)fb_imageForViewLayer:(UIView *)view scale:(CGFloat)scale { [view layoutIfNeeded]; - return [self fb_imageForLayer:view.layer]; + return [self fb_imageForLayer:view.layer scale:scale]; } -+ (UIImage *)fb_imageForView:(UIView *)view ++ (UIImage *)fb_imageForView:(UIView *)view scale:(CGFloat)scale { // If the input view is already a UIWindow, then just use that. Otherwise wrap in a window. UIWindow *window = [view isKindOfClass:[UIWindow class]] ? (UIWindow *)view : view.window; @@ -58,7 +58,7 @@ + (UIImage *)fb_imageForView:(UIView *)view NSAssert1(CGRectGetWidth(bounds), @"Zero width for view %@", view); NSAssert1(CGRectGetHeight(bounds), @"Zero height for view %@", view); - UIGraphicsBeginImageContextWithOptions(bounds.size, NO, 0); + UIGraphicsBeginImageContextWithOptions(bounds.size, NO, scale); [view drawViewHierarchyInRect:view.bounds afterScreenUpdates:YES]; UIImage *snapshot = UIGraphicsGetImageFromCurrentImageContext(); diff --git a/FBSnapshotTestCase/FBSnapshotTestCase.h b/FBSnapshotTestCase/FBSnapshotTestCase.h index e78762c..668a876 100644 --- a/FBSnapshotTestCase/FBSnapshotTestCase.h +++ b/FBSnapshotTestCase/FBSnapshotTestCase.h @@ -122,6 +122,11 @@ */ @property (readwrite, nonatomic, assign) BOOL usesDrawViewHierarchyInRect; +/** + When set to values different than 0, it uses a specific scale for generating and comparing images rather than using the main screen's default scale. + */ +@property (readwrite, nonatomic, assign) CGFloat manualScale; + - (void)setUp NS_REQUIRES_SUPER; - (void)tearDown NS_REQUIRES_SUPER; diff --git a/FBSnapshotTestCase/FBSnapshotTestCase.m b/FBSnapshotTestCase/FBSnapshotTestCase.m index abd54fb..ea0e6b3 100644 --- a/FBSnapshotTestCase/FBSnapshotTestCase.m +++ b/FBSnapshotTestCase/FBSnapshotTestCase.m @@ -74,6 +74,17 @@ - (void)setUsesDrawViewHierarchyInRect:(BOOL)usesDrawViewHierarchyInRect _snapshotController.usesDrawViewHierarchyInRect = usesDrawViewHierarchyInRect; } +- (void)setManualScale:(CGFloat)manualScale +{ + NSAssert1(_snapshotController, @"%s cannot be called before [super setUp]", __FUNCTION__); + _snapshotController.manualScale = manualScale; +} + +- (CGFloat)manualScale +{ + return _snapshotController.manualScale; +} + #pragma mark - Public API - (NSString *)snapshotVerifyViewOrLayer:(id)viewOrLayer @@ -184,7 +195,6 @@ - (NSString *)getReferenceImageDirectoryWithDefault:(NSString *)dir return [[NSBundle bundleForClass:self.class].resourcePath stringByAppendingPathComponent:@"ReferenceImages"]; } - #pragma mark - Private API - (BOOL)_compareSnapshotOfViewOrLayer:(id)viewOrLayer diff --git a/FBSnapshotTestCase/FBSnapshotTestController.h b/FBSnapshotTestCase/FBSnapshotTestController.h index daccacf..e137a37 100644 --- a/FBSnapshotTestCase/FBSnapshotTestController.h +++ b/FBSnapshotTestCase/FBSnapshotTestController.h @@ -85,6 +85,11 @@ extern NSString *const FBDiffedImageKey; */ @property (readwrite, nonatomic, copy) NSString *referenceImagesDirectory; +/** + When set to values different than 0, it uses a specific scale for generating and comparing images rather than using the main screen's default scale. + */ +@property (readwrite, nonatomic, assign) CGFloat manualScale; + /** @param testClass The subclass of FBSnapshotTestCase that is using this controller. @returns An instance of FBSnapshotTestController. diff --git a/FBSnapshotTestCase/FBSnapshotTestController.m b/FBSnapshotTestCase/FBSnapshotTestController.m index 3e53c7d..fc3e493 100644 --- a/FBSnapshotTestCase/FBSnapshotTestController.m +++ b/FBSnapshotTestCase/FBSnapshotTestController.m @@ -106,7 +106,8 @@ - (UIImage *)referenceImageForSelector:(SEL)selector { NSString *filePath = [self _referenceFilePathForSelector:selector identifier:identifier]; UIImage *image = [UIImage imageWithContentsOfFile:filePath]; - if (nil == image && NULL != errorPtr) { + CGFloat scale = (self.manualScale > 0 ? self.manualScale : [[UIScreen mainScreen] scale]); + if ((nil == image && NULL != errorPtr) || (image && image.scale != scale)) { BOOL exists = [_fileManager fileExistsAtPath:filePath]; if (!exists) { *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain @@ -130,7 +131,13 @@ - (BOOL)compareReferenceImage:(UIImage *)referenceImage tolerance:(CGFloat)tolerance error:(NSError **)errorPtr { - BOOL sameImageDimensions = CGSizeEqualToSize(referenceImage.size, image.size); + CGSize referenceImagePixelSize = referenceImage.size; + referenceImagePixelSize = CGSizeMake(referenceImagePixelSize.width * referenceImage.scale, + referenceImagePixelSize.height * referenceImage.scale); + CGSize imagePixelSize = image.size; + imagePixelSize = CGSizeMake(imagePixelSize.width * image.scale, + imagePixelSize.height * image.scale); + BOOL sameImageDimensions = CGSizeEqualToSize(referenceImagePixelSize, imagePixelSize); if (sameImageDimensions && [referenceImage fb_compareWithImage:image tolerance:tolerance]) { return YES; } @@ -138,7 +145,7 @@ - (BOOL)compareReferenceImage:(UIImage *)referenceImage if (NULL != errorPtr) { NSString *errorDescription = sameImageDimensions ? @"Images different" : @"Images different sizes"; NSString *errorReason = sameImageDimensions ? [NSString stringWithFormat:@"image pixels differed by more than %.2f%% from the reference image", tolerance * 100] - : [NSString stringWithFormat:@"referenceImage:%@, image:%@", NSStringFromCGSize(referenceImage.size), NSStringFromCGSize(image.size)]; + : [NSString stringWithFormat:@"referenceImage:%@, image:%@", NSStringFromCGSize(referenceImagePixelSize), NSStringFromCGSize(imagePixelSize)]; FBSnapshotTestControllerErrorCode errorCode = sameImageDimensions ? FBSnapshotTestControllerErrorCodeImagesDifferent : FBSnapshotTestControllerErrorCodeImagesDifferentSizes; *errorPtr = [NSError errorWithDomain:FBSnapshotTestControllerErrorDomain @@ -241,8 +248,9 @@ - (NSString *)_fileNameForSelector:(SEL)selector fileName = FBDeviceAgnosticNormalizedFileNameFromOption(fileName, self.agnosticOptions); } - if ([[UIScreen mainScreen] scale] > 1) { - fileName = [fileName stringByAppendingFormat:@"@%.fx", [[UIScreen mainScreen] scale]]; + CGFloat scale = (self.manualScale > 0 ? self.manualScale : [[UIScreen mainScreen] scale]); + if (scale > 1) { + fileName = [fileName stringByAppendingFormat:@"@%.fx", scale]; } fileName = [fileName stringByAppendingPathExtension:@"png"]; return fileName; @@ -347,12 +355,12 @@ - (UIImage *)_imageForViewOrLayer:(id)viewOrLayer { if ([viewOrLayer isKindOfClass:[UIView class]]) { if (_usesDrawViewHierarchyInRect) { - return [UIImage fb_imageForView:viewOrLayer]; + return [UIImage fb_imageForView:viewOrLayer scale:self.manualScale]; } else { - return [UIImage fb_imageForViewLayer:viewOrLayer]; + return [UIImage fb_imageForViewLayer:viewOrLayer scale:self.manualScale]; } } else if ([viewOrLayer isKindOfClass:[CALayer class]]) { - return [UIImage fb_imageForLayer:viewOrLayer]; + return [UIImage fb_imageForLayer:viewOrLayer scale:self.manualScale]; } else { [NSException raise:@"Only UIView and CALayer classes can be snapshotted" format:@"%@", viewOrLayer]; }