Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабораторный_практикум.doc
Скачиваний:
74
Добавлен:
15.11.2019
Размер:
45.35 Mб
Скачать

4. Модификация приложения Windows Forms: функциональность растрового редактора

Есть два основных способа работы с изображением и последующим его сохранением. Работа с растровым изображением и работа с векторным изображением. Не путать с типами изображений.

Начнём с того, что любое растровое изображение является простым набором сведений о каждом пикселе.

Растровое изображение — представляющее собой сетку пикселей или цветных точек (обычно прямоугольную) на компьютерном мониторе, бумаге и других отображающих устройствах и материалах (растр).

Важными характеристиками изображения являются:

1. Количество пикселей — количество цветов. Может указываться отдельно количество пикселей по ширине и высоте (1024×768, 640×480, …) или же, редко, общее количество пикселей (часто измеряется в мегапикселях).

2. Количество используемых цветов или глубина цвета (эти характеристики имеют следующую зависимость: N = 2k, где N — количество цветов, а k — глубина цвета);

3. Цветовое пространство (цветовая модель) RGB, CMYK, XYZ, YCbCr и др.

4. Разрешение — справочная величина, говорящая о рекомендуемом размере пикселя изображения.

Растровую графику редактируют с помощью растровых графических редакторов. Создаётся растровая графика фотоаппаратами, сканерами, непосредственно в растровом редакторе, также путём экспорта из векторного редактора или в виде снимка экрана.

Типичный растровый редактор: Paint. Фактически редактор оперирует только точками. Нарисованную линию нельзя передвинуть или растянуть. А всё рисование состоит только из заполнения массива координат и пикселей. Именно так и будет работать наше приложение. Второй же способ, это на этапе рисования (до сохранения в обычные графические форматы типа *.bmp) представлять каждый объект рисунка как самостоятельный объект. Для линии это координаты положения, толщина, цвет и что самое главное — аналоги имени. Таким образом, такую линию можно переместить, растянуть, удалить как объект (ближайший аналог: символ в Word’е). Для приложения такой рисунок предстаёт как векторное изображение, которое можно экспортировать в растровое (данные о примитивах удаляются, остаётся лишь слитое воедино изображение).

Теперь о нашем приложении:

Первая кнопка загружает сторонний рисунок с жёсткого диска через диалог открытия файла. После загрузки рисунок отображается в качестве растрового изображения в PictureBox и этот рисунок становится доступен для редактирования.

Событие Click для элемента меню (Выбрать рисунок) можно создать просто дважды кликнув на элементе меню. Также можно выбрать этот элемент меню, далее на панели Свойства справа перейти на вкладку События и далее ввести имя метода для события Click, либо просто дважды клинуть по Click (Действия):

Рис. 3. 5. Свойства: Событие Click элемента меню Выбрать рисунок

Код события Click для кнопки Выбрать рисунок:

private void выбратьРисунокToolStripMenuItem_Click(object sender, EventArgs e)

{

// Если рисунок был выбран

if (OFD_Picture.ShowDialog() == DialogResult.OK)

{

// Получаем изображения по указанному в диалоге OpenFileDialog пути (OFD_Picture.FileName)

image = Image.FromFile(OFD_Picture.FileName);

int width = image.Width; // Получаем ширину изображения

int height = image.Height; // Получаем высоту изображения

// Устанавливаем размеры изображения в PictureBox

PB_Bitmap.Width = width;

PB_Bitmap.Height = height;

// Создаём Bitmap на основе изображения

bitmap = new Bitmap(Image.FromFile(OFD_Picture.FileName), width, height);

PB_Bitmap.Image = bitmap; // Отправляем Bitmap в PictureBox

StatusLabel.Text = "Изображение " + Path.GetFileName(OFD_Picture.FileName) + " успешно загружено! Полный путь: " + OFD_Picture.FileName;

}

}

Кнопка Сохранить как... сохраняет всё изображение в рабочей области в выбранном формате (BMP-, JPEG-, GIF, TIF- или PGN-файл) и выводит результат в качестве текста в строке состояния.

