日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > C# >内容正文

C#

c#双缓冲绘图(不闪烁的几种方法)

發(fā)布時(shí)間:2023/12/18 C# 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c#双缓冲绘图(不闪烁的几种方法) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

C#繪圖雙緩沖

?

C#雙緩沖解釋:

簡(jiǎn)單說(shuō)就是當(dāng)我們?cè)谶M(jìn)行畫(huà)圖操作時(shí),系統(tǒng)并不是直接把內(nèi)容呈現(xiàn)到屏幕上,而是先在內(nèi)存中保存,然后一次性把結(jié)果輸出來(lái),如果沒(méi)用雙緩沖的話,你會(huì)發(fā)現(xiàn)在畫(huà)圖過(guò)程中屏幕會(huì)閃的很厲害,因?yàn)楹笈_(tái)一直在刷新,而如果等用戶(hù)畫(huà)完之后再輸出就不會(huì)出現(xiàn)這種情況,具體的做法,其實(shí)也就是先創(chuàng)建一個(gè)位圖對(duì)象,然后把內(nèi)容保存在里面,最后把圖呈現(xiàn)出來(lái)。

?

GDI+的雙緩沖問(wèn)題

一直以來(lái)的誤區(qū):.net1.1 和 .net 2.0 在處理控件雙緩沖上是有區(qū)別的。 .net 1.1 中,使用:this.SetStyle(ControlStyles.DoubleBuffer, true);? .net 2.0中,使用:this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 導(dǎo)致畫(huà)面閃爍的關(guān)鍵原因分析: ????? 一、繪制窗口由于大小位置狀態(tài)改變進(jìn)行重繪操作時(shí) 繪圖窗口內(nèi)容或大小每改變一次,都要調(diào)用Paint事件進(jìn)行重繪操作,該操作會(huì)使畫(huà)面重新刷新一次以維持窗口正常顯示。刷新過(guò)程中會(huì)導(dǎo)致所有圖元重新繪制,而各個(gè)圖元的重繪操作并不會(huì)導(dǎo)致Paint事件發(fā)生,因此窗口的每一次刷新只會(huì)調(diào)用Paint事件一次。窗口刷新一次的過(guò)程中,每一個(gè)圖元的重繪都會(huì)立即顯示到窗口,因此整個(gè)窗口中,只要是圖元所在的位置,都在刷新,而刷新的時(shí)間是有差別的,閃爍現(xiàn)象自然會(huì)出現(xiàn)。 所以說(shuō),此時(shí)導(dǎo)致窗口閃爍現(xiàn)象的關(guān)鍵因素并不在于Paint事件調(diào)用的次數(shù)多少,而在于各個(gè)圖元的重繪。 根據(jù)以上分析可知,當(dāng)圖元數(shù)目不多時(shí),窗口刷新的位置也不多,窗口閃爍效果并不嚴(yán)重;當(dāng)圖元數(shù)目較多時(shí),繪圖窗口進(jìn)行重繪的圖元數(shù)量增加,繪圖窗口每一次刷新都會(huì)導(dǎo)致較多的圖元重新繪制,窗口的較多位置都在刷新,閃爍現(xiàn)象自然就會(huì)越來(lái)越嚴(yán)重。特別是圖元比較大繪制時(shí)間比較長(zhǎng)時(shí),閃爍問(wèn)題會(huì)更加嚴(yán)重,因?yàn)闀r(shí)間延遲會(huì)更長(zhǎng)。 解決上述問(wèn)題的關(guān)鍵在于:窗口刷新一次的過(guò)程中,讓所有圖元同時(shí)顯示到窗口。 ????? 二、進(jìn)行鼠標(biāo)跟蹤繪制操作或者對(duì)圖元進(jìn)行變形操作時(shí) 當(dāng)進(jìn)行鼠標(biāo)跟蹤繪制操作或者對(duì)圖元進(jìn)行變形操作時(shí),Paint事件會(huì)頻繁發(fā)生,這會(huì)使窗口的刷新次數(shù)大大增加。雖然窗口刷新一次的過(guò)程中所有圖元同時(shí)顯示到窗口,但也會(huì)有時(shí)間延遲,因?yàn)榇藭r(shí)窗口刷新的時(shí)間間隔遠(yuǎn)小于圖元每一次顯示到窗口所用的時(shí)間。因此閃爍現(xiàn)象并不能完全消除! 所以說(shuō),此時(shí)導(dǎo)致窗口閃爍現(xiàn)象的關(guān)鍵因素在于Paint事件發(fā)生的次數(shù)多少。 解決此問(wèn)題的關(guān)鍵在于:設(shè)置窗體或控件的幾個(gè)關(guān)鍵屬性。
使用雙緩沖
解決雙緩沖的關(guān)鍵技術(shù): 1、設(shè)置顯示圖元控件的幾個(gè)屬性:? 必須要設(shè)置,否則效果不是很明顯! this.SetStyle(ControlStyles.OptimizedDoubleBuffer |    ??????????????????? ControlStyles.ResizeRedraw | ??????????????????? ControlStyles.AllPaintingInWmPaint, true); 2、窗口刷新一次的過(guò)程中,讓所有圖元同時(shí)顯示到窗口。 ??? 可以通過(guò)以下幾種方式實(shí)現(xiàn),這幾種方式都涉及到Graphics對(duì)象的創(chuàng)建方式。

