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