Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
New Документ Microsoft Word (3).doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
1.34 Mб
Скачать

9.4.Растягивание фигур

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

Шаг 16. Растягивание линий.

Очевидно, что код растягивания надо включать в обработчик перемещения мыши OnMouseMove(). Чтобы нарисовать линию новой длины и/или в ее новом положении надо выполнить два действия: стереть старое изображение линии от начальной точки к предыдущей и нарисовать новую линию от начальной точки к текущему положению указателя мыши.

Чтобы иметь возможность стереть старую линию, надо запоминать ее положение, для чего мы заблаговременно добавили в программу член класса OldPoint. При нажатии ЛКМ необходимо занести в OldPoint текущие координаты мыши. Для этого необходимо добавить в обработчик OnLButtonDown() операторы:

OldPoint.x=point.x; OldPoint.y=point.y; // или OldPoint=point;

Чтобы стереть линию от точки Anchor до точки OldPoint, лучше всего выбрать в контексте устройства бинарный растровый режим R2_NOT и снова нарисовать ту же линию. Что это значит? Бинарный растровый режим управляет тем, как будут выглядеть результаты рисования на экране. Всего таких режимов 16 и R2_NOT – один из них. Включение этого режима приводит к выводу пикселя, цвет которого является инвертированным цветом соответствующего пикселя экрана. Перед установкой нового бинарного режима надо запомнить текущий режим с тем, чтобы потом его восстановить. С учетом сказанного код метода OnMouseMove() должен стать таким:

Тестируйте программу. Обратили внимание, что над черным объектом линия при растягивании выводится белым цветом?

Шаг 17. Растягивание прямоугольников и эллипсов.

Код для выполнения этих операций подобен коду растягивания линий:

Опять-таки, тестируйте программу. Все работает? А если попробовать свернуть окно с рисунком и вновь его развернуть, что случится с изображением. Оно потеряется навеки! Обидно, да?

9.5.Обновление изображения

Метафайлом называется хранящийся в памяти объект, поддерживающий собственный контекст устройства. Любые операции, выполняемые с контекстом устройства, можно продублировать в метафайле. Если в дальнейшем потребуется повторить действия, выполненные пользователем (например, при обновлении изображения), достаточно воспроизвести данный метафайл. В результате такого воспроизведения все графические операции повторяются и изображение восстанавливается.

Шаг 18. Описание и инициализация класса метафайл.

Указатель на контекст устройства для метафайла CMetaFileDC имеет смысл описать в классе документа СPainterDoc. Для этого в заголовочный файл PainterDoc.h добавьте описание указателя:

// PainterDoc.h : interface of the CPainterDoc class

// …

class CPainterDoc : public CDocument

