| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 /* |  | 
| 2  * Copyright (C) 2009 Zan Dobersek <zandobersek@gmail.com> |  | 
| 3  * Copyright (C) 2010 Igalia S.L. |  | 
| 4  * Copyright (C) 2011 ProFUSION Embedded Systems |  | 
| 5  * Copyright (C) 2011 Samsung Electronics |  | 
| 6  * |  | 
| 7  * Redistribution and use in source and binary forms, with or without |  | 
| 8  * modification, are permitted provided that the following conditions |  | 
| 9  * are met: |  | 
| 10  * |  | 
| 11  * 1.  Redistributions of source code must retain the above copyright |  | 
| 12  *     notice, this list of conditions and the following disclaimer. |  | 
| 13  * 2.  Redistributions in binary form must reproduce the above copyright |  | 
| 14  *     notice, this list of conditions and the following disclaimer in the |  | 
| 15  *     documentation and/or other materials provided with the distribution. |  | 
| 16  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of |  | 
| 17  *     its contributors may be used to endorse or promote products derived |  | 
| 18  *     from this software without specific prior written permission. |  | 
| 19  * |  | 
| 20  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |  | 
| 21  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |  | 
| 22  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |  | 
| 23  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |  | 
| 24  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |  | 
| 25  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |  | 
| 26  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |  | 
| 27  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |  | 
| 28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |  | 
| 29  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  | 
| 30  */ |  | 
| 31 |  | 
| 32 #include "config.h" |  | 
| 33 |  | 
| 34 #include <Ecore.h> |  | 
| 35 #include <Ecore_Evas.h> |  | 
| 36 #include <Evas.h> |  | 
| 37 #include <algorithm> |  | 
| 38 #include <cmath> |  | 
| 39 #include <cstdio> |  | 
| 40 #include <cstdlib> |  | 
| 41 #include <getopt.h> |  | 
| 42 #include <sys/stat.h> |  | 
| 43 #include <sys/types.h> |  | 
| 44 #include <unistd.h> |  | 
| 45 #include <wtf/OwnArrayPtr.h> |  | 
| 46 #include <wtf/OwnPtr.h> |  | 
| 47 #include <wtf/PassOwnPtr.h> |  | 
| 48 #include <wtf/PassRefPtr.h> |  | 
| 49 #include <wtf/efl/RefPtrEfl.h> |  | 
| 50 |  | 
| 51 enum PixelComponent { |  | 
| 52     Red, |  | 
| 53     Green, |  | 
| 54     Blue, |  | 
| 55     Alpha |  | 
| 56 }; |  | 
| 57 |  | 
| 58 static OwnPtr<Ecore_Evas> gEcoreEvas; |  | 
| 59 static double gTolerance = 0; |  | 
| 60 |  | 
| 61 static void abortWithErrorMessage(const char* errorMessage); |  | 
| 62 |  | 
| 63 static unsigned char* pixelFromImageData(unsigned char* imageData, int rowStride
     , int x, int y) |  | 
| 64 { |  | 
| 65     return imageData + (y * rowStride) + (x << 2); |  | 
| 66 } |  | 
| 67 |  | 
| 68 static Evas_Object* differenceImageFromDifferenceBuffer(Evas* evas, unsigned cha
     r* buffer, int width, int height) |  | 
| 69 { |  | 
| 70     Evas_Object* image = evas_object_image_filled_add(evas); |  | 
| 71     if (!image) |  | 
| 72         abortWithErrorMessage("could not create difference image"); |  | 
| 73 |  | 
| 74     evas_object_image_size_set(image, width, height); |  | 
| 75     evas_object_image_colorspace_set(image, EVAS_COLORSPACE_ARGB8888); |  | 
| 76 |  | 
| 77     unsigned char* diffPixels = static_cast<unsigned char*>(evas_object_image_da
     ta_get(image, EINA_TRUE)); |  | 
| 78     const int rowStride = evas_object_image_stride_get(image); |  | 
| 79     for (int x = 0; x < width; x++) { |  | 
| 80         for (int y = 0; y < height; y++) { |  | 
| 81             unsigned char* diffPixel = pixelFromImageData(diffPixels, rowStride,
      x, y); |  | 
| 82             diffPixel[Red] = diffPixel[Green] = diffPixel[Blue] = *buffer++; |  | 
| 83             diffPixel[Alpha] = 0xff; |  | 
| 84         } |  | 
| 85     } |  | 
| 86 |  | 
| 87     evas_object_image_data_set(image, diffPixels); |  | 
| 88 |  | 
| 89     return image; |  | 
| 90 } |  | 
| 91 |  | 
| 92 static float computeDistanceBetweenPixelComponents(unsigned char actualComponent
     , unsigned char baseComponent) |  | 
