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