雙緩衝在畫板程式中的應用(一) (轉)
/文 14E.T.
1.用雙緩衝解決畫板中的重新整理問題
我們用編制畫板程式的時候,總是存在一個重新整理的問題:當Canvas所在的視窗最小化或者被其他應用程式遮擋後,再次恢復,Canvas上的圖形資料將被部分或者完全擦除掉.
通常解決這個問題的方法是在Canvas的paint()中重繪圖形,但是由於在繪圖的過程中產生了大量的資料,重新在Canvas上繪製這些資料將導致大量的開銷,還會產生閃爍,故該方法可行但並不可取.
利用雙緩衝技術可以很好的解決這個問題,其主要原理是開闢兩個圖形緩衝區,一個是前臺的顯示緩衝(也就是Canvas),一個是後臺的圖形緩衝(通常是Image).在繪製圖形時,我們對這兩個緩衝區進行同步,相當於為前臺的資料作了一個後臺.當前臺的圖形被遮蓋需要恢復的時候,我們就可以用這個後臺備份來恢復,具體方法是重寫paint()函式,將備份好的影像一次性的畫到螢幕上去.
為什麼是paint()?這裡需要先了解一下有關Java處理AWT繪圖的基礎知識:Java的顯示更新是由一個AWT執行緒來控制完成的.該執行緒主要負責兩種與顯示更新相關的情況.第一種情況稱為曝光,表示部分顯示區域毀壞或需要清除.這種情況發生時,系統直接paint()方法;第二種情況是程式決定重畫顯示區域,新增一些新的內容,此時需要程式呼叫成員的repaint()方法,repaint()方法呼叫成員的update(),update()再呼叫paint()方法.顯然我們所說的就是第一種.只要Canvas所在的視窗最小化或者被其他應用程式遮擋住,系統就會呼叫paint()對畫布進行重繪.如果我們在paint()方法中什麼都不做,就只能眼睜睜的看著辛辛苦苦畫的線條一旦被覆蓋就再也看不見了.
作為起點,請先看一個最簡單的畫板程式,請注意,以下程式使用的是j2sdk1.4.1版本,在的IE下(不是Tencent Explorer)全部測試透過:
//:WBApplet.java
import java.applet.*;
public class WBApplet extends Applet{
final static int DEFAULT_BOARDWIDTH=700;
final static int DEFAULT_BOARDHEIGHT=400;
public void init(){
super.init();
setLayout(null);
whiteBoard = new WhiteBoard(this);
whiteBoard.reshape(0,0,DEFAULT_BOARDWIDTH,DEFAULT_BOARDHEIGHT);
add(whiteBoard);
}
WhiteBoard whiteBoard;
}
///:~
//:WhiteBoard.java
java.awt.*;
import java.awt.event.*;
public class WhiteBoard extends Canvas implements MouseMotionListener,MouseListener{
final static int DEFAULT_BOARDWIDTH=700;
final static int DEFAULT_BOARDHEIGHT=400;
int x0,y0,x1,y1;
WhiteBoard(WBApplet WBApplet1){
parent = WBApplet1;
addMouseMotionListener(this);
addMouseListener(this);
}
synchronized public void update_buffer(Graphics g,DrawItem data) {
g.drawLine(data.x0,data.y0,data.x1,data.y1);
}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
public void mouseMoved(MouseEvent e){}
public void mouseDragged(MouseEvent e){
x1=e.getX();
y1=e.getY();
Graphics g = getGraphics();
update_buffer(g,new DrawItem(x0,y0,x1,y1));
g.dispose();
x0=x1;
y0=y1;
}
public void mousePressed(MouseEvent e){
x0 =e.getX();
y0 =e.getY();
}
WBApplet parent;
}
class DrawItem{
DrawItem(int x0,int y0,int x1,int y1){
this.x0=x0;
this.y0=y0;
this.x1=x1;
this.y1=y1;
}
int x0;
int y0;
int x1;
int y1;
}
///:~
我們將白板需完成的所有邏輯操作封裝在了一個WhiteBoard類中,以方便主程式的Applet呼叫.同時,定義了一個繪圖的資料類DrawItem,用來封裝圖形資料.繪圖的操作都寫在update_buffer中.顯然,這個程式無法實現重新整理後的恢復,我們需要使用雙緩衝技術.
為了實現雙緩衝,首先定義圖形緩衝區如下
private Image off_screen_buf;
private Graphics off_screen_gc;
並在WhiteBoard類的建構函式中對其進行初始化
off_screen_buf =parent.createImage(DEFAULT_BOARDWIDTH,DEFAULT_BOARDHEIGHT);
off_screen_gc = off_screen_buf.getGraphics();
在處理使用者繪製圖形的函式中,我們使用update_buffer對顯示緩衝和圖形緩衝同時進行更新
Graphics g = getGraphics();
update_buffer(g,new DrawItem(x0,y0,x1,y1));//前臺更新畫布
update_buffer(off_screen_gc,new DrawItem(x0,y0,x1,y1));//後臺更新緩衝
g.dispose();
顯然,後臺的更新操作是不可視的,所以是off-screen.
最後,重寫paint()方法,呼叫copy_from_offscreen_buf(g)將圖形緩衝區的影像畫到螢幕上去.
public void paint(Graphics g){
copy_from_offscreen_buf(g);
}
void copy_from_offscreen_buf(Graphics g){
if(g != null)
g.drawImage(off_screen_buf, 0, 0, null);
}
就是這麼簡單的幾行程式碼,就可以讓我們完全的避免圖形不能恢復的問題.下面是WhiteBoard經改進後的完整程式碼.
//:WhiteBoard.java
import java.awt.*;
import java.awt.event.*;
public class WhiteBoard extends Canvas implements MouseMotionListener,MouseListener{
final static int DEFAULT_BOARDWIDTH=700;
final static int DEFAULT_BOARDHEIGHT=400;
int x0,y0,x1,y1;
WhiteBoard(WBApplet WBApplet1){
parent = WBApplet1;
off_screen_buf =parent.createImage(DEFAULT_BOARDWIDTH,DEFAULT_BOARDHEIGHT);
off_screen_gc = off_screen_buf.getGraphics();
addMouseMotionListener(this);
addMouseListener(this);
}
synchronized public void update_buffer(Graphics g,DrawItem data) {
g.drawLine(data.x0,data.y0,data.x1,data.y1);
}
public void mouseMoved(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
public void mouseDragged(MouseEvent e){
x1=e.getX();
y1=e.getY();
Graphics g = getGraphics();
update_buffer(g,new DrawItem(x0,y0,x1,y1));
update_buffer(off_screen_gc,new DrawItem(x0,y0,x1,y1));
g.dispose();
x0=x1;
y0=y1;
}
public void mousePressed(MouseEvent e){
x0 =e.getX();
y0 =e.getY();
}
public void paint(Graphics g){
copy_from_offscreen_buf(g);//把這句話遮蔽掉,就不能恢復使用者繪製的圖形了
}
void copy_from_offscreen_buf(Graphics g){
if(g != null)
g.drawImage(off_screen_buf, 0, 0, null);
}
private Image off_screen_buf;
private Graphics off_screen_gc;
WBApplet parent;
}
class DrawItem{
DrawItem(int x0,int y0,int x1,int y1){
this.x0=x0;
this.y0=y0;
this.x1=x1;
this.y1=y1;
}
int x0;
int y0;
int x1;
int y1;
}
///:~
執行一下,看是不是不一樣了.這一次你想讓你畫的東西消失都不可能了.為了將這個原理說清楚,以上的程式碼我都沒有編寫的太複雜,下一次我們會建立更加複雜,更加完善的畫板程式.
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752019/viewspace-956253/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- PSRAM在資料緩衝應用中可以取代SRAM或SDRAM
- Duilib的雙緩衝實現,附帶GDI、WTL的雙緩衝實現UI
- 雙緩衝學習
- PHP 輸出緩衝區應用PHP
- Libevent應用 (三) 資料緩衝
- 你真的會用PostGIS中的buffer緩衝嗎?
- android View 繪圖雙緩衝技術AndroidView繪圖
- 嵌入式核心板在麻醉系統中的應用
- InnoDB 中的緩衝池(Buffer Pool)
- 用canvas畫一個七竅板Canvas
- Golang併發程式設計有緩衝通道和無緩衝通道(channel)Golang程式設計
- Qt5雙緩衝機制與例項QT
- Java_轉換流和緩衝流Java
- radan軟體在鈑金件數衝加工中的應用
- Java中縮放緩衝影像Java
- Java緩衝流概述詳解(原理畫圖分析)Java
- 用canvas實現一個簡單的畫板Canvas
- 緩衝區溢位小程式分析
- 緩衝管理
- 測試C#GDI+雙緩衝高效繪圖--BufferedGraphicsContextC#繪圖Context
- STM32串列埠DMA接收雙緩衝串列埠
- Android自定義View之雙緩衝機制和SurfaceViewAndroidView
- 「譯」有限狀態機在 CSS 動畫中的應用CSS動畫
- socket程式設計在TCP中的應用程式設計TCP
- 什麼?無限緩衝的佇列(一)?佇列
- Mysql innodb引擎(一)緩衝和索引MySql索引
- 緩衝帶裡的少年
- 輸出緩衝
- MySQL中讀頁緩衝區buffer poolMySql
- GlusterFS在Kubernetes中的應用實戰(一)
- 設計模式在vue中的應用(一)設計模式Vue
- Debian 12採用 Ubuntu三重緩衝Ubuntu
- 動畫利器-lottie在懂表帝App中的實戰應用動畫APP
- 從一個問題中瞭解數學在程式設計中的應用程式設計
- PHP的輸出緩衝區PHP
- arcgis10之緩衝區轉化成四邊形
- IO流(02)--屬性集、緩衝流、轉換流
- 面試官:Redis中的緩衝區瞭解嗎面試Redis
- 2.3 應用程式容器中的應用程式概述