Skip to main content

j2k/adapter/
device_plan.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2// j2k-coverage: shared-accelerator-host
3
4use crate::error::J2kError;
5use j2k_core::{Downscale, Rect};
6
7/// Device decode shape requested by a GPU adapter.
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
9pub enum DeviceDecodeRequest {
10    /// Decode the full image at full resolution.
11    Full,
12    /// Decode a full-resolution source region.
13    Region {
14        /// Source region of interest.
15        roi: Rect,
16    },
17    /// Decode the full image at reduced resolution.
18    Scaled {
19        /// Requested downscale factor.
20        scale: Downscale,
21    },
22    /// Decode a source region at reduced resolution.
23    RegionScaled {
24        /// Source region of interest.
25        roi: Rect,
26        /// Requested downscale factor.
27        scale: Downscale,
28    },
29}
30
31/// Normalized device decode plan derived from source dimensions and request.
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub struct DeviceDecodePlan {
34    source_dims: (u32, u32),
35    source_rect: Rect,
36    scale: Downscale,
37    output_rect: Rect,
38}
39
40impl DeviceDecodePlan {
41    /// Build a normalized plan for an image.
42    pub fn for_image(
43        source_dims: (u32, u32),
44        request: DeviceDecodeRequest,
45    ) -> Result<Self, J2kError> {
46        let (source_rect, scale) = match request {
47            DeviceDecodeRequest::Full => (Rect::full(source_dims), Downscale::None),
48            DeviceDecodeRequest::Region { roi } => (roi, Downscale::None),
49            DeviceDecodeRequest::Scaled { scale } => (Rect::full(source_dims), scale),
50            DeviceDecodeRequest::RegionScaled { roi, scale } => (roi, scale),
51        };
52
53        if source_rect.w == 0 || source_rect.h == 0 || !source_rect.is_within(source_dims) {
54            return Err(J2kError::InvalidRegion {
55                x: source_rect.x,
56                y: source_rect.y,
57                w: source_rect.w,
58                h: source_rect.h,
59                image_w: source_dims.0,
60                image_h: source_dims.1,
61            });
62        }
63
64        Ok(Self {
65            source_dims,
66            source_rect,
67            scale,
68            output_rect: source_rect.scaled_covering(scale),
69        })
70    }
71
72    /// Original image dimensions.
73    pub fn source_dims(self) -> (u32, u32) {
74        self.source_dims
75    }
76
77    /// Full-resolution source rectangle to read.
78    pub fn source_rect(self) -> Rect {
79        self.source_rect
80    }
81
82    /// Requested downscale factor.
83    pub fn scale(self) -> Downscale {
84        self.scale
85    }
86
87    /// Output rectangle in reduced-resolution coordinates.
88    pub fn output_rect(self) -> Rect {
89        self.output_rect
90    }
91
92    /// Output dimensions in pixels.
93    pub fn output_dims(self) -> (u32, u32) {
94        (self.output_rect.w, self.output_rect.h)
95    }
96
97    /// Target resolution hint for native decoders that accept one.
98    pub fn target_resolution(self) -> Option<(u32, u32)> {
99        (self.scale != Downscale::None).then_some(self.output_dims())
100    }
101
102    /// Return true when the request is an unscaled full-frame decode.
103    pub fn is_full_frame(self) -> bool {
104        self.source_rect == Rect::full(self.source_dims) && self.scale == Downscale::None
105    }
106}