Конспект лекций по курсу Выбранные вопросы информатики (часть 2) для специальности Информатика Графика

Вид материалаКонспект
Растровые изображения и анимация
Загрузка и рисование растрового изображения
Класс Image
Ожидание загрузки изображений
Ожидание загрузки добавленных изображений
Другие методы класса MediaTracker
Применение интерфейса ImageObserver
Видео в окне аплета
Аплет CDRotation
Описание исходных текстов
Метод stop
Метод paint
Метод run
Метод displayImage
Звук в аплетах Java
Загрузка и проигрывание звуковых файлов
Аплет PlayClip
Исходные тексты приложения
Подобный материал:
1   ...   9   10   11   12   13   14   15   16   17

Лекция 6

Растровые изображения и анимация

Одно из наиболее распространенный применений аплетов связано с рисованием простых или анимированных растровых изображений. На серверах Web изображения обычно хранятся в форматах GIF или JPEG. Оба эти формата обеспечивают сжатие изображения, что весьма актуально из-за невысокой скорости передачи данных в сети Internet.

Рисование растровых изображений в приложениях для операционной системы Windows, составленных на языке программирования С - непростая задача. В классическом программном интерфейсе этой операционной системы отсутствуют функции, с помощью которых можно было бы непосредственно рисовать содержимое файлов с растровыми изображениями. Программист вынужден работать с заголовками таких файлов, выделять таблицу цветов и биты изображений, создавать и реализовывать палитру, заниматься восстановлением сжатых данных и так далее.

Разработчик приложений Java находится в намного лучшем положении, так как библиотеки классов Java содержат простые в использовании и мощные средства, предназначенные для работы с растровыми изображениями. В этой статье мы научим вас рисовать в окне аплета содержимое файлов GIF и JPEG, выполняя при необходимости масштабирование, а также создавать на базе таких файлов анимационные изображения, показывая по очереди отдельные кадры небольшого видеофильма.


Загрузка и рисование растрового изображения

Загрузка растрового изображения из файла выполняется очень просто - с помощью метода getImage, определенного в классе Applet:

public Image getImage(URL url);

public Image getImage(URL url, String name);

Первый вариант метода предполагает использование только одного параметра - адреса URL файла графического изображения. Второй позволяет дополнительно указать относительное расположение файла изображения относительно адреса URL, например:

Image img;

img = getImage("et.ru/~frolov/pic","cd.gif");

Если аплет желает загрузить изображение, расположенное в том же каталоге, что и он сам, это можно сделать следующим образом:

img = getImage(getCodeBase(), "cd.gif");

Метод getCodeBase, определенный в классе Applet, возвращает адрес URL аплета. Вместо него можно использовать метод getDocumentBase, который также определен в классе Applet и возвращает адрес URL документа HTML, содержащего аплет:

img = getImage(getDocumentBase(), "cd.gif");

В любом случае метод getImage создает объект класса Image.

Заметим, что на самом деле метод getImage вовсе не загружает изображение через сеть, как это можно было бы подумать. Он только создает объект класса Image. Реальная загрузка файла растрового изображения будет выполняться методом рисования drawImage, который определен в классе Graphics:

public abstract boolean drawImage(Image img, int x, int y, ImageObserver observer);

public abstract boolean drawImage(Image img, int x, int y, Color bgcolor, ImageObserver observer);

public abstract boolean drawImage(Image img, int x, int y, int width, int height, ImageObserver observer);

public abstract boolean drawImage(Image img, int x, int y, int width, int height, Color bgcolor, ImageObserver observer);

Как видите, существует четыре варианта этого метода.

В качестве первого параметра любому варианту метода передается ссылка на объект класса Image, полученный ранее с помощью метода getImage.

Параметры x и y задают координаты верхнего левого угла прямоугольной области, внутри которой будет нарисовано изображение. Эти параметры также задаются для любого варианта метода drawImage.

Параметр bgcolor задает цвет фона, на котором будет нарисовано изображение. Как вы, вероятно, знаете, изображения GIF могут быть прозрачными. В этом случае цвет фона может иметь большое значение.

Если для рисования выбраны варианты метода drawImage с параметрами width и height, изображение будет нарисовано с масштабированием. При этом указанные параметры будут определять, соответственно, ширину и высоту изображения.

