1use crate::{Colorspace, Info, TileLayout};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
11#[non_exhaustive]
12pub enum CompressedTransferSyntax {
13 JpegBaseline8,
15 JpegExtendedSequential,
17 JpegProgressive,
19 JpegLossless,
21 JpegLosslessSv1,
26 Jpeg2000Lossless,
28 Jpeg2000Lossy,
30 HtJpeg2000Lossless,
32 HtJpeg2000Lossy,
34}
35
36impl CompressedTransferSyntax {
37 #[must_use]
39 pub const fn is_lossless(self) -> bool {
40 matches!(
41 self,
42 Self::JpegLossless
43 | Self::JpegLosslessSv1
44 | Self::Jpeg2000Lossless
45 | Self::HtJpeg2000Lossless
46 )
47 }
48
49 #[must_use]
51 pub const fn is_jpeg_family(self) -> bool {
52 matches!(
53 self,
54 Self::JpegBaseline8
55 | Self::JpegExtendedSequential
56 | Self::JpegProgressive
57 | Self::JpegLossless
58 | Self::JpegLosslessSv1
59 )
60 }
61
62 #[must_use]
64 pub const fn is_jpeg2000_family(self) -> bool {
65 matches!(
66 self,
67 Self::Jpeg2000Lossless
68 | Self::Jpeg2000Lossy
69 | Self::HtJpeg2000Lossless
70 | Self::HtJpeg2000Lossy
71 )
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
77#[non_exhaustive]
78pub enum CompressedPayloadKind {
79 JpegInterchange,
81 Jpeg2000Codestream,
83 Jp2File,
85 JphFile,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
91pub struct PassthroughCandidate<'a> {
92 bytes: &'a [u8],
93 transfer_syntax: CompressedTransferSyntax,
94 payload_kind: CompressedPayloadKind,
95 info: Info,
96}
97
98impl<'a> PassthroughCandidate<'a> {
99 #[must_use]
101 pub const fn new(
102 bytes: &'a [u8],
103 transfer_syntax: CompressedTransferSyntax,
104 payload_kind: CompressedPayloadKind,
105 info: Info,
106 ) -> Self {
107 Self {
108 bytes,
109 transfer_syntax,
110 payload_kind,
111 info,
112 }
113 }
114
115 #[must_use]
118 pub const fn bytes(&self) -> &'a [u8] {
119 self.bytes
120 }
121
122 #[must_use]
124 pub const fn transfer_syntax(&self) -> CompressedTransferSyntax {
125 self.transfer_syntax
126 }
127
128 #[must_use]
130 pub const fn payload_kind(&self) -> CompressedPayloadKind {
131 self.payload_kind
132 }
133
134 #[must_use]
136 pub const fn info(&self) -> &Info {
137 &self.info
138 }
139
140 #[must_use]
143 pub fn evaluate(&self, requirements: &PassthroughRequirements) -> PassthroughDecision<'a> {
144 match self.copy_bytes_if_eligible(requirements) {
145 Ok(bytes) => PassthroughDecision::Copy { bytes },
146 Err(reason) => PassthroughDecision::Transcode { reason },
147 }
148 }
149
150 pub fn copy_bytes_if_eligible(
157 &self,
158 requirements: &PassthroughRequirements,
159 ) -> Result<&'a [u8], PassthroughRejectReason> {
160 if self.bytes.is_empty() {
161 return Err(PassthroughRejectReason::EmptyPayload);
162 }
163 if self.transfer_syntax != requirements.transfer_syntax {
164 return Err(PassthroughRejectReason::TransferSyntaxMismatch {
165 source: self.transfer_syntax,
166 destination: requirements.transfer_syntax,
167 });
168 }
169 if self.payload_kind != requirements.payload_kind {
170 return Err(PassthroughRejectReason::PayloadKindMismatch {
171 source: self.payload_kind,
172 destination: requirements.payload_kind,
173 });
174 }
175 if let Some(destination) = requirements.dimensions {
176 if self.info.dimensions != destination {
177 return Err(PassthroughRejectReason::DimensionsMismatch {
178 source: self.info.dimensions,
179 destination,
180 });
181 }
182 }
183 if let Some(destination) = requirements.components {
184 if self.info.components != destination {
185 return Err(PassthroughRejectReason::ComponentsMismatch {
186 source: self.info.components,
187 destination,
188 });
189 }
190 }
191 if let Some(destination) = requirements.bit_depth {
192 if self.info.bit_depth != destination {
193 return Err(PassthroughRejectReason::BitDepthMismatch {
194 source: self.info.bit_depth,
195 destination,
196 });
197 }
198 }
199 if let Some(destination) = requirements.colorspace {
200 if self.info.colorspace != destination {
201 return Err(PassthroughRejectReason::ColorspaceMismatch {
202 source: self.info.colorspace,
203 destination,
204 });
205 }
206 }
207 if let Some(destination) = requirements.tile_layout {
208 if self.info.tile_layout != Some(destination) {
209 return Err(PassthroughRejectReason::TileLayoutMismatch {
210 source: self.info.tile_layout,
211 destination,
212 });
213 }
214 }
215
216 Ok(self.bytes)
217 }
218}
219
220#[derive(Debug, Clone, Copy, PartialEq, Eq)]
222pub struct PassthroughRequirements {
223 pub transfer_syntax: CompressedTransferSyntax,
225 pub payload_kind: CompressedPayloadKind,
227 pub dimensions: Option<(u32, u32)>,
229 pub components: Option<u16>,
231 pub bit_depth: Option<u8>,
233 pub colorspace: Option<Colorspace>,
235 pub tile_layout: Option<TileLayout>,
237}
238
239impl PassthroughRequirements {
240 #[must_use]
242 pub const fn new(
243 transfer_syntax: CompressedTransferSyntax,
244 payload_kind: CompressedPayloadKind,
245 ) -> Self {
246 Self {
247 transfer_syntax,
248 payload_kind,
249 dimensions: None,
250 components: None,
251 bit_depth: None,
252 colorspace: None,
253 tile_layout: None,
254 }
255 }
256
257 #[must_use]
259 pub const fn with_dimensions(mut self, dimensions: (u32, u32)) -> Self {
260 self.dimensions = Some(dimensions);
261 self
262 }
263
264 #[must_use]
266 pub const fn with_components(mut self, components: u8) -> Self {
267 self.components = Some(components as u16);
268 self
269 }
270
271 #[must_use]
273 pub const fn with_component_count(mut self, components: u16) -> Self {
274 self.components = Some(components);
275 self
276 }
277
278 #[must_use]
280 pub const fn with_bit_depth(mut self, bit_depth: u8) -> Self {
281 self.bit_depth = Some(bit_depth);
282 self
283 }
284
285 #[must_use]
287 pub const fn with_colorspace(mut self, colorspace: Colorspace) -> Self {
288 self.colorspace = Some(colorspace);
289 self
290 }
291
292 #[must_use]
294 pub const fn with_tile_layout(mut self, tile_layout: TileLayout) -> Self {
295 self.tile_layout = Some(tile_layout);
296 self
297 }
298}
299
300#[derive(Debug, Clone, Copy, PartialEq, Eq)]
302pub enum PassthroughDecision<'a> {
303 Copy {
305 bytes: &'a [u8],
307 },
308 Transcode {
310 reason: PassthroughRejectReason,
312 },
313}
314
315#[derive(Debug, Clone, Copy, PartialEq, Eq)]
317#[non_exhaustive]
318pub enum PassthroughRejectReason {
319 EmptyPayload,
321 TransferSyntaxMismatch {
323 source: CompressedTransferSyntax,
325 destination: CompressedTransferSyntax,
327 },
328 PayloadKindMismatch {
330 source: CompressedPayloadKind,
332 destination: CompressedPayloadKind,
334 },
335 DimensionsMismatch {
337 source: (u32, u32),
339 destination: (u32, u32),
341 },
342 ComponentsMismatch {
344 source: u16,
346 destination: u16,
348 },
349 BitDepthMismatch {
351 source: u8,
353 destination: u8,
355 },
356 ColorspaceMismatch {
358 source: Colorspace,
360 destination: Colorspace,
362 },
363 TileLayoutMismatch {
365 source: Option<TileLayout>,
367 destination: TileLayout,
369 },
370}