PDA

View Full Version : سوال: کد نویسی به روش Double Buffering



habibb
چهارشنبه 11 بهمن 1391, 17:20 عصر
مهمترین اصل در ساخت گرافیک بازی های رایانه ای با #C

دابل بافرینگ _Double Buffering


چگونه یک فرم را دابل بافرینگ کنم
فرمی که یک کنترل در حال حرکت است
تا لرزش و چشمک زدن تصویر و لکه هایی که روی تصویر پس زمینه از کنترل در حال حرکت به جا می ماند از بین برود ؟
اگر در هنگام اجرای برنامه و مشاهده ی حرکت کنترل ها در فرم ، با موس ، نوار عنوان بالای فرم را بگیریم.
شاید برایتان پیش آمده باشد که : نمی توانیم نوار عنوان بالای فرم را گرفته و حرکت بدهیم !
علتش اینست که توابع اجرا شده ی ما دارند در فرم ترسیم می شوند . بنابراین فرم غیر قابل حرکت می شود
و توابع اجرایی تمام cpu سیستم را اشغال می کنند. و جابجایی کنترل ها که به سرعت در حرکتند نیز دیده می شود
در نتیجه چشمک زدن ، لرزش ، پرش زدن تصویر یا به اصطلاح Flicker دیده می شود .
و آن لکه هایی که جای مکان قبلی کنترل در حال حرکت ، داخل صفحه ی فرم بود پاک نشده
و مسیر حرکت کنترل به شکل خطوط نامنظم ، تمام صفحه را خطخطی می کند.
و رد مسیر کنترل در حال حرکت دیده می شود .
به همین علت تمام نرم افزار های پیچیده ی گرافیکی نیاز دارند تا ترسیم هایشان ، اول از همه داخل بافر کشیده شود
و بعد از این که خروجی آماده شد .
خروجی را به فرم و صفحه نمایش منتقل کنند تا ما ترسیم های اضافی را نبینیم .
بلکه ترسیم های لازم و زیبا ببینیم .:متعجب:




Double Buffered Graphics


NET Framework 4.5
Flicker is a common problem when programming graphics. Graphics operations that require multiple complex painting operations can cause the rendered images to appear to flicker or have an otherwise unacceptable appearance. To address these problems, the .NET Framework provides access to double buffering.
Double buffering uses a memory buffer to address the flicker problems associated with multiple paint operations. When double buffering is enabled, all paint operations are first rendered to a memory buffer instead of the drawing surface on the screen. After all paint operations are completed, the memory buffer is copied directly to the drawing surface associated with it. Because only one graphics operation is performed on the screen, the image flickering associated with complex painting operations is eliminated.


Default Double Buffering


The easiest way to use double buffering in your applications is to use the default double buffering for forms and controls that is provided by the .NET Framework. You can enable default double buffering for your Windows Forms and authored Windows controls by setting the DoubleBuffered property to true or by using the SetStyle method. For more information, see How to: Reduce Graphics Flicker with Double Buffering for Forms and Controls.


Manually Managing Buffered Graphics


For more advanced double buffering scenarios, such as animation or advanced memory management, you can use the .NET Framework classes to implement your own double-buffering logic. The class responsible for allocating and managing individual graphics buffers is the BufferedGraphicsContext class. Every application domain has its own default BufferedGraphicsContext instance that manages all of the default double buffering for that application. In most cases there will be only one application domain per application, so there is generally one default BufferedGraphicsContext per application. Default BufferedGraphicsContext instances are managed by the BufferedGraphicsManager class. You can retrieve a reference to the default BufferedGraphicsContext instance by calling the BufferedGraphicsManager.Current Property. You can also create a dedicated BufferedGraphicsContext instance, which can improve performance for graphically intensive applications. For information on how to create a BufferedGraphicsContext instance, see How to: Manually Manage Buffered Graphics.


Manually Displaying Buffered Graphics


You can use an instance of the BufferedGraphicsContext class to create graphics buffers by calling the BufferedGraphicsContext.Allocate Method, which returns an instance of the BufferedGraphics class. A BufferedGraphics object manages a memory buffer that is associated with a rendering surface, such as a form or control.
After it is instantiated, the BufferedGraphics class manages rendering to an in-memory graphics buffer. You can render graphics to the memory buffer through the BufferedGraphics.Graphics Property, which exposes a Graphics object that directly represents the memory buffer. You can paint to this Graphics object just as you would to a Graphics object that represents a drawing surface. After all the graphics have been drawn to the buffer, you can use the BufferedGraphics.Render Method to copy the contents of the buffer to the drawing surface on the screen.
For more information on using the BufferedGraphics class, see Manually Rendering Buffered Graphics. For more information on rendering graphics, see Graphics and Drawing in Windows Forms

