1use crate::chroma::yuv_420;
3use crate::chroma::yuv_422;
4use crate::chroma::yuv_444;
5use crate::color::{ChromaSampling, MatrixCoefficients};
6use crate::Config;
7use crate::Decoder;
8use crate::Error;
9use crate::FrameTempRef;
10use crate::Result;
11use crate::RowsIter;
12use crate::RowsIters;
13use imgref::ImgVec;
14use yuv::color::{Depth, Range};
15use yuv::convert::RGBConvert;
16use yuv::RGB;
17use yuv::RGBA;
18use yuv::YUV;
19use rgb::prelude::*;
20
21pub struct Avif {
23 decoder: Decoder,
24 avif: ParsedAvifData,
25}
26
27pub enum Image {
29 RGB8(ImgVec<RGB<u8>>),
30 RGBA8(ImgVec<RGBA<u8>>),
31 RGB16(ImgVec<RGB<u16>>),
32 RGBA16(ImgVec<RGBA<u16>>),
33 Gray8(ImgVec<u8>),
34 Gray16(ImgVec<u16>),
35}
36
37pub use avif_parse::AvifData as ParsedAvifData;
39pub use avif_parse::AV1Metadata;
42pub use avif_parse::Error as AvifParseError;
43pub use yuv::Error as YuvError;
44
45impl Avif {
46 pub fn decode(data: &[u8], config: &Config) -> Result<Self> {
47 Self::from_parsed_avif_data(Self::parse_avif(data)?, config)
48 }
49
50 pub fn convert(&mut self) -> Result<Image> {
51 let has_alpha = self.avif.alpha_item.is_some();
54 let color = self.raw_color_data()?;
55 let range = color.range();
56 let mut img = match color.rows_iter()? {
57 RowsIters::YuvPlanes8 {y,u,v,chroma_sampling} => {
58 yuv_to_rgb8(&color, range, y, chroma_sampling, u, v, has_alpha)?
59 },
60 RowsIters::Mono8(y) => {
61 yuv_to_gray8(&color, range, y, has_alpha)?
62 },
63 RowsIters::Mono16(y, depth) => {
64 yuv_to_gray16(&color, range, depth, y, has_alpha)?
65 },
66 RowsIters::YuvPlanes16 {y,u,v,chroma_sampling, depth} => {
67 yuv_to_rgb16(&color, range, depth, y, chroma_sampling, u, v, has_alpha)?
68 },
69 };
70 let color_mc = color.matrix_coefficients().unwrap_or(MatrixCoefficients::Identity);
71 drop(color);
72
73 let premultiplied_alpha = self.avif.premultiplied_alpha;
74 if let Some(alpha) = self.raw_alpha_data()? {
75 let range = alpha.range();
76 if let Some(alpha_mc) = alpha.matrix_coefficients().filter(|&mc| mc != MatrixCoefficients::Identity) {
77 log::warn!("The alpha channel is not allowed to have a color matrix: {alpha_mc:?}");
78 if color_mc != alpha_mc {
79 return Err(Error::Unsupported("alpha image has color info"));
80 }
81 }
82 let mc = MatrixCoefficients::Identity;
83 match alpha.rows_iter()? {
84 RowsIters::YuvPlanes8 { y, .. } | RowsIters::Mono8(y) => {
85 let conv = RGBConvert::<u8>::new(range, mc)?;
86 add_alpha8(&mut img, y, conv, premultiplied_alpha)?;
87 },
88 RowsIters::YuvPlanes16 { y, depth, .. } | RowsIters::Mono16(y, depth) => {
89 let conv = RGBConvert::<u16>::new(range, mc, depth)?;
90 add_alpha16(&mut img, y, conv, premultiplied_alpha)?;
91 },
92 };
93 } else if has_alpha {
94 return Err(Error::Unsupported("invalid alpha"));
95 }
96 Ok(img)
97 }
98
99 pub fn parse_avif(data: &[u8]) -> Result<ParsedAvifData> {
100 Ok(avif_parse::read_avif(&mut &data[..])?)
101 }
102
103 pub fn from_parsed_avif_data(avif: ParsedAvifData, config: &Config) -> Result<Self> {
104 let decoder = Decoder::new(config)?;
105 Ok(Self { decoder, avif })
106 }
107
108 pub fn raw_color_data(&mut self) -> Result<FrameTempRef<'_>> {
109 self.decoder.decode_frame(&self.avif.primary_item)
110 }
111
112 pub fn raw_alpha_data(&mut self) -> Result<Option<FrameTempRef<'_>>> {
113 Ok(if let Some(alpha) = &self.avif.alpha_item {
114 Some(self.decoder.decode_frame(alpha)?)
115 } else {
116 None
117 })
118 }
119}
120
121fn add_alpha16(img: &mut Image, y: RowsIter<[u8; 2]>, conv: RGBConvert<u16>, premultiplied_alpha: bool) -> Result<()> {
122 if let RGBConvert::Matrix(_) = conv {
123 return Err(Error::Unsupported("alpha image has color info"));
124 }
125 match img {
126 Image::RGBA8(img) => {
127 for (y_row, img_row) in y.zip(img.rows_mut()) {
128 if y_row.len() != img_row.len() {
129 return Err(Error::Unsupported("invalid alpha size"));
130 }
131 for (y, px) in y_row.iter().copied().zip(img_row.iter_mut()) {
132 let y = u16::from_ne_bytes(y);
133 px.a = (conv.to_luma(y) >> 8) as u8;
134 }
135 if premultiplied_alpha {
136 unpremultiply8(img_row);
137 }
138 }
139 },
140 Image::RGBA16(img) => {
141 for (y_row, img_row) in y.zip(img.rows_mut()) {
142 if y_row.len() != img_row.len() {
143 return Err(Error::Unsupported("invalid alpha size"));
144 }
145 for (y, px) in y_row.iter().copied().zip(img_row.iter_mut()) {
146 let y = u16::from_ne_bytes(y);
147 px.a = conv.to_luma(y);
148 }
149 if premultiplied_alpha {
150 unpremultiply16(img_row);
151 }
152 }
153 },
154 _ => return Err(Error::Unsupported("internal error")),
155 };
156 Ok(())
157}
158
159fn add_alpha8(img: &mut Image, y: RowsIter<u8>, conv: RGBConvert, premultiplied_alpha: bool) -> Result<()> {
160 if let RGBConvert::Matrix(_) = conv {
161 return Err(Error::Unsupported("alpha image has color info"));
162 }
163 match img {
164 Image::RGBA8(img) => {
165 for (y_row, img_row) in y.zip(img.rows_mut()) {
166 if y_row.len() != img_row.len() {
167 return Err(Error::Unsupported("invalid alpha size"));
168 }
169 for (y, px) in y_row.iter().copied().zip(img_row.iter_mut()) {
170 px.a = conv.to_luma(y);
171 }
172 if premultiplied_alpha {
173 unpremultiply8(img_row);
174 }
175 }
176 },
177 Image::RGBA16(img) => {
178 for (y_row, img_row) in y.zip(img.rows_mut()) {
179 if y_row.len() != img_row.len() {
180 return Err(Error::Unsupported("invalid alpha size"));
181 }
182 for (y, px) in y_row.iter().copied().zip(img_row.iter_mut()) {
183 let g = u16::from(conv.to_luma(y));
184 px.a = (g << 8) | g;
185 }
186 if premultiplied_alpha {
187 unpremultiply16(img_row);
188 }
189 }
190 },
191 _ => return Err(Error::Unsupported("internal error")),
192 };
193 Ok(())
194}
195
196#[inline(never)]
197fn unpremultiply8(img_row: &mut [RGBA<u8>]) {
198 for px in img_row.iter_mut() {
199 if px.a != 255 && px.a != 0 {
200 *px.rgb_mut() = px.rgb().map(|c| (c as u16 * 255 / px.a as u16).min(255) as u8)
201 }
202 }
203}
204
205#[inline(never)]
206fn unpremultiply16(img_row: &mut [RGBA<u16>]) {
207 for px in img_row.iter_mut() {
208 if px.a != 0xFFFF && px.a != 0 {
209 *px.rgb_mut() = px.rgb().map(|c| (c as u32 * 0xFFFF / px.a as u32).min(0xFFFF) as u16)
210 }
211 }
212}
213
214fn yuv_to_rgb16(color: &FrameTempRef, range: Range, depth: Depth, y: RowsIter<[u8; 2]>, chroma_sampling: ChromaSampling, u: RowsIter<[u8; 2]>, v: RowsIter<[u8; 2]>, has_alpha: bool) -> Result<Image, Error> {
215 let mc = color.matrix_coefficients().unwrap_or(MatrixCoefficients::BT601);
216 let conv = RGBConvert::<u16>::new(range, mc, depth).inspect_err(move |e| {
217 log_unsupported_color(e, mc, range);
218 })?;
219 let width = y.width();
220 let height = y.height();
221 let mut tmp1;
222 let mut tmp2;
223 let mut tmp3;
224 let px_iter: &mut dyn Iterator<Item = YUV<[u8; 2]>> = match chroma_sampling {
225 ChromaSampling::Cs444 => {
226 tmp1 = yuv_444(y, u, v);
227 &mut tmp1
228 },
229 ChromaSampling::Cs420 => {
230 tmp2 = yuv_420(y, u, v);
231 &mut tmp2
232 },
233 ChromaSampling::Cs422 => {
234 tmp3 = yuv_422(y, u, v);
235 &mut tmp3
236 },
237 ChromaSampling::Monochrome => unreachable!(),
238 };
239 if has_alpha {
240 let mut out = Vec::with_capacity(width * height);
241 out.extend(px_iter.map(|px| conv.to_rgb(YUV{
242 y: u16::from_ne_bytes(px.y),
243 u: u16::from_ne_bytes(px.u),
244 v: u16::from_ne_bytes(px.v),
245 }).with_alpha(0)));
246 Ok(Image::RGBA16(ImgVec::new(out, width, height)))
247 } else {
248 let mut out = Vec::with_capacity(width * height);
249 out.extend(px_iter.map(|px| conv.to_rgb(YUV{
250 y: u16::from_ne_bytes(px.y),
251 u: u16::from_ne_bytes(px.u),
252 v: u16::from_ne_bytes(px.v),
253 })));
254 Ok(Image::RGB16(ImgVec::new(out, width, height)))
255 }
256}
257
258fn yuv_to_gray16(color: &FrameTempRef, range: Range, depth: Depth, y: RowsIter<[u8; 2]>, has_alpha: bool) -> Result<Image, Error> {
259 let mc = color.matrix_coefficients().unwrap_or(MatrixCoefficients::Identity);
260 let conv = RGBConvert::<u16>::new(range, mc, depth).inspect_err(move |e| {
261 log_unsupported_color(e, mc, range);
262 })?;
263 let width = y.width();
264 let height = y.height();
265 if has_alpha {
266 let mut out = Vec::with_capacity(width * height);
267 out.extend(y.flat_map(|row| {
268 row.iter().copied().map(|y| {
269 let g = conv.to_luma(u16::from_ne_bytes(y));
270 RGBA::new(g, g, g, 0)
271 })
272 }));
273 Ok(Image::RGBA16(ImgVec::new(out, width, height)))
274 } else {
275 let mut out = Vec::with_capacity(width * height);
276 out.extend(y.flat_map(|row| {
277 row.iter()
278 .copied()
279 .map(|y| conv.to_luma(u16::from_ne_bytes(y)))
280 }));
281 Ok(Image::Gray16(ImgVec::new(out, width, height)))
282 }
283}
284
285fn yuv_to_gray8(color: &FrameTempRef, range: Range, y: RowsIter<u8>, has_alpha: bool) -> Result<Image, Error> {
286 let mc = color.matrix_coefficients().unwrap_or(MatrixCoefficients::Identity);
287 let conv = RGBConvert::<u8>::new(range, mc).inspect_err(move |e| {
288 log_unsupported_color(e, mc, range);
289 })?;
290 let width = y.width();
291 let height = y.height();
292 if has_alpha {
293 let mut out = Vec::with_capacity(width * height);
294 out.extend(y.flat_map(|row| {
295 row.iter()
296 .copied()
297 .map(|y| conv.to_rgb(YUV { y, u: 128, v: 128 }).with_alpha(0))
298 }));
299 Ok(Image::RGBA8(ImgVec::new(out, width, height)))
300 } else {
301 let mut out = Vec::with_capacity(width * height);
302 out.extend(y.flat_map(|row| {
303 row.iter()
304 .copied()
305 .map(|y| conv.to_rgb(YUV { y, u: 128, v: 128 }).g)
306 }));
307 Ok(Image::Gray8(ImgVec::new(out, width, height)))
308 }
309}
310
311fn yuv_to_rgb8(color: &FrameTempRef, range: Range, y: RowsIter<u8>, chroma_sampling: ChromaSampling, u: RowsIter<u8>, v: RowsIter<u8>, has_alpha: bool) -> Result<Image, Error> {
312 let mc = color.matrix_coefficients().unwrap_or(MatrixCoefficients::BT601);
313 let conv = RGBConvert::<u8>::new(range, mc).inspect_err(move |e| {
314 log_unsupported_color(e, mc, range);
315 })?;
316 let width = y.width();
317 let height = y.height();
318 let mut tmp1;
319 let mut tmp2;
320 let mut tmp3;
321 let px_iter: &mut dyn Iterator<Item = YUV<u8>> = match chroma_sampling {
322 ChromaSampling::Cs444 => {
323 tmp1 = yuv_444(y, u, v);
324 &mut tmp1
325 },
326 ChromaSampling::Cs420 => {
327 tmp2 = yuv_420(y, u, v);
328 &mut tmp2
329 },
330 ChromaSampling::Cs422 => {
331 tmp3 = yuv_422(y, u, v);
332 &mut tmp3
333 },
334 ChromaSampling::Monochrome => unreachable!(),
335 };
336 if has_alpha {
337 let mut out = Vec::with_capacity(width * height);
338 out.extend(px_iter.map(|px| conv.to_rgb(px).with_alpha(0)));
339 Ok(Image::RGBA8(ImgVec::new(out, width, height)))
340 } else {
341 let mut out = Vec::with_capacity(width * height);
342 out.extend(px_iter.map(|px| conv.to_rgb(px)));
343 Ok(Image::RGB8(ImgVec::new(out, width, height)))
344 }
345}
346
347#[cold]
348fn log_unsupported_color(e: &yuv::Error, mc: MatrixCoefficients, range: Range) {
349 log::debug!("{e} (color={mc:?}, range={range:?})");
350}