| OLD | NEW |
| 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2009 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // This file input format is based loosely on | 5 // This file input format is based loosely on |
| 6 // WebKitTools/DumpRenderTree/ImageDiff.m | 6 // WebKitTools/DumpRenderTree/ImageDiff.m |
| 7 | 7 |
| 8 // The exact format of this tool's output to stdout is important, to match | 8 // The exact format of this tool's output to stdout is important, to match |
| 9 // what the run-webkit-tests script expects. | 9 // what the run-webkit-tests script expects. |
| 10 | 10 |
| 11 #include <algorithm> | 11 #include <algorithm> |
| 12 #include <vector> | 12 #include <vector> |
| 13 #include <string> | 13 #include <string> |
| 14 #include <iostream> | 14 #include <iostream> |
| 15 | 15 |
| 16 #include "base/basictypes.h" | 16 #include "base/basictypes.h" |
| 17 #include "base/command_line.h" | 17 #include "base/command_line.h" |
| 18 #include "base/file_path.h" |
| 18 #include "base/file_util.h" | 19 #include "base/file_util.h" |
| 19 #include "base/logging.h" | 20 #include "base/logging.h" |
| 20 #include "base/process_util.h" | 21 #include "base/process_util.h" |
| 21 #include "base/scoped_ptr.h" | 22 #include "base/scoped_ptr.h" |
| 22 #include "base/utf_string_conversions.h" | 23 #include "base/utf_string_conversions.h" |
| 23 #include "gfx/codec/png_codec.h" | 24 #include "gfx/codec/png_codec.h" |
| 24 | 25 |
| 25 #if defined(OS_WIN) | 26 #if defined(OS_WIN) |
| 26 #include "windows.h" | 27 #include "windows.h" |
| 27 #endif | 28 #endif |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 81 gfx::PNGCodec::FORMAT_RGBA, | 82 gfx::PNGCodec::FORMAT_RGBA, |
| 82 &data_, &w_, &h_)) { | 83 &data_, &w_, &h_)) { |
| 83 Clear(); | 84 Clear(); |
| 84 return false; | 85 return false; |
| 85 } | 86 } |
| 86 return true; | 87 return true; |
| 87 } | 88 } |
| 88 | 89 |
| 89 // Creates the image from the given filename on disk, and returns true on | 90 // Creates the image from the given filename on disk, and returns true on |
| 90 // success. | 91 // success. |
| 91 bool CreateFromFilename(const char* filename) { | 92 bool CreateFromFilename(const FilePath& path) { |
| 92 FilePath path = FilePath::FromWStringHack(ASCIIToWide(filename)); | |
| 93 FILE* f = file_util::OpenFile(path, "rb"); | 93 FILE* f = file_util::OpenFile(path, "rb"); |
| 94 if (!f) | 94 if (!f) |
| 95 return false; | 95 return false; |
| 96 | 96 |
| 97 std::vector<unsigned char> compressed; | 97 std::vector<unsigned char> compressed; |
| 98 const int buf_size = 1024; | 98 const int buf_size = 1024; |
| 99 unsigned char buf[buf_size]; | 99 unsigned char buf[buf_size]; |
| 100 size_t num_read = 0; | 100 size_t num_read = 0; |
| 101 while ((num_read = fread(buf, 1, buf_size, f)) > 0) { | 101 while ((num_read = fread(buf, 1, buf_size, f)) > 0) { |
| 102 std::copy(buf, &buf[num_read], std::back_inserter(compressed)); | 102 std::copy(buf, &buf[num_read], std::back_inserter(compressed)); |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 184 /* For unfinished webkit-like-mode (see below) | 184 /* For unfinished webkit-like-mode (see below) |
| 185 "\n" | 185 "\n" |
| 186 " image_diff -s\n" | 186 " image_diff -s\n" |
| 187 " Reads stream input from stdin, should be EXACTLY of the format\n" | 187 " Reads stream input from stdin, should be EXACTLY of the format\n" |
| 188 " \"Content-length: <byte length> <data>Content-length: ...\n" | 188 " \"Content-length: <byte length> <data>Content-length: ...\n" |
| 189 " it will take as many file pairs as given, and will compare them as\n" | 189 " it will take as many file pairs as given, and will compare them as\n" |
| 190 " (cmp_file, reference_file) pairs\n"); | 190 " (cmp_file, reference_file) pairs\n"); |
| 191 */ | 191 */ |
| 192 } | 192 } |
| 193 | 193 |
| 194 int CompareImages(const char* file1, const char* file2) { | 194 int CompareImages(const FilePath& file1, const FilePath& file2) { |
| 195 Image actual_image; | 195 Image actual_image; |
| 196 Image baseline_image; | 196 Image baseline_image; |
| 197 | 197 |
| 198 if (!actual_image.CreateFromFilename(file1)) { | 198 if (!actual_image.CreateFromFilename(file1)) { |
| 199 fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file1); | 199 fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n", |
| 200 file1.value().c_str()); |
| 200 return kStatusError; | 201 return kStatusError; |
| 201 } | 202 } |
| 202 if (!baseline_image.CreateFromFilename(file2)) { | 203 if (!baseline_image.CreateFromFilename(file2)) { |
| 203 fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file2); | 204 fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n", |
| 205 file2.value().c_str()); |
| 204 return kStatusError; | 206 return kStatusError; |
| 205 } | 207 } |
| 206 | 208 |
| 207 float percent = PercentageDifferent(actual_image, baseline_image); | 209 float percent = PercentageDifferent(actual_image, baseline_image); |
| 208 if (percent > 0.0) { | 210 if (percent > 0.0) { |
| 209 // failure: The WebKit version also writes the difference image to | 211 // failure: The WebKit version also writes the difference image to |
| 210 // stdout, which seems excessive for our needs. | 212 // stdout, which seems excessive for our needs. |
| 211 printf("diff: %01.2f%% failed\n", percent); | 213 printf("diff: %01.2f%% failed\n", percent); |
| 212 return kStatusDifferent; | 214 return kStatusDifferent; |
| 213 } | 215 } |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 283 uint32 alpha = base_pixel & RGBA_ALPHA; | 285 uint32 alpha = base_pixel & RGBA_ALPHA; |
| 284 uint32 new_pixel = base_pixel - ((alpha / 2) & RGBA_ALPHA); | 286 uint32 new_pixel = base_pixel - ((alpha / 2) & RGBA_ALPHA); |
| 285 out->set_pixel_at(x, y, new_pixel); | 287 out->set_pixel_at(x, y, new_pixel); |
| 286 } | 288 } |
| 287 } | 289 } |
| 288 } | 290 } |
| 289 | 291 |
| 290 return same; | 292 return same; |
| 291 } | 293 } |
| 292 | 294 |
| 293 int DiffImages(const char* file1, const char* file2, const char* out_file) { | 295 int DiffImages(const FilePath& file1, const FilePath& file2, |
| 296 const FilePath& out_file) { |
| 294 Image actual_image; | 297 Image actual_image; |
| 295 Image baseline_image; | 298 Image baseline_image; |
| 296 | 299 |
| 297 if (!actual_image.CreateFromFilename(file1)) { | 300 if (!actual_image.CreateFromFilename(file1)) { |
| 298 fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file1); | 301 fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n", |
| 302 file1.value().c_str()); |
| 299 return kStatusError; | 303 return kStatusError; |
| 300 } | 304 } |
| 301 if (!baseline_image.CreateFromFilename(file2)) { | 305 if (!baseline_image.CreateFromFilename(file2)) { |
| 302 fprintf(stderr, "image_diff: Unable to open file \"%s\"\n", file2); | 306 fprintf(stderr, "image_diff: Unable to open file \"%" PRFilePath "\"\n", |
| 307 file2.value().c_str()); |
| 303 return kStatusError; | 308 return kStatusError; |
| 304 } | 309 } |
| 305 | 310 |
| 306 Image diff_image; | 311 Image diff_image; |
| 307 bool same = CreateImageDiff(baseline_image, actual_image, &diff_image); | 312 bool same = CreateImageDiff(baseline_image, actual_image, &diff_image); |
| 308 if (same) | 313 if (same) |
| 309 return kStatusSame; | 314 return kStatusSame; |
| 310 | 315 |
| 311 std::vector<unsigned char> png_encoding; | 316 std::vector<unsigned char> png_encoding; |
| 312 gfx::PNGCodec::Encode(diff_image.data(), gfx::PNGCodec::FORMAT_RGBA, | 317 gfx::PNGCodec::Encode(diff_image.data(), gfx::PNGCodec::FORMAT_RGBA, |
| 313 diff_image.w(), diff_image.h(), diff_image.w() * 4, | 318 diff_image.w(), diff_image.h(), diff_image.w() * 4, |
| 314 false, &png_encoding); | 319 false, &png_encoding); |
| 315 FilePath out_path = FilePath::FromWStringHack(ASCIIToWide(out_file)); | 320 if (file_util::WriteFile(out_file, |
| 316 if (file_util::WriteFile(out_path, | |
| 317 reinterpret_cast<char*>(&png_encoding.front()), png_encoding.size()) < 0) | 321 reinterpret_cast<char*>(&png_encoding.front()), png_encoding.size()) < 0) |
| 318 return kStatusError; | 322 return kStatusError; |
| 319 | 323 |
| 320 return kStatusDifferent; | 324 return kStatusDifferent; |
| 321 } | 325 } |
| 322 | 326 |
| 327 // It isn't strictly correct to only support ASCII paths, but this |
| 328 // program reads paths on stdin and the program that spawns it outputs |
| 329 // paths as non-wide strings anyway. |
| 330 FilePath FilePathFromASCII(const std::string& str) { |
| 331 #if defined(OS_WIN) |
| 332 return FilePath(ASCIIToWide(str)); |
| 333 #else |
| 334 return FilePath(str); |
| 335 #endif |
| 336 } |
| 337 |
| 323 int main(int argc, const char* argv[]) { | 338 int main(int argc, const char* argv[]) { |
| 324 base::EnableTerminationOnHeapCorruption(); | 339 base::EnableTerminationOnHeapCorruption(); |
| 325 CommandLine::Init(argc, argv); | 340 CommandLine::Init(argc, argv); |
| 326 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); | 341 const CommandLine& parsed_command_line = *CommandLine::ForCurrentProcess(); |
| 327 if (parsed_command_line.HasSwitch(kOptionPollStdin)) { | 342 if (parsed_command_line.HasSwitch(kOptionPollStdin)) { |
| 328 // Watch stdin for filenames. | 343 // Watch stdin for filenames. |
| 329 std::string stdin_buffer; | 344 std::string stdin_buffer; |
| 330 std::string filename1_buffer; | 345 FilePath filename1; |
| 331 bool have_filename1 = false; | |
| 332 while (std::getline(std::cin, stdin_buffer)) { | 346 while (std::getline(std::cin, stdin_buffer)) { |
| 333 if (stdin_buffer.empty()) | 347 if (stdin_buffer.empty()) |
| 334 continue; | 348 continue; |
| 335 | 349 |
| 336 if (have_filename1) { | 350 if (!filename1.empty()) { |
| 337 // CompareImages writes results to stdout unless an error occurred. | 351 // CompareImages writes results to stdout unless an error occurred. |
| 338 if (CompareImages(filename1_buffer.c_str(), stdin_buffer.c_str()) == | 352 FilePath filename2 = FilePathFromASCII(stdin_buffer); |
| 339 kStatusError) | 353 if (CompareImages(filename1, filename2) == kStatusError) |
| 340 printf("error\n"); | 354 printf("error\n"); |
| 341 fflush(stdout); | 355 fflush(stdout); |
| 342 have_filename1 = false; | 356 filename1 = FilePath(); |
| 343 } else { | 357 } else { |
| 344 // Save the first filename in another buffer and wait for the second | 358 // Save the first filename in another buffer and wait for the second |
| 345 // filename to arrive via stdin. | 359 // filename to arrive via stdin. |
| 346 filename1_buffer = stdin_buffer; | 360 filename1 = FilePathFromASCII(stdin_buffer); |
| 347 have_filename1 = true; | |
| 348 } | 361 } |
| 349 } | 362 } |
| 350 return 0; | 363 return 0; |
| 351 } | 364 } |
| 352 | 365 |
| 366 // TODO: CommandLine::GetLooseValues() should eventually return |
| 367 // CommandLine::StringType (which is the same as |
| 368 // FilePath::StringType and can convert to FilePaths directly). |
| 353 std::vector<std::wstring> values = parsed_command_line.GetLooseValues(); | 369 std::vector<std::wstring> values = parsed_command_line.GetLooseValues(); |
| 354 if (parsed_command_line.HasSwitch(kOptionGenerateDiff)) { | 370 if (parsed_command_line.HasSwitch(kOptionGenerateDiff)) { |
| 355 if (values.size() == 3) { | 371 if (values.size() == 3) { |
| 356 return DiffImages(WideToUTF8(values[0]).c_str(), | 372 return DiffImages(FilePath::FromWStringHack(values[0]), |
| 357 WideToUTF8(values[1]).c_str(), | 373 FilePath::FromWStringHack(values[1]), |
| 358 WideToUTF8(values[2]).c_str()); | 374 FilePath::FromWStringHack(values[2])); |
| 359 } | 375 } |
| 360 } else if (values.size() == 2) { | 376 } else if (values.size() == 2) { |
| 361 return CompareImages(argv[1], argv[2]); | 377 return CompareImages(FilePath::FromWStringHack(values[0]), |
| 378 FilePath::FromWStringHack(values[1])); |
| 362 } | 379 } |
| 363 | 380 |
| 364 PrintHelp(); | 381 PrintHelp(); |
| 365 return kStatusError; | 382 return kStatusError; |
| 366 } | 383 } |
| OLD | NEW |