[gdk-pixbuf] tests: Add rudimentary correctness tests for scaler



commit ecea71eb64944f141d4ce001d2d12bb86b35b053
Author: Martin Guy <martinwguy gmail com>
Date:   Thu Dec 1 13:30:48 2016 +0100

    tests: Add rudimentary correctness tests for scaler
    
    The scaler's testsuite has no checks for correctness of the scaled image.
    
    This add some rudimentary ones, scaling a checkerboard to 1/2 size and
    seeing if it's all gray and checking the "dest" and "offset" parameters.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=80925

 tests/pixbuf-scale.c |  288 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 288 insertions(+), 0 deletions(-)
---
diff --git a/tests/pixbuf-scale.c b/tests/pixbuf-scale.c
index 239fa44..8a3f8f5 100644
--- a/tests/pixbuf-scale.c
+++ b/tests/pixbuf-scale.c
@@ -169,9 +169,282 @@ test_rotate (gconstpointer data)
   g_object_unref (ref);
 }
 
+/* Test images creation functions */
+
+/* Checkerboard of black and white pxels (actually (1,1,1) and (255,255,255)
+ * so they average to (128,128,128)) */
+static GdkPixbuf *
+make_checkerboard (int width, int height)
+{
+  GdkPixbuf *checkerboard;
+  guint x, y;
+  guchar *row;   /* Pointer to start of row of pixels within the image */
+  guchar *pixel; /* Pointer to current pixel data in row */
+
+  checkerboard = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 0, 8, width, height);
+  g_assert_nonnull (checkerboard);
+
+  for (y = 0, row = gdk_pixbuf_get_pixels (checkerboard);
+       y < height;
+       y++, row += gdk_pixbuf_get_rowstride (checkerboard))
+    {
+      for (x = 0, pixel = row;
+           x < width;
+           x++, pixel += gdk_pixbuf_get_n_channels (checkerboard))
+        {
+          pixel[0] = pixel[1] = pixel[2] = (x ^ y) & 1 ? 1 : 255;
+        }
+    }
+
+  return checkerboard;
+}
+
+/* Image where all the pixels have different colours */
+static GdkPixbuf *
+make_rg (int width, int height)
+{
+  GdkPixbuf *pixbuf;
+  guint x, y;
+  guchar *row;   /* Pointer to start of row of pixels within the image */
+  guchar *pixel; /* Pointer to current pixel data in row */
+
+  /* Make a source image whose pixels are all of different colors */
+  pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 0, 8, width, height);
+  g_assert_nonnull (pixbuf);
+
+  for (y = 0, row = gdk_pixbuf_get_pixels (pixbuf);
+       y < height;
+       y++, row += gdk_pixbuf_get_rowstride (pixbuf))
+    {
+      for (x = 0, pixel = row;
+           x < width;
+           x++, pixel += gdk_pixbuf_get_n_channels (pixbuf))
+        {
+          pixel[0] = x & 255; pixel[1] = y & 255;
+          /* If image > 256 pixels wide/high put the extra bits in the last pixel */
+          pixel[2] = ((x >> 8) & 15) | ((( y >> 8) & 15) << 4);
+        }
+    }
+
+  return pixbuf;
+}
+
+/* Create a 256x256 checkerboard of (1,1,1) and (255,255,255) pixels,
+ * scale it to exactly half size and check that all pixels are (128,128,128). */
+static void
+test_halve_checkerboard (gconstpointer data)
+{
+  GdkInterpType interp_type = *(GdkInterpType *) data;
+  const GdkPixbuf *source;              /* Source image */
+  gint width = 256, height = 256;       /* Size of source image */
+  GdkPixbuf *scaled;                    /* Scaled version */
+  guchar *row;                          /* Pointer to start of row of pixels within the image */
+  guchar *pixel;                        /* Pointer to current pixel data in row */
+  guint x, y;
+  guchar expected;                      /* Expected color of all pixels */
+
+  expected = (interp_type == GDK_INTERP_NEAREST) ? 255 : 128;
+
+  source = make_checkerboard (width, height);
+
+  scaled = gdk_pixbuf_scale_simple (source, width / 2, height / 2, interp_type);
+
+  /* Check that the result is all gray (or all white in the case of NEAREST) */
+  for (y = 0, row = gdk_pixbuf_get_pixels (scaled);
+       y < gdk_pixbuf_get_height (scaled);
+       y++, row += gdk_pixbuf_get_rowstride (scaled))
+    {
+      for (x = 0, pixel = row;
+           x < gdk_pixbuf_get_width (scaled);
+           x++, pixel += gdk_pixbuf_get_n_channels (scaled))
+        {
+          if (!(pixel[0] == expected && pixel[1] == expected && pixel[2] == expected))
+            {
+              /* Expected failure: HYPER has a different opinion about the color
+               * of the corner pixels: (126,126,126) and (130,130,130) */
+              if (interp_type == GDK_INTERP_HYPER &&
+                  (x == 0 || x == gdk_pixbuf_get_width (scaled) - 1) &&
+                  (y == 0 || y == gdk_pixbuf_get_height (scaled) - 1))
+                {
+                  continue;
+                }
+              g_test_fail ();
+            }
+        }
+    }
+
+  g_object_unref (G_OBJECT (scaled));
+  g_object_unref (G_OBJECT (source));
+}
+
+/* Crop a region out of a source image using subpixbuf() and using the scaler,
+ * and check that the results are the same */
+static void
+crop_n_compare (const GdkPixbuf *source,
+                gint             offset_x,
+                gint             offset_y,
+                guint            width,
+                guint            height,
+                GdkInterpType    interp_type)
+{
+  GdkPixbuf *cropped, *scaled;
+  guchar *crow, *srow;          /* Pointer to current row in image data */
+  guchar *cpixel, *spixel;      /* Pointer to current pixel in row */
+  guint x, y;
+
+  cropped = gdk_pixbuf_new_subpixbuf ((GdkPixbuf *)source, offset_x, offset_y, width, height);
+  g_assert_nonnull (cropped);
+
+  scaled = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 0, 8, width, height);
+  g_assert_nonnull (scaled);
+  gdk_pixbuf_scale (source, scaled,
+                    0, 0,                             /* dest_[xy] */
+                    width, height,                    /* dest_width/height */
+                    -1.0 * offset_x, -1.0 * offset_y, /* offset_[xy] */
+                    1.0, 1.0,                         /* scale_[xy] */
+                    interp_type);
+
+  for (y = 0, crow = gdk_pixbuf_get_pixels (cropped),
+              srow = gdk_pixbuf_get_pixels (scaled);
+       y < gdk_pixbuf_get_height (scaled);
+       y++, crow += gdk_pixbuf_get_rowstride (cropped),
+            srow += gdk_pixbuf_get_rowstride (scaled))
+    {
+      for (x = 0, cpixel = crow, spixel = srow;
+           x < gdk_pixbuf_get_width (scaled);
+           x++, cpixel += gdk_pixbuf_get_n_channels (cropped),
+                spixel += gdk_pixbuf_get_n_channels (scaled))
+        {
+          if (!(spixel[0] == cpixel[0] &&
+                spixel[1] == cpixel[1] &&
+                spixel[2] == cpixel[2]))
+            {
+              /* Expected failure: HYPER has a different opinion about the
+               * colors of the edge pixels */
+              if (interp_type == GDK_INTERP_HYPER &&
+                  ((x == 0 || x == gdk_pixbuf_get_width (scaled) - 1) ||
+                   (y == 0 || y == gdk_pixbuf_get_height (scaled) - 1)))
+                {
+                  continue;
+                }
+              g_test_fail();
+            }
+        }
+    }
+
+  g_object_unref (G_OBJECT (cropped));
+  g_object_unref (G_OBJECT (scaled));
+}
+
+/* Check that offsets work.
+ * We should be able to copy a region of an image using the scaler using
+ * negative offsets. */
+static void
+test_offset (gconstpointer data)
+{
+  GdkInterpType interp_type = *(GdkInterpType *) data;
+  gint width = 256, height = 256;
+  const GdkPixbuf *source;  /* Source image */
+
+  source = make_rg (width, height);
+
+  /* Check that the scaler correctly crops out an image half the size of the
+   * original from each corner and from the middle */
+  crop_n_compare (source, 0,         0,          width / 2, height / 2, interp_type);
+  crop_n_compare (source, width / 2, 0,          width / 2, height / 2, interp_type);
+  crop_n_compare (source, 0,         height / 2, width / 2, height / 2, interp_type);
+  crop_n_compare (source, width / 2, height / 2, width / 2, height / 2, interp_type);
+  crop_n_compare (source, width / 4, height / 4, width / 2, height / 2, interp_type);
+
+  g_object_unref (G_OBJECT (source));
+}
+
+/* Test the dest_x and dest_y fields by making a copy of an image by
+ * scaling 1:1 and using dest to copy the four quadrants separately.
+ *
+ * When scaled, images are:
+ * 1) scaled by the scale factors with respect to the top-left corner
+ * 2) translated by the offsets (negative to shift the image left/up in its
+ *    frame)
+ * 3) a region of size dest_width x dest-height starting at (dest_x,dest_y)
+ *    in the scaled-and-offset image is copied to (dest_x,dest_y) in the
+ *    destination image. See the illustration at
+ *    https://developer.gnome.org/gdk-pixbuf/2.22/gdk-pixbuf-scaling.html#gdk-pixbuf-composite */
+static void
+test_dest (gconstpointer data)
+{
+  GdkInterpType interp_type = *(GdkInterpType *) data;
+  gint width = 256, height = 256;
+  const GdkPixbuf *source;  /* Source image */
+  GdkPixbuf *copy;          /* Destination image */
+  gint x, y;
+  guchar *srow, *crow;     /* Pointer to current row in source and copy data */
+  guchar *spixel, *cpixel;  /* Pointer to current pixel in row */
+
+  source = make_rg (width, height);
+
+  copy = gdk_pixbuf_new (GDK_COLORSPACE_RGB, 0, 8, width, height);
+  g_assert_nonnull (copy);
+
+  /* Copy the four quadrants with a no-op scale */
+  gdk_pixbuf_scale ((const GdkPixbuf *)source, copy,
+                    0, 0,                  /* dest_[xy] */
+                    width / 2, height / 2, /* dest_width/height */
+                    0.0, 0.0,              /* offset_[xy] */
+                    1.0, 1.0,              /* scale_[xy] */
+                    interp_type);
+  gdk_pixbuf_scale ((const GdkPixbuf *)source, copy,
+                    width / 2, 0,          /* dest_[xy] */
+                    width / 2, height / 2, /* dest_width/height */
+                    0.0, 0.0,              /* offset_[xy] */
+                    1.0, 1.0,              /* scale_[xy] */
+                    interp_type);
+  gdk_pixbuf_scale ((const GdkPixbuf *)source, copy,
+                    0, height / 2,         /* dest_[xy] */
+                    width / 2, height / 2, /* dest_width/height */
+                    0.0, 0.0,              /* offset_[xy] */
+                    1.0, 1.0,              /* scale_[xy] */
+                    interp_type);
+  gdk_pixbuf_scale ((const GdkPixbuf *)source, copy,
+                    width / 2, height / 2, /* dest_[xy] */
+                    width / 2, height / 2, /* dest_width/height */
+                    0.0, 0.0,              /* offset_[xy] */
+                    1.0, 1.0,              /* scale_[xy] */
+                    interp_type);
+
+  /* Compare the original and the copy */
+  for (y = 0, srow = gdk_pixbuf_get_pixels (source),
+              crow = gdk_pixbuf_get_pixels (copy);
+       y < gdk_pixbuf_get_height (source);
+       y++, srow += gdk_pixbuf_get_rowstride (source),
+            crow += gdk_pixbuf_get_rowstride (copy))
+    {
+      for (x = 0, spixel = srow, cpixel = crow;
+           x < gdk_pixbuf_get_width (source);
+           x++, spixel += gdk_pixbuf_get_n_channels (source),
+                cpixel += gdk_pixbuf_get_n_channels (copy))
+        {
+          if (!(spixel[0] == cpixel[0] &&
+                spixel[1] == cpixel[1] &&
+                spixel[2] == cpixel[2]))
+            {
+              g_test_fail();
+            }
+        }
+    }
+
+  g_object_unref (G_OBJECT (source));
+  g_object_unref (G_OBJECT (copy));
+}
+
 int
 main (int argc, char **argv)
 {
+  GdkInterpType nearest = GDK_INTERP_NEAREST;
+  GdkInterpType tiles = GDK_INTERP_TILES;
+  GdkInterpType bilinear = GDK_INTERP_BILINEAR;
+  GdkInterpType hyper = GDK_INTERP_HYPER;
+
   g_test_init (&argc, &argv, NULL);
 
   g_test_add_data_func ("/pixbuf/scale/png", "test-images/randomly-modified/valid.1.png", test_scale);
@@ -189,5 +462,20 @@ main (int argc, char **argv)
       g_test_add_data_func ("/pixbuf/rotate/large", "large.png", test_rotate);
     }
 
