Compare commits
3 Commits
a4adcd42c8
...
de76574654
| Author | SHA1 | Date | |
|---|---|---|---|
| de76574654 | |||
| 09d29473bb | |||
| 849cfb247d |
284
src/mscanvas.rs
284
src/mscanvas.rs
@@ -87,8 +87,8 @@ impl Edge {
|
||||
return None;
|
||||
}
|
||||
|
||||
let dx = (bottom.x - top.x);
|
||||
let dy = (bottom.y - top.y);
|
||||
let dx = bottom.x - top.x;
|
||||
let dy = bottom.y - top.y;
|
||||
let dx_dy = dx / dy;
|
||||
|
||||
Some(Edge {
|
||||
@@ -145,16 +145,19 @@ impl MSCanvas {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn line_width(&mut self, line_width: i32) {
|
||||
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) {
|
||||
pub fn fill_color(&mut self, color: MSColor) -> &mut Self {
|
||||
self.fill_color = color;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn stroke_color(&mut self, color: MSColor) {
|
||||
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)
|
||||
@@ -208,7 +211,7 @@ impl MSCanvas {
|
||||
|
||||
pub fn pixel_at(&self, x: i32, y: i32) -> MSColor {
|
||||
// 边界检查
|
||||
if x < 0 || x >= self.width || y < 0 || y >= self.height as i32 {
|
||||
if x < 0 || x >= self.width || y < 0 || y >= self.height {
|
||||
return MSColor::ZERO;
|
||||
}
|
||||
|
||||
@@ -221,6 +224,31 @@ impl MSCanvas {
|
||||
self.pixels[index + 3],
|
||||
)
|
||||
}
|
||||
|
||||
pub fn overlay_pixels(&mut self, canvas: &MSCanvas) -> Vec<u8> {
|
||||
let mut pixels = canvas.get_pixels();
|
||||
|
||||
let width = canvas.width.min(self.width);
|
||||
let height = canvas.height.min(self.height);
|
||||
|
||||
for y in 0..height {
|
||||
for x in 0..width {
|
||||
let index = ((y * width + x) * 4) as usize;
|
||||
|
||||
// 跳过透明像素
|
||||
if self.pixels[index + 3] == 0 {
|
||||
continue;
|
||||
}
|
||||
|
||||
pixels[index] = self.pixels[index];
|
||||
pixels[index + 1] = self.pixels[index + 1];
|
||||
pixels[index + 2] = self.pixels[index + 2];
|
||||
pixels[index + 3] = self.pixels[index + 3];
|
||||
}
|
||||
}
|
||||
|
||||
pixels
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
@@ -267,32 +295,67 @@ impl MSCanvas {
|
||||
self.pixels[index + 3] = self.stroke_color.a; // A
|
||||
}
|
||||
|
||||
fn fill_row(&mut self, xs: i32, xe: i32, y: i32) {
|
||||
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 as i32);
|
||||
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] = 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
|
||||
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 stroke_cross_color(&mut self, point: Point) {
|
||||
pub fn stroke_cross(&mut self, point: Point) {
|
||||
let Point { x, y } = point;
|
||||
let r = 10;
|
||||
for dy in -r..=r {
|
||||
@@ -651,6 +714,26 @@ impl MSCanvas {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -855,17 +938,78 @@ impl MSCanvas {
|
||||
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 as i32);
|
||||
let xe = xe.clamp(0, self.width);
|
||||
for x in xs..xe {
|
||||
let index = ((y * self.width + x) * 4) as usize;
|
||||
|
||||
@@ -931,47 +1075,63 @@ impl MSCanvas {
|
||||
self.bezier(points);
|
||||
}
|
||||
|
||||
/// 绘制带线宽的椭圆(描边)
|
||||
/// - center: 椭圆中心
|
||||
/// - rx, ry: 椭圆的水平/垂直半径(指中心线位置)
|
||||
pub fn stroke_ellipse(&mut self, center: Point, rx: f32, ry: f32) {
|
||||
let mut x = 0.0;
|
||||
let mut y = ry; // 初始时将y设置为半高
|
||||
|
||||
// 计算初始决策参数
|
||||
let mut decision = ry * ry - rx * rx * ry + rx * rx / 4.0;
|
||||
|
||||
while ry * ry * x < rx * rx * y {
|
||||
// 在每个阶段,根据对称性绘制四个方向上的点
|
||||
self.stroke_pixel_at(Point::new(center.x + x, center.y + y));
|
||||
self.stroke_pixel_at(Point::new(center.x - x, center.y + y));
|
||||
self.stroke_pixel_at(Point::new(center.x + x, center.y - y));
|
||||
self.stroke_pixel_at(Point::new(center.x - x, center.y - y));
|
||||
|
||||
if decision < 0.0 {
|
||||
x += 1.0;
|
||||
decision += 2.0 * ry * ry * x + ry * ry;
|
||||
} else {
|
||||
x += 1.0;
|
||||
y -= 1.0;
|
||||
decision += 2.0 * ry * ry * x - 2.0 * rx * rx * y + ry * ry;
|
||||
}
|
||||
if self.line_width <= 0 || rx <= 0.0 || ry <= 0.0 {
|
||||
return;
|
||||
}
|
||||
|
||||
decision =
|
||||
ry * ry * (x + 0.5) * (x + 0.5) + rx * rx * (y - 1.0) * (y - 1.0) - rx * rx * ry * ry;
|
||||
// 外椭圆半径(膨胀)
|
||||
let outer_rx = rx;
|
||||
let outer_ry = ry;
|
||||
|
||||
while y > 0.0 {
|
||||
// 同样地,根据对称性绘制四个方向上的点
|
||||
self.stroke_pixel_at(Point::new(center.x + x, center.y + y));
|
||||
self.stroke_pixel_at(Point::new(center.x - x, center.y + y));
|
||||
self.stroke_pixel_at(Point::new(center.x + x, center.y - y));
|
||||
self.stroke_pixel_at(Point::new(center.x - x, center.y - y));
|
||||
// 内椭圆半径(收缩)——不能为负
|
||||
let inner_rx = (rx - self.line_width as f32).max(0.0);
|
||||
let inner_ry = (ry - self.line_width as f32).max(0.0);
|
||||
|
||||
if decision > 0.0 {
|
||||
y -= 1.0;
|
||||
decision += rx * rx - 2.0 * rx * rx * y;
|
||||
} else {
|
||||
x += 1.0;
|
||||
y -= 1.0;
|
||||
decision += 2.0 * ry * ry * x - 2.0 * rx * rx * y + rx * rx;
|
||||
// 计算包围矩形(基于外椭圆)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1007,14 +1167,14 @@ impl MSCanvas {
|
||||
return;
|
||||
}
|
||||
|
||||
let rx = rx as f32;
|
||||
let ry = ry as f32;
|
||||
let rx = rx;
|
||||
let ry = ry;
|
||||
|
||||
// 计算包围矩形(整数边界)
|
||||
let min_x = (center.x as f32 - rx).ceil() as i32;
|
||||
let max_x = (center.x as f32 + rx).floor() as i32;
|
||||
let min_y = (center.y as f32 - ry).ceil() as i32;
|
||||
let max_y = (center.y as f32 + ry).floor() as i32;
|
||||
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;
|
||||
@@ -1023,8 +1183,8 @@ impl MSCanvas {
|
||||
|
||||
for y in min_y..=max_y {
|
||||
for x in min_x..=max_x {
|
||||
let dx = x as f32 - center.x as f32;
|
||||
let dy = y as f32 - center.y as f32;
|
||||
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²
|
||||
@@ -1177,14 +1337,6 @@ fn normalize_radian(radian: f32) -> f32 {
|
||||
r
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// 辅助函数:计算两点间距离的平方(避免开方)
|
||||
fn distance_sq(a: Point, b: Point) -> f32 {
|
||||
let dx = a.x - b.x;
|
||||
let dy = a.y - b.y;
|
||||
dx * dx + dy * dy
|
||||
}
|
||||
|
||||
#[inline]
|
||||
/// 辅助函数:计算两点间距离的平方(避免开方)
|
||||
fn distance_sq1(a: (i32, i32), b: (i32, i32)) -> f32 {
|
||||
|
||||
175
src/paint.rs
175
src/paint.rs
@@ -301,7 +301,10 @@ struct PaintApp {
|
||||
tool_states: [bool; Tool::Count as usize],
|
||||
tool_selected: Tool,
|
||||
|
||||
/// 画图层
|
||||
canvas: MSCanvas,
|
||||
/// 显示层
|
||||
view_canvas: MSCanvas,
|
||||
|
||||
/// 是否正在绘制
|
||||
is_drawing: bool,
|
||||
@@ -336,10 +339,18 @@ impl PaintApp {
|
||||
let (width, height) = canvas.size();
|
||||
let pixels = canvas.get_pixels();
|
||||
|
||||
let mut view_canvas = MSCanvas::new(width, height);
|
||||
view_canvas.clear_zero();
|
||||
view_canvas
|
||||
.fill_color(MSColor::WHITE)
|
||||
.stroke_color(MSColor::BLACK)
|
||||
.line_width(1);
|
||||
|
||||
let mut ins = Self {
|
||||
tool_states: [false; Tool::Count as usize],
|
||||
tool_selected: Tool::Count,
|
||||
canvas,
|
||||
view_canvas,
|
||||
is_drawing: false,
|
||||
begin_point: Point::ORIGIN,
|
||||
end_point: Point::ORIGIN,
|
||||
@@ -568,6 +579,9 @@ impl PaintApp {
|
||||
|
||||
pub fn update(&mut self, message: Message) -> Task<Message> {
|
||||
match self.tool_selected {
|
||||
Tool::Select => {
|
||||
self.update_with_select(message);
|
||||
}
|
||||
Tool::Eraser => {
|
||||
self.update_with_eraser(message);
|
||||
}
|
||||
@@ -702,6 +716,33 @@ impl PaintApp {
|
||||
|
||||
// region tool update
|
||||
|
||||
pub fn update_with_select(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::MousePressed(pos) => {
|
||||
self.is_drawing = true;
|
||||
self.view_canvas.save_pixels();
|
||||
self.begin_point = pos;
|
||||
}
|
||||
Message::MouseReleased(pos) => {
|
||||
self.is_drawing = false;
|
||||
self.view_canvas.restore_pixels();
|
||||
self.view_canvas
|
||||
.stroke_dashed_rect1(self.begin_point, pos, 1, 1);
|
||||
self.begin_point = pos;
|
||||
self.dirty = true;
|
||||
}
|
||||
Message::MouseMoved(pos) => {
|
||||
if self.is_drawing {
|
||||
self.view_canvas.restore_pixels();
|
||||
self.view_canvas
|
||||
.stroke_dashed_rect1(self.begin_point, pos, 3, 1);
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_with_eraser(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::MousePressed(pos) => {
|
||||
@@ -727,6 +768,20 @@ impl PaintApp {
|
||||
);
|
||||
self.dirty = true;
|
||||
}
|
||||
self.view_canvas.clear_zero();
|
||||
self.view_canvas.clear_rect(
|
||||
(pos.x as i32) - self.config.eraser_width / 2,
|
||||
(pos.y as i32) - self.config.eraser_width / 2,
|
||||
self.config.eraser_width,
|
||||
self.config.eraser_width,
|
||||
);
|
||||
self.view_canvas.stroke_rect(
|
||||
(pos.x as i32) - self.config.eraser_width / 2,
|
||||
(pos.y as i32) - self.config.eraser_width / 2,
|
||||
self.config.eraser_width,
|
||||
self.config.eraser_width,
|
||||
);
|
||||
self.dirty = true;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
@@ -766,15 +821,12 @@ impl PaintApp {
|
||||
}
|
||||
|
||||
pub fn update_with_brush(&mut self, message: Message) {
|
||||
let mut brush_fn: fn(&mut MSCanvas, Point) = MSCanvas::brush_circle;
|
||||
if let Some(kind) = self.config.brush_kind {
|
||||
brush_fn = match kind {
|
||||
BrushKind::Circle => MSCanvas::brush_circle,
|
||||
BrushKind::Square => MSCanvas::brush_square,
|
||||
BrushKind::Slash => MSCanvas::brush_slash,
|
||||
BrushKind::Backslash => MSCanvas::brush_backslash,
|
||||
}
|
||||
}
|
||||
let brush_fn = match self.get_brush_kind() {
|
||||
BrushKind::Circle => MSCanvas::brush_circle,
|
||||
BrushKind::Square => MSCanvas::brush_square,
|
||||
BrushKind::Slash => MSCanvas::brush_slash,
|
||||
BrushKind::Backslash => MSCanvas::brush_backslash,
|
||||
};
|
||||
match message {
|
||||
Message::MousePressed(pos) => {
|
||||
self.is_drawing = true;
|
||||
@@ -948,7 +1000,20 @@ impl PaintApp {
|
||||
Message::MouseMoved(pos) => {
|
||||
if self.is_drawing {
|
||||
self.canvas.restore_pixels();
|
||||
self.canvas.stroke_rect1(self.begin_point, pos);
|
||||
match self.get_shape_style() {
|
||||
ShapeStyle::Stroke => {
|
||||
self.canvas.stroke_rect1(self.begin_point, pos);
|
||||
}
|
||||
ShapeStyle::Fill => {
|
||||
self.canvas.fill_rect1(self.begin_point, pos);
|
||||
}
|
||||
ShapeStyle::FillAndStroke => {
|
||||
self.canvas.fill_color(self.background_color);
|
||||
self.canvas.fill_rect1(self.begin_point, pos);
|
||||
self.canvas.fill_color(self.foreground_color);
|
||||
self.canvas.stroke_rect1(self.begin_point, pos);
|
||||
}
|
||||
}
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
@@ -956,6 +1021,24 @@ impl PaintApp {
|
||||
}
|
||||
}
|
||||
|
||||
fn close_polygon(&mut self) {
|
||||
self.canvas.close_path();
|
||||
match self.get_shape_style() {
|
||||
ShapeStyle::Stroke => {
|
||||
self.canvas.stroke();
|
||||
}
|
||||
ShapeStyle::Fill => {
|
||||
self.canvas.fill();
|
||||
}
|
||||
ShapeStyle::FillAndStroke => {
|
||||
self.canvas.fill_color(self.background_color);
|
||||
self.canvas.fill();
|
||||
self.canvas.fill_color(self.foreground_color);
|
||||
self.canvas.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_with_polygon(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::MousePressed(pos) => match self.control_state {
|
||||
@@ -992,10 +1075,9 @@ impl PaintApp {
|
||||
}
|
||||
}
|
||||
Message::MouseDoubleClick(_pos) => {
|
||||
self.canvas.close_path();
|
||||
self.canvas.stroke();
|
||||
self.is_drawing = false;
|
||||
self.control_state = ControlState::Zero;
|
||||
self.close_polygon();
|
||||
self.dirty = true;
|
||||
}
|
||||
_ => {}
|
||||
@@ -1016,7 +1098,20 @@ impl PaintApp {
|
||||
Message::MouseMoved(pos) => {
|
||||
if self.is_drawing {
|
||||
self.canvas.restore_pixels();
|
||||
self.canvas.stroke_ellipse1(self.begin_point, pos);
|
||||
match self.get_shape_style() {
|
||||
ShapeStyle::Stroke => {
|
||||
self.canvas.stroke_ellipse1(self.begin_point, pos);
|
||||
}
|
||||
ShapeStyle::Fill => {
|
||||
self.canvas.fill_ellipse1(self.begin_point, pos);
|
||||
}
|
||||
ShapeStyle::FillAndStroke => {
|
||||
self.canvas.fill_color(self.background_color);
|
||||
self.canvas.fill_ellipse1(self.begin_point, pos);
|
||||
self.canvas.fill_color(self.foreground_color);
|
||||
self.canvas.stroke_ellipse1(self.begin_point, pos);
|
||||
}
|
||||
}
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
@@ -1038,11 +1133,36 @@ impl PaintApp {
|
||||
Message::MouseMoved(pos) => {
|
||||
if self.is_drawing {
|
||||
self.canvas.restore_pixels();
|
||||
self.canvas.stroke_round_rect1(
|
||||
self.begin_point,
|
||||
pos,
|
||||
self.config.rounded_radius as f32,
|
||||
);
|
||||
match self.get_shape_style() {
|
||||
ShapeStyle::Stroke => {
|
||||
self.canvas.stroke_round_rect1(
|
||||
self.begin_point,
|
||||
pos,
|
||||
self.config.rounded_radius as f32,
|
||||
);
|
||||
}
|
||||
ShapeStyle::Fill => {
|
||||
self.canvas.fill_round_rect1(
|
||||
self.begin_point,
|
||||
pos,
|
||||
self.config.rounded_radius as f32,
|
||||
);
|
||||
}
|
||||
ShapeStyle::FillAndStroke => {
|
||||
self.canvas.fill_color(self.background_color);
|
||||
self.canvas.fill_round_rect1(
|
||||
self.begin_point,
|
||||
pos,
|
||||
self.config.rounded_radius as f32,
|
||||
);
|
||||
self.canvas.fill_color(self.foreground_color);
|
||||
self.canvas.stroke_round_rect1(
|
||||
self.begin_point,
|
||||
pos,
|
||||
self.config.rounded_radius as f32,
|
||||
);
|
||||
}
|
||||
}
|
||||
self.dirty = true;
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +1172,14 @@ impl PaintApp {
|
||||
|
||||
// endregion
|
||||
|
||||
fn get_shape_style(&self) -> ShapeStyle {
|
||||
self.config.shape_style.unwrap_or(ShapeStyle::Stroke)
|
||||
}
|
||||
|
||||
fn get_brush_kind(&self) -> BrushKind {
|
||||
self.config.brush_kind.unwrap_or(BrushKind::Circle)
|
||||
}
|
||||
|
||||
pub fn update_tool_states(&mut self, tool: Tool) {
|
||||
let idx = tool as usize;
|
||||
if idx >= self.tool_states.len() {
|
||||
@@ -1059,8 +1187,7 @@ impl PaintApp {
|
||||
}
|
||||
if self.tool_selected == Tool::Polygon && self.control_state != ControlState::Zero {
|
||||
// 切换到其他工具,闭合路径
|
||||
self.canvas.close_path();
|
||||
self.canvas.stroke();
|
||||
self.close_polygon();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
@@ -1073,15 +1200,19 @@ impl PaintApp {
|
||||
|
||||
self.is_drawing = false;
|
||||
self.control_state = ControlState::Zero;
|
||||
self.view_canvas.clear_zero();
|
||||
self.dirty = true;
|
||||
}
|
||||
|
||||
/// 将原始字节转换为 Iced 的图像句柄
|
||||
fn update_image_handle(&mut self) {
|
||||
// 克隆数据以避免所有权问题,或者使用 Arc 如果数据量大
|
||||
// 这里为了简单直接 clone,对于 800x600 (约 2MB) 来说很快
|
||||
let data = self.canvas.get_pixels();
|
||||
// let data = self.canvas.get_pixels();
|
||||
let data = self.view_canvas.overlay_pixels(&self.canvas);
|
||||
let (width, height) = self.canvas.size();
|
||||
|
||||
self.image_handle = image::Handle::from_rgba(WIDTH, HEIGHT, data);
|
||||
self.image_handle = image::Handle::from_rgba(width as u32, height as u32, data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user