Передача повідомлення WM_PAINT

Лекція № 15

Тема: Інтерфейс графічних пристроїв

 

План

1. Введення в інтерфейс графічних пристроїв

2. Пристрої GDI

3. Контекст пристрою

4. Повідомлення WM_PAINT

5. Графічні координати

6. Передача повідомлення WM_PAINT

 

Операційна система Windows володіє графічним інтерфейсом, тому всі створювані для неї застосування зобов'язані використовувати саме його. Графічний інтерфейс істотно простіший, зручніше і зрозуміліше для користувачів, чим текстовий. Інтерфейс графічних пристроїв Windows (GDI — Graphic Device Interface) позбавляє програмістів від необхідності вивчення подробиць відображення діалогових вікон, промальовування їх елементів або створення кнопок. Але, програмування в графічному середовищі спричиняє за собою абсолютно інший набір проблем і питань, особливо якщо створюване застосування відображає дані у вигляді діаграм, графіків або схем.

 

Введення в інтерфейс графічних пристроїв

Інтерфейс графічних пристроїв (GDI) є невід'ємною частиною ядра операційної системи Windows, що контролюючої і забезпечує всі операції графічного виведення додатків Windows. Отже, незалежно від того, відображається на екрані вікно діалогового застосування або шикарна заставка хранителя екрану, або здійснюється роздрук додатком документа на принтері, у всіх цих операціях бере участь GDI.

При створенні меню, діалогових вікон і інших елементів управління само собою зрозуміло, що операційна система Windows сама здогадається звернутися до функцій GDI і промальовувати на екрані ці елементи інтерфейсу. GDI використовується навіть для відображення курсора миші при його переміщенні по екрану.

Але найчудовішою в GDI є його внутрішня архітектура. Корпорація Microsoft створила GDI, щоб відокремити графічні операції від фактично використовуваного устаткування і забезпечити таким чином певний рівень абстракції для графічних застосувань (таких, наприклад, як робочий стіл Windows). Для цього GDI володіє графічними функціями високого рівня, що забезпечують однакові результати виконання незалежно від використовуваного устаткування. Розмежування графічних функцій і апаратних засобів дозволяє здійснювати складні графічні операції при вельми незначних зусиллях з боку розробника, а також при мінімальному обліку особливостей апаратних засобів.

 

Пристрої GDI

Щоб вважатися пристроєм GDI, фізичний графічний пристрій (принтер або відеоадаптер наприклад) зовсім не зобов'язаний підтримувати всі три типи графіки. І в цьому вся краса GDI. Використовуючи встановлені драйвери пристроїв, інтерфейс GDI здатний адаптуватися до вживаних апаратних засобів і забезпечити прийнятний результат відображення графіки для найрізноманітніших апаратних засобів. Фактично, більшість пристроїв практично не мають вбудованої підтримки на рівні вище, ніж необхідно для інтерфейсу GDI3.

Єдиною вимогою, що пред'являється до пристроїв GDI, є здатність відображення крапок (пікселів). От і все. А для решти операцій, що виходять за рамки цієї дуже простій функції, інтерфейс GDI, проаналізувавши можливості конкретного пристрою, сформує простіші і зрозуміліші йому команди, в результаті виконання яких необхідне зображення буде відображено. Проте, щоб повідомити інтерфейс GDI про те, якими саме можливостями володіє даний пристрій, необхідний його драйвер. Драйвер пристрою передає інтерфейсу GDI відповідний набір бітів, що дозволяє останньому настроїтися для правильної взаємодії з пристроєм.

Для дуже простих пристроїв, можливості яких вельми обмежені, велику частину роботи інтерфейс GDI виконує самостійно. Інтерфейс GDI здатний внутрішньо, самостійно перетворити всі звернення до функцій векторної графіки і текст в растрові дані, які зможе зрозуміти фізичний пристрій. Не дивлячись на те, що растрова графіка вимагає більшого об'єму пам'яті, її застосування дозволяє відобразити навіть достатньо складні зображення на апаратних засобах, що володіють вельми посередніми возможностями4.

