Будь умным!


У вас вопросы?
У нас ответы:) SamZan.net

При створенні компонента тобто обєкта класу Component автоматично формується його графічний контекст grphics

Работа добавлена на сайт samzan.net: 2016-06-20

Поможем написать учебную работу

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

Предоплата всего

от 25%

Подписываем

договор

Выберите тип работы:

Скидка 25% при заказе до 21.5.2024

Урок 9

Графічні примітиви

  •  Методи класу Graphics
  •  Як задати колір
  •  Як нарисувати фігуру
  •  Клас Polygon
  •  Як вивести текст
  •  Як установити шрифт
  •  Як задати шрифт
  •  Клас FontMetrics
  •  Можливості Java 2D
  •  Перетворення координат
  •  Клас AffineTransform
  •  Рисування фігур засобами Java 2D
  •  Клас BasicStroke
  •  Клас GeneralPath
  •  Класи GradientPaint і TexturePaint
  •  Виведення тексту засобами Java 2D
  •  Методи покращення візуалізації
  •  Заключення

Потрібні в цьому уроці нові класи ви можете знайти в папці Java9.

При створенні компонента, тобто обєкта класу Component, автоматично формується його графічний контекст (graphics context). В контексті розміщується область рисування і виведення тексту та зображень. Контекст містить поточний і альтернативний колір рисування і колір фону — обєкти класу Сolor, поточний шрифт для виведення тексту — обєкт класу Font. В контексті визначена система координат, початок якої з координатами (0, 0) розташований у верхньому лівому куті області рисування, вісь Ох направлена вправо, вісь Оу — вниз. Точки координат знаходяться між пікселями.

Керує контекстом класс Graphics або новий клас Graphics2D, введений в Java 2. Оскільки графічний контекст сильно залежить від конкретної графічної платформи, ці класи зроблені абстрактними. Тому не можна безпосередньо створити екземпляри класу Graphics або Graphics2D. Одначе кожна віртуальна машина Java реалізує методи цих класів, створює їх екземпляри для компонента і представляє обєкт класу Graphics методом getGraphics() класу Component або як аргумент методів paint() і update(). Подивимось спочатку, які методи роботи з графікою і текстом представляє нам класс Graphics.

9.1. Методи класу Graphics

При створенні контексту в ньому задається поточний колір для рисування, зазвичай чорний, і колір фону області рисування — білий або сірий. Змінити поточний колір можна методом setСoІor (Color newCoІor), аргумент newСoІor котрого — обєкт класу Color. Узнати поточний колір можна методом getСolor(), повертаючим обєкт класу Сolor.

9.1.1. Як задати колір 

Колір, як і все в Java, — обєкт певного класу, а саме, класу Сolor. Основу класу складають сім  конструкторів кольору. Самий простий конструктор:

Color(int red, int green, int blue) 

створює колір, одержаний змішуванням червоного red, зеленого green і синього blue . Ця кольорова модель називається RGB. Кожна складова змінюється від 0 (відсутність складової) до 255 (повна інтенсивність цієї складової). Наприклад:

Color pureRed = new Color(255, 0, 0);

Color pureGreen = new Color(0, 255, 0);

визначають чистий яскраво-червоний pureRed і чистий яскраво-зелений pureGreen кольори.

В другому конструкторі інтенсивність складової можна змінювати більш гладко дійсними числами від 0.0 (відсутність складової) до 1.0 (повна інтенсивність складової):

Color(float red, float green, float blue)

 

Наприклад:

 

Color someColor = new Color(0.05f, 0.4f, 0.95f);

Третій конструктор

Color(int rgb)

задає всі три складові в одному цілому числі. В бітах 16—23 записується червона складова, в бітах 8—15 — зелена, а в бітах 0—7 — синя складова кольору. Наприклад:

Color с = new Color(0хFF8F48FF);

Тут червона складова задана з інтенсивністю 0x8F, зелена — 0x48, синя — 0xFF.

Наступні три конструктори

Color(int red, int green, int blue, int alpha)

Color(float red, float green, float blue, float alpha)

Color(int rgb, boolean hasAlpha)

вводять четверту складову кольору, так звану "альфу", що визначає прозорість кольору. Ця складова проявляє себе при накладанні одного кольору на другий. Якщо альфа рівна 255 або 1,0, то колір повністю непрозорий, попередній колір не просвічується крізь нього. Якщо альфа рівна 0 або 0,0, то колір абсолютно прозорий, для кожного пікселя видно тільки попередній колір. Останній із цих конструкторів враховує складову альфа, що знаходиться в бітах 24—31, якщо параметр hasAІpha рівний true. Якщо ж hasAІpha рівне false, то складова альфа вважається рівною 255, незалежно від того, що записано в старших бітах параметра rgb. Перші три конструктори створюють непрозорий колір з альфою, рівною 255 або 1,0.

 

Сьомий конструктор

Color(ColorSpace cspace, float[] components, float alpha)

 

дозволяє створювати колір не тільки в кольоровій моделі (color model) RGB, але і в інших моделях: CMYK, HSB, CIEXYZ, визначених обєктом класу ColorSpace. Для створення кольору в моделі HSB можна скористатися статичним методом

getHSBColor(float hue, float saturation, float brightness).

Якщо немає необхідності ретельно підбирати кольори, то можна просто скористатися однією із тринадцати статичних констант типу color класу Сolor. Наперекір стандарту "Code Conventions" вони записуються рядковими літерами: black, blue, cyan, darkGray, gray, green, lightGray, magenta, orange, pink, red, white, yellow.

Методи класу Color дозволяють одержати складові поточного кольору: getRed(), getGreen(), getBlue(), getAlpha(), getRGB(), getColorSpace (), getComponents (). Два методи створюють більш яскравий brighter() і більш темний darker() кольори в порівнянні з поточним кольором. Вони корисні, якщо треба виділити активний компонент або, навпаки, показати неактивний компонент блідніше решти компонентів. Два статичних методи повертають колір, перетворений із кольорової моделі RGB в HSB і навпаки:

 

float[] RGBtoHSB(int red, int green, int blue, float[] hsb)

int HSBtoRGB(int hue, int saturation, int brightness)

Створивши колір, можна рисувати ним в графічному контексті.

9.1.2. Як нарисувати фігуру

Основний метод рисування

drawLine(int x1, int y1, int х2, int y2)

рисує  поточним  кольором  відрізок  прямої  між  точками  з  координатами  (x1, y1) і (х2, у2). Одного цього методу достатньо, щоб нарисувати будь-яку картину по точках, рисуюючи кожну точку з координатами (х, у) методом drawLine (x, у, х, у) і змінюючи кольори від точки до точки. Але ніхто, зрозуміло, не стане цього робити.

Інші графічні примітиви:

  •  drawRect(int x, int у, int width, int height) — рисує прямокутник зі сторонами, параллльними краям екрана, і задається координатами верхнього лівого кута (х, у), шириною width пікселів і висотою height пікселів;
  •  draw3DRect(int x, int у, int width, int height, boolean raised) — рисує прямокутник, що ніби-то виділяється із площини рисування, якщо аргумент raised рівний true, або ніби-то вдавлений в площину, якщо аргумент raised рівний false;
  •  drawOval(int x, int у, int width, int height) — рисує овал, вписаний в прямокутник, заданий аргументами метода. Якщо width == height, то одержимо коло;
  •  drawArc(int x, int у, int width, int height, int startAngle, int arc) — рисує дугу овала, вписаного в прямокутник, заданий першми чотирма аргументами. Дуга має величину arc градусів і відраховується від кута startAngle. Кут відраховується в градусах від вісі Ох. Додатній кут відраховується проти годиникової стрілки, відємний — по годинниковій стрілці;
  •  drawRoundRect (int x, int у, int width, int height, int arcWidth, int arcHeight) — рисує прямокутник із закругленими краями. Закруглення викреслюються четвертинками овалів, вписаних в прямокутники шириною arcWidth і висотою arcHeight, побудовані в кутах основного прямокутника;
  •  drawPolyline(int[] xPoints, int[] yPoints, int nPoints) — рисує ламану з вершинами в точках xPoints[i], yРoints[i]) і числом вершин nPoints; 
  •  drawPolygon(int[] xPoints, int[] yPoints, int nPoints) — рисує замкнуту ламану, проводячи замикаючий відрізок прямої між першою і останньої точками;
  •  drawРoІygon(Polygon p) — рисує замкнуту ламану, вершини якої задані объектом р класу Polygon.

9.2. Клас Polygon

