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 || meta.min_nits != 0.0;
412 crate::trace::debug_eprintln!(
413 "META [bit {}]: extra_fields = {}",
414 writer.bits_written(),
415 extra_fields
416 );
417 writer.write_bit(extra_fields)?;
418
419 if extra_fields {
420 writer.write(3, (meta.orientation as u8 - 1) as u64)?;
422
423 writer.write_bit(meta.have_intrinsic_size)?;
425 if meta.have_intrinsic_size {
426 self.write_size_u2s(writer, meta.intrinsic_width - 1)?;
428 self.write_size_u2s(writer, meta.intrinsic_height - 1)?;
429 }
430
431 writer.write_bit(false)?;
433
434 writer.write_bit(meta.animation.is_some())?;
436 if let Some(ref anim) = meta.animation {
437 anim.write(writer)?;
438 }
439 }
440
441 crate::trace::debug_eprintln!("META [bit {}]: Writing bit_depth", writer.bits_written());
443 meta.bit_depth.write(writer)?;
444 crate::trace::debug_eprintln!("META [bit {}]: After bit_depth", writer.bits_written());
445
446 let mod16_sufficient = meta.bit_depth.bits_per_sample <= 12;
449 crate::trace::debug_eprintln!(
450 "META [bit {}]: modular_16_bit_buffer_sufficient = {}",
451 writer.bits_written(),
452 mod16_sufficient
453 );
454 writer.write_bit(mod16_sufficient)?;
455
456 let num_extra = meta.extra_channels.len() as u32;
458 crate::trace::debug_eprintln!(
459 "META [bit {}]: num_extra_channels = {}",
460 writer.bits_written(),
461 num_extra
462 );
463 writer.write_u32_coder(num_extra, 0, 1, 2, 1, 12)?;
464
465 for ec in &meta.extra_channels {
466 ec.write(writer)?;
467 }
468
469 crate::trace::debug_eprintln!(
471 "META [bit {}]: xyb_encoded = {}",
472 writer.bits_written(),
473 meta.xyb_encoded
474 );
475 writer.write_bit(meta.xyb_encoded)?;
476
477 crate::trace::debug_eprintln!(
479 "META [bit {}]: Writing color_encoding",
480 writer.bits_written()
481 );
482 meta.color_encoding.write(writer)?;
483 crate::trace::debug_eprintln!("META [bit {}]: After color_encoding", writer.bits_written());
484
485 if extra_fields {
487 let tone_all_default = meta.intensity_target == 255.0 && meta.min_nits == 0.0;
488 writer.write_bit(tone_all_default)?;
489 if !tone_all_default {
490 crate::f16::write_f16(meta.intensity_target, writer)?;
491 crate::f16::write_f16(meta.min_nits, writer)?;
492 writer.write_bit(false)?; crate::f16::write_f16(0.0, writer)?; }
495 }
496
497 writer.write(2, 0)?;
500
501 Ok(())
502 }
503
504 fn is_metadata_default(&self) -> bool {
507 false
511 }
512}
513
514impl BitDepth {
515 pub fn write(&self, writer: &mut BitWriter) -> Result<()> {
517 writer.write_bit(self.float_sample)?;
518 if self.float_sample {
519 writer.write_u32_coder(self.bits_per_sample, 32, 16, 24, 1, 6)?;
521 writer.write(4, (self.exponent_bits - 1) as u64)?;
523 } else {
524 writer.write_u32_coder(self.bits_per_sample, 8, 10, 12, 1, 6)?;
526 }
527 Ok(())
528 }
529}
530
531#[cfg(test)]
532mod tests {
533 use super::*;
534
535 #[test]
536 fn test_signature() {
537 let mut writer = BitWriter::new();
538 FileHeader::write_signature(&mut writer).unwrap();
539 let bytes = writer.finish();
540 assert_eq!(bytes, vec![0xFF, 0x0A]);
541 }
542
543 #[test]
544 fn test_simple_header() {
545 let header = FileHeader::new_rgb(256, 256);
546 let mut writer = BitWriter::new();
547 header.write(&mut writer).unwrap();
548
549 let bytes = writer.finish_with_padding();
550 assert_eq!(&bytes[0..2], &[0xFF, 0x0A]);
552 }
553}