| Differences between
and this patch
- Tools/ChangeLog +26 lines
Lines 1-3 Tools/ChangeLog_sec1
1
2011-06-16  Leandro Pereira  <leandro@profusion.mobi>
2
3
        Reviewed by NOBODY (OOPS!).
4
5
        [EFL] Add ImageDiff implementation.
6
        Build system changes will be made as soon as the complete
7
        DumpRenderTree implementation is upstreamed.
8
        https://bugs.webkit.org/show_bug.cgi?id=61942
9
10
        * DumpRenderTree/efl: Added.
11
        * DumpRenderTree/efl/ImageDiff.cpp: Added.
12
        (differenceImageFromDifferenceBuffer):
13
        (calculateDifference):
14
        (getTemporaryFile):
15
        (printImage):
16
        (printImageDifferences):
17
        (readImageFromStdin):
18
        (parseCommandLineOptions):
19
        (resizeEcoreEvasIfNeeded):
20
        (shutdownEfl):
21
        (abortWithErrorMessage):
22
        (initEfl):
23
        (readImageFromStdinIfEmpty):
24
        (printDifferencesAndCleanup):
25
        (main):
26
1
2011-06-16  Tommy Widenflycht  <tommyw@google.com>
27
2011-06-16  Tommy Widenflycht  <tommyw@google.com>
2
28
3
        Reviewed by Tony Gentilcore.
29
        Reviewed by Tony Gentilcore.
