Repository: shssoichiro/oxipng (language: rust)

Issue:
`oxipng` keeps iCCP chunk when making PNGs greyscale, resulting in warnings from libpng
1. Get a greyscale PNG encoded using RGB and saved with an `iCCP` chunk. For example, the below image:

![Image](https://github.com/user-attachments/assets/552cdfd4-9565-4fe4-bcd2-d310e9199be1)

2. Open that image with a program that uses libpng (for example, `cwebp` or `cjxl`). No warnings will show.
3. Run `oxipng` on that image.
4. Open again the image with a problem that uses libpng. Now a warning will show:

```
libpng warning: iCCP: profile 'icc': 'RGB ': RGB color space not permitted on grayscale PNG
```

Most programs will not be bothered by this warning, but some will (I encountered this because [`cjpegli` refuses to compress an image if it encounters this error](https://github.com/google/jpegli/blob/9e21f7e38283fe0837be763e02d97b7f4988831f/third_party/apngdis/dec.cc#L1107-L1109) (I have verified that replacing this `return` with a `continue` solves my problem). Though, strangely, despite [having the same code](https://github.com/libjxl/libjxl/blob/b3fc5f67b21c52a3a0cd24242ffabde4e79c018d/lib/extras/dec/apng.cc#L1107-L1109) `cjxl` only prints the warning and does not error, and I’m not sure why).

I’m not sure what the best solution is here. Maybe libpng is wrong, or `cjpegli` is wrong.


Discussion / hints:
Thanks for reporting this great catch! The [PNG specification](https://www.w3.org/TR/png-3/#11iCCP) states the following on this matter:

> [...] The color space of the ICC profile shall be an RGB color space for color images ([color types](https://www.w3.org/TR/png-3/#3colourType) 2, 3, and 6), or a greyscale color space for [greyscale](https://www.w3.org/TR/png-3/#dfn-greyscale) images ([color types](https://www.w3.org/TR/png-3/#3colourType) 0 and 4).

Therefore, Oxipng should never generate grayscale images containing an `iCCP` chunk with an RGB color profile, which currently it may do as it just copies the `iCCP` chunk around. I think this is a bug in Oxipng that should be addressed.

A naive solution for Oxipng would be to drop the `iCCP` chunk when reducing an image from RGB to grayscale. However, this would remove potentially useful gamma information needed to display the image with the intended brightness and color correction. A better, albeit more complex, approach would be to convert the profile to its grayscale equivalent.

In the meantime, you can work around this issue by disabling color type reductions, or allowing Oxipng to strip all chunks, including `iCCP`, by using the `--strip all` option.

Fix patch:
diff --git a/src/headers.rs b/src/headers.rs
index cf447ed0..2bfbda11 100644
--- a/src/headers.rs
+++ b/src/headers.rs
@@ -1,5 +1,5 @@
 use indexmap::IndexSet;
-use log::warn;
+use log::{debug, trace, warn};
 use rgb::{RGB16, RGBA8};
 
 use crate::{
@@ -8,7 +8,7 @@ use crate::{
     display_chunks::DISPLAY_CHUNKS,
     error::PngError,
     interlace::Interlacing,
-    Deflaters, PngResult,
+    Deflaters, Options, PngResult,
 };
 
 #[derive(Debug, Clone)]
@@ -314,3 +314,97 @@ pub fn srgb_rendering_intent(icc_data: &[u8]) -> Option<u8> {
         _ => None,
     }
 }
+
+/// Process aux chunks and potentially adjust options before optimizing
+pub fn preprocess_chunks(aux_chunks: &mut Vec<Chunk>, opts: &mut Options) {
+    let has_srgb = aux_chunks.iter().any(|c| &c.name == b"sRGB");
+    // Grayscale conversion should not be performed if the image is not in the sRGB colorspace
+    // An sRGB profile would need to be stripped on conversion, so disallow if stripping is disabled
+    let mut allow_grayscale = !has_srgb || opts.strip != StripChunks::None;
+
+    if let Some(iccp_idx) = aux_chunks.iter().position(|c| &c.name == b"iCCP") {
+        allow_grayscale = false;
+        // See if we can replace an iCCP chunk with an sRGB chunk
+        let may_replace_iccp = opts.strip != StripChunks::None && opts.strip.keep(b"sRGB");
+        if may_replace_iccp && has_srgb {
+            // Files aren't supposed to have both chunks, so we chose to honor sRGB
+            trace!("Removing iCCP chunk due to conflict with sRGB chunk");
+            aux_chunks.remove(iccp_idx);
+            allow_grayscale = true;
+        } else if let Some(icc) = extract_icc(&aux_chunks[iccp_idx]) {
+            let intent = if may_replace_iccp {
+                srgb_rendering_intent(&icc)
+            } else {
+                None
+            };
+            // sRGB-like profile can be replaced with an sRGB chunk with the same rendering intent
+            if let Some(intent) = intent {
+                trace!("Replacing iCCP chunk with equivalent sRGB chunk");
+                aux_chunks[iccp_idx] = Chunk {
+                    name: *b"sRGB",
+                    data: vec![intent],
+                };
+                allow_grayscale = true;
+            } else if opts.idat_recoding {
+                // Try recompressing the profile
+                let cur_len = aux_chunks[iccp_idx].data.len();
+                if let Ok(iccp) = make_iccp(&icc, opts.deflate, Some(cur_len - 1)) {
+                    debug!(
+                        "Recompressed iCCP chunk: {} ({} bytes decrease)",
+                        iccp.data.len(),
+                        cur_len - iccp.data.len()
+                    );
+                    aux_chunks[iccp_idx] = iccp;
+                }
+            }
+        }
+    }
+
+    if !allow_grayscale && opts.grayscale_reduction {
+        debug!("Disabling grayscale reduction due to presence of sRGB or iCCP chunk");
+        opts.grayscale_reduction = false;
+    }
+
+    // Check for APNG by presence of acTL chunk
+    if aux_chunks.iter().any(|c| &c.name == b"acTL") {
+        warn!("APNG detected, disabling all reductions");
+        opts.interlace = None;
+        opts.bit_depth_reduction = false;
+        opts.color_type_reduction = false;
+        opts.palette_reduction = false;
+        opts.grayscale_reduction = false;
+    }
+}
+
+/// Perform cleanup of certain aux chunks after optimization has been completed
+pub fn postprocess_chunks(aux_chunks: &mut Vec<Chunk>, ihdr: &IhdrData, orig_ihdr: &IhdrData) {
+    // If the depth/color type has changed, some chunks may be invalid and should be dropped
+    // While these could potentially be converted, they have no known use case today and are
+    // generally more trouble than they're worth
+    if orig_ihdr.bit_depth != ihdr.bit_depth || orig_ihdr.color_type != ihdr.color_type {
+        aux_chunks.retain(|c| {
+            let invalid = &c.name == b"bKGD" || &c.name == b"sBIT" || &c.name == b"hIST";
+            if invalid {
+                warn!(
+                    "Removing {} chunk as it no longer matches the image data",
+                    std::str::from_utf8(&c.name).unwrap()
+                );
+            }
+            !invalid
+        });
+    }
+
+    // Remove any sRGB or iCCP chunks if the image was converted to or from grayscale
+    if orig_ihdr.color_type.is_gray() != ihdr.color_type.is_gray() {
+        aux_chunks.retain(|c| {
+            let invalid = &c.name == b"sRGB" || &c.name == b"iCCP";
+            if invalid {
+                trace!(
+                    "Removing {} chunk as it no longer matches the color type",
+                    std::str::from_utf8(&c.name).unwrap()
+                );
+            }
+            !invalid
+        });
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 3546cc05..10c85d09 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -26,7 +26,6 @@ extern crate rayon;
 mod rayon;
 
 use std::{
-    borrow::Cow,
     fs::{File, Metadata},
     io::{stdin, stdout, BufWriter, Read, Write},
     path::Path,
@@ -155,23 +154,27 @@ impl RawImage {
 
     /// Create an optimized png from the raw image data using the options provided
     pub fn create_optimized_png(&self, opts: &Options) -> PngResult<Vec<u8>> {
+        let mut opts = opts.to_owned();
+        let mut aux_chunks: Vec<_> = self
+            .aux_chunks
+            .iter()
+            .filter(|c| opts.strip.keep(&c.name))
+            .cloned()
+            .collect();
+        preprocess_chunks(&mut aux_chunks, &mut opts);
+
         let deadline = Arc::new(Deadline::new(opts.timeout));
-        let Some(result) = optimize_raw(self.png.clone(), opts, deadline, None) else {
+        let Some(result) = optimize_raw(self.png.clone(), &opts, deadline, None) else {
             return Err(PngError::new("Failed to optimize input data"));
         };
 
         let mut png = PngData {
             raw: result.image,
             idat_data: result.idat_data,
-            aux_chunks: self
-                .aux_chunks
-                .iter()
-                .filter(|c| opts.strip.keep(&c.name))
-                .cloned()
-                .collect(),
+            aux_chunks,
             frames: Vec::new(),
         };
-        postprocess_chunks(&mut png, opts, &self.png.ihdr);
+        postprocess_chunks(&mut png.aux_chunks, &png.raw.ihdr, &self.png.ihdr);
 
         Ok(png.output())
     }
@@ -346,19 +349,9 @@ fn optimize_png(
     debug!("    IDAT size = {} bytes", idat_original_size);
     debug!("    File size = {} bytes", file_original_size);
 
-    // Check for APNG by presence of acTL chunk
-    let opts = if png.aux_chunks.iter().any(|c| &c.name == b"acTL") {
-        warn!("APNG detected, disabling all reductions");
-        let mut opts = opts.to_owned();
-        opts.interlace = None;
-        opts.bit_depth_reduction = false;
-        opts.color_type_reduction = false;
-        opts.palette_reduction = false;
-        opts.grayscale_reduction = false;
-        Cow::Owned(opts)
-    } else {
-        Cow::Borrowed(opts)
-    };
+    let mut opts = opts.to_owned();
+    preprocess_chunks(&mut png.aux_chunks, &mut opts);
+
     let max_size = if opts.force {
         None
     } else {
@@ -368,10 +361,9 @@ fn optimize_png(
         png.raw = result.image;
         png.idat_data = result.idat_data;
         recompress_frames(png, &opts, deadline, result.filter)?;
+        postprocess_chunks(&mut png.aux_chunks, &png.raw.ihdr, &raw.ihdr);
     }
 
-    postprocess_chunks(png, &opts, &raw.ihdr);
-
     let output = png.output();
 
     if idat_original_size >= png.idat_data.len() {
@@ -619,61 +611,6 @@ fn report_format(prefix: &str, png: &PngImage) {
     );
 }
 
-/// Perform cleanup of certain chunks from the `PngData` object, after optimization has been completed
-fn postprocess_chunks(png: &mut PngData, opts: &Options, orig_ihdr: &IhdrData) {
-    if let Some(iccp_idx) = png.aux_chunks.iter().position(|c| &c.name == b"iCCP") {
-        // See if we can replace an iCCP chunk with an sRGB chunk
-        let may_replace_iccp = opts.strip != StripChunks::None && opts.strip.keep(b"sRGB");
-        if may_replace_iccp && png.aux_chunks.iter().any(|c| &c.name == b"sRGB") {
-            // Files aren't supposed to have both chunks, so we chose to honor sRGB
-            trace!("Removing iCCP chunk due to conflict with sRGB chunk");
-            png.aux_chunks.remove(iccp_idx);
-        } else if let Some(icc) = extract_icc(&png.aux_chunks[iccp_idx]) {
-            let intent = if may_replace_iccp {
-                srgb_rendering_intent(&icc)
-            } else {
-                None
-            };
-            // sRGB-like profile can be replaced with an sRGB chunk with the same rendering intent
-            if let Some(intent) = intent {
-                trace!("Replacing iCCP chunk with equivalent sRGB chunk");
-                png.aux_chunks[iccp_idx] = Chunk {
-                    name: *b"sRGB",
-                    data: vec![intent],
-                };
-            } else if opts.idat_recoding {
-                // Try recompressing the profile
-                let cur_len = png.aux_chunks[iccp_idx].data.len();
-                if let Ok(iccp) = make_iccp(&icc, opts.deflate, Some(cur_len - 1)) {
-                    debug!(
-                        "Recompressed iCCP chunk: {} ({} bytes decrease)",
-                        iccp.data.len(),
-                        cur_len - iccp.data.len()
-                    );
-                    png.aux_chunks[iccp_idx] = iccp;
-                }
-            }
-        }
-    }
-
-    // If the depth/color type has changed, some chunks may be invalid and should be dropped
-    // While these could potentially be converted, they have no known use case today and are
-    // generally more trouble than they're worth
-    let ihdr = &png.raw.ihdr;
-    if orig_ihdr.bit_depth != ihdr.bit_depth || orig_ihdr.color_type != ihdr.color_type {
-        png.aux_chunks.retain(|c| {
-            let invalid = &c.name == b"bKGD" || &c.name == b"sBIT" || &c.name == b"hIST";
-            if invalid {
-                warn!(
-                    "Removing {} chunk as it no longer matches the image data",
-                    std::str::from_utf8(&c.name).unwrap()
-                );
-            }
-            !invalid
-        });
-    }
-}
-
 /// Recompress the additional frames of an APNG
 fn recompress_frames(
     png: &mut PngData,


Test patch:
diff --git a/tests/files/filter_0_for_grayscale_16.png b/tests/files/filter_0_for_grayscale_16.png
index 5eb0e8fc..895a3672 100644
Binary files a/tests/files/filter_0_for_grayscale_16.png and b/tests/files/filter_0_for_grayscale_16.png differ
diff --git a/tests/files/filter_0_for_grayscale_8.png b/tests/files/filter_0_for_grayscale_8.png
index 21ee6424..231aca0d 100644
Binary files a/tests/files/filter_0_for_grayscale_8.png and b/tests/files/filter_0_for_grayscale_8.png differ
diff --git a/tests/files/filter_0_for_grayscale_alpha_16.png b/tests/files/filter_0_for_grayscale_alpha_16.png
index 4291b283..e5dabbf5 100644
Binary files a/tests/files/filter_0_for_grayscale_alpha_16.png and b/tests/files/filter_0_for_grayscale_alpha_16.png differ
diff --git a/tests/files/filter_0_for_grayscale_alpha_8.png b/tests/files/filter_0_for_grayscale_alpha_8.png
index 79a1a540..2c094a33 100644
Binary files a/tests/files/filter_0_for_grayscale_alpha_8.png and b/tests/files/filter_0_for_grayscale_alpha_8.png differ
diff --git a/tests/files/filter_0_for_palette_1.png b/tests/files/filter_0_for_palette_1.png
index fbcc40fb..3002cd36 100644
Binary files a/tests/files/filter_0_for_palette_1.png and b/tests/files/filter_0_for_palette_1.png differ
diff --git a/tests/files/filter_0_for_palette_2.png b/tests/files/filter_0_for_palette_2.png
index 4e3ef4f3..2b75fd22 100644
Binary files a/tests/files/filter_0_for_palette_2.png and b/tests/files/filter_0_for_palette_2.png differ
diff --git a/tests/files/filter_0_for_palette_4.png b/tests/files/filter_0_for_palette_4.png
index 56052b33..e37c46a5 100644
Binary files a/tests/files/filter_0_for_palette_4.png and b/tests/files/filter_0_for_palette_4.png differ
diff --git a/tests/files/filter_0_for_rgb_16.png b/tests/files/filter_0_for_rgb_16.png
index e1f41729..9868d032 100644
Binary files a/tests/files/filter_0_for_rgb_16.png and b/tests/files/filter_0_for_rgb_16.png differ
diff --git a/tests/files/filter_0_for_rgb_8.png b/tests/files/filter_0_for_rgb_8.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/filter_0_for_rgb_8.png and b/tests/files/filter_0_for_rgb_8.png differ
diff --git a/tests/files/filter_0_for_rgba_16.png b/tests/files/filter_0_for_rgba_16.png
index bfc0e07d..656ee55c 100644
Binary files a/tests/files/filter_0_for_rgba_16.png and b/tests/files/filter_0_for_rgba_16.png differ
diff --git a/tests/files/filter_0_for_rgba_8.png b/tests/files/filter_0_for_rgba_8.png
index 18a49183..a3e6e8c4 100644
Binary files a/tests/files/filter_0_for_rgba_8.png and b/tests/files/filter_0_for_rgba_8.png differ
diff --git a/tests/files/filter_1_for_grayscale_16.png b/tests/files/filter_1_for_grayscale_16.png
index 5eb0e8fc..895a3672 100644
Binary files a/tests/files/filter_1_for_grayscale_16.png and b/tests/files/filter_1_for_grayscale_16.png differ
diff --git a/tests/files/filter_1_for_grayscale_8.png b/tests/files/filter_1_for_grayscale_8.png
index 21ee6424..231aca0d 100644
Binary files a/tests/files/filter_1_for_grayscale_8.png and b/tests/files/filter_1_for_grayscale_8.png differ
diff --git a/tests/files/filter_1_for_grayscale_alpha_16.png b/tests/files/filter_1_for_grayscale_alpha_16.png
index 4291b283..e5dabbf5 100644
Binary files a/tests/files/filter_1_for_grayscale_alpha_16.png and b/tests/files/filter_1_for_grayscale_alpha_16.png differ
diff --git a/tests/files/filter_1_for_grayscale_alpha_8.png b/tests/files/filter_1_for_grayscale_alpha_8.png
index 79a1a540..2c094a33 100644
Binary files a/tests/files/filter_1_for_grayscale_alpha_8.png and b/tests/files/filter_1_for_grayscale_alpha_8.png differ
diff --git a/tests/files/filter_1_for_palette_1.png b/tests/files/filter_1_for_palette_1.png
index fbcc40fb..3002cd36 100644
Binary files a/tests/files/filter_1_for_palette_1.png and b/tests/files/filter_1_for_palette_1.png differ
diff --git a/tests/files/filter_1_for_palette_2.png b/tests/files/filter_1_for_palette_2.png
index 4e3ef4f3..2b75fd22 100644
Binary files a/tests/files/filter_1_for_palette_2.png and b/tests/files/filter_1_for_palette_2.png differ
diff --git a/tests/files/filter_1_for_palette_4.png b/tests/files/filter_1_for_palette_4.png
index 56052b33..e37c46a5 100644
Binary files a/tests/files/filter_1_for_palette_4.png and b/tests/files/filter_1_for_palette_4.png differ
diff --git a/tests/files/filter_1_for_rgb_16.png b/tests/files/filter_1_for_rgb_16.png
index e1f41729..9868d032 100644
Binary files a/tests/files/filter_1_for_rgb_16.png and b/tests/files/filter_1_for_rgb_16.png differ
diff --git a/tests/files/filter_1_for_rgb_8.png b/tests/files/filter_1_for_rgb_8.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/filter_1_for_rgb_8.png and b/tests/files/filter_1_for_rgb_8.png differ
diff --git a/tests/files/filter_1_for_rgba_16.png b/tests/files/filter_1_for_rgba_16.png
index bfc0e07d..656ee55c 100644
Binary files a/tests/files/filter_1_for_rgba_16.png and b/tests/files/filter_1_for_rgba_16.png differ
diff --git a/tests/files/filter_1_for_rgba_8.png b/tests/files/filter_1_for_rgba_8.png
index 18a49183..a3e6e8c4 100644
Binary files a/tests/files/filter_1_for_rgba_8.png and b/tests/files/filter_1_for_rgba_8.png differ
diff --git a/tests/files/filter_2_for_grayscale_16.png b/tests/files/filter_2_for_grayscale_16.png
index 5eb0e8fc..895a3672 100644
Binary files a/tests/files/filter_2_for_grayscale_16.png and b/tests/files/filter_2_for_grayscale_16.png differ
diff --git a/tests/files/filter_2_for_grayscale_8.png b/tests/files/filter_2_for_grayscale_8.png
index 21ee6424..231aca0d 100644
Binary files a/tests/files/filter_2_for_grayscale_8.png and b/tests/files/filter_2_for_grayscale_8.png differ
diff --git a/tests/files/filter_2_for_grayscale_alpha_16.png b/tests/files/filter_2_for_grayscale_alpha_16.png
index 4291b283..e5dabbf5 100644
Binary files a/tests/files/filter_2_for_grayscale_alpha_16.png and b/tests/files/filter_2_for_grayscale_alpha_16.png differ
diff --git a/tests/files/filter_2_for_grayscale_alpha_8.png b/tests/files/filter_2_for_grayscale_alpha_8.png
index 79a1a540..2c094a33 100644
Binary files a/tests/files/filter_2_for_grayscale_alpha_8.png and b/tests/files/filter_2_for_grayscale_alpha_8.png differ
diff --git a/tests/files/filter_2_for_palette_1.png b/tests/files/filter_2_for_palette_1.png
index fbcc40fb..3002cd36 100644
Binary files a/tests/files/filter_2_for_palette_1.png and b/tests/files/filter_2_for_palette_1.png differ
diff --git a/tests/files/filter_2_for_palette_2.png b/tests/files/filter_2_for_palette_2.png
index 4e3ef4f3..2b75fd22 100644
Binary files a/tests/files/filter_2_for_palette_2.png and b/tests/files/filter_2_for_palette_2.png differ
diff --git a/tests/files/filter_2_for_palette_4.png b/tests/files/filter_2_for_palette_4.png
index 56052b33..e37c46a5 100644
Binary files a/tests/files/filter_2_for_palette_4.png and b/tests/files/filter_2_for_palette_4.png differ
diff --git a/tests/files/filter_2_for_rgb_16.png b/tests/files/filter_2_for_rgb_16.png
index e1f41729..9868d032 100644
Binary files a/tests/files/filter_2_for_rgb_16.png and b/tests/files/filter_2_for_rgb_16.png differ
diff --git a/tests/files/filter_2_for_rgb_8.png b/tests/files/filter_2_for_rgb_8.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/filter_2_for_rgb_8.png and b/tests/files/filter_2_for_rgb_8.png differ
diff --git a/tests/files/filter_2_for_rgba_16.png b/tests/files/filter_2_for_rgba_16.png
index bfc0e07d..656ee55c 100644
Binary files a/tests/files/filter_2_for_rgba_16.png and b/tests/files/filter_2_for_rgba_16.png differ
diff --git a/tests/files/filter_2_for_rgba_8.png b/tests/files/filter_2_for_rgba_8.png
index 18a49183..a3e6e8c4 100644
Binary files a/tests/files/filter_2_for_rgba_8.png and b/tests/files/filter_2_for_rgba_8.png differ
diff --git a/tests/files/filter_3_for_grayscale_16.png b/tests/files/filter_3_for_grayscale_16.png
index 5eb0e8fc..895a3672 100644
Binary files a/tests/files/filter_3_for_grayscale_16.png and b/tests/files/filter_3_for_grayscale_16.png differ
diff --git a/tests/files/filter_3_for_grayscale_8.png b/tests/files/filter_3_for_grayscale_8.png
index 21ee6424..231aca0d 100644
Binary files a/tests/files/filter_3_for_grayscale_8.png and b/tests/files/filter_3_for_grayscale_8.png differ
diff --git a/tests/files/filter_3_for_grayscale_alpha_16.png b/tests/files/filter_3_for_grayscale_alpha_16.png
index 4291b283..e5dabbf5 100644
Binary files a/tests/files/filter_3_for_grayscale_alpha_16.png and b/tests/files/filter_3_for_grayscale_alpha_16.png differ
diff --git a/tests/files/filter_3_for_grayscale_alpha_8.png b/tests/files/filter_3_for_grayscale_alpha_8.png
index 79a1a540..2c094a33 100644
Binary files a/tests/files/filter_3_for_grayscale_alpha_8.png and b/tests/files/filter_3_for_grayscale_alpha_8.png differ
diff --git a/tests/files/filter_3_for_palette_1.png b/tests/files/filter_3_for_palette_1.png
index fbcc40fb..3002cd36 100644
Binary files a/tests/files/filter_3_for_palette_1.png and b/tests/files/filter_3_for_palette_1.png differ
diff --git a/tests/files/filter_3_for_palette_2.png b/tests/files/filter_3_for_palette_2.png
index 4e3ef4f3..2b75fd22 100644
Binary files a/tests/files/filter_3_for_palette_2.png and b/tests/files/filter_3_for_palette_2.png differ
diff --git a/tests/files/filter_3_for_palette_4.png b/tests/files/filter_3_for_palette_4.png
index 56052b33..e37c46a5 100644
Binary files a/tests/files/filter_3_for_palette_4.png and b/tests/files/filter_3_for_palette_4.png differ
diff --git a/tests/files/filter_3_for_rgb_16.png b/tests/files/filter_3_for_rgb_16.png
index e1f41729..9868d032 100644
Binary files a/tests/files/filter_3_for_rgb_16.png and b/tests/files/filter_3_for_rgb_16.png differ
diff --git a/tests/files/filter_3_for_rgb_8.png b/tests/files/filter_3_for_rgb_8.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/filter_3_for_rgb_8.png and b/tests/files/filter_3_for_rgb_8.png differ
diff --git a/tests/files/filter_3_for_rgba_16.png b/tests/files/filter_3_for_rgba_16.png
index bfc0e07d..656ee55c 100644
Binary files a/tests/files/filter_3_for_rgba_16.png and b/tests/files/filter_3_for_rgba_16.png differ
diff --git a/tests/files/filter_3_for_rgba_8.png b/tests/files/filter_3_for_rgba_8.png
index 18a49183..a3e6e8c4 100644
Binary files a/tests/files/filter_3_for_rgba_8.png and b/tests/files/filter_3_for_rgba_8.png differ
diff --git a/tests/files/filter_4_for_grayscale_16.png b/tests/files/filter_4_for_grayscale_16.png
index 5eb0e8fc..895a3672 100644
Binary files a/tests/files/filter_4_for_grayscale_16.png and b/tests/files/filter_4_for_grayscale_16.png differ
diff --git a/tests/files/filter_4_for_grayscale_8.png b/tests/files/filter_4_for_grayscale_8.png
index 21ee6424..231aca0d 100644
Binary files a/tests/files/filter_4_for_grayscale_8.png and b/tests/files/filter_4_for_grayscale_8.png differ
diff --git a/tests/files/filter_4_for_grayscale_alpha_16.png b/tests/files/filter_4_for_grayscale_alpha_16.png
index 4291b283..e5dabbf5 100644
Binary files a/tests/files/filter_4_for_grayscale_alpha_16.png and b/tests/files/filter_4_for_grayscale_alpha_16.png differ
diff --git a/tests/files/filter_4_for_grayscale_alpha_8.png b/tests/files/filter_4_for_grayscale_alpha_8.png
index 79a1a540..2c094a33 100644
Binary files a/tests/files/filter_4_for_grayscale_alpha_8.png and b/tests/files/filter_4_for_grayscale_alpha_8.png differ
diff --git a/tests/files/filter_4_for_palette_1.png b/tests/files/filter_4_for_palette_1.png
index fbcc40fb..3002cd36 100644
Binary files a/tests/files/filter_4_for_palette_1.png and b/tests/files/filter_4_for_palette_1.png differ
diff --git a/tests/files/filter_4_for_palette_2.png b/tests/files/filter_4_for_palette_2.png
index 4e3ef4f3..2b75fd22 100644
Binary files a/tests/files/filter_4_for_palette_2.png and b/tests/files/filter_4_for_palette_2.png differ
diff --git a/tests/files/filter_4_for_palette_4.png b/tests/files/filter_4_for_palette_4.png
index 56052b33..e37c46a5 100644
Binary files a/tests/files/filter_4_for_palette_4.png and b/tests/files/filter_4_for_palette_4.png differ
diff --git a/tests/files/filter_4_for_rgb_16.png b/tests/files/filter_4_for_rgb_16.png
index e1f41729..9868d032 100644
Binary files a/tests/files/filter_4_for_rgb_16.png and b/tests/files/filter_4_for_rgb_16.png differ
diff --git a/tests/files/filter_4_for_rgb_8.png b/tests/files/filter_4_for_rgb_8.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/filter_4_for_rgb_8.png and b/tests/files/filter_4_for_rgb_8.png differ
diff --git a/tests/files/filter_4_for_rgba_16.png b/tests/files/filter_4_for_rgba_16.png
index bfc0e07d..656ee55c 100644
Binary files a/tests/files/filter_4_for_rgba_16.png and b/tests/files/filter_4_for_rgba_16.png differ
diff --git a/tests/files/filter_4_for_rgba_8.png b/tests/files/filter_4_for_rgba_8.png
index 18a49183..a3e6e8c4 100644
Binary files a/tests/files/filter_4_for_rgba_8.png and b/tests/files/filter_4_for_rgba_8.png differ
diff --git a/tests/files/filter_5_for_grayscale_16.png b/tests/files/filter_5_for_grayscale_16.png
index 5eb0e8fc..895a3672 100644
Binary files a/tests/files/filter_5_for_grayscale_16.png and b/tests/files/filter_5_for_grayscale_16.png differ
diff --git a/tests/files/filter_5_for_grayscale_8.png b/tests/files/filter_5_for_grayscale_8.png
index 21ee6424..231aca0d 100644
Binary files a/tests/files/filter_5_for_grayscale_8.png and b/tests/files/filter_5_for_grayscale_8.png differ
diff --git a/tests/files/filter_5_for_grayscale_alpha_16.png b/tests/files/filter_5_for_grayscale_alpha_16.png
index 4291b283..e5dabbf5 100644
Binary files a/tests/files/filter_5_for_grayscale_alpha_16.png and b/tests/files/filter_5_for_grayscale_alpha_16.png differ
diff --git a/tests/files/filter_5_for_grayscale_alpha_8.png b/tests/files/filter_5_for_grayscale_alpha_8.png
index 79a1a540..2c094a33 100644
Binary files a/tests/files/filter_5_for_grayscale_alpha_8.png and b/tests/files/filter_5_for_grayscale_alpha_8.png differ
diff --git a/tests/files/filter_5_for_palette_1.png b/tests/files/filter_5_for_palette_1.png
index fbcc40fb..3002cd36 100644
Binary files a/tests/files/filter_5_for_palette_1.png and b/tests/files/filter_5_for_palette_1.png differ
diff --git a/tests/files/filter_5_for_palette_2.png b/tests/files/filter_5_for_palette_2.png
index 4e3ef4f3..2b75fd22 100644
Binary files a/tests/files/filter_5_for_palette_2.png and b/tests/files/filter_5_for_palette_2.png differ
diff --git a/tests/files/filter_5_for_palette_4.png b/tests/files/filter_5_for_palette_4.png
index 56052b33..e37c46a5 100644
Binary files a/tests/files/filter_5_for_palette_4.png and b/tests/files/filter_5_for_palette_4.png differ
diff --git a/tests/files/filter_5_for_rgb_16.png b/tests/files/filter_5_for_rgb_16.png
index e1f41729..9868d032 100644
Binary files a/tests/files/filter_5_for_rgb_16.png and b/tests/files/filter_5_for_rgb_16.png differ
diff --git a/tests/files/filter_5_for_rgb_8.png b/tests/files/filter_5_for_rgb_8.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/filter_5_for_rgb_8.png and b/tests/files/filter_5_for_rgb_8.png differ
diff --git a/tests/files/filter_5_for_rgba_16.png b/tests/files/filter_5_for_rgba_16.png
index bfc0e07d..656ee55c 100644
Binary files a/tests/files/filter_5_for_rgba_16.png and b/tests/files/filter_5_for_rgba_16.png differ
diff --git a/tests/files/filter_5_for_rgba_8.png b/tests/files/filter_5_for_rgba_8.png
index 18a49183..a3e6e8c4 100644
Binary files a/tests/files/filter_5_for_rgba_8.png and b/tests/files/filter_5_for_rgba_8.png differ
diff --git a/tests/files/grayscale_16_should_be_grayscale_1.png b/tests/files/grayscale_16_should_be_grayscale_1.png
index 66fe12e3..90407e70 100644
Binary files a/tests/files/grayscale_16_should_be_grayscale_1.png and b/tests/files/grayscale_16_should_be_grayscale_1.png differ
diff --git a/tests/files/grayscale_16_should_be_grayscale_16.png b/tests/files/grayscale_16_should_be_grayscale_16.png
index 5eb0e8fc..895a3672 100644
Binary files a/tests/files/grayscale_16_should_be_grayscale_16.png and b/tests/files/grayscale_16_should_be_grayscale_16.png differ
diff --git a/tests/files/grayscale_16_should_be_grayscale_8.png b/tests/files/grayscale_16_should_be_grayscale_8.png
index 2492ff86..c3fbbccf 100644
Binary files a/tests/files/grayscale_16_should_be_grayscale_8.png and b/tests/files/grayscale_16_should_be_grayscale_8.png differ
diff --git a/tests/files/grayscale_2_should_be_grayscale_1.png b/tests/files/grayscale_2_should_be_grayscale_1.png
index c614d443..6d530437 100644
Binary files a/tests/files/grayscale_2_should_be_grayscale_1.png and b/tests/files/grayscale_2_should_be_grayscale_1.png differ
diff --git a/tests/files/grayscale_4_should_be_grayscale_1.png b/tests/files/grayscale_4_should_be_grayscale_1.png
index 12d05931..a8d52eef 100644
Binary files a/tests/files/grayscale_4_should_be_grayscale_1.png and b/tests/files/grayscale_4_should_be_grayscale_1.png differ
diff --git a/tests/files/grayscale_4_should_be_grayscale_2.png b/tests/files/grayscale_4_should_be_grayscale_2.png
index eb4570ff..0ee9f949 100644
Binary files a/tests/files/grayscale_4_should_be_grayscale_2.png and b/tests/files/grayscale_4_should_be_grayscale_2.png differ
diff --git a/tests/files/grayscale_8_should_be_grayscale_1.png b/tests/files/grayscale_8_should_be_grayscale_1.png
index 89248c61..05ca3a68 100644
Binary files a/tests/files/grayscale_8_should_be_grayscale_1.png and b/tests/files/grayscale_8_should_be_grayscale_1.png differ
diff --git a/tests/files/grayscale_8_should_be_grayscale_2.png b/tests/files/grayscale_8_should_be_grayscale_2.png
index a3e063c9..9b1c8df8 100644
Binary files a/tests/files/grayscale_8_should_be_grayscale_2.png and b/tests/files/grayscale_8_should_be_grayscale_2.png differ
diff --git a/tests/files/grayscale_8_should_be_grayscale_4.png b/tests/files/grayscale_8_should_be_grayscale_4.png
index 13537e8e..7180c3dd 100644
Binary files a/tests/files/grayscale_8_should_be_grayscale_4.png and b/tests/files/grayscale_8_should_be_grayscale_4.png differ
diff --git a/tests/files/grayscale_8_should_be_grayscale_8.png b/tests/files/grayscale_8_should_be_grayscale_8.png
index 21ee6424..231aca0d 100644
Binary files a/tests/files/grayscale_8_should_be_grayscale_8.png and b/tests/files/grayscale_8_should_be_grayscale_8.png differ
diff --git a/tests/files/grayscale_8_should_be_palette_1.png b/tests/files/grayscale_8_should_be_palette_1.png
index d3e68df2..71485561 100644
Binary files a/tests/files/grayscale_8_should_be_palette_1.png and b/tests/files/grayscale_8_should_be_palette_1.png differ
diff --git a/tests/files/grayscale_8_should_be_palette_2.png b/tests/files/grayscale_8_should_be_palette_2.png
index 5933b710..79625bda 100644
Binary files a/tests/files/grayscale_8_should_be_palette_2.png and b/tests/files/grayscale_8_should_be_palette_2.png differ
diff --git a/tests/files/grayscale_8_should_be_palette_4.png b/tests/files/grayscale_8_should_be_palette_4.png
index 2edd6dbf..e7b8ccef 100644
Binary files a/tests/files/grayscale_8_should_be_palette_4.png and b/tests/files/grayscale_8_should_be_palette_4.png differ
diff --git a/tests/files/grayscale_8_should_be_palette_8.png b/tests/files/grayscale_8_should_be_palette_8.png
index ff94dfb8..1ac8da6c 100644
Binary files a/tests/files/grayscale_8_should_be_palette_8.png and b/tests/files/grayscale_8_should_be_palette_8.png differ
diff --git a/tests/files/grayscale_alpha_16_reduce_alpha.png b/tests/files/grayscale_alpha_16_reduce_alpha.png
index 78b72778..8e8ff73c 100644
Binary files a/tests/files/grayscale_alpha_16_reduce_alpha.png and b/tests/files/grayscale_alpha_16_reduce_alpha.png differ
diff --git a/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png b/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png
index 4291b283..e5dabbf5 100644
Binary files a/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png and b/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_16.png differ
diff --git a/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png b/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png
index e4b33085..a60025f1 100644
Binary files a/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png and b/tests/files/grayscale_alpha_16_should_be_grayscale_alpha_8.png differ
diff --git a/tests/files/grayscale_alpha_16_should_be_grayscale_trns_16.png b/tests/files/grayscale_alpha_16_should_be_grayscale_trns_16.png
index 0ef1ada1..031074d5 100644
Binary files a/tests/files/grayscale_alpha_16_should_be_grayscale_trns_16.png and b/tests/files/grayscale_alpha_16_should_be_grayscale_trns_16.png differ
diff --git a/tests/files/grayscale_alpha_8_reduce_alpha.png b/tests/files/grayscale_alpha_8_reduce_alpha.png
index c2e1d1ce..09038348 100644
Binary files a/tests/files/grayscale_alpha_8_reduce_alpha.png and b/tests/files/grayscale_alpha_8_reduce_alpha.png differ
diff --git a/tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png b/tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png
index 79a1a540..2c094a33 100644
Binary files a/tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png and b/tests/files/grayscale_alpha_8_should_be_grayscale_alpha_8.png differ
diff --git a/tests/files/grayscale_alpha_8_should_be_grayscale_trns_1.png b/tests/files/grayscale_alpha_8_should_be_grayscale_trns_1.png
index 3434291f..f2db7870 100644
Binary files a/tests/files/grayscale_alpha_8_should_be_grayscale_trns_1.png and b/tests/files/grayscale_alpha_8_should_be_grayscale_trns_1.png differ
diff --git a/tests/files/grayscale_alpha_8_should_be_grayscale_trns_8.png b/tests/files/grayscale_alpha_8_should_be_grayscale_trns_8.png
index a339411d..b949f7fe 100644
Binary files a/tests/files/grayscale_alpha_8_should_be_grayscale_trns_8.png and b/tests/files/grayscale_alpha_8_should_be_grayscale_trns_8.png differ
diff --git a/tests/files/grayscale_alpha_8_should_be_palette_8.png b/tests/files/grayscale_alpha_8_should_be_palette_8.png
index 69aedf34..96301c81 100644
Binary files a/tests/files/grayscale_alpha_8_should_be_palette_8.png and b/tests/files/grayscale_alpha_8_should_be_palette_8.png differ
diff --git a/tests/files/grayscale_trns_8_should_be_grayscale_1.png b/tests/files/grayscale_trns_8_should_be_grayscale_1.png
index a322aadb..8409d8d6 100644
Binary files a/tests/files/grayscale_trns_8_should_be_grayscale_1.png and b/tests/files/grayscale_trns_8_should_be_grayscale_1.png differ
diff --git a/tests/files/interlaced_0_to_1_other_filter_mode.png b/tests/files/interlaced_0_to_1_other_filter_mode.png
index b79a99ee..1e1707da 100644
Binary files a/tests/files/interlaced_0_to_1_other_filter_mode.png and b/tests/files/interlaced_0_to_1_other_filter_mode.png differ
diff --git a/tests/files/interlaced_grayscale_16_should_be_grayscale_16.png b/tests/files/interlaced_grayscale_16_should_be_grayscale_16.png
index ede03fa8..d79583ef 100644
Binary files a/tests/files/interlaced_grayscale_16_should_be_grayscale_16.png and b/tests/files/interlaced_grayscale_16_should_be_grayscale_16.png differ
diff --git a/tests/files/interlaced_grayscale_16_should_be_grayscale_8.png b/tests/files/interlaced_grayscale_16_should_be_grayscale_8.png
index 399e5dc2..e8c449fc 100644
Binary files a/tests/files/interlaced_grayscale_16_should_be_grayscale_8.png and b/tests/files/interlaced_grayscale_16_should_be_grayscale_8.png differ
diff --git a/tests/files/interlaced_grayscale_8_should_be_grayscale_8.png b/tests/files/interlaced_grayscale_8_should_be_grayscale_8.png
index 8384c4cb..9203199d 100644
Binary files a/tests/files/interlaced_grayscale_8_should_be_grayscale_8.png and b/tests/files/interlaced_grayscale_8_should_be_grayscale_8.png differ
diff --git a/tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_16.png b/tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_16.png
index f7388809..1af2d0d0 100644
Binary files a/tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_16.png and b/tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_16.png differ
diff --git a/tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_8.png b/tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_8.png
index 5b06dd76..18928b86 100644
Binary files a/tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_8.png and b/tests/files/interlaced_grayscale_alpha_16_should_be_grayscale_alpha_8.png differ
diff --git a/tests/files/interlaced_grayscale_alpha_8_should_be_grayscale_alpha_8.png b/tests/files/interlaced_grayscale_alpha_8_should_be_grayscale_alpha_8.png
index cba55fcf..7d737bf6 100644
Binary files a/tests/files/interlaced_grayscale_alpha_8_should_be_grayscale_alpha_8.png and b/tests/files/interlaced_grayscale_alpha_8_should_be_grayscale_alpha_8.png differ
diff --git a/tests/files/interlaced_odd_width.png b/tests/files/interlaced_odd_width.png
index 82e7bc0a..181bebd2 100644
Binary files a/tests/files/interlaced_odd_width.png and b/tests/files/interlaced_odd_width.png differ
diff --git a/tests/files/interlaced_palette_1_should_be_palette_1.png b/tests/files/interlaced_palette_1_should_be_palette_1.png
index 8fad7ec4..951a52fb 100644
Binary files a/tests/files/interlaced_palette_1_should_be_palette_1.png and b/tests/files/interlaced_palette_1_should_be_palette_1.png differ
diff --git a/tests/files/interlaced_palette_2_should_be_palette_1.png b/tests/files/interlaced_palette_2_should_be_palette_1.png
index 2ae2c18f..be4e16e3 100644
Binary files a/tests/files/interlaced_palette_2_should_be_palette_1.png and b/tests/files/interlaced_palette_2_should_be_palette_1.png differ
diff --git a/tests/files/interlaced_palette_2_should_be_palette_2.png b/tests/files/interlaced_palette_2_should_be_palette_2.png
index d762bf9c..7e3f2a84 100644
Binary files a/tests/files/interlaced_palette_2_should_be_palette_2.png and b/tests/files/interlaced_palette_2_should_be_palette_2.png differ
diff --git a/tests/files/interlaced_palette_4_should_be_palette_1.png b/tests/files/interlaced_palette_4_should_be_palette_1.png
index d752f06b..81c1e672 100644
Binary files a/tests/files/interlaced_palette_4_should_be_palette_1.png and b/tests/files/interlaced_palette_4_should_be_palette_1.png differ
diff --git a/tests/files/interlaced_palette_4_should_be_palette_2.png b/tests/files/interlaced_palette_4_should_be_palette_2.png
index 2604e434..fcc3ae24 100644
Binary files a/tests/files/interlaced_palette_4_should_be_palette_2.png and b/tests/files/interlaced_palette_4_should_be_palette_2.png differ
diff --git a/tests/files/interlaced_palette_4_should_be_palette_4.png b/tests/files/interlaced_palette_4_should_be_palette_4.png
index ad7906bb..1e26c44e 100644
Binary files a/tests/files/interlaced_palette_4_should_be_palette_4.png and b/tests/files/interlaced_palette_4_should_be_palette_4.png differ
diff --git a/tests/files/interlaced_palette_8_should_be_grayscale_8.png b/tests/files/interlaced_palette_8_should_be_grayscale_8.png
index 17356782..20b2f819 100644
Binary files a/tests/files/interlaced_palette_8_should_be_grayscale_8.png and b/tests/files/interlaced_palette_8_should_be_grayscale_8.png differ
diff --git a/tests/files/interlaced_palette_8_should_be_palette_1.png b/tests/files/interlaced_palette_8_should_be_palette_1.png
index 3b00238f..0137b818 100644
Binary files a/tests/files/interlaced_palette_8_should_be_palette_1.png and b/tests/files/interlaced_palette_8_should_be_palette_1.png differ
diff --git a/tests/files/interlaced_palette_8_should_be_palette_2.png b/tests/files/interlaced_palette_8_should_be_palette_2.png
index c694bfc0..d31af207 100644
Binary files a/tests/files/interlaced_palette_8_should_be_palette_2.png and b/tests/files/interlaced_palette_8_should_be_palette_2.png differ
diff --git a/tests/files/interlaced_palette_8_should_be_palette_4.png b/tests/files/interlaced_palette_8_should_be_palette_4.png
index 46088e1c..2eb78fb4 100644
Binary files a/tests/files/interlaced_palette_8_should_be_palette_4.png and b/tests/files/interlaced_palette_8_should_be_palette_4.png differ
diff --git a/tests/files/interlaced_palette_8_should_be_palette_8.png b/tests/files/interlaced_palette_8_should_be_palette_8.png
index bf8dfa92..e2d98723 100644
Binary files a/tests/files/interlaced_palette_8_should_be_palette_8.png and b/tests/files/interlaced_palette_8_should_be_palette_8.png differ
diff --git a/tests/files/interlaced_rgb_16_should_be_grayscale_16.png b/tests/files/interlaced_rgb_16_should_be_grayscale_16.png
index 67ed56d0..7fe72578 100644
Binary files a/tests/files/interlaced_rgb_16_should_be_grayscale_16.png and b/tests/files/interlaced_rgb_16_should_be_grayscale_16.png differ
diff --git a/tests/files/interlaced_rgb_16_should_be_grayscale_8.png b/tests/files/interlaced_rgb_16_should_be_grayscale_8.png
index a769e131..a23ca9fa 100644
Binary files a/tests/files/interlaced_rgb_16_should_be_grayscale_8.png and b/tests/files/interlaced_rgb_16_should_be_grayscale_8.png differ
diff --git a/tests/files/interlaced_rgb_16_should_be_palette_1.png b/tests/files/interlaced_rgb_16_should_be_palette_1.png
index f0fb5a56..d90ebdfd 100644
Binary files a/tests/files/interlaced_rgb_16_should_be_palette_1.png and b/tests/files/interlaced_rgb_16_should_be_palette_1.png differ
diff --git a/tests/files/interlaced_rgb_16_should_be_palette_2.png b/tests/files/interlaced_rgb_16_should_be_palette_2.png
index 7242fac1..3ce2951c 100644
Binary files a/tests/files/interlaced_rgb_16_should_be_palette_2.png and b/tests/files/interlaced_rgb_16_should_be_palette_2.png differ
diff --git a/tests/files/interlaced_rgb_16_should_be_palette_4.png b/tests/files/interlaced_rgb_16_should_be_palette_4.png
index 702ac8f4..f26a6cdb 100644
Binary files a/tests/files/interlaced_rgb_16_should_be_palette_4.png and b/tests/files/interlaced_rgb_16_should_be_palette_4.png differ
diff --git a/tests/files/interlaced_rgb_16_should_be_palette_8.png b/tests/files/interlaced_rgb_16_should_be_palette_8.png
index 45444185..cf915429 100644
Binary files a/tests/files/interlaced_rgb_16_should_be_palette_8.png and b/tests/files/interlaced_rgb_16_should_be_palette_8.png differ
diff --git a/tests/files/interlaced_rgb_16_should_be_rgb_16.png b/tests/files/interlaced_rgb_16_should_be_rgb_16.png
index 3be3b01f..957db7fc 100644
Binary files a/tests/files/interlaced_rgb_16_should_be_rgb_16.png and b/tests/files/interlaced_rgb_16_should_be_rgb_16.png differ
diff --git a/tests/files/interlaced_rgb_16_should_be_rgb_8.png b/tests/files/interlaced_rgb_16_should_be_rgb_8.png
index aa3768d4..a8606208 100644
Binary files a/tests/files/interlaced_rgb_16_should_be_rgb_8.png and b/tests/files/interlaced_rgb_16_should_be_rgb_8.png differ
diff --git a/tests/files/interlaced_rgb_8_should_be_grayscale_8.png b/tests/files/interlaced_rgb_8_should_be_grayscale_8.png
index 53550a14..390354c1 100644
Binary files a/tests/files/interlaced_rgb_8_should_be_grayscale_8.png and b/tests/files/interlaced_rgb_8_should_be_grayscale_8.png differ
diff --git a/tests/files/interlaced_rgb_8_should_be_palette_1.png b/tests/files/interlaced_rgb_8_should_be_palette_1.png
index 4f77419e..4e3a9279 100644
Binary files a/tests/files/interlaced_rgb_8_should_be_palette_1.png and b/tests/files/interlaced_rgb_8_should_be_palette_1.png differ
diff --git a/tests/files/interlaced_rgb_8_should_be_palette_2.png b/tests/files/interlaced_rgb_8_should_be_palette_2.png
index 88d72a01..f0636fd6 100644
Binary files a/tests/files/interlaced_rgb_8_should_be_palette_2.png and b/tests/files/interlaced_rgb_8_should_be_palette_2.png differ
diff --git a/tests/files/interlaced_rgb_8_should_be_palette_4.png b/tests/files/interlaced_rgb_8_should_be_palette_4.png
index 23be3225..5c75cdc6 100644
Binary files a/tests/files/interlaced_rgb_8_should_be_palette_4.png and b/tests/files/interlaced_rgb_8_should_be_palette_4.png differ
diff --git a/tests/files/interlaced_rgb_8_should_be_palette_8.png b/tests/files/interlaced_rgb_8_should_be_palette_8.png
index 464c6cdc..3da5f37e 100644
Binary files a/tests/files/interlaced_rgb_8_should_be_palette_8.png and b/tests/files/interlaced_rgb_8_should_be_palette_8.png differ
diff --git a/tests/files/interlaced_rgb_8_should_be_rgb_8.png b/tests/files/interlaced_rgb_8_should_be_rgb_8.png
index 23375db0..34ba05a9 100644
Binary files a/tests/files/interlaced_rgb_8_should_be_rgb_8.png and b/tests/files/interlaced_rgb_8_should_be_rgb_8.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_grayscale_16.png b/tests/files/interlaced_rgba_16_should_be_grayscale_16.png
index f2f349f6..0fa8d6d3 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_grayscale_16.png and b/tests/files/interlaced_rgba_16_should_be_grayscale_16.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_grayscale_8.png b/tests/files/interlaced_rgba_16_should_be_grayscale_8.png
index ee0245e1..05d874d1 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_grayscale_8.png and b/tests/files/interlaced_rgba_16_should_be_grayscale_8.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_grayscale_alpha_16.png b/tests/files/interlaced_rgba_16_should_be_grayscale_alpha_16.png
index f11382a1..614cac28 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_grayscale_alpha_16.png and b/tests/files/interlaced_rgba_16_should_be_grayscale_alpha_16.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_grayscale_alpha_8.png b/tests/files/interlaced_rgba_16_should_be_grayscale_alpha_8.png
index 8131ae79..900dc1ea 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_grayscale_alpha_8.png and b/tests/files/interlaced_rgba_16_should_be_grayscale_alpha_8.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_palette_1.png b/tests/files/interlaced_rgba_16_should_be_palette_1.png
index 8ccb3117..1087b9ed 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_palette_1.png and b/tests/files/interlaced_rgba_16_should_be_palette_1.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_palette_2.png b/tests/files/interlaced_rgba_16_should_be_palette_2.png
index 8dfee69a..56445b24 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_palette_2.png and b/tests/files/interlaced_rgba_16_should_be_palette_2.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_palette_4.png b/tests/files/interlaced_rgba_16_should_be_palette_4.png
index ce414935..3d681657 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_palette_4.png and b/tests/files/interlaced_rgba_16_should_be_palette_4.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_palette_8.png b/tests/files/interlaced_rgba_16_should_be_palette_8.png
index d5114b21..5c4056a6 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_palette_8.png and b/tests/files/interlaced_rgba_16_should_be_palette_8.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_rgb_16.png b/tests/files/interlaced_rgba_16_should_be_rgb_16.png
index 9b54dded..3383eb4b 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_rgb_16.png and b/tests/files/interlaced_rgba_16_should_be_rgb_16.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_rgb_8.png b/tests/files/interlaced_rgba_16_should_be_rgb_8.png
index 408bbcdc..f591df0d 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_rgb_8.png and b/tests/files/interlaced_rgba_16_should_be_rgb_8.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_rgba_16.png b/tests/files/interlaced_rgba_16_should_be_rgba_16.png
index 87a04b62..417c65d5 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_rgba_16.png and b/tests/files/interlaced_rgba_16_should_be_rgba_16.png differ
diff --git a/tests/files/interlaced_rgba_16_should_be_rgba_8.png b/tests/files/interlaced_rgba_16_should_be_rgba_8.png
index d90811b0..978afd65 100644
Binary files a/tests/files/interlaced_rgba_16_should_be_rgba_8.png and b/tests/files/interlaced_rgba_16_should_be_rgba_8.png differ
diff --git a/tests/files/interlaced_rgba_8_should_be_palette_1.png b/tests/files/interlaced_rgba_8_should_be_palette_1.png
index 5a2092a2..1936bd74 100644
Binary files a/tests/files/interlaced_rgba_8_should_be_palette_1.png and b/tests/files/interlaced_rgba_8_should_be_palette_1.png differ
diff --git a/tests/files/interlaced_rgba_8_should_be_palette_2.png b/tests/files/interlaced_rgba_8_should_be_palette_2.png
index 1280eaba..1462c5be 100644
Binary files a/tests/files/interlaced_rgba_8_should_be_palette_2.png and b/tests/files/interlaced_rgba_8_should_be_palette_2.png differ
diff --git a/tests/files/interlaced_rgba_8_should_be_palette_4.png b/tests/files/interlaced_rgba_8_should_be_palette_4.png
index 16836c5e..454a6f1a 100644
Binary files a/tests/files/interlaced_rgba_8_should_be_palette_4.png and b/tests/files/interlaced_rgba_8_should_be_palette_4.png differ
diff --git a/tests/files/interlaced_rgba_8_should_be_rgba_8.png b/tests/files/interlaced_rgba_8_should_be_rgba_8.png
index ecef7322..4101f2f7 100644
Binary files a/tests/files/interlaced_rgba_8_should_be_rgba_8.png and b/tests/files/interlaced_rgba_8_should_be_rgba_8.png differ
diff --git a/tests/files/interlaced_small_files.png b/tests/files/interlaced_small_files.png
index 91af6176..b161edd2 100644
Binary files a/tests/files/interlaced_small_files.png and b/tests/files/interlaced_small_files.png differ
diff --git a/tests/files/interlacing_0_to_1.png b/tests/files/interlacing_0_to_1.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/interlacing_0_to_1.png and b/tests/files/interlacing_0_to_1.png differ
diff --git a/tests/files/interlacing_0_to_1_small_files.png b/tests/files/interlacing_0_to_1_small_files.png
index 576b88c9..106c5729 100644
Binary files a/tests/files/interlacing_0_to_1_small_files.png and b/tests/files/interlacing_0_to_1_small_files.png differ
diff --git a/tests/files/interlacing_1_to_0.png b/tests/files/interlacing_1_to_0.png
index 23375db0..34ba05a9 100644
Binary files a/tests/files/interlacing_1_to_0.png and b/tests/files/interlacing_1_to_0.png differ
diff --git a/tests/files/interlacing_1_to_0_small_files.png b/tests/files/interlacing_1_to_0_small_files.png
index 91af6176..b161edd2 100644
Binary files a/tests/files/interlacing_1_to_0_small_files.png and b/tests/files/interlacing_1_to_0_small_files.png differ
diff --git a/tests/files/issue-140.png b/tests/files/issue-140.png
index 12e9d2a4..f2e8fc0b 100644
Binary files a/tests/files/issue-140.png and b/tests/files/issue-140.png differ
diff --git a/tests/files/issue-171.png b/tests/files/issue-171.png
index bf83f52b..e0561555 100644
Binary files a/tests/files/issue-171.png and b/tests/files/issue-171.png differ
diff --git a/tests/files/issue-56.png b/tests/files/issue-56.png
index 052d0cf1..edb4ec2a 100644
Binary files a/tests/files/issue-56.png and b/tests/files/issue-56.png differ
diff --git a/tests/files/issue-58.png b/tests/files/issue-58.png
index 8a58f5d2..d427b5d3 100644
Binary files a/tests/files/issue-58.png and b/tests/files/issue-58.png differ
diff --git a/tests/files/issue-59.png b/tests/files/issue-59.png
index f2101164..e6636a37 100644
Binary files a/tests/files/issue-59.png and b/tests/files/issue-59.png differ
diff --git a/tests/files/issue-60.png b/tests/files/issue-60.png
index 7fd3731a..96729e15 100644
Binary files a/tests/files/issue-60.png and b/tests/files/issue-60.png differ
diff --git a/tests/files/palette_1_should_be_palette_1.png b/tests/files/palette_1_should_be_palette_1.png
index fbcc40fb..3002cd36 100644
Binary files a/tests/files/palette_1_should_be_palette_1.png and b/tests/files/palette_1_should_be_palette_1.png differ
diff --git a/tests/files/palette_2_should_be_palette_1.png b/tests/files/palette_2_should_be_palette_1.png
index dc400713..77dee5a1 100644
Binary files a/tests/files/palette_2_should_be_palette_1.png and b/tests/files/palette_2_should_be_palette_1.png differ
diff --git a/tests/files/palette_2_should_be_palette_2.png b/tests/files/palette_2_should_be_palette_2.png
index 4e3ef4f3..2b75fd22 100644
Binary files a/tests/files/palette_2_should_be_palette_2.png and b/tests/files/palette_2_should_be_palette_2.png differ
diff --git a/tests/files/palette_4_should_be_palette_1.png b/tests/files/palette_4_should_be_palette_1.png
index 9f88d93c..317ad441 100644
Binary files a/tests/files/palette_4_should_be_palette_1.png and b/tests/files/palette_4_should_be_palette_1.png differ
diff --git a/tests/files/palette_4_should_be_palette_2.png b/tests/files/palette_4_should_be_palette_2.png
index da5f29af..be58f3b1 100644
Binary files a/tests/files/palette_4_should_be_palette_2.png and b/tests/files/palette_4_should_be_palette_2.png differ
diff --git a/tests/files/palette_4_should_be_palette_4.png b/tests/files/palette_4_should_be_palette_4.png
index 56052b33..e37c46a5 100644
Binary files a/tests/files/palette_4_should_be_palette_4.png and b/tests/files/palette_4_should_be_palette_4.png differ
diff --git a/tests/files/palette_8_should_be_grayscale_8.png b/tests/files/palette_8_should_be_grayscale_8.png
index 5c0330c4..e49015a9 100644
Binary files a/tests/files/palette_8_should_be_grayscale_8.png and b/tests/files/palette_8_should_be_grayscale_8.png differ
diff --git a/tests/files/palette_8_should_be_palette_1.png b/tests/files/palette_8_should_be_palette_1.png
index 2977b017..8f1485de 100644
Binary files a/tests/files/palette_8_should_be_palette_1.png and b/tests/files/palette_8_should_be_palette_1.png differ
diff --git a/tests/files/palette_8_should_be_palette_2.png b/tests/files/palette_8_should_be_palette_2.png
index 3627ad2f..659f956b 100644
Binary files a/tests/files/palette_8_should_be_palette_2.png and b/tests/files/palette_8_should_be_palette_2.png differ
diff --git a/tests/files/palette_8_should_be_palette_4.png b/tests/files/palette_8_should_be_palette_4.png
index 72414145..a4edc7cc 100644
Binary files a/tests/files/palette_8_should_be_palette_4.png and b/tests/files/palette_8_should_be_palette_4.png differ
diff --git a/tests/files/palette_8_should_be_palette_8.png b/tests/files/palette_8_should_be_palette_8.png
index 3f4de11f..2bd7902d 100644
Binary files a/tests/files/palette_8_should_be_palette_8.png and b/tests/files/palette_8_should_be_palette_8.png differ
diff --git a/tests/files/palette_8_should_be_rgb.png b/tests/files/palette_8_should_be_rgb.png
index cb6b1678..2e0b4438 100644
Binary files a/tests/files/palette_8_should_be_rgb.png and b/tests/files/palette_8_should_be_rgb.png differ
diff --git a/tests/files/palette_8_should_be_rgba.png b/tests/files/palette_8_should_be_rgba.png
index 351a7a1b..047335f1 100644
Binary files a/tests/files/palette_8_should_be_rgba.png and b/tests/files/palette_8_should_be_rgba.png differ
diff --git a/tests/files/palette_should_be_reduced_with_both.png b/tests/files/palette_should_be_reduced_with_both.png
index ede9b2bc..7893dbd6 100644
Binary files a/tests/files/palette_should_be_reduced_with_both.png and b/tests/files/palette_should_be_reduced_with_both.png differ
diff --git a/tests/files/palette_should_be_reduced_with_dupes.png b/tests/files/palette_should_be_reduced_with_dupes.png
index bf8dfa92..e2d98723 100644
Binary files a/tests/files/palette_should_be_reduced_with_dupes.png and b/tests/files/palette_should_be_reduced_with_dupes.png differ
diff --git a/tests/files/palette_should_be_reduced_with_unused.png b/tests/files/palette_should_be_reduced_with_unused.png
index 9c0ac746..35787942 100644
Binary files a/tests/files/palette_should_be_reduced_with_unused.png and b/tests/files/palette_should_be_reduced_with_unused.png differ
diff --git a/tests/files/preserve_attrs.png b/tests/files/preserve_attrs.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/preserve_attrs.png and b/tests/files/preserve_attrs.png differ
diff --git a/tests/files/profile_adobe_rgb_disallow_gray.png b/tests/files/profile_adobe_rgb_disallow_gray.png
new file mode 100644
index 00000000..1db8cbc1
Binary files /dev/null and b/tests/files/profile_adobe_rgb_disallow_gray.png differ
diff --git a/tests/files/profile_gray_disallow_color.png b/tests/files/profile_gray_disallow_color.png
new file mode 100644
index 00000000..42c7aba2
Binary files /dev/null and b/tests/files/profile_gray_disallow_color.png differ
diff --git a/tests/files/profile_srgb_allow_gray.png b/tests/files/profile_srgb_allow_gray.png
new file mode 100644
index 00000000..3fd8aef7
Binary files /dev/null and b/tests/files/profile_srgb_allow_gray.png differ
diff --git a/tests/files/profile_srgb_no_strip_disallow_gray.png b/tests/files/profile_srgb_no_strip_disallow_gray.png
new file mode 100644
index 00000000..3fd8aef7
Binary files /dev/null and b/tests/files/profile_srgb_no_strip_disallow_gray.png differ
diff --git a/tests/files/quiet_mode.png b/tests/files/quiet_mode.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/quiet_mode.png and b/tests/files/quiet_mode.png differ
diff --git a/tests/files/raw_api.png b/tests/files/raw_api.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/raw_api.png and b/tests/files/raw_api.png differ
diff --git a/tests/files/rgb_16_should_be_grayscale_16.png b/tests/files/rgb_16_should_be_grayscale_16.png
index b79a99ee..1e1707da 100644
Binary files a/tests/files/rgb_16_should_be_grayscale_16.png and b/tests/files/rgb_16_should_be_grayscale_16.png differ
diff --git a/tests/files/rgb_16_should_be_grayscale_8.png b/tests/files/rgb_16_should_be_grayscale_8.png
index eb5375a3..ae72f7ee 100644
Binary files a/tests/files/rgb_16_should_be_grayscale_8.png and b/tests/files/rgb_16_should_be_grayscale_8.png differ
diff --git a/tests/files/rgb_16_should_be_palette_1.png b/tests/files/rgb_16_should_be_palette_1.png
index ee125faa..919337b1 100644
Binary files a/tests/files/rgb_16_should_be_palette_1.png and b/tests/files/rgb_16_should_be_palette_1.png differ
diff --git a/tests/files/rgb_16_should_be_palette_2.png b/tests/files/rgb_16_should_be_palette_2.png
index cae6ec7b..cc60991a 100644
Binary files a/tests/files/rgb_16_should_be_palette_2.png and b/tests/files/rgb_16_should_be_palette_2.png differ
diff --git a/tests/files/rgb_16_should_be_palette_4.png b/tests/files/rgb_16_should_be_palette_4.png
index 928de6ea..c576ffb0 100644
Binary files a/tests/files/rgb_16_should_be_palette_4.png and b/tests/files/rgb_16_should_be_palette_4.png differ
diff --git a/tests/files/rgb_16_should_be_palette_8.png b/tests/files/rgb_16_should_be_palette_8.png
index 6492a2f9..c5417faa 100644
Binary files a/tests/files/rgb_16_should_be_palette_8.png and b/tests/files/rgb_16_should_be_palette_8.png differ
diff --git a/tests/files/rgb_16_should_be_rgb_16.png b/tests/files/rgb_16_should_be_rgb_16.png
index e1f41729..9868d032 100644
Binary files a/tests/files/rgb_16_should_be_rgb_16.png and b/tests/files/rgb_16_should_be_rgb_16.png differ
diff --git a/tests/files/rgb_16_should_be_rgb_8.png b/tests/files/rgb_16_should_be_rgb_8.png
index f1fa62bc..0d8a0466 100644
Binary files a/tests/files/rgb_16_should_be_rgb_8.png and b/tests/files/rgb_16_should_be_rgb_8.png differ
diff --git a/tests/files/rgb_8_should_be_grayscale_8.png b/tests/files/rgb_8_should_be_grayscale_8.png
index b50aaf13..733074a5 100644
Binary files a/tests/files/rgb_8_should_be_grayscale_8.png and b/tests/files/rgb_8_should_be_grayscale_8.png differ
diff --git a/tests/files/rgb_8_should_be_palette_1.png b/tests/files/rgb_8_should_be_palette_1.png
index cfa64eb2..ca61e1af 100644
Binary files a/tests/files/rgb_8_should_be_palette_1.png and b/tests/files/rgb_8_should_be_palette_1.png differ
diff --git a/tests/files/rgb_8_should_be_palette_2.png b/tests/files/rgb_8_should_be_palette_2.png
index c479811f..857f7ed4 100644
Binary files a/tests/files/rgb_8_should_be_palette_2.png and b/tests/files/rgb_8_should_be_palette_2.png differ
diff --git a/tests/files/rgb_8_should_be_palette_4.png b/tests/files/rgb_8_should_be_palette_4.png
index 818c30d3..4535c031 100644
Binary files a/tests/files/rgb_8_should_be_palette_4.png and b/tests/files/rgb_8_should_be_palette_4.png differ
diff --git a/tests/files/rgb_8_should_be_palette_8.png b/tests/files/rgb_8_should_be_palette_8.png
index 9a7488a7..488e94aa 100644
Binary files a/tests/files/rgb_8_should_be_palette_8.png and b/tests/files/rgb_8_should_be_palette_8.png differ
diff --git a/tests/files/rgb_8_should_be_rgb_8.png b/tests/files/rgb_8_should_be_rgb_8.png
index 3f9bdb1a..16ab08aa 100644
Binary files a/tests/files/rgb_8_should_be_rgb_8.png and b/tests/files/rgb_8_should_be_rgb_8.png differ
diff --git a/tests/files/rgb_trns_8_should_be_palette_8.png b/tests/files/rgb_trns_8_should_be_palette_8.png
index f3bdd70a..d8ec8638 100644
Binary files a/tests/files/rgb_trns_8_should_be_palette_8.png and b/tests/files/rgb_trns_8_should_be_palette_8.png differ
diff --git a/tests/files/rgba_16_reduce_alpha.png b/tests/files/rgba_16_reduce_alpha.png
index 4792bf00..7911a123 100644
Binary files a/tests/files/rgba_16_reduce_alpha.png and b/tests/files/rgba_16_reduce_alpha.png differ
diff --git a/tests/files/rgba_16_should_be_grayscale_16.png b/tests/files/rgba_16_should_be_grayscale_16.png
index bf178b63..be21996e 100644
Binary files a/tests/files/rgba_16_should_be_grayscale_16.png and b/tests/files/rgba_16_should_be_grayscale_16.png differ
diff --git a/tests/files/rgba_16_should_be_grayscale_8.png b/tests/files/rgba_16_should_be_grayscale_8.png
index d6460208..14499c7c 100644
Binary files a/tests/files/rgba_16_should_be_grayscale_8.png and b/tests/files/rgba_16_should_be_grayscale_8.png differ
diff --git a/tests/files/rgba_16_should_be_grayscale_alpha_16.png b/tests/files/rgba_16_should_be_grayscale_alpha_16.png
index aebed341..5b9a894b 100644
Binary files a/tests/files/rgba_16_should_be_grayscale_alpha_16.png and b/tests/files/rgba_16_should_be_grayscale_alpha_16.png differ
diff --git a/tests/files/rgba_16_should_be_grayscale_alpha_8.png b/tests/files/rgba_16_should_be_grayscale_alpha_8.png
index 36dc1679..2af41686 100644
Binary files a/tests/files/rgba_16_should_be_grayscale_alpha_8.png and b/tests/files/rgba_16_should_be_grayscale_alpha_8.png differ
diff --git a/tests/files/rgba_16_should_be_palette_1.png b/tests/files/rgba_16_should_be_palette_1.png
index 94f15f01..e919bb90 100644
Binary files a/tests/files/rgba_16_should_be_palette_1.png and b/tests/files/rgba_16_should_be_palette_1.png differ
diff --git a/tests/files/rgba_16_should_be_palette_2.png b/tests/files/rgba_16_should_be_palette_2.png
index 66a68782..9b644011 100644
Binary files a/tests/files/rgba_16_should_be_palette_2.png and b/tests/files/rgba_16_should_be_palette_2.png differ
diff --git a/tests/files/rgba_16_should_be_palette_4.png b/tests/files/rgba_16_should_be_palette_4.png
index 8c159806..aa0f0bc3 100644
Binary files a/tests/files/rgba_16_should_be_palette_4.png and b/tests/files/rgba_16_should_be_palette_4.png differ
diff --git a/tests/files/rgba_16_should_be_palette_8.png b/tests/files/rgba_16_should_be_palette_8.png
index 081b2a64..3f1ac86c 100644
Binary files a/tests/files/rgba_16_should_be_palette_8.png and b/tests/files/rgba_16_should_be_palette_8.png differ
diff --git a/tests/files/rgba_16_should_be_rgb_16.png b/tests/files/rgba_16_should_be_rgb_16.png
index 2c2bf7f2..2ab9986a 100644
Binary files a/tests/files/rgba_16_should_be_rgb_16.png and b/tests/files/rgba_16_should_be_rgb_16.png differ
diff --git a/tests/files/rgba_16_should_be_rgb_8.png b/tests/files/rgba_16_should_be_rgb_8.png
index 852f8ffa..a38fd8a4 100644
Binary files a/tests/files/rgba_16_should_be_rgb_8.png and b/tests/files/rgba_16_should_be_rgb_8.png differ
diff --git a/tests/files/rgba_16_should_be_rgb_trns_16.png b/tests/files/rgba_16_should_be_rgb_trns_16.png
index 5174bd31..a26673a9 100644
Binary files a/tests/files/rgba_16_should_be_rgb_trns_16.png and b/tests/files/rgba_16_should_be_rgb_trns_16.png differ
diff --git a/tests/files/rgba_16_should_be_rgba_16.png b/tests/files/rgba_16_should_be_rgba_16.png
index bfc0e07d..656ee55c 100644
Binary files a/tests/files/rgba_16_should_be_rgba_16.png and b/tests/files/rgba_16_should_be_rgba_16.png differ
diff --git a/tests/files/rgba_16_should_be_rgba_8.png b/tests/files/rgba_16_should_be_rgba_8.png
index ccc59f7d..99817294 100644
Binary files a/tests/files/rgba_16_should_be_rgba_8.png and b/tests/files/rgba_16_should_be_rgba_8.png differ
diff --git a/tests/files/rgba_8_reduce_alpha.png b/tests/files/rgba_8_reduce_alpha.png
index 22a5a059..cbbcd8bf 100644
Binary files a/tests/files/rgba_8_reduce_alpha.png and b/tests/files/rgba_8_reduce_alpha.png differ
diff --git a/tests/files/rgba_8_should_be_grayscale_8.png b/tests/files/rgba_8_should_be_grayscale_8.png
index 318d4012..e5f27e9b 100644
Binary files a/tests/files/rgba_8_should_be_grayscale_8.png and b/tests/files/rgba_8_should_be_grayscale_8.png differ
diff --git a/tests/files/rgba_8_should_be_grayscale_alpha_8.png b/tests/files/rgba_8_should_be_grayscale_alpha_8.png
index bd78424a..fad729b3 100644
Binary files a/tests/files/rgba_8_should_be_grayscale_alpha_8.png and b/tests/files/rgba_8_should_be_grayscale_alpha_8.png differ
diff --git a/tests/files/rgba_8_should_be_palette_1.png b/tests/files/rgba_8_should_be_palette_1.png
index b74454b7..1a098842 100644
Binary files a/tests/files/rgba_8_should_be_palette_1.png and b/tests/files/rgba_8_should_be_palette_1.png differ
diff --git a/tests/files/rgba_8_should_be_palette_2.png b/tests/files/rgba_8_should_be_palette_2.png
index 834bfe84..e1df46a6 100644
Binary files a/tests/files/rgba_8_should_be_palette_2.png and b/tests/files/rgba_8_should_be_palette_2.png differ
diff --git a/tests/files/rgba_8_should_be_palette_4.png b/tests/files/rgba_8_should_be_palette_4.png
index 2efe3501..7df71f0c 100644
Binary files a/tests/files/rgba_8_should_be_palette_4.png and b/tests/files/rgba_8_should_be_palette_4.png differ
diff --git a/tests/files/rgba_8_should_be_palette_8.png b/tests/files/rgba_8_should_be_palette_8.png
index 4fe2aa15..575d1113 100644
Binary files a/tests/files/rgba_8_should_be_palette_8.png and b/tests/files/rgba_8_should_be_palette_8.png differ
diff --git a/tests/files/rgba_8_should_be_rgb_8.png b/tests/files/rgba_8_should_be_rgb_8.png
index 14b16cd3..5f286d38 100644
Binary files a/tests/files/rgba_8_should_be_rgb_8.png and b/tests/files/rgba_8_should_be_rgb_8.png differ
diff --git a/tests/files/rgba_8_should_be_rgb_trns_8.png b/tests/files/rgba_8_should_be_rgb_trns_8.png
index 783ccf29..d27a8b09 100644
Binary files a/tests/files/rgba_8_should_be_rgb_trns_8.png and b/tests/files/rgba_8_should_be_rgb_trns_8.png differ
diff --git a/tests/files/rgba_8_should_be_rgba_8.png b/tests/files/rgba_8_should_be_rgba_8.png
index 18a49183..a3e6e8c4 100644
Binary files a/tests/files/rgba_8_should_be_rgba_8.png and b/tests/files/rgba_8_should_be_rgba_8.png differ
diff --git a/tests/files/small_files.png b/tests/files/small_files.png
index 576b88c9..106c5729 100644
Binary files a/tests/files/small_files.png and b/tests/files/small_files.png differ
diff --git a/tests/files/strip_headers_all.png b/tests/files/strip_chunks_all.png
similarity index 100%
rename from tests/files/strip_headers_all.png
rename to tests/files/strip_chunks_all.png
diff --git a/tests/files/strip_headers_list.png b/tests/files/strip_chunks_list.png
similarity index 100%
rename from tests/files/strip_headers_list.png
rename to tests/files/strip_chunks_list.png
diff --git a/tests/files/strip_headers_none.png b/tests/files/strip_chunks_none.png
similarity index 100%
rename from tests/files/strip_headers_none.png
rename to tests/files/strip_chunks_none.png
diff --git a/tests/files/strip_headers_safe.png b/tests/files/strip_chunks_safe.png
similarity index 100%
rename from tests/files/strip_headers_safe.png
rename to tests/files/strip_chunks_safe.png
diff --git a/tests/files/zopfli_mode.png b/tests/files/zopfli_mode.png
index 4f3ba4f7..7c7f12ce 100644
Binary files a/tests/files/zopfli_mode.png and b/tests/files/zopfli_mode.png differ
diff --git a/tests/flags.rs b/tests/flags.rs
index 4d4feb63..ed4770cf 100644
--- a/tests/flags.rs
+++ b/tests/flags.rs
@@ -1,5 +1,3 @@
-#[cfg(feature = "filetime")]
-use std::cell::RefCell;
 #[cfg(feature = "zopfli")]
 use std::num::NonZeroU8;
 use std::{
@@ -10,19 +8,17 @@ use std::{
 use indexmap::indexset;
 use oxipng::{internal_tests::*, *};
 
+const GRAY: u8 = 0;
 const RGB: u8 = 2;
 const INDEXED: u8 = 3;
 
-fn get_opts(input: &Path) -> (OutFile, oxipng::Options) {
-    let mut options = oxipng::Options {
+fn get_opts(input: &Path) -> (OutFile, Options) {
+    let options = Options {
         force: true,
         fast_evaluation: false,
+        filter: indexset! {RowFilter::None},
         ..Default::default()
     };
-    let mut filter = IndexSet::new();
-    filter.insert(RowFilter::None);
-    options.filter = filter;
-
     (OutFile::from_path(input.with_extension("out.png")), options)
 }
 
@@ -39,15 +35,19 @@ fn test_it_converts_callbacks<CBPRE, CBPOST>(
     mut callback_pre: CBPRE,
     mut callback_post: CBPOST,
 ) where
-    CBPOST: FnMut(&Path),
-    CBPRE: FnMut(&Path),
+    CBPOST: FnMut(&PngData),
+    CBPRE: FnMut(&PngData),
 {
-    let png = PngData::new(&input, opts).unwrap();
+    let parse_opts = Options {
+        fix_errors: true,
+        ..Default::default()
+    };
+    let png = PngData::new(&input, &parse_opts).unwrap();
 
     assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_in);
     assert_eq!(png.raw.ihdr.bit_depth, bit_depth_in);
 
-    callback_pre(&input);
+    callback_pre(&png);
 
     match oxipng::optimize(&InFile::Path(input), output, opts) {
         Ok(_) => (),
@@ -56,8 +56,6 @@ fn test_it_converts_callbacks<CBPRE, CBPOST>(
     let output = output.path().unwrap();
     assert!(output.exists());
 
-    callback_post(output);
-
     let png = match PngData::new(output, opts) {
         Ok(x) => x,
         Err(x) => {
@@ -66,6 +64,8 @@ fn test_it_converts_callbacks<CBPRE, CBPOST>(
         }
     };
 
+    callback_post(&png);
+
     assert_eq!(png.raw.ihdr.color_type.png_header_code(), color_type_out);
     assert_eq!(png.raw.ihdr.bit_depth, bit_depth_out);
 
@@ -205,139 +205,111 @@ fn count_chunk(png: &PngData, name: &[u8; 4]) -> usize {
 }
 
 #[test]
-fn strip_headers_list() {
-    let input = PathBuf::from("tests/files/strip_headers_list.png");
+fn strip_chunks_list() {
+    let input = PathBuf::from("tests/files/strip_chunks_list.png");
     let (output, mut opts) = get_opts(&input);
     opts.strip = StripChunks::Strip(indexset![*b"iCCP", *b"tEXt"]);
 
-    let png = PngData::new(&input, &Options::default()).unwrap();
-
-    assert_eq!(count_chunk(&png, b"tEXt"), 3);
-    assert_eq!(count_chunk(&png, b"iTXt"), 1);
-    assert_eq!(count_chunk(&png, b"iCCP"), 1);
-
-    match oxipng::optimize(&InFile::Path(input), &output, &opts) {
-        Ok(_) => (),
-        Err(x) => panic!("{}", x),
-    };
-    let output = output.path().unwrap();
-    assert!(output.exists());
-
-    let png = match PngData::new(output, &opts) {
-        Ok(x) => x,
-        Err(x) => {
-            remove_file(output).ok();
-            panic!("{}", x)
-        }
-    };
-
-    assert_eq!(count_chunk(&png, b"tEXt"), 0);
-    assert_eq!(count_chunk(&png, b"iTXt"), 1);
-    assert_eq!(count_chunk(&png, b"iCCP"), 0);
-
-    remove_file(output).ok();
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        RGB,
+        BitDepth::Eight,
+        RGB,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(count_chunk(png, b"tEXt"), 3);
+            assert_eq!(count_chunk(png, b"iTXt"), 1);
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
+        |png| {
+            assert_eq!(count_chunk(png, b"tEXt"), 0);
+            assert_eq!(count_chunk(png, b"iTXt"), 1);
+            assert_eq!(count_chunk(png, b"iCCP"), 0);
+        },
+    );
 }
 
 #[test]
-fn strip_headers_safe() {
-    let input = PathBuf::from("tests/files/strip_headers_safe.png");
+fn strip_chunks_safe() {
+    let input = PathBuf::from("tests/files/strip_chunks_safe.png");
     let (output, mut opts) = get_opts(&input);
     opts.strip = StripChunks::Safe;
 
-    let png = PngData::new(&input, &Options::default()).unwrap();
-
-    assert_eq!(count_chunk(&png, b"tEXt"), 3);
-    assert_eq!(count_chunk(&png, b"iTXt"), 1);
-    assert_eq!(count_chunk(&png, b"iCCP"), 1);
-
-    match oxipng::optimize(&InFile::Path(input), &output, &opts) {
-        Ok(_) => (),
-        Err(x) => panic!("{}", x),
-    };
-    let output = output.path().unwrap();
-    assert!(output.exists());
-
-    let png = match PngData::new(output, &opts) {
-        Ok(x) => x,
-        Err(x) => {
-            remove_file(output).ok();
-            panic!("{}", x)
-        }
-    };
-
-    assert_eq!(count_chunk(&png, b"tEXt"), 0);
-    assert_eq!(count_chunk(&png, b"iTXt"), 0);
-    assert_eq!(count_chunk(&png, b"sRGB"), 1);
-
-    remove_file(output).ok();
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        RGB,
+        BitDepth::Eight,
+        RGB,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(count_chunk(png, b"tEXt"), 3);
+            assert_eq!(count_chunk(png, b"iTXt"), 1);
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
+        |png| {
+            assert_eq!(count_chunk(png, b"tEXt"), 0);
+            assert_eq!(count_chunk(png, b"iTXt"), 0);
+            assert_eq!(count_chunk(png, b"iCCP"), 0);
+        },
+    );
 }
 
 #[test]
-fn strip_headers_all() {
-    let input = PathBuf::from("tests/files/strip_headers_all.png");
+fn strip_chunks_all() {
+    let input = PathBuf::from("tests/files/strip_chunks_all.png");
     let (output, mut opts) = get_opts(&input);
     opts.strip = StripChunks::All;
 
-    let png = PngData::new(&input, &Options::default()).unwrap();
-
-    assert_eq!(count_chunk(&png, b"tEXt"), 3);
-    assert_eq!(count_chunk(&png, b"iTXt"), 1);
-    assert_eq!(count_chunk(&png, b"iCCP"), 1);
-
-    match oxipng::optimize(&InFile::Path(input), &output, &opts) {
-        Ok(_) => (),
-        Err(x) => panic!("{}", x),
-    };
-    let output = output.path().unwrap();
-    assert!(output.exists());
-
-    let png = match PngData::new(output, &opts) {
-        Ok(x) => x,
-        Err(x) => {
-            remove_file(output).ok();
-            panic!("{}", x)
-        }
-    };
-
-    assert_eq!(count_chunk(&png, b"tEXt"), 0);
-    assert_eq!(count_chunk(&png, b"iTXt"), 0);
-    assert_eq!(count_chunk(&png, b"iCCP"), 0);
-
-    remove_file(output).ok();
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        RGB,
+        BitDepth::Eight,
+        RGB,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(count_chunk(png, b"tEXt"), 3);
+            assert_eq!(count_chunk(png, b"iTXt"), 1);
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
+        |png| {
+            assert_eq!(count_chunk(png, b"tEXt"), 0);
+            assert_eq!(count_chunk(png, b"iTXt"), 0);
+            assert_eq!(count_chunk(png, b"iCCP"), 0);
+        },
+    );
 }
 
 #[test]
-fn strip_headers_none() {
-    let input = PathBuf::from("tests/files/strip_headers_none.png");
+fn strip_chunks_none() {
+    let input = PathBuf::from("tests/files/strip_chunks_none.png");
     let (output, mut opts) = get_opts(&input);
     opts.strip = StripChunks::None;
 
-    let png = PngData::new(&input, &Options::default()).unwrap();
-
-    assert_eq!(count_chunk(&png, b"tEXt"), 3);
-    assert_eq!(count_chunk(&png, b"iTXt"), 1);
-    assert_eq!(count_chunk(&png, b"iCCP"), 1);
-
-    match oxipng::optimize(&InFile::Path(input), &output, &opts) {
-        Ok(_) => (),
-        Err(x) => panic!("{}", x),
-    };
-    let output = output.path().unwrap();
-    assert!(output.exists());
-
-    let png = match PngData::new(output, &opts) {
-        Ok(x) => x,
-        Err(x) => {
-            remove_file(output).ok();
-            panic!("{}", x)
-        }
-    };
-
-    assert_eq!(count_chunk(&png, b"tEXt"), 3);
-    assert_eq!(count_chunk(&png, b"iTXt"), 1);
-    assert_eq!(count_chunk(&png, b"iCCP"), 1);
-
-    remove_file(output).ok();
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        RGB,
+        BitDepth::Eight,
+        RGB,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(count_chunk(png, b"tEXt"), 3);
+            assert_eq!(count_chunk(png, b"iTXt"), 1);
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
+        |png| {
+            assert_eq!(count_chunk(png, b"tEXt"), 3);
+            assert_eq!(count_chunk(png, b"iTXt"), 1);
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
+    );
 }
 
 #[test]
@@ -346,28 +318,21 @@ fn interlacing_0_to_1() {
     let (output, mut opts) = get_opts(&input);
     opts.interlace = Some(Interlacing::Adam7);
 
-    let png = PngData::new(&input, &opts).unwrap();
-
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
-
-    match oxipng::optimize(&InFile::Path(input), &output, &opts) {
-        Ok(_) => (),
-        Err(x) => panic!("{}", x),
-    };
-    let output = output.path().unwrap();
-    assert!(output.exists());
-
-    let png = match PngData::new(output, &opts) {
-        Ok(x) => x,
-        Err(x) => {
-            remove_file(output).ok();
-            panic!("{}", x)
-        }
-    };
-
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
-
-    remove_file(output).ok();
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        RGB,
+        BitDepth::Eight,
+        RGB,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
+        },
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
+        },
+    );
 }
 
 #[test]
@@ -376,28 +341,21 @@ fn interlacing_1_to_0() {
     let (output, mut opts) = get_opts(&input);
     opts.interlace = Some(Interlacing::None);
 
-    let png = PngData::new(&input, &opts).unwrap();
-
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
-
-    match oxipng::optimize(&InFile::Path(input), &output, &opts) {
-        Ok(_) => (),
-        Err(x) => panic!("{}", x),
-    };
-    let output = output.path().unwrap();
-    assert!(output.exists());
-
-    let png = match PngData::new(output, &opts) {
-        Ok(x) => x,
-        Err(x) => {
-            remove_file(output).ok();
-            panic!("{}", x)
-        }
-    };
-
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
-
-    remove_file(output).ok();
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        RGB,
+        BitDepth::Eight,
+        RGB,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
+        },
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
+        },
+    );
 }
 
 #[test]
@@ -406,32 +364,21 @@ fn interlacing_0_to_1_small_files() {
     let (output, mut opts) = get_opts(&input);
     opts.interlace = Some(Interlacing::Adam7);
 
-    let png = PngData::new(&input, &opts).unwrap();
-
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
-    assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
-    assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
-
-    match oxipng::optimize(&InFile::Path(input), &output, &opts) {
-        Ok(_) => (),
-        Err(x) => panic!("{}", x),
-    };
-    let output = output.path().unwrap();
-    assert!(output.exists());
-
-    let png = match PngData::new(output, &opts) {
-        Ok(x) => x,
-        Err(x) => {
-            remove_file(output).ok();
-            panic!("{}", x)
-        }
-    };
-
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
-    assert_eq!(png.raw.ihdr.color_type.png_header_code(), RGB);
-    assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
-
-    remove_file(output).ok();
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        INDEXED,
+        BitDepth::Eight,
+        RGB,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
+        },
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
+        },
+    );
 }
 
 #[test]
@@ -440,32 +387,21 @@ fn interlacing_1_to_0_small_files() {
     let (output, mut opts) = get_opts(&input);
     opts.interlace = Some(Interlacing::None);
 
-    let png = PngData::new(&input, &opts).unwrap();
-
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
-    assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
-    assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
-
-    match oxipng::optimize(&InFile::Path(input), &output, &opts) {
-        Ok(_) => (),
-        Err(x) => panic!("{}", x),
-    };
-    let output = output.path().unwrap();
-    assert!(output.exists());
-
-    let png = match PngData::new(output, &opts) {
-        Ok(x) => x,
-        Err(x) => {
-            remove_file(output).ok();
-            panic!("{}", x)
-        }
-    };
-
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
-    assert_eq!(png.raw.ihdr.color_type.png_header_code(), RGB);
-    assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
-
-    remove_file(output).ok();
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        INDEXED,
+        BitDepth::Eight,
+        RGB,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
+        },
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
+        },
+    );
 }
 
 #[test]
@@ -473,13 +409,42 @@ fn interlaced_0_to_1_other_filter_mode() {
     let input = PathBuf::from("tests/files/interlaced_0_to_1_other_filter_mode.png");
     let (output, mut opts) = get_opts(&input);
     opts.interlace = Some(Interlacing::Adam7);
-    let mut filter = IndexSet::new();
-    filter.insert(RowFilter::Paeth);
-    opts.filter = filter;
+    opts.filter = indexset! {RowFilter::Paeth};
+
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        RGB,
+        BitDepth::Sixteen,
+        GRAY,
+        BitDepth::Sixteen,
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
+        },
+        |png| {
+            assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
+        },
+    );
+}
 
-    let png = PngData::new(&input, &opts).unwrap();
+#[test]
+fn preserve_attrs() {
+    let input = PathBuf::from("tests/files/preserve_attrs.png");
 
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::None);
+    #[cfg(feature = "filetime")]
+    let meta_input = input
+        .metadata()
+        .expect("unable to get file metadata for output file");
+    #[cfg(feature = "filetime")]
+    let atime_canon = filetime::FileTime::from_last_access_time(&meta_input);
+    #[cfg(feature = "filetime")]
+    let mtime_canon = filetime::FileTime::from_last_modification_time(&meta_input);
+
+    let (mut output, opts) = get_opts(&input);
+    if let OutFile::Path { preserve_attrs, .. } = &mut output {
+        *preserve_attrs = true;
+    }
 
     match oxipng::optimize(&InFile::Path(input), &output, &opts) {
         Ok(_) => (),
@@ -488,7 +453,24 @@ fn interlaced_0_to_1_other_filter_mode() {
     let output = output.path().unwrap();
     assert!(output.exists());
 
-    let png = match PngData::new(output, &opts) {
+    #[cfg(feature = "filetime")]
+    let meta_output = output
+        .metadata()
+        .expect("unable to get file metadata for output file");
+    #[cfg(feature = "filetime")]
+    assert_eq!(
+        &atime_canon,
+        &filetime::FileTime::from_last_access_time(&meta_output),
+        "expected access time to be identical to that of input",
+    );
+    #[cfg(feature = "filetime")]
+    assert_eq!(
+        &mtime_canon,
+        &filetime::FileTime::from_last_modification_time(&meta_output),
+        "expected modification time to be identical to that of input",
+    );
+
+    match PngData::new(output, &opts) {
         Ok(x) => x,
         Err(x) => {
             remove_file(output).ok();
@@ -496,61 +478,50 @@ fn interlaced_0_to_1_other_filter_mode() {
         }
     };
 
-    assert_eq!(png.raw.ihdr.interlaced, Interlacing::Adam7);
-
     remove_file(output).ok();
+
+    // TODO: Actually check permissions
 }
 
 #[test]
-fn preserve_attrs() {
-    let input = PathBuf::from("tests/files/preserve_attrs.png");
-
-    #[cfg(feature = "filetime")]
-    let atime_canon = RefCell::new(filetime::FileTime::from_unix_time(0, 0));
-    #[cfg(feature = "filetime")]
-    let mtime_canon = RefCell::new(filetime::FileTime::from_unix_time(0, 0));
+fn fix_errors() {
+    let input = PathBuf::from("tests/files/fix_errors.png");
+    let (output, mut opts) = get_opts(&input);
+    opts.fix_errors = true;
 
-    let (mut output, opts) = get_opts(&input);
-    if let OutFile::Path { preserve_attrs, .. } = &mut output {
-        *preserve_attrs = true;
-    }
+    test_it_converts(
+        input,
+        &output,
+        &opts,
+        INDEXED,
+        BitDepth::Eight,
+        INDEXED,
+        BitDepth::Eight,
+    );
+}
 
-    #[cfg(feature = "filetime")]
-    let callback_pre = |path_in: &Path| {
-        let meta_input = path_in
-            .metadata()
-            .expect("unable to get file metadata for output file");
+#[test]
+fn no_grayscale_change() {
+    let input = PathBuf::from("tests/files/rgb_8_should_be_grayscale_8.png");
+    let (output, mut opts) = get_opts(&input);
+    opts.grayscale_reduction = false;
 
-        atime_canon.replace(filetime::FileTime::from_last_access_time(&meta_input));
-        mtime_canon.replace(filetime::FileTime::from_last_modification_time(&meta_input));
-    };
-    #[cfg(not(feature = "filetime"))]
-    let callback_pre = |_: &Path| {};
+    test_it_converts(
+        input,
+        &output,
+        &opts,
+        RGB,
+        BitDepth::Eight,
+        INDEXED,
+        BitDepth::Eight,
+    );
+}
 
-    #[cfg(feature = "filetime")]
-    let callback_post = |path_out: &Path| {
-        let meta_output = path_out
-            .metadata()
-            .expect("unable to get file metadata for output file");
-
-        let cellref_atime_canon = atime_canon.borrow();
-        let cellref_mtime_canon = mtime_canon.borrow();
-        let ref_atime_canon: &filetime::FileTime = &cellref_atime_canon;
-        let ref_mtime_canon: &filetime::FileTime = &cellref_mtime_canon;
-
-        assert_eq!(
-            ref_atime_canon,
-            &filetime::FileTime::from_last_access_time(&meta_output),
-            "expected access time to be identical to that of input",
-        );
-        assert_eq!(
-            ref_mtime_canon,
-            &filetime::FileTime::from_last_modification_time(&meta_output),
-            "expected modification time to be identical to that of input",
-        );
-    };
-    #[cfg(not(feature = "filetime"))]
-    let callback_post = |_: &Path| {};
+#[test]
+fn profile_adobe_rgb_disallow_gray() {
+    let input = PathBuf::from("tests/files/profile_adobe_rgb_disallow_gray.png");
+    let (output, mut opts) = get_opts(&input);
+    opts.strip = StripChunks::Safe;
 
     test_it_converts_callbacks(
         input,
@@ -558,79 +529,84 @@ fn preserve_attrs() {
         &opts,
         RGB,
         BitDepth::Eight,
-        RGB,
+        INDEXED,
         BitDepth::Eight,
-        callback_pre,
-        callback_post,
+        |png| {
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
+        |png| {
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
     );
-
-    // TODO: Actually check permissions
 }
 
 #[test]
-fn fix_errors() {
-    let input = PathBuf::from("tests/files/fix_errors.png");
+fn profile_srgb_allow_gray() {
+    let input = PathBuf::from("tests/files/profile_srgb_allow_gray.png");
     let (output, mut opts) = get_opts(&input);
-    opts.fix_errors = true;
-
-    let png = PngData::new(&input, &opts).unwrap();
-
-    assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
-    assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
-
-    match oxipng::optimize(&InFile::Path(input), &output, &opts) {
-        Ok(_) => (),
-        Err(x) => panic!("{}", x),
-    };
-    let output = output.path().unwrap();
-    assert!(output.exists());
-
-    let png = match PngData::new(output, &Options::default()) {
-        Ok(x) => x,
-        Err(x) => {
-            remove_file(output).ok();
-            panic!("{}", x)
-        }
-    };
-
-    assert_eq!(png.raw.ihdr.color_type.png_header_code(), INDEXED);
-    assert_eq!(png.raw.ihdr.bit_depth, BitDepth::Eight);
+    opts.strip = StripChunks::Safe;
 
-    // Cannot check if pixels are equal because image crate cannot read corrupt (input) PNGs
-    remove_file(output).ok();
+    test_it_converts_callbacks(
+        input,
+        &output,
+        &opts,
+        RGB,
+        BitDepth::Eight,
+        GRAY,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
+        |png| {
+            assert_eq!(count_chunk(png, b"iCCP"), 0);
+            assert_eq!(count_chunk(png, b"sRGB"), 0);
+        },
+    );
 }
 
 #[test]
-fn no_color_type_change() {
-    let input = PathBuf::from("tests/files/palette_8_should_be_rgb.png");
+fn profile_srgb_no_strip_disallow_gray() {
+    let input = PathBuf::from("tests/files/profile_srgb_no_strip_disallow_gray.png");
     let (output, mut opts) = get_opts(&input);
-    opts.color_type_reduction = false;
+    opts.strip = StripChunks::None;
 
-    test_it_converts(
+    test_it_converts_callbacks(
         input,
         &output,
         &opts,
-        INDEXED,
+        RGB,
         BitDepth::Eight,
         INDEXED,
-        BitDepth::One,
+        BitDepth::Eight,
+        |png| {
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
+        |png| {
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
     );
 }
 
 #[test]
-fn no_grayscale_change() {
-    let input = PathBuf::from("tests/files/rgb_8_should_be_grayscale_8.png");
+fn profile_gray_disallow_color() {
+    let input = PathBuf::from("tests/files/profile_gray_disallow_color.png");
     let (output, mut opts) = get_opts(&input);
-    opts.grayscale_reduction = false;
+    opts.strip = StripChunks::Safe;
 
-    test_it_converts(
+    test_it_converts_callbacks(
         input,
         &output,
         &opts,
-        RGB,
+        GRAY,
         BitDepth::Eight,
-        INDEXED,
+        GRAY,
         BitDepth::Eight,
+        |png| {
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
+        |png| {
+            assert_eq!(count_chunk(png, b"iCCP"), 1);
+        },
     );
 }