buffer-diff.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. /* imagediff - Compare two images
  2. *
  3. * Copyright © 2004 Richard D. Worth
  4. *
  5. * Permission to use, copy, modify, distribute, and sell this software
  6. * and its documentation for any purpose is hereby granted without
  7. * fee, provided that the above copyright notice appear in all copies
  8. * and that both that copyright notice and this permission notice
  9. * appear in supporting documentation, and that the name of Richard Worth
  10. * not be used in advertising or publicity pertaining to distribution
  11. * of the software without specific, written prior permission.
  12. * Richard Worth makes no representations about the suitability of this
  13. * software for any purpose. It is provided "as is" without express
  14. * or implied warranty.
  15. *
  16. * RICHARD WORTH DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
  17. * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
  18. * NO EVENT SHALL RICHARD WORTH BE LIABLE FOR ANY SPECIAL, INDIRECT OR
  19. * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
  20. * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  21. * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  22. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  23. *
  24. * Author: Richard D. Worth <richard@theworths.org> */
  25. #if HAVE_CONFIG_H
  26. #include "config.h"
  27. #endif
  28. #include <stdio.h>
  29. #include <stdlib.h>
  30. #ifdef HAVE_UNISTD_H
  31. #include <unistd.h>
  32. #endif
  33. #include <errno.h>
  34. #include <string.h>
  35. #include <pixman.h>
  36. #include "cairo-test.h"
  37. #include "pdiff.h"
  38. #include "buffer-diff.h"
  39. /* Don't allow any differences greater than this value, even if pdiff
  40. * claims that the images are identical */
  41. #define PERCEPTUAL_DIFF_THRESHOLD 25
  42. /* Compare two buffers, returning the number of pixels that are
  43. * different and the maximum difference of any single color channel in
  44. * result_ret.
  45. *
  46. * This function should be rewritten to compare all formats supported by
  47. * cairo_format_t instead of taking a mask as a parameter.
  48. */
  49. static void
  50. buffer_diff_core (const unsigned char *_buf_a, int stride_a,
  51. const unsigned char *_buf_b, int stride_b,
  52. unsigned char *_buf_diff, int stride_diff,
  53. int width,
  54. int height,
  55. uint32_t mask,
  56. buffer_diff_result_t *result_ret)
  57. {
  58. const uint32_t *buf_a = (const uint32_t*) _buf_a;
  59. const uint32_t *buf_b = (const uint32_t*) _buf_b;
  60. uint32_t *buf_diff = (uint32_t*) _buf_diff;
  61. int x, y;
  62. buffer_diff_result_t result = {0, 0};
  63. stride_a /= sizeof (uint32_t);
  64. stride_b /= sizeof (uint32_t);
  65. stride_diff /= sizeof (uint32_t);
  66. for (y = 0; y < height; y++) {
  67. const uint32_t *row_a = buf_a + y * stride_a;
  68. const uint32_t *row_b = buf_b + y * stride_b;
  69. uint32_t *row = buf_diff + y * stride_diff;
  70. for (x = 0; x < width; x++) {
  71. /* check if the pixels are the same */
  72. if ((row_a[x] & mask) != (row_b[x] & mask)) {
  73. int channel;
  74. uint32_t diff_pixel = 0;
  75. /* calculate a difference value for all 4 channels */
  76. for (channel = 0; channel < 4; channel++) {
  77. int value_a = (row_a[x] >> (channel*8)) & 0xff;
  78. int value_b = (row_b[x] >> (channel*8)) & 0xff;
  79. unsigned int diff;
  80. diff = abs (value_a - value_b);
  81. if (diff > result.max_diff)
  82. result.max_diff = diff;
  83. diff *= 4; /* emphasize */
  84. if (diff)
  85. diff += 128; /* make sure it's visible */
  86. if (diff > 255)
  87. diff = 255;
  88. diff_pixel |= diff << (channel*8);
  89. }
  90. result.pixels_changed++;
  91. if ((diff_pixel & 0x00ffffff) == 0) {
  92. /* alpha only difference, convert to luminance */
  93. uint8_t alpha = diff_pixel >> 24;
  94. diff_pixel = alpha * 0x010101;
  95. }
  96. row[x] = diff_pixel;
  97. } else {
  98. row[x] = 0;
  99. }
  100. row[x] |= 0xff000000; /* Set ALPHA to 100% (opaque) */
  101. }
  102. }
  103. *result_ret = result;
  104. }
  105. /* Compares two image surfaces
  106. *
  107. * Provides number of pixels changed and maximum single-channel
  108. * difference in result.
  109. *
  110. * Also fills in a "diff" surface intended to visually show where the
  111. * images differ.
  112. */
  113. static void
  114. compare_surfaces (const cairo_test_context_t *ctx,
  115. cairo_surface_t *surface_a,
  116. cairo_surface_t *surface_b,
  117. cairo_surface_t *surface_diff,
  118. buffer_diff_result_t *result)
  119. {
  120. /* These default values were taken straight from the
  121. * perceptualdiff program. We'll probably want to tune these as
  122. * necessary. */
  123. double gamma = 2.2;
  124. double luminance = 100.0;
  125. double field_of_view = 45.0;
  126. int discernible_pixels_changed;
  127. /* First, we run cairo's old buffer_diff algorithm which looks for
  128. * pixel-perfect images, (we do this first since the test suite
  129. * runs about 3x slower if we run pdiff_compare first).
  130. */
  131. buffer_diff_core (cairo_image_surface_get_data (surface_a),
  132. cairo_image_surface_get_stride (surface_a),
  133. cairo_image_surface_get_data (surface_b),
  134. cairo_image_surface_get_stride (surface_b),
  135. cairo_image_surface_get_data (surface_diff),
  136. cairo_image_surface_get_stride (surface_diff),
  137. cairo_image_surface_get_width (surface_a),
  138. cairo_image_surface_get_height (surface_a),
  139. cairo_surface_get_content (surface_a) & CAIRO_CONTENT_ALPHA ? 0xffffffff : 0x00ffffff,
  140. result);
  141. if (result->pixels_changed == 0)
  142. return;
  143. cairo_test_log (ctx,
  144. "%d pixels differ (with maximum difference of %d) from reference image\n",
  145. result->pixels_changed, result->max_diff);
  146. /* Then, if there are any different pixels, we give the pdiff code
  147. * a crack at the images. If it decides that there are no visually
  148. * discernible differences in any pixels, then we accept this
  149. * result as good enough.
  150. *
  151. * Only let pdiff have a crack at the comparison if the max difference
  152. * is lower than a threshold, otherwise some problems could be masked.
  153. */
  154. if (result->max_diff < PERCEPTUAL_DIFF_THRESHOLD) {
  155. discernible_pixels_changed = pdiff_compare (surface_a, surface_b,
  156. gamma, luminance, field_of_view);
  157. if (discernible_pixels_changed == 0) {
  158. result->pixels_changed = 0;
  159. cairo_test_log (ctx,
  160. "But perceptual diff finds no visually discernible difference.\n"
  161. "Accepting result.\n");
  162. }
  163. }
  164. }
  165. void
  166. buffer_diff_noalpha (const unsigned char *buf_a,
  167. const unsigned char *buf_b,
  168. unsigned char *buf_diff,
  169. int width,
  170. int height,
  171. int stride,
  172. buffer_diff_result_t *result)
  173. {
  174. buffer_diff_core(buf_a, stride,
  175. buf_b, stride,
  176. buf_diff, stride,
  177. width, height,
  178. 0x00ffffff,
  179. result);
  180. }
  181. static cairo_bool_t
  182. same_size (cairo_surface_t *a, cairo_surface_t *b)
  183. {
  184. unsigned int width_a, height_a;
  185. unsigned int width_b, height_b;
  186. width_a = cairo_image_surface_get_width (a);
  187. height_a = cairo_image_surface_get_height (a);
  188. width_b = cairo_image_surface_get_width (b);
  189. height_b = cairo_image_surface_get_height (b);
  190. return width_a == width_b && height_a == height_b;
  191. }
  192. /* Image comparison code courtesy of Richard Worth <richard@theworths.org>
  193. * Returns number of pixels changed, (or -1 on error).
  194. * Also saves a "diff" image intended to visually show where the
  195. * images differ.
  196. *
  197. * The return value simply indicates whether a check was successfully
  198. * made, (as opposed to a file-not-found condition or similar). It
  199. * does not indicate anything about how much the images differ. For
  200. * that, see result.
  201. *
  202. * One failure mode is if the two images provided do not have the same
  203. * dimensions. In this case, this function will return
  204. * CAIRO_STATUS_SURFACE_TYPE_MISMATCH (which is a bit of an abuse, but
  205. * oh well).
  206. */
  207. cairo_status_t
  208. image_diff (const cairo_test_context_t *ctx,
  209. cairo_surface_t *surface_a,
  210. cairo_surface_t *surface_b,
  211. cairo_surface_t *surface_diff,
  212. buffer_diff_result_t *result)
  213. {
  214. if (cairo_surface_status (surface_a))
  215. return cairo_surface_status (surface_a);
  216. if (cairo_surface_status (surface_b))
  217. return cairo_surface_status (surface_b);
  218. if (cairo_surface_status (surface_diff))
  219. return cairo_surface_status (surface_diff);
  220. if (! same_size (surface_a, surface_b) ||
  221. ! same_size (surface_a, surface_diff))
  222. {
  223. cairo_test_log (ctx, "Error: Image size mismatch\n");
  224. return CAIRO_STATUS_SURFACE_TYPE_MISMATCH;
  225. }
  226. compare_surfaces (ctx, surface_a, surface_b, surface_diff, result);
  227. return CAIRO_STATUS_SUCCESS;
  228. }
  229. cairo_bool_t
  230. image_diff_is_failure (const buffer_diff_result_t *result,
  231. unsigned int tolerance)
  232. {
  233. return result->pixels_changed &&
  234. result->max_diff > tolerance;
  235. }