1use jxl_bitstream::{Bitstream, BitstreamResult, U};
9use jxl_oxide_common::{Bundle, Name, define_bundle};
10
11pub mod color;
12use color::*;
13
14#[derive(Debug)]
18pub struct ImageHeader {
19 pub size: SizeHeader,
21 pub metadata: ImageMetadata,
23}
24
25impl<Ctx> Bundle<Ctx> for ImageHeader {
26 type Error = jxl_bitstream::Error;
27
28 fn parse(bitstream: &mut Bitstream, _: Ctx) -> BitstreamResult<Self> {
29 let signature = bitstream.read_bits(16)?;
30 if signature != 0xaff {
31 return Err(jxl_bitstream::Error::ValidationFailed(
32 "JPEG XL signature mismatch",
33 ));
34 }
35
36 let size = SizeHeader::parse(bitstream, ())?;
37 let metadata = ImageMetadata::parse(bitstream, ())?;
38
39 if metadata.ec_info.len() > 256 {
40 tracing::error!(num_extra = metadata.ec_info.len(), "num_extra too large");
41 return Err(jxl_bitstream::Error::ProfileConformance(
42 "num_extra too large",
43 ));
44 }
45
46 let tone_mapping = &metadata.tone_mapping;
47 if tone_mapping.intensity_target <= 0.0 {
48 return Err(jxl_bitstream::Error::ValidationFailed(
49 "Invalid intensity target",
50 ));
51 }
52 if tone_mapping.min_nits < 0.0 || tone_mapping.min_nits > tone_mapping.intensity_target {
53 return Err(jxl_bitstream::Error::ValidationFailed(
54 "Invalid tone mapping min_nits",
55 ));
56 }
57 if tone_mapping.linear_below < 0.0
58 || (tone_mapping.relative_to_max_display && tone_mapping.linear_below > 1.0)
59 {
60 return Err(jxl_bitstream::Error::ValidationFailed(
61 "Invalid tone mapping linear_below",
62 ));
63 }
64
65 Ok(Self { size, metadata })
66 }
67}
68
69impl ImageHeader {
70 #[inline]
72 pub fn width_with_orientation(&self) -> u32 {
73 self.metadata
74 .apply_orientation(self.size.width, self.size.height, 0, 0, false)
75 .0
76 }
77
78 #[inline]
80 pub fn height_with_orientation(&self) -> u32 {
81 self.metadata
82 .apply_orientation(self.size.width, self.size.height, 0, 0, false)
83 .1
84 }
85}
86
87define_bundle! {
88 #[derive(Debug)]
90 pub struct SizeHeader {
91 div8: ty(Bool) default(false),
92 h_div8: ty(1 + u(5)) cond(div8) default(0),
93 pub height:
95 ty(U32(1 + u(9), 1 + u(13), 1 + u(18), 1 + u(30))) cond(!div8)
96 default(8 * h_div8),
97 ratio: ty(u(3)) default(0),
98 w_div8: ty(1 + u(5)) cond(div8 && ratio == 0) default(0),
99 pub width:
101 ty(U32(1 + u(9), 1 + u(13), 1 + u(18), 1 + u(30))) cond(!div8 && ratio == 0)
102 default(SizeHeader::compute_default_width(ratio, w_div8, height)),
103 }
104}
105
106impl SizeHeader {
107 fn compute_default_width(ratio: u32, w_div8: u32, height: u32) -> u32 {
108 let height = height as u64;
109 let res = match ratio {
110 0 => 8 * w_div8 as u64,
111 1 => height,
112 2 => height * 12 / 10,
113 3 => height * 4 / 3,
114 4 => height * 3 / 2,
115 5 => height * 16 / 9,
116 6 => height * 5 / 4,
117 7 => height * 2,
118 _ => panic!("Invalid ratio const: {}", ratio),
119 };
120 res as u32
121 }
122}
123
124define_bundle! {
125 #[derive(Debug)]
127 pub struct ImageMetadata {
128 all_default: ty(Bool) default(true),
129 extra_fields: ty(Bool) cond(!all_default) default(false),
130 pub orientation: ty(1 + u(3)) cond(extra_fields) default(1),
132 have_intr_size: ty(Bool) cond(extra_fields) default(false),
133 pub intrinsic_size: ty(Bundle(Option<SizeHeader>)) cond(have_intr_size),
135 have_preview: ty(Bool) cond(extra_fields) default(false),
136 pub preview: ty(Bundle(Option<PreviewHeader>)) cond(have_preview),
138 have_animation: ty(Bool) cond(extra_fields) default(false),
139 pub animation: ty(Bundle(Option<AnimationHeader>)) cond(have_animation),
141 pub bit_depth: ty(Bundle(BitDepth)) cond(!all_default),
143 pub modular_16bit_buffers: ty(Bool) cond(!all_default) default(true),
145 num_extra: ty(U32(0, 1, 2 + u(4), 1 + u(12))) cond(!all_default) default(0),
146 pub ec_info: ty(Vec[Bundle(ExtraChannelInfo)]; num_extra) cond(!all_default),
148 pub xyb_encoded: ty(Bool) cond(!all_default) default(true),
150 pub colour_encoding: ty(Bundle(ColourEncoding)) cond(!all_default),
156 pub tone_mapping: ty(Bundle(ToneMapping)) cond(extra_fields),
158 pub extensions: ty(Bundle(Extensions)) cond(!all_default),
159 default_m: ty(Bool),
160 pub opsin_inverse_matrix: ty(Bundle(OpsinInverseMatrix)) cond(!default_m && xyb_encoded),
162 cw_mask: ty(u(3)) cond(!default_m) default(0),
163 pub up2_weight: ty(Array[F16]; 15) cond(cw_mask & 1 != 0) default(Self::D_UP2),
165 pub up4_weight: ty(Array[F16]; 55) cond(cw_mask & 2 != 0) default(Self::D_UP4),
167 pub up8_weight: ty(Array[F16]; 210) cond(cw_mask & 4 != 0) default(Self::D_UP8),
169 }
170
171 #[derive(Debug)]
172 pub struct PreviewHeader {
173 div8: ty(Bool),
174 h_div8: ty(U32(16, 32, 1 + u(5), 33 + u(9))) cond(div8) default(1),
175 pub height:
177 ty(U32(1 + u(6), 65 + u(8), 321 + u(10), 1345 + u(12))) cond(!div8)
178 default(8 * h_div8),
179 ratio: ty(u(3)),
180 w_div8: ty(U32(16, 32, 1 + u(5), 33 + u(9))) cond(div8) default(1),
181 pub width:
183 ty(U32(1 + u(6), 65 + u(8), 321 + u(10), 1345 + u(12))) cond(!div8)
184 default(SizeHeader::compute_default_width(ratio, w_div8, height)),
185 }
186
187 #[derive(Debug)]
192 pub struct AnimationHeader {
193 pub tps_numerator: ty(U32(100, 1000, 1 + u(10), 1 + u(30))) default(0),
195 pub tps_denominator: ty(U32(1, 1001, 1 + u(8), 1 + u(10))) default(0),
197 pub num_loops: ty(U32(0, u(3), u(16), u(32))) default(0),
199 pub have_timecodes: ty(Bool) default(false),
201 }
202}
203
204#[derive(Debug, Default)]
205#[allow(unused)]
206pub struct Extensions {
207 extension_bits: u64,
208}
209
210impl<Ctx> Bundle<Ctx> for Extensions {
211 type Error = jxl_bitstream::Error;
212
213 fn parse(bitstream: &mut Bitstream, _: Ctx) -> jxl_bitstream::BitstreamResult<Self> {
214 let extension_bits = bitstream.read_u64()?;
215 let mut bits = extension_bits;
216 let mut extension_data_bitlen = Vec::with_capacity(extension_bits.count_ones() as usize);
217 for extension_idx in 0..64 {
218 if bits & 1 != 0 {
219 tracing::warn!(extension_idx, "Unknown extension");
220 extension_data_bitlen.push(bitstream.read_u64()?);
221 }
222 bits >>= 1;
223 }
224
225 for len in extension_data_bitlen {
226 bitstream.skip_bits(len as usize)?;
227 }
228
229 Ok(Self { extension_bits })
230 }
231}
232
233impl ImageMetadata {
234 #[inline]
236 pub fn grayscale(&self) -> bool {
237 self.colour_encoding.colour_space() == ColourSpace::Grey
238 }
239
240 pub fn alpha(&self) -> Option<usize> {
242 self.ec_info
243 .iter()
244 .position(|info| matches!(info.ty, ExtraChannelType::Alpha { .. }))
245 }
246
247 #[inline]
249 pub fn apply_orientation(
250 &self,
251 width: u32,
252 height: u32,
253 left: i32,
254 top: i32,
255 inverse: bool,
256 ) -> (u32, u32, i32, i32) {
257 let (left, top) = match self.orientation {
258 1 => (left, top),
259 2 => (width as i32 - left - 1, top),
260 3 => (width as i32 - left - 1, height as i32 - top - 1),
261 4 => (left, height as i32 - top - 1),
262 5 => (top, left),
263 6 if inverse => (top, width as i32 - left - 1),
264 6 => (height as i32 - top - 1, left),
265 7 => (height as i32 - top - 1, width as i32 - left - 1),
266 8 if inverse => (height as i32 - top - 1, left),
267 8 => (top, width as i32 - left - 1),
268 _ => unreachable!(),
269 };
270 let (width, height) = match self.orientation {
271 1..=4 => (width, height),
272 5..=8 => (height, width),
273 _ => unreachable!(),
274 };
275 (width, height, left, top)
276 }
277}
278
279#[derive(Debug, Default, Clone)]
281pub struct ExtraChannelInfo {
282 pub ty: ExtraChannelType,
284 pub bit_depth: BitDepth,
286 pub dim_shift: u32,
288 pub name: Name,
290}
291
292impl<Ctx> Bundle<Ctx> for ExtraChannelInfo {
293 type Error = jxl_bitstream::Error;
294
295 fn parse(bitstream: &mut Bitstream, _: Ctx) -> BitstreamResult<Self> {
296 let default_alpha_channel = bitstream.read_bool()?;
297 if default_alpha_channel {
298 return Ok(Self::default());
299 }
300
301 let ty_id = bitstream.read_enum::<ExtraChannelTypeRaw>()?;
302 let bit_depth = BitDepth::parse(bitstream, ())?;
303 let dim_shift = bitstream.read_u32(0, 3, 4, 1 + U(3))?;
304 let name = Name::parse(bitstream, ())?;
305
306 let ty = match ty_id {
307 ExtraChannelTypeRaw::Alpha => ExtraChannelType::Alpha {
308 alpha_associated: bitstream.read_bool()?,
309 },
310 ExtraChannelTypeRaw::Depth => ExtraChannelType::Depth,
311 ExtraChannelTypeRaw::SpotColour => ExtraChannelType::SpotColour {
312 red: bitstream.read_f16_as_f32()?,
313 green: bitstream.read_f16_as_f32()?,
314 blue: bitstream.read_f16_as_f32()?,
315 solidity: bitstream.read_f16_as_f32()?,
316 },
317 ExtraChannelTypeRaw::SelectionMask => ExtraChannelType::SelectionMask,
318 ExtraChannelTypeRaw::Black => ExtraChannelType::Black,
319 ExtraChannelTypeRaw::Cfa => ExtraChannelType::Cfa {
320 cfa_channel: bitstream.read_u32(1, U(2), 3 + U(4), 19 + U(8))?,
321 },
322 ExtraChannelTypeRaw::Thermal => ExtraChannelType::Thermal,
323 ExtraChannelTypeRaw::NonOptional => ExtraChannelType::NonOptional,
324 ExtraChannelTypeRaw::Optional => ExtraChannelType::Optional,
325 };
326
327 Ok(Self {
328 ty,
329 bit_depth,
330 dim_shift,
331 name,
332 })
333 }
334}
335
336impl ExtraChannelInfo {
337 #[inline]
339 pub fn is_alpha(&self) -> bool {
340 matches!(self.ty, ExtraChannelType::Alpha { .. })
341 }
342
343 #[inline]
345 pub fn alpha_associated(&self) -> Option<bool> {
346 if let ExtraChannelType::Alpha { alpha_associated } = self.ty {
347 Some(alpha_associated)
348 } else {
349 None
350 }
351 }
352
353 #[inline]
355 pub fn is_black(&self) -> bool {
356 self.ty == ExtraChannelType::Black
357 }
358}
359
360#[derive(Debug, PartialEq, Copy, Clone)]
362#[repr(u8)]
363pub enum ExtraChannelType {
364 Alpha {
365 alpha_associated: bool,
366 } = 0,
367 Depth,
368 SpotColour {
369 red: f32,
370 green: f32,
371 blue: f32,
372 solidity: f32,
373 },
374 SelectionMask,
375 Black,
376 Cfa {
377 cfa_channel: u32,
378 },
379 Thermal,
380 NonOptional = 15,
381 Optional,
382}
383
384impl Default for ExtraChannelType {
385 fn default() -> Self {
386 Self::Alpha {
387 alpha_associated: false,
388 }
389 }
390}
391
392#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
393#[repr(u8)]
394enum ExtraChannelTypeRaw {
395 Alpha = 0,
396 Depth,
397 SpotColour,
398 SelectionMask,
399 Black,
400 Cfa,
401 Thermal,
402 NonOptional = 15,
403 Optional,
404}
405
406impl TryFrom<u32> for ExtraChannelTypeRaw {
407 type Error = ();
408
409 fn try_from(value: u32) -> std::result::Result<Self, Self::Error> {
410 Ok(match value {
411 0 => Self::Alpha,
412 1 => Self::Depth,
413 2 => Self::SpotColour,
414 3 => Self::SelectionMask,
415 4 => Self::Black,
416 5 => Self::Cfa,
417 6 => Self::Thermal,
418 15 => Self::NonOptional,
419 16 => Self::Optional,
420 _ => return Err(()),
421 })
422 }
423}
424
425#[derive(Debug, Copy, Clone, PartialEq, Eq)]
427pub enum BitDepth {
428 IntegerSample { bits_per_sample: u32 },
433 FloatSample { bits_per_sample: u32, exp_bits: u32 },
436}
437
438impl Default for BitDepth {
439 fn default() -> Self {
440 Self::IntegerSample { bits_per_sample: 8 }
441 }
442}
443
444impl BitDepth {
445 #[inline]
446 pub fn bits_per_sample(self) -> u32 {
447 match self {
448 Self::IntegerSample { bits_per_sample } => bits_per_sample,
449 Self::FloatSample {
450 bits_per_sample, ..
451 } => bits_per_sample,
452 }
453 }
454
455 #[inline]
457 pub fn parse_integer_sample(self, sample: i32) -> f32 {
458 match self {
459 Self::IntegerSample { bits_per_sample } => {
460 let div = (1i32 << bits_per_sample) - 1;
461 sample as f32 / div as f32
462 }
463 Self::FloatSample {
464 bits_per_sample,
465 exp_bits,
466 } => {
467 let sample = sample as u32;
468 let mantissa_bits = bits_per_sample - exp_bits - 1;
469 let mantissa_mask = (1u32 << mantissa_bits) - 1;
470 let exp_mask = ((1u32 << (bits_per_sample - 1)) - 1) ^ mantissa_mask;
471
472 let is_signed = (sample & (1u32 << (bits_per_sample - 1))) != 0;
473 let mantissa = sample & mantissa_mask;
474 let exp = ((sample & exp_mask) >> mantissa_bits) as i32;
475 let exp = exp - ((1 << (exp_bits - 1)) - 1);
476
477 let f32_mantissa_bits = f32::MANTISSA_DIGITS - 1;
479 let mantissa = match mantissa_bits.cmp(&f32_mantissa_bits) {
480 std::cmp::Ordering::Less => mantissa << (f32_mantissa_bits - mantissa_bits),
481 std::cmp::Ordering::Greater => mantissa >> (mantissa_bits - f32_mantissa_bits),
482 _ => mantissa,
483 };
484 let exp = (exp + 127) as u32;
485 let sign = is_signed as u32;
486
487 let bits = (sign << 31) | (exp << f32_mantissa_bits) | mantissa;
488 f32::from_bits(bits)
489 }
490 }
491 }
492}
493
494impl<Ctx> Bundle<Ctx> for BitDepth {
495 type Error = jxl_bitstream::Error;
496
497 fn parse(bitstream: &mut Bitstream, _ctx: Ctx) -> BitstreamResult<Self> {
498 if bitstream.read_bool()? {
499 let bits_per_sample = bitstream.read_u32(32, 16, 24, 1 + U(6))?;
501 let exp_bits = bitstream.read_bits(4)? + 1;
502 if !(2..=8).contains(&exp_bits) {
503 return Err(jxl_bitstream::Error::ValidationFailed(
504 "Invalid exp_bits per float sample",
505 ));
506 }
507 let mantissa_bits = bits_per_sample.wrapping_sub(exp_bits + 1);
508 if !(2..=23).contains(&mantissa_bits) {
509 return Err(jxl_bitstream::Error::ValidationFailed(
510 "Invalid mantissa_bits per float sample",
511 ));
512 }
513 Ok(Self::FloatSample {
514 bits_per_sample,
515 exp_bits,
516 })
517 } else {
518 let bits_per_sample = bitstream.read_u32(8, 10, 12, 1 + U(6))?;
519 if bits_per_sample > 31 {
520 return Err(jxl_bitstream::Error::ValidationFailed(
521 "Invalid bits_per_sample",
522 ));
523 }
524 Ok(Self::IntegerSample { bits_per_sample })
525 }
526 }
527}
528
529#[allow(clippy::excessive_precision)]
530#[rustfmt::skip]
531impl ImageMetadata {
532 const D_UP2: [f32; 15] = [
533 -0.01716200, -0.03452303, -0.04022174, -0.02921014, -0.00624645,
534 0.14111091, 0.28896755, 0.00278718, -0.01610267, 0.56661550,
535 0.03777607, -0.01986694, -0.03144731, -0.01185068, -0.00213539,
536 ];
537 const D_UP4: [f32; 55] = [
538 -0.02419067, -0.03491987, -0.03693351, -0.03094285, -0.00529785,
539 -0.01663432, -0.03556863, -0.03888905, -0.03516850, -0.00989469,
540 0.23651958, 0.33392945, -0.01073543, -0.01313181, -0.03556694,
541 0.13048175, 0.40103025, 0.03951150, -0.02077584, 0.46914198,
542 -0.00209270, -0.01484589, -0.04064806, 0.18942530, 0.56279892,
543 0.06674400, -0.02335494, -0.03551682, -0.00754830, -0.02267919,
544 -0.02363578, 0.00315804, -0.03399098, -0.01359519, -0.00091653,
545 -0.00335467, -0.01163294, -0.01610294, -0.00974088, -0.00191622,
546 -0.01095446, -0.03198464, -0.04455121, -0.02799790, -0.00645912,
547 0.06390599, 0.22963888, 0.00630981, -0.01897349, 0.67537268,
548 0.08483369, -0.02534994, -0.02205197, -0.01667999, -0.00384443,
549 ];
550 const D_UP8: [f32; 210] = [
551 -0.02928613, -0.03706353, -0.03783812, -0.03324558, -0.00447632,
552 -0.02519406, -0.03752601, -0.03901508, -0.03663285, -0.00646649,
553 -0.02066407, -0.03838633, -0.04002101, -0.03900035, -0.00901973,
554 -0.01626393, -0.03954148, -0.04046620, -0.03979621, -0.01224485,
555 0.29895328, 0.35757708, -0.02447552, -0.01081748, -0.04314594,
556 0.23903219, 0.41119301, -0.00573046, -0.01450239, -0.04246845,
557 0.17567618, 0.45220643, 0.02287757, -0.01936783, -0.03583255,
558 0.11572472, 0.47416733, 0.06284440, -0.02685066, 0.42720050,
559 -0.02248939, -0.01155273, -0.04562755, 0.28689496, 0.49093869,
560 -0.00007891, -0.01545926, -0.04562659, 0.21238920, 0.53980934,
561 0.03369474, -0.02070211, -0.03866988, 0.14229550, 0.56593398,
562 0.08045181, -0.02888298, -0.03680918, -0.00542229, -0.02920477,
563 -0.02788574, -0.02118180, -0.03942402, -0.00775547, -0.02433614,
564 -0.03193943, -0.02030828, -0.04044014, -0.01074016, -0.01930822,
565 -0.03620399, -0.01974125, -0.03919545, -0.01456093, -0.00045072,
566 -0.00360110, -0.01020207, -0.01231907, -0.00638988, -0.00071592,
567 -0.00279122, -0.00957115, -0.01288327, -0.00730937, -0.00107783,
568 -0.00210156, -0.00890705, -0.01317668, -0.00813895, -0.00153491,
569 -0.02128481, -0.04173044, -0.04831487, -0.03293190, -0.00525260,
570 -0.01720322, -0.04052736, -0.05045706, -0.03607317, -0.00738030,
571 -0.01341764, -0.03965629, -0.05151616, -0.03814886, -0.01005819,
572 0.18968273, 0.33063684, -0.01300105, -0.01372950, -0.04017465,
573 0.13727832, 0.36402234, 0.01027890, -0.01832107, -0.03365072,
574 0.08734506, 0.38194295, 0.04338228, -0.02525993, 0.56408126,
575 0.00458352, -0.01648227, -0.04887868, 0.24585519, 0.62026135,
576 0.04314807, -0.02213737, -0.04158014, 0.16637289, 0.65027023,
577 0.09621636, -0.03101388, -0.04082742, -0.00904519, -0.02790922,
578 -0.02117818, 0.00798662, -0.03995711, -0.01243427, -0.02231705,
579 -0.02946266, 0.00992055, -0.03600283, -0.01684920, -0.00111684,
580 -0.00411204, -0.01297130, -0.01723725, -0.01022545, -0.00165306,
581 -0.00313110, -0.01218016, -0.01763266, -0.01125620, -0.00231663,
582 -0.01374149, -0.03797620, -0.05142937, -0.03117307, -0.00581914,
583 -0.01064003, -0.03608089, -0.05272168, -0.03375670, -0.00795586,
584 0.09628104, 0.27129991, -0.00353779, -0.01734151, -0.03153981,
585 0.05686230, 0.28500998, 0.02230594, -0.02374955, 0.68214326,
586 0.05018048, -0.02320852, -0.04383616, 0.18459474, 0.71517975,
587 0.10805613, -0.03263677, -0.03637639, -0.01394373, -0.02511203,
588 -0.01728636, 0.05407331, -0.02867568, -0.01893131, -0.00240854,
589 -0.00446511, -0.01636187, -0.02377053, -0.01522848, -0.00333334,
590 -0.00819975, -0.02964169, -0.04499287, -0.02745350, -0.00612408,
591 0.02727416, 0.19446600, 0.00159832, -0.02232473, 0.74982506,
592 0.11452620, -0.03348048, -0.01605681, -0.02070339, -0.00458223,
593 ];
594}