Назад
Вперед
9.6. Работа с фильтром RGBImageFilter
Пример показывает использование фильтра,
созданного на базе класса RGBImageFilter, для
динамического изменения цветовой палитры
компоненты (кнопки), расположенной в окне аплета.
Когда пользователь размещает курсор мыши над
поверхностью компоненты, расположенный там
цветной рисунок становится черно-белым.
Исходный текст примера
Архив проекта для Java WorkShop 2.0
Демонстрация
(ваш браузер должен уметь работать с аплетами
Java JDK 1.1)
Немного теории
В процессе создания изображения задействованы
два объекта: ImageProducer и ImageConsumer. Первый из них
является интерфейсом для тех объектов, которые
поставляют данные для изображений, а второй -
интерфейсом для получения и преобразования
данных.
Библиотека классов AWT позволяет встроить
фильтр в поток данных, передаваемых из ImageProducer в
ImageConsumer. В результате вы сможете изменять
изображения "на лету", получая интересные
эффекты.
Фильтр может выполнить обработку всех или
отдельных пикселов изображения, изменив их цвет
или прозрачность. И, что немаловажно, создание и
подключение фильтра не является чрезвычайно
сложной задачей.
В этом разделе мы рассмотрим методику создания
и использования фильтра RGBImageFilter, определенного в
import java.awt.image.* на базе интерфейса ImageFilter.
Создание фильтра
В общем виде фильтр выглядит так:
class SomeMyOwnImageFilter extends RGBImageFilter
{
public int filterRGB(int x, int y, int nRGB)
{
int nA = (nRGB >> 255) & 0xff;
int nR = (nRGB >> 16) & 0xff;
int nG = (nRGB >> 8) & 0xff;
int nB = nRGB & 0xff;
. . .
return((nA >> 255) | (nR << 16) |
(nG << 8) | nB);
}
}
В процессе преобразования изображения для
каждого пиксела вызывается метод filterRGB. Через
параметры ему передаются координаты (параметры x
и y), значения цвета и прозрачности (параметр nRGB).
В примере, приведенном выше, мы показали как
можно выделить из параметра nRGB значения
прозрачности и отдельных компонент цвета.
Метод filterRGB должен вернуть новое значение
прозрачности и цвета данного пиксела. Мы
показали, как скомбинировать это значение из
отдельных компонент.
В процессе преобразования вы можете проверять
координаты пикселов или игнорировать их, а также
выполнять произвольные преобразования значений
цветовых компонент и прозрачности, лежащих в
диапазоне от 0 до 255.
Подключение фильтра
Теперь о том как подключить фильтр.
Рассмотрим практическую ситуацию. Пусть,
например, мы создаем фильтр для преобразования
некоего изображения imgSrc в изображение imgResult.
Прежде всего вы должны создать новый фильтр как
объект:
SomeMyOwnImageFilter someFilter =
new SomeMyOwnImageFilter();
Далее нужно создать объект класса ImageProducer:
ImageProducer ip = new FilteredImageSource(
imgSrc.getSource(), someFilter);
Этот объект создается при помощи конструктора
класса FilteredImageSource. В качестве первого параметра
мы передаем конструктору ссылку на источник
данных исходного изображения, полученный
методом getSource, а через второй - ссылку на свой
фильтр.
Затем мы можем создать новое изображение
методом createImage, как это показано ниже:
Image imgResult = createImage(ip);
Чтобы дождаться процесса завершения
формирования изображения imgResult, используйте
класс MediaTracker:
MediaTracker mt = new MediaTracker(this);
mt.addImage(imgResult, 0);
try
{
mt.waitForAll();
}
catch(InterruptedException ie)
{
}
Теперь изображение imgResult готово и доступно для
рисования методом drawImage.
Описание примера
В аплете GrayFilter мы применили фильтр, выполняющий
преобразование цветных изображений кнопок в
черно-белые (рис. 1).
Рис. 1. Кнопки могут становиться черно-белыми
Когда пользователь помещает курсор мыши над
поверхностью кнопки, она становится черно-белой,
а когда убирает - цвет кнопки восстанавливается.
С помощью кнопок вы можете изменять цвет фона
окна аплета, делая его красным, голубым или
желтым.
Рассмотрим наиболее важные фрагменты
исходного текста аплета.
Главный класс аплета GrayFilter
Главный класс нашего аплета создан на базе
класса Applet и не имеет никаких особенностей:
import java.applet.Applet;
import java.awt.*;
import java.awt.image.*;
public class GrayFilter extends Applet
{
. . .
}
В нем мы определили три поля для хранения
ссылок на кнопки класса cButton и два поля для
хранения изображений кнопок в нажатом и отжатом
состоянии:
cButton btnRed;
cButton btnBlue;
cButton btnYellow;
Image imgButtonUp;
Image imgButtonDn;
Класс cButton мы рассмотрим позже.
Метод init класса GrayFilter
В процессе инициализации метод init вначале
устанавливает для окна аплета цвет фона и текста:
setBackground(Color.yellow);
setForeground(Color.black);
Далее он загружает изображения кнопки в
нажатом и отжатом состоянии, дожидаясь
завершения этого процесса при помощи класса
MediaTracker:
imgButtonUp =
getImage(getCodeBase(), "btnup.gif");
imgButtonDn =
getImage(getCodeBase(), "btndn.gif");
MediaTracker mt = new MediaTracker(this);
mt.addImage(imgButtonUp, 0);
mt.addImage(imgButtonDn, 0);
try
{
mt.waitForAll();
}
catch(InterruptedException ie)
{
return;
}
После этого метод init создает три кнопки и
добавляет их в окно аплета:
btnRed = new cButton(this,
imgButtonUp, imgButtonDn, "Red",
80, 20);
btnBlue = new cButton(this,
imgButtonUp, imgButtonDn, "Blue",
80, 20);
btnYellow = new cButton(this,
imgButtonUp, imgButtonDn, "Yellow",
80, 20);
Соответствующим конструкторам передаются
ссылки на изображения кнопки в отжатом и нажатом
состоянии, надпись на кнопке, ширина кнопки и ее
высота.
Кнопки добавляются в окно аплета методом add:
add(btnRed);
add(btnBlue);
add(btnYellow);
Метод doButtonAction класса GrayFilter
Когда пользователь нажимает на кнопки класса
cButton, вызывается метод doButtonAction, определенный в
главном классе аплета. В его задачу входит
идентификация нажатой кнопки и соответствующее
изменение цвета фона окна аплета:
public void doButtonAction(cButton btn)
{
if(btn.equals(btnRed))
setBackground(Color.red);
else if(btn.equals(btnBlue))
setBackground(Color.blue);
else if(btn.equals(btnYellow))
setBackground(Color.yellow);
repaint();
}
Класс cButton
При описании этого класса мы подробно остановимся
на моментах, связанных с использованием фильтра.
Метод paint класса cButton
Когда происходит перерисовывание изображения
поверхности кнопки, метод paint проверяет флаг
bMouseEnter:
if(bButtonUp)
{
g.setColor(clrTitleColor);
if(!bMouseEnter)
drawButton(imgButtonUp, 0);
else
drawButton(imgToGray(imgButtonUp), 0);
}
else
{
g.setColor(Color.blue);
drawButton(imgToGray(imgButtonDn), 1);
}
Флаг bMouseEnter устанавливается в том случае, если
курсор мыши находится над поверхностью кнопки.
Если это так, или если кнопка нажата, то методу
drawButton, примененному нами для рисования
изображения, передается не само изображение, а
результат его обработки с применением фильтра:
drawButton(imgToGray(imgButtonUp), 0);
Обработка выполняется методом imgToGray, который
получает через единственный параметр исходное
изображение, а возвращает обработанное.
Метод mouseEnter класса cButton
Когда курсор мыши попадает в область окна
кнопки, метод mouseEnter устанавливает флаг bMouseEnter и
перерисовывает поверхность кнопки серым цветом:
public boolean mouseEnter(
Event ev, int x, int y)
{
bMouseEnter = true;
drawButton(imgToGray(imgButtonUp), 0);
return true;
}
Для преобразования цвета мы здесь использовали
метод imgToGray.
Метод mouseEnter класса cButton
Если курсор выходит за пределы окна кнопки,
метод mouseEnter сбрасывает флаг bMouseEnter и
перерисовывает кнопку, используя цветное
изображение:
public boolean mouseExit(
Event ev, int x, int y)
{
bMouseEnter = false;
drawButton(imgButtonUp, 0);
return true;
}
Метод drawButton класса cButton
Этот метод получает контекст отображения и
рисует кнопку в окне аплета:
void drawButton(Image imgSrc, int offset)
{
Graphics g = getGraphics();
g.drawImage(imgSrc, 0, 0, this);
drawTitle(g, offset);
}
Затем метод drawButton надписывает кнопку методом
drawTitle. При этом методу передается значение
смещения, необходимого для достижения
визуального эффекта нажатия кнопки.
Метод drawTitle класса cButton
Метод drawTitle вычисляет расположение надписи в
окне кнопки с учетом смещения и рисует эту
надпись:
void drawTitle(Graphics g, int offset)
{
FontMetrics fm = g.getFontMetrics();
int nTitileWidth = fm.stringWidth(btnTitle);
int nTitleHeight = fm.getAscent() -
fm.getLeading() - fm.getDescent();
int x0 = (btnWidth - nTitileWidth) / 2;
int y0 = ((btnHeight - nTitleHeight) / 2) +
nTitleHeight;
g.drawString(btnTitle,
x0 - offset, y0 - offset);
}
Метод imgToGray класса cButton
Метод imgToGray класса cButton выполняет
преобразование цветного изображения, ссылка на
которое передается ему в качестве параметра, в
черно-белое:
Image imgToGray(Image imgSrc)
{
. . .
}
Результат возвращается вызывающему методу как
объект класса Image.
Теперь о том, как выполняется преобразование.
Прежде всего мы создаем фильтр класса GrayImageFilter,
выполняющий преобразование цветных пикселов в
черно-белые:
GrayImageFilter grFilter =
new GrayImageFilter();
Далее метод imgToGray образует объект класса
ImageProducer, указывая соответствующему конструктору
ссылку на источник данных исходного изображения
и фильтр:
ImageProducer ip = new FilteredImageSource(
imgSrc.getSource(), grFilter);
Преобразованное изображение создается методом
createImage:
Image imGrayed = createImage(ip);
Чтобы дождаться завершения преобразования, мы
применяем класс MediaTracker:
MediaTracker mt = new MediaTracker(this);
mt.addImage(imGrayed, 0);
try
{
mt.waitForAll();
}
catch(InterruptedException ie)
{
return null;
}
В случае возникновения исключения InterruptedException
метод imgToGray возвращает значение null. Если же все
закончилось нормально, возвращается ссылка на
новое черно-белое изображение:
return imGrayed;
Класс GrayImageFilter
В классе GrayImageFilter, созданного на базе
интерфейса класса RGBImageFilter, мы определили
единственный метод filterRGB:
class GrayImageFilter extends RGBImageFilter
{
public int filterRGB(int x, int y, int nRGB)
{
int nR = (nRGB >> 16) & 0xff;
int nG = (nRGB >> 8) & 0xff;
int nB = nRGB & 0xff;
int nGray = (int)(0.56 * nG + 0.33 * nR +
0.11 * nB);
return (0xff000000 | nGray << 16 |
nGray << 8 | nGray);
}
}
Этот метод вначале получает исходные цветовые
компоненты пиксела, а затем складывает их с
определенными весовыми коэффициентами для
достижения необходимого результата. При этом
получается серый цвет такой же яркости, что и
исходный цвет.
Алгоритм преобразования цвета в яркость
разработан в NTSC и упомянут в книге Патрика
Нортона и Герберта Шилдта "Полный справочник
по Java", переведенной на русский язык.
Назад Вперед |