webengine/osswebengine/WebKitTools/DumpRenderTree/mac/ImageDiff.m
changeset 0 dd21522fd290
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebKitTools/DumpRenderTree/mac/ImageDiff.m	Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
+ * Copyright (C) 2005 Ben La Monica <ben.lamonica@gmail.com>.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#import <Foundation/Foundation.h>
+#import <QuartzCore/CoreImage.h>
+#import <AppKit/NSBitmapImageRep.h>
+#import <AppKit/NSGraphicsContext.h>
+#import <AppKit/NSCIImageRep.h>
+
+/* prototypes */
+int main(int argc, const char *argv[]);
+CGImageRef createImageFromStdin(int imageSize);
+void compareImages(CGImageRef actualBitmap, CGImageRef baselineImage);
+NSBitmapImageRep *getDifferenceBitmap(CGImageRef actualBitmap, CGImageRef baselineImage);
+float computePercentageDifferent(NSBitmapImageRep *diffBitmap);
+
+
+int main(int argc, const char *argv[])
+{
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+
+    char buffer[2048];
+    CGImageRef actualImage = nil;
+    CGImageRef baselineImage = nil;
+
+    NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];
+    while (fgets(buffer, sizeof(buffer), stdin)) {
+        // remove the CR
+        char *newLineCharacter = strchr(buffer, '\n');
+        if (newLineCharacter) {
+            *newLineCharacter = '\0';
+        }
+        
+        if (strncmp("Content-length: ", buffer, 16) == 0) {
+            strtok(buffer, " ");
+            int imageSize = strtol(strtok(NULL, " "), NULL, 10);
+
+            if(imageSize > 0 && actualImage == nil) 
+                actualImage = createImageFromStdin(imageSize);
+            else if (imageSize > 0 && baselineImage == nil)
+                baselineImage = createImageFromStdin(imageSize);
+            else
+                fputs("error, image size must be specified.\n", stdout);
+        }
+
+        if (actualImage != nil && baselineImage != nil) {
+            compareImages(actualImage, baselineImage);
+            CGImageRelease(actualImage);
+            CGImageRelease(baselineImage);
+            actualImage = nil;
+            baselineImage = nil;
+            [innerPool release];
+            innerPool = [[NSAutoreleasePool alloc] init];
+        }
+        
+        fflush(stdout);
+    }
+    [innerPool release];
+    
+    [pool release];
+    return 0;
+}
+
+CGImageRef createImageFromStdin(int bytesRemaining)
+{
+    unsigned char buffer[2048];
+    NSMutableData *data = [[NSMutableData alloc] initWithCapacity:bytesRemaining];
+    
+    int bytesRead = 0;
+    while (bytesRemaining > 0) {
+        bytesRead = (bytesRemaining > 2048 ? 2048 : bytesRemaining);
+        fread(buffer, bytesRead, 1, stdin);
+        [data appendBytes:buffer length:bytesRead];
+        bytesRemaining -= bytesRead;
+    }
+    CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((CFDataRef)data);
+    CGImageRef image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO, kCGRenderingIntentDefault);
+    [data release];
+    CGDataProviderRelease(dataProvider);
+    
+    return image; 
+}
+
+void compareImages(CGImageRef actualBitmap, CGImageRef baselineBitmap)
+{
+    // prepare the difference blend to check for pixel variations
+    NSBitmapImageRep *diffBitmap = getDifferenceBitmap(actualBitmap, baselineBitmap);
+            
+    float percentage = computePercentageDifferent(diffBitmap);
+    
+    // send message to let them know if an image was wrong
+    if (percentage > 0.0) {
+        // since the diff might actually show something, send it to stdout
+        NSData *diffPNGData = [diffBitmap representationUsingType:NSPNGFileType properties:nil];
+        fprintf(stdout, "Content-length: %d\n", [diffPNGData length]);
+        fwrite([diffPNGData bytes], [diffPNGData length], 1, stdout);
+        fprintf(stdout, "diff: %01.2f%% failed\n", percentage);
+    } else
+        fprintf(stdout, "diff: %01.2f%% passed\n", percentage);
+}
+
+NSBitmapImageRep *getDifferenceBitmap(CGImageRef testBitmap, CGImageRef referenceBitmap)
+{
+    // we must have both images to take diff
+    if (testBitmap == nil || referenceBitmap == nil)
+        return nil;
+
+    NSBitmapImageRep *diffBitmap = [NSBitmapImageRep alloc];
+    [diffBitmap initWithBitmapDataPlanes:NULL
+                              pixelsWide:CGImageGetWidth(testBitmap)
+                              pixelsHigh:CGImageGetHeight(testBitmap)
+                           bitsPerSample:CGImageGetBitsPerComponent(testBitmap)
+                         samplesPerPixel:CGImageGetBitsPerPixel(testBitmap) / CGImageGetBitsPerComponent(testBitmap)
+                                hasAlpha:YES
+                                isPlanar:NO
+                          colorSpaceName:NSCalibratedRGBColorSpace
+                            bitmapFormat:0
+                             bytesPerRow:CGImageGetBytesPerRow(testBitmap)
+                            bitsPerPixel:CGImageGetBitsPerPixel(testBitmap)
+    ];
+
+    NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:diffBitmap];
+    CGContextRef cgContext = [nsContext graphicsPort];
+    CGContextSetBlendMode(cgContext, kCGBlendModeNormal);
+    CGContextDrawImage(cgContext, CGRectMake(0, 0, CGImageGetWidth(testBitmap), CGImageGetHeight(testBitmap)), testBitmap);
+    CGContextSetBlendMode(cgContext, kCGBlendModeDifference);
+    CGContextDrawImage(cgContext, CGRectMake(0, 0, CGImageGetWidth(referenceBitmap), CGImageGetHeight(referenceBitmap)), referenceBitmap);
+
+    return [diffBitmap autorelease];
+}
+
+/**
+ * Counts the number of non-black pixels, and returns the percentage
+ * of non-black pixels to total pixels in the image.
+ */
+float computePercentageDifferent(NSBitmapImageRep *diffBitmap)
+{
+    // if diffBiatmap is nil, then there was an error, and it didn't match.
+    if (diffBitmap == nil)
+        return 100.0;
+    
+    unsigned bitmapFormat = [diffBitmap bitmapFormat];
+    assert(!(bitmapFormat & NSAlphaFirstBitmapFormat));
+    assert(!(bitmapFormat & NSFloatingPointSamplesBitmapFormat));
+    
+    unsigned pixelsHigh = [diffBitmap pixelsHigh];
+    unsigned pixelsWide = [diffBitmap pixelsWide];
+    unsigned bytesPerRow = [diffBitmap bytesPerRow];
+    unsigned char *pixelRowData = [diffBitmap bitmapData];
+    unsigned differences = 0;
+    
+    // NOTE: This may not be safe when switching between ENDIAN types
+    for (unsigned row = 0; row < pixelsHigh; row++) {
+        for (unsigned col = 0; col < (pixelsWide * 4); col += 4) {
+            if (*(pixelRowData + col) != 0 || *(pixelRowData + col + 1) != 0 || *(pixelRowData + col + 2) != 0)
+                differences++;
+        }
+        pixelRowData += bytesPerRow;
+    }
+    
+    float totalPixels = pixelsHigh * pixelsWide;
+    return (differences * 100.f) / totalPixels;
+}