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}
58
59#[derive(Debug, Clone, PartialEq, Eq)]
61pub struct PassthroughCandidate<'a> {
62 bytes: &'a [u8],
63 transfer_syntax: CompressedTransferSyntax,
64 payload_kind: CompressedPayloadKind,
65 info: Info,
66}
67
68impl<'a> PassthroughCandidate<'a> {
69 #[must_use]
71 pub const fn new(
72 bytes: &'a [u8],
73 transfer_syntax: CompressedTransferSyntax,
74 payload_kind: CompressedPayloadKind,
75 info: Info,
76 ) -> Self {
77 Self {
78 bytes,
79 transfer_syntax,
80 payload_kind,
81 info,
82 }
83 }
84
85 #[must_use]
88 pub const fn bytes(&self) -> &'a [u8] {
89 self.bytes
90 }
91
92 #[must_use]
94 pub const fn transfer_syntax(&self) -> CompressedTransferSyntax {
95 self.transfer_syntax
96 }
97
98 #[must_use]
100 pub const fn payload_kind(&self) -> CompressedPayloadKind {
101 self.payload_kind
102 }
103
104 #[must_use]
106 pub const fn info(&self) -> &Info {
107 &self.info
108 }
109
110 #[must_use]
113 pub fn evaluate(&self, requirements: &PassthroughRequirements) -> PassthroughDecision<'a> {
114 match self.copy_bytes_if_eligible(requirements) {
115 Ok(bytes) => PassthroughDecision::Copy { bytes },
116 Err(reason) => PassthroughDecision::Transcode { reason },
117 }
118 }
119
120 pub fn copy_bytes_if_eligible(
122 &self,
123 requirements: &PassthroughRequirements,
124 ) -> Result<&'a [u8], PassthroughRejectReason> {
125 if self.bytes.is_empty() {
126 return Err(PassthroughRejectReason::EmptyPayload);
127 }
128 if self.transfer_syntax != requirements.transfer_syntax {
129 return Err(PassthroughRejectReason::TransferSyntaxMismatch {
130 source: self.transfer_syntax,
131 destination: requirements.transfer_syntax,
132 });
133 }
134 if self.payload_kind != requirements.payload_kind {
135 return Err(PassthroughRejectReason::PayloadKindMismatch {
136 source: self.payload_kind,
137 destination: requirements.payload_kind,
138 });
139 }
140 if let Some(destination) = requirements.dimensions {
141 if self.info.dimensions != destination {
142 return Err(PassthroughRejectReason::DimensionsMismatch {
143 source: self.info.dimensions,
144 destination,
145 });
146 }
147 }
148 if let Some(destination) = requirements.components {
149 if self.info.components != destination {
150 return Err(PassthroughRejectReason::ComponentsMismatch {
151 source: self.info.components,
152 destination,
153 });
154 }
155 }
156 if let Some(destination) = requirements.bit_depth {
157 if self.info.bit_depth != destination {
158 return Err(PassthroughRejectReason::BitDepthMismatch {
159 source: self.info.bit_depth,
160 destination,
161 });
162 }
163 }
164 if let Some(destination) = requirements.colorspace {
165 if self.info.colorspace != destination {
166 return Err(PassthroughRejectReason::ColorspaceMismatch {
167 source: self.info.colorspace,
168 destination,
169 });
170 }
171 }
172 if let Some(destination) = requirements.tile_layout {
173 if self.info.tile_layout != Some(destination) {
174 return Err(PassthroughRejectReason::TileLayoutMismatch {
175 source: self.info.tile_layout,
176 destination,
177 });
178 }
179 }
180
181 Ok(self.bytes)
182 }
183}
184
185#[derive(Debug, Clone, Copy, PartialEq, Eq)]
187pub struct PassthroughRequirements {
188 pub transfer_syntax: CompressedTransferSyntax,
190 pub payload_kind: CompressedPayloadKind,
192 pub dimensions: Option<(u32, u32)>,
194 pub components: Option<u8>,
196 pub bit_depth: Option<u8>,
198 pub colorspace: Option<Colorspace>,
200 pub tile_layout: Option<TileLayout>,
202}
203
204impl PassthroughRequirements {
205 #[must_use]
207 pub const fn new(
208 transfer_syntax: CompressedTransferSyntax,
209 payload_kind: CompressedPayloadKind,
210 ) -> Self {
211 Self {
212 transfer_syntax,
213 payload_kind,
214 dimensions: None,
215 components: None,
216 bit_depth: None,
217 colorspace: None,
218 tile_layout: None,
219 }
220 }
221
222 #[must_use]
224 pub const fn with_dimensions(mut self, dimensions: (u32, u32)) -> Self {
225 self.dimensions = Some(dimensions);
226 self
227 }
228
229 #[must_use]
231 pub const fn with_components(mut self, components: u8) -> Self {
232 self.components = Some(components);
233 self
234 }
235
236 #[must_use]
238 pub const fn with_bit_depth(mut self, bit_depth: u8) -> Self {
239 self.bit_depth = Some(bit_depth);
240 self
241 }
242
243 #[must_use]
245 pub const fn with_colorspace(mut self, colorspace: Colorspace) -> Self {
246 self.colorspace = Some(colorspace);
247 self
248 }
249
250 #[must_use]
252 pub const fn with_tile_layout(mut self, tile_layout: TileLayout) -> Self {
253 self.tile_layout = Some(tile_layout);
254 self
255 }
256}
257
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
260pub enum PassthroughDecision<'a> {
261 Copy {
263 bytes: &'a [u8],
265 },
266 Transcode {
268 reason: PassthroughRejectReason,
270 },
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
275#[non_exhaustive]
276pub enum PassthroughRejectReason {
277 EmptyPayload,
279 TransferSyntaxMismatch {
281 source: CompressedTransferSyntax,
283 destination: CompressedTransferSyntax,
285 },
286 PayloadKindMismatch {
288 source: CompressedPayloadKind,
290 destination: CompressedPayloadKind,
292 },
293 DimensionsMismatch {
295 source: (u32, u32),
297 destination: (u32, u32),
299 },
300 ComponentsMismatch {
302 source: u8,
304 destination: u8,
306 },
307 BitDepthMismatch {
309 source: u8,
311 destination: u8,
313 },
314 ColorspaceMismatch {
316 source: Colorspace,
318 destination: Colorspace,
320 },
321 TileLayoutMismatch {
323 source: Option<TileLayout>,
325 destination: TileLayout,
327 },
328}