Skip to content

Commit a4875fc

Browse files
committed
Compare test textures by pixel diff
1 parent 3cb0c3d commit a4875fc

1 file changed

Lines changed: 92 additions & 2 deletions

File tree

Tests/vgerTests/testUtils.mm

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
// Copyright © 2021 Audulus LLC. All rights reserved.
22

33
#include "testUtils.h"
4+
#include <algorithm>
5+
#include <cstdlib>
46
#include <vector>
57
#import <ImageIO/ImageIO.h>
68
#import <UniformTypeIdentifiers/UniformTypeIdentifiers.h>
79

10+
static constexpr int kMaxPixelDifference = 2;
11+
812
void writeCGImage(CGImageRef image, CFURLRef url) {
913
auto dest = CGImageDestinationCreateWithURL(url, (CFStringRef) UTTypePNG.identifier, 1, nil);
1014
CGImageDestinationAddImage(dest, image, nil);
@@ -102,13 +106,53 @@ @implementation checkTextureBundleFinder
102106

103107
@end
104108

109+
static CGImageRef createImage(NSURL* url) {
110+
auto source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, nil);
111+
if(!source) {
112+
return nil;
113+
}
114+
115+
auto image = CGImageSourceCreateImageAtIndex(source, 0, nil);
116+
CFRelease(source);
117+
return image;
118+
}
119+
120+
static bool getImagePixels(CGImageRef image, std::vector<UInt8>& pixels) {
121+
if(!image) {
122+
return false;
123+
}
124+
125+
auto w = CGImageGetWidth(image);
126+
auto h = CGImageGetHeight(image);
127+
pixels.assign(4*w*h, 0);
128+
129+
auto colorSpace = CGColorSpaceCreateDeviceRGB();
130+
auto bitmapInfo = static_cast<CGBitmapInfo>(kCGBitmapByteOrder32Big) |
131+
static_cast<CGBitmapInfo>(kCGImageAlphaPremultipliedLast);
132+
auto context = CGBitmapContextCreate(pixels.data(),
133+
w,
134+
h,
135+
8,
136+
4*w,
137+
colorSpace,
138+
bitmapInfo);
139+
CGColorSpaceRelease(colorSpace);
140+
141+
if(!context) {
142+
return false;
143+
}
144+
145+
CGContextDrawImage(context, CGRectMake(0, 0, w, h), image);
146+
CGContextRelease(context);
147+
return true;
148+
}
149+
105150
bool checkTexture(id<MTLTexture> texture, NSString* name) {
106151

107152
auto tmpURL = [NSFileManager.defaultManager.temporaryDirectory URLByAppendingPathComponent:name];
108153
NSLog(@"saving to %@", tmpURL);
109154
CGImageRef image = createImage(texture);
110155
writeCGImage(image, (__bridge CFURLRef)tmpURL);
111-
CGImageRelease(image);
112156

113157
// Get URL for baseline.
114158
NSString* path = @"Contents/Resources/vger_vgerTests.bundle/Contents/Resources/images/";
@@ -117,13 +161,59 @@ bool checkTexture(id<MTLTexture> texture, NSString* name) {
117161
auto baselineURL = [bundle.bundleURL URLByAppendingPathComponent:path];
118162
NSLog(@"checking against baseline %@", baselineURL);
119163

120-
bool equal = [NSFileManager.defaultManager contentsEqualAtPath:baselineURL.path andPath:tmpURL.path];
164+
CGImageRef baselineImage = createImage(baselineURL);
165+
bool equal = false;
166+
167+
if(!baselineImage) {
168+
NSLog(@"missing or unreadable baseline image %@", baselineURL);
169+
} else if(CGImageGetWidth(image) != CGImageGetWidth(baselineImage) ||
170+
CGImageGetHeight(image) != CGImageGetHeight(baselineImage)) {
171+
NSLog(@"image size mismatch: got %zux%zu, expected %zux%zu",
172+
CGImageGetWidth(image),
173+
CGImageGetHeight(image),
174+
CGImageGetWidth(baselineImage),
175+
CGImageGetHeight(baselineImage));
176+
} else {
177+
std::vector<UInt8> actualPixels;
178+
std::vector<UInt8> baselinePixels;
179+
180+
if(getImagePixels(image, actualPixels) && getImagePixels(baselineImage, baselinePixels)) {
181+
int maxDifference = 0;
182+
size_t maxDifferenceIndex = 0;
183+
184+
for(size_t i=0;i<actualPixels.size();++i) {
185+
int difference = std::abs(int(actualPixels[i]) - int(baselinePixels[i]));
186+
if(difference > maxDifference) {
187+
maxDifference = difference;
188+
maxDifferenceIndex = i;
189+
}
190+
}
191+
192+
equal = maxDifference <= kMaxPixelDifference;
193+
if(!equal) {
194+
auto pixelIndex = maxDifferenceIndex / 4;
195+
NSLog(@"maximum pixel difference %d exceeds epsilon %d at (%zu, %zu), channel %zu",
196+
maxDifference,
197+
kMaxPixelDifference,
198+
pixelIndex % CGImageGetWidth(image),
199+
pixelIndex / CGImageGetWidth(image),
200+
maxDifferenceIndex % 4);
201+
}
202+
} else {
203+
NSLog(@"could not decode images for comparison");
204+
}
205+
}
121206

122207
if(!equal) {
123208
#if TARGET_OS_OSX
124209
system([NSString stringWithFormat:@"open %@", tmpURL.path].UTF8String);
125210
#endif
126211
}
127212

213+
if(baselineImage) {
214+
CGImageRelease(baselineImage);
215+
}
216+
CGImageRelease(image);
217+
128218
return equal;
129219
}

0 commit comments

Comments
 (0)