Цей клас призначений для роботи з многокутником, зокрема, з трикутниками і довільними чотирикутниками. Обєкти цього класу можна створити двома конструкторами:

 

  •  Polygon () — створює пустий обєкт;
  •  Polygon(int[] xPoints, int[] yPoints, int nPoints) — задаються вершини многокутника (xPoints[i], yPoints[i]) і їх число nPoints 

Після створення обєкта в нього можна додавати вершини методом

addPoint(int x, int у)

Логічні методи contains() дозволяють перевірити, чи не лежить в многокутнику задана аргументами метода точка, відрізок прямої або цілий прямокутник зі сторонами, паралельними сторонам екрана. Логічні методи intersects() дозволяють перевірити, чи не перетинаються з даним многокутником відрізок прямої, заданий аргументами метода, або прямокутник зі сторонами, паралельними сторонам экрана. Методи getBounds() і getBounds2D() повертають прямокутник, цілком вміщаючий в себе даний многокутник.

Повернемося до методів класу Graphics. Декілька методів викреслюють фігури, залиті поточним кольором: fillRect(), fill3DRect(), filІArco, fiІІОval(), filІPoІiygon(), filІRoundRect(). У них такі ж аргументи, як і у відповідних методів, викреслюючих незаповнені фігури. Наприклад, якщо ви хочете змінити колір фону області рисування, то установіть новий поточний колір і накресліть  ним заповнений прямокутник величиною у всю область:

public void paint(Graphics g){

Color initColor = g.getColor(); // Зберігаємо початковий колір

g.setColor(new Color(0, 0, 255)); // Устанавлюємо колір фону

// Заливаємо область рисування

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

g.setColor(initColor); // Відновлюємо початковий колір

// Подальші дії

} 

Як бачимо, в класі Graphics зібрані тільки самі необхідні засоби рисування. Немає навіть методу, що задає колір фону (хоча можна задати колір фону компонента методом setBackground() класу Сomponent).  Засоби рисування, виведення тексту в область рисування і виведення зображень значно доповнені і розширені в підкласі Graphics2D, що входить в систему Java 2D. Наприклад, в ньому єсть метод задання кольору фону setBackground(Color с). Перед тим як звернутися до класу Graphics2D, розглянемо засоби класу Graphics для виведення тексту.

9.3. Як вивести текст

Для виведення тексту в область рисування поточним ольором і шрифтом, починаючи з точки (х, у), в  класі Graphics єсть декілька методів:

  •  drawString (String s, int x, int y) – виводить рядок s;
  •  drawBytes(byte[] b, int offset, int length, int x, int у) — виводить length елементів масиву байтів b, починаючи з індексу offset;
  •  drawChars(chart] ch, int offset, int length, int x, int у) — виводить length елементів масиву символів ch, починаючи з індексу offset.

Четвертий метод виводить текст, занесений в обєкт класу, реалізуючого інтерфейс AttributedCharacterІterator. Це дозволяє задавати свій шрифт для кожного  символу:

drawString(AttributedCharacterІterator iter, int x, int y)

Точка (х, у) — це ліва нижня точка першої літери тексту на базовій лінії (baseline) виведення шрифта.

9.3.1. Як установити шрифт

Метод setFont(Font newFont) класу Graphics установлює поточний шрифт для виведення тексту. Метод getFont() повертає поточний шрифт. Як і все в мові Java, шрифт — це обєкт класу Font. Подивимося, які можливості представляє цей клас.

9.3.2. Як задати шрифт

Обєкти класу Font зберігають накреслення (glyphs) символів, що утворюють шрифт. Їх можна створити двома конструкторами:

  •  Font (Map attributes) — задає шрифт із заданими аргументом attributes. Ключі атрибутів i деякі їх значенния задаються константами класу TextAttribute із пакету java.awt.font. Цей конструктор характерний для Java 2D і буде розглянутий далі в цьому уроці.
  •  Font (String name, int style, int size) — задає шрифт по імені name, із стилем style і розміром size типографських пунктів. Цей конструктор характерний для JDK 1.1, але широко використовується і в Java 2D в силу своєї простоти.

Типографський пункт в Россії і деяких європейських країнах рівний 0,376 мм, Точніше, 1/72 частини французького дюйма. В англо-американській системі мір пункт рівний 1/72 частини англійського дюйма, 0,351 мм. Останній пункт і використовується в компютерній графіці. Імя шрифту name може бути рядком із  фізичним іменем шрифту, наприклад, "Courier New", або один із рядків "Dialog", "Dialoglnput",' "Monospaced", "Serif", "SansSerif", "Symbol". Це так звані логічні імена шрифтів (logical font names). Якщо name == null, то задається шрифт по замовчувнню. Стиль шрифту style — це одна із констант класу Font:

  •  BOLD — напівжирный;
  •  ITALIC — курсив;
  •  PLAIN — звичайний.

Напівжирний курсив (bolditalic) можна задати операцією побітового додавання, Font. BOLD | Font. ITALIC, як це зроблено в лістинзі 8.3. При виведенні тексту логічним іменам шрифтів і стилям співставляються фізичні імена шрифтів (font face name) або імена сімейств шрифтів (font name). Ці імена реальних шрифтів, наявних у графічній підсистемі операційної системи. Наприклад, логічному іменi "Serif" може бути співставлено імя сімейства (family) шрифтів Times New Roman, а в комбінації зі стилями — конкретні фізичні імена Times New Roman Bold, Times New Roman Italic. Ці шрифти повинні знаходитися в складі шрифтів графічної системи тієї машини, на якій виконується додаток. Список імен доступних шрифтів можна прородивитися наступними операторами:

Font[] fnt = Toolkit.getGraphicsEnvironment.getAHFonts();

for (int i = 0; i< fnt.length; i++)

System.out.println(fnt[i].getFontName());

В склад SUN J2SDK входить сімейство шрифтів Lucida. Установивши SDK, ви можете бути впевнені, що ці шрифти єсть у вашій системі. Таблиці співставлення логічених і фізичних імен шрифтів знаходяться у файлах з іменами

  •  font.properties;
  •  font.properties.ar;
  •  font.properties.ja;
  •  font.properties.ru.

і т. д. Ці файли повинні бути розташовані в JDK в каталозі jdkl.3\jre\lib або в якому-небудь іншому підкаталозі lib кореневого каталога JDK тієї машини, на якій виконується додаток. Потрібний файл вибирається віртуальною машиною Java по закінченні імені файла. Це закінчення співпадає з міжнародним кодом мови, установленого в локалі або в системній властивості user.language (див. рис. 6.2). Якщо у вас установлена російська локаль з міжнародним кодом мови "ru", то для співставлення буде вибраний файл font.properties.ru. Якщо такий файл не знайдено, то використовується файл font.properties, що не відповідає ніякій конкретній локалі. Тому можна залишити в системі тільки один файл font.properties, переписавши в нього зміст потрібного файла або створивши файл заново. Для будь-якої локалі буде використовуватися цей файл. В лістинзі 9.1 показано скорочений зміст файла font.properties.ru із JDK 1.3 для платформи MS Windows.

Лістинг 9.1. Примірний файл font.properties.ru :

# %W% %E%

# Це просто коментарі

# AWT Font default Properties for Russian Windows

#

# Три співставлення логічному імені "Dialog":

dialog.0=Arial,RUSSIAN_CHARSET

dialog.l=WingDings,SYMBOL_CHARSET,NEED_CONVERTED

dialog.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

# По три співставлення стилям ITALIC, BOLD, ITALIC+BOLD:

dialog.italic.0=Arial Italic,RUSSIAN_CHARSET

dialog.italic.l=WingDings,SYMBOL_CHARSET,NEED_CONVERTED

dialog.italic.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

dialog.bold.0=Arial Bold,RUSSIAN_CHARSET

dialog.bold.l=WingDings,SYMBOL_CHARSET,NEED_CONVERTED

dialog.bold.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

dialog.bolditalic.0=Arial Bold Italic,RUSSIAN_CHARSET

dialog.bolditalic.l=WingDings,SYMBOL_CHARSET,NEED_CONVERTED

dialog.bolditalic.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

# По три співставлення імені "Dialoglnput" і стилям:

dialoginput.0=Courier New,RUSSIAN_CHARSET

dialoginput.l=WingDings,SYMBOL_CHARSET,NEED_CONVERTED

dialoginput.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

dialoginput.italic.0=Courier New Italic,RUSSIAN_CHARSET

# і так далі

#

# По три співставлення імені "Serif" і стилям: 

serif.0=Times New Roman,RUSSIAN_CHARSET 

serif.l=WingDings,SYMBOL_CHARSET,NEED_CONVERTED 

