aspose file tools*
The moose likes Swing / AWT / SWT and the fly likes Text in different colours Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Swing / AWT / SWT
Bookmark "Text in different colours" Watch "Text in different colours" New topic
Author

Text in different colours

Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

Say I'm trying to do some ASCII art and want to output characters to a GUI where each character's colour depends on what it is. What's the best way of doing this?

Ideas:
1) Paint the characters on an object like a JPanel using a Graphics2D.drawString() method, and set a new pen colour before each character

2) JEditorPane - load Strings into a DefaultStyledDocument and use the setCharacterAttributes method to go through each character before loading it into the pane

3) JTextPane - use JTextPane's setCharacterAttributes method

I've never used any of these before so I don't know how easy / fast they are, or if there's some easier way of doing this.. I'm planning on animating the output so I'd like the method to be fast if possible.
Paul Clapham
Bartender

Joined: Oct 14, 2005
Posts: 18911
    
    8

From reading the documentation (for about 20 seconds) it looks like JTextPane is a subclass of JEditorPane, and the reason for subclassing is so that it can organize its text into paragraphs. Since you didn't mention that, I would suggest that JEditorPane would be fine.

As for (1) versus (2), if you want your text to be nicely organized in lines, then JEditorPane would do that. However if you want to have letters all over the place and not necessarily in lines, then you would need to draw them yourself.

As for speed: computers are fast these days. Everything you would be doing here would be CPU-bound, so you would either have to have an enormous amount of text or a phenomenally bad code base to make either of those two approaches appear slow.
Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

The text will be arranged in lines. Currently my code outputs one long string for the whole picture, but this is easily altered.

The size will be variable, but if I crank it up to full screen it could be up to around 200x100 characters, which is 20,000, and if I'm animating at 50 fps that's a million characters per second to process - at which point speed could well be an issue! But I'll start with the simplest way (whatever that is) and see what the performance is like.

Yes JTextPane does subclass JEditorPane - I was wondering if the setCharacterAttributes (which JEditorPane doesn't have) might be useful. Like I implied, I have very little idea what I'm doing.
Paul Clapham
Bartender

Joined: Oct 14, 2005
Posts: 18911
    
    8

Luigi Plinge wrote:Like I implied, I have very little idea what I'm doing.


Then I definitely recommend reading the tutorial (linked from the API documentation for JEditorPane).
Rob Camick
Ranch Hand

Joined: Jun 13, 2009
Posts: 2216
    
    7
I would use JTextPane. JEditorPane is for HTML. I find using character attributes much easier than building your own HTML.

Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

The following brings up a text pane with some multi-coloured text - is this the right way to go about it? Any steps I can cut out / do more easily?



Paul Clapham
Bartender

Joined: Oct 14, 2005
Posts: 18911
    
    8

You can't get much more simple than that. In fact if we'd asked you to produce an SSCCE, that would have been a good example of one.
Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

Thanks. Another question, what does TextPane's setCharacterAttributes method do? From the javadoc
setCharacterAttributes
public void setCharacterAttributes(AttributeSet attr, boolean replace)

Applies the given attributes to character content. If there is a selection, the attributes are applied to the selection range. If there is no selection, the attributes are applied to the input attribute set which defines the attributes for any new text that gets inserted.


So is there another way to insert text into your TextPane other than Document's insertString(int offset, String str, AttributeSet a) method, which must include an AttributeSet in its arguments anyway? Any example where you might use this method?
Rob Camick
Ranch Hand

Joined: Jun 13, 2009
Posts: 2216
    
    7
The insertString(...) method is the method used to add text to a Document. The attributes can be null if you wish.

Also, you don't need to use the input attributes. You can just make you your own:



This way you can have a different set of attributes for different types of words.
Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

And we have colour!





It's noticeably slower than the monochrome version, particularly when I increase the display size to around 200x60, there's a noticeable delay. (My CPU is a Core Duo E7400 - not exactly the zippiest, but not too slow either.) I could define the attribute sets in advance rather than on each screen update, but I don't think it'll make much difference. I might have a go at a drawString() implementation to see if it's any faster.

If anyone wants to play, I've uploaded the jar to Rapidshare, along with the source.
Rob Camick
Ranch Hand

Joined: Jun 13, 2009
Posts: 2216
    
    7
How do you update the Document with the characters. Do you create one big string and insert all the characters at once. Or do you update each character individually. If its the latter case then you can set the attribute at the same time as you insert the character.
Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

I update the text using JTextPane.setText() with one long String.

I tried doing the latter just now and actually it improves performance a lot. Still feels a bit slower than monochrome but more acceptable. Might still be too slow for fast animation at full screen, but we'll see when I implement that.
Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441



OK, I implemented a routine using drawString(), and it's MUCH faster than using the JTextPane and setting text attributes.

Above, we're using 2 point text, and with 490 x 292 characters it's still pretty zippy!

I just want to get some feedback on how I did it. I subclassed JPanel as an inner class to the main panel. In this code:

am is my AsciiMandel object, which contains the characters to display in its int[][] PIXELMAP field, as well as the number of columns XCOLS and rows YROWS, and the characters to display in char[] CHARS. Color[] palette contains colours to display.


My questions are:

1) Is it OK to set the preferred size of this panel, and revalidate, within the paintComponent method? I would put it outside, but we only know the component's size when we have the FontRenderContext. Can we (and should we) get the FontRenderContext somewhere outside paintComponent()?

2) Is LineMetrics the best way to get the correct height for a font in general? As you can see, it takes a String argument, so I had to put some random characters in. It has the advantage of returning the recommended line spacing along with the font height.

3) How about the width? I found a method in Font that returns a bounding rectangle. This seems OK for a monospaced font like Consolas, but a) I shouldn't use Consolas because it's a Windows font, and b) when I tried using the font "Monospaced", i.e. the platform independent monospaced font, I get a rectangle that is about 3 times too wide for any font size. How best to get the width (preferably with recommended spacing) of Monospaced, at a given font size?

4) I don't suppose there's any more efficient way to draw a char apart from converting it to a String, as here, in the drawString method?
Rob Camick
Ranch Hand

Joined: Jun 13, 2009
Posts: 2216
    
    7
1) You should not be using revalidate() or setPreferredSize() inside the paintComponent(). In many cases change a property of the class will cause an infinite loop. Its better to override the getPreferredSize() method to return the size.

2) Use FontMetrics.getHeight()

3) FontMetrics.stringWidth(...) or charWidth()

4) Don't convert from a char. Keep the data as a String?

Note: you should not be using variable xBorder and yBorder. Swing already supports the concept of Borders. You can add an EmptyBorder to your panel. Then in your painting code you use the getInsets() method to get the border size.

Note2: you should also not be setting the Font in the paintComponent() method. Just use the panel.setFont() method and the Font of the Graphics object will already be set. Also usually use setColor(...) to set the Graphics color.
Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

Thanks Rob, I've changed my inner class as suggested. I didn't know the FontMetrics class existed!

I'd already changed my data creation class, AsciiMandel, so that it produced an int array at the same time as producing the String output. The data (consisting of ints from 0 to 12) is translated into chars then converted into a String using StringBuilder; it seems a bit of a waste to build this String only to pull it apart again using substring. Using the raw data seems like it should be more efficient since I don't need to produce the long string at all.

Luigi Plinge
Ranch Hand

Joined: Jan 06, 2011
Posts: 441

Just saw Update 2: added a constructor to do the setFont outside the paintComponent, and changed setPaint to setColor... works fine. Thanks for the tips.

 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: Text in different colours