Событие Click кнопки Сохранить как...:

private void сохранитьКакToolStripMenuItem_Click(object sender, EventArgs e)

{

// Если выбрали, сохраняем

if (SFD_Picture.ShowDialog() == DialogResult.OK)

{

// Получаем имя файла из диалога

string fileName = SFD_Picture.FileName;

// Получаем расширения файла из диалога

string strFilExtn = fileName.Remove(0, fileName.Length - 3);

// Сохраняем файл в выбранном расширении

switch (strFilExtn)

{

case "bmp":

bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Bmp);

break;

case "jpg":

bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Jpeg);

break;

case "gif":

bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Gif);

break;

case "tif":

bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Tiff);

break;

case "png":

bitmap.Save(fileName, System.Drawing.Imaging.ImageFormat.Png);

break;

default:

break;

}

StatusLabel.Text = "Изображение " + Path.GetFileName(SFD_Picture.FileName) + " успешно сохранено! Полный путь: " + SFD_Picture.FileName;

}

}

Событие Click кнопки Выход:

private void выходToolStripMenuItem_Click(object sender, EventArgs e)

{

Close();

}

Кнопка Очистить, полностью очищает рабочую область и Bitmap в которой сохраняются изменения.

Событие Click кнопки Очистить:

private void очиститьToolStripMenuItem_Click(object sender, EventArgs e)

{

// Очищаем рабочее поле

// Создаём пустой Bitmap на основе размеров PictureBox

bitmap = new Bitmap(PB_Bitmap.Size.Width, PB_Bitmap.Size.Height);

// Инициализируем фон для поля рисования

graphics = Graphics.FromImage(bitmap);

graphics.Clear(Color.White); // Задаём белый цвет фона, иначе фон будет прозрачным

//graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; // Выключаем сглаживание

PB_Bitmap.Image = bitmap; // Отправляем Bitmap в PictureBox

StatusLabel.Text = "Рабочее поле очищено";

}

Кнопка Линия активирует режим в котором в PictureBox можно рисовать линии. Код события содержит лишь действия по управлению элементами меню и выводом текстового сообщения в строку состояния. При нажатии, снимается выделение (галочка слева от надписи) с кнопок меню «Рисование»: «Прямоугольник» или «Окружность», если до этого они были активированы. Событие Click:

private void линияToolStripMenuItem_Click(object sender, EventArgs e)

{

if (!DrawLine)

{

DrawLine = true;

линияToolStripMenuItem.Checked = true;

DrawRect = false;

прямоугольникToolStripMenuItem.Checked = false;

DrawCirc = false;

окружностьToolStripMenuItem.Checked = false;

StatusLabel.Text = "Включен режим рисования линии";

}

}

Событие Click кнопки Прямоугольник:

private void прямоугольникToolStripMenuItem_Click(object sender, EventArgs e)

{

if (!DrawRect)

{

DrawLine = false;

линияToolStripMenuItem.Checked = false;

DrawRect = true;

прямоугольникToolStripMenuItem.Checked = true;

DrawCirc = false;

окружностьToolStripMenuItem.Checked = false;

StatusLabel.Text = "Включен режим рисования прямоугольника";

}

}

Событие Click кнопки Окружность:

private void окружностьToolStripMenuItem_Click(object sender, EventArgs e)

{

if (!DrawCirc)

{

DrawLine = false;

линияToolStripMenuItem.Checked = false;

DrawRect = false;

прямоугольникToolStripMenuItem.Checked = false;

DrawCirc = true;

окружностьToolStripMenuItem.Checked = true;

StatusLabel.Text = "Включен режим рисования окружности";

}

}

Кнопка «Непрерывная» включает режим рисования линий при котором предыдущая линия становится началом следующей. Получается ломаная линия. Событие Click этой кнопки выглядит так:

private void непрерывнаяToolStripMenuItem_Click(object sender, EventArgs e)