具體實(shí)現(xiàn)

1、? 利用默認(rèn)雙緩沖?
(1)在應(yīng)用程序中使用雙緩沖的最簡(jiǎn)便的方法是使用 .NET Framework 為窗體和控件提供的默認(rèn)雙緩沖。通過(guò)將 DoubleBuffered 屬性設(shè)置為 true。?
????? this.DoubleBuffered=true;?
(2)使用 SetStyle 方法可以為 Windows 窗體和所創(chuàng)作的 Windows 控件啟用默認(rèn)雙緩沖。?
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);?
2、? 手工設(shè)置雙緩沖?
.netframework提供了一個(gè)類(lèi)BufferedGraphicsContext負(fù)責(zé)單獨(dú)分配和管理圖形緩沖區(qū)。每個(gè)應(yīng)用程序域都有自己的默認(rèn) BufferedGraphicsContext 實(shí)例來(lái)管理此應(yīng)用程序的所有默認(rèn)雙緩沖。大多數(shù)情況下,每個(gè)應(yīng)用程序只有一個(gè)應(yīng)用程序域,所以每個(gè)應(yīng)用程序通常只有一個(gè)默認(rèn) BufferedGraphicsContext。默認(rèn) BufferedGraphicsContext 實(shí)例由 BufferedGraphicsManager 類(lèi)管理。通過(guò)管理BufferedGraphicsContext實(shí)現(xiàn)雙緩沖的步驟如下:

(1)獲得對(duì) BufferedGraphicsContext 類(lèi)的實(shí)例的引用。

(2)通過(guò)調(diào)用 BufferedGraphicsContext.Allocate 方法創(chuàng)建 BufferedGraphics 類(lèi)的實(shí)例。

(3)通過(guò)設(shè)置 BufferedGraphics.Graphics 屬性將圖形繪制到圖形緩沖區(qū)。

(4)當(dāng)完成所有圖形緩沖區(qū)中的繪制操作時(shí),可調(diào)用 BufferedGraphics.Render 方法將緩沖區(qū)的內(nèi)容呈現(xiàn)到與該緩沖區(qū)關(guān)聯(lián)的繪圖圖面或者指定的繪圖圖面。

(5)完成呈現(xiàn)圖形之后,對(duì) BufferedGraphics 實(shí)例調(diào)用釋放系統(tǒng)資源的 Dispose 方法。

完整的例子,在一個(gè)400*400的矩形框內(nèi)繪制10000個(gè)隨機(jī)生成的小圓。


?????????? BufferedGraphicsContext current = BufferedGraphicsManager.Current; //(1)

?????????? BufferedGraphics bg;

