1363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/*
2363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * Copyright 2012 Google Inc.
3363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger *
4363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * Use of this source code is governed by a BSD-style license that can be
5363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger * found in the LICENSE file.
6363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger */
7363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
8363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff.h"
9363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff_html.h"
10363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkStream.h"
11363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkTime.h"
12363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
13363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/// Make layout more consistent by scaling image to 240 height, 360 width,
14363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/// or natural size, whichever is smallest.
15363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic int compute_image_height(int height, int width) {
16363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    int retval = 240;
17363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (height < retval) {
18363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        retval = height;
19363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
20363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    float scale = (float) retval / height;
21363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (width * scale > 360) {
22363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        scale = (float) 360 / width;
23363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        retval = static_cast<int>(height * scale);
24363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
25363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    return retval;
26363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
27363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
28363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void print_table_header(SkFILEWStream* stream,
29363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                               const int matchCount,
30363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                               const int colorThreshold,
31363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                               const RecordArray& differences,
32363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                               const SkString &baseDir,
33363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                               const SkString &comparisonDir,
34363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                               bool doOutputDate = false) {
35363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("<table>\n");
36363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("<tr><th>");
37363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("select image</th>\n<th>");
38363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (doOutputDate) {
39363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkTime::DateTime dt;
40363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkTime::GetDateTime(&dt);
41363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("SkDiff run at ");
42363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeDecAsText(dt.fHour);
43363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText(":");
44363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (dt.fMinute < 10) {
45363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            stream->writeText("0");
46363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
47363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeDecAsText(dt.fMinute);
48363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText(":");
49363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (dt.fSecond < 10) {
50363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            stream->writeText("0");
51363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
52363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeDecAsText(dt.fSecond);
53363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("<br>");
54363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
55363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeDecAsText(matchCount);
56363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(" of ");
57363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeDecAsText(differences.count());
58363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(" diffs matched ");
59363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (colorThreshold == 0) {
60363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("exactly");
61363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    } else {
62363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("within ");
63363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeDecAsText(colorThreshold);
64363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText(" color units per component");
65363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
66363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(".<br>");
67363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("</th>\n<th>");
68363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("every different pixel shown in white");
69363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("</th>\n<th>");
70363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("color difference at each pixel");
71363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("</th>\n<th>baseDir: ");
72363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(baseDir.c_str());
73363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("</th>\n<th>comparisonDir: ");
74363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(comparisonDir.c_str());
75363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("</th>\n");
76363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("</tr>\n");
77363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
78363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
79363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void print_pixel_count(SkFILEWStream* stream, const DiffRecord& diff) {
80363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("<br>(");
81363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeDecAsText(static_cast<int>(diff.fFractionDifference *
82363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                            diff.fBase.fBitmap.width() *
83363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                            diff.fBase.fBitmap.height()));
84363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(" pixels)");
85363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/*
86363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeDecAsText(diff.fWeightedFraction *
87363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                           diff.fBaseWidth *
88363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                           diff.fBaseHeight);
89363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(" weighted pixels)");
90363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger*/
91363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
92363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
93363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void print_checkbox_cell(SkFILEWStream* stream, const DiffRecord& diff) {
94363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("<td><input type=\"checkbox\" name=\"");
95363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(diff.fBase.fFilename.c_str());
96363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("\" checked=\"yes\"></td>");
97363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
98363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
99363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void print_label_cell(SkFILEWStream* stream, const DiffRecord& diff) {
100363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    char metricBuf [20];
101363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
102363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("<td><b>");
103363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(diff.fBase.fFilename.c_str());
104363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("</b><br>");
105363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    switch (diff.fResult) {
106363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger      case DiffRecord::kEqualBits_Result:
107363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkDEBUGFAIL("should not encounter DiffRecord with kEqualBits here");
108363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
109363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger      case DiffRecord::kEqualPixels_Result:
110363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkDEBUGFAIL("should not encounter DiffRecord with kEqualPixels here");
111363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
112363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger      case DiffRecord::kDifferentSizes_Result:
113363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("Image sizes differ</td>");
114363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
115363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger      case DiffRecord::kDifferentPixels_Result:
116363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        sprintf(metricBuf, "%.4f%%", 100 * diff.fFractionDifference);
117363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText(metricBuf);
118363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText(" of pixels differ");
119363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("\n  (");
120363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        sprintf(metricBuf, "%.4f%%", 100 * diff.fWeightedFraction);
121363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText(metricBuf);
122363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText(" weighted)");
123363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        // Write the actual number of pixels that differ if it's < 1%
124363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (diff.fFractionDifference < 0.01) {
125363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            print_pixel_count(stream, diff);
126363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
127d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger        stream->writeText("<br>");
128d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger        if (SkScalarRoundToInt(diff.fAverageMismatchA) > 0) {
129d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger          stream->writeText("<br>Average alpha channel mismatch ");
130d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger          stream->writeDecAsText(SkScalarRoundToInt(diff.fAverageMismatchA));
131d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger        }
132d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger
133d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger        stream->writeText("<br>Max alpha channel mismatch ");
134d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger        stream->writeDecAsText(SkScalarRoundToInt(diff.fMaxMismatchA));
135d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger
136d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger        stream->writeText("<br>Total alpha channel mismatch ");
137d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger        stream->writeDecAsText(static_cast<int>(diff.fTotalMismatchA));
138d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger
139d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger        stream->writeText("<br>");
140363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("<br>Average color mismatch ");
141d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger        stream->writeDecAsText(SkScalarRoundToInt(MAX3(diff.fAverageMismatchR,
142d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger                                                       diff.fAverageMismatchG,
143d686ac77c2c485c4a3302eda9c1de597a6f8c568Derek Sollenberger                                                       diff.fAverageMismatchB)));
144363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("<br>Max color mismatch ");
145363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeDecAsText(MAX3(diff.fMaxMismatchR,
146363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                    diff.fMaxMismatchG,
147363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                    diff.fMaxMismatchB));
148363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("</td>");
149363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        break;
150363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger      case DiffRecord::kCouldNotCompare_Result:
151363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("Could not compare.<br>base: ");
152363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText(DiffResource::getStatusDescription(diff.fBase.fStatus));
153363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("<br>comparison: ");
154363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText(DiffResource::getStatusDescription(diff.fComparison.fStatus));
155363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("</td>");
156363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
157363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger      default:
158363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        SkDEBUGFAIL("encountered DiffRecord with unknown result type");
159363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
160363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
161363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
162363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
163363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void print_image_cell(SkFILEWStream* stream, const SkString& path, int height) {
164363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("<td><a href=\"");
165363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(path.c_str());
166363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("\"><img src=\"");
167363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(path.c_str());
168363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("\" height=\"");
169363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeDecAsText(height);
170363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("px\"></a></td>");
171363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
172363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
173363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void print_link_cell(SkFILEWStream* stream, const SkString& path, const char* text) {
174363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("<td><a href=\"");
175363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(path.c_str());
176363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("\">");
177363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText(text);
178363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("</a></td>");
179363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
180363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
181363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void print_diff_resource_cell(SkFILEWStream* stream, DiffResource& resource,
182363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                                     const SkString& relativePath, bool local) {
183363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (resource.fBitmap.empty()) {
184363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        if (DiffResource::kCouldNotDecode_Status == resource.fStatus) {
185363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (local && !resource.fFilename.isEmpty()) {
186363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                print_link_cell(stream, resource.fFilename, "N/A");
187363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                return;
188363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
189363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (!resource.fFullPath.isEmpty()) {
190363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                if (!resource.fFullPath.startsWith(PATH_DIV_STR)) {
191363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                    resource.fFullPath.prepend(relativePath);
192363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                }
193363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                print_link_cell(stream, resource.fFullPath, "N/A");
194363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                return;
195363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
196363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
197363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        stream->writeText("<td>N/A</td>");
198363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
199363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
200363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
201363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    int height = compute_image_height(resource.fBitmap.height(), resource.fBitmap.width());
202363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (local) {
203363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        print_image_cell(stream, resource.fFilename, height);
204363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        return;
205363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
206363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (!resource.fFullPath.startsWith(PATH_DIV_STR)) {
207363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        resource.fFullPath.prepend(relativePath);
208363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
209363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    print_image_cell(stream, resource.fFullPath, height);
210363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
211363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
212363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void print_diff_row(SkFILEWStream* stream, DiffRecord& diff, const SkString& relativePath) {
213363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("<tr>\n");
214363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    print_checkbox_cell(stream, diff);
215363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    print_label_cell(stream, diff);
216363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    print_diff_resource_cell(stream, diff.fWhite, relativePath, true);
217363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    print_diff_resource_cell(stream, diff.fDifference, relativePath, true);
218363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    print_diff_resource_cell(stream, diff.fBase, relativePath, false);
219363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    print_diff_resource_cell(stream, diff.fComparison, relativePath, false);
220363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->writeText("</tr>\n");
221363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    stream->flush();
222363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
223363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
224363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergervoid print_diff_page(const int matchCount,
225363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                     const int colorThreshold,
226363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                     const RecordArray& differences,
227363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                     const SkString& baseDir,
228363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                     const SkString& comparisonDir,
229363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                     const SkString& outputDir) {
230363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
231363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkASSERT(!baseDir.isEmpty());
232363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkASSERT(!comparisonDir.isEmpty());
233363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkASSERT(!outputDir.isEmpty());
234363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
235363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkString outputPath(outputDir);
236363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    outputPath.append("index.html");
237363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    //SkFILEWStream outputStream ("index.html");
238363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkFILEWStream outputStream(outputPath.c_str());
239363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
240363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // Need to convert paths from relative-to-cwd to relative-to-outputDir
241363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // FIXME this doesn't work if there are '..' inside the outputDir
242363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
243363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    bool isPathAbsolute = false;
244363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // On Windows or Linux, a path starting with PATH_DIV_CHAR is absolute.
245363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (outputDir.size() > 0 && PATH_DIV_CHAR == outputDir[0]) {
246363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        isPathAbsolute = true;
247363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
248363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#ifdef SK_BUILD_FOR_WIN32
249363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // On Windows, absolute paths can also start with "x:", where x is any
250363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    // drive letter.
251363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (outputDir.size() > 1 && ':' == outputDir[1]) {
252363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        isPathAbsolute = true;
253363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
254363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#endif
255363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
256363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    SkString relativePath;
257363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    if (!isPathAbsolute) {
258363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        unsigned int ui;
259363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        for (ui = 0; ui < outputDir.size(); ui++) {
260363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            if (outputDir[ui] == PATH_DIV_CHAR) {
261363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                relativePath.append(".." PATH_DIV_STR);
262363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            }
263363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
264363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
265363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
266363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    outputStream.writeText(
267363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "<html>\n<head>\n"
268363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "<script src=\"https://ajax.googleapis.com/ajax/"
269363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "libs/jquery/1.7.2/jquery.min.js\"></script>\n"
270363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "<script type=\"text/javascript\">\n"
271363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "function generateCheckedList() {\n"
272363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "var boxes = $(\":checkbox:checked\");\n"
273363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "var fileCmdLineString = '';\n"
274363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "var fileMultiLineString = '';\n"
275363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "for (var i = 0; i < boxes.length; i++) {\n"
276363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "fileMultiLineString += boxes[i].name + '<br>';\n"
277363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "fileCmdLineString += boxes[i].name + '&nbsp;';\n"
278363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "}\n"
279363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "$(\"#checkedList\").html(fileCmdLineString + "
280363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "'<br><br>' + fileMultiLineString);\n"
281363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "}\n"
282363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "</script>\n</head>\n<body>\n");
283363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    print_table_header(&outputStream, matchCount, colorThreshold, differences,
284363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger                       baseDir, comparisonDir);
285363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    int i;
286363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    for (i = 0; i < differences.count(); i++) {
287363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        DiffRecord* diff = differences[i];
288363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger
289363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        switch (diff->fResult) {
290363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          // Cases in which there is no diff to report.
291363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kEqualBits_Result:
292363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kEqualPixels_Result:
293363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
294363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          // Cases in which we want a detailed pixel diff.
295363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kDifferentPixels_Result:
296363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kDifferentSizes_Result:
297363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          case DiffRecord::kCouldNotCompare_Result:
298363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            print_diff_row(&outputStream, *diff, relativePath);
299363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
300363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger          default:
301363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            SkDEBUGFAIL("encountered DiffRecord with unknown result type");
302363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger            continue;
303363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        }
304363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    }
305363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    outputStream.writeText(
306363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "</table>\n"
307363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "<input type=\"button\" "
308363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "onclick=\"generateCheckedList()\" "
309363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "value=\"Create Rebaseline List\">\n"
310363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "<div id=\"checkedList\"></div>\n"
311363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger        "</body>\n</html>\n");
312363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger    outputStream.flush();
313363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger}
314