avifdec: Add PNG compression level arg

Fixes: #706
diff --git a/apps/avifdec.c b/apps/avifdec.c
index 39273fb..eb7d93b 100644
--- a/apps/avifdec.c
+++ b/apps/avifdec.c
@@ -33,6 +33,7 @@
     printf("    -c,--codec C      : AV1 codec to use (choose from versions list below)\n");
     printf("    -d,--depth D      : Output depth [8,16]. (PNG only; For y4m, depth is retained, and JPEG is always 8bpc)\n");
     printf("    -q,--quality Q    : Output quality [0-100]. (JPEG only, default: %d)\n", DEFAULT_JPEG_QUALITY);
+    printf("    --png-compress L  : Set PNG compression level (PNG only; 0-9, 0=none, 9=max). Defaults to libpng's builtin default.\n");
     printf("    -u,--upsampling U : Chroma upsampling (for 420/422). automatic (default), fastest, best, nearest, or bilinear\n");
     printf("    -r,--raw-color    : Output raw RGB values instead of multiplying by alpha when saving to opaque formats\n");
     printf("                        (JPEG only; not applicable to y4m)\n");
@@ -53,6 +54,7 @@
     int requestedDepth = 0;
     int jobs = 1;
     int jpegQuality = DEFAULT_JPEG_QUALITY;
+    int pngCompressionLevel = -1; // -1 is a sentinel to avifPNGWrite() to skip calling png_set_compression_level()
     avifCodecChoice codecChoice = AVIF_CODEC_CHOICE_AUTO;
     avifBool infoOnly = AVIF_FALSE;
     avifChromaUpsampling chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC;
@@ -115,6 +117,14 @@
             } else if (jpegQuality > 100) {
                 jpegQuality = 100;
             }
+        } else if (!strcmp(arg, "--png-compress")) {
+            NEXTARG();
+            pngCompressionLevel = atoi(arg);
+            if (pngCompressionLevel < 0) {
+                pngCompressionLevel = 0;
+            } else if (pngCompressionLevel > 9) {
+                pngCompressionLevel = 9;
+            }
         } else if (!strcmp(arg, "-u") || !strcmp(arg, "--upsampling")) {
             NEXTARG();
             if (!strcmp(arg, "automatic")) {
@@ -291,7 +301,7 @@
             returnCode = 1;
         }
     } else if (outputFormat == AVIF_APP_FILE_FORMAT_PNG) {
-        if (!avifPNGWrite(outputFilename, decoder->image, requestedDepth, chromaUpsampling)) {
+        if (!avifPNGWrite(outputFilename, decoder->image, requestedDepth, chromaUpsampling, pngCompressionLevel)) {
             returnCode = 1;
         }
     } else {
diff --git a/apps/shared/avifpng.c b/apps/shared/avifpng.c
index 5af38ea..21a0557 100644
--- a/apps/shared/avifpng.c
+++ b/apps/shared/avifpng.c
@@ -121,7 +121,8 @@
     avif->yuvFormat = requestedFormat;
     if (avif->yuvFormat == AVIF_PIXEL_FORMAT_NONE) {
         // Identity is only valid with YUV444.
-        avif->yuvFormat = (avif->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) ? AVIF_PIXEL_FORMAT_YUV444 : AVIF_APP_DEFAULT_PIXEL_FORMAT;
+        avif->yuvFormat = (avif->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) ? AVIF_PIXEL_FORMAT_YUV444
+                                                                                          : AVIF_APP_DEFAULT_PIXEL_FORMAT;
     }
     avif->depth = requestedDepth;
     if (avif->depth == 0) {
@@ -160,7 +161,7 @@
     return readResult;
 }
 
-avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint32_t requestedDepth, avifChromaUpsampling chromaUpsampling)
+avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint32_t requestedDepth, avifChromaUpsampling chromaUpsampling, int compressionLevel)
 {
     volatile avifBool writeResult = AVIF_FALSE;
     png_structp png = NULL;
@@ -217,6 +218,10 @@
     // It is up to the enduser to decide if they want to keep their ICC profiles or not.
     png_set_option(png, PNG_SKIP_sRGB_CHECK_PROFILE, 1);
 
+    if (compressionLevel >= 0) {
+        png_set_compression_level(png, compressionLevel);
+    }
+
     png_set_IHDR(png, info, avif->width, avif->height, rgb.depth, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
     if (avif->icc.data && (avif->icc.size > 0)) {
         png_set_iCCP(png, info, "libavif", 0, avif->icc.data, (png_uint_32)avif->icc.size);
diff --git a/apps/shared/avifpng.h b/apps/shared/avifpng.h
index 45c7f75..d3dba8e 100644
--- a/apps/shared/avifpng.h
+++ b/apps/shared/avifpng.h
@@ -8,6 +8,10 @@
 
 // if (requestedDepth == 0), do best-fit
 avifBool avifPNGRead(const char * inputFilename, avifImage * avif, avifPixelFormat requestedFormat, uint32_t requestedDepth, uint32_t * outPNGDepth);
-avifBool avifPNGWrite(const char * outputFilename, const avifImage * avif, uint32_t requestedDepth, avifChromaUpsampling chromaUpsampling);
+avifBool avifPNGWrite(const char * outputFilename,
+                      const avifImage * avif,
+                      uint32_t requestedDepth,
+                      avifChromaUpsampling chromaUpsampling,
+                      int compressionLevel);
 
 #endif // ifndef LIBAVIF_APPS_SHARED_AVIFPNG_H