Параметр observer представляет собой ссылку на объект класса ImageObserver, который получит извещение при загрузке изображения. Обычно в качестве такого объекта используется сам аплет, поэтому данный параметр указывается как this.

Вот два примера использования метода drawImage:

g.drawImage(FloppyDiskImg, 25, 3, this);

g.drawImage(FloppyDiskImg, 25, 42, 200, 200, this);

В первой строке изображение FloppyDiskImg рисуется в точке с координатами (25, 3) без масштабирования, во второй - в точке с координатами (25, 42), причем высота и ширина нарисованного изображения будет равна 200 пикселам.

Метод drawImage запускает процесс загрузки и рисования изображения, а затем, не дожидаясь его завершения, возвращает управление. Так как загрузка файла изображения по сети может отнять немало времени, она выполняется асинхронно в отдельной задаче.


Класс Image

Процесс рисования растрового изображения в окне аплета предельно прост - вам достаточно загрузить изображение методом getImage и затем нарисовать его методом drawImage.

Но не забывайте, что метод getImage в действительности только создает объект класса Image, но не загружает его. Давайте посмотрим на класс Image.

В этом классе имеется единственный конструктор без параметров:

public Image();

Вы, однако, скорее всего будете создавать объекты класса Image при помощи метода getImage.

Методы getHeight и getWidth, определенные в классе Image, позволяют определить, соответственно, высоту и ширину изображения:

public abstract int getHeight(ImageObserver observer);

public abstract int getWidth(ImageObserver observer);

Так как при вызове этих методов изображение еще может быть не загружено, в качестве параметров методам передается ссылка на объект ImageObserver. Этот объект получит извещение, когда будет доступна высота или ширина изображения.

Метод getGraphics позволяет получить так называемый внеэкранный контекст отображения для рисования изображения не в окне аплета, а в оперативной памяти:

public abstract Graphics getGraphics();

Эта техника используется для того, чтобы вначале подготовить изображение в памяти, а затем за один прием отобразить его на экране.

Еще один метод класса Image, который мы рассмотрим, называется flush:

public abstract void flush();

Он освобождает ресурсы, занятые изображением.


Ожидание загрузки изображений

Как мы уже говорили в наших предыдущих статьях, загрузка изображений из сети Internet - длительный процесс, который в среднем идет со скоростью 1 Кбайт в секунду. Поэтому изображения загружаются навигатором в отдельной задаче. При этом метод getImage только создает объект класса Image, а метод drawImage инициирует загрузку изображения и рисует его. Причем если файл изображения имеет большую длину, он будет появляться в окне аплета постепенно по мере загрузки.

Однако в некоторых случаях было бы удобно вначале загрузить изображение полностью, а лишь затем выполнять рисование, чтобы изображение появилось на экране сразу. Кроме того, аплет может рисовать сразу несколько изображений в разных местах своего окна или показывать их по очереди в одном и том же месте для достижения эффекта анимации.

Есть ли способ определить, когда изображение будет загружено полностью?

Есть, и причем целых два. Один из них связан с использованием класса MediaTracker, специально предназначенного для этой цели и достаточно удобного в использовании, другой основан на переопределении одного из методов интерфейса ImageObserver.

Применение класса MediaTracker

Для того чтобы выполнить ожидание загрузки нескольких изображений, проще воспользоваться классом MediaTracker, а не интерфейсом ImageObserver.

Как это сделать?

Обычно метод init аплета создает объект класса MediaTracker с помощью конструктора и добавляет в него все изображения, загрузки которых необходимо дождаться.

Создание объекта класса MediaTracker

Объект класса MediaTracker создается следующим образом:

MediaTracker mt;

mt = new MediaTracker(this);

Конструктору класса MediaTracker передается ссылка на компонент, для которого необходимо отслеживать загрузку изображений. В данном случае это наш аплет, поэтому мы передаем конструктору значение this.

Добавление изображений в объект класса MediaTracker

Далее метод init должен создать все необходимые объекты класса Image и добавить их в объект MediaTracker методом addImage. Ниже мы показали фрагмент кода, в котором выполняется добавление трех изображений:

Image img1;

Image img2;

Image img3;

img1 = getImage(getCodeBase(), "pic1.gif");

img2 = getImage(getCodeBase(), "pic2.gif");