serif.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

serif.italic.0=Times New Roman Italic,RUSSIAN_CHARSET

# і так далі

# Інші логічні імена

sansserif. CMArial,RUSSIAN_CHARSET

sansserif.l=WingDings,SVMBOL_CHARSET,NEED_CONVERTED

sansserif.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

sansserif.italic. 0=Arial Italic,ROSSIAN_CHARSET

# і так далі

#

monospaced.0=Courier New,RUSSIAN_CHARSET

monospaced.l=WingDings,SYMBOL_CHARSET,NEED_CONVERTED

monospaced.2=Symbol,SYMBOL_CHARSET,NEED_CONVERTED

monospaced.italic.0=Courier New Italic,RUSSIAN_CHARSET

# і так далі

# Default font definition

#

default.char=2751

# for backword compatibility

# Старі логічні імена версії JDK 1.0

timesroman.0=Times New Roman,RUSSIAN_CHARSET

helvetica.0=Arial,RUSSIAN_CHARSET

courier.0=Courier New,RUSSIAN_CHARSET

zapfdingbats.0=WingDings,SYMBOL_CHARSET

# font filenames for reduced initialization time

# Файли зі шрифтами

filename.Arial=ARIAL.TTF

filename.Arial_Bold=ARIALBD.TTF

filename.Arial_Italic=ARIALI.TTF

filename.Arial_Bold_Italic=ARIALBI.TTF

filename.Courier_New=COUR.TTF

filename.Courier_New_Bold=COURBD.TTF

filename.Courier_New_Italic=COURI.TTF

filename.Courier_New_Bold_Italic=COURBI.TTF

filename.Times_New_Roman=TIMES.TTF

filename.Times_New_Roman_Bold=TlMESBD.TTF

filename.Times_New_Roman_Italic=TIMES3.TTF

filename.Times_New_Roman_Bold Italic=TIMESBI.TTF

filename.WingDings=WINGDING.TTF

filename.Symbol=SYMBOl.TTF

# name aliases

# Псевдоіими логічних імен закоментовані

# alias.timesroman=serif

# alias.helvetica=sansserif

# alias.courier=monospaced

# Static FontCharset info

#

# Класи перетворення символів у байти

fontcharset.dialog.0=sun.io.CharToByteCP1251

fontcharset.dialog.l=sun.awt.windows.CharToByteWingDings

fontcharset.dialog.2=sun.awt.CharToByteSymbol

fontcharset.dialoginput.0=sun.io.CharToByteCP1251

fontcharset.dialoginput.l=sun.awt.windows.CharToByteWingDings

fontcharset.dialoginput.2=sun.awt.CharToByteSymbol

fontcharset.serif.0=sun.io.CharToByteCP1251

fontcharset.serif.l=sun.awt.windows.CharToByteWingDings

fontcharset.serif.2=sun.awt.CharToByteSymbol

fontcharset.sansserif.0=sun.io.CharToByteCP1251

fontcharset.sansserif.l=sun.awt.windows.CharToByteWingDings

fontcharset.sansserif.2=sun.awt.CharToByteSymbol

fontcharset.monospaced.0=sun.io.CharToByteCP1251

fontcharset.monospaced.l=sun.awt.windows.CharToByteWingDings

fontcharset.monospaced.2=sun.awt.CharToByteSymbol

# Exclusion Range info

#

# He проглядати вцьому шрифті вказані діапазони

exclusion.dialog.0=0100-0400,0460-ffff

exclusion.dialoginput.0=0100-0400, 0460-ffff

exclusion.serif.0=0100-0400,04 60-ffff

exclusion.sansserif.0=0100-0400, 0460-ffff

exclusion.monospaced.0=0100-0400,0460-ffff

# charset for text input

#

# Введені байтові символи кодуються в кириличний діапазон  кодуванняи Unicode

inputtextcharset=RUSSIAN_CHARSET

Більша частина цього файла зайнята співставленнями логічних і фізичних імен. Ви бачите, що під номером 0:

  •  логічномлу імені "Dialog" співставлено імя сімейства Arial;
  •  логічному імені "Dialoginput" співставлено імя сімейства Courier New; 
  •  логічному імені "Serif" співставлено імя сімейства Times New Roman; 
  •  логічному імені "Sansserif" співставлено імя сімейства Arial; 
  •  логічному імені "Monospaced" співставлено імя сімейства Courier New.

Там, де указаний стиль: dialog.italic, dialog.bold і т.д., підставлений відповідний фізичний шрифт. В рядках лістинга 9.1, що починаються зі слова filename, указані файли з відповідними фізичними шрифтами, наприклад:

filename.Arial=ARIAL.TTF

Ці рядки  необобовязкові, але вони прискорюють пошук файлів зі шрифтами. Теперь подивимося на останні рядки лістинга 9.1. Рядок

 

exclusion.dialog.0=0100-0400, 0460-ffff

означає, що в шрифті, співставленому логічному імені "Dialog" під номером 0, а саме, Arial, не стануть відшукуватися накреслення (glyphs) символів з кодами в діапазонах '\u0100' —'\u0400' і '\u0460' —'\uFFFF'. Вони будуть взяті із шрифту, співставленого цьому імени під номером 1, а саме, WingDings. Те ж саме буде відбуватися, якщо потрібні накреслення не знайдені в шрифті, співставленому логічному імені під номером 0. Не всі файли зі шрифтами Unicode містять накреслення (glyphs) всіх символів. Якщо потрібні накреслення не знайдені і в співставленні 1 (в даному прикладі в шрифті WingDings), вони будуть відшукуватися у співставленні 2 (тобто в шрифті Symbol) і т. д. Подібних співставлень можна написати скільки завгодно. Таким чином, кожному логічому імені шрифта можна співставити різні діапазони різних реальних шрифтів, а також застрахуватися від відсутності накреслень деяких символів в шрифтах Unicode.

Всі співставлення під номерами 0, 1, 2, 3, 4 належить повторити для всіх стилів: bold, italic, bolditalic. Якщо  в графічній системі використовуються шрифти Unicode, як, наприклад, в MS Windows NT/2000, то більше ні про що не треба турбуватися. Якщо ж графічна система використовує байтові ASCII-шрифти як, наприклад, MS Windows 95/98/ME, то треба потурбуватися про їх правильне перекодування в Unicode і навпаки. Для цього на платформі MS Windows використовуються константи Win32 API RUSSIAN_CHARSET, SYMBOL_CHARSET, ANSI_CHARSET, OEM_CHARSET і ін., показуючі, яку кодову таблицю використовувати при перекодуванні, так же, як це відмічалося в уроці 5 при створенні рядка із масиву байтів. Якщо логічним іменам співставлені байтові ASCII-шрифти (в прикладі це шрифти WingDings і Symbol), то необхідність перекодування задається константою NEED_CONVERTED. Перекодуванням займаються методи спеціальних класів charToByteCP1251, TiarToByteWingDings, CharToByteSymbol. Вони указуються для кожного співставлення імен в рядках, що починаються зі слова fontcharset. Ці рядки обовязкові для всіх шрифтів, помічених константою NEED_CONVERTED. В останньому рядку файла указана кодова сторінка для перекодування в Unicode символів, що вводяться в поля введення:

inputtextcharset = RUSSIAN_CHARSET

Цей запис задає кодову таблицю СР1251. Отже, збираючись виводити рядок str в графічний контекст методом drawstring(), ми створюємо поточний шрифт конструктором класуа Font, указуючи в ньому логічне імя шрифту, наприклад, "Serif". Виконуюча система Java відшукує у файле font.properties, відповідному локальній мові, співставлений цьому логічному імені фізичний шрифт операційної системи, наприклад, Times New Roman. Якщо це Unicode-шрифт, то із нього добуваються накреслення символів рядка str по їх кодуванню Unicode і відображаються в графічний контекст. Якщо це байтовий ASCII-шрифт, то рядок str попередньо перекодовується в масив байтів методами класу, указаного в одному із рядків fontcharset, наприклад, CharToByteCP1251. Хороші приклади файлів font.properties.ru зібрані на сторінці Сергія Астахова, указаній в уроці 0. Обговорення цих питань і прикладів файлів font.properties для X Window System дані в документації SUN J2SDK в файлі docs/guide/intl /fontprop.html. 

Завершуючи обговорення логічних і фізичних імен шрифтів, належить сказати, що в JDK 1.0 використовувались логічні імена "Helvetica", "TimesRoman", "Courier", замінені в JDK 1.1.З "SansSerif", "Serif", "Monospaced", відповідно, із ліцензійних міркувань. Старі імена залишились у файлах font.properties для сумісності.

