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