Thursday, May 19, 2011

Varying gradients...

Just a short post about varying gradients. If you take a bar for example that has a linear gradient from top to bottom and you would like to adjust the gradient with the horizontal size of the bar you have to calculate the colors for the gradient from the size of horizontal size of the bar.
I created a little drawing to visualize what i mean (i hope you could understand it)...

The horizontal bar in the center should vary it's gradient by it's horizontal size. To achieve this you need to calculate two gradients, one over the three colors on the top of the box (the dark colors) and one over the three colors on the bottom of the box (the bright colors).
I faced the problem when i created the battery component of the SteelSeries lib yesterday which looks like this...


Exactly for this kind of problem i created the LinearGradientPaint wrapper last week. For the example with the horizontal box you have to create two instances of the LinearGradient2 class that is also part of the SteelSeries lib. One for the three dark colors and the other for the three bright colors.
Then you create a LinearGradientPaint that fills the horizontal box from top to bottom with the colors that you get from the getColorAt(float fraction) method of the LinearGradientPaint2 class. Hmm...hard to put into words...maybe some code will help here...

public class TestPanel extends javax.swing.JPanel
{
    public TestPanel()
    {
        super();
    }
    
    @Override
    protected void paintComponent(java.awt.Graphics g)
    {
        super.paintComponent(g);
        
        java.awt.Graphics2D G2 = (java.awt.Graphics2D) g;
        
        G2.setRenderingHint(java.awt.RenderingHints.KEY_ANTIALIASING, java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
        
        // Define three boxes with the size of 100%, 66% and 33%
        final Rectangle2D BOX100 = new Rectangle2D.Double(0, 0, 300, 50);
        final Rectangle2D BOX66 = new Rectangle2D.Double(0, 100, 200, 50);
        final Rectangle2D BOX33 = new Rectangle2D.Double(0, 200, 100, 50);
        final Point2D START = new Point2D.Double();
        final Point2D STOP = new Point2D.Double();
        
        // Define the fractions for the dark and bright gradient
        final float[] FRACTIONS =
        {
            0.0f,
            0.5f,
            1.0f
        };        
        
        // Define the dark colors (from top to top)
        final Color[] DARK_COLORS =
        {
            Color.GREEN.darker(),
            Color.YELLOW.darker(),
            Color.RED.darker()
        };
        
        // Define the bright colors (from bottom to bottom)
        final Color[] BRIGHT_COLORS =
        {
            Color.GREEN.brighter(),
            Color.YELLOW.brighter(),
            Color.RED.brighter()
        };
        
        // Define the two LinearGradientPaint2 instances
        LinearGradientPaint2 DARK_GRADIENT = new LinearGradientPaint2(new Point2D.Double(0,0), new Point2D.Double(100,0), FRACTIONS, DARK_COLORS);
        LinearGradientPaint2 BRIGHT_GRADIENT = new LinearGradientPaint2(new Point2D.Double(0,0), new Point2D.Double(100,0), FRACTIONS, BRIGHT_COLORS);
                
        // Define the fractions for the linear gradient of the horizontal box
        final float[] BOX_FRACTIONS =
        {
            0.0f,
            1.0f
        };
        
        // Define the gradient for the 100% box and fill it with the right colors
        START.setLocation(0, BOX100.getMinY());
        STOP.setLocation(0, BOX100.getMaxY());                
        Color[] boxColors =
        {
            DARK_GRADIENT.getColorAt((float) BOX100.getWidth() / 300f),
            BRIGHT_GRADIENT.getColorAt((float) BOX100.getWidth() / 300f)
        };        
        LinearGradientPaint boxGradient = new LinearGradientPaint(START, STOP, BOX_FRACTIONS, boxColors);
        G2.setPaint(boxGradient);
        G2.fill(BOX100);
        
        // Define the gradient for the 66% box and fill it with the right colors
        START.setLocation(0, BOX66.getMinY());
        STOP.setLocation(0, BOX66.getMaxY());
        boxColors = new Color[]
        {
            DARK_GRADIENT.getColorAt((float) BOX66.getWidth() / 300f),
            BRIGHT_GRADIENT.getColorAt((float) BOX66.getWidth() / 300f)
        };        
        boxGradient = new LinearGradientPaint(START, STOP, BOX_FRACTIONS, boxColors);
        G2.setPaint(boxGradient);
        G2.fill(BOX66);

        // Define the gradient for the 33% box and fill it with the right colors
        START.setLocation(0, BOX33.getMinY());
        STOP.setLocation(0, BOX33.getMaxY());
        boxColors = new Color[]
        {
            DARK_GRADIENT.getColorAt((float) BOX33.getWidth() / 300f),
            BRIGHT_GRADIENT.getColorAt((float) BOX33.getWidth() / 300f)
        };        
        boxGradient = new LinearGradientPaint(START, STOP, BOX_FRACTIONS, boxColors);
        G2.setPaint(boxGradient);
        G2.fill(BOX33);
        
        G2.dispose();
    }
}  

This is a JPanel which you could put into a JFrame and it will look like this...




Hopefully this post will explain better why something like the getColorAt(float fraction) method is really usefull and why i created the LinearGradientPaint2 class.
It safes you a lot of code and gives you a lot of new ways to explore...


Here's another example where i vary the colors of a linear gradient. If you click the link below the image the application will start via webstart...


Webstart link




So enjoy the upcoming weekend and keep coding...




5 comments:

  1. This was the same thing we did in J2ME 4 years back to get a suedo 3D look and fill :) nice post by the way.

    Javin
    How Synchronization works in Java

    ReplyDelete
  2. Hi Javin,
    So i'll have to use Google more often before i start coding... ;-)
    Cheers, Gerrit

    ReplyDelete
  3. Hi! I love your posts. Great and beautiful work. Thanks for sharing your skills. Maybe I can learn something :-)

    ReplyDelete
  4. Nice work! Keep it up. Liked the cute battery icon. You're an artist too. Well done. Hats off.

    Really liked it. Why you call it legacy Swing? And btw, can it be ported to android??

    ReplyDelete
  5. Hi Mohsin,
    Well i would love to see the components on Android but i simply have no time for that. But i know that Alexander Pupeikis is thinking about a port of the lib to Android (you will find his blog at http://www.ableben.com).
    Cheers,
    Gerrit

    ReplyDelete