Статьи

GraphicsBuilder Tutorial III: Краски и Цвета

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

Вступление 

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

Плоские цвета 

В предыдущих частях вы могли заметить, что для придания цвета фигурам и контурам использовались два свойства: это borderColor и fill . Первый (как следует из названия) определяет цвет, который будет применен к границе (или обводке, как мы увидим в следующей главе), второй — цвет, который будет заполнять область формы. Примеры показали, что вы можете указать цветовой литерал, но на самом деле вы можете использовать любой экземпляр java.awt.Color. Литералы являются сокращением для наиболее распространенных цветов (java.awt.Color определяет только 13 основных цветов), но GraphicsBuilder предоставляет дополнительные цветовые литералы для всего набора цветов CSS2, таких как ‘aqua’, ‘bisque’, ‘indigo’, SteelBlue ‘среди других.

Узел цвета позаботится о переводе цветового литерала в правильное представление java.awt.color, полагаясь на синглтон ColorCache. ColorCache отвечает за хранение имен цветов и их значений, вы можете использовать их для хранения новых цветов, если хотите. Свойства цвета, которые принимают цветовой литерал, также используют ColorCache под обложками.

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

[Img_assist | NID = тысяча триста сорок четыре | название = | убывание = | ссылка = нет | выравнивание не = среднего | ширина = 100 | высота = 100]

 

rect( x: 0, y: 0, width: 50, height: 50, borderColor: 'black', fill: 'red' )
rect( x: 50, y: 0, width: 50, height: 50, borderColor: color('black') ){
colorPaint( 'blue' )
}
rect( x: 0, y: 50, width: 50, height: 50, borderColor: color('#000000'), fill: color('#FF0') )
rect( x: 50, y: 50, width: 50, height: 50, borderColor: color(red:0) ){
colorPaint( green: 1 )
}
rect( x: 25, y: 25, width: 50, height: 50, borderColor: 'black' ){
colorPaint( color('white').derive(alpha:0.5) )
}

Давайте рассмотрим каждый прямоугольник один за другим. Красный прямоугольник устанавливает свои цветовые свойства так же, как и в предыдущих примерах, здесь ничего нового. Синий прямоугольник представляет узел цвета и узел colorPaint , оба они имеют одинаковые свойства, разница в том, что colorPaint возвращает PaintProvider, внутренний класс GraphicsBuilder, который работает с красками, тогда как цвет (как вы уже знаете) возвращает Java .awt.Color. Желтый прямоугольник показывает другую особенность цветового узла, он может переводить шестнадцатеричные литералы цвета, как в полном, так и в компактном режиме. Зеленый прямоугольник показывает другой набор свойств для цветаузел, он принимает комбинацию значений [красный, зеленый, синий, альфа] в диапазоне от 0f..1f до 1i..255i; неопределенные свойства будут иметь ноль (0), назначенный их значениям, поэтому черный цвет границы может быть установлен с цветом (красный: 0) . Последний прямоугольник, белый полупрозрачный, показывает, что colorPaint принимает вывод цвета , и что java.awt.color был расширен (через Groovy MetaClasses) с помощью нового метода получить , который позволит вам создать новый цвет из базовой один; этот метод принимает комбинацию свойств [красный, зеленый, синий, альфа].

Двухцветные градиенты 

Плоские цвета в порядке, но все знают, что градиенты лучше, они добавляют глубину и тепло любому рисунку, поэтому давайте посмотрим, что может предложить GraphicsBuilder. Поскольку первые годы Java2D-разработчики имели возможность рисовать градиенты, используя java.awt.GradientPaint, который позволит вам рисовать двухцветный линейный градиент, на самом деле не так много загадок, поэтому узел градиента довольно прямой вперед, как показывает следующий пример

[Img_assist | NID = 1345 | название = | убывание = | ссылка = нет | выравнивание не = средняя | ширина = 200 | высота = 100]

 

