valo_codec_software/
lib.rs1#![warn(missing_docs)]
9
10#[cfg(animation)]
11mod animation;
12mod color;
13#[cfg(any(feature = "png", feature = "jpeg", feature = "webp"))]
14mod encode;
15mod probe;
16
17#[cfg(animation)]
18use animation::FrameStream;
19#[cfg(any(feature = "png", feature = "jpeg", feature = "webp"))]
20pub use encode::{encode, EncodeError, Encoding};
21
22use color::ColorSpace;
23use image::DynamicImage;
24use probe::Probe;
25use std::io::Cursor;
26use std::sync::Arc;
27use std::time::Duration;
28use valo::{AlphaType, PixelBuffer, PixelFormat, PixelLayout};
29use valo_codec::{
30 DecodeError, DecodedFrame, Decoder, Decoding, FramePixels, FrameReader, ImageInfo, OpenError,
31 OpenRequest,
32};
33
34#[derive(Clone, Copy, Debug, Default)]
39pub struct SoftwareDecoder;
40
41impl Decoder for SoftwareDecoder {
42 fn name(&self) -> &'static str {
43 "software"
44 }
45
46 fn open<'a>(
47 &'a self,
48 request: &'a OpenRequest,
49 ) -> Decoding<'a, Result<Box<dyn FrameReader>, OpenError>> {
50 Box::pin(std::future::ready(
52 SoftwareReader::open(request).map(|reader| Box::new(reader) as Box<dyn FrameReader>),
53 ))
54 }
55}
56
57struct SoftwareReader {
60 encoded: Arc<[u8]>,
61 probe: Probe,
62 color: ColorSpace,
63 limits: image::Limits,
64 output_size: [u32; 2],
65 #[cfg(animation)]
66 animation: Option<FrameStream>,
67 next: u32,
68}
69
70impl SoftwareReader {
71 fn open(request: &OpenRequest) -> Result<Self, OpenError> {
72 let limits = request.options.limits;
73 let probe = probe::probe(&request.encoded, limits)?;
74 let color = ColorSpace::detect(&probe, &request.encoded)?;
75 let image_limits = image_limits(limits);
76 #[cfg(animation)]
77 let animation = probe
78 .animated
79 .then(|| FrameStream::new(request.encoded.clone(), probe.format, image_limits.clone()));
80 Ok(Self {
81 output_size: request.options.fit(probe.oriented_size),
82 encoded: request.encoded.clone(),
83 probe,
84 color,
85 limits: image_limits,
86 #[cfg(animation)]
87 animation,
88 next: 0,
89 })
90 }
91
92 fn decode_still(&self) -> Result<DynamicImage, DecodeError> {
93 let mut reader =
94 image::ImageReader::with_format(Cursor::new(self.encoded.clone()), self.probe.format);
95 reader.limits(self.limits.clone());
96 reader.decode().map_err(decode_error)
97 }
98
99 #[cfg(animation)]
100 fn decoded(&mut self, index: u32) -> Result<(DynamicImage, Duration), DecodeError> {
101 match &mut self.animation {
102 Some(stream) => {
103 let (frame, duration) = stream.frame(index)?;
104 Ok((DynamicImage::ImageRgba8(frame), duration))
105 }
106 None => Ok((self.decode_still()?, Duration::ZERO)),
107 }
108 }
109
110 #[cfg(not(animation))]
111 fn decoded(&mut self, _index: u32) -> Result<(DynamicImage, Duration), DecodeError> {
112 Ok((self.decode_still()?, Duration::ZERO))
113 }
114
115 fn finish(&self, mut image: DynamicImage) -> Result<PixelBuffer, DecodeError> {
116 image.apply_orientation(self.probe.orientation);
117 if [image.width(), image.height()] != self.output_size {
118 let [width, height] = self.output_size;
119 image = image.resize_exact(width, height, image::imageops::FilterType::Triangle);
120 }
121 let rgba = self.color.to_srgb_rgba8(image)?;
122 let layout = PixelLayout::packed(self.output_size, PixelFormat::Rgba8, AlphaType::Straight);
123 Ok(PixelBuffer::new(layout, rgba)?)
124 }
125}
126
127impl FrameReader for SoftwareReader {
128 fn info(&self) -> ImageInfo {
129 ImageInfo {
130 size: self.output_size,
131 frame_count: self.probe.frame_count,
132 repetition: self.probe.repetition,
133 }
134 }
135
136 fn next_frame(&mut self) -> Decoding<'_, Result<DecodedFrame, DecodeError>> {
137 Box::pin(std::future::ready(self.decode_next()))
138 }
139}
140
141impl SoftwareReader {
142 fn decode_next(&mut self) -> Result<DecodedFrame, DecodeError> {
143 let index = self.next;
144 self.next = (index + 1) % self.probe.frame_count;
145 let (image, duration) = self.decoded(index)?;
146 Ok(DecodedFrame {
147 pixels: FramePixels::Cpu(self.finish(image)?),
148 duration,
149 })
150 }
151}
152
153fn image_limits(limits: valo_codec::DecodeLimits) -> image::Limits {
156 let mut image_limits = image::Limits::default();
157 image_limits.max_alloc = Some(limits.max_pixels.saturating_mul(16));
158 image_limits
159}
160
161pub(crate) fn decode_error(error: image::ImageError) -> DecodeError {
163 use image::ImageError;
164 match error {
165 ImageError::Decoding(error) => DecodeError::InvalidData(error.to_string()),
166 ImageError::Limits(_) => DecodeError::LimitExceeded("software decoder allocation"),
167 ImageError::Unsupported(error) => DecodeError::Failed(error.to_string()),
168 other => DecodeError::Failed(other.to_string()),
169 }
170}