summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorBen Morss <morss@google.com>2021-03-03 21:35:56 -0500
committerGitHub <noreply@github.com>2021-03-03 21:35:56 -0500
commitf2aa2836ed910ca3510585a47a8a064b5140e148 (patch)
tree0830fdcac9308d28bc8d911b43f4e1ccb7ed1f8d /tests
parente697147788720da40eb3e9c3e17ef385c08191b7 (diff)
downloadlibgd-f2aa2836ed910ca3510585a47a8a064b5140e148.tar.gz
AVIF support (#671)
Demand for AVIF support on the web is growing, as the word gets out about this new file format which allows higher-quality encoding at smaller sizes. Core contributors to major open-source CMSs are interested in auto-generating AVIF images! They've been simply waiting for support to appear in libgd. This PR aims to meet the growing demand, and to help bring smaller, more beautiful images to more of the web - to sites created by experienced developers and CMS users alike. This PR adds support by incorporating libavif in addition to the existing libheif support. It's generally felt that libavif has more complete support for the AVIF format. libavif is also used by the Chromium project and squoosh.app. In this PR, I've endeavored to incorporate the latest research into best practices for AVIF encoding - not just for default quantizer values, but also an algorithm for determining the number of horizontal tiles, vertical tiles, and threads. Fixes #557.
Diffstat (limited to 'tests')
-rw-r--r--tests/CMakeLists.txt1
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/avif/.gitignore5
-rw-r--r--tests/avif/CMakeLists.txt17
-rw-r--r--tests/avif/Makemodule.am16
-rw-r--r--tests/avif/avif_im2im.c67
-rw-r--r--tests/avif/avif_null.c23
-rw-r--r--tests/avif/avif_ptr_double_free.c34
-rw-r--r--tests/avif/baboon.avifbin0 -> 16521 bytes
-rw-r--r--tests/avif/baboon.pngbin0 -> 49030 bytes
-rw-r--r--tests/avif/bad_input.c72
-rw-r--r--tests/avif/compare_avif_to_png.c100
-rw-r--r--tests/avif/dice_with_alpha.avifbin0 -> 6248 bytes
-rw-r--r--tests/avif/dice_with_alpha.pngbin0 -> 48210 bytes
-rw-r--r--tests/avif/plum_blossom_12bit.avifbin0 -> 5202 bytes
-rw-r--r--tests/avif/plum_blossom_12bit.pngbin0 -> 10185 bytes
-rw-r--r--tests/avif/sunset.avifbin0 -> 1174 bytes
-rw-r--r--tests/avif/sunset.pngbin0 -> 47803 bytes
18 files changed, 337 insertions, 0 deletions
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 6775948..b279f63 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -17,6 +17,7 @@ if (BUILD_TEST)
include_directories (BEFORE ${GD_INCLUDE_DIR} "${GDTEST_SOURCE_DIR}" "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}/tests/gdtest")
SET(TESTS_DIRS
+ avif
bmp
fontconfig
freetype
diff --git a/tests/Makefile.am b/tests/Makefile.am
index efbe26a..3630810 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -13,6 +13,7 @@ CLEANFILES =
EXTRA_DIST =
TESTS =
+include avif/Makemodule.am
include bmp/Makemodule.am
include fontconfig/Makemodule.am
include freetype/Makemodule.am
@@ -99,6 +100,7 @@ EXTRA_DIST += \
# We don't keep any media files in the top dir ... just generated outputs.
CLEANFILES += \
+ *.avif \
*.bmp \
*.gd \
*.gd2 \
diff --git a/tests/avif/.gitignore b/tests/avif/.gitignore
new file mode 100644
index 0000000..66233b0
--- /dev/null
+++ b/tests/avif/.gitignore
@@ -0,0 +1,5 @@
+/avif_im2im
+/avif_null
+/avif_ptr_double_free
+/bad_input
+/compare_avif_to_png
diff --git a/tests/avif/CMakeLists.txt b/tests/avif/CMakeLists.txt
new file mode 100644
index 0000000..e2df47b
--- /dev/null
+++ b/tests/avif/CMakeLists.txt
@@ -0,0 +1,17 @@
+IF(AVIF_FOUND)
+LIST(APPEND TESTS_FILES
+ avif_ptr_double_free
+ avif_im2im
+ avif_null
+ bad_input
+)
+
+IF(PNG_FOUND)
+LIST(APPEND TESTS_FILES
+ compare_avif_to_png
+)
+ENDIF(PNG_FOUND)
+
+ENDIF(AVIF_FOUND)
+
+ADD_GD_TESTS()
diff --git a/tests/avif/Makemodule.am b/tests/avif/Makemodule.am
new file mode 100644
index 0000000..b446a93
--- /dev/null
+++ b/tests/avif/Makemodule.am
@@ -0,0 +1,16 @@
+if HAVE_LIBAVIF
+libgd_test_programs += \
+ avif/avif_ptr_double_free
+ avif/avif_im2im
+ avif/avif_null
+ avif/bad_input
+
+if HAVE_LIBPNG
+libgd_test_programs += \
+ avif/compare_avif_to_png
+endif
+
+endif
+
+EXTRA_DIST += \
+ avif/CMakeLists.txt
diff --git a/tests/avif/avif_im2im.c b/tests/avif/avif_im2im.c
new file mode 100644
index 0000000..3a07ebd
--- /dev/null
+++ b/tests/avif/avif_im2im.c
@@ -0,0 +1,67 @@
+/**
+ * File: avif_im2im
+ *
+ * Sanity check for AVIF encoding and decoding.
+ * We create a simple gd image, we encode it to AVIF, and we decode it back to gd.
+ * Then we make sure the image we started with and the image we finish with are the same.
+ *
+ */
+
+#include "gd.h"
+#include "gdtest.h"
+#include <stdio.h>
+
+int main()
+{
+ gdImagePtr srcGdIm, destGdIm;
+ void *avifImageDataPtr;
+ FILE *fp;
+ int r, g, b;
+ int size = 0;
+ CuTestImageResult result = {0, 0};
+
+ // Create new gd image and add some shapes to it.
+ srcGdIm = gdImageCreateTrueColor(100, 100);
+ gdTestAssertMsg(srcGdIm != NULL, "could not create source image\n");
+
+ r = gdImageColorAllocate(srcGdIm, 0xFF, 0, 0);
+ g = gdImageColorAllocate(srcGdIm, 0, 0xFF, 0);
+ b = gdImageColorAllocate(srcGdIm, 0, 0, 0xFF);
+ gdImageFilledRectangle(srcGdIm, 0, 0, 99, 99, r);
+ gdImageRectangle(srcGdIm, 20, 20, 79, 79, g);
+ gdImageEllipse(srcGdIm, 70, 25, 30, 20, b);
+
+ // Encode the gd image to a test AVIF file.
+ fp = gdTestTempFp();
+ gdImageAvif(srcGdIm, fp);
+ fclose(fp);
+
+ // Encode the gd image to an AVIF image in memory.
+ avifImageDataPtr = gdImageAvifPtrEx(srcGdIm, &size, 100, 10);
+ gdTestAssertMsg(avifImageDataPtr != NULL, "gdImageAvifPtr() returned null\n");
+ gdTestAssertMsg(size > 0, "gdImageAvifPtr() returned a non-positive size\n");
+
+ // Encode the AVIF image back into a gd image.
+ destGdIm = gdImageCreateFromAvifPtr(size, avifImageDataPtr);
+ gdTestAssertMsg(destGdIm != NULL, "gdImageAvifPtr() returned null\n");
+
+ // Encode that gd image to a test AVIF file.
+ fp = gdTestTempFp();
+ gdImageAvif(destGdIm, fp);
+ fclose(fp);
+
+ // Make sure the image we started with is the same as the image after two conversions.
+ gdTestImageDiff(srcGdIm, destGdIm, NULL, &result);
+ gdTestAssertMsg(result.pixels_changed == 0, "pixels changed: %d\n", result.pixels_changed);
+
+ if (srcGdIm)
+ gdImageDestroy(srcGdIm);
+
+ if (destGdIm)
+ gdImageDestroy(destGdIm);
+
+ if (avifImageDataPtr)
+ gdFree(avifImageDataPtr);
+
+ return gdNumFailures();
+}
diff --git a/tests/avif/avif_null.c b/tests/avif/avif_null.c
new file mode 100644
index 0000000..bbcc9c6
--- /dev/null
+++ b/tests/avif/avif_null.c
@@ -0,0 +1,23 @@
+/**
+ * File: avif_null.c
+ *
+ * Simple test case, confirming that if you try to create an AVIF image from a
+ * null file pointer, the creation will fail, and it will return NULL.
+ */
+
+#include "gd.h"
+#include "gdtest.h"
+
+
+int main()
+{
+ gdImagePtr im;
+
+ im = gdImageCreateFromAvif(NULL);
+ if (!gdTestAssert(im == NULL))
+ gdImageDestroy(im);
+
+ gdImageAvif(im, NULL); /* noop safely */
+
+ return gdNumFailures();
+}
diff --git a/tests/avif/avif_ptr_double_free.c b/tests/avif/avif_ptr_double_free.c
new file mode 100644
index 0000000..8160950
--- /dev/null
+++ b/tests/avif/avif_ptr_double_free.c
@@ -0,0 +1,34 @@
+/**
+ * Test that failure to convert to AVIF returns NULL
+ *
+ * We are creating an image, set its width to zero, and pass this image to
+ * gdImageAvifPtr().
+ * This is supposed to fail, and as such should return NULL.
+ *
+ * See also <https://github.com/libgd/libgd/issues/381>
+ */
+
+#include "gd.h"
+#include "gdtest.h"
+
+int main()
+{
+ gdImagePtr src, dst;
+ int size;
+
+ src = gdImageCreateTrueColor(1, 10);
+ gdTestAssert(src != NULL);
+
+ src->sx = 0; // making the width 0 should cause gdImageAvifPtr() to fail
+
+ dst = gdImageAvifPtr(src, &size);
+ gdTestAssert(dst == NULL);
+
+ if (src)
+ gdImageDestroy(src);
+
+ if (dst)
+ gdImageDestroy(dst);
+
+ return gdNumFailures();
+}
diff --git a/tests/avif/baboon.avif b/tests/avif/baboon.avif
new file mode 100644
index 0000000..f5821db
--- /dev/null
+++ b/tests/avif/baboon.avif
Binary files differ
diff --git a/tests/avif/baboon.png b/tests/avif/baboon.png
new file mode 100644
index 0000000..fdc5dcb
--- /dev/null
+++ b/tests/avif/baboon.png
Binary files differ
diff --git a/tests/avif/bad_input.c b/tests/avif/bad_input.c
new file mode 100644
index 0000000..a9f976b
--- /dev/null
+++ b/tests/avif/bad_input.c
@@ -0,0 +1,72 @@
+/**
+ * File: bad_input.c
+ *
+ * Make sure that the AVIF encoding and decoding functions handle bad input gracefully.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "gd.h"
+#include "gdtest.h"
+
+#define PATH "avif/"
+#define MAX_FILEPATH_LENGTH 50
+
+#define NON_AVIF_FILE_NAME "sunset.png"
+#define AVIF_FILE_NAME "sunset.avif"
+
+int main() {
+ FILE *fp;
+ int retval;
+ char nonAvifFilePath[MAX_FILEPATH_LENGTH], avifFilePath[MAX_FILEPATH_LENGTH];
+ gdImagePtr realIm, badIm;
+ void *rv;
+ int size;
+
+// Create paths for our files.
+ strcpy(avifFilePath, PATH);
+ strcat(avifFilePath, AVIF_FILE_NAME);
+
+ strcpy(nonAvifFilePath, PATH);
+ strcat(nonAvifFilePath, NON_AVIF_FILE_NAME);
+
+// Read in an AVIF image for testing.
+
+ fp = gdTestFileOpen(avifFilePath);
+ realIm = gdImageCreateFromAvif(fp);
+ fclose(fp);
+ if (!gdTestAssertMsg(realIm != NULL, "gdImageCreateFromAvif() failed\n"))
+ return 1;
+
+// Try to decode a non-AVIF file.
+
+ fp = gdTestFileOpen(nonAvifFilePath);
+ badIm = gdImageCreateFromAvif(fp);
+ fclose(fp);
+ gdTestAssertMsg(badIm == NULL, "gdImageCreateFromAvif() failed to return NULL when passed a non-AVIF file\n");
+
+ if (badIm)
+ gdImageDestroy(badIm);
+
+ // Try to encode a valid image with bad quality parameters. This should still work.
+
+ rv = gdImageAvifPtrEx(realIm, &size, 400, 10);
+ gdTestAssertMsg(rv != NULL, "gdImageAvifPtrEx() rejected an overly high quality param instead of clamping it to a valid value");
+ gdFree(rv);
+
+ rv = gdImageAvifPtrEx(realIm, &size, -4, 10);
+ gdTestAssertMsg(rv != NULL, "gdImageAvifPtrEx() rejected a negative quality param instead of clamping it to a valid value");
+ gdFree(rv);
+
+ rv = gdImageAvifPtrEx(realIm, &size, 30, 30);
+ gdTestAssertMsg(rv != NULL, "gdImageAvifPtrEx() rejected an overly high speed param instead of clamping it to a valid value");
+ gdFree(rv);
+
+ rv = gdImageAvifPtrEx(realIm, &size, 30, -4);
+ gdTestAssertMsg(rv != NULL, "gdImageAvifPtrEx() rejected a negative speed param instead of clamping it to a valid value");
+ gdFree(rv);
+
+ gdImageDestroy(realIm);
+
+ return gdNumFailures();
+}
diff --git a/tests/avif/compare_avif_to_png.c b/tests/avif/compare_avif_to_png.c
new file mode 100644
index 0000000..716eae9
--- /dev/null
+++ b/tests/avif/compare_avif_to_png.c
@@ -0,0 +1,100 @@
+/**
+ * File: compare_avif_to_png
+ *
+ * Thorough check for AVIF encoding and decoding.
+ * This test reqiures a set of PNG images that have been losslessly encoded to AVIFs.
+ * For each such image, we encode the PNG into an AVIF, with the GD format as an intermediary,
+ * then compare the resulting AVIF with the original PNG.
+ *
+ * We then do this process in reverse, encoding the AVIF into a PNG,
+ * and compare the resulting PNG with the original AVIF.
+ *
+ * We report any discrepancies in the images, or any other errors that may occur.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "gd.h"
+#include "gdtest.h"
+
+#define PATH "avif/"
+#define MAX_FILEPATH_LENGTH 200
+
+int main() {
+ FILE *fp;
+ gdImagePtr imFromPng = NULL, imFromAvif = NULL;
+ void *avifImDataPtr = NULL, *pngImDataPtr = NULL;
+ int size;
+ char filePath[MAX_FILEPATH_LENGTH], pngFilePath[MAX_FILEPATH_LENGTH], avifFilePath[MAX_FILEPATH_LENGTH];
+ char errMsg[MAX_FILEPATH_LENGTH + 100];
+
+ const int filesCount = 4;
+ const char *filenames[filesCount] = {"baboon", "dice_with_alpha", "plum_blossom_12bit", "sunset"};
+
+ for (int i = 0; i < filesCount; i++) {
+
+ // First, encode each PNG into an AVIF (with the GD format as an intermediary),
+ // then compare the result with the original PNG.
+
+ strcpy(filePath, PATH);
+ strcat(filePath, filenames[i]);
+ strcat(strcpy(pngFilePath, filePath), ".png");
+ strcat(strcpy(avifFilePath, filePath), ".avif");
+
+ fp = gdTestFileOpen(pngFilePath);
+ imFromPng = gdImageCreateFromPng(fp);
+ fclose(fp);
+
+ strcat(strcpy(errMsg, filenames[i]), ".png: gdImageCreateFromPng failed\n");
+ if (!gdTestAssertMsg(imFromPng != NULL, errMsg))
+ goto avif2png;
+
+ strcat(strcpy(errMsg, filenames[i]), ": gdImageAvifPtrEx failed\n");
+ avifImDataPtr = gdImageAvifPtrEx(imFromPng, &size, 100, 0);
+ if (!gdTestAssertMsg(avifImDataPtr != NULL, errMsg))
+ goto avif2png;
+
+ strcat(strcpy(errMsg, filenames[i]), ": gdImageCreateFromAvifPtr failed\n");
+ imFromAvif = gdImageCreateFromAvifPtr(size, avifImDataPtr);
+ if (!gdTestAssertMsg(imFromAvif != NULL, errMsg))
+ goto avif2png;
+
+ strcat(strcpy(errMsg, filenames[i]), ".png: Encoded AVIF image did not match original PNG\n");
+ gdTestAssertMsg(gdAssertImageEquals(imFromPng, imFromAvif), errMsg);
+
+ // Then, decode each AVIF into a GD format, and compare that with the orginal PNG.
+
+avif2png:
+ continue;
+
+/* Skip this reverse test for now, until we can find images that encode to PNGs
+ losslessly.
+
+ fp = gdTestFileOpen(avifFilePath);
+ imFromAvif = gdImageCreateFromAvif(fp);
+ fclose(fp);
+
+ strcat(strcpy(errMsg, filenames[i]), ".avif: gdImageCreateFromAvif failed\n");
+ if (!gdTestAssertMsg(imFromAvif != NULL, errMsg))
+ continue;
+
+ strcat(strcpy(errMsg, filenames[i]), ".avif: Encoded PNG image did not match original AVIF\n");
+ gdTestAssertMsg(gdAssertImageEqualsToFile(pngFilePath, imFromAvif), errMsg);
+*/
+
+}
+
+ if (imFromPng)
+ gdImageDestroy(imFromPng);
+
+ if (imFromAvif)
+ gdImageDestroy(imFromAvif);
+
+ if (avifImDataPtr)
+ gdFree(avifImDataPtr);
+
+ if (pngImDataPtr)
+ gdFree(pngImDataPtr);
+
+ return gdNumFailures();
+}
diff --git a/tests/avif/dice_with_alpha.avif b/tests/avif/dice_with_alpha.avif
new file mode 100644
index 0000000..dce625b
--- /dev/null
+++ b/tests/avif/dice_with_alpha.avif
Binary files differ
diff --git a/tests/avif/dice_with_alpha.png b/tests/avif/dice_with_alpha.png
new file mode 100644
index 0000000..d2809d1
--- /dev/null
+++ b/tests/avif/dice_with_alpha.png
Binary files differ
diff --git a/tests/avif/plum_blossom_12bit.avif b/tests/avif/plum_blossom_12bit.avif
new file mode 100644
index 0000000..7959d15
--- /dev/null
+++ b/tests/avif/plum_blossom_12bit.avif
Binary files differ
diff --git a/tests/avif/plum_blossom_12bit.png b/tests/avif/plum_blossom_12bit.png
new file mode 100644
index 0000000..e6901e2
--- /dev/null
+++ b/tests/avif/plum_blossom_12bit.png
Binary files differ
diff --git a/tests/avif/sunset.avif b/tests/avif/sunset.avif
new file mode 100644
index 0000000..4964558
--- /dev/null
+++ b/tests/avif/sunset.avif
Binary files differ
diff --git a/tests/avif/sunset.png b/tests/avif/sunset.png
new file mode 100644
index 0000000..21f45d5
--- /dev/null
+++ b/tests/avif/sunset.png
Binary files differ