Home | History | Annotate | Download | only in cg
      1 /*
      2  * Copyright (C) 2011 Apple Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  * 1. Redistributions of source code must retain the above copyright
      8  *    notice, this list of conditions and the following disclaimer.
      9  * 2. Redistributions in binary form must reproduce the above copyright
     10  *    notice, this list of conditions and the following disclaimer in the
     11  *    documentation and/or other materials provided with the distribution.
     12  *
     13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
     14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
     17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     23  * THE POSSIBILITY OF SUCH DAMAGE.
     24  */
     25 
     26 #include "config.h"
     27 #include "TestInvocation.h"
     28 
     29 #include "PlatformWebView.h"
     30 #include "TestController.h"
     31 #include <ImageIO/CGImageDestination.h>
     32 #include <WebKit2/WKImageCG.h>
     33 #include <wtf/MD5.h>
     34 #include <wtf/RetainPtr.h>
     35 #include <wtf/StringExtras.h>
     36 
     37 #if PLATFORM(MAC)
     38 #include <LaunchServices/UTCoreTypes.h>
     39 #endif
     40 
     41 #if PLATFORM(WIN)
     42 static const CFStringRef kUTTypePNG = CFSTR("public.png");
     43 #endif
     44 
     45 namespace WTR {
     46 
     47 static CGContextRef createCGContextFromImage(WKImageRef wkImage)
     48 {
     49     RetainPtr<CGImageRef> image(AdoptCF, WKImageCreateCGImage(wkImage));
     50 
     51     size_t pixelsWide = CGImageGetWidth(image.get());
     52     size_t pixelsHigh = CGImageGetHeight(image.get());
     53     size_t rowBytes = (4 * pixelsWide + 63) & ~63;
     54     void* buffer = calloc(pixelsHigh, rowBytes);
     55 
     56     CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
     57     CGContextRef context = CGBitmapContextCreate(buffer, pixelsWide, pixelsHigh, 8, rowBytes, colorSpace, kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host);
     58     CGColorSpaceRelease(colorSpace);
     59 
     60     CGContextDrawImage(context, CGRectMake(0, 0, pixelsWide, pixelsHigh), image.get());
     61 
     62     return context;
     63 }
     64 
     65 void computeMD5HashStringForContext(CGContextRef bitmapContext, char hashString[33])
     66 {
     67     ASSERT(CGBitmapContextGetBitsPerPixel(bitmapContext) == 32); // ImageDiff assumes 32 bit RGBA, we must as well.
     68     size_t pixelsHigh = CGBitmapContextGetHeight(bitmapContext);
     69     size_t pixelsWide = CGBitmapContextGetWidth(bitmapContext);
     70     size_t bytesPerRow = CGBitmapContextGetBytesPerRow(bitmapContext);
     71 
     72     // We need to swap the bytes to ensure consistent hashes independently of endianness
     73     MD5 md5;
     74     unsigned char* bitmapData = static_cast<unsigned char*>(CGBitmapContextGetData(bitmapContext));
     75 #if PLATFORM(MAC)
     76     if ((CGBitmapContextGetBitmapInfo(bitmapContext) & kCGBitmapByteOrderMask) == kCGBitmapByteOrder32Big) {
     77         for (unsigned row = 0; row < pixelsHigh; row++) {
     78             Vector<uint8_t> buffer(4 * pixelsWide);
     79             for (unsigned column = 0; column < pixelsWide; column++)
     80                 buffer[column] = OSReadLittleInt32(bitmapData, 4 * column);
     81             md5.addBytes(buffer);
     82             bitmapData += bytesPerRow;
     83         }
     84     } else {
     85 #endif
     86         for (unsigned row = 0; row < pixelsHigh; row++) {
     87             md5.addBytes(bitmapData, 4 * pixelsWide);
     88             bitmapData += bytesPerRow;
     89         }
     90 #if PLATFORM(MAC)
     91     }
     92 #endif
     93 
     94     Vector<uint8_t, 16> hash;
     95     md5.checksum(hash);
     96 
     97     hashString[0] = '\0';
     98     for (int i = 0; i < 16; i++)
     99         snprintf(hashString, 33, "%s%02x", hashString, hash[i]);
    100 }
    101 
    102 static void dumpBitmap(CGContextRef bitmapContext)
    103 {
    104     RetainPtr<CGImageRef> image(AdoptCF, CGBitmapContextCreateImage(bitmapContext));
    105     RetainPtr<CFMutableDataRef> imageData(AdoptCF, CFDataCreateMutable(0, 0));
    106     RetainPtr<CGImageDestinationRef> imageDest(AdoptCF, CGImageDestinationCreateWithData(imageData.get(), kUTTypePNG, 1, 0));
    107     CGImageDestinationAddImage(imageDest.get(), image.get(), 0);
    108     CGImageDestinationFinalize(imageDest.get());
    109 
    110     const unsigned char* data = CFDataGetBytePtr(imageData.get());
    111     const size_t dataLength = CFDataGetLength(imageData.get());
    112 
    113 
    114     fprintf(stdout, "Content-Type: %s\n", "image/png");
    115     fprintf(stdout, "Content-Length: %lu\n", static_cast<unsigned long>(dataLength));
    116 
    117     const size_t bytesToWriteInOneChunk = 1 << 15;
    118     size_t dataRemainingToWrite = dataLength;
    119     while (dataRemainingToWrite) {
    120         size_t bytesToWriteInThisChunk = std::min(dataRemainingToWrite, bytesToWriteInOneChunk);
    121         size_t bytesWritten = fwrite(data, 1, bytesToWriteInThisChunk, stdout);
    122         if (bytesWritten != bytesToWriteInThisChunk)
    123             break;
    124         dataRemainingToWrite -= bytesWritten;
    125         data += bytesWritten;
    126     }
    127 }
    128 
    129 void TestInvocation::dumpPixelsAndCompareWithExpected(WKImageRef image)
    130 {
    131     CGContextRef context = createCGContextFromImage(image);
    132 
    133     // Compute the hash of the bitmap context pixels
    134     char actualHash[33];
    135     computeMD5HashStringForContext(context, actualHash);
    136     fprintf(stdout, "\nActualHash: %s\n", actualHash);
    137 
    138     // Check the computed hash against the expected one and dump image on mismatch
    139     bool hashesMatch = false;
    140     if (m_expectedPixelHash.length() > 0) {
    141         ASSERT(m_expectedPixelHash.length() == 32);
    142 
    143         fprintf(stdout, "\nExpectedHash: %s\n", m_expectedPixelHash.c_str());
    144 
    145         // FIXME: Do case insensitive compare.
    146         if (m_expectedPixelHash == actualHash)
    147             hashesMatch = true;
    148     }
    149 
    150     if (!hashesMatch)
    151         dumpBitmap(context);
    152 }
    153 
    154 } // namespace WTR
    155