Gdi графіка нового покоління

Gdi графіка нового покоління

графічні об'єкти

Для виведення примітивів потрібні певні настройки: установка кольору і товщини ліній ( «пера»), виду заливки суцільних областей ( «кисті»), розмірів і накреслення шрифтів і так далі. Як правило, різні графічні програмні інтерфейси зберігають такі параметри як власних структур даних - графічних об'єктів. Це справедливо як для GDI, так і для GDI +, однак використання об'єктів різниться. Розглянемо ці відмінності на прикладі маленької завдання - малювання квадрата.

Stateful model в GDI

У GDI використана програмна модель, яка називається stateful model. Це означає, що для пристрою виведення запам'ятовуються зроблені настройки і є поняття поточного обраного об'єкта. Перед виведенням примітивів необхідний графічний об'єкт потрібно вибрати в пристрій виведення (за допомогою функції SelectObject). Встановивши таким чином, наприклад, товщину лінії в 3 пікселі, потім можна вивести кілька відрізків обраної товщини:

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

Погляньте на виділену рядок. Навіщо знадобилося запам'ятовувати і відновлювати в контексті старе перо, якщо воно все одно не використовувалося для малювання? Вся справа в тому, що обраний в контексті графічний об'єкт не може бути видалений - така вже архітектура була обрана при створенні GDI. Отже, без цього рядка виклик DeleteObject завершиться невдало, і створене перо залишиться «висіти» в GDI Heap. Так як повідомлення WM_PAINT приходить вікна досить часто, в системах Windows 9x (де розмір купи GDI обмежений величиною 64 Кб) це швидко призведе до вичерпання графічних ресурсів системи. Після цього всі програми почнуть вести себе дуже незвично: не перемальовувати деякі елементи меню та іконки, малювати текст підозрілими шрифтами і т.д. Системи на платформі NT будуть «триматися на плаву» трохи довше, так як їх графічна купа може рости в міру необхідності, але і там існує межа на число одночасно створених в процесі графічних об'єктів - 12 тисяч.

Інша проблема полягає в тому, що C ++ - програмісти звикли до автоматичному очищенню використовуваних ресурсів завдяки використанню класів з деструкторами і «розумних покажчиків». При цьому легко взагалі забути про необхідність щось явно видаляти. Однак існуючі бібліотеки класів-обгорток GDI не можуть за програміста вирішити проблему видалення обраного в контексті об'єкта. Це також призводить до витоку GDI-ресурсів.

Stateless model в GDI +

Відмінною для програміста особливістю GDI + є зміна програмної моделі в роботі з пристроями виведення. Концепція поточного обраного в пристрої графічного об'єкта викинута як неефективна і застаріла. Замість послідовної установки параметрів контексту (Graphics) використовується перерахування атрибутів при кожному виклику графічного методу. Пристрій виведення як би не володіє станом, а отримує необхідну інформацію через параметри. Така модель має назву stateless model.

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

Тепер кожен метод, який використовує для виведення графічний об'єкт, вимагає в якості параметра явної вказівки цього об'єкта:

Скориставшись параметром callbackData, можна, наприклад, передати в процедуру metaCallback (версії для C ++) покажчик на метафайл, записи якого перераховуються в даний момент. Тоді для виконання графічних команд можна буде скористатися методом Metafile :: PlayRecord:

Параметр recordType при кожному виклику callback-процедури приймає значення з перерахування EmfPlusRecordType. що містить аж 253 елемента (найбільшого перерахування GDI +). Всі ці елементи або прямо відповідають командам GDI / GDI +, або позначають службові записи метафайлу. При виклику PlayRecord можна підміняти цей параметр своїми значеннями для виконання абсолютно інших команд GDI + в методі PlayRecord. Але для цього необхідно твердо знати значення відповідних недокументованих параметрів recordData і dataSize (і передавати, відповідно, змінені значення і в них).

Перерахування записів: специфіка .NET

У документації по WinForms вказується, що навколишнє середовище .NET надає власну версію делегата для виконання відповідних команд метафайлу в тілі нашого callback-обробника. Ця версія передається в обробник в параметрі callbackData. Для виконання команди досить викликати отриманий делегат (приклад скопійований з документації):

Увага! Це явна помилка документації .NET Framework (ох, вже вкотре!) Наведений приклад відкомпілюйте, але його виконання ні до чого доброго не приведе. Про те ж сказано і в книзі Петцольда [3].

Експерименти показали, що в параметрі callbackData завжди передається нульове значення, незалежно від фази місяця. Виклик такого «делегата» завершиться винятком NullReferenceException.

Все-таки створити робочу версію застосування методу EnumerateMetafile для .NET цілком можливо. Для цього нам доведеться трохи повозитися з методом Metafile.PlayRecord. Справа в тому, що він як параметр data приймає масив байтів:

Так, для досягнення заповітної мети (перебору записів метафайлу в WinForms) нам довелося трохи попрацювати. Але ці труднощі з лишком окупаються іншим чудовим якістю .NET - reflection. Будь-який елемент перерахування (навіть такого гігантського, як EmfPlusRecordType), «знає» своє строкове ім'я, що дозволяє легко створити версію програми, перераховуються записи обраного метафайлу поіменно. Ось фрагмент демонстраційної програми:

Просто, чи не так? Метод ToString поверне строкове ім'я елемента перерахування, позбавляючи програміста від стомлюючого кодування 253 строкових констант. Ось результат роботи даного методу:

Gdi графіка нового покоління

Повний текст програми (воно дозволяє виконувати тільки вибрані користувачем команди метафайлів) знаходиться на компакт-диску до журналу. Там же є відкомпільоване версія, яку можна використовувати для вивчення вмісту різних метафайлів (наприклад, що входять в комплект поставки Microsoft Visual Studio):

Gdi графіка нового покоління

Удачі в дослідницькій роботі!

Корисні ресурси

Схожі статті