diff options
-rw-r--r-- | src/app.rs | 101 | ||||
-rw-r--r-- | src/brush.rs | 61 |
2 files changed, 83 insertions, 79 deletions
@@ -1,6 +1,6 @@ | |||
1 | use crate::{ | 1 | use crate::{ |
2 | bitmap::{positive_angle_with_x, MapPoint, Pixmap}, | 2 | bitmap::{positive_angle_with_x, MapPoint, Pixmap}, |
3 | brush::Brush, | 3 | brush::{Brush, CircleBrush, LineBrush}, |
4 | command::CommandBox, | 4 | command::CommandBox, |
5 | consts::{colors::*, FONT_PATH}, | 5 | consts::{colors::*, FONT_PATH}, |
6 | dither, | 6 | dither, |
@@ -8,7 +8,7 @@ use crate::{ | |||
8 | message::Message, | 8 | message::Message, |
9 | rect, | 9 | rect, |
10 | symmetry::Symmetry, | 10 | symmetry::Symmetry, |
11 | undo::{ModifyRecord, OpKind, Operation, UndoStack}, | 11 | undo::{ModifyRecord, OpKind, PaintRecord, UndoStack}, |
12 | utils::{draw_text, is_copy_event, is_paste_event}, | 12 | utils::{draw_text, is_copy_event, is_paste_event}, |
13 | }; | 13 | }; |
14 | 14 | ||
@@ -39,7 +39,7 @@ pub struct AppState<'ctx, 'file> { | |||
39 | pub canvas: Canvas<Window>, | 39 | pub canvas: Canvas<Window>, |
40 | pub command_box: CommandBox, | 40 | pub command_box: CommandBox, |
41 | pub context: &'ctx Sdl, | 41 | pub context: &'ctx Sdl, |
42 | pub current_operation: Operation, | 42 | pub current_operation: Vec<PaintRecord>, |
43 | pub dither_level: u8, | 43 | pub dither_level: u8, |
44 | pub file_name: Option<&'file Path>, | 44 | pub file_name: Option<&'file Path>, |
45 | pub grid: Grid, | 45 | pub grid: Grid, |
@@ -52,7 +52,7 @@ pub struct AppState<'ctx, 'file> { | |||
52 | pub start: Point, | 52 | pub start: Point, |
53 | pub symmetry: Symmetry, | 53 | pub symmetry: Symmetry, |
54 | pub ttf_context: &'ctx Sdl2TtfContext, | 54 | pub ttf_context: &'ctx Sdl2TtfContext, |
55 | pub undo_stack: UndoStack<Operation>, | 55 | pub undo_stack: UndoStack<ModifyRecord>, |
56 | pub zoom: u8, | 56 | pub zoom: u8, |
57 | } | 57 | } |
58 | 58 | ||
@@ -146,7 +146,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
146 | center: P, | 146 | center: P, |
147 | val: bool, | 147 | val: bool, |
148 | brush_size: u8, | 148 | brush_size: u8, |
149 | ) -> Result<Vec<ModifyRecord>, ()> { | 149 | ) -> Result<Vec<PaintRecord>, ()> { |
150 | let radius = brush_size as u32; | 150 | let radius = brush_size as u32; |
151 | let center = self.idx_at_coord(center).ok_or(())?; | 151 | let center = self.idx_at_coord(center).ok_or(())?; |
152 | let dither_level = self.dither_level; | 152 | let dither_level = self.dither_level; |
@@ -162,7 +162,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
162 | .filter(|&pt| dither::bayer(dither_level, pt)) | 162 | .filter(|&pt| dither::bayer(dither_level, pt)) |
163 | { | 163 | { |
164 | let old_val = self.pixmap.set(point, val); | 164 | let old_val = self.pixmap.set(point, val); |
165 | modify_record.push(ModifyRecord::new(point, old_val, val)); | 165 | modify_record.push(PaintRecord::new(point, old_val, val)); |
166 | } | 166 | } |
167 | Ok(modify_record) | 167 | Ok(modify_record) |
168 | } | 168 | } |
@@ -173,7 +173,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
173 | end: P, | 173 | end: P, |
174 | val: bool, | 174 | val: bool, |
175 | brush_size: u8, | 175 | brush_size: u8, |
176 | ) -> Result<Vec<ModifyRecord>, ()> { | 176 | ) -> Result<Vec<PaintRecord>, ()> { |
177 | let MapPoint { x, y } = start; | 177 | let MapPoint { x, y } = start; |
178 | let end = self.idx_at_coord(end).ok_or(())?; | 178 | let end = self.idx_at_coord(end).ok_or(())?; |
179 | let dither_level = self.dither_level; | 179 | let dither_level = self.dither_level; |
@@ -189,23 +189,29 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
189 | .filter(|&pt| dither::bayer(dither_level, pt)) | 189 | .filter(|&pt| dither::bayer(dither_level, pt)) |
190 | { | 190 | { |
191 | let old_val = self.pixmap.set(c, val); | 191 | let old_val = self.pixmap.set(c, val); |
192 | line_modify_record.push(ModifyRecord::new(c, old_val, val)); | 192 | line_modify_record.push(PaintRecord::new(c, old_val, val)); |
193 | } | 193 | } |
194 | } | 194 | } |
195 | Ok(line_modify_record) | 195 | Ok(line_modify_record) |
196 | } | 196 | } |
197 | 197 | ||
198 | pub fn apply_operation(&mut self, op: Operation, op_kind: OpKind) { | 198 | pub fn apply_operation(&mut self, operation: ModifyRecord, op_kind: OpKind) { |
199 | for ModifyRecord { | 199 | match operation { |
200 | point, | 200 | ModifyRecord::Paint(paints) => { |
201 | old_val, | 201 | for PaintRecord { point, old, new } in paints { |
202 | val, | 202 | if self.pixmap.contains(point) { |
203 | } in op.into_iter() | 203 | match op_kind { |
204 | { | 204 | OpKind::Undo => self.pixmap.set(point, old), |
205 | if self.pixmap.contains(point) { | 205 | OpKind::Redo => self.pixmap.set(point, new), |
206 | }; | ||
207 | } | ||
208 | } | ||
209 | } | ||
210 | ModifyRecord::Invert => self.pixmap.invert(), | ||
211 | ModifyRecord::Brush { old, new } => { | ||
206 | match op_kind { | 212 | match op_kind { |
207 | OpKind::Undo => self.pixmap.set(point, old_val), | 213 | OpKind::Undo => self.brush = old, |
208 | OpKind::Redo => self.pixmap.set(point, val), | 214 | OpKind::Redo => self.brush = new, |
209 | }; | 215 | }; |
210 | } | 216 | } |
211 | } | 217 | } |
@@ -216,9 +222,9 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
216 | let op = self | 222 | let op = self |
217 | .current_operation | 223 | .current_operation |
218 | .drain(..) | 224 | .drain(..) |
219 | .filter(|v| !v.old_val == v.val) | 225 | .filter(|v| !v.old == v.new) |
220 | .collect::<Vec<_>>(); | 226 | .collect::<Vec<_>>(); |
221 | self.undo_stack.push(op); | 227 | self.undo_stack.push(ModifyRecord::Paint(op)); |
222 | } | 228 | } |
223 | } | 229 | } |
224 | 230 | ||
@@ -258,16 +264,6 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
258 | ); | 264 | ); |
259 | } | 265 | } |
260 | 266 | ||
261 | // pub fn increase_brush_size(&mut self) { | ||
262 | // self.brush_size += 1; | ||
263 | // } | ||
264 | |||
265 | // pub fn decrease_brush_size(&mut self) { | ||
266 | // if self.brush_size > 0 { | ||
267 | // self.brush_size -= 1; | ||
268 | // } | ||
269 | // } | ||
270 | |||
271 | pub fn reduce_intensity(&mut self) { | 267 | pub fn reduce_intensity(&mut self) { |
272 | if self.dither_level > 0 { | 268 | if self.dither_level > 0 { |
273 | self.dither_level -= 1; | 269 | self.dither_level -= 1; |
@@ -293,13 +289,6 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
293 | } | 289 | } |
294 | self.command_box.hist_append(); | 290 | self.command_box.hist_append(); |
295 | 291 | ||
296 | // if let Some(path) = self.command_box.text.strip_prefix("(save ") { | ||
297 | // let image = self.export(); | ||
298 | // let encoded = image.encode().unwrap(); | ||
299 | // let mut buffer = File::create(path).unwrap(); | ||
300 | // buffer.write_all(&encoded[..]).unwrap(); | ||
301 | // self.command_box.hist_append(); | ||
302 | // } | ||
303 | self.command_box.clear(); | 292 | self.command_box.clear(); |
304 | self.mode = Mode::Draw; | 293 | self.mode = Mode::Draw; |
305 | } | 294 | } |
@@ -425,7 +414,7 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
425 | } | 414 | } |
426 | } | 415 | } |
427 | match self.brush { | 416 | match self.brush { |
428 | Brush::Line { start, size, .. } => { | 417 | Brush::Line(LineBrush { start, size, .. }) => { |
429 | let size = self.zoom as u32 * size as u32; | 418 | let size = self.zoom as u32 * size as u32; |
430 | if let (Some(from), Some(to)) = (start, pt) { | 419 | if let (Some(from), Some(to)) = (start, pt) { |
431 | let line = self.pixmap.get_line(from, to.into()); | 420 | let line = self.pixmap.get_line(from, to.into()); |
@@ -629,10 +618,16 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
629 | // toggle grid | 618 | // toggle grid |
630 | Keycode::Tab => self.toggle_grid(), | 619 | Keycode::Tab => self.toggle_grid(), |
631 | // invert canvas | 620 | // invert canvas |
632 | Keycode::I => self.pixmap.invert(), | 621 | Keycode::I => { |
622 | self.pixmap.invert(); | ||
623 | self.undo_stack.push(ModifyRecord::Invert); | ||
624 | } | ||
633 | // line drawing | 625 | // line drawing |
634 | Keycode::F => { | 626 | Keycode::F => { |
635 | if matches!(self.brush, Brush::Line { extend: false, .. }) { | 627 | if matches!( |
628 | self.brush, | ||
629 | Brush::Line(LineBrush { extend: false, .. }) | ||
630 | ) { | ||
636 | self.brush = Brush::line(0, true); | 631 | self.brush = Brush::line(0, true); |
637 | } else { | 632 | } else { |
638 | self.brush = Brush::line(0, false); | 633 | self.brush = Brush::line(0, false); |
@@ -675,31 +670,31 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
675 | _ => self.active_color, | 670 | _ => self.active_color, |
676 | }; | 671 | }; |
677 | match self.brush { | 672 | match self.brush { |
678 | Brush::Circle { size } => { | 673 | Brush::Circle(CircleBrush { size }) => { |
679 | if let Ok(o) = self.paint_point(pt, val, size) { | 674 | if let Ok(o) = self.paint_point(pt, val, size) { |
680 | self.current_operation.extend(o); | 675 | self.current_operation.extend(o); |
681 | } | 676 | } |
682 | } | 677 | } |
683 | Brush::Line { | 678 | Brush::Line(LineBrush { |
684 | size, | 679 | size, |
685 | start, | 680 | start, |
686 | extend, | 681 | extend, |
687 | } => { | 682 | }) => { |
688 | if start.is_none() { | 683 | if start.is_none() { |
689 | self.brush = Brush::Line { | 684 | self.brush = Brush::Line(LineBrush { |
690 | size, | 685 | size, |
691 | start: contact, | 686 | start: contact, |
692 | extend, | 687 | extend, |
693 | }; | 688 | }); |
694 | } else if let Ok(o) = | 689 | } else if let Ok(o) = |
695 | self.paint_line(start.unwrap(), pt, val, size) | 690 | self.paint_line(start.unwrap(), pt, val, size) |
696 | { | 691 | { |
697 | self.current_operation.extend(o); | 692 | self.current_operation.extend(o); |
698 | self.brush = Brush::Line { | 693 | self.brush = Brush::Line(LineBrush { |
699 | size, | 694 | size, |
700 | start: if extend { contact } else { None }, | 695 | start: if extend { contact } else { None }, |
701 | extend, | 696 | extend, |
702 | }; | 697 | }); |
703 | } | 698 | } |
704 | } | 699 | } |
705 | Brush::Fill => { | 700 | Brush::Fill => { |
@@ -715,12 +710,12 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
715 | self.current_operation.extend( | 710 | self.current_operation.extend( |
716 | operation | 711 | operation |
717 | .into_iter() | 712 | .into_iter() |
718 | .map(|point| ModifyRecord { | 713 | .map(|point| PaintRecord { |
719 | point, | 714 | point, |
720 | old_val: target, | 715 | old: target, |
721 | val: self.active_color, | 716 | new: self.active_color, |
722 | }) | 717 | }) |
723 | .collect::<Vec<ModifyRecord>>(), | 718 | .collect::<Vec<PaintRecord>>(), |
724 | ) | 719 | ) |
725 | } | 720 | } |
726 | } | 721 | } |
@@ -732,8 +727,8 @@ impl<'ctx, 'file> AppState<'ctx, 'file> { | |||
732 | x, y, mousestate, .. | 727 | x, y, mousestate, .. |
733 | } => { | 728 | } => { |
734 | let size = match self.brush { | 729 | let size = match self.brush { |
735 | Brush::Circle { size } => size, | 730 | Brush::Circle(CircleBrush { size }) => size, |
736 | Brush::Line { size, .. } => size, | 731 | Brush::Line(LineBrush { size, .. }) => size, |
737 | _ => continue, | 732 | _ => continue, |
738 | }; | 733 | }; |
739 | if mousestate.is_mouse_button_pressed(MouseButton::Left) { | 734 | if mousestate.is_mouse_button_pressed(MouseButton::Left) { |
diff --git a/src/brush.rs b/src/brush.rs index 1a365b1..8ff0cda 100644 --- a/src/brush.rs +++ b/src/brush.rs | |||
@@ -4,29 +4,36 @@ use crate::bitmap::MapPoint; | |||
4 | 4 | ||
5 | #[derive(Debug, Copy, Clone)] | 5 | #[derive(Debug, Copy, Clone)] |
6 | pub enum Brush { | 6 | pub enum Brush { |
7 | Line { | 7 | Line(LineBrush), |
8 | size: u8, | 8 | Circle(CircleBrush), |
9 | start: Option<MapPoint>, | 9 | RectSelect(RectSelectBrush), |
10 | extend: bool, | ||
11 | }, | ||
12 | Circle { | ||
13 | size: u8, | ||
14 | }, | ||
15 | RectSelect { | ||
16 | start: MapPoint, | ||
17 | end: MapPoint, | ||
18 | }, | ||
19 | Fill, | 10 | Fill, |
20 | Custom { | 11 | Custom { size: u8 }, |
21 | size: u8, | 12 | } |
22 | }, | 13 | |
14 | #[derive(Debug, Copy, Clone)] | ||
15 | pub struct LineBrush { | ||
16 | pub size: u8, | ||
17 | pub start: Option<MapPoint>, | ||
18 | pub extend: bool, | ||
19 | } | ||
20 | |||
21 | #[derive(Debug, Copy, Clone)] | ||
22 | pub struct CircleBrush { | ||
23 | pub size: u8, | ||
24 | } | ||
25 | |||
26 | #[derive(Debug, Copy, Clone)] | ||
27 | pub struct RectSelectBrush { | ||
28 | pub start: MapPoint, | ||
29 | pub end: MapPoint, | ||
23 | } | 30 | } |
24 | 31 | ||
25 | impl Brush { | 32 | impl Brush { |
26 | pub fn grow(&mut self) { | 33 | pub fn grow(&mut self) { |
27 | match self { | 34 | match self { |
28 | Brush::Line { ref mut size, .. } => *size += 1, | 35 | Brush::Line(LineBrush { ref mut size, .. }) => *size += 1, |
29 | Brush::Circle { ref mut size, .. } => *size += 1, | 36 | Brush::Circle(CircleBrush { ref mut size, .. }) => *size += 1, |
30 | Brush::Custom { ref mut size, .. } => *size += 1, | 37 | Brush::Custom { ref mut size, .. } => *size += 1, |
31 | _ => (), | 38 | _ => (), |
32 | } | 39 | } |
@@ -34,23 +41,23 @@ impl Brush { | |||
34 | 41 | ||
35 | pub fn shrink(&mut self) { | 42 | pub fn shrink(&mut self) { |
36 | match self { | 43 | match self { |
37 | Brush::Line { ref mut size, .. } => *size = size.saturating_sub(1), | 44 | Brush::Line(LineBrush { ref mut size, .. }) => *size = size.saturating_sub(1), |
38 | Brush::Circle { ref mut size, .. } => *size = size.saturating_sub(1), | 45 | Brush::Circle(CircleBrush { ref mut size, .. }) => *size = size.saturating_sub(1), |
39 | Brush::Custom { ref mut size, .. } => *size = size.saturating_sub(1), | 46 | Brush::Custom { ref mut size, .. } => *size = size.saturating_sub(1), |
40 | _ => (), | 47 | _ => (), |
41 | } | 48 | } |
42 | } | 49 | } |
43 | 50 | ||
44 | pub fn new(size: u8) -> Self { | 51 | pub fn new(size: u8) -> Self { |
45 | Brush::Circle { size } | 52 | Brush::Circle(CircleBrush { size }) |
46 | } | 53 | } |
47 | 54 | ||
48 | pub fn line(size: u8, extend: bool) -> Self { | 55 | pub fn line(size: u8, extend: bool) -> Self { |
49 | Brush::Line { | 56 | Brush::Line(LineBrush { |
50 | size, | 57 | size, |
51 | start: None, | 58 | start: None, |
52 | extend, | 59 | extend, |
53 | } | 60 | }) |
54 | } | 61 | } |
55 | 62 | ||
56 | pub fn is_line(&self) -> bool { | 63 | pub fn is_line(&self) -> bool { |
@@ -59,8 +66,8 @@ impl Brush { | |||
59 | 66 | ||
60 | pub fn size(&self) -> Option<u8> { | 67 | pub fn size(&self) -> Option<u8> { |
61 | match self { | 68 | match self { |
62 | Brush::Line { size, .. } => Some(size.clone()), | 69 | Brush::Line(LineBrush { size, .. }) => Some(size.clone()), |
63 | Brush::Circle { size } => Some(size.clone()), | 70 | Brush::Circle(CircleBrush { size }) => Some(size.clone()), |
64 | _ => None, | 71 | _ => None, |
65 | } | 72 | } |
66 | } | 73 | } |
@@ -69,8 +76,10 @@ impl Brush { | |||
69 | impl fmt::Display for Brush { | 76 | impl fmt::Display for Brush { |
70 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | 77 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
71 | match self { | 78 | match self { |
72 | Brush::Line { extend, .. } => write!(f, "LINE{}", if *extend { "+" } else { "" }), | 79 | Brush::Line(LineBrush { extend, .. }) => { |
73 | Brush::Circle { .. } => write!(f, "CIRCLE"), | 80 | write!(f, "LINE{}", if *extend { "+" } else { "" }) |
81 | } | ||
82 | Brush::Circle(..) => write!(f, "CIRCLE"), | ||
74 | Brush::RectSelect { .. } => write!(f, "SELECT"), | 83 | Brush::RectSelect { .. } => write!(f, "SELECT"), |
75 | Brush::Fill => write!(f, "FILL"), | 84 | Brush::Fill => write!(f, "FILL"), |
76 | Brush::Custom { .. } => write!(f, "CUSTOM"), | 85 | Brush::Custom { .. } => write!(f, "CUSTOM"), |