• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
createCGContextFromImage(WKImageRef wkImage)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 
computeMD5HashStringForContext(CGContextRef bitmapContext,char hashString[33])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 
dumpBitmap(CGContextRef bitmapContext)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 
dumpPixelsAndCompareWithExpected(WKImageRef image)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