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}