rect( x: 0, y: 0, width: 100, height: 50, borderColor: 'black'  ){
gradientPaint( x1: 0, y1: 0, x2: 100, y2: 50, color2: 'orange' )
}
rect( x: 100, y: 0, width: 100, height: 50, borderColor: 'black' ){
gradientPaint( x1: 0, y1: 0, x2: 50, y2: 30, color2: 'orange' )
}
rect( x: 0, y: 50, width: 100, height: 50, borderColor: 'black' ){
gradientPaint( x1: 0, y1: 0, x2: 50, y2:30, color2: 'orange', stretch: yes )
}
rect( x: 100, y: 50, width: 100, height: 50, borderColor: 'black' ){
gradientPaint( x1: 0, y1: 0, x2: 50, y2: 30, color2: 'orange', fit: no )
}

GradientPaint требует линии и двух цветов, имеет некоторые разумные значения по умолчанию (например, color1, являющийся «черным»). вы заметите, что верхние прямоугольники имеют одинаковую краску, даже если определения линий для каждого из них разные, это потому, что GradientPaint будет пытаться вписать градиент в границы фигуры по умолчанию, сохраняя соотношение сторон. Левый нижний прямоугольник показывает другое свойство, stretch , которое также будет пытаться подогнать градиент в границах фигуры, но оно не сохраняет соотношение сторон. Последний прямоугольник показывает, как рисуется градиент, когда подгонка отключена. Стоит отметить, что растяжение и подгонка являются взаимоисключающими, если вы включите одно, другое будет отключено автоматически.

Линейные градиенты

GradientPaint имеет ограничение на способность обрабатывать только два цвета, но, поскольку jdk6 Java2D имеет возможность рисовать разноцветные линейные градиенты, благодаря LinearGradient. Узел linearGradient разделяет все свойства градиента, но требует вложенного набора узлов остановки, которые определяют каждый цвет и их положение (смещение) относительно расстояния линии градиента. Давайте вернемся к примеру градиента, но вместо этого будем использовать linearGradient , заменив черный / оранжевый набор цветов на красный / оранжевый / darkGreen / синий. Этот пример также покажет, как растяжение и подгонка влияют на градиент.

[Img_assist | NID = тысяча триста сорок семь | название = | убывание = | ссылка = нет | выравнивание не = среднего | ширина = 200 | высота = 100]

 

rect( x: 0, y: 0, width: 100, height: 50, borderColor: 'black'  ){
linearGradient( x1: 0, y1: 0, x2: 100, y2: 50 ){
stop( offset: 0, color: 'red' )
stop( offset: 0.3, color: 'orange' )
stop( offset: 0.6, color: 'darkGreen' )
stop( offset: 1, color: 'blue' )
}
}
rect( x: 100, y: 0, width: 100, height: 50, borderColor: 'black' ){
linearGradient( x1: 0, y1: 0, x2: 50, y2: 30 ){
stop( offset: 0, color: 'red' )
stop( offset: 0.3, color: 'orange' )
stop( offset: 0.6, color: 'darkGreen' )
stop( offset: 1, color: 'blue' )
}
}
rect( x: 0, y: 50, width: 100, height: 50, borderColor: 'black' ){
linearGradient( x1: 0, y1: 0, x2: 50, y2: 30, stretch: yes ){
stop( offset: 0, color: 'red' )
stop( offset: 0.3, color: 'orange' )
stop( offset: 0.6, color: 'darkGreen' )
stop( offset: 1, color: 'blue' )
}
}
rect( x: 100, y: 50, width: 100, height: 50, borderColor: 'black' ){
linearGradient( x1: 0, y1: 0, x2: 50, y2: 30, fit: no ){
stop( offset: 0, color: 'red' )
stop( offset: 0.3, color: 'orange' )
stop( offset: 0.6, color: 'darkGreen' )
stop( offset: 1, color: 'blue' )
}
}

