1use crate::JXL_SIGNATURE;
8use crate::bit_writer::BitWriter;
9use crate::error::Result;
10
11use super::color_encoding::ColorEncoding;
12use super::extra_channels::ExtraChannelInfo;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16#[repr(u8)]
17pub enum Orientation {
18 #[default]
19 Identity = 1,
20 FlipHorizontal = 2,
21 Rotate180 = 3,
22 FlipVertical = 4,
23 Transpose = 5,
24 Rotate90CW = 6,
25 AntiTranspose = 7,
26 Rotate90CCW = 8,
27}
28
29#[derive(Debug, Clone, Copy)]
31pub struct BitDepth {
32 pub float_sample: bool,
34 pub bits_per_sample: u32,
36 pub exponent_bits: u32,
38}
39
40impl Default for BitDepth {
41 fn default() -> Self {
42 Self {
43 float_sample: false,
44 bits_per_sample: 8,
45 exponent_bits: 0,
46 }
47 }
48}
49
50impl BitDepth {
51 pub fn uint8() -> Self {
53 Self::default()
54 }
55
56 pub fn uint16() -> Self {
58 Self {
59 float_sample: false,
60 bits_per_sample: 16,
61 exponent_bits: 0,
62 }
63 }
64
65 pub fn float32() -> Self {
67 Self {
68 float_sample: true,
69 bits_per_sample: 32,
70 exponent_bits: 8,
71 }
72 }
73
74 pub fn float16() -> Self {
76 Self {
77 float_sample: true,
78 bits_per_sample: 16,
79 exponent_bits: 5,
80 }
81 }
82}
83
84#[derive(Debug, Clone, Default)]
86pub struct AnimationHeader {
87 pub tps_numerator: u32,
89 pub tps_denominator: u32,
91 pub num_loops: u32,
93 pub have_timecodes: bool,
95}
96
97impl AnimationHeader {
98 pub fn write(&self, writer: &mut BitWriter) -> Result<()> {
106 match self.tps_numerator {
108 100 => writer.write(2, 0)?,
109 1000 => writer.write(2, 1)?,
110 v if (1..=1024).contains(&v) => {
111 writer.write(2, 2)?;
112 writer.write(10, (v - 1) as u64)?;
113 }
114 v => {
115 debug_assert!(v >= 1, "tps_numerator must be >= 1");
116 writer.write(2, 3)?;
117 writer.write(30, (v - 1) as u64)?;
118 }
119 }
120
121 match self.tps_denominator {
123 1 => writer.write(2, 0)?,
124 1001 => writer.write(2, 1)?,
125 v @ 2..=256 => {
126 writer.write(2, 2)?;
127 writer.write(8, (v - 1) as u64)?;
128 }
129 v => {
130 debug_assert!((1..=1025).contains(&v), "tps_denominator {v} out of range");
131 writer.write(2, 3)?;
132 writer.write(10, (v - 1) as u64)?;
133 }
134 }
135
136 match self.num_loops {
138 0 => writer.write(2, 0)?,
139 v @ 1..=7 => {
140 writer.write(2, 1)?;
141 writer.write(3, v as u64)?;
142 }
143 v @ 8..=65535 => {
144 writer.write(2, 2)?;
145 writer.write(16, v as u64)?;
146 }
147 v => {
148 writer.write(2, 3)?;
149 writer.write(32, v as u64)?;
150 }
151 }
152
153 writer.write_bit(self.have_timecodes)?;
155
156 Ok(())
157 }
158}
159
160#[derive(Debug, Clone)]
162pub struct ImageMetadata {
163 pub bit_depth: BitDepth,
165 pub color_encoding: ColorEncoding,
167 pub extra_channels: Vec<ExtraChannelInfo>,
169 pub orientation: Orientation,
171 pub animation: Option<AnimationHeader>,
173 pub intensity_target: f32,
175 pub min_nits: f32,
177 pub have_intrinsic_size: bool,
179 pub intrinsic_width: u32,
181 pub intrinsic_height: u32,
183 pub xyb_encoded: bool,
185}
186
187impl Default for ImageMetadata {
188 fn default() -> Self {
189 Self {
190 bit_depth: BitDepth::default(),
191 color_encoding: ColorEncoding::default(),
192 extra_channels: Vec::new(),
193 orientation: Orientation::default(),
194 animation: None,
195 intensity_target: 255.0,
196 min_nits: 0.0,
197 have_intrinsic_size: false,
198 intrinsic_width: 0,
199 intrinsic_height: 0,
200 xyb_encoded: false, }
202 }
203}
204
205#[derive(Debug, Clone)]
207pub struct FileHeader {
208 pub width: u32,
210 pub height: u32,
212 pub metadata: ImageMetadata,
214}
215
216impl FileHeader {
217 pub fn new_rgb(width: u32, height: u32) -> Self {
219 Self {
220 width,
221 height,
222 metadata: ImageMetadata::default(),
223 }
224 }
225
226 pub fn new_rgba(width: u32, height: u32) -> Self {
228 let mut header = Self::new_rgb(width, height);
229 header
230 .metadata
231 .extra_channels
232 .push(ExtraChannelInfo::alpha());
233 header
234 }
235
236 pub fn new_gray(width: u32, height: u32) -> Self {
238 let mut header = Self::new_rgb(width, height);
239 header.metadata.color_encoding = ColorEncoding::gray();
240 header
241 }
242
243 pub fn new_rgb_lossy(width: u32, height: u32) -> Self {
245 let mut header = Self::new_rgb(width, height);
246 header.metadata.xyb_encoded = true;
247 header
248 }
249
250 pub fn write_signature(writer: &mut BitWriter) -> Result<()> {
252 writer.write_u8(JXL_SIGNATURE[0])?;
253 writer.write_u8(JXL_SIGNATURE[1])?;
254 Ok(())
255 }
256
257 fn write_size_header(&self, writer: &mut BitWriter) -> Result<()> {
270 let h_div8 = self.height.is_multiple_of(8) && self.height / 8 >= 1 && self.height / 8 <= 32;
272 let w_div8 = self.width.is_multiple_of(8) && self.width / 8 >= 1 && self.width / 8 <= 32;
273 let small = h_div8 && w_div8;
274
275 crate::trace::debug_eprintln!(
276 "SIZE_HDR: {}x{}, small={}, h_div8={}, w_div8={}",
277 self.width,
278 self.height,
279 small,
280 h_div8,
281 w_div8
282 );
283 writer.write_bit(small)?;
284
285 if small {
286 crate::trace::debug_eprintln!("SIZE_HDR: ysize_div8_minus_1 = {}", self.height / 8 - 1);
288 writer.write(5, (self.height / 8 - 1) as u64)?;
289
290 let ratio = self.compute_ratio();
291 crate::trace::debug_eprintln!("SIZE_HDR: ratio = {}", ratio);
292 writer.write(3, ratio as u64)?;
293
294 if ratio == 0 {
295 crate::trace::debug_eprintln!(
297 "SIZE_HDR: xsize_div8_minus_1 = {}",
298 self.width / 8 - 1
299 );
300 writer.write(5, (self.width / 8 - 1) as u64)?;
301 }
302 } else {
303 self.write_size_u2s(writer, self.height - 1)?;
306
307 let ratio = self.compute_ratio();
308 writer.write(3, ratio as u64)?;
309
310 if ratio == 0 {
311 self.write_size_u2s(writer, self.width - 1)?;
313 }
314 }
315
316 Ok(())
317 }
318
319 fn write_size_u2s(&self, writer: &mut BitWriter, value: u32) -> Result<()> {
322 if value < (1 << 9) {
323 writer.write(2, 0)?; writer.write(9, value as u64)?;
325 } else if value < (1 << 13) {
326 writer.write(2, 1)?; writer.write(13, value as u64)?;
328 } else if value < (1 << 18) {
329 writer.write(2, 2)?; writer.write(18, value as u64)?;
331 } else {
332 writer.write(2, 3)?; writer.write(30, value as u64)?;
334 }
335 Ok(())
336 }
337
338 fn compute_ratio(&self) -> u8 {
340 if self.width == self.height {
342 1 } else if self.width * 10 == self.height * 12 {
344 2 } else if self.width * 3 == self.height * 4 {
346 3 } else if self.width * 2 == self.height * 3 {
348 4 } else if self.width * 9 == self.height * 16 {
350 5 } else if self.width * 4 == self.height * 5 {
352 6 } else if self.width == self.height * 2 {
354 7 } else {
356 0 }
358 }
359
360 pub fn write(&self, writer: &mut BitWriter) -> Result<()> {
362 crate::trace::debug_eprintln!("FHDR [bit {}]: Starting file header", writer.bits_written());
363 Self::write_signature(writer)?;
364 crate::trace::debug_eprintln!("FHDR [bit {}]: After signature", writer.bits_written());
365 self.write_size_header(writer)?;
366 crate::trace::debug_eprintln!("FHDR [bit {}]: After size header", writer.bits_written());
367 self.write_image_metadata(writer)?;
368 crate::trace::debug_eprintln!("FHDR [bit {}]: After metadata", writer.bits_written());
369 self.write_transform_data(writer)?;
372 crate::trace::debug_eprintln!("FHDR [bit {}]: After transform_data", writer.bits_written());
373 Ok(())
374 }
375
376 fn write_transform_data(&self, writer: &mut BitWriter) -> Result<()> {
379 crate::trace::debug_eprintln!(
382 "XFRM [bit {}]: transform_data.all_default = true",
383 writer.bits_written()
384 );
385 writer.write_bit(true)?;
386 Ok(())
387 }
388
389 fn write_image_metadata(&self, writer: &mut BitWriter) -> Result<()> {
391 let meta = &self.metadata;
392
393 let all_default = self.is_metadata_default();
395 crate::trace::debug_eprintln!(
396 "META [bit {}]: all_default = {}",
397 writer.bits_written(),
398 all_default
399 );
400 writer.write_bit(all_default)?;
401
402 if all_default {
403 return Ok(());
404 }
405
406 let extra_fields = meta.animation.is_some()
408 || meta.orientation != Orientation::Identity
409 || meta.have_intrinsic_size
410 || meta.intensity_target != 255.0;
411 crate::trace::debug_eprintln!(
412 "META [bit {}]: extra_fields = {}",
413 writer.bits_written(),
414 extra_fields
415 );
416 writer.write_bit(extra_fields)?;
417
418 if extra_fields {
419 writer.write(3, (meta.orientation as u8 - 1) as u64)?;
421
422 writer.write_bit(meta.have_intrinsic_size)?;
424 if meta.have_intrinsic_size {
425 self.write_size_u2s(writer, meta.intrinsic_width - 1)?;
427 self.write_size_u2s(writer, meta.intrinsic_height - 1)?;
428 }
429
430 writer.write_bit(false)?;
432
433 writer.write_bit(meta.animation.is_some())?;
435 if let Some(ref anim) = meta.animation {
436 anim.write(writer)?;
437 }
438 }
439
440 crate::trace::debug_eprintln!("META [bit {}]: Writing bit_depth", writer.bits_written());
442 meta.bit_depth.write(writer)?;
443 crate::trace::debug_eprintln!("META [bit {}]: After bit_depth", writer.bits_written());
444
445 let mod16_sufficient = meta.bit_depth.bits_per_sample <= 12;
448 crate::trace::debug_eprintln!(
449 "META [bit {}]: modular_16_bit_buffer_sufficient = {}",
450 writer.bits_written(),
451 mod16_sufficient
452 );
453 writer.write_bit(mod16_sufficient)?;
454
455 let num_extra = meta.extra_channels.len() as u32;
457 crate::trace::debug_eprintln!(
458 "META [bit {}]: num_extra_channels = {}",
459 writer.bits_written(),
460 num_extra
461 );
462 writer.write_u32_coder(num_extra, 0, 1, 2, 1, 12)?;
463
464 for ec in &meta.extra_channels {
465 ec.write(writer)?;
466 }
467
468 crate::trace::debug_eprintln!(
470 "META [bit {}]: xyb_encoded = {}",
471 writer.bits_written(),
472 meta.xyb_encoded
473 );
474 writer.write_bit(meta.xyb_encoded)?;
475
476 crate::trace::debug_eprintln!(
478 "META [bit {}]: Writing color_encoding",
479 writer.bits_written()
480 );
481 meta.color_encoding.write(writer)?;
482 crate::trace::debug_eprintln!("META [bit {}]: After color_encoding", writer.bits_written());
483
484 if extra_fields {
486 writer.write_bit(true)?; }
488
489 writer.write(2, 0)?;
492
493 Ok(())
494 }
495
496 fn is_metadata_default(&self) -> bool {
499 false
503 }
504}
505
506impl BitDepth {
507 pub fn write(&self, writer: &mut BitWriter) -> Result<()> {
509 writer.write_bit(self.float_sample)?;
510 if self.float_sample {
511 writer.write_u32_coder(self.bits_per_sample, 32, 16, 24, 1, 6)?;
513 writer.write(4, (self.exponent_bits - 1) as u64)?;
515 } else {
516 writer.write_u32_coder(self.bits_per_sample, 8, 10, 12, 1, 6)?;
518 }
519 Ok(())
520 }
521}
522
523#[cfg(test)]
524mod tests {
525 use super::*;
526
527 #[test]
528 fn test_signature() {
529 let mut writer = BitWriter::new();
530 FileHeader::write_signature(&mut writer).unwrap();
531 let bytes = writer.finish();
532 assert_eq!(bytes, vec![0xFF, 0x0A]);
533 }
534
535 #[test]
536 fn test_simple_header() {
537 let header = FileHeader::new_rgb(256, 256);
538 let mut writer = BitWriter::new();
539 header.write(&mut writer).unwrap();
540
541 let bytes = writer.finish_with_padding();
542 assert_eq!(&bytes[0..2], &[0xFF, 0x0A]);
544 }
545}