during my work at Quintiq i stumbled upon a problem when drawing lot's of linear gradients in "realtime". In our software we have a gantt chart component that could contain thousands of nodes. Each of these nodes could contain multiple so called compartments which are representing a range of values. So the node itself is visualized using a LinearGradientPaint and also each compartment is visualized with it's own LinearGradientPaint. So far this should not be a problem but now it comes...we can't really cache things because the whole gantt chart is so to say "live". This means if somewhere in the businessmodel a value changed it will affect the ganttchart in the way that the nodes and their compartments will change it's size. Another problem is that the nodes could not only have different widths but also different heights.
The compartments are separate shapes that will drawn on top of the nodes.
And that's not enough, the chart that is visible on the screen only represents a small part of the whole data which means one could scroll horizontal which let new nodes appear on the screen and others might change their height (this is something special to this kind of graph in our software).
Long story short...we could nail the performance problem to the drawing of the linear gradients and their creation.
So i was thinking about how to improve the drawing speed of these gradients and came to an interesting approach that i would like to share with you in this post.
First of all i created a 1px wide BufferedImage with the most common height of the nodes. Then i filled this 1px image with a linear gradient (e.g. the green one) and saved the image for later use. Everytime when there was a new color of a compartment i created an 1px gradient image for this color.
The trick is now to use the TexturePaint to fill all these nodes which has the big advantage that it will scale the gradientimage automaticaly to the needed height. Because we have to create each gradient only once now the repaint speed could be reduced by 30ms on each repaint.
This performance problem only occured when the gantt chart became really huge with thousands of nodes, but i thought it might be interesting for you to know.
Here is a little code snippet that will give you a hint on what i did...
private BufferedImage nodeTexture;
private void init() {
private final float[] fractions = {
0.0f,
0.5f,
1.0f
};
private final Color[] colors = {
Color.RED,
Color.GREEN,
Color.BLUE
};
nodeTexture = createNodeTexture(24, fractions, colors);
}
@Override
protected void paintComponent(Graphics g) {
...
Graphics2D g2 = (Graphics2D) g.create();
for (RoundRectangle2D node : nodes) {
TexturePaint nodePaint = new TexturePaint(nodeTexture, node.getBounds());
g2.setPaint(nodePaint);
g2.fill(node);
}
g2.dispose();
...
}
// Method that creates a 1px wide gradient image
private BufferedImage createNodeTexture(final int HEIGHT, float[] fractions, Color[] colors) {
GraphicsEnvironment gfxEnv = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsConfiguration gfxConf = gfxEnv.getDefaultScreenDevice().getDefaultConfiguration();
BufferedImage image = gfxConf.createCompatibleImage(1, HEIGHT, Transparency.OPAQUE);
Graphics2D g2 = image.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Point2D start = new Point2D.Double(0, 0);
Point2D stop = new Point2D.Double(0, HEIGHT);
final LinearGradientPaint GRADIENT_NODE = new LinearGradientPaint(
start,
stop,
fractions,
colors);
g2.setPaint(GRADIENT_NODE);
g2.fillRect(0, 0, 1, HEIGHT);
g2.dispose();
return image;
}
Before i forget it...we are hiring, so if you are looking for a job in the Java and/or C++ area please check here
That's it for today, so keep coding...
That's it for today, so keep coding...




