migrate anim_diff tool from C++ to C89

+ jenkins fixes for native config (library order)
+ add a missing -lm
+ replace log10 by log, just in case
+ partially reverted configure.ac to remove the C++ part

Change-Id: Iee099c544451b23c6cfaca53d5a95d2d332e066e
diff --git a/examples/anim_diff.c b/examples/anim_diff.c
new file mode 100644
index 0000000..dab9c6b
--- /dev/null
+++ b/examples/anim_diff.c
@@ -0,0 +1,216 @@
+// 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 <limits.h>
+#include <stdio.h>
+#include <stdlib.h>  // for 'strtod'.
+#include <string.h>  // for 'strcmp'.
+
+#include "./anim_util.h"
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#endif
+
+// Returns true if 'a + b' will overflow.
+static int AdditionWillOverflow(int a, int b) {
+  return (b > 0) && (a > INT_MAX - b);
+}
+
+// Minimize number of frames by combining successive frames that have exact same
+// ARGB data into a single longer duration frame.
+static void MinimizeAnimationFrames(AnimatedImage* const img) {
+  uint32_t i;
+  for (i = 1; i < img->num_frames; ++i) {
+    DecodedFrame* const frame1 = &img->frames[i - 1];
+    DecodedFrame* const frame2 = &img->frames[i];
+    const uint8_t* const rgba1 = frame1->rgba;
+    const uint8_t* const rgba2 = frame2->rgba;
+    // If merging frames will result in integer overflow for 'duration',
+    // skip merging.
+    if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue;
+    if (!memcmp(rgba1, rgba2, img->canvas_width * 4 * img->canvas_height)) {
+      // Merge 'i+1'th frame into 'i'th frame.
+      frame1->duration += frame2->duration;
+      if (i + 1 < img->num_frames) {
+        memmove(&img->frames[i], &img->frames[i + 1],
+                (img->num_frames - i - 1) * sizeof(*img->frames));
+      }
+      --img->num_frames;
+      --i;
+    }
+  }
+}
+
+static int CompareValues(uint32_t a, uint32_t b, const char* output_str) {
+  if (a != b) {
+    fprintf(stderr, "%s: %d vs %d\n", output_str, a, b);
+    return 0;
+  }
+  return 1;
+}
+
+// Note: As long as frame durations and reconstructed frames are identical, it
+// is OK for other aspects like offsets, dispose/blend method to vary.
+static int CompareAnimatedImagePair(const AnimatedImage* const img1,
+                                    const AnimatedImage* const img2,
+                                    int premultiply,
+                                    double min_psnr) {
+  int ok = 1;
+  const int is_multi_frame_image = (img1->num_frames > 1);
+  uint32_t i;
+
+  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->num_frames, img2->num_frames,
+                     "Frame count mismatch") && ok;
+  if (!ok) return 0;  // These are fatal failures, can't proceed.
+
+  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 (i = 0; i < img1->num_frames; ++i) {
+    // Pixel-by-pixel comparison.
+    const uint8_t* const rgba1 = img1->frames[i].rgba;
+    const uint8_t* const rgba2 = img2->frames[i].rgba;
+    int max_diff;
+    double psnr;
+    if (is_multi_frame_image) {  // Check relevant for multi-frame images only.
+      const char format[] = "Frame #%d, duration mismatch";
+      char tmp[sizeof(format) + 8];
+      ok = ok && (snprintf(tmp, sizeof(tmp), format, i) >= 0);
+      ok = ok && CompareValues(img1->frames[i].duration,
+                               img2->frames[i].duration, tmp);
+    }
+    GetDiffAndPSNR(rgba1, rgba2, img1->canvas_width, img1->canvas_height,
+                   premultiply, &max_diff, &psnr);
+    if (min_psnr > 0.) {
+      if (psnr < min_psnr) {
+        fprintf(stderr, "Frame #%d, psnr = %.2lf (min_psnr = %f)\n", i,
+                psnr, min_psnr);
+        ok = 0;
+      }
+    } else {
+      if (max_diff != 0) {
+        fprintf(stderr, "Frame #%d, max pixel diff: %d\n", i, max_diff);
+        ok = 0;
+      }
+    }
+  }
+  return ok;
+}
+
+static void Help(void) {
+  printf("\nUsage: anim_diff <image1> <image2> [-dump_frames <folder>] "
+         "[-min_psnr <float>][-raw_comparison]\n");
+}
+
+int main(int argc, const char* argv[]) {
+  int return_code = -1;
+  int dump_frames = 0;
+  const char* dump_folder = NULL;
+  double min_psnr = 0.;
+  int got_input1 = 0;
+  int got_input2 = 0;
+  int premultiply = 1;
+  int i, c;
+  const char* files[2] = { NULL, NULL };
+  AnimatedImage images[2];
+
+  if (argc < 3) {
+    Help();
+    return -1;
+  }
+
+  for (c = 1; c < argc; ++c) {
+    int parse_error = 0;
+    if (!strcmp(argv[c], "-dump_frames")) {
+      if (c < argc - 1) {
+        dump_frames = 1;
+        dump_folder = argv[++c];
+      } else {
+        parse_error = 1;
+      }
+    } 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 = 1;
+          fprintf(stderr, "Error! '%s' is not a floating point number.\n", v);
+        }
+        min_psnr = d;
+      } else {
+        parse_error = 1;
+      }
+    } else if (!strcmp(argv[c], "-raw_comparison")) {
+      premultiply = 0;
+    } else {
+      if (!got_input1) {
+        files[0] = argv[c];
+        got_input1 = 1;
+      } else if (!got_input2) {
+        files[1] = argv[c];
+        got_input2 = 1;
+      } else {
+        parse_error = 1;
+      }
+    }
+    if (parse_error) {
+      Help();
+      return -1;
+    }
+  }
+  if (!got_input2) {
+    Help();
+    return -1;
+  }
+
+  if (dump_frames) {
+    printf("Dumping decoded frames in: %s\n", dump_folder);
+  }
+
+  memset(images, 0, sizeof(images));
+  for (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_code = -2;
+      goto End;
+    } else {
+      MinimizeAnimationFrames(&images[i]);
+    }
+  }
+
+  if (!CompareAnimatedImagePair(&images[0], &images[1],
+                                premultiply, min_psnr)) {
+    fprintf(stderr, "\nFiles %s and %s differ.\n", files[0], files[1]);
+    return_code = -3;
+  } else {
+    printf("\nFiles %s and %s are identical.\n", files[0], files[1]);
+    return_code = 0;
+  }
+ End:
+  ClearAnimatedImage(&images[0]);
+  ClearAnimatedImage(&images[1]);
+  return return_code;
+}