Remove rgb::Image::ignore_alpha for simplicity (#28)

rgb::Image::format can be set to an opaque format instead.

Also do not premultiply samples with alpha before dropping alpha.
diff --git a/src/capi/reformat.rs b/src/capi/reformat.rs
index 31e1c68..935cf9a 100644
--- a/src/capi/reformat.rs
+++ b/src/capi/reformat.rs
@@ -41,7 +41,7 @@
             format: rgb.format,
             chroma_upsampling: rgb.chroma_upsampling,
             chroma_downsampling: rgb.chroma_downsampling,
-            ignore_alpha: rgb.ignore_alpha,
+            ignore_alpha: false,
             alpha_premultiplied: rgb.alpha_premultiplied,
             is_float: rgb.is_float,
             max_threads: rgb.max_threads,
@@ -54,20 +54,33 @@
 impl From<*mut avifRGBImage> for rgb::Image {
     fn from(rgb: *mut avifRGBImage) -> rgb::Image {
         let rgb = unsafe { &(*rgb) };
-        rgb::Image {
+        let dst = rgb::Image {
             width: rgb.width,
             height: rgb.height,
             depth: rgb.depth,
             format: rgb.format,
             chroma_upsampling: rgb.chroma_upsampling,
             chroma_downsampling: rgb.chroma_downsampling,
-            ignore_alpha: rgb.ignore_alpha,
             alpha_premultiplied: rgb.alpha_premultiplied,
             is_float: rgb.is_float,
             max_threads: rgb.max_threads,
             pixels: Some(Pixels::Pointer(rgb.pixels)),
             row_bytes: rgb.row_bytes,
-        }
+        };
+        let format = match (rgb.format, rgb.ignore_alpha) {
+            (rgb::Format::Rgb, _) => rgb::Format::Rgb,
+            (rgb::Format::Rgba, true) => rgb::Format::Rgb,
+            (rgb::Format::Rgba, false) => rgb::Format::Rgba,
+            (rgb::Format::Argb, true) => rgb::Format::Rgb,
+            (rgb::Format::Argb, false) => rgb::Format::Argb,
+            (rgb::Format::Bgr, _) => rgb::Format::Bgr,
+            (rgb::Format::Bgra, true) => rgb::Format::Bgr,
+            (rgb::Format::Bgra, false) => rgb::Format::Bgra,
+            (rgb::Format::Abgr, true) => rgb::Format::Bgr,
+            (rgb::Format::Abgr, false) => rgb::Format::Abgr,
+            (rgb::Format::Rgb565, _) => rgb::Format::Rgb565,
+        };
+        dst.shuffle_channels_to(format).unwrap()
     }
 }
 
diff --git a/src/reformat/libyuv.rs b/src/reformat/libyuv.rs
index 46f5461..abe1e46 100644
--- a/src/reformat/libyuv.rs
+++ b/src/reformat/libyuv.rs
@@ -322,16 +322,12 @@
     }
 }
 
