jxl_render/
image.rs

1use std::sync::Arc;
2
3use jxl_frame::{data::GlobalModular, FrameHeader};
4use jxl_grid::{AlignedGrid, AllocTracker, MutableSubgrid};
5use jxl_image::{BitDepth, ImageHeader};
6use jxl_modular::{ChannelShift, Sample};
7use jxl_threadpool::JxlThreadPool;
8use jxl_vardct::LfChannelDequantization;
9
10use crate::{util, FrameRender, FrameRenderHandle, Region, Result};
11
12#[derive(Debug)]
13pub enum ImageBuffer {
14    F32(AlignedGrid<f32>),
15    I32(AlignedGrid<i32>),
16    I16(AlignedGrid<i16>),
17}
18
19impl ImageBuffer {
20    #[inline]
21    pub fn from_modular_channel<S: Sample>(g: AlignedGrid<S>) -> Self {
22        let g = match S::try_into_grid_i16(g) {
23            Ok(g) => return Self::I16(g),
24            Err(g) => g,
25        };
26        match S::try_into_grid_i32(g) {
27            Ok(g) => Self::I32(g),
28            Err(_) => panic!(),
29        }
30    }
31
32    #[inline]
33    pub fn zeroed_f32(width: usize, height: usize, tracker: Option<&AllocTracker>) -> Result<Self> {
34        let grid = AlignedGrid::with_alloc_tracker(width, height, tracker)?;
35        Ok(Self::F32(grid))
36    }
37
38    #[inline]
39    pub fn try_clone(&self) -> Result<Self> {
40        match self {
41            Self::F32(g) => g.try_clone().map(Self::F32),
42            Self::I32(g) => g.try_clone().map(Self::I32),
43            Self::I16(g) => g.try_clone().map(Self::I16),
44        }
45        .map_err(From::from)
46    }
47
48    #[inline]
49    pub fn width(&self) -> usize {
50        match self {
51            Self::F32(g) => g.width(),
52            Self::I32(g) => g.width(),
53            Self::I16(g) => g.width(),
54        }
55    }
56
57    #[inline]
58    pub fn height(&self) -> usize {
59        match self {
60            Self::F32(g) => g.height(),
61            Self::I32(g) => g.height(),
62            Self::I16(g) => g.height(),
63        }
64    }
65
66    #[inline]
67    pub fn tracker(&self) -> Option<AllocTracker> {
68        match self {
69            Self::F32(g) => g.tracker(),
70            Self::I32(g) => g.tracker(),
71            Self::I16(g) => g.tracker(),
72        }
73    }
74
75    #[inline]
76    pub fn as_float(&self) -> Option<&AlignedGrid<f32>> {
77        if let Self::F32(g) = self {
78            Some(g)
79        } else {
80            None
81        }
82    }
83
84    #[inline]
85    pub fn as_float_mut(&mut self) -> Option<&mut AlignedGrid<f32>> {
86        if let Self::F32(g) = self {
87            Some(g)
88        } else {
89            None
90        }
91    }
92
93    pub fn convert_to_float_modular(
94        &mut self,
95        bit_depth: BitDepth,
96    ) -> Result<&mut AlignedGrid<f32>> {
97        Ok(match self {
98            Self::F32(g) => g,
99            Self::I32(g) => {
100                let mut out =
101                    AlignedGrid::with_alloc_tracker(g.width(), g.height(), g.tracker().as_ref())?;
102                for (o, &i) in out.buf_mut().iter_mut().zip(g.buf()) {
103                    *o = bit_depth.parse_integer_sample(i);
104                }
105
106                *self = Self::F32(out);
107                self.as_float_mut().unwrap()
108            }
109            Self::I16(g) => {
110                let mut out =
111                    AlignedGrid::with_alloc_tracker(g.width(), g.height(), g.tracker().as_ref())?;
112                for (o, &i) in out.buf_mut().iter_mut().zip(g.buf()) {
113                    *o = bit_depth.parse_integer_sample(i as i32);
114                }
115
116                *self = Self::F32(out);
117                self.as_float_mut().unwrap()
118            }
119        })
120    }
121
122    pub fn cast_to_float(&mut self) -> Result<&mut AlignedGrid<f32>> {
123        Ok(match self {
124            Self::F32(g) => g,
125            Self::I32(g) => {
126                let mut out =
127                    AlignedGrid::with_alloc_tracker(g.width(), g.height(), g.tracker().as_ref())?;
128                for (o, &i) in out.buf_mut().iter_mut().zip(g.buf()) {
129                    *o = i as f32;
130                }
131
132                *self = Self::F32(out);
133                self.as_float_mut().unwrap()
134            }
135            Self::I16(g) => {
136                let mut out =
137                    AlignedGrid::with_alloc_tracker(g.width(), g.height(), g.tracker().as_ref())?;
138                for (o, &i) in out.buf_mut().iter_mut().zip(g.buf()) {
139                    *o = i as f32;
140                }
141
142                *self = Self::F32(out);
143                self.as_float_mut().unwrap()
144            }
145        })
146    }
147
148    pub fn convert_to_float_modular_xyb<'g>(
149        yxb: [&'g mut Self; 3],
150        lf_dequant: &LfChannelDequantization,
151    ) -> Result<[&'g mut AlignedGrid<f32>; 3]> {
152        match yxb {
153            [Self::F32(_), Self::F32(_), Self::F32(_)] => {
154                panic!("channels are already converted");
155            }
156            [Self::I32(y), Self::I32(_), Self::I32(b)] => {
157                for (b, &y) in b.buf_mut().iter_mut().zip(y.buf()) {
158                    *b = b.saturating_add(y);
159                }
160            }
161            [Self::I16(y), Self::I16(_), Self::I16(b)] => {
162                for (b, &y) in b.buf_mut().iter_mut().zip(y.buf()) {
163                    *b = b.saturating_add(y);
164                }
165            }
166            _ => panic!(),
167        }
168
169        let [y, x, b] = yxb;
170        let y = y.cast_to_float()?;
171        let x = x.cast_to_float()?;
172        let b = b.cast_to_float()?;
173        let buf_y = y.buf_mut();
174        let buf_x = x.buf_mut();
175        let buf_b = b.buf_mut();
176        let m_x_lf = lf_dequant.m_x_lf_unscaled();
177        let m_y_lf = lf_dequant.m_y_lf_unscaled();
178        let m_b_lf = lf_dequant.m_b_lf_unscaled();
179
180        for ((y, x), b) in buf_y.iter_mut().zip(buf_x).zip(buf_b) {
181            let py = *y;
182            let px = *x;
183            *y = px * m_x_lf;
184            *x = py * m_y_lf;
185            *b *= m_b_lf;
186        }
187
188        Ok([y, x, b])
189    }
190
191    pub(crate) fn upsample_nn(&self, factor: u32) -> Result<ImageBuffer> {
192        #[inline]
193        fn inner<S: Copy>(
194            original: &[S],
195            target: &mut [S],
196            width: usize,
197            height: usize,
198            factor: u32,
199        ) {
200            assert_eq!(original.len(), width * height);
201            assert_eq!(target.len(), original.len() << (factor * 2));
202            let step = 1usize << factor;
203            let stride = width << factor;
204            for y in 0..height {
205                let original = &original[y * width..];
206                let target = &mut target[y * step * stride..];
207                for (x, &value) in original[..width].iter().enumerate() {
208                    target[x * step..][..step].fill(value);
209                }
210                for row in 1..step {
211                    target.copy_within(..stride, stride * row);
212                }
213            }
214        }
215
216        if factor == 0 {
217            return self.try_clone();
218        }
219
220        let tracker = self.tracker();
221        let width = self.width();
222        let height = self.height();
223        Ok(match self {
224            Self::F32(g) => {
225                let up_width = width << factor;
226                let up_height = height << factor;
227                let mut out =
228                    AlignedGrid::with_alloc_tracker(up_width, up_height, tracker.as_ref())?;
229
230                let original = g.buf();
231                let target = out.buf_mut();
232                inner(original, target, width, height, factor);
233                Self::F32(out)
234            }
235            Self::I32(g) => {
236                let up_width = width << factor;
237                let up_height = height << factor;
238                let mut out =
239                    AlignedGrid::with_alloc_tracker(up_width, up_height, tracker.as_ref())?;
240
241                let original = g.buf();
242                let target = out.buf_mut();
243                inner(original, target, width, height, factor);
244                Self::I32(out)
245            }
246            Self::I16(g) => {
247                let up_width = width << factor;
248                let up_height = height << factor;
249                let mut out =
250                    AlignedGrid::with_alloc_tracker(up_width, up_height, tracker.as_ref())?;
251
252                let original = g.buf();
253                let target = out.buf_mut();
254                inner(original, target, width, height, factor);
255                Self::I16(out)
256            }
257        })
258    }
259}
260
261#[derive(Debug)]
262pub struct ImageWithRegion {
263    buffer: Vec<ImageBuffer>,
264    regions: Vec<(Region, ChannelShift)>,
265    color_channels: usize,
266    ct_done: bool,
267    blend_done: bool,
268    tracker: Option<AllocTracker>,
269}
270
271impl ImageWithRegion {
272    pub(crate) fn new(color_channels: usize, tracker: Option<&AllocTracker>) -> Self {
273        Self {
274            buffer: Vec::new(),
275            regions: Vec::new(),
276            color_channels,
277            ct_done: false,
278            blend_done: false,
279            tracker: tracker.cloned(),
280        }
281    }
282
283    pub(crate) fn try_clone(&self) -> Result<Self> {
284        Ok(Self {
285            buffer: self
286                .buffer
287                .iter()
288                .map(|x| x.try_clone())
289                .collect::<std::result::Result<_, _>>()?,
290            regions: self.regions.clone(),
291            color_channels: self.color_channels,
292            ct_done: self.ct_done,
293            blend_done: false,
294            tracker: self.tracker.clone(),
295        })
296    }
297
298    #[inline]
299    pub(crate) fn alloc_tracker(&self) -> Option<&AllocTracker> {
300        self.tracker.as_ref()
301    }
302
303    #[inline]
304    pub fn channels(&self) -> usize {
305        self.buffer.len()
306    }
307
308    #[inline]
309    pub fn buffer(&self) -> &[ImageBuffer] {
310        &self.buffer
311    }
312
313    #[inline]
314    pub fn buffer_mut(&mut self) -> &mut [ImageBuffer] {
315        &mut self.buffer
316    }
317
318    #[inline]
319    pub fn take_buffer(&mut self) -> Vec<ImageBuffer> {
320        std::mem::take(&mut self.buffer)
321    }
322
323    #[inline]
324    pub fn regions_and_shifts(&self) -> &[(Region, ChannelShift)] {
325        &self.regions
326    }
327
328    #[inline]
329    pub fn append_channel(&mut self, buffer: ImageBuffer, region: Region) {
330        assert_eq!(buffer.width(), region.width as usize);
331        assert_eq!(buffer.height(), region.height as usize);
332        self.buffer.push(buffer);
333        self.regions.push((region, ChannelShift::from_shift(0)));
334    }
335
336    #[inline]
337    pub fn append_channel_shifted(
338        &mut self,
339        buffer: ImageBuffer,
340        original_region: Region,
341        shift: ChannelShift,
342    ) {
343        let (width, height) = shift.shift_size((original_region.width, original_region.height));
344        assert_eq!(buffer.width(), width as usize);
345        assert_eq!(buffer.height(), height as usize);
346        self.buffer.push(buffer);
347        self.regions.push((original_region, shift));
348    }
349
350    #[inline]
351    pub fn replace_channel(&mut self, index: usize, buffer: ImageBuffer, region: Region) {
352        assert_eq!(buffer.width(), region.width as usize);
353        assert_eq!(buffer.height(), region.height as usize);
354        self.buffer[index] = buffer;
355        self.regions[index] = (region, ChannelShift::from_shift(0));
356    }
357
358    #[inline]
359    pub fn replace_channel_shifted(
360        &mut self,
361        index: usize,
362        buffer: ImageBuffer,
363        original_region: Region,
364        shift: ChannelShift,
365    ) {
366        let (width, height) = shift.shift_size((original_region.width, original_region.height));
367        assert_eq!(buffer.width(), width as usize);
368        assert_eq!(buffer.height(), height as usize);
369        self.buffer[index] = buffer;
370        self.regions[index] = (original_region, shift);
371    }
372
373    #[inline]
374    pub(crate) fn swap_channel_f32(
375        &mut self,
376        index: usize,
377        buffer: &mut AlignedGrid<f32>,
378        region: Region,
379    ) {
380        assert_eq!(buffer.width(), region.width as usize);
381        assert_eq!(buffer.height(), region.height as usize);
382        let ImageBuffer::F32(original_buffer) = &mut self.buffer[index] else {
383            panic!("original buffer is not F32");
384        };
385        std::mem::swap(original_buffer, buffer);
386        self.regions[index] = (region, ChannelShift::from_shift(0));
387    }
388
389    pub fn extend_from_gmodular<S: Sample>(&mut self, gmodular: GlobalModular<S>) {
390        let Some(image) = gmodular.modular.into_image() else {
391            return;
392        };
393        for (g, info) in image.into_image_channels_with_info() {
394            let (width, height) = info.original_size();
395            let shift = info.shift();
396
397            let original_region = Region::with_size(width, height);
398            let buffer = ImageBuffer::from_modular_channel(g);
399            self.append_channel_shifted(buffer, original_region, shift);
400        }
401    }
402
403    pub(crate) fn clone_gray(&mut self) -> Result<()> {
404        assert_eq!(self.color_channels, 1);
405
406        let gray = self.buffer[0].try_clone()?;
407        let region = self.regions[0];
408        self.buffer.insert(1, gray.try_clone()?);
409        self.regions.insert(1, region);
410        self.buffer.insert(2, gray);
411        self.regions.insert(2, region);
412
413        self.color_channels = 3;
414        Ok(())
415    }
416
417    pub(crate) fn convert_modular_color(&mut self, bit_depth: BitDepth) -> Result<()> {
418        assert!(self.buffer.len() >= self.color_channels);
419        for g in self.buffer.iter_mut().take(self.color_channels) {
420            g.convert_to_float_modular(bit_depth)?;
421        }
422        Ok(())
423    }
424
425    pub(crate) fn convert_modular_xyb(
426        &mut self,
427        lf_dequant: &LfChannelDequantization,
428    ) -> Result<()> {
429        assert_eq!(self.color_channels, 3);
430        let [y, x, b, ..] = &mut *self.buffer else {
431            panic!()
432        };
433        ImageBuffer::convert_to_float_modular_xyb([y, x, b], lf_dequant)?;
434        Ok(())
435    }
436
437    pub(crate) fn upsample_lf(&self, lf_level: u32) -> Result<Self> {
438        let factor = lf_level * 3;
439        let mut out = Self::new(self.color_channels, self.tracker.as_ref());
440        for (&(region, shift), buffer) in self.regions.iter().zip(&self.buffer) {
441            let up_region = region.upsample(factor);
442            let buffer = buffer.upsample_nn(factor)?;
443            out.append_channel_shifted(buffer, up_region, shift);
444        }
445        Ok(out)
446    }
447
448    pub(crate) fn upsample_jpeg(
449        &mut self,
450        valid_region: Region,
451        bit_depth: BitDepth,
452    ) -> Result<()> {
453        assert_eq!(self.color_channels, 3);
454        self.convert_modular_color(bit_depth)?;
455
456        for (g, (region, shift)) in self.buffer.iter_mut().zip(&mut self.regions).take(3) {
457            let downsampled_image_region = region.downsample_with_shift(*shift);
458            let downsampled_valid_region = valid_region.downsample_with_shift(*shift);
459            let left = downsampled_valid_region
460                .left
461                .abs_diff(downsampled_image_region.left);
462            let top = downsampled_valid_region
463                .top
464                .abs_diff(downsampled_image_region.top);
465            let width = downsampled_valid_region.width;
466            let height = downsampled_valid_region.height;
467
468            let subgrid = g.as_float().unwrap().as_subgrid().subgrid(
469                left as usize..(left + width) as usize,
470                top as usize..(top + height) as usize,
471            );
472            let out = crate::filter::apply_jpeg_upsampling_single(
473                subgrid,
474                *shift,
475                valid_region,
476                self.tracker.as_ref(),
477            )?;
478
479            *g = ImageBuffer::F32(out);
480            *region = valid_region;
481            *shift = ChannelShift::from_shift(0);
482        }
483
484        Ok(())
485    }
486
487    pub(crate) fn upsample_nonseparable(
488        &mut self,
489        image_header: &ImageHeader,
490        frame_header: &FrameHeader,
491        upsampled_valid_region: Region,
492        ec_to_color_only: bool,
493    ) -> Result<()> {
494        if frame_header.upsampling != 1 && self.buffer[0].as_float().is_none() {
495            debug_assert!(!image_header.metadata.xyb_encoded);
496        }
497
498        let color_channels = self.color_channels;
499        let color_shift = frame_header.upsampling.trailing_zeros();
500
501        for (idx, ((region, shift), g)) in self.regions.iter_mut().zip(&mut self.buffer).enumerate()
502        {
503            let tracker = g.tracker();
504            let ChannelShift::Shifts(upsampling_factor) = *shift else {
505                panic!("invalid channel shift for upsampling: {:?}", shift);
506            };
507            let bit_depth = if let Some(ec_idx) = idx.checked_sub(color_channels) {
508                image_header.metadata.ec_info[ec_idx].bit_depth
509            } else {
510                image_header.metadata.bit_depth
511            };
512
513            let target_factor = if ec_to_color_only { color_shift } else { 0 };
514            if upsampling_factor == target_factor {
515                continue;
516            }
517            let grid = g.convert_to_float_modular(bit_depth)?;
518
519            let downsampled_image_region = region.downsample(upsampling_factor);
520            let downsampled_valid_region = upsampled_valid_region.downsample(upsampling_factor);
521            let left = downsampled_valid_region
522                .left
523                .abs_diff(downsampled_image_region.left);
524            let top = downsampled_valid_region
525                .top
526                .abs_diff(downsampled_image_region.top);
527            let width = downsampled_valid_region.width;
528            let height = downsampled_valid_region.height;
529
530            let subgrid = grid.as_subgrid().subgrid(
531                left as usize..(left + width) as usize,
532                top as usize..(top + height) as usize,
533            );
534            *region = downsampled_valid_region;
535            let out = tracing::trace_span!(
536                "Non-separable upsampling",
537                channel_idx = idx,
538                upsampling_factor,
539                target_factor
540            )
541            .in_scope(|| {
542                crate::features::upsample(
543                    subgrid,
544                    region,
545                    image_header,
546                    upsampling_factor - target_factor,
547                    tracker.as_ref(),
548                )
549            })?;
550            if let Some(out) = out {
551                *g = ImageBuffer::F32(out);
552            }
553            *shift = ChannelShift::from_shift(target_factor);
554        }
555
556        Ok(())
557    }
558
559    pub(crate) fn prepare_color_upsampling(&mut self, frame_header: &FrameHeader) {
560        let upsampling_factor = frame_header.upsampling.trailing_zeros();
561        for (region, shift) in &mut self.regions {
562            match shift {
563                ChannelShift::Raw(..=-1, _) | ChannelShift::Raw(_, ..=-1) => continue,
564                ChannelShift::Raw(h, v) => {
565                    *h = h.wrapping_add_unsigned(upsampling_factor);
566                    *v = v.wrapping_add_unsigned(upsampling_factor);
567                }
568                ChannelShift::Shifts(shift) => {
569                    *shift += upsampling_factor;
570                }
571                ChannelShift::JpegUpsampling {
572                    has_h_subsample: false,
573                    h_subsample: false,
574                    has_v_subsample: false,
575                    v_subsample: false,
576                } => {
577                    *shift = ChannelShift::Shifts(upsampling_factor);
578                }
579                ChannelShift::JpegUpsampling { .. } => {
580                    panic!("unexpected chroma subsampling {:?}", shift);
581                }
582            }
583            *region = region.upsample(upsampling_factor);
584        }
585    }
586
587    #[inline]
588    pub(crate) fn remove_color_channels(&mut self, count: usize) {
589        assert!(self.color_channels >= count);
590        self.buffer.drain(count..self.color_channels);
591        self.regions.drain(count..self.color_channels);
592        self.color_channels = count;
593    }
594
595    #[inline]
596    pub fn color_channels(&self) -> usize {
597        self.color_channels
598    }
599
600    #[inline]
601    pub(crate) fn ct_done(&self) -> bool {
602        self.ct_done
603    }
604
605    #[inline]
606    pub(crate) fn set_ct_done(&mut self, ct_done: bool) {
607        self.ct_done = ct_done;
608    }
609
610    #[inline]
611    pub(crate) fn set_blend_done(&mut self, blend_done: bool) {
612        self.blend_done = blend_done;
613    }
614}
615
616impl ImageWithRegion {
617    pub(crate) fn as_color_floats(&self) -> [&AlignedGrid<f32>; 3] {
618        assert_eq!(self.color_channels, 3);
619        let [a, b, c, ..] = &*self.buffer else {
620            panic!()
621        };
622        let a = a.as_float().unwrap();
623        let b = b.as_float().unwrap();
624        let c = c.as_float().unwrap();
625        [a, b, c]
626    }
627
628    pub(crate) fn as_color_floats_mut(&mut self) -> [&mut AlignedGrid<f32>; 3] {
629        assert_eq!(self.color_channels, 3);
630        let [a, b, c, ..] = &mut *self.buffer else {
631            panic!()
632        };
633        let a = a.as_float_mut().unwrap();
634        let b = b.as_float_mut().unwrap();
635        let c = c.as_float_mut().unwrap();
636        [a, b, c]
637    }
638
639    pub(crate) fn color_groups_with_group_id(
640        &mut self,
641        frame_header: &jxl_frame::FrameHeader,
642    ) -> Vec<(u32, [MutableSubgrid<'_, f32>; 3])> {
643        assert_eq!(self.color_channels, 3);
644        let [fb_x, fb_y, fb_b, ..] = &mut *self.buffer else {
645            panic!();
646        };
647
648        let group_dim = frame_header.group_dim();
649        let base_group_x = self.regions[0].0.left as u32 / group_dim;
650        let base_group_y = self.regions[0].0.top as u32 / group_dim;
651        let width = self.regions[0].0.width;
652        let frame_groups_per_row = frame_header.groups_per_row();
653        let groups_per_row = width.div_ceil(group_dim);
654
655        let [fb_x, fb_y, fb_b] = [(0usize, fb_x), (1, fb_y), (2, fb_b)].map(|(idx, fb)| {
656            let fb = fb.as_float_mut().unwrap().as_subgrid_mut();
657            let hshift = self.regions[idx].1.hshift();
658            let vshift = self.regions[idx].1.vshift();
659            let group_dim = group_dim as usize;
660            fb.into_groups(group_dim >> hshift, group_dim >> vshift)
661        });
662
663        fb_x.into_iter()
664            .zip(fb_y)
665            .zip(fb_b)
666            .enumerate()
667            .map(|(idx, ((fb_x, fb_y), fb_b))| {
668                let idx = idx as u32;
669                let group_x = base_group_x + (idx % groups_per_row);
670                let group_y = base_group_y + (idx / groups_per_row);
671                let group_idx = group_y * frame_groups_per_row + group_x;
672                (group_idx, [fb_x, fb_y, fb_b])
673            })
674            .collect()
675    }
676}
677
678pub struct RenderedImage<S: Sample> {
679    image: Arc<FrameRenderHandle<S>>,
680}
681
682impl<S: Sample> RenderedImage<S> {
683    pub(crate) fn new(image: Arc<FrameRenderHandle<S>>) -> Self {
684        Self { image }
685    }
686}
687
688impl<S: Sample> RenderedImage<S> {
689    pub(crate) fn blend(
690        &self,
691        oriented_image_region: Option<Region>,
692        pool: &JxlThreadPool,
693    ) -> Result<Arc<ImageWithRegion>> {
694        let image_header = self.image.frame.image_header();
695        let frame_header = self.image.frame.header();
696        let image_region = self.image.image_region;
697        let oriented_image_region = oriented_image_region
698            .unwrap_or_else(|| util::apply_orientation_to_image_region(image_header, image_region));
699        let frame_region = oriented_image_region
700            .translate(-frame_header.x0, -frame_header.y0)
701            .downsample(frame_header.lf_level * 3);
702        let frame_region = util::pad_lf_region(frame_header, frame_region);
703        let frame_region = util::pad_color_region(image_header, frame_header, frame_region);
704        let frame_region = frame_region.upsample(frame_header.upsampling.ilog2());
705        let frame_region = if frame_header.frame_type.is_normal_frame() {
706            let full_image_region_in_frame =
707                Region::with_size(image_header.size.width, image_header.size.height)
708                    .translate(-frame_header.x0, -frame_header.y0);
709            frame_region.intersection(full_image_region_in_frame)
710        } else {
711            frame_region
712        };
713
714        let mut grid_lock = self.image.wait_until_render()?;
715        if let FrameRender::Blended(image) = &*grid_lock {
716            return Ok(Arc::clone(image));
717        }
718
719        let render = std::mem::replace(&mut *grid_lock, FrameRender::ErrTaken);
720        let FrameRender::Done(mut grid) = render else {
721            panic!();
722        };
723
724        if frame_header.can_reference() {
725            let bit_depth_it = std::iter::repeat(image_header.metadata.bit_depth)
726                .take(grid.color_channels)
727                .chain(image_header.metadata.ec_info.iter().map(|ec| ec.bit_depth));
728            for (buffer, bit_depth) in grid.buffer.iter_mut().zip(bit_depth_it) {
729                buffer.convert_to_float_modular(bit_depth)?;
730            }
731        }
732
733        let skip_blending =
734            !frame_header.frame_type.is_normal_frame() || frame_header.resets_canvas;
735
736        if !(grid.ct_done() || frame_header.save_before_ct || skip_blending && frame_header.is_last)
737        {
738            util::convert_color_for_record(image_header, frame_header.do_ycbcr, &mut grid, pool)?;
739        }
740
741        if skip_blending {
742            grid.blend_done = true;
743            let image = Arc::new(grid);
744            *grid_lock = FrameRender::Blended(Arc::clone(&image));
745            return Ok(image);
746        }
747
748        *grid_lock = FrameRender::Rendering;
749        drop(grid_lock);
750
751        let image = crate::blend::blend(
752            image_header,
753            self.image.refs.clone(),
754            &self.image.frame,
755            &mut grid,
756            frame_region,
757            pool,
758        )?;
759
760        let image = Arc::new(image);
761        drop(
762            self.image
763                .done_render(FrameRender::Blended(Arc::clone(&image))),
764        );
765        Ok(image)
766    }
767
768    pub(crate) fn try_take_blended(&self) -> Option<ImageWithRegion> {
769        let mut grid_lock = self.image.render.lock().unwrap();
770        match std::mem::take(&mut *grid_lock) {
771            FrameRender::Blended(image) => {
772                let cloned_image = Arc::clone(&image);
773                let image_inner = Arc::into_inner(cloned_image);
774                if image_inner.is_none() {
775                    *grid_lock = FrameRender::Blended(image);
776                }
777                image_inner
778            }
779            render => {
780                *grid_lock = render;
781                None
782            }
783        }
784    }
785}