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
+ );
+ }
}