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