При виведенні рядка у вікно додатку дуже часто виникає необхідність розташувати її певним способом відносно інших елементів зображення: центрувати, вивести над або під іншим графічним обєктом. Для цього треба знати метрику рядка: ії висоту і ширину. Для вимірювання розмірів окремих символів і рядка в цілому розроблений клас FontMetrics. В Java 2D клас FontMetrics замінений класом TextLayout. Його ми розглянемо в кінці цього уроку , а зараз вияснимо, яку користь можна отримати із методів класу FontMetrics.

9.4. Класс FontMetrics

 

Клас FontMetrics являється абстрактним, тому не можна скористатися його конструктором. Для одержання обєкта класу FontMetrics, що містить набір метричних характеристик шрифту f, треба звернутися до методу getFontMetrics (f) класу Graphics або класу Component. Клас FontMetrics дозволяє узнати ширину окремого символу ch в пікселях методом charwidth(ch), загальну ширину всіх символів масиву або під-масиву символів або байтів методами getСhars() і getBytes(), ширину цілого рядка str в пікселях методом stringwidth(str).

Декілька методів повертають в пікселях вертикальні розміри шрифту. Інтерліньяж (leading) — відстань між нижньою точкою звисаючих елементів такихлітер, как р, у і верхньою точкою виступаючих елементів таких літер, як б і в наступного — повертає метод getLeading(). Середня відстань від базової лінії шрифту до верхньої точки прописных літер і виступаючих елементів того ж рядка (ascent) повертаєт метод getAscent(), а максимальне — метод getMaxAscent(). Середню відстань звисаючих елементів від базової лінії того ж рядка (descent) повертає метод getDescent(), а максимальну — метод getMaxDescent(). Нарешті, висоту шрифту (height) — суму ascent + descent + leading — повертає метод getHeight(). Висота шрифту рівна відстані між базовими лініями сусідніх рядків. Ці елементи показані на рис. 9.1.

 

Рис. 9.1. Елементи шрифту

Додаткові характеристики шрифту можно визначити методами класу LineMetrics із пакету java.awt.font. Обєкт цього класу можна одержати кількома методами getLineMetrics() класу FontMetrics. Лістинг 9.2 показує застосування графічних примітивів і шрифтів, а рис. 9.2 — результат виконання програми із цього лістингу.

Лістинг 9.2. Використання графічиих примітивів і шрифтів

import java.awt.*;

import java.awt.event.*;

class GraphTest extends Frame{

GraphTest(String s){  

super(s);

}

public void paint(Graphics g){

Dimension d = getSize();

int dx = d.width / 20, dy = d.height / 20;

g.drawRect(dx, dy + 20, d.width - 2*dx, d.height - 2*dy - 20);

g.drawRoundRect(2 * dx, 2*dy + 20, d.width - 4*dx, d.height - 4*dy - 20, dx, dy);

g.fillArc(d.width / 2 - dx, d.height - 2*dy + 1, 2*dx, dy - 1, 0, 360);

g.drawArc(d.width / 2 - 3*dx, d.height - 3*dy / 2 - 5, dx, dy / 2, 0, 360);

g.drawArc(d.width / 2 + 2*dx, d.height - 3*dy / 2 - 5, dx, dy / 2, 0, 360);

Font f1 = new Font("Serif", Font.BOLD|Font.ITALIC, 2 * dy);

Font f2 = new Font ("Serif", Font.BOLD, 5 * dy / 2);

FontMetrics fm1 = getFontMetrics(f1);

FontMetrics fm2 = getFontMetrics(f2);

String s1 = "Всяка останя помилка";

String s2 = "являється передостанньою.";

String s3 = "Закон налаштування програми";

int firstLine = d.height / 3;

int nextLine = fm1.getHeight();

int secondLine = firstLine + nextLine / 2;

g.setFont(f2);

g.drawString(s3, (d.width-fm2.stringWidth(s3)) / 2, firstLine);

g.drawLine(d.width / 4, secondLine - 2,3 * d.width / 4, secondLine - 2);

g.drawLine(d.width / 4, secondLine - 1, 3 * d.width / 4, secondLine - 1);

g.drawLine(d.width / 4, secondLine, 3 * d.width / 4, secondLine);

g.setFont(f1);

g.drawString(s1, (d.width - fm1.stringWidth(s1)) /2,  firstLine + 2 * nextLine);

g.drawString(s2, (d.width - fm1.stringWidth(s2)) / 2,  firstLine + 3 * nextLine);

}

public static void main(String[] args){

GraphTest f = new GraphTest(" Приклад рисування");

f.setBounds(0, 0, 500, 300);

f.setVisible(true);

f.addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent ev){

System.exit(0);

}

});

}

}

В лістинзі 9.2 використаний простий клас Dimension, головне призначення якого — зберігати ширину і висоту прямокутного обєкта в своїх полях width і height. Метод getsize() класу Сomponent повертає розміри компонента у вигляді обєкта класу Dimension. В лістинзі 9.2 розміри компонента f типу GrapMest установлені в конструкторі методом setBounds() рівнbми 500x300 пікселів. Ще одна особливість лістингу 9.2 — для викреслювання товстої лінії, відділяючої заголовок від тексту, прийшлось провести три паралельні прямі на відстані один піксель одна від одної.

 

Рис. 9.2. Приклад використання класу Graphics

Як бачимо із огляду класу Graphics і супутніх йому класів, засоби рисування и виведення тексту в цьому класі досить обмежені. Лінії можна проводити тільки суцільні і тільки товщиною в один піксель, текст виводиться тільки горизонтально і зліва направо, не враховуються особливості пристроїв виведення, наприклад, розрішимість екрана. Ці обмеження можна обійти різними хитрощами: креслити декілька паралельних ліній, притиснутих одна до одної, як в лістинзі 9.2, або вузький заповнений прямокутник, виводити текст по одній літері, одердати розрішимість екрана методом getScreenSize() класу Java.awt.Toolkit і використовувати його надалі. Але все це утруднює програмування, лишає його стрункості і природності, порушує принцип KISS. В Java 2 клас Graphics, в рамках системи Java 2D, значно розширений класом Graphics2D.

9.5. Можливості Java 2D 

В систему пакетів і класів Java 2D, основа якої— клас Graphics2D пакета java.awt, внесено декілька принципово нових положень.

  •  Крім координатної системи, прийнятої в класі Graphics і названої координатним простором користувача (User Space), введена ще система координат пристрою виведення (Device Space): екрана монітора, принтера. Методи класу Graphics2D автоматично переводять (transform) систему координат користувача в систему координат пристрою при виведенні графіки.
  •  Перетворення координат користувача в координати пристроюа можна задати "вручну", причому перетворенням може служити будь-яке афіне перетворення площини, зокрема, поворот на довільний кут і/або стиснення/розтягнення. Воно визначається як обєкт класу AffineTransform. Його можна установити як перетворення по замовчуванню методом setTransform(). Можливо виконати перетворення "на льоту" методами transform() і translate() і робити композицію перетворень методом concatenate().
  •  Оскільки аффінне перетворення дійсне, координаты задаються дійсними, а не цілими числами.
  •  Графічні примітивb: прямокутник, овал, дуга і ін., реалізують тепер новий інтерфейс shape пакету java.awt. Для їх викреслювання можна використовувати новий єдиний для всіх фігур метод draw(), аргументом кякого може бути будь-який обєкт, реалізувавший інтерфейс shape. Введено метод fill(), заповнюючий фігури— обєкти класу, реалізувавшого інтерфейс shape.
  •  Для викреслювання (stroke) ліній введено поняття пера (реn). Властивості пера описує інтерфейс stroke. Клас Basicstroke реалізує цей інтерфейс. Перо володіє чотирма характеристиками:
  •  воно має толщину (width) в один (по замовчуванню) або декілька пікселів;
  •  воно може закінчити лінію (end cap) закругленням — статична константа CAP_ROUND, прямим обрізом — CAP_SQUARE (по замовчуванню), або не фиксувати певний спосіб закінчення — CAP_BUTT;
  •  воно може спрягати лінії (line joins) закругленням — статична константа JOIN_ROUND, відрізком прямої — JOIN_BEVEL, або просто зістикувати — JOIN_MITER (по замовчуванню);
  •  воно може креслити лінію різними пунктирами (dash) і штрих-пунктирами, довжини штрихів і проміжків задаються в масиві, елементи масиву з парними індексами задають довжину штриха, з непарними індексами — довжину проміжку між штрихами.
  •  Методи заповнення фігур описані в інтерфейсі Paint. Три класи реалізують цей інтерфейс. Клас color реалізує його суцільною (solid) заливкою, класс GradientPaint — градієнтним (gradient) заповненням, при якому колір плавно змінюєтья від однієї заданої точки до другої заданої точки, класс Texturepaint — заповненням по попередньо заданному зразку (pattern fill).
  •  Літери тексту розуміються як фігуры, тобто обєкти, реалізуючі інтерфейс shape, і можуть викреслюватися методом draw() з використанням всіх можливостей цього методу. При їх викреслюванні використовується  перо, всі методи заповнення і перетворення.
  •  Крім імені, стилю і розміру, шрифт отримав багато додаткових атрибутів, наприклад, перетворення координат, підкреслювання або закреслювання тексту, виведення тексту справа наліво. Колір тексту і його фону являються тепер атрибутами самого тексту, а не графічного контексту. Можна задати різну ширину символів шрифту, надрядкові і підрядкові індекси. Атрибути установлюються константами класу TextAttribute.
  •  Процес візуалізації (rendering) регулюється правилами (hints), визначеними константами класу RenderingHints.

