xref: /StarryEngine/starry_client/src/base/renderer.rs (revision 282ef85ca13266353a8fb594bdc60918a2715518)
1 use std::{cell::Cell, cmp};
2 
3 use super::{
4     color::Color,
5     graphicspath::{GraphicsPath, PointType},
6 };
7 
8 static FONT_ASSET: &[u8] = include_bytes!("../font/unifont.font");
9 
10 /// 渲染模式: 混合/覆盖
11 #[derive(Clone, Copy, Debug)]
12 pub enum RenderMode {
13     /// 颜色混合
14     Blend,
15     /// 颜色覆盖
16     Overwrite,
17 }
18 
19 /// 可渲染对象需要实现的特性
20 pub trait Renderer {
21     /// 获取渲染窗口宽度
22     fn width(&self) -> u32;
23 
24     /// 获取渲染窗口高度
25     fn height(&self) -> u32;
26 
27     /// 获取帧缓冲数据
28     fn data(&self) -> &[Color];
29 
30     /// 获取可变帧缓存数据
31     fn data_mut(&mut self) -> &mut [Color];
32 
33     /// 同步渲染数据
34     fn sync(&mut self) -> bool;
35 
36     /// 获取/设置渲染模式
37     fn mode(&self) -> &Cell<RenderMode>;
38 
39     /// # 函数功能
40     /// 绘制指定位置的像素(左下角为原点)
41     ///
42     /// ## 参数
43     /// - x: 像素x坐标
44     /// - y: 像素y坐标
45     /// - color: 像素颜色值
46     fn pixel(&mut self, x: i32, y: i32, color: Color) {
47         let replace = match self.mode().get() {
48             RenderMode::Blend => false,
49             RenderMode::Overwrite => true,
50         };
51         let w = self.width();
52         let h = self.height();
53         let data = self.data_mut();
54 
55         if x >= 0 && y >= 0 && x < w as i32 && y < h as i32 {
56             let new_color = color.data;
57             let alpha = (new_color >> 24) & 0xFF;
58             let old_color = &mut data[y as usize * w as usize + x as usize].data;
59 
60             if alpha >= 255 || replace {
61                 *old_color = new_color;
62             }
63             // 颜色混合算法(效率更高的实现方法)
64             else if alpha > 0 {
65                 let n_alpha = 255 - alpha;
66                 let rb = ((n_alpha * (*old_color & 0x00FF00FF))
67                     + (alpha * (new_color & 0x00FF00FF)))
68                     >> 8;
69                 let ag = (n_alpha * ((*old_color & 0xFF00FF00) >> 8))
70                     + (alpha * (0x01000000 | ((new_color & 0x0000FF00) >> 8)));
71 
72                 *old_color = (rb & 0x00FF00FF) | (ag & 0xFF00FF00);
73             }
74         }
75     }
76 
77     /// TODO 注释补充
78     fn arc(&mut self, x0: i32, y0: i32, radius: i32, parts: u8, color: Color) {
79         let mut x = radius.abs();
80         let mut y = 0;
81         let mut err = 0;
82 
83         // https://github.com/rust-lang/rust-clippy/issues/5354
84         while x >= y {
85             if radius < 0 {
86                 if parts & 1 << 0 != 0 {
87                     self.rect(x0 - x, y0 + y, x as u32, 1, color);
88                 }
89                 if parts & 1 << 1 != 0 {
90                     self.rect(x0, y0 + y, x as u32 + 1, 1, color);
91                 }
92                 if parts & 1 << 2 != 0 {
93                     self.rect(x0 - y, y0 + x, y as u32, 1, color);
94                 }
95                 if parts & 1 << 3 != 0 {
96                     self.rect(x0, y0 + x, y as u32 + 1, 1, color);
97                 }
98                 if parts & 1 << 4 != 0 {
99                     self.rect(x0 - x, y0 - y, x as u32, 1, color);
100                 }
101                 if parts & 1 << 5 != 0 {
102                     self.rect(x0, y0 - y, x as u32 + 1, 1, color);
103                 }
104                 if parts & 1 << 6 != 0 {
105                     self.rect(x0 - y, y0 - x, y as u32, 1, color);
106                 }
107                 if parts & 1 << 7 != 0 {
108                     self.rect(x0, y0 - x, y as u32 + 1, 1, color);
109                 }
110             } else if radius == 0 {
111                 self.pixel(x0, y0, color);
112             } else {
113                 if parts & 1 << 0 != 0 {
114                     self.pixel(x0 - x, y0 + y, color);
115                 }
116                 if parts & 1 << 1 != 0 {
117                     self.pixel(x0 + x, y0 + y, color);
118                 }
119                 if parts & 1 << 2 != 0 {
120                     self.pixel(x0 - y, y0 + x, color);
121                 }
122                 if parts & 1 << 3 != 0 {
123                     self.pixel(x0 + y, y0 + x, color);
124                 }
125                 if parts & 1 << 4 != 0 {
126                     self.pixel(x0 - x, y0 - y, color);
127                 }
128                 if parts & 1 << 5 != 0 {
129                     self.pixel(x0 + x, y0 - y, color);
130                 }
131                 if parts & 1 << 6 != 0 {
132                     self.pixel(x0 - y, y0 - x, color);
133                 }
134                 if parts & 1 << 7 != 0 {
135                     self.pixel(x0 + y, y0 - x, color);
136                 }
137             }
138 
139             y += 1;
140             err += 1 + 2 * y;
141             if 2 * (err - x) + 1 > 0 {
142                 x -= 1;
143                 err += 1 - 2 * x;
144             }
145         }
146     }
147 
148     /// TODO 注释补充
149     fn circle(&mut self, x0: i32, y0: i32, radius: i32, color: Color) {
150         let mut x = radius.abs();
151         let mut y = 0;
152         let mut err = -radius.abs();
153 
154         match radius {
155             radius if radius > 0 => {
156                 err = 0;
157                 while x >= y {
158                     self.pixel(x0 - x, y0 + y, color);
159                     self.pixel(x0 + x, y0 + y, color);
160                     self.pixel(x0 - y, y0 + x, color);
161                     self.pixel(x0 + y, y0 + x, color);
162                     self.pixel(x0 - x, y0 - y, color);
163                     self.pixel(x0 + x, y0 - y, color);
164                     self.pixel(x0 - y, y0 - x, color);
165                     self.pixel(x0 + y, y0 - x, color);
166 
167                     y += 1;
168                     err += 1 + 2 * y;
169                     if 2 * (err - x) + 1 > 0 {
170                         x -= 1;
171                         err += 1 - 2 * x;
172                     }
173                 }
174             }
175 
176             radius if radius < 0 => {
177                 while x >= y {
178                     let lasty = y;
179                     err += y;
180                     y += 1;
181                     err += y;
182                     self.line4points(x0, y0, x, lasty, color);
183                     if err >= 0 {
184                         if x != lasty {
185                             self.line4points(x0, y0, lasty, x, color);
186                         }
187                         err -= x;
188                         x -= 1;
189                         err -= x;
190                     }
191                 }
192             }
193             _ => {
194                 self.pixel(x0, y0, color);
195             }
196         }
197     }
198 
199     /// TODO 注释补充
200     fn line4points(&mut self, x0: i32, y0: i32, x: i32, y: i32, color: Color) {
201         //self.line(x0 - x, y0 + y, (x+x0), y0 + y, color);
202         self.rect(x0 - x, y0 + y, x as u32 * 2 + 1, 1, color);
203         if y != 0 {
204             //self.line(x0 - x, y0 - y, (x+x0), y0-y , color);
205             self.rect(x0 - x, y0 - y, x as u32 * 2 + 1, 1, color);
206         }
207     }
208 
209     /// # 函数功能
210     /// 绘制指定颜色的一条线段
211     ///
212     /// ## 参数
213     /// - argx1: 起点x坐标
214     /// - argy1: 起点y坐标
215     /// - argx2: 终点x坐标
216     /// - argy2: 终点y坐标
217     /// - color:绘制颜色
218     /// TODO
219     fn line(&mut self, argx1: i32, argy1: i32, argx2: i32, argy2: i32, color: Color) {
220         let mut x = argx1;
221         let mut y = argy1;
222 
223         let dx = if argx1 > argx2 {
224             argx1 - argx2
225         } else {
226             argx2 - argx1
227         };
228         let dy = if argy1 > argy2 {
229             argy1 - argy2
230         } else {
231             argy2 - argy1
232         };
233 
234         let sx = if argx1 < argx2 { 1 } else { -1 };
235         let sy = if argy1 < argy2 { 1 } else { -1 };
236 
237         let mut err = if dx > dy { dx } else { -dy } / 2;
238         let mut err_tolerance;
239 
240         loop {
241             self.pixel(x, y, color);
242 
243             if x == argx2 && y == argy2 {
244                 break;
245             };
246 
247             err_tolerance = 2 * err;
248 
249             if err_tolerance > -dx {
250                 err -= dy;
251                 x += sx;
252             }
253             if err_tolerance < dy {
254                 err += dx;
255                 y += sy;
256             }
257         }
258     }
259 
260     /// # 函数功能
261     /// 绘制指定颜色的若干线段(首尾相连)
262     ///
263     /// ## 参数
264     /// - points: 点集合
265     /// - color: 绘制颜色
266     fn lines(&mut self, points: &[[i32; 2]], color: Color) {
267         if points.is_empty() {
268         } else if points.len() == 1 {
269             self.pixel(points[0][0], points[0][1], color);
270         } else {
271             for i in 0..points.len() - 1 {
272                 self.line(
273                     points[i][0],
274                     points[i][1],
275                     points[i + 1][0],
276                     points[i + 1][1],
277                     color,
278                 );
279             }
280         }
281     }
282 
283     /// # 函数功能
284     /// 绘制一条指定颜色的几何路径
285     ///
286     /// ## 参数
287     /// - graphicspath: 几何路径
288     /// - color: 绘制颜色
289     fn draw_path(&mut self, graphicspath: GraphicsPath, color: Color) {
290         let mut x: i32 = 0;
291         let mut y: i32 = 0;
292 
293         for point in graphicspath.points {
294             if let PointType::Connect = point.2 {
295                 self.line(x, y, point.0, point.1, color);
296             }
297             x = point.0;
298             y = point.1;
299         }
300     }
301 
302     /// # 函数功能
303     /// 绘制单一颜色的矩形
304     ///
305     /// ## 参数
306     /// - x: 起始x坐标
307     /// - y: 起始y坐标
308     /// - w: 矩形宽度
309     /// - h: 矩形高度
310     /// - color: 矩形颜色
311     fn rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
312         let replace = match self.mode().get() {
313             RenderMode::Blend => false,
314             RenderMode::Overwrite => true,
315         };
316         let self_w = self.width();
317         let self_h = self.height();
318 
319         let start_y = cmp::max(0, cmp::min(self_h as i32 - 1, y));
320         let end_y = cmp::max(start_y, cmp::min(self_h as i32, y + h as i32));
321 
322         let start_x = cmp::max(0, cmp::min(self_w as i32 - 1, x));
323         let end_x = cmp::max(start_x, cmp::min(self_w as i32, x + w as i32));
324         let len_x = end_x - start_x;
325 
326         let alpha = (color.data >> 24) & 0xFF;
327 
328         if alpha >= 255 || replace {
329             let data = self.data_mut();
330             let data_ptr = data.as_mut_ptr();
331             for y in start_y..end_y {
332                 let start = (y * self_w as i32 + start_x) as isize;
333                 let end = start + len_x as isize;
334                 for i in start..end {
335                     unsafe {
336                         *data_ptr.offset(i) = color;
337                     }
338                 }
339             }
340         } else {
341             for y in start_y..end_y {
342                 for x in start_x..end_x {
343                     self.pixel(x, y, color);
344                 }
345             }
346         }
347     }
348 
349     /// # 函数功能
350     /// 将整个窗口填充单一颜色
351     ///
352     /// ## 参数
353     /// - color: 窗口颜色
354     fn set(&mut self, color: Color) {
355         let data = self.data_mut();
356         let data_ptr = data.as_mut_ptr();
357         for i in 0..data.len() as isize {
358             unsafe {
359                 *data_ptr.offset(i) = color;
360             }
361         }
362     }
363 
364     /// # 函数功能
365     /// 将整个窗口置黑
366     fn clear(&mut self) {
367         self.set(Color::rgb(0, 0, 0));
368     }
369 
370     /// # 函数功能
371     /// 获取指定坐标的像素颜色
372     ///
373     /// ## 参数
374     /// - x: x坐标
375     /// - y: y坐标
376     ///
377     /// ## 返回值
378     /// 像素颜色
379     fn get_pixel(&self, x: i32, y: i32) -> Color {
380         let p = (self.width() as i32 * y + x) as usize;
381         if p >= self.data().len() {
382             println!("[Error] Client window get pixel overflow!");
383             return Color::rgb(0, 0, 0);
384         }
385         return self.data()[p];
386     }
387 
388     /// # 函数功能
389     /// 在指定位置绘制字符
390     ///
391     /// ## 参数
392     /// - x: x坐标(局部坐标)
393     /// - y: y坐标(局部坐标)
394     /// - c: 待绘制的字符
395     /// - color: 字符颜色
396     fn char(&mut self, x: i32, y: i32, c: char, color: Color) {
397         let mut offset = (c as usize) * 16;
398         for row in 0..16 {
399             let row_data = if offset < FONT_ASSET.len() {
400                 FONT_ASSET[offset]
401             } else {
402                 0
403             };
404 
405             for col in 0..8 {
406                 let pixel = (row_data >> (7 - col)) & 1;
407                 if pixel > 0 {
408                     self.pixel(x + col, y + row, color);
409                 }
410             }
411             offset += 1;
412         }
413     }
414 
415     /// # 函数功能
416     /// 在指定位置绘制一幅图像至帧缓冲区
417     ///
418     /// ## 参数
419     /// - start_x: 起始x坐标(局部坐标)
420     /// - start_y: 起始y坐标(局部坐标)
421     /// - w: 图像宽度
422     /// - h: 图像高度
423     /// - data: 图像数据
424     fn image(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) {
425         match self.mode().get() {
426             RenderMode::Blend => self.image_fast(start_x, start_y, w, h, data),
427             RenderMode::Overwrite => self.image_opaque(start_x, start_y, w, h, data),
428         }
429     }
430 
431     /// # 函数功能
432     /// 从指定行开始绘制一幅图像至帧缓冲区
433     ///
434     /// ## 参数
435     /// - start: 起始行数
436     /// - image_data: 图像帧缓冲数据
437     fn image_over(&mut self, start: i32, image_data: &[Color]) {
438         let start = start as usize * self.width() as usize;
439         let window_data = self.data_mut();
440         let stop = cmp::min(start + image_data.len(), window_data.len());
441         let end = cmp::min(image_data.len(), window_data.len() - start);
442 
443         window_data[start..stop].copy_from_slice(&image_data[..end]);
444     }
445 
446     ///Display an image using non transparent method
447     /// TODO 注释补充
448     #[inline(always)]
449     fn image_opaque(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, image_data: &[Color]) {
450         let w = w as usize;
451         let mut h = h as usize;
452         let width = self.width() as usize;
453         let height = self.height() as usize;
454         let start_x = start_x as usize;
455         let start_y = start_y as usize;
456 
457         //check boundaries
458         if start_x >= width || start_y >= height {
459             return;
460         }
461         if h + start_y > height {
462             h = height - start_y;
463         }
464         let window_data = self.data_mut();
465         let offset = start_y * width + start_x;
466         //copy image slices to window line by line
467         for l in 0..h {
468             let start = offset + l * width;
469             let mut stop = start + w;
470             let begin = l * w;
471             let mut end = begin + w;
472             //check boundaries
473             if start_x + w > width {
474                 stop = (start_y + l + 1) * width - 1;
475                 end = begin + stop - start;
476             }
477             window_data[start..stop].copy_from_slice(&image_data[begin..end]);
478         }
479     }
480 
481     /// Speed improved, image can be outside of window boundary
482     /// TODO 注释补充
483     #[inline(always)]
484     fn image_fast(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, image_data: &[Color]) {
485         let w = w as usize;
486         let h = h as usize;
487         let width = self.width() as usize;
488         let start_x = start_x as usize;
489         let start_y = start_y as usize;
490 
491         //simply return if image is outside of window
492         if start_x >= width || start_y >= self.height() as usize {
493             return;
494         }
495         let window_data = self.data_mut();
496         let offset = start_y * width + start_x;
497 
498         //copy image slices to window line by line
499         for l in 0..h {
500             let start = offset + l * width;
501             let mut stop = start + w;
502             let begin = l * w;
503             let end = begin + w;
504 
505             //check boundaries
506             if start_x + w > width {
507                 stop = (start_y + l + 1) * width;
508             }
509             let mut k = 0;
510             for i in begin..end {
511                 if i < image_data.len() {
512                     let new = image_data[i].data;
513                     let alpha = (new >> 24) & 0xFF;
514                     if alpha > 0 && (start + k) < window_data.len() && (start + k) < stop {
515                         let old = &mut window_data[start + k].data;
516                         if alpha >= 255 {
517                             *old = new;
518                         } else {
519                             let n_alpha = 255 - alpha;
520                             let rb = ((n_alpha * (*old & 0x00FF00FF))
521                                 + (alpha * (new & 0x00FF00FF)))
522                                 >> 8;
523                             let ag = (n_alpha * ((*old & 0xFF00FF00) >> 8))
524                                 + (alpha * (0x01000000 | ((new & 0x0000FF00) >> 8)));
525 
526                             *old = (rb & 0x00FF00FF) | (ag & 0xFF00FF00);
527                         }
528                     }
529                     k += 1;
530                 }
531             }
532         }
533     }
534 }
535