Якщо пристрій повідомляє про те, що воно володіє додатковими можливостями, то інтерфейс GDI буде позбавлений від необхідності виконання деяких дій. Якщо апаратний пристрій здатний самостійно виконувати запити на промальовування, то інтерфейс GDI передаватиме йому команди у вигляді відповідних запитів. Це приведе до підвищення загальної продуктивності по двох причинах. По-перше, функціям GDI доведеться виконувати менший об'єм робіт, а отже, менше навантажувати процесор. По-друге, високорівневі запити на промальовування займають меншу кількість оперативної пам'яті і дискового простору в порівнянні із загальною сумою низькорівневих складових, на які їх довелося б розкласти, а зменшення об'єму інформації, що зберігається, знижує навантаження і на систему в цілому, що приводить до підвищення загальної продуктивності.

Можливість взаємодії з щонайширшим діапазоном різних апаратних засобів є одній з причин, по якій графічний інтерфейс GDI називають незалежним від зовнішніх пристроїв. Якщо пристрій нездібний самостійно забезпечити відображення графічної інформації, інтерфейс GDI може піти йому назустріч і змоделювати відповідну послідовність запитів програмно. З іншого боку, якщо пристрій володіє вбудованою підтримкою запитів, то GDI перейде на передачу більш високорівневих запитів, що забезпечить підвищення продуктивності системи.

 

Контекст пристрою

Контекст пристрою (DC — Device Context) — структура даних, що створюється інтерфейсом GDI для представлення підключених пристроїв. Наприклад, щоб мати можливість відобразити зображення на екрані, додатку Windows необхідний контекст пристрою екрану, а для організації виводу на друк необхідний контекст іншого пристрою — принтера. Якщо тому ж самому застосуванню знадобиться організувати виведення зображення в метафайл і растр, то потрібно буде додати контексти третього і четвертого пристроїв. Фактично, роль контексту пристрою полягає в забезпеченні обміну даними між додатком і фізичним пристроєм (або псевдопристроєм).

Окрім забезпечення взаємодії з пристроєм, в обов'язки контексту пристрою входить зберігання набору встановлених графічних атрибутів. Наприклад, щоб з'ясувати колір тексту, що відображається, функція відображення звертається при виклику до атрибуту кольору тексту.

Значення будь-якого графічного атрибуту можна змінити, викликаючи певні функції GDI. Наприклад, колір тексту встановлює функція SetTextColor. Кожній функції установки атрибуту відповідає функція повернення значення цього атрибуту. Отже, щоб з'ясувати поточне значення кольору тексту, досить викликати функцію GetTextColor.

 

Повідомлення WM_PAINT

Найважливішим повідомленням для промальовування є WM_PAINT. У найпростішому випадку це повідомлення оповіщає вікно про необхідність перемальовування його вмісту. Повідомлення WM_PAINT може бути передане з багатьох причин. Наприклад, інше вікно могло бути відкрите поверх даного вікна і частково закрити його вміст . Або користувач може розвернути або відновити раніше згорнуте вікно. Крім того, це повідомлення може бути передане ^росто тому, що програма була тільки що запущена і її вікно необхідно промальовувати. | Основною причиною, по якій вікна повинні приймати і обробляти повідомлення WM_PAINT, є те, що система нездібна самостійно відновлювати втрачений вміст клієнтської області вікна. Група розробників Windows розглядала спочатку різні способи, за допомогою яких система могла б зберігати зображення вікна. Один з даних підходів полягав в отриманні і збереженні знімка вікна перед тим, як його закриє інше окно6. Проте величезний об'єм пам'яті, необхідної для цього підходу, зробив подібне рішення нездійсненним.