gwbasic
چهارشنبه 11 بهمن 1391, 18:25 عصر
در هر فرم خاصیتی به نام DoubleBuffered وجود داره که با true کردن آن کمی چشمک زدن فرم کمتر می شه. اما راه بهتر اینه که شما تصویری که قرار نمایش بدین رو اول در یک Bitmap ایجاد کنید و سپس اون رو روی فرم Draw کنید. پستهای قدیمی من رو جستجو کنید در این موردا توضیح دادم

mousa1992
چهارشنبه 11 بهمن 1391, 18:45 عصر
در هر فرم خاصیتی به نام DoubleBuffered وجود داره که با true کردن آن کمی چشمک زدن فرم کمتر می شه. اما راه بهتر اینه که شما تصویری که قرار نمایش بدین رو اول در یک Bitmap ایجاد کنید و سپس اون رو روی فرم Draw کنید. پستهای قدیمی من رو جستجو کنید در این موردا توضیح دادم

سلام ممکنه لینک پست ها رو بذارید
ممنون

habibb
پنج شنبه 06 تیر 1392, 20:06 عصر
راه بهتر اینه که شما تصویری که قرار نمایش بدین رو اول در یک Bitmap ایجاد کنید


// Double Buffering بافر کردن تصویر کنترل در حال حرکت برای اجرای تکنیک
Bitmap buffer = new Bitmap(pictureBox1.DisplayRectangle.Width
,pictureBox1.DisplayRectangle.Height);


و سپس اون رو روی فرم Draw کنید.



using (Graphics context = Graphics.FromImage(buffer))
{
// کیفیت ترسیم
context.SmoothingMode = SmoothingMode.HighQuality;

// ترسیم پس زمینه درون بافر
context.DrawImage(this.background, 0, 0);


// PictureBoxترسیم بافر بر روی کنترل
pictureBox1.CreateGraphics().DrawImage(buffer, 0, 0);
}



پستهای قدیمی من رو جستجو کنید در این موردا توضیح دادم


http://barnamenevis.org/showthread.php?202784-%D8%A7%D9%81%D8%B2%D8%A7%DB%8C%D8%B4-%D8%B3%D8%B1%D8%B9%D8%AA-%D8%AA%D8%B1%D8%B3%DB%8C%D9%85&highlight=Double+Buffer
================================================== =======
ولی یک روش بهتر و سخت تر دیگه هم هست که این روش کاربردی تره!:متفکر:
کد هاش رو که از سایت MSDN با موضوع BufferedGraphics Class گرفتم اینجا میگذارم لطفا مفهومش رو توضیح بدین و یک مثال کاربردی ازش بزنید و هر کی هر چی می دونه بگه چون این موضوع خیلی کم راجبش بحث شده .:متفکر::قلب:
جزء 1


