| GDI در دلفی
GDI مخفف کلمه Graphics Device Interface و تکنیک رسم گرافیک دو بعدی ویندوز است. این روش متاسفانه روش بسیار کندی برای ترسم گرافیکی میباشد ولی بدلیل اینکه اساس برنامه نویسی گرافیک است یاد گیری آن بعنوان پایه برنامه نویسی گرافیک برای برنامه نویسان لازم به نظر میرسد. اولین نکته قابل توجه در این رابطه این است که نباید سعی کنید از GDI برای ایجاد هر جلوه خیالی گرافیکی استفاده کنید بدلیل اینکه GDI یک تکنیک ابتدایی در گرافیک است . برای استفاده حرفه ای از گرافیک سعی کنید از DirectX , OpenGL و ... بهره بگیرید. اگرچه شما با کمی خلاقیت قادر ید جلوه های ساده را با GDI ایجاد کنید .
یکی از کلمات عمومی که در GDI زیاد با آن برخورد خواهید کرد DC (Device Context ) است که نشان دهنده همان ناحیه ای است که ترسیم روی آن صورت میگیرد و در دلفی با TCanvas نشان داده میشود . در واقع DC محل نشان دادن خروجی توابع گرافیکی است . بنابرین شما میتوانید از برخی توابع ترسیم برای ترسیم در صفحه نمایش یا پرینتر استفاده کنید . نکته دیگری که باید مورد توجه قرار بگیرد این است که توابعی که شما از آن استفاده میکنید توابع گرافیکی استاندارد دلفی بوده و پوششی برای توابع گرافیکی ویندوز است و دلفی آنها را برای ایجاد یک رابط کاربر خوب و مناسب آماده کرده است. در ابتدا برای آشنایی با سازماندهی GDI با معرفی بعضی از کلاسهای مهم GDI شروع میکنیم :
Pen : برای رسم خطوط ساده ( مثال آن تابع LineTo است.) و ایجاد کادر برای اشکال دیگر استفاده میشود .
Brush : برای پر کردن یک محیط بسته با رنگ استفاده میشود . مثال آن توابع, FillRect FloodFill. است .
Font : برای تعیین فونت و اندازه فونت در هر متنی که در ترسیم آن را مینویسید استفاده میشود .
Region : یک ناحیه بسته از فضای ویندوز که میتواند یه بیضی ، چهار ضلعی یا یه دوازه وجهی یا هر چیز دیگری باشد .
خوب ، حالا باید شروع به ترسیم کنیم . ابتدا با خطوط و اشکال ساده شروع میکنیم و بعد به سراغ توابع مورد استفاده در ترسیم Bitmap ها میرویم و در آخر راجع به تکنیکهای ترسیم در صفحه نمایش صحبت میکنیم . قبل از هر چیز باید بدانید که مختصات (0و0) نقطه بالا ، سمت چپ است . اگر یک تجسم ریاضی از مختصات داشته باشید ممکن است کمی گیج شوید . فراموش نکنید که در اینجا Y در بالاترین نقطه صفر و به سمت پایین صفحه نمایش مثبت و X در منتهی الیه سمت چپ صفر و به سمت راست صفحه نمایش مثبت است . در واقع مختصات اینجا بصورت (ارتفاع از بالا ، فاصله از چپ) میباشد. مثلا (0,50) یک نقطه در سمت چپ و 50 پیکسل پایین تر از بالای صفحه نمایش است.
خطوط و اشکال
رسم خطوط و اشکال خیلی ساده است. چیزی مهمی که اینجا باید بخاطر داشته باشید تفاوت میان Pen و Brush است. بطور ساده از Pen برای رسم خطوط استفاده میشود اگرچه ممکن است این خطوط یک چهار گوش را تشکیل دهد. ولیBrush درون یک ناحیه ی بسته است که بوسیله خطوط بوجود آمده است. دو تابع برای رسم خطوط وجود دارد که هر دو از متعلقات TCanvas هستند .
MoveTo : با استفاده از این تابع موقعیت قلم را جابجا میکنیم . مثال :
Canvas.MoveTo(75,200);
LineTo : با استفاده از این تابع، از موقعیت فعلی قلم به یک نقطه خاص، خط رسم میکنیم . مثال:
Canvas.LineTo(50,150);
شما میتوانید بجای استفاده از MoveTo موقعیت قلم را بوسیله تابع PenPos هم تغییر بدهید . برای مثال:
Canvas.PenPos.X:=150; Canvas.PenPos.Y:=100;
یا
Canvas.PenPos:=Point(150,100);
به یاد داشته باشید که موقعیت پیش فرض قلم موقعیت (0,0) میباشد. اگر لازم است خط شما از نقطه دیگه ای شروع شود قبل از استفاده از LineTo باید موقعیت قلم را اصلاح کنید . بعد از استفاده از LineTo موقعیت قلم همان نقطه ای است که خط به آن کشیده شده مثلا بعد از اجرای Canvas.LineTo(100;200); موقعیت قلم نقطه (100,200) خواهد بود. خطوط رسم شده بوسیله Pen (TPen) در Canvas ایجاد میشود . شما براحتی میتوانید پارامترهای قلم را همانطور که میخواهید تغییر بدهید. مثلا برای تغییر اندازه قلم Canvas.Pen.Width:=4; و برای تغییر رنگ Canvas.Pen.Color:=clBlack; عمل کنید. یک مثال ساده را ببینیم . یک پروژه جدید ایجاد کنید و یک عدد Button روی آن قرار دهید و کد رویداد کلیک Button را بصورت زیر بنویسید.
|
procedure TForm1.Button1Click(Sender: TObject);
const NUM_LINES = 2000;
var i:integer;
begin
Randomize;
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; |
در مثال بالا، 2000 خط با جهت های تصادفی و با رنگهای تصادفی فرم را پر میکند . تابع RGB برای ایجاد رنگ با تعیین مقادیر قرمز، سبز و آبی مورد استفاده قرار میگیرد. RGB(Red,Green,Blue) . این مقادیر میتواند مابین 0 تا 255 باشد . این تابع به شما مقداری از نوع TColor بر میگرداند . در این مرحله به سراغ رسم اشکال میرویم. تابع رسم اشکال زیادی در TCanvas وجود دارند که براحتی قابل استفاده هستند. چند مورد از پر استفاده ترین آنها به قرار زیرند:
Ellipse : برای رسم بیضی . مثال : Canvas.Ellipse(0,0,100,50); (پارامتر ها به ترتیب : X سمت چپ بیضی ، X سمت راست بیضی ، Y بالای بیضیی ، Y پایین بیضی).
FillRect : پر کردن یک چهارگوش با Brush Color ولی بدون خط دور آن . مثال : Canvas.FillRect(Bounds(0,0,150,200));
FloodFill : پر کردن یک ناحیه یا Brush Color با ایجاد خطوط لبه مثال : Canvas.FloodFill(10,10,clBlue,fsBorder);
Rectangle : رسم چهارگوش و پرکردن آن با Brosh Color و خطوط لبه آن با Pen Color . مثال : Canvas.Rectangle( Bounds(20, 20, 50, 50));
RoundRect : ایجاد چهارگوش با گوشه های منحنی. مثال : Canvas.RoundRect(20, 20, 50, 50, 3, 3);
(تابع Bounds یک مقدار TRect برمیگرداند. TRect چهار مقدار top, left, bottom, right را نگه میدارد. بجای این تابع میتوانید از Rect هم استفاده کنید که تفاوت چندانی با هم ندارند ) .
یک تابع مفید دیگر هم وجود دارد که برای ایجاد متن به کار میرود . این تابع به شما اجازه میدهد که متن مورد نظر خود را با فونت تعیین شده (Canvas font) ایجاد کنید . مثال :
Canvas.TextOut(20,50,'GDI in Delphi');
شما میتوانید نوع قلم ، رنگ و اندازه قلم را به دلخواه خود تغییر دهید. مثال :
Canvas.Font.Name := 'Verdana'; Canvas.Font.Size := 24; Canvas.Font.Color := clRed;
در زیر یک مثال را میبینیم که 200 شکل را بصورت تصادفی روی فرم رسم میکند .
|
procedure TForm1. Button1Click(Sender: TObject);;
const
NUM_SHAPES = 200;
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);
Canvas.TextOut ( ShapeLeft, ShapeTop, 'Delphi');
end;
end;
end;
end; |
در مثال بالا وقتی برنامه را اجرا میکنید فرم برنامه در حال اجرا را کمینه (Minimize) کنید و دوباره آنرا به حالت اول برگردانید . میبینید که خطوط پاک شدند . اصلا نگران نباشید. ناپدید شدن اشکالی که شما رسم کردید یکی از امتیازات مهم GDI است و برمیگردد به تفاوت بین Drawing و Panting.
Drawing: Drawing بهمان صورتی است که در مثال قبل مشاهده فرمودید. گرافیک ایجاد شده بوسیله آن تا وقتی وجود دارد که پنجره Refresh نشده باشد.
Painting: هر گاه یک پنجره نیاز به Refresh شدن دارد ویندوز یک پیام به پنجره مورد نظر ارسال میکند. این پیام میتواند Handle رویداد OnPaint فرم باشد . پس وقتی شما گرافیک مورد نظر خود را در رویداد OnPaint تعریف کنید جلوه ایجاد شده در هنگام Refresh ناپدید نمیشود. این تفاوت اساسی بین این دو است. Drawing فقط یک رویداد موقتی است در صورتی که Painting هر گاه پنجره احتیاج به Refresh داشته باشد فرا خونده میشود.
بطور کلی اگه خواسته باشیم در صفحه بوسیله Drawing ترسم را انجام دهیم در حالیکه این تغییرات موقتی ایجاد شده به نحوی ذخیره شوند چند راه وجود دارد. یک را استفاده از Bitmap است به این صورت که میتوانیم یک Bitmap ایجاد کنیم و این تغییرات را همزمان با صفحه نمایش در Bitmap هم اعمال کنیم و بعد در زمان لازم این Bitmap را در صفحه نمایش، نمایش بدهیم.
استفاده از Handle ها
اگر چه هنگام استفاده از دلفی شما میتوانید بدون نگرانی از توابع گرافیکی دلفی استفاده کنید با این حال دسترسی شما به API ویندوز، بصورت مستقیم هم به هیچ وجه محدود نشده است. اما برای استفاده از توابع API ویندوز، این توابع از شما HDC میخواهند. اما میدانید HDC چیست؟
شاید بدانید که برای هر چیزی در ویندوز یک Handle (دستگیره) در نظر گرفته میشود. این Handle ها برای شناسایی آبجکت ها استفاده میشوند. تمام پنجره ها، کلید ها، منو ها و... داری دستگیره هستند. پس نباید تعجب کنید اگه بفهمید که تمام آبجکت های شما این دستگیره را بصورت یک خصوصیت در خود دارند. مثلا:
Form1.Canvas.Handle
توضیح دستگیره ها در Win32.hlp دلفی به این صورت است:
"An application must obtain an object handle and use this handle to examine or modify the system resource (or both). In the Microsoft® Win32® application programming interface (API), handles are usually implemented as indirect pointers, but this is not always the case."
HDC ها یک نوع دستگیره هستند که مشخص کننده بخشی از فضای زمینه (Device Context) می باشند. همانطور که گفته شد TCanvas بیشترین توابع DC (فضای زمینه) را دارد. کافیست دستگیره TCanvas آبجکت مورد نظر را در توابع API ویندوز استفاده کنید.
Abject .Canvas.Handle
در اینجا برخی از توابع API که معادل VCL آنها گفته شد را ببینید.
|
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); |
بطور کلی توابع بهمان صورت قابل استفاده هستند با این تفاوت که اول باید Handle را پاس کنید. بخاطر داشته باشید که شما میتوانید دستگیره ها متفاوت برای Drawing در محلهای مختلف استفاده کنید. مثلا از Bitmap1.Canvas.Handle برای یک Bitmap و از Form1.Canvas.Handle برای فرم.
دقت داشته باشید که برای استفاده از داده رشته ای در این توابع، داده مورد نظر باید از نوع PChar باشد. (مثلا تابع TextOut()). گاهی ممکن است شما مجبور باشید طول رشته را نیز در تابع وارد کنید که در این صورت کافیست از تابع Length استفاده کنید.
Bitmap چیست؟
رسم خطوط و اشکال به روش فوق روش مفیدی است ولی توانایی شما برای رسم عکسها مستلزم شناخت و استفاده از Bitmap هاست.
آبجکتی که معمولا برای رسم تصاویر استفاده میشود Bitmap است. Bitmap یک آبجکت گرافیکی است که دارای دو قسمت میباشد 1- هدری شامل اطلاعات مهمی درباره تصویر است (شامل طول، ارتفاع، اطلاعات رنگ و...) 2- بخشی که شامل داده های خود تصویر است (شامل یک آرایه بزرگ ار رنگ هر پیکسل). خوشبختانه شما نباید برای استفاده از Bitmap هیچ نگرانی داشته باشید بدلیل اینکه کلاس Bitmap از قبل در دلفی وجود داشته که TBitmap نامیده میشود. Bitmap ها خارق العاده هستند، آنها به ما آزادی عمل خیلی زیادی نسبت به خطوط و اشکال میدهند. شما حتی میتوانید Bitmap ها را بوسیله برنامه مورد نظرتان ایجاد نمایید و در برنامه خودتان از آنها استفاده نمایید. لازم است اول یک مثال از استفاده از Bitmap ها ببینیم.
|
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 روی فرم در موقعیت (x,y) دارد. اگر شما قصد ندارید آبجکت های مورد نظر را در زمان راه اندازی (Run-Time) ایجاد کنید من شدیدا استفاده از اشاره گرها را توصیه میکنم. بهر حال در بالای رویه، Bitmap را ایجاد کنید و تصویر مورد نظر را در آن لود نمایید. استفاده از بلوک Try … Finally هم شما را از عدم ایجاد خطا مطمئن مینمایید حتی در صورت وجود فایل با فرمت نا معتبر. روش استفاده شده در مثال بالا همیشه موثر نیست. این روش ابتدا وجود فایل را چک میکند سپس Bitmap را ایجاد کرده و پس از استفاده آزاد میکند. یک روش بهتر برای استفاده و نمایش تصویر، ایجاد Bitmap و لود کردن تصویر در FormCreate و آزاد کردن آن در FormDestory است. این روش موثر تر است و در سراسر عمل قابل دسترسی است.
توابع رسم در GDI
TCanvas توابع رسم زیادی دارد که همه آنها با TGraphic سر و کار دارند. TGraphic کلاس پایه آبجکت های گرافیکی در دلفی است. برخی از مثال های آن عبارتند از: TBitmap برای Bitmap ها، Ticon برای icon ها، TMetafile برای Metafile ها و TJPEGImage برای JPEG ها. در زیر لیست سایر تابع رسم در GDI را میبینید.(تمام این توابع متدهای Tcanvas هستند.)
|
نام |
توضیح |
مثال |
|
Draw |
رسم تصویر در نقطه مورد نظر.
|
Canvas.Draw(5,10,MyGraphic); |
|
StrechDraw |
رسم تصویر در محدوده مورد نظر با ایجاد وضعیت ارتجاعی.
|
Canvas.StretchDraw( Bounds(0,0,32,32), MyGraphic); |
|
CopyRect |
کپی کردن بخشی از یک Tcanvas به دیگری و ایجاد حالت ارتجاعی در صورت لزوم |
Canvas.CopyRect( Bounds(0,0,32,32), MyBmp.Canvas, Bounds(0, 0, 640, 480)); |
همه این توابع راحت و آسان هستند اگرچه تابعی که در زیر با آن آشنا میشوید کمی مشکل تر است و نیاز به دقت بیشتری دارد. این تابع BitBlt است.
|
function BitBlt(
hdcDest: HDC; // handle to destination device context
nXDest, // x-coordinate of destination rectangle's upper-left corner
nYDest, // y-coordinate of destination rectangle's upper-left corner
nWidth, // width of destination rectangle
nHeight: Integer; // height of destination rectangle
hdcSrc: HDC; // handle to source device context
nXSrc, // x-coordinate of source rectangle's upper-left corner
nYSrc: Integer; // y-coordinate of source rectangle's upper-left corner
dwRop: DWORD // raster operation code ): Boolean; | همانطور که میبینید این تابع برای ترسیم تصویر یک DC در محدوده یک DC دیگر است. درک این تابع کمی مشکل به نظر میرسد ولی با وجود Canvas.Draw شما احتمالا نیازی به استفاده از این تابع پیدا نخواهید کرد. برای درک بهتر این تابع به مثال زیر توجه فرمایید.
|
procedure TForm1.Button1Click(Sender: TObject); var MyBitmap:TBitmap; begin MyBitmap:=TBitmap.Create; MyBitmap.Width:=Screen.Width; MyBitmap.Height:=screen.Height; BitBlt(MyBitmap.canvas.handle, 0, 0, MyBitmap.width, MyBitmap.height, GetDC(0),0,0,Srccopy); image1.Picture.Bitmap:= MyBitmap; MyBitmap.Free; end; |
در مثال بالا برنامه بوسیله این تابع از صفحه نمایش عکس گرفته و در Image1 نمایش داده میشود. از تابع GetDC برای بدست آوردن دستگیره فضای زمینه یک ناحیه که Handle آن را داریم استفاده میکنیم. در این مثال برای اشاره به تمامی صفحه نمایش مقدار صفر را در تابع GetDC قرار میدهیم.
تا اینجا فرض بر این بود که شما Bitmap را در زمان راه اندازی ترسیم میکنید. اما راه بهتر در این موارد استفاده از TImage و تنظیم کردن خاصیت Picture آن در زمان طراحی، برای نمایش تصویر از زمان راه اندازی است. این روش گذشته از دقت در محل روی فرم، حرکت دادن تصویر روی فرم را در زمان اجرا آسانتر میکند.
منبع: انجمن دلفی ایران نوشته شده توسط ابراهیم خدائی در چهارشنبه بیست و دوم فروردین 1386 ساعت 2:50 | لینک ثابت |
|