?????????? bg = current.Allocate(this.CreateGraphics(),this.DisplayRectangle); //(2)

?????????? Graphics g = bg.Graphics;//(3)

?????????? //隨機(jī) 寬400 高400

?

?????????? System.Random rnd = new Random();

?????????? int x,y,w,h,r,i;

?????????? for (i = 0; i < 10000; i++)

?????????? {

?????????????? x = rnd.Next(400);

?????????????? y = rnd.Next(400);

?????????????? r = rnd.Next(20);

?????????????? w = rnd.Next(10);

?????????????? h = rnd.Next(10);

?????????????? g.DrawEllipse(Pens.Blue, x, y, w, h);

?????????? }

?????????? bg.Render();//(4)

?????????? //bg.Render(this.CreateGraphics());

?????????? bg.Dispose();//(5)

3、?? 自己開(kāi)辟一個(gè)緩沖區(qū)(如一個(gè)不顯示的Bitmap對(duì)象),在其中繪制完成后,再一次性顯示。

完整代碼如下:


?????????? Bitmap bt = new Bitmap(400, 400);

?????????? Graphics bg = Graphics.FromImage(bt);

?????????? System.Random rnd = new Random();

?????????? int x, y, w, h, r, i;

?????????? for (i = 0; i < 10000; i++)

?????????? {

?????????????? x = rnd.Next(400);

?????????????? y = rnd.Next(400);

?????????????? r = rnd.Next(20);

?????????????? w = rnd.Next(10);

?????????????? h = rnd.Next(10);

?????????????? bg.DrawEllipse(Pens.Blue, x, y, w, h);

?

?????????? }

?????????? this.CreateGraphics().DrawImage(bt, new Point(0, 0));?
?

另外一個(gè)例子,差不多
Graphics對(duì)象的創(chuàng)建方式:

a、在內(nèi)存上創(chuàng)建一塊和顯示控件相同大小的畫(huà)布,在這塊畫(huà)布上創(chuàng)建Graphics對(duì)象。 接著所有的圖元都在這塊畫(huà)布上繪制,繪制完成以后再使用該畫(huà)布覆蓋顯示控件的背景,從而達(dá)到“顯示一次僅刷新一次”的效果! 實(shí)現(xiàn)代碼(在OnPaint方法中): Rectangle rect = e.ClipRectangle; Bitmap bufferimage = new Bitmap(this.Width, this.Height);
?????Graphics g = Graphics.FromImage(bufferimage); g.Clear(this.BackColor);
?????g.SmoothingMode = SmoothingMode.HighQuality; //高質(zhì)量
?????g.PixelOffsetMode = PixelOffsetMode.HighQuality; //高像素偏移質(zhì)量 foreach (IShape drawobject in doc.drawObjectList)
??????{ if (rect.IntersectsWith(drawobject.Rect))
??????????????? {
??????????????????? drawobject.Draw(g);
??????????????????? if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected
??????????????????????? && this.CurrentOperator == Enum.Operator.Transfrom)//僅當(dāng)編輯節(jié)點(diǎn)操作時(shí)顯示圖元熱點(diǎn)
??????????????????? {
??????????????????????? drawobject.DrawTracker(g);
??????????????????? }
??????????????? }