В світі символьних екранів що всі відображаються на екрані дані можуть бути збережені в крихітному буфері розміром порядка 2 Кбайт. Але, як вже було сказано, розмір буфера, необхідного для зберігання зображення екрану 640x480 пікселів на 256 квітів, складає приблизно 300 Кбайт. Це 300 000 байт по порівнянню 2 000 байт! Всього лише три вікна займуть цілий мегабайт (і це при мінімальних характеристиках дисплея); а як бути, якщо одночасно запущено декілька застосувань? Для перших версій Windows, коли оперативна пам'ять комп'ютерів складала всього лише 1 Мбайт, ця ціна була дуже висока.

Навіть якщо об'єм пам'яті достатньо великий, залишається інша проблема. Якщо система зберігатиме знімки екранів при кожній частковій зміні даних вікон, кешування стане безглуздим. У такій ситуації додаток вимушений перемальовувати вміст вікон регулярно. В результаті, вся пам'ять і велика частина часу процесора були б даремно витрачені на збереження і відображення знімків. Таким чином, технологія знімків екрану — це, в кращому разі, неефективне рішення.

Отже, застосування повідомлення WM_PAINT залишається якнайкращим вирішенням проблеми підтримки поточного стану вікна. Всю відповідальність за підтримку вмісту своїх вікон операційна система Windows повністю покладає на само застосування. Таким чином, повідомлення WM_PAINT забезпечує роботу механізму, за допомогою якого операційна система повідомляє додаток про те, що прийшло час відновити вікно. Все, що необхідно зробити розробникові, — це гарантувати, що додаток здатний зберігати всю інформацію про стан, необхідну для виконання цього завдання.

 

Графічні координати

У тільки що створеного контексту пристрою (DC) одиницями координат за умовчанням є пікселі або одиниці пристрою. Згодом всю систему координат можна змінити і використовувати дюйми, сантиметри або одиниці точок принтера. Масштаб зображення також можна змінити або довільно, або за розміром листа. Стандартними одиницями вимірювання є одиниці пристрою (пікселі). З погляду атрибутів контексту пристрою цей режим відображення відомий як Мм_техт.

Стандартною початковою точкою (тобто координатами 0, 0) є верхній лівий кут поверхні промальовування, що на екрані відповідає верхньому лівому куту клієнтської області вікна. На друкарській сторінці це відповідає самому верхньому лівому куту листа, де можливо нанести зображення (не забувайте про поля). Подібно до того, як можна змінити систему координат, цілком можливо змінити і розташування їх початкової точки, але в прикладах цього розділу, проте, використовується початкова точка, задана за умовчанням.

Стандартний напрям руху по осях х і у декілька відрізняється від прийнятого в Декартової системи координат. Прийнята за умовчанням система координат вікна називається координатами клієнтської області (client-area coordinates). Оскільки для положення курсора миші використовується та ж сама система координат, що і для клієнтської області вікна, розпізнати клацання миші на розташованому у вікні об'єкті виявляється значно простішим.

 

Передача повідомлення WM_PAINT

Представляючи повідомлення WM_PAINT, автор підкреслив, що це найбільш важливе повідомлення з погляду графіки. Нагадаємо, що вікно може отримувати це повідомлення по найрізноманітніших приводах: коли, наприклад, одне вікно, що частково закривало інше, було переміщене або видалене, коли вікно було відновлене після згортання, був змінений розмір вікна і так далі Насправді, причина не така важлива. У будь-якому випадку це повідомлення свідчить про необхідність перемальовування всіх частин вікна.

Але в деяких випадках причиною повідомлення про необхідність перемальовування є не зовнішній чинник, такий, наприклад, як зміна користувачем розміру вікна, а внутрішній, який не так те і просто розпізнати. Наприклад, користувач міг би ввести який або текст у вікні поля введення, додати або видалити стовпець даних з вікна електронної таблиці. Будь-яка внутрішня причина, пов'язана із зміною що відображаються у вікні даних, повинна приводити до передачі повідомлення про перемальовування.

