pico_rendering/render_pipeline/
_sprite_renderer.rs1use pico_engine_hardware::Display;
2
3#[inline]
6pub fn draw_sprite(
7 display: &mut impl Display,
8 data: &[u16],
9 x: i16,
10 y: i16,
11 width: usize,
12 height: usize,
13 is_opaque: bool,
14) {
15 if x >= display.get_screen_width() as i16
17 || y >= display.get_screen_height() as i16
18 || x + width as i16 <= 0
19 || y + height as i16 <= 0
20 {
21 return;
22 }
23
24 if is_opaque {
25 draw_sprite_opaque(display, data, x, y, width, height);
26 } else {
27 draw_sprite_strip_encoded(display, data, x, y, width, height);
28 }
29}
30
31#[inline]
33fn draw_sprite_opaque(
34 display: &mut impl Display,
35 data: &[u16],
36 x: i16,
37 y: i16,
38 width: usize,
39 height: usize,
40) {
41 let start_y = if y < 0 { -y } else { 0 };
43 let end_y = ((y + height as i16).min(display.get_screen_height() as i16) - y).max(0) as usize;
44 let start_x = if x < 0 { -x } else { 0 };
45 let end_x = ((x + width as i16).min(display.get_screen_width() as i16) - x).max(0) as usize;
46
47 if start_y == 0 && end_y == height && start_x == 0 && end_x == width {
49 for row in 0..height {
50 let screen_y = (y + row as i16) as usize;
51 let dst_start = screen_y * display.get_screen_width() + x as usize;
52 let src_start = row * width;
53 unsafe {
54 core::ptr::copy_nonoverlapping(
55 data.as_ptr().add(src_start),
56 display.frame_buffer().as_mut_ptr().add(dst_start),
57 width,
58 );
59 }
60 }
61 } else {
62 for row in start_y as usize..end_y {
64 let screen_y = (y + row as i16) as usize;
65 let dst_start = screen_y * display.get_screen_width() + (x + start_x) as usize;
66 let src_start = row * width + start_x as usize;
67 let copy_len = end_x - start_x as usize;
68
69 unsafe {
70 core::ptr::copy_nonoverlapping(
71 data.as_ptr().add(src_start),
72 display.frame_buffer().as_mut_ptr().add(dst_start),
73 copy_len,
74 );
75 }
76 }
77 }
78}
79
80#[inline(never)]
83fn draw_sprite_strip_encoded(
84 display: &mut impl Display,
85 data: &[u16],
86 x: i16,
87 y: i16,
88 width: usize,
89 height: usize,
90) {
91 let mut data_idx = 0;
92 let mut row = 0;
93 let mut col = 0;
94 let data_len = data.len();
95 let fb_ptr = display.frame_buffer().as_mut_ptr();
96 let data_ptr = data.as_ptr();
97
98 unsafe {
99 while data_idx < data_len {
100 let header = *data_ptr.add(data_idx);
102 data_idx += 1;
103
104 let transparent_count = (header & 0xFF) as usize;
105 let opaque_count = ((header >> 8) & 0xFF) as usize;
106
107 let mut skip = transparent_count;
109 while skip > 0 {
110 let available = width - col;
111 if skip >= available {
112 skip -= available;
113 col = 0;
114 row += 1;
115 if row >= height {
116 return;
117 }
118 } else {
119 col += skip;
120 break;
121 }
122 }
123
124 let mut remaining = opaque_count;
126
127 while remaining > 0 && row < height {
128 let pixels_in_row = width - col;
129 let batch_size = remaining.min(pixels_in_row);
130
131 let screen_x = x + col as i16;
132 let screen_y = y + row as i16;
133
134 if screen_y >= 0
136 && screen_y < display.get_screen_height() as i16
137 && screen_x >= 0
138 && screen_x + batch_size as i16 <= display.get_screen_width() as i16
139 {
140 let fb_idx = screen_y as usize * display.get_screen_width() + screen_x as usize;
142 core::ptr::copy_nonoverlapping(
143 data_ptr.add(data_idx),
144 fb_ptr.add(fb_idx),
145 batch_size,
146 );
147 } else if screen_y >= 0 && screen_y < display.get_screen_height() as i16 {
148 for i in 0..batch_size {
150 let px = screen_x + i as i16;
151 if px >= 0 && px < display.get_screen_width() as i16 {
152 let fb_idx =
153 screen_y as usize * display.get_screen_width() + px as usize;
154 *fb_ptr.add(fb_idx) = *data_ptr.add(data_idx + i);
155 }
156 }
157 }
158
159 data_idx += batch_size;
160 col += batch_size;
161 remaining -= batch_size;
162
163 if col >= width {
164 col = 0;
165 row += 1;
166 }
167 }
168 }
169 }
170}
171
172#[inline(never)]
177pub fn shift_buffer(display: &mut impl Display, delta_x: i16, delta_y: i16) {
178 if delta_x == 0 && delta_y == 0 {
179 return;
180 }
181
182 let fb_ptr = display.frame_buffer().as_mut_ptr();
183
184 unsafe {
185 if delta_y != 0 {
186 let shift_amount = delta_y.abs() as usize;
189 if delta_y > 0 {
190 for y in 0..(display.get_screen_height() - shift_amount) {
192 let src_idx = (y + shift_amount) * display.get_screen_width();
193 let dst_idx = y * display.get_screen_width();
194 core::ptr::copy(
195 fb_ptr.add(src_idx),
196 fb_ptr.add(dst_idx),
197 display.get_screen_width(),
198 );
199 }
200 } else {
201 for y in (shift_amount..display.get_screen_height()).rev() {
203 let src_idx = (y - shift_amount) * display.get_screen_width();
204 let dst_idx = y * display.get_screen_width();
205 core::ptr::copy(
206 fb_ptr.add(src_idx),
207 fb_ptr.add(dst_idx),
208 display.get_screen_width(),
209 );
210 }
211 }
212 }
213
214 if delta_x != 0 {
215 let shift_amount = delta_x.abs() as usize;
218 if delta_x > 0 {
219 for y in 0..display.get_screen_height() {
221 let row_start = y * display.get_screen_width();
222 core::ptr::copy(
223 fb_ptr.add(row_start + shift_amount),
224 fb_ptr.add(row_start),
225 display.get_screen_width() - shift_amount,
226 );
227 }
228 } else {
229 for y in 0..display.get_screen_height() {
231 let row_start = y * display.get_screen_width();
232 core::ptr::copy(
233 fb_ptr.add(row_start),
234 fb_ptr.add(row_start + shift_amount),
235 display.get_screen_width() - shift_amount,
236 );
237 }
238 }
239 }
240 }
241}