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 Jpeg2000Lossless,
19 Jpeg2000Lossy,
21 HtJpeg2000Lossless,
23 HtJpeg2000Lossy,
25}
26
27impl CompressedTransferSyntax {
28 #[must_use]
30 pub const fn is_lossless(self) -> bool {
31 matches!(self, Self::Jpeg2000Lossless | Self::HtJpeg2000Lossless)
32 }
33
34 #[must_use]
36 pub const fn is_jpeg2000_family(self) -> bool {
37 matches!(
38 self,
39 Self::Jpeg2000Lossless
40 | Self::Jpeg2000Lossy
41 | Self::HtJpeg2000Lossless
42 | Self::HtJpeg2000Lossy
43 )
44 }
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
49#[non_exhaustive]
50pub enum CompressedPayloadKind {
51 JpegInterchange,
53 Jpeg2000Codestream,
55 Jp2File,
57 JphFile,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct PassthroughCandidate<'a> {
64 bytes: &'a [u8],
65 transfer_syntax: CompressedTransferSyntax,
66 payload_kind: CompressedPayloadKind,
67 info: Info,
68}
69
70impl<'a> PassthroughCandidate<'a> {
71 #[must_use]
73 pub const fn new(
74 bytes: &'a [u8],
75 transfer_syntax: CompressedTransferSyntax,
76 payload_kind: CompressedPayloadKind,
77 info: Info,
78 ) -> Self {
79 Self {
80 bytes,
81 transfer_syntax,
82 payload_kind,
83 info,
84 }
85 }
86
87 #[must_use]
90 pub const fn bytes(&self) -> &'a [u8] {
91 self.bytes
92 }
93
94 #[must_use]
96 pub const fn transfer_syntax(&self) -> CompressedTransferSyntax {
97 self.transfer_syntax
98 }
99
100 #[must_use]
102 pub const fn payload_kind(&self) -> CompressedPayloadKind {
103 self.payload_kind
104 }
105
106 #[must_use]
108 pub const fn info(&self) -> &Info {
109 &self.info
110 }
111
112 #[must_use]
115 pub fn evaluate(&self, requirements: &PassthroughRequirements) -> PassthroughDecision<'a> {
116 match self.copy_bytes_if_eligible(requirements) {
117 Ok(bytes) => PassthroughDecision::Copy { bytes },
118 Err(reason) => PassthroughDecision::Transcode { reason },
119 }
120 }
121
122 pub fn copy_bytes_if_eligible(
124 &self,
125 requirements: &PassthroughRequirements,
126 ) -> Result<&'a [u8], PassthroughRejectReason> {
127 if self.bytes.is_empty() {
128 return Err(PassthroughRejectReason::EmptyPayload);
129 }
130 if self.transfer_syntax != requirements.transfer_syntax {
131 return Err(PassthroughRejectReason::TransferSyntaxMismatch {
132 source: self.transfer_syntax,
133 destination: requirements.transfer_syntax,
134 });
135 }
136 if self.payload_kind != requirements.payload_kind {
137 return Err(PassthroughRejectReason::PayloadKindMismatch {
138 source: self.payload_kind,
139 destination: requirements.payload_kind,
140 });
141 }
142 if let Some(destination) = requirements.dimensions {
143 if self.info.dimensions != destination {
144 return Err(PassthroughRejectReason::DimensionsMismatch {
145 source: self.info.dimensions,
146 destination,
147 });
148 }
149 }
150 if let Some(destination) = requirements.components {
151 if self.info.components != destination {
152 return Err(PassthroughRejectReason::ComponentsMismatch {
153 source: self.info.components,
154 destination,
155 });
156 }
157 }
158 if let Some(destination) = requirements.bit_depth {
159 if self.info.bit_depth != destination {
160 return Err(PassthroughRejectReason::BitDepthMismatch {
161 source: self.info.bit_depth,
162 destination,
163 });
164 }
165 }
166 if let Some(destination) = requirements.colorspace {
167 if self.info.colorspace != destination {
168 return Err(PassthroughRejectReason::ColorspaceMismatch {
169 source: self.info.colorspace,
170 destination,
171 });
172 }
173 }
174 if let Some(destination) = requirements.tile_layout {
175 if self.info.tile_layout != Some(destination) {
176 return Err(PassthroughRejectReason::TileLayoutMismatch {
177 source: self.info.tile_layout,
178 destination,
179 });
180 }
181 }
182
183 Ok(self.bytes)
184 }
185}
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub struct PassthroughRequirements {
190 pub transfer_syntax: CompressedTransferSyntax,
192 pub payload_kind: CompressedPayloadKind,
194 pub dimensions: Option<(u32, u32)>,
196 pub components: Option<u16>,
198 pub bit_depth: Option<u8>,
200 pub colorspace: Option<Colorspace>,
202 pub tile_layout: Option<TileLayout>,
204}
205
206impl PassthroughRequirements {
207 #[must_use]
209 pub const fn new(
210 transfer_syntax: CompressedTransferSyntax,
211 payload_kind: CompressedPayloadKind,
212 ) -> Self {
213 Self {
214 transfer_syntax,
215 payload_kind,
216 dimensions: None,
217 components: None,
218 bit_depth: None,
219 colorspace: None,
220 tile_layout: None,
221 }
222 }
223
224 #[must_use]
226 pub const fn with_dimensions(mut self, dimensions: (u32, u32)) -> Self {
227 self.dimensions = Some(dimensions);
228 self
229 }
230
231 #[must_use]
233 pub const fn with_components(mut self, components: u8) -> Self {
234 self.components = Some(components as u16);
235 self
236 }
237
238 #[must_use]
240 pub const fn with_component_count(mut self, components: u16) -> Self {
241 self.components = Some(components);
242 self
243 }
244
245 #[must_use]
247 pub const fn with_bit_depth(mut self, bit_depth: u8) -> Self {
248 self.bit_depth = Some(bit_depth);
249 self
250 }
251
252 #[must_use]
254 pub const fn with_colorspace(mut self, colorspace: Colorspace) -> Self {
255 self.colorspace = Some(colorspace);
256 self
257 }
258
259 #[must_use]
261 pub const fn with_tile_layout(mut self, tile_layout: TileLayout) -> Self {
262 self.tile_layout = Some(tile_layout);
263 self
264 }
265}
266
267#[derive(Debug, Clone, Copy, PartialEq, Eq)]
269pub enum PassthroughDecision<'a> {
270 Copy {
272 bytes: &'a [u8],
274 },
275 Transcode {
277 reason: PassthroughRejectReason,
279 },
280}
281
282#[derive(Debug, Clone, Copy, PartialEq, Eq)]
284#[non_exhaustive]
285pub enum PassthroughRejectReason {
286 EmptyPayload,
288 TransferSyntaxMismatch {
290 source: CompressedTransferSyntax,
292 destination: CompressedTransferSyntax,
294 },
295 PayloadKindMismatch {
297 source: CompressedPayloadKind,
299 destination: CompressedPayloadKind,
301 },
302 DimensionsMismatch {
304 source: (u32, u32),
306 destination: (u32, u32),
308 },
309 ComponentsMismatch {
311 source: u16,
313 destination: u16,
315 },
316 BitDepthMismatch {
318 source: u8,
320 destination: u8,
322 },
323 ColorspaceMismatch {
325 source: Colorspace,
327 destination: Colorspace,
329 },
330 TileLayoutMismatch {
332 source: Option<TileLayout>,
334 destination: TileLayout,
336 },
337}