Files
mspaint/src/mscanvas.rs

1505 lines
46 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use iced::{Color, Point};
use rand::prelude::*;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct MSColor {
r: u8,
g: u8,
b: u8,
a: u8,
}
#[allow(unused)]
impl MSColor {
fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
Self { r, g, b, a }
}
pub const ZERO: MSColor = MSColor {
r: 0,
g: 0,
b: 0,
a: 0,
};
pub const BLACK: MSColor = MSColor {
r: 0,
g: 0,
b: 0,
a: 255,
};
pub const WHITE: MSColor = MSColor {
r: 255,
g: 255,
b: 255,
a: 255,
};
pub const RED: MSColor = MSColor {
r: 255,
g: 0,
b: 0,
a: 255,
};
pub const GREEN: MSColor = MSColor {
r: 0,
g: 255,
b: 0,
a: 255,
};
pub const BLUE: MSColor = MSColor {
r: 0,
g: 0,
b: 255,
a: 255,
};
pub fn into_color(self) -> Color {
Color::from_rgba8(self.r, self.g, self.b, (self.a / 255) as f32)
}
/// rgb 十六进制
pub fn from_hex(n: i32) -> Self {
let r = (n >> 16) as u8;
let g = (n >> 8) as u8;
let b = n as u8;
Self { r, g, b, a: 0xff }
}
pub fn invert(&self) -> Self {
Self::new(255 - self.r, 255 - self.g, 255 - self.b, self.a)
}
}
#[derive(Clone, Copy, PartialEq)]
struct Edge {
y_max: i32, // 边的最大 y不包含
x: f32, // 当前扫描线 y 处的 x 值
dx_dy: f32, // 1 / slope = Δx / Δy
}
impl Edge {
fn new(p1: Point, p2: Point) -> Option<Self> {
// 确保 p1.y <= p2.y
let (top, bottom) = if p1.y < p2.y { (p1, p2) } else { (p2, p1) };
// 忽略水平边(不会贡献交点)
if (top.y - bottom.y).abs() < f32::EPSILON {
return None;
}
let dx = bottom.x - top.x;
let dy = bottom.y - top.y;
let dx_dy = dx / dy;
Some(Edge {
y_max: bottom.y as i32,
x: top.x,
dx_dy,
})
}
}
pub struct Path2D {
points: Vec<Point>,
}
impl Path2D {
pub fn new() -> Self {
Self { points: vec![] }
}
pub fn push(&mut self, point: Point) {
self.points.push(point);
}
}
pub struct RectSelection {
/// 左上角坐标
x: i32,
y: i32,
w: i32,
h: i32,
pixels: Vec<u8>,
}
impl RectSelection {
pub fn new(x: i32, y: i32, w: i32, h: i32, pixels: Vec<u8>) -> RectSelection {
Self { x, y, w, h, pixels }
}
pub fn contains(&self, p: Point) -> bool {
let x = p.x as i32;
let y = p.y as i32;
!(x < self.x || y < self.y || x >= self.x + self.w || y >= self.y + self.h)
}
pub fn move_offset(&mut self, offset_x: i32, offset_y: i32) {
self.x += offset_x;
self.y += offset_y;
}
pub fn size(&self) -> (i32, i32) {
(self.w, self.h)
}
pub fn position(&self) -> (i32, i32) {
(self.x, self.y)
}
}
pub struct MSCanvas {
width: i32,
height: i32,
/// 原始像素数据RGBA 格式
/// 长度 = width * height * 4
pixels: Vec<u8>,
pixels_bak: Vec<u8>,
fill_color: MSColor,
stroke_color: MSColor,
line_width: i32,
path2d: Path2D,
}
#[allow(unused)]
impl MSCanvas {
pub fn new(width: i32, height: i32) -> Self {
Self {
width,
height,
pixels: vec![255; (width * height * 4) as usize],
pixels_bak: Vec::new(),
fill_color: MSColor::BLACK,
stroke_color: MSColor::WHITE,
line_width: 1,
path2d: Path2D::new(),
}
}
pub fn line_width(&mut self, line_width: i32) -> &mut Self {
self.line_width = line_width;
self
}
pub fn fill_color(&mut self, color: MSColor) -> &mut Self {
self.fill_color = color;
self
}
pub fn stroke_color(&mut self, color: MSColor) -> &mut Self {
self.stroke_color = color;
self
}
pub fn size(&self) -> (i32, i32) {
(self.width, self.height)
}
pub fn get_pixels_ref(&self) -> &[u8] {
&self.pixels
}
pub fn get_pixels(&self) -> Vec<u8> {
self.pixels.clone()
}
pub fn get_pixels_scale(&self, scale: i32) -> Vec<u8> {
if scale <= 1 {
return self.pixels.clone();
}
let dst_width = self.width * scale;
let dst_height = self.height * scale;
let mut dst = vec![0; (dst_width * dst_height * 4) as usize]; // RGBA
for y in 0..self.height {
for x in 0..self.width {
// 源像素索引
let src_idx = ((y * self.width + x) * 4) as usize;
// 源像素颜色
let r = self.pixels[src_idx];
let g = self.pixels[src_idx + 1];
let b = self.pixels[src_idx + 2];
let a = self.pixels[src_idx + 3];
// 在目标图像中填充 scale×scale 区域
for dy in 0..scale {
for dx in 0..scale {
let dst_x = x * scale + dx;
let dst_y = y * scale + dy;
let dst_idx = ((dst_y * dst_width + dst_x) * 4) as usize;
dst[dst_idx] = r;
dst[dst_idx + 1] = g;
dst[dst_idx + 2] = b;
dst[dst_idx + 3] = a;
}
}
}
}
dst
}
pub fn pixel_at(&self, x: i32, y: i32) -> Option<MSColor> {
// 边界检查
if x < 0 || x >= self.width || y < 0 || y >= self.height {
return None;
}
let index = ((y * self.width + x) * 4) as usize;
Some(MSColor::new(
self.pixels[index],
self.pixels[index + 1],
self.pixels[index + 2],
self.pixels[index + 3],
))
}
pub fn pixel_is(&self, x: i32, y: i32, color: MSColor) -> bool {
match self.pixel_at(x, y) {
Some(c) => c == color,
None => false,
}
}
pub fn select_rect(&self, x: i32, y: i32, w: i32, h: i32, is_transparent_white: bool) -> RectSelection {
let mut result = vec![255; (w * h * 4) as usize];
let mut index = 0;
let default_color = if is_transparent_white {
MSColor::ZERO
} else {
MSColor::WHITE
};
for yi in y..(y + h) {
for xi in x..(x + w) {
let mut color = self.pixel_at(xi, yi).unwrap_or(default_color);
if is_transparent_white && color == MSColor::WHITE {
color = MSColor::ZERO;
}
result[index] = color.r;
result[index + 1] = color.g;
result[index + 2] = color.b;
result[index + 3] = color.a;
index += 4;
}
}
RectSelection::new(x, y, w, h, result)
}
pub fn overlay_pixels(&mut self, canvas: &MSCanvas) -> Vec<u8> {
let mut pixels = canvas.get_pixels();
let width = canvas.width.min(self.width) as usize;
let height = canvas.height.min(self.height) as usize;
let src_stride = self.width as usize * 4;
let dst_stride = canvas.width as usize * 4; // ⚠️ Bug fix: use canvas.width, not `width`
for y in 0..height {
let src_row = &self.pixels[y * src_stride..y * src_stride + width * 4];
let dst_row = &mut pixels[y * dst_stride..y * dst_stride + width * 4];
// Process 4 pixels at a time (16 bytes) for better auto-vectorization
let chunks = width / 4;
let remainder = width % 4;
for chunk in 0..chunks {
let base = chunk * 16;
// Check alpha bytes for 4 pixels at once
let a0 = src_row[base + 3];
let a1 = src_row[base + 7];
let a2 = src_row[base + 11];
let a3 = src_row[base + 15];
if a0 | a1 | a2 | a3 != 0 {
// At least one pixel is non-transparent
if a0 != 0 && a1 != 0 && a2 != 0 && a3 != 0 {
// All 4 pixels are non-transparent — bulk copy 16 bytes
dst_row[base..base + 16].copy_from_slice(&src_row[base..base + 16]);
} else {
// Mixed — copy individually
for i in 0..4 {
let off = base + i * 4;
if src_row[off + 3] != 0 {
dst_row[off..off + 4].copy_from_slice(&src_row[off..off + 4]);
}
}
}
}
// else: all 4 transparent — skip entirely
}
// Handle remaining pixels
for x in (chunks * 4)..width {
let off = x * 4;
if src_row[off + 3] != 0 {
dst_row[off..off + 4].copy_from_slice(&src_row[off..off + 4]);
}
}
}
pixels
}
}
#[allow(unused)]
impl MSCanvas {
pub fn fill_pixel_at(&mut self, point: Point) {
let Point { x, y } = point;
self.fill_pixel_at1(x as i32, y as i32);
}
pub fn stroke_pixel_at(&mut self, point: Point) {
let Point { x, y } = point;
self.stroke_pixel_at1(x as i32, y as i32);
}
fn fill_pixel_at1(&mut self, x: i32, y: i32) {
// 边界检查
if x < 0 || x >= self.width || y < 0 || y >= self.height {
return;
}
// 计算索引:(y * width + x) * 4
let index = ((y * self.width + x) * 4) as usize;
// 写入 RGBA 数据
// 注意Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255
self.pixels[index] = self.fill_color.r; // R
self.pixels[index + 1] = self.fill_color.g; // G
self.pixels[index + 2] = self.fill_color.b; // B
self.pixels[index + 3] = self.fill_color.a; // A
}
fn stroke_pixel_at1(&mut self, x: i32, y: i32) {
// 边界检查
if x < 0 || x >= self.width || y < 0 || y >= self.height {
return;
}
// 计算索引:(y * width + x) * 4
let index = ((y * self.width + x) * 4) as usize;
// 写入 RGBA 数据
// 注意Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255
self.pixels[index] = self.stroke_color.r; // R
self.pixels[index + 1] = self.stroke_color.g; // G
self.pixels[index + 2] = self.stroke_color.b; // B
self.pixels[index + 3] = self.stroke_color.a; // A
}
fn draw_column_with_color(&mut self, ys: i32, ye: i32, x: i32, color: MSColor) {
if x < 0 || x >= self.width {
return;
}
let ys = ys.clamp(0, self.height - 1);
let ye = ye.clamp(0, self.height);
for y in ys..ye {
let index = ((y * self.width + x) * 4) as usize;
// 写入 RGBA 数据
// 注意Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255
self.pixels[index] = color.r; // R
self.pixels[index + 1] = color.g; // G
self.pixels[index + 2] = color.b; // B
self.pixels[index + 3] = color.a; // A
}
}
fn draw_row_with_color(&mut self, xs: i32, xe: i32, y: i32, color: MSColor) {
if y < 0 || y >= self.height {
return;
}
let xs = xs.clamp(0, self.width - 1);
let xe = xe.clamp(0, self.width);
for x in xs..xe {
let index = ((y * self.width + x) * 4) as usize;
// 写入 RGBA 数据
// 注意Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255
self.pixels[index] = color.r; // R
self.pixels[index + 1] = color.g; // G
self.pixels[index + 2] = color.b; // B
self.pixels[index + 3] = color.a; // A
}
}
fn fill_column(&mut self, ys: i32, ye: i32, x: i32) {
self.draw_column_with_color(ys, ye, x, self.fill_color);
}
fn fill_row(&mut self, xs: i32, xe: i32, y: i32) {
self.draw_row_with_color(xs, xe, y, self.fill_color);
}
fn stroke_column(&mut self, ys: i32, ye: i32, x: i32) {
self.draw_column_with_color(ys, ye, x, self.stroke_color);
}
fn stroke_row(&mut self, xs: i32, xe: i32, y: i32) {
self.draw_row_with_color(xs, xe, y, self.stroke_color);
}
pub fn fill_pixels(&mut self, points: Vec<Point>) {
for point in points {
self.fill_pixel_at(point);
}
}
pub fn fill_pixels_i32(&mut self, points: Vec<Point<i32>>) {
for point in points {
self.fill_pixel_at1(point.x, point.y);
}
}
pub fn stroke_cross(&mut self, point: Point) {
let Point { x, y } = point;
let r = 10;
for dy in -r..=r {
self.stroke_pixel_at(Point::new(point.x, point.y + dy as f32));
}
for dx in -r..=r {
self.stroke_pixel_at(Point::new(point.x + dx as f32, point.y));
}
}
pub fn brush_circle(&mut self, center: Point) {
if self.line_width <= 1 {
self.fill_pixel_at(center);
return;
}
let square = (self.line_width as f32) * (self.line_width as f32) / 4.0;
let l = self.line_width / 2;
let r = self.line_width - l;
for dy in -l..r {
for dx in -l..r {
if (dx * dx + dy * dy) as f32 <= square {
self.fill_pixel_at(Point::new(center.x + dx as f32, center.y + dy as f32));
}
}
}
}
pub fn brush_square(&mut self, center: Point) {
if self.line_width <= 1 {
self.fill_pixel_at(center);
return;
}
let l = self.line_width / 2;
let r = self.line_width - l;
for dy in -l..r {
for dx in -l..r {
self.fill_pixel_at(Point::new(center.x + dx as f32, center.y + dy as f32));
}
}
}
pub fn brush_slash(&mut self, center: Point) {
let r = self.line_width / 2;
let l = self.line_width - r;
// 左右扩展,填补线条的空白
for dx in 0..2 {
for d in -l..r {
self.fill_pixel_at(Point::new(
center.x - (d as f32) + (dx as f32),
center.y - d as f32,
));
}
}
}
pub fn brush_backslash(&mut self, center: Point) {
let r = self.line_width / 2;
let l = self.line_width - r;
// 左右扩展,填补线条的空白
for dx in 0..2 {
for d in -l..r {
self.fill_pixel_at(Point::new(
center.x + d as f32 + (dx as f32),
center.y - d as f32,
));
}
}
}
fn bresenham_line(&mut self, begin: Point, end: Point) -> Vec<Point> {
let x1 = begin.x;
let y1 = begin.y;
let x2 = end.x;
let y2 = end.y;
let dx = (x2 - x1);
let dy = (y2 - y1);
let dx1 = dx.abs();
let dy1 = dy.abs();
let mut px = 2.0 * dy1 - dx1;
let mut py = 2.0 * dx1 - dy1;
let mut x;
let mut y;
let xe;
let ye;
let mut points = Vec::new();
if dy1 <= dx1 {
if dx >= 0.0 {
x = x1;
y = y1;
xe = x2;
ye = y2;
} else {
x = x2;
y = y2;
xe = x1;
ye = y1;
}
points.push(Point::new(x, y));
while x < xe {
x += 1.0;
if px < 0.0 {
px = px + 2.0 * dy1;
} else {
if (dx < 0.0 && dy < 0.0) || (dx > 0.0 && dy > 0.0) {
y = y + 1.0;
} else {
y = y - 1.0;
}
px = px + 2.0 * (dy1 - dx1);
}
points.push(Point::new(x, y));
}
} else {
if dy >= 0.0 {
x = x1;
y = y1;
ye = y2;
xe = x2;
} else {
x = x2;
y = y2;
ye = y1;
xe = x1;
}
points.push(Point::new(x, y));
while y < ye {
y = y + 1.0;
if py <= 0.0 {
py = py + 2.0 * dx1;
} else {
if (dx < 0.0 && dy < 0.0) || (dx > 0.0 && dy > 0.0) {
x = x + 1.0;
} else {
x = x - 1.0;
}
py = py + 2.0 * (dx1 - dy1);
}
points.push(Point::new(x, y));
}
}
points.push(Point::new(xe, ye));
points
}
pub fn draw_line(&mut self, begin: Point, end: Point) {
let points = self.bresenham_line(begin, end);
for point in points {
self.fill_pixel_at(point);
}
}
pub fn draw_line_with_circle_brush(&mut self, begin: Point, end: Point) {
self.draw_line_with(begin, end, MSCanvas::brush_circle);
}
pub fn draw_line_with_square_brush(&mut self, begin: Point, end: Point) {
self.draw_line_with(begin, end, MSCanvas::brush_square);
}
pub fn draw_line_with<F>(&mut self, begin: Point, end: Point, brush_fn: F)
where
F: Fn(&mut MSCanvas, Point) -> (),
{
let points = self.bresenham_line(begin, end);
for point in points {
brush_fn(self, point);
}
}
pub fn save_pixels(&mut self) {
self.pixels_bak = self.pixels.clone();
}
pub fn restore_pixels(&mut self) {
self.pixels = self.pixels_bak.clone();
}
pub fn fill_slow(&mut self, begin: Point) {
let start_x = begin.x as i32;
let start_y = begin.y as i32;
let target_color = self.pixel_at(start_x, start_y).unwrap_or(MSColor::ZERO);
if target_color == self.fill_color {
return;
}
let mut scan_points = vec![(start_x, start_y)];
let width = self.width;
let height = self.height;
while let Some((x, y)) = scan_points.pop() {
if x < 0 || x >= self.width || y < 0 || y >= self.height {
continue;
}
if self.pixel_is(x, y, target_color) {
self.fill_pixel_at1(x, y);
let p1 = (x - 1, y);
let p2 = (x + 1, y);
let p3 = (x, y - 1);
let p4 = (x, y + 1);
scan_points.push(p1);
scan_points.push(p2);
scan_points.push(p3);
scan_points.push(p4);
}
}
}
pub fn fill_scanline(&mut self, begin: Point) {
let start_x = begin.x as i32;
let start_y = begin.y as i32;
let width = self.width;
let height = self.height;
// 边界检查
if start_x < 0 || start_x >= width || start_y < 0 || start_y >= height {
return;
}
let target_color = self.pixel_at(start_x, start_y).unwrap_or(MSColor::ZERO);
if target_color == self.fill_color {
return;
}
// 栈中存储 (y, x1, x2):表示第 y 行从 x1 到 x2需要向上/下扫描
let mut stack = vec![(start_y, start_x, start_x)];
while let Some((y, mut lx, mut rx)) = stack.pop() {
// 向左扩展 lx
while lx - 1 >= 0 && self.pixel_is(lx - 1, y, target_color) {
lx -= 1;
}
// 向右扩展 rx
while rx + 1 < width && self.pixel_is(rx + 1, y, target_color) {
rx += 1;
}
// 填充当前行 [lx, rx]
for x in lx..=rx {
self.fill_pixel_at1(x, y);
}
// 检查上一行 (y - 1)
if y - 1 >= 0 {
let mut x = lx;
while x <= rx {
if self.pixel_is(x, y - 1, target_color) {
let span_start = x;
// 跳过连续的目标色块
while x <= rx && self.pixel_is(x, y - 1, target_color) {
x += 1;
}
// 将这个 span 入栈(用于后续处理上一行的上一行)
stack.push((y - 1, span_start, x - 1));
} else {
x += 1;
}
}
}
// 检查下一行 (y + 1)
if y + 1 < height {
let mut x = lx;
while x <= rx {
if self.pixel_is(x, y + 1, target_color) {
let span_start = x;
while x <= rx && self.pixel_is(x, y + 1, target_color) {
x += 1;
}
stack.push((y + 1, span_start, x - 1));
} else {
x += 1;
}
}
}
}
}
/// 圆弧从 x 轴方向开始计算, start_angle end_angle 为弧度counterclockwise 是否逆时针方向
pub fn stroke_arc(
&mut self,
center: Point,
radius: f32,
start_angle: f32,
end_angle: f32,
counterclockwise: bool,
) {
if radius <= 1.0 {
self.fill_pixel_at(center);
return;
}
let start_angle = normalize_radian(start_angle);
let end_angle = normalize_radian(end_angle);
let full_circle = (start_angle - end_angle).abs() < f32::EPSILON;
let min_x = ((center.x - radius) as i32).max(0);
let max_x = ((center.x + radius) as i32).min(self.width - 1);
let min_y = ((center.y - radius) as i32).max(0);
let max_y = ((center.y + radius) as i32).min(self.height - 1);
let min_r = (radius - self.line_width as f32).max(0.0);
let max_r = radius + 0.05;
let min_sq = min_r * min_r;
let max_sq = max_r * max_r;
let center_x = center.x as i32;
let center_y = center.y as i32;
for y in min_y..=max_y {
for x in min_x..=max_x {
let dx = (x - center_x) as f32;
let dy = (y - center_y) as f32;
let dist_sq = dx * dx + dy * dy;
// 判断点是不是在扇区内
if dist_sq > max_sq || dist_sq <= min_sq {
continue;
}
// 判断角度是不是在扇区内
let theta = dy.atan2(dx);
let theta = normalize_radian(theta);
if full_circle
|| (counterclockwise && (theta >= end_angle || theta <= start_angle))
|| (!counterclockwise && theta >= start_angle && theta <= end_angle)
{
self.stroke_pixel_at1(x, y);
}
}
}
}
pub fn clear_rect(&mut self, x: i32, y: i32, width: i32, height: i32) {
for yi in y..(y + height) {
self.clear_row(x, x + width, yi);
}
}
fn fill_rect_stroke_color(&mut self, x: i32, y: i32, width: i32, height: i32) {
let old_fill_color = self.fill_color;
self.fill_color(self.stroke_color);
for yi in y..(y + height) {
self.fill_row(x, x + width, yi);
}
self.fill_color(old_fill_color);
}
pub fn fill_rect(&mut self, x: i32, y: i32, width: i32, height: i32) {
for yi in y..(y + height) {
self.fill_row(x, x + width, yi);
}
}
pub fn fill_rect1(&mut self, p1: Point, p2: Point) {
let mut x = p1.x;
let mut y = p1.y;
let mut width = (p2.x - p1.x);
let mut height = (p2.y - p1.y);
if width < 0.0 && height < 0.0 {
x = p2.x;
y = p2.y;
width = (p1.x - p2.x);
height = (p1.y - p2.y);
} else if width < 0.0 {
x += width;
width = -width;
} else if height < 0.0 {
y += height;
height = -height;
}
self.fill_rect(x as i32, y as i32, width as i32, height as i32);
}
/// 填充一个圆角矩形
/// - x, y: 左上角坐标
/// - width, height: 尺寸(必须 > 0
/// - radius: 圆角半径(会被 clamp 到合理范围)
pub fn fill_round_rect(&mut self, x: f32, y: f32, width: f32, height: f32, radius: f32) {
if (width as i32) <= 2 * self.line_width
|| (height as i32) <= 2 * self.line_width
|| width <= 2.0 * radius
|| height <= 2.0 * radius
{
self.fill_rect(x as i32, y as i32, width as i32, height as i32);
return;
}
let min_x = x;
let max_x = x + width - 1.0;
let min_y = y;
let max_y = y + height - 1.0;
// 预计算四个圆角的圆心
let corners = [
((x + radius) as i32, (y + radius) as i32), // 左上
((x + width - 1.0 - radius) as i32, (y + radius) as i32), // 右上
(
(x + width - 1.0 - radius) as i32,
(y + height - 1.0 - radius) as i32,
), // 右下
((x + radius) as i32, (y + height - 1.0 - radius) as i32), // 左下
];
let min_x = min_x as i32;
let max_x = max_x as i32;
let min_y = min_y as i32;
let max_y = max_y as i32;
let radius_sq = (radius + 0.05) * (radius + 0.05);
let radius_int = radius as i32;
for py in min_y..=max_y {
for px in min_x..=max_x {
// 判断是否在“直边区域”(无需检查圆角)
if px >= min_x + radius_int && px <= max_x - radius_int {
// 在左右圆角之间的竖直带 → 一定在内部
self.fill_pixel_at1(px, py);
continue;
}
if py >= min_y + radius_int && py <= max_y - radius_int {
// 在上下圆角之间的水平带 → 一定在内部
self.fill_pixel_at1(px, py);
continue;
}
// 否则,点位于四个角落之一,需检查是否在四分之一圆内
let in_corner =
// 左上角区域
(distance_sq1((px, py), corners[0]) <= radius_sq) ||
// 右上角区域
(distance_sq1((px, py), corners[1]) <= radius_sq) ||
// 右下角区域
(distance_sq1((px, py), corners[2]) <= radius_sq) ||
// 左下角区域
(distance_sq1((px, py), corners[3]) <= radius_sq);
if in_corner {
self.fill_pixel_at1(px, py);
}
}
}
}
pub fn fill_round_rect1(&mut self, p1: Point, p2: Point, radius: f32) {
let mut x = p1.x;
let mut y = p1.y;
let mut width = (p2.x - p1.x);
let mut height = (p2.y - p1.y);
if width < 0.0 && height < 0.0 {
x = p2.x;
y = p2.y;
width = (p1.x - p2.x);
height = (p1.y - p2.y);
} else if width < 0.0 {
x += width;
width = -width;
} else if height < 0.0 {
y += height;
height = -height;
}
self.fill_round_rect(x, y, width, height, radius);
}
pub fn stroke_round_rect(&mut self, x: f32, y: f32, width: f32, height: f32, radius: f32) {
if (width as i32) < 2 * self.line_width
|| (height as i32) < 2 * self.line_width
|| width < 2.0 * radius
|| height < 2.0 * radius
{
self.fill_rect_stroke_color(x as i32, y as i32, width as i32, height as i32);
return;
}
self.fill_rect_stroke_color(
(x + radius) as i32,
y as i32,
(width - 2.0 * radius) as i32,
self.line_width,
);
self.fill_rect_stroke_color(
(x + radius) as i32,
(y + height) as i32 - self.line_width,
(width - 2.0 * radius) as i32,
self.line_width,
);
self.fill_rect_stroke_color(
x as i32,
(y + radius) as i32,
self.line_width,
(height - 2.0 * radius) as i32,
);
self.fill_rect_stroke_color(
(x + width) as i32 - self.line_width,
(y + radius) as i32,
self.line_width,
(height - 2.0 * radius) as i32,
);
self.stroke_arc(
Point::new(x + radius, y + radius),
radius,
std::f32::consts::PI,
std::f32::consts::PI + std::f32::consts::FRAC_PI_2,
false,
);
self.stroke_arc(
Point::new(x + width - 1.0 - radius, y + radius),
radius,
0.0,
std::f32::consts::PI + std::f32::consts::FRAC_PI_2,
true,
);
self.stroke_arc(
Point::new(x + radius, y + height - 1.0 - radius),
radius,
std::f32::consts::FRAC_PI_2,
std::f32::consts::PI,
false,
);
self.stroke_arc(
Point::new(x + width - 1.0 - radius, y + height - 1.0 - radius),
radius,
0.0,
std::f32::consts::FRAC_PI_2,
false,
);
}
pub fn stroke_round_rect1(&mut self, p1: Point, p2: Point, radius: f32) {
let mut x = p1.x;
let mut y = p1.y;
let mut width = (p2.x - p1.x);
let mut height = (p2.y - p1.y);
if width < 0.0 && height < 0.0 {
x = p2.x;
y = p2.y;
width = (p1.x - p2.x);
height = (p1.y - p2.y);
} else if width < 0.0 {
x += width;
width = -width;
} else if height < 0.0 {
y += height;
height = -height;
}
self.stroke_round_rect(x, y, width, height, radius);
}
pub fn stroke_rect(&mut self, x: i32, y: i32, width: i32, height: i32) {
if width < 2 * self.line_width || height < 2 * self.line_width {
self.fill_rect_stroke_color(x, y, width, height);
return;
}
self.fill_rect_stroke_color(x, y, width, self.line_width);
self.fill_rect_stroke_color(x, y + height - self.line_width, width, self.line_width);
self.fill_rect_stroke_color(x, y, self.line_width, height);
self.fill_rect_stroke_color(x + width - self.line_width, y, self.line_width, height);
}
pub fn stroke_rect1(&mut self, p1: Point, p2: Point) {
let mut x = p1.x;
let mut y = p1.y;
let mut width = (p2.x - p1.x);
let mut height = (p2.y - p1.y);
if width < 0.0 && height < 0.0 {
x = p2.x;
y = p2.y;
width = (p1.x - p2.x);
height = (p1.y - p2.y);
} else if width < 0.0 {
x += width;
width = -width;
} else if height < 0.0 {
y += height;
height = -height;
}
self.stroke_rect(x as i32, y as i32, width as i32, height as i32);
}
pub fn stroke_dashed_rect(
&mut self,
x: i32,
y: i32,
width: i32,
height: i32,
dash_length: i32,
gap_length: i32,
) {
let mut is_dash = true;
// 上下两边边框
for dx in (x..x + width).step_by((dash_length + gap_length) as usize) {
if is_dash {
self.stroke_row(dx, dx + dash_length, y);
self.stroke_row(dx, dx + dash_length, y + height - 1);
}
is_dash = !is_dash;
}
// 左右两边边框
for dy in (y..y + height).step_by((dash_length + gap_length) as usize) {
if is_dash {
self.stroke_column(dy, dy + dash_length, x);
self.stroke_column(dy, dy + dash_length, x + width - 1);
}
is_dash = !is_dash;
}
}
pub fn stroke_dashed_rect1(&mut self, p1: Point, p2: Point, dash_length: i32, gap_length: i32) {
let mut x = p1.x;
let mut y = p1.y;
let mut width = (p2.x - p1.x);
let mut height = (p2.y - p1.y);
if width < 0.0 && height < 0.0 {
x = p2.x;
y = p2.y;
width = (p1.x - p2.x);
height = (p1.y - p2.y);
} else if width < 0.0 {
x += width;
width = -width;
} else if height < 0.0 {
y += height;
height = -height;
}
self.stroke_dashed_rect(
x as i32,
y as i32,
width as i32,
height as i32,
dash_length,
gap_length,
);
}
pub fn clear(&mut self) {
self.pixels.fill(255);
}
pub fn clear_zero(&mut self) {
self.pixels.fill(0);
}
pub fn clear_row(&mut self, xs: i32, xe: i32, y: i32) {
if y < 0 || y >= self.height {
return;
}
let xs = xs.clamp(0, self.width - 1);
let xe = xe.clamp(0, self.width);
for x in xs..xe {
let index = ((y * self.width + x) * 4) as usize;
// 写入 RGBA 数据
// 注意Color 的 r, g, b, a 是 0.0 - 1.0,需要转为 0 - 255
self.pixels[index] = 255; // R
self.pixels[index + 1] = 255; // G
self.pixels[index + 2] = 255; // B
self.pixels[index + 3] = 255; // A
}
}
/// 实现喷枪效果的函数
pub fn spray_paint(&mut self, center: Point, radius: i32, density: u32) {
let mut rng = rand::rng();
for _ in 0..density {
// 在给定半径内随机产生偏移量
let offset_x = rng.random_range(-radius..=radius);
let offset_y = rng.random_range(-radius..=radius);
// 确保我们只在圆形区域内绘制,而非整个正方形区域
if (offset_x * offset_x + offset_y * offset_y) <= (radius * radius) {
let point = Point::new(center.x + offset_x as f32, center.y + offset_y as f32);
self.fill_pixel_at(point);
}
}
}
fn recursive_bezier(&mut self, control_points: &[Point], t: f32) -> Point {
// Implement de Casteljau's algorithm
let n = control_points.len();
if n == 1 {
return control_points[0];
}
let mut new_points: Vec<Point> = Vec::with_capacity(n - 1);
for i in 0..(n - 1) {
let p0 = control_points[i];
let p1 = control_points[i + 1];
let point = point_add(point_muln(p0, 1.0 - t), point_muln(p1, t));
new_points.push(point);
}
self.recursive_bezier(&new_points[..], t)
}
pub fn bezier(&mut self, control_points: Vec<Point>) {
// Iterate through all t = 0 to t = 1 with small steps, and call de Casteljau's recursive Bezier algorithm.
let mut t = 0.0;
while t <= 1.0 {
let point = self.recursive_bezier(&control_points, t);
self.brush_circle(point);
t += 0.0005;
}
}
pub fn quadratic_bezier(&mut self, begin: Point, end: Point, p1: Point) {
let points = vec![begin, p1, end];
self.bezier(points);
}
pub fn cubic_bezier(&mut self, begin: Point, end: Point, p1: Point, p2: Point) {
let points = vec![begin, p1, p2, end];
self.bezier(points);
}
/// 绘制带线宽的椭圆(描边)
/// - center: 椭圆中心
/// - rx, ry: 椭圆的水平/垂直半径(指中心线位置)
pub fn stroke_ellipse(&mut self, center: Point, rx: f32, ry: f32) {
if self.line_width <= 0 || rx <= 0.0 || ry <= 0.0 {
return;
}
// 外椭圆半径(膨胀)
let outer_rx = rx;
let outer_ry = ry;
// 内椭圆半径(收缩)——不能为负
let inner_rx = (rx - self.line_width as f32).max(0.0);
let inner_ry = (ry - self.line_width as f32).max(0.0);
// 计算包围矩形(基于外椭圆)
let min_x = (center.x - outer_rx).ceil() as i32;
let max_x = (center.x + outer_rx).floor() as i32;
let min_y = (center.y - outer_ry).ceil() as i32;
let max_y = (center.y + outer_ry).floor() as i32;
// 预计算外椭圆参数
let outer_rx2 = outer_rx * outer_rx;
let outer_ry2 = outer_ry * outer_ry;
let outer_threshold = outer_rx2 * outer_ry2;
// 预计算内椭圆参数(仅当存在内区域时)
let (inner_rx2, inner_ry2, inner_threshold) = if inner_rx > 0.0 && inner_ry > 0.0 {
let rx2 = inner_rx * inner_rx;
let ry2 = inner_ry * inner_ry;
(rx2, ry2, rx2 * ry2)
} else {
(0.0, 0.0, -1.0) // inner_threshold < 0 表示无内区域(实心圆)
};
for y in min_y..=max_y {
for x in min_x..=max_x {
let dx = x as f32 - center.x;
let dy = y as f32 - center.y;
// 判断是否在外椭圆内
let outer_val = dx * dx * outer_ry2 + dy * dy * outer_rx2;
if outer_val > outer_threshold {
continue; // 在外椭圆外
}
// 判断是否在内椭圆内(如果是,则跳过)
if inner_threshold >= 0.0 {
let inner_val = dx * dx * inner_ry2 + dy * dy * inner_rx2;
if inner_val <= inner_threshold {
continue; // 在内椭圆内 → 不绘制(空心)
}
}
// 否则:在外椭圆内,且不在内椭圆内 → 属于描边区域
self.stroke_pixel_at1(x, y);
}
}
}
pub fn stroke_ellipse1(&mut self, p1: Point, p2: Point) {
let mut rx = (p2.x - p1.x) / 2.0;
let mut ry = (p2.y - p1.y) / 2.0;
if rx > 0.0 && ry > 0.0 {
let center = Point::new(p1.x + rx, p1.y + ry);
self.stroke_ellipse(center, rx, ry);
} else if rx < 0.0 && ry < 0.0 {
rx = -rx;
ry = -ry;
let center = Point::new(p1.x - rx, p1.y - ry);
self.stroke_ellipse(center, rx, ry);
} else if ry < 0.0 {
ry = -ry;
let center = Point::new(p1.x + rx, p1.y - ry);
self.stroke_ellipse(center, rx, ry);
} else {
rx = -rx;
let center = Point::new(p1.x - rx, p1.y + ry);
self.stroke_ellipse(center, rx, ry);
}
}
/// 填充一个椭圆
/// - center: 椭圆中心
/// - rx: 水平半径(必须 >= 0
/// - ry: 垂直半径(必须 >= 0
pub fn fill_ellipse(&mut self, center: Point, rx: f32, ry: f32) {
if rx <= 0.0 || ry <= 0.0 {
return;
}
let rx = rx;
let ry = ry;
// 计算包围矩形(整数边界)
let min_x = (center.x - rx).ceil() as i32;
let max_x = (center.x + rx).floor() as i32;
let min_y = (center.y - ry).ceil() as i32;
let max_y = (center.y + ry).floor() as i32;
// 预计算常量rx², ry², rx² * ry²
let rx2 = rx * rx;
let ry2 = ry * ry;
let rxy2 = rx2 * ry2; // 右边阈值
for y in min_y..=max_y {
for x in min_x..=max_x {
let dx = x as f32 - center.x;
let dy = y as f32 - center.y;
// 判断是否在椭圆内dx²/ rx² + dy²/ ry² <= 1
// 等价于dx² * ry² + dy² * rx² <= rx² * ry²
if dx * dx * ry2 + dy * dy * rx2 <= rxy2 {
self.fill_pixel_at1(x, y);
}
}
}
}
pub fn fill_ellipse1(&mut self, p1: Point, p2: Point) {
let mut rx = (p2.x - p1.x) / 2.0;
let mut ry = (p2.y - p1.y) / 2.0;
if rx > 0.0 && ry > 0.0 {
let center = Point::new(p1.x + rx, p1.y + ry);
self.fill_ellipse(center, rx, ry);
} else if rx < 0.0 && ry < 0.0 {
rx = -rx;
ry = -ry;
let center = Point::new(p1.x - rx, p1.y - ry);
self.fill_ellipse(center, rx, ry);
} else if ry < 0.0 {
ry = -ry;
let center = Point::new(p1.x + rx, p1.y - ry);
self.fill_ellipse(center, rx, ry);
} else {
rx = -rx;
let center = Point::new(p1.x - rx, p1.y + ry);
self.fill_ellipse(center, rx, ry);
}
}
fn rasterize_polygon(&mut self, points: &[Point]) -> Vec<Point<i32>> {
let mut result = Vec::new();
if points.len() < 3 {
return result;
}
// 计算 y 范围
let min_y = points.iter().map(|p| p.y as i32).min().unwrap_or(0);
let max_y = points.iter().map(|p| p.y as i32).max().unwrap_or(0);
// 构建边表Edge Table按 ymin 分组
let mut edge_table: Vec<Vec<Edge>> = vec![Vec::new(); (max_y - min_y + 1) as usize];
for i in 0..points.len() {
let p1 = points[i];
let p2 = points[(i + 1) % points.len()];
if let Some(edge) = Edge::new(p1, p2) {
let idx = (edge.x as i32).min(max_y).max(min_y); // 实际应使用 top.y
// 正确做法:使用 top.y 作为插入位置
let top_y = p1.y.min(p2.y) as i32;
if top_y >= min_y && top_y <= max_y {
edge_table[(top_y - min_y) as usize].push(edge);
}
}
}
// 活动边表Active Edge List按 x 排序
let mut active_edges: Vec<Edge> = Vec::new();
// 从 min_y 到 max_y 扫描每一行
for y in min_y..=max_y {
let y_idx = (y - min_y) as usize;
// 1. 添加新边y == ymin 的边)
if y_idx < edge_table.len() {
active_edges.append(&mut edge_table[y_idx]);
}
// 2. 移除 y >= y_max 的边
active_edges.retain(|e| y < e.y_max);
// 3. 按 x 排序
active_edges.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap_or(std::cmp::Ordering::Equal));
// 4. 成对填充像素
for i in (0..active_edges.len()).step_by(2) {
if i + 1 < active_edges.len() {
let x_start = active_edges[i].x.ceil() as i32;
let x_end = active_edges[i + 1].x.floor() as i32;
for x in x_start..=x_end {
result.push(Point::new(x, y));
}
}
}
// 5. 更新活动边的 x为下一行准备
for edge in &mut active_edges {
edge.x += edge.dx_dy;
}
}
result
}
/// 填充一个简单多边形(支持凹多边形)
pub fn fill_polygon(&mut self, points: &[Point]) {
let filled_points = self.rasterize_polygon(points);
self.fill_pixels_i32(filled_points);
}
pub fn begin_path(&mut self) {
self.path2d = Path2D::new();
}
pub fn close_path(&mut self) {
let points = &self.path2d.points;
if points.len() < 3 {
return;
}
self.path2d.push(points[0]);
}
pub fn move_to(&mut self, point: Point) {
self.path2d.push(point);
}
pub fn line_to(&mut self, point: Point) {
self.path2d.push(point);
}
pub fn stroke(&mut self) {
let mut points = self.path2d.points.clone();
if points.len() < 2 {
return;
}
// 连线
for i in 0..(points.len() - 1) {
self.draw_line_with_circle_brush(points[i], points[i + 1]);
}
}
pub fn fill(&mut self) {
let points = self.path2d.points.clone();
self.fill_polygon(&points[..]);
}
pub fn draw_rect_selection(&mut self, x: i32, y: i32, rect: &RectSelection) {
// 边界检查
if x >= self.width || y >= self.height {
return;
}
// 考虑矩形四边出界的情况
let w = (rect.w).min(self.width - x).min(rect.w + x);
let h = (rect.h).min(self.height - y).min(rect.h + y);
if w == 0 || h == 0 {
return;
}
let mut src_offset_x = 0usize;
let mut src_offset_y = 0usize;
if x < 0 {
src_offset_x = (-x) as usize;
}
if y < 0 {
src_offset_y = (-y) as usize;
}
let x = x.max(0) as usize;
let y = y.max(0) as usize;
let w = w as usize;
let h = h as usize;
let width = self.width as usize;
for dy in 0..h {
for dx in 0..w {
let src_index = ((dy + src_offset_y) * rect.w as usize + dx + src_offset_x) * 4;
if rect.pixels[src_index + 3] == 0 {
continue;
}
let dst_index = ((y + dy) * width + x + dx) * 4;
self.pixels[dst_index] = rect.pixels[src_index];
self.pixels[dst_index + 1] = rect.pixels[src_index + 1];
self.pixels[dst_index + 2] = rect.pixels[src_index + 2];
self.pixels[dst_index + 3] = rect.pixels[src_index + 3];
}
}
}
}
fn point_muln(point: Point, t: f32) -> Point {
Point::new(point.x * t, point.y * t)
}
fn point_add(p1: Point, p2: Point) -> Point {
Point::new(p1.x + p2.x, p1.y + p2.y)
}
/// 将弧度规范到 [0, 2π)
fn normalize_radian(radian: f32) -> f32 {
let mut r = radian;
while r < 0.0 {
r += std::f32::consts::TAU;
}
while r >= std::f32::consts::TAU {
r -= std::f32::consts::TAU;
}
r
}
#[inline]
/// 辅助函数:计算两点间距离的平方(避免开方)
fn distance_sq1(a: (i32, i32), b: (i32, i32)) -> f32 {
let dx = a.0 - b.0;
let dy = a.1 - b.1;
(dx * dx + dy * dy) as f32
}