1use bevy::prelude::*;
2
3#[derive(Component, Copy, Clone, Debug)]
4pub struct Color(pub u8, pub u8, pub u8, pub u8);
5
6impl Default for Color {
7 fn default() -> Self {
8 Self(0x22, 0x22, 0x22, 0xff)
9 }
10}
11
12#[derive(Resource, Default)]
13pub struct PixelsResource {
14 pub width: u32,
15 pub height: u32,
16 buffer: Vec<u8>,
17 default_color: Color,
18}
19
20impl PixelsResource {
21 pub fn new(width: u32, height: u32) -> Self {
22 PixelsResource {
23 width,
24 height,
25 buffer: vec![0u8; (width * height * 4) as usize],
26 ..default()
27 }
28 }
29
30 pub fn frame(&self) -> &[u8] {
31 &self.buffer
32 }
33
34 pub fn get_frame_mut(&mut self) -> &mut [u8] {
35 &mut self.buffer
36 }
37
38 pub fn fill(&mut self, color: &Color) {
39 self.fill_rect(
40 0,
41 0,
42 self.width as i32,
43 self.height as i32,
44 Some(color),
45 false,
46 );
47 }
48
49 pub fn fill_rect(
50 &mut self,
51 x: i32,
52 y: i32,
53 w: i32,
54 h: i32,
55 color_option: Option<&Color>,
56 blend: bool,
57 ) {
58 let width = self.width as i32;
59 let height = self.height as i32;
60
61 let x_min = 0.max(x);
62 let x_max = width.min(x + w);
63 if x_min > x_max {
64 return;
65 }
66 let y_min = 0.max(y);
67 let y_max = height.min(y + h);
68 if y_min > y_max {
69 return;
70 }
71
72 let color = color_option.unwrap_or(&self.default_color);
73 if blend == true && color.3 != 0xff {
74 let c = &[color.0, color.1, color.2, color.3];
75 for y in y_min..y_max {
76 for x in x_min..x_max {
77 self.mix_color((y * width + x) as usize * 4, c, c[3]);
78 }
79 }
80 return;
81 }
82
83 let x_offset = x_min as usize * 4;
84 let x_max_offset = x_max as usize * 4;
85 let line_bytes = width as usize * 4;
86 let render_width = (x_max - x_min) as usize;
87 let object_row = &[color.0, color.1, color.2, color.3].repeat(render_width);
88
89 for y in y_min..y_max {
90 let y_offset = y as usize * line_bytes;
91 self.buffer[(x_offset + y_offset)..(x_max_offset + y_offset)]
92 .copy_from_slice(object_row);
93 }
94 }
95
96 pub fn mix_color(&mut self, index: usize, color: &[u8], fg_a: u8) {
97 if color[3] == 0 {
98 return;
99 }
100 let fg_r = color[0] as u32;
101 let fg_g = color[1] as u32;
102 let fg_b = color[2] as u32;
103 let fg_a = fg_a as u32;
104
105 let g_idx = index + 1;
106 let b_idx = index + 2;
107 let a_idx = index + 3;
108
109 let buffer = &mut self.buffer;
110
111 let r = buffer[index] as u32;
112 let g = buffer[g_idx] as u32;
113 let b = buffer[b_idx] as u32;
114 let bg_a = buffer[a_idx] as u32;
115
116 let ra = 255 - (255 - fg_a) * (255 - bg_a) / 255;
117 let rr = (fg_r * fg_a) / ra + (r * bg_a * (255 - fg_a)) / ra / 255;
118 let rg = (fg_g * fg_a) / ra + (g * bg_a * (255 - fg_a)) / ra / 255;
119 let rb = (fg_b * fg_a) / ra + (b * bg_a * (255 - fg_a)) / ra / 255;
120 buffer[index] = rr as u8;
121 buffer[g_idx] = rg as u8;
122 buffer[b_idx] = rb as u8;
123 buffer[a_idx] = ra as u8;
124 }
125
126 pub fn mix_rect(&mut self, x: i32, y: i32, buffer: &[u8], width: u32, color: Option<&Color>) {
127 let color = color.map(|c| [c.0, c.1, c.2, c.3]);
128 let w = width as i32;
129 let h = buffer.len() as i32 / w / 4;
130
131 let width = self.width as i32;
132 let height = self.height as i32;
133
134 let x_min = 0.max(x);
135 let x_max = width.min(x + w);
136 if x_min > x_max {
137 return;
138 }
139 let y_min = 0.max(y);
140 let y_max = height.min(y + h);
141 if y_min > y_max {
142 return;
143 }
144
145 for yy in y_min..y_max {
146 let y_offset = w * (yy - y) * 4;
147 for xx in x_min..x_max {
148 let offset = (y_offset + (xx - x) * 4) as usize;
149 self.mix_color(
150 (yy * width + xx) as usize * 4,
151 &color.unwrap_or(buffer[offset..(offset + 4)].try_into().unwrap()),
152 buffer[offset + 3],
153 );
154 }
155 }
156 }
157}