using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace BufferingExample
{


جزء 2


public class BufferingExample : Form
{
private BufferedGraphicsContext context;
private BufferedGraphics grafx;

private byte bufferingMode;
private string[] bufferingModeStrings =
{ "Draw to Form without OptimizedDoubleBufferring control style",
"Draw to Form using OptimizedDoubleBuffering control style",
"Draw to HDC for form" };

private System.Windows.Forms.Timer timer1;
private byte count;


جزء 3


public BufferingExample() : base()
{


جزء 4
// Configure the Form for this example.


this.Text = "User double buffering";
this.MouseDown += new MouseEventHandler(this.MouseDownHandler);
this.Resize += new EventHandler(this.OnResize);
this.SetStyle( ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true );

جزء 5
// Configure a timer to draw graphics updates.


timer1 = new System.Windows.Forms.Timer();
timer1.Interval = 200;
timer1.Tick += new EventHandler(this.OnTimer);
bufferingMode = 2;
count = 0;

جزء 6
// Retrieves the BufferedGraphicsContext for the
// current application domain.


context = BufferedGraphicsManager.Current;

جزء 7
// Sets the maximum size for the primary graphics buffer
// of the buffered graphics context for the application
// domain. Any allocation requests for a buffer larger
// than this will create a temporary buffered graphics
// context to host the graphics buffer.


context.MaximumBuffer = new Size(this.Width+1, this.Height+1);

جزء 8
// Allocates a graphics buffer the size of this form
// using the pixel format of the Graphics created by
// the Form.CreateGraphics() method, which returns a
// Graphics object that matches the pixel format of the form.


grafx = context.Allocate(this.CreateGraphics(),
new Rectangle( 0, 0, this.Width, this.Height ));

جزء 9
// Draw the first frame to the buffer.


DrawToBuffer(grafx.Graphics);
}

جزء 10


private void MouseDownHandler(object sender, MouseEventArgs e)
{
if( e.Button == MouseButtons.Right )
{

جزء 11
// Cycle the buffering mode.


if( ++bufferingMode > 2 )
bufferingMode = 0;

جزء 12
// If the previous buffering mode used
// the OptimizedDoubleBuffering ControlStyle,
// disable the control style.


if( bufferingMode == 1 )
this.SetStyle( ControlStyles.OptimizedDoubleBuffer, true );

جزء 13
// If the current buffering mode uses
// the OptimizedDoubleBuffering ControlStyle,
// enabke the control style.


if( bufferingMode == 2 )
this.SetStyle( ControlStyles.OptimizedDoubleBuffer, false );

جزء 14
// Cause the background to be cleared and redraw.


count = 6;
DrawToBuffer(grafx.Graphics);
this.Refresh();
}
else
{

جزء 15
// Toggle whether the redraw timer is active.


if( timer1.Enabled )
timer1.Stop();
else
timer1.Start();
}
}

جزء 16


private void OnTimer(object sender, EventArgs e)
{

جزء 17
// Draw randomly positioned ellipses to the buffer.


DrawToBuffer(grafx.Graphics);

جزء 18
// If in bufferingMode 2, draw to the form's HDC.


if( bufferingMode == 2 )

جزء 19
// Render the graphics buffer to the form's HDC.


grafx.Render(Graphics.FromHwnd(this.Handle));

جزء 20
// If in bufferingMode 0 or 1, draw in the paint method.


else
this.Refresh();
}

جزء 21


private void OnResize(object sender, EventArgs e)
{

جزء 22
// Re-create the graphics buffer for a new window size.


context.MaximumBuffer = new Size(this.Width+1, this.Height+1);
if( grafx != null )
{
grafx.Dispose();
grafx = null;
}
grafx = context.Allocate(this.CreateGraphics(),
new Rectangle( 0, 0, this.Width, this.Height ));

جزء 23
// Cause the background to be cleared and redraw.


count = 6;
DrawToBuffer(grafx.Graphics);
this.Refresh();
}

جزء 24


private void DrawToBuffer(Graphics g)
{

جزء 25
// Clear the graphics buffer every five updates.


if( ++count > 5 )
{
count = 0;
grafx.Graphics.FillRectangle(Brushes.Black, 0, 0, this.Width, this.Height);
}

جزء 26
// Draw randomly positioned and colored ellipses.


Random rnd = new Random();
for( int i=0; i<20; i++ )
{
int px = rnd.Next(20,this.Width-40);
int py = rnd.Next(20,this.Height-40);
g.DrawEllipse(new Pen(Color.FromArgb(rnd.Next(0, 255), rnd.Next(0,255), rnd.Next(0,255)), 1),
px, py, px+rnd.Next(0, this.Width-px-20), py+rnd.Next(0, this.Height-py-20));
}

جزء 27
// Draw information strings.


g.DrawString("Buffering Mode: "+bufferingModeStrings[bufferingMode], new Font("Arial", 8), Brushes.White, 10, 10);
g.DrawString("Right-click to cycle buffering mode", new Font("Arial", 8), Brushes.White, 10, 22);
g.DrawString("Left-click to toggle timed display refresh", new Font("Arial", 8), Brushes.White, 10, 34);
}

جزء 28


protected override void OnPaint(PaintEventArgs e)
{
grafx.Render(e.Graphics);
}

جزء 29


[STAThread]
public static void Main(string[] args)
{
Application.Run(new BufferingExample());
}
}
}

habibb
جمعه 07 تیر 1392, 08:54 صبح
سلام من کلاس BufferedGraphics رو جزء به جزء جدا کردم .
هر کی هر چی می دونه راجب هر جزئیش که هست یک کمی توضیح بده:متفکر:
من خودم هم دنبالش می گردم و اینجا نتیجه ی کارم رو می گذارم .
در ضمن لینک برنامه های دابل بافرینگ هم که کلاس BufferedGraphics توش به کار رفته رو هم بگذارید .:متفکر:

habibb
جمعه 07 تیر 1392, 11:21 صبح
جزء 2 خط 10

