Жаргон GDI.

GDI розшифровується як Graphics Device Interface, і являє собою
інтерфейс, який Windows використовує для малювання 2D графіки. Також це самий
повільний спосіб відображення графіки з існуючих, проте найпростіший для
розуміння основ. Отже, для початку, поговоримо про основні поняття і терміни у
GDI.

Почнемо з того, що GDI зазвичай не використовують для створення крутих графічних
ефектів, для цього є DirectX, OpenGL, або будь-які графічні бібліотеки
(Такі як: DelphiX, FastLib, DIBUltra, Graphics32 …). Однак, для створення
простих ефектів з мінімальними зусиллями GDI цілком згодиться.

З GDI тісно пов'язана ще одна абревіатура – DC ("Device Context" – контекст
пристрою). Це те, на чому ми малюємо, і в Delphi контекст пристрою
представлений як TCanvas. Ідея контексту пристрою полягає в тому, що це
універсальний пристрій виводу, тому можна використовувати однакові функції
як для екрану, так і для принтера.

Всі графічні функції в Delphi є надбудовами над стандартними GDI
функціями Windows. Пізніше ми поговоримо про ці функції.

А тепер саме час приступити до розгляду того, як влаштований GDI. Нижче, в
таблиці, представлені деякі важливі класи:


















Ім'я Опис
Pen Використовується для малювання простих ліній. Звичайно застосовується для функції
LineTo або при малюванні рамки для певної фігури (наприклад для функції
Rectangle).
Brush Кисть використовується для заповнення області певним кольором. Застосовується в
функціях Rectangle, FillRect або FloodFill.
Font Використовується для завдання шрифту, яким буде намальований текст. Можна
вказати ім'я шрифту, розмір і т.д.
Region Дозволяє задати регіон (замкнутий простір). Регіоном може бути коло,
квадрат або довільна фігура. Дозволяє так само робити дірки в
фігурах.

Проте, пора переходити від слів до справи, а саме, почати малювати лінії і
фігури.

Малювання ліній


Спершу необхідно чітко усвідомити, що координата (0,0) це верхній лівий кут
екрану. Тобто значення по осі y збільшуються вниз екрану. Відповідно,
координата (0, 50) означає, що ми просто відступили на 50 пікселів від верху
екрану.

Найголовніше, що треба знати при малюванні ліній і фігур, це розходження між
пером (Pen) і пензлем (Brush). Все дуже просто: перо (Pen) використовується при
малюванні ліній чи рамок, а кисть (Brush) для заповнення фігури.

Нижче наведено дві функції, які використовуються для малювання ліній і обидві
належать TCanvas:














Ім'я
Опис Приклад
MoveTo Переміщує точку початку малювання лінії у зазначені координати x і y Canvas.MoveTo(50, 100);
LineTo Малює лінію починаючи з поточної позиції (див. MoveTo) до зазначених координат x
і y.
Canvas.LineTo(50, 100);

Ефект переміщення точки початку малювання лінії так само досягається за допомогою
установки своства PenPos в канвасе … наприклад, "Canvas.PenPos.x: = 20;",
"Canvas.PenPos.y: = 50", або "Canvas.PenPos: = Point (20,50);".

За замовчуванням, точка початку малювання встановлена в (0,0), тобто, якщо відразу
викликати "Canvas.LineTo (100,100);" то буде намальована лінія з точки (0,0) в
точку (100, 100). Точка початку малювання автоматично переміститься в (100,
100), тобто, якщо виконати команду "Canvas.LineTo (200, 100);", то наступна
лінія буде намальована з точки (100, 100) в (200, 100). Тому, якщо ми хочемо
малювати лінії нез'єднаних один з одним, то доведеться скористатися методом
MoveTo.

Лінія, намальована за допомогою LineTo використовує поточний перо канвасе (типу
TPen). Основні властивості пера, це ширина – "Canvas.Pen.Width: = 4;" (за допомогою
якого можна задавати різну ширину ліній), і колір "Canvas.Pen.Color: =
clLime;”.

