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}