1use std::borrow::Cow;
43use std::sync::Arc;
44
45use enough::StopReason;
46use whereat::At;
47use zencodec::decode::{
48 AnimationFrameDecoder, BatchMeta, Decode, DecodeCapabilities, DecodeJob, DecodeOutput,
49 DecodeRowSink, DecoderConfig, Event, IncrementalDecode, Input, InputRequest, MetadataUpdate,
50 OutputInfo, Progress, SinkError, SourceColor, StepError, StreamingDecode,
51};
52use zencodec::encode::{
53 AnimationFrameEncoder, AnimationOutputTiming, EncodeCapabilities, EncodeJob, EncodeOutput,
54 EncodePolicy, Encoder, EncoderConfig, OutputBuffer, OutputTiming,
55};
56use zencodec::gainmap::{
57 DecodedGainMap, GainMapInfo, GainMapPresence, GainMapRender, GainMapSource, Iso21496Format,
58 parse_iso21496_fmt, serialize_iso21496_fmt,
59};
60use zencodec::source::{AccessPattern, DynReadAt};
61use zencodec::{
62 AnimationFrame, CategorizedError, Cicp, CodecError, CodecIoKind, ContentLightLevel,
63 ErrorCategory, ImageFormat, ImageFormatDefinition, ImageInfo, ImageSequence, MasteringDisplay,
64 Metadata, Orientation, ResourceLimits, SharedPulse, UnsupportedOperation,
65};
66use zenpixels::{
67 ColorContext, ColorModel, PixelBuffer, PixelDescriptor, PixelSlice, TransferFunction,
68};
69
70#[derive(Debug)]
76pub enum RefError {
77 Unsupported(UnsupportedOperation),
79 Invalid(String),
81 Cancelled(StopReason),
84 Limit(zencodec::LimitExceeded),
86 Sink(SinkError),
88}
89
90impl std::fmt::Display for RefError {
91 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92 match self {
93 Self::Unsupported(e) => write!(f, "reference: unsupported: {e}"),
94 Self::Invalid(s) => write!(f, "reference: invalid: {s}"),
95 Self::Cancelled(r) => write!(f, "reference: cancelled: {r}"),
96 Self::Limit(e) => write!(f, "reference: limit: {e}"),
97 Self::Sink(e) => write!(f, "reference: sink: {e}"),
98 }
99 }
100}
101
102impl std::error::Error for RefError {
103 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
104 match self {
105 Self::Unsupported(e) => Some(e),
106 Self::Limit(e) => Some(e),
107 Self::Sink(e) => Some(e.as_ref()),
108 Self::Cancelled(_) | Self::Invalid(_) => None,
110 }
111 }
112}
113
114impl From<UnsupportedOperation> for RefError {
115 fn from(e: UnsupportedOperation) -> Self {
116 Self::Unsupported(e)
117 }
118}
119impl From<StopReason> for RefError {
120 fn from(r: StopReason) -> Self {
121 Self::Cancelled(r)
122 }
123}
124
125impl CategorizedError for RefError {
128 fn codec_name(&self) -> Option<&'static str> {
129 Some(MINIMAL_CODEC_NAME)
130 }
131 fn category(&self) -> ErrorCategory {
132 match self {
133 Self::Unsupported(e) => e.category(),
134 Self::Invalid(_) => ErrorCategory::Image(zencodec::ImageError::Malformed),
135 Self::Cancelled(r) => r.category(),
136 Self::Limit(e) => e.category(),
137 Self::Sink(_) => ErrorCategory::Io(CodecIoKind::opaque()),
138 }
139 }
140}
141impl From<zencodec::LimitExceeded> for RefError {
142 fn from(e: zencodec::LimitExceeded) -> Self {
143 Self::Limit(e)
144 }
145}
146
147impl From<RefError> for At<CodecError> {
158 #[track_caller]
159 fn from(e: RefError) -> Self {
160 use whereat::ErrorAtExt;
161 CodecError::of(e.start_at())
162 }
163}
164
165pub(crate) const MINIMAL_CODEC_NAME: &str = "zencodec-testkit/minimal";
169
170const MAGIC: &[u8; 4] = b"ZCR1";
175
176pub(crate) fn descriptor_for_bpp(bpp: u8) -> Result<PixelDescriptor, RefError> {
177 match bpp {
178 3 => Ok(PixelDescriptor::RGB8_SRGB),
179 4 => Ok(PixelDescriptor::RGBA8_SRGB),
180 6 => Ok(PixelDescriptor::RGB16_SRGB),
181 8 => Ok(PixelDescriptor::RGBA16_SRGB),
182 b => Err(RefError::Invalid(format!("unsupported bpp {b}"))),
183 }
184}
185
186pub(crate) fn descriptor_for_header(h: &Header) -> Result<PixelDescriptor, RefError> {
190 let base = descriptor_for_bpp(h.bpp)?;
191 Ok(match h.meta.cicp {
192 Some(c) => {
193 let with_transfer = match TransferFunction::from_cicp(c.transfer_characteristics) {
194 Some(t) => base.with_transfer(t),
195 None => base,
196 };
197 with_transfer.with_primaries(c.color_primaries_enum())
198 }
199 None => base,
200 })
201}
202
203#[derive(Clone, Debug)]
206pub(crate) struct GainMapWire {
207 pub(crate) info: GainMapInfo,
208 pub(crate) pixels: Vec<u8>,
209}
210
211impl GainMapWire {
212 fn from_decoded(gm: DecodedGainMap) -> Result<Self, RefError> {
217 let ps = gm.pixels.as_slice();
218 let channels = gm.channels();
219 let bytes_per_sample = ps.descriptor().bytes_per_pixel() / channels.max(1) as usize;
220 if !matches!(channels, 1 | 3) || bytes_per_sample != 1 {
221 return Err(RefError::Invalid(format!(
222 "gain map must be 8-bit with 1 or 3 channels, got {:?}",
223 ps.descriptor()
224 )));
225 }
226 let mut pixels =
227 Vec::with_capacity(ps.width() as usize * ps.rows() as usize * channels as usize);
228 let rb = ps.width() as usize * channels as usize;
229 for y in 0..ps.rows() {
230 pixels.extend_from_slice(&ps.row(y)[..rb]);
231 }
232 let mut info = gm.metadata;
233 info.width = ps.width();
234 info.height = ps.rows();
235 info.channels = channels;
236 info.bit_depth = 8;
237 Ok(Self { info, pixels })
238 }
239
240 fn descriptor(&self) -> PixelDescriptor {
241 if self.info.channels == 1 {
243 PixelDescriptor::GRAY8
244 } else {
245 PixelDescriptor::RGB8
246 }
247 }
248
249 fn to_decoded(&self) -> Result<DecodedGainMap, RefError> {
250 let buf = PixelBuffer::from_vec(
251 self.pixels.clone(),
252 self.info.width,
253 self.info.height,
254 self.descriptor(),
255 )
256 .map_err(|e| RefError::Invalid(format!("gain map buffer: {e}")))?;
257 Ok(DecodedGainMap::new(buf, self.info.clone()))
258 }
259}
260
261pub(crate) struct Header {
263 pub(crate) width: u32,
264 pub(crate) height: u32,
265 pub(crate) frame_count: u32,
266 pub(crate) bpp: u8,
267 pub(crate) meta: Metadata,
268 pub(crate) gain_map: Option<GainMapWire>,
269 pub(crate) frames_offset: usize,
270}
271
272fn push_u32(out: &mut Vec<u8>, v: u32) {
273 out.extend_from_slice(&v.to_le_bytes());
274}
275
276fn read_u32(data: &[u8], at: usize) -> Result<u32, RefError> {
277 data.get(at..at + 4)
278 .map(|s| u32::from_le_bytes(s.try_into().unwrap()))
279 .ok_or_else(|| RefError::Invalid("truncated u32".into()))
280}
281
282fn push_cicp(out: &mut Vec<u8>, c: &Cicp) {
283 out.push(c.color_primaries);
284 out.push(c.transfer_characteristics);
285 out.push(c.matrix_coefficients);
286 out.push(c.full_range as u8);
287}
288
289fn write_header(
290 out: &mut Vec<u8>,
291 w: u32,
292 h: u32,
293 frames: u32,
294 bpp: u8,
295 meta: &Metadata,
296 gain_map: Option<&GainMapWire>,
297) {
298 out.extend_from_slice(MAGIC);
299 push_u32(out, w);
300 push_u32(out, h);
301 push_u32(out, frames);
302 out.push(bpp);
303 out.push(meta.orientation.to_exif());
304
305 let mut flags = 0u8;
306 if meta.icc_profile.is_some() {
307 flags |= 1;
308 }
309 if meta.exif.is_some() {
310 flags |= 2;
311 }
312 if meta.xmp.is_some() {
313 flags |= 4;
314 }
315 if meta.cicp.is_some() {
316 flags |= 8;
317 }
318 if meta.content_light_level.is_some() {
319 flags |= 16;
320 }
321 if meta.mastering_display.is_some() {
322 flags |= 32;
323 }
324 if gain_map.is_some() {
325 flags |= 64;
326 }
327 out.push(flags);
328
329 if let Some(icc) = &meta.icc_profile {
330 push_u32(out, icc.len() as u32);
331 out.extend_from_slice(icc);
332 }
333 if let Some(exif) = &meta.exif {
334 push_u32(out, exif.len() as u32);
335 out.extend_from_slice(exif);
336 }
337 if let Some(xmp) = &meta.xmp {
338 push_u32(out, xmp.len() as u32);
339 out.extend_from_slice(xmp);
340 }
341 if let Some(c) = &meta.cicp {
342 push_cicp(out, c);
343 }
344 if let Some(cll) = &meta.content_light_level {
345 out.extend_from_slice(&cll.max_content_light_level.to_le_bytes());
346 out.extend_from_slice(&cll.max_frame_average_light_level.to_le_bytes());
347 }
348 if let Some(md) = &meta.mastering_display {
349 for xy in md.primaries_xy {
350 out.extend_from_slice(&xy[0].to_le_bytes());
351 out.extend_from_slice(&xy[1].to_le_bytes());
352 }
353 out.extend_from_slice(&md.white_point_xy[0].to_le_bytes());
354 out.extend_from_slice(&md.white_point_xy[1].to_le_bytes());
355 out.extend_from_slice(&md.max_luminance.to_le_bytes());
356 out.extend_from_slice(&md.min_luminance.to_le_bytes());
357 }
358 if let Some(gm) = gain_map {
359 push_u32(out, gm.info.width);
360 push_u32(out, gm.info.height);
361 out.push(gm.info.channels);
362 match &gm.info.alternate_cicp {
363 Some(c) => {
364 out.push(1);
365 push_cicp(out, c);
366 }
367 None => out.push(0),
368 }
369 let iso = serialize_iso21496_fmt(&gm.info.params, Iso21496Format::JxlJhgm);
370 push_u32(out, iso.len() as u32);
371 out.extend_from_slice(&iso);
372 out.extend_from_slice(&gm.pixels);
373 }
374}
375
376fn read_cicp(data: &[u8], at: usize) -> Result<Cicp, RefError> {
377 let c = data
378 .get(at..at + 4)
379 .ok_or_else(|| RefError::Invalid("truncated cicp".into()))?;
380 Ok(Cicp::new(c[0], c[1], c[2], c[3] != 0))
381}
382
383fn read_f32(data: &[u8], at: usize) -> Result<f32, RefError> {
384 data.get(at..at + 4)
385 .map(|s| f32::from_le_bytes(s.try_into().unwrap()))
386 .ok_or_else(|| RefError::Invalid("truncated f32".into()))
387}
388
389pub(crate) fn parse_header(data: &[u8]) -> Result<Header, RefError> {
390 if data.len() < 19 || &data[..4] != MAGIC {
393 return Err(RefError::Invalid("bad magic / short header".into()));
394 }
395 let width = read_u32(data, 4)?;
396 let height = read_u32(data, 8)?;
397 let frame_count = read_u32(data, 12)?;
398 let bpp = data[16];
399 let orient = data[17];
400 let flags = data[18];
401 descriptor_for_bpp(bpp)?; if width == 0 || height == 0 || frame_count == 0 {
403 return Err(RefError::Invalid("zero dimension / frame count".into()));
404 }
405
406 let mut meta = Metadata::none()
407 .with_orientation(Orientation::from_exif(orient).unwrap_or(Orientation::Identity));
408 let mut at = 19usize;
409
410 let read_blob = |at: &mut usize| -> Result<Vec<u8>, RefError> {
411 let len = read_u32(data, *at)? as usize;
412 *at += 4;
413 let bytes = data
414 .get(*at..*at + len)
415 .ok_or_else(|| RefError::Invalid("truncated blob".into()))?
416 .to_vec();
417 *at += len;
418 Ok(bytes)
419 };
420
421 if flags & 1 != 0 {
422 meta = meta.with_icc(read_blob(&mut at)?);
423 }
424 if flags & 2 != 0 {
425 meta = meta.with_exif(read_blob(&mut at)?);
426 }
427 if flags & 4 != 0 {
428 meta = meta.with_xmp(read_blob(&mut at)?);
429 }
430 if flags & 8 != 0 {
431 meta = meta.with_cicp(read_cicp(data, at)?);
432 at += 4;
433 }
434 if flags & 16 != 0 {
435 let c = data
436 .get(at..at + 4)
437 .ok_or_else(|| RefError::Invalid("truncated clli".into()))?;
438 meta = meta.with_content_light_level(ContentLightLevel::new(
439 u16::from_le_bytes([c[0], c[1]]),
440 u16::from_le_bytes([c[2], c[3]]),
441 ));
442 at += 4;
443 }
444 if flags & 32 != 0 {
445 let mut f = [0f32; 10];
446 for (i, v) in f.iter_mut().enumerate() {
447 *v = read_f32(data, at + i * 4)?;
448 }
449 at += 40;
450 meta = meta.with_mastering_display(MasteringDisplay::new(
451 [[f[0], f[1]], [f[2], f[3]], [f[4], f[5]]],
452 [f[6], f[7]],
453 f[8],
454 f[9],
455 ));
456 }
457 let gain_map = if flags & 64 != 0 {
458 let gw = read_u32(data, at)?;
459 let gh = read_u32(data, at + 4)?;
460 let channels = *data
461 .get(at + 8)
462 .ok_or_else(|| RefError::Invalid("truncated gain map".into()))?;
463 let has_alt = *data
464 .get(at + 9)
465 .ok_or_else(|| RefError::Invalid("truncated gain map".into()))?;
466 at += 10;
467 let alternate_cicp = if has_alt != 0 {
468 let c = read_cicp(data, at)?;
469 at += 4;
470 Some(c)
471 } else {
472 None
473 };
474 let iso = read_blob(&mut at)?;
475 let params = parse_iso21496_fmt(&iso, Iso21496Format::JxlJhgm)
476 .map_err(|e| RefError::Invalid(format!("gain map metadata: {e}")))?;
477 if !matches!(channels, 1 | 3) || gw == 0 || gh == 0 {
478 return Err(RefError::Invalid("bad gain map geometry".into()));
479 }
480 let len = gw as usize * gh as usize * channels as usize;
481 let pixels = data
482 .get(at..at + len)
483 .ok_or_else(|| RefError::Invalid("truncated gain map pixels".into()))?
484 .to_vec();
485 at += len;
486 let mut info = GainMapInfo::new(params, gw, gh, channels);
487 if let Some(c) = alternate_cicp {
488 info = info.with_alternate_cicp(c);
489 }
490 Some(GainMapWire { info, pixels })
491 } else {
492 None
493 };
494
495 Ok(Header {
496 width,
497 height,
498 frame_count,
499 bpp,
500 meta,
501 gain_map,
502 frames_offset: at,
503 })
504}
505
506pub(crate) fn frame_pixels_offset(h: &Header, i: u32) -> usize {
508 let frame_bytes = 4 + h.width as usize * h.height as usize * h.bpp as usize;
509 h.frames_offset + i as usize * frame_bytes + 4
510}
511
512pub(crate) fn frame_pixel_len(h: &Header) -> usize {
513 h.width as usize * h.height as usize * h.bpp as usize
514}
515
516pub(crate) fn zcr_inventory(
525 data: &[u8],
526 format: ImageFormat,
527) -> Result<zencodec::inventory::Inventory, RefError> {
528 use zencodec::inventory::{
529 Disposition, Inventory, InventoryError, MetadataKind, Part, PartKind, PartTag,
530 };
531 let len = data.len() as u64;
532 let mut inv = Inventory::new(format, len);
533 let cap = |e: InventoryError| RefError::Invalid(format!("inventory: {e}"));
534 let malformed_from = |inv: &mut Inventory, at: u64, why: &str| -> Result<(), RefError> {
535 if at < len {
536 inv.push(
537 None,
538 Part::new(
539 PartKind::Gap,
540 PartTag::None,
541 at..len,
542 Disposition::Malformed,
543 )
544 .with_detail(why.to_owned()),
545 )
546 .map_err(cap)?;
547 }
548 Ok(())
549 };
550
551 let header_ok = data.len() >= 19
552 && &data[..4] == MAGIC
553 && descriptor_for_bpp(data[16]).is_ok()
554 && read_u32(data, 4).is_ok_and(|w| w > 0)
555 && read_u32(data, 8).is_ok_and(|h| h > 0)
556 && read_u32(data, 12).is_ok_and(|f| f > 0);
557 if !header_ok {
558 malformed_from(&mut inv, 0, "not a ZCR1 header")?;
559 return Ok(inv);
560 }
561 let (w, h, frames, bpp, flags) = (
562 u64::from(read_u32(data, 4)?),
563 u64::from(read_u32(data, 8)?),
564 u64::from(read_u32(data, 12)?),
565 u64::from(data[16]),
566 data[18],
567 );
568 inv.push(
569 None,
570 Part::new(
571 PartKind::Header,
572 PartTag::None,
573 0..19,
574 Disposition::Structure,
575 )
576 .with_label("ZCR1")
577 .with_detail("magic, geometry, frame count, layout, orientation, flags"),
578 )
579 .map_err(cap)?;
580
581 let mut at: u64 = 19;
582 let blob_end = |at: u64| -> Option<u64> {
584 let n = read_u32(data, usize::try_from(at).ok()?).ok()?;
585 let end = at.checked_add(4)?.checked_add(u64::from(n))?;
586 (end <= len).then_some(end)
587 };
588 let fixed_end = |at: u64, n: u64| -> Option<u64> {
589 let end = at.checked_add(n)?;
590 (end <= len).then_some(end)
591 };
592 let sections: [(u8, &str, MetadataKind); 6] = [
593 (1, "icc", MetadataKind::Icc),
594 (2, "exif", MetadataKind::Exif),
595 (4, "xmp", MetadataKind::Xmp),
596 (8, "cicp", MetadataKind::Cicp),
597 (16, "clli", MetadataKind::HdrStatic),
598 (32, "mdcv", MetadataKind::HdrStatic),
599 ];
600 for (bit, name, kind) in sections {
601 if flags & bit == 0 {
602 continue;
603 }
604 let end = match bit {
605 1 | 2 | 4 => blob_end(at),
606 8 | 16 => fixed_end(at, 4),
607 _ => fixed_end(at, 40),
608 };
609 let Some(end) = end else {
610 malformed_from(&mut inv, at, &format!("truncated {name} section"))?;
611 return Ok(inv);
612 };
613 inv.push(
614 None,
615 Part::new(
616 PartKind::Chunk,
617 PartTag::Name(name.into()),
618 at..end,
619 Disposition::Metadata(kind),
620 ),
621 )
622 .map_err(cap)?;
623 at = end;
624 }
625 if flags & 64 != 0 {
626 let gm = (|| -> Option<u64> {
628 let at_us = usize::try_from(at).ok()?;
629 let gw = u64::from(read_u32(data, at_us).ok()?);
630 let gh = u64::from(read_u32(data, at_us.checked_add(4)?).ok()?);
631 let channels = u64::from(*data.get(at_us.checked_add(8)?)?);
632 let has_alt = *data.get(at_us.checked_add(9)?)? != 0;
633 let iso_at = at + 10 + if has_alt { 4 } else { 0 };
634 let iso_end = blob_end(iso_at)?;
635 fixed_end(iso_end, gw.checked_mul(gh)?.checked_mul(channels)?)
636 })();
637 let Some(end) = gm else {
638 malformed_from(&mut inv, at, "truncated gain-map section")?;
639 return Ok(inv);
640 };
641 inv.push(
642 None,
643 Part::new(
644 PartKind::Chunk,
645 PartTag::Name("gain-map".into()),
646 at..end,
647 Disposition::Metadata(MetadataKind::GainMap),
648 ),
649 )
650 .map_err(cap)?;
651 at = end;
652 }
653
654 let pixels = w.checked_mul(h).and_then(|n| n.checked_mul(bpp));
655 for i in 0..frames {
656 let (Some(dur_end), Some(px_end)) = (
657 fixed_end(at, 4),
658 pixels.and_then(|n| fixed_end(at.saturating_add(4), n)),
659 ) else {
660 malformed_from(&mut inv, at, &format!("truncated frame {i}"))?;
661 return Ok(inv);
662 };
663 inv.push(
664 None,
665 Part::new(
666 PartKind::Chunk,
667 PartTag::Code(i as u32),
668 at..dur_end,
669 Disposition::Metadata(MetadataKind::Animation),
670 )
671 .with_label("duration"),
672 )
673 .map_err(cap)?;
674 inv.push(
675 None,
676 Part::new(
677 PartKind::Chunk,
678 PartTag::Code(i as u32),
679 dur_end..px_end,
680 Disposition::ImageData,
681 )
682 .with_label("pixels"),
683 )
684 .map_err(cap)?;
685 at = px_end;
686 }
687 inv.fill_gaps(None, Disposition::Trailing).map_err(cap)?;
688 Ok(inv)
689}
690
691pub(crate) fn build_info(h: &Header) -> ImageInfo {
692 let mut info =
693 ImageInfo::new(h.width, h.height, ImageFormat::Pnm).with_orientation(h.meta.orientation);
694 if let Some(icc) = &h.meta.icc_profile {
695 info = info.with_icc_profile(icc.to_vec());
696 }
697 if let Some(exif) = &h.meta.exif {
698 info = info.with_exif(exif.to_vec());
699 }
700 if let Some(xmp) = &h.meta.xmp {
701 info = info.with_xmp(xmp.to_vec());
702 }
703 if let Some(c) = h.meta.cicp {
704 info = info.with_cicp(c);
705 }
706 if let Some(cll) = h.meta.content_light_level {
707 info = info.with_content_light_level(cll);
708 }
709 if let Some(md) = h.meta.mastering_display {
710 info = info.with_mastering_display(md);
711 }
712 info = match &h.gain_map {
714 Some(gm) => {
715 let mut supplements = info.supplements.clone();
716 supplements.gain_map = true;
717 info.with_supplements(supplements)
718 .with_gain_map(GainMapPresence::Available(Box::new(gm.info.clone())))
719 }
720 None => info.with_gain_map(GainMapPresence::Absent),
721 };
722 if h.frame_count > 1 {
723 info = info.with_sequence(ImageSequence::Animation {
724 frame_count: Some(h.frame_count),
725 loop_count: None,
726 random_access: false,
727 });
728 }
729 info
730}
731
732pub(crate) fn class_gated_context(
755 source: &SourceColor,
756 model: ColorModel,
757) -> Option<Arc<ColorContext>> {
758 let mut ctx = source.to_color_context();
759 if let Some(icc) = ctx
760 .icc
761 .take_if(|icc| zenpixels::icc::profile_color_space(icc) != Some(model))
762 {
763 ctx.cicp = zenpixels::icc::extract_cicp(&icc)
764 .or_else(|| zenpixels::icc::identify_common(&icc).and_then(|id| id.to_cicp()))
765 .or(ctx.cicp)
766 .or(source.cicp);
767 }
768 if ctx.icc.is_none() && ctx.cicp.is_none() {
769 return None;
770 }
771 Some(Arc::new(ctx))
772}
773
774pub(crate) fn encode_single(
775 pixels: PixelSlice<'_>,
776 meta: &Metadata,
777 gain_map: Option<&GainMapWire>,
778) -> Vec<u8> {
779 let bpp = pixels.descriptor().bytes_per_pixel() as u8;
780 let mut out = Vec::new();
781 write_header(
782 &mut out,
783 pixels.width(),
784 pixels.rows(),
785 1,
786 bpp,
787 meta,
788 gain_map,
789 );
790 push_u32(&mut out, 0); for y in 0..pixels.rows() {
792 out.extend_from_slice(pixels.row(y));
793 }
794 out
795}
796
797static ENCODE_CAPS: EncodeCapabilities = EncodeCapabilities::new()
807 .with_animation_timing(true)
808 .with_lossless(true)
809 .with_native_alpha(true)
810 .with_native_16bit(true)
811 .with_hdr(true) .with_gain_map(true) .with_animation(true)
814 .with_push_rows(true)
815 .with_streaming_output(true) .with_encode_from(false) .with_icc(true)
818 .with_exif(true)
819 .with_xmp(true)
820 .with_cicp(true);
821
822static DECODE_CAPS: DecodeCapabilities = DecodeCapabilities::new()
823 .with_icc(true)
824 .with_exif(true)
825 .with_xmp(true)
826 .with_cicp(true)
827 .with_cheap_probe(true)
828 .with_animation(true)
829 .with_streaming(true)
830 .with_native_alpha(true)
831 .with_native_16bit(true)
832 .with_hdr(true)
833 .with_gain_map(true)
837 .with_inventory(true)
838 .with_incremental(true)
839 .with_seekable(true);
840
841static DESCRIPTORS: [PixelDescriptor; 6] = [
844 PixelDescriptor::RGB8_SRGB,
845 PixelDescriptor::RGBA8_SRGB,
846 PixelDescriptor::RGB16_SRGB,
847 PixelDescriptor::RGBA16_SRGB,
848 PixelDescriptor::RGB16_BT2100_PQ,
849 PixelDescriptor::RGB16_BT2100_HLG,
850];
851
852#[derive(Clone, Debug, Default)]
858pub struct ReferenceEncoderConfig {}
859
860impl ReferenceEncoderConfig {
861 pub fn new() -> Self {
863 Self::default()
864 }
865}
866
867impl EncoderConfig for ReferenceEncoderConfig {
868 type Error = RefError;
869 type Job = RefEncodeJob;
870
871 fn format() -> ImageFormat {
872 ImageFormat::Pnm
873 }
874 fn supported_descriptors() -> &'static [PixelDescriptor] {
875 &DESCRIPTORS
876 }
877 fn capabilities() -> &'static EncodeCapabilities {
878 &ENCODE_CAPS
879 }
880 fn with_lossless(self, _lossless: bool) -> Self {
889 self
890 }
891 fn is_lossless(&self) -> Option<bool> {
892 Some(true)
893 }
894 fn job(self) -> RefEncodeJob {
895 RefEncodeJob {
896 metadata: Metadata::none(),
897 policy: EncodePolicy::none(),
898 loop_count: None,
899 gain_map: None,
900 }
901 }
902}
903
904pub struct RefEncodeJob {
906 metadata: Metadata,
907 policy: EncodePolicy,
908 loop_count: Option<u32>,
909 gain_map: Option<GainMapWire>,
910}
911
912fn apply_embed_policy(mut metadata: Metadata, policy: EncodePolicy) -> Metadata {
913 if policy.embed_icc == Some(false) {
914 metadata.icc_profile = None;
915 }
916 if policy.embed_exif == Some(false) {
917 metadata.exif = None;
918 }
919 if policy.embed_xmp == Some(false) {
920 metadata.xmp = None;
921 }
922 metadata
923}
924
925impl EncodeJob for RefEncodeJob {
926 type Error = RefError;
927 type Enc = RefEnc;
928 type AnimationFrameEnc = RefAnimEnc;
929
930 fn with_pulse(self, _stop: SharedPulse) -> Self {
931 self
932 }
933 fn with_limits(self, _limits: ResourceLimits) -> Self {
934 self
935 }
936 fn with_policy(mut self, policy: EncodePolicy) -> Self {
937 self.policy = policy;
938 self
939 }
940 fn try_set_policy(&mut self, policy: EncodePolicy) -> Result<(), UnsupportedOperation> {
941 let mut unsupported = policy;
942 unsupported.embed_icc = None;
943 unsupported.embed_exif = None;
944 unsupported.embed_xmp = None;
945 if unsupported != EncodePolicy::none() {
946 return Err(UnsupportedOperation::EncodePolicy);
947 }
948 self.policy = policy;
949 Ok(())
950 }
951 fn with_metadata(mut self, meta: Metadata) -> Self {
952 self.metadata = meta;
953 self
954 }
955 fn with_loop_count(mut self, count: Option<u32>) -> Self {
956 self.loop_count = count;
957 self
958 }
959
960 fn with_gain_map_pixels(mut self, gain_map: DecodedGainMap) -> Result<Self, RefError> {
963 self.gain_map = Some(GainMapWire::from_decoded(gain_map)?);
964 Ok(self)
965 }
966
967 fn with_gain_map_encoded(mut self, gain_map: GainMapSource) -> Result<Self, RefError> {
973 if gain_map.format != ImageFormat::Pnm {
974 return Err(RefError::Invalid(format!(
975 "encoded gain map is {:?}, not the reference's own format — decode it and use \
976 with_gain_map_pixels",
977 gain_map.format
978 )));
979 }
980 let h = parse_header(&gain_map.data)?;
981 if h.frame_count != 1 || h.bpp != 3 {
982 return Err(RefError::Invalid(
983 "encoded gain map must be a single-frame RGB8 reference image".into(),
984 ));
985 }
986 let start = frame_pixels_offset(&h, 0);
987 let len = frame_pixel_len(&h);
988 let pixels = gain_map
989 .data
990 .get(start..start + len)
991 .ok_or_else(|| RefError::Invalid("truncated gain map pixels".into()))?
992 .to_vec();
993 let mut info = gain_map.metadata;
994 info.width = h.width;
995 info.height = h.height;
996 info.channels = 3;
997 info.bit_depth = 8;
998 self.gain_map = Some(GainMapWire { info, pixels });
999 Ok(self)
1000 }
1001
1002 fn encoder(self) -> Result<RefEnc, RefError> {
1003 let metadata = apply_embed_policy(self.metadata, self.policy);
1004 Ok(RefEnc {
1005 metadata,
1006 gain_map: self.gain_map,
1007 accumulated: Vec::new(),
1008 out: OutputBuffer::new(),
1009 width: None,
1010 rows: 0,
1011 desc: None,
1012 declared_height: None,
1013 })
1014 }
1015
1016 fn animation_frame_encoder(self) -> Result<RefAnimEnc, RefError> {
1017 let metadata = apply_embed_policy(self.metadata, self.policy);
1018 Ok(RefAnimEnc {
1019 metadata,
1020 gain_map: self.gain_map,
1021 frames: Vec::new(),
1022 timing: zencodec::animation::TimingQuantizer::new(1000, u32::MAX.into()).unwrap(),
1023 streaming: None,
1024 })
1025 }
1026}
1027
1028pub struct RefEnc {
1037 metadata: Metadata,
1038 gain_map: Option<GainMapWire>,
1039 accumulated: Vec<u8>,
1041 out: OutputBuffer,
1043 width: Option<u32>,
1044 rows: u32,
1045 desc: Option<PixelDescriptor>,
1046 declared_height: Option<u32>,
1047}
1048
1049impl Encoder for RefEnc {
1050 type Error = RefError;
1051
1052 fn reject(op: UnsupportedOperation) -> RefError {
1053 RefError::Unsupported(op)
1054 }
1055
1056 fn preferred_strip_height(&self) -> u32 {
1057 4
1058 }
1059
1060 fn encode(self, pixels: PixelSlice<'_>) -> Result<EncodeOutput, RefError> {
1061 if self.width.is_some() {
1064 return Err(RefError::Invalid("encode after begin or push_rows".into()));
1065 }
1066 Ok(EncodeOutput::new(
1067 encode_single(pixels, &self.metadata, self.gain_map.as_ref()),
1068 ImageFormat::Pnm,
1069 ))
1070 }
1071
1072 fn begin(
1073 &mut self,
1074 width: u32,
1075 height: u32,
1076 desc: PixelDescriptor,
1077 ) -> Result<OutputTiming, RefError> {
1078 if self.declared_height.is_some() {
1079 return Err(RefError::Invalid("begin called twice".into()));
1080 }
1081 if self.width.is_some() {
1082 return Err(RefError::Invalid("begin after push_rows".into()));
1083 }
1084 self.width = Some(width);
1085 self.desc = Some(desc);
1086 self.declared_height = Some(height);
1087 let file = self.out.vec_mut();
1088 write_header(
1089 file,
1090 width,
1091 height,
1092 1,
1093 desc.bytes_per_pixel() as u8,
1094 &self.metadata,
1095 self.gain_map.as_ref(),
1096 );
1097 push_u32(file, 0); Ok(OutputTiming::streaming(0))
1100 }
1101
1102 fn push_rows(&mut self, rows: PixelSlice<'_>) -> Result<(), RefError> {
1103 if self.width.is_none() {
1104 self.width = Some(rows.width());
1105 self.desc = Some(rows.descriptor());
1106 }
1107 if Some(rows.width()) != self.width || Some(rows.descriptor()) != self.desc {
1108 return Err(RefError::Invalid(
1109 "push_rows: width or format changed".into(),
1110 ));
1111 }
1112 if let Some(h) = self.declared_height
1113 && self.rows.saturating_add(rows.rows()) > h
1114 {
1115 return Err(RefError::Invalid(format!(
1116 "push_rows: more than the {h} rows declared at begin"
1117 )));
1118 }
1119 let dest = match self.declared_height {
1120 Some(_) => self.out.vec_mut(),
1121 None => &mut self.accumulated,
1122 };
1123 for y in 0..rows.rows() {
1124 dest.extend_from_slice(rows.row(y));
1125 self.rows += 1;
1126 }
1127 Ok(())
1128 }
1129
1130 fn pending_output(&self) -> &[u8] {
1131 self.out.pending()
1132 }
1133
1134 fn consume_output(&mut self, n: usize) {
1135 self.out.consume(n);
1136 }
1137
1138 fn take_output(&mut self, into: &mut Vec<u8>) {
1139 self.out.take_into(into);
1140 }
1141
1142 fn finish(self) -> Result<EncodeOutput, RefError> {
1143 if let Some(h) = self.declared_height {
1144 if self.rows != h {
1145 return Err(RefError::Invalid(format!(
1146 "finish: {} rows pushed, {h} declared at begin",
1147 self.rows
1148 )));
1149 }
1150 return Ok(EncodeOutput::new(
1151 self.out.into_remaining(),
1152 ImageFormat::Pnm,
1153 ));
1154 }
1155 let w = self
1156 .width
1157 .ok_or_else(|| RefError::Invalid("finish before push_rows".into()))?;
1158 let desc = self.desc.unwrap();
1159 let buf = PixelBuffer::from_vec(self.accumulated, w, self.rows, desc)
1160 .map_err(|e| RefError::Invalid(format!("buffer: {e}")))?;
1161 Ok(EncodeOutput::new(
1162 encode_single(buf.as_slice(), &self.metadata, self.gain_map.as_ref()),
1163 ImageFormat::Pnm,
1164 ))
1165 }
1166}
1167
1168pub struct RefAnimEnc {
1175 timing: zencodec::animation::TimingQuantizer,
1176 metadata: Metadata,
1177 gain_map: Option<GainMapWire>,
1178 frames: Vec<(Vec<u8>, u32, u32, u32, PixelDescriptor)>, streaming: Option<RefAnimStream>,
1180}
1181
1182struct RefAnimStream {
1184 width: u32,
1185 height: u32,
1186 desc: PixelDescriptor,
1187 frame_count: u32,
1188 pushed: u32,
1189 out: OutputBuffer,
1190}
1191
1192impl AnimationFrameEncoder for RefAnimEnc {
1193 type Error = RefError;
1194
1195 fn reject(op: UnsupportedOperation) -> RefError {
1196 RefError::Unsupported(op)
1197 }
1198
1199 fn push_frame(
1200 &mut self,
1201 pixels: PixelSlice<'_>,
1202 duration_ms: u32,
1203 stop: Option<&dyn zencodec::Pulse>,
1204 ) -> Result<(), RefError> {
1205 if let Some(s) = stop {
1206 s.check()?;
1207 }
1208 let (w, h, desc) = (pixels.width(), pixels.rows(), pixels.descriptor());
1209 if let Some(st) = &mut self.streaming {
1210 if (w, h, desc) != (st.width, st.height, st.desc) {
1211 return Err(RefError::Invalid(
1212 "push_frame: frame differs from the canvas declared at begin".into(),
1213 ));
1214 }
1215 if st.pushed == st.frame_count {
1216 return Err(RefError::Invalid(format!(
1217 "push_frame: more than the {} frames declared at begin",
1218 st.frame_count
1219 )));
1220 }
1221 let file = st.out.vec_mut();
1222 push_u32(file, duration_ms);
1223 for y in 0..h {
1224 file.extend_from_slice(pixels.row(y));
1225 }
1226 st.pushed += 1;
1227 return Ok(());
1228 }
1229 let mut bytes = Vec::with_capacity(w as usize * h as usize * desc.bytes_per_pixel());
1230 for y in 0..h {
1231 bytes.extend_from_slice(pixels.row(y));
1232 }
1233 self.frames.push((bytes, w, h, duration_ms, desc));
1234 Ok(())
1235 }
1236
1237 fn begin(
1238 &mut self,
1239 width: u32,
1240 height: u32,
1241 desc: PixelDescriptor,
1242 frame_count: Option<u32>,
1243 ) -> Result<AnimationOutputTiming, RefError> {
1244 if self.streaming.is_some() {
1245 return Err(RefError::Invalid("begin called twice".into()));
1246 }
1247 if !self.frames.is_empty() {
1248 return Err(RefError::Invalid("begin after push_frame".into()));
1249 }
1250 let Some(frame_count) = frame_count else {
1253 return Ok(AnimationOutputTiming::AT_FINISH);
1254 };
1255 if frame_count == 0 {
1256 return Err(RefError::Invalid("no frames".into()));
1257 }
1258 let mut out = OutputBuffer::new();
1259 write_header(
1260 out.vec_mut(),
1261 width,
1262 height,
1263 frame_count,
1264 desc.bytes_per_pixel() as u8,
1265 &self.metadata,
1266 self.gain_map.as_ref(),
1267 );
1268 self.streaming = Some(RefAnimStream {
1269 width,
1270 height,
1271 desc,
1272 frame_count,
1273 pushed: 0,
1274 out,
1275 });
1276 Ok(AnimationOutputTiming::streaming(0))
1277 }
1278
1279 fn pending_output(&self) -> &[u8] {
1280 self.streaming.as_ref().map_or(&[], |st| st.out.pending())
1281 }
1282
1283 fn consume_output(&mut self, n: usize) {
1284 if let Some(st) = &mut self.streaming {
1285 st.out.consume(n);
1286 }
1287 }
1288
1289 fn take_output(&mut self, into: &mut Vec<u8>) {
1290 if let Some(st) = &mut self.streaming {
1291 st.out.take_into(into);
1292 }
1293 }
1294
1295 fn push_frame_exact(
1296 &mut self,
1297 pixels: PixelSlice<'_>,
1298 duration: zencodec::animation::FrameDuration,
1299 stop: Option<&dyn zencodec::Pulse>,
1300 ) -> Result<(), RefError> {
1301 let ticks = duration
1302 .ticks_at(1000)
1303 .map_err(|e| RefError::Invalid(e.to_string()))?;
1304 let ticks =
1305 u32::try_from(ticks).map_err(|_| RefError::Invalid("duration overflow".into()))?;
1306 self.push_frame(pixels, ticks, stop)
1307 }
1308
1309 fn push_frame_timed(
1310 &mut self,
1311 pixels: PixelSlice<'_>,
1312 duration: zencodec::animation::FrameDuration,
1313 stop: Option<&dyn zencodec::Pulse>,
1314 ) -> Result<zencodec::animation::FrameDuration, RefError> {
1315 let mut timing = self.timing;
1316 let encoded = timing
1317 .quantize(duration)
1318 .map_err(|e| RefError::Invalid(e.to_string()))?;
1319 self.push_frame_exact(pixels, encoded, stop)?;
1320 self.timing = timing;
1321 Ok(encoded)
1322 }
1323
1324 fn finish(self, stop: Option<&dyn zencodec::Pulse>) -> Result<EncodeOutput, RefError> {
1325 if let Some(s) = stop {
1326 s.check()?;
1327 }
1328 if let Some(st) = self.streaming {
1329 if st.pushed != st.frame_count {
1330 return Err(RefError::Invalid(format!(
1331 "finish: {} frames pushed, {} declared at begin",
1332 st.pushed, st.frame_count
1333 )));
1334 }
1335 return Ok(EncodeOutput::new(st.out.into_remaining(), ImageFormat::Pnm));
1336 }
1337 let (first_bytes, w, h, _, desc) = self
1338 .frames
1339 .first()
1340 .ok_or_else(|| RefError::Invalid("no frames".into()))?;
1341 let bpp = desc.bytes_per_pixel() as u8;
1342 let mut out = Vec::new();
1343 write_header(
1344 &mut out,
1345 *w,
1346 *h,
1347 self.frames.len() as u32,
1348 bpp,
1349 &self.metadata,
1350 self.gain_map.as_ref(),
1351 );
1352 push_u32(&mut out, self.frames[0].3);
1353 out.extend_from_slice(first_bytes);
1354 for (bytes, _, _, dur, _) in &self.frames[1..] {
1355 push_u32(&mut out, *dur);
1356 out.extend_from_slice(bytes);
1357 }
1358 Ok(EncodeOutput::new(out, ImageFormat::Pnm))
1359 }
1360}
1361
1362#[derive(Clone, Debug, Default)]
1368pub struct ReferenceDecoderConfig;
1369
1370impl DecoderConfig for ReferenceDecoderConfig {
1371 type Error = RefError;
1372 type Job<'a> = RefDecodeJob;
1373
1374 fn formats() -> &'static [ImageFormat] {
1375 &[ImageFormat::Pnm]
1376 }
1377 fn supported_descriptors() -> &'static [PixelDescriptor] {
1378 &DESCRIPTORS
1379 }
1380 fn capabilities() -> &'static DecodeCapabilities {
1381 &DECODE_CAPS
1382 }
1383 fn job<'a>(self) -> Self::Job<'a> {
1384 RefDecodeJob {
1385 limits: ResourceLimits::none(),
1386 render: GainMapRender::BaseOnly,
1387 format: ImageFormat::Pnm,
1388 }
1389 }
1390}
1391
1392fn detect_zcr(data: &[u8]) -> bool {
1393 data.starts_with(MAGIC)
1394}
1395
1396pub static ZCR_FORMAT: ImageFormatDefinition = ImageFormatDefinition::new(
1406 "zcr",
1407 None,
1408 "ZCR (reference codec wire format)",
1409 "zcr",
1410 &["zcr"],
1411 "image/x-zcr",
1412 &["image/x-zcr"],
1413 true, true, true, false, MAGIC.len(),
1418 detect_zcr,
1419);
1420
1421static ZCR_FORMATS: &[ImageFormat] = &[ImageFormat::Custom(&ZCR_FORMAT)];
1422
1423#[derive(Clone, Debug, Default)]
1431pub struct ReferenceZcrDecoderConfig;
1432
1433impl DecoderConfig for ReferenceZcrDecoderConfig {
1434 type Error = RefError;
1435 type Job<'a> = RefDecodeJob;
1436
1437 fn formats() -> &'static [ImageFormat] {
1438 ZCR_FORMATS
1439 }
1440 fn supported_descriptors() -> &'static [PixelDescriptor] {
1441 ReferenceDecoderConfig::supported_descriptors()
1442 }
1443 fn capabilities() -> &'static DecodeCapabilities {
1444 ReferenceDecoderConfig::capabilities()
1445 }
1446 fn job<'a>(self) -> Self::Job<'a> {
1447 RefDecodeJob {
1448 limits: ResourceLimits::none(),
1449 render: GainMapRender::BaseOnly,
1450 format: ZCR_FORMATS[0],
1451 }
1452 }
1453}
1454
1455pub struct RefDecodeJob {
1457 limits: ResourceLimits,
1458 render: GainMapRender,
1459 format: ImageFormat,
1461}
1462
1463impl<'a> DecodeJob<'a> for RefDecodeJob {
1464 type Error = RefError;
1465 type Dec = RefDec<'a>;
1466 type StreamDec = RefStreamDec<'a>;
1467 type AnimationFrameDec = RefAnimDec;
1468
1469 fn with_pulse(self, _stop: SharedPulse) -> Self {
1470 self
1471 }
1472 fn with_limits(mut self, limits: ResourceLimits) -> Self {
1473 self.limits = limits;
1474 self
1475 }
1476 fn with_extract_gain_map(mut self, extract: bool) -> Self {
1478 self.render = if extract {
1479 GainMapRender::Components
1480 } else {
1481 GainMapRender::BaseOnly
1482 };
1483 self
1484 }
1485 fn with_gain_map_render(mut self, render: GainMapRender) -> Self {
1486 self.render = render;
1487 self
1488 }
1489
1490 fn probe(&self, data: &[u8]) -> Result<ImageInfo, RefError> {
1491 Ok(build_info(&parse_header(data)?))
1492 }
1493
1494 fn inventory(&self, data: &[u8]) -> Result<zencodec::inventory::Inventory, At<CodecError>> {
1495 self.limits
1496 .check_input_size(data.len() as u64)
1497 .map_err(RefError::from)?;
1498 zcr_inventory(data, self.format).map_err(Into::into)
1499 }
1500
1501 fn output_info(&self, data: &[u8]) -> Result<OutputInfo, RefError> {
1502 let h = parse_header(data)?;
1503 Ok(OutputInfo::full_decode(
1504 h.width,
1505 h.height,
1506 descriptor_for_header(&h)?,
1507 ))
1508 }
1509
1510 fn decoder(
1511 self,
1512 data: Cow<'a, [u8]>,
1513 _preferred: &[PixelDescriptor],
1514 ) -> Result<RefDec<'a>, RefError> {
1515 parse_header(&data)?; Ok(RefDec {
1517 data,
1518 render: self.render,
1519 })
1520 }
1521
1522 fn push_decoder(
1523 self,
1524 data: Cow<'a, [u8]>,
1525 sink: &mut dyn DecodeRowSink,
1526 preferred: &[PixelDescriptor],
1527 ) -> Result<OutputInfo, RefError> {
1528 zencodec::helpers::copy_decode_to_sink(self, data, sink, preferred, RefError::Sink)
1529 }
1530
1531 fn streaming_decoder(
1532 self,
1533 data: Cow<'a, [u8]>,
1534 _preferred: &[PixelDescriptor],
1535 ) -> Result<RefStreamDec<'a>, RefError> {
1536 let h = parse_header(&data)?;
1537 let info = build_info(&h);
1538 let desc = descriptor_for_header(&h)?;
1539 let ctx = class_gated_context(&info.source_color, desc.color_model());
1540 Ok(RefStreamDec {
1541 data,
1542 info,
1543 width: h.width,
1544 height: h.height,
1545 bpp: h.bpp,
1546 desc,
1547 pixels_offset: frame_pixels_offset(&h, 0),
1548 next_row: 0,
1549 ctx,
1550 })
1551 }
1552
1553 fn dyn_incremental_decoder(
1554 self,
1555 _preferred: &[PixelDescriptor],
1556 ) -> Result<Box<zencodec::decode::DynIncrementalDecoder>, zencodec::decode::BoxedError> {
1557 Ok(zencodec::decode::erase_incremental(RefIncDec::new(
1558 self.limits,
1559 )))
1560 }
1561
1562 fn push_decoder_from_source(
1567 self,
1568 source: &mut dyn DynReadAt,
1569 sink: &mut dyn DecodeRowSink,
1570 _preferred: &[PixelDescriptor],
1571 ) -> Result<OutputInfo, zencodec::decode::BoxedError> {
1572 let mut want = 64usize;
1575 let h = loop {
1576 let mut prefix = vec![0u8; want];
1577 let n = source.read_at(0, &mut prefix)?;
1578 prefix.truncate(n);
1579 self.limits
1580 .check_input_size(n as u64)
1581 .map_err(RefError::from)?;
1582 match parse_header(&prefix) {
1583 Ok(h) => break h,
1584 Err(e) if n < want => return Err(Box::new(e)),
1585 Err(_) => want *= 4,
1586 }
1587 };
1588 let info = build_info(&h);
1589 let desc = descriptor_for_header(&h)?;
1590 let _ = info;
1591 let row_bytes = h.width as usize * h.bpp as usize;
1592 let base = frame_pixels_offset(&h, 0) as u64;
1593 sink.begin(h.width, h.height, desc)
1594 .map_err(RefError::Sink)?;
1595 let mut y = 0u32;
1596 while y < h.height {
1597 let mut strips: Vec<(u32, u32, Vec<u8>)> = Vec::new();
1599 let mut yy = y;
1600 while yy < h.height && strips.len() < 2 {
1601 let rows = (h.height - yy).min(4);
1602 strips.push((yy, rows, vec![0u8; rows as usize * row_bytes]));
1603 yy += rows;
1604 }
1605 {
1606 let mut reqs: Vec<(u64, &mut [u8])> = strips
1607 .iter_mut()
1608 .map(|(sy, _, buf)| (base + *sy as u64 * row_bytes as u64, &mut buf[..]))
1609 .collect();
1610 source.read_many_at(&mut reqs)?;
1611 }
1612 for (sy, rows, buf) in &strips {
1613 let mut dst = sink
1614 .provide_next_buffer(*sy, *rows, h.width, desc)
1615 .map_err(RefError::Sink)?;
1616 for r in 0..*rows {
1617 let start = r as usize * row_bytes;
1618 dst.row_mut(r)
1619 .copy_from_slice(&buf[start..start + row_bytes]);
1620 }
1621 }
1622 y = yy;
1623 }
1624 sink.finish().map_err(RefError::Sink)?;
1625 Ok(OutputInfo::full_decode(h.width, h.height, desc))
1626 }
1627
1628 fn animation_frame_decoder(
1629 self,
1630 data: Cow<'a, [u8]>,
1631 _preferred: &[PixelDescriptor],
1632 ) -> Result<RefAnimDec, RefError> {
1633 let h = parse_header(&data)?;
1634 let info = build_info(&h);
1635 let desc = descriptor_for_header(&h)?;
1636 let ctx = class_gated_context(&info.source_color, desc.color_model());
1637 Ok(RefAnimDec {
1638 data: data.into_owned(),
1639 info,
1640 width: h.width,
1641 height: h.height,
1642 frame_count: h.frame_count,
1643 bpp: h.bpp,
1644 desc,
1645 frames_offset: h.frames_offset,
1646 next_frame: 0,
1647 ctx,
1648 })
1649 }
1650}
1651
1652#[derive(Debug)]
1654pub struct RefDec<'a> {
1655 data: Cow<'a, [u8]>,
1656 render: GainMapRender,
1657}
1658
1659impl Decode for RefDec<'_> {
1660 type Error = RefError;
1661
1662 fn decode(self) -> Result<DecodeOutput, RefError> {
1663 let h = parse_header(&self.data)?;
1664 let desc = descriptor_for_header(&h)?;
1665 let start = frame_pixels_offset(&h, 0);
1666 let len = frame_pixel_len(&h);
1667 let pixels = self
1668 .data
1669 .get(start..start + len)
1670 .ok_or_else(|| RefError::Invalid("truncated pixels".into()))?;
1671 let mut buf = PixelBuffer::from_vec(pixels.to_vec(), h.width, h.height, desc)
1672 .map_err(|e| RefError::Invalid(format!("buffer: {e}")))?;
1673 let info = build_info(&h);
1674 if let Some(ctx) = class_gated_context(&info.source_color, desc.color_model()) {
1676 buf = buf.with_color_context(ctx);
1677 }
1678 let mut out = DecodeOutput::new(buf, info);
1679 let surface = matches!(
1684 self.render,
1685 GainMapRender::Components | GainMapRender::ReconstructHdr { .. }
1686 );
1687 if surface && let Some(gm) = &h.gain_map {
1688 out = out.with_extras(gm.to_decoded()?);
1689 }
1690 Ok(out)
1691 }
1692}
1693
1694pub struct RefStreamDec<'a> {
1696 data: Cow<'a, [u8]>,
1697 info: ImageInfo,
1698 width: u32,
1699 height: u32,
1700 bpp: u8,
1701 desc: PixelDescriptor,
1702 pixels_offset: usize,
1703 next_row: u32,
1704 ctx: Option<Arc<ColorContext>>,
1707}
1708
1709impl StreamingDecode for RefStreamDec<'_> {
1710 type Error = RefError;
1711
1712 fn next_batch(&mut self) -> Result<Option<(u32, PixelSlice<'_>)>, RefError> {
1713 if self.next_row >= self.height {
1714 return Ok(None);
1715 }
1716 let strip = (self.height - self.next_row).min(4);
1717 let row_bytes = self.width as usize * self.bpp as usize;
1718 let off = self.pixels_offset + self.next_row as usize * row_bytes;
1719 let span = strip as usize * row_bytes;
1720 let bytes = self
1721 .data
1722 .get(off..off + span)
1723 .ok_or_else(|| RefError::Invalid("truncated strip".into()))?;
1724 let mut ps = PixelSlice::new(bytes, self.width, strip, row_bytes, self.desc)
1725 .map_err(|e| RefError::Invalid(format!("slice: {e}")))?;
1726 if let Some(ctx) = &self.ctx {
1727 ps = ps.with_color_context(ctx.clone());
1728 }
1729 let y = self.next_row;
1730 self.next_row += strip;
1731 Ok(Some((y, ps)))
1732 }
1733
1734 fn info(&self) -> &ImageInfo {
1735 &self.info
1736 }
1737}
1738
1739pub struct RefIncDec {
1751 limits: ResourceLimits,
1752 header: Option<(Header, PixelDescriptor, ImageInfo)>,
1753 header_want: usize,
1755 next_row: u32,
1756 request: [InputRequest; 1],
1757}
1758
1759impl RefIncDec {
1760 fn new(limits: ResourceLimits) -> Self {
1761 Self {
1762 limits,
1763 header: None,
1764 header_want: 64,
1765 next_row: 0,
1766 request: [InputRequest::at_least(0, 64)],
1767 }
1768 }
1769
1770 fn need(&mut self, offset: u64, min_len: usize) -> Progress {
1771 self.request = [InputRequest::at_least(offset, min_len)];
1772 Progress::need(0)
1773 }
1774}
1775
1776impl IncrementalDecode for RefIncDec {
1777 type Error = RefError;
1778
1779 fn step(
1780 &mut self,
1781 input: Input<'_>,
1782 sink: &mut dyn DecodeRowSink,
1783 _stop: Option<&dyn zencodec::Pulse>,
1784 ) -> Result<Progress, StepError<RefError>> {
1785 if let Some(total) = input.total_len {
1786 self.limits
1787 .check_input_size(total)
1788 .map_err(|e| StepError::Codec(RefError::from(e)))?;
1789 }
1790 let Some((h, desc, _)) = &self.header else {
1791 if input.offset != 0 {
1792 return Ok(self.need(0, self.header_want));
1793 }
1794 if input.bytes.len() >= MAGIC.len() && !input.bytes.starts_with(MAGIC) {
1795 return Err(StepError::Codec(RefError::Invalid("bad magic".into())));
1796 }
1797 return match parse_header(input.bytes) {
1798 Ok(h) => {
1799 let desc = descriptor_for_header(&h).map_err(StepError::Codec)?;
1800 let info = build_info(&h);
1801 sink.begin(h.width, h.height, desc)
1802 .map_err(StepError::Sink)?;
1803 let consumed = (frame_pixels_offset(&h, 0)).min(input.bytes.len());
1806 self.header = Some((h, desc, info));
1807 Ok(Progress::new(
1808 consumed,
1809 Event::Metadata(MetadataUpdate::HeaderReady),
1810 ))
1811 }
1812 Err(e) if input.reaches_end() => Err(StepError::Codec(e)),
1813 Err(_) => {
1814 if input.bytes.len() >= self.header_want {
1815 self.header_want *= 4;
1816 }
1817 Ok(self.need(0, self.header_want))
1818 }
1819 };
1820 };
1821 let (width, height, bpp, desc) = (h.width, h.height, h.bpp, *desc);
1822 let row_bytes = width as usize * bpp as usize;
1823 let pixels_start = frame_pixels_offset(h, 0) as u64;
1824 if self.next_row >= height {
1825 sink.finish().map_err(StepError::Sink)?;
1826 return Ok(Progress::new(
1827 0,
1828 Event::Done(OutputInfo::full_decode(width, height, desc)),
1829 ));
1830 }
1831 let rows = (height - self.next_row).min(4);
1832 let strip_start = pixels_start + self.next_row as u64 * row_bytes as u64;
1833 let strip_len = rows as usize * row_bytes;
1834 if input.offset != strip_start || input.bytes.len() < strip_len {
1835 if input.offset == strip_start && input.reaches_end() {
1836 return Err(StepError::Codec(RefError::Invalid(
1837 "truncated strip".into(),
1838 )));
1839 }
1840 return Ok(self.need(strip_start, strip_len));
1841 }
1842 {
1843 let mut dst = sink
1844 .provide_next_buffer(self.next_row, rows, width, desc)
1845 .map_err(StepError::Sink)?;
1846 for r in 0..rows {
1847 let s = r as usize * row_bytes;
1848 dst.row_mut(r)
1849 .copy_from_slice(&input.bytes[s..s + row_bytes]);
1850 }
1851 }
1852 let meta = BatchMeta::final_rows(self.next_row);
1853 self.next_row += rows;
1854 Ok(Progress::new(strip_len, Event::Rows { meta, rows }))
1855 }
1856
1857 fn requested(&self) -> &[InputRequest] {
1858 &self.request
1859 }
1860
1861 fn info(&self) -> Option<&ImageInfo> {
1862 self.header.as_ref().map(|(_, _, info)| info)
1863 }
1864
1865 fn output_descriptor(&self) -> Option<PixelDescriptor> {
1866 self.header.as_ref().map(|(_, desc, _)| *desc)
1867 }
1868
1869 fn access_pattern(&self) -> AccessPattern {
1870 AccessPattern::Sequential
1871 }
1872}
1873
1874pub struct RefAnimDec {
1876 data: Vec<u8>,
1877 info: ImageInfo,
1878 width: u32,
1879 height: u32,
1880 frame_count: u32,
1881 bpp: u8,
1882 desc: PixelDescriptor,
1883 frames_offset: usize,
1884 next_frame: u32,
1885 ctx: Option<Arc<ColorContext>>,
1888}
1889
1890impl AnimationFrameDecoder for RefAnimDec {
1891 type Error = RefError;
1892
1893 fn wrap_sink_error(err: SinkError) -> RefError {
1894 RefError::Sink(err)
1895 }
1896
1897 fn info(&self) -> &ImageInfo {
1898 &self.info
1899 }
1900 fn frame_count(&self) -> Option<u32> {
1901 Some(self.frame_count)
1902 }
1903 fn loop_count(&self) -> Option<u32> {
1904 Some(0)
1905 }
1906
1907 fn render_next_frame(
1908 &mut self,
1909 stop: Option<&dyn zencodec::Pulse>,
1910 ) -> Result<Option<AnimationFrame<'_>>, RefError> {
1911 if let Some(s) = stop {
1912 s.check()?;
1913 }
1914 if self.next_frame >= self.frame_count {
1915 return Ok(None);
1916 }
1917 let row_bytes = self.width as usize * self.bpp as usize;
1918 let frame_bytes = 4 + self.height as usize * row_bytes;
1919 let base = self.frames_offset + self.next_frame as usize * frame_bytes;
1920 let dur = read_u32(&self.data, base)?;
1921 let start = base + 4;
1922 let span = self.height as usize * row_bytes;
1923 let bytes = self
1924 .data
1925 .get(start..start + span)
1926 .ok_or_else(|| RefError::Invalid("truncated frame".into()))?;
1927 let mut ps = PixelSlice::new(bytes, self.width, self.height, row_bytes, self.desc)
1928 .map_err(|e| RefError::Invalid(format!("frame slice: {e}")))?;
1929 if let Some(ctx) = &self.ctx {
1930 ps = ps.with_color_context(ctx.clone());
1931 }
1932 let frame = AnimationFrame::new(ps, dur, self.next_frame);
1933 self.next_frame += 1;
1934 Ok(Some(frame))
1935 }
1936
1937 fn render_next_frame_to_sink(
1938 &mut self,
1939 stop: Option<&dyn zencodec::Pulse>,
1940 sink: &mut dyn DecodeRowSink,
1941 ) -> Result<Option<OutputInfo>, RefError> {
1942 zencodec::helpers::copy_frame_to_sink(self, stop, sink)
1943 }
1944}