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.
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.
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.
Thanks. Another question, what does TextPane's setCharacterAttributes method do? From the javadoc
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?
Joined: Jun 13, 2009
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.
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.
Joined: Jun 13, 2009
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.
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.
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?
Joined: Jun 13, 2009
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.
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.