Andres is a DZone Zone Leader and has posted 143 posts at DZone. You can read more from them at their website. View Full User Profile

GraphicsBuilder Tutorial III: Paints & Colors

02.26.2008
| 20318 views |
  • submit to reddit

Linear Gradients

GradientPaint has the limitation of being able to process only two colors, but since jdk6 Java2D has the option of drawing multicolored linear gradients, thanks to LinearGradient. The linearGradient node shares all properties of gradientPaint but requires a nested set of stop nodes that define each color and their position (offset) relative to the gradient's line distance. Let's revisit the gradientPaint example but using linearGradient instead, changing the black/orange set of colors to red/orange/darkGreen/blue. This example will also show how stretch and fit affect the gradient.

 

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' )
}
}

Stops accept a color property (and opacity), but they also accept [red,green,blue,alpha] as the color and colorPaint nodes do. You may define stops in the order you choose and GraphicsBuilder will sort them in the correct order, of course the preferred way is to define them in proper offset order.

Radial Gradients

 Jdk6 also delivers support for radial gradients, not just linear ones. Radial gradients require a set of stops to define their colors much as linear gradients do too, but that is where the similarities stop, as radial gradients require (at least) a center (cx,cy) and a radius, you may specify an optional focus (fx,fy) to further customize how the gradient looks, by default the focus will be equal to the center.

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 )
}

As the image shows we have 5 squares with a similar radial gradient applied to them. The center square display its gradient in a different way, because the gradient is anchored in absolute coordinates rather than relative. All gradients will always be translated to relative coordinates in order to fit the shape's bounds unless told otherwise, this is also the case on linear gradients (fit=false). Looking back at the linearGradient example you may notice that the code is a bit verbose, as the gradient definition is repeated over and over again,shouldn't be a better way to reuse gradients or paints for that matter ? Well there is one, and it is shown in this example, there is a paint node that serves as a placeholder for any saved paint. As it is the case with shapes and their asShape property, all paints share a asPaint property that prevents the paint to be applied right away, just remember to assign an id to that paint in order to be able to reference it later. The paint node not also wraps an existing paint but will also let you modify the wrapped paint (it actually clones its paint the first time), that is why the fifth gradient can be set as absolute without altering the previous ones.

Linear and Radial gradients can share their stops definitions, as they are essentially the same, and if needed be, you can add more stops at any time. You can enable this feature by setting a linkTo property on the referencing gradient. let's see an example of this feature.

 

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' )
}
}

We define a linearGradient first and let the other three gradients refer to it while redefining one of the stops and adding new ones.

Published at DZone with permission of its author, Andres Almiray.