webengine/osswebengine/WebKitTools/DumpRenderTree/mac/ImageDiff.m
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * Copyright (C) 2005 Apple Computer, Inc.  All rights reserved.
       
     3  * Copyright (C) 2005 Ben La Monica <ben.lamonica@gmail.com>.  All rights reserved.
       
     4  *
       
     5  * Redistribution and use in source and binary forms, with or without
       
     6  * modification, are permitted provided that the following conditions
       
     7  * are met:
       
     8  * 1. Redistributions of source code must retain the above copyright
       
     9  *    notice, this list of conditions and the following disclaimer.
       
    10  * 2. Redistributions in binary form must reproduce the above copyright
       
    11  *    notice, this list of conditions and the following disclaimer in the
       
    12  *    documentation and/or other materials provided with the distribution.
       
    13  *
       
    14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY
       
    15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
       
    16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR
       
    18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
       
    22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
       
    23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
       
    24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
       
    25  */
       
    26 
       
    27 #import <Foundation/Foundation.h>
       
    28 #import <QuartzCore/CoreImage.h>
       
    29 #import <AppKit/NSBitmapImageRep.h>
       
    30 #import <AppKit/NSGraphicsContext.h>
       
    31 #import <AppKit/NSCIImageRep.h>
       
    32 
       
    33 /* prototypes */
       
    34 int main(int argc, const char *argv[]);
       
    35 CGImageRef createImageFromStdin(int imageSize);
       
    36 void compareImages(CGImageRef actualBitmap, CGImageRef baselineImage);
       
    37 NSBitmapImageRep *getDifferenceBitmap(CGImageRef actualBitmap, CGImageRef baselineImage);
       
    38 float computePercentageDifferent(NSBitmapImageRep *diffBitmap);
       
    39 
       
    40 
       
    41 int main(int argc, const char *argv[])
       
    42 {
       
    43     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
       
    44 
       
    45     char buffer[2048];
       
    46     CGImageRef actualImage = nil;
       
    47     CGImageRef baselineImage = nil;
       
    48 
       
    49     NSAutoreleasePool *innerPool = [[NSAutoreleasePool alloc] init];
       
    50     while (fgets(buffer, sizeof(buffer), stdin)) {
       
    51         // remove the CR
       
    52         char *newLineCharacter = strchr(buffer, '\n');
       
    53         if (newLineCharacter) {
       
    54             *newLineCharacter = '\0';
       
    55         }
       
    56         
       
    57         if (strncmp("Content-length: ", buffer, 16) == 0) {
       
    58             strtok(buffer, " ");
       
    59             int imageSize = strtol(strtok(NULL, " "), NULL, 10);
       
    60 
       
    61             if(imageSize > 0 && actualImage == nil) 
       
    62                 actualImage = createImageFromStdin(imageSize);
       
    63             else if (imageSize > 0 && baselineImage == nil)
       
    64                 baselineImage = createImageFromStdin(imageSize);
       
    65             else
       
    66                 fputs("error, image size must be specified.\n", stdout);
       
    67         }
       
    68 
       
    69         if (actualImage != nil && baselineImage != nil) {
       
    70             compareImages(actualImage, baselineImage);
       
    71             CGImageRelease(actualImage);
       
    72             CGImageRelease(baselineImage);
       
    73             actualImage = nil;
       
    74             baselineImage = nil;
       
    75             [innerPool release];
       
    76             innerPool = [[NSAutoreleasePool alloc] init];
       
    77         }
       
    78         
       
    79         fflush(stdout);
       
    80     }
       
    81     [innerPool release];
       
    82     
       
    83     [pool release];
       
    84     return 0;
       
    85 }
       
    86 
       
    87 CGImageRef createImageFromStdin(int bytesRemaining)
       
    88 {
       
    89     unsigned char buffer[2048];
       
    90     NSMutableData *data = [[NSMutableData alloc] initWithCapacity:bytesRemaining];
       
    91     
       
    92     int bytesRead = 0;
       
    93     while (bytesRemaining > 0) {
       
    94         bytesRead = (bytesRemaining > 2048 ? 2048 : bytesRemaining);
       
    95         fread(buffer, bytesRead, 1, stdin);
       
    96         [data appendBytes:buffer length:bytesRead];
       
    97         bytesRemaining -= bytesRead;
       
    98     }
       
    99     CGDataProviderRef dataProvider = CGDataProviderCreateWithCFData((CFDataRef)data);
       
   100     CGImageRef image = CGImageCreateWithPNGDataProvider(dataProvider, NULL, NO, kCGRenderingIntentDefault);
       
   101     [data release];
       
   102     CGDataProviderRelease(dataProvider);
       
   103     
       
   104     return image; 
       
   105 }
       
   106 
       
   107 void compareImages(CGImageRef actualBitmap, CGImageRef baselineBitmap)
       
   108 {
       
   109     // prepare the difference blend to check for pixel variations
       
   110     NSBitmapImageRep *diffBitmap = getDifferenceBitmap(actualBitmap, baselineBitmap);
       
   111             
       
   112     float percentage = computePercentageDifferent(diffBitmap);
       
   113     
       
   114     // send message to let them know if an image was wrong
       
   115     if (percentage > 0.0) {
       
   116         // since the diff might actually show something, send it to stdout
       
   117         NSData *diffPNGData = [diffBitmap representationUsingType:NSPNGFileType properties:nil];
       
   118         fprintf(stdout, "Content-length: %d\n", [diffPNGData length]);
       
   119         fwrite([diffPNGData bytes], [diffPNGData length], 1, stdout);
       
   120         fprintf(stdout, "diff: %01.2f%% failed\n", percentage);
       
   121     } else
       
   122         fprintf(stdout, "diff: %01.2f%% passed\n", percentage);
       
   123 }
       
   124 
       
   125 NSBitmapImageRep *getDifferenceBitmap(CGImageRef testBitmap, CGImageRef referenceBitmap)
       
   126 {
       
   127     // we must have both images to take diff
       
   128     if (testBitmap == nil || referenceBitmap == nil)
       
   129         return nil;
       
   130 
       
   131     NSBitmapImageRep *diffBitmap = [NSBitmapImageRep alloc];
       
   132     [diffBitmap initWithBitmapDataPlanes:NULL
       
   133                               pixelsWide:CGImageGetWidth(testBitmap)
       
   134                               pixelsHigh:CGImageGetHeight(testBitmap)
       
   135                            bitsPerSample:CGImageGetBitsPerComponent(testBitmap)
       
   136                          samplesPerPixel:CGImageGetBitsPerPixel(testBitmap) / CGImageGetBitsPerComponent(testBitmap)
       
   137                                 hasAlpha:YES
       
   138                                 isPlanar:NO
       
   139                           colorSpaceName:NSCalibratedRGBColorSpace
       
   140                             bitmapFormat:0
       
   141                              bytesPerRow:CGImageGetBytesPerRow(testBitmap)
       
   142                             bitsPerPixel:CGImageGetBitsPerPixel(testBitmap)
       
   143     ];
       
   144 
       
   145     NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithBitmapImageRep:diffBitmap];
       
   146     CGContextRef cgContext = [nsContext graphicsPort];
       
   147     CGContextSetBlendMode(cgContext, kCGBlendModeNormal);
       
   148     CGContextDrawImage(cgContext, CGRectMake(0, 0, CGImageGetWidth(testBitmap), CGImageGetHeight(testBitmap)), testBitmap);
       
   149     CGContextSetBlendMode(cgContext, kCGBlendModeDifference);
       
   150     CGContextDrawImage(cgContext, CGRectMake(0, 0, CGImageGetWidth(referenceBitmap), CGImageGetHeight(referenceBitmap)), referenceBitmap);
       
   151 
       
   152     return [diffBitmap autorelease];
       
   153 }
       
   154 
       
   155 /**
       
   156  * Counts the number of non-black pixels, and returns the percentage
       
   157  * of non-black pixels to total pixels in the image.
       
   158  */
       
   159 float computePercentageDifferent(NSBitmapImageRep *diffBitmap)
       
   160 {
       
   161     // if diffBiatmap is nil, then there was an error, and it didn't match.
       
   162     if (diffBitmap == nil)
       
   163         return 100.0;
       
   164     
       
   165     unsigned bitmapFormat = [diffBitmap bitmapFormat];
       
   166     assert(!(bitmapFormat & NSAlphaFirstBitmapFormat));
       
   167     assert(!(bitmapFormat & NSFloatingPointSamplesBitmapFormat));
       
   168     
       
   169     unsigned pixelsHigh = [diffBitmap pixelsHigh];
       
   170     unsigned pixelsWide = [diffBitmap pixelsWide];
       
   171     unsigned bytesPerRow = [diffBitmap bytesPerRow];
       
   172     unsigned char *pixelRowData = [diffBitmap bitmapData];
       
   173     unsigned differences = 0;
       
   174     
       
   175     // NOTE: This may not be safe when switching between ENDIAN types
       
   176     for (unsigned row = 0; row < pixelsHigh; row++) {
       
   177         for (unsigned col = 0; col < (pixelsWide * 4); col += 4) {
       
   178             if (*(pixelRowData + col) != 0 || *(pixelRowData + col + 1) != 0 || *(pixelRowData + col + 2) != 0)
       
   179                 differences++;
       
   180         }
       
   181         pixelRowData += bytesPerRow;
       
   182     }
       
   183     
       
   184     float totalPixels = pixelsHigh * pixelsWide;
       
   185     return (differences * 100.f) / totalPixels;
       
   186 }