1e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com/*
2e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com * Copyright 2012 Google Inc.
3e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com *
4e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com * Use of this source code is governed by a BSD-style license that can be
5e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com * found in the LICENSE file.
6e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com */
7e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
8e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#include "skdiff.h"
9e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#include "skdiff_html.h"
10e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#include "SkStream.h"
11e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#include "SkTime.h"
12e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
13e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com/// Make layout more consistent by scaling image to 240 height, 360 width,
14e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com/// or natural size, whichever is smallest.
15e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic int compute_image_height(int height, int width) {
16e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    int retval = 240;
17e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (height < retval) {
18e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        retval = height;
19e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
20e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    float scale = (float) retval / height;
21e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (width * scale > 360) {
22e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        scale = (float) 360 / width;
23e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        retval = static_cast<int>(height * scale);
24e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
25e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    return retval;
26e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
27e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
28e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void print_table_header(SkFILEWStream* stream,
29e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                               const int matchCount,
30e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                               const int colorThreshold,
31e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                               const RecordArray& differences,
32e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                               const SkString &baseDir,
33e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                               const SkString &comparisonDir,
34e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                               bool doOutputDate = false) {
35e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("<table>\n");
36e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("<tr><th>");
37e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("select image</th>\n<th>");
38e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (doOutputDate) {
39e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        SkTime::DateTime dt;
40e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        SkTime::GetDateTime(&dt);
41e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("SkDiff run at ");
42e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeDecAsText(dt.fHour);
43e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText(":");
44e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (dt.fMinute < 10) {
45e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            stream->writeText("0");
46e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
47e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeDecAsText(dt.fMinute);
48e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText(":");
49e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (dt.fSecond < 10) {
50e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            stream->writeText("0");
51e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
52e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeDecAsText(dt.fSecond);
53e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("<br>");
54e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
55e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeDecAsText(matchCount);
56e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(" of ");
57e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeDecAsText(differences.count());
58e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(" diffs matched ");
59e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (colorThreshold == 0) {
60e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("exactly");
61e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    } else {
62e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("within ");
63e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeDecAsText(colorThreshold);
64e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText(" color units per component");
65e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
66e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(".<br>");
67e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("</th>\n<th>");
68e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("every different pixel shown in white");
69e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("</th>\n<th>");
70e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("color difference at each pixel");
71e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("</th>\n<th>baseDir: ");
72e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(baseDir.c_str());
73e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("</th>\n<th>comparisonDir: ");
74e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(comparisonDir.c_str());
75e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("</th>\n");
76e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("</tr>\n");
77e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
78e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
79e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void print_pixel_count(SkFILEWStream* stream, const DiffRecord& diff) {
80e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("<br>(");
81e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeDecAsText(static_cast<int>(diff.fFractionDifference *
82e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                            diff.fBase.fBitmap.width() *
83e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                            diff.fBase.fBitmap.height()));
84e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(" pixels)");
85e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com/*
86e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeDecAsText(diff.fWeightedFraction *
87e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                           diff.fBaseWidth *
88e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                           diff.fBaseHeight);
89e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(" weighted pixels)");
90e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com*/
91e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
92e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
93e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void print_checkbox_cell(SkFILEWStream* stream, const DiffRecord& diff) {
94e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("<td><input type=\"checkbox\" name=\"");
95e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(diff.fBase.fFilename.c_str());
96e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("\" checked=\"yes\"></td>");
97e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
98e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
99e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void print_label_cell(SkFILEWStream* stream, const DiffRecord& diff) {
100e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    char metricBuf [20];
101e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
102e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("<td><b>");
103e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(diff.fBase.fFilename.c_str());
104e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("</b><br>");
105e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    switch (diff.fResult) {
106e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com      case DiffRecord::kEqualBits_Result:
107e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        SkDEBUGFAIL("should not encounter DiffRecord with kEqualBits here");
108e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        return;
109e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com      case DiffRecord::kEqualPixels_Result:
110e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        SkDEBUGFAIL("should not encounter DiffRecord with kEqualPixels here");
111e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        return;
112e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com      case DiffRecord::kDifferentSizes_Result:
113e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("Image sizes differ</td>");
114e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        return;
115e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com      case DiffRecord::kDifferentPixels_Result:
116e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        sprintf(metricBuf, "%.4f%%", 100 * diff.fFractionDifference);
117e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText(metricBuf);
118e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText(" of pixels differ");
119e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("\n  (");
120e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        sprintf(metricBuf, "%.4f%%", 100 * diff.fWeightedFraction);
121e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText(metricBuf);
122e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText(" weighted)");
123e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        // Write the actual number of pixels that differ if it's < 1%
124e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (diff.fFractionDifference < 0.01) {
125e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            print_pixel_count(stream, diff);
126e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
127ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com        stream->writeText("<br>");
12889d15a28b52faed37b1bda1fbcdd1afae4bae457reed@google.com        if (SkScalarRoundToInt(diff.fAverageMismatchA) > 0) {
129ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com          stream->writeText("<br>Average alpha channel mismatch ");
13089d15a28b52faed37b1bda1fbcdd1afae4bae457reed@google.com          stream->writeDecAsText(SkScalarRoundToInt(diff.fAverageMismatchA));
131ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com        }
132ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com
133ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com        stream->writeText("<br>Max alpha channel mismatch ");
13489d15a28b52faed37b1bda1fbcdd1afae4bae457reed@google.com        stream->writeDecAsText(SkScalarRoundToInt(diff.fMaxMismatchA));
135ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com
136ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com        stream->writeText("<br>Total alpha channel mismatch ");
137ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com        stream->writeDecAsText(static_cast<int>(diff.fTotalMismatchA));
138ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com
139ee5a5eee12b3befac33ed6c379f81bf749f57161rmistry@google.com        stream->writeText("<br>");
140e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("<br>Average color mismatch ");
14189d15a28b52faed37b1bda1fbcdd1afae4bae457reed@google.com        stream->writeDecAsText(SkScalarRoundToInt(MAX3(diff.fAverageMismatchR,
14289d15a28b52faed37b1bda1fbcdd1afae4bae457reed@google.com                                                       diff.fAverageMismatchG,
14389d15a28b52faed37b1bda1fbcdd1afae4bae457reed@google.com                                                       diff.fAverageMismatchB)));
144e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("<br>Max color mismatch ");
145e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeDecAsText(MAX3(diff.fMaxMismatchR,
146e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                    diff.fMaxMismatchG,
147e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                    diff.fMaxMismatchB));
148e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("</td>");
149e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        break;
150e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com      case DiffRecord::kCouldNotCompare_Result:
151e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("Could not compare.<br>base: ");
152e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText(DiffResource::getStatusDescription(diff.fBase.fStatus));
153e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("<br>comparison: ");
154e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText(DiffResource::getStatusDescription(diff.fComparison.fStatus));
155e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("</td>");
156e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        return;
157e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com      default:
158e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        SkDEBUGFAIL("encountered DiffRecord with unknown result type");
159e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        return;
160e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
161e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
162e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
163e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void print_image_cell(SkFILEWStream* stream, const SkString& path, int height) {
164e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("<td><a href=\"");
165e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(path.c_str());
166e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("\"><img src=\"");
167e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(path.c_str());
168e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("\" height=\"");
169e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeDecAsText(height);
170e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("px\"></a></td>");
171e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
172e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
173e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void print_link_cell(SkFILEWStream* stream, const SkString& path, const char* text) {
174e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("<td><a href=\"");
175e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(path.c_str());
176e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("\">");
177e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText(text);
178e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("</a></td>");
179e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
180e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
181e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void print_diff_resource_cell(SkFILEWStream* stream, DiffResource& resource,
182e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                                     const SkString& relativePath, bool local) {
183e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (resource.fBitmap.empty()) {
184e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        if (DiffResource::kCouldNotDecode_Status == resource.fStatus) {
185e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (local && !resource.fFilename.isEmpty()) {
186e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                print_link_cell(stream, resource.fFilename, "N/A");
187e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                return;
188e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
189e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (!resource.fFullPath.isEmpty()) {
190e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                if (!resource.fFullPath.startsWith(PATH_DIV_STR)) {
191e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                    resource.fFullPath.prepend(relativePath);
192e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                }
193e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                print_link_cell(stream, resource.fFullPath, "N/A");
194e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                return;
195e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
196e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
197e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        stream->writeText("<td>N/A</td>");
198e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        return;
199e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
200e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
201e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    int height = compute_image_height(resource.fBitmap.height(), resource.fBitmap.width());
202e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (local) {
203e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        print_image_cell(stream, resource.fFilename, height);
204e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        return;
205e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
206e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (!resource.fFullPath.startsWith(PATH_DIV_STR)) {
207e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        resource.fFullPath.prepend(relativePath);
208e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
209e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    print_image_cell(stream, resource.fFullPath, height);
210e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
211e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
212e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comstatic void print_diff_row(SkFILEWStream* stream, DiffRecord& diff, const SkString& relativePath) {
213e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("<tr>\n");
214e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    print_checkbox_cell(stream, diff);
215e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    print_label_cell(stream, diff);
216e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    print_diff_resource_cell(stream, diff.fWhite, relativePath, true);
217e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    print_diff_resource_cell(stream, diff.fDifference, relativePath, true);
218e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    print_diff_resource_cell(stream, diff.fBase, relativePath, false);
219e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    print_diff_resource_cell(stream, diff.fComparison, relativePath, false);
220e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->writeText("</tr>\n");
221e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    stream->flush();
222e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
223e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
224e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.comvoid print_diff_page(const int matchCount,
225e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                     const int colorThreshold,
226e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                     const RecordArray& differences,
227e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                     const SkString& baseDir,
228e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                     const SkString& comparisonDir,
229e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                     const SkString& outputDir) {
230e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
231e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    SkASSERT(!baseDir.isEmpty());
232e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    SkASSERT(!comparisonDir.isEmpty());
233e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    SkASSERT(!outputDir.isEmpty());
234e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
235e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    SkString outputPath(outputDir);
236e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    outputPath.append("index.html");
237e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    //SkFILEWStream outputStream ("index.html");
238e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    SkFILEWStream outputStream(outputPath.c_str());
239e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
240e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    // Need to convert paths from relative-to-cwd to relative-to-outputDir
241e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    // FIXME this doesn't work if there are '..' inside the outputDir
242e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
243e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    bool isPathAbsolute = false;
244e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    // On Windows or Linux, a path starting with PATH_DIV_CHAR is absolute.
245e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (outputDir.size() > 0 && PATH_DIV_CHAR == outputDir[0]) {
246e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        isPathAbsolute = true;
247e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
248e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#ifdef SK_BUILD_FOR_WIN32
249e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    // On Windows, absolute paths can also start with "x:", where x is any
250e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    // drive letter.
251e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (outputDir.size() > 1 && ':' == outputDir[1]) {
252e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        isPathAbsolute = true;
253e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
254e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com#endif
255e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
256e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    SkString relativePath;
257e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    if (!isPathAbsolute) {
258e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        unsigned int ui;
259e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        for (ui = 0; ui < outputDir.size(); ui++) {
260e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            if (outputDir[ui] == PATH_DIV_CHAR) {
261e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                relativePath.append(".." PATH_DIV_STR);
262e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            }
263e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
264e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
265e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
266e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    outputStream.writeText(
267e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "<html>\n<head>\n"
268e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "<script src=\"https://ajax.googleapis.com/ajax/"
269e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "libs/jquery/1.7.2/jquery.min.js\"></script>\n"
270e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "<script type=\"text/javascript\">\n"
271e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "function generateCheckedList() {\n"
272e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "var boxes = $(\":checkbox:checked\");\n"
273e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "var fileCmdLineString = '';\n"
274e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "var fileMultiLineString = '';\n"
275e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "for (var i = 0; i < boxes.length; i++) {\n"
276e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "fileMultiLineString += boxes[i].name + '<br>';\n"
277e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "fileCmdLineString += boxes[i].name + '&nbsp;';\n"
278e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "}\n"
279e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "$(\"#checkedList\").html(fileCmdLineString + "
280e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "'<br><br>' + fileMultiLineString);\n"
281e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "}\n"
282e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "</script>\n</head>\n<body>\n");
283e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    print_table_header(&outputStream, matchCount, colorThreshold, differences,
284e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com                       baseDir, comparisonDir);
285e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    int i;
286e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    for (i = 0; i < differences.count(); i++) {
287e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        DiffRecord* diff = differences[i];
288e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com
289e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        switch (diff->fResult) {
290e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          // Cases in which there is no diff to report.
291e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kEqualBits_Result:
292e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kEqualPixels_Result:
293e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            continue;
294e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          // Cases in which we want a detailed pixel diff.
295e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kDifferentPixels_Result:
296e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kDifferentSizes_Result:
297e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          case DiffRecord::kCouldNotCompare_Result:
298e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            print_diff_row(&outputStream, *diff, relativePath);
299e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            continue;
300e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com          default:
301e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            SkDEBUGFAIL("encountered DiffRecord with unknown result type");
302e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com            continue;
303e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        }
304e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    }
305e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    outputStream.writeText(
306e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "</table>\n"
307e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "<input type=\"button\" "
308e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "onclick=\"generateCheckedList()\" "
309e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "value=\"Create Rebaseline List\">\n"
310e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "<div id=\"checkedList\"></div>\n"
311e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com        "</body>\n</html>\n");
312e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com    outputStream.flush();
313e3c8ddfd03fdf587b4d8400718ae4bb6e9aa8b6dbungeman@google.com}
314