aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/app.rs101
-rw-r--r--src/brush.rs61
2 files changed, 83 insertions, 79 deletions
diff --git a/src/app.rs b/src/app.rs
index 42e63de..ea1e893 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -1,6 +1,6 @@
1use crate::{ 1use 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)]
6pub enum Brush { 6pub 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)]
15pub struct LineBrush {
16 pub size: u8,
17 pub start: Option<MapPoint>,
18 pub extend: bool,
19}
20
21#[derive(Debug, Copy, Clone)]
22pub struct CircleBrush {
23 pub size: u8,
24}
25
26#[derive(Debug, Copy, Clone)]
27pub struct RectSelectBrush {
28 pub start: MapPoint,
29 pub end: MapPoint,
23} 30}
24 31
25impl Brush { 32impl 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 {
69impl fmt::Display for Brush { 76impl 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"),