mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-20 12:28:26 +01:00
172 lines
5.1 KiB
C++
172 lines
5.1 KiB
C++
|
// Copyright 2015 Google Inc. All Rights Reserved.
|
||
|
//
|
||
|
// Use of this source code is governed by a BSD-style license
|
||
|
// that can be found in the COPYING file in the root of the source
|
||
|
// tree. An additional intellectual property rights grant can be found
|
||
|
// in the file PATENTS. All contributing project authors may
|
||
|
// be found in the AUTHORS file in the root of the source tree.
|
||
|
// -----------------------------------------------------------------------------
|
||
|
//
|
||
|
// Checks if given pair of animated GIF/WebP images are identical:
|
||
|
// That is: their reconstructed canvases match pixel-by-pixel and their other
|
||
|
// animation properties (loop count etc) also match.
|
||
|
//
|
||
|
// example: anim_diff foo.gif bar.webp
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h> // for 'strtod'.
|
||
|
#include <string.h> // for 'strcmp'.
|
||
|
|
||
|
#include <iostream> // for 'cout'.
|
||
|
#include <sstream> // for 'ostringstream'.
|
||
|
|
||
|
#include "./anim_util.h"
|
||
|
|
||
|
namespace {
|
||
|
|
||
|
template<typename T>
|
||
|
bool CompareValues(T a, T b, const std::string& output_str) {
|
||
|
if (a != b) {
|
||
|
std::cout << output_str << ": " << a << " vs " << b << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Note: As long as frame durations and reconstructed frames are identical, it
|
||
|
// is OK for other aspects like offsets, dispose/blend method to vary.
|
||
|
bool CompareAnimatedImagePair(const AnimatedImage& img1,
|
||
|
const AnimatedImage& img2,
|
||
|
double min_psnr) {
|
||
|
bool ok = true;
|
||
|
ok = CompareValues(img1.canvas_width, img2.canvas_width,
|
||
|
"Canvas width mismatch") && ok;
|
||
|
ok = CompareValues(img1.canvas_height, img2.canvas_height,
|
||
|
"Canvas height mismatch") && ok;
|
||
|
ok = CompareValues(img1.frames.size(), img2.frames.size(),
|
||
|
"Frame count mismatch") && ok;
|
||
|
if (!ok) return false; // These are fatal failures, can't proceed.
|
||
|
|
||
|
const bool is_multi_frame_image = (img1.frames.size() > 1);
|
||
|
if (is_multi_frame_image) { // Checks relevant for multi-frame images only.
|
||
|
ok = CompareValues(img1.loop_count, img2.loop_count,
|
||
|
"Loop count mismatch") && ok;
|
||
|
ok = CompareValues(img1.bgcolor, img2.bgcolor,
|
||
|
"Background color mismatch") && ok;
|
||
|
}
|
||
|
|
||
|
for (size_t i = 0; i < img1.frames.size(); ++i) {
|
||
|
if (is_multi_frame_image) { // Check relevant for multi-frame images only.
|
||
|
std::ostringstream error_str;
|
||
|
error_str << "Frame #" << i << ", duration mismatch";
|
||
|
ok = CompareValues(img1.frames[i].duration, img2.frames[i].duration,
|
||
|
error_str.str()) && ok;
|
||
|
}
|
||
|
// Pixel-by-pixel comparison.
|
||
|
const uint8_t* rgba1 = img1.frames[i].rgba.data();
|
||
|
const uint8_t* rgba2 = img2.frames[i].rgba.data();
|
||
|
int max_diff;
|
||
|
double psnr;
|
||
|
GetDiffAndPSNR(rgba1, rgba2, img1.canvas_width, img1.canvas_height,
|
||
|
&max_diff, &psnr);
|
||
|
if (min_psnr > 0.) {
|
||
|
if (psnr < min_psnr) {
|
||
|
fprintf(stderr, "Frame #%zu, psnr = %.2lf (min_psnr = %f)\n", i,
|
||
|
psnr, min_psnr);
|
||
|
ok = false;
|
||
|
}
|
||
|
} else {
|
||
|
if (max_diff != 0) {
|
||
|
fprintf(stderr, "Frame #%zu, max pixel diff: %d\n", i, max_diff);
|
||
|
ok = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return ok;
|
||
|
}
|
||
|
|
||
|
void Help() {
|
||
|
printf("\nUsage: anim_diff <image1> <image2> [-dump_frames <folder>] "
|
||
|
"[-min_psnr <float>]\n");
|
||
|
}
|
||
|
|
||
|
} // namespace
|
||
|
|
||
|
int main(int argc, const char* argv[]) {
|
||
|
bool dump_frames = false;
|
||
|
const char* dump_folder = NULL;
|
||
|
double min_psnr = 0.;
|
||
|
bool got_input1 = false;
|
||
|
bool got_input2 = false;
|
||
|
const char* files[2];
|
||
|
|
||
|
if (argc < 3) {
|
||
|
Help();
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
for (int c = 1; c < argc; ++c) {
|
||
|
bool parse_error = false;
|
||
|
if (!strcmp(argv[c], "-dump_frames")) {
|
||
|
if (c < argc - 1) {
|
||
|
dump_frames = true;
|
||
|
dump_folder = argv[++c];
|
||
|
} else {
|
||
|
parse_error = true;
|
||
|
}
|
||
|
} else if (!strcmp(argv[c], "-min_psnr")) {
|
||
|
if (c < argc - 1) {
|
||
|
const char* const v = argv[++c];
|
||
|
char* end = NULL;
|
||
|
const double d = strtod(v, &end);
|
||
|
if (end == v) {
|
||
|
parse_error = true;
|
||
|
fprintf(stderr, "Error! '%s' is not a floating point number.\n", v);
|
||
|
}
|
||
|
min_psnr = d;
|
||
|
} else {
|
||
|
parse_error = true;
|
||
|
}
|
||
|
} else {
|
||
|
if (!got_input1) {
|
||
|
files[0] = argv[c];
|
||
|
got_input1 = true;
|
||
|
} else if (!got_input2) {
|
||
|
files[1] = argv[c];
|
||
|
got_input2 = true;
|
||
|
} else {
|
||
|
parse_error = true;
|
||
|
}
|
||
|
}
|
||
|
if (parse_error) {
|
||
|
Help();
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
if (!got_input2) {
|
||
|
Help();
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (dump_frames) {
|
||
|
printf("Dumping decoded frames in: %s\n", dump_folder);
|
||
|
}
|
||
|
|
||
|
AnimatedImage images[2];
|
||
|
for (int i = 0; i < 2; ++i) {
|
||
|
printf("Decoding file: %s\n", files[i]);
|
||
|
if (!ReadAnimatedImage(files[i], &images[i], dump_frames, dump_folder)) {
|
||
|
fprintf(stderr, "Error decoding file: %s\n Aborting.\n", files[i]);
|
||
|
return -2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!CompareAnimatedImagePair(images[0], images[1], min_psnr)) {
|
||
|
fprintf(stderr, "\nFiles %s and %s differ.\n", files[0], files[1]);
|
||
|
return -3;
|
||
|
}
|
||
|
|
||
|
printf("\nFiles %s and %s are identical.\n", files[0], files[1]);
|
||
|
return 0;
|
||
|
}
|