{

if (!CurveLine)

{

CurveLine = true;

непрерывнаяToolStripMenuItem.Checked = true;

StatusLabel.Text = "Включен режим непрерывной линии";

}

else

{

CurveLine = false;

непрерывнаяToolStripMenuItem.Checked = false;

StatusLabel.Text = "Выключен режим непрерывной линии";

}

}

Кнопка Цвет пера вызывает стандартный диалог Windows по выбору цвета и задаёт цвет главного пера, которым рисуется линия или контуры фигур. Если цвет не выбран, значение устанавливается по умолчанию как чёрный. Событие Click кнопки:

private void цветПераToolStripMenuItem_Click(object sender, EventArgs e)

{

if (CD_Pen.ShowDialog() == DialogResult.OK)

{

Color_Pen = CD_Pen.Color;

Main_Pen = new Pen(Color_Pen, Convert.ToInt16(X));

Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor;

}

else

{

Main_Pen = new Pen(Color.Black, Convert.ToInt16(X));

Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor;

}

}

Кнопка Цвет заливки устанавливает цвет заполнения SolidBrush-объекта для прямоугольника или окружности. Если цвет не выбран, значение сбрасывается на цвет с именем Color.Violet, который выбран в качестве цвета для сбора заливки (заливка не осуществляется и фигура не заполняется цветом.

private void цветЗаливкиToolStripMenuItem_Click(object sender, EventArgs e)

{

if (CD_Fill.ShowDialog() == DialogResult.OK)

{

Color_Fill = CD_Fill.Color;

Fill = new SolidBrush(Color_Fill);

}

else

{

Color_Fill = Color.Violet; // Цвет сбора заливки

Fill = new SolidBrush(Color_Fill);

}

}

Для элемента TextBox (толщинаПераToolStripMenuItem) определены четыре события. Основное событие TextChanged вызывается всякий раз когда будет меняться текст внутри поля. Само поле предназначено для ввода числа отвечающего за толщину пера (положительное значение от 0 и до примерно 1000 пикселей0; после 1000 значение не нельзя будет заменить разницы):

private void толщинаПераToolStripMenuItem_TextChanged(object sender, EventArgs e)

{

try

{

X = Convert.ToDouble(толщинаПераToolStripMenuItem.Text);

Main_Pen = new Pen(Color_Pen, Convert.ToInt16(X));

}

catch { }

}

Событие KeyDown выполняющее операцию сохранения числа в текстовом поле (аналогично предыдущему событию), но после нажатия клавиши Enter:

private void толщинаПераToolStripMenuItem_KeyDown(object sender, KeyEventArgs e)

{

if (e.KeyCode == Keys.Enter) // Если нажат Enter

{

X = Convert.ToDouble(толщинаПераToolStripMenuItem.Text);

Main_Pen = new Pen(Color_Pen, Convert.ToInt16(X));

}

}

События MouseEnter и MouseLeave реализуют визуальное «оформление» элемента. Наводим курсор и получаем значение 1. «1» остаётся, если ничего не вводить в поле или оставить поле пустым:

private void толщинаПераtoolStripMenuItem_MouseEnter(object sender, EventArgs e)

{

if (толщинаПераToolStripMenuItem.Text == "Толщина пера")

{

толщинаПераToolStripMenuItem.Text = "1";

}

}

private void толщинаПераtoolStripMenuItem_MouseLeave(object sender, EventArgs e)

{

if (толщинаПераToolStripMenuItem.Text == "")

{

толщинаПераToolStripMenuItem.Text = "1";

}

}

События кнопок ToolStrip-меню похожи и выполняют роль переопределения вызовов некоторых событий нажатия кнопок верхнего меню. Все события Click по порядку следования кнопок на рисунке 3. 4.:

private void выбратьРисунокToolStripButton_Click(object sender, EventArgs e)

{

выбратьРисунокToolStripMenuItem.PerformClick(); // Вызываем нажатие аналогичной кнопки в MenuStrip

}

private void сохранитьКакToolStripButton_Click(object sender, EventArgs e)

{

сохранитьКакToolStripMenuItem.PerformClick();

}

private void выходToolStripButton_Click(object sender, EventArgs e)

{

выходToolStripMenuItem.PerformClick();

}

private void очиститьToolStripButton_Click(object sender, EventArgs e)

{

очиститьToolStripMenuItem.PerformClick();

}

private void линияToolStripButton_Click(object sender, EventArgs e)

{

линияToolStripMenuItem.PerformClick();

линияToolStripButton.Checked = true;

прямоугольникToolStripButton.Checked = false;

окружностьToolStripButton.Checked = false;

}

private void прямоугольникToolStripButton_Click(object sender, EventArgs e)

{

прямоугольникToolStripMenuItem.PerformClick();

линияToolStripButton.Checked = false;

прямоугольникToolStripButton.Checked = true;

окружностьToolStripButton.Checked = false;

}

private void окружностьToolStripButton_Click(object sender, EventArgs e)

{

окружностьToolStripMenuItem.PerformClick();

линияToolStripButton.Checked = false;

прямоугольникToolStripButton.Checked = false;

окружностьToolStripButton.Checked = true;

}

private void цветПераToolStripButton_Click(object sender, EventArgs e)

{

цветПераToolStripMenuItem.PerformClick();

}

private void цветЗаливкиToolStripButton_Click(object sender, EventArgs e)

{

цветЗаливкиToolStripMenuItem.PerformClick();

}

Теперь перейдём к редактированию непосредственно кода и основным события формы. Для начала откроем код файла LWP14Main.cs (правая кнопка мыши на значке формы, далее Перейти к коду или нажмём на клавишу F7). В самом начале файла добавим ссылку:

using System.IO; // Для работы класса Path

Найдём:

public partial class LWP14Main : Form

{

Добавим после:

// Диалоги

OpenFileDialog OFD_Picture;

SaveFileDialog SFD_Picture;

ColorDialog CD_Pen;

ColorDialog CD_Fill;

// Рабочее поле

Image image;

Graphics graphics;

Bitmap bitmap;

// Инструменты рисования

Pen Main_Pen;

Color Color_Pen;

Color Color_Fill;

// Объекты рисования

Point FirstPoint = new Point();

Point ToPoint = new Point();

Point LastPoint = new Point();

Rectangle SelectRect = new Rectangle();

Rectangle CircleRect = new Rectangle();

Rectangle LightRect = new Rectangle();

SolidBrush Fill;

// Вспомогательные переменные

Boolean DrawLine;

Boolean CurveLine;

Boolean DrawRect;

Boolean DrawCirc;

Double X;

Код метода LWP14Main() изменим следующим образом:

public LWP14Main()

{

InitializeComponent();

// Инициализируем диалоги

OFD_Picture = new OpenFileDialog();

OFD_Picture.Filter = "Файлы изображений (*.bmp, *.jpg, *.gif, *.tif, *.png, *.ico, *.emf, *.wmf)|*.bmp;*.jpg;*.gif; *.tif; *.png; *.ico; *.emf; *.wmf";

SFD_Picture = new SaveFileDialog();

SFD_Picture.Title = "Сохранить как";

SFD_Picture.OverwritePrompt = true;

SFD_Picture.CheckPathExists = true;

SFD_Picture.Filter = "Изображение в формате PNG|*.png|" + "Изображение в формате JPEG|*.jpg|" + "Изображение в формате BMP|*.bmp|" + "Изображение в формате GIF|*.gif|" + "Изображение в формате TIF|*.tif";

CD_Pen = new ColorDialog();

CD_Fill = new ColorDialog();

// Инициализируем рабочее поле

// Создаём пустой Bitmap на основе размеров PictureBox

bitmap = new Bitmap(PB_Bitmap.Size.Width, PB_Bitmap.Size.Height);

// Инициализируем фон для поля рисования

graphics = Graphics.FromImage(bitmap);

graphics.Clear(Color.White); // Задаём белый цвет фона, иначе фон будет прозрачным

//graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.None; // Выключаем сглаживание

PB_Bitmap.Image = bitmap; // Отправляем Bitmap в PictureBox

// Инициализируем инструменты рисования по умолчанию

Main_Pen = new Pen(Color.Black, 1);

Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor; // Не задаём маркер на конце пера

//graphics.DrawLine(Main_Pen, 10, 10, 11, 10); // Рисует точку в один пиксель, при отсутствии маркера на Pen и толщине в 1

Color_Pen = Color.Black;

Color_Fill = Color.Violet;

Fill = new SolidBrush(Color_Fill);

// Инициализируем объекты рисования...

// ...нету их :)

// Инициализируем вспомогательные переменные по умолчанию

DrawLine = true;

линияToolStripMenuItem.Checked = true;

линияToolStripButton.Checked = true;

CurveLine = false;

DrawRect = false;

прямоугольникToolStripButton.Checked = false;

DrawCirc = false;

окружностьToolStripButton.Checked = false;

X = 1;

// Инициализируем прочее

StatusLabel.Text = "Приложение готово к работе";

}

Код выше помимо подготовки диалогов сохранения и открытия изображений, также инициализируем поле для рисования во всём PictureBox. Заполняем при старте PictureBox объектом Bitmap, очищает и задаёт белый фон. Важно понимать, что сохранение происходит не из самого PictureBox, а из объекта растрового изображения, загруженного в качестве текущего PictureBox.Image. Без задания фона пустого Bitmap’а, сохранённое изображение будет иметь чёрный или прозрачный фон, в зависимости от типа сохраняемого изображения (поддержки альфа-канала).

Рисование любого объекта в приложении проходит в три этапа. Первый этап это получение координат начала (начальной точки), затем промежуточный второй этап (движение мыши по рабочей зоне) и третий этап получения координат окончания (конечной точки) и сам процесс отрисовки объекта.

Первый этап реализован в методе PictureBox: MouseDown. Нажатие ЛКМ в любом месте рабочей зоны приводит к сохранению координат (так устроено само событие и его аргументы). В зависимости от выбранного режима эти координаты используются по разному. Для отрисовки линии координаты нажатия отправляются в значения X и Y для объекта Point,а для фигур формируется начальная точка объекта Rectangle. Событие MouseDown для элемента PictureBox (PB_Bitmap) выглядит так:

private void PB_Bitmap_MouseDown(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Right) // Если нажата правая кнопка мыши

{

LightRect.Width = 0;

LightRect.Height = 0;

LightRect.X = e.X;

LightRect.Y = e.Y;

}

if (e.Button == MouseButtons.Left) // Если нажата левая кнопка мыши

{

// Если рисуем линию

if (DrawLine)

{

// Получаем координаты нажатия левой кнопки мыши в PictureBox

FirstPoint.X = e.X;

FirstPoint.Y = e.Y;

ToPoint = FirstPoint;

if (CurveLine)

{

FirstPoint = LastPoint;

}

}

// Если рисуем прямоугольник

if (DrawRect)

{

SelectRect.Width = 0;

SelectRect.Height = 0;

SelectRect.X = e.X;

SelectRect.Y = e.Y;

}

// Если рисуем окружность

if (DrawCirc)

{

CircleRect.Height = 0;

CircleRect.Width = 0;

CircleRect.X = e.X;

CircleRect.Y = e.Y;

}

}

}

Промежуточный этап для наглядности «усовершенствован». Если зажать ЛКМ во время рисования линии, то за курсором от начальной точки будет тянуться чёрная тонкая линия. В случае если будет включен режим рисования прямоугольника или окружности, то вместо линии будет пунктирный прямоугольник. Эти действия реализованы в событии MouseMove для PictureBox:

private void PB_Bitmap_MouseMove(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Right)

{

LightRect.Width = e.X - LightRect.X;

LightRect.Height = e.Y - LightRect.Y;

}

if (e.Button == MouseButtons.Left)

{

// Если рисуем линию, отображаем заготовку линии

if (DrawLine)

{

// Отображаем заготовку линии до тех пор пока не отпустим левую кнопку мыши

ControlPaint.DrawReversibleLine(PB_Bitmap.PointToScreen(FirstPoint), PB_Bitmap.PointToScreen(ToPoint), Color.Black);

ToPoint = new Point(e.X, e.Y);

ControlPaint.DrawReversibleLine(PB_Bitmap.PointToScreen(FirstPoint), PB_Bitmap.PointToScreen(ToPoint), Color.Black);

if (CurveLine) // Убираем возможные "артефакты", возникающие при отрисовки непрерывной линии

PB_Bitmap.Refresh();

}

// Если рисуем прямоугольник, отображаем заготовку прямоугольника пунктирными линиями

if (DrawRect)

{

ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(SelectRect), Color.Black, FrameStyle.Dashed);

SelectRect.Width = e.X - SelectRect.X; // Получаем значение ширины прямоугольника

SelectRect.Height = e.Y - SelectRect.Y; // Получаем значение высоты прямоугольника

ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(SelectRect), Color.Black, FrameStyle.Dashed);

//PB_Bitmap.Refresh();

}