+  g_test_add_data_func ("/pixbuf/scale/halve-checkerboard/nearest", &nearest, test_halve_checkerboard);
+  g_test_add_data_func ("/pixbuf/scale/halve-checkerboard/tiles", &tiles, test_halve_checkerboard);
+  g_test_add_data_func ("/pixbuf/scale/halve-checkerboard/bilinear", &bilinear, test_halve_checkerboard);
+  g_test_add_data_func ("/pixbuf/scale/halve-checkerboard/hyper", &hyper, test_halve_checkerboard);
+
+  g_test_add_data_func ("/pixbuf/scale/offset/nearest", &nearest, test_offset);
+  g_test_add_data_func ("/pixbuf/scale/offset/tiles", &tiles, test_offset);
+  g_test_add_data_func ("/pixbuf/scale/offset/bilinear", &bilinear, test_offset);
+  g_test_add_data_func ("/pixbuf/scale/offset/hyper", &hyper, test_offset);
+
+  g_test_add_data_func ("/pixbuf/scale/dest/nearest", &nearest, test_dest);
+  g_test_add_data_func ("/pixbuf/scale/dest/tiles", &tiles, test_dest);
+  g_test_add_data_func ("/pixbuf/scale/dest/bilinear", &bilinear, test_dest);
+  /* Don't bother with hyper as it changes the edge pixels */
+
   return g_test_run ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]