21#include "core/fxcrt/fx_memory.h"
22#include "testing/image_diff/image_diff_png.h"
23#include "testing/utils/path_service.h"
24#include "third_party/base/numerics/safe_conversions.h"
46 int w()
const {
return w_; }
47 int h()
const {
return h_; }
48 pdfium::span<
const uint8_t> span()
const {
return data_; }
53 return CreateFromFilenameImpl(path,
false);
58 return CreateFromFilenameImpl(path,
true);
68 if (!pixel_in_bounds(x, y))
70 return *
reinterpret_cast<
const uint32_t*>(&(data_[pixel_address(x, y)]));
74 if (!pixel_in_bounds(x, y))
77 void* addr = &data_[pixel_address(x, y)];
78 *
reinterpret_cast<uint32_t*>(addr) = color;
82 bool CreateFromFilenameImpl(
const std::string& path,
83 bool reverse_byte_order) {
84 FILE* f = fopen(path.c_str(),
"rb");
88 std::vector<uint8_t> compressed;
89 const size_t kBufSize = 1024;
90 uint8_t buf[kBufSize];
92 while ((num_read = fread(buf, 1, kBufSize, f)) > 0) {
93 compressed.insert(compressed.end(), buf, buf + num_read);
98 data_ = image_diff_png::DecodePNG(compressed, reverse_byte_order, &w_, &h_);
106 bool pixel_in_bounds(
int x,
int y)
const {
107 return x >= 0 && x < w_ && y >= 0 && y < h_;
110 size_t pixel_address(
int x,
int y)
const {
return (y * w_ + x) * 4; }
116 std::vector<uint8_t> data_;
123 static_cast<
float>(actual
.w()) *
static_cast<
float>(actual
.h());
124 if (total_pixels == 0) {
128 return 100.0f * pixels_different / total_pixels;
133 int* pixels_different) {
141 *pixels_different += (max_w - w) * h;
143 *pixels_different += (max_h - h) * max_w;
148 :
red(packed & 0xff),
149 green((packed >> 8) & 0xff),
150 blue((packed >> 16) & 0xff),
151 alpha((packed >> 24) & 0xff) {}
159uint8_t
ChannelDelta(uint8_t baseline_channel, uint8_t actual_channel) {
162 return std::abs(baseline_channel - actual_channel);
175 uint8_t max_pixel_per_channel_delta) {
180 int pixels_different = 0;
181 for (
int y = 0; y < h; ++y) {
182 for (
int x = 0; x < w; ++x) {
183 const uint32_t baseline_pixel = baseline
.pixel_at(x
, y
);
185 if (baseline_pixel == actual_pixel) {
191 max_pixel_per_channel_delta) {
210 std::map<uint32_t, int32_t> baseline_histogram;
211 for (
int y = 0; y < h; ++y) {
212 for (
int x = 0; x < w; ++x) {
219 int pixels_different = 0;
220 for (
int y = 0; y < h; ++y) {
221 for (
int x = 0; x < w; ++x) {
223 auto it = baseline_histogram.find(actual_rgba);
224 if (it != baseline_histogram.end() && it->second > 0)
239 " %s OPTIONS <compare_file> <reference_file>\n"
240 " Compares two files on disk, returning 0 when they are the same.\n"
241 " Passing \"--histogram\" additionally calculates a diff of the\n"
242 " RGBA value histograms (which is resistant to shifts in layout).\n"
243 " Passing \"--reverse-byte-order\" additionally assumes the\n"
244 " compare file has BGRA byte ordering.\n"
245 " Passing \"--fuzzy\" additionally allows individual pixels to\n"
246 " differ by at most 1 on each channel.\n\n"
247 " %s --diff <compare_file> <reference_file> <output_file>\n"
248 " Compares two files on disk, and if they differ, outputs an image\n"
249 " to <output_file> that visualizes the differing pixels as red\n"
251 " %s --subtract <compare_file> <reference_file> <output_file>\n"
252 " Compares two files on disk, and if they differ, outputs an image\n"
253 " to <output_file> that visualizes the difference as a scaled\n"
254 " subtraction of pixel values.\n",
255 binary_name.c_str(), binary_name.c_str(), binary_name.c_str());
259 const std::string& file1,
260 const std::string& file2,
261 bool compare_histograms,
262 bool reverse_byte_order,
263 uint8_t max_pixel_per_channel_delta) {
265 Image baseline_image;
267 bool actual_load_result =
271 if (!actual_load_result) {
272 fprintf(stderr,
"%s: Unable to open file \"%s\"\n", binary_name.c_str(),
277 fprintf(stderr,
"%s: Unable to open file \"%s\"\n", binary_name.c_str(),
282 if (compare_histograms) {
284 const char* passed = percent > 0.0 ?
"failed" :
"passed";
285 printf(
"histogram diff: %01.2f%% %s\n", percent, passed);
288 const char*
const diff_name = compare_histograms ?
"exact diff" :
"diff";
290 max_pixel_per_channel_delta
);
291 const char*
const passed = percent > 0.0 ?
"failed" :
"passed";
292 printf(
"%s: %01.2f%% %s\n", diff_name, percent, passed);
311 for (
int y = 0; y < h; ++y) {
312 for (
int x = 0; x < w; ++x) {
321 uint32_t new_pixel = base_pixel - ((alpha / 2) &
RGBA_ALPHA);
336 for (
int y = 0; y < h; ++y) {
337 for (
int x = 0; x < w; ++x) {
339 int32_t r1 = pixel1 & 0xff;
340 int32_t g1 = (pixel1 >> 8) & 0xff;
341 int32_t b1 = (pixel1 >> 16) & 0xff;
344 int32_t r2 = pixel2 & 0xff;
345 int32_t g2 = (pixel2 >> 8) & 0xff;
346 int32_t b2 = (pixel2 >> 16) & 0xff;
348 int32_t delta_r = r1 - r2;
349 int32_t delta_g = g1 - g2;
350 int32_t delta_b = b1 - b2;
351 same &= (delta_r == 0 && delta_g == 0 && delta_b == 0);
353 delta_r =
std::clamp(128 + delta_r * 8, 0, 255);
354 delta_g =
std::clamp(128 + delta_g * 8, 0, 255);
355 delta_b =
std::clamp(128 + delta_b * 8, 0, 255);
358 new_pixel |= delta_r;
359 new_pixel |= (delta_g << 8);
360 new_pixel |= (delta_b << 16);
368 const std::string& file1,
369 const std::string& file2,
370 const std::string& out_file,
372 bool reverse_byte_order) {
374 Image baseline_image;
376 bool actual_load_result =
381 if (!actual_load_result) {
382 fprintf(stderr,
"%s: Unable to open file \"%s\"\n", binary_name.c_str(),
387 fprintf(stderr,
"%s: Unable to open file \"%s\"\n", binary_name.c_str(),
393 bool same = do_subtraction
399 std::vector<uint8_t> png_encoding = image_diff_png::EncodeRGBAPNG(
400 diff_image.span(), diff_image.w(), diff_image.h(), diff_image.w() * 4);
401 if (png_encoding.empty())
404 FILE* f = fopen(out_file.c_str(),
"wb");
408 size_t size = png_encoding.size();
409 char* ptr =
reinterpret_cast<
char*>(&png_encoding.front());
410 if (fwrite(ptr, 1, size, f) != size)
416int main(
int argc,
const char* argv[]) {
419 bool histograms =
false;
420 bool produce_diff_image =
false;
421 bool produce_image_subtraction =
false;
422 bool reverse_byte_order =
false;
423 uint8_t max_pixel_per_channel_delta = 0;
424 std::string filename1;
425 std::string filename2;
426 std::string diff_filename;
430 std::string binary_name = last_separator ? last_separator + 1 : argv[0];
433 for (i = 1; i < argc; ++i) {
434 const char* arg = argv[i];
435 if (strstr(arg,
"--") != arg)
437 if (strcmp(arg,
"--histogram") == 0) {
439 }
else if (strcmp(arg,
"--diff") == 0) {
440 produce_diff_image =
true;
441 }
else if (strcmp(arg,
"--subtract") == 0) {
442 produce_image_subtraction =
true;
443 }
else if (strcmp(arg,
"--reverse-byte-order") == 0) {
444 reverse_byte_order =
true;
445 }
else if (strcmp(arg,
"--fuzzy") == 0) {
446 max_pixel_per_channel_delta = 1;
450 filename1 = argv[i++];
452 filename2 = argv[i++];
454 diff_filename = argv[i++];
456 if (produce_diff_image || produce_image_subtraction) {
457 if (!diff_filename.empty()) {
459 produce_image_subtraction
, reverse_byte_order
);
461 }
else if (!filename2.empty()) {
463 reverse_byte_order
, max_pixel_per_channel_delta
);
void set_pixel_at(int x, int y, uint32_t color)
Image & operator=(const Image &other)=default
Image(const Image &image)=default
bool CreateFromFilenameWithReverseByteOrder(const std::string &path)
bool CreateFromFilename(const std::string &path)
uint32_t pixel_at(int x, int y) const
void FX_InitializeMemoryAllocators()
uint8_t ChannelDelta(uint8_t baseline_channel, uint8_t actual_channel)
bool CreateImageDiff(const Image &image1, const Image &image2, Image *out)
float CalculateDifferencePercentage(const Image &actual, int pixels_different)
void PrintHelp(const std::string &binary_name)
void CountImageSizeMismatchAsPixelDifference(const Image &baseline, const Image &actual, int *pixels_different)
float HistogramPercentageDifferent(const Image &baseline, const Image &actual)
uint8_t MaxPixelPerChannelDelta(const UnpackedPixel &baseline_pixel, const UnpackedPixel &actual_pixel)
bool SubtractImages(const Image &image1, const Image &image2, Image *out)
constexpr uint32_t RGBA_RED
constexpr int kStatusDifferent
int DiffImages(const std::string &binary_name, const std::string &file1, const std::string &file2, const std::string &out_file, bool do_subtraction, bool reverse_byte_order)
constexpr uint32_t RGBA_ALPHA
int CompareImages(const std::string &binary_name, const std::string &file1, const std::string &file2, bool compare_histograms, bool reverse_byte_order, uint8_t max_pixel_per_channel_delta)
constexpr int kStatusError
float PercentageDifferent(const Image &baseline, const Image &actual, uint8_t max_pixel_per_channel_delta)
constexpr int kStatusSame
int main(int argc, const char *argv[])
UnpackedPixel(uint32_t packed)