| 93 { |  | 
| 94     return (actualComponent - baseComponent) / std::max<float>(255 - baseCompone
     nt, baseComponent); |  | 
| 95 } |  | 
| 96 |  | 
| 97 static float computeDistanceBetweenPixelComponents(unsigned char* actualPixel, u
     nsigned char* basePixel, PixelComponent component) |  | 
| 98 { |  | 
| 99     return computeDistanceBetweenPixelComponents(actualPixel[component], basePix
     el[component]); |  | 
| 100 } |  | 
| 101 |  | 
| 102 static float calculatePixelDifference(unsigned char* basePixel, unsigned char* a
     ctualPixel) |  | 
| 103 { |  | 
| 104     const float red = computeDistanceBetweenPixelComponents(actualPixel, basePix
     el, Red); |  | 
| 105     const float green = computeDistanceBetweenPixelComponents(actualPixel, baseP
     ixel, Green); |  | 
| 106     const float blue = computeDistanceBetweenPixelComponents(actualPixel, basePi
     xel, Blue); |  | 
| 107     const float alpha = computeDistanceBetweenPixelComponents(actualPixel, baseP
     ixel, Alpha); |  | 
| 108     return sqrtf(red * red + green * green + blue * blue + alpha * alpha) / 2.0f
     ; |  | 
| 109 } |  | 
| 110 |  | 
| 111 static float calculateDifference(Evas_Object* baselineImage, Evas_Object* actual
     Image, RefPtr<Evas_Object>& differenceImage) |  | 