Остановки принимают свойство цвета (и непрозрачность), но они также принимают [красный, зеленый, синий, альфа], как это делают узлы color и colorPaint . Вы можете определить остановки в выбранном вами порядке, и GraphicsBuilder отсортирует их в правильном порядке, конечно, предпочтительным способом является их определение в правильном порядке смещения.

Радиальные градиенты

 Jdk6 также поддерживает радиальные градиенты, а не только линейные. Радиальные градиенты требуют набора упоров, чтобы определить их цвета так же, как и линейные градиенты, но на этом сходство заканчивается, так как радиальные градиенты требуют (по крайней мере) центра (cx, cy) и радиуса, вы можете указать дополнительный focus (fx, fy) для дальнейшей настройки внешнего вида градиента, по умолчанию фокус будет равен центру.

[Img_assist | NID = 1348 | название = | убывание = | ссылка = нет | выравнивание не = средняя | ширина = 200 | высота = 200]

radialGradient( cx: 50, cy: 50, radius: 75, id: 'g', asPaint: yes ){
stop( offset: 0, color: 'red' )
stop( offset: 0.5, color: 'orange' )
stop( offset: 1, color: 'black' )
}
rect( x: 0, y: 0, width: 100, height: 100, borderColor: 'black' ){
paint( g )
}
rect( x: 100, y: 0, width: 100, height: 100, borderColor: 'black' ){
paint( g )
}
rect( x: 0, y: 100, width: 100, height: 100, borderColor: 'black' ){
paint( g )
}
rect( x: 100, y: 100, width: 100, height: 100, borderColor: 'black' ){
paint( g )
}
rect( x: 50, y: 50, width: 100, height: 100, borderColor: 'black' ){
paint( g, absolute: yes )
}

Как показано на рисунке, у нас есть 5 квадратов с одинаковым радиальным градиентом. Центральный квадрат отображает свой градиент по-другому, потому что градиент привязан в абсолютных координатах, а не в относительных. Все градиенты всегда будут переводиться в относительные координаты, чтобы соответствовать границам фигуры, если не указано иное, это также относится и к линейным градиентам (fit = false). Оглядываясь на пример linearGradient, вы можете заметить, что код немного многословен, поскольку определение градиента повторяется снова и снова, не должен ли быть лучший способ повторно использовать градиенты или краски в этом отношении? Ну, есть один, и это показано в этом примере, есть узел рисования, который служит заполнителем для любой сохраненной краски. Как это происходит с формами и ихВ свойстве asShape все краски имеют общее свойство asPaint, которое предотвращает немедленное применение краски, просто не забудьте назначить идентификатор этой краске, чтобы иметь возможность ссылаться на него позже. Узел рисования также не оборачивает существующую краску, но также позволяет изменять обернутую краску (она фактически клонирует свою краску в первый раз), поэтому пятый градиент можно установить как абсолютный, не изменяя предыдущие.

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

[Img_assist | NID = 1349 | название = | убывание = | ссылка = нет | выравнивание не = средняя | ширина = 200 | высота = 200]

 

rect( x: 0, y: 0, width: 100, height: 100, borderColor: 'black'  ){
linearGradient( id: 'linear' ){
stop( offset: 0, color: 'red' )
stop( offset: 0.5, color: 'green' )
stop( offset: 1, color: 'black' )
}
}
rect( x: 100, y: 0, width: 100, height: 100, borderColor: 'black' ){
radialGradient( cx: 50, cy: 50, radius: 75, linkTo: linear )
}
rect( x: 0, y: 100, width: 100, height: 100, borderColor: 'black' ){
linearGradient( y2: 50, linkTo: linear ){
stop( offset: 0.5, color: 'blue' )
}
}
rect( x: 100, y: 100, width: 100, height: 100, borderColor: 'black' ){
radialGradient( cx: 50, cy: 50, radius: 75, linkTo: linear ){
stop( offset: 0.25, color: 'orange' )
stop( offset: 0.75, color: 'blue' )
}
}

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

