old school music tracker
1use crate::coordinates::FONT_SIZE16;
2
3use super::coordinates::{CharPosition, CharRect, FONT_SIZE, PixelRect, WINDOW_SIZE};
4use font8x8::UnicodeFonts;
5
6pub struct DrawBuffer {
7 pub framebuffer: Box<[[u8; WINDOW_SIZE.0 as usize]; WINDOW_SIZE.1 as usize]>,
8}
9
10impl Default for DrawBuffer {
11 fn default() -> Self {
12 Self {
13 framebuffer: Box::new([[0; WINDOW_SIZE.0 as usize]; WINDOW_SIZE.1 as usize]),
14 }
15 }
16}
17
18impl DrawBuffer {
19 pub const BACKGROUND_COLOR: u8 = 2;
20
21 pub fn set(&mut self, y: u16, x: u16, color: u8) {
22 self.framebuffer[usize::from(y)][usize::from(x)] = color;
23 }
24
25 pub fn draw_char(
26 &mut self,
27 char_data: [u8; 8],
28 position: CharPosition,
29 fg_color: u8,
30 bg_color: u8,
31 ) {
32 let pixel_pos = PixelRect::from(CharRect::from(position));
33 let position = (pixel_pos.left(), pixel_pos.top());
34 for (y, line) in char_data.iter().enumerate() {
35 let y = u16::try_from(y).unwrap();
36 for x in 0..8 {
37 let color = match (line >> x) & 1 == 1 {
38 true => fg_color,
39 false => bg_color,
40 };
41 self.set(position.1 + y, position.0 + x, color);
42 }
43 }
44 }
45
46 pub fn draw_string(
47 &mut self,
48 string: &str,
49 position: CharPosition,
50 fg_color: u8,
51 bg_color: u8,
52 ) {
53 for (num, char) in string.char_indices() {
54 self.draw_char(
55 font8x8::BASIC_FONTS.get(char).unwrap(),
56 position + (u8::try_from(num).unwrap(), 0),
57 fg_color,
58 bg_color,
59 );
60 }
61 }
62
63 /// cuts off the string if it's too long
64 /// if it's too short it gets filled with whitespace
65 pub fn draw_string_length(
66 &mut self,
67 string: &str,
68 position: CharPosition,
69 lenght: u8,
70 fg_color: u8,
71 bg_color: u8,
72 ) {
73 let ulen = usize::from(lenght);
74 if string.len() > ulen {
75 self.draw_string(&string[..=ulen], position, fg_color, bg_color)
76 } else {
77 self.draw_string(string, position, fg_color, bg_color);
78 self.draw_rect(
79 bg_color,
80 CharRect::new(
81 position.y(),
82 position.y(),
83 position.x() + u8::try_from(string.len()).unwrap(),
84 position.x() + lenght,
85 ),
86 );
87 }
88 }
89
90 /// doesn't draw background. That should be done before calling this function
91 pub fn draw_out_border(
92 &mut self,
93 char_rect: CharRect,
94 top_left_color: u8,
95 bot_right_color: u8,
96 thickness: u8,
97 ) {
98 assert!(thickness <= FONT_SIZE);
99 assert!(thickness > 0);
100 let pixel_rect = PixelRect::from(char_rect);
101
102 let thickness = u16::from(thickness);
103 for x in pixel_rect.left()..=pixel_rect.right() {
104 for y in 0..thickness {
105 self.set(pixel_rect.top() + y, x, top_left_color);
106 self.set(pixel_rect.bot() - y, x, bot_right_color);
107 }
108 }
109 for y in pixel_rect.top()..=pixel_rect.bot() {
110 for x in 0..thickness {
111 self.set(y, pixel_rect.right() - x, bot_right_color);
112 self.set(y, pixel_rect.left() + x, top_left_color);
113 }
114 }
115 }
116
117 pub fn draw_in_box(
118 &mut self,
119 char_rect: CharRect,
120 background_color: u8,
121 top_left_color: u8,
122 bot_right_color: u8,
123 thickness: u8,
124 ) {
125 assert!(thickness < FONT_SIZE);
126 assert!(thickness > 0);
127 let space_from_border = FONT_SIZE16 - u16::from(thickness);
128
129 let pixel_rect = PixelRect::from(char_rect);
130
131 // all pixel lines except those in top and bottom char line
132 for y in (pixel_rect.top() + FONT_SIZE16)..=(pixel_rect.bot() - FONT_SIZE16) {
133 // left side foreground
134 for x in (pixel_rect.left() + space_from_border)..(pixel_rect.left() + FONT_SIZE16) {
135 self.set(y, x, top_left_color);
136 }
137 // left side background
138 for x in pixel_rect.left()..(pixel_rect.left() + space_from_border) {
139 self.set(y, x, background_color);
140 }
141
142 // need the plus ones, as the '..' would need to be exclusive on the low and inclusive on the high, which i dont know how to do
143 for x in
144 (pixel_rect.right() - FONT_SIZE16 + 1)..(pixel_rect.right() - space_from_border + 1)
145 {
146 self.set(y, x, bot_right_color);
147 }
148 // right side background
149 for x in (pixel_rect.right() - space_from_border + 1)..=pixel_rect.right() {
150 self.set(y, x, background_color);
151 }
152 }
153
154 // top char line
155 for y in pixel_rect.top()..(pixel_rect.top() + FONT_SIZE16) {
156 if y < pixel_rect.top() + space_from_border {
157 for x in pixel_rect.horizontal_range() {
158 self.set(y, x, background_color);
159 }
160 } else {
161 for x in pixel_rect.left()..=pixel_rect.right() {
162 let color = if x < pixel_rect.left() + space_from_border
163 || x > pixel_rect.right() - space_from_border
164 {
165 background_color
166 } else if x < pixel_rect.right()
167 - space_from_border
168 - (y - (pixel_rect.top() + space_from_border))
169 {
170 top_left_color
171 } else {
172 bot_right_color
173 };
174
175 self.set(y, x, color);
176 }
177 }
178 }
179
180 // bottom char line
181 for y in (pixel_rect.bot() - FONT_SIZE16 + 1)..=pixel_rect.bot() {
182 // does the top 'SPACE_FROM_BORDER' rows in background color
183 if y > pixel_rect.bot() - space_from_border {
184 for x in pixel_rect.horizontal_range() {
185 self.set(y, x, background_color);
186 }
187 } else {
188 for x in pixel_rect.horizontal_range() {
189 let color = if x < pixel_rect.left() + space_from_border
190 || x > pixel_rect.right() - space_from_border
191 {
192 background_color
193 } else if x < pixel_rect.left() + (pixel_rect.bot() - y) {
194 top_left_color
195 } else {
196 bot_right_color
197 };
198
199 // self.framebuffer[y][x] = color;
200 self.set(y, x, color);
201 }
202 }
203 }
204 }
205
206 pub fn draw_rect(&mut self, color: u8, rect: CharRect) {
207 let pixel_rect = PixelRect::from(rect);
208 self.draw_pixel_rect(color, pixel_rect)
209 }
210
211 pub fn draw_pixel_rect(&mut self, color: u8, rect: PixelRect) {
212 for line in &mut self.framebuffer[usize::from(rect.top())..=usize::from(rect.bot())] {
213 line[usize::from(rect.left())..=usize::from(rect.right())].fill(color);
214 }
215 }
216
217 pub fn show_colors(&mut self) {
218 for i in 0..crate::render::palettes::PALETTE_SIZE as u8 {
219 self.draw_rect(i, CharPosition::new(i, 5).into());
220 }
221 }
222}