// Если рисуем окружность, отображаем заготовку окружности пунктирными линиями

if (DrawCirc)

{

ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(CircleRect), Color.Black, FrameStyle.Dashed);

CircleRect.Width = e.X - CircleRect.X;

CircleRect.Height = e.Y - CircleRect.Y;

ControlPaint.DrawReversibleFrame(PB_Bitmap.RectangleToScreen(CircleRect), Color.Black, FrameStyle.Dashed);

//PB_Bitmap.Refresh();

}

}

}

Третий этап собственно реализует рисование объекта. После «отжатия» левой кнопки мыши будут получены координаты конечной точки и отрисован объект. Это реализует событий MouseUp для PictureBox:

private void PB_Bitmap_MouseUp(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Right)

{

graphics = Graphics.FromImage(PB_Bitmap.Image);

graphics.DrawRectangle(Main_Pen, LightRect); // Нарисуем прямоугольник-контур для осветлённой области

graphics.Dispose();

PB_Bitmap.Invalidate();

}

if (e.Button == MouseButtons.Left) // Если нажата левая кнопка мыши

{

// Рисуем в PictureBox (только в загруженном в элементе изображении)

graphics = Graphics.FromImage(PB_Bitmap.Image);

if (DrawLine)

{

LastPoint.X = e.X;

LastPoint.Y = e.Y;

graphics.DrawLine(Main_Pen, FirstPoint, LastPoint);

}

if (DrawRect)

{

if (Color_Fill != Color.Violet) { graphics.FillRectangle(Fill, SelectRect); } // Заполнение цветом прямоугольной области ограниченной SelectRect

graphics.DrawRectangle(Main_Pen, SelectRect);

}

if (DrawCirc)

{

if (Color_Fill != Color.Violet) { graphics.FillEllipse(Fill, CircleRect); } // Заполнение цветом эллептической области ограниченной CircleRect

graphics.DrawEllipse(Main_Pen, CircleRect);

}

graphics.Dispose();

PB_Bitmap.Invalidate(); // Обновляем PictureBox

}

}