Щоб передати повідомлення про необхідність перемальовування, досить оголосити вміст вікна неприпустимим (invalid) або, кажучи простіше, застарілим. Це можна зробити за допомогою декількох різних функцій-членів класу CWnd. Найпростішою з них є функція Invalidate, оскільки їй передається тільки один параметр логічного типу (Boolean), вказуючий на необхідність очищення області вікна перед промальовуванням. Приведений нижче приклад виклику цієї функції оголошує неприпустимою всю клієнтську область вікна:

 

// Перемальовування всього вікна з попередньою

// очищенням його вмісту.

Invalidate(TRUE);

 

Іншою функцією, що генерує повідомлення про необхідність перемальовування є InvalidateRect, яка дозволяє вказати прямокутник, що підлягає перемальовуванню. На ^достаточно швидкодіючих апаратних засобах це дозволяє зменшити неже-чательное мерехтіння экранна (flicker). Але незалежно від потужності апаратних ;редств краще залишатися консервативним і, по можливості, запрошувати перемальовування шшь необхідних ділянок, оскільки дуже часте мерехтіння екрану дратує користувачів. З погляду ергономіки таке мерехтіння украй небажано, оскільки викликає втому очей, а отже, не тільки дратує користувачів, але і вимушує їх при турботі з таким програмним забезпеченням частіше робити перерви. Отже, щоб запитати юобшение про перемальовування для прямокутника, обмеженого координатами (10, 10) і (IQQ, 100), необхідно зробити наступне:

Визначити неприпустимий прямокутник

 

// і передати повідомлення про необхідність перемальовування

CRect rectUO, 10, 100, 100);

InvalidateRect{Srect, TRUE);

 

В порівнянні з іншими типами повідомлень (наприклад, про події миші або введення з клавіатури) повідомлення про необхідність перемальовування мають дуже низький пріоритет. Врешті-решт, якщо дані міняються часто і у великих кількостях, то в додаткових питаннях на перемальовування особливої необхідності немає. Після того, як стан вікна буде оголошений неприпустимим, воно цілком може встигнути отримати (і обробити) дещо інших повідомлень, перш ніж поступить повідомлення WM_PAINT.

В деяких випадках виявляється необхідним підняти пріоритет повідомлення WM_PAINT. Щоб відправити повідомлення про необхідність перемальовування негайно, можна викликати функцію UpdateWindow. Наприклад, за бажання, можна викликати функцію Invalidate, щоб запитати повідомлення про перемальовування, а потім негайно модифікувати вікно. Не так вже і рідко доводиться оголошувати частину вікна неприпустимою і відразу передавати повідомлення про необхідність її перемальовування. Для цього застосовується наступний код:

 

InvalidateRect( SRect, TRUE );

UpdateWindow ();

 

Таким чином, коли зображення у вікні изменено7, відповідну область вікна, що підлягає зміні, оголошують такою, що знаходиться в неприпустимому стані. Ця область перемальовує під час вступу чергового повідомлення WM_PAINT. Звернення до функції UpdateWindow не оголошує частину вікна неприпустимою, ця функція лише прискорює перемальовування області, вже оголошене що знаходиться в неприпустимому стані. Але якщо такої області немає, то звернення до функції UpdateWindow ніяк не вплине на вміст вікна. Консервативна політика перемальовування Windows обмежує промальовування виділеним прямокутником, тобто тільки тією частиною вікна, яка є неприпустимою.

Як уже згадувалося, виклик функції UpdateWindow приводить до негайного перемальовування, і подальше малювання можна здійснювати у вікні, що є допустимим. Але це має сенс тільки тоді, коли промальовування здійснюється у відповідь на повідомлення, відмінне від WM_PAINT. Наступний розділ присвячений опису того, як це можна зробити, а також обговоренню деяких проблем, пов'язаних з подібним підходом.