Changeset 37928 in webkit


Ignore:
Timestamp:
Oct 28, 2008 10:26:58 AM (15 years ago)
Author:
Simon Fraser
Message:

2008-10-28 Pierre-Olivier Latour <pol@apple.com>

Reviewed by Dan Bernstein

Primary changes in DumpRenderTree:

  • Ensure font smoothing is disabled (this is also called LCD anti-aliasing and is different from regular font CG anti-aliasing) as font-smoothing settings depends on the display and can also be changed by the user
  • Use a new cleared buffer for each test instead of the reusing same one to avoid potential result corruption across tests
  • Can now receive the expected pixel hash as a suffix to the test path or url as "path'hash"
  • Make sure hash is computed in a endian-independent way
  • Improve the code that sets/restores the screen color profile
  • Make the code more cross-platformy with std::string goodness
  • Added an "on-screen" mode where the snapshot will take into account surfaces on the window (like OpenGL content): this uses the new CG APIs on 10.5 or reading from the display framebuffer on 10.4. This mode is not active by default for performance reason, but must be explicitly activated from the test file using the new "testOnscreen()" JS API.

Primary changes in ImageDiff:

  • Provide a new comparison algorithm that is more tolerant to "acceptable" failures (i.e. very small differences in font rendering, which --threshold is not really good at handling)
  • Generate normalized intensity-only diff images

Primary changes in run-webkit-tests:

  • Take advantage of hashes for pixel tests which makes them much faster by minimizing image comparisons
  • Removed repaint options as these should be set from within test files using JS API
  • Replaced "threshold" option in by "tolerance" expressed in percents
  • Added more logging when in "verbose" mode
