use std::{cell::Cell, cmp}; use super::{ color::Color, graphicspath::{GraphicsPath, PointType}, }; static FONT_ASSET: &[u8] = include_bytes!("../font/unifont.font"); /// 渲染模式: 混合/覆盖 #[derive(Clone, Copy, Debug)] pub enum RenderMode { /// 颜色混合 Blend, /// 颜色覆盖 Overwrite, } /// 可渲染对象需要实现的特性 pub trait Renderer { /// 获取渲染窗口宽度 fn width(&self) -> u32; /// 获取渲染窗口高度 fn height(&self) -> u32; /// 获取帧缓冲数据 fn data(&self) -> &[Color]; /// 获取可变帧缓存数据 fn data_mut(&mut self) -> &mut [Color]; /// 同步渲染数据 fn sync(&mut self) -> bool; /// 获取/设置渲染模式 fn mode(&self) -> &Cell; /// # 函数功能 /// 绘制指定位置的像素(左下角为原点) /// /// ## 参数 /// - x: 像素x坐标 /// - y: 像素y坐标 /// - color: 像素颜色值 fn pixel(&mut self, x: i32, y: i32, color: Color) { let replace = match self.mode().get() { RenderMode::Blend => false, RenderMode::Overwrite => true, }; let w = self.width(); let h = self.height(); let data = self.data_mut(); if x >= 0 && y >= 0 && x < w as i32 && y < h as i32 { let new_color = color.data; let alpha = (new_color >> 24) & 0xFF; let old_color = &mut data[y as usize * w as usize + x as usize].data; if alpha >= 255 || replace { *old_color = new_color; } // 颜色混合算法(效率更高的实现方法) else if alpha > 0 { let n_alpha = 255 - alpha; let rb = ((n_alpha * (*old_color & 0x00FF00FF)) + (alpha * (new_color & 0x00FF00FF))) >> 8; let ag = (n_alpha * ((*old_color & 0xFF00FF00) >> 8)) + (alpha * (0x01000000 | ((new_color & 0x0000FF00) >> 8))); *old_color = (rb & 0x00FF00FF) | (ag & 0xFF00FF00); } } } /// TODO 注释补充 fn arc(&mut self, x0: i32, y0: i32, radius: i32, parts: u8, color: Color) { let mut x = radius.abs(); let mut y = 0; let mut err = 0; // https://github.com/rust-lang/rust-clippy/issues/5354 while x >= y { if radius < 0 { if parts & 1 << 0 != 0 { self.rect(x0 - x, y0 + y, x as u32, 1, color); } if parts & 1 << 1 != 0 { self.rect(x0, y0 + y, x as u32 + 1, 1, color); } if parts & 1 << 2 != 0 { self.rect(x0 - y, y0 + x, y as u32, 1, color); } if parts & 1 << 3 != 0 { self.rect(x0, y0 + x, y as u32 + 1, 1, color); } if parts & 1 << 4 != 0 { self.rect(x0 - x, y0 - y, x as u32, 1, color); } if parts & 1 << 5 != 0 { self.rect(x0, y0 - y, x as u32 + 1, 1, color); } if parts & 1 << 6 != 0 { self.rect(x0 - y, y0 - x, y as u32, 1, color); } if parts & 1 << 7 != 0 { self.rect(x0, y0 - x, y as u32 + 1, 1, color); } } else if radius == 0 { self.pixel(x0, y0, color); } else { if parts & 1 << 0 != 0 { self.pixel(x0 - x, y0 + y, color); } if parts & 1 << 1 != 0 { self.pixel(x0 + x, y0 + y, color); } if parts & 1 << 2 != 0 { self.pixel(x0 - y, y0 + x, color); } if parts & 1 << 3 != 0 { self.pixel(x0 + y, y0 + x, color); } if parts & 1 << 4 != 0 { self.pixel(x0 - x, y0 - y, color); } if parts & 1 << 5 != 0 { self.pixel(x0 + x, y0 - y, color); } if parts & 1 << 6 != 0 { self.pixel(x0 - y, y0 - x, color); } if parts & 1 << 7 != 0 { self.pixel(x0 + y, y0 - x, color); } } y += 1; err += 1 + 2 * y; if 2 * (err - x) + 1 > 0 { x -= 1; err += 1 - 2 * x; } } } /// TODO 注释补充 fn circle(&mut self, x0: i32, y0: i32, radius: i32, color: Color) { let mut x = radius.abs(); let mut y = 0; let mut err = -radius.abs(); match radius { radius if radius > 0 => { err = 0; while x >= y { self.pixel(x0 - x, y0 + y, color); self.pixel(x0 + x, y0 + y, color); self.pixel(x0 - y, y0 + x, color); self.pixel(x0 + y, y0 + x, color); self.pixel(x0 - x, y0 - y, color); self.pixel(x0 + x, y0 - y, color); self.pixel(x0 - y, y0 - x, color); self.pixel(x0 + y, y0 - x, color); y += 1; err += 1 + 2 * y; if 2 * (err - x) + 1 > 0 { x -= 1; err += 1 - 2 * x; } } } radius if radius < 0 => { while x >= y { let lasty = y; err += y; y += 1; err += y; self.line4points(x0, y0, x, lasty, color); if err >= 0 { if x != lasty { self.line4points(x0, y0, lasty, x, color); } err -= x; x -= 1; err -= x; } } } _ => { self.pixel(x0, y0, color); } } } /// TODO 注释补充 fn line4points(&mut self, x0: i32, y0: i32, x: i32, y: i32, color: Color) { //self.line(x0 - x, y0 + y, (x+x0), y0 + y, color); self.rect(x0 - x, y0 + y, x as u32 * 2 + 1, 1, color); if y != 0 { //self.line(x0 - x, y0 - y, (x+x0), y0-y , color); self.rect(x0 - x, y0 - y, x as u32 * 2 + 1, 1, color); } } /// # 函数功能 /// 绘制指定颜色的一条线段 /// /// ## 参数 /// - argx1: 起点x坐标 /// - argy1: 起点y坐标 /// - argx2: 终点x坐标 /// - argy2: 终点y坐标 /// - color:绘制颜色 /// TODO fn line(&mut self, argx1: i32, argy1: i32, argx2: i32, argy2: i32, color: Color) { let mut x = argx1; let mut y = argy1; let dx = if argx1 > argx2 { argx1 - argx2 } else { argx2 - argx1 }; let dy = if argy1 > argy2 { argy1 - argy2 } else { argy2 - argy1 }; let sx = if argx1 < argx2 { 1 } else { -1 }; let sy = if argy1 < argy2 { 1 } else { -1 }; let mut err = if dx > dy { dx } else { -dy } / 2; let mut err_tolerance; loop { self.pixel(x, y, color); if x == argx2 && y == argy2 { break; }; err_tolerance = 2 * err; if err_tolerance > -dx { err -= dy; x += sx; } if err_tolerance < dy { err += dx; y += sy; } } } /// # 函数功能 /// 绘制指定颜色的若干线段(首尾相连) /// /// ## 参数 /// - points: 点集合 /// - color: 绘制颜色 fn lines(&mut self, points: &[[i32; 2]], color: Color) { if points.is_empty() { } else if points.len() == 1 { self.pixel(points[0][0], points[0][1], color); } else { for i in 0..points.len() - 1 { self.line( points[i][0], points[i][1], points[i + 1][0], points[i + 1][1], color, ); } } } /// # 函数功能 /// 绘制一条指定颜色的几何路径 /// /// ## 参数 /// - graphicspath: 几何路径 /// - color: 绘制颜色 fn draw_path(&mut self, graphicspath: GraphicsPath, color: Color) { let mut x: i32 = 0; let mut y: i32 = 0; for point in graphicspath.points { if let PointType::Connect = point.2 { self.line(x, y, point.0, point.1, color); } x = point.0; y = point.1; } } /// # 函数功能 /// 绘制单一颜色的矩形 /// /// ## 参数 /// - x: 起始x坐标 /// - y: 起始y坐标 /// - w: 矩形宽度 /// - h: 矩形高度 /// - color: 矩形颜色 fn rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) { let replace = match self.mode().get() { RenderMode::Blend => false, RenderMode::Overwrite => true, }; let self_w = self.width(); let self_h = self.height(); let start_y = cmp::max(0, cmp::min(self_h as i32 - 1, y)); let end_y = cmp::max(start_y, cmp::min(self_h as i32, y + h as i32)); let start_x = cmp::max(0, cmp::min(self_w as i32 - 1, x)); let end_x = cmp::max(start_x, cmp::min(self_w as i32, x + w as i32)); let len_x = end_x - start_x; let alpha = (color.data >> 24) & 0xFF; if alpha >= 255 || replace { let data = self.data_mut(); let data_ptr = data.as_mut_ptr(); for y in start_y..end_y { let start = (y * self_w as i32 + start_x) as isize; let end = start + len_x as isize; for i in start..end { unsafe { *data_ptr.offset(i) = color; } } } } else { for y in start_y..end_y { for x in start_x..end_x { self.pixel(x, y, color); } } } } /// # 函数功能 /// 将整个窗口填充单一颜色 /// /// ## 参数 /// - color: 窗口颜色 fn set(&mut self, color: Color) { let data = self.data_mut(); let data_ptr = data.as_mut_ptr(); for i in 0..data.len() as isize { unsafe { *data_ptr.offset(i) = color; } } } /// # 函数功能 /// 将整个窗口置黑 fn clear(&mut self) { self.set(Color::rgb(0, 0, 0)); } /// # 函数功能 /// 获取指定坐标的像素颜色 /// /// ## 参数 /// - x: x坐标 /// - y: y坐标 /// /// ## 返回值 /// 像素颜色 fn get_pixel(&self, x: i32, y: i32) -> Color { let p = (self.width() as i32 * y + x) as usize; if p >= self.data().len() { println!("[Error] Client window get pixel overflow!"); return Color::rgb(0, 0, 0); } return self.data()[p]; } /// # 函数功能 /// 在指定位置绘制字符 /// /// ## 参数 /// - x: x坐标 /// - y: y坐标 /// - c: 待绘制的字符 /// - color: 字符颜色 fn char(&mut self, x: i32, y: i32, c: char, color: Color) { let mut offset = (c as usize) * 16; for row in 0..16 { let row_data = if offset < FONT_ASSET.len() { FONT_ASSET[offset] } else { 0 }; for col in 0..8 { let pixel = (row_data >> (7 - col)) & 1; if pixel > 0 { self.pixel(x + col, y + row, color); } } offset += 1; } } /// # 函数功能 /// 在指定位置绘制一幅图像至帧缓冲区 /// /// ## 参数 /// - start_x: 起始x坐标(左上角) /// - start_y: 起始y坐标(左上角) /// - w: 图像宽度 /// - h: 图像高度 /// - data: 图像数据 fn image(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) { match self.mode().get() { RenderMode::Blend => self.image_fast(start_x, start_y, w, h, data), RenderMode::Overwrite => self.image_opaque(start_x, start_y, w, h, data), } } /// # 函数功能 /// 从指定行开始绘制一幅图像至帧缓冲区 /// /// ## 参数 /// - start: 起始行数 /// - image_data: 图像帧缓冲数据 fn image_over(&mut self, start: i32, image_data: &[Color]) { let start = start as usize * self.width() as usize; let window_data = self.data_mut(); let stop = cmp::min(start + image_data.len(), window_data.len()); let end = cmp::min(image_data.len(), window_data.len() - start); window_data[start..stop].copy_from_slice(&image_data[..end]); } ///Display an image using non transparent method /// TODO 注释补充 #[inline(always)] fn image_opaque(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, image_data: &[Color]) { let w = w as usize; let mut h = h as usize; let width = self.width() as usize; let height = self.height() as usize; let start_x = start_x as usize; let start_y = start_y as usize; //check boundaries if start_x >= width || start_y >= height { return; } if h + start_y > height { h = height - start_y; } let window_data = self.data_mut(); let offset = start_y * width + start_x; //copy image slices to window line by line for l in 0..h { let start = offset + l * width; let mut stop = start + w; let begin = l * w; let mut end = begin + w; //check boundaries if start_x + w > width { stop = (start_y + l + 1) * width - 1; end = begin + stop - start; } window_data[start..stop].copy_from_slice(&image_data[begin..end]); } } /// Speed improved, image can be outside of window boundary /// TODO 注释补充 #[inline(always)] fn image_fast(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, image_data: &[Color]) { let w = w as usize; let h = h as usize; let width = self.width() as usize; let start_x = start_x as usize; let start_y = start_y as usize; //simply return if image is outside of window if start_x >= width || start_y >= self.height() as usize { return; } let window_data = self.data_mut(); let offset = start_y * width + start_x; //copy image slices to window line by line for l in 0..h { let start = offset + l * width; let mut stop = start + w; let begin = l * w; let end = begin + w; //check boundaries if start_x + w > width { stop = (start_y + l + 1) * width; } let mut k = 0; for i in begin..end { if i < image_data.len() { let new = image_data[i].data; let alpha = (new >> 24) & 0xFF; if alpha > 0 && (start + k) < window_data.len() && (start + k) < stop { let old = &mut window_data[start + k].data; if alpha >= 255 { *old = new; } else { let n_alpha = 255 - alpha; let rb = ((n_alpha * (*old & 0x00FF00FF)) + (alpha * (new & 0x00FF00FF))) >> 8; let ag = (n_alpha * ((*old & 0xFF00FF00) >> 8)) + (alpha * (0x01000000 | ((new & 0x0000FF00) >> 8))); *old = (rb & 0x00FF00FF) | (ag & 0xFF00FF00); } } k += 1; } } } } }