img3 = getImage(getCodeBase(), "pic3.gif");

mt.addImage(img1 , 0);

mt.addImage(img2 , 0);

mt.addImage(img3 , 0);

В качестве первого параметра методу addImage передается ссылка на изображение, загрузку которого необходимо отслеживать, а в качестве второго - идентификатор, который можно будет использовать в процессе отслеживания. Если все, что вам нужно, это дождаться окончания загрузки изображений, то для второго параметра вы можете указать нулевое значение.

Ожидание загрузки добавленных изображений

Для того чтобы убедиться, что все изображения загружены, вы можете воспользоваться методом waitForAll. Этот метод инициирует загрузку изображений, а также задержит выполнение вызвавшего потока до момента полной загрузки всех изображений, добавленных в объект класса MediaTracker:

try

{

mt.waitForAll();

}

catch (InterruptedException ex)

{

}

Обратите внимание, что метод waitForAll может создавать исключение InterruptedException. Это исключение возникает, если по какой-либо причине процесс ожидания прерывается.

Чаще всего рисование выполняется в отдельном потоке, поэтому метод waitForAll должен вызываться в начале соответствующего метода run. Ниже мы привели исходные тексты приложения ImageDrawWait, в котором такое ожидание выполняется в методе paint, что приводит, однако, к блокировке работы аплета до момента загрузки всех изображений. В данном случае это не критично, так как кроме рисования изображений наш аплет ничего не делает, однако более предпочтительным является выполнение длительных процессов в отдельном потоке.

Другие методы класса MediaTracker

Какие другие полезные методы, кроме методов addImage и waitForAll есть в классе MediaTracker?

public boolean waitForAll(long ms);

Метод waitForAll с параметром ms позволяет выполнять ожидание в течение заданного времени. Время ожидания задается в миллисекундах. При этом если за указанное время все изображения были успешно загружены, метод waitForAll возвращает значение true, если нет - false.

Вариант метода checkAll с параметром load позволяет проверить, завершилась ли загрузка отслеживаемых изображений:

public boolean checkAll(boolean load);

Если значение параметра load равно true, метод инициирует загрузку изображений.

Если при добавлении изображений методом addImage вы использовали второй параметр этого метода для присваивания разным группам изображений различные идентификаторы, то с помощью метода checkID можно дождаться завершения загрузки отдельной группы изображений:

public boolean checkID(int id);

Есть также вариант этого метода, позволяющий проверить загрузку группы изображений с заданным идентификатором:

public boolean checkID(int id, boolean load);

Метод waitForID с параметрами id и ms позволяет выполнять ожидание загрузки группы изображений с заданным идентификатором в течении указанного периода времени:

public boolean waitForID(int id, long ms);

Класс MediaTracker предоставляет также возможность прослеживать сам процесс загрузки всех добавленных в него изображений или отдельных групп изображений с помощью методов statusAll и statusID:

public int statusAll(boolean load);

public int statusID(int id, boolean load);

В зависимости от значения параметра load эти методы могут инициировать загрузку изображений. Если параметр равен true, загрузка изображений инициируется, если false - выполняется только проверка текущего состояния загрузки.

Методы statusAll и statusID возвращают значение, составленное из отдельных битов состояния при помощи логической операции ИЛИ. Ниже мы перечислили эти биты состояния и привели их краткое описание.

Биты состояния

Описание

MediaTracker.LOADING

Один или несколько отслеживаемых файлов продолжают загружаться

MediaTracker.ABORTED

Загрузка одного или нескольких файлов была прервана

MediatTracker.ERRORED

При загрузке одного или нескольких файлов произошла ошибка

MediaTracker.COMPLETE

Загрузка всех отслеживаемых файлов произошла полностью и успешно

Еще четыре метода, определенных в классе MediaTracker, связаны с обработкой ошибок:

public boolean isErrorAny();

public boolean isErrorID(int id);

public Object[] getErrorsAny();

public Object[] getErrorsID(int id);

Методы isErrorAny и isErrorID позволяют проверить, возникла ли ошибка при загрузке, соответственно, любого из отслеживаемых изображений или изображений из заданной группы. Если ошибка произошла, возвращается значение true, если нет - значение false.

