1#![doc = include_str!("../README.md")]
2#![warn(unreachable_pub)]
3#![allow(unsafe_op_in_unsafe_fn)]
4#![cfg_attr(not(feature = "all-formats"), allow(unused))]
7
8#[cfg(not(any(
9 feature = "I420",
10 feature = "I422",
11 feature = "I444",
12 feature = "I010",
13 feature = "I012",
14 feature = "I210",
15 feature = "I212",
16 feature = "I410",
17 feature = "I412",
18 feature = "NV12",
19 feature = "P010",
20 feature = "P012",
21 feature = "YUYV",
22 feature = "RGBA",
23 feature = "BGRA",
24 feature = "ARGB",
25 feature = "ABGR",
26 feature = "RGB",
27 feature = "BGR",
28)))]
29compile_error!("At least one image format feature must be enabled");
30
31use formats::*;
32
33mod color;
34mod copy;
35mod crop;
36mod formats;
37mod image;
38mod image_traits;
39#[cfg(feature = "multi-thread")]
40mod multi_thread;
41mod pixel_format;
42mod plane_decs;
43mod planes;
44mod primitive;
45#[cfg(feature = "resize")]
46pub mod resize;
47pub(crate) mod util;
48mod vector;
49
50mod arch {
51 #[cfg(target_arch = "x86")]
52 pub(crate) use std::arch::x86::*;
53 #[cfg(target_arch = "x86_64")]
54 pub(crate) use std::arch::x86_64::*;
55
56 #[cfg(target_arch = "aarch64")]
57 pub(crate) use std::arch::aarch64::*;
58 #[cfg(target_arch = "aarch64")]
59 pub(crate) use std::arch::is_aarch64_feature_detected;
60}
61
62pub use color::{ColorInfo, ColorPrimaries, ColorSpace, ColorTransfer, RgbColorInfo, YuvColorInfo};
63#[doc(hidden)]
64pub use copy::copy;
65pub use crop::{CropError, Cropped, Window};
66pub use image::{BufferKind, Image, ImageError};
67pub use image_traits::{ImageMut, ImageRef, ImageRefExt};
68#[cfg(feature = "multi-thread")]
69pub use multi_thread::convert_multi_thread;
70pub use pixel_format::{BoundsCheckError, PixelFormat};
71pub use planes::*;
72
73#[derive(Debug, thiserror::Error)]
75pub enum ConvertError {
76 #[error("image dimensions are not divisible by 2")]
77 OddImageDimensions,
78
79 #[error("source image has different size than destination image")]
80 MismatchedImageSize,
81
82 #[error("invalid color info for pixel format")]
83 InvalidColorInfo,
84
85 #[error(transparent)]
86 BoundsCheck(#[from] BoundsCheckError),
87
88 #[error(transparent)]
89 InvalidNumberOfPlanes(#[from] InvalidNumberOfPlanesError),
90}
91
92#[inline(never)]
96pub fn convert(src: &dyn ImageRef, dst: &mut dyn ImageMut) -> Result<(), ConvertError> {
97 verify_input_windows(src, dst)?;
98
99 if src.format() == dst.format() && src.color() == dst.color() {
100 return copy(src, dst);
102 }
103
104 let src_color = src.color();
105 let dst_color = dst.color();
106
107 let reader = read_any_to_rgba(src)?;
108
109 if need_transfer_and_primaries_convert(&src_color, &dst_color) {
110 let reader = TransferAndPrimariesConvert::new(&src_color, &dst_color, reader);
111
112 rgba_to_any(dst, reader)
113 } else {
114 rgba_to_any(dst, reader)
115 }
116}
117
118#[inline(never)]
119fn read_any_to_rgba<'a>(
120 src: &'a dyn ImageRef,
121) -> Result<Box<dyn DynRgbaReader + 'a>, ConvertError> {
122 use PixelFormat::*;
123
124 match src.format() {
125 #[cfg(feature = "I420")]
126 I420 => Ok(Box::new(yuv420::ToRgb::new(
127 &src.color(),
128 yuv420::Read3Plane::<u8>::new(src)?,
129 )?)),
130 #[cfg(feature = "I422")]
131 I422 => Ok(Box::new(yuv422::ToRgb::new(
132 &src.color(),
133 yuv422::Read3Plane::<u8>::new(src)?,
134 )?)),
135 #[cfg(feature = "I444")]
136 I444 => Ok(Box::new(yuv444::ToRgb::new(
137 &src.color(),
138 yuv444::Read3Plane::<u8>::new(src)?,
139 )?)),
140 #[cfg(feature = "I010")]
141 I010 => Ok(Box::new(yuv420::ToRgb::new(
142 &src.color(),
143 yuv420::Read3Plane::<u16>::new(src)?,
144 )?)),
145 #[cfg(feature = "I012")]
146 I012 => Ok(Box::new(yuv420::ToRgb::new(
147 &src.color(),
148 yuv420::Read3Plane::<u16>::new(src)?,
149 )?)),
150 #[cfg(feature = "I210")]
151 I210 => Ok(Box::new(yuv422::ToRgb::new(
152 &src.color(),
153 yuv422::Read3Plane::<u16>::new(src)?,
154 )?)),
155 #[cfg(feature = "I212")]
156 I212 => Ok(Box::new(yuv422::ToRgb::new(
157 &src.color(),
158 yuv422::Read3Plane::<u16>::new(src)?,
159 )?)),
160 #[cfg(feature = "I410")]
161 I410 => Ok(Box::new(yuv444::ToRgb::new(
162 &src.color(),
163 yuv444::Read3Plane::<u16>::new(src)?,
164 )?)),
165 #[cfg(feature = "I412")]
166 I412 => Ok(Box::new(yuv444::ToRgb::new(
167 &src.color(),
168 yuv444::Read3Plane::<u16>::new(src)?,
169 )?)),
170 #[cfg(feature = "NV12")]
171 NV12 => Ok(Box::new(yuv420::ToRgb::new(
172 &src.color(),
173 yuv420::Read2Plane::<u8>::new(src)?,
174 )?)),
175 #[cfg(feature = "P010")]
176 P010 => Ok(Box::new(yuv420::ToRgb::new(
177 &src.color(),
178 yuv420::Read2Plane::<u16>::new(src)?,
179 )?)),
180 #[cfg(feature = "P012")]
181 P012 => Ok(Box::new(yuv420::ToRgb::new(
182 &src.color(),
183 yuv420::Read2Plane::<u16>::new(src)?,
184 )?)),
185 #[cfg(feature = "YUYV")]
186 YUYV => Ok(Box::new(yuv422::ToRgb::new(
187 &src.color(),
188 yuv422::Read1Plane::<u8>::new(src)?,
189 )?)),
190 #[cfg(feature = "RGBA")]
191 RGBA => Ok(Box::new(rgb::ReadRgba::<u8>::new(src)?)),
192 #[cfg(feature = "BGRA")]
193 BGRA => Ok(Box::new(rgb::ReadBgra::<u8>::new(src)?)),
194 #[cfg(feature = "ARGB")]
195 ARGB => Ok(Box::new(rgb::ReadArgb::<u8>::new(src)?)),
196 #[cfg(feature = "ABGR")]
197 ABGR => Ok(Box::new(rgb::ReadAbgr::<u8>::new(src)?)),
198 #[cfg(feature = "RGB")]
199 RGB => Ok(Box::new(rgb::ReadRgb::<u8>::new(src)?)),
200 #[cfg(feature = "BGR")]
201 BGR => Ok(Box::new(rgb::ReadBgr::<u8>::new(src)?)),
202 }
203}
204
205#[inline(never)]
206fn rgba_to_any(dst: &mut dyn ImageMut, reader: impl rgb::RgbaSrc) -> Result<(), ConvertError> {
207 use PixelFormat::*;
208
209 let color = dst.color();
210
211 match dst.format() {
212 #[cfg(feature = "I420")]
213 I420 => yuv420::Write3Plane::<u8, _>::write(dst, yuv420::FromRgb::new(&color, reader)?),
214 #[cfg(feature = "I422")]
215 I422 => yuv422::Write3Plane::<u8, _>::write(dst, yuv422::FromRgb::new(&color, reader)?),
216 #[cfg(feature = "I444")]
217 I444 => yuv444::Write3Plane::<u8, _>::write(dst, yuv444::FromRgb::new(&color, reader)?),
218 #[cfg(feature = "I010")]
219 I010 => yuv420::Write3Plane::<u16, _>::write(dst, yuv420::FromRgb::new(&color, reader)?),
220 #[cfg(feature = "I012")]
221 I012 => yuv420::Write3Plane::<u16, _>::write(dst, yuv420::FromRgb::new(&color, reader)?),
222 #[cfg(feature = "I210")]
223 I210 => yuv422::Write3Plane::<u16, _>::write(dst, yuv422::FromRgb::new(&color, reader)?),
224 #[cfg(feature = "I212")]
225 I212 => yuv422::Write3Plane::<u16, _>::write(dst, yuv422::FromRgb::new(&color, reader)?),
226 #[cfg(feature = "I410")]
227 I410 => yuv444::Write3Plane::<u16, _>::write(dst, yuv444::FromRgb::new(&color, reader)?),
228 #[cfg(feature = "I412")]
229 I412 => yuv444::Write3Plane::<u16, _>::write(dst, yuv444::FromRgb::new(&color, reader)?),
230 #[cfg(feature = "NV12")]
231 NV12 => yuv420::Write2Plane::<u8, _>::write(dst, yuv420::FromRgb::new(&color, reader)?),
232 #[cfg(feature = "P010")]
233 P010 => yuv420::Write2Plane::<u16, _>::write(dst, yuv420::FromRgb::new(&color, reader)?),
234 #[cfg(feature = "P012")]
235 P012 => yuv420::Write2Plane::<u16, _>::write(dst, yuv420::FromRgb::new(&color, reader)?),
236 #[cfg(feature = "YUYV")]
237 YUYV => yuv422::Write1Plane::<u8, _>::write(dst, yuv422::FromRgb::new(&color, reader)?),
238 #[cfg(feature = "RGBA")]
239 RGBA => rgb::WriteRgba::<u8, _>::write(dst, reader),
240 #[cfg(feature = "BGRA")]
241 BGRA => rgb::WriteBgra::<u8, _>::write(dst, reader),
242 #[cfg(feature = "ARGB")]
243 ARGB => rgb::WriteArgb::<u8, _>::write(dst, reader),
244 #[cfg(feature = "ABGR")]
245 ABGR => rgb::WriteAbgr::<u8, _>::write(dst, reader),
246 #[cfg(feature = "RGB")]
247 RGB => rgb::WriteRgb::<u8, _>::write(dst, reader),
248 #[cfg(feature = "BGR")]
249 BGR => rgb::WriteBgr::<u8, _>::write(dst, reader),
250 }
251}
252
253#[deny(clippy::arithmetic_side_effects)]
255fn verify_input_windows(src: &dyn ImageRef, dst: &dyn ImageMut) -> Result<(), ConvertError> {
256 if src.width() != dst.width() || src.height() != dst.height() {
258 return Err(ConvertError::MismatchedImageSize);
259 }
260
261 if src.width() % 2 == 1 || src.height() % 2 == 1 {
263 return Err(ConvertError::OddImageDimensions);
264 }
265
266 Ok(())
267}
268
269trait StrictApi {
270 fn strict_add_(self, rhs: Self) -> Self;
271 fn strict_mul_(self, rhs: Self) -> Self;
272}
273
274impl StrictApi for usize {
275 #[track_caller]
276 fn strict_add_(self, rhs: Self) -> Self {
277 self.checked_add(rhs).expect("attempt to add with overflow")
278 }
279
280 #[track_caller]
281 fn strict_mul_(self, rhs: Self) -> Self {
282 self.checked_mul(rhs)
283 .expect("attempt to multiply with overflow")
284 }
285}