Страницы

Поиск по вопросам

понедельник, 17 июня 2019 г.

Рисование на Panel (WInForms)

Здравствуйте, решил вот в качестве практики написать свой аналог Paint'а (рисую на Panel, "обрабатывая" холст через BitMap map, также поставил "заглушку" на PaintBackground, чтобы не вызывалась чистка холста) и столкнулся с рядом проблем:
1) Почему то, если для создания BitMap использовать Graphics.Clear(цвет заливки), то фон отсутствует как таковой, хотя если использовать готовую картинку то все нормально.
2) Никак не могу понять, как сделать отрисовку элементов такой, чтобы "он не рисовал map, в map", а именно это у меня происходит, если я правильно все понимаю.
Прошу совета о смене методики рисования или о том, как исправить вышестоящие проблемы...
Код(убрал все лишнее, чтобы не мешало):
class Canvas : Panel { //################################################################### //Имя объекта типа Canvas на форме - canvas //################################################################### protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); }
protected override void OnPaintBackground(PaintEventArgs e) //"заглушка", если это так называется { } }
//описание формы, на которой находится canvas public partial class MainForm : Form { private bool enableBrush; private PointF coordinate; private PaintMode mode; private Bitmap map; private Color colorBrush;
public enum PaintMode { None, Line, Brush, Rect, Rectangle }
public MainForm() { InitializeComponent(); mode = PaintMode.None; Graphics g = canvas.CreateGraphics(); g.Clear(Color.White); //не отображает
map = new Bitmap(canvas.Width, canvas.Height, g);
colorBrush = Color.Green; enableBrush = false; }
private void canvas_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Left) { coordinate = e.Location; //запоминает позицию курсора при нажатии для рисования enableBrush = true; } }
private void canvas_MouseUp(object sender, MouseEventArgs e) { if(e.Button == MouseButtons.Left) { enableBrush = false; map = new Bitmap(canvas.Width,canvas.Height); canvas.DrawToBitmap(map, new Rectangle(new Point(), canvas.Size)); // "сейвлю" текущую карту в map(BitMap) } }
public void canvas_Paint(object sender, PaintEventArgs e) { if (enableBrush) { switch (mode) { case PaintMode.None: break; case PaintMode.Line: e.Graphics.DrawLine(new Pen(colorBrush), coordinate, canvas.PointToClient(MousePosition)); break; case PaintMode.Brush: break; case PaintMode.Rect: break; case PaintMode.Rectangle: break; default: break; } } }
private void canvas_MouseMove(object sender, MouseEventArgs e) { canvas.Invalidate(); //вызываю перерисовку в каждый момент времени, когда курсор мыши находится над canvas }
private void LineButton_CheckedChanged(object sender, EventArgs e) { if (mode == PaintMode.Line) mode = PaintMode.None; else mode = PaintMode.Line; } }


Ответ

Изображения на рисунке можно разделить на две группы
постоянные, это собственно сама картинка, которая должна в итоге получиться временные, это "технические" изображения, которые могут фиксироваться как основные после завершения некоторого действия. Например при рисовании прямой линии вы можете поворачивать ее вокруг начальной точки.
Эти две группы должны рисоваться на разных слоях. Постоянные можно рисовать разу на Bitmap, а временные поверх Bitmap на самом контроле. Это самый простой вариант, разумеется есть и другие варианты.
Для этого вам понадобится определить такую сущность как "инструмент" (карандаш, для рисования точек, линия и т.д.). Тогда Алгоритм отрисовки будет таким:
Bitmap с зафиксированным изображением. Текущий выбранный инструмент в его текущем состоянии и положении.
Этот порядок обеспечит правильное расположение изображений - инструмент над изображением.
Для инструмента отдельный Bitmap не требуется, нам же не нужно хранить все его промежуточные состояния. На основной Bitmap инструмент переносится только в момент фиксации (клик мышкой по конечной точке линии например).
В идеале вам нужно 3 слоя:
контекст контрола для рисования текущего состояния инструмента Bitmap фиксированного размера, не зависимый от размера редактируемого изображения, для отображения видимой на части редактируемого изображения. Bitmap редактируемого изображения.
При этом отрисовываются на контроле только 1 и 2. 2й заполняется нужным фрагментом в нужном масштабе из 3. На 3 фиксируются результаты применения инструментов.
Если вы хотите поддержку слоев как в PhotoShop, то соответственно по 2 дополнительных Bitmap на каждый слой, но это уже совсем другая история.
Теперь про фон и прочие прелести.
Работа Graphics.Clear(цвет заливки) зависит от того, как и из чего получен объект Graphics. Если вы получаете его из контекста контрола, то будет перекрашен фон контрола, если из контекста Bitmap будет перекрашен Bitmap
Не нужно размещать картинку в качестве фона контрола, это не даст вам возможности сделать нормальный зум и скрол для больших картинок. К тому же большой разницы между выводом фоновой картинки контрола или вызовом Graphics.DrawImage в плане производительности нет.
Чтобы отрисовка не "тормозила" и контрол не "моргал", нужно сделать два обязательных действия:
Включить двойную буферизацию, это не потребует изменять код отрисовки, но уже заметно отразится на плавности отрисовки. Вынести тяжелые операции из обработки события Paint. Эти операции можно делать и без мгновенного отображения.

Комментариев нет:

Отправить комментарий