jxl_render/
lib.rs

1//! This crate is the core of jxl-oxide that provides JPEG XL renderer.
2use 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
34/// Render context that tracks loaded and rendered frames.
35pub 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    /// Returns the image width.
233    #[inline]
234    pub fn width(&self) -> u32 {
235        self.image_header.size.width
236    }
237
238    /// Returns the image height.
239    #[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    /// Returns the number of loaded keyframes in the context.
250    #[inline]
251    pub fn loaded_keyframes(&self) -> usize {
252        self.keyframes.len()
253    }
254
255    /// Returns the number of loaded frames in the context, including frames that are not shown
256    /// directly.
257    #[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        // Check if LF frame exists
422        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    /// Returns the frame with the keyframe index, or `None` if the keyframe does not exist.
454    #[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            // Handle partial rendering. If val != self.keyframes.len(), is_keyframe() should be
538            // false, since if not the index should exist in self.keyframes.
539            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            // Frame rendering will run immediately, this is not we want.
562            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    /// Renders the first keyframe.
592    ///
593    /// The keyframe should be loaded in prior to rendering, with one of the loading methods.
594    #[inline]
595    pub fn render(&mut self) -> Result<Arc<ImageWithRegion>> {
596        self.render_keyframe(0)
597    }
598
599    /// Renders the keyframe.
600    ///
601    /// The keyframe should be loaded in prior to rendering, with one of the loading methods.
602    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                // 0 means full ink; invert samples
876                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/// Frame with its index in the image.
895#[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    /// Returns the frame index.
910    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}