З такими можливостями Java 2D стала повноцінною системою рисування, виведення тексту і зображень. Подивимося, як реалізовані ці можливості, і як ними можна скористатися.

9.6. Перетворення координат

Правило перетворення координат користувача в координати графічного пристрою (transform) задається автоматично при створенні графічного контексту так же, як колір і шрифт. Надалі його можна змінити методом setTransform() так же, як змінюються колір або шрифт. Аргументом цього методу служить обєкт класу AffineTransform із пакету java.awt.geom, подібно обєктам класу Сolor або Font при заданні кольору або шрифту. Ррозглянемо детальніше клас AffineTransform.

Афінне перетворення координат задається двома основними конструкторами класу AffineTransform:

AffineTransform(double a, double b, double с,  double d, double e, double f)

AffineTransform (float a, float b, float c, float d, float e, float f)

При цьому точка з координатами (х, у) в просторі користувача перейде в точку з координатами (а*х+с*у+е, b*x+d*y+f) в просторі графічного пристрою. Таке перетворення не викривляє площину — прямі лінії переходять в прямі, кути мж лініями зберігаються. Прикладами афінних перетворень служать повороти навколо будь-якої точки на будь-який кут, паралельні зcуви, відображення від осей, стиснення і розтягнення по вісям.

Наступні два конструктори використовують в якості аргумента масив {а, b, с, d, e, f} або {а, b, с, d}, якщо

e = f = 0, складений із таких же коефіцієнтів в тому ж порядку:

AffineTransform(double[] arr)

AffineTransform(float[] arr)

Пятий конструктор створює новий обєкт по іншому, уже наявному, обєкту:

AffineTransform(AffineTransform at)

Шостий конструктор — конструктор по замовчуванню — створює тотожне перетворення:

AffineTransform ()

Ці конструктори математично точні, але незручні при заданні конкретних перетворень. Попробуйте розрахувати коефіцієнти повороту на 57° навколо точки з координатами (20, 40) або догадатися, як буде перетворено простір користувача після виконання методів:

AffineTransform at = new AffineTransform(-1.5, 4.45, -0.56, 34.7, 2.68, 0.01);

g.setTransform(at);

В багатьох випадках зручніше створювати перетворення статичними методами, повертаючими обєкт класу AffineTransform.

getRotatelnstance (double angle) — повертає поворот на кут angle, заданий в радіанах, навколо початку координат. Додатній напрям повороту такий, що точки вісі Ох повертаються в напряму до вісі Оу. Якщо вісі координат користувача не змінювалися перетворенням відображення, то додатнє значення angle задає поворот по годинниковій стрілці.

  •  getRotatelnstance(double angle, double x, double у) — такий же поворот навколо точки з координатами (х, у).
  •  getScalelnstance (double sx, double sy) — змінює маштаб по вісі Ох в sx раз, по вісі Оу — в sy раз.
  •  getSharelnstance (double shx, double shy) — перетворює кожну точку (x, у) в точку (x + shx*y, y + shy*x ).
  •  getTranslatelnstance (double tx, double ty) — здвигає кожну точку (х, у) в точку (x + tx, y + ty).

Метод createІnverse() повертає перетворення, обернене поточному перетворенню. Після створення перетворення його можна змінювати методами:

  •  setTransform(AffineTransform at)
  •  setTransform(double a, double b, double c, double d, double e, double f)
  •  setToIdentity()
  •  setToRotation(double angle)
  •  setToRotation(double angle, double x, double y)
  •  setToScale(double sx, double sy)
  •  setToShare(double shx, double shy)
  •  setToTranslate(double tx, double ty)

зробивши поточним перетворення, задане одним із цих методів. Перетворення, задані методами:

  •  concatenate(AffineTransform at)
  •  rotate(double angle)
  •  rotate(double angle, double x, double y)
  •  scale(double sx, double sy)
  •  shear(double shx, double shy)
  •  translate(double tx, double ty)

виконується перед поточним перетворенням, створюючи композицію перетворень.

Перетворення, задане методом preconcatenate{AffineTransform at), навпаки, здійснюються післе поточного перетворення. Інші методи класу AffineTransform виконують перетворення різних фігур в просторі користувача. Пора привести приклад. Додамо в початок методу paint() в лістинзі 9.2 чотири оператори, як записано в лістинзі 9.3.

Лiстинг 9.3. Перетворення простору користувача

import java.awt.*;

import java.awt.event.*;

import java.awt.geom.*;

class GraphTest extends Frame{

GraphTest(String s){  

super(s);

}

public void paint(Graphics gr){

Graphics2D g = (Graphics2D)gr;

AffineTransform at = AffineTransform.getRotateInstance(-Math.PI/4.0, 250.0,150.0);

at.concatenate(new AffineTransform(0.5, 0.0, 0.0, 0.5, 100.0, 60.0));

g.setTransform(at);

Dimension d = getSize();

int dx = d.width / 20, dy = d.height / 20;

g.drawRect(dx, dy + 20, d.width - 2*dx, d.height - 2*dy - 20);

g.drawRoundRect(2 * dx, 2*dy + 20, d.width - 4*dx, d.height - 4*dy - 20, dx, dy);

g.fillArc(d.width / 2 - dx, d.height - 2*dy + 1, 2*dx, dy - 1, 0, 360);

g.drawArc(d.width / 2 - 3*dx, d.height - 3*dy / 2 - 5, dx, dy / 2, 0, 360);

g.drawArc(d.width / 2 + 2*dx, d.height - 3*dy / 2 - 5, dx, dy / 2, 0, 360);

Font f1 = new Font("Serif", Font.BOLD|Font.ITALIC, 2 * dy);

Font f2 = new Font ("Serif", Font.BOLD, 5 * dy / 2);

FontMetrics fm1 = getFontMetrics(f1);

FontMetrics fm2 = getFontMetrics(f2);

String s1 = "Всяка останя помилка";

String s2 = "являється передостанньою.";

String s3 = "Закон налаштування програми";

int firstLine = d.height / 3;

int nextLine = fm1.getHeight();

int secondLine = firstLine + nextLine / 2;

g.setFont(f2);

g.drawString(s3, (d.width-fm2.stringWidth(s3)) / 2, firstLine);

g.drawLine(d.width / 4, secondLine - 2,3 * d.width / 4, secondLine - 2);

g.drawLine(d.width / 4, secondLine - 1, 3 * d.width / 4, secondLine - 1);

g.drawLine(d.width / 4, secondLine, 3 * d.width / 4, secondLine);

g.setFont(f1);

g.drawString(s1, (d.width - fm1.stringWidth(s1)) /2,  firstLine + 2 * nextLine);

g.drawString(s2, (d.width - fm1.stringWidth(s2)) / 2,  firstLine + 3 * nextLine);

}

public static void main(String[] args){

GraphTest f = new GraphTest(" Приклад рисування");

f.setBounds(0, 0, 500, 300);

f.setVisible(true);

f.addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent ev){

System.exit(0);

}

});

}

}

Метод paint () починається з отримання екземпляру g класу Graphics2D простим приведенням аргументу gr до типу Graphics2D. Потім, методом getRotatelnstance() визначаються поворот на 45° проти годинникової стрілки навколо точки (250.0, 150.0). Це перетворення — экземпляр at класу AffineTransform. Метод concatenate(), виконуваний обєктом at, додає до цього перетворення стиснення в два рази по обом вісям координат і перенос початку координат в точку (100.0, 60.0). Нарешті, композиція цих перетворень установлюється як поточне перетворення обєкта g методом setTransform(). Перетворення виконується в наступному порядку. Спочатку простір користувача стискується в два рази вподовж обох вісей, потім початок координат користувача — лівий верхній кут — переноситься в точку (100.0, 60.0) простору графічного пристрою. Потім картинка повертається на кут 45° проти часової стрілки навколо точки (250.0, 150.0). Результат цих перетворень показаний на рис. 9.3.