Реализуем ещё одну интересную функцию, которую «повесим» на событие нажатия правой кнопки мыши в области PictureBox. Код события MouseClick:

private void PB_Bitmap_MouseClick(object sender, MouseEventArgs e)

{

if (e.Button == MouseButtons.Right)

{

StatusLabel.Text = "Произведено осветление зоны";

int newRed, newGreen, newBlue;

Color pixel;

// Операция увеличения яркости может занять много времени, пользователя предупредят, если выбран большой участок

if ((LightRect.Width > 500) || (LightRect.Height > 500))

{

DialogResult result = MessageBox.Show("Выделенная область велика! " + "Изменение яркости может требовать значительного времени!", "Простой растровый редактор (C#) :: Изменения яркости", MessageBoxButtons.OKCancel);

// При нажатии кнопки "Отмена" выходим из метода

// и возвращаемся к месту его вызова

if (result == DialogResult.Cancel) return;

}

/* Перебираем последовательно все пиксели данного участка и удваиваем значение яркости компонент RGB пикселей */

// Перебор по горизонтали слева направо...

for (int x = LightRect.X; x < LightRect.X + LightRect.Width; x++)

{

// и по вертикали сверху вниз...

for (int y = LightRect.Y; y < (LightRect.Y + LightRect.Height); y++)

{

// Считываем текущий пиксель

pixel = bitmap.GetPixel(x, y);

// Увеличиваем яркость цветовых компонент пикселя

newRed = (int)Math.Round(pixel.R * 2.0, 0);

if (newRed > 255) newRed = 255;

newGreen = (int)Math.Round(pixel.G * 2.0, 0);

if (newGreen > 255) newGreen = 255;

newBlue = (int)Math.Round(pixel.B * 2.0, 0);

if (newBlue > 255) newBlue = 255;

// Присваиваем пикселю новые цветовые значения

bitmap.SetPixel(x, y, Color.FromArgb((byte)newRed, (byte)newGreen, (byte)newBlue));

}

}

}

}