| 112 { |  | 
| 113     int width, height, baselineWidth, baselineHeight; |  | 
| 114     evas_object_image_size_get(actualImage, &width, &height); |  | 
| 115     evas_object_image_size_get(baselineImage, &baselineWidth, &baselineHeight); |  | 
| 116 |  | 
| 117     if (width != baselineWidth || height != baselineHeight) { |  | 
| 118         printf("Error, test and reference image have different sizes.\n"); |  | 
| 119         return 100; // Completely different. |  | 
| 120     } |  | 
| 121 |  | 
| 122     OwnArrayPtr<unsigned char> diffBuffer = adoptArrayPtr(new unsigned char[widt
     h * height]); |  | 
| 123     if (!diffBuffer) |  | 
| 124         abortWithErrorMessage("could not create difference buffer"); |  | 
| 125 |  | 
| 126     const int actualRowStride = evas_object_image_stride_get(actualImage); |  | 
| 127     const int baseRowStride = evas_object_image_stride_get(baselineImage); |  | 
| 128     unsigned numberOfDifferentPixels = 0; |  | 
| 129     float totalDistance = 0; |  | 
| 130     float maxDistance = 0; |  | 
| 131     unsigned char* actualPixels = static_cast<unsigned char*>(evas_object_image_
     data_get(actualImage, EINA_FALSE)); |  | 
| 132     unsigned char* basePixels = static_cast<unsigned char*>(evas_object_image_da
     ta_get(baselineImage, EINA_FALSE)); |  | 
| 133     unsigned char* currentDiffPixel = diffBuffer.get(); |  | 
| 134 |  | 
| 135     for (int x = 0; x < width; x++) { |  | 
| 136         for (int y = 0; y < height; y++) { |  | 
| 137             unsigned char* actualPixel = pixelFromImageData(actualPixels, actual
     RowStride, x, y); |  | 
| 138             unsigned char* basePixel = pixelFromImageData(basePixels, baseRowStr
     ide, x, y); |  | 
| 139 |  | 
| 140             const float distance = calculatePixelDifference(basePixel, actualPix
     el); |  | 
| 141             *currentDiffPixel++ = static_cast<unsigned char>(distance * 255.0f); |  | 
| 142 |  | 
| 143             if (distance >= 1.0f / 255.0f) { |  | 
| 144                 ++numberOfDifferentPixels; |  | 
| 145                 totalDistance += distance; |  | 
| 146                 maxDistance = std::max<float>(maxDistance, distance); |  | 
| 147             } |  | 
| 148         } |  | 
| 149     } |  | 
| 150 |  | 
| 151     // When using evas_object_image_data_get(), a complementary evas_object_data
     _set() must be |  | 
| 152     // issued to balance the reference count, even if the image hasn't been chan
     ged. |  | 
| 153     evas_object_image_data_set(baselineImage, basePixels); |  | 
| 154     evas_object_image_data_set(actualImage, actualPixels); |  | 
| 155 |  | 
| 156     // Compute the difference as a percentage combining both the number of |  | 
| 157     // different pixels and their difference amount i.e. the average distance |  | 
| 158     // over the entire image |  | 
| 159     float difference = 0; |  | 
| 160     if (numberOfDifferentPixels) |  | 
| 161         difference = 100.0f * totalDistance / (height * width); |  | 
| 162     if (difference <= gTolerance) |  | 
| 163         difference = 0; |  | 
| 164     else { |  | 
| 165         difference = roundf(difference * 100.0f) / 100.0f; |  | 
| 166         difference = std::max(difference, 0.01f); // round to 2 decimal places |  | 
| 167 |  | 
| 168         differenceImage = adoptRef(differenceImageFromDifferenceBuffer(evas_obje
     ct_evas_get(baselineImage), diffBuffer.get(), width, height)); |  | 
| 169     } |  | 
| 170 |  | 
| 171     return difference; |  | 
| 172 } |  | 
| 173 |  | 
| 174 static int getTemporaryFile(char *fileName, size_t fileNameLength) |  | 
| 175 { |  | 
| 176     char* tempDirectory = getenv("TMPDIR"); |  | 
| 177     if (!tempDirectory) |  | 
| 178         tempDirectory = getenv("TEMP"); |  | 
| 179 |  | 
| 180     if (tempDirectory) |  | 
| 181         snprintf(fileName, fileNameLength, "%s/ImageDiffXXXXXX.png", tempDirecto
     ry); |  | 
| 182     else { |  | 
| 183 #if __linux__ |  | 
| 184         strcpy(fileName, "/dev/shm/ImageDiffXXXXXX.png"); |  | 
| 185         const int fileDescriptor = mkstemps(fileName, sizeof(".png") - 1); |  | 
| 186         if (fileDescriptor >= 0) |  | 
| 187             return fileDescriptor; |  | 
| 188 #endif // __linux__ |  | 
| 189 |  | 
| 190         strcpy(fileName, "ImageDiffXXXXXX.png"); |  | 
| 191     } |  | 
| 192 |  | 
| 193     return mkstemps(fileName, sizeof(".png") - 1); |  | 
| 194 } |  | 
| 195 |  | 
| 196 static void printImage(Evas_Object* image) |  | 
| 197 { |  | 
| 198     char fileName[PATH_MAX]; |  | 
| 199 |  | 
| 200     const int tempImageFd = getTemporaryFile(fileName, PATH_MAX); |  | 
| 201     if (tempImageFd == -1) |  | 
| 202         abortWithErrorMessage("could not create temporary file"); |  | 
| 203 |  | 
| 204     evas_render(evas_object_evas_get(image)); |  | 
| 205 |  | 
| 206     if (evas_object_image_save(image, fileName, 0, 0)) { |  | 
| 207         struct stat fileInfo; |  | 
| 208         if (!stat(fileName, &fileInfo)) { |  | 
| 209             printf("Content-Length: %ld\n", fileInfo.st_size); |  | 
| 210             fflush(stdout); |  | 
| 211 |  | 
| 212             unsigned char buffer[2048]; |  | 
| 213             ssize_t bytesRead; |  | 
| 214             while ((bytesRead = read(tempImageFd, buffer, sizeof(buffer))) > 0) 
     { |  | 
| 215                 ssize_t bytesWritten = 0; |  | 
| 216                 ssize_t count; |  | 
| 217                 do { |  | 
| 218                     if ((count = write(1, buffer + bytesWritten, bytesRead - byt
     esWritten)) <= 0) |  | 
| 219                         break; |  | 
| 220                     bytesWritten += count; |  | 
| 221                 } while (bytesWritten < bytesRead); |  | 
| 222             } |  | 
| 223         } |  | 
| 224     } |  | 
| 225     close(tempImageFd); |  | 
| 226     unlink(fileName); |  | 
| 227 } |  | 
| 228 |  | 
| 229 static void printImageDifferences(Evas_Object* baselineImage, Evas_Object* actua
     lImage) |  | 
