1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
// SPDX-License-Identifier: Apache-2.0
use crate::{Colorspace, Info, TileLayout};
/// Compressed syntax carried by a source frame or accepted by a destination.
///
/// The enum intentionally names codec profiles rather than container-specific
/// UIDs. Container integrations can map these variants to their local transfer
/// syntax identifiers and keep that policy outside the codec crates.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum CompressedTransferSyntax {
/// Baseline 8-bit JPEG interchange format.
JpegBaseline8,
/// Sequential JPEG beyond the baseline profile.
JpegExtendedSequential,
/// Classic JPEG 2000 codestream using reversible coding.
Jpeg2000Lossless,
/// Classic JPEG 2000 codestream using irreversible coding.
Jpeg2000Lossy,
/// High-throughput JPEG 2000 codestream using reversible coding.
HtJpeg2000Lossless,
/// High-throughput JPEG 2000 codestream using irreversible coding.
HtJpeg2000Lossy,
}
impl CompressedTransferSyntax {
/// True when the syntax profile is lossless.
#[must_use]
pub const fn is_lossless(self) -> bool {
matches!(self, Self::Jpeg2000Lossless | Self::HtJpeg2000Lossless)
}
/// True when the syntax belongs to the JPEG 2000 family.
#[must_use]
pub const fn is_jpeg2000_family(self) -> bool {
matches!(
self,
Self::Jpeg2000Lossless
| Self::Jpeg2000Lossy
| Self::HtJpeg2000Lossless
| Self::HtJpeg2000Lossy
)
}
}
/// Encapsulation shape of the compressed bytes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum CompressedPayloadKind {
/// Complete JPEG interchange byte stream.
JpegInterchange,
/// Raw JPEG 2000 / HTJ2K codestream bytes.
Jpeg2000Codestream,
/// JP2 file-format wrapper around a JPEG 2000 codestream.
Jp2File,
}
/// A borrowed compressed frame/tile that may be copied unchanged.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PassthroughCandidate<'a> {
bytes: &'a [u8],
transfer_syntax: CompressedTransferSyntax,
payload_kind: CompressedPayloadKind,
info: Info,
}
impl<'a> PassthroughCandidate<'a> {
/// Construct a candidate from already-inspected compressed bytes.
#[must_use]
pub const fn new(
bytes: &'a [u8],
transfer_syntax: CompressedTransferSyntax,
payload_kind: CompressedPayloadKind,
info: Info,
) -> Self {
Self {
bytes,
transfer_syntax,
payload_kind,
info,
}
}
/// Original compressed bytes. A successful passthrough decision returns
/// this exact slice.
#[must_use]
pub const fn bytes(&self) -> &'a [u8] {
self.bytes
}
/// Source compressed syntax.
#[must_use]
pub const fn transfer_syntax(&self) -> CompressedTransferSyntax {
self.transfer_syntax
}
/// Source payload/container shape.
#[must_use]
pub const fn payload_kind(&self) -> CompressedPayloadKind {
self.payload_kind
}
/// Header metadata inspected from the compressed payload.
#[must_use]
pub const fn info(&self) -> &Info {
&self.info
}
/// Evaluate whether this candidate can be copied unchanged into a
/// destination with the supplied requirements.
#[must_use]
pub fn evaluate(&self, requirements: &PassthroughRequirements) -> PassthroughDecision<'a> {
match self.copy_bytes_if_eligible(requirements) {
Ok(bytes) => PassthroughDecision::Copy { bytes },
Err(reason) => PassthroughDecision::Transcode { reason },
}
}
/// Return the original compressed bytes only when passthrough is legal.
pub fn copy_bytes_if_eligible(
&self,
requirements: &PassthroughRequirements,
) -> Result<&'a [u8], PassthroughRejectReason> {
if self.bytes.is_empty() {
return Err(PassthroughRejectReason::EmptyPayload);
}
if self.transfer_syntax != requirements.transfer_syntax {
return Err(PassthroughRejectReason::TransferSyntaxMismatch {
source: self.transfer_syntax,
destination: requirements.transfer_syntax,
});
}
if self.payload_kind != requirements.payload_kind {
return Err(PassthroughRejectReason::PayloadKindMismatch {
source: self.payload_kind,
destination: requirements.payload_kind,
});
}
if let Some(destination) = requirements.dimensions {
if self.info.dimensions != destination {
return Err(PassthroughRejectReason::DimensionsMismatch {
source: self.info.dimensions,
destination,
});
}
}
if let Some(destination) = requirements.components {
if self.info.components != destination {
return Err(PassthroughRejectReason::ComponentsMismatch {
source: self.info.components,
destination,
});
}
}
if let Some(destination) = requirements.bit_depth {
if self.info.bit_depth != destination {
return Err(PassthroughRejectReason::BitDepthMismatch {
source: self.info.bit_depth,
destination,
});
}
}
if let Some(destination) = requirements.colorspace {
if self.info.colorspace != destination {
return Err(PassthroughRejectReason::ColorspaceMismatch {
source: self.info.colorspace,
destination,
});
}
}
if let Some(destination) = requirements.tile_layout {
if self.info.tile_layout != Some(destination) {
return Err(PassthroughRejectReason::TileLayoutMismatch {
source: self.info.tile_layout,
destination,
});
}
}
Ok(self.bytes)
}
}
/// Destination requirements for copying compressed bytes unchanged.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PassthroughRequirements {
/// Required destination compressed syntax.
pub transfer_syntax: CompressedTransferSyntax,
/// Required destination payload/container shape.
pub payload_kind: CompressedPayloadKind,
/// Optional exact output dimensions.
pub dimensions: Option<(u32, u32)>,
/// Optional exact component count.
pub components: Option<u8>,
/// Optional exact bit depth.
pub bit_depth: Option<u8>,
/// Optional exact colorspace.
pub colorspace: Option<Colorspace>,
/// Optional exact tile layout.
pub tile_layout: Option<TileLayout>,
}
impl PassthroughRequirements {
/// Start a requirements set with the mandatory syntax and payload shape.
#[must_use]
pub const fn new(
transfer_syntax: CompressedTransferSyntax,
payload_kind: CompressedPayloadKind,
) -> Self {
Self {
transfer_syntax,
payload_kind,
dimensions: None,
components: None,
bit_depth: None,
colorspace: None,
tile_layout: None,
}
}
/// Require exact frame/tile dimensions.
#[must_use]
pub const fn with_dimensions(mut self, dimensions: (u32, u32)) -> Self {
self.dimensions = Some(dimensions);
self
}
/// Require an exact component count.
#[must_use]
pub const fn with_components(mut self, components: u8) -> Self {
self.components = Some(components);
self
}
/// Require an exact bit depth.
#[must_use]
pub const fn with_bit_depth(mut self, bit_depth: u8) -> Self {
self.bit_depth = Some(bit_depth);
self
}
/// Require an exact colorspace.
#[must_use]
pub const fn with_colorspace(mut self, colorspace: Colorspace) -> Self {
self.colorspace = Some(colorspace);
self
}
/// Require an exact tile layout.
#[must_use]
pub const fn with_tile_layout(mut self, tile_layout: TileLayout) -> Self {
self.tile_layout = Some(tile_layout);
self
}
}
/// Result of a passthrough eligibility check.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PassthroughDecision<'a> {
/// Copy these compressed bytes unchanged.
Copy {
/// Borrowed source bytes to copy unchanged.
bytes: &'a [u8],
},
/// Decode/transcode instead, for the stated reason.
Transcode {
/// Reason byte-preserving passthrough was rejected.
reason: PassthroughRejectReason,
},
}
/// First reason a compressed payload was rejected for byte-preserving copy.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum PassthroughRejectReason {
/// The source compressed payload is empty.
EmptyPayload,
/// Source and destination compressed syntaxes differ.
TransferSyntaxMismatch {
/// Source syntax found in the candidate.
source: CompressedTransferSyntax,
/// Required destination syntax.
destination: CompressedTransferSyntax,
},
/// Source and destination payload/container shapes differ.
PayloadKindMismatch {
/// Source payload shape found in the candidate.
source: CompressedPayloadKind,
/// Required destination payload shape.
destination: CompressedPayloadKind,
},
/// Source and destination dimensions differ.
DimensionsMismatch {
/// Source dimensions found in the candidate.
source: (u32, u32),
/// Required destination dimensions.
destination: (u32, u32),
},
/// Source and destination component counts differ.
ComponentsMismatch {
/// Source component count found in the candidate.
source: u8,
/// Required destination component count.
destination: u8,
},
/// Source and destination bit depths differ.
BitDepthMismatch {
/// Source bit depth found in the candidate.
source: u8,
/// Required destination bit depth.
destination: u8,
},
/// Source and destination colorspaces differ.
ColorspaceMismatch {
/// Source colorspace found in the candidate.
source: Colorspace,
/// Required destination colorspace.
destination: Colorspace,
},
/// Source and destination tile layouts differ.
TileLayoutMismatch {
/// Source tile layout found in the candidate.
source: Option<TileLayout>,
/// Required destination tile layout.
destination: TileLayout,
},
}