- •О.С. Зеленський
- •Розділ 1. Загальні відомості створення додатку windows
- •1.1. Створення додатку Windows за допомогою майстра AppWizard
- •1.2. Варіанти майстрів для різних проектів
- •1.3. Короткий опис sdi програми
- •1.4. Короткий опис mdi програми
- •1.5. Короткий опис простого діалогового додатку
- •Контрольні питання
- •Розділ 2. Повідомлення і команди
- •2.1. Обробка повідомлень
- •2.2. Цикли обробки повідомлень
- •2.3. Карти повідомлень
- •Контрольні питання
- •Розділ 3. Документи та види
- •3.1. Клас додатку
- •3.2. Клас головного вікна
- •3.3. Клас документа
- •3.4. Класи виду
- •Контрольні питання
- •Розділ 4. Робота з клавіатурою, мишею і меню
- •4.1. Робота з клавіатурою
- •4.2. Робота з мишею
- •4.3. Робота з меню
- •Контрольні питання
- •Розділ 5. Виведення на екран
- •5.1. Класи графічних об'єктів
- •5.2. Робота зі шрифтами
- •5.3. Робота з пензликами та малювання графічних фігур
- •5.4. Робота з пензликом
- •5.5. Робота зі скролінгом
- •5.6. Приклад роботи з таблицями
- •5.7. Малювання на екрані маніпулятором "миша"
- •5.8. Завантаження та виведення на екран бітових зображень
- •5.9. Копіювання бітових образів
- •5.10. Малювання графічних об'єктів з використанням резинових контурів та метафайлів
- •5.11. Виділення графічних об'єктів у прямокутній області
- •5.12. Універсальний приклад роботи з двовимірною графікою з використанням резинового контуру
- •5.13. Запис на диск та зчитування з диску графічних об'єктів
- •5.14. Побудова кругових діаграм і гістограм
- •5.15. Користувацький режим роботи з графікою на прикладі малювання годинника Clock
- •Контрольні питання
- •Завдання
- •Розділ 6. Друк і попередній перегляд документів
- •6.1. Вибір і налаштування параметрів друку
- •6.2. Створення контекста пристрою
- •6.3. Друк документів і бібліотека mfc
- •6.4. Масштабування
- •6.5. Друк багатосторінкового документа
- •Контрольні питання
- •Розділ 7. Робота з файлами
- •7.1. Приклад роботи з файлами на основі класів cFile, cStdioFile та потоку fstream
- •7.1.1. Робота з класом cFile
- •7.1.2. Робота з потоком fstream
- •Можливі режими доступу
- •7.1.3. Робота з класом cStdioFile
- •7.2. Серіалізація даних, клас cArchive
- •7.3. Використання реєстру в додатках
- •Контрольні питання
- •Завдання
- •Розділ 8. Діалогові вікна
- •8.1. Створення діалогового вікна та простіші елементи керування
- •8.2. Робота зі списками і комбінованими полями
- •8.3. Ускладнений приклад зі списками
- •8.4. Робота з повзунками
- •8.5. Виведення бітових матриць в діалозі та у вікні виду
- •8.6. Лінійний регулятор, лінійний індикатор, інкриментний регулятор
- •8.7. Стандартні діалоги вибору файлів, шрифтів та кольору
- •8.8. Взаємоз'вязок діалога, документа та виду при розробці додатку
- •8.8.1. Клас cDialDoc
- •8.8.2. Клас cDialView
- •8.8.3. Клас Cdlg
- •8.9. Формування вхідного документа на основі діалогу
- •Контрольні питання
- •Завдання Робота з типовими елементами керування
- •Робота зі списками і комбінованими полями
- •Список літератури
5.13. Запис на диск та зчитування з диску графічних об'єктів
Приклад знаходиться у папці DISK\GDI\GDI11.
Для запису графічних об'єктів на диск використаємо програму розроблену у п. 5.3, де відбувалося малювання графічних об’єктів.
У пункті 5.9. було розглянуто виведення та формування бітових зображень у формі прямокутників. Метою даної програми є малювання графічних об’єктів у вікні з подальшим збереженням їх на диск у форматі *.bmp. Як зазначалося раніше, для виведення бітових зображень необхідно мати два контексти: контекст для малювання pDC та контекст пам’яті memDC. Так, зображення можна виводити з одного контексту в інший і навпаки.
Для формування та зберігання бітового зображення нам буде потрібен об'єкт m_Bit класу CBitmap. Для виведення графіки оголосимо змінну kod_graf типу int. Додамо їх у клас вікна виду:
class CGrafView : public CView
{
.............................................................
CBitmap m_Bit;
int kod_graf;
.............................................................
};
У меню даної програми «Графика» можна вибрати три опції: «Фигуры»; «Фигуры 2»; «Сохранение картинки на диск». При виборі опції меню «Фигуры» відбувається формування бітового зображення m_Bit та виведення його на екран дисплею. При виборі опції «Фигуры 2» відбувається просте виведення на екран бітового зображення m_Bit, яке було сформовано у опції меню «Фигуры». При спрацюванні першої або другої опції меню змінній kod_graf привласнюється відповідне значення – 1 або 2. При чому значення 2 може бути привласнено, коли бітове зображення вже сформоване, тобто після натиснення першої опції меню (при kod_graf дорівнює 1).
Наведемо відповідні функції відгуків.
void CGrafView::OnFigures()
{
if(kod_graf!=1)
{
kod_graf = 1;
Invalidate();
}
}
void CGrafView::OnFigures2()
{
if(kod_graf==1)
{
kod_graf = 2;
Invalidate();
}
}
Дані функції викликають функцію OnDraw() (за допомогою Invalidate()), де і відбувається формування та виведення бітового зображення.
Наведемо текст функції OnDraw().
void CGrafView::OnDraw(CDC* pDC)
{
CGrafDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
CPen pen;
CPen* pen_st;
CBrush br;
CBrush* br_st;
CBitmap* old_bit;
CRect rect,rr;
GetClientRect(&rect);
rr = rect;
CDC memDC;
//Малювання фігур
if(kod_graf==1)
{
//Організувати виведення з memDC
//Створення memDC
memDC.CreateCompatibleDC(pDC);
m_Bit.DeleteObject();
m_Bit.CreateCompatibleBitmap(pDC,rr.Width(),rr.Height());
old_bit = memDC.SelectObject(&m_Bit);
//Малювання в memDC
pen.CreatePen(PS_SOLID,1,RGB(255,0,0));
pen_st = memDC.SelectObject(&pen);
br.CreateSolidBrush(RGB(255,0,0));
br_st = memDC.SelectObject(&br);
rect.InflateRect(-rect.right/8,-rect.bottom/8);
memDC.Rectangle(&rect);
br.DeleteObject();
br.CreateSolidBrush(RGB(0,255,0));
memDC.SelectObject(&br);
rect.InflateRect(-rect.right/8,-rect.bottom/8);
memDC.Rectangle(&rect);
br.DeleteObject();
br.CreateSolidBrush(RGB(0,0,255));
memDC.SelectObject(&br);
rect.InflateRect(-rect.right/8,-rect.bottom/8);
memDC.Ellipse(&rect);
memDC.SelectObject(pen_st);
memDC.SelectObject(br_st);
//Малювання картинки з пам’яті
pDC->BitBlt(0, 0, rr.Width(), rr.Height(), &memDC, 0,
0, SRCCOPY);
memDC.SelectObject(old_bit);
}
//Виведення картинки m_Bit на екран
if(kod_graf==2)
{
memDC.CreateCompatibleDC(pDC);
old_bit = memDC.SelectObject(&m_Bit);
//Малювання картинки з пам’яті
pDC->BitBlt(0, 0, rr.Width(), rr.Height(), &memDC,
0, 0, SRCCOPY);
memDC.SelectObject(old_bit);
}
}
Розглянемо цю функцію більш детально. При спрацюванні опції меню «Фигуры» (kod_graf дорівнює 1) відбувається формування зображення у об’єкт m_Bit класу CBitmap.
На початку створюємо контекст пам’яті memDC за допомогою використання функції CreateCompatibleDC. Потім за допомогою функції CreateCompatibleBitmap відбувається створення об’єкту m_Bit з встановленими розмірами клієнтського вікна. При створенні початковий бітовий образ заповнений нулями, тобто кожний піксель зображення має чорний колір. Після цього треба зв’язати контекст пам’яті memDC з бітовим зображенням mBit, використовуючи наступний рядок коду:
old_bit = memDC.SelectObject(&m_Bit);
Далі відбувається виведення графічних об’єктів до контексту memDC та формується бітовий образ у об’єкті m_Bit.
Після формування бітового образу, використовуючи функцію BitBlt() він виводиться на екран дисплею. При цьому, та область де не мало місце малювання буде замальована чорним кольором, так як область бітового зображення при його створенні за умовчанням заповнюється нулями.
Результат роботи програми при натисненні опції меню «Фигуры» наведено на рис. 5.29.
При спрацюванні опції меню «Фигуры 2» відбувається виведення на екран сформованого бітового зображення. Відмінністю є те, що при зміні розмірів вікна у цій опції, зображення залишиться без змін, як продемонстровано на рис.5.30.
Рис.5.29. Результат формування та виведення бітового зображення («Фигуры»)
Рис.5.30. Результат виведення бітового зображення («Фигуры 2»)
Після формування та виведення зображення його можна зберегти на диск за допомогою опції меню «Сохранение картинки на диск», якій додамо функцію відгуку OnSavebmp, у якій напишемо наступний код.
void CGrafView::OnSavebmp()
{
if(!kod_graf) return;
//Вибір файлу для зберігання
CString str;
CFileDialog SaveDialog(false, "", "1.bmp",
OFN_PATHMUSTEXIST|OFN_NOCHANGEDIR|
OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT,
"Bitmap (*.bmp)|*.bmp|");
if(SaveDialog.DoModal()!=IDOK) return;
str = SaveDialog.GetPathName();
//---------Зберігання картинки на диск---------
BITMAP bm;
BITMAPINFO bb;
BITMAPFILEHEADER bm_hed0;
BITMAPINFOHEADER bm_hed1;
int sizeB;
unsigned char *data1;
memset(&bm_hed1,0,sizeof(bm_hed1));
memset(&bb,0,sizeof(bb));
m_Bit.GetBitmap(&bm);
//Заповнення структури шапки BITMAPFILEHEADER
sizeB = (bm.bmWidth+1)*(bm.bmHeight+1);
bm_hed0.bfType = 'MB';
bm_hed0.bfSize = 3*sizeB+
sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
bm_hed0.bfReserved1 = 0;
bm_hed0.bfReserved2 = 0;
bm_hed0.bfOffBits = sizeof(BITMAPFILEHEADER)
+sizeof(BITMAPINFOHEADER);
//Заповнення структури шапки BITMAPINFOHEADER
bm_hed1.biSize = sizeof(BITMAPINFOHEADER);
bm_hed1.biBitCount = 24;
bm_hed1.biWidth = bm.bmWidth;
bm_hed1.biHeight = bm.bmHeight;
bm_hed1.biPlanes = 1;
bm_hed1.biCompression = BI_RGB;
//Рядок заповнення для структури BITMAPINFO
bb.bmiHeader = bm_hed1;
data1 = new unsigned char[3*sizeB];
GetDIBits(GetDC()->m_hDC,(HBITMAP)m_Bit.m_hObject,
0,bm.bmHeight,data1,&bb,DIB_RGB_COLORS);
//Запис на диск двох структур та масиву пікселів
fstream pp;
pp.open(str,ios::out|ios::binary);
pp.write((char*)&bm_hed0,sizeof(bm_hed0));
pp.write((char*)&bm_hed1,sizeof(bm_hed1));
pp.write((char*)data1,3*sizeB);
pp.close();
delete []data1;
}
На початку функції ми перевіряємо чи вибрав користувач який небудь із режимів малювання, якщо ні – то виходимо з функції. Далі йде виклик стандартного діалога CFileDialog для вибору файла. При створенні вікна діалогу ми використали наступні константи:
OFN_NOCHANGEDIR – встановлення флагу, який при виборі користувачем іншої папки не змінює початкову директорію у структурі діалогового вікна. При повторних запусках вікна діалогу він завжди буде відкриватись із початкової папки і не буде змінювати її на останню вибрану користувачем.
OFN_OVERWRITEPROMPT – флаг який видає попередження коли при зберіганні файлу виявиться, що файл з таким ім'ям вже існує. Користувачу буде надана можливість змінити ім'я файлу або перезаписати існуючий. Флаг працює лише на діалогах виду "Сохранить как".
Якщо користувач натисне у діалозі кнопку Отмена, тоді відбудеться вихід з функції. Шлях до введеного файлу записується у змінну str через функцію діалогу GetPathName. Далі йде підготовка інформації необхідної для збереження файлу *.bmp. Коротко розглянемо структуру цих файлів. Вони (файли *.bmp) складаються з таких частин:
структура BITMAPFILEHEADER
структура BITMAPINFOHEADER
палітра (у зображеннях з 24-бітними кольорами не використовується)
данні зображення
Отже вводимо структури bm_hed0 і bm_hed1. Обнуляємо структуру bm_hed1 функцією memset. Далі відбувається заповнення цих структур. Структура bm_hed0 містить наступні параметри:
typedef struct tagBITMAPFILEHEADER
{ // bmfh
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER
У параметрі bfType повинні бути символи "BM" скорочення від слів "Bit Map". Всі файли формату *.bmp повинні містити ці символи. У коді функції ми пишемо bm_hed0.bfType = 'MB'; тому, що числові дані задаються у зворотньому порядку. Тобто символ B буде першим а M другим у файлі.
У параметрі bfSize повинен міститись розмір файлу *.bmp. У змінній sizeB ми обчислили кількість пікселів у зображенні. Повний розмір файлу буде дорівнювати сумі sizeB помноженому на 3 та розміру структур BITMAPFILEHEADER і BITMAPINFOHEADER. Кількість пікселів множимо на 3 тому, що зображення будемо зберігати у 24-бітному виді по 8 біт на червоний, зелений та синій кольори.
У параметри bfReserved1 і bfReserved2 ставимо 0, вони зарезервовані для використання у системних функціях. По аналогії з класами можна сказати що вони були б захищеними членами класу. Але у структурах захищених змінних не передбачено тому при використанні структури ми не повинні змінювати їх значення.
У змінну bfOffBits повинно бути записано зміщення початку даних зображення відносно початку файла. У даному випадку це зміщення дорівнює розміру структур BITMAPFILEHEADER та BITMAPINFOHEADER. Якби ми використовували палітру то нам довелося б сюди додавати також і її розмір.
Структура bm_hed1 містить наступні параметри:
typedef struct tagBITMAPINFOHEADER
{ // bmih
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
Опис параметрів структури:
• biSize містить розмір структури в байтах.
• biWidth і biHeight містять ширину і висоту бітового образу.
• biPlanes містить кількість зображень у пристрої. Повинно бути рівним 1.
• biBitCount кількість біт у одному пікселі зображення.
• biCompression визначає метод стиснення (компресії) зображення, може приймати значення наведені у табл. 5.6:
Таблиця 5.6
Види компресії зображень у файлах *.bmp
Значення |
Ідентифікатор |
Компресія |
0 |
BI_RGB |
не стиснуте зображення |
1 |
BI_RLE8 |
стиснення RLE для 8-бітних зображень |
2 |
BI_RLE4 |
стиснення RLE для 4-бітних зображень |
3 |
BI_BITFIELDS |
зображення не стиснуто, палітра містить три 4-байтних маски для червоного, зеленого і синього компонентів кольора. Використовується для 16- и 32-бітних зображень |
4 |
BI_JPEG |
Win98/Me/2000/XP: JPEG - стиснення |
5 |
BI_PNG |
Win98/Me/2000/XP: PNG - стиснення |
6 |
BI_ALPHABITFIELDS |
WinCE: зображення не стиснено, палітра містить чотири 4-байтні маски для червоного, зеленого, синього і прозорого (альфа-канал) компонентів кольора. Використовується для 16- и 32-бітних зображень |
При зберіганні файлу нам потрібно буде створити масив з бітами зображення. Зображення міститься у контексті екрану. Для того щоб отримати біти зображення із контексту екрану у потрібний нам масив використаємо функцію GetDIBits:
int GetDIBits(
HDC hdc, // дескриптор DC
HBITMAP hbmp, // дескриптор рисунка
UINT uStartScan, // перший встановлюваний рядок розгортки
UINT cScanLines, // число копіюємих рядків розгортки
LPVOID lpvBits, // масив для бітів рисунка
LPBITMAPINFO lpbi, // буфер даних рисунка
UINT uUsage // індекси RGB або палітри
);
У параметрі lpvBits передаємо створений нами масив data1.
Збереження файлу робимо через об’єкт pp класу fstream. Його функціями: open, write та close ми відкриваємо файл для запису, пишемо структури та закриваємо файл. У разі використання класу CFile для збереження файлів код може мати наступний вид:
CFile pp;
pp.Open(str,CFile::modeCreate|CFile::modeWrite|CFile::typeBinary);
pp.Write((char*)&bm_hed0,sizeof(bm_hed0));
pp.Write((char*)&bm_hed1,sizeof(bm_hed1));
pp.Write((char*)data1,3*sizeB);
pp.Close();