{

// …

// Implementation

public:

virtual ~CPainterDoc();

CMetaFileDC *pMetaFileDC;

В конструкторе класса документа мы должны инициализировать указатель pMetaFileDC, создав объект класса CMetaFileDC:

// PainterDoc.cpp : implementation of the CPainterDoc class

// …

CPainterDoc::CPainterDoc()

{

pMetaFileDC= new CMetaFileDC;

pMetaFileDC->Create();

}

Теперь мы подготовили указатель на контекст метафайла к работе и им можно пользоваться.

Шаг 19. Дублирование графических операций в метафайле.

Все, что пользователь рисует в окне, должно дублироваться в метафайле. Это означает, что при каждом вызове метода для контекста устройства вида необходимо вызвать аналогичный метод для контекста метафайла. (Естественно, что вызовы методов по «растягиванию» фигур дублировать смысла нет.) Например, в случае рисования линий и прямоугольников это делается так:

В приведенном фрагменте метода OnLButtonUp() добавления выделены комментарием /*!*/. Вы должны дополнить метод OnLButtonUp() операторами, обеспечивающими запись в метафайл кода рисования эллипса и заливки фигур по аналогии с записью линий и прямоугольников. Кроме того, Вы должны добавить код записи в метафайл фигуры произвольной формы, для чего надо модифицировать метод OnMouseMove() по аналогии с тем, как изображения записываются в метафайл в методе OnLButtonUp().

Так как указатель pMetaFileDC объявлен в классе документа, то для его использования надо иметь ссылку на объект документ типа CPainterDoc. Такую ссылку получают с помощью специально предназначенного для этой цели метода CPainterView:: GetDocument():

CPainterDoc *pDoc = GetDocument();

Обратите внимание на тот факт, что объект документ создавать не нужно и нельзя, так же как и уничтожать его: эту работу выполняет каркас приложения. Можно и нужно только получить указатель на этот объект. Реализацию метода CPainterView::GetDocument() Вы можете посмотреть в файле PainterView.h. А где находится еще одна реализация метода CPainterView::GetDocument()?

Вопрос. Существует объект класса вид, равно как и объект документ. Где они описаны?

Соберите программу и проверьте ее работу. Что-нибудь изменилось? Попробуйте что-нибудь нарисовать, свернуть окно и восстановить. Рисунок восстановился или нет? Если восстановился, позовите инженера: пусть он проверит компьютер.

Шаг 20. Воспроизведение метафайла.

За обновление изображения отвечает метод CPainterView::OnDraw(), который мы пока не модифицировали и он практически ничего не делает. А Вы еще не забыли, как тогда изображение появляется в окне вида. Если забыли – вновь просмотрите шаг 10.

Для того чтобы изображение в окне вида восстанавливалось, надо просто в методе CPainterView::OnDraw() воспроизвести метафайл.

Замечание. Вспомним, что метафайлом называют независимый от устройств формат графического файла, позволяющий сочетать растровое и векторное описание изображения. Существует несколько форматов метафайлов, например, Computer Graphics Metafile (файлы с расширением .CGM), Windows Metafile (файлы с расширением .WMF). Windows Metafile хранит не само изображение, а команды GDI, используемые для его создания.

Подобно любому другому файлу, метафайл надо открывать (или создавать) и закрывать. Очевидно, что сначала в методе OnDraw() метафайл надо закрыть, а затем воспроизвести его. Воспроизведение метафайла выполняет метод PlayMetaFile(), единственным параметром которого является дескриптор (логический номер) метафайла, который, в частности, возвращает метод закрытия метафайла Close().

Итак, для воспроизведения метафайла метод OnDraw() надо привести к следующему виду:

Код функции OnDraw() нуждается в некоторых комментариях.

Во-первых, «стандартным» приемом поиска ошибки является отладка, однако этот способ не годится для трассировки метода OnDraw(), так как он будет вызываться постоянно для перерисовки изображения. В этом случае удобно воспользоваться отладочной печатью, применив для этой цели макрос TRACE. Параметры у этого макроса такие же, как и у функции printf(). Ценным качеством TRACE является тот факт, что он работает только в отладочной версии программы (и игнорируется в окончательной сборке) и выводит сообщения на вкладку Debug окна вывода Output.

Во-вторых, когда Вы используете в своей программе указатели, то хорошим стилем программирования считается проверка корректности указателя перед его использованием: указатель не должен быть равен 0. Обратите внимание, например, на макрос ASSERT_VALID(pDoc), который имеется в методе OnDraw(). Что он делает? Да проверяет корректность указателя pDoc (только в отладочной версии приложения). Если указатель некорректен, выводится окно с сообщением об ошибке (рис. 7), текст в котором начинается с “Debug Assertion Failed”.

Рис. 7. Окно с сообщением об ошибке, инициированное макросом ASSERT_VALID

Если Вы внимательно посмотрите на код скелета приложения, сгенерированный ИС, то обнаружите там немалое число макросов ASSERT_VALID, ASSERT, методов AssertValid(), проверок типа if (!CDocument::OnNewDocument()) и т.п.

Сравнительно длинный код метода OnDraw() объясняется следующим. Для того чтобы можно было воспроизвести метафайл (т.е. восстановить изображение в окне) с помощью метода PlayMetaFile(), этот файл надо предварительно закрыть. Далее, класс CMetaFileDC не имеет метода открытия метафайла для дописывания в него новых изображений (точнее, команд генерации этих изображений). Выход такой: надо создать новый метафайл и воспроизвести в нем старый, для которого у нас есть логический номер MetaFileHandle. После этого можно подменить старый метафайл новым и удалить старый метафайл.

Соберите и тестируйте приложение. Проверьте, что однажды созданное изображение сохраняется при любых трансформациях окна: сворачивании, разворачивании, изменении размеров и т.д.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]