GDI: графіка в Delphi (исходники), Різне, Програмування, статті

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>

*

*