diff options
author | Martin Guy <martinwguy@gmail.com> | 2016-12-01 13:30:48 +0100 |
---|---|---|
committer | Bastien Nocera <hadess@hadess.net> | 2016-12-19 17:27:01 +0100 |
commit | ecea71eb64944f141d4ce001d2d12bb86b35b053 (patch) | |
tree | 95b84c0826da39983e1a4884a5e9fb5a2d0d4797 | |
parent | c7b58acd3b157512407bc85497cd9cd2da40ab3c (diff) | |
download | gdk-pixbuf-ecea71eb64944f141d4ce001d2d12bb86b35b053.tar.gz |
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
-rw-r--r-- | 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 239fa4490..8a3f8f5ae 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 (); } |