Глава 23 (продолжение)
Вверх
Вверх
Вверх
Но это не так. Чтобы изображение стало видимым, их нужно рисовать в окне. Однако класс Image не имеет достаточной инф для того, чтобы создать надлежащий формат данных для экрана. Поэтому класс Component (из пакета java.awt) содержит специальный производственный (factory) метод с именем createImage(), который используется для создания Image-объектов. (Напомним, что все AWT-компоненты являются подклассами Component, поэтому все они поддерживают данный метод).
Метод createImage имеет две формы:
Первая форма возвращает изображение, изготовленное параметром imgProd, который является объектом класса, реализующего интерфейс ImageProducer (производителей изображений мы рассмотрим позже).
Вторая форма возвращает пустое изображение, которое имеет указанную ширину и высоту.
Например:
Здесь создается объект класса Canvas и затем вызывается производственный метод createImage(), чтобы фактически постороить объект типа Image. В этом случае изображение будет пустым. Позже вы увидите как записать в него данные.
Вверх
Первая версия возвращает Image-объект, который инкапсулирует (содержит) изображение, найденное по универсальному адресу, указанному в параметре.
Вторая версия возвращает Image-объект, который инкапсулирует (содержит) изображение, найденное по универсальному адресу, указанному в параметре url, и имеющему имя, указанное в imageName.
Вверх
Он выводит изображение, переданное ему в параметр imgObj, размещая левый верхний угол согласно второму и третьему параметру. Здесь imgOb - ссылка на класс, который реализует интерфейс
ImageObserver (Наблюдатель изображения). Класс ImageObserver описан в следующем разделе.
С помощью getImage и drawImage действительно очень просто загружать и просматривать изображение. Ниже показан пример апплета, который загружает и выводит одиночное изображение.
Вы можете загружайть любой формат файла gif или jpeg, только удостоверьтесь, что он находится в одном каталоге с html-файлом, который содержит апплет.
Код программы:
public class
SimpleImageLoad
extends Applet
{
public void init() { public void paint(Graphics g) {
Результат :
Вверх
Подобный вид уведомления очень полезен, когда изображение загружается по сети, где проектировщик содержимого редко принимает во внимание, что люди часто пробуют загружать апплеты через медленный модем.
Метод imageUpdate() имеет следующую общую форму:
Здесь imgObj - загружаемое изображение, а flags - целое число, которое сообщает состояние отчета обновления.
Четыре целых параметра left, top, width и height представляют прямоугольник, который содержит различные значения в зависимости от передаваемых в flags-значений.
imageUpdate() должен возвратить false, если он завершил загрузку, и true, если еще имеется остаток изображения для обработки.
Параметр flags содержит один или несколько разрядных флажков, определенных как статич переменные внутри интерфейса ImageObserver. Эти флажки и информация, которую они обеспечивают, перечислены в таб 23.1
Таб 23.1 Разрядные флажки параметра flags метода imageUpdate()
Форматы графических файлов
В большинстве случаев вас не будет даже интересовать, какой формат вы используете в своих программах. В Языке Java все различия в кодировании изображений скрыты за ясными и удобными интерфейсами их классов.
Создание, загрузка и просмотр изображений
Создание объекта изображения
Можно было бы ожидать, что для создания изображения в памяти достаточно записать:
Image test = new Image(200,100); // Ошибка - не работает!
Image createImage(ImageProducer imgProd)
Image createImage(int width, int height)
Canvas c = new Canvas();
Image test = c.createImage(200, 100);
Загрузка изображения
Другой способ получить изображение - его загрузка. Для этого используйте метод getImage(), определенный классом Applet.
Image getImage(URL url)
Image getImage(URL url, String imageName)
Просмотр изображения
Имея изображение, вы можете выводить его на экран используя метод drawImage, который является членом класса Graphics. Он содержит несколько форм. Мы будем использовать метод в следующей форме:
boolean drawImage(Image imgObj, int left, int top, ImageObserver imgOb)
listing 1
/*
* <applet code="SimpleImageLoad" width=248 height=146>
* <param name="img" value="seattle.jpg">
* </applet>
*/
import java.awt.*;
import java.applet.*;
Интерфейс ImageObserver
ImageObserver - это интерфейс, используемый для приема уведомлений о том, как генерируются изображения. ImageObserver определяет только один метод - imageUpdate(). Использование ImageObserver
позволяет выполнять (параллельно с загрузкой изображения) другие действия, такие как показ индикатора хода работы (progress-индакатора) или дополнительного экрана, которые информируют вас о ходе загрузки.
boolean imageUpdate(Image imgObj, int flags, int left, int top, int width, int height)
(АВ: в параметр flags функция imageUpdate() возвращает значение по окончанию своей работы. Мы должны проанализировать состояние этого параметра, и в зависимости от его значения направить выполнение программы в ту или другую ветвь).
| Флажок | Значение |
| WIDTH | Параметр правилен и содержит ширину изображения |
| HEIGHT | Параметр правилен и содержит высоту изображения |
| PROPERTIES | Свойства, связанные с изображением могут теперь быть получены через imgObj.getProperty() |
| SOMEBITS | Получена следующая порция пикселов, необходимых для вывода изображения. Параметры left,top,width и height определяют прямоугольник, содержащий новые пикселы. |
| FRAMEBITS | Получен поный фрейм, являющийся частью многофреймового изображения, которое было предварительно нарисовано. Данный фрейм может быть отображен. Параметры left,top,width и height не используются. |
| ALLBITS | Изображение выведено целиком. Параметры left,top,width и height не используются. |
| ERROR | Произошла ошибка с изображением, которое прослеживалось асинхронно. изображение неполно и не может быть отображено. Никакая далнейшая инф не может быть получена. Для удобства будет так же установлен флажок ABORT, чтобы указать, что производство изображения было прервано. |
| ABORT | Изображение, которое прослеживалось асинхронно было прервано прежде, чем оно было закончено. Однако, если ошибка не произошла, доступ к любой части данных изображения перезапустит производство изображения. |
Класс Applet имеет реализацию метода imageUpdate() интерфейса ImageObserver, который используется для перерисовки изображений во время их загрузки. Его можно переопределить в вашем классе, чтобы изменить поведение метода.
Простой пример метода imageUpdate():
Код программы:
listing 2
public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h) {
if ((flags & ALLBITS) == 0) {
System.out.println("Still processing the image.");
return true;
} else {
System.out.println("Done processing the image.");
return false;
}
}
Эта функция используется в следующем примере.
Вверх
Во первых она перерисовывает полное изображение каждый раз, кагда прибывают какие-нибудь новые данные. Это вызывает вспышки между цветом фона и изображением.
Во вторых, он использует свойство Applet.repaint(), заставляющее систему перерисовывать изображение только каждую десятую долю секунды или около этого. Это вызывает мерцание изображения.
Наконец заданная по умолчанию реализация не значит ничего относительно изображений, которые могут быть не в состоянии загружаться должным образом.
Многие начинающие программисты недовольны тем, что метод getImage() преуспевает даже тогда, когда указанное изображение не существует. Вы не узнаете об отсутствии изображения, пока не начнет работу imageUpdate(). Если вы используете умалчиваемую реализацию imageUpdate(), то никогда не узнаете что случилось. Ваш метод paint() просто не будет ничего делать, когда вы вызываете g.drawImage().
Следующий ниже пример фиксирует (исправляет) все эти три проблемы в десяти строках кода.
Во первых он устраняет мерцание с помощью двух небольших изменений.
Он переопределяет метод update() так, чтобы тот вызывал paint() без первоначальной прорисовки цвета фона.
Фон устанавливается через метод setBackground() в init(), так что начальный фон рисуется только однажды.
Он так же использует версию метода repaint() (АВ: с четыремя параметрами), которая указывает прямоугольник для рисования. Система установит область отсечения такой, чтобы ничего вне данного прямоугольника не рисовалось. Это устраняет мерцание перерисовки и улучшает исполнение.
public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h) {
АВ: Здесь выполнение условия
АВ: Условие
АВ: Условие
Во вторых это избавит от негладкого показа входящего изображения путем перерисовки каждый раз, когда repaint() принимает обновление. Эти обновления происходят на базе построчного сканирования так что изображение, которое имеет 100 пикселов в высоту, будет перерисовываться 100 раз за время загрузки. Обратите внимание, что это не самый быстрый способ отображать изображение, а лишь самый сглаженный.
Наконец он обрабатывает ошибку, вызванную тем, что нужный файл не найден, исследуя ABORT-разряд параметра flags. Если он установлен, переменная экземпляра error принимает значение true, и затем вызывает repaint(). Метод paint() модифицируется так, что если переменная error равна true, то сообщение об ошибках печатается на ярко красном фоне.
Код программы:
listing 3
/*
public class
ObservedImageLoad
extends Applet {
public void init() { public void paint(Graphics g) { public void update(Graphics g) { public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h) {
Результат :
Пример с ImageObserver
Теперь рассмотрим пример, который переопределяет imageUpdate() так, чтобы избавить версию апплета SimpleImageLoad от частого мерциния изображения. Умалчиваемая реализация imageUpdate() в классе Applet имеет несколько проблем.
public void update(Graphics g) {
public void init() {
. . .
if ((flags & ABORT) != 0) {
repaint(); // paint whole applet
return (flags & (ALLBITS|ABORT)) == 0;
if ((flags & SOMEBITS) != 0) говорит о том, что
получена следующая порция пикселов, необходимых для вывода изображения. Параметры left,top,width и height определяют прямоугольник, содержащий новые пикселы, и именно этот
прямоугольник только и надо перерисовать. Поэтому вызывается перегруженная repaint()
с четыремя параметрами. Условие if ((flags & SOMEBITS) == 0) означает, что процесс загрузки пикселов продолжается и перерисовывать пока ничего не надо.
if ((flags & ABORT) != 0) говорит о том, что изображение, которое прослеживалось асинхронно было прервано прежде, чем оно было закончено. (Изображение не найдено). В этом случае error=true и вызывается repaint() без параметров (будет выведено целиком окно с предупреждением (сообщением) на красном фоне).
(flags & (ALLBITS|ABORT)) == 0 будет верным (true) в том случае, если загрузка пикселов изображения все еще продолжается (процесс загрузки продолжается). Если процесс загрузки изображения закончен (ALLBITS) или прерван (ABORT), то возвращаемый функцией результат будет false
* <applet code="ObservedImageLoad" width=248 height=146>
* <param name="img" value="seattle.jpg">
* </applet>
*/
import java.awt.*;
import java.applet.*;
boolean error = false;
String imgname;
imgname = getParameter("img");
img = getImage(getDocumentBase(), imgname);
g.setColor(Color.red);
g.fillRect(0, 0, d.width, d.height);
g.setColor(Color.black);
g.drawString("Image not found: " + imgname, 10, d.height/2);
if ((flags & ABORT) != 0) {
repaint(); // paint whole applet
return (flags & (ALLBITS|ABORT)) == 0;
Рис 23.2 Пример вывода апплета. Полузагруженное изображение и сообщение об ошибке.
АВ: Суть апплета в том, что в функции imageUpdate() анализируются параметры, на предмет того, закончилась ли загрузка изображения в память, или может быть изображение (картинка) вообще не найдено. Если даже загрузка изображения еще не закончена, все равно изображение выводится на экран, но в процессе вывода не полностью загруженного изображения рисуется (обновляется) только та часть, которая вновь получена, а не весь рисунок.
Ниже показан интересный вариант imageUpdate(), который вы можете попробовать испытать. Он ждет, пока изображение не будет полностью загружено перед моментальной вставкой его в экран с единственной перерисовкой.
Код программы:
listing 4
public boolean imageUpdate(Image img, int flags,
int x, int y, int w, int h) {
if ((flags & ALLBITS) != 0) {
repaint();
} else
if ((flags & (ABORT|ERROR)) != 0) {
error = true; // file not found
repaint();
}
return (flags & (ALLBITS|ABORT|ERROR)) == 0;
}
Вверх
Преимущество здесь состоит в том, что изображение становится видимым только тогда, когда оно уже окончательно построено. Рисование сложного изображения может занимать нескольок миллисекунд или больше, что может наблюдаться пользователем как мерцание. Это мерцание отвлекает пользователя.
Использование внеэкранного изображения для ослабления мерцания называется двойной буферизацией, из за того, что экран рассматривается как буфер для пикселов, а внеэкранное изображение - это второй буфер, где вы можете готовить пикселы для показа на экране.
Ранее в этой главе вы видели, что можно создать пустой Image-объект Теперь вы увидите как можно рисовать на данном изображении, а не на экране. Для этого необходим объект типа Graphics, чтобы использовать любой из его визуализующих методов. Удобно так же, что Graphics-объект, который вы можете использовать для рисования на изображении, доступен через метод getGrahpics().
Ниже представлен фрагмент кода, который создает новое изображение, получает его графич контекст, и заполняет полное изображение красными пикселами:
Как только вы создали и заполнили внеэкранное изображение, оно еще не будет видимым. Для его окончательного отображения вызовите drawImage().
Чтобы продемонстрировать влияние двойной буферизации на время рисования, ниже приведен пример, рисующий изображение со значительным временем прорисовки:
Код программы:
import java.awt.*;
public class
DoubleBuffer
extends Applet {
public void init() {
public void paint(Graphics g) {
if (!flicker) { g.setColor(Color.blue);
g.setColor(Color.red);
g.setColor(Color.black);
g.setColor(Color.yellow);
if (!flicker) {
Результат :
Рис 23.3 Пример вывода апплета DoubleBuffer без двойной буферизации и с двойной буферизацией.
Предложенный простой апплет имеет усложненный метод paint(). Он заполняет фон синим и затем рисует сверху красный муаровый образец. В верхней его части выводится некоторый черный текст, и затем рисуется желтый круг с центром в (mx,my)-координатах.
public void paint(Graphics g) {
if (!flicker) { g.setColor(Color.blue);
g.setColor(Color.red);
g.setColor(Color.black);
g.setColor(Color.yellow);
if (!flicker) {
Методы mouseMoved() и mouseDragged() переопределены так, чтобы отслеживать позицию мыши. Эти методы идентичны за исключением установок булевой переменной flicker. mouseMoved() устанавливает flicker в true, а mouseDragged() - в false.
Это приводит к вызову repaint() с установкой flicker в true, когда мышь передвигается (но ни какая кнопка не нажата), и установкой ее в false, когда мышь перетаскивается с любой нажатой кнопкой.
Когда вызывается paint() с переменной flicker, установленной в true (функция mouseMoved()), мы видим на экране выполнение каждой операции рисования. (АВ: При этом мы видим мерцание).
g.setColor(Color.blue);
g.setColor(Color.red);
и далее
В случае, когда кнопка мыши нажата, и paint() вызывается с flicker, установленной в false
(функция mouseDragged()), мы видим совершенно иную картину.
Метод paint() обменивает Graphics-ссылку g с графич контекстом, который отсылает к внеэкранному полотну buffer, который мы создали в init(). Тогда все операции рисования становятся невидимыми.
В конце paint(), мы просто вызываем drawImage(), чтобы показать результаты этих методов рисования все сразу. (АВ: При этом естественно нет мерцания. Нажимаем кнопку и движем мышь - Четкая картинка без мерцания).
. . .//операции рисования невидимы
if (!flicker) {
Обратите внимание, что удобно передавать в drawImage() в качестве четвертого параметра null. Этот параметр используется для передачи объекта типа ImageObserver, который принимает уведомление об image-событиях. Так как это Ие не производится из сетевого потока, нам не нужно уведомление.
Левый снимок показывает как выглядит апплет с ненажатыми кнопками мыши. Вы видите, что снимок был сделан, когда Ие было где-то в середине перерисовки.
Правый снимок показывает, что когда кнопка мыши нажимается, изображение всегда имеет законченный четкий вид.
Вверх
Для использования MediaTracker вы создаете его новый экземпляр и применяете его метод addImage(), чтобы прослеживать состояние загрузки изображения. Общий формат addImage():
Здесь imgObj - прослеживаемое изображение. Его идентифик номер передается в imgID Номера не должны быть уникальными. Вы можете использовать тот же номер с несколькими изображениями, как средство их идентификации в качестве части группы.
Во второй форме параметры width и height определяют размеры отображаемого объекта.
Как только вы зарегистрировали изображение, можете проверить загружено ли оно, или можете ждать когда оно полностью загрузится. Для проверки состояния изображения вызывают метод checkID(). Версия, используемая в этой главе имеет формат:
Здесь imgID определяет ID изображения, которое вы хотите проверить. Метод возвращает true, если все изображения, которые имеют указанный идентификатор были загружены (или когда загрузка закончилась с ошибкой или пользователь совершил аварийное завершение работы). Иначе он возвращает false. Чтобы увидеть были ли все отслеживаемые изображения загружены, можно использовать метод checkAll().
Предупреждение:
Если вы используете MediaTracker, когда уже вызвали addImage() для изображения, ссылка на MediaTracker предохранит систему от сборки мусора.
Если вы хотите, чтобы система была способной к сборке мусора прослеживаемых изображений, удостовертесь, что она может собирать так же и экземпляр MediaTracker.
Далее следует пример, который загружает слайд-показ с семью изображениями и отображает прогресс-полоску процесса загрузки:
Код программы:
public class
TrackedImageLoad
extends Applet implements Runnable {
public void init() {
while(st.hasMoreTokens() && tracked <= MAXIMAGES) {
public void paint(Graphics g) {
for(int i=0; i< tracked; i++) { Dimension d = getSize();
if (donecount == tracked) { public void start() { public void stop() { public void run() {
if(stopFlag)
Результат :
Двойная буферизация
Мало того, что изображения (Image) полезны для хранения картинок, но вы так же можете использовать их для рисования поверхностей вне экрана. Это позволит вам передавать любое изображение, включая текст и графику, во внеэкранный буфер, который вы можете отображать в более позднее время.
Canvas c = new Canvas();
Image test = c.createImage(200/100);
Graphics gc = test.getGraphics();
gc.setColor(Color.red);
gc.fillRect(0, 0, 200, 100);
listing 5
/*
* <applet code=DoubleBuffer width=250 height=250>
* </applet>
*/
import java.awt.event.*;
import java.applet.*;
int mx, my;
boolean flicker = true;
Image buffer = null;
int w, h;
w = d.width;
h = d.height;
buffer = createImage(w, h);
my = me.getY();
flicker = false;
repaint();
public void mouseMoved(MouseEvent me) {
my = me.getY();
flicker = true;
repaint();
g =
buffer
.getGraphics();
g.fillRect(0, 0, w, h);
for (int i=0; i< w; i+=gap)
g.drawString("Press mouse button to double buffer", 10, h/2);
g.fillOval(mx - gap, my - gap, gap*2+1, gap*2+1);
public void update(Graphics g) {
g = buffer.getGraphics();
g.fillRect(0, 0, w, h);
for (int i=0; i< w; i+=gap)
g.drawString("Press mouse button to double buffer", 10, h/2);
g.fillOval(mx - gap, my - gap, gap*2+1, gap*2+1);
addMouseMotionListener(new MouseMotionAdapter() {
my = me.getY();
flicker = false;
repaint();
public void mouseMoved(MouseEvent me) {
my = me.getY();
flicker = true;
repaint();
g.fillRect(0, 0, w, h);
for (int i=0; i< w; i+=gap)
. . .
if (!flicker) {
g =
buffer
.getGraphics();
Класс MediaTracker
Многие из ранних разработчиков Java находили интерфейс ImageObserver слишком сложным для понимания и управления загрузкой множественных изображений. Требовалось более простое решение, которое бы позволило программистам загружать все их изображения синхронно, не беспокоясь относительно imageUpdate(). В ответ на это в java.awt был добавлен класс с именем MediaTracker. MediaTracker создает объект, который будет параллельно проверять состояние произвольного числа изображений.
void addImage(Image imgObj, int imgID)
void addImage(Image imgObj, int imgID, int width, int height)
boolean checkID(int imgID)
listing 6
/*
* <applet code="TrackedImageLoad" width=300 height=400>
* <param name="img"
* value="vincent+leonardo+matisse+picasso+renoir+seurat+vermeer">
* </applet>
*/
import java.util.*;
import java.applet.*;
import java.awt.*;
int
tracked;
int frame_rate = 5;
int current_img = 0;
Thread motor;
static final int MAXIMAGES = 10;
Image img[] = new Image[MAXIMAGES];
String name[] = new String[MAXIMAGES];
boolean stopFlag;
StringTokenizer st = new StringTokenizer(getParameter("img"),
"+");
img[tracked] = getImage(getDocumentBase(),
name[tracked] + ".jpg");
tracker.addImage(img[tracked], tracked);
tracked++;
int donecount = 0;
loaded += name[i] + " ";
int w = d.width;
int h = d.height;
Image i = img[current_img++];
int iw = i.getWidth(null);
int ih = i.getHeight(null);
g.drawImage(i, (w - iw)/2, (h - ih)/2, null);
if (current_img >= tracked)
else {
g.setColor(Color.black);
g.fillRect(0, h/3, x, 16);
g.setColor(Color.white);
g.fillRect(x, h/3, w-x, 16);
g.setColor(Color.black);
g.drawString(loaded, 10, h/2);
stopFlag = false;
motor.start();
while (true) {
try {
Рис 23.4 Пример вывода апплета TrackedImageLoad: прогресс полоска и картинка.
Данный апплет на время загрузки картинок показывает прогресс-полоску, а когда все картинки загрузятся, то начинает их показ в цикле чередуя - меняя каждую картинку через одну секунду.
Представленный пример создает новый MediaTracker в методе init(), и затем добавляет каждое из именованных прослеживаемых изображений с помощью метода addImage().
АВ: Напомню в addImage() передается в первом параметре изображение, во втором параметре - его идентифик номер)
public void init() {
while(st.hasMoreTokens() && tracked <= MAXIMAGES) {
MediaTracker tracker;
int
tracked;
. . .
StringTokenizer st = new StringTokenizer(getParameter("img"),
"+");
img[tracked] = getImage(getDocumentBase(),
name[tracked] + ".jpg");
tracker.addImage(img[tracked], tracked);
tracked++;
АВ: Как видим в методе init() используется так же объект st класса StringTokenizer. При его создании ему передается строка, которая содержит все наименования изображений, разделенные символом "+". Далее в цикле при использовании методов класса StringTokenizer - hasMoreTokens() и nextToken() инициируется массив name класса String именами изображений. И этот массив с указанным индексом элемента передается в параметр функции getImage() для того, чтобы инициировать массив изображений img класса Image.
Затем этот массив изображений передается в качестве параметра функции addImage() класса MediaTracker, чтобы в цикле заполнить экземпляр tracker данными обо всех изображениях.
В методе paint() на очередном из прослеживаемых изображений вызывается checkID() на предмет того, загружена ли данная картинка. В параметре checkID() передается идентифик номер картинки. Если картинка под таким идентифик номером загружена, то инкременируется donecount и ее имя добавляется в строку loaded. Так в цикле проверяется загруженность каждой картинки. Если следующая картинка еще не загружена, то цикл обрывается, donecount меньше tracked и выводится прогресс-полоска с полученными данными.
for(int i=0; i< tracked; i++) {
String loaded = "";
int donecount = 0;
loaded += name[i] + " ";
Если все изображения загружены, они отображаются.
if (donecount == tracked) {
Image i = img[current_img++];
int iw = i.getWidth(null);
int ih = i.getHeight(null);
g.drawImage(i, (w - iw)/2, (h - ih)/2, null);
if (current_img >= tracked)
Если нет, показывается простая прогресс-полоска числа загруженных изображений с именами полностью загруженных (отображаемых ниже той полоски). Рис 23.4
else {
g.setColor(Color.black);
g.fillRect(0, h/3, x, 16);
g.setColor(Color.white);
g.fillRect(x, h/3, w-x, 16);
g.setColor(Color.black);
g.drawString(loaded, 10, h/2);
}
АВ: Для того, чтобы прогресс-полоска росла и отображалась в движении, и чтобы при росте полоски отображалось все большее число наименований картинок, для этого сделан такой код в функции paint():
for(int i=0; i< tracked; i++) {
String loaded = "";
int donecount = 0;
loaded += name[i] + " ";
Здесь строка loaded при каждом вызове paint() содержит разное значение. Все большее количество наименований изображений при каждом новом вызове. Переменная tracked проверит число загруженных изображений. Так что цикл пройдется по всему массиву и проверит при помощи checkID() загружено ли каждое изображение в память. Если изображение с индексом i (это идентификационный номер) уже загружено, то имя его добавляется в строку loaded, чтобы далее вывести эти данные в прогресс-полоску. И donecount инкременируется. Пока tracked меньше tracked, будет отображаться прогресс-полоска с новыми и новыми данными. Как только условие if (donecount == tracked) будет выполнено, значит все картинки уже загружены в память, будет выведена первая картинка и апплет начнет работу (чередование картинок).
Еще надо сказать об использовании в этом апплете объекта motor класса Thread (поток).
Сначала его объявление
Thread motor;
Затем его создание в методе апплета start() и запуск потока motor.start():
public void start() {
stopFlag = false;
motor.start();
Затем в методе апплета run() поток работает "на полную катушку":
public void run() {
if(stopFlag)
while (true) {
try {
АВ: Таким образом в апплете показано действие класса MediaTracker, который проверяет состояние загрузки картинок (изображений) в память компа. При выводе картинок в процессе, когда картинки загружены и апплет работает, для вывода их используется только массив img класса Image. А объект класса MediaTracker уже не используется. Он был нужен только на момент загрузки изображений в память. Чтобы контролировать загрузку и четко уловить момент, когда все изображения загружены в память и их можно выводить на экран.
А для вывода полностью загруженных изображений на экран используется массив изображений и поток (Thread).
Вверх
Как вы видели ранее одна из форм метода createImage() имеет объект ImageProducer в качестве своего параметра.
Существуют два производителя изображений (ImageProducer), содержащиеся в java.awt.image: MemoryImageSource и FilterImageSource. Здесь мы рассмотрим MemoryImageSource и создадим новый Image объект из данных, генерируемых внутри апплета.
Вверх
Объект MemoryImageSource создается из массива целых чисел (в формате умалчиваемой цветовой модели RGB), указанного в параметре pixel (он то и содержит данные для воспроизведения Image-объекта). В умалчиваемой цветовой модели пиксел - это целое число формата 0xAARRGGBB, где A - Alpha, R - Red, G -Green, B -Blue.
Значение Alpha представляет степень прозрачности пиксела (0 -полностью прозрачный, 255 - полностью непрозрачный). Ширина и высота результирующего изображения передается в параметрах width и height. Исходную точку для начала чтения данных в массиве пикселов задает параметр offset.
Ширину строки сканирования (которая часто совпадает с шириной изображения) задает параметр scanLineWidth.
Следующий короткий пример генерирует MemoryImageSource-объект, используя разновидность простого алгоритма (поразрядное исключающее ИЛИ (x,y)-координат каждого пиксела):
Код программы:
public class
MemoryImageGenerator
extends Applet {
for(int y=0; y< h; y++) {
Результат :
Рис 23.5 Пример вывода апплета MemoryImageGenerator.
Данные для нового MemoryImageSource создаются в методе init(). Массив целых pixels предназначен для хранения пиксельных значений.
Данные генерируются во вложенных for-циклах, где значения r,g и b организуют сдвиги в пикселах массива pixels.
Наконец вызывается метод createImage() с новым экземпляром MemoryImageSource, созданным из необработанных пиксельных данных, в качестве последнего аргумента.
АВ: То есть здесь создается из инициированного особым образом (при помощи сложенных циклов for) массива пикселей (pixels) картинка (объект Image). Именно на это способен MemoryImageSource - создать из подготовленного массива пикселей картинку (Объект Image).
Далее остается нарисовать Image-объект на экране:
Рис 23.5 показывает изображение после выполнения апплета.
Вверх
Очевидно, что этот интерфейс является противоположностью интерфейса ImageProducer. Объект, который реализует интерфейс ImageConsumer, собирается создавать массивы int или byte, которые представлют пикселы Image-объекта.
Рассмотрим класс PixelGrabber, который является простой реализацией интерфейса ImageConsumer.
Вверх
Для использования PixelGrabber вы сначала организуете int-массив достаточно большой, чтобы содержать данные пикселов, и затем создаете экземпляр PixelGrabber, передавая ему прямоугольную область, которую вы хотите преобразовать в пикселное представление. Наконец, вы вызываете метод grabPixels() этого экземпляра.
Конструктор PixelGrabber, который используется в этой главе, имеет следующую форму:
PixelGrabber(Image imgObj, int left, int top, int width, int height, int pixel[], int offset, int scanLineWidth)
Здесь ingObj-объект, чьи пикселы преобразуются. Значение left и top определяют левый верхний угол прямоугольника, а width и height - его размеры, из которого будут получены пикселы. Пикселы будут сохранены в массиве pixel, со смещением offset. Ширину строки сканирования (которая часто такая же, как ширина изображения) задают в scanLibeWidth.
Метод grabPixels() определяется так:
Оба метода возвращают true, если завершаются успешно, и false - в противном случае. Во второй форме параметр milliseconds определяет, как долго метод будет ожидать пикселы.
Далее показан пример, который захватывает пикселы изображения и затем создает гистограмму их яркости. Под гистограммой яркости здесь понимается определение количества пикселов с определенной яркостью по всем значениям шкалы яркости (от 0 до 255). После того, как апплет рисует изображение, он выводит (поверх этого изображения) гистограмму его яркости.
Код программы:
public class
HistoGrab
extends Applet {
public void init() {
try {
for (int i=0; i< iw*ih; i++) { public void update() {}
public void paint(Graphics g) {
Результат :
Вверх
Вверх
Код программы:
public class TileImage extends Applet {
public void init() {
public void update(Graphics g) {
Результат :
Рис 23.7 Пример вывода апплета TitleImage
Назад |
Начало урока |
Вверх |
Вперед
Интерфейс ImageProducer
ImageProducer - это интерфейс для объектов, которые хотят производить данные для изображений. Объект, реализующий интерфейс ImageProducer поставляет целочисленные или байтовые массивы, которые представляют данные изображения и производят Image-объекты.
Производитель изображений MemoryImageSource
MemoryImageSource - это класс, который создает новый Image-объект из массива данных. Он определяет несколько конструкторов. Тот который мы будем использовать имеет следующую сигнатуру:
MemoryImageSource(int width, int height, int pixel[], int offset, int scanLineWidth)
listing 7
/*
* <applet code="MemoryImageGenerator" width=256 height=256>
* </applet>
*/
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
public void init() {
int w = d.width;
int h = d.height;
int pixels[] = new int[w * h];
int i = 0;
int g = (x*2^y*2)&0xff;
int b = (x*4^y*4)&0xff;
pixels[i++] = (255 << 24) | (r << 16) | (g << 8) | b;
img = createImage(new MemoryImageSource(w, h, pixels, 0, w));
public void paint(Graphics g) {
Dimension d = getSize();
int w = d.width;
int h = d.height;
int pixels[] = new int[w * h];
int i = 0;
for(int y=0; y< h; y++) {
int g = (x*2^y*2)&0xff;
int b = (x*4^y*4)&0xff;
pixels[i++] = (255 << 24) | (r << 16) | (g << 8) | b;
img = createImage(new MemoryImageSource(w, h, pixels, 0, w));
public void paint(Graphics g) {
Интерфейс ImageConsumer
ImageConsumer - это абстрактный интерфейс для объектов, которые хотят получать пиксельные данные изображений (скажем от производителя) и поставлять их (скажем, на экран) уже как другой вид данных.
Класс PixelGrabber
Класс PixelGrabber определен в java.lang.image. Это инверсия класса MemoryImageSource. Вместо построения изображения из массива пиксельных значений, он берет существующее изображение и строит из него массив пикселов.
boolean grabPixels() throw InterruptedException
boolean grabPixels(long milliseconds) throw InterruptedException
listing 8
/*
* <applet code=HistoGrab.class width=341 height=400>
* <param name=img value=vermeer.jpg>
* </applet> */
import java.applet.*;
import java.awt.* ;
import java.awt.image.* ;
Image img;
int iw, ih;
int pixels[];
int w, h;
int hist[] = new int[256];
int max_hist = 0;
w = d.width;
h = d.height;
MediaTracker t = new MediaTracker(this);
t.addImage(img, 0);
t.waitForID(0);
iw = img.getWidth(null);
ih = img.getHeight(null);
pixels = new int[iw * ih];
PixelGrabber pg = new PixelGrabber(img, 0, 0, iw, ih,
pixels, 0, iw);
pg.grabPixels();
int r = 0xff & (p >> 16);
int g = 0xff & (p >> 8);
int b = 0xff & (p);
int y = (int) (.33 * r + .56 * g + .11 * b);
hist[y]++;
for (int i=0; i<256; i++) {
max_hist = hist[i];
int x = (w - 256) / 2;
int lasty = h - h * hist[0] / max_hist;
for (int i=0; i<256; i++, x++) {
g.setColor(new Color(i, i, i));
g.fillRect(x, y, 1, h);
g.setColor(Color.red);
g.drawLine(x-1,lasty,x,y);
lasty = y;
Класс ImageFilter
Фильтр CropImageFilter
listing 9
/*
* <applet code=TileImage.class width=288 height=399>
* <param name=img value=picasso.jpg>
* </applet>
*/
import java.applet.*;
import java.awt.*;
import java.awt.image.*;
MediaTracker t = new MediaTracker(this);
t.addImage(img, 0);
t.waitForID(0);
iw = img.getWidth(null);
ih = img.getHeight(null);
tw = iw / 4;
th = ih / 4;
CropImageFilter f;
FilteredImageSource fis;
t = new MediaTracker(this);
for (int y=0; y<4; y++) {
fis = new FilteredImageSource(img.getSource(), f);
int i = y*4+x;
cell[i] = createImage(fis);
t.addImage(cell[i], i);
t.waitForAll();
for (int i=0; i<32; i++) {
int di = (int)(Math.random() * 16);
Image tmp = cell[si];
cell[si] = cell[di];
cell[di] = tmp;
public void paint(Graphics g) {
Изображение выводится фрагментировано случайным образом.
Содержание