Skip to main content

j2k_jpeg/decoder/routing/
owned_output.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Freshly allocated decode-output routing with external-owner accounting.
4
5use alloc::vec::Vec;
6
7use crate::allocation::checked_add_allocation_bytes;
8
9use super::super::{
10    additional_decode_scratch_bytes, allocate_output_buffer_with_live_budget,
11    checked_output_geometry, output_format_from_parts, scaled_dimensions, scaled_rect_covering,
12    DecodeOutcome, DecodeRequest, Decoder, JpegError, Rect, ScratchPool, DEFAULT_SCRATCH,
13};
14
15impl Decoder<'_> {
16    /// Decode into a freshly allocated tightly packed buffer using a request
17    /// object instead of a method-name cross-product.
18    ///
19    /// # Errors
20    ///
21    /// Returns an output-geometry, unsupported-format, or scan decode error.
22    pub fn decode_request(
23        &self,
24        request: DecodeRequest,
25    ) -> Result<(Vec<u8>, DecodeOutcome), JpegError> {
26        self.decode_request_with_external_live(request, 0)
27    }
28
29    /// Decode into a new buffer while charging caller-owned host allocations.
30    ///
31    /// `external_live_bytes` excludes this decoder, its checkpoint cache,
32    /// thread-local scratch, and the output allocated by this call.
33    ///
34    /// # Errors
35    ///
36    /// Returns [`JpegError::MemoryCapExceeded`] before a planned aggregate
37    /// owner graph exceeds the decode cap, or another ordinary decode error.
38    #[doc(hidden)]
39    pub fn decode_request_with_external_live(
40        &self,
41        request: DecodeRequest,
42        external_live_bytes: usize,
43    ) -> Result<(Vec<u8>, DecodeOutcome), JpegError> {
44        DEFAULT_SCRATCH.with(|pool| {
45            self.decode_request_with_scratch_and_external_live(
46                &mut pool.borrow_mut(),
47                request,
48                external_live_bytes,
49            )
50        })
51    }
52
53    /// Decode into a new buffer with caller-owned scratch and external owners.
54    ///
55    /// # Errors
56    ///
57    /// Returns an aggregate host-cap or ordinary decode error.
58    #[doc(hidden)]
59    pub fn decode_request_with_scratch_and_external_live(
60        &self,
61        pool: &mut ScratchPool,
62        request: DecodeRequest,
63        external_live_bytes: usize,
64    ) -> Result<(Vec<u8>, DecodeOutcome), JpegError> {
65        let legacy = output_format_from_parts(self.info.sof_kind, request.fmt, request.scale)?;
66        let source_roi = request
67            .region
68            .unwrap_or_else(|| Rect::full(self.info.dimensions));
69        if !source_roi.is_within(self.info.dimensions) {
70            return Err(JpegError::RectOutOfBounds {
71                rect: source_roi,
72                width: self.info.dimensions.0,
73                height: self.info.dimensions.1,
74            });
75        }
76        let output_rect = if request.region.is_some() {
77            scaled_rect_covering(source_roi, legacy.downscale())?
78        } else {
79            let (width, height) = scaled_dimensions(self.info.dimensions, legacy.downscale());
80            Rect::full((width, height))
81        };
82        let (stride, len) =
83            checked_output_geometry(output_rect.w, output_rect.h, legacy.bytes_per_pixel())?;
84        let additional_scratch = additional_decode_scratch_bytes(
85            self.info.sof_kind,
86            self.info.dimensions,
87            legacy,
88            source_roi,
89            output_rect,
90            legacy.downscale(),
91        )?;
92        let planned_output_live = checked_add_allocation_bytes(external_live_bytes, len)?;
93        self.prepare_decode_workspace_with_additional(
94            pool,
95            planned_output_live,
96            additional_scratch,
97        )?;
98        let workspace_cap = self.decode_workspace_cap()?;
99        let mut allocation_live =
100            checked_add_allocation_bytes(external_live_bytes, pool.retained_bytes())?;
101        let mut out =
102            allocate_output_buffer_with_live_budget(len, &mut allocation_live, workspace_cap)?;
103        let decode_external_live =
104            checked_add_allocation_bytes(external_live_bytes, out.capacity())?;
105        let outcome = if let Some(roi) = request.region {
106            self.decode_region_into_output_format_with_scratch_and_external(
107                pool,
108                &mut out,
109                stride,
110                legacy,
111                roi,
112                decode_external_live,
113            )?
114        } else {
115            self.decode_into_output_format_with_scratch_and_external(
116                pool,
117                &mut out,
118                stride,
119                legacy,
120                decode_external_live,
121            )?
122        };
123        Ok((out, outcome))
124    }
125}