Улучшение изображения двойной буферизацией
Суть двойной буферизации в том, что в оперативной памяти создается буфер — объект класса image или Bufferedimage, и вызывается его графический контекст, в котором формируется изображение. Там же происходит очистка буфера, которая тоже не отражается на экране. Только после выполнения всех действий готовое изображение выводится на экран.
Все это происходит в методе updateo, а метод paint о только обращается к update (). Листинги 15.10—15.11 разъясняют данный прием.
Листинг 15.10. Двойная буферизация с помощью класса image
public void update(Graphics g){
int w = getSize().width, h = getSize().height;
// Создаем изображение-буфер в оперативной памяти
Image offlmg = createlmage(w, h);
// Получаем его графический контекст
Graphics offGr = offImg.getGraphics();
// Меняем текущий цвет буфера на цвет фона
offGr.setColor(getBackground());
//и заполняем им окно компонента, очищая буфер
offGr.fillRect(0, 0, w, h);
// Восстанавливаем текущий цвет буфера
offGr.setColor(getForeground());
// Для листинга 15.9 выводим в контекст изображение
offGr.drawlmage(img[count % 10], 0, 0, this);
// Рисуем в графическом контексте буфера
// (необязательное действие)
paint(offGr);
// Выводим изображение-буфер на экран
// (можно перенести в метод paint())
g.drawlmage(offlmg, 0, 0, this); }
// Метод paint() необязателен
public void paint(Graphics g)J update(g); }
Листинг 15.11. Двойная буферизация с помощью класса Bufferedimage
public void update(Graphics g){
Graphics2D g2 = (Graphics2D},g;
int w = getSize().width, h = getSize().height;
// Создаем изображение-буфер в оперативной памяти
Bufferedimage bi = (Bufferedimage)createlmage(w, h);
// Создаем графический контекст буфера
Graphics2D big = bi.createGraphics();
// Устанавливаем цвет фона
big.setColor(getBackground());
// Очищаем буфер цветом фона
big.clearRect(0, 0, w, h);
// Восстанавливаем текущий цвет
big.setColor(getForeground());
// Выводим что-нибудь в графический контекст big
// ...
// Выводим буфер на экран
g2.drawImage(bi, 0, 0, this);
}
Метод двойной буферизации стал фактическим стандартом вывода изменяющихся изображений, а в библиотеке Swing он применяется автоматически.
Данный метод удобен и при перерисовке отдельных частей изображения. В этом случае в изображении-буфере рисуется неизменяемая часть изображения, а в методе paint() — то, что меняется при каждой перерисовке.
В листинге 15.12 показан второй способ анимации — кадры изображения рисуются непосредственно в программе, в методе update (), по заданному закону изменения изображения. В результате красный мячик прыгает на фоне изображения.
Листинг 15.12. Анимация рисованием
import Java.awt.*;
import j ava.awt.event.*;
import Java.awt.geom.*;
import java.awt.image.*;
class DrawAniml extends Frame{
private Image img;
private int count;
DrawAniml(String s) {
super(s);
MediaTracker tr = new MediaTracker(this);
img = getToolkit().getlmage("back2.jpg");
tr.addlmage(img, 0);
try{
tr.waitForlD(0) ;
}catch(InterruptedException e) {}
SetSize(400, 400);
setvisible(true);
}
public void update(Graphics g){
Graphics2D g2 = (Graphics2D)g;
int w = getSizeO.width, h = getSize().height;
Bufferedlmage bi = (Bufferedlmage)createlmage(w, h) ;
Graphics2D big = bi.createGraphics();
// Заполняем фон изображением img
big.drawlmage(img, 0, 0, this);
// Устанавливаем цвет рисования
big.setColor(Color.red);
// Рисуем в графическом контексте буфера круг,
// перемещающийся по синусоиде
big.fill(new Arc2D.Double(4*count, 50+30*Math.sin(count),
50, 50, 0, 360, Arc2D.OPEN));
// Меняем цвет рисования
big.setColor(getForeground());
// Рисуем горизонтальную прямую
big.draw(new Line2D.Double(0, 125, w, 125));
// Выводим изображение-буфер на экран
g2.drawlmage(bi, 0, 0, this); }
public void go(){
while(count < 100){
repaint();
try{
Thread.sleep(10);
}catch(InterruptedException e){}
count++;
}
}
public static void main(String[] args){
DrawAniml f = new DrawAniml(" Анимация");
f.go();
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent ev){
System.exit(0);
}
});
}
}
Эффект мерцания, переливы цвета, затемнение и прочие эффекты, получающиеся заменой отдельных пикселов изображения, удобно создавать с помощью класса Memoryimagesource. Методы newPixeis() этого класса вызывают немедленную перерисовку изображения даже без обращения к методу repaint(), если перед этим выполнен метод setAnimated(true). Чаще всего применяются два метода:
В листинге 15.13 показано применение этого способа. Квадрат, выведенный на экран, переливается разными цветами.
Листинг 15.13. Анимация с помощью MemorylmageSource
import Java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
class InMemory extends Frame{
private int w = 100, h = 100, count;
private int[] pix = new int[w * h];
private Image img;
MemorylmageSource mis;
InMemory(String s){ super(s);
int i = 0;
for(int у = 0; у < h; y++){
int red = 255 * у / (h - 1);
for(int x = 0; x < w; x++){
int green = 25$ * x / (w - 1);
pix[i++] = (255 « 24}|(red << 16)|(green << 8)| 128;
}
}
mis = new MemorylmageSource(w, h, pix, 0, w);
// Задаем возможность анимации
mis.setAnimated(true);
img = createImage(mis);
setSize(350, 300);
setVisible(true);
}
public void paint(Graphics gr){
gr.drawImage(img, 10, 30, this);
}
public void update(Graphics g){ paint(g); }
public void got){
while (count < 100){
int i = 0;
// Изменяем массив пикселов по некоторому закону
for(int у - 0; у < h;,y++)
for (int x. = 0; x < w; x++)
pix[i++J = (255 « 24)|(255 + 8 * count « 16)|
(8*count «8)| 255 + 8 * count;
// Уведомляем потребителя об изменении
mis.newPixels();
try{
Thread.sleep(100);
}catch(InterruptedException e){}
count++;
}
}
public static void main(String[] args){
InMemory f= new InMemory(" Изображение в памяти");
f.go();
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent ev){
System.exit(0);
}
));
}
}
Вот и все средства для анимации, остальное — умелое их применение. Комбинируя рассмотренные способы, можно добиться удивительных эффектов. В документации SUN J2SDK, в каталогах demo\applets и demo\jfc\Java2D \src, приведено много примеров апплетов и приложений с анимацией.