1use std::sync::{Arc, Mutex};
3
4use jxl_bitstream::Bitstream;
5use jxl_color::{
6 ColorEncodingWithProfile, ColorManagementSystem, ColorTransform, ColourEncoding, ColourSpace,
7 EnumColourEncoding, RenderingIntent, TransferFunction,
8};
9use jxl_frame::{Frame, FrameContext, header::FrameType};
10use jxl_grid::AllocTracker;
11use jxl_image::{ImageHeader, ImageMetadata};
12use jxl_modular::Sample;
13use jxl_oxide_common::Bundle;
14use jxl_threadpool::JxlThreadPool;
15
16mod blend;
17mod error;
18mod features;
19mod filter;
20mod image;
21mod modular;
22mod region;
23mod render;
24mod state;
25mod util;
26mod vardct;
27
28pub use error::{Error, Result};
29pub use features::render_spot_color;
30pub use image::{ImageBuffer, ImageWithRegion};
31pub use region::Region;
32use state::*;
33
34pub struct RenderContext {
36 image_header: Arc<ImageHeader>,
37 pool: JxlThreadPool,
38 tracker: Option<AllocTracker>,
39 pub(crate) frames: Vec<Arc<IndexedFrame>>,
40 pub(crate) renders_wide: Vec<Arc<FrameRenderHandle<i32>>>,
41 pub(crate) renders_narrow: Vec<Arc<FrameRenderHandle<i16>>>,
42 pub(crate) keyframes: Vec<usize>,
43 pub(crate) keyframe_in_progress: Option<usize>,
44 pub(crate) refcounts: Vec<usize>,
45 pub(crate) frame_deps: Vec<FrameDependence>,
46 pub(crate) lf_frame: [usize; 4],
47 pub(crate) reference: [usize; 4],
48 pub(crate) loading_frame: Option<IndexedFrame>,
49 pub(crate) loading_render_cache_wide: Option<RenderCache<i32>>,
50 pub(crate) loading_render_cache_narrow: Option<RenderCache<i16>>,
51 pub(crate) loading_region: Option<Region>,
52 requested_image_region: Region,
53 embedded_icc: Vec<u8>,
54 requested_color_encoding: ColorEncodingWithProfile,
55 cms: Box<dyn ColorManagementSystem + Send + Sync>,
56 cached_transform: Mutex<Option<ColorTransform>>,
57 force_wide_buffers: bool,
58}
59
60impl std::fmt::Debug for RenderContext {
61 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 f.debug_struct("RenderContext").finish_non_exhaustive()
63 }
64}
65
66impl RenderContext {
67 pub fn builder() -> RenderContextBuilder {
68 RenderContextBuilder::default()
69 }
70}
71
72#[derive(Debug, Default)]
73pub struct RenderContextBuilder {
74 embedded_icc: Vec<u8>,
75 pool: Option<JxlThreadPool>,
76 tracker: Option<AllocTracker>,
77 force_wide_buffers: bool,
78}
79
80impl RenderContextBuilder {
81 pub fn embedded_icc(mut self, icc: Vec<u8>) -> Self {
82 self.embedded_icc = icc;
83 self
84 }
85
86 pub fn pool(mut self, pool: JxlThreadPool) -> Self {
87 self.pool = Some(pool);
88 self
89 }
90
91 pub fn alloc_tracker(mut self, tracker: AllocTracker) -> Self {
92 self.tracker = Some(tracker);
93 self
94 }
95
96 pub fn force_wide_buffers(mut self, force_wide_buffers: bool) -> Self {
97 self.force_wide_buffers = force_wide_buffers;
98 self
99 }
100
101 pub fn build(self, image_header: Arc<ImageHeader>) -> Result<RenderContext> {
102 let color_encoding = &image_header.metadata.colour_encoding;
103 let requested_color_encoding = match color_encoding {
104 ColourEncoding::Enum(encoding) => ColorEncodingWithProfile::new(encoding.clone()),
105 ColourEncoding::IccProfile(color_space) => {
106 let header_is_gray = *color_space == ColourSpace::Grey;
107
108 let parsed_icc = match ColorEncodingWithProfile::with_icc(&self.embedded_icc) {
109 Ok(parsed_icc) => {
110 let icc_is_gray = parsed_icc.is_grayscale();
111 if header_is_gray != icc_is_gray {
112 tracing::error!(
113 header_is_gray,
114 icc_is_gray,
115 "Color channel mismatch between header and ICC profile"
116 );
117 return Err(jxl_bitstream::Error::ValidationFailed(
118 "Color channel mismatch between header and ICC profile",
119 )
120 .into());
121 }
122
123 let is_supported_icc = parsed_icc.icc_profile().is_empty();
124 if !is_supported_icc {
125 tracing::trace!(
126 "Failed to convert embedded ICC into enum color encoding"
127 );
128 }
129
130 let allow_parsed_icc =
131 !image_header.metadata.xyb_encoded || is_supported_icc;
132 allow_parsed_icc.then_some(parsed_icc)
133 }
134 Err(e) => {
135 tracing::warn!(%e, "Malformed embedded ICC profile");
136 None
137 }
138 };
139
140 if let Some(profile) = parsed_icc {
141 profile
142 } else if header_is_gray {
143 ColorEncodingWithProfile::new(EnumColourEncoding::gray_srgb(
144 RenderingIntent::Relative,
145 ))
146 } else {
147 ColorEncodingWithProfile::new(EnumColourEncoding::srgb(
148 RenderingIntent::Relative,
149 ))
150 }
151 }
152 };
153
154 tracing::debug!(
155 default_color_encoding = ?requested_color_encoding,
156 "Setting default output color encoding"
157 );
158
159 let full_image_region = Region::with_size(
160 image_header.width_with_orientation(),
161 image_header.height_with_orientation(),
162 );
163
164 Ok(RenderContext {
165 image_header,
166 tracker: self.tracker,
167 pool: self.pool.unwrap_or_else(JxlThreadPool::none),
168 frames: Vec::new(),
169 renders_wide: Vec::new(),
170 renders_narrow: Vec::new(),
171 keyframes: Vec::new(),
172 keyframe_in_progress: None,
173 refcounts: Vec::new(),
174 frame_deps: Vec::new(),
175 lf_frame: [usize::MAX; 4],
176 reference: [usize::MAX; 4],
177 loading_frame: None,
178 loading_render_cache_wide: None,
179 loading_render_cache_narrow: None,
180 loading_region: None,
181 requested_image_region: full_image_region,
182 embedded_icc: self.embedded_icc,
183 requested_color_encoding,
184 cms: Box::new(jxl_color::NullCms),
185 cached_transform: Mutex::new(None),
186 force_wide_buffers: self.force_wide_buffers,
187 })
188 }
189}
190
191impl RenderContext {
192 #[inline]
193 pub fn alloc_tracker(&self) -> Option<&AllocTracker> {
194 self.tracker.as_ref()
195 }
196}
197
198impl RenderContext {
199 #[inline]
200 pub fn set_cms(&mut self, cms: impl ColorManagementSystem + Send + Sync + 'static) {
201 *self.cached_transform.get_mut().unwrap() = None;
202 self.cms = Box::new(cms);
203 }
204
205 pub fn suggested_hdr_tf(&self) -> Option<TransferFunction> {
206 let tf = match &self.image_header.metadata.colour_encoding {
207 ColourEncoding::Enum(e) => e.tf,
208 ColourEncoding::IccProfile(_) => {
209 let icc = self.embedded_icc().unwrap();
210 jxl_color::icc::icc_tf(icc)?
211 }
212 };
213
214 match tf {
215 TransferFunction::Pq | TransferFunction::Hlg => Some(tf),
216 _ => None,
217 }
218 }
219
220 #[inline]
221 pub fn request_color_encoding(&mut self, encoding: ColorEncodingWithProfile) {
222 *self.cached_transform.get_mut().unwrap() = None;
223 self.requested_color_encoding = encoding;
224 }
225
226 #[inline]
227 pub fn requested_color_encoding(&self) -> &ColorEncodingWithProfile {
228 &self.requested_color_encoding
229 }
230
231 #[inline]
232 pub fn request_image_region(&mut self, image_region: Region) {
233 self.requested_image_region = image_region;
234 self.reset_cache();
235 }
236
237 #[inline]
238 pub fn image_region(&self) -> Region {
239 self.requested_image_region
240 }
241}
242
243impl RenderContext {
244 #[inline]
246 pub fn width(&self) -> u32 {
247 self.image_header.size.width
248 }
249
250 #[inline]
252 pub fn height(&self) -> u32 {
253 self.image_header.size.height
254 }
255
256 #[inline]
257 pub fn embedded_icc(&self) -> Option<&[u8]> {
258 (!self.embedded_icc.is_empty()).then_some(&self.embedded_icc)
259 }
260
261 #[inline]
263 pub fn loaded_keyframes(&self) -> usize {
264 self.keyframes.len()
265 }
266
267 #[inline]
270 pub fn loaded_frames(&self) -> usize {
271 self.frames.len()
272 }
273
274 #[inline]
275 fn metadata(&self) -> &ImageMetadata {
276 &self.image_header.metadata
277 }
278
279 #[inline]
280 fn narrow_modular(&self) -> bool {
281 !self.force_wide_buffers && self.image_header.metadata.modular_16bit_buffers
282 }
283
284 fn preserve_current_frame(&mut self) {
285 let Some(frame) = self.loading_frame.take() else {
286 return;
287 };
288
289 let header = frame.header();
290 let idx = self.frames.len();
291
292 self.refcounts.push(0);
293
294 let lf = if header.flags.use_lf_frame() {
295 let lf = self.lf_frame[header.lf_level as usize];
296 self.refcounts[lf] += 1;
297 lf
298 } else {
299 usize::MAX
300 };
301 for ref_idx in self.reference {
302 if ref_idx != usize::MAX {
303 self.refcounts[ref_idx] += 1;
304 }
305 }
306
307 let deps = FrameDependence {
308 lf,
309 ref_slots: self.reference,
310 };
311
312 if header.can_reference() {
313 let ref_idx = header.save_as_reference as usize;
314 self.reference[ref_idx] = idx;
315 }
316 if header.lf_level != 0 {
317 let lf_idx = header.lf_level as usize - 1;
318 self.lf_frame[lf_idx] = idx;
319 }
320
321 if header.is_keyframe() {
322 self.refcounts[idx] += 1;
323 self.keyframes.push(idx);
324 self.keyframe_in_progress = None;
325 } else if header.frame_type.is_normal_frame() {
326 self.keyframe_in_progress = Some(idx);
327 }
328
329 let frame = Arc::new(frame);
330 let image_region = self.requested_image_region;
331
332 if self.narrow_modular() {
333 let reference_frames = ReferenceFrames {
334 lf: (deps.lf != usize::MAX).then(|| Reference {
335 frame: Arc::clone(&self.frames[deps.lf]),
336 image: Arc::clone(&self.renders_narrow[deps.lf]),
337 }),
338 refs: deps.ref_slots.map(|r| {
339 (r != usize::MAX).then(|| Reference {
340 frame: Arc::clone(&self.frames[r]),
341 image: Arc::clone(&self.renders_narrow[r]),
342 })
343 }),
344 };
345 let refs = reference_frames.refs.clone();
346
347 let render_op = self.render_op::<i16>(Arc::clone(&frame), reference_frames);
348 let handle = if let Some(cache) = self.loading_render_cache_narrow.take() {
349 FrameRenderHandle::from_cache(
350 Arc::clone(&frame),
351 image_region,
352 cache,
353 render_op,
354 refs,
355 )
356 } else {
357 FrameRenderHandle::new(Arc::clone(&frame), image_region, render_op, refs)
358 };
359 self.renders_narrow.push(Arc::new(handle));
360 } else {
361 let reference_frames = ReferenceFrames {
362 lf: (deps.lf != usize::MAX).then(|| Reference {
363 frame: Arc::clone(&self.frames[deps.lf]),
364 image: Arc::clone(&self.renders_wide[deps.lf]),
365 }),
366 refs: deps.ref_slots.map(|r| {
367 (r != usize::MAX).then(|| Reference {
368 frame: Arc::clone(&self.frames[r]),
369 image: Arc::clone(&self.renders_wide[r]),
370 })
371 }),
372 };
373 let refs = reference_frames.refs.clone();
374
375 let render_op = self.render_op::<i32>(Arc::clone(&frame), reference_frames);
376 let handle = if let Some(cache) = self.loading_render_cache_wide.take() {
377 FrameRenderHandle::from_cache(
378 Arc::clone(&frame),
379 image_region,
380 cache,
381 render_op,
382 refs,
383 )
384 } else {
385 FrameRenderHandle::new(Arc::clone(&frame), image_region, render_op, refs)
386 };
387 self.renders_wide.push(Arc::new(handle));
388 }
389
390 self.frames.push(Arc::clone(&frame));
391 self.frame_deps.push(deps);
392 }
393
394 fn loading_frame(&self) -> Option<&IndexedFrame> {
395 let search_from = self
396 .keyframe_in_progress
397 .or_else(|| self.keyframes.last().map(|x| x + 1))
398 .unwrap_or(0);
399 self.frames[search_from..]
400 .iter()
401 .map(|r| &**r)
402 .chain(self.loading_frame.as_ref())
403 .rev()
404 .find(|x| x.header().frame_type.is_progressive_frame())
405 }
406}
407
408impl RenderContext {
409 pub fn load_frame_header(&mut self, bitstream: &mut Bitstream) -> Result<&mut IndexedFrame> {
410 if self.loading_frame.is_some() && !self.try_finalize_current_frame() {
411 panic!("another frame is still loading");
412 }
413
414 let image_header = &self.image_header;
415
416 let bitstream_original = bitstream.clone();
417 let frame = match Frame::parse(
418 bitstream,
419 FrameContext {
420 image_header: image_header.clone(),
421 tracker: self.tracker.as_ref(),
422 pool: self.pool.clone(),
423 },
424 ) {
425 Ok(frame) => frame,
426 Err(e) => {
427 *bitstream = bitstream_original;
428 return Err(e.into());
429 }
430 };
431
432 let header = frame.header();
433 if header.flags.use_lf_frame() && self.lf_frame[header.lf_level as usize] == usize::MAX {
435 return Err(Error::UninitializedLfFrame(header.lf_level));
436 }
437
438 self.loading_frame = Some(IndexedFrame::new(frame, self.frames.len()));
439 Ok(self.loading_frame.as_mut().unwrap())
440 }
441
442 pub fn current_loading_frame(&mut self) -> Option<&mut IndexedFrame> {
443 self.try_finalize_current_frame();
444 self.loading_frame.as_mut()
445 }
446
447 pub fn finalize_current_frame(&mut self) {
448 if !self.try_finalize_current_frame() {
449 panic!("frame is not fully loaded");
450 }
451 }
452
453 fn try_finalize_current_frame(&mut self) -> bool {
454 if let Some(loading_frame) = &self.loading_frame {
455 if loading_frame.is_loading_done() {
456 self.preserve_current_frame();
457 return true;
458 }
459 }
460 false
461 }
462}
463
464impl RenderContext {
465 #[inline]
467 pub fn keyframe(&self, keyframe_idx: usize) -> Option<&IndexedFrame> {
468 if keyframe_idx == self.keyframes.len() {
469 self.loading_frame()
470 } else if let Some(&idx) = self.keyframes.get(keyframe_idx) {
471 Some(&self.frames[idx])
472 } else {
473 None
474 }
475 }
476
477 #[inline]
478 pub fn frame(&self, frame_idx: usize) -> Option<&IndexedFrame> {
479 if self.frames.len() == frame_idx {
480 self.loading_frame.as_ref()
481 } else {
482 self.frames.get(frame_idx).map(|x| &**x)
483 }
484 }
485}
486
487impl RenderContext {
488 fn do_render<S: Sample>(
489 frame: &IndexedFrame,
490 reference_frames: &ReferenceFrames<S>,
491 mut state: FrameRender<S>,
492 image_region: Region,
493 prev_frame_visibility: (usize, usize),
494 pool: &JxlThreadPool,
495 ) -> FrameRender<S> {
496 if let Some(lf) = &reference_frames.lf {
497 tracing::trace!(idx = lf.frame.idx, "Spawn LF frame renderer");
498 let lf_handle = Arc::clone(&lf.image);
499 pool.spawn(move || {
500 lf_handle.run(image_region);
501 });
502 }
503 for grid in reference_frames.refs.iter().flatten() {
504 tracing::trace!(idx = grid.frame.idx, "Spawn reference frame renderer");
505 let ref_handle = Arc::clone(&grid.image);
506 pool.spawn(move || {
507 ref_handle.run(image_region);
508 });
509 }
510
511 let mut cache = match state {
512 FrameRender::InProgress(cache) => cache,
513 _ => {
514 state = FrameRender::InProgress(Box::new(RenderCache::new(frame)));
515 let FrameRender::InProgress(cache) = state else {
516 unreachable!()
517 };
518 cache
519 }
520 };
521
522 let result = render::render_frame(
523 frame,
524 reference_frames.clone(),
525 &mut cache,
526 image_region,
527 pool.clone(),
528 prev_frame_visibility,
529 );
530 match result {
531 Ok(grid) => FrameRender::Done(grid),
532 Err(e) if e.unexpected_eof() || matches!(e, Error::IncompleteFrame) => {
533 if frame.is_loading_done() {
534 FrameRender::Err(e)
535 } else {
536 FrameRender::InProgress(cache)
537 }
538 }
539 Err(e) => FrameRender::Err(e),
540 }
541 }
542
543 fn render_op<S: Sample>(
544 &self,
545 frame: Arc<IndexedFrame>,
546 reference_frames: ReferenceFrames<S>,
547 ) -> RenderOp<S> {
548 let prev_frame_visibility = self.get_previous_frames_visibility(&frame);
549
550 let pool = self.pool.clone();
551 Arc::new(move |state, image_region| {
552 Self::do_render(
553 &frame,
554 &reference_frames,
555 state,
556 image_region,
557 prev_frame_visibility,
558 &pool,
559 )
560 })
561 }
562
563 fn get_previous_frames_visibility<'a>(&'a self, frame: &'a IndexedFrame) -> (usize, usize) {
564 let frame_idx = frame.index();
565 let (is_keyframe, keyframe_idx) = match self.keyframes.binary_search(&frame_idx) {
566 Ok(val) => (true, val),
567 Err(val) => (frame.header().is_keyframe(), val),
570 };
571 let prev_keyframes = &self.keyframes[..keyframe_idx];
572
573 let visible_frames_num = keyframe_idx + is_keyframe as usize;
574
575 let invisible_frames_num = if is_keyframe {
576 0
577 } else if prev_keyframes.is_empty() {
578 1 + frame_idx
579 } else {
580 let last_visible_frame = prev_keyframes[keyframe_idx - 1];
581 frame_idx - last_visible_frame
582 };
583
584 (visible_frames_num, invisible_frames_num)
585 }
586}
587
588impl RenderContext {
589 fn spawn_renderer(&self, index: usize) {
590 if !self.pool.is_multithreaded() {
591 return;
593 }
594
595 let image_region = self.requested_image_region;
596 if self.narrow_modular() {
597 let render_handle = Arc::clone(&self.renders_narrow[index]);
598 self.pool.spawn(move || {
599 render_handle.run(image_region);
600 });
601 } else {
602 let render_handle = Arc::clone(&self.renders_wide[index]);
603 self.pool.spawn(move || {
604 render_handle.run(image_region);
605 });
606 }
607 }
608
609 fn render_by_index(&self, index: usize) -> Result<Arc<ImageWithRegion>> {
610 if self.narrow_modular() {
611 Arc::clone(&self.renders_narrow[index])
612 .run_with_image()?
613 .blend(None, &self.pool)
614 } else {
615 Arc::clone(&self.renders_wide[index])
616 .run_with_image()?
617 .blend(None, &self.pool)
618 }
619 }
620
621 #[inline]
625 pub fn render(&mut self) -> Result<Arc<ImageWithRegion>> {
626 self.render_keyframe(0)
627 }
628
629 pub fn render_keyframe(&self, keyframe_idx: usize) -> Result<Arc<ImageWithRegion>> {
633 let idx = *self
634 .keyframes
635 .get(keyframe_idx)
636 .ok_or(Error::IncompleteFrame)?;
637 let grid = self.render_by_index(idx)?;
638 let frame = &*self.frames[idx];
639
640 self.postprocess_keyframe(frame, grid)
641 }
642
643 pub fn render_loading_keyframe(&mut self) -> Result<(&IndexedFrame, Arc<ImageWithRegion>)> {
644 let mut current_frame_grid = None;
645 if self.loading_frame().is_some() {
646 let ret = self.render_loading_frame();
647 match ret {
648 Ok(grid) => current_frame_grid = Some(grid),
649 Err(Error::IncompleteFrame) => {}
650 Err(e) => return Err(e),
651 }
652 }
653
654 let (frame, grid) = if let Some(grid) = current_frame_grid {
655 let frame = self.loading_frame().unwrap();
656 (frame, Arc::new(grid))
657 } else if let Some(idx) = self.keyframe_in_progress {
658 let grid = self.render_by_index(idx)?;
659 let frame = &*self.frames[idx];
660 (frame, grid)
661 } else {
662 return Err(Error::IncompleteFrame);
663 };
664
665 let grid = self.postprocess_keyframe(frame, grid)?;
666 Ok((frame, grid))
667 }
668
669 pub fn reset_cache(&mut self) {
670 let image_region = self.requested_image_region;
671
672 self.loading_region = None;
673 self.loading_render_cache_wide = None;
674 self.loading_render_cache_narrow = None;
675 for (idx, frame) in self.frames.iter().enumerate() {
676 if frame.header().frame_type == FrameType::ReferenceOnly {
677 continue;
678 }
679
680 let deps = self.frame_deps[idx];
681 if self.narrow_modular() {
682 let reference_frames = ReferenceFrames {
683 lf: (deps.lf != usize::MAX).then(|| Reference {
684 frame: Arc::clone(&self.frames[deps.lf]),
685 image: Arc::clone(&self.renders_narrow[deps.lf]),
686 }),
687 refs: deps.ref_slots.map(|r| {
688 (r != usize::MAX).then(|| Reference {
689 frame: Arc::clone(&self.frames[r]),
690 image: Arc::clone(&self.renders_narrow[r]),
691 })
692 }),
693 };
694 let refs = reference_frames.refs.clone();
695
696 let render_op = self.render_op::<i16>(Arc::clone(frame), reference_frames);
697 let handle =
698 FrameRenderHandle::new(Arc::clone(frame), image_region, render_op, refs);
699 self.renders_narrow[idx] = Arc::new(handle);
700 } else {
701 let reference_frames = ReferenceFrames {
702 lf: (deps.lf != usize::MAX).then(|| Reference {
703 frame: Arc::clone(&self.frames[deps.lf]),
704 image: Arc::clone(&self.renders_wide[deps.lf]),
705 }),
706 refs: deps.ref_slots.map(|r| {
707 (r != usize::MAX).then(|| Reference {
708 frame: Arc::clone(&self.frames[r]),
709 image: Arc::clone(&self.renders_wide[r]),
710 })
711 }),
712 };
713 let refs = reference_frames.refs.clone();
714
715 let render_op = self.render_op::<i32>(Arc::clone(frame), reference_frames);
716 let handle =
717 FrameRenderHandle::new(Arc::clone(frame), image_region, render_op, refs);
718 self.renders_wide[idx] = Arc::new(handle);
719 }
720 }
721 }
722
723 fn render_loading_frame(&mut self) -> Result<ImageWithRegion> {
724 let frame = self.loading_frame().unwrap();
725 if !frame.header().frame_type.is_progressive_frame() {
726 return Err(Error::IncompleteFrame);
727 }
728
729 let image_region = self.requested_image_region;
730 let frame_region = util::image_region_to_frame(frame, image_region, false);
731 self.loading_region = Some(frame_region);
732
733 let frame = self.loading_frame().unwrap();
734 let header = frame.header();
735 let lf_global_failed = if self.narrow_modular() {
736 frame.try_parse_lf_global::<i16>().is_none()
737 } else {
738 frame.try_parse_lf_global::<i32>().is_none()
739 };
740 if lf_global_failed {
741 return Err(Error::IncompleteFrame);
742 }
743
744 let lf_frame_idx = self.lf_frame[header.lf_level as usize];
745 if header.flags.use_lf_frame() {
746 self.spawn_renderer(lf_frame_idx);
747 }
748 for idx in self.reference {
749 if idx != usize::MAX {
750 self.spawn_renderer(idx);
751 }
752 }
753
754 tracing::debug!(?image_region, ?frame_region, "Rendering loading frame");
755 let image = if self.narrow_modular() {
756 let state = if let Some(cache) = self.loading_render_cache_narrow.take() {
757 FrameRender::InProgress(Box::new(cache))
758 } else {
759 FrameRender::None
760 };
761
762 let reference_frames = ReferenceFrames {
763 lf: (lf_frame_idx != usize::MAX).then(|| Reference {
764 frame: Arc::clone(&self.frames[lf_frame_idx]),
765 image: Arc::clone(&self.renders_narrow[lf_frame_idx]),
766 }),
767 refs: self.reference.map(|r| {
768 (r != usize::MAX).then(|| Reference {
769 frame: Arc::clone(&self.frames[r]),
770 image: Arc::clone(&self.renders_narrow[r]),
771 })
772 }),
773 };
774
775 let frame = self.loading_frame().unwrap();
776 let prev_frame_visibility = self.get_previous_frames_visibility(frame);
777
778 let state = Self::do_render(
779 frame,
780 &reference_frames,
781 state,
782 image_region,
783 prev_frame_visibility,
784 &self.pool,
785 );
786
787 match state {
788 FrameRender::InProgress(cache) => {
789 self.loading_render_cache_narrow = Some(*cache);
790 return Err(Error::IncompleteFrame);
791 }
792 FrameRender::Err(e) => {
793 return Err(e);
794 }
795 FrameRender::Done(mut image) => {
796 let skip_composition =
797 image::composite_preprocess(frame, &mut image, &self.pool)?;
798
799 if !skip_composition {
800 let oriented_image_region = util::apply_orientation_to_image_region(
801 &self.image_header,
802 image_region,
803 );
804 image::composite(
805 frame,
806 &mut image,
807 reference_frames.refs,
808 oriented_image_region,
809 &self.pool,
810 )?;
811 }
812
813 image
814 }
815 _ => todo!(),
816 }
817 } else {
818 let state = if let Some(cache) = self.loading_render_cache_wide.take() {
819 FrameRender::InProgress(Box::new(cache))
820 } else {
821 FrameRender::None
822 };
823
824 let reference_frames = ReferenceFrames {
825 lf: (lf_frame_idx != usize::MAX).then(|| Reference {
826 frame: Arc::clone(&self.frames[lf_frame_idx]),
827 image: Arc::clone(&self.renders_wide[lf_frame_idx]),
828 }),
829 refs: self.reference.map(|r| {
830 (r != usize::MAX).then(|| Reference {
831 frame: Arc::clone(&self.frames[r]),
832 image: Arc::clone(&self.renders_wide[r]),
833 })
834 }),
835 };
836
837 let frame = self.loading_frame().unwrap();
838 let prev_frame_visibility = self.get_previous_frames_visibility(frame);
839
840 let state = Self::do_render(
841 frame,
842 &reference_frames,
843 state,
844 image_region,
845 prev_frame_visibility,
846 &self.pool,
847 );
848
849 match state {
850 FrameRender::InProgress(cache) => {
851 self.loading_render_cache_wide = Some(*cache);
852 return Err(Error::IncompleteFrame);
853 }
854 FrameRender::Err(e) => {
855 return Err(e);
856 }
857 FrameRender::Done(mut image) => {
858 let skip_composition =
859 image::composite_preprocess(frame, &mut image, &self.pool)?;
860
861 if !skip_composition {
862 let oriented_image_region = util::apply_orientation_to_image_region(
863 &self.image_header,
864 image_region,
865 );
866 image::composite(
867 frame,
868 &mut image,
869 reference_frames.refs,
870 oriented_image_region,
871 &self.pool,
872 )?;
873 }
874
875 image
876 }
877 _ => todo!(),
878 }
879 };
880
881 let frame = self.loading_frame().unwrap();
882 if frame.header().lf_level > 0 {
883 let mut render = image.upsample_lf(frame.header().lf_level)?;
884 render.fill_opaque_alpha(&self.image_header.metadata.ec_info);
885 Ok(render)
886 } else {
887 Ok(image)
888 }
889 }
890
891 fn cache_color_transform(&self) -> Result<()> {
892 let mut cached_transform = self.cached_transform.lock().unwrap();
893 if cached_transform.is_some() {
894 return Ok(());
895 }
896
897 let metadata = self.metadata();
898 let header_color_encoding = &metadata.colour_encoding;
899
900 let frame_color_encoding = if metadata.xyb_encoded {
901 ColorEncodingWithProfile::new(EnumColourEncoding::xyb(RenderingIntent::Perceptual))
902 } else if let ColourEncoding::Enum(encoding) = header_color_encoding {
903 ColorEncodingWithProfile::new(encoding.clone())
904 } else {
905 ColorEncodingWithProfile::with_icc(&self.embedded_icc)?
906 };
907 tracing::trace!(?frame_color_encoding);
908 tracing::trace!(requested_color_encoding = ?self.requested_color_encoding);
909
910 let mut transform = ColorTransform::builder();
911 transform.set_srgb_icc(!self.cms.supports_linear_tf());
912 transform.from_pq(self.suggested_hdr_tf() == Some(TransferFunction::Pq));
913 let transform = transform.build(
914 &frame_color_encoding,
915 &self.requested_color_encoding,
916 &metadata.opsin_inverse_matrix,
917 &metadata.tone_mapping,
918 &*self.cms,
919 )?;
920
921 *cached_transform = Some(transform);
922 Ok(())
923 }
924
925 fn postprocess_keyframe(
926 &self,
927 frame: &IndexedFrame,
928 grid: Arc<ImageWithRegion>,
929 ) -> Result<Arc<ImageWithRegion>> {
930 let frame_header = frame.header();
931 let metadata = self.metadata();
932
933 tracing::trace_span!("Transform to requested color encoding").in_scope(|| -> Result<_> {
934 if grid.ct_done() {
935 return Ok(grid);
936 }
937
938 self.cache_color_transform()?;
939
940 tracing::trace!(do_ycbcr = frame_header.do_ycbcr);
941
942 let transform = self.cached_transform.lock().unwrap();
943 let transform = transform.as_ref().unwrap();
944 if transform.is_noop() && !frame_header.do_ycbcr {
945 return Ok(grid);
946 }
947
948 let mut grid = grid.try_clone()?;
949
950 if frame_header.do_ycbcr {
951 grid.convert_modular_color(self.image_header.metadata.bit_depth)?;
952 let [cb, y, cr] = grid.as_color_floats_mut();
953 jxl_color::ycbcr_to_rgb([cb.buf_mut(), y.buf_mut(), cr.buf_mut()]);
954 }
955 if transform.is_noop() {
956 let output_channels = transform.output_channels();
957 grid.remove_color_channels(output_channels);
958 return Ok(Arc::new(grid));
959 }
960
961 let encoded_color_channels = frame_header.encoded_color_channels();
962 if encoded_color_channels < 3 {
963 grid.clone_gray()?;
964 }
965
966 grid.convert_modular_color(self.image_header.metadata.bit_depth)?;
967 let (color_channels, extra_channels) = grid.buffer_mut().split_at_mut(3);
968 let mut channels = Vec::new();
969 for grid in color_channels {
970 channels.push(grid.as_float_mut().unwrap().buf_mut());
971 }
972
973 let mut has_black = false;
974 for (grid, ec_info) in extra_channels.iter_mut().zip(&metadata.ec_info) {
975 if ec_info.is_black() {
976 channels.push(grid.convert_to_float_modular(ec_info.bit_depth)?.buf_mut());
977 has_black = true;
978 break;
979 }
980 }
981
982 if has_black {
983 for grid in channels.iter_mut() {
985 for v in &mut **grid {
986 *v = 1.0 - *v;
987 }
988 }
989 }
990
991 let output_channels = transform.run_with_threads(&mut channels, &self.pool)?;
992 if output_channels < 3 {
993 grid.remove_color_channels(output_channels);
994 }
995 grid.set_ct_done(true);
996 Ok(Arc::new(grid))
997 })
998 }
999}
1000
1001#[derive(Debug)]
1003pub struct IndexedFrame {
1004 f: Frame,
1005 idx: usize,
1006}
1007
1008impl IndexedFrame {
1009 fn new(frame: Frame, index: usize) -> Self {
1010 IndexedFrame {
1011 f: frame,
1012 idx: index,
1013 }
1014 }
1015
1016 pub fn index(&self) -> usize {
1018 self.idx
1019 }
1020}
1021
1022impl std::ops::Deref for IndexedFrame {
1023 type Target = Frame;
1024
1025 fn deref(&self) -> &Self::Target {
1026 &self.f
1027 }
1028}
1029
1030impl std::ops::DerefMut for IndexedFrame {
1031 fn deref_mut(&mut self) -> &mut Self::Target {
1032 &mut self.f
1033 }
1034}
1035
1036#[derive(Debug, Copy, Clone)]
1037struct FrameDependence {
1038 pub(crate) lf: usize,
1039 pub(crate) ref_slots: [usize; 4],
1040}
1041
1042#[derive(Debug, Clone, Default)]
1043struct ReferenceFrames<S: Sample> {
1044 pub(crate) lf: Option<Reference<S>>,
1045 pub(crate) refs: [Option<Reference<S>>; 4],
1046}
1047
1048#[derive(Debug, Clone)]
1049struct Reference<S: Sample> {
1050 pub(crate) frame: Arc<IndexedFrame>,
1051 pub(crate) image: Arc<FrameRenderHandle<S>>,
1052}