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#include "skdiff.h" 8363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "skdiff_utils.h" 9363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkBitmap.h" 10363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkData.h" 11363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkImageDecoder.h" 12363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkImageEncoder.h" 13363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkOSFile.h" 14363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkTDArray.h" 15363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkTemplates.h" 16363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#include "SkTypes.h" 17363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 180a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger#include <stdio.h> 190a657bbc2c6fc9daf699942e023050536d5ec95fDerek Sollenberger 20363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger/// If outputDir.isEmpty(), don't write out diff files. 21363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void create_diff_images (DiffMetricProc dmp, 22363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const int colorThreshold, 23363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const SkString& baseFile, 24363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const SkString& comparisonFile, 25363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const SkString& outputDir, 26363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const SkString& outputFilename, 27363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger DiffRecord* drp) { 28363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkASSERT(!baseFile.isEmpty()); 29363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkASSERT(!comparisonFile.isEmpty()); 30363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 31363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fBase.fFilename = baseFile; 32363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fBase.fFullPath = baseFile; 33363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fBase.fStatus = DiffResource::kSpecified_Status; 34363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 35363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fComparison.fFilename = comparisonFile; 36363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fComparison.fFullPath = comparisonFile; 37363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fComparison.fStatus = DiffResource::kSpecified_Status; 38363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 39363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkAutoDataUnref baseFileBits(read_file(drp->fBase.fFullPath.c_str())); 40363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (NULL != baseFileBits) { 41363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fBase.fStatus = DiffResource::kRead_Status; 42363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 43363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkAutoDataUnref comparisonFileBits(read_file(drp->fComparison.fFullPath.c_str())); 44363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (NULL != comparisonFileBits) { 45363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fComparison.fStatus = DiffResource::kRead_Status; 46363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 47363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (NULL == baseFileBits || NULL == comparisonFileBits) { 48363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (NULL == baseFileBits) { 49363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fBase.fStatus = DiffResource::kCouldNotRead_Status; 50363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 51363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (NULL == comparisonFileBits) { 52363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fComparison.fStatus = DiffResource::kCouldNotRead_Status; 53363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 54363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fResult = DiffRecord::kCouldNotCompare_Result; 55363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return; 56363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 57363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 58363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (are_buffers_equal(baseFileBits, comparisonFileBits)) { 59363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fResult = DiffRecord::kEqualBits_Result; 60363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return; 61363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 62363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 63363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger get_bitmap(baseFileBits, drp->fBase, SkImageDecoder::kDecodePixels_Mode); 64363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger get_bitmap(comparisonFileBits, drp->fComparison, SkImageDecoder::kDecodePixels_Mode); 65363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (DiffResource::kDecoded_Status != drp->fBase.fStatus || 66363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger DiffResource::kDecoded_Status != drp->fComparison.fStatus) 67363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger { 68363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger drp->fResult = DiffRecord::kCouldNotCompare_Result; 69363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return; 70363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 71363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 72363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger create_and_write_diff_image(drp, dmp, colorThreshold, outputDir, outputFilename); 73363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger //TODO: copy fBase.fFilename and fComparison.fFilename to outputDir 74363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // svn and git often present tmp files to diff tools which are promptly deleted 75363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 76363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger //TODO: serialize drp to outputDir 77363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // write a tool to deserialize them and call print_diff_page 78363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 79363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkASSERT(DiffRecord::kUnknown_Result != drp->fResult); 80363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 81363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 82363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerstatic void usage (char * argv0) { 83363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("Skia image diff tool\n"); 84363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("\n" 85363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"Usage: \n" 86363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger" %s <baseFile> <comparisonFile>\n" , argv0); 87363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf( 88363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\nArguments:" 89363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n --failonresult <result>: After comparing all file pairs, exit with nonzero" 90363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n return code (number of file pairs yielding this" 91363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n result) if any file pairs yielded this result." 92363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n This flag may be repeated, in which case the" 93363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n return code will be the number of fail pairs" 94363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n yielding ANY of these results." 95363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n --failonstatus <baseStatus> <comparisonStatus>: exit with nonzero return" 96363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n code if any file pairs yeilded this status." 97363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n --help: display this info" 98363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n --listfilenames: list all filenames for each result type in stdout" 99363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n --nodiffs: don't write out image diffs, just generate report on stdout" 100363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n --outputdir: directory to write difference images" 101363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n --threshold <n>: only report differences > n (per color channel) [default 0]" 102363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n -u: ignored. Recognized for compatibility with svn diff." 103363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n -L: first occurrence label for base, second occurrence label for comparison." 104363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n Labels must be of the form \"<filename>(\t<specifier>)?\"." 105363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n The base <filename> will be used to create files in outputdir." 106363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n" 107363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n baseFile: baseline image file." 108363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n comparisonFile: comparison image file" 109363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n" 110363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\nIf no sort is specified, it will sort by fraction of pixels mismatching." 111363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger"\n"); 112363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 113363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 114363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerconst int kNoError = 0; 115363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerconst int kGenericError = -1; 116363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 117363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerint tool_main(int argc, char** argv); 118363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerint tool_main(int argc, char** argv) { 119363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger DiffMetricProc diffProc = compute_diff_pmcolor; 120363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 121363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Maximum error tolerated in any one color channel in any one pixel before 122363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // a difference is reported. 123363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger int colorThreshold = 0; 124363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkString baseFile; 125363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkString baseLabel; 126363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkString comparisonFile; 127363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkString comparisonLabel; 128363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkString outputDir; 129363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 130363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger bool listFilenames = false; 131363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 132363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger bool failOnResultType[DiffRecord::kResultCount]; 133363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger for (int i = 0; i < DiffRecord::kResultCount; i++) { 134363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger failOnResultType[i] = false; 135363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 136363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 137363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger bool failOnStatusType[DiffResource::kStatusCount][DiffResource::kStatusCount]; 138363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger for (int base = 0; base < DiffResource::kStatusCount; ++base) { 139363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) { 140363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger failOnStatusType[base][comparison] = false; 141363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 142363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 143363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 144363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger int i; 145363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger int numUnflaggedArguments = 0; 146363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger int numLabelArguments = 0; 147363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger for (i = 1; i < argc; i++) { 148363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!strcmp(argv[i], "--failonresult")) { 149363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (argc == ++i) { 150363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("failonresult expects one argument.\n"); 151363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 152363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 153363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger DiffRecord::Result type = DiffRecord::getResultByName(argv[i]); 154363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (type != DiffRecord::kResultCount) { 155363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger failOnResultType[type] = true; 156363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } else { 157363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("ignoring unrecognized result <%s>\n", argv[i]); 158363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 159363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 160363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 161363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!strcmp(argv[i], "--failonstatus")) { 162363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (argc == ++i) { 163363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("failonstatus missing base status.\n"); 164363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 165363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 166363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger bool baseStatuses[DiffResource::kStatusCount]; 167363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!DiffResource::getMatchingStatuses(argv[i], baseStatuses)) { 168363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("unrecognized base status <%s>\n", argv[i]); 169363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 170363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 171363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (argc == ++i) { 172363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("failonstatus missing comparison status.\n"); 173363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 174363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 175363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger bool comparisonStatuses[DiffResource::kStatusCount]; 176363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!DiffResource::getMatchingStatuses(argv[i], comparisonStatuses)) { 177363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("unrecognized comarison status <%s>\n", argv[i]); 178363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 179363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 180363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger for (int base = 0; base < DiffResource::kStatusCount; ++base) { 181363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger for (int comparison = 0; comparison < DiffResource::kStatusCount; ++comparison) { 182363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger failOnStatusType[base][comparison] |= 183363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger baseStatuses[base] && comparisonStatuses[comparison]; 184363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 185363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 186363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 187363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 188363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!strcmp(argv[i], "--help")) { 189363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger usage(argv[0]); 190363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return kNoError; 191363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 192363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!strcmp(argv[i], "--listfilenames")) { 193363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger listFilenames = true; 194363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 195363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 196363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!strcmp(argv[i], "--outputdir")) { 197363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (argc == ++i) { 198363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("outputdir expects one argument.\n"); 199363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 200363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 201363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger outputDir.set(argv[i]); 202363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 203363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 204363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!strcmp(argv[i], "--threshold")) { 205363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger colorThreshold = atoi(argv[++i]); 206363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 207363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 208363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!strcmp(argv[i], "-u")) { 209363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger //we don't produce unified diffs, ignore parameter to work with svn diff 210363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 211363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 212363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!strcmp(argv[i], "-L")) { 213363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (argc == ++i) { 214363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("label expects one argument.\n"); 215363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 216363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 217363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger switch (numLabelArguments++) { 218363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger case 0: 219363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger baseLabel.set(argv[i]); 220363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 221363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger case 1: 222363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger comparisonLabel.set(argv[i]); 223363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 224363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger default: 225363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("extra label argument <%s>\n", argv[i]); 226363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger usage(argv[0]); 227363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return kGenericError; 228363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 229363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 230363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 231363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (argv[i][0] != '-') { 232363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger switch (numUnflaggedArguments++) { 233363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger case 0: 234363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger baseFile.set(argv[i]); 235363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 236363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger case 1: 237363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger comparisonFile.set(argv[i]); 238363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger continue; 239363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger default: 240363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("extra unflagged argument <%s>\n", argv[i]); 241363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger usage(argv[0]); 242363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return kGenericError; 243363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 244363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 245363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 246363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkDebugf("Unrecognized argument <%s>\n", argv[i]); 247363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger usage(argv[0]); 248363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return kGenericError; 249363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 250363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 251363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (numUnflaggedArguments != 2) { 252363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger usage(argv[0]); 253363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return kGenericError; 254363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 255363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 256363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (listFilenames) { 257363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("Base file is [%s]\n", baseFile.c_str()); 258363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 259363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 260363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (listFilenames) { 261363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("Comparison file is [%s]\n", comparisonFile.c_str()); 262363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 263363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 264363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (outputDir.isEmpty()) { 265363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (listFilenames) { 266363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("Not writing any diffs. No output dir specified.\n"); 267363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 268363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } else { 269363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (!outputDir.endsWith(PATH_DIV_STR)) { 270363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger outputDir.append(PATH_DIV_STR); 271363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 272363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (listFilenames) { 273363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("Writing diffs. Output dir is [%s]\n", outputDir.c_str()); 274363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 275363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 276363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 277363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Some obscure documentation about diff/patch labels: 278363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // 279363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Posix says the format is: <filename><tab><date> 280363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // It also states that if a filename contains <tab> or <newline> 281363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // the result is implementation defined 282363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // 283363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Svn diff --diff-cmd provides labels of the form: <filename><tab><revision> 284363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // 285363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Git diff --ext-diff does not supply arguments compatible with diff. 286363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // However, it does provide the filename directly. 287363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // skimagediff_git.sh: skimagediff %2 %5 -L "%1\t(%3)" -L "%1\t(%6)" 288363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // 289363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Git difftool sets $LOCAL, $REMOTE, $MERGED, and $BASE instead of command line parameters. 290363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // difftool.<>.cmd: skimagediff $LOCAL $REMOTE -L "$MERGED\t(local)" -L "$MERGED\t(remote)" 291363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // 292363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Diff will write any specified label verbatim. Without a specified label diff will write 293363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // <filename><tab><date> 294363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // However, diff will encode the filename as a cstring if the filename contains 295363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Any of <space> or <double quote> 296363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // A char less than 32 297096defe64d408e54474fe19f418c95bf1a554fc7Derek Sollenberger // Any escapable character \\, \a, \b, \t, \n, \v, \f, \r 298363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // 299363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Patch decodes: 300363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // If first <non-white-space> is <double quote>, parse filename from cstring. 301363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // If there is a <tab> after the first <non-white-space>, filename is 302363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // [first <non-white-space>, the next run of <white-space> with an embedded <tab>). 303363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Otherwise the filename is [first <non-space>, the next <white-space>). 304363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // 305363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // The filename /dev/null means the file does not exist (used in adds and deletes). 306363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 307363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // Considering the above, skimagediff will consider the contents of a -L parameter as 308363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger // <filename>(\t<specifier>)? 309363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger SkString outputFile; 310363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 311363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (baseLabel.isEmpty()) { 312363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger baseLabel.set(baseFile); 313363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger outputFile = baseLabel; 314363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } else { 315363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const char* baseLabelCstr = baseLabel.c_str(); 316363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger const char* tab = strchr(baseLabelCstr, '\t'); 317363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (NULL == tab) { 318363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger outputFile = baseLabel; 319363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } else { 320363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger outputFile.set(baseLabelCstr, tab - baseLabelCstr); 321363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 322363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 323363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (comparisonLabel.isEmpty()) { 324363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger comparisonLabel.set(comparisonFile); 325363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 326363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("Base: %s\n", baseLabel.c_str()); 327363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("Comparison: %s\n", comparisonLabel.c_str()); 328363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 329363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger DiffRecord dr; 330363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger create_diff_images(diffProc, colorThreshold, baseFile, comparisonFile, outputDir, outputFile, 331363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger &dr); 332363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 333363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (DiffResource::isStatusFailed(dr.fBase.fStatus)) { 334363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("Base %s.\n", DiffResource::getStatusDescription(dr.fBase.fStatus)); 335363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 336363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (DiffResource::isStatusFailed(dr.fComparison.fStatus)) { 337363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("Comparison %s.\n", DiffResource::getStatusDescription(dr.fComparison.fStatus)); 338363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 339363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("Base and Comparison %s.\n", DiffRecord::getResultDescription(dr.fResult)); 340363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 341363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (DiffRecord::kDifferentPixels_Result == dr.fResult) { 342363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("%.4f%% of pixels differ", 100 * dr.fFractionDifference); 343363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf(" (%.4f%% weighted)", 100 * dr.fWeightedFraction); 344363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (dr.fFractionDifference < 0.01) { 345363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf(" %d pixels", static_cast<int>(dr.fFractionDifference * 346363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger dr.fBase.fBitmap.width() * 347363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger dr.fBase.fBitmap.height())); 348363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 349363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 350363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("\nAverage color mismatch: "); 351363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("%d", static_cast<int>(MAX3(dr.fAverageMismatchR, 352363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger dr.fAverageMismatchG, 353363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger dr.fAverageMismatchB))); 354363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("\nMax color mismatch: "); 355363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("%d", MAX3(dr.fMaxMismatchR, 356363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger dr.fMaxMismatchG, 357363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger dr.fMaxMismatchB)); 358363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("\n"); 359363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 360363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger printf("\n"); 361363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 362363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger int num_failing_results = 0; 363363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (failOnResultType[dr.fResult]) { 364363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger ++num_failing_results; 365363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 366363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger if (failOnStatusType[dr.fBase.fStatus][dr.fComparison.fStatus]) { 367363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger ++num_failing_results; 368363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger } 369363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 370363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return num_failing_results; 371363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 372363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger 373363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#if !defined SK_BUILD_FOR_IOS 374363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenbergerint main(int argc, char * const argv[]) { 375363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger return tool_main(argc, (char**) argv); 376363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger} 377363e546ed626b6dbbc42f5db87b3594bc0b5944bDerek Sollenberger#endif 378