Погляньмо на простий приклад безладного малювання різнокольорових ліній:

procedure TForm1.FormCreate(Sender: TObject);
begin
/ / Инициализируем генератор
/ / Випадкових чисел
Randomize;
end;

const NUM_LINES = 2000;

procedure TForm1.DrawLines;
var
i: Integer;
begin
for i := 0 to NUM_LINES – 1 do
begin
Canvas.Pen.Color :=
RGB(Random(256),
Random(256),
Random(256)
);
Canvas.LineTo
(Random(ClientWidth),
Random(ClientHeight));
end;
end;


Процедура DrawLines викликається з обробника кнопки OnClick. Кількість
ліній задається в константі NUM_LINES. Між іншим, функція RGB, становить колір
кожній лінії із трьох основних складових: червоного, зеленого і синього
(Значення від 0 до 255) і повертає нам колір у вигляді TColor. Про квіти поговоримо
трохи пізніше, а ось так виглядає намальований пейзаж:



Тепер, коли лінії намальовані, спробуйте трішки посувати форму. Якщо
форму перемістити за краї екрана, то Ви побачите, що лінії частково стерлися.
Це не глюк, і вирішується ця проблема дуже просто. Але не зараз ;-). Спершу
подивимося, як малювати різні фігури.

Малювання фігур


Для малювання фігур, в TCanvas передбачені наступні функції:


























ІМ'Я
ОПИС ПРИКЛАД
Ellipse Малює еліпси, вписаний в невидимий квадрат з координатами верхнього лівого
кута і правого нижнього. Якщо координати х і y у кутів будуть збігатися, то
вийде коло.
Canvas.Ellipse(0,0,50,50);
FillRect Заповнює прямокутник кольором поточної кисті (brush), але ніяк не за
межами нього.
Canvas.FillRect( Bounds(0,0,100,100));
FloodFill Заповнює дану область кольором поточної кисті, до тих пір поки не буде
досягнутий край.
Canvas.FloodFill(10, 10, clBlack, fsBorder);
Rectangle Малює прямокутник (або квадрат), заповнений кольором поточної кисті і
обрамлений кольором поточного пера
Canvas.Rectangle( Bounds(20, 20, 50, 50));
RoundRect Теж, що і Rectangle, але з загругленнимі кутами. Canvas.RoundRect( 20, 20, 50, 50, 3, 3);

Ще є дуже потрібна функція TextOut, яка дозволяє малювати текст,
використовуючи шрифт, заданий в канвасе:










ІМ'Я
ОПИС ПРИКЛАД
TextOut Малює дану рядок на канвасе починаючи з координат (x, y) – фон тексту
заповнюється поточним кольором кисті.
Canvas.TextOut(10, 10, “Some text”);

До речі, функція дозволяє малювати текст, не заповнюючи його фон. Якщо Вам
необхідно змінити шрифт, використовуваний в TextOut, то необхідно змінити
властивість Font канвасе (ця властивість має тип TFont) – наприклад
"Canvas.Font.Name: =" Verdana ";", "Canvas.Font.Size: = 24;" або
“Canvas.Font.Color := clRed;”.

Коротенько хотілося б звернути Вашу увагу на досить корисний клас TRect,
який вміє зберігати в собі значення ліво, право, верху і низу (до речі, в
Windows API це RECT). Те їсть, досить вказати ліву і верхню координату і
ширину і висоту області, а TRect автоматично підставить у вигляді (ліво, верх,
ліво + ширина, верх + висота). Ще є інша функція Rect (), яка робить
теж саме, але координати в ній задаються безпосередньо як ліво, право, верх і низ.
Ну і за бажанням, можна використовувати API функцію SetRect.

Нижче наведено приклад, який малює випадковим чином різні фігури:

const NUM_SHAPES = 200;

