WebKitTools/DumpRenderTree/cg/ImageDiffCG.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * Copyright (C) 2005, 2007 Apple 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 #define min min
       
    28 
       
    29 #include <stdio.h>
       
    30 #include <wtf/Platform.h>
       
    31 #include <wtf/RetainPtr.h>
       
    32 
       
    33 #if PLATFORM(WIN)
       
    34 #include <winsock2.h>
       
    35 #include <windows.h>
       
    36 #include <fcntl.h>
       
    37 #include <io.h>
       
    38 #include <wtf/MathExtras.h>
       
    39 #endif
       
    40 
       
    41 #include <CoreGraphics/CGBitmapContext.h>
       
    42 #include <CoreGraphics/CGImage.h>
       
    43 #include <ImageIO/CGImageDestination.h>
       
    44 
       
    45 #if PLATFORM(MAC)
       
    46 #include <LaunchServices/UTCoreTypes.h>
       
    47 #endif
       
    48 
       
    49 #ifndef CGFLOAT_DEFINED
       
    50 #ifdef __LP64__
       
    51 typedef double CGFloat;
       
    52 #else
       
    53 typedef float CGFloat;
       
    54 #endif
       
    55 #define CGFLOAT_DEFINED 1
       
    56 #endif
       
    57 
       
    58 using namespace std;
       
    59 
       
    60 #if PLATFORM(WIN)
       
    61 static inline float strtof(const char *nptr, char **endptr)
       
    62 {
       
    63     return strtod(nptr, endptr);
       
    64 }
       
    65 static const CFStringRef kUTTypePNG = CFSTR("public.png");
       
    66 #endif
       
    67 
       
    68 static RetainPtr<CGImageRef> createImageFromStdin(int bytesRemaining)
       
    69 {
       
    70     unsigned char buffer[2048];
       
    71     RetainPtr<CFMutableDataRef> data(AdoptCF, CFDataCreateMutable(0, bytesRemaining));
       
    72 
       
    73     while (bytesRemaining > 0) {
       
    74         size_t bytesToRead = min(bytesRemaining, 2048);
       
    75         size_t bytesRead = fread(buffer, 1, bytesToRead, stdin);
       
    76         CFDataAppendBytes(data.get(), buffer, static_cast<CFIndex>(bytesRead));
       
    77         bytesRemaining -= static_cast<int>(bytesRead);
       
    78     }
       
    79     RetainPtr<CGDataProviderRef> dataProvider(AdoptCF, CGDataProviderCreateWithCFData(data.get()));
       
    80     return RetainPtr<CGImageRef>(AdoptCF, CGImageCreateWithPNGDataProvider(dataProvider.get(), 0, false, kCGRenderingIntentDefault));
       
    81 }
       
    82 
       
    83 static void releaseMallocBuffer(void* info, const void* data, size_t size)
       
    84 {
       
    85     free((void*)data);
       
    86 }
       
    87 
       
    88 static RetainPtr<CGImageRef> createDifferenceImage(CGImageRef baseImage, CGImageRef testImage, float& difference)
       
    89 {
       
    90     size_t width = CGImageGetWidth(baseImage);
       
    91     size_t height = CGImageGetHeight(baseImage);
       
    92     size_t rowBytes = width * 4;
       
    93 
       
    94     // Draw base image in bitmap context
       
    95     void* baseBuffer = calloc(height, rowBytes);
       
    96     RetainPtr<CGContextRef> baseContext(AdoptCF, CGBitmapContextCreate(baseBuffer, width, height, 8, rowBytes, CGImageGetColorSpace(baseImage), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
       
    97     CGContextDrawImage(baseContext.get(), CGRectMake(0, 0, width, height), baseImage);
       
    98 
       
    99     // Draw test image in bitmap context
       
   100     void* buffer = calloc(height, rowBytes);
       
   101     RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(buffer, width, height, 8, rowBytes, CGImageGetColorSpace(testImage), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host));
       
   102     CGContextDrawImage(context.get(), CGRectMake(0, 0, width, height), testImage);
       
   103 
       
   104     // Compare the content of the 2 bitmaps
       
   105     void* diffBuffer = malloc(width * height);
       
   106     float count = 0.0f;
       
   107     float sum = 0.0f;
       
   108     float maxDistance = 0.0f;
       
   109     unsigned char* basePixel = (unsigned char*)baseBuffer;
       
   110     unsigned char* pixel = (unsigned char*)buffer;
       
   111     unsigned char* diff = (unsigned char*)diffBuffer;
       
   112     for (size_t y = 0; y < height; ++y) {
       
   113         for (size_t x = 0; x < width; ++x) {
       
   114             float red = (pixel[0] - basePixel[0]) / max<float>(255 - basePixel[0], basePixel[0]);
       
   115             float green = (pixel[1] - basePixel[1]) / max<float>(255 - basePixel[1], basePixel[1]);
       
   116             float blue = (pixel[2] - basePixel[2]) / max<float>(255 - basePixel[2], basePixel[2]);
       
   117             float alpha = (pixel[3] - basePixel[3]) / max<float>(255 - basePixel[3], basePixel[3]);
       
   118             float distance = sqrtf(red * red + green * green + blue * blue + alpha * alpha) / 2.0f;
       
   119             
       
   120             *diff++ = (unsigned char)(distance * 255.0f);
       
   121             
       
   122             if (distance >= 1.0f / 255.0f) {
       
   123                 count += 1.0f;
       
   124                 sum += distance;
       
   125                 if (distance > maxDistance)
       
   126                     maxDistance = distance;
       
   127             }
       
   128             
       
   129             basePixel += 4;
       
   130             pixel += 4;
       
   131         }
       
   132     }
       
   133     
       
   134     // Compute the difference as a percentage combining both the number of different pixels and their difference amount i.e. the average distance over the entire image
       
   135     if (count > 0.0f)
       
   136         difference = 100.0f * sum / (height * width);
       
   137     else
       
   138         difference = 0.0f;
       
   139 
       
   140     RetainPtr<CGImageRef> diffImage;
       
   141     // Generate a normalized diff image if there is any difference
       
   142     if (difference > 0.0f) {
       
   143         if (maxDistance < 1.0f) {
       
   144             diff = (unsigned char*)diffBuffer;
       
   145             for(size_t p = 0; p < height * width; ++p)
       
   146                 diff[p] = diff[p] / maxDistance;
       
   147         }
       
   148         
       
   149         static CGColorSpaceRef diffColorspace = CGColorSpaceCreateDeviceGray();
       
   150         RetainPtr<CGDataProviderRef> provider(AdoptCF, CGDataProviderCreateWithData(0, diffBuffer, width * height, releaseMallocBuffer));
       
   151         diffImage.adoptCF(CGImageCreate(width, height, 8, 8, width, diffColorspace, 0, provider.get(), 0, false, kCGRenderingIntentDefault));
       
   152     }
       
   153     else
       
   154         free(diffBuffer);
       
   155     
       
   156     // Destroy drawing buffers
       
   157     if (buffer)
       
   158         free(buffer);
       
   159     if (baseBuffer)
       
   160         free(baseBuffer);
       
   161     
       
   162     return diffImage;
       
   163 }
       
   164 
       
   165 static inline bool imageHasAlpha(CGImageRef image)
       
   166 {
       
   167     CGImageAlphaInfo info = CGImageGetAlphaInfo(image);
       
   168     
       
   169     return (info >= kCGImageAlphaPremultipliedLast) && (info <= kCGImageAlphaFirst);
       
   170 }
       
   171 
       
   172 int main(int argc, const char* argv[])
       
   173 {
       
   174 #if PLATFORM(WIN)
       
   175     _setmode(0, _O_BINARY);
       
   176     _setmode(1, _O_BINARY);
       
   177 #endif
       
   178 
       
   179     float tolerance = 0.0f;
       
   180 
       
   181     for (int i = 1; i < argc; ++i) {
       
   182         if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--tolerance")) {
       
   183             if (i >= argc - 1)
       
   184                 exit(1);
       
   185             tolerance = strtof(argv[i + 1], 0);
       
   186             ++i;
       
   187             continue;
       
   188         }
       
   189     }
       
   190 
       
   191     char buffer[2048];
       
   192     RetainPtr<CGImageRef> actualImage;
       
   193     RetainPtr<CGImageRef> baselineImage;
       
   194 
       
   195     while (fgets(buffer, sizeof(buffer), stdin)) {
       
   196         // remove the CR
       
   197         char* newLineCharacter = strchr(buffer, '\n');
       
   198         if (newLineCharacter)
       
   199             *newLineCharacter = '\0';
       
   200 
       
   201         if (!strncmp("Content-Length: ", buffer, 16)) {
       
   202             strtok(buffer, " ");
       
   203             int imageSize = strtol(strtok(0, " "), 0, 10);
       
   204 
       
   205             if (imageSize > 0 && !actualImage)
       
   206                 actualImage = createImageFromStdin(imageSize);
       
   207             else if (imageSize > 0 && !baselineImage)
       
   208                 baselineImage = createImageFromStdin(imageSize);
       
   209             else
       
   210                 fputs("error, image size must be specified.\n", stdout);
       
   211         }
       
   212 
       
   213         if (actualImage && baselineImage) {
       
   214             RetainPtr<CGImageRef> diffImage;
       
   215             float difference = 100.0f;
       
   216             
       
   217             if ((CGImageGetWidth(actualImage.get()) == CGImageGetWidth(baselineImage.get())) && (CGImageGetHeight(actualImage.get()) == CGImageGetHeight(baselineImage.get())) && (imageHasAlpha(actualImage.get()) == imageHasAlpha(baselineImage.get()))) {
       
   218                 diffImage = createDifferenceImage(actualImage.get(), baselineImage.get(), difference); // difference is passed by reference
       
   219                 if (difference <= tolerance)
       
   220                     difference = 0.0f;
       
   221                 else {
       
   222                     difference = roundf(difference * 100.0f) / 100.0f;
       
   223                     difference = max(difference, 0.01f); // round to 2 decimal places
       
   224                 }
       
   225             } else
       
   226                 fputs("error, test and reference image have different properties.\n", stderr);
       
   227                 
       
   228             if (difference > 0.0f) {
       
   229                 if (diffImage) {
       
   230                     RetainPtr<CFMutableDataRef> imageData(AdoptCF, CFDataCreateMutable(0, 0));
       
   231                     RetainPtr<CGImageDestinationRef> imageDest(AdoptCF, CGImageDestinationCreateWithData(imageData.get(), kUTTypePNG, 1, 0));
       
   232                     CGImageDestinationAddImage(imageDest.get(), diffImage.get(), 0);
       
   233                     CGImageDestinationFinalize(imageDest.get());
       
   234                     printf("Content-Length: %lu\n", CFDataGetLength(imageData.get()));
       
   235                     fwrite(CFDataGetBytePtr(imageData.get()), 1, CFDataGetLength(imageData.get()), stdout);
       
   236                 }
       
   237                 
       
   238                 fprintf(stdout, "diff: %01.2f%% failed\n", difference);
       
   239             } else
       
   240                 fprintf(stdout, "diff: %01.2f%% passed\n", difference);
       
   241             
       
   242             actualImage = 0;
       
   243             baselineImage = 0;
       
   244         }
       
   245 
       
   246         fflush(stdout);
       
   247     }
       
   248 
       
   249     return 0;
       
   250 }