445 lines
13 KiB
Rust
445 lines
13 KiB
Rust
use {
|
|
crate::{
|
|
cmm::{
|
|
cmm_description::{ColorDescription, LinearColorDescription},
|
|
cmm_render_intent::RenderIntent,
|
|
},
|
|
gfx_api::{
|
|
AcquireSync, AlphaMode, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt,
|
|
GfxTexture, ReleaseSync, RoundedCopyTexture, RoundedFillRect, SampleRect,
|
|
},
|
|
rect::Rect,
|
|
scale::Scale,
|
|
theme::{Color, CornerRadius},
|
|
tree::Transform,
|
|
},
|
|
std::rc::Rc,
|
|
};
|
|
|
|
pub struct RendererBase<'a> {
|
|
pub ops: &'a mut Vec<GfxApiOpt>,
|
|
pub scaled: bool,
|
|
pub scale: Scale,
|
|
pub scalef: f64,
|
|
pub transform: Transform,
|
|
pub fb_width: f32,
|
|
pub fb_height: f32,
|
|
}
|
|
|
|
impl RendererBase<'_> {
|
|
pub fn scale(&self) -> Scale {
|
|
self.scale
|
|
}
|
|
|
|
pub fn scale_point(&self, mut x: i32, mut y: i32) -> (i32, i32) {
|
|
if self.scaled {
|
|
[x, y] = self.scale.pixel_size([x, y]);
|
|
}
|
|
(x, y)
|
|
}
|
|
|
|
pub fn scale_point_f(&self, mut x: f32, mut y: f32) -> (f32, f32) {
|
|
if self.scaled {
|
|
x = (x as f64 * self.scalef) as _;
|
|
y = (y as f64 * self.scalef) as _;
|
|
}
|
|
(x, y)
|
|
}
|
|
|
|
pub fn scale_rect(&self, mut rect: Rect) -> Rect {
|
|
if self.scaled {
|
|
let [x1, y1, x2, y2] =
|
|
self.scale
|
|
.pixel_size([rect.x1(), rect.y1(), rect.x2(), rect.y2()]);
|
|
rect = Rect::new_saturating(x1, y1, x2, y2);
|
|
}
|
|
rect
|
|
}
|
|
|
|
pub fn scale_rect_f(&self, mut rect: (f32, f32, f32, f32)) -> (f32, f32, f32, f32) {
|
|
if self.scaled {
|
|
let x1 = (rect.0 as f64 * self.scalef).round() as _;
|
|
let y1 = (rect.1 as f64 * self.scalef).round() as _;
|
|
let x2 = (rect.2 as f64 * self.scalef).round() as _;
|
|
let y2 = (rect.3 as f64 * self.scalef).round() as _;
|
|
rect = (x1, y1, x2, y2)
|
|
}
|
|
rect
|
|
}
|
|
|
|
pub fn fill_scaled_boxes(
|
|
&mut self,
|
|
boxes: &[Rect],
|
|
color: &Color,
|
|
alpha: Option<f32>,
|
|
cd: &Rc<LinearColorDescription>,
|
|
render_intent: RenderIntent,
|
|
) {
|
|
self.fill_boxes3(boxes, color, alpha, cd, render_intent, 0, 0, true);
|
|
}
|
|
|
|
pub fn fill_boxes(
|
|
&mut self,
|
|
boxes: &[Rect],
|
|
color: &Color,
|
|
cd: &Rc<LinearColorDescription>,
|
|
render_intent: RenderIntent,
|
|
) {
|
|
self.fill_boxes3(boxes, color, None, cd, render_intent, 0, 0, false);
|
|
}
|
|
|
|
pub fn fill_boxes2(
|
|
&mut self,
|
|
boxes: &[Rect],
|
|
color: &Color,
|
|
cd: &Rc<LinearColorDescription>,
|
|
render_intent: RenderIntent,
|
|
dx: i32,
|
|
dy: i32,
|
|
) {
|
|
self.fill_boxes3(boxes, color, None, cd, render_intent, dx, dy, false);
|
|
}
|
|
|
|
fn fill_boxes3(
|
|
&mut self,
|
|
boxes: &[Rect],
|
|
color: &Color,
|
|
alpha: Option<f32>,
|
|
cd: &Rc<LinearColorDescription>,
|
|
render_intent: RenderIntent,
|
|
dx: i32,
|
|
dy: i32,
|
|
scaled: bool,
|
|
) {
|
|
if boxes.is_empty() || *color == Color::TRANSPARENT {
|
|
return;
|
|
}
|
|
for bx in boxes {
|
|
let bx = bx.move_(dx, dy);
|
|
let bx = match scaled {
|
|
false => self.scale_rect(bx),
|
|
true => bx,
|
|
};
|
|
self.ops.push(GfxApiOpt::FillRect(FillRect {
|
|
rect: FramebufferRect::new(
|
|
bx.x1() as f32,
|
|
bx.y1() as f32,
|
|
bx.x2() as f32,
|
|
bx.y2() as f32,
|
|
self.transform,
|
|
self.fb_width,
|
|
self.fb_height,
|
|
),
|
|
color: *color,
|
|
alpha,
|
|
render_intent,
|
|
cd: cd.clone(),
|
|
}));
|
|
}
|
|
}
|
|
|
|
pub fn fill_boxes_f(
|
|
&mut self,
|
|
boxes: &[(f32, f32, f32, f32)],
|
|
color: &Color,
|
|
cd: &Rc<LinearColorDescription>,
|
|
render_intent: RenderIntent,
|
|
) {
|
|
self.fill_boxes2_f(boxes, color, cd, render_intent, 0.0, 0.0);
|
|
}
|
|
|
|
pub fn fill_boxes2_f(
|
|
&mut self,
|
|
boxes: &[(f32, f32, f32, f32)],
|
|
color: &Color,
|
|
cd: &Rc<LinearColorDescription>,
|
|
render_intent: RenderIntent,
|
|
dx: f32,
|
|
dy: f32,
|
|
) {
|
|
if boxes.is_empty() || *color == Color::TRANSPARENT {
|
|
return;
|
|
}
|
|
let (dx, dy) = self.scale_point_f(dx, dy);
|
|
for bx in boxes {
|
|
let (x1, y1, x2, y2) = self.scale_rect_f(*bx);
|
|
self.ops.push(GfxApiOpt::FillRect(FillRect {
|
|
rect: FramebufferRect::new(
|
|
x1 + dx,
|
|
y1 + dy,
|
|
x2 + dx,
|
|
y2 + dy,
|
|
self.transform,
|
|
self.fb_width,
|
|
self.fb_height,
|
|
),
|
|
color: *color,
|
|
alpha: None,
|
|
render_intent,
|
|
cd: cd.clone(),
|
|
}));
|
|
}
|
|
}
|
|
|
|
pub fn render_texture(
|
|
&mut self,
|
|
texture: &Rc<dyn GfxTexture>,
|
|
alpha: Option<f32>,
|
|
x: i32,
|
|
y: i32,
|
|
tpoints: Option<SampleRect>,
|
|
tsize: Option<(i32, i32)>,
|
|
tscale: Scale,
|
|
bounds: Option<&Rect>,
|
|
buffer_resv: Option<Rc<dyn BufferResv>>,
|
|
acquire_sync: AcquireSync,
|
|
release_sync: ReleaseSync,
|
|
opaque: bool,
|
|
cd: &Rc<ColorDescription>,
|
|
render_intent: RenderIntent,
|
|
alpha_mode: AlphaMode,
|
|
) {
|
|
// log::info!("rendering texture {:?}", std::ptr::from_ref(&**texture) as *const u8);
|
|
// log::info!("{:?}", backtrace::Backtrace::new());
|
|
|
|
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
|
|
|
let (twidth, theight) = if let Some(size) = tsize {
|
|
size
|
|
} else {
|
|
let (mut w, mut h) = texcoord.buffer_transform.maybe_swap(texture.size());
|
|
if tscale != self.scale {
|
|
let tscale = tscale.to_f64();
|
|
w = (w as f64 * self.scalef / tscale).round() as _;
|
|
h = (h as f64 * self.scalef / tscale).round() as _;
|
|
}
|
|
(w, h)
|
|
};
|
|
|
|
let mut target_x = [x, x + twidth];
|
|
let mut target_y = [y, y + theight];
|
|
|
|
if let Some(bounds) = bounds
|
|
&& bound_target(&mut target_x, &mut target_y, &mut texcoord, bounds)
|
|
{
|
|
return;
|
|
}
|
|
|
|
let target = FramebufferRect::new(
|
|
target_x[0] as f32,
|
|
target_y[0] as f32,
|
|
target_x[1] as f32,
|
|
target_y[1] as f32,
|
|
self.transform,
|
|
self.fb_width,
|
|
self.fb_height,
|
|
);
|
|
|
|
self.ops.push(GfxApiOpt::CopyTexture(CopyTexture {
|
|
tex: texture.clone(),
|
|
source: texcoord,
|
|
target,
|
|
alpha,
|
|
buffer_resv,
|
|
acquire_sync,
|
|
release_sync,
|
|
opaque,
|
|
render_intent,
|
|
cd: cd.clone(),
|
|
alpha_mode,
|
|
}));
|
|
}
|
|
|
|
pub fn fill_rounded_rect(
|
|
&mut self,
|
|
rect: Rect,
|
|
color: &Color,
|
|
alpha: Option<f32>,
|
|
cd: &Rc<LinearColorDescription>,
|
|
render_intent: RenderIntent,
|
|
corner_radius: CornerRadius,
|
|
border_width: f32,
|
|
) {
|
|
self.fill_rounded_rect_z(rect, color, alpha, cd, render_intent, corner_radius, border_width, 0)
|
|
}
|
|
|
|
pub fn fill_rounded_rect_z(
|
|
&mut self,
|
|
rect: Rect,
|
|
color: &Color,
|
|
alpha: Option<f32>,
|
|
cd: &Rc<LinearColorDescription>,
|
|
render_intent: RenderIntent,
|
|
corner_radius: CornerRadius,
|
|
border_width: f32,
|
|
z_order: u32,
|
|
) {
|
|
if *color == Color::TRANSPARENT {
|
|
return;
|
|
}
|
|
let rect = self.scale_rect(rect);
|
|
let width = (rect.x2() - rect.x1()) as f32;
|
|
let height = (rect.y2() - rect.y1()) as f32;
|
|
let scale = self.scalef as f32;
|
|
let fitted = corner_radius.fit_to(width, height);
|
|
let cr: [f32; 4] = fitted.into();
|
|
self.ops
|
|
.push(GfxApiOpt::RoundedFillRect(RoundedFillRect {
|
|
rect: FramebufferRect::new(
|
|
rect.x1() as f32,
|
|
rect.y1() as f32,
|
|
rect.x2() as f32,
|
|
rect.y2() as f32,
|
|
self.transform,
|
|
self.fb_width,
|
|
self.fb_height,
|
|
),
|
|
color: *color,
|
|
alpha,
|
|
render_intent,
|
|
cd: cd.clone(),
|
|
size: [width, height],
|
|
corner_radius: cr,
|
|
border_width,
|
|
scale,
|
|
z_order,
|
|
}));
|
|
}
|
|
|
|
pub fn render_rounded_texture(
|
|
&mut self,
|
|
texture: &Rc<dyn GfxTexture>,
|
|
alpha: Option<f32>,
|
|
x: i32,
|
|
y: i32,
|
|
tpoints: Option<SampleRect>,
|
|
tsize: Option<(i32, i32)>,
|
|
tscale: Scale,
|
|
bounds: Option<&Rect>,
|
|
buffer_resv: Option<Rc<dyn BufferResv>>,
|
|
acquire_sync: AcquireSync,
|
|
release_sync: ReleaseSync,
|
|
cd: &Rc<ColorDescription>,
|
|
render_intent: RenderIntent,
|
|
alpha_mode: AlphaMode,
|
|
corner_radius: CornerRadius,
|
|
) {
|
|
let mut texcoord = tpoints.unwrap_or_else(SampleRect::identity);
|
|
|
|
let (twidth, theight) = if let Some(size) = tsize {
|
|
size
|
|
} else {
|
|
let (mut w, mut h) = texcoord.buffer_transform.maybe_swap(texture.size());
|
|
if tscale != self.scale {
|
|
let tscale = tscale.to_f64();
|
|
w = (w as f64 * self.scalef / tscale).round() as _;
|
|
h = (h as f64 * self.scalef / tscale).round() as _;
|
|
}
|
|
(w, h)
|
|
};
|
|
|
|
let mut target_x = [x, x + twidth];
|
|
let mut target_y = [y, y + theight];
|
|
|
|
if let Some(bounds) = bounds
|
|
&& bound_target(&mut target_x, &mut target_y, &mut texcoord, bounds)
|
|
{
|
|
return;
|
|
}
|
|
|
|
let target = FramebufferRect::new(
|
|
target_x[0] as f32,
|
|
target_y[0] as f32,
|
|
target_x[1] as f32,
|
|
target_y[1] as f32,
|
|
self.transform,
|
|
self.fb_width,
|
|
self.fb_height,
|
|
);
|
|
|
|
let width = (target_x[1] - target_x[0]) as f32;
|
|
let height = (target_y[1] - target_y[0]) as f32;
|
|
let scale = self.scalef as f32;
|
|
let fitted = corner_radius.fit_to(width, height);
|
|
let cr: [f32; 4] = fitted.into();
|
|
|
|
self.ops
|
|
.push(GfxApiOpt::RoundedCopyTexture(RoundedCopyTexture {
|
|
tex: texture.clone(),
|
|
source: texcoord,
|
|
target,
|
|
alpha,
|
|
buffer_resv,
|
|
acquire_sync,
|
|
release_sync,
|
|
opaque: false,
|
|
render_intent,
|
|
cd: cd.clone(),
|
|
alpha_mode,
|
|
size: [width, height],
|
|
corner_radius: cr,
|
|
scale,
|
|
}));
|
|
}
|
|
|
|
pub fn sync(&mut self) {
|
|
self.ops.push(GfxApiOpt::Sync);
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
fn bound_target(
|
|
target_x: &mut [i32; 2],
|
|
target_y: &mut [i32; 2],
|
|
texcoord: &mut SampleRect,
|
|
bounds: &Rect,
|
|
) -> bool {
|
|
let bounds_x = [bounds.x1(), bounds.x2()];
|
|
let bounds_y = [bounds.y1(), bounds.y2()];
|
|
|
|
if target_x[0] >= bounds_x[0]
|
|
&& target_x[1] <= bounds_x[1]
|
|
&& target_y[0] >= bounds_y[0]
|
|
&& target_y[1] <= bounds_y[1]
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#[cold]
|
|
fn cold() {}
|
|
cold();
|
|
|
|
let SampleRect {
|
|
x1: t_x1,
|
|
x2: t_x2,
|
|
y1: t_y1,
|
|
y2: t_y2,
|
|
..
|
|
} = texcoord;
|
|
|
|
macro_rules! clamp {
|
|
($desired:ident, $bounds:ident, $test_idx:expr, $test_cmp:ident, $test_cmp_eq:ident, $modify:ident, $keep:ident) => {{
|
|
let desired_test = $desired[$test_idx];
|
|
let desired_other = $desired[1 - $test_idx];
|
|
let bound = $bounds[$test_idx];
|
|
if desired_test.$test_cmp(&bound) {
|
|
cold();
|
|
if desired_other.$test_cmp_eq(&bound) {
|
|
return true;
|
|
}
|
|
let max = (desired_other - bound) as f32;
|
|
let desired = ($desired[1] - $desired[0]) as f32;
|
|
let factor = max.abs() / desired;
|
|
*$modify = *$keep + (*$modify - *$keep) * factor;
|
|
$desired[$test_idx] = bound;
|
|
}
|
|
}};
|
|
}
|
|
|
|
clamp!(target_x, bounds_x, 0, lt, le, t_x1, t_x2);
|
|
clamp!(target_x, bounds_x, 1, gt, ge, t_x2, t_x1);
|
|
clamp!(target_y, bounds_y, 0, lt, le, t_y1, t_y2);
|
|
clamp!(target_y, bounds_y, 1, gt, ge, t_y2, t_y1);
|
|
|
|
false
|
|
}
|