Пограничные краски

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

[Img_assist | NID = 1350 | название = | убывание = | ссылка = нет | выравнивание не = средняя | ширина = 220 | Высота = 220]

rect( x: 0, y: 0, width: 220, height: 220, fill: 'white' )
rect( x: 20, y: 20, width: 180, height: 180, borderWidth: 10 ){
borderPaint {
colorPaint( 'blue' )
}
}
rect( x: 40, y: 40, width: 140, height: 140, borderWidth: 10 ){
borderPaint {
gradientPaint( color2: 'blue' )
}
}
rect( x: 60, y: 60, width: 100, height: 100, borderWidth: 10 ){
borderPaint {
linearGradient( x1: 50, x2: 0, id: 'g' ) {
stop( offset: 0, color: 'red' )
stop( offset: 0.33, color: 'orange' )
stop( offset: 0.66, color: 'darkGreen' )
stop( offset: 1, color: 'blue' )
}
}
}
rect( x: 85, y: 85, width: 50, height: 50, borderWidth: 20 ){
borderPaint {
radialGradient( cx: 25, cy: 25, radius: 50, linkTo: g )
}
}

Вы должны быть в состоянии применить любые из предыдущих настроек рисования к границам.

Multipaints

Последняя опция рисования довольно уникальна для GraphicsBuilder, она позволяет применять группу красок к одной и той же фигуре, каждый из которых наносится один за другим. Следующий пример взят из книги Filthy Rich Clients (настоятельно рекомендуется), вы можете увидеть похожую картинку в главе, где Ромен вводит градиенты.

[Img_assist | NID = 1351 | название = | убывание = | ссылка = нет | выравнивание не = средняя | ширина = 220 | Высота = 220]

То, что вы видите, — это не то, что вы думаете, это не сфера, а круг с серией градиентов, которые создают иллюзию сферы. Мультикраска состоит из основного цвета, окружающего градиента, который дает ощущение глубины (сферический контекст), зеркального градиента (внизу) и выделения (или блеска) в верхней части. Если вы думаете, что сделать этот чертеж с помощью Java2D сложно, вы, вероятно, правы, если не используете GraphicsBuilder для упрощения процесса.

// some useful variables
def width = 200
def height = 200
def cx = width/2 + 10
def cy = height/2 + 10
// turn on antialias
antialias on
// paint the background
rect( x: 0, y: 0, width: 220, height: 220, fill: 'white' )
// this is the base shape and color
circle( cx: cx, cy: cx, radius: width/2, borderColor: no, fill: 'blue' )
// sphere-like gradients
circle( cx: cx, cy: cx, radius: width/2, borderColor: no ){
multiPaint {
// ambient
radialGradient( cx: cx, cy: cy, radius: width/2 ) {
stop( offset: 0, color: color(red: 6, green: 76, blue: 160, alpha: 127) )
stop( offset: 1, color: color(alpha: 204) )
}
// specular
def lighting = color(red: 64, green: 142, blue: 203, alpha: 255)
radialGradient( cx: cx, cy: height*1.5,
fx: cx, fy: (height*1.75)+16,
radius: width/2, absolute: yes ) {
stop( offset: 0, color: lighting )
stop( offset: 0.8, color: lighting.derive(alpha:0) )
transformations{ scale(y:0.5) }
}
// shine
radialGradient( cx: cx, cy: cy,
fx: 55, fy: 35, radius: width/1.4 ){
stop( offset: 0, color: color('white').derive(alpha:0.5) )
stop( offset: 0.5, color: color('white').derive(alpha:0) )
}
}
}

Вывод

Существует еще один вариант рисования, который мы еще не видели, texturePaint , так как он требует знаний о том, как работают изображения в GraphicsBuilder, мы вернемся к этому в следующей главе этой серии. На данный момент вы уже знаете, как изменить цвета формы, контура и даже границ, что, мы надеемся, приведет вас к часам игривого взлома с graphicsPad.