aboutsummaryrefslogtreecommitdiffhomepage
path: root/experimental/skpdiff/main.cpp
blob: f546008beaf19c7cff46e5941cf033d1dd8e201c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
/*
 * Copyright 2013 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#if SK_SUPPORT_OPENCL
#define __NO_STD_VECTOR // Uses cl::vectpr instead of std::vectpr
#define __NO_STD_STRING // Uses cl::STRING_CLASS instead of std::string
#include <CL/cl.hpp>
#endif

#include "SkCommandLineFlags.h"
#include "SkGraphics.h"
#include "SkStream.h"
#include "SkTDArray.h"

#include "SkDifferentPixelsMetric.h"
#include "SkDiffContext.h"
#include "SkImageDiffer.h"
#include "SkPMetric.h"
#include "skpdiff_util.h"

#include "SkForceLinking.h"
__SK_FORCE_IMAGE_DECODER_LINKING;

// Command line argument definitions go here
DEFINE_bool2(list, l, false, "List out available differs");
DEFINE_string2(differs, d, "", "The names of the differs to use or all of them by default");
DEFINE_string2(folders, f, "", "Compare two folders with identical subfile names: <baseline folder> <test folder>");
DEFINE_string2(patterns, p, "", "Use two patterns to compare images: <baseline> <test>");
DEFINE_string2(output, o, "skpdiff_output.json", "Writes the output of these diffs to output: <output>");
DEFINE_bool(jsonp, true, "Output JSON with padding");
DEFINE_string(csv, "", "Writes the output of these diffs to a csv file");

#if SK_SUPPORT_OPENCL
/// A callback for any OpenCL errors
CL_CALLBACK void error_notify(const char* errorInfo, const void* privateInfoSize, ::size_t cb, void* userData) {
    SkDebugf("OpenCL error notify: %s\n", errorInfo);
    exit(1);
}

/// Creates a device and context with OpenCL
static bool init_device_and_context(cl::Device* device, cl::Context* context) {
    // Query for a platform
    cl::vector<cl::Platform> platformList;
    cl::Platform::get(&platformList);
    SkDebugf("The number of platforms is %u\n", platformList.size());

    // Print some information about the platform for debugging
    cl::Platform& platform = platformList[0];
    cl::STRING_CLASS platformName;
    platform.getInfo(CL_PLATFORM_NAME, &platformName);
    SkDebugf("Platform index 0 is named %s\n", platformName.c_str());

    // Query for a device
    cl::vector<cl::Device> deviceList;
    platform.getDevices(CL_DEVICE_TYPE_GPU, &deviceList);
    SkDebugf("The number of GPU devices is %u\n", deviceList.size());

    // Print some information about the device for debugging
    *device = deviceList[0];
    cl::STRING_CLASS deviceName;
    device->getInfo(CL_DEVICE_NAME, &deviceName);
    SkDebugf("Device index 0 is named %s\n", deviceName.c_str());

    // Create a CL context and check for all errors
    cl_int contextErr = CL_SUCCESS;
    *context = cl::Context(deviceList, NULL, error_notify, NULL, &contextErr);
    if (contextErr != CL_SUCCESS) {
        SkDebugf("Context creation failed: %s\n", cl_error_to_string(contextErr));
        return false;
    }

    return true;
}

static bool init_cl_diff(SkImageDiffer* differ) {
    // Setup OpenCL
    cl::Device device;
    cl::Context context;
    if (!init_device_and_context(&device, &context)) {
        return false;
    }

    // Setup our differ of choice
    SkCLImageDiffer* clDiffer = (SkCLImageDiffer*)differ;
    return clDiffer->init(device(), context());
}
#endif

// TODO Find a better home for the diff registry. One possibility is to have the differs self
// register.

// List here every differ
SkDifferentPixelsMetric gDiffPixel;
SkPMetric gPDiff;

// A null terminated array of pointer to every differ declared above
SkImageDiffer* gDiffers[] = { &gDiffPixel, &gPDiff, NULL };

int main(int argc, char** argv) {
    // Setup command line parsing
    SkCommandLineFlags::SetUsage("Compare images using various metrics.");
    SkCommandLineFlags::Parse(argc, argv);

    // Needed by various Skia components
    SkAutoGraphics ag;

    if (FLAGS_list) {
        SkDebugf("Available Metrics:\n");
    }

    // Figure which differs the user chose, and optionally print them if the user requests it
    SkTDArray<SkImageDiffer*> chosenDiffers;
    for (int differIndex = 0; NULL != gDiffers[differIndex]; differIndex++) {
        SkImageDiffer* differ = gDiffers[differIndex];
        if (FLAGS_list) {
            SkDebugf("    %s", differ->getName());
            SkDebugf("\n");
        }

        // Check if this differ was chosen by any of the flags. Initialize them if they were chosen.
        if (FLAGS_differs.isEmpty()) {
            // If no differs were chosen, they all get added
            if (differ->requiresOpenCL()) {
#if SK_SUPPORT_OPENCL
                init_cl_diff(differ);
                chosenDiffers.push(differ);
#endif
            } else {
                chosenDiffers.push(differ);
            }
        } else {
            for (int flagIndex = 0; flagIndex < FLAGS_differs.count(); flagIndex++) {
                if (SkString(FLAGS_differs[flagIndex]).equals(differ->getName())) {
                    // Initialize OpenCL for the differ if it needs it and support was compiled in.
                    if (differ->requiresOpenCL()) {
#if SK_SUPPORT_OPENCL
                        init_cl_diff(differ);
                        chosenDiffers.push(differ);
#endif
                    } else {
                        chosenDiffers.push(differ);
                    }
                    break;
                }
            }
        }
    }

    // Don't attempt to initialize the differ if we aren't going to use it
    if (FLAGS_folders.isEmpty() && FLAGS_patterns.isEmpty()) {
        return 0;
    }

    // Validate command line flags
    if (!FLAGS_folders.isEmpty()) {
        if (2 != FLAGS_folders.count()) {
            SkDebugf("Folders flag expects two arguments: <baseline folder> <test folder>\n");
            return 1;
        }
    }

    if (!FLAGS_patterns.isEmpty()) {
        if (2 != FLAGS_patterns.count()) {
            SkDebugf("Patterns flag expects two arguments: <baseline pattern> <test pattern>\n");
            return 1;
        }
    }

    if (!FLAGS_csv.isEmpty()) {
        if (1 != FLAGS_csv.count()) {
            SkDebugf("csv flag expects one argument: <csv file>\n");
            return 1;
        }
    }

    SkDiffContext ctx;
    ctx.setDiffers(chosenDiffers);

    // Perform a folder diff if one is requested
    if (!FLAGS_folders.isEmpty()) {
        ctx.diffDirectories(FLAGS_folders[0], FLAGS_folders[1]);
    }

    // Perform a pattern diff if one is requested
    if (!FLAGS_patterns.isEmpty()) {
        ctx.diffPatterns(FLAGS_patterns[0], FLAGS_patterns[1]);
    }

    // Output to the file specified
    if (!FLAGS_output.isEmpty()) {
        SkFILEWStream outputStream(FLAGS_output[0]);
        ctx.outputRecords(outputStream, FLAGS_jsonp);
    }

    return 0;
}