use crate::{Color, Pen};
pub struct Canvas<'a> {
buffer: &'a mut [u32],
width: usize,
height: usize,
clamped_width: i32,
clamped_height: i32,
}
impl<'a> Canvas<'a> {
#[allow(clippy::cast_possible_truncation, clippy::cast_possible_wrap)]
#[must_use]
pub fn new(buffer: &'a mut [u32], width: usize, height: usize) -> Self {
assert!(buffer.len() == width * height);
Self {
buffer,
width,
height,
clamped_width: width.min(i32::MAX as usize) as i32,
clamped_height: height.min(i32::MAX as usize) as i32,
}
}
#[must_use]
pub fn width(&self) -> usize {
self.width
}
#[must_use]
pub fn height(&self) -> usize {
self.height
}
#[must_use]
pub fn buffer(&self) -> &[u32] {
self.buffer
}
#[must_use]
pub fn buffer_mut(&mut self) -> &mut [u32] {
self.buffer
}
#[must_use]
pub fn pen(&mut self) -> Pen<'_, 'a> {
Pen::new(self)
}
pub fn clear(&mut self, color: impl Into<Color>) {
let raw_color = u32::from(color.into());
self.buffer.fill(raw_color);
}
#[inline]
pub fn set_pixel(&mut self, x: i32, y: i32, color: impl Into<Color>) {
if 0 <= x && x < self.clamped_width && 0 <= y && y < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x, y, u32::from(color.into()));
}
}
}
#[inline]
pub unsafe fn set_pixel_unchecked(&mut self, x: i32, y: i32, color: impl Into<Color>) {
self.set_pixel_unchecked_raw_i32(x, y, u32::from(color.into()));
}
pub fn pixel_iter(&self) -> impl Iterator<Item = (usize, usize, u32)> + '_ {
self.buffer
.iter()
.enumerate()
.map(|(i, p)| (i % self.width, i / self.width, *p))
}
pub fn pixel_iter_mut(&mut self) -> impl Iterator<Item = (usize, usize, &mut u32)> + '_ {
self.buffer
.iter_mut()
.enumerate()
.map(|(i, p)| (i % self.width, i / self.width, p))
}
#[allow(clippy::cast_sign_loss)]
pub fn fill_rect(&mut self, x: i32, y: i32, w: i32, h: i32, color: impl Into<Color>) {
let raw_color = u32::from(color.into());
let (from_x, to_x, from_y, to_y) = self.clamp_rect_i32(x, x + w, y, y + h);
let offset = from_y as usize * self.width;
let mut from_idx = offset + from_x as usize;
let mut to_idx = offset + to_x as usize;
for _ in from_y..to_y {
self.buffer[from_idx..to_idx].fill(raw_color);
from_idx += self.width;
to_idx += self.width;
}
}
#[allow(clippy::cast_sign_loss)]
pub fn outline_rect(&mut self, x: i32, y: i32, w: i32, h: i32, color: impl Into<Color>) {
if w <= 0 || h <= 0 {
return;
}
let raw_color = u32::from(color.into());
let x1 = x;
let x2 = x + w - 1;
let y1 = y;
let y2 = y + h - 1;
if x2 >= 0 && y1 < self.clamped_height {
let from_x = x1.clamp(0, self.clamped_width - 1) as usize;
let to_x = (x2 + 1).min(self.clamped_width) as usize;
if 0 <= y1 {
let offset = y1 as usize * self.width;
self.buffer[offset + from_x..offset + to_x].fill(raw_color);
}
if 0 <= y2 && y2 < self.clamped_height {
let offset = y2 as usize * self.width;
self.buffer[offset + from_x..offset + to_x].fill(raw_color);
}
}
if y2 >= 0 && x1 < self.clamped_width {
let from_y = y1.clamp(0, self.clamped_height - 1);
let to_y = y2.min(self.clamped_height);
if 0 <= x1 {
for j in from_y..to_y {
unsafe {
self.set_pixel_unchecked_raw_i32(x1, j, raw_color);
}
}
}
if 0 <= x2 && x2 < self.clamped_width {
for j in from_y..to_y {
unsafe {
self.set_pixel_unchecked_raw_i32(x2, j, raw_color);
}
}
}
}
}
#[allow(clippy::cast_sign_loss)]
pub fn thick_outline_rect(
&mut self,
x: i32,
y: i32,
w: i32,
h: i32,
thickness: i32,
color: impl Into<Color>,
) {
if w <= 0 || h <= 0 || thickness <= 0 {
return;
} else if thickness == 1 {
self.outline_rect(x, y, w, h, color);
return;
}
let raw_color = u32::from(color.into());
let x1 = x;
let x2 = x + w;
let y1 = y;
let y2 = y + h;
let half_thickness = thickness / 2;
if x2 + half_thickness >= 0 && y1 - half_thickness < self.clamped_height {
let from_x = (x1 - half_thickness).clamp(0, self.clamped_width - 1) as usize;
let to_x = (x2 + half_thickness).min(self.clamped_width) as usize;
if 0 <= y1 + half_thickness {
for j in
(y1 - half_thickness).max(0)..(y1 + half_thickness).min(self.clamped_height)
{
let offset = j as usize * self.width;
self.buffer[offset + from_x..offset + to_x].fill(raw_color);
}
}
if 0 <= y2 + half_thickness && y2 - half_thickness < self.clamped_height {
for j in
(y2 - half_thickness).max(0)..(y2 + half_thickness).min(self.clamped_height)
{
let offset = j as usize * self.width;
self.buffer[offset + from_x..offset + to_x].fill(raw_color);
}
}
}
if y2 + half_thickness >= 0 && x1 - half_thickness < self.clamped_width {
let from_y = y1.clamp(0, self.clamped_height - 1);
let to_y = y2.min(self.clamped_height);
if 0 <= x1 + half_thickness {
for j in from_y..to_y {
for i in
(x1 - half_thickness).max(0)..(x1 + half_thickness).min(self.clamped_width)
{
unsafe {
self.set_pixel_unchecked_raw_i32(i, j, raw_color);
}
}
}
}
if 0 <= x2 + half_thickness && x2 - half_thickness < self.clamped_width {
for j in from_y..to_y {
for i in
(x2 - half_thickness).max(0)..(x2 + half_thickness).min(self.clamped_width)
{
unsafe {
self.set_pixel_unchecked_raw_i32(i, j, raw_color);
}
}
}
}
}
}
#[allow(clippy::cast_sign_loss, clippy::many_single_char_names)]
pub fn fill_circle(&mut self, x: i32, y: i32, mut r: i32, color: impl Into<Color>) {
if r < 1 {
return;
}
let raw_color = u32::from(color.into());
let mut i = -r;
let mut j = 0;
let mut err = 2 - 2 * r;
loop {
let y1 = y - j;
let y2 = y + j;
let from_x = (x + i).clamp(0, self.clamped_width - 1);
let to_x = (x - i).clamp(from_x, self.clamped_width);
if 0 <= y1 && y1 < self.clamped_height {
let offset = y1 as usize * self.width;
let range = offset + from_x as usize..offset + to_x as usize;
self.buffer[range].fill(raw_color);
}
if 0 <= y2 && y2 < self.clamped_height {
let offset = y2 as usize * self.width;
let range = offset + from_x as usize..offset + to_x as usize;
self.buffer[range].fill(raw_color);
}
r = err;
if r <= j {
j += 1;
err += j * 2 + 1;
}
if r > i || err > j {
i += 1;
err += i * 2 + 1;
}
if i >= 0 {
break;
}
}
}
#[allow(clippy::cast_sign_loss, clippy::many_single_char_names)]
pub fn outline_circle(&mut self, x: i32, y: i32, mut r: i32, color: impl Into<Color>) {
if r < 1 {
return;
}
let raw_color = u32::from(color.into());
let mut i = -r;
let mut j = 0;
let mut err = 2 - 2 * r;
loop {
let x1 = x - i;
let x2 = x + i;
let y1 = y - j;
let y2 = y + j;
if 0 <= x1 && x1 < self.clamped_width {
if 0 <= y1 && y1 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x1, y1, raw_color);
}
}
if 0 <= y2 && y2 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x1, y2, raw_color);
}
}
}
if 0 <= x2 && x2 < self.clamped_width {
if 0 <= y1 && y1 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x2, y1, raw_color);
}
}
if 0 <= y2 && y2 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x2, y2, raw_color);
}
}
}
r = err;
if r <= j {
j += 1;
err += j * 2 + 1;
}
if r > i || err > j {
i += 1;
err += i * 2 + 1;
}
if i >= 0 {
break;
}
}
}
#[allow(clippy::similar_names)]
pub fn thick_outline_circle(
&mut self,
x: i32,
y: i32,
r: i32,
thickness: i32,
color: impl Into<Color>,
) {
if thickness <= 0 || r < 1 {
return;
} else if thickness == 1 {
self.outline_circle(x, y, r, color);
return;
}
let raw_color = u32::from(color.into());
let half_thickness = thickness / 2;
let ro = r + half_thickness;
let ri = ro - thickness + 1;
let mut xo = ro;
let mut xi = ri;
let mut j = 0;
let mut erro = 1 - xo;
let mut erri = 1 - xi;
while xo >= j {
self.hline(y + j, x + xi, x + xo, raw_color);
self.vline(x + j, y + xi, y + xo, raw_color);
self.hline(y + j, x - xo, x - xi, raw_color);
self.vline(x - j, y + xi, y + xo, raw_color);
self.hline(y - j, x - xo, x - xi, raw_color);
self.vline(x - j, y - xo, y - xi, raw_color);
self.hline(y - j, x + xi, x + xo, raw_color);
self.vline(x + j, y - xo, y - xi, raw_color);
j += 1;
if erro < 0 {
erro += 2 * j + 1;
} else {
xo -= 1;
erro += 2 * (j - xo) + 1;
}
if j > ri {
xi = j;
} else if erri < 0 {
erri += 2 * j + 1;
} else {
xi -= 1;
erri += 2 * (j - xi) + 1;
}
}
}
#[allow(clippy::many_single_char_names, clippy::cast_sign_loss)]
pub fn fill_ellipse(&mut self, x: i32, y: i32, a: i32, b: i32, color: impl Into<Color>) {
if a < 1 || b < 1 {
return;
}
let raw_color = u32::from(color.into());
let mut i = -a;
let mut j = 0;
let b2 = i64::from(b) * i64::from(b);
let a2 = i64::from(a) * i64::from(a);
let mut err = i64::from(i) * (2 * b2 + i64::from(i)) + b2;
loop {
let y1 = y - j;
let y2 = y + j;
let from_x = (x + i).clamp(0, self.clamped_width - 1);
let to_x = (x - i).clamp(from_x, self.clamped_width);
if 0 <= y1 && y1 < self.clamped_height {
let offset = y1 as usize * self.width;
let range = offset + from_x as usize..offset + to_x as usize;
self.buffer[range].fill(raw_color);
}
if 0 <= y2 && y2 < self.clamped_height {
let offset = y2 as usize * self.width;
let range = offset + from_x as usize..offset + to_x as usize;
self.buffer[range].fill(raw_color);
}
let e2 = 2 * err;
if e2 >= i64::from(i * 2 + 1) * b2 {
i += 1;
err += i64::from(i * 2 + 1) * b2;
}
if e2 <= i64::from(j * 2 + 1) * a2 {
j += 1;
err += i64::from(j * 2 + 1) * a2;
}
if i > 0 {
break;
}
}
while j < b {
j += 1;
if 0 <= x && x < self.clamped_width {
let y1 = y + j;
let y2 = y - j;
if 0 <= y1 && y1 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x, y1, raw_color);
}
}
if 0 <= y2 && y2 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x, y2, raw_color);
}
}
}
}
}
#[allow(clippy::many_single_char_names)]
pub fn outline_ellipse(&mut self, x: i32, y: i32, a: i32, b: i32, color: impl Into<Color>) {
if a < 1 || b < 1 {
return;
}
let raw_color = u32::from(color.into());
let mut i = -a;
let mut j = 0;
let b2 = i64::from(b) * i64::from(b);
let a2 = i64::from(a) * i64::from(a);
let mut err = i64::from(i) * (2 * b2 + i64::from(i)) + b2;
loop {
let x1 = x - i;
let x2 = x + i;
let y1 = y - j;
let y2 = y + j;
if 0 <= x1 && x1 < self.clamped_width {
if 0 <= y1 && y1 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x1, y1, raw_color);
}
}
if 0 <= y2 && y2 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x1, y2, raw_color);
}
}
}
if 0 <= x2 && x2 < self.clamped_width {
if 0 <= y1 && y1 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x2, y1, raw_color);
}
}
if 0 <= y2 && y2 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x2, y2, raw_color);
}
}
}
let e2 = 2 * err;
if e2 >= i64::from(i * 2 + 1) * b2 {
i += 1;
err += i64::from(i * 2 + 1) * b2;
}
if e2 <= i64::from(j * 2 + 1) * a2 {
j += 1;
err += i64::from(j * 2 + 1) * a2;
}
if i > 0 {
break;
}
}
while j < b {
j += 1;
if 0 <= x && x < self.clamped_width {
let y1 = y + j;
let y2 = y - j;
if 0 <= y1 && y1 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x, y1, raw_color);
}
}
if 0 <= y2 && y2 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x, y2, raw_color);
}
}
}
}
}
#[allow(
clippy::too_many_arguments,
clippy::cast_sign_loss,
clippy::cast_possible_truncation
)]
pub fn fill_triangle(
&mut self,
mut x1: i32,
mut y1: i32,
mut x2: i32,
mut y2: i32,
mut x3: i32,
mut y3: i32,
color: impl Into<Color>,
) {
use std::mem::swap;
let raw_color = u32::from(color.into());
if y2 > y3 {
swap(&mut x2, &mut x3);
swap(&mut y2, &mut y3);
}
if y1 > y2 {
swap(&mut x1, &mut x2);
swap(&mut y1, &mut y2);
}
if y2 > y3 {
swap(&mut x2, &mut x3);
swap(&mut y2, &mut y3);
}
let dx_far = f64::from(x3 - x1) / f64::from(y3 - y1 + 1);
let dx_upper = f64::from(x2 - x1) / f64::from(y2 - y1 + 1);
let dx_low = f64::from(x3 - x2) / f64::from(y3 - y2 + 1);
let mut xf = f64::from(x1);
let mut xt = xf + dx_upper;
for y in y1..=y3.min(self.clamped_height - 1) {
if y >= 0 {
let offset = y as usize * self.width;
{
let from_x = xf.max(0.0) as usize;
let to_x = if xt < f64::from(self.clamped_width) {
xt as usize
} else {
(self.clamped_width - 1) as usize
};
let range = offset + from_x..=offset + to_x;
if !range.is_empty() {
self.buffer[range].fill(raw_color);
}
}
{
let from_x = xt.max(0.0) as usize;
let to_x = if xf < f64::from(self.clamped_width) {
xf as usize
} else {
self.clamped_width as usize - 1
};
let range = offset + from_x..=offset + to_x;
if !range.is_empty() {
self.buffer[range].fill(raw_color);
}
}
}
xf += dx_far;
if y < y2 {
xt += dx_upper;
} else {
xt += dx_low;
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn outline_triangle(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
x3: i32,
y3: i32,
color: impl Into<Color>,
) {
let raw_color = u32::from(color.into());
self.line(x1, y1, x2, y2, raw_color);
self.line(x1, y1, x3, y3, raw_color);
self.line(x2, y2, x3, y3, raw_color);
}
#[allow(clippy::too_many_arguments)]
pub fn thick_outline_triangle(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
x3: i32,
y3: i32,
thickness: i32,
color: impl Into<Color>,
) {
if thickness < 0 {
return;
} else if thickness == 1 {
self.outline_triangle(x1, y1, x2, y2, x3, y3, color);
return;
}
let raw_color = u32::from(color.into());
let half_thickness = thickness / 2;
self.thick_line(x1, y1, x2, y2, thickness, raw_color);
self.thick_line(x1, y1, x3, y3, thickness, raw_color);
self.thick_line(x2, y2, x3, y3, thickness, raw_color);
self.fill_circle(x1, y1, half_thickness, raw_color);
self.fill_circle(x2, y2, half_thickness, raw_color);
self.fill_circle(x3, y3, half_thickness, raw_color);
}
#[allow(clippy::cast_sign_loss)]
#[inline]
pub fn hline(&mut self, y: i32, x1: i32, x2: i32, color: impl Into<Color>) {
let raw_color = u32::from(color.into());
if 0 <= y && y < self.clamped_height {
let (x1, x2) = if x1 > x2 { (x2, x1) } else { (x1, x2) };
let from_x = x1.clamp(0, self.clamped_width - 1);
let to_x = (x2 + 1).clamp(from_x, self.clamped_width);
let offset = y as usize * self.width;
let range = offset + from_x as usize..offset + to_x as usize;
self.buffer[range].fill(raw_color);
}
}
#[allow(clippy::cast_sign_loss)]
#[inline]
pub fn vline(&mut self, x: i32, y1: i32, y2: i32, color: impl Into<Color>) {
let raw_color = u32::from(color.into());
if 0 <= x && x < self.clamped_width {
let (y1, y2) = if y1 > y2 { (y2, y1) } else { (y1, y2) };
let from_y = y1.clamp(0, self.clamped_height - 1);
let to_y = (y2 + 1).clamp(from_y, self.clamped_height);
for y in from_y..to_y {
unsafe { self.set_pixel_unchecked_raw_i32(x, y, raw_color) }
}
}
}
#[inline]
pub fn thick_hline(
&mut self,
y: i32,
x1: i32,
x2: i32,
thickness: i32,
color: impl Into<Color>,
) {
let thickness = thickness.max(0);
let (x1, x2) = if x1 > x2 { (x2, x1) } else { (x1, x2) };
self.fill_rect(x1, y + thickness / 2, x2 - x1, thickness, color);
}
#[inline]
pub fn thick_vline(
&mut self,
x: i32,
y1: i32,
y2: i32,
thickness: i32,
color: impl Into<Color>,
) {
let thickness = thickness.max(0);
let (y1, y2) = if y1 > y2 { (y2, y1) } else { (y1, y2) };
self.fill_rect(x - thickness / 2, y1, thickness, y2 - y1, color);
}
pub fn line(&mut self, mut x1: i32, mut y1: i32, x2: i32, y2: i32, color: impl Into<Color>) {
let raw_color = u32::from(color.into());
let dx = (x2 - x1).abs();
let sx = if x1 < x2 { 1 } else { -1 };
let dy = -(y2 - y1).abs();
let sy = if y1 < y2 { 1 } else { -1 };
let mut err = dx + dy;
loop {
if 0 <= x1 && x1 < self.clamped_width && 0 <= y1 && y1 < self.clamped_height {
unsafe {
self.set_pixel_unchecked_raw_i32(x1, y1, raw_color);
}
}
if x1 == x2 && y1 == y2 {
break;
}
let e2 = 2 * err;
if e2 >= dy {
err += dy;
x1 += sx;
}
if e2 <= dx {
err += dx;
y1 += sy;
}
}
}
#[inline]
pub fn line_maybe_axis_aligned(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
color: impl Into<Color>,
) {
if x1 == x2 {
self.vline(x1, y1, y2, color);
} else if y1 == y2 {
self.hline(y1, x1, x2, color);
} else {
self.line(x1, y1, x2, y2, color);
}
}
#[allow(clippy::similar_names, clippy::cast_possible_truncation)]
pub fn thick_line(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
thickness: i32,
color: impl Into<Color>,
) {
if thickness < 0 {
return;
} else if thickness == 1 {
self.line(x1, y1, x2, y2, color);
return;
}
let raw_color = u32::from(color.into());
let dx = f64::from(x2 - x1);
let dy = f64::from(y2 - y1);
let length = (dx * dx + dy * dy).sqrt();
let half_thickness = f64::from(thickness) * 0.5;
let px = ((-dy / length) * half_thickness) as i32;
let py = ((dx / length) * half_thickness) as i32;
let v1x = x1 + px;
let v1y = y1 + py;
let v2x = x1 - px;
let v2y = y1 - py;
let v3x = x2 + px;
let v3y = y2 + py;
let v4x = x2 - px;
let v4y = y2 - py;
self.fill_triangle(v1x, v1y, v2x, v2y, v3x, v3y, raw_color);
self.fill_triangle(v2x, v2y, v4x, v4y, v3x, v3y, raw_color);
}
#[inline]
pub fn thick_line_maybe_axis_aligned(
&mut self,
x1: i32,
y1: i32,
x2: i32,
y2: i32,
thickness: i32,
color: impl Into<Color>,
) {
if x1 == x2 {
self.thick_vline(x1, y1, y2, thickness, color);
} else if y1 == y2 {
self.thick_hline(y1, x1, x2, thickness, color);
} else {
self.thick_line(x1, y1, x2, y2, thickness, color);
}
}
#[allow(clippy::cast_sign_loss)]
pub fn flood_fill(&mut self, x: i32, y: i32, color: impl Into<Color>) {
if 0 <= x && x < self.clamped_width && 0 <= y && y < self.clamped_height {
let raw_color = u32::from(color.into());
let xu = x as usize;
let yu = y as usize;
let seed_color = self.buffer[yu * self.width + xu];
if seed_color != raw_color {
self.flood_fill_start(xu, yu, seed_color, raw_color);
}
}
}
fn flood_fill_start(&mut self, mut x: usize, mut y: usize, seed_color: u32, raw_color: u32) {
loop {
let ox = x;
let oy = y;
while y != 0 && self.buffer[(y - 1) * self.width + x] == seed_color {
y -= 1;
}
while x != 0 && self.buffer[y * self.width + (x - 1)] == seed_color {
x -= 1;
}
if x == ox && y == oy {
break;
}
}
self.flood_fill_core(x, y, seed_color, raw_color);
}
fn flood_fill_core(&mut self, mut x: usize, mut y: usize, seed_color: u32, raw_color: u32) {
let mut last_row_len = 0;
loop {
let mut row_len = 0;
let mut sx = x;
if last_row_len != 0 && self.buffer[y * self.width + x] != seed_color {
loop {
last_row_len -= 1;
if last_row_len == 0 {
return;
}
x += 1;
if self.buffer[y * self.width + x] == seed_color {
break;
}
}
sx = x;
} else {
while x != 0 && self.buffer[y * self.width + x - 1] == seed_color {
x -= 1;
self.buffer[y * self.width + x] = raw_color;
if y != 0 && self.buffer[(y - 1) * self.width + x] == seed_color {
self.flood_fill_start(x, y - 1, seed_color, raw_color);
}
row_len += 1;
last_row_len += 1;
}
}
while sx < self.width && self.buffer[y * self.width + sx] == seed_color {
self.buffer[y * self.width + sx] = raw_color;
row_len += 1;
sx += 1;
}
if row_len < last_row_len {
let end = x + last_row_len;
loop {
sx += 1;
if sx >= end {
break;
}
if self.buffer[y * self.width + sx] == seed_color {
self.flood_fill_core(sx, y, seed_color, raw_color);
}
}
} else if row_len > last_row_len && y != 0 {
let mut ux = x + last_row_len;
loop {
ux += 1;
if ux >= sx {
break;
}
if self.buffer[(y - 1) * self.width + ux] == seed_color {
self.flood_fill_start(ux, y - 1, seed_color, raw_color);
}
}
}
last_row_len = row_len;
y += 1;
if last_row_len == 0 || y >= self.height {
break;
}
}
}
#[allow(clippy::similar_names)]
#[inline]
fn clamp_rect_i32(&self, xmin: i32, xmax: i32, ymin: i32, ymax: i32) -> (i32, i32, i32, i32) {
let from_x = xmin.clamp(0, self.clamped_width - 1);
let to_x = xmax.clamp(from_x, self.clamped_width);
let from_y = ymin.clamp(0, self.clamped_height - 1);
let to_y = ymax.clamp(from_y, self.clamped_height);
(from_x, to_x, from_y, to_y)
}
#[allow(clippy::cast_sign_loss)]
#[inline]
unsafe fn set_pixel_unchecked_raw_i32(&mut self, x: i32, y: i32, raw_color: u32) {
debug_assert!(x >= 0 && y >= 0);
let idx = y as usize * self.width + x as usize;
debug_assert!(idx < self.buffer.len());
*self.buffer.get_unchecked_mut(idx) = raw_color;
}
}