MDIPaint — настольное приложение для редактирования растровых изображений, реализованное на C# с использованием Windows Forms и паттерна MDI (Multiple Document Interface). Позволяет одновременно работать с несколькими изображениями в отдельных дочерних окнах внутри единого главного окна.
| Технология | Назначение |
|---|---|
| C# / .NET Framework 4.7.2 | Язык и платформа |
| Windows Forms | Графический интерфейс |
| System.Drawing | Работа с графикой и растровыми данными |
| System.Drawing.Imaging | Форматы сохранения файлов |
Сторонние зависимости (NuGet-пакеты) не используются.
MDIPaint/
├── Program.cs — точка входа в приложение
├── MainForm.cs — главная MDI-форма
├── MainForm.Designer.cs — инициализация интерфейса главной формы
├── DocumentForm.cs — дочерняя форма с холстом рисования
├── DocumentForm.Designer.cs
├── CanvasSizeForm.cs — диалог изменения размера холста
├── CanvasSizeForm.Designer.cs
├── TextInputForm.cs — диалог ввода текста для инструмента «Текст»
├── TextInputForm.Designer.cs
├── AboutForm.cs — окно «О программе»
├── AboutForm.Designer.cs
└── MDIPaint.csproj — файл проекта
Приложение построено по классическому MDI-паттерну Windows Forms:
MainForm (IsMdiContainer = true)
│
├── DocumentForm (дочернее окно #1)
├── DocumentForm (дочернее окно #2)
└── ...
Диалоги: CanvasSizeForm, TextInputForm, AboutForm
MainForm — родительский контейнер. Хранит глобальные параметры рисования через статические свойства и управляет дочерними окнами через меню и панель инструментов.
DocumentForm — каждый открытый документ живёт в своём дочернем окне. Содержит Bitmap как основной холст, обрабатывает события мыши для рисования, поддерживает масштабирование и управляет сохранением файла.
Взаимодействие между формами прямое: DocumentForm читает статические свойства MainForm для получения параметров рисования и вызывает ((MainForm)MdiParent).UpdateStatus(...) для обновления строки состояния.
| Инструмент | Клавиша | Описание | Режим заливки |
|---|---|---|---|
| Перо | P | Свободное рисование линией | — |
| Линия | L | Прямая линия / залитый прямоугольник | ✓ |
| Эллипс | E | Эллипс контуром или залитый | ✓ |
| Ластик | R | Стирание круглой областью (радиус 10 пкс) | — |
| Масштаб+ | + | Увеличение масштаба (до 8×) | — |
| Масштаб- | - | Уменьшение масштаба (до 0.25×) | — |
| Текст | T | Вставка текста с выбором шрифта и размера | — |
| Ведро с краской | F | Заливка замкнутой области (flood fill) | — |
| Стрелка | A | Стрелка с наконечником / закрашенным острием | ✓ |
Кнопка «Заливка» на панели инструментов включает режим заливки фигур для инструментов, поддерживающих его (✓).
Файл: Program.cs
Точка входа. Инициализирует визуальные стили Windows и запускает главную форму.
Файл: MainForm.cs
Главное окно приложения (MDI-контейнер). Содержит меню, панель инструментов и строку состояния.
| Свойство | Тип | По умолчанию | Описание |
|---|---|---|---|
PenColor |
Color |
Color.Black |
Текущий цвет пера |
PenWidth |
int |
3 |
Толщина пера (1–100 пикселей) |
CurrentTool |
string |
"Pen" |
Активный инструмент рисования |
FillShape |
bool |
false |
Режим заливки фигур |
ZoomFactor |
float |
1.0f |
Текущий масштаб отображения (0.25–8.0) |
| Метод | Описание |
|---|---|
UpdateStatus(x,y,w,h) |
Обновляет строку состояния. Вызывается из DocumentForm при движении мыши. |
UpdateToolStatus() |
Обновляет название текущего инструмента в строке состояния. |
UpdateCursor() |
Устанавливает курсор активного документа в соответствии с текущим инструментом. |
SetTool(tool) |
Переключает инструмент, обновляет статус и курсор. |
ProcessCmdKey(...) |
Перехватывает горячие клавиши P/L/E/R/T/F/A/+/−. |
Файл
├── Создать (Ctrl+N)
├── Открыть... (Ctrl+O)
├── Сохранить (Ctrl+S) — активно при открытом документе
├── Сохранить как... (Ctrl+Shift+S)
└── Выход (Alt+F4)
Рисунок
└── Размер холста... — активно при открытом документе
Инструменты
├── Перо / Линия / Эллипс / Ластик
├── ─────────────
├── Масштаб+ / Масштаб-
├── Текст
├── Ведро с краской
└── Стрелка
Цвет
├── Чёрный / Красный / Синий / Зелёный
└── Другой...
Окно
├── Каскад / Горизонтально / Вертикально / Упорядочить значки
└── [список дочерних окон]
Справка
└── О программе...
Файл: DocumentForm.cs
Дочерняя форма. Представляет один открытый документ. Содержит холст (Bitmap) и реализует всю логику рисования.
| Поле | Тип | Назначение |
|---|---|---|
bitmap |
Bitmap |
Основной холст — постоянное хранилище пикселей |
previewBitmap |
Bitmap |
Временная копия для предпросмотра линии / эллипса / стрелки |
startX, startY |
int |
Координаты точки нажатия кнопки мыши (в координатах холста) |
isDrawing |
bool |
Флаг: идёт ли рисование в данный момент |
isModified |
bool |
Флаг несохранённых изменений |
filePath |
string |
Путь к файлу (null для нового документа) |
zoom |
float |
Текущий масштаб отображения холста |
ZoomLevels |
float[] static |
Допустимые уровни масштаба: 0.25, 0.5, 1, 2, 4, 8 |
MouseDown
- ZoomIn / ZoomOut: вызывает
ApplyZoomи выходит. - Text: открывает
TextInputForm, рисует строку наbitmapи выходит. - Fill: запускает
FloodFillи выходит. - Остальные инструменты: запоминает
(startX, startY)в координатах холста, устанавливаетisDrawing = true.
MouseMove
| Инструмент | Действие |
|---|---|
| Pen | Рисует отрезок на bitmap, обновляет стартовую точку. |
| Eraser | Закрашивает белым эллипс радиуса 10 пкс на bitmap. |
| Line/Ellipse/Arrow | Клонирует bitmap → previewBitmap, рисует фигуру для предпросмотра. |
MouseUp
- Для Line / Ellipse / Arrow: рисует итоговую фигуру на основном
bitmap. - Сбрасывает
previewBitmap, устанавливаетisModified = true.
- Уровни масштаба: 0.25×, 0.5×, 1×, 2×, 4×, 8× (переключение кликом).
- Отрисовка использует
InterpolationMode.NearestNeighbor— чёткие пиксели без размытия. AutoScrollMinSizeобновляется при каждом изменении масштаба — прокрутка при zoom > 1.- Координаты мыши переводятся в координаты холста через
ScreenToCanvas(sx, sy):canvasX = (screenX - scrollOffset.X) / zoom.
- Итеративный алгоритм на основе
Stack<Point>— нет рискаStackOverflowна больших областях. - 4-связная связность (вверх / вниз / влево / вправо).
- Заменяет все пиксели цвета
targetColorнаfillColorначиная от точки клика. - Если
targetColor == fillColor— операция пропускается. - Реализовано через
LockBits+Marshal.Copy— пиксели обрабатываются как массивint[]без вызоваGetPixel/SetPixelна каждый пиксель, что даёт прирост скорости в ~100× по сравнению с наивной реализацией.
- Рисует основную линию от
(x1, y1)до(x2, y2). - Добавляет два луча наконечника под углом ±π/7 (~25.7°) длиной 15 пикселей.
- При
FillShape = trueнаконечник закрашивается как залитый треугольник.
| Метод | Поведение |
|---|---|
OpenFile(path) |
Загружает изображение через new Bitmap(path). Обновляет заголовок. |
SaveFile() |
Сохраняет по существующему filePath или вызывает SaveFileAs. |
SaveFileAs() |
Показывает диалог сохранения (BMP / JPEG), обновляет filePath и заголовок. |
Поддерживаемые форматы:
| Операция | Форматы |
|---|---|
| Открытие | BMP, JPG, JPEG, PNG |
| Сохранение | BMP, JPEG |
Создаёт новый Bitmap заданного размера, заливает белым, копирует старое содержимое. Пересчитывает AutoScrollMinSize для текущего масштаба.
При isModified = true показывает диалог «Сохранить изменения?». Ответ «Нет» — закрывает без сохранения, «Отмена» — отменяет закрытие.
Файл: CanvasSizeForm.cs
Модальный диалог изменения размера холста. Содержит два NumericUpDown (диапазон 1–9999).
Файл: TextInputForm.cs
Модальный диалог для инструмента «Текст». Позволяет задать:
- Текст — строка для вставки на холст.
- Шрифт — выпадающий список: Arial, Times New Roman, Courier New, Verdana, Tahoma.
- Размер — кегль шрифта от 6 до 144 пт (по умолчанию 12).
Файл: AboutForm.cs
Стандартный диалог «О программе».
MouseDown → startX/Y = ScreenToCanvas(e.X, e.Y), isDrawing = true
MouseMove → DrawLine(bitmap, startX,Y → canvas.X,Y)
startX/Y = canvas.X,Y
Invalidate()
MouseUp → isModified = true, isDrawing = false
MouseDown → startX/Y = ScreenToCanvas(e.X, e.Y), isDrawing = true
MouseMove → previewBitmap = bitmap.Clone()
DrawShape(previewBitmap, startX,Y → canvas)
Invalidate() // показываем предпросмотр
MouseUp → DrawShape(bitmap, startX,Y → canvas)
previewBitmap.Dispose()
isModified = true
MouseDown (ZoomIn/ZoomOut)
└─ ApplyZoom(bool zoomIn)
├─ найти текущий индекс в ZoomLevels
├─ idx++ / idx--
└─ Zoom = ZoomLevels[idx]
├─ AutoScrollMinSize = bitmap.Size * zoom
└─ Invalidate()
MouseDown (Text)
└─ TextInputForm.ShowDialog()
└─ OK → DrawString(bitmap, text, font, brush, canvasX, canvasY)
isModified = true
MouseDown (Fill)
└─ targetColor = bitmap.GetPixel(x, y)
FloodFill(x, y, targetColor, PenColor)
└─ Stack<Point> итерация 4-связная:
SetPixel(p, fillColor) → push соседей
Требования: Visual Studio 2019+ с поддержкой .NET Framework 4.7.2 (или MSBuild 15+).
1. Открыть MDIPaint-main_v2.sln в Visual Studio
2. Сборка → Собрать решение (Ctrl+Shift+B)
3. Запуск: F5 (с отладчиком) или Ctrl+F5 (без отладчика)
Через MSBuild из командной строки:
msbuild MDIPaint.csproj /p:Configuration=Debug
Исполняемый файл после сборки: bin\Debug\MDIPaint.exe
- Инструменты: добавлены «Ведро с краской» (F), «Текст» (T), «Стрелка» (A), масштабирование (+/−).
- Flood Fill: алгоритм заливки переведён на
LockBits— устранено зависание при работе с изображениями любого размера. - TextInputForm: диалог ввода текста добавлен в проект (
MDIPaint.csproj). - Предпросмотр: линия, эллипс и стрелка отображаются в реальном времени при перетаскивании мыши.