1
0
Fork 0
forked from wry/wry
wry/src/renderer/renderer_base.rs
2026-05-03 05:49:42 -04:00

452 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,
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
}