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