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(
129 &self,
130 requirements: &PassthroughRequirements,
131 ) -> Result<&'a [u8], PassthroughRejectReason> {
132 if self.bytes.is_empty() {
133 return Err(PassthroughRejectReason::EmptyPayload);
134 }
135 if self.transfer_syntax != requirements.transfer_syntax {
136 return Err(PassthroughRejectReason::TransferSyntaxMismatch {
137 source: self.transfer_syntax,
138 destination: requirements.transfer_syntax,
139 });
140 }
141 if self.payload_kind != requirements.payload_kind {
142 return Err(PassthroughRejectReason::PayloadKindMismatch {
143 source: self.payload_kind,
144 destination: requirements.payload_kind,
145 });
146 }
147 if let Some(destination) = requirements.dimensions {
148 if self.info.dimensions != destination {
149 return Err(PassthroughRejectReason::DimensionsMismatch {
150 source: self.info.dimensions,
151 destination,
152 });
153 }
154 }
155 if let Some(destination) = requirements.components {
156 if self.info.components != destination {
157 return Err(PassthroughRejectReason::ComponentsMismatch {
158 source: self.info.components,
159 destination,
160 });
161 }
162 }
163 if let Some(destination) = requirements.bit_depth {
164 if self.info.bit_depth != destination {
165 return Err(PassthroughRejectReason::BitDepthMismatch {
166 source: self.info.bit_depth,
167 destination,
168 });
169 }
170 }
171 if let Some(destination) = requirements.colorspace {
172 if self.info.colorspace != destination {
173 return Err(PassthroughRejectReason::ColorspaceMismatch {
174 source: self.info.colorspace,
175 destination,
176 });
177 }
178 }
179 if let Some(destination) = requirements.tile_layout {
180 if self.info.tile_layout != Some(destination) {
181 return Err(PassthroughRejectReason::TileLayoutMismatch {
182 source: self.info.tile_layout,
183 destination,
184 });
185 }
186 }
187
188 Ok(self.bytes)
189 }
190}
191
192#[derive(Debug, Clone, Copy, PartialEq, Eq)]
194pub struct PassthroughRequirements {
195 pub transfer_syntax: CompressedTransferSyntax,
197 pub payload_kind: CompressedPayloadKind,
199 pub dimensions: Option<(u32, u32)>,
201 pub components: Option<u16>,
203 pub bit_depth: Option<u8>,
205 pub colorspace: Option<Colorspace>,
207 pub tile_layout: Option<TileLayout>,
209}
210
211impl PassthroughRequirements {
212 #[must_use]
214 pub const fn new(
215 transfer_syntax: CompressedTransferSyntax,
216 payload_kind: CompressedPayloadKind,
217 ) -> Self {
218 Self {
219 transfer_syntax,
220 payload_kind,
221 dimensions: None,
222 components: None,
223 bit_depth: None,
224 colorspace: None,
225 tile_layout: None,
226 }
227 }
228
229 #[must_use]
231 pub const fn with_dimensions(mut self, dimensions: (u32, u32)) -> Self {
232 self.dimensions = Some(dimensions);
233 self
234 }
235
236 #[must_use]
238 pub const fn with_components(mut self, components: u8) -> Self {
239 self.components = Some(components as u16);
240 self
241 }
242
243 #[must_use]
245 pub const fn with_component_count(mut self, components: u16) -> Self {
246 self.components = Some(components);
247 self
248 }
249
250 #[must_use]
252 pub const fn with_bit_depth(mut self, bit_depth: u8) -> Self {
253 self.bit_depth = Some(bit_depth);
254 self
255 }
256
257 #[must_use]
259 pub const fn with_colorspace(mut self, colorspace: Colorspace) -> Self {
260 self.colorspace = Some(colorspace);
261 self
262 }
263
264 #[must_use]
266 pub const fn with_tile_layout(mut self, tile_layout: TileLayout) -> Self {
267 self.tile_layout = Some(tile_layout);
268 self
269 }
270}
271
272#[derive(Debug, Clone, Copy, PartialEq, Eq)]
274pub enum PassthroughDecision<'a> {
275 Copy {
277 bytes: &'a [u8],
279 },
280 Transcode {
282 reason: PassthroughRejectReason,
284 },
285}
286
287#[derive(Debug, Clone, Copy, PartialEq, Eq)]
289#[non_exhaustive]
290pub enum PassthroughRejectReason {
291 EmptyPayload,
293 TransferSyntaxMismatch {
295 source: CompressedTransferSyntax,
297 destination: CompressedTransferSyntax,
299 },
300 PayloadKindMismatch {
302 source: CompressedPayloadKind,
304 destination: CompressedPayloadKind,
306 },
307 DimensionsMismatch {
309 source: (u32, u32),
311 destination: (u32, u32),
313 },
314 ComponentsMismatch {
316 source: u16,
318 destination: u16,
320 },
321 BitDepthMismatch {
323 source: u8,
325 destination: u8,
327 },
328 ColorspaceMismatch {
330 source: Colorspace,
332 destination: Colorspace,
334 },
335 TileLayoutMismatch {
337 source: Option<TileLayout>,
339 destination: TileLayout,
341 },
342}