- Tools/DumpRenderTree/efl/ImageDiff.cpp +341 lines
Line 0 Tools/DumpRenderTree/efl/ImageDiff.cpp_sec1
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
47
static Ecore_Evas* ecoreEvas = 0;
48
static double tolerance = 0;
49
50
static void abortWithErrorMessage(const char* errorMessage);
51
52
static Evas_Object* differenceImageFromDifferenceBuffer(Evas* evas, unsigned char* buffer, int width, int height)
53
{
54
    Evas_Object* image = evas_object_image_filled_add(evas);
55
    if (!image)
56
        abortWithErrorMessage("could not create difference image");
57
58
    evas_object_image_size_set(image, width, height);
59
    evas_object_image_colorspace_set(image, EVAS_COLORSPACE_ARGB8888);
60
61
    unsigned char* diffPixels = static_cast<unsigned char*>(evas_object_image_data_get(image, EINA_TRUE));
62
    const int rowStride = evas_object_image_stride_get(image);
63
    for (int x = 0; x < width; x++) {
64
        for (int y = 0; y < height; y++) {
65
            unsigned char* diffPixel = diffPixels + (y * rowStride) + (x * 4);
66
            diffPixel[0] = diffPixel[1] = diffPixel[2] = *buffer++;
67
            diffPixel[3] = 0xff; // Alpha channel
68
        }
69
    }
70
71
    return image;
72
}
73
74
static float calculateDifference(Evas_Object* baselineImage, Evas_Object* actualImage, Evas_Object** differenceImage)
75
{
76
    int width, height, baselineWidth, baselineHeight;
77
    evas_object_image_size_get(actualImage, &width, &height);
78
    evas_object_image_size_get(baselineImage, &baselineWidth, &baselineHeight);
79
80
    if (width != baselineWidth
81
        || height != baselineHeight
82
        || evas_object_image_alpha_get(actualImage) != evas_object_image_alpha_get(baselineImage)) {
83
        printf("Error, test and reference image have different properties.\n");
84
        return 100; // Completely different.
85
    }
86
87
    evas_object_image_colorspace_set(actualImage, EVAS_COLORSPACE_ARGB8888);
88
    evas_object_image_colorspace_set(baselineImage, EVAS_COLORSPACE_ARGB8888);
89
90
    OwnArrayPtr<unsigned char> diffBuffer = adoptArrayPtr(new unsigned char[width * height]);
91
    if (!diffBuffer)
92
        abortWithErrorMessage("could not create difference buffer");
93
94
    const int numberOfChannels = 4; // Alpha + R + G + B
95
    const int actualRowStride = evas_object_image_stride_get(actualImage);
96
    const int baseRowStride = evas_object_image_stride_get(baselineImage);
97
    float count = 0;
98
    float sum = 0;
99
    float maxDistance = 0;
100
    unsigned char* actualPixels = static_cast<unsigned char*>(evas_object_image_data_get(actualImage, EINA_FALSE));
101
    unsigned char* basePixels = static_cast<unsigned char*>(evas_object_image_data_get(baselineImage, EINA_FALSE));
102
    unsigned char* currentDiffPixel = diffBuffer.get();
103
104
    for (int x = 0; x < width; x++) {
105
        for (int y = 0; y < height; y++) {
106
            unsigned char* actualPixel = actualPixels + (y * actualRowStride) + (x * numberOfChannels);
107
            unsigned char* basePixel = basePixels + (y * baseRowStride) + (x * numberOfChannels);
108
109
            float red = (actualPixel[0] - basePixel[0]) / std::max<float>(255 - basePixel[0], basePixel[0]);
110
            float green = (actualPixel[1] - basePixel[1]) / std::max<float>(255 - basePixel[1], basePixel[1]);
111
            float blue = (actualPixel[2] - basePixel[2]) / std::max<float>(255 - basePixel[2], basePixel[2]);
112
            float alpha = (actualPixel[3] - basePixel[3]) / std::max<float>(255 - basePixel[3], basePixel[3]);
113
            float distance = sqrtf(red * red + green * green + blue * blue + alpha * alpha) / 2.0f;
114
115
            *currentDiffPixel++ = static_cast<unsigned char>(distance * 255.0f);
116
117
            if (distance >= 1.0f / 255.0f) {
118
                count += 1.0f;
119
                sum += distance;
120
                maxDistance = std::max<float>(maxDistance, distance);
121
            }
122
        }
123
    }
124
125
    // Compute the difference as a percentage combining both the number of
126
    // different pixels and their difference amount i.e. the average distance
127
    // over the entire image
128
    float difference = 0;
129
    if (count > 0.0f)
130
        difference = 100.0f * sum / (height * width);
131
    if (difference <= tolerance)
132
        difference = 0;
133
    else {
134
        difference = roundf(difference * 100.0f) / 100.0f;
135
        difference = std::max(difference, 0.01f); // round to 2 decimal places
136
        *differenceImage = differenceImageFromDifferenceBuffer(evas_object_evas_get(baselineImage), diffBuffer.get(), width, height);
137
    }
138
139
    return difference;
140
}
141
142
static int getTemporaryFile(char *fileName, size_t fileNameLength)
143
{
144
    if (getenv("TMPDIR"))
145
        snprintf(fileName, fileNameLength, "%s/ImageDiffXXXXXX.png", getenv("TMPDIR"));
146
    else if (getenv("TEMP"))
147
        snprintf(fileName, fileNameLength, "%s/ImageDiffXXXXXX.png", getenv("TEMP"));
148
    else {
149
#if __linux__
150
        strcpy(fileName, "/dev/shm/ImageDiffXXXXXX.png");
151
        int fileDescriptor = mkstemps(fileName, sizeof(".png") - 1);
152
        if (fileDescriptor >= 0)
153
            return fileDescriptor;
154
#endif // __linux__
155
156
        strcpy(fileName, "ImageDiffXXXXXX.png");
157
    }
158
159
    return mkstemps(fileName, sizeof(".png") - 1);
160
}
161
162
static void printImage(Evas_Object* image)
163
{
164
    char fileName[PATH_MAX];
165
166
    const int tempImageFd = getTemporaryFile(fileName, PATH_MAX);
167
    if (tempImageFd == -1)
168
        abortWithErrorMessage("could not create temporary file");
169
170
    if (evas_object_image_save(image, fileName, 0, 0)) {
171
        struct stat fileInfo;
172
        if (!stat(fileName, &fileInfo)) {
173
            printf("Content-Length: %ld\n", fileInfo.st_size);
174
            fflush(stdout);
175
176
            unsigned char buffer[2048];
177
            ssize_t bytesRead;
178
            while ((bytesRead = read(tempImageFd, buffer, sizeof(buffer))) > 0)
179
                write(1, buffer, bytesRead);
180
        }
181
    }
182
    close(tempImageFd);
183
    unlink(fileName);
184
}
185
186
static void printImageDifferences(Evas_Object* baselineImage, Evas_Object* actualImage)
187
{
188
    Evas_Object* differenceImage = 0;
189
    float difference = calculateDifference(baselineImage, actualImage, &differenceImage);
190
    if (difference > 0.0f) {
191
        if (differenceImage) {
192
            printImage(differenceImage);
193
            evas_object_del(differenceImage);
194
        }
195
        printf("diff: %01.2f%% failed\n", difference);
196
    } else
197
        printf("diff: %01.2f%% passed\n", difference);
198
}
199
200
static Evas_Object* readImageFromStdin(Evas* evas, long imageSize, int *width, int *height)
201
{
202
    OwnArrayPtr<unsigned char> imageBuffer = adoptArrayPtr(new unsigned char[imageSize]);
203
    if (!imageBuffer)
204
        abortWithErrorMessage("cannot allocate image");
205
206
    Evas_Object* image = evas_object_image_filled_add(evas);
207
    const size_t bytesRead = fread(imageBuffer.get(), 1, imageSize, stdin);
208
    if (bytesRead > 0)
209
        evas_object_image_memfile_set(image, imageBuffer.get(), bytesRead, 0, 0);
210
    else {
211
        evas_object_del(image);
212
        return 0;
213
    }
214
215
    evas_object_image_size_get(image, width, height);
216
    return image;
217
}
218
219
static bool parseCommandLineOptions(int argc, char** argv)
220
{
221
    static const option options[] = {
222
        { "tolerance", required_argument, 0, 't' },
223
        { 0, 0, 0, 0 }
224
    };
225
    int option;
226
227
    while ((option = getopt_long(argc, (char* const*)argv, "t:", options, 0)) != -1) {
228
        switch (option) {
229
        case 't':
230
            tolerance = atof(optarg);
231
            break;
232
        case '?':
233
        case ':':
234
            return false;
235
        }
236
    }
237
238
    return true;
239
}
240
241
static void resizeEcoreEvasIfNeeded(int newWidth, int newHeight)
242
{
243
    int currentWidth, currentHeight;
244
    ecore_evas_screen_geometry_get(ecoreEvas, 0, 0, &currentWidth, &currentHeight);
245
    if (newWidth > currentWidth)
246
        currentWidth = newWidth;
247
    if (newHeight > currentHeight)
248
        currentHeight = newHeight;
249
    ecore_evas_resize(ecoreEvas, currentWidth, currentHeight);
250
}
251
252
static void shutdownEfl()
253
{
254
    if (ecoreEvas)
255
        ecore_evas_free(ecoreEvas);
256
    ecore_evas_shutdown();
257
    ecore_shutdown();
258
    evas_shutdown();
259
}
260
261
static void abortWithErrorMessage(const char* errorMessage)
262
{
263
    shutdownEfl();
264
265
    printf("Error, %s\n", errorMessage);
266
    exit(EXIT_FAILURE);
267
}
268
269
static Evas* initEfl()
270
{
271
    evas_init();
272
    ecore_init();
273
    ecore_evas_init();
274
275
    ecoreEvas = ecore_evas_buffer_new(1, 1);
276
    Evas* evas = ecore_evas_get(ecoreEvas);
277
    if (!evas)
278
        abortWithErrorMessage("could not create Ecore_Evas buffer");
279
280
    return evas;
281
}
282
283
static bool readImageFromStdinIfEmpty(Evas* evas, Evas_Object** image, long imageSize)
284
{
285
    Evas_Object* newImage = *image;
286
    if (!newImage) {
287
        int width, height;
288
        newImage = readImageFromStdin(evas, imageSize, &width, &height);
289
        if (!newImage)
290
            abortWithErrorMessage("could not read image from standard input");
291
292
        resizeEcoreEvasIfNeeded(width, height);
293
294
        *image = newImage;
295
296
        return true;
297
    }
298
299
    return false;
300
}
301
302
static void printDifferencesAndCleanup(Evas_Object** actualImage, Evas_Object** baselineImage)
303
{
304
    printImageDifferences(*baselineImage, *actualImage);
305
    evas_object_del(*actualImage);
306
    evas_object_del(*baselineImage);
307
    *actualImage = 0;
308
    *baselineImage = 0;
309
}
310
311
int main(int argc, char* argv[])
312
{
313
    if (!parseCommandLineOptions(argc, argv))
314
        return 1;
315
316
    Evas* evas = initEfl();
317
    Evas_Object* actualImage = 0;
318
    Evas_Object* baselineImage = 0;
319
    char buffer[2048];
320
    while (fgets(buffer, sizeof(buffer), stdin)) {
321
        char* contentLengthStart = strstr(buffer, "Content-Length: ");
322
        if (!contentLengthStart)
323
            continue;
324
        long imageSize;
325
        if (sscanf(contentLengthStart, "Content-Length: %ld", &imageSize) == 1) {
326
            if (imageSize <= 0)
327
                abortWithErrorMessage("image size must be specified");
328
329
            if (readImageFromStdinIfEmpty(evas, &actualImage, imageSize))
330
                continue;
331
332
            if (readImageFromStdinIfEmpty(evas, &baselineImage, imageSize))
333
                printDifferencesAndCleanup(&actualImage, &baselineImage);
334
        }
335
336
        fflush(stdout);
337
    }
338
339
    shutdownEfl();
340
    return EXIT_SUCCESS;
341
}

Return to Bug 61942