1use j2k_core::{
4 submit_ready_device, BackendRequest, DecodeOutcome, Downscale, ImageCodec, ImageDecode,
5 ImageDecodeDevice, ImageDecodeSubmit, PixelFormat, ReadySubmission, Rect,
6};
7#[cfg(feature = "cuda-runtime")]
8use j2k_jpeg::adapter::decoder_bytes;
9use j2k_jpeg::{
10 Decoder as CpuDecoder, JpegView, ScratchPool as CpuScratchPool, Warning as CpuWarning,
11};
12
13#[cfg(feature = "cuda-runtime")]
14use crate::owned_decode::decode_owned_cuda_rgb8;
15use crate::owned_decode::unsupported_owned_cuda_output_format;
16use crate::runtime::{validate_surface_request, wrap_surface};
17use crate::{CudaSession, Error, Surface};
18
19pub struct Decoder<'a> {
21 inner: CpuDecoder<'a>,
22}
23
24impl<'a> Decoder<'a> {
25 pub fn new(input: &'a [u8]) -> Result<Self, Error> {
27 Ok(Self {
28 inner: CpuDecoder::new(input)?,
29 })
30 }
31
32 fn decode_to_surface_impl(
33 &mut self,
34 session: &mut CudaSession,
35 fmt: PixelFormat,
36 backend: BackendRequest,
37 ) -> Result<Surface, Error> {
38 validate_surface_request(backend)?;
39 if j2k_profile::gpu_route_profile_enabled() {
40 let request_s = format!("{backend:?}");
41 let fmt_s = format!("{fmt:?}");
42 let width_s = self.inner.info().dimensions.0.to_string();
43 let height_s = self.inner.info().dimensions.1.to_string();
44 j2k_profile::emit_gpu_route_profile(
45 "jpeg",
46 "cuda",
47 &[
48 ("op", "full"),
49 ("request", request_s.as_str()),
50 ("fmt", fmt_s.as_str()),
51 ("width", width_s.as_str()),
52 ("height", height_s.as_str()),
53 ("decision", "begin"),
54 ],
55 );
56 }
57 if backend == BackendRequest::Cuda {
58 if fmt == PixelFormat::Rgb8 {
59 return self.decode_cuda_rgb8(session);
60 }
61 return Err(unsupported_owned_cuda_output_format());
62 }
63 let (bytes, _outcome) = self.inner.decode(fmt)?;
64 if j2k_profile::gpu_route_profile_enabled() {
65 let request_s = format!("{backend:?}");
66 let fmt_s = format!("{fmt:?}");
67 j2k_profile::emit_gpu_route_profile(
68 "jpeg",
69 "cuda",
70 &[
71 ("op", "full"),
72 ("request", request_s.as_str()),
73 ("fmt", fmt_s.as_str()),
74 ("decision", "cpu_decode_then_wrap"),
75 ],
76 );
77 }
78 wrap_surface(bytes, self.inner.info().dimensions, fmt, backend, session)
79 }
80
81 #[cfg(feature = "cuda-runtime")]
82 fn decode_cuda_rgb8(&mut self, session: &mut CudaSession) -> Result<Surface, Error> {
83 let dimensions = self.inner.info().dimensions;
84 let surface = decode_owned_cuda_rgb8(decoder_bytes(&self.inner), dimensions, session)?;
85 if j2k_profile::gpu_route_profile_enabled() {
86 let width_s = dimensions.0.to_string();
87 let height_s = dimensions.1.to_string();
88 j2k_profile::emit_gpu_route_profile(
89 "jpeg",
90 "cuda",
91 &[
92 ("op", "full"),
93 ("request", "Cuda"),
94 ("fmt", "Rgb8"),
95 ("width", width_s.as_str()),
96 ("height", height_s.as_str()),
97 ("decision", "owned_cuda"),
98 ],
99 );
100 }
101 Ok(surface)
102 }
103
104 #[cfg(not(feature = "cuda-runtime"))]
105 #[allow(clippy::unnecessary_wraps, clippy::unused_self)]
106 fn decode_cuda_rgb8(&mut self, _session: &mut CudaSession) -> Result<Surface, Error> {
107 if j2k_profile::gpu_route_profile_enabled() {
108 j2k_profile::emit_gpu_route_profile(
109 "jpeg",
110 "cuda",
111 &[
112 ("op", "full"),
113 ("request", "Cuda"),
114 ("fmt", "Rgb8"),
115 ("decision", "owned_cuda_unavailable"),
116 ("reason", "cuda_runtime_feature_disabled"),
117 ],
118 );
119 }
120 Err(Error::CudaUnavailable)
121 }
122
123 fn decode_region_to_surface_impl(
124 &mut self,
125 session: &mut CudaSession,
126 fmt: PixelFormat,
127 roi: Rect,
128 backend: BackendRequest,
129 ) -> Result<Surface, Error> {
130 validate_surface_request(backend)?;
131 if backend == BackendRequest::Cuda {
132 return Err(Error::UnsupportedCudaRequest {
133 reason: "J2K CUDA JPEG owned decode does not support region output",
134 });
135 }
136 let (bytes, outcome) = self.inner.decode_region(fmt, roi.into())?;
137 wrap_surface(
138 bytes,
139 (outcome.decoded.w, outcome.decoded.h),
140 fmt,
141 backend,
142 session,
143 )
144 }
145
146 fn decode_scaled_to_surface_impl(
147 &mut self,
148 session: &mut CudaSession,
149 fmt: PixelFormat,
150 scale: Downscale,
151 backend: BackendRequest,
152 ) -> Result<Surface, Error> {
153 validate_surface_request(backend)?;
154 if backend == BackendRequest::Cuda {
155 return Err(Error::UnsupportedCudaRequest {
156 reason: "J2K CUDA JPEG owned decode does not support scaled output",
157 });
158 }
159 let (bytes, outcome) = self.inner.decode_scaled(fmt, scale)?;
160 wrap_surface(
161 bytes,
162 (outcome.decoded.w, outcome.decoded.h),
163 fmt,
164 backend,
165 session,
166 )
167 }
168
169 fn decode_region_scaled_to_surface_impl(
170 &mut self,
171 session: &mut CudaSession,
172 fmt: PixelFormat,
173 roi: Rect,
174 scale: Downscale,
175 backend: BackendRequest,
176 ) -> Result<Surface, Error> {
177 validate_surface_request(backend)?;
178 if backend == BackendRequest::Cuda {
179 return Err(Error::UnsupportedCudaRequest {
180 reason: "J2K CUDA JPEG owned decode does not support scaled region output",
181 });
182 }
183 let (bytes, outcome) = self.inner.decode_region_scaled(fmt, roi.into(), scale)?;
184 wrap_surface(
185 bytes,
186 (outcome.decoded.w, outcome.decoded.h),
187 fmt,
188 backend,
189 session,
190 )
191 }
192}
193
194impl ImageCodec for Decoder<'_> {
195 type Error = Error;
196 type Warning = CpuWarning;
197 type Pool = CpuScratchPool;
198}
199
200impl<'a> ImageDecode<'a> for Decoder<'a> {
201 type View = JpegView<'a>;
202
203 fn inspect(input: &'a [u8]) -> Result<j2k_core::Info, Self::Error> {
204 Ok(CpuDecoder::inspect(input)?.to_core_info())
205 }
206
207 fn parse(input: &'a [u8]) -> Result<Self::View, Self::Error> {
208 Ok(JpegView::parse(input)?)
209 }
210
211 fn from_view(view: Self::View) -> Result<Self, Self::Error> {
212 Ok(Self {
213 inner: CpuDecoder::from_view(view)?,
214 })
215 }
216
217 fn decode_into(
218 &mut self,
219 out: &mut [u8],
220 stride: usize,
221 fmt: PixelFormat,
222 ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
223 Ok(self.inner.decode_into(out, stride, fmt)?.into())
224 }
225
226 fn decode_into_with_scratch(
227 &mut self,
228 pool: &mut Self::Pool,
229 out: &mut [u8],
230 stride: usize,
231 fmt: PixelFormat,
232 ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
233 Ok(self
234 .inner
235 .decode_into_with_scratch(pool, out, stride, fmt)?
236 .into())
237 }
238
239 fn decode_region_into(
240 &mut self,
241 pool: &mut Self::Pool,
242 out: &mut [u8],
243 stride: usize,
244 fmt: PixelFormat,
245 roi: Rect,
246 ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
247 Ok(self
248 .inner
249 .decode_region_into_with_scratch(pool, out, stride, fmt, roi.into())?
250 .into())
251 }
252
253 fn decode_scaled_into(
254 &mut self,
255 pool: &mut Self::Pool,
256 out: &mut [u8],
257 stride: usize,
258 fmt: PixelFormat,
259 scale: Downscale,
260 ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
261 Ok(self
262 .inner
263 .decode_scaled_into_with_scratch(pool, out, stride, fmt, scale)?
264 .into())
265 }
266
267 fn decode_region_scaled_into(
268 &mut self,
269 pool: &mut Self::Pool,
270 out: &mut [u8],
271 stride: usize,
272 fmt: PixelFormat,
273 roi: Rect,
274 scale: Downscale,
275 ) -> Result<DecodeOutcome<Self::Warning>, Self::Error> {
276 Ok(self
277 .inner
278 .decode_region_scaled_into_with_scratch(pool, out, stride, fmt, roi.into(), scale)?
279 .into())
280 }
281}
282
283impl<'a> ImageDecodeDevice<'a> for Decoder<'a> {
284 type DeviceSurface = Surface;
285}
286
287impl<'a> ImageDecodeSubmit<'a> for Decoder<'a> {
288 type Session = CudaSession;
289 type DeviceSurface = Surface;
290 type SubmittedSurface = ReadySubmission<Surface, Error>;
291
292 fn submit_to_device(
293 &mut self,
294 session: &mut Self::Session,
295 fmt: PixelFormat,
296 backend: BackendRequest,
297 ) -> Result<Self::SubmittedSurface, Self::Error> {
298 validate_surface_request(backend)?;
299 Ok(submit_ready_device(session, |session| {
300 self.decode_to_surface_impl(session, fmt, backend)
301 }))
302 }
303
304 fn submit_region_to_device(
305 &mut self,
306 session: &mut Self::Session,
307 fmt: PixelFormat,
308 roi: Rect,
309 backend: BackendRequest,
310 ) -> Result<Self::SubmittedSurface, Self::Error> {
311 validate_surface_request(backend)?;
312 Ok(submit_ready_device(session, |session| {
313 self.decode_region_to_surface_impl(session, fmt, roi, backend)
314 }))
315 }
316
317 fn submit_scaled_to_device(
318 &mut self,
319 session: &mut Self::Session,
320 fmt: PixelFormat,
321 scale: Downscale,
322 backend: BackendRequest,
323 ) -> Result<Self::SubmittedSurface, Self::Error> {
324 validate_surface_request(backend)?;
325 Ok(submit_ready_device(session, |session| {
326 self.decode_scaled_to_surface_impl(session, fmt, scale, backend)
327 }))
328 }
329
330 fn submit_region_scaled_to_device(
331 &mut self,
332 session: &mut Self::Session,
333 fmt: PixelFormat,
334 roi: Rect,
335 scale: Downscale,
336 backend: BackendRequest,
337 ) -> Result<Self::SubmittedSurface, Self::Error> {
338 validate_surface_request(backend)?;
339 Ok(submit_ready_device(session, |session| {
340 self.decode_region_scaled_to_surface_impl(session, fmt, roi, scale, backend)
341 }))
342 }
343}