#[cfg(target_os = "macos")]
use std::sync::Arc;
use j2k::{
DeviceDecodePlan, DeviceDecodeRequest, J2kDecoder as CpuDecoder,
J2kScratchPool as CpuJ2kScratchPool, J2kView,
};
use j2k_core::BackendRequest;
#[cfg(target_os = "macos")]
use j2k_native::{
DecoderContext as NativeDecoderContext, Image as NativeImage, J2kDirectColorPlan,
J2kDirectGrayscalePlan,
};
use super::request::{
surface_with_report, DecodeSurfaceWithReport, MetalDecodeOp, MetalDecodeRequest,
};
#[cfg(target_os = "macos")]
use super::surface::{allocate_cpu_surface, upload_surface_to_metal_with_device};
use crate::{routing, Error, MetalBackendSession, Surface};
pub struct J2kDecoder<'a> {
#[cfg(target_os = "macos")]
pub(super) bytes: &'a [u8],
pub(crate) inner: CpuDecoder<'a>,
pub(super) pool: CpuJ2kScratchPool,
#[cfg(target_os = "macos")]
pub(super) native_image: Option<NativeImage<'a>>,
#[cfg(target_os = "macos")]
pub(super) native_context: NativeDecoderContext<'a>,
#[cfg(target_os = "macos")]
pub(super) native_direct_gray_plan: Option<Arc<J2kDirectGrayscalePlan>>,
#[cfg(target_os = "macos")]
pub(super) native_prepared_direct_gray_plan:
Option<Arc<crate::compute::PreparedDirectGrayscalePlan>>,
#[cfg(target_os = "macos")]
pub(super) native_prepared_direct_gray_device_registry_id: Option<u64>,
#[cfg(target_os = "macos")]
pub(super) native_direct_color_plan: Option<Arc<J2kDirectColorPlan>>,
#[cfg(target_os = "macos")]
pub(super) native_prepared_direct_color_plan:
Option<Arc<crate::compute::PreparedDirectColorPlan>>,
#[cfg(target_os = "macos")]
pub(super) native_prepared_direct_color_device_registry_id: Option<u64>,
}
impl<'a> J2kDecoder<'a> {
pub fn new(input: &'a [u8]) -> Result<Self, Error> {
Ok(Self {
#[cfg(target_os = "macos")]
bytes: input,
inner: CpuDecoder::new(input)?,
pool: CpuJ2kScratchPool::new(),
#[cfg(target_os = "macos")]
native_image: None,
#[cfg(target_os = "macos")]
native_context: NativeDecoderContext::default(),
#[cfg(target_os = "macos")]
native_direct_gray_plan: None,
#[cfg(target_os = "macos")]
native_prepared_direct_gray_plan: None,
#[cfg(target_os = "macos")]
native_prepared_direct_gray_device_registry_id: None,
#[cfg(target_os = "macos")]
native_direct_color_plan: None,
#[cfg(target_os = "macos")]
native_prepared_direct_color_plan: None,
#[cfg(target_os = "macos")]
native_prepared_direct_color_device_registry_id: None,
})
}
pub fn from_view(view: J2kView<'a>) -> Result<Self, Error> {
#[cfg(target_os = "macos")]
let bytes = view.bytes();
Ok(Self {
#[cfg(target_os = "macos")]
bytes,
inner: CpuDecoder::from_view(view)?,
pool: CpuJ2kScratchPool::new(),
#[cfg(target_os = "macos")]
native_image: None,
#[cfg(target_os = "macos")]
native_context: NativeDecoderContext::default(),
#[cfg(target_os = "macos")]
native_direct_gray_plan: None,
#[cfg(target_os = "macos")]
native_prepared_direct_gray_plan: None,
#[cfg(target_os = "macos")]
native_prepared_direct_gray_device_registry_id: None,
#[cfg(target_os = "macos")]
native_direct_color_plan: None,
#[cfg(target_os = "macos")]
native_prepared_direct_color_plan: None,
#[cfg(target_os = "macos")]
native_prepared_direct_color_device_registry_id: None,
})
}
pub fn inner(&self) -> &CpuDecoder<'a> {
&self.inner
}
pub fn decode_request_to_device(
&mut self,
request: MetalDecodeRequest,
) -> Result<Surface, Error> {
match request.op {
MetalDecodeOp::Full => self.decode_to_surface_impl(request.fmt, request.backend),
MetalDecodeOp::Region(roi) => {
self.decode_region_to_surface_impl(request.fmt, roi, request.backend)
}
MetalDecodeOp::Scaled(scale) => {
self.decode_scaled_to_surface_impl(request.fmt, scale, request.backend)
}
MetalDecodeOp::RegionScaled { roi, scale } => {
self.decode_region_scaled_to_surface_impl(request.fmt, roi, scale, request.backend)
}
}
}
#[doc(hidden)]
pub fn decode_request_to_device_with_report(
&mut self,
request: MetalDecodeRequest,
) -> Result<DecodeSurfaceWithReport, Error> {
let surface = self.decode_request_to_device(request)?;
Ok(surface_with_report(
surface,
request.op.report_operation(),
request.backend,
request.fmt,
))
}
pub fn decode_request_to_device_with_session(
&mut self,
request: MetalDecodeRequest,
session: &MetalBackendSession,
) -> Result<Surface, Error> {
if request.backend != BackendRequest::Metal {
return self.decode_request_to_device(request);
}
if let Some(error) =
routing::decision_error(routing::decide_route(BackendRequest::Metal, request.fmt))
{
return Err(error);
}
#[cfg(target_os = "macos")]
{
match request.op {
MetalDecodeOp::Full => crate::compute::with_runtime_for_session(session, |_| {
if let Some(surface) =
self.decode_direct_to_surface_with_session(request.fmt, session)?
{
Ok(surface)
} else {
self.decode_full_to_metal_surface_with_device(
request.fmt,
session.device_handle(),
)
}
}),
MetalDecodeOp::Region(roi) => {
let plan = DeviceDecodePlan::for_image(
self.inner.info().dimensions,
DeviceDecodeRequest::Region { roi },
)?;
crate::compute::with_runtime_for_session(session, |_| {
self.decode_region_to_metal_surface_with_device(
request.fmt,
plan,
session.device_handle(),
)
})
}
MetalDecodeOp::Scaled(scale) => {
let plan = DeviceDecodePlan::for_image(
self.inner.info().dimensions,
DeviceDecodeRequest::Scaled { scale },
)?;
crate::compute::with_runtime_for_session(session, |_| {
self.decode_scaled_to_metal_surface_with_device(
request.fmt,
scale,
plan,
session.device_handle(),
)
})
}
MetalDecodeOp::RegionScaled { roi, scale } => {
let plan = DeviceDecodePlan::for_image(
self.inner.info().dimensions,
DeviceDecodeRequest::RegionScaled { roi, scale },
)?;
self.decode_region_scaled_to_metal_surface_with_session(
request.fmt,
roi,
scale,
plan,
session,
)
}
}
}
#[cfg(not(target_os = "macos"))]
{
let _ = session;
Err(Error::MetalUnavailable)
}
}
pub fn decode_request_to_host_surface(
&mut self,
request: MetalDecodeRequest,
) -> Result<Surface, Error> {
match request.op {
MetalDecodeOp::Full => self.decode_to_cpu_surface(request.fmt),
MetalDecodeOp::Region(roi) => {
let plan = DeviceDecodePlan::for_image(
self.inner.info().dimensions,
DeviceDecodeRequest::Region { roi },
)?;
self.decode_region_to_cpu_surface(request.fmt, plan)
}
MetalDecodeOp::Scaled(scale) => {
let plan = DeviceDecodePlan::for_image(
self.inner.info().dimensions,
DeviceDecodeRequest::Scaled { scale },
)?;
self.decode_scaled_to_cpu_surface(request.fmt, scale, plan)
}
MetalDecodeOp::RegionScaled { roi, scale } => {
let plan = DeviceDecodePlan::for_image(
self.inner.info().dimensions,
DeviceDecodeRequest::RegionScaled { roi, scale },
)?;
self.decode_region_scaled_to_cpu_surface(request.fmt, roi, scale, plan)
}
}
}
pub fn decode_request_to_cpu_staged_metal_surface_with_session(
&mut self,
request: MetalDecodeRequest,
session: &MetalBackendSession,
) -> Result<Surface, Error> {
#[cfg(target_os = "macos")]
{
match request.op {
MetalDecodeOp::Full => {
let dims = self.inner.info().dimensions;
let (mut out, stride) = allocate_cpu_surface(dims, request.fmt)?;
self.inner.decode_into_with_scratch(
&mut self.pool,
&mut out,
stride,
request.fmt,
)?;
upload_surface_to_metal_with_device(&out, dims, request.fmt, session.device())
}
MetalDecodeOp::Region(roi) => {
let plan = DeviceDecodePlan::for_image(
self.inner.info().dimensions,
DeviceDecodeRequest::Region { roi },
)?;
let dims = plan.output_dims();
let (mut out, stride) = allocate_cpu_surface(dims, request.fmt)?;
self.inner.decode_region_into(
&mut self.pool,
&mut out,
stride,
request.fmt,
plan.source_rect(),
)?;
upload_surface_to_metal_with_device(&out, dims, request.fmt, session.device())
}
MetalDecodeOp::Scaled(scale) => {
let plan = DeviceDecodePlan::for_image(
self.inner.info().dimensions,
DeviceDecodeRequest::Scaled { scale },
)?;
let dims = plan.output_dims();
let (mut out, stride) = allocate_cpu_surface(dims, request.fmt)?;
self.inner.decode_scaled_into(
&mut self.pool,
&mut out,
stride,
request.fmt,
scale,
)?;
upload_surface_to_metal_with_device(&out, dims, request.fmt, session.device())
}
MetalDecodeOp::RegionScaled { roi, scale } => {
let plan = DeviceDecodePlan::for_image(
self.inner.info().dimensions,
DeviceDecodeRequest::RegionScaled { roi, scale },
)?;
let dims = plan.output_dims();
let (mut out, stride) = allocate_cpu_surface(dims, request.fmt)?;
self.inner.decode_region_scaled_into(
&mut self.pool,
&mut out,
stride,
request.fmt,
roi,
scale,
)?;
upload_surface_to_metal_with_device(&out, dims, request.fmt, session.device())
}
}
}
#[cfg(not(target_os = "macos"))]
{
let _ = (request, session);
Err(Error::MetalUnavailable)
}
}
}