Методы getErrorsAny и getErrorsID возвращают массив объектов, при ожидании загрузки которых произошла ошибка. Первый из этих методов возвращает массив для всех отслеживаемых объектов, второй - только объектов из заданной группы.

Применение интерфейса ImageObserver

Второй способ ожидания завершения процесса загрузки изображений связан с интерфейсом ImageObserver:

Биты флагов для параметра infoflags метода imageUpdate

public final static int ABORT;

public final static int ALLBITS;

public final static int ERROR;

public final static int FRAMEBITS;

public final static int HEIGHT;

public final static int PROPERTIES;

public final static int SOMEBITS;

public final static int WIDTH;

Метод imageUpdate

public abstract boolean

imageUpdate(Image img, int infoflags, int x, int y, int width, int height);

Как видите, в интерфейсе ImageObserver определен единственный метод imageUpdate и набор битовых флагов для этого метода.

Класс Component, от которого происходит класс Applet, реализует интерфейс ImageObserver:

public abstract class java.awt.Component

extends java.lang.Object

implements java.awt.image.ImageObserver

{

. . .

}

Этот интерфейс используется для отслеживания процесса загрузки и перерисовки изображений и других компонент, расположенных внутри компонента. В частности, он используется для отслеживания загрузки и рисования растровых изображений в окне аплета, чем мы и воспользуемся.

В процессе загрузки вызывается метод imageUpdate, поэтому чтобы отслеживать загрузку изображений, наш аплет должен переопределить этот метод.

Процедура ожидания загрузки изображений достаточно проста.

Прежде всего, аплет должен передать в последнем параметре методу drawImage ссылку на интерфейс ImageObserver, который будет применяться для отслеживания процесса загрузки:

g.drawImage(Img, x, y, width, height, this);

Здесь в качестве ссылки на интерфейс ImageObserver мы передали значение this. При этом будет применен интерфейс нашего аплета. Соответственно, нам нужно определить в классе аплета метод imageUpdate, который будет вызываться в процессе загрузки изображений.

Ниже мы привели возможный вариант реализации этого метода:

public boolean imageUpdate( Image img, int flags, int x, int y, int w, int h)

{

// Проверяем, все ли биты изображения загружены

fAllLoaded = ((flags & ALLBITS) != 0);


// Если все, перерисовываем окно

if(fAllLoaded)

repaint();


// Если все биты загружены, дальнейшие вызовы метода imageUpdate не нужны

return !fAllLoaded;

}

Через первый параметр img методу imageUpdate передается ссылка на изображение, загрузка которого отслеживается.

Параметр flags отражает состояние процесса загрузки.

Через остальные параметры x, y, w и h передаются, соответственно, координаты и размеры изображения.

Основное, что должен делать метод imageUpdate для отслеживания процесса загрузки - это проверять флаги flags, дожидаясь установки нужных флагов.

Флаги определены следующим образом:

public final static int WIDTH;

public final static int HEIGHT = 2;

public final static int PROPERTIES = 4;

public final static int SOMEBITS = 8;

public final static int FRAMEBITS = 16;

public final static int ALLBITS = 32;

public final static int ERROR = 64;

public final static int ABORT = 128;

Ниже мы привели краткое описание перечисленных флагов.

Флаг

Описание

WIDTH

Изображение загружено настолько, что стала доступна его ширина. Значение ширины изображения можно получить из параметра w метода imageUpdate

HEIGHT

Аналогично предыдущему, но для высоты изображения. Высоту изображения можно получить из параметра h метода imageUpdateimageUpdate

PROPERTIES

Стали доступны свойства изображения, которые можно получить методом getProperty класса Image. В нашей книге мы опустили описание этого метода

SOMEBITS

Стали доступны биты изображения для рисования в масштабе. Через параметры x, y, h и w передаются координаты и размеры прямоугольной области, которая ограничивает загруженную часть изображения

FRAMEBITS

Загружен очередной фрейм изображения, состоящего из нескольких фреймов. Параметры x, y, h и w следует игнорировать

ALLBITS

Изображение загружено полностью. Параметры x, y, h и w следует игнорировать

ERROR

При загрузке произошла ошибка

ABORT

Загрузка изображения была прервана или отменена

Анализируя состояние флагов, метод imageUpdate может следить за ходом загрузки изображений, отображая, например, процент завершения процесса загрузки или выполняя какие-либо другие действия.