-pub fn yuv_to_rgb(
-    image: &image::Image,
-    rgb: &mut rgb::Image,
-    reformat_alpha: bool,
-) -> AvifResult<bool> {
+pub fn yuv_to_rgb(image: &image::Image, rgb: &mut rgb::Image) -> AvifResult<bool> {
     if rgb.depth != 8 || (image.depth != 8 && image.depth != 10 && image.depth != 12) {
         return Err(AvifError::NotImplemented);
     }
     let (matrix_yuv, matrix_yvu) = find_constants(image).ok_or(AvifError::NotImplemented)?;
-    let alpha_preferred = reformat_alpha && image.has_alpha();
+    let alpha_preferred = rgb.has_alpha() && image.has_alpha();
     let conversion_function =
         find_conversion_function(image.yuv_format, image.depth, rgb, alpha_preferred)
             .ok_or(AvifError::NotImplemented)?;
diff --git a/src/reformat/mod.rs b/src/reformat/mod.rs
index edad514..8d21d8c 100644
--- a/src/reformat/mod.rs
+++ b/src/reformat/mod.rs
@@ -16,11 +16,7 @@
     use crate::reformat::*;
     use crate::*;
 
-    pub fn yuv_to_rgb(
-        _image: &image::Image,
-        _rgb: &mut rgb::Image,
-        _reformat_alpha: bool,
-    ) -> AvifResult<bool> {
+    pub fn yuv_to_rgb(_image: &image::Image, _rgb: &mut rgb::Image) -> AvifResult<bool> {
         return Err(AvifError::NotImplemented);
     }
 
diff --git a/src/reformat/rgb.rs b/src/reformat/rgb.rs
index d81523f..995db4a 100644
--- a/src/reformat/rgb.rs
+++ b/src/reformat/rgb.rs
@@ -91,7 +91,6 @@
     pub format: Format,
     pub chroma_upsampling: ChromaUpsampling,
     pub chroma_downsampling: ChromaDownsampling,
-    pub ignore_alpha: bool,
     pub alpha_premultiplied: bool,
     pub is_float: bool,
     pub max_threads: i32,
@@ -124,7 +123,6 @@
             format: Format::Rgba,
             chroma_upsampling: ChromaUpsampling::Automatic,
             chroma_downsampling: ChromaDownsampling::Automatic,
-            ignore_alpha: false,
             alpha_premultiplied: false,
             is_float: false,
             max_threads: 1,
@@ -258,17 +256,12 @@
     }
 
     pub fn convert_from_yuv(&mut self, image: &image::Image) -> AvifResult<()> {
-        // TODO: use plane constant here and elsewhere.
         if !image.has_plane(Plane::Y) {
             return Err(AvifError::ReformatFailed);
         }
         let mut alpha_multiply_mode = AlphaMultiplyMode::NoOp;
-        if image.has_alpha() {
-            if !self.has_alpha() || self.ignore_alpha {
-                if !image.alpha_premultiplied {
-                    alpha_multiply_mode = AlphaMultiplyMode::Multiply;
-                }
-            } else if !image.alpha_premultiplied && self.alpha_premultiplied {
+        if image.has_alpha() && self.has_alpha() {
+            if !image.alpha_premultiplied && self.alpha_premultiplied {
                 alpha_multiply_mode = AlphaMultiplyMode::Multiply;
             } else if image.alpha_premultiplied && !self.alpha_premultiplied {
                 alpha_multiply_mode = AlphaMultiplyMode::UnMultiply;
@@ -276,11 +269,9 @@
         }
 
         let mut converted_with_libyuv: bool = false;
-        let reformat_alpha = self.has_alpha()
-            && (!self.ignore_alpha || alpha_multiply_mode != AlphaMultiplyMode::NoOp);
         let mut alpha_reformatted_with_libyuv = false;
         if alpha_multiply_mode == AlphaMultiplyMode::NoOp || self.has_alpha() {
-            match libyuv::yuv_to_rgb(image, self, reformat_alpha) {
+            match libyuv::yuv_to_rgb(image, self) {
                 Ok(alpha_reformatted) => {
                     alpha_reformatted_with_libyuv = alpha_reformatted;
                     converted_with_libyuv = true;
@@ -292,7 +283,7 @@
                 }
             }
         }
-        if reformat_alpha && !alpha_reformatted_with_libyuv {
+        if self.has_alpha() && !alpha_reformatted_with_libyuv {
             if image.has_alpha() {
                 self.import_alpha_from(image)?;
             } else {
@@ -319,6 +310,71 @@
         }
         Ok(())
     }
+
+    pub fn shuffle_channels_to(self, format: Format) -> AvifResult<Image> {
+        if self.format == format {
+            return Ok(self);
+        }
+        if self.format == Format::Rgb565 || format == Format::Rgb565 {
+            return Err(AvifError::NotImplemented);
+        }
+
+        let mut dst = Image {
+            format,
+            pixels: None,
+            row_bytes: 0,
+            ..self
+        };
+        dst.allocate()?;
+
+        let src_channel_count = self.channel_count();
+        let dst_channel_count = dst.channel_count();
+        let src_offsets = self.format.offsets();
+        let dst_offsets = dst.format.offsets();
+        let src_has_alpha = self.has_alpha();
+        let dst_has_alpha = dst.has_alpha();
+        let dst_max_channel = dst.max_channel();
+        for y in 0..self.height {
+            if self.depth == 8 {
+                let src_row = self.row(y)?;
+                let dst_row = &mut dst.row_mut(y)?;
+                for x in 0..self.width {
+                    let src_pixel_i = (src_channel_count * x) as usize;
+                    let dst_pixel_i = (dst_channel_count * x) as usize;
+                    for c in 0..3 {
+                        dst_row[dst_pixel_i + dst_offsets[c]] =
+                            src_row[src_pixel_i + src_offsets[c]];
+                    }
+                    if dst_has_alpha {
+                        dst_row[dst_pixel_i + dst_offsets[3]] = if src_has_alpha {
+                            src_row[src_pixel_i + src_offsets[3]]
+                        } else {
+                            dst_max_channel as u8
+                        };
+                    }
+                }
+            } else {
+                let src_row = self.row16(y)?;
+                let dst_row = &mut dst.row16_mut(y)?;
+                for x in 0..self.width {
+                    let src_pixel_i = (src_channel_count * x) as usize;
+                    let dst_pixel_i = (dst_channel_count * x) as usize;
+                    for c in 0..3 {
+                        dst_row[dst_pixel_i + dst_offsets[c]] =
+                            src_row[src_pixel_i + src_offsets[c]];
+                    }
+                    if dst_has_alpha {
+                        dst_row[dst_pixel_i + dst_offsets[3]] = if src_has_alpha {
+                            src_row[src_pixel_i + src_offsets[3]]
+                        } else {
+                            dst_max_channel
+                        };
+                    }
+                }
+            }
+        }
+        Ok(dst)
+    }
 }
 
 #[cfg(test)]
@@ -329,6 +385,7 @@
     use crate::image::ALL_PLANES;
     use crate::image::MAX_PLANE_COUNT;
 
+    use test_case::test_case;
     use test_case::test_matrix;
 
     const WIDTH: usize = 3;
@@ -471,4 +528,23 @@
         }
         Ok(())
     }
+
+    #[test_case(Format::Rgba, &[0, 1, 2, 3])]
+    #[test_case(Format::Abgr, &[3, 2, 1, 0])]
+    #[test_case(Format::Rgb, &[0, 1, 2])]
+    fn shuffle_channels_to(format: Format, expected: &[u8]) {
+        let image = Image {
+            width: 1,
+            height: 1,
+            depth: 8,
+            format: Format::Rgba,
+            pixels: Some(Pixels::Buffer(vec![0u8, 1, 2, 3])),
+            row_bytes: 4,
+            ..Default::default()
+        };
+        assert_eq!(
+            image.shuffle_channels_to(format).unwrap().row(0).unwrap(),
+            expected
+        );
+    }
 }