Рис. 9.3. Перетворення координат

9.7. Рисування фігур засобами Java2D 

Характеристики пера для рисування фігур описані в інтерфейсі stroke. В Java 2D єсть поки що тільки один клас, реалізуючий цей інтерфейс — клас BasicStroke.

9.7.1. Класс BasicStroke

Конструктори класу BasicStroke визначають характеристики пера. Основний конструктор

BasicStroke(float width, int cap, int join, float miter, float[] dash, float dashBegin)

задає:

  •  товщину пера width в пікселях;
  •  оформлення кінця лінії cap; це одна із констант:
  •  CAP_ROUND — закруглений кінець лінії;
  •  CAP_SQUARE — квадратний кінець лінії;
  •  CAP_BUTT — оформлення відсутнє;
  •  спосіб спряження ліній join; це одна із констант:
  •  JOIN_ROUND — лінії спрягаються дугою кола;
  •  JOIN_BEVEL — лінії спрягаються відрізком прямої, перпендикулярним биісектрисі кута між лініями;
  •  JOIN_MITER — лінії просто стикуються;
  •  відстань між лініями miter, починаючи з якої застосовується спряження JOIN_MITER;
  •  довжину штрихів і проміжків міжу штрихами — масив dash; елементи масиву з парними індексами задають довжину штриха в пікселях, елементи з непарними індексами — довжину проміжка; масив перебирається циклічно;
  •  індекс dashBegin, починаючи з якого перебираютсья елементи масиву dash.

Решта конструкторів задають деякі характеристики по замовчуванню:

  •  BasicStroke (float width, int cap, int join, float miter) — суцільна лінія;
  •  BasicStroke (float width, int cap, int join) — суцільна лінія із спряженням JOIN_ROUND або JOIN_BEVEL; для спряження JOIN_MITER задається значення miter = 10.0f;
  •  BasicStroke (float width) — прямий обріз CAP_SQUARE і спряження JOIN_MITER із значенням miter = 10.0f;
  •  BasicStroke () — ширина1. 0f. 

Краще один раз побачити, ніж сто раз прочитати. В лістинзі 9.4 визначено пять пер з різними характеристиками, рис. 9.4 показує, як вони рисують.

Лістинг 9.4. Визначення пер

import java.awt.*;

import java.awt.geom.*;

import java.awt.event.*;

class StrokeTest extends Frame{

StrokeTest(String s) {

super (s) ;

setSize(500, 400);

setVisible(true);

addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent ev){

System.exit(0);

}

});

}

public void paint(Graphics gr){

Graphics2D g = (Graphics2D)gr;

g.setFont(new Font("Serif", Font.PLAIN, 15));

BasicStroke pen1 = new BasicStroke(20, BasicStroke.CAP_BUTT,BasicStroke.JOIN_MITER,30);

BasicStroke pen2 = new BasicStroke(20, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);

BasicStroke рen3 = new BasicStroke(20, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_BEVEL);

float[] dash1 = {5, 20};

BasicStroke pen4 = new BasicStroke(10, BasicStroke.CAP_ROUND,BasicStroke.JOIN_BEVEL, 10, dash1, 0);

float[] dash2 = {10, 5, 5, 5};

BasicStroke pen5 = new BasicStroke(10, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 10, dash2, 0);

g.setStroke(pen1);

g.draw(new Rectangle2D.Double(50, 50, 50, 50));

g.draw(new Line2D.Double(50, 180, 150, 180));

g.setStroke(pen2);

g.draw(new Rectangle2D.Double(200, 50, 50, 50));

g.draw(new Line2D.Double(50, 230, 150, 230));

g.setStroke(рen3);

g.draw(new Rectangle2D.Double(350, 50, 50, 50));

g.draw(new Line2D.Double(50, 280, 150, 280));

g.drawString("JOIN_MITER", 40, 130);

g.drawString("JOIN_ROUND", 180, 130);

g.drawString("JOINJBEVEL", 330, 130);

g.drawString("CAP_BUTT", 170, 190);

g.drawString("CAP_ROUND", 170, 240);

g.drawString("CAP_SQUARE", 170, 290);

g.setStroke(pen5);

g.draw(new Line2D.Double(50, 330, 250, 330));

g.setStroke(pen4);

g.draw(new Line2D.Double(50, 360, 250, 360));

g.drawString("{10, 5, 5, 5,...}", 260, 335);

g.drawString("(5, 10,...}", 260, 365);

}

public static void main(String[] args){

new StrokeTest("Мої пера");

}

}

Після створення пера одним із конструкторов і установки пера методом setStroke() можна рисувати різні фігури методами draw() і fill(). Загальні вастивості фігур, які можна нарисувати методом draw() класу Graphics2D, описані в інтерфейсі shape. Цей інтерфейс реалізований для сстворення звичайного набору фігур —прямокутников, прямих, эліпсів, дуг, точок — класами Rectangle2D, RoundRectangle2D, Line2D, Ellipse2D, Arc2D, Point2D пакету java.awt.geom. В цьому пакеті єсть ще класи CubicСurve2D і QuadCurve2D для створення кривих третього і другого порядку. Всі ці класи абстрактні, але існують їх реалізації — вкладені класи Double і Float для задання координат числами відповідного типа. В Лістинзі 9.4 використані класи Rectangle2D.Double і Line2d.Double для викреслювання прямокутників і відрізків.

9.7.2. Клас GeneralPath

В пакеті java.awt.geom єсть ще один цікавий клас — GeneralPath.  Обєкти цього класу можуть містити складні конструкції, складені із відрізків прямых або кривих ліній і інших фігур, або не  зєднаних між собою. Більше того, оскільки цей клас реалізує інтерфейс shape, його екземпляри самі являються фігурами і можуть бути елементами інших обєктів класу GeneralPath.

Спочатку створюється пустий обєкт класуа GeneralPath конструктором по замовчуванню GeneralPath() або обєкт, що містить одну фігуру, конструктором GeneralPath(Shape sh). Потім до цього объекта додаються фігуры методом append(Shape sh, boolean connect). Якщо параметр connect рівний true, то нова фігура зєднується з попередніми фігурами за допомогою поточного пера. В обєкті єсть поточна точка. Спочатку її координати (0, 0), потім її можна перемістити в точку (х, у) методом moveTo(float x,float у).

Від поточної точки до точки (х, у) можна провести:

 

  •  відрізок прямої методом lineTo(float x, float у); 
  •  відрізок квадратичної кривої методом quadTot (float x1, float y1, float x, float y), 
  •  криву Безье методом curveTo(float x1, float y1, float x2, float y2, float x, float y).

Поточною точкою після цього становитсья точка (х, у). Початкову і кінцеву точки можна зєднати методом cІosePath(). Ось як можна створити трикутник із заданими вершинами:

GeneralPath p = new GeneralPath();

p.moveTo(xl, yl); // Переносимо поточну точку в першу вершину,

p.lineTo(x2, y2); // проводимо сторону трикутника до другої вершини,

p.lineTo(x3, уЗ); // проводимо другу сторону,

p.closePathf); // проводимо третю сторону до першої вершини

Способи заповнення фігур визначені в інтерфейсі Paint. Зараз Java 2D містить три реалізації цього інтерфейса — класи Сolor, GradientPaint і TexturePaint. Клас Color нам відомий, подивимось, які способи заливки пропонують класи GradientPaint і TexturePaint.

Рис. 9.4. Пера з різними характеристиками

9.7.3. Класи GradientPaint і TexturePaint

Клас GradientPaint пропонує зробити заливку наступним чином. В двох точках М і N устанавлюються різні кольори. В точці M(x1, y1) задається колір c1, в точці N(х2, у2) — колір с2. Колір заливки гладко змінюється від с1 до с2 вподовж прямої, що зєднує точки М і N, залишаючись постійним вподовж кожної прямої, перпендикулярної прямій МN. Таку заливку створює конструктор

GradientPaint(float x1, float y1, Color c1, float x2, float y2, Color c2)

При цьому зовні відрізка МN колір залишається постійним: за точкою М - колір c1, за точкою N - колір с2.

Другий конструктор

GradientPaint(float xl, float yl, Color cl, float x2, float y2, Color c2, boolean cyclic)

