Графический редактор, история редактирования изображения
Нужно сделать графический редактор типа "Paint". Сделал всё, кроме отмены последнего действия. Отмена действия реализована как помещение битмапа в стек, и при нажатии "Отмена" последний битмап удаляется. Проблема в том, что в 1 случае "Отмена" не работает и битмап не обновляется, во 2 случае "Отмена" работает, но битмап не переприсваивается, и после продолжения рисования битмап ставится с неотменёнными действиями. Как решить? (Отмена последнего действия обязательно должна быть в другом классе)
public partial class Form1 : Form
{
Brush b = new Brush();
public Bitmap bitmap;
Graphics gr;
Point py;
Rectangle rectangle;
Pen p = new Pen(Color.Black, 1);
Pen erase = new Pen(Color.White, 1);
bool isPressed = false;
int Tool;
public Form1()
{
InitializeComponent();
rectangle = Screen.PrimaryScreen.Bounds;
bitmap = new Bitmap(rectangle.Width, rectangle.Height);
gr = Graphics.FromImage(bitmap);
p.StartCap = System.Drawing.Drawing2D.LineCap.Round;
p.EndCap = System.Drawing.Drawing2D.LineCap.Round;
pictureBox1.Image = bitmap;
b.SaveDraw(pictureBox1);
Tool = 1;
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
isPressed = true;
b.StartDraw(e.Location);
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
py = e.Location;
if (isPressed)
{
if (Tool == 1)
{
b.Draw(gr, pictureBox1, bitmap, p, e.Location);
}
if (Tool == 2)
{
b.Draw(gr, pictureBox1, bitmap, erase, e.Location);
}
}
pictureBox1.Refresh();
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
b.SaveDraw(pictureBox1);
isPressed = false;
if (Tool == 5)
{
b.DrawEllipse(gr, pictureBox1, bitmap, p, e.Location);
}
if (Tool == 4)
{
b.DrawRectangle(gr, pictureBox1, bitmap, p, e.Location);
}
if (Tool == 3)
{
b.DrawLine(gr, pictureBox1, bitmap, p, e.Location);
}
}
private void Clear_Click(object sender, EventArgs e)
{
b.Cancel(pictureBox1, bitmap);
}
}
Класс, где не переопределяется битмап
class Brush
{
Point fp;
Stack<Bitmap> bmp = new Stack<Bitmap>();
Stack<Bitmap> bmp2 = new Stack<Bitmap>();
Bitmap bpa;
public void StartDraw(Point fp)
{
this.fp = fp;
}
public void SaveDraw(PictureBox pb)
{
bpa = new Bitmap(pb.Image);
bmp.Push(bpa);
}
public void Draw(Graphics gr, PictureBox pb, Bitmap bp, Pen p, Point sp)
{
gr.DrawLine(p, this.fp, sp);
this.fp = sp;
pb.Image = bp;
}
public void Cancel(PictureBox pb, Bitmap bp)
{
if (bmp.Count > 1)
{
bmp2.Push(bmp.Pop());
pb.Image = bmp.Peek();
bp = bpa;
}
}
}
Класс, где не обновляется битмап
class Brush
{
Point fp;
Stack<Bitmap> bmp = new Stack<Bitmap>();
Stack<Bitmap> bmp2 = new Stack<Bitmap>();
Bitmap bpa;
public void StartDraw(Point fp)
{
this.fp = fp;
}
public void SaveDraw(PictureBox pb)
{
bmp.Push((Bitmap)pb.Image);
}
public void Draw(Graphics gr, PictureBox pb, Bitmap bp, Pen p, Point sp)
{
gr.DrawLine(p, this.fp, sp);
this.fp = sp;
pb.Image = bp;
}
public void Cancel(PictureBox pb, Bitmap bp)
{
if (bmp.Count > 1)
{
bmp2.Push(bmp.Pop());
pb.Image = bmp.Peek();
}
}
}
Ответы (1 шт):
Я не смог разобраться в вашем коде, уж простите.
Набросал короткий пример. Здесь нет классов для рисования, вы можете сделать их сами, а есть только класс для управления историей редактирования.
public class BitmapHistory
{
private readonly PictureBox _pictureBox;
private readonly Stack<Bitmap> _undoHistory;
private readonly Stack<Bitmap> _redoHistory;
public bool CanUndo => _undoHistory.Count > 0;
public bool CanRedo => _redoHistory.Count > 0;
public BitmapHistory(PictureBox pictureBox)
{
_pictureBox = pictureBox;
_undoHistory = new Stack<Bitmap>();
_redoHistory = new Stack<Bitmap>();
Reset();
}
public void Reset()
{
_undoHistory.Clear();
_redoHistory.Clear();
Bitmap bmp = new Bitmap(_pictureBox.Width, _pictureBox.Height, PixelFormat.Format24bppRgb);
using Graphics g = Graphics.FromImage(bmp);
g.FillRectangle(Brushes.White, 0, 0, bmp.Width, bmp.Height);
_pictureBox.Image = bmp;
}
public void Save()
{
Bitmap clone = (Bitmap)_pictureBox.Image.Clone();
_undoHistory.Push(clone);
_redoHistory.Clear();
}
public void Undo()
{
if (CanUndo)
{
_redoHistory.Push((Bitmap)_pictureBox.Image);
_pictureBox.Image = _undoHistory.Pop();
}
}
public void Redo()
{
if (CanRedo)
{
_undoHistory.Push((Bitmap)_pictureBox.Image);
_pictureBox.Image = _redoHistory.Pop();
}
}
}
Очень просто, передаете ему в конструктор PictureBox, далее он все сделает сам. Перед каждым рисованием нужно выполнить Save(). Чтобы отменить изменения, нужно вызвать Undo(), чтобы отменить отменение изменений :), нужно вызвать Redo().
Вот простая форма для демонстрации: 2 кнопки и картинка.
public partial class Form1 : Form
{
private BitmapHistory _history;
private Random _rnd;
private Pen _pen;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_history = new BitmapHistory(pictureBox1);
_rnd = new Random();
_pen = new Pen(Color.Black, 1);
UpdateButtons();
}
private void UpdateButtons()
{
button1.Enabled = _history.CanUndo;
button2.Enabled = _history.CanRedo;
}
private void pictureBox1_Click(object sender, EventArgs e)
{
_history.Save();
using Graphics g = Graphics.FromImage(pictureBox1.Image);
g.DrawEllipse(_pen, _rnd.Next(0, pictureBox1.Width), _rnd.Next(0, pictureBox1.Height), 10, 10);
pictureBox1.Invalidate();
UpdateButtons();
}
private void UndoButton_Click(object sender, EventArgs e)
{
_history.Undo();
UpdateButtons();
}
private void RedoButton_Click(object sender, EventArgs e)
{
_history.Redo();
UpdateButtons();
}
}
Скриншот ничего полезного не покажет, но пусть будет