Если вам нужно только дождаться завершения процесса загрузки, достаточно использовать флаг ALLBITS. Для проверки ошибок воспользуйтесь флагами ERROR и ABORT.


Видео в окне аплета

Наиболее динамичные страницы сервера Web содержат анимационные изображения в виде небольших видеофильмов. Вы можете подготовить видеофильм как файл AVI или как многосекционный файл GIF.

Файл AVI представляет собой многопоточный файл, содержащий видео и звук. Файлы AVI можно создавать при помощи специального видеоадаптера, который способен оцифровывать сигнал с видеокамеры или видеомагнитофона, а также из отдельных изображений, составляющих кадры видеофильма.

Заметим, однако, что озвученный видеофильм в формате AVI продолжительностью в 1 минуту занимает мегабайты дискового пространства. При существующих на сегодняшний день скоростях передачи данных через Internet не имеет никакого смысла размещать на страницах сервера Web файлы такого размера.

Многосекционные файлы GIF не содержат звуковой информации и состоят обычно из одного-двух десятков кадров. Для каждого такого кадра вы можете задавать время отображения и координаты, где этот кадр будет отображаться. Можно также добиться зацикленного отображения видеофильма, созданного как многосекционный файл GIF.

Аплеты Java предоставляют вам еще одну возможность отображения небольших видеофильмов на страницах сервера Web.

Для реализации этой возможности вы должны подготовить и разместить в одном из каталогов сервера Web файлы отдельных кадров видеофильма в формате GIF или JPEG.

Аплет Java должен загрузить эти изображения, дождавшись окончания процесса загрузки, что можно сделать либо при помощи рассмотренного в этой главе класса MediaTracker либо при помощи интерфейса ImageObserver.

Как только все изображения будут полностью загружены, аплет может начинать их поочередное отображение в цикле. Этот цикл должен выполняться в отдельной задаче.

Так как аплет полностью контролирует отображение кадров фильма, он может реализовывать эффекты, недостижимые при использовании файлов AVI или многосекционных файлов GIF. Например, аплет может накладывать или смешивать кадры различных фильмов, рисовать поверх кадров произвольные изображения или делать надписи, масштабировать отдельные фрагменты кадров или весь кадр и так далее. Здесь все ограничивается главным образом вашей фантазией.

Так как мы уже научились выполнять все необходимые для показа видеофильма операции, перейдем сразу к исходным текстам приложения CDRotation.


Аплет CDRotation

В этом разделе мы расскажем об аплете CDRotation, в окне которого вращается компакт-диск.

В левом верхнем углу каждого кадра отображается его порядковый номер (рис. 1). Этот номер не нарисован в файлах кадров, а надписывается приложением после рисования очередного кадра. Такое невозможно, если располагать в документе HTML файл AVI или многосекционный файл GIF.



Рис. 1. Изображение вращающегося компакт-диска в окне аплета CDRotation

Исходные тексты приложения

Главный файл исходных текстов приложения CDRotation представлен в листинге 1.

Листинг 1. Файл CDRotation.java

import java.applet.*;

import java.awt.*;

public class CDRotation extends Applet

implements Runnable

{

Thread m_CDRotation = null;

private Graphics m_Graphics;

private Image m_Images[];

private int m_nCurrImage;

private int m_nImgWidth = 0;

private int m_nImgHeight = 0;

private boolean m_fAllLoaded = false;

private final int NUM_IMAGES = 11;

public String getAppletInfo()

{

return "Name: CDRotation";

}

private void displayImage(Graphics g)

{

if (!m_fAllLoaded)

return;

g.drawImage(m_Images[m_nCurrImage],(size().width - m_nImgWidth) / 2,

(size().height - m_nImgHeight) / 2, null);

g.drawString((new Integer(m_nCurrImage)).toString(), (size().width - m_nImgWidth) /2,

((size().height - m_nImgHeight)/2) + 10);

}

public void paint(Graphics g)

{

Dimension dimAppWndDimension = size();

g.setColor(Color.white);

g.fillRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);

g.setColor(Color.black);

g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);

if (m_fAllLoaded)

{

displayImage(g);

}

else

g.drawString("Please, wait...", 10, dimAppWndDimension.height / 2);

}

public void start()