Location:
trunk/WebKitTools
Files:
2 added
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebKitTools/ChangeLog

    r37904 r37928  
     12008-10-28  Pierre-Olivier Latour  <pol@apple.com>
     2
     3        Reviewed by Dan Bernstein
     4
     5        Primary changes in DumpRenderTree:
     6        - Ensure font smoothing is disabled (this is also called LCD anti-aliasing and
     7        is different from regular font CG anti-aliasing) as font-smoothing settings
     8        depends on the display and can also be changed by the user
     9        - Use a new cleared buffer for each test instead of the reusing same one to
     10        avoid potential result corruption across tests
     11        - Can now receive the expected pixel hash as a suffix to the test path or
     12        url as "path'hash"
     13        - Make sure hash is computed in a endian-independent way
     14        - Improve the code that sets/restores the screen color profile
     15        - Make the code more cross-platformy with std::string goodness
     16        - Added an "on-screen" mode where the snapshot will take into account surfaces
     17        on the window (like OpenGL content): this uses the new CG APIs on 10.5 or
     18        reading from the display framebuffer on 10.4. This mode is not active by
     19        default for performance reason, but must be explicitly activated from the test
     20        file using the new "testOnscreen()" JS API.
     21
     22        Primary changes in ImageDiff:
     23        - Provide a new comparison algorithm that is more tolerant to "acceptable"
     24        failures (i.e. very small differences in font rendering, which --threshold is
     25        not really good at handling)
     26        - Generate normalized intensity-only diff images
     27
     28        Primary changes in run-webkit-tests:
     29        - Take advantage of hashes for pixel tests which makes them much faster by
     30        minimizing image comparisons
     31        - Removed repaint options as these should be set from within test files using
     32        JS API
     33        - Replaced "threshold" option in  by "tolerance" expressed in percents
     34        - Added more logging when in "verbose" mode
     35
     36        https://bugs.webkit.org/show_bug.cgi?id=21322
     37        * DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj:
     38        * DumpRenderTree/ForwardingHeaders/wtf/PassRefPtr.h: Copied from JavaScriptGlue/ForwardingHeaders/wtf/PassRefPtr.h.
     39        * DumpRenderTree/ForwardingHeaders/wtf/RefPtr.h: Copied from JavaScriptGlue/ForwardingHeaders/wtf/RefPtr.h.
     40        * DumpRenderTree/LayoutTestController.cpp:
     41        (LayoutTestController::LayoutTestController):
     42        (testOnscreenCallback):
     43        (LayoutTestController::staticFunctions):
     44        * DumpRenderTree/LayoutTestController.h:
     45        (LayoutTestController::testOnscreen):
     46        (LayoutTestController::setTestOnscreen):
     47        (LayoutTestController::testPathOrURL):
     48        (LayoutTestController::expectedPixelHash):
     49        * DumpRenderTree/PixelDumpSupport.h:
     50        * DumpRenderTree/cg/ImageDiffCG.cpp:
     51        (strtof):
     52        (releaseMallocBuffer):
     53        (createDifferenceImage):
     54        (imageHasAlpha):
     55        (main):
     56        * DumpRenderTree/cg/PixelDumpSupportCG.cpp:
     57        (computeMD5HashStringForBitmapContext):
     58        (dumpWebViewAsPixelsAndCompareWithExpected):
     59        * DumpRenderTree/cg/PixelDumpSupportCG.h:
     60        (BitmapContext::createByAdoptingBitmapAndContext):
     61        (BitmapContext::~BitmapContext):
     62        (BitmapContext::cgContext):
     63        (BitmapContext::BitmapContext):
     64        * DumpRenderTree/mac/DumpRenderTree.mm:
     65        (shouldIgnoreWebCoreNodeLeaks):
     66        (setDefaultsToConsistentValuesForTesting):
     67        (crashHandler):
     68        (initializeGlobalsFromCommandLineOptions):
     69        (prepareConsistentTestingEnvironment):
     70        (dumpRenderTree):
     71        (sizeWebViewForCurrentTest):
     72        (dump):
     73        (runTest):
     74        * DumpRenderTree/mac/PixelDumpSupportMac.mm:
     75        (restoreMainDisplayColorProfile):
     76        (setupMainDisplayColorProfile):
     77        (createBitmapContextFromWebView):
     78        * DumpRenderTree/win/DumpRenderTree.cpp:
     79        (dump):
     80        (runTest):
     81        * DumpRenderTree/win/PixelDumpSupportWin.cpp:
     82        (getBitmapContextFromWebView):
     83        * Scripts/run-webkit-tests:
     84
    1852008-10-27  Kevin Ollivier  <kevino@theolliviers.com>
    286
  • trunk/WebKitTools/DumpRenderTree/DumpRenderTree.xcodeproj/project.pbxproj

    r35193 r37928  
    3434                1AC6C84A0D07638600CD3161 /* PluginObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AC6C7800D07589B00CD3161 /* PluginObject.cpp */; };
    3535                1AC6C84B0D07638600CD3161 /* TestObject.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1AC6C7810D07589B00CD3161 /* TestObject.cpp */; };
     36                23BCB8900EA57623003C6289 /* OpenGL.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 23BCB88F0EA57623003C6289 /* OpenGL.framework */; };
    3637                9340994C08540CAE007F3BC8 /* DumpRenderTreePrefix.h in Headers */ = {isa = PBXBuildFile; fileRef = 32A70AAB03705E1F00C91783 /* DumpRenderTreePrefix.h */; };
    3738                9340995108540CAE007F3BC8 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9335435F03D75502008635CE /* WebKit.framework */; };
     
    132133                1AC6C7800D07589B00CD3161 /* PluginObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PluginObject.cpp; sourceTree = "<group>"; };
    133134                1AC6C7810D07589B00CD3161 /* TestObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TestObject.cpp; sourceTree = "<group>"; };
     135                23BCB88F0EA57623003C6289 /* OpenGL.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGL.framework; path = /System/Library/Frameworks/OpenGL.framework; sourceTree = "<absolute>"; };
    134136                32A70AAB03705E1F00C91783 /* DumpRenderTreePrefix.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DumpRenderTreePrefix.h; sourceTree = "<group>"; };
    135137                375F09710DAC3CB600C8B4E5 /* WebKitWeightWatcher100.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; name = WebKitWeightWatcher100.ttf; path = fonts/WebKitWeightWatcher100.ttf; sourceTree = "<group>"; };
     
    235237                                A817090408B164D300CCB9FB /* JavaScriptCore.framework in Frameworks */,
    236238                                9340995108540CAE007F3BC8 /* WebKit.framework in Frameworks */,
     239                                23BCB8900EA57623003C6289 /* OpenGL.framework in Frameworks */,
    237240                        );
    238241                        runOnlyForDeploymentPostprocessing = 0;
     
    376379                                B5A752A108AF5D1F00138E45 /* QuartzCore.framework */,
    377380                                9335435F03D75502008635CE /* WebKit.framework */,
     381                                23BCB88F0EA57623003C6289 /* OpenGL.framework */,
    378382                        );
    379383                        name = Frameworks;
  • trunk/WebKitTools/DumpRenderTree/LayoutTestController.cpp

    r37633 r37928  
    3535#include <wtf/MathExtras.h>
    3636
    37 LayoutTestController::LayoutTestController(bool testRepaintDefault, bool testRepaintSweepHorizontallyDefault)
     37LayoutTestController::LayoutTestController(const std::string& testPathOrURL, const std::string& expectedPixelHash)
    3838    : m_dumpAsText(false)
    3939    , m_dumpAsPDF(false)
     
    5555    , m_closeRemainingWindowsWhenComplete(true)
    5656    , m_stopProvisionalFrameLoads(false)
    57     , m_testRepaint(testRepaintDefault)
    58     , m_testRepaintSweepHorizontally(testRepaintSweepHorizontallyDefault)
     57    , m_testOnscreen(false)
     58    , m_testRepaint(false)
     59    , m_testRepaintSweepHorizontally(false)
    5960    , m_waitToDump(false)
    6061    , m_windowIsKey(true)
    6162    , m_globalFlag(false)
     63    , m_testPathOrURL(testPathOrURL)
     64    , m_expectedPixelHash(expectedPixelHash)
    6265{
    6366}
     
    203206    LayoutTestController* controller = reinterpret_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
    204207    controller->setCloseRemainingWindowsWhenComplete(JSValueToBoolean(context, arguments[0]));
     208    return JSValueMakeUndefined(context);
     209}
     210
     211static JSValueRef testOnscreenCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
     212{
     213    LayoutTestController* controller = reinterpret_cast<LayoutTestController*>(JSObjectGetPrivate(thisObject));
     214    controller->setTestOnscreen(true);
    205215    return JSValueMakeUndefined(context);
    206216}
     
    746756        { "setUserStyleSheetLocation", setUserStyleSheetLocationCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
    747757        { "setWindowIsKey", setWindowIsKeyCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
     758        { "testOnscreen", testOnscreenCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
    748759        { "testRepaint", testRepaintCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
    749760        { "waitUntilDone", waitUntilDoneCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
  • trunk/WebKitTools/DumpRenderTree/LayoutTestController.h

    r37633 r37928  
    3232#include <JavaScriptCore/JSObjectRef.h>
    3333#include <wtf/RefCounted.h>
     34#include <string>
    3435
    3536class LayoutTestController : public RefCounted<LayoutTestController> {
    3637public:
    37     LayoutTestController(bool testRepaintDefault, bool testRepaintSweepHorizontallyDefault);
     38    LayoutTestController(const std::string& testPathOrURL, const std::string& expectedPixelHash);
    3839    ~LayoutTestController();
    3940
     
    131132    void setStopProvisionalFrameLoads(bool stopProvisionalFrameLoads) { m_stopProvisionalFrameLoads = stopProvisionalFrameLoads; }
    132133
     134    bool testOnscreen() const { return m_testOnscreen; }
     135    void setTestOnscreen(bool testOnscreen) { m_testOnscreen = testOnscreen; }
     136
    133137    bool testRepaint() const { return m_testRepaint; }
    134138    void setTestRepaint(bool testRepaint) { m_testRepaint = testRepaint; }
     
    145149    bool globalFlag() const { return m_globalFlag; }
    146150    void setGlobalFlag(bool globalFlag) { m_globalFlag = globalFlag; }
    147 
     151   
     152    const std::string& testPathOrURL() const { return m_testPathOrURL; }
     153    const std::string& expectedPixelHash() const { return m_expectedPixelHash; }
     154   
    148155private:
    149156    bool m_dumpAsText;
     
    166173    bool m_closeRemainingWindowsWhenComplete;
    167174    bool m_stopProvisionalFrameLoads;
     175    bool m_testOnscreen;
    168176    bool m_testRepaint;
    169177    bool m_testRepaintSweepHorizontally;
     
    173181    bool m_globalFlag;
    174182
     183    std::string m_testPathOrURL;
     184    std::string m_expectedPixelHash;    // empty string if no hash
     185   
    175186    static JSClassRef getJSClass();
    176187    static JSStaticValue* staticValues();
  • trunk/WebKitTools/DumpRenderTree/PixelDumpSupport.h

    r29663 r37928  
    3030#define PixelDumpSupport_h
    3131
    32 void dumpWebViewAsPixelsAndCompareWithExpected(const char* currentTest, bool forceAllTestsToDumpPixels);
     32#include <string>
     33
     34void dumpWebViewAsPixelsAndCompareWithExpected(const std::string& expectedHash);
     35
     36#if PLATFORM(MAC)
    3337
    3438// Can be used as a signal handler
    35 void restoreColorSpace(int ignored);
     39void restoreMainDisplayColorProfile(int ignored);
    3640
    37 // May change your color space, requiring a call to restoreColorSpace
    38 void initializeColorSpaceAndScreeBufferForPixelTests();
     41// May change your color space, requiring a call to restoreMainDisplayColorProfile
     42void setupMainDisplayColorProfile();
     43
     44#endif
    3945
    4046#endif // PixelDumpSupport_h
  • trunk/WebKitTools/DumpRenderTree/cg/ImageDiffCG.cpp

    r37270 r37928  
    3737#include <fcntl.h>
    3838#include <io.h>
     39#include <wtf/MathExtras.h>
    3940#endif
    4041
     
    5556
    5657#if PLATFORM(WIN)
     58static inline float strtof(const char *nptr, char **endptr)
     59{
     60    return strtod(nptr, endptr);
     61}
    5762static const CFStringRef kUTTypePNG = CFSTR("public.png");
    5863#endif
     
    7378}
    7479
    75 // Generates an RGBA8 bitmap in the reference image colorspace containing the differences between the 2 images
    76 static RetainPtr<CGContextRef> createDifferenceBitmap(CGImageRef testImage, CGImageRef referenceImage)
    77 {
    78     // we must have both images to take diff
    79     if (!testImage || !referenceImage) {
    80         fprintf(stderr, "test or reference image is missing!");
    81         return 0;
    82     }
    83 
    84     // we must have both images be the same dimensions
    85     if ((CGImageGetWidth(testImage) != CGImageGetWidth(referenceImage)) || (CGImageGetHeight(testImage) != CGImageGetHeight(referenceImage))) {
    86         fprintf(stderr, "test and reference image dimensions don't match!");
    87         return 0;
    88     }
    89 
    90     // we must have both images in the same colorspace
    91     if (!CFEqual(CGImageGetColorSpace(testImage), CGImageGetColorSpace(referenceImage))) {
    92         fprintf(stderr, "test and reference image colorspaces don't match!");
    93         return 0;
    94     }
    95 
    96     static CFMutableDataRef data = CFDataCreateMutable(kCFAllocatorDefault, 0);
    97     size_t rowBytes = (4 * CGImageGetWidth(referenceImage) + 63) & ~63; // Use a multiple of 64 bytes to improve CG performance
    98     CFDataSetLength(data, CGImageGetHeight(referenceImage) * rowBytes);
    99     RetainPtr<CGContextRef> context(AdoptCF, CGBitmapContextCreate(CFDataGetMutableBytePtr(data), CGImageGetWidth(referenceImage), CGImageGetHeight(referenceImage), 8, rowBytes, CGImageGetColorSpace(referenceImage), kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big));
    100 
    101     CGContextSetBlendMode(context.get(), kCGBlendModeNormal);
    102     CGContextDrawImage(context.get(), CGRectMake(0, 0, static_cast<CGFloat>(CGImageGetWidth(testImage)), static_cast<CGFloat>(CGImageGetHeight(testImage))), testImage);
    103     CGContextSetBlendMode(context.get(), kCGBlendModeDifference);
    104     CGContextDrawImage(context.get(), CGRectMake(0, 0, static_cast<CGFloat>(CGImageGetWidth(referenceImage)), static_cast<CGFloat>(CGImageGetHeight(referenceImage))), referenceImage);
    105 
    106     return context;
    107 }
    108 
    109 // Counts the number of non-black pixels, and returns the percentage of non-black pixels to total pixels in the image.
    110 static float computePercentageDifferent(CGContextRef diffBitmap, unsigned threshold)
    111 {
    112     // if diffBitmap is nil, then there was an error, and it didn't match.
    113     if (!diffBitmap)
    114         return 100.0f;
    115 
    116     size_t pixelsHigh = CGBitmapContextGetHeight(diffBitmap);
    117     size_t pixelsWide = CGBitmapContextGetWidth(diffBitmap);
    118     size_t bytesPerRow = CGBitmapContextGetBytesPerRow(diffBitmap);
    119     unsigned char* pixelRowData = static_cast<unsigned char*>(CGBitmapContextGetData(diffBitmap));
    120     unsigned differences = 0;
    121 
    122     for (unsigned row = 0; row < pixelsHigh; row++) {
    123         for (unsigned col = 0; col < (pixelsWide * 4); col += 4) {
    124             unsigned char* red = pixelRowData + col;
    125             unsigned char* green = red + 1;
    126             unsigned char* blue = red + 2;
    127             unsigned distance = *red + *green + *blue;
    128             if (distance > threshold) {
    129                 differences++;
    130                 // shift the pixels towards white to make them more visible
    131                 *red = static_cast<unsigned char>(min(UCHAR_MAX, *red + 100));
    132                 *green = static_cast<unsigned char>(min(UCHAR_MAX, *green + 100));
    133                 *blue = static_cast<unsigned char>(min(UCHAR_MAX, *blue + 100));
     80static void releaseMallocBuffer(void* info, const void* data, size_t size)
     81{
     82    free((void*)data);
     83}
     84
     85static RetainPtr<CGImageRef> createDifferenceImage(CGImageRef baseImage, CGImageRef testImage, float& difference)
     86{
     87    static RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB());
     88    RetainPtr<CGImageRef> diffImage;
     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    CGContextRef baseContext = CGBitmapContextCreate(baseBuffer, width, height, 8, rowBytes, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
     97    CGContextDrawImage(baseContext, CGRectMake(0, 0, width, height), baseImage);
     98    CGContextRelease(baseContext);
     99   
     100    // Draw test image in bitmap context
     101    void* buffer = calloc(height, rowBytes);
     102    CGContextRef context = CGBitmapContextCreate(buffer, width, height, 8, rowBytes, colorSpace.get(), kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
     103    CGContextDrawImage(context, CGRectMake(0, 0, width, height), testImage);
     104    CGContextRelease(context);
     105   
     106    // Compare the content of the 2 bitmaps
     107    void* diffBuffer = malloc(width * height);
     108    float count = 0.0f;
     109    float sum = 0.0f;
     110    float maxDistance = 0.0f;
     111    unsigned char* basePixel = (unsigned char*)baseBuffer;
     112    unsigned char* pixel = (unsigned char*)buffer;
     113    unsigned char* diff = (unsigned char*)diffBuffer;
     114    for (size_t y = 0; y < height; ++y) {
     115        for (size_t x = 0; x < width; ++x) {
     116            float red = (pixel[0] - basePixel[0]) / max<float>(255 - basePixel[0], basePixel[0]);
     117            float green = (pixel[1] - basePixel[1]) / max<float>(255 - basePixel[1], basePixel[1]);
     118            float blue = (pixel[2] - basePixel[2]) / max<float>(255 - basePixel[2], basePixel[2]);
     119            float alpha = (pixel[3] - basePixel[3]) / max<float>(255 - basePixel[3], basePixel[3]);
     120            float distance = sqrtf(red * red + green * green + blue * blue + alpha * alpha) / 2.0f;
     121           
     122            *diff++ = (unsigned char)(distance * 255.0f);
     123           
     124            if (distance >= 1.0f / 255.0f) {
     125                count += 1.0f;
     126                sum += distance;
     127                if (distance > maxDistance)
     128                    maxDistance = distance;
    134129            }
    135         }
    136         pixelRowData += bytesPerRow;
    137     }
    138 
    139     float totalPixels = static_cast<float>(pixelsHigh * pixelsWide);
    140     return (differences * 100.f) / totalPixels;
    141 }
    142 
    143 static void compareImages(CGImageRef testImage, CGImageRef referenceImage, unsigned threshold)
    144 {
    145     // prepare the difference blend to check for pixel variations
    146     RetainPtr<CGContextRef> diffBitmap = createDifferenceBitmap(testImage, referenceImage);
    147 
    148     float percentage = computePercentageDifferent(diffBitmap.get(), threshold);
    149 
    150     percentage = (float)((int)(percentage * 100.0f)) / 100.0f; // round to 2 decimal places
    151 
    152     // send message to let them know if an image was wrong
    153     if (percentage > 0.0f) {
    154         // since the diff might actually show something, send it to stdout
    155         if (diffBitmap) {
    156             RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(diffBitmap.get()));
    157             RetainPtr<CFMutableDataRef> imageData(AdoptCF, CFDataCreateMutable(0, 0));
    158             RetainPtr<CGImageDestinationRef> imageDest(AdoptCF, CGImageDestinationCreateWithData(imageData.get(), kUTTypePNG, 1, 0));
    159             CGImageDestinationAddImage(imageDest.get(), image.get(), 0);
    160             CGImageDestinationFinalize(imageDest.get());
    161             printf("Content-Length: %lu\n", CFDataGetLength(imageData.get()));
    162             fwrite(CFDataGetBytePtr(imageData.get()), 1, CFDataGetLength(imageData.get()), stdout);
     130           
     131            basePixel += 4;
     132            pixel += 4;
     133        }
     134    }
     135   
     136    // 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
     137    if (count > 0.0f)
     138        difference = 100.0f * sum / (height * width);
     139    else
     140        difference = 0.0f;
     141   
     142    // Generate a normalized diff image if there is any difference
     143    if (difference > 0.0f) {
     144        if (maxDistance < 1.0f) {
     145            diff = (unsigned char*)diffBuffer;
     146            for(size_t p = 0; p < height * width; ++p)
     147                diff[p] = diff[p] / maxDistance;
    163148        }
    164149       
    165         fprintf(stdout, "diff: %01.2f%% failed\n", percentage);
    166     } else
    167         fprintf(stdout, "diff: %01.2f%% passed\n", percentage);
     150        CGDataProviderRef provider = CGDataProviderCreateWithData(0, diffBuffer, width * height, releaseMallocBuffer);
     151        CGColorSpaceRef diffColorspace = CGColorSpaceCreateDeviceGray();
     152        diffImage.adoptCF(CGImageCreate(width, height, 8, 8, width, diffColorspace, 0, provider, 0, false, kCGRenderingIntentDefault));
     153        CGColorSpaceRelease(diffColorspace);
     154        CGDataProviderRelease(provider);
     155    }
     156    else
     157        free(diffBuffer);
     158   
     159    // Destroy drawing buffers
     160    if (buffer)
     161        free(buffer);
     162    if (baseBuffer)
     163        free(baseBuffer);
     164   
     165    return diffImage;
     166}
     167
     168static inline bool imageHasAlpha(CGImageRef image)
     169{
     170    CGImageAlphaInfo info = CGImageGetAlphaInfo(image);
     171   
     172    return (info >= kCGImageAlphaPremultipliedLast) && (info <= kCGImageAlphaFirst);
    168173}
    169174
     
    175180#endif
    176181
    177     unsigned threshold = 0;
     182    float tolerance = 0.0f;
    178183
    179184    for (int i = 1; i < argc; ++i) {
    180         if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--threshold")) {
     185        if (!strcmp(argv[i], "-t") || !strcmp(argv[i], "--tolerance")) {
    181186            if (i >= argc - 1)
    182187                exit(1);
    183             threshold = strtol(argv[i + 1], 0, 0);
     188            tolerance = strtof(argv[i + 1], 0);
    184189            ++i;
    185190            continue;
     
    210215
    211216        if (actualImage && baselineImage) {
    212             compareImages(actualImage.get(), baselineImage.get(), threshold);
     217            RetainPtr<CGImageRef> diffImage;
     218            float difference = 100.0f;
     219           
     220            if ((CGImageGetWidth(actualImage.get()) == CGImageGetWidth(baselineImage.get())) && (CGImageGetHeight(actualImage.get()) == CGImageGetHeight(baselineImage.get())) && (imageHasAlpha(actualImage.get()) == imageHasAlpha(baselineImage.get()))) {
     221                diffImage = createDifferenceImage(actualImage.get(), baselineImage.get(), difference); // difference is passed by reference
     222                if (difference <= tolerance)
     223                    difference = 0.0f;
     224                else {
     225                    difference = roundf(difference * 100.0f) / 100.0f;
     226                    difference = max(difference, 0.01f); // round to 2 decimal places
     227                }
     228            } else
     229                fputs("error, test and reference image have different properties.\n", stderr);
     230               
     231            if (difference > 0.0f) {
     232                if (diffImage) {
     233                    RetainPtr<CFMutableDataRef> imageData(AdoptCF, CFDataCreateMutable(0, 0));
     234                    RetainPtr<CGImageDestinationRef> imageDest(AdoptCF, CGImageDestinationCreateWithData(imageData.get(), kUTTypePNG, 1, 0));
     235                    CGImageDestinationAddImage(imageDest.get(), diffImage.get(), 0);
     236                    CGImageDestinationFinalize(imageDest.get());
     237                    printf("Content-Length: %lu\n", CFDataGetLength(imageData.get()));
     238                    fwrite(CFDataGetBytePtr(imageData.get()), 1, CFDataGetLength(imageData.get()), stdout);
     239                }
     240               
     241                fprintf(stdout, "diff: %01.2f%% failed\n", difference);
     242            } else
     243                fprintf(stdout, "diff: %01.2f%% passed\n", difference);
     244           
    213245            actualImage = 0;
    214246            baselineImage = 0;
  • trunk/WebKitTools/DumpRenderTree/cg/PixelDumpSupportCG.cpp

    r37594 r37928  
    3535#include "LayoutTestController.h"
    3636#include <ImageIO/CGImageDestination.h>
     37#include <algorithm>
     38#include <ctype.h>
    3739#include <wtf/Assertions.h>
     40#include <wtf/RefPtr.h>
    3841#include <wtf/RetainPtr.h>
    3942#include <wtf/StringExtras.h>
     
    6467}
    6568
    66 static void getMD5HashStringForBitmap(CGContextRef bitmap, char string[33])
     69static void computeMD5HashStringForBitmapContext(CGContextRef bitmapContext, char hashString[33])
    6770{
     71    ASSERT(CGBitmapContextGetBitsPerPixel(bitmapContext) == 32); // ImageDiff assumes 32 bit RGBA, we must as well.
     72    size_t pixelsHigh = CGBitmapContextGetHeight(bitmapContext);
     73    size_t pixelsWide = CGBitmapContextGetWidth(bitmapContext);
     74    size_t bytesPerRow = CGBitmapContextGetBytesPerRow(bitmapContext);
     75
     76    // We need to swap the bytes to ensure consistent hashes independently of endianness
    6877    MD5_CTX md5Context;
     78    MD5_Init(&md5Context);
     79    unsigned char* bitmapData = static_cast<unsigned char*>(CGBitmapContextGetData(bitmapContext));
     80#if PLATFORM(MAC)
     81    if ((CGBitmapContextGetBitmapInfo(bitmapContext) & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Big) {
     82        for (unsigned row = 0; row < pixelsHigh; row++) {
     83            uint32_t buffer[pixelsWide];
     84            for (unsigned column = 0; column < pixelsWide; column++)
     85                buffer[column] = OSReadLittleInt32(bitmapData, 4 * column);
     86            MD5_Update(&md5Context, buffer, 4 * pixelsWide);
     87            bitmapData += bytesPerRow;
     88        }
     89    } else
     90#endif
     91    {
     92        for (unsigned row = 0; row < pixelsHigh; row++) {
     93            MD5_Update(&md5Context, bitmapData, 4 * pixelsWide);
     94            bitmapData += bytesPerRow;
     95        }
     96    }
    6997    unsigned char hash[16];
    70 
    71     size_t bitsPerPixel = CGBitmapContextGetBitsPerPixel(bitmap);
    72     ASSERT(bitsPerPixel == 32); // ImageDiff assumes 32 bit RGBA, we must as well.
    73     size_t bytesPerPixel = bitsPerPixel / 8;
    74     size_t pixelsHigh = CGBitmapContextGetHeight(bitmap);
    75     size_t pixelsWide = CGBitmapContextGetWidth(bitmap);
    76     size_t bytesPerRow = CGBitmapContextGetBytesPerRow(bitmap);
    77     ASSERT(bytesPerRow >= (pixelsWide * bytesPerPixel));
    78 
    79     MD5_Init(&md5Context);
    80     unsigned char* bitmapData = static_cast<unsigned char*>(CGBitmapContextGetData(bitmap));
    81     for (unsigned row = 0; row < pixelsHigh; row++) {
    82         MD5_Update(&md5Context, bitmapData, static_cast<unsigned>(pixelsWide * bytesPerPixel));
    83         bitmapData += bytesPerRow;
    84     }
    8598    MD5_Final(hash, &md5Context);
    8699
    87     string[0] = '\0';
     100    hashString[0] = '\0';
    88101    for (int i = 0; i < 16; i++)
    89         snprintf(string, 33, "%s%02x", string, hash[i]);
     102        snprintf(hashString, 33, "%s%02x", hashString, hash[i]);
    90103}
    91104
    92 void drawSelectionRect(CGContextRef context, const CGRect& rect)
     105void dumpWebViewAsPixelsAndCompareWithExpected(const std::string& expectedHash)
    93106{
    94     CGContextSaveGState(context);
    95     CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
    96     CGContextStrokeRect(context, rect);
    97     CGContextRestoreGState(context);
    98 }
    99 
    100 void dumpWebViewAsPixelsAndCompareWithExpected(const char* /*currentTest*/, bool /*forceAllTestsToDumpPixels*/)
    101 {
    102     RetainPtr<CGContextRef> context = getBitmapContextFromWebView();
    103 
     107    RefPtr<BitmapContext> context;
     108   
    104109#if PLATFORM(MAC)
    105     if (gLayoutTestController->testRepaint())
    106         repaintWebView(context.get(), gLayoutTestController->testRepaintSweepHorizontally());
    107     else
    108         paintWebView(context.get());
    109 
    110     if (gLayoutTestController->dumpSelectionRect())
    111         drawSelectionRect(context.get(), getSelectionRect());
     110    context = createBitmapContextFromWebView(gLayoutTestController->testOnscreen(), gLayoutTestController->testRepaint(), gLayoutTestController->testRepaintSweepHorizontally(), gLayoutTestController->dumpSelectionRect());
    112111#endif
    113 
    114     // Compute the actual hash to compare to the expected image's hash.
     112    ASSERT(context);
     113   
     114    // Compute the hash of the bitmap context pixels
    115115    char actualHash[33];
    116     getMD5HashStringForBitmap(context.get(), actualHash);
     116    computeMD5HashStringForBitmapContext(context->cgContext(), actualHash);
    117117    printf("\nActualHash: %s\n", actualHash);
    118 
    119     // FIXME: We should compare the actualHash to the expected hash here and
    120     // only set dumpImage to true if they don't match, but DRT doesn't have
    121     // enough information currently to find the expected checksum file.
     118   
     119    // Check the computed hash against the expected one and dump image on mismatch
    122120    bool dumpImage = true;
    123 
     121    if (expectedHash.length() > 0) {
     122        ASSERT(expectedHash.length() == 32);
     123       
     124        printf("\nExpectedHash: %s\n", expectedHash.c_str());
     125       
     126        if (expectedHash == actualHash)     // FIXME: do case insensitive compare
     127            dumpImage = false;
     128    }
     129   
    124130    if (dumpImage) {
    125         RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context.get()));
     131        RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(context->cgContext()));
    126132        printPNG(image.get());
    127133    }
  • trunk/WebKitTools/DumpRenderTree/cg/PixelDumpSupportCG.h

    r29663 r37928  
    3232#define PixelDumpSupportCG_h
    3333
     34#include <wtf/PassRefPtr.h>
     35#include <wtf/RefCounted.h>
    3436#include <wtf/RetainPtr.h>
    3537
    36 #ifndef CGFLOAT_DEFINED
    37 #ifdef __LP64__
    38 typedef double CGFloat;
    39 #else
    40 typedef float CGFloat;
    41 #endif
    42 #define CGFLOAT_DEFINED 1
     38typedef struct CGContext* CGContextRef;
     39
     40#if PLATFORM(MAC)
     41typedef void* PlatformBitmapBuffer;
     42#elif PLATFORM(WIN)
     43typedef HBITMAP PlatformBitmapBuffer;
    4344#endif
    4445
    45 typedef struct CGContext* CGContextRef;
    46 struct CGRect;
     46class BitmapContext : public RefCounted<BitmapContext> {
     47public:
     48    static PassRefPtr<BitmapContext> createByAdoptingBitmapAndContext(PlatformBitmapBuffer buffer, CGContextRef context)
     49    {
     50        return adoptRef(new BitmapContext(buffer, context));
     51    }
    4752
    48 RetainPtr<CGContextRef> getBitmapContextFromWebView();
    49 CGRect getSelectionRect();
     53    ~BitmapContext()
     54    {
     55        if (m_buffer)
     56#if PLATFORM(MAC)
     57            free(m_buffer);
     58#elif PLATFORM(WIN)
     59            DeleteObject(m_buffer);
     60#endif
     61    }
    5062
    51 void paintWebView(CGContextRef);
    52 void repaintWebView(CGContextRef context, bool horizontal);
    53 void drawSelectionRect(CGContextRef, const CGRect&);
     63    CGContextRef cgContext() const { return m_context.get(); }
     64
     65private:
     66
     67    BitmapContext(PlatformBitmapBuffer buffer, CGContextRef context)
     68        : m_buffer(buffer)
     69        , m_context(AdoptCF, context)
     70    {
     71    }
     72
     73    PlatformBitmapBuffer m_buffer;
     74    RetainPtr<CGContextRef> m_context;
     75
     76};
     77
     78PassRefPtr<BitmapContext> createBitmapContextFromWebView(bool onscreen, bool incrementalRepaint, bool sweepHorizontally, bool drawSelectionRect);
    5479
    5580#endif // PixelDumpSupportCG_h
  • trunk/WebKitTools/DumpRenderTree/mac/DumpRenderTree.mm

    r37633 r37928  
    7676#import <wtf/OwnPtr.h>
    7777
     78using namespace std;
     79
    7880@interface DumpRenderTreeEvent : NSEvent
    7981@end
    8082
    81 static void runTest(const char *pathOrURL);
     83static void runTest(const string& testPathOrURL);
    8284
    8385// Deciding when it's OK to dump out the state is a bit tricky.  All these must be true:
     
    112114
    113115static int dumpPixels;
    114 static int dumpAllPixels;
    115116static int threaded;
    116 static int testRepaintDefault;
    117 static int repaintSweepHorizontallyDefault;
    118117static int dumpTree = YES;
    119118static int forceComplexText;
    120119static BOOL printSeparators;
    121 static NSString *currentTest = nil;
    122120static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
    123121
     
    182180}
    183181
    184 static BOOL shouldIgnoreWebCoreNodeLeaks(CFStringRef URLString)
    185 {
    186     static CFStringRef const ignoreSet[] = {
     182static bool shouldIgnoreWebCoreNodeLeaks(const string& URLString)
     183{
     184    static char* const ignoreSet[] = {
    187185        // Keeping this infrastructure around in case we ever need it again.
    188186    };
    189     static const int ignoreSetCount = sizeof(ignoreSet) / sizeof(CFStringRef);
     187    static const int ignoreSetCount = sizeof(ignoreSet) / sizeof(char*);
    190188   
    191189    for (int i = 0; i < ignoreSetCount; i++) {
    192         CFStringRef ignoreString = ignoreSet[i];
    193         CFRange range = CFRangeMake(0, CFStringGetLength(URLString));
    194         CFOptionFlags flags = kCFCompareAnchored | kCFCompareBackwards | kCFCompareCaseInsensitive;
    195         if (CFStringFindWithOptions(URLString, ignoreString, range, flags, NULL))
    196             return YES;
    197     }
    198     return NO;
     190        // FIXME: ignore case
     191        string curIgnore(ignoreSet[i]);
     192        // Match at the end of the URLString
     193        if (!URLString.compare(URLString.length() - curIgnore.length(), curIgnore.length(), curIgnore))
     194            return true;
     195    }
     196    return false;
    199197}
    200198
     
    308306{
    309307    // Give some clear to undocumented defaults values
    310     static const int MediumFontSmoothing = 2;
     308    static const int NoFontSmoothing = 0;
    311309    static const int BlueTintedAppearance = 1;
    312310
     
    314312    [defaults setObject:@"DoubleMax" forKey:@"AppleScrollBarVariant"];
    315313    [defaults setInteger:4 forKey:@"AppleAntiAliasingThreshold"]; // smallest font size to CG should perform antialiasing on
    316     [defaults setInteger:MediumFontSmoothing forKey:@"AppleFontSmoothing"];
     314    [defaults setInteger:NoFontSmoothing forKey:@"AppleFontSmoothing"];
    317315    [defaults setInteger:BlueTintedAppearance forKey:@"AppleAquaColorVariant"];
    318316    [defaults setObject:@"0.709800 0.835300 1.000000" forKey:@"AppleHighlightColor"];
     
    351349    write(STDERR_FILENO, signalName, strlen(signalName));
    352350    write(STDERR_FILENO, "\n", 1);
    353     restoreColorSpace(0);
     351    restoreMainDisplayColorProfile(0);
    354352    exit(128 + sig);
    355353}
     
    400398{
    401399    struct option options[] = {
    402         {"dump-all-pixels", no_argument, &dumpAllPixels, YES},
    403         {"horizontal-sweep", no_argument, &repaintSweepHorizontallyDefault, YES},
    404400        {"notree", no_argument, &dumpTree, NO},
    405401        {"pixel-tests", no_argument, &dumpPixels, YES},
    406         {"repaint", no_argument, &testRepaintDefault, YES},
    407402        {"tree", no_argument, &dumpTree, YES},
    408403        {"threaded", no_argument, &threaded, YES},
     
    461456   
    462457    if (dumpPixels)
    463         initializeColorSpaceAndScreeBufferForPixelTests();
     458        setupMainDisplayColorProfile();
    464459    allocateGlobalControllers();
    465460   
     
    527522
    528523    if (dumpPixels)
    529         restoreColorSpace(0);
     524        restoreMainDisplayColorProfile(0);
    530525}
    531526
     
    822817{
    823818    // W3C SVG tests expect to be 480x360
    824     bool isSVGW3CTest = ([currentTest rangeOfString:@"svg/W3C-SVG-1.1"].length);
     819    bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg/W3C-SVG-1.1") != string::npos);
    825820    if (isSVGW3CTest)
    826821        [[mainFrame webView] setFrameSize:NSMakeSize(480, 360)];
     
    916911    }
    917912   
    918     if (dumpAllPixels || (dumpPixels && !dumpAsText))
    919         dumpWebViewAsPixelsAndCompareWithExpected([currentTest UTF8String], dumpAllPixels);
     913    if (dumpPixels && !dumpAsText)
     914        dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash());
    920915   
    921916    puts("#EOF");   // terminate the (possibly empty) pixels block
     
    930925{
    931926    return strstr(pathOrURL, "loading/");
    932 }
    933 
    934 static CFURLRef createCFURLFromPathOrURL(CFStringRef pathOrURLString)
    935 {
    936     CFURLRef URL;
    937     if (CFStringHasPrefix(pathOrURLString, CFSTR("http://")) || CFStringHasPrefix(pathOrURLString, CFSTR("https://")))
    938         URL = CFURLCreateWithString(NULL, pathOrURLString, NULL);
    939     else
    940         URL = CFURLCreateWithFileSystemPath(NULL, pathOrURLString, kCFURLPOSIXPathStyle, FALSE);
    941     return URL;
    942927}
    943928
     
    970955}
    971956
    972 static void runTest(const char *pathOrURL)
    973 {
    974     CFStringRef pathOrURLString = CFStringCreateWithCString(NULL, pathOrURL, kCFStringEncodingUTF8);
     957static void runTest(const string& testPathOrURL)
     958{
     959    ASSERT(!testPathOrURL.empty());
     960   
     961    // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
     962    string pathOrURL(testPathOrURL);
     963    string expectedPixelHash;
     964   
     965    size_t separatorPos = pathOrURL.find("'");
     966    if (separatorPos != string::npos) {
     967        pathOrURL = string(testPathOrURL, 0, separatorPos);
     968        expectedPixelHash = string(testPathOrURL, separatorPos + 1);
     969    }
     970
     971    NSString *pathOrURLString = [NSString stringWithUTF8String:pathOrURL.c_str()];
    975972    if (!pathOrURLString) {
    976         fprintf(stderr, "Failed to parse filename as UTF-8: %s\n", pathOrURL);
     973        fprintf(stderr, "Failed to parse \"%s\" as UTF-8\n", pathOrURL.c_str());
    977974        return;
    978975    }
    979976
    980     CFURLRef URL = createCFURLFromPathOrURL(pathOrURLString);
    981     if (!URL) {
    982         CFRelease(pathOrURLString);
    983         fprintf(stderr, "Can't turn %s into a CFURL\n", pathOrURL);
     977    NSURL *url;
     978    if ([pathOrURLString hasPrefix:@"http://"] || [pathOrURLString hasPrefix:@"https://"])
     979        url = [NSURL URLWithString:pathOrURLString];
     980    else
     981        url = [NSURL fileURLWithPath:pathOrURLString];
     982    if (!url) {
     983        fprintf(stderr, "Failed to parse \"%s\" as a URL\n", pathOrURL.c_str());
    984984        return;
    985985    }
    986986
     987    const string testURL([[url absoluteString] UTF8String]);
     988   
    987989    resetWebViewToConsistentStateBeforeTesting();
    988990
    989     gLayoutTestController = new LayoutTestController(testRepaintDefault, repaintSweepHorizontallyDefault);
     991    gLayoutTestController = new LayoutTestController(testURL, expectedPixelHash);
    990992    topLoadingFrame = nil;
    991993    done = NO;
     
    993995    if (disallowedURLs)
    994996        CFSetRemoveAllValues(disallowedURLs);
    995     if (shouldLogFrameLoadDelegates(pathOrURL))
     997    if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
    996998        gLayoutTestController->setDumpFrameLoadCallbacks(true);
    997999
     
    10011003    lastClickPosition = NSZeroPoint;
    10021004
    1003     if (currentTest != nil)
    1004         CFRelease(currentTest);
    1005     currentTest = (NSString *)pathOrURLString;
    10061005    [prevTestBFItem release];
    10071006    prevTestBFItem = [[[[mainFrame webView] backForwardList] currentItem] retain];
     
    10101009    WorkQueue::shared()->setFrozen(false);
    10111010
    1012     BOOL _shouldIgnoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(CFURLGetString(URL));
    1013     if (_shouldIgnoreWebCoreNodeLeaks)
     1011    bool ignoreWebCoreNodeLeaks = shouldIgnoreWebCoreNodeLeaks(testURL);
     1012    if (ignoreWebCoreNodeLeaks)
    10141013        [WebCoreStatistics startIgnoringWebCoreNodeLeaks];
    10151014
    10161015    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    1017     [mainFrame loadRequest:[NSURLRequest requestWithURL:(NSURL *)URL]];
    1018     CFRelease(URL);
     1016    [mainFrame loadRequest:[NSURLRequest requestWithURL:url]];
    10191017    [pool release];
    10201018    while (!done) {
     
    10591057    gLayoutTestController = 0;
    10601058
    1061     if (_shouldIgnoreWebCoreNodeLeaks)
     1059    if (ignoreWebCoreNodeLeaks)
    10621060        [WebCoreStatistics stopIgnoringWebCoreNodeLeaks];
    10631061}
  • trunk/WebKitTools/DumpRenderTree/mac/PixelDumpSupportMac.mm

    r32995 r37928  
    3535#include "LayoutTestController.h"
    3636#include <CoreGraphics/CGBitmapContext.h>
     37#ifndef BUILDING_ON_LEOPARD
     38#include <OpenGL/OpenGL.h>
     39#include <OpenGL/CGLMacro.h>
     40#endif
    3741#include <wtf/Assertions.h>
    38 #include <wtf/RetainPtr.h>
     42#include <wtf/RefPtr.h>
    3943
    4044#import <WebKit/WebDocumentPrivate.h>
    4145#import <WebKit/WebKit.h>
    4246
    43 static unsigned char* screenCaptureBuffer;
    44 
    45 static bool changedColorProfile = false;
    46 static CMProfileLocation currentColorProfileLocation;
    47 static CGColorSpaceRef sharedColorSpace;
    48 
    49 void restoreColorSpace(int ignored)
     47// To ensure pixel tests consistency, we need to always render in the same colorspace.
     48// Unfortunately, because of AppKit / WebKit constraints, we can't render directly in the colorspace of our choice.
     49// This implies we have to temporarily change the profile of the main display to the colorspace we want to render into.
     50// We also need to make sure the CGBitmapContext we return is in that same colorspace.
     51
     52#define PROFILE_PATH "/System/Library/ColorSync/Profiles/Generic RGB Profile.icc" // FIXME: Should we be using "/System/Library/ColorSync/Profiles/sRGB Profile.icc"?
     53
     54static CMProfileLocation sInitialProfileLocation; // The locType field is initialized to 0 which is the same as cmNoProfileBase
     55
     56void restoreMainDisplayColorProfile(int ignored)
    5057{
    5158    // This is used as a signal handler, and thus the calls into ColorSync are unsafe
    5259    // But we might as well try to restore the user's color profile, we're going down anyway...
    53     if (changedColorProfile) {
    54         CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost };
    55         int error = CMSetDeviceProfile(cmDisplayDeviceClass, cmDefaultDeviceID, &scope, cmDefaultProfileID, &currentColorProfileLocation);
     60    if (sInitialProfileLocation.locType != cmNoProfileBase) {
     61        const CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost };
     62        int error = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)kCGDirectMainDisplay, &scope, cmDefaultProfileID, &sInitialProfileLocation);
    5663        if (error)
    57             fprintf(stderr, "Failed to retore previous color profile!  You may need to open System Preferences : Displays : Color and manually restore your color settings.  (Error: %i)", error);
    58         changedColorProfile = false;
     64            fprintf(stderr, "Failed to restore initial color profile for main display! Open System Preferences > Displays > Color and manually re-select the profile.  (Error: %i)", error);
     65        sInitialProfileLocation.locType = cmNoProfileBase;
    5966    }
    6067}
    6168
    62 static void failedGettingCurrentProfile(int error)
     69void setupMainDisplayColorProfile()
    6370{
    64     fprintf(stderr, "Failed to get current color profile.  I will not be able to restore your current profile, thus I'm not changing it.  Many pixel tests may fail as a result.  (Error: %i)\n", error);
     71    const CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost };
     72    int error;
     73   
     74    CMProfileRef profile = 0;
     75    error = CMGetProfileByAVID((CMDisplayIDType)kCGDirectMainDisplay, &profile);
     76    if (!error) {
     77        UInt32 size = sizeof(CMProfileLocation);
     78        error = NCMGetProfileLocation(profile, &sInitialProfileLocation, &size);
     79        CMCloseProfile(profile);
     80    }
     81    if (error) {
     82        fprintf(stderr, "Failed to retrieve current color profile for main display, thus it won't be changed.  Many pixel tests may fail as a result.  (Error: %i)", error);
     83        sInitialProfileLocation.locType = cmNoProfileBase;
     84        return;
     85    }
     86   
     87    CMProfileLocation location;
     88    location.locType = cmFileBasedProfile;
     89    NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:PROFILE_PATH]];
     90    FSRef fsRef;
     91    if (url && CFURLGetFSRef((CFURLRef)url, &fsRef)) {
     92        error = FSGetCatalogInfo(&fsRef, 0, 0, 0, &location.u.fileLoc.spec, 0);
     93        if (error == noErr)
     94        error = CMSetDeviceProfile(cmDisplayDeviceClass, (CMDeviceID)kCGDirectMainDisplay, &scope, cmDefaultProfileID, &location);
     95    }
     96    else
     97        error = -1;
     98
     99    if (error) {
     100        fprintf(stderr, "Failed to set color profile for main display!  Many pixel tests may fail as a result.  (Error: %i)", error);
     101        sInitialProfileLocation.locType = cmNoProfileBase;
     102        return;
     103    }
     104   
     105    // Other signals are handled in installSignalHandlers() which also calls restoreMainDisplayColorProfile()
     106    signal(SIGINT, restoreMainDisplayColorProfile);
     107    signal(SIGHUP, restoreMainDisplayColorProfile);
     108    signal(SIGTERM, restoreMainDisplayColorProfile);
     109   
     110    fprintf(stderr, "\n\nWARNING: Temporarily changing the main display color profile to \"%s\": the colors on your screen will change for the duration of the testing.\n", PROFILE_PATH);
     111    fprintf(stderr, "This allows the WebKit pixel-based regression tests to have consistent color values across all machines.\n");
    65112}
    66113
    67 static void setDefaultColorProfileToRGB()
     114PassRefPtr<BitmapContext> createBitmapContextFromWebView(bool onscreen, bool incrementalRepaint, bool sweepHorizontally, bool drawSelectionRect)
    68115{
    69     CMProfileRef genericProfile = (CMProfileRef)[[NSColorSpace genericRGBColorSpace] colorSyncProfile];
    70     CMProfileLocation genericProfileLocation;
    71     UInt32 locationSize = sizeof(genericProfileLocation);
    72     int error = NCMGetProfileLocation(genericProfile, &genericProfileLocation, &locationSize);
    73     if (error) {
    74         failedGettingCurrentProfile(error);
    75         return;
    76     }
    77 
    78     CMProfileLocation previousProfileLocation;
    79     error = CMGetDeviceProfile(cmDisplayDeviceClass, cmDefaultDeviceID, cmDefaultProfileID, &previousProfileLocation);
    80     if (error) {
    81         failedGettingCurrentProfile(error);
    82         return;
    83     }
    84 
    85     CMProfileRef previousProfile;
    86     error = CMOpenProfile(&previousProfile, &previousProfileLocation);
    87     if (error) {
    88         failedGettingCurrentProfile(error);
    89         return;
    90     }
    91 
    92     CFStringRef previousProfileName;
    93     CFStringRef genericProfileName;
    94     char previousProfileNameString[1024];
    95     char genericProfileNameString[1024];
    96     CMCopyProfileDescriptionString(previousProfile, &previousProfileName);
    97     CMCopyProfileDescriptionString(genericProfile, &genericProfileName);
    98     CFStringGetCString(previousProfileName, previousProfileNameString, sizeof(previousProfileNameString), kCFStringEncodingUTF8);
    99     CFStringGetCString(genericProfileName, genericProfileNameString, sizeof(previousProfileNameString), kCFStringEncodingUTF8);
    100     CFRelease(previousProfileName);
    101     CFRelease(genericProfileName);
    102 
    103     CMCloseProfile(previousProfile);
    104 
    105     fprintf(stderr, "\n\nWARNING: Temporarily changing your system color profile from \"%s\" to \"%s\".\n", previousProfileNameString, genericProfileNameString);
    106     fprintf(stderr, "This allows the WebKit pixel-based regression tests to have consistent color values across all machines.\n");
    107     fprintf(stderr, "The colors on your screen will change for the duration of the testing.\n\n");
    108 
    109     CMDeviceScope scope = { kCFPreferencesCurrentUser, kCFPreferencesCurrentHost };
    110     if ((error = CMSetDeviceProfile(cmDisplayDeviceClass, cmDefaultDeviceID, &scope, cmDefaultProfileID, &genericProfileLocation))) {
    111         fprintf(stderr, "Failed to set color profile to \"%s\"! Many pixel tests will fail as a result.  (Error: %i)",
    112             genericProfileNameString, error);
     116    WebView* view = [mainFrame webView];
     117    NSSize webViewSize = [view frame].size;
     118    size_t pixelsWide = static_cast<size_t>(webViewSize.width);
     119    size_t pixelsHigh = static_cast<size_t>(webViewSize.height);
     120    size_t rowBytes = (4 * pixelsWide + 63) & ~63; // Use a multiple of 64 bytes to improve CG performance
     121
     122    void *buffer = calloc(pixelsHigh, rowBytes);
     123    if (!buffer)
     124        return 0;
     125   
     126    static CGColorSpaceRef colorSpace = 0;
     127    if (!colorSpace) {
     128        CMProfileLocation location;
     129        location.locType = cmFileBasedProfile;
     130        NSURL *url = [NSURL fileURLWithPath:[NSString stringWithUTF8String:PROFILE_PATH]];
     131        FSRef fsRef;
     132        if (url && CFURLGetFSRef((CFURLRef)url, &fsRef)) {
     133            if (FSGetCatalogInfo(&fsRef, 0, 0, 0, &location.u.fileLoc.spec, 0) == noErr) {
     134                CMProfileRef profile;
     135                if (CMOpenProfile(&profile, &location) == noErr) {
     136                    colorSpace = CGColorSpaceCreateWithPlatformColorSpace(profile);
     137                    CMCloseProfile(profile);
     138                }
     139            }
     140        }
     141    }
     142   
     143    CGContextRef context = CGBitmapContextCreate(buffer, pixelsWide, pixelsHigh, 8, rowBytes, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host); // Use ARGB8 on PPC or BGRA8 on X86 to improve CG performance
     144    if (!context) {
     145        free(buffer);
     146        return 0;
     147    }
     148
     149    // The BitmapContext keeps the CGContextRef and the pixel buffer alive
     150    RefPtr<BitmapContext> bitmapContext = BitmapContext::createByAdoptingBitmapAndContext(buffer, context);
     151   
     152    NSGraphicsContext *nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
     153    ASSERT(nsContext);
     154   
     155    if (incrementalRepaint) {
     156        if (sweepHorizontally) {
     157            for (NSRect column = NSMakeRect(0, 0, 1, webViewSize.height); column.origin.x < webViewSize.width; column.origin.x++)
     158                [view displayRectIgnoringOpacity:column inContext:nsContext];
     159        } else {
     160            for (NSRect line = NSMakeRect(0, 0, webViewSize.width, 1); line.origin.y < webViewSize.height; line.origin.y++)
     161                [view displayRectIgnoringOpacity:line inContext:nsContext];
     162        }
    113163    } else {
    114         currentColorProfileLocation = previousProfileLocation;
    115         changedColorProfile = true;
    116         signal(SIGINT, restoreColorSpace);
    117         signal(SIGHUP, restoreColorSpace);
    118         signal(SIGTERM, restoreColorSpace);
    119     }
     164        if (onscreen) {
     165#ifdef BUILDING_ON_LEOPARD
     166            // Ask the window server to provide us a composited version of the *real* window content including surfaces (i.e. OpenGL content)
     167            // Note that the returned image might differ very slightly from the window backing because of dithering artifacts in the window server compositor
     168           
     169            CGImageRef image = CGWindowListCreateImage(CGRectNull, kCGWindowListOptionIncludingWindow, [[view window] windowNumber], kCGWindowImageBoundsIgnoreFraming | kCGWindowImageShouldBeOpaque);
     170            CGContextDrawImage(context, CGRectMake(0, 0, CGImageGetWidth(image), CGImageGetHeight(image)), image);
     171            CGImageRelease(image);
     172#else
     173            // On 10.4 and earlier, we have to move the window temporarily "onscreen" and read directly from the display framebuffer using OpenGL
     174            // In this code path, we need to ensure the window is above any other window or captured result will be corrupted
     175           
     176            NSWindow *window = [view window];
     177            int oldLevel = [window level];
     178            NSRect oldFrame = [window frame];
     179           
     180            NSRect newFrame = [[[NSScreen screens] objectAtIndex:0] frame];
     181            newFrame = NSMakeRect(newFrame.origin.x + (newFrame.size.width - oldFrame.size.width) / 2, newFrame.origin.y + (newFrame.size.height - oldFrame.size.height) / 2, oldFrame.size.width, oldFrame.size.height);
     182            [window setLevel:NSScreenSaverWindowLevel];
     183            [window setFrame:newFrame display:NO animate:NO];
     184           
     185            CGRect rect = CGRectMake(newFrame.origin.x, newFrame.origin.y, webViewSize.width, webViewSize.height);
     186            CGDirectDisplayID displayID;
     187            CGDisplayCount count;
     188            if (CGGetDisplaysWithRect(rect, 1, &displayID, &count) == kCGErrorSuccess) {
     189                CGRect bounds = CGDisplayBounds(displayID);
     190                rect.origin.x -= bounds.origin.x;
     191                rect.origin.y -= bounds.origin.y;
     192               
     193                CGLPixelFormatAttribute attributes[] = {kCGLPFAAccelerated, kCGLPFANoRecovery, kCGLPFAFullScreen, kCGLPFADisplayMask, (CGLPixelFormatAttribute)CGDisplayIDToOpenGLDisplayMask(displayID), (CGLPixelFormatAttribute)0};
     194                CGLPixelFormatObj pixelFormat;
     195                GLint num;
     196                if (CGLChoosePixelFormat(attributes, &pixelFormat, &num) == kCGLNoError) {
     197                    CGLContextObj cgl_ctx;
     198                    if (CGLCreateContext(pixelFormat, 0, &cgl_ctx) == kCGLNoError) {
     199                        if (CGLSetFullScreen(cgl_ctx) == kCGLNoError) {
     200                            void *flipBuffer = calloc(pixelsHigh, rowBytes);
     201                            if (flipBuffer) {
     202                                glPixelStorei(GL_PACK_ROW_LENGTH, rowBytes / 4);
     203                                glPixelStorei(GL_PACK_ALIGNMENT, 4);
     204#if __BIG_ENDIAN__
     205                                glReadPixels(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, flipBuffer);
     206#else
     207                                glReadPixels(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV, flipBuffer);
     208#endif
     209                                if (!glGetError()) {
     210                                    for(size_t i = 0; i < pixelsHigh; ++i)
     211                                    bcopy((char*)flipBuffer + rowBytes * i, (char*)buffer + rowBytes * (pixelsHigh - i - 1), pixelsWide * 4);
     212                                }
     213                               
     214                                free(flipBuffer);
     215                            }
     216                        }
     217                        CGLDestroyContext(cgl_ctx);
     218                    }
     219                    CGLDestroyPixelFormat(pixelFormat);
     220                }
     221            }
     222           
     223            [window setFrame:oldFrame display:NO animate:NO];
     224            [window setLevel:oldLevel];
     225#endif
     226        } else {
     227            // Grab directly the contents of the window backing buffer (this ignores any surfaces on the window)
     228            // FIXME: This path is suboptimal: data is read from window backing store, converted to RGB8 then drawn again into an RGBA8 bitmap
     229           
     230            [view displayIfNeeded];
     231            [view lockFocus];
     232            NSBitmapImageRep *imageRep = [[[NSBitmapImageRep alloc] initWithFocusedViewRect:[view frame]] autorelease];
     233            [view unlockFocus];
     234
     235            RetainPtr<NSGraphicsContext> savedContext = [NSGraphicsContext currentContext];
     236            [NSGraphicsContext setCurrentContext:nsContext];
     237            [imageRep draw];
     238            [NSGraphicsContext setCurrentContext:savedContext.get()];
     239        }
     240    }
     241
     242    if (drawSelectionRect) {
     243        NSView *documentView = [[mainFrame frameView] documentView];
     244        ASSERT([documentView conformsToProtocol:@protocol(WebDocumentSelection)]);
     245        NSRect rect = [documentView convertRect:[(id <WebDocumentSelection>)documentView selectionRect] fromView:nil];
     246        CGContextSaveGState(context);
     247        CGContextSetLineWidth(context, 1.0);
     248        CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
     249        CGContextStrokeRect(context, CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height));
     250        CGContextRestoreGState(context);
     251    }
     252   
     253    return bitmapContext.release();
    120254}
    121 
    122 void initializeColorSpaceAndScreeBufferForPixelTests()
    123 {
    124     setDefaultColorProfileToRGB();
    125     screenCaptureBuffer = (unsigned char *)malloc(maxViewHeight * maxViewWidth * 4);
    126     sharedColorSpace = CGColorSpaceCreateDeviceRGB();
    127 }
    128 
    129 // Declared in PixelDumpSupportCG.h
    130 
    131 RetainPtr<CGContextRef> getBitmapContextFromWebView()
    132 {
    133     NSSize webViewSize = [[mainFrame webView] frame].size;
    134     return RetainPtr<CGContextRef>(AdoptCF, CGBitmapContextCreate(screenCaptureBuffer, static_cast<size_t>(webViewSize.width), static_cast<size_t>(webViewSize.height), 8, static_cast<size_t>(webViewSize.width) * 4, sharedColorSpace, kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedLast));
    135 }
    136 
    137 void paintWebView(CGContextRef context)
    138 {
    139     RetainPtr<NSGraphicsContext> savedContext = [NSGraphicsContext currentContext];
    140 
    141     NSGraphicsContext* nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
    142     [NSGraphicsContext setCurrentContext:nsContext];
    143 
    144     WebView* view = [mainFrame webView];
    145     [view displayIfNeeded];
    146     [view lockFocus];
    147     NSBitmapImageRep *imageRep = [[NSBitmapImageRep alloc] initWithFocusedViewRect:[view frame]];
    148     [view unlockFocus];
    149     [imageRep draw];
    150     [imageRep release];
    151 
    152     [NSGraphicsContext setCurrentContext:savedContext.get()];
    153 }
    154 
    155 void repaintWebView(CGContextRef context, bool horizontal)
    156 {
    157     RetainPtr<NSGraphicsContext> savedContext = [NSGraphicsContext currentContext];
    158 
    159     NSGraphicsContext* nsContext = [NSGraphicsContext graphicsContextWithGraphicsPort:context flipped:NO];
    160     [NSGraphicsContext setCurrentContext:nsContext];
    161 
    162     WebView *view = [mainFrame webView];
    163     NSSize webViewSize = [view frame].size;
    164 
    165     if (horizontal) {
    166         for (NSRect column = NSMakeRect(0, 0, 1, webViewSize.height); column.origin.x < webViewSize.width; column.origin.x++)
    167             [view displayRectIgnoringOpacity:column inContext:nsContext];
    168     } else {
    169         for (NSRect line = NSMakeRect(0, 0, webViewSize.width, 1); line.origin.y < webViewSize.height; line.origin.y++)
    170             [view displayRectIgnoringOpacity:line inContext:nsContext];
    171     }
    172 
    173     [NSGraphicsContext setCurrentContext:savedContext.get()];
    174 }
    175 
    176 CGRect getSelectionRect()
    177 {
    178     NSView *documentView = [[mainFrame frameView] documentView];
    179     if ([documentView conformsToProtocol:@protocol(WebDocumentSelection)]) {
    180         NSRect rect = [documentView convertRect:[(id <WebDocumentSelection>)documentView selectionRect] fromView:nil];
    181         return CGRectMake(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
    182     }
    183 
    184     ASSERT_NOT_REACHED();
    185     return CGRectZero;
    186 }
  • trunk/WebKitTools/DumpRenderTree/win/DumpRenderTree.cpp

    r37633 r37928  
    3939#include "WorkQueueItem.h"
    4040#include "WorkQueue.h"
     41
     42#include <fcntl.h>
     43#include <io.h>
     44#include <math.h>
     45#include <pthread.h>
     46#include <shlwapi.h>
     47#include <stdio.h>
     48#include <string.h>
     49#include <tchar.h>
    4150#include <wtf/RetainPtr.h>
    4251#include <wtf/Vector.h>
     52#include <windows.h>
     53#include <CFNetwork/CFURLCachePriv.h>
     54#include <CoreFoundation/CoreFoundation.h>
     55#include <JavaScriptCore/JavaScriptCore.h>
    4356#include <WebCore/COMPtr.h>
    44 #include <CoreFoundation/CoreFoundation.h>
    45 #include <CFNetwork/CFURLCachePriv.h>
    46 #include <JavaScriptCore/JavaScriptCore.h>
    47 #include <math.h>
    48 #include <pthread.h>
    49 #include <string>
    50 #include <tchar.h>
    5157#include <WebKit/ForEachCoClass.h>
    5258#include <WebKit/WebKit.h>
    53 #include <fcntl.h>
    54 #include <io.h>
    55 #include <windows.h>
    56 #include <stdio.h>
    57 #include <shlwapi.h>
    58 
    59 using std::wstring;
     59
     60using namespace std;
    6061
    6162#ifndef NDEBUG
     
    7879static bool forceComplexText = false;
    7980static RetainPtr<CFStringRef> persistentUserStyleSheetLocation;
    80 
    81 static const char* currentTest;
    8281
    8382volatile bool done;
     
    563562            resultString = SysAllocStringLen(result.data(), result.size());
    564563        } else {
    565             bool isSVGW3CTest = strstr(currentTest, "svg\\W3C-SVG-1.1");
     564            bool isSVGW3CTest = (gLayoutTestController->testPathOrURL().find("svg\\W3C-SVG-1.1") != string::npos);
    566565            unsigned width;
    567566            unsigned height;
     
    609608    if (dumpPixels) {
    610609        if (!gLayoutTestController->dumpAsText() && !gLayoutTestController->dumpDOMAsWebArchive() && !gLayoutTestController->dumpSourceAsWebArchive())
    611             dumpWebViewAsPixelsAndCompareWithExpected(currentTest, dumpAllPixels);
     610            dumpWebViewAsPixelsAndCompareWithExpected(gLayoutTestController->expectedPixelHash());
    612611    }
    613612
     
    676675}
    677676
    678 static void runTest(const char* pathOrURL)
     677static void runTest(const string& testPathOrURL)
    679678{
    680679    static BSTR methodBStr = SysAllocString(TEXT("GET"));
    681680
     681    // Look for "'" as a separator between the path or URL, and the pixel dump hash that follows.
     682    string pathOrURL(testPathOrURL);
     683    string expectedPixelHash;
     684   
     685    size_t separatorPos = pathOrURL.find("'");
     686    if (separatorPos != string::npos) {
     687        pathOrURL = string(testPathOrURL, 0, separatorPos);
     688        expectedPixelHash = string(testPathOrURL, separatorPos + 1);
     689    }
     690   
    682691    BSTR urlBStr;
    683692 
    684     CFStringRef str = CFStringCreateWithCString(0, pathOrURL, kCFStringEncodingWindowsLatin1);
     693    CFStringRef str = CFStringCreateWithCString(0, pathOrURL.c_str(), kCFStringEncodingWindowsLatin1);
    685694    CFURLRef url = CFURLCreateWithString(0, str, 0);
    686695
     
    701710    CFRelease(url);
    702711
    703     currentTest = pathOrURL;
    704 
    705     ::gLayoutTestController = new LayoutTestController(false, false);
     712    ::gLayoutTestController = new LayoutTestController(pathOrURL, expectedPixelHash);
    706713    done = false;
    707714    topLoadingFrame = 0;
    708715
    709     if (shouldLogFrameLoadDelegates(pathOrURL))
     716    if (shouldLogFrameLoadDelegates(pathOrURL.c_str()))
    710717        gLayoutTestController->setDumpFrameLoadCallbacks(true);
    711718
  • trunk/WebKitTools/DumpRenderTree/win/PixelDumpSupportWin.cpp

    r37587 r37928  
    3535#include <wtf/RetainPtr.h>
    3636
    37 RetainPtr<CGContextRef> getBitmapContextFromWebView()
     37PassRefPtr<BitmapContext> getBitmapContextFromWebView(bool onscreen, bool incrementalRepaint, bool sweepHorizontally, bool drawSelectionRect)
    3838{
    3939    RECT frame;
     
    4949    bmp.bmiHeader.biCompression = BI_RGB;
    5050
    51     // FIXME: Currently we leak this HBITMAP because we don't have a good way
    52     // to destroy it when the CGBitmapContext gets destroyed.
    5351    void* bits = 0;
    5452    HBITMAP bitmap = CreateDIBSection(0, &bmp, DIB_RGB_COLORS, &bits, 0, 0);
     
    6462
    6563    RetainPtr<CGColorSpaceRef> colorSpace(AdoptCF, CGColorSpaceCreateDeviceRGB());
    66     return RetainPtr<CGContextRef>(AdoptCF, CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8,
    67                                                 info.bmWidthBytes, colorSpace.get(), kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst));
     64    CGContextRef context = CGBitmapContextCreate(info.bmBits, info.bmWidth, info.bmHeight, 8,
     65                                                info.bmWidthBytes, colorSpace.get(), kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
     66
     67    return BitmapContext::createByAdoptingBitmapAndContext(bitmap, context);
    6868}
  • trunk/WebKitTools/Scripts/run-webkit-tests

    r37675 r37928  
    113113my $pixelTests = '';
    114114my $quiet = '';
    115 my $repaintSweepHorizontally = '';
    116 my $repaintTests = '';
    117115my $report10Slowest = 0;
    118116my $resetResults = 0;
     
    124122my $testResultsDirectory = "/tmp/layout-test-results";
    125123my $threaded = 0;
    126 my $threshold = 0;
     124my $tolerance = 0;
    127125my $treatSkipped = "default";
    128126my $verbose = 0;
     
    181179  -g|--guard-malloc               Enable malloc guard
    182180  --help                          Show this help message
    183   -h|--horizontal-sweep           Change repaint to sweep horizontally instead of vertically (implies --repaint-tests)
    184181  --[no-]http                     Run (or do not run) http tests (default: $httpDefault)
    185182  -i|--ignore-tests               Comma-separated list of directories or tests to ignore
     
    188185  --[no-]new-test-results         Generate results for new tests
    189186  -p|--pixel-tests                Enable pixel tests
     187  --tolerance t                   Ignore image differences less than this percentage (implies --pixel-tests)
    190188  --platform                      Override the detected platform to use for tests and results (default: $platform)
    191189  --port                          Web server port to use with http tests
    192190  -q|--quiet                      Less verbose output
    193   -r|--repaint-tests              Run repaint tests (implies --pixel-tests)
    194191  --reset-results                 Reset ALL results (including pixel tests if --pixel-tests is set)
    195192  -o|--results-directory          Output results directory (default: $testResultsDirectory)
     
    207204  --[no-]strip-editing-callbacks  Remove editing callbacks from expected results
    208205  -t|--threaded                   Run a concurrent JavaScript thead with each test
    209   --threshold t                   Ignore pixel value deviations less than or equal to t
    210206  --valgrind                      Run DumpRenderTree inside valgrind (Qt/Linux only)
    211207  -v|--verbose                    More verbose output (overrides --quiet)
     
    222218    'guard-malloc|g' => \$guardMalloc,
    223219    'help' => \$showHelp,
    224     'horizontal-sweep|h' => \$repaintSweepHorizontally,
    225220    'http!' => \$testHTTP,
    226221    'ignore-tests|i=s' => \$ignoreTests,
     
    231226    'port=i' => \$httpdPort,
    232227    'quiet|q' => \$quiet,
    233     'repaint-tests|r' => \$repaintTests,
    234228    'reset-results' => \$resetResults,
    235229    'new-test-results!' => \$generateNewResults,
     
    240234    'slowest' => \$report10Slowest,
    241235    'threaded|t' => \$threaded,
    242     'threshold=i' => \$threshold,
     236    'tolerance=f' => \$tolerance,
    243237    'verbose|v' => \$verbose,
    244238    'valgrind' => \$useValgrind,
     
    268262my $configurationOption = "--" . lc($configuration);
    269263
    270 $repaintTests = 1 if $repaintSweepHorizontally;
    271 
    272 $pixelTests = 1 if $repaintTests;
    273 $pixelTests = 1 if $threshold > 0;
     264$pixelTests = 1 if $tolerance > 0;
    274265
    275266$testsPerDumpTool = 1000 if !$testsPerDumpTool;
     
    292283chdirWebKit();
    293284
    294 if(!defined($root)){
     285if (!defined($root)) {
    295286    # Push the parameters to build-dumprendertree as an array
    296287    my @args;
     
    455446my $leaksOutputFileNumber = 1;
    456447my $totalLeaks = 0;
    457 my $dumpAllPixels = $pixelTests && $resetResults;
    458448
    459449my @toolArgs = ();
    460 push @toolArgs, "--dump-all-pixels" if $dumpAllPixels;
    461450push @toolArgs, "--pixel-tests" if $pixelTests;
    462 push @toolArgs, "--repaint" if $repaintTests;
    463 push @toolArgs, "--horizontal-sweep" if $repaintSweepHorizontally;
    464451push @toolArgs, "--threaded" if $threaded;
    465452push @toolArgs, "--complex-text" if $complexText;
     
    467454
    468455my @diffToolArgs = ();
    469 push @diffToolArgs, "--threshold", $threshold;
     456push @diffToolArgs, "--tolerance", $tolerance;
    470457
    471458$| = 1;
     
    554541    my $startTime = time if $report10Slowest;
    555542
     543    # Try to read expected hash file for pixel tests
     544    my $suffixExpectedHash = "";
     545    if ($pixelTests && !$resetResults) {
     546        my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png");
     547        if (open EXPECTEDHASH, "$expectedPixelDir/$base-$expectedTag.checksum") {
     548            my $expectedHash = <EXPECTEDHASH>;
     549            chomp($expectedHash);
     550            close EXPECTEDHASH;
     551           
     552            # Format expected hash into a suffix string that is appended to the path / URL passed to DRT
     553            $suffixExpectedHash = "'$expectedHash";
     554        }
     555    }
     556
    556557    if ($test !~ /^http\//) {
    557558        my $testPath = "$testDirectory/$test";
     
    561562            $testPath = canonpath($testPath);
    562563        }
    563         print OUT "$testPath\n";
     564        print OUT "$testPath$suffixExpectedHash\n";
    564565    } else {
    565566        openHTTPDIfNeeded();
     
    567568            my $path = canonpath($test);
    568569            $path =~ s/^http\/tests\///;
    569             print OUT "http://127.0.0.1:$httpdPort/$path\n";
     570            print OUT "http://127.0.0.1:$httpdPort/$path$suffixExpectedHash\n";
    570571        } elsif ($test =~ /^http\/tests\/ssl\//) {
    571572            my $path = canonpath($test);
    572573            $path =~ s/^http\/tests\///;
    573             print OUT "https://127.0.0.1:$httpdSSLPort/$path\n";
     574            print OUT "https://127.0.0.1:$httpdSSLPort/$path$suffixExpectedHash\n";
    574575        } else {
    575576            my $testPath = "$testDirectory/$test";
     
    579580                $testPath = canonpath($testPath);
    580581            }
    581             print OUT "$testPath\n";
     582            print OUT "$testPath$suffixExpectedHash\n";
    582583        }
    583584    }
     
    642643    my $diffResult = "passed";
    643644
    644     # Look for the pixel block, and do an image diff if we find one
    645645    my $actualHash = "";
    646646    my $expectedHash = "";
     
    651651        if (/ActualHash: ([a-f0-9]{32})/) {
    652652            $actualHash = $1;
    653         } elsif (/BaselineHash: ([a-f0-9]{32})/) {
     653        } elsif (/ExpectedHash: ([a-f0-9]{32})/) {
    654654            $expectedHash = $1;
    655655        } elsif (/Content-Length: (\d+)\s*/) {
     
    659659    }
    660660
    661     # If we got some PNG data, diff with expected
     661    if ($verbose && $pixelTests && !$resetResults && $actualPNGSize) {
     662        if ($actualHash eq "" && $expectedHash eq "") {
     663            printFailureMessageForTest($test, "WARNING: actual & expected pixel hashes are missing!");
     664        } elsif ($actualHash eq "") {
     665            printFailureMessageForTest($test, "WARNING: actual pixel hash is missing!");
     666        } elsif ($expectedHash eq "") {
     667            printFailureMessageForTest($test, "WARNING: expected pixel hash is missing!");
     668        }
     669    }
     670
    662671    if ($actualPNGSize > 0) {
    663         my $expectedPixelDir = expectedDirectoryForTest($base, $isText, "png");
    664 
    665         if ($expectedHash ne $actualHash && -f "$expectedPixelDir/$base-$expectedTag.png") {
    666             my $expectedPNGSize = -s "$expectedPixelDir/$base-$expectedTag.png";
    667             my $expectedPNG = "";
    668             open EXPECTEDPNG, "$expectedPixelDir/$base-$expectedTag.png";
    669             read(EXPECTEDPNG, $expectedPNG, $expectedPNGSize);
    670 
    671             print DIFFOUT "Content-Length: $actualPNGSize\n";
    672             print DIFFOUT $actualPNG;
    673 
    674             print DIFFOUT "Content-Length: $expectedPNGSize\n";
    675             print DIFFOUT $expectedPNG;
    676 
    677             while (<DIFFIN>) {
    678                 last if /^error/ || /^diff:/;
    679                 if (/Content-Length: (\d+)\s*/) {
    680                     read(DIFFIN, $diffPNG, $1);
     672        my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png");
     673
     674        if (!$resetResults && ($expectedHash ne $actualHash || ($actualHash eq "" && $expectedHash eq ""))) {
     675            if (-f "$expectedPixelDir/$base-$expectedTag.png") {
     676                my $expectedPNGSize = -s "$expectedPixelDir/$base-$expectedTag.png";
     677                my $expectedPNG = "";
     678                open EXPECTEDPNG, "$expectedPixelDir/$base-$expectedTag.png";
     679                read(EXPECTEDPNG, $expectedPNG, $expectedPNGSize);
     680
     681                print DIFFOUT "Content-Length: $actualPNGSize\n";
     682                print DIFFOUT $actualPNG;
     683
     684                print DIFFOUT "Content-Length: $expectedPNGSize\n";
     685                print DIFFOUT $expectedPNG;
     686
     687                while (<DIFFIN>) {
     688                    last if /^error/ || /^diff:/;
     689                    if (/Content-Length: (\d+)\s*/) {
     690                        read(DIFFIN, $diffPNG, $1);
     691                    }
    681692                }
     693
     694                if (/^diff: (.+)% (passed|failed)/) {
     695                    $diffPercentage = $1;
     696                    $imageDifferences{$base} = $diffPercentage;
     697                    $diffResult = $2;
     698                }
     699               
     700                if ($diffPercentage == 0) {
     701                    printFailureMessageForTest($test, "pixel hash failed (but pixel test still passes)");
     702                }
     703            } elsif ($verbose) {
     704                printFailureMessageForTest($test, "WARNING: expected image is missing!");
    682705            }
    683 
    684             if (/^diff: (.+)% (passed|failed)/) {
    685                 $diffPercentage = $1;
    686                 $imageDifferences{$base} = $diffPercentage;
    687                 $diffResult = $2;
    688             }
    689         }
    690 
    691         if ($actualPNGSize && ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.png")) {
     706        }
     707
     708        if ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.png") {
    692709            mkpath catfile($expectedPixelDir, dirname($base)) if $testDirectory ne $expectedPixelDir;
    693710            writeToFile("$expectedPixelDir/$base-$expectedTag.png", $actualPNG);
    694711        }
    695712
    696         # update the expected hash if the image diff said that there was no difference
    697713        if ($actualHash ne "" && ($resetResults || !-f "$expectedPixelDir/$base-$expectedTag.checksum")) {
    698714            writeToFile("$expectedPixelDir/$base-$expectedTag.checksum", $actualHash);
     
    742758
    743759          $diffResult = "failed";
    744           if($verbose) {
     760          if ($verbose) {
    745761            print "\n";
    746762            system "cat /tmp/simplified.diff";
     
    818834            writeToFile("$testResultsDirectory/$base-$diffsTag.png", $diffPNG);
    819835
    820             my $expectedPixelDir = expectedDirectoryForTest($base, $isText, "png");
     836            my $expectedPixelDir = expectedDirectoryForTest($base, 0, "png");
    821837            copy("$expectedPixelDir/$base-$expectedTag.png", "$testResultsDirectory/$base-$expectedTag.png");
    822838
Note: See TracChangeset for help on using the changeset viewer.