use { crate::{ cmm::cmm_description::{ColorDescription, LinearColorDescription}, gfx_api::{ AcquireSync, BufferResv, CopyTexture, FillRect, FramebufferRect, GfxApiOpt, GfxTexture, ReleaseSync, SampleRect, }, rect::Rect, scale::Scale, theme::Color, utils::transform_ext::TransformExt, }, jay_config::video::Transform, std::rc::Rc, }; pub struct RendererBase<'a> { pub ops: &'a mut Vec, 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(x1, y1, x2, y2).unwrap(); } 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, cd: &Rc, ) { self.fill_boxes3(boxes, color, alpha, cd, 0, 0, true); } pub fn fill_boxes(&mut self, boxes: &[Rect], color: &Color, cd: &Rc) { self.fill_boxes3(boxes, color, None, cd, 0, 0, false); } pub fn fill_boxes2( &mut self, boxes: &[Rect], color: &Color, cd: &Rc, dx: i32, dy: i32, ) { self.fill_boxes3(boxes, color, None, cd, dx, dy, false); } pub fn fill_boxes3( &mut self, boxes: &[Rect], color: &Color, alpha: Option, cd: &Rc, dx: i32, dy: i32, scaled: bool, ) { if boxes.is_empty() || *color == Color::TRANSPARENT { return; } let (dx, dy) = self.scale_point(dx, dy); for bx in boxes { let bx = match scaled { false => self.scale_rect(*bx), true => *bx, }; self.ops.push(GfxApiOpt::FillRect(FillRect { rect: FramebufferRect::new( (bx.x1() + dx) as f32, (bx.y1() + dy) as f32, (bx.x2() + dx) as f32, (bx.y2() + dy) as f32, self.transform, self.fb_width, self.fb_height, ), color: *color, alpha, cd: cd.clone(), })); } } pub fn fill_boxes_f( &mut self, boxes: &[(f32, f32, f32, f32)], color: &Color, cd: &Rc, ) { self.fill_boxes2_f(boxes, color, cd, 0.0, 0.0); } pub fn fill_boxes2_f( &mut self, boxes: &[(f32, f32, f32, f32)], color: &Color, cd: &Rc, 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, cd: cd.clone(), })); } } pub fn render_texture( &mut self, texture: &Rc, alpha: Option, x: i32, y: i32, tpoints: Option, tsize: Option<(i32, i32)>, tscale: Scale, bounds: Option<&Rect>, buffer_resv: Option>, acquire_sync: AcquireSync, release_sync: ReleaseSync, opaque: bool, cd: &Rc, ) { 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, cd: cd.clone(), })); } } #[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 }