{

if (m_CDRotation == null)

{

m_CDRotation = new Thread(this);

m_CDRotation.start();

}

}

public void stop()

{

if (m_CDRotation != null)

{

m_CDRotation.stop();

m_CDRotation = null;

}

}

public void run()

{

m_nCurrImage = 0;

if (!m_fAllLoaded)

{

repaint();

m_Graphics = getGraphics();

m_Images = new Image[NUM_IMAGES];

MediaTracker tracker = new MediaTracker(this);

String strImage;

for (int i = 0; i < NUM_IMAGES; i++)

{

strImage = "images/cdimg0" + ((i < 10) ? "0" : "") + i + ".gif";

m_Images[i] = getImage(getDocumentBase(), strImage);

tracker.addImage(m_Images[i], 0);

}

try

{

tracker.waitForAll();

m_fAllLoaded = !tracker.isErrorAny();

}

catch (InterruptedException e)

{

}

if (!m_fAllLoaded)

{

stop();

m_Graphics.drawString("Load error", 10, size().height / 2);

return;

}

m_nImgWidth = m_Images[0].getWidth(this);

m_nImgHeight = m_Images[0].getHeight(this);

}

repaint();

while (true)

{

try

{

displayImage(m_Graphics);

m_nCurrImage++;

if(m_nCurrImage == NUM_IMAGES)

m_nCurrImage = 0;

Thread.sleep(30);

}

catch (InterruptedException e)

{

stop();

}

}

}

}


Описание исходных текстов

Рассмотрим наиболее важные методы нашего аплета.

Метод start

В задачу метода start, который получает управление при отображении окна аплета, входит создание и запуск потока, отображающего кадры видеофильма с изображением вращающегося компакт-диска:

if (m_CDRotation == null)

{

m_CDRotation = new Thread(this);

m_CDRotation.start();

}

Поток создается как объект класса Thread, причем конструктору передается ссылка на главный класс аплета. Поэтому при запуске потока управление получит метод run, определенный в классе аплета.

Метод stop

Метод stop останавливает работу потока, когда окно аплета исчезает с экрана:

if(m_CDRotation != null)

{

m_CDRotation.stop();

m_CDRotation = null;

}

Для остановки вызывается метод stop.

Метод paint

Сразу после получения управления, метод paint закрашивает окно аплета белым цветом и рисует вокруг него черную рамку.

Затем метод проверяет содержимое флага m_fAllLoaded. Этот флаг установлен в значение true, когда все кадры видеофильма загружены и сброшен в значение false, когда загрузка кадров еще не завершена. Последняя ситуация возникает всегда при первом вызове метода paint.

Если все изображения загружены, метод paint вызывает метод displayImage, определенный в нашем приложении:

if(m_fAllLoaded)

{

displayImage(g);

}

Этот метод, о котором мы еще расскажем подробнее, отображает в окне аплета текущий кадр видеофильма.

Если же кадры видеофильма еще не загружены, в окне аплета отображается соответствующее сообщение:

else

g.drawString("Please, wait...", 10, dimAppWndDimension.height / 2);

Метод run

Метод run работает в рамках отдельного потока. Он занимается последовательным рисованием кадров нашего видеофильма.

Прежде всего метод run записывает нулевое значение в поле m_nCurrImage, хранящее номер текущего отображаемого кадра:

m_nCurrImage = 0;

Далее выполняется проверка, загружены ли все кадры видеофильма, для чего анализируется содержимое флага m_fAllLoaded.

Если изображения не загружены (а в самом начале так оно и есть) метод run перерисовывает окно аплета и получает контекст отображения для этого окна. Затем создается массив объектов Image для хранения кадров видеофильма:

m_Images = new Image[NUM_IMAGES];

Метод run создает также объект класса MediaTracker для ожидания загрузки всех кадров видеофильма:

MediaTracker tracker = new MediaTracker(this);

Далее метод run в цикле загружает изображения и добавляет их в объект класса MediaTracker для того чтобы можно было дождаться загрузки всех кадров:

for (int i = 0; i < NUM_IMAGES; i++)

{

strImage = "images/cdimg0" + ((i < 10) ? "0" : "") + i + ".gif";

m_Images[i] = getImage(getDocumentBase(), strImage);

tracker.addImage(m_Images[i], 0);

}