procedure TForm1.DrawShapes;
var
i,
ShapeLeft,
ShapeTop: Integer;
begin
for i := 0 to NUM_SHAPES – 1 do
begin
Canvas.Brush.Color :=
RGB(Random(256),
Random(256),
Random(256));
ShapeLeft := Random(ClientWidth);
ShapeTop := Random(ClientHeight);
/ / Тепер, випадковим чином, вирішуємо що малювати
case Random(3) of
0: Canvas.Rectangle(ShapeLeft,
ShapeTop,
ShapeLeft + Random(50),
ShapeTop + Random(50));
1: Canvas.Ellipse(ShapeLeft,
ShapeTop,
ShapeLeft + Random(50),
ShapeTop + Random(50));
2: begin
Canvas.Font.Size: = 10 + Random (7); / / від 10 до 16
Canvas.TextOut ( ShapeLeft, ShapeTop, “Some text”);
end;
end;
end;
end;



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

Перемальовування вікна


Тепер давайте розберемося, чому в самому першому прикладі у нас стиралися
лінії при переміщенні форми за межі екрану. А саме, нам потрібно з'ясувати
різницю між "малюванням" і "перемальовуванням".

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

Перемальовування дещо відрізняється від поняття "малювання". Коли вікна
необхідно перемальовувати, то Windows посилає певне повідомлення. Це
повідомлення надходить в обробник події "OnPaint". Будь-який код, який помістити
в обробник OnPaint буде викликаний кожен раз, коли формі необхідно оновитися.

Для прикладу, помістіть наступний код в проект:

procedure TForm1.DrawSomeText;
begin
Canvas.TextOut(10, 10, “Some text”);
end;

Якщо помістити на форму кнопку і викликати DrawSomeText з обробника кнопки
OnClick, то проблема зі зникненням тексту при переміщенні форми залишиться.
ОДНАК, якщо викликати DrawSomeText з обробника форми OnPaint, то текст
залишиться на своєму місці остаточно.

Дескриптори, або як користуватися аналогічними API функціями


Отже, ми навчилися малювати лінії, різні фігури, навчилися робити так,
щоб наше творіння не стиралася при переміщенні форми, і виконали ми все це
за допомогою стандартних функцій VCL (таких як Canvas.TextOut і т.д.). Однак,
що робити, якщо Ви не хочете користуватися графічними функціями VCL, які
всього навсього є надбудовами над аналогічними функціями з Windows API?
Будь ласка! Ніхто нам не забороняє користуватися API функціями безпосередньо! Але
постійте-ка, всі вони вимагають якогось HDC! Що таке HDC?

Майже все в Windows використовує "Дескриптор" (Handle). Дескриптор, це спосіб
ідентифікації Вашого об'єкта в системі. У кожного вікна є свій дескриптор, у
кожної кнопки теж є свій дескриптор і т.д. Саме тому всі наші об'єкти
мають дескриптор в якості властивості – наприклад, "MyForm.Canvas.Handle".

Тип HDC це Дескриптор (Handle) Контексту Прилади (Device Context). Я вже
говорив на самому початку, що TCanvas включає в себе більшість функцій DC.
Тому, ми спокійно можемо підставляти властивість канвасе Handle скрізь, де нам
це буде потрібно.

Заради інтересу можна поглянути на таблицю, в якій представлені приклади
викликів деяких функцій з VCL і їх аналогів з Windows API.

VCL WINDOWS API
Canvas.TextOut(x,y,myString);
TextOut(Canvas.Handle, x, y, PChar(myString), Length(String));
Canvas.FloodFill(X, Y, Color,fsBorder);
ExtFloodFill (Canvas.Handle, x, y, YourColour, FLOODFILLBORDER);
Canvas.LineTo(x,y); LineTo(Canvas.Handle, x, y);
Canvas.MoveTo(x,y); MoveToEx(Canvas.Handle, x, y, nil);

Так само можна використовувати різні дескриптори, щоб малювати в різних місцях.
Наприклад, можна використовувати "SomeBmp.Canvas.Handle" для малювання на картинці
(Бітмапами), або "Form1.Canvas.Handle", щоб малювати на формі.

