Модификация приложения WindowsForms: функциональность растрового редактора
Есть два основных способа работы с изображением и последующим его сохранением. Работа с растровым изображением и работа с векторным изображением. Не путать с типами изображений.
Начнём с того, что любое растровое изображение является простым набором сведений о каждом пикселе.
Растровое изображение — представляющее собой сетку пикселей или цветных точек (обычно прямоугольную) на компьютерном мониторе, бумаге и других отображающих устройствах и материалах (растр).
Важными характеристиками изображения являются:
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для кнопки Выбрать рисунок:
privatevoidвыбратьРисунок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 = newBitmap(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кнопки Сохранить как...:
privatevoidсохранитьКак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кнопкиВыход:
privatevoidвыходToolStripMenuItem_Click(object sender, EventArgs e)
{
Close();
}
Кнопка Очистить, полностью очищает рабочую область и Bitmapв которой сохраняются изменения.
СобытиеClickкнопкиОчистить:
privatevoidочиститьToolStripMenuItem_Click(object sender, EventArgs e)
{
// Очищаем рабочее поле
// Создаём пустой Bitmap на основе размеров PictureBox
bitmap = newBitmap(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:
privatevoidлиния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кнопкиПрямоугольник:
privatevoidпрямоугольникToolStripMenuItem_Click(objectsender, EventArgse)
{
if (!DrawRect)
{
DrawLine = false;
линияToolStripMenuItem.Checked = false;
DrawRect = true;
прямоугольникToolStripMenuItem.Checked = true;
DrawCirc = false;
окружностьToolStripMenuItem.Checked = false;
StatusLabel.Text = "Включенрежимрисованияпрямоугольника";
}
}
СобытиеClickкнопкиОкружность:
privatevoidокружностьToolStripMenuItem_Click(objectsender, EventArgse)
{
if (!DrawCirc)
{
DrawLine = false;
линияToolStripMenuItem.Checked = false;
DrawRect = false;
прямоугольникToolStripMenuItem.Checked = false;
DrawCirc = true;
окружностьToolStripMenuItem.Checked = true;
StatusLabel.Text = "Включенрежимрисованияокружности";
}
}
Кнопка «Непрерывная» включает режим рисования линий при котором предыдущая линия становится началом следующей. Получается ломаная линия. Событие Clickэтой кнопки выглядит так:
privatevoidнепрерывнаяToolStripMenuItem_Click(objectsender, EventArgse)
{
if (!CurveLine)
{
CurveLine = true;
непрерывнаяToolStripMenuItem.Checked = true;
StatusLabel.Text = "Включенрежимнепрерывнойлинии";
}
else
{
CurveLine = false;
непрерывнаяToolStripMenuItem.Checked = false;
StatusLabel.Text = "Выключенрежимнепрерывнойлинии";
}
}
Кнопка Цвет пера вызывает стандартный диалог Windowsпо выбору цвета и задаёт цвет главного пера, которым рисуется линия или контуры фигур. Если цвет не выбран, значение устанавливается по умолчанию как чёрный. СобытиеClickкнопки:
privatevoidцветПераToolStripMenuItem_Click(object sender, EventArgs e)
{
if (CD_Pen.ShowDialog() == DialogResult.OK)
{
Color_Pen = CD_Pen.Color;
Main_Pen = newPen(Color_Pen, Convert.ToInt16(X));
Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor;
}
else
{
Main_Pen = newPen(Color.Black, Convert.ToInt16(X));
Main_Pen.EndCap = System.Drawing.Drawing2D.LineCap.NoAnchor;
}
}
Кнопка Цвет заливки устанавливает цвет заполнения SolidBrush-объекта для прямоугольника или окружности. Если цвет не выбран, значение сбрасывается на цвет с именем Color.Violet, который выбран в качестве цвета для сбора заливки (заливка не осуществляется и фигура не заполняется цветом.
privatevoidцветЗаливкиToolStripMenuItem_Click(object sender, EventArgs e)
{
if (CD_Fill.ShowDialog() == DialogResult.OK)
{
Color_Fill = CD_Fill.Color;
Fill = newSolidBrush(Color_Fill);
}
else
{
Color_Fill = Color.Violet; // Цветсборазаливки
Fill = newSolidBrush(Color_Fill);
}
}
Для элемента TextBox (толщинаПераToolStripMenuItem) определены четыре события. Основное событие TextChangedвызывается всякий раз когда будет меняться текст внутри поля. Само поле предназначено для ввода числа отвечающего за толщину пера (положительное значение от 0 и до примерно 1000 пикселей0; после 1000 значение не нельзя будет заменить разницы):
privatevoidтолщинаПераToolStripMenuItem_TextChanged(object sender, EventArgs e)
{
try
{
X = Convert.ToDouble(толщинаПераToolStripMenuItem.Text);
Main_Pen = newPen(Color_Pen, Convert.ToInt16(X));
}
catch { }
}
Событие KeyDownвыполняющее операцию сохранения числа в текстовом поле (аналогично предыдущему событию), но после нажатия клавиши Enter:
privatevoidтолщинаПераToolStripMenuItem_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter) // Еслинажат Enter
{
X = Convert.ToDouble(толщинаПераToolStripMenuItem.Text);
Main_Pen = newPen(Color_Pen, Convert.ToInt16(X));
}
}
События MouseEnterиMouseLeaveреализуют визуальное «оформление» элемента. Наводим курсор и получаем значение 1. «1» остаётся, если ничего не вводить в поле или оставить поле пустым:
privatevoidтолщинаПераtoolStripMenuItem_MouseEnter(object sender, EventArgs e)
{
if (толщинаПераToolStripMenuItem.Text == "Толщинапера")
{
толщинаПераToolStripMenuItem.Text = "1";
}
}
privatevoidтолщинаПераtoolStripMenuItem_MouseLeave(object sender, EventArgs e)
{
if (толщинаПераToolStripMenuItem.Text == "")
{
толщинаПераToolStripMenuItem.Text = "1";
}
}
События кнопок ToolStrip-меню похожи и выполняют роль переопределения вызовов некоторых событий нажатия кнопок верхнего меню. Все события Click по порядку следования кнопокна рисунке 3. 4.:
privatevoidвыбратьРисунокToolStripButton_Click(objectsender, EventArgse)
{
выбратьРисунокToolStripMenuItem.PerformClick(); // Вызываем нажатие аналогичной кнопки в MenuStrip
}
privatevoidсохранитьКакToolStripButton_Click(object sender, EventArgs e)
{
сохранитьКакToolStripMenuItem.PerformClick();
}
privatevoidвыходToolStripButton_Click(object sender, EventArgs e)
{
выходToolStripMenuItem.PerformClick();
}
privatevoidочиститьToolStripButton_Click(object sender, EventArgs e)
{
очиститьToolStripMenuItem.PerformClick();
}
privatevoidлинияToolStripButton_Click(object sender, EventArgs e)
{
линияToolStripMenuItem.PerformClick();
линияToolStripButton.Checked = true;
прямоугольникToolStripButton.Checked = false;
окружностьToolStripButton.Checked = false;
}
privatevoidпрямоугольникToolStripButton_Click(object sender, EventArgs e)
{
прямоугольникToolStripMenuItem.PerformClick();
линияToolStripButton.Checked = false;
прямоугольникToolStripButton.Checked = true;
окружностьToolStripButton.Checked = false;
}
privatevoidокружностьToolStripButton_Click(object sender, EventArgs e)
{
окружностьToolStripMenuItem.PerformClick();
линияToolStripButton.Checked = false;
прямоугольникToolStripButton.Checked = false;
окружностьToolStripButton.Checked = true;
}
privatevoidцветПераToolStripButton_Click(object sender, EventArgs e)
{
цветПераToolStripMenuItem.PerformClick();
}
privatevoidцветЗаливкиToolStripButton_Click(object sender, EventArgs e)
{
цветЗаливкиToolStripMenuItem.PerformClick();
}
Теперь перейдём к редактированию непосредственно кода и основным события формы. Для начала откроем код файла LWP14Main.cs (правая кнопка мыши на значке формы, далее Перейти к коду или нажмём на клавишу F7). В самом начале файла добавим ссылку:
using System.IO; // Для работы класса Path
Найдём:
publicpartialclassLWP14Main : 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 = newPoint();
Point ToPoint = newPoint();
Point LastPoint = newPoint();
Rectangle SelectRect = newRectangle();
Rectangle CircleRect = newRectangle();
Rectangle LightRect = newRectangle();
SolidBrushFill;
// Вспомогательныепеременные
BooleanDrawLine;
Boolean CurveLine;
Boolean DrawRect;
Boolean DrawCirc;
Double X;
КодметодаLWP14Main()изменимследующимобразом:
publicLWP14Main()
{
InitializeComponent();
// Инициализируемдиалоги
OFD_Picture = newOpenFileDialog();
OFD_Picture.Filter = "Файлыизображений (*.bmp, *.jpg, *.gif, *.tif, *.png, *.ico, *.emf, *.wmf)|*.bmp;*.jpg;*.gif; *.tif; *.png; *.ico; *.emf; *.wmf";
SFD_Picture = newSaveFileDialog();
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 = newColorDialog();
CD_Fill = newColorDialog();
// Инициализируем рабочее поле
// Создаём пустой Bitmap на основе размеров PictureBox
bitmap = newBitmap(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 = newPen(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 = newSolidBrush(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) выглядиттак:
privatevoid 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:
privatevoid 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 = newPoint(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:
privatevoid 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:
privatevoid 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;
Добавим после:
// Ластик
PenPen_Erase;
Boolean DrawErase;
SolidBrush Erase;
Rectangle EraseRect = newRectangle();
Найдём:
// Инициализируем прочее
StatusLabel.Text = "Приложение готово к работе";
Добавимпосле:
// Ластик
Pen_Erase = newPen(Color.White, 1);
DrawErase = false;
Erase = newSolidBrush(Color.White);
Найдём:
privatevoid 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 = "Режимластика/карандаша";
}
Перепишем код события нажатия на кнопку «Цвет заливки»:
privatevoidцветЗаливкиToolStripMenuItem_Click(object sender, EventArgs e)
{
if (CD_Fill.ShowDialog() == DialogResult.OK)
{
Color_Fill = CD_Fill.Color;
Fill = newSolidBrush(Color_Fill);
Erase = newSolidBrush(Color_Fill);
}
else
{
Color_Fill = Color.Violet; // Цветсборазаливки
Fill = newSolidBrush(Color_Fill);
Erase = newSolidBrush(Color.White);
}
}
А также инициализируем два события главной формы (отлов нажатия и отжатия клавиш левый и правый Shift): KeyDownи KeyUp:
privatevoid LWP14Main_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey) // Еслинажат Shift
{
DrawErase = true;
}
}
privatevoid LWP14Main_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.ShiftKey) // Еслинажат Shift
{
DrawErase = false;
}
}
Готово. Можно компилировать и проверять работоспособность.
Завершающая часть
Компилируем приложение (Release) и запускаем. Рисуем что хотим, при помощи всех доступных инструментов, осветляем области и жмём Сохранить как..., после чего сохраняем творение на диск и проверяем что и как сохранилось:
Рис. 5. 1. Модифицированное приложение WindowsForms: рисуем
Сохранённый документ в формате *.gif (Рис. 5. 2):
Рис. 5. 2. Результат работы приложения: изображение Рисунок.gif, открытое в стандартном редакторе изображений Windows 7
Теперь жмём Очистить и далее откроем при помощи нашего приложения сторонний рисунок:
Рис. 5. 3. Модифицированное приложение WindowsForms: загружаем сторонний рисунок
И осветляем его:
Рис. 5. 4. Модифицированное приложение WindowsForms: осветляем сторонний рисунок
Сохраняем полученный рисунок и проверяем, всё ли сохранилось.
Рис. 5. 5. Модифицированное приложение WindowsForms: применяем «ластик/карандаш» с толщиной пера в 25 пикселей (Shift+ПКМ) на заново открытом рисунке