"Draw to HDC for form" };
HDC آدرس داده گرافيکي
که درContex Device ذخيره شده ، می باشد:متفکر:

پس یعنی داده ی گرافیکی رو روی فرم رسم کن.:متعجب:


*

habibb
جمعه 07 تیر 1392, 11:44 صبح
حال بايد تابعي بسازيم که تصاوير گرافيکي را درون حافظه load کند .
نکته مهمي که بايد به آن توجه کنيد اينست که يک device context خودش به تنهايي هيچ داده گرافيکي ندارد
و بايستي يک bitmap موجود باشد تا درون آن load شود
براي مثال يک فايل bmp يا يک bitmap خالي که از آن بعنوان back buffer استفاده مي کنيد .
تابعي که خواهيم نوشت يک device context منطبق با صفحه مي سازد سپس فايلهاي گرافيکي مورد نظر را درون device context قرار مي دهد .

کدوم خط؟
ورودی تصاویر گرافیکیه که يک bitmap هست که تصاویر در درونش load میشه ؟در واقع کدومش back buffer هست؟ :متفکر:

habibb
جمعه 07 تیر 1392, 13:04 عصر
تابعي که يک انتقال از داده هاي مرتبط به يک مستطيل از پيکسلها همون (sprite)به يک device context مقصد انجام مي دهد .
به عبارت ديگر داده هاي گرافيکي را از محيط گرافيکي ( يک bitmap ) به محيط گرافيکي ديگري ( screen و form ) کپي مي کند .
sprite چیست ؟ به اشیاء متحرکی گفته میشه که در بازی ها وجود دارن .
حالا اون تابعی که این وظیفه رو انجام بده . کدومه ؟:متعجب:

habibb
جمعه 07 تیر 1392, 13:27 عصر
بررسی کل جزء29


[STAThread]
public static void Main(string[] args)
{
Application.Run(new BufferingExample());
}
}
}

پروسه ی ورودی و اصلی برنامه که در Program.cs قرار دارد در داخل کلاس BufferedGraphics قرار گرفته شاید به این خاطر این کار صورت گرفته تا سرعت لود برنامه بالا تر رود ولی بهتر است پروسه ی Main برنامه در ابتدای کلاس قرار گیرد تا تابع Main در اولین بررسی توسط کامپایلر شناخته شود جهت اجرای اولیه ، هر چند که از نظر سرعت خیلی به چشم نمی آید. در ضمن حالا که تابع Main رو داخل کلاس BufferedGraphics آوردیم پس دیگه احتیاجی به فایل Program.cs نداریم .
بنابراین من فایل Program.cs رو از Solution Explorer پروژم Delete می کنم .:متعجب:

habibb
جمعه 07 تیر 1392, 19:11 عصر
بررسی کل جزء 3


public BufferingExample() : base()
{

سازنده ی کلاس BufferingExample می باشد که هم نام با نام کلاس و بدون نوع خروجی می باشد که سازنده از کلاس base ارث می برد .
در اینجا چون هیج جایی از کلاس base استفاده نشده پس ارث بری سازنده از کلاس base رو من از پروژم Delete می کنم .:متعجب:
ولی اگر در پروژه کلاس پایه هم داشتیم پس باید سازنده ی BufferingExample از سازنده ی کلاس base ارث ببرد .:متفکر:

habibb
جمعه 07 تیر 1392, 20:50 عصر
بررسی کل جزء1


using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace BufferingExample
{

در این روش نیازی به اضافه کردن using ها نداریم و به جای BufferingExample می تونید نام دلخواهی برای پروژتون انتخاب کنید .:لبخندساده:
کسی نیست بیات کمک ما کنه این کد ها خیلی سخته:گریه:

habibb
جمعه 07 تیر 1392, 22:56 عصر
جزء 4 خط 1


this.Text = "User double buffering";

این قطعه کد عنوان فرم را مشخص می کند. که یکی از Property های فرم می باشد .:شیطان:

habibb
جمعه 07 تیر 1392, 23:36 عصر
بررسی کل جزء 28


protected override void OnPaint(PaintEventArgs e)
{
grafx.Render(e.Graphics);
}
در متود OnPaint هیچگونه پردازش و رسم و ... ای وجود نخواهد داشت
و تنها وظیفه OnPaint نمایش بافر از قبل آماده روی صفحه هست. :متعجب:

http://substantialmotion.ir/Content/%D8%A7%D8%B3%D9%85_%D8%A7%D9%84%D9%86%D8%A8%D9%8A_ %D9%85%D8%AD%D9%85%D8%AF.jpg