Здесь предполагается, что файлы изображений находятся в каталоге images, который, в свою очередь, размещен там же, где и двоичный файл аплета.

Имена файлов, составляющих отдельные кадры, начинаются с префикса cdimg0, вслед за которым идет номер кадра (00, 01, 02, и так далее), и расширение имени .gif.

Ожидание загрузки кадров выполняется с помощью метода waitForAll, о котором мы вам уже рассказывали:

try

{

tracker.waitForAll();

m_fAllLoaded = !tracker.isErrorAny();

}

catch (InterruptedException e)

{

}

После окончания ожидания флаг завершения загрузки устанавливается только в том случае, если метод isErrorAny вернул значение false, то есть если не было никаких ошибок.

Если же произошла ошибка, в окне аплета отображается соответствующее сообщение, после чего работа метода run (и, следовательно, работа созданного для него потока) заканчивается:

if(!m_fAllLoaded)

{

stop();

m_Graphics.drawString("Load error",10, size().height / 2);

return;

}

В случае удачной загрузки всех кадров метод run получает ширину и высоту первого кадра видеофильма и сохраняет эти значения в переменных m_nImgWidth и m_nImgHeight:

m_nImgWidth = m_Images[0].getWidth(this);

m_nImgHeight = m_Images[0].getHeight(this);

Далее окно аплета перерисовывается:

repaint();

При этом метод paint отображает в окне аплета первый кадр видеофильма.

На следующем этапе работы метода run запускается цикл отображения кадров фильма:

while (true)

{

try

{

displayImage(m_Graphics);

m_nCurrImage++;

if(m_nCurrImage == NUM_IMAGES)

m_nCurrImage = 0;

Thread.sleep(30);

}

catch (InterruptedException e)

{

stop();

}

}

В этом бесконечном цикле вызывается метод displayImage, рисующий текущий кадр видеофильма, после чего номер текущего кадра увеличивается на единицу. Если показаны все кадры, номер текущего кадра становится равным нулю, а затем процесс продолжается.

Между отображением кадров выполняется задержка величиной 30 миллисекунд.

Метод displayImage

Метод displayImage вызывается из двух мест - из метода paint при перерисовке окна аплета и из метода run (периодически).

Если кадры видеофильма не загружены, содержимое флага m_fAllLoaded равно false и метод displayImage просто возвращает управление, ничего не делая:

if(!m_fAllLoaded)

return;

Если же загрузка изображений завершена, этот метод рисует в центре окна текущий кадр видеофильма, вызывая для этого знакомый вам метод drawImage:

g.drawImage(m_Images[m_nCurrImage], (size().width - m_nImgWidth) / 2,

(size().height - m_nImgHeight) / 2, null);

После того как кадр нарисован, мы надписываем на нем его порядковый номер, вызывая для этого метод drawString:

g.drawString((new Integer(

m_nCurrImage)).toString(),

(size().width - m_nImgWidth) / 2,

((size().height - m_nImgHeight) / 2) + 10);


Звук в аплетах Java

Нельзя сказать, что звуковые возможности аплетов Java чрезмерно велики. Скорее наоборот, они минимальны. Тем не менее, аплеты могут проигрывать звуковые клипы, записанные в файлах формата AU, который пришел из мира компьютеров фирмы Sun.

Сказанное, однако, не означает, что если у вас нет рабочей станции Sun, то вы не сможете озвучить свои аплеты. Во-первых, в сети Internet можно найти много готовых звуковых файлов AU, а во-вторых, там же есть программы для преобразования форматов звуковых файлов. Одну из таких условно-бесплатных программ, которая называется GoldWave, вы можете загрузить с сервера ftp.winsite.com.


Загрузка и проигрывание звуковых файлов

Работа со звуковыми файлами во многом напоминает работу с растровыми графическими файлами. Вначале вы должны получить ссылку на интерфейс AudioClip, а затем, пользуясь его методами, вы сможете выполнять проигрывание содержимого этого файла.

Для получения интерфейса AudioClip вы должны воспользоваться одним из двух вариантов метода getAudioClip, определенных в классе Applet:

public AudioClip

getAudioClip(URL url):

public AudioClip

getAudioClip(URL url, String name);

Первый вариант метода предполагает указание адреса URL звукового файла через единственный параметр, второй допускает раздельное указание адреса URL каталога, содержащего файл, и имени файла.