при заданні параметра cyclic == true повторює заливку смуги  МN у всій фігурі. Ще два конструктори задають точки як обєкти класу Point2D.

Класс TexturePaint поступє складніше. Спочатку створюється буфер — обєкт класу Bufferedlmage із пакету java.awt.image. Це великий складний клас. Ми з ним ще зустрінемося в уроці 15, а поки що нам знадобиться тільки його графічний контекст, що управляється екземпляром класу Graphics2D. Цей екземпляр можна отримати методом createGraphics() класу Bufferedlmage. Графічний контекст буферу заповнюється фигурою, яка буде служить зразком заповнення. Потім по буферу створюється обєкт класу TexturePaint. При цьому ще задається прямокутник, розміри якого будуть розмірами зразка заповнення. Конструктор виглядає так:

TexturePaint(Bufferedlmage buffer, Rectangle2D anchor)

Після створення заливки — обєкта класу Сolor, GradientPaint або TexturePaint — вона установлюється в графічному контексті методом setPaint (Paint p) і використовується надалі методом fill (Shape sh). Все це демонструє лістинг 9.5 і рис. 9.5.

Лістинг 9.5. Способи заливки

import java.awt.*;

import java.awt.geom.*;

import java.awt.image.*;

import java.awt.event.*;

class PaintTest extends Frame{ PaintTest(String s){ super(s) ;

setSize(300, 300);

setVisible(true);

addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent ev){ System.exit(0); } });

}

public void paint(Graphics gr){

Graphics2D g = (Graphics2D)gr;

BufferedImage bi = new BufferedImage(20, 20, BufferedImage.TYPE_INT_RGB);

Graphics2D big = bi.createGraphics();

big.draw(new Line2D.Double(0.0, 0.0, 10.0, 10.0));

big.draw(new Line2D.Double(0.0, 10.0, 10.0, 0.0));

TexturePaint tp = new TexturePaint(bi,

new Rectangle2D.Double(0.0, 0.0, 10.0, 10.0));

g.setPaint(tp);

g.fill(new Rectangle2D. Double (50, 50, 200, 200));

GradientPaint gp = new GradientPaint(100, 100, Color.white,

150, 150, Color.black, true); g.setPaint(gp);

g.fill(new Ellipse2D.Double (100, 100, 200, 200));

}

public static void main(String[] args){

new PaintTest(" Способи заливки");

}

}

Рис. 9.5. Способи заливки

9.8. Виведення тексту засобами Java 2D

Шрифт — обєкт класу Font — крім імені, стилю і розміру має ще півтора десятка атрибутів: підкреслювання, закреслювання, нахили, колір шрифту і колір фону, ширину і товщину символів, аффінне перетворення, розташування зліва направо або справа наліво. Атрибути шрифту задаються як статичні константи класу TextAttribute. Найбільш використовувані атрибути перечислені в табл. 9.1.

Таблиця 9.1. Атрибути шрифту 

Атрибут

Значення

BACKGROUND

Колір фону. Обєкт, реалізуючий інтерфейс Paint

FOREGROUND

Колір тексту. Обєкт, реалізуючий інтерфейс Paint

BIDI EMBEDDED

Рівень вкладеності перегляду тексту. Ціле від 1 до 1 5

CHAR_REPLACEMENT

Фігура, що заміняє символ. Обєкт GraphicAttribute

FAMILY

Сімейство шрифта. Рядок типу string

FONT

Шрифт. Обєкт класу Font

JUSTIFICATION

Допуск при вирівнюванні абзаца. Обєкт класу Float із значеннями від 0,0 до 1,0. Єсть дві константи: JUSTIFICATION FULL і JUSTIFICATION NONE

POSTURE

Нахил шрифту. Обєкт класу Float. Єсть дві константи:

POSTURE_OBLIQUE і POSTURE_REGULAR

RUNJHRECTION

Перегляд тексту: RUN DIRECTION LTR — зліва направо, RUN DIRECTION RTL — справа наліво

SIZE

Розмір шрифта в пунктах. Обєкт класу Float

STRIKETHROUGH

Перекреслювання шрифта. Задається константою STRIKETHROUGH ON, по замовчуванню перекреслювання немає

SUPERSCRIPT

Нижні або верхні індекси. Константи: SUPERSCRIPT_NONE, SUPERSCRIPT_SUB, SUPERSCRIPT_SUPER

SWAP COLORS

Переміна місцями кольору тексті і кольору фону. Константа SWAP COLORS ON, по замовчуванню заміни немає

TRANSFORM

Перетворення шрифту. Обєкт класу AffineTransform

UNDERLINE

Підкреслювання шрифту. Константи: UNDERLINE_ON, UNDERLINE_LOW_DASHED, UNDERLINE_LOW_DOTTED, UNDERLINE LOW GRAY, UNDERLINE LOW ONE PIXEL, UNDERLINE_LOW_TWO_PIXEL

WEIGHT

Товщина шрифту. Константи: WEIGHT ULTRA LIGHT, WEIGHT _ EXTRA_LIGHT, WEIGHT _ LIGHT, WEIGHT _ DEMILIGHT, WEIGHT _ REGULAR, WEIGHT _ SEMIBOLD, WEIGHT MEDIUM, WEIGHT DEMIBOLD, WEIGHT _ BOLD, WEIGHT HEAVY, WEIGHT _ EXTRABOLD, WEIGHT _ ULTRABOLD

WIDTH

Ширина шрифту. Константи: WIDTH CONDENSED,WIDTH SEMI CONDENSED, WIDTH REGULAR, WIDTH_SEMI_EXTENDED, WIDTH_EXTENDED

На жаль, не всі шрифти дозволяют задавати всі атрибути. Побачити список допустимих атрибутів для даного шрифта можна методом getAvailableAttributes() класу Font. В класі Font єсть конструктор Font(Map attributes), яким можна зразу задати потрібні атрибути створюваному шрифту. Це вимагає   попереднього запису атрибутів у спеціально створений для цього обєкт класу, реалізуючий інтерфейс Map: класу HashMap, WeakHashMap або Hashtable (див. урок 7). Наприклад:

HashMap hm = new HashMap ();

hm.put(TextAttribute.SIZE, new Float(60.Of));

hm.put(TextAttribute.POSTURE, TextAttribute.POSTUREJDBLIQUE);

Font f = new Font(hm);

Можна створити шрифт і другим конструктором, яким ми користувалися в лістинзі 9.2, а потім додавати і змінювати атрибути методами deriveFont() класу Font. Текст в Java 2D має свій власний контекст — обєкт класу FontRenderContext, що зберігає всю інформацію, необхідну для виведення тексту. Отримати його можна методом getFontRendercontext() класу Graphics2D. Вся інформація про текст, в тому числі і про його контекст, збирається в обєкті класу TextLayout. Цей класс в Java 2D заміняє клас FontMetrics. В конструкторі класу TextLayout задається текст, шрифт і контекст. Початок методу paint() з усіма цими  визначеннями може виглядати так:

public void paint(Graphics gr){

Graphics2D g = (Graphics2D)gr;

FontRenderContext frc = g.getFontRenderContext();

Font f = new Font("Serif", Font.BOLD, 15);

String s = "Якийсь текст";

TextLayout tl = new TextLayout(s, f, frc) ; // Продовження методу }

В класі TextLayout єсть не тільки більше двадцати методів getxxxo, що дозволяють узнати різні дані про текст, його шрифте і контекст, але і метод

draw(Graphics2D g, float x, float у)

що викреслює зміст обєкта класу TextLayout в графічній області g, починаючи з точки (х, у). Ще один цікавий метод

getOutline(AffineTransform at)

повертаєт контур шрифту у вигляді обєкта shape. Цей контур можна потім заповнити по якому-небудь зразку або вивести тільки контур, як показано в лістинзі 9.6.

Лістинг 9.6. Виведення тексту засобами Java 2D

 

import java.awt.*;

import java.awt.font.*;

import java.awt.geom.*;

import java.awt.event.*;

class StillText extends Frame{

StillText(String s) {

super(s);

setSize(400, 200);

setVisible(true);

addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent ev){

System.exit(0) ;

}

});

}

public void paint(Graphics gr){

Graphics2D g = (Graphics2D)gr;

int w = getSize().width, h = getSize().height;

FontRenderContext frc = g.getFontRenderContext();

String s = "Тінь";

Font f = new Font("Serif", Font.BOLD, h/3);

TextLayout tl = new TextLayout(s, f, frc);

AffineTransform at = new AffineTransform();

at.setToTranslation(w/2-tl.getBounds().getWidth()/2, h/2);

Shape sh = tl.getOutline(at);

g.draw(sh);

AffineTransform atsh = new AffineTransform(1, 0.0, 1.5, -1, 0.0, 0.0);

g.transform(at);

g.transform(atsh);

Font df = f.deriveFont(atsh);

TextLayout dtl = new TextLayout(s, df, frc);

Shape sh2 = dtl.getOutline(atsh);

g.fill(sh2); }

