VÍ DỤ MINH HỌA
public class Bounce extends Applet implements Runnable,
ActionListener{
private ArrayList circles;
private int width, height;
private Button startButton, stopButton;
private Thread animationThread = null;
public void init() {
setBackground(Color.WHITE);
width = getSize() width;
getSize().height = getSize().height;
circles = new ArrayList();
startButton = new Button("Start a circle");
startButton.addActionListener(this);
add(startButton);
stopButton = new Button("Stop all circles");
stopButton.addActionListener(this);
add(stopButton);
}VÍ DỤ MINH HỌA
public void actionPerformed(ActionEvent event) {
if (event.getSource() == startButton) {
if (circles.size() == 0) {
getGraphics().clearRect(0, 0, getSize().width,
getSize().height);
animationThread new Thread(this);
animationThread.start();
}
int radius = 25;
int x = radius + randomInt(width - 2 * radius);
int y = radius + randomInt(height - 2 * radius);
int deltaX = 1 + randomInt(10);
int deltaY = 1 + randomInt(10);
circles.add(new MovingCircle(x, y, radius, deltaX, deltaY));
} else if (event.getSource() == stopButton) {
if (animationThread != null) {
animationThread = null;
circles.clear();
}
}
repaint();
}
30 trang |
Chia sẻ: trungkhoi17 | Lượt xem: 434 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Bài giảng Đa luồng trong đồ họa - Vũ Đình Hồng, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
Multithreaded
Graphics
Đa luồng trong đồ họa
GV: Vũ Đình Hồng
Khoa: CNTT – TỨD
NỘI DUNG CHÍNH
1. Các phương pháp đa luồng trong tiếp
cận đồ họa:
+ Khả năng vẽ lại trong paint
+ Khả năng vẽ trực tiếp trên cửa sổ
Window
+ Bỏ qua lối update thông thường khi vẽ
mà sử dụng paint để tăng khả năng
update
+ Kỹ thuật Double Buffering
2. Giảm thiểu giật và nháy hình (Reducing
Flicker) trong đồ họa chuyển động
3. Hiện thực kĩ thuật Double Buffering
4. Kĩ thuật chuyển động hình ảnh
5. Điều khiển và kiểm soát các Timer trong
chương trình
THREAD
Đơn luồng và Đa luồng ?
- Một “dòng điều khiển" trong chương trình.
- Các chương trình thường chỉ có một dòng
điều khiển.
- Với đa luồng, ta có thể có nhiều dòng điều
khiển được thực hiện cùng lúc trong 1 chương
trình.
Luồng trong Java ?
- Khi chương trình Java thực thi hàm main() tức
là tạo ra một luồng (luồng main). Trong luồng
main:
+ Có thể tạo các luồng con.
+ Phải đảm bảo main là luồng kết thúc cuối
cùng.
+ Khi luồng main ngừng thực thi, chương trình
sẽ kết thúc
TIẾP CẬN ĐỒ HỌA
Các phương pháp chính
- Vẽ lại tất cả trong paint:
* Đơn giản và dễ dàng, tuy nhiên kéo theo là
hình ảnh vẽ sẽ bị giật và màn hình đôi khi nhấp
nháy.
- Vẽ trực tiếp trên cửa sổ Window:
* Dễ dàng, hiệu quả, không bị giật hình, tuy
nhiên khi update kết quả không bảo đảm vẫn
tồn tại ở lần vẽ sau.
- Tránh update, dùng paint để tăng khả năng
update:
* Loại bỏ hoàn toàn nhấp nháy và giật hình, có
cải thiện hơn về mặt hiệu quả và mặt update,
nhưng đòi hỏi hình ảnh không được chồng chéo
lên nhau
- Kĩ thuật Double Buffering (tăng gấp đôi bộ đệm):
* Hầu như là hiệu quả và không gặp vấn đề khi
hình ảnh có sự chồng chéo, phức tạp và tốn
tài nguyên
VẼ LẠI TRONG PAINT
*Ý tưởng:
- Dùng các tác động người dùng để thay đổi
những cấu trúc dữ liệu phi-đồ họa, sau đó
gọi hàm vẽ lại repaint.
- Phương thức repaint thiết lập 1 cờ, trong
quá trình xử lý, dùng để gọi phương thức
update.
- Phương thức update sẽ xóa màn hình, và
gọi hàm paint.
- Phương thức paint vẽ lại tất cả mọi thứ.
Sửa dữ liệu -> repaint -> update -> paint
*Ưu điểm: Đơn giản
* Khuyết điểm: Chậm, giật và nháy
hình.
-
VÍ DỤ MINH HỌA
import java.applet.Applet;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
public class DrawCircles extends Applet
{
private ArrayList circles;
public void init() {
circles = new ArrayList();
addMouseListener(new CircleDrawer());
setBackground(Color.WHITE);
}
...
private class CircleDrawer extends MouseAdapter {
public void mousePressed(MouseEvent event) {
circles.add(new SimpleCircle(event.getX(),
event.getY(), 25));
repaint();
}
}
VÍ DỤ MINH HỌA
public void paint(Graphics g) {
for(SimpleCircle circle: circles) {
circle.draw(g);
}
}
}
//------------------------------------------------
public class SimpleCircle {
private int x, y, radius;
public SimpleCircle(int x, int y, int radius) {
setX(x);
setY(y);
setRadius(radius);
}
public void draw(Graphics g) {
g.fillOval(x - radius, y - radius,
radius * 2, radius * 2);
}
...
}
VÍ DỤ MINH HỌA
Lưu trữ các kết quả của các lần vẽ trước trong cơ
sở dữ liệu thường trực của ứng dụng, sau đó là vẽ
lại toàn bộ tất cả mỗi khi lệnh paint mới được gọi,
kết quả là chỉ 1 bản vẽ tồn tại trong khi đó là sự
chồng nhau và lộ ra của Window
VẼ TRỰC TIẾP TRÊN WINDOW
*Ý tưởng:
- Tạo 1 phương thức tùy ý nào đó (khác với
paint) có thể gọi được hàm getGraphics,từ đó
lấy được đối tượng đồ họa của Window.
- Sử dụng đối tượng này để vẽ đồ họa.
- Hình ảnh sẽ bị mất nếu:
+ Window bị chồng hay bị lộ ra ngoài.
+ Có phương thức update (ví dụ như
repaint)
*Ưu điểm: Nhanh chóng
* Khuyết điểm: Chỉ có tính tạm thời
VÍ DỤ MINH HỌA
public class Rubberband extends Applet {
private int startX, startY, lastX, lastY; p
...
private void drawRectangle(Graphics g, int startX,
int startY, int stopX, int stopY ) {
int x, y, w, h;
x = Math.min(startX, stopX);
y = Math.min(startY, stopY);
w = Math.abs(startX - stopX);
h = Math abs(startY stopY);
Math.- g.drawRect(x, y, w, h);
}
...
private class RectRecorder extends MouseAdapter {
public void mousePressed(MouseEvent event) {
startX = event.getX();
startY = event.getY();
lastX = startX;
lastY = startY;
}
VÍ DỤ MINH HỌA
public void mouseReleased(MouseEvent event) {
Graphics g = getGraphics();
g.setColor(Color.BLUE);
drawRectangle(g, startX, startY, lastX, lastY);
}
}
private class RectDrawer extends MouseMotionAdapter {
public void mouseDragged(MouseEvent event) {
int x = event getX();
event.int y = event.getY();
Graphics g = getGraphics();
g.setXORMode(Color.LIGHT_GRAY);
drawRectangle(g, startX, startY, lastX, lastY);
drawRectangle(g, startX, startY, x, y);
lastX = x;
lastY = y;
}
}
}
VÍ DỤ MINH HỌA
BỎ QUA PHƯƠNG THỨC UPDATE
*Ý tưởng:
- Vẫn có repaint (gồm update) nhưng tránh
không xóa màn hình bằng cách bỏ qua
phương thức update:
public void update(Graphics g) {
paint(g);
}
- Giả định các đối tượng được vẽ không
trùng nhau, khi vẽ 1 hình mới, ta sẽ xóa đi
hình cũ bằng cách vẽ lên hình cũ một hình
cùng màu với màu nền. *Ưu điểm: Nhanh
hơn, không giựt và nháy hình
* Khuyết điểm: Lỗi khi xử lý hình
chồng nhau
VÍ DỤ MINH HỌA
public class Bounce extends Applet implements Runnable,
ActionListener{
private ArrayList circles;
private int width, height;
private Button startButton, stopButton;
private Thread animationThread = null;
public void init() {
setBackground(Color.WHITE);
width = getSize() width;
getSize().height = getSize().height;
circles = new ArrayList();
startButton = new Button("Start a circle");
startButton.addActionListener(this);
add(startButton);
stopButton = new Button("Stop all circles");
stopButton.addActionListener(this);
add(stopButton);
}
VÍ DỤ MINH HỌA
public void actionPerformed(ActionEvent event) {
if (event.getSource() == startButton) {
if (circles.size() == 0) {
getGraphics().clearRect(0, 0, getSize().width,
getSize().height);
animationThread new Thread(this);
animationThread.start();
}
int radius = 25;
int x = radius + randomInt(width - 2 * radius);
int y = radius + randomInt(height - 2 * radius);
int deltaX = 1 + randomInt(10);
int deltaY = 1 + randomInt(10);
circles.add(new MovingCircle(x, y, radius, deltaX, deltaY));
} else if (event.getSource() == stopButton) {
if (animationThread != null) {
animationThread = null;
circles.clear();
}
}
repaint();
}
VÍ DỤ MINH HỌA
public void run() {
Thread myThread = Thread.currentThread();
while(animationThread==myThread) {
repaint();
pause(100);
}
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
for(MovingCircle circle: circles) {
g.setColor(getBackground());
circle.draw(g); // Old position.
circle.move(width, height);
g.setColor(getForeground());
circle.draw(g); // New position.
}
}
VÍ DỤ MINH HỌA
public void run() {
Thread myThread = Thread.currentThread();
while(animationThread==myThread) {
repaint();
pause(100);
}
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
for(MovingCircle circle: circles) {
g.setColor(getBackground());
circle.draw(g); // Old position.
circle.move(width, height);
g.setColor(getForeground());
circle.draw(g); // New position.
}
}
VÍ DỤ MINH HỌA
TĂNG GẤP ĐÔI BỘ NHỚ ĐỆM
*Ý tưởng:
-Không vẽ trên cửa sổ window mà trên cửa
sổ Off-screen Pixmap, sau đó ta vẽ Pixmap
này lên cửa sổ.
*Các bước thực hiện:
1. Bỏ qua cập nhật, chỉ gọi hàm paint :
Giảm giật và nháy hình
2. Tạo hình ảnh bằng createImage: hình ảnh
này ở một cửa sổ khác, khi cửa sổ vẽ
thực sự được gọi thì hình ảnh mới hoàn
toàn được gọi.
3. Sử dụng getGraphics: để lấy đối tượng
đồ họa đã vẽ
TĂNG GẤP ĐÔI BỘ NHỚ ĐỆM
4. Với mỗi bước mới, xóa các hình ảnh và
vẽ lại tất cả lên trên: Nhanh hơn so với
việc vẽ trên 1 cửa sổ đang hiển thị.
5. Vẽ các hình ảnh Off-Screen lên cửa sổ
vẽ, sử dụng hàm drawImage
*Ưu điểm: Nhanh hơn, xử lý tốt những
hình chồng nhau trên cửa sổ
* Khuyết điểm: Trở nên phức tạp hơn,
sử dụng Off-Screen Pixmap tốn bộ nhớ hơn,
đôi khi việc giật và nháy hình sẽ xảy ra
(nếu tạo bộ đệm không tốt)
DOUBLE BUFFERING
public class DoubleBufferBounce extends Applet
implements Runnable, ActionListener {
private ArrayList circles;
private int width, height;
private Image offScreenImage;
private Graphics offScreenGraphics;
private Button startButton, stopButton;
private Thread animationThread = null;
public void init() {
setBackground(Color.WHITE);
width = getSize().width;
height = getSize().height;
offScreenImage = createImage(width, height);
offScreenGraphics = offScreenImage.getGraphics();
offScreenGraphics.setColor(Color.BLACK);
circles = new ArrayList();
...
}
DOUBLE BUFFERING
public void run() {
Thread myThread = Thread.currentThread();
while(animationThread==myThread) {
for(MovingCircle circle: circles) {
circle.move(width, height);
}
repaint();
pause(100);
}
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
offScreenGraphics.clearRect(0, 0, width, height);
for(MovingCircle circle: circles) {
circle.draw(offScreenGraphics);
}
g.drawImage(offScreenImage, 0, 0, this);
}
DOUBLE BUFFERING
CHUYỂN ĐỘNG ĐỒ HỌA
*Ý tưởng:
- Sử dụng mảng array, ta lưu 1 chuỗi các
hình ảnh liên tục vào trong 1 mảng.
- Khởi động một luồng tạo chu kì cho chuỗi
hình ảnh và vẽ lên trên đối tượng đồ họa.
* Mỗi lần luồng tạo chu kì với vòng lặp
while, hàm repaint (gồm update) sẽ
được gọi để cập nhật hình ảnh lên cửa sổ
vẽ
- Sử dụng cờ để ngưng chuyển động.
CHUYỂN ĐỘNG ĐỒ HỌA
public class ImageAnimation extends Applet {
private static final int NUMDUKES = 2;
private Duke[] dukes;
private int i;
public void init() {
dukes = new Duke[NUMDUKES];
setBackground(Color.white);
}
public void start() {
int tumbleDirection;
for (int i=0; i<NUMDUKES ; i++) {
tumbleDirection = (i%2 == 0) ? 1 : -1 ;
dukes[i] = new Duke(tumbleDirection,
this);
dukes[i].start();
}
}
...
CHUYỂN ĐỘNG ĐỒ HỌA
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
for ( i=0 ; i<NUMDUKES ; i++) { ){
if (dukes[i] != null) {
g.drawImage(Duke.images[dukes[i].getIndex(
)],200*i, 0, this);
}
}
}
public void stop() for (int i=0; i<NUMDUKES ; i++)
{
if (dukes[i] != null) {
dukes[i].setState(Duke.STOP);
}
}
TIMER
- Timer la ̀một lớp tiện ích giúp cho việc lập
lịch và kiểm soát việc thực thi một task vụ
nào đó.
- Một số hàm thông dụng trong Timer như:
* schedule lên lic̣h để thực thi TimerTask
khi naò bắt đâù, kết thúc hay lặp lại,
* cancel dừng timer và hủy tất cả các
task đã lên lịch trong timer
* purge xóa tất ca ̉các task đa ̃dùng
trong hàng đợi timer
- TimerTask là một lớp trừu tượng
implement Runnable interface, nó giúp cho
việc lập lịch thực thi các thread
PHƯƠNG PHÁP TIẾP CẬN TIMER
- Swing định nghĩa 1 lớp Timer là dùng để
gọi lên 1 chu trình đơn và định kì nào đó,
mỗi lần gọi lên lớp Timer là 1 ActionEvent sẽ
được gọi.
-Phương pháp tiếp cận của Timer trong
Java:
Timer timer = new Timer(milliseconds, listener);
timer.start();
...
...
timer stop();
PHƯƠNG THỨC CỦA TIMER
- Star/Stop
* Bắt đầu hoặc dừng lại chu trình của Timer
- Restart
* Hủy bỏ tất cả các event nào chưa được thực
hiện và khởi động lại bộ đếm thời gian.
- setCoalese
* Khi tạo một chuỗi gồm nhiều chu trình (liên
hiệp), hàm trên dùng để tắt hay mở 1 liên hiệp.
* Mặc định, nếu 1 event đang ở trong hàng đợi
(có liên hiệp), 1 ActionEvent mới sẽ không được
tạo ra trong lần gọi tiếp theo.
- setRepeats:
* Thiết lập bộ đếm thời gian sẽ được gọi 1 lần
hay theo định kì.
Q & A
THANKS FOR
LISTENING !!!
Các file đính kèm theo tài liệu này:
- bai_giang_da_luong_trong_do_hoa_vu_dinh_hong.pdf