use drmkit_planes::{Layer as PlaneLayer, PropTag};
use crate::display::{DisplayParams, to_16_16};
use crate::source::{BindingModel, SourceFormat};
#[derive(Debug, Clone, Copy)]
#[allow(
clippy::exhaustive_structs,
reason = "built by callers, with no default to fill a new field from"
)]
pub struct LoweringInput {
pub display: DisplayParams,
pub format: SourceFormat,
pub binding: BindingModel,
pub fb_id: u32,
pub crtc_id: u32,
pub default_zpos: Option<u64>,
}
pub fn lower_layer(input: &LoweringInput, dst: &mut PlaneLayer) {
let display = &input.display;
let format = input.format;
let driver_owns_binding = input.binding == BindingModel::DriverOwnsBinding;
dst.set_externally_bound(driver_owns_binding);
if !driver_owns_binding {
dst.set_property(PropTag::FbId, u64::from(input.fb_id));
}
dst.set_property(PropTag::CrtcId, u64::from(input.crtc_id));
let mut source = if let Some(fixed) = display.src_rect_fixed {
let extent = |value: u32, full: u32| {
if value == 0 {
to_16_16(full)
} else {
u64::from(value)
}
};
Source {
x: u64::from(fixed.x),
y: u64::from(fixed.y),
w: extent(fixed.w, format.width),
h: extent(fixed.h, format.height),
}
} else {
let src_w = if display.src_rect.w == 0 {
format.width
} else {
display.src_rect.w
};
let src_h = if display.src_rect.h == 0 {
format.height
} else {
display.src_rect.h
};
Source {
x: to_16_16(display.src_rect.x.cast_unsigned()),
y: to_16_16(display.src_rect.y.cast_unsigned()),
w: to_16_16(src_w),
h: to_16_16(src_h),
}
};
let mut dst_x = display.dst_rect.x;
let mut dst_y = display.dst_rect.y;
let mut dst_w = display.dst_rect.w;
let mut dst_h = display.dst_rect.h;
if clips_cleanly(display.rotation, format.fourcc) {
(dst_x, dst_w, source.x, source.w) = clip_leading(dst_x, dst_w, source.x, source.w);
(dst_y, dst_h, source.y, source.h) = clip_leading(dst_y, dst_h, source.y, source.h);
}
dst.set_property(PropTag::CrtcX, i64::from(dst_x).cast_unsigned());
dst.set_property(PropTag::CrtcY, i64::from(dst_y).cast_unsigned());
dst.set_property(PropTag::CrtcW, u64::from(dst_w));
dst.set_property(PropTag::CrtcH, u64::from(dst_h));
dst.set_property(PropTag::SrcX, source.x);
dst.set_property(PropTag::SrcY, source.y);
dst.set_property(PropTag::SrcW, source.w);
dst.set_property(PropTag::SrcH, source.h);
dst.set_property(PropTag::PixelFormat, u64::from(format.fourcc));
dst.set_property(PropTag::FbModifier, format.modifier);
if display.rotation != 0 {
dst.set_property(PropTag::Rotation, display.rotation);
}
if let Some(zpos) = display.zpos.or(input.default_zpos) {
dst.set_property(PropTag::Zpos, zpos);
}
if let Some(alpha) = display.alpha {
dst.set_property(PropTag::Alpha, u64::from(alpha));
}
}
#[derive(Debug, Clone, Copy)]
struct Source {
x: u64,
y: u64,
w: u64,
h: u64,
}
pub(crate) const fn clips_cleanly(rotation: u64, fourcc: u32) -> bool {
const ROTATE_0: u64 = 1;
(rotation == 0 || rotation == ROTATE_0) && !chroma_subsampled(fourcc)
}
const fn chroma_subsampled(fourcc: u32) -> bool {
use drmkit_fmt::fourcc::{
NV12, NV15, NV16, NV20, NV21, NV61, P010, P012, P016, P210, UYVY, VYUY, Y210, Y212, Y216,
YUV410, YUV411, YUV420, YUV422, YUYV, YVU410, YVU411, YVU420, YVU422, YVYU,
};
matches!(
fourcc,
NV12 | NV21
| NV16
| NV61
| NV15
| NV20
| P010
| P012
| P016
| P210
| YUV410
| YVU410
| YUV411
| YVU411
| YUV420
| YVU420
| YUV422
| YVU422
| YUYV
| YVYU
| UYVY
| VYUY
| Y210
| Y212
| Y216
)
}
pub(crate) fn clip_leading(
dst_pos: i32,
dst_len: u32,
src_pos: u64,
src_len: u64,
) -> (i32, u32, u64, u64) {
let Ok(clip) = u32::try_from(-i64::from(dst_pos)) else {
return (dst_pos, dst_len, src_pos, src_len);
};
if clip == 0 || clip >= dst_len || src_len > u64::from(u32::MAX) {
return (dst_pos, dst_len, src_pos, src_len);
}
let kept = dst_len - clip;
let product = src_len * u64::from(kept);
let dst_len_wide = u64::from(dst_len);
let new_src_len = if src_len < dst_len_wide << 16 {
product.div_ceil(dst_len_wide)
} else {
product / dst_len_wide
};
(0, kept, src_pos + (src_len - new_src_len), new_src_len)
}