Sunday, September 6, 2009

Fancy JLabels With JHLabs Filters

Recently I was looking for a way to create "fancy" text for some title panes and I stumbled over JHLabs Image Filters.





Over at JHLabs there are examples of how to create e.g., glowing text:



Or chrome like text:



To get a simmlar effect on a JLabel just replace the JLabel with a JHLabsLabel e.g.;

JLabel label = new JHLabsLabel("Hello World", new ChromeFilter(), new ShadowFilter(5, 5, 2, .7f));


Here is the code for JHLabsLabel:

/**
* Notice that this is not perfect wrt alignment to see just uncomment super.paintComponent(g)
* Maybe somebody with BasicLabelUI knowledge could help?
*
*/
public static final class JHLabsLabel extends JLabel {

private int textX;

private int textY;

private AbstractBufferedImageOp[] filters;

public JHLabsLabel(String string, AbstractBufferedImageOp... filters) {
super(string);
this.filters = filters;
}

@Override
public void paintComponent(Graphics g) {

// super.paintComponent(g);

BufferedImage img = createTextImage(getText(), getFont());

for (AbstractBufferedImageOp f : filters) {
img = f.filter(img, null);
}

// g.drawImage(f.filter(img, null), textX, textY, null);
g.drawImage(img, textX, textY, null);
}

private BufferedImage createTextImage(String text, Font font) {

Rectangle paintIconR = new Rectangle();
Rectangle paintTextR = new Rectangle();
Rectangle paintViewR = new Rectangle();
Insets paintViewInsets = new Insets(0, 0, 0, 0);

paintViewInsets = getInsets(paintViewInsets);
paintViewR.x = paintViewInsets.left;
paintViewR.y = paintViewInsets.top;
paintViewR.width = getWidth()
- (paintViewInsets.left + paintViewInsets.right);
paintViewR.height = getHeight()
- (paintViewInsets.top + paintViewInsets.bottom);

String clippedText = SwingUtilities.layoutCompoundLabel(
(JComponent) this, getFontMetrics(getFont()), text,
getIcon(), getVerticalAlignment(),
getHorizontalAlignment(), getVerticalTextPosition(),
getHorizontalTextPosition(), paintViewR, paintIconR,
paintTextR, getIconTextGap());

boolean isAntiAliased = true;
boolean usesFractionalMetrics = false;
FontRenderContext frc = new FontRenderContext(null, isAntiAliased,
usesFractionalMetrics);
TextLayout layout = new TextLayout(clippedText, font, frc);
Rectangle2D bounds = layout.getBounds();
int w = (int) Math.ceil(bounds.getWidth());
int h = (int) Math.ceil(bounds.getHeight());
BufferedImage image = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setColor(new Color(0, 0, 0, 0));
g.fillRect(0, 0, w, h);
g.setColor(getForeground());
g.setFont(font);
Object antiAliased = isAntiAliased ? RenderingHints.VALUE_TEXT_ANTIALIAS_ON
: RenderingHints.VALUE_TEXT_ANTIALIAS_OFF;
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
antiAliased);
Object fractionalMetrics = usesFractionalMetrics ? RenderingHints.VALUE_FRACTIONALMETRICS_ON
: RenderingHints.VALUE_FRACTIONALMETRICS_OFF;
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
fractionalMetrics);
g.drawString(clippedText, (float) -bounds.getX(), (float) -bounds
.getY());
// g.drawString(clippedText, (float) 0, (float) 0);
g.dispose();

textX = paintTextR.x;
textY = paintTextR.y;// + getFontMetrics(font).getAscent() / 2;
System.out.println(String.format("X=%d Y=%d, w=%d h=%d", textX,
textY, w, h));

return image;
}

}


I also could not resist to play to the JHLabs texture filters as background painters e.g.,

JXPanel woodPanel = new JXPanel();
woodPanel.setBackgroundPainter(new JHTexturePainter<JXPanel>(new CausticsFilter(), new SparkleFilter()));

Creates the initial background for the demo.

Here is the code for JHTexturePainter:

/**
*
* Paint the background using a combination of the JHLabs image filters
*
* @param <T>
*/
static final class JHTexturePainter<T> implements Painter<T> {

private AbstractBufferedImageOp[] filters;

public JHTexturePainter(AbstractBufferedImageOp... filters) {
this.filters = filters;
}

@Override
public void paint(Graphics2D g2d, T arg1, int w, int h) {
BufferedImage image = new BufferedImage(w, h,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = image.createGraphics();
g.setColor(new Color(0, 0, 0, 0));
g.fillRect(0, 0, w, h);

for (AbstractBufferedImageOp f : filters) {
image = f.filter(image, null);
}
g.dispose();
g2d.drawImage(image, 0, 0, null);
}

}

6 comments:

Carsten said...

JeanetteWinzenburg (jw54674) posted the following on Wed Sep 09 05:41:41 EDT 2009:

-----------

Carsten, (too lazy to dig for my google account, so cant comment in directly your site)

SwingX already has much of the stuff you implemented manually: AbstractPainter has a setFilters method.

[code]
JXLabel label = new JXLabel("Hello World");
label.setFont(new Font("SansSerif", Font.BOLD, 80));
AbstractPainter> fg = (AbstractPainter>) label.getForegroundPainter();
fg.setFilters(new ChromeFilter(), new ShadowFilter(5, 5, 2, .7f));
JXPanel panel = new JXPanel();
MattePainter background = new MattePainter(Color.BLUE);
background.setFilters(new CausticsFilter(), new SparkleFilter());
panel.setBackgroundPainter(background);
panel.add(label);
[/code]

Enjoy!
Jeanette

tbee said...

Interesting component. I tried to duplicate your code, but in all versions of JHLabs filters I can find on the internet, non of the filters extend AbstractBufferedImageOp.

What JHLabs version did you use?

tbee said...

Same goes for Jeannettes version here.

Carsten said...

You can find the file here www.jhlabs.com/ip/AbstractBufferedImageOp.java

so it should be part jhlabs

tbee said...

I found the problem; it turned out there was a very old and obscurely named JHLabs jar in the project, but Eclipse was referring to the newer one when I clicked on the classes. Eclipse frequently fools me with its classpath behavior.

Anonymous said...

[ ... ] link is being shared on Twitter right now. @zenx, an influential author, said RT @1ndus: Xtreme [ ... ]

Live Traffic Map