pixels_primitives/lib.rs
1//! An easy and simple wrapper for lines and simple shapes for the [pixels](https://docs.rs/pixels/latest/pixels/) crate.
2
3use std::mem;
4
5mod math;
6
7/// Draws a 2d line to a frame of pixels.
8///
9/// # Example
10///
11/// ```no_run
12/// use pixels::{Pixels, SurfaceTexture};
13/// use winit::dpi::LogicalSize;
14/// use winit::event_loop::{EventLoop};
15/// use std::error::Error;
16/// use winit::window::WindowBuilder;
17///
18/// const WIDTH: i32 = 800;
19/// const HEIGHT: i32 = 800;
20///
21/// fn main() -> Result<(), Box<dyn Error>> {
22/// let event_loop = EventLoop::new();
23/// let window = {
24/// let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
25/// WindowBuilder::new()
26/// .with_title("Circle Example")
27/// .with_inner_size(size)
28/// .with_min_inner_size(size)
29/// .build(&event_loop)
30/// .unwrap()
31/// };
32///
33/// let mut pixels = {
34/// let window_size = window.inner_size();
35/// let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
36/// Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
37/// };
38///
39/// pixels_primitives::line(
40/// pixels.get_frame(),
41/// WIDTH,
42/// 200.0,
43/// 100.0,
44/// 700.0,
45/// 300.0,
46/// &[255, 255, 255, 255],
47/// );
48///
49/// // Run your event loop here!
50///
51/// Ok(())
52/// }
53///
54/// ```
55#[warn(missing_docs)]
56pub fn line(
57 frame: &mut [u8],
58 canvas_width: i32,
59 starting_x: f64,
60 starting_y: f64,
61 ending_x: f64,
62 ending_y: f64,
63 rgba: &[u8; 4],
64) {
65 let canvas_height = frame.len() as i32 / 4 / canvas_width;
66
67 // Clone our immutable values into mutable values.
68 let (mut mx0, mut my0, mut mx1, mut my1) = (
69 starting_x as i32,
70 starting_y as i32,
71 ending_x as i32,
72 ending_y as i32,
73 );
74
75 // Checks to see if range is bigger than the domain.
76 let steep = (mx0 - mx1).abs() < (my0 - my1).abs();
77
78 // If the line is steep, we transpose the line (by swapping our Xs and Ys, we will undo it later).
79 if steep {
80 mem::swap(&mut mx0, &mut my0);
81 mem::swap(&mut mx1, &mut my1);
82 };
83
84 // Make it left−to−right.
85 if mx0 > mx1 {
86 mem::swap(&mut mx0, &mut mx1);
87 mem::swap(&mut my0, &mut my1);
88 }
89
90 // Error is the distance from the mathematically "correct" line. (because we're displaying in terms of pixels and not precise mathematically terms)
91 let dx: i32 = mx1 - mx0;
92 let dy: i32 = my1 - my0;
93 let error_increment2 = dy.abs() * 2;
94 let mut error2: i32 = 0;
95
96 let mut y = my0;
97 for x in mx0..mx1 {
98 if steep {
99 color_position(y, x, canvas_width, canvas_height, frame, rgba);
100 } else {
101 color_position(x, y, canvas_width, canvas_height, frame, rgba);
102 }
103 error2 += error_increment2;
104 if error2 > dx {
105 y += if my1 > my0 { 1 } else { -1 };
106 error2 -= dx * 2;
107 }
108 }
109}
110
111/// Draws an outline of a triangle to a frame of pixels.
112///
113/// # Example
114///
115/// ```no_run
116/// use pixels::{Pixels, SurfaceTexture};
117/// use winit::dpi::LogicalSize;
118/// use winit::event_loop::{EventLoop};
119/// use std::error::Error;
120/// use winit::window::WindowBuilder;
121///
122/// const WIDTH: i32 = 800;
123/// const HEIGHT: i32 = 800;
124///
125/// fn main() -> Result<(), Box<dyn Error>> {
126/// let event_loop = EventLoop::new();
127/// let window = {
128/// let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
129/// WindowBuilder::new()
130/// .with_title("Filled Triangle Example")
131/// .with_inner_size(size)
132/// .with_min_inner_size(size)
133/// .build(&event_loop)
134/// .unwrap()
135/// };
136///
137/// let mut pixels = {
138/// let window_size = window.inner_size();
139/// let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
140/// Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
141/// };
142///
143/// pixels_primitives::triangle(
144/// pixels.get_frame(),
145/// WIDTH,
146/// 410,
147/// 500,
148/// 700,
149/// 180,
150/// 430,
151/// 430,
152/// &[255, 255, 255, 255],
153/// );
154///
155/// // Run your event loop here!
156///
157/// Ok(())
158/// }
159///
160/// ```
161#[warn(missing_docs)]
162pub fn triangle(
163 frame: &mut [u8],
164 canvas_width: i32,
165 v0x: i32,
166 v0y: i32,
167 v1x: i32,
168 v1y: i32,
169 v2x: i32,
170 v2y: i32,
171 rgba: &[u8; 4],
172) {
173 line(
174 frame,
175 canvas_width,
176 v0x as f64,
177 v0y as f64,
178 v1x as f64,
179 v1y as f64,
180 rgba,
181 );
182
183 line(
184 frame,
185 canvas_width,
186 v1x as f64,
187 v1y as f64,
188 v2x as f64,
189 v2y as f64,
190 rgba,
191 );
192
193 line(
194 frame,
195 canvas_width,
196 v2x as f64,
197 v2y as f64,
198 v0x as f64,
199 v0y as f64,
200 rgba,
201 );
202}
203
204// TODO: this does not line up perfectly with a normal triangle and I don't know why.
205// TODO: this can be optimized by using barycentric coordinates instead of line sweeping.
206
207/// Draws a filled triangle to a frame of pixels.
208///
209/// # Example
210///
211/// ```no_run
212/// use pixels::{Pixels, SurfaceTexture};
213/// use winit::dpi::LogicalSize;
214/// use winit::event_loop::{EventLoop};
215/// use std::error::Error;
216/// use winit::window::WindowBuilder;
217///
218/// const WIDTH: i32 = 800;
219/// const HEIGHT: i32 = 800;
220///
221/// fn main() -> Result<(), Box<dyn Error>> {
222/// let event_loop = EventLoop::new();
223/// let window = {
224/// let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
225/// WindowBuilder::new()
226/// .with_title("Filled Triangle Example")
227/// .with_inner_size(size)
228/// .with_min_inner_size(size)
229/// .build(&event_loop)
230/// .unwrap()
231/// };
232///
233/// let mut pixels = {
234/// let window_size = window.inner_size();
235/// let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
236/// Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
237/// };
238///
239/// pixels_primitives::triangle_filled(
240/// pixels.get_frame(),
241/// WIDTH,
242/// 410,
243/// 500,
244/// 700,
245/// 180,
246/// 430,
247/// 430,
248/// &[255, 255, 255, 255],
249/// );
250///
251/// // Run your event loop here!
252///
253/// Ok(())
254/// }
255///
256/// ```
257#[warn(missing_docs)]
258pub fn triangle_filled(
259 frame: &mut [u8],
260 canvas_width: i32,
261 v0x: i32,
262 v0y: i32,
263 v1x: i32,
264 v1y: i32,
265 v2x: i32,
266 v2y: i32,
267 rgba: &[u8; 4],
268) {
269 let (mut mv0x, mut mv0y, mut mv1x, mut mv1y, mut mv2x, mut mv2y) =
270 (v0x, v0y, v1x, v1y, v2x, v2y);
271
272 // bubble sort the vectors by y-height
273 math::simple_bubble_sort_vector_by_y(
274 &mut mv0x, &mut mv0y, &mut mv1x, &mut mv1y, &mut mv2x, &mut mv2y,
275 );
276
277 let total_height = (mv2y - mv0y) as f64;
278 // y will start at the lowest vertex y value, and increment by 1 to the middle vertex y value
279 // this makes it so we're only drawing half of the B boundary
280 // each iteration will draw two points, one on the left side and one on the right (for each y value)
281
282 // draws the first "half" of the triangle
283 for y in (mv0y as i32)..=(mv1y as i32) {
284 let segment_height = (mv1y - mv0y) as f64;
285 let alpha = (y - mv0y) as f64 / total_height;
286 let beta = (y - mv0y) as f64 / segment_height;
287
288 let left_point_x = mv0x as f64 + ((mv2x - mv0x) as f64 * alpha);
289 let right_point_x = mv0x as f64 + ((mv1x - mv0x) as f64 * beta);
290
291 line(
292 frame,
293 canvas_width,
294 right_point_x,
295 y as f64,
296 left_point_x,
297 y as f64,
298 rgba,
299 );
300 }
301
302 // draws the second "half" of the triangle
303 for y in (mv1y as i32)..=(mv2y as i32) {
304 let segment_height = (mv2y - mv1y) as f64;
305 let alpha = (y - mv0y) as f64 / total_height;
306 let beta = (y - mv1y) as f64 / segment_height;
307 let left_point_x = mv0x as f64 + ((mv2x - mv0x) as f64 * alpha);
308 let right_point_x = mv1x as f64 + ((mv2x - mv1x) as f64 * beta);
309 line(
310 frame,
311 canvas_width,
312 right_point_x,
313 y as f64,
314 left_point_x,
315 y as f64,
316 rgba,
317 );
318 }
319}
320
321// TODO: this function can be optimized by removing the square root used in the distance function
322
323/// Draws an outline of a circle to a frame of pixels.
324///
325/// # Example
326///
327/// ```no_run
328/// use pixels::{Pixels, SurfaceTexture};
329/// use winit::dpi::LogicalSize;
330/// use winit::event_loop::{EventLoop};
331/// use std::error::Error;
332/// use winit::window::WindowBuilder;
333///
334/// const WIDTH: i32 = 800;
335/// const HEIGHT: i32 = 800;
336///
337/// fn main() -> Result<(), Box<dyn Error>> {
338/// let event_loop = EventLoop::new();
339/// let window = {
340/// let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
341/// WindowBuilder::new()
342/// .with_title("Circle Example")
343/// .with_inner_size(size)
344/// .with_min_inner_size(size)
345/// .build(&event_loop)
346/// .unwrap()
347/// };
348///
349/// let mut pixels = {
350/// let window_size = window.inner_size();
351/// let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
352/// Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
353/// };
354///
355/// pixels_primitives::circle(
356/// pixels.get_frame(),
357/// WIDTH,
358/// 200.0,
359/// 200.0,
360/// 50.0,
361/// 1.5,
362/// &[255, 255, 255, 255],
363/// );
364///
365/// // Run your event loop here!
366///
367/// Ok(())
368/// }
369///
370/// ```
371#[warn(missing_docs)]
372pub fn circle(
373 frame: &mut [u8],
374 canvas_width: i32,
375 center_x: f64,
376 center_y: f64,
377 radius: f64,
378 outline_width: f64,
379 rgba: &[u8; 4],
380) {
381 // Note that rough_maximum_y will not actually be rendered higher than rough_minimum_y, as we are working in the 4th quadrant
382 let canvas_height = frame.len() as i32 / 4 / canvas_width;
383 let rough_minimum_y = (center_y - radius) as i32;
384 let rough_minimum_x = (center_x - radius) as i32;
385 let rough_maximum_y = (center_y + radius) as i32;
386 let rough_maximum_x = (center_x + radius) as i32;
387
388 for y in rough_minimum_y..=rough_maximum_y {
389 for x in rough_minimum_x..=rough_maximum_x {
390 let distance = math::distance(center_x, center_y, x as f64, y as f64);
391 if (distance <= radius) && (distance >= (radius - outline_width)) {
392 color_position(x, y, canvas_width, canvas_height, frame, rgba);
393 }
394 }
395 }
396}
397
398// TODO: this function can be optimized by removing the square root used in the distance function
399
400/// Draws a filled circle to a frame of pixels.
401///
402/// # Example
403///
404/// ```no_run
405/// use pixels::{Pixels, SurfaceTexture};
406/// use winit::dpi::LogicalSize;
407/// use winit::event_loop::{EventLoop};
408/// use std::error::Error;
409/// use winit::window::WindowBuilder;
410///
411/// const WIDTH: i32 = 800;
412/// const HEIGHT: i32 = 800;
413///
414/// fn main() -> Result<(), Box<dyn Error>> {
415/// let event_loop = EventLoop::new();
416/// let window = {
417/// let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
418/// WindowBuilder::new()
419/// .with_title("Filled Circle Example")
420/// .with_inner_size(size)
421/// .with_min_inner_size(size)
422/// .build(&event_loop)
423/// .unwrap()
424/// };
425///
426/// let mut pixels = {
427/// let window_size = window.inner_size();
428/// let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
429/// Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
430/// };
431///
432/// pixels_primitives::circle_filled(
433/// pixels.get_frame(),
434/// WIDTH,
435/// 200.0,
436/// 200.0,
437/// 50.0,
438/// &[255, 255, 255, 255],
439/// );
440///
441/// // Run your event loop here!
442///
443/// Ok(())
444/// }
445///
446/// ```
447#[warn(missing_docs)]
448pub fn circle_filled(
449 frame: &mut [u8],
450 canvas_width: i32,
451 center_x: f64,
452 center_y: f64,
453 radius: f64,
454 rgba: &[u8; 4],
455) {
456 let canvas_height = frame.len() as i32 / 4 / canvas_width;
457 let rough_minimum_y = (center_y - radius) as i32;
458 let rough_minimum_x = (center_x - radius) as i32;
459 let rough_maximum_y = (center_y + radius) as i32;
460 let rough_maximum_x = (center_x + radius) as i32;
461
462 for y in rough_minimum_y..=rough_maximum_y {
463 for x in rough_minimum_x..=rough_maximum_x {
464 let distance = math::distance(center_x, center_y, x as f64, y as f64);
465 if distance <= radius {
466 color_position(x, y, canvas_width, canvas_height, frame, rgba);
467 }
468 }
469 }
470}
471
472/// Draws an outline of a square to a frame of pixels.
473///
474/// # Example
475///
476/// ```no_run
477/// use pixels::{Pixels, SurfaceTexture};
478/// use winit::dpi::LogicalSize;
479/// use winit::event_loop::{EventLoop};
480/// use std::error::Error;
481/// use winit::window::WindowBuilder;
482///
483/// const WIDTH: i32 = 800;
484/// const HEIGHT: i32 = 800;
485///
486/// fn main() -> Result<(), Box<dyn Error>> {
487/// let event_loop = EventLoop::new();
488/// let window = {
489/// let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
490/// WindowBuilder::new()
491/// .with_title("Filled Square Example")
492/// .with_inner_size(size)
493/// .with_min_inner_size(size)
494/// .build(&event_loop)
495/// .unwrap()
496/// };
497///
498/// let mut pixels = {
499/// let window_size = window.inner_size();
500/// let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
501/// Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
502/// };
503///
504/// pixels_primitives::square(
505/// pixels.get_frame(),
506/// WIDTH,
507/// 200.0,
508/// 200.0,
509/// 100.0,
510/// &[255, 255, 255, 255],
511/// );
512///
513/// // Run your event loop here!
514///
515/// Ok(())
516/// }
517///
518/// ```
519#[warn(missing_docs)]
520pub fn square(
521 frame: &mut [u8],
522 canvas_width: i32,
523 center_x: f64,
524 center_y: f64,
525 side_length: f64,
526 rgba: &[u8; 4],
527) {
528 // Note that top_right_y will not actually be rendered on the top right of the square, as we are working in the 4th quadrant
529 let bottom_left_x = center_x - (side_length / 2.0);
530 let bottom_left_y = center_y - (side_length / 2.0);
531 let top_right_x = center_x + (side_length / 2.0);
532 let top_right_y = center_y + (side_length / 2.0);
533
534 line(
535 frame,
536 canvas_width,
537 bottom_left_x,
538 bottom_left_y,
539 bottom_left_x,
540 top_right_y,
541 rgba,
542 );
543
544 line(
545 frame,
546 canvas_width,
547 bottom_left_x,
548 bottom_left_y,
549 top_right_x,
550 bottom_left_y,
551 rgba,
552 );
553
554 line(
555 frame,
556 canvas_width,
557 top_right_x,
558 bottom_left_y,
559 top_right_x,
560 top_right_y,
561 rgba,
562 );
563
564 line(
565 frame,
566 canvas_width,
567 top_right_x,
568 bottom_left_y,
569 top_right_x,
570 top_right_y,
571 rgba,
572 );
573
574 line(
575 frame,
576 canvas_width,
577 top_right_x,
578 top_right_y,
579 bottom_left_x,
580 top_right_y,
581 rgba,
582 );
583}
584
585/// Draws a filled square to a frame of pixels.
586///
587/// # Example
588///
589/// ```no_run
590/// use pixels::{Pixels, SurfaceTexture};
591/// use winit::dpi::LogicalSize;
592/// use winit::event_loop::{EventLoop};
593/// use std::error::Error;
594/// use winit::window::WindowBuilder;
595///
596/// const WIDTH: i32 = 800;
597/// const HEIGHT: i32 = 800;
598///
599/// fn main() -> Result<(), Box<dyn Error>> {
600/// let event_loop = EventLoop::new();
601/// let window = {
602/// let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
603/// WindowBuilder::new()
604/// .with_title("Square Example")
605/// .with_inner_size(size)
606/// .with_min_inner_size(size)
607/// .build(&event_loop)
608/// .unwrap()
609/// };
610///
611/// let mut pixels = {
612/// let window_size = window.inner_size();
613/// let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
614/// Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
615/// };
616///
617/// pixels_primitives::square_filled(
618/// pixels.get_frame(),
619/// WIDTH,
620/// 200.0,
621/// 200.0,
622/// 100.0,
623/// &[255, 255, 255, 255],
624/// );
625///
626/// // Run your event loop here!
627///
628/// Ok(())
629/// }
630///
631/// ```
632#[warn(missing_docs)]
633pub fn square_filled(
634 frame: &mut [u8],
635 canvas_width: i32,
636 center_x: f64,
637 center_y: f64,
638 side_length: f64,
639 rgba: &[u8; 4],
640) {
641 // Note that rough_maximum_y will not actually be rendered higher than rough_minimum_y, as we are working in the 4th quadrant
642 let rough_minimum_y = (center_y - (side_length / 2.0)) as i32;
643 let rough_minimum_x = (center_x - (side_length / 2.0)) as i32;
644 let rough_maximum_y = (center_y + (side_length / 2.0)) as i32;
645 let rough_maximum_x = (center_x + (side_length / 2.0)) as i32;
646
647 for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
648 let x = i as i32 % canvas_width;
649 let y = i as i32 / canvas_width;
650
651 // dont calculate distance if its not within the bounding box
652 if (x < rough_minimum_x)
653 || (x > rough_maximum_x)
654 || (y < rough_minimum_y)
655 || (y > rough_maximum_y)
656 {
657 continue;
658 }
659
660 pixel.copy_from_slice(rgba);
661 }
662}
663
664/// Draws an outline of a rectangle to a frame of pixels.
665///
666/// Note: bottom_left_y and top_right_y are only named correctly mathematically. [pixels](https://docs.rs/pixels/latest/pixels/)
667/// renders in the 4th quadrant, so the y values are flipped, with y=0 starting at the top. This means that bottom_left_y is actually
668/// rendered to the top left of the rectangle, and top_right_y is rendered to the bottom right of the triangle.
669///
670/// # Example
671///
672/// ```no_run
673/// use pixels::{Pixels, SurfaceTexture};
674/// use winit::dpi::LogicalSize;
675/// use winit::event_loop::{EventLoop};
676/// use std::error::Error;
677/// use winit::window::WindowBuilder;
678///
679/// const WIDTH: i32 = 800;
680/// const HEIGHT: i32 = 800;
681///
682/// fn main() -> Result<(), Box<dyn Error>> {
683/// let event_loop = EventLoop::new();
684/// let window = {
685/// let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
686/// WindowBuilder::new()
687/// .with_title("Rectangle Example")
688/// .with_inner_size(size)
689/// .with_min_inner_size(size)
690/// .build(&event_loop)
691/// .unwrap()
692/// };
693///
694/// let mut pixels = {
695/// let window_size = window.inner_size();
696/// let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
697/// Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
698/// };
699///
700/// pixels_primitives::rect(
701/// pixels.get_frame(),
702/// WIDTH,
703/// 200,
704/// 200,
705/// 500,
706/// 300,
707/// &[255, 255, 255, 255],
708/// );
709///
710/// // Run your event loop here!
711///
712/// Ok(())
713/// }
714///
715/// ```
716#[warn(missing_docs)]
717pub fn rect(
718 frame: &mut [u8],
719 canvas_width: i32,
720 bottom_left_x: i32,
721 bottom_left_y: i32,
722 top_right_x: i32,
723 top_right_y: i32,
724 rgba: &[u8; 4],
725) {
726 line(
727 frame,
728 canvas_width,
729 bottom_left_x as f64,
730 bottom_left_y as f64,
731 bottom_left_x as f64,
732 top_right_y as f64,
733 rgba,
734 );
735
736 line(
737 frame,
738 canvas_width,
739 bottom_left_x as f64,
740 bottom_left_y as f64,
741 top_right_x as f64,
742 bottom_left_y as f64,
743 rgba,
744 );
745
746 line(
747 frame,
748 canvas_width,
749 top_right_x as f64,
750 bottom_left_y as f64,
751 top_right_x as f64,
752 top_right_y as f64,
753 rgba,
754 );
755
756 line(
757 frame,
758 canvas_width,
759 top_right_x as f64,
760 bottom_left_y as f64,
761 top_right_x as f64,
762 top_right_y as f64,
763 rgba,
764 );
765
766 line(
767 frame,
768 canvas_width,
769 top_right_x as f64,
770 top_right_y as f64,
771 bottom_left_x as f64,
772 top_right_y as f64,
773 rgba,
774 );
775}
776
777// TODO: make it so this function works with two arbitrary opposite corners
778
779/// Draws a filled rectangle to a frame of pixels.
780///
781/// Note: bottom_left_y and top_right_y are only named correctly mathematically. [pixels](https://docs.rs/pixels/latest/pixels/)
782/// renders in the 4th quadrant, so the y values are flipped, with y=0 starting at the top. This means that bottom_left_y is actually
783/// rendered to the top left of the rectangle, and top_right_y is rendered to the bottom right of the triangle.
784///
785/// # Example
786///
787/// ```no_run
788/// use pixels::{Pixels, SurfaceTexture};
789/// use winit::dpi::LogicalSize;
790/// use winit::event_loop::{EventLoop};
791/// use std::error::Error;
792/// use winit::window::WindowBuilder;
793///
794/// const WIDTH: i32 = 800;
795/// const HEIGHT: i32 = 800;
796///
797/// fn main() -> Result<(), Box<dyn Error>> {
798/// let event_loop = EventLoop::new();
799/// let window = {
800/// let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64);
801/// WindowBuilder::new()
802/// .with_title("Filled Rectangle Example")
803/// .with_inner_size(size)
804/// .with_min_inner_size(size)
805/// .build(&event_loop)
806/// .unwrap()
807/// };
808///
809/// let mut pixels = {
810/// let window_size = window.inner_size();
811/// let surface_texture = SurfaceTexture::new(window_size.width, window_size.height, &window);
812/// Pixels::new(WIDTH as u32, HEIGHT as u32, surface_texture)?
813/// };
814///
815/// pixels_primitives::rect_filled(
816/// pixels.get_frame(),
817/// WIDTH,
818/// 200,
819/// 200,
820/// 500,
821/// 300,
822/// &[255, 255, 255, 255],
823/// );
824///
825/// // Run your event loop here!
826///
827/// Ok(())
828/// }
829///
830/// ```
831#[warn(missing_docs)]
832pub fn rect_filled(
833 frame: &mut [u8],
834 canvas_width: i32,
835 bottom_left_x: i32,
836 bottom_left_y: i32,
837 top_right_x: i32,
838 top_right_y: i32,
839 rgba: &[u8; 4],
840) {
841 assert!(
842 bottom_left_x <= top_right_x,
843 "bottom_left_x must be smaller or equal to top_right_x"
844 );
845 assert!(
846 bottom_left_y <= top_right_y,
847 "bottom_left_y must be smaller or equal to top_right_y"
848 );
849 for (i, pixel) in frame.chunks_exact_mut(4).enumerate() {
850 let x = i as i32 % canvas_width;
851 let y = i as i32 / canvas_width;
852
853 // dont calculate distance if its not within the bounding box
854 if (x < bottom_left_x) || (x > top_right_x) || (y < bottom_left_y) || (y > top_right_y) {
855 continue;
856 }
857
858 pixel.copy_from_slice(rgba);
859 }
860}
861
862#[inline]
863fn get_starting_pixel_index(x: i32, y: i32, canvas_width: i32) -> usize {
864 (((y * canvas_width) + (x)) * 4) as usize
865}
866
867#[inline]
868fn color_position(
869 x: i32,
870 y: i32,
871 canvas_width: i32,
872 canvas_height: i32,
873 frame: &mut [u8],
874 rgba: &[u8],
875) {
876 if (x < 0) || (y < 0) || (x >= canvas_width) || (y >= canvas_height) {
877 return;
878 }
879 let index = get_starting_pixel_index(x, y, canvas_width);
880 let pixel = &mut frame[index..index + 4];
881 pixel.copy_from_slice(rgba);
882}