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