jxl_render/
lib.rs

1//! This crate is the core of jxl-oxide that provides JPEG XL renderer.
2use 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
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    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    /// Returns the image width.
245    #[inline]
246    pub fn width(&self) -> u32 {
247        self.image_header.size.width
248    }
249
250    /// Returns the image height.
251    #[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    /// Returns the number of loaded keyframes in the context.
262    #[inline]
263    pub fn loaded_keyframes(&self) -> usize {
264        self.keyframes.len()
265    }
266
267    /// Returns the number of loaded frames in the context, including frames that are not shown
268    /// directly.
269    #[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        // Check if LF frame exists
434        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    /// Returns the frame with the keyframe index, or `None` if the keyframe does not exist.
466    #[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            // Handle partial rendering. If val != self.keyframes.len(), is_keyframe() should be
568            // false, since if not the index should exist in self.keyframes.
569            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            // Frame rendering will run immediately, this is not we want.
592            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    /// Renders the first keyframe.
622    ///
623    /// The keyframe should be loaded in prior to rendering, with one of the loading methods.
624    #[inline]
625    pub fn render(&mut self) -> Result<Arc<ImageWithRegion>> {
626        self.render_keyframe(0)
627    }
628
629    /// Renders the keyframe.
630    ///
631    /// The keyframe should be loaded in prior to rendering, with one of the loading methods.
632    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                // 0 means full ink; invert samples
984                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/// Frame with its index in the image.
1002#[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    /// Returns the frame index.
1017    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}