public static void main(String[] args) {

new StillText(" Ефект тіні");

}

}

На рис. 9.6 показано виведення цієї програми.

Рис. 9.6. Виведення тексту засобами Java 2D

Ще одна можливість створити текст з атрибутами — визначити обєкт класуа AttributedString із пакета java. text. Конструктор цього класу

AttributedString(String text, Map attributes)

задає зразу і текст, і його атрибути. Потім можна додати або змінити характеристики тексту одним із трьох методів addAttibute().

Якщо текст займає декілька рядків, то постає питання його форматування. Для цього замість класу TextLayout використовується клас LineBreakMeasurer, методи якого дозволяють відформатувати абзац. Для кожного сегмента тексту можна одержати екземпляр класу TextLayout і вивести текст, використовуючи його атрибути. Для редагування тексту необхідно відслідкувати курсором (caret) поточну позицію в тексті. Це здійснюється методами класу TextHitinfo, а методи класу TextLayout дозволяють одержати позицію курсора, виділити блок тексту і підсвітити його. Нарешті, можна задати окремі правила для виведення кожного символу текста. Для цього треба одержати екземпляр класу Glyphvector методом createGІyphvector() класу Font, змінити позицію символу методом setСІiyphPosition(), задати перетворення символу, якщо це допустимо для даного шрифта, методом setСІyphTransform(), і вивести змінений текст методом drawGІyphVector() класу Graphics2D. Все це показано в лістинзі 9.7 і на рис. 9.7 — виведення програми лістинга 9.7.

Лістинг 9.7. Виведення окремих символів

import j ava.awt.*;

import java.awt.font.*;

import java.awt.geom.*;

import java.awt.event.*;

class GlyphTest extends Frame{ GlyphTest(String s){ super(s) ;

setSize(400, 150);

setVisible(true);

addWindowListener(new WindowAdapter(){

public void windowClosing(WindowEvent ev){

System.exit(0);

}

});

}

public void paint(Graphics gr){

int h = 5;

Graphics2D g = (Graphics2D)gr;

FontRenderContext frc = g.getFontRenderContext();

Font f = new Font("Serif", Font.BOLD, 30);

GlyphVector gv = f.createGiyphvector(frc, "Танцюючий текст");

int len = gv.getNumGlyphs();

for (int i = 0; i < len; i++){

Point2D.Double p = new Point2D.Double(25 * i, h = -h);

gv.setGlyphPosition(i, p) ;

}

g.drawGiyphVector(gv, 10, 100); }

public static void main(String[] args)(

new GlyphTest(" Виведення окремих символів");

}

}

Рис. 9.7. Виведення окремих символів

9.9. Методи покращення візуалізації 

Візуалізацію (rendering) створеної графіки можна удосконалити, установивши один із методів (hint) покращення одним із методів класу Graphics2D:

setRenderingHints(RenderingHints.Key key, Object value)

setRenderingHints(Map hints)

Ключі — методи покращення — і їх значення задаються константами класу RenderingHints, перечисленными в табл. 9.2.

Таблиця 9.2. Методи візуалізації і їх значення 

Метод

Значення

KEY_ANTIALIASING

Розмивання крайніх пікселів ліній для гладкості зображення; три значення, що задаються константами VALUE ANTIALIAS DEFAULT, VALUE ANTIALIAS ON, VALUE~ANTIALIAS OFF

KEY_TEXT_ANTTALIAS ING

Tе ж для тексту. Константи: VALUE TEXT ANTIALIASING_DEFAULT, VALUE TEXT ANTIALIASING ON, VALUE_TEXT_ANTIALIASING_OFF

KEY RENDERING

Три типи візуалізації. Константи: VALUE RENDER SPEED, VALUE RENDER QUALITY, VALUE RENDER DEFAULT

KEY COLOR RENDERING

Tе ж для кольору. Константи: VALUE COLOR RENDER_SPEED, VALUE COLOR RENDER QUALITY, VALUE COLOR RENDER DEFAULT

KEY ALPHA INTERPOLATION

Плавне спряження ліній. Константи: VALUE ALPHA INTERPOLATION_SPEED, VALUE ALPHA INTERPOLATION QUALITY, VALUE_ALPHA_INTERPOLATION_DEFAULT

KEY_INTERPOLATION

Способи спряження. Константи: VALUE_INTERPOLATION_BILINEAR, VALUE INTERPOLATION BICUBIC, VALUE   INTERPOLATION_NEAREST_NEIGHBOR

KEY_DITHERING

Заміна близьких кольорів. Константи: VALUE DITHER ENABLE, VALUE DITHER DISABLE, VALUE DITHER DEFAULT

He всі графічні системи забезпечують виконання цих методів, тому задання указаних атрибутив не означає, що визначені ними методи будуть застосовуватися на самім ділі. Ось як може виглядати початок метода paint() з указанням методів покращення візуалізації:

public void paint(Graphics gr){

Graphics2D g = (Graphics2D)gr;

g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,

RenderingHints.VALUE_ANTIALIAS_ON);

g.setRenderingHint(RenderingHints.KEY_RENDERING,

RenderingHints.VALUE_RENDER_QUALITY);

// Продовження методу

} 

9.10. Заключення

В цьому уроці ми, розуміється, не змогли детально разібрати всі можливості Java 2D. Ми не торкнулись моделів задання кольору і змішування кольорів, друку графіки і тексту, динамічного завантаження шрифтів, зміни області рисування. В уроці 15 ми розглянемо засоби Java 2D для роботи із зображеннями, в уроці 18 — засоби друку. В документації SUN J2SDK, в каталозі docs\guide\2d\spec, єсть керівництво Java 2 API Guide з оглядом всіх можливостей Java 2D. Там єсть посилання на посібники по Java 2D. В каталозі demo\jfc\Java2D\src приведени вихідні тексти програм, що використовують Java 2D.

Лабораторна робота 8. Робота з класом Graphics і Graphics2D

  1.  Ви вже, без сумніву, випробували програму лістинга 9.2. Візьміть її за зразок і виведіть у вікно всі фігури, перечислені в параграфі 9.1.2. Нижче кожної фігури виведіть її назву. Вибирайте для кожної фігури свій колір. Для кожної назви виберіть свій шрифт, розмір, колір і стиль. Особливу увагу зверніть на способи задання вершин полігону і ламаної. Ви повинні проілюструвати всі можливі випадки.
  2.  Розберіться з програмою 9.5 і попробуйте залити пару фігур узорами відмінними від використаних у цій програмі, в тому числі розгляньте і заливку суцільним кольором.  




1. тема в XVII в В сельском хозяйстве господствовала барщинная система крестьяне находились в личной зависимост
2. Наука про суспільство ~ найскладніша з усіх наук
3. Местное обезболивание
4. Что такое концентрированная пища Это пища прошедшая процесс обезвоживания
5. ся главным образом чз сущие в норме анастомозы м-у ветвями различных артериальных стволов т
6. на тему- Студента ки курсу .html
7. государственного организма даны и обоснованы в нашей статье в 1м томе настоящего издания
8. і Підступність нікотину алкоголю і наркотиків у тому що вони до певного часу руйнують організм людини неп
9. Об утверждении ведомственной целевой программы Ремонт и противопожарные мероприятия в муниципальных у
10. ки лингвист закономерностей изучаемого языка особенности его грамматического строя фонетич и лексич сис
11. 2014 учебный год График проведения практических занятий по дисциплине Фармакогнозия для студентов 5
12. один из учеников Христа
13. Тема 15 Ефективність менеджменту Операційний менеджмент
14. задание 3 Сметная стоимость тыс.
15. Что такое сыроедение и как стать сыроедом ДИЕТА ИЗ КОПИЛКИ КУРЬЕЗОВ Вместо предисловия Как
16. российское юридическое лицо включенное в реестр таможенных перевозчиков осуществляющее перевозку товар
17. конспекта физкультурного занятия Сведения о занятии дата время место проведения количество детей и
18. Введение Основными задачами стоящими перед пищевой промышленностью являются обеспечение устойчивого сна
19. гігієнічній основі
20. Статья 329. Рабочее время и время отдыха работников труд которых непосредственно связан с движением транспорт