???????} using (Graphics tg = e.Graphics)
??????????? {
??????????????? tg.DrawImage(bufferimage, 0, 0);  //把畫(huà)布貼到畫(huà)面上
??????????? } b、直接在內(nèi)存上創(chuàng)建Graphics對(duì)象: Rectangle rect = e.ClipRectangle; BufferedGraphicsContext currentContext = BufferedGraphicsManager.Current;
??????????? BufferedGraphics myBuffer = currentContext.Allocate(e.Graphics, e.ClipRectangle);
??????????? Graphics g = myBuffer.Graphics;
??????????? g.SmoothingMode = SmoothingMode.HighQuality;
??????????? g.PixelOffsetMode = PixelOffsetMode.HighSpeed;
??????????? g.Clear(this.BackColor);
??????????? foreach (IShape drawobject in doc.drawObjectList)
??????????? {
??????????????? if (rect.IntersectsWith(drawobject.Rect))
??????????????? {
??????????????????? drawobject.Draw(g);
??????????????????? if (drawobject.TrackerState == config.Module.Core.TrackerState.Selected
??????????????????????? && this.CurrentOperator == Enum.Operator.Transfrom)//僅當(dāng)編輯節(jié)點(diǎn)操作時(shí)顯示圖元熱點(diǎn)
??????????????????? {
??????????????????????? drawobject.DrawTracker(g);
??????????????????? }
??????????????? }
??????????? } myBuffer.Render(e.Graphics);
???????????g.Dispose();
???????????myBuffer.Dispose();//釋放資源 至此,雙緩沖問(wèn)題解決,兩種方式的實(shí)現(xiàn)效果都一樣,但最后一種方式的占有的內(nèi)存很少,不會(huì)出現(xiàn)內(nèi)存泄露!



接下來(lái)是對(duì)acdsee拖動(dòng)圖片效果的實(shí)現(xiàn)。開(kāi)始不懂雙緩沖,以為雙緩沖可以解決這個(gè)問(wèn)題,結(jié)果發(fā)現(xiàn)使用了雙緩沖沒(méi)啥效果,請(qǐng)教了高人,然后修改了些代碼,完成這個(gè)效果。
圖片是在pictureBox1里。
??????? Bitmap currentMap;
??????? bool first = true;
??????? private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
??????? {
??????????? if (zoom == 0)
??????????? {
??????????????? if (e.Button == MouseButtons.Left) //dragging
??????????????????? mousedrag = e.Location;
??????????????? Image myImage = myMap.GetMap();
??????????????? currentMap = new Bitmap(myImage);
??????????????? first = false;
??????????? }????
??????? }

??????? private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
??????? {
??????????? if (zoom == 0&&!first)
??????????? {
??????????????????? Image img = new Bitmap(Size.Width, Size.Height);
??????????????????? Graphics g = Graphics.FromImage(img);
??????????????????? g.Clear(Color.Transparent);//圖片移動(dòng)后顯示的底色
??????????????????? g.SmoothingMode = SmoothingMode.HighQuality; //高質(zhì)量
??????????????????? g.PixelOffsetMode = PixelOffsetMode.HighQuality; //高像素偏移質(zhì)量
??????????????????? g.DrawImageUnscaled(currentMap, new System.Drawing.Point(e.Location.X - mousedrag.X, e.Location.Y - mousedrag.Y));//在g中移動(dòng)圖片,原圖在(0,0)畫(huà)的,所以直接用new System.Drawing.Point(e.Location.X - mousedrag.X, e.Location.Y - mousedrag.Y)就好。
??????????????????? g.Dispose();
??????????????????? pictureBox1.Image = img;//img是在鼠標(biāo)這個(gè)位置時(shí)生成被移動(dòng)后的暫時(shí)的圖片
??????????? }
??????? }

??????? private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
??????? {
??????????? if (zoom == 0)
??????????? {
??????????????? System.Drawing.Point pnt = new System.Drawing.Point(Width / 2 + (mousedrag.X - e.Location.X),
??????????????????????????????????????????????????????????????????????????????? Height / 2 + (mousedrag.Y - e.Location.Y));
???????????????? myMap.Center = myMap.ImageToWorld(pnt);
??????????????? pictureBox1.Image = myMap.GetMap();
??????????????? first = true;
??????????? }
??????? }

說(shuō)說(shuō)思路,在鼠標(biāo)點(diǎn)下時(shí)創(chuàng)建一個(gè)bitmap,currentMap,用它來(lái)存放當(dāng)前圖像。鼠標(biāo)移動(dòng)時(shí),根據(jù)鼠標(biāo)位置畫(huà)圖,最后,鼠標(biāo)up時(shí),重新畫(huà)圖。

總結(jié)

以上是生活随笔為你收集整理的c#双缓冲绘图(不闪烁的几种方法)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。