В документации на метод getAudioClip сказано, что этот метод фактически не выполняет загрузку звуковых данных, а только возвращает ссылку на интерфейс AudioClip и немедленно возвращает управление. Загрузка звуковых данных выполняется методами, предназначенными для проигрывания файла.

Однако в книге "The Java Tutorial. Object-Oriented Programming for the Internet", подготовленной специалистами группы JavaSoft, утверждается, что текущие реализации Java работают по другому: метод getAudioClip возвращает управление только после завершения загрузки звукового файла. Очевидно, вам не стоит полагаться на то, что так будет всегда. В тех случаях, когда нежелательно блокирование работы аплета на время загрузки звукового файла, загрузку и проигрывание следует выполнять в отдельном потоке.

Интерфейс AudioClip определен следующим образом:

public interface java.applet.AudioClip

{

public abstract void play();

public abstract void loop();

public abstract void stop();

}

Метод play запускает однократное проигрывание звукового файла, которое выполняется от начала файла и до его конца.

Метод loop запускает проигрывание звукового файла в цикле, которое будет продолжаться до тех пор, пока вы не остановите его, вызвав метод stop.

Метод stop, как нетрудно догадаться из его названия, останавливает проигрывание звукового файла, как однократное, так и выполняемое в цикле.


Аплет PlayClip

Аплет PlayClip демонстрирует использование интерфейса AudioClip. В его окне (рис. 1) имеются три кнопки с названиями Play, Loop и Stop.



Рис. 1. Окно аплета PlayClip

Сразу после запуска аплета кнопка Stop находится в заблокированном состоянии. Если нажать кнопку Play или Loop, начнется, соответственно, однократное проигрывание или проигрывание в цикле файла с именем kaas.au, распложенного в том же каталоге, что и двоичный файл аплета PlayClip.

Когда начинается проигрывание звукового файла, кнопка Stop разблокируется, что позволяет остановить проигрывание.

Исходные тексты приложения

Основной файл исходных текстов приложения приведен в листинге 1.

Листинг 1. Файл PlayClip.java

import java.applet.*;

import java.awt.*;

public class PlayClip extends Applet

{

private String m_ClipName = "kaas.au";

private final String

PARAM_ClipName = "ClipName";

AudioClip auClip;

Button btPlay;

Button btLoop;

Button btStop;

boolean fLoopPlay = false;

public String getAppletInfo()

{

return "Name: PlayClip";

}

public String[][] getParameterInfo()

{

String[][] info =

{

{

PARAM_ClipName, String", Audioclip filename"

},

};

return info;

}

public void init()

{

String param;

param = getParameter(PARAM_ClipName);

if (param != null)

m_ClipName = param;

btPlay = new Button("Play");

btLoop = new Button("Loop");

btStop = new Button("Stop");

btStop.disable();

add(btPlay);

add(btLoop);

add(btStop);

auClip = this.getAudioClip(getCodeBase(), m_ClipName);

}

public boolean action(Event evt, Object obj)

{

Button btn;

if(evt.target instanceof Button)

{

btn = (Button)evt.target;

if(evt.target.equals(btPlay))

{

auClip.play();

btStop.enable();

}

else if(evt.target.equals(btLoop))

{

auClip.loop();

fLoopPlay = true;

btStop.enable();

}

else if(evt.target.equals(btStop))

{

auClip.stop();

fLoopPlay = false;

btStop.disable();

}

else

{

return false;

}

return true;

}

return false;

}

public void paint(Graphics g)

{

Dimension dimAppWndDimension = size();

g.setColor(Color.yellow);

g.fillRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);

g.setColor(Color.black);

g.drawRect(0, 0, dimAppWndDimension.width - 1, dimAppWndDimension.height - 1);

}

public void start()

{

if(fLoopPlay)

auClip.loop();

}

public void stop()

{

if(fLoopPlay)

auClip.stop();

}

}

В листинге 2 вы найдете исходный текст документа HTML, созданного автоматически для нашего приложения системой Java WorkShop.

Листинг 2. Файл PlayClip.tmp.phpl


code="PlayClip" codebase="file:/e:/sun/articles/vol14/src/PlayClip" width="200" height="100"

align="Top" alt="If you had a java-enabled browser, you would see an applet here.">