В API версії функції TextOut необхідно передавати рядок завершену нулем.
Це означає, що замість того, щоб передати рядок у функцію безпосередньо,
необхідно передати її як PChar. Так само не забувайте передавати у функцію довжину
рядка. Для цього можна скористатися функцією Length.

Ну що, Вам вже захотілося помістити на форму якусь красиву картинку
?

Що таке бітмапами (Bitmaps)?


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

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

Бітмапами можна малювати не тільки на формі, а й по всьому екрану. Може це і
може здатися трохи дивним, але іноді це буває корисно, особливо при
створення скрінсейвера. Однак, спочатку нам необхідно розібратися з тим, як
працювати з бітмапами. Ось невеличкий приклад:

procedure Form1.DrawBitmap (const Filename: String; const x, y: Integer);
var
Bmp: TBitmap;
begin
/ / Спершу переконаємося, що файл існує!
if not FileExists(Filename) then
begin
ShowMessage(“The bitmap ” + Filename + ” was not found!”);
Exit;
end;

Bmp := TBitmap.Create;
try
Bmp.LoadFromFile(Filename);
Canvas.Draw(x, y, Bmp);
finally
Bmp.Free;
end;
end;


Ця функція намагається завантажити і показати картинку, (з ім'ям Filename,
наприклад "myBitmap.bmp") починаючи з точки (x, y).

Відразу скажу, що ця функція досить неефективна. Вона створює і знищує
бітмапами кожен раз коли викликається, а так само кожен раз перевіряє існування
файлу. Краще оголошувати об'єкт TBitmap як частина форми, створювати та завантажувати
зображення в FormCreate, а звільняти її в FormDestroy.

Функції малювання в GDI


TCanvas має кілька корисних функцій, які працюють з типом TGraphic.
Тип TGraphic є базовим класом для графічних об'єктів у Delphi, таких
як: бітмапами (TBitmap), іконки (TIcon), метафайли (TMetafile) і JPEG-і
(TJPEGImage). Всі вони використовують одні і ті ж функції, які наведені в
таблиці:

Всі ці функції є методами TCanvas.


















ІМ'Я
ОПИС ПРИКЛАД
Draw Малює TGraphic на канвасе так як він є, не розтягуючи. Canvas.Draw(5,10,MyGraphic);
StrechDraw Малює TGraphic на канвасе, підганяючи (розтягуючи) його під задану
область.
Canvas.StretchDraw( Bounds(0,0,32,32), MyGraphic);
CopyRect Копіює частина TCanvas-а в іншій, при необхідності розтягуючи його. Canvas.CopyRect (Bounds (0,0,32,32), MyBmp.Canvas, Bounds (0, 0, 640,
480));

TCanvas.Draw є обгорткою для API функції BitBlt:

function BitBlt(
hdcDest: HDC; / / дескриптор кінцевого контексту пристрої
nXDest, / / коорд. x верхнього лівого кута кінцевого прямокутника
nYDest, / / коорд. y верхнього лівого кута кінцевого прямокутника
nWidth, / / ширина кінцевого прямокутника
nHeight: Integer; / / висота кінцевого прямокутника
hdcSrc: HDC; / / дескриптор вихідного контексту пристрої
nXSrc, / / коорд. x верхнього лівого кута вихідного прямокутника
nYSrc: Integer; / / коорд. y верхнього лівого кута вихідного прямокутника
dwRop: DWORD / / код растрової операції
): Boolean;

Описаний вище спосіб дозволяє малювати бітмапами в run-time. Звичайно, простіше
помістити на форму TImage і встановити в неї картинку. Зображення буде
постійно залишатися на тому місці, де Ви його помістили, але це нудно ;-). Куди
цікавіше зробити за допомогою бітмапами анімацію.

З іншого боку, зрозумівши принципи роботи з бітмапами, Вам буде легше перейти
до інших графічним бібліотекам (наприклад DirectX).

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*