Thursday, November 18, 2010

Contour Gradient Paint

Sorry, no new component this time but another little class that might be of help for you.
When i created the ConicalGradientPaint some time ago i knew that there's one more gradientpaint that is not in the java2d api. The so called ContourGradient or it's special form the DiamondGradient.
This kind of gradient is not used as often as the Linear- or RadialGradient but nevertheless it could be really useful.
To give you an idea of what i'm talking about here let me show you some images that explains the gradients...

ContourGradientPaint horizontal

ContourGradientPaint vertical

ContourGradientPaint square (diamond)

Right now my implementation only supports rectangular shapes which means i use the boundary of a given shape and fill it with the gradient. That also means it is not a real contour gradient paint because it won't work with a contour of a polygon.
A polygon filled with the ContourGradientPaint will look like this...

ContourGradient on a polygon
As you can see it uses the rectangular boundary of the star shape and fill it with the gradient.
To create a custom gradientpaint you have to understand how the gradients realized in java2d. So let me try to explain...

Say we would like to create a contour gradient that looks like this...

As you can see we have a gradient from red to yellow which is defined by half of the height of the rectangle. And if you take a closer look you will figure out the following structure in the filled box.

You can see four sections and each of this section is filled by a linear gradient from red to yellow. Only the direction of the gradient changes. And because the gradient is defined by half the size of the height, every gradient has the same parameters which means fractions and related colors.

The part in the gradient creation where the magic happens is the getRaster() method. In this method you will get a X and Y coordinate and a TILE_WIDTH and TILE_HEIGHT. 
This means your structure will be rastered in tiles of the given TILE_WIDTH TILE_HEIGHT and with every call of getRaster() the position of the tile will be stored in X and Y.

I have tried to create a little image that visualizes the procedure...

In my approach i created a lookup table for the colors and use the GeneralPath in java.awt.geom package to create the four sections.
When iterating over all the pixels in each tile i check for each pixel in which sector he's placed by calling the contains() method of the related sector general path object.

Now you just have to calculate the position in the color lookup table from the position in the current sector (for SECTOR_A and SECTOR_C we'll take the current x position and for SECTOR_B and SECTOR_D we'll take the current y position). 
The last step is storing the values for red, green, blue and alpha for each pixel in the data array that will be used to fill the shape.

The ContourGradientPaint has the following signature:

    Rectangle2D boundary,  
    float[] fractions, 
    Color[] colors)

This is very similar to the inbuild gradient paints and should be easy for you to use.

To realize the box we used above as our example we need the following code:

Graphics2D g2 = (Graphics2D) g;

Rectangle2D box = new Rectangle2D.Double(0, 0, 500, 250);
float[] fractions = { 0.0f, 1.0f };
Color[] colors = { Color.RED, Color.YELLOW };
ContourGradientPaint cgp =  
    new ContourGradientPaint(box.getBounds(), fractions, colors);


and here is the result...

You might think "Well...nice but what should i do with this kind of graphic...???"...

To give you an idea i created another example that maybe will open a door to your imagination... it's up to you...

If you like it, please find the source and the binary here:


    Binary: ContourGradientPaint.jar

That's it for today and now i'll enjoy the best conference i know...Devoxx...


  1. Very nice idea and that could be a start-up for the new controls, Swing controls, of course. But, maybe and for the custom Android painting, that is not so distinct from pure Swing and Java2D (however, it has the distinguishes). Whatsoever, this is very fascinating for the new deep dive brain storming!

  2. Ooh, I am a bit disappointed, as I expected to see contour gradient for an arbitrary closed Bézier shape... :-)
    And I think the start should have 5 gradients, not 4.
    Anyway, I appreciate the development of the idea and the explanations.

  3. I agree that it would be nice having a "real" contour gradientpaint and i'm still thinking about creating one.
    I also agree to the point with the star but if you read the text around the red star image i explain that this implementation did not work for polygons because i use the rectangular boundary box to fill it and therefor the star with it's 5 corners will be filled as it would be a rectangle.

  4. Hi,
    Thanks for sharing this. I really enjoy reading your blog.
    Great work. Whats the license of your code.
    I would like to use this as a template for some paints that I would need to implement.

    Have fun,
    - Rossi

  5. Hi Rossi,

    The code is under BSD license which makes it free for any kind of use...
    Keep coding...

  6. Yeah, "star", not "start", my fingers shouldn't type by themselves...
    And indeed, I misread "will look like this" as "should look like this"... :-(
    Sorry, and keep up the good work.

  7. Why java does uses integers for holding 8 bit data.
    int [] data = new data[w*h*4];
    data[BASE+0]=currentRed; // it's int but holds 8 bit value.