| 230 { |  | 
| 231     RefPtr<Evas_Object> differenceImage; |  | 
| 232     const float difference = calculateDifference(baselineImage, actualImage, dif
     ferenceImage); |  | 
| 233 |  | 
| 234     if (difference > 0.0f) { |  | 
| 235         if (differenceImage) |  | 
| 236             printImage(differenceImage.get()); |  | 
| 237 |  | 
| 238         printf("diff: %01.2f%% failed\n", difference); |  | 
| 239     } else |  | 
| 240         printf("diff: %01.2f%% passed\n", difference); |  | 
| 241 } |  | 
| 242 |  | 
| 243 static void resizeEcoreEvasIfNeeded(Evas_Object* image) |  | 
| 244 { |  | 
| 245     int newWidth, newHeight; |  | 
| 246     evas_object_image_size_get(image, &newWidth, &newHeight); |  | 
| 247 |  | 
| 248     int currentWidth, currentHeight; |  | 
| 249     ecore_evas_screen_geometry_get(gEcoreEvas.get(), 0, 0, ¤tWidth, &curre
     ntHeight); |  | 
| 250 |  | 
| 251     if (newWidth > currentWidth) |  | 
| 252         currentWidth = newWidth; |  | 
| 253     if (newHeight > currentHeight) |  | 
| 254         currentHeight = newHeight; |  | 
| 255 |  | 
| 256     ecore_evas_resize(gEcoreEvas.get(), currentWidth, currentHeight); |  | 
| 257 } |  | 
| 258 |  | 
| 259 static PassRefPtr<Evas_Object> readImageFromStdin(Evas* evas, long imageSize) |  | 
| 260 { |  | 
| 261     OwnArrayPtr<unsigned char> imageBuffer = adoptArrayPtr(new unsigned char[ima
     geSize]); |  | 
| 262     if (!imageBuffer) |  | 
| 263         abortWithErrorMessage("cannot allocate image"); |  | 
| 264 |  | 
| 265     const size_t bytesRead = fread(imageBuffer.get(), 1, imageSize, stdin); |  | 
| 266     if (!bytesRead) |  | 
| 267         return PassRefPtr<Evas_Object>(); |  | 
| 268 |  | 
| 269     Evas_Object* image = evas_object_image_filled_add(evas); |  | 
| 270     evas_object_image_colorspace_set(image, EVAS_COLORSPACE_ARGB8888); |  | 
| 271     evas_object_image_memfile_set(image, imageBuffer.get(), bytesRead, 0, 0); |  | 
| 272 |  | 
| 273     resizeEcoreEvasIfNeeded(image); |  | 
| 274 |  | 
| 275     return adoptRef(image); |  | 
| 276 } |  | 
| 277 |  | 
| 278 static bool parseCommandLineOptions(int argc, char** argv) |  | 
| 279 { |  | 
| 280     static const option options[] = { |  | 
| 281         { "tolerance", required_argument, 0, 't' }, |  | 
| 282         { 0, 0, 0, 0 } |  | 
| 283     }; |  | 
| 284     int option; |  | 
| 285 |  | 
| 286     while ((option = getopt_long(argc, (char* const*)argv, "t:", options, 0)) !=
      -1) { |  | 
| 287         switch (option) { |  | 
| 288         case 't': |  | 
| 289             gTolerance = atof(optarg); |  | 
| 290             break; |  | 
| 291         case '?': |  | 
| 292         case ':': |  | 
| 293             return false; |  | 
| 294         } |  | 
| 295     } |  | 
| 296 |  | 
| 297     return true; |  | 
| 298 } |  | 
| 299 |  | 
| 300 static void shutdownEfl() |  | 
| 301 { |  | 
| 302     ecore_evas_shutdown(); |  | 
| 303     ecore_shutdown(); |  | 
| 304     evas_shutdown(); |  | 
| 305 } |  | 
| 306 |  | 
| 307 static void abortWithErrorMessage(const char* errorMessage) |  | 
| 308 { |  | 
| 309     shutdownEfl(); |  | 
| 310 |  | 
| 311     printf("Error, %s.\n", errorMessage); |  | 
| 312     exit(EXIT_FAILURE); |  | 
| 313 } |  | 
| 314 |  | 
| 315 static Evas* initEfl() |  | 
| 316 { |  | 
| 317     evas_init(); |  | 
| 318     ecore_init(); |  | 
| 319     ecore_evas_init(); |  | 
| 320 |  | 
| 321     gEcoreEvas = adoptPtr(ecore_evas_buffer_new(1, 1)); |  | 
| 322     Evas* evas = ecore_evas_get(gEcoreEvas.get()); |  | 
| 323     if (!evas) |  | 
| 324         abortWithErrorMessage("could not create Ecore_Evas buffer"); |  | 
| 325 |  | 
| 326     return evas; |  | 
| 327 } |  | 
| 328 |  | 
| 329 int main(int argc, char* argv[]) |  | 
| 330 { |  | 
| 331     if (!parseCommandLineOptions(argc, argv)) |  | 
| 332         return EXIT_FAILURE; |  | 
| 333 |  | 
| 334     Evas* evas = initEfl(); |  | 
| 335 |  | 
| 336     RefPtr<Evas_Object> actualImage; |  | 
| 337     RefPtr<Evas_Object> baselineImage; |  | 
| 338 |  | 
| 339     char buffer[2048]; |  | 
| 340     while (fgets(buffer, sizeof(buffer), stdin)) { |  | 
| 341         char* contentLengthStart = strstr(buffer, "Content-Length: "); |  | 
| 342         if (!contentLengthStart) |  | 
| 343             continue; |  | 
| 344         long imageSize; |  | 
| 345         if (sscanf(contentLengthStart, "Content-Length: %ld", &imageSize) == 1) 
     { |  | 
| 346             if (imageSize <= 0) |  | 
| 347                 abortWithErrorMessage("image size must be specified"); |  | 
| 348 |  | 
| 349             if (!actualImage) |  | 
| 350                 actualImage = readImageFromStdin(evas, imageSize); |  | 
| 351             else if (!baselineImage) { |  | 
| 352                 baselineImage = readImageFromStdin(evas, imageSize); |  | 
| 353 |  | 
| 354                 printImageDifferences(baselineImage.get(), actualImage.get()); |  | 
| 355 |  | 
| 356                 actualImage.clear(); |  | 
| 357                 baselineImage.clear(); |  | 
| 358             } |  | 
| 359         } |  | 
| 360 |  | 
| 361         fflush(stdout); |  | 
| 362     } |  | 
| 363 |  | 
| 364     gEcoreEvas.clear(); // Make sure ecore_evas_free is called before the EFL ar
     e shut down |  | 
| 365 |  | 
| 366     shutdownEfl(); |  | 
| 367     return EXIT_SUCCESS; |  | 
| 368 } |  | 
| OLD | NEW | 
|---|