Код выше «осветляет» прямоугольную область при зажатой правой кнопке мыши. Вокруг осветлённой области также рисуется прямоугольник толщина пера и цвет общий для остальных фигур.

Наконец, сделаем ластик. Работать он будет при зажатой клавиши Shift (+ нажатие ПКМ в области, которую нужно стереть). Сделаем ластик также ещё и карандашом. Его цвет будет завесить от цвета заливки, а толщина поля «стирания» от толщины пера.

Найдём:

Boolean DrawCirc;

Double X;

Добавим после:

// Ластик

Pen Pen_Erase;

Boolean DrawErase;

SolidBrush Erase;

Rectangle EraseRect = new Rectangle();

Найдём:

// Инициализируем прочее

StatusLabel.Text = "Приложение готово к работе";

Добавим после:

// Ластик

Pen_Erase = new Pen(Color.White, 1);

DrawErase = false;

Erase = new SolidBrush(Color.White);

Найдём:

private void PB_Bitmap_MouseMove(object sender, MouseEventArgs e)

{

Добавим после:

// Ластик

if (e.Button == MouseButtons.Right && DrawErase == true)

{

EraseRect.X = e.X - Convert.ToInt32(X);

EraseRect.Y = e.Y - Convert.ToInt32(X);

EraseRect.Width = Convert.ToInt32(X) * 2;

EraseRect.Height = Convert.ToInt32(X) * 2;

graphics = Graphics.FromImage(PB_Bitmap.Image);

graphics.FillEllipse(Erase, EraseRect);

graphics.Dispose();

PB_Bitmap.Invalidate();

StatusLabel.Text = "Режим ластика/карандаша";

}

Перепишем код события нажатия на кнопку «Цвет заливки»:

private void цветЗаливкиToolStripMenuItem_Click(object sender, EventArgs e)

{

if (CD_Fill.ShowDialog() == DialogResult.OK)

{

Color_Fill = CD_Fill.Color;

Fill = new SolidBrush(Color_Fill);

Erase = new SolidBrush(Color_Fill);

}

else

{

Color_Fill = Color.Violet; // Цвет сбора заливки

Fill = new SolidBrush(Color_Fill);

Erase = new SolidBrush(Color.White);

}

}

А также инициализируем два события главной формы (отлов нажатия и отжатия клавиш левый и правый Shift): KeyDown и KeyUp:

private void LWP14Main_KeyDown(object sender, KeyEventArgs e)

{

if (e.KeyCode == Keys.ShiftKey) // Если нажат Shift

{

DrawErase = true;

}

}

private void LWP14Main_KeyUp(object sender, KeyEventArgs e)

{

if (e.KeyCode == Keys.ShiftKey) // Если нажат Shift

{

DrawErase = false;

}

}

Готово. Можно компилировать и проверять работоспособность.