Fast text with less memory
Text occurs in every diagramming application, for instance as labels on nodes and links. In JViews, you have the choice between IlvZoomableLabel and IlvText; IlvZoomableLabel is more a text picture that allows fancy gradient paints inside the glyphs while the solid colored IlvText is usually faster and allows more traditional text manipulations (wrapping mode and so on). Both classes provide plenty of features that allow to adapt them to all possible situations.
Plenty of features … sigh! Features cost memory and performance. Each text parameter that can be customized needs to be stored in the text object, and if you have many features, you also have many parameters. Indeed, IlvText for instance uses quite a bit of memory, and if you have 100000 text objects, you can run into memory problems … that are avoidable.
Reducing the number of parameters
What if we don’t need all these customizable parameters? Let’s assume all text objects in our application display a single line each, and the only variable parameters are the color of the text and of the background rectangle, and of course the label and position of the text object. Then we need to store 2 colors, a string and a position in the text object, but not parameters such as antialiasing, font, wrapping mode, margins and so on, because these can be hard coded and are fixed for all text objects. This is the essential idea of a lightweight text: use only memory for parameters that are customizable, and avoid memory for parameters that we never want to customize in our application.
Good idea, but it sounds like a lot of effort! My new LightText class needs to render text to display it. How can I do this without writing a text rendering engine from scratch? The idea is to use the text rendering engine of IlvText. Whenever I need to draw the LightText, I allocate a new IlvText object, draw it instead, and then give it free. That is, I delegate the rendering of the LightText to a temporary IlvText object allocated on the fly. The Java garbage collection will make sure that the memory footprint remains low. The first sketch is this:
public class LightText extends IlvGraphic {
Color foreground;
Color background;
IlvRect bounds; // the current position
String label; // the label text
...
public void draw(Graphics dst, IlvTransformer t) {
getDelegate(true).draw(dst, t);
}
private IlvText getDelegate(boolean includeBounds) {
IlvText text = new IlvText(new IlvPoint(), label));
// hard coded parameters
text.setAntialiasing(true);
text.setLeftMargin(4);
text.setRightMargin(4);
text.setTopMargin(4);
text.setBottomMargin(4);
text.setFillOn(true);
text.setStrokeOn(false);
// customized parameters
text.setForeground(foreground);
text.setFillPaint(background);
if (includeBounds)
text.moveResize(bounds);
else if (bounds != null)
text.move(bounds.x, bounds.y);
return text;
}
}
Now LightText uses less memory, because it stores less parameters than IlvText, but of course this is not fast. When drawing the objects, delegates must be allocated, and this needs time. This does not hurt much when we draw only 10-30 objects, but when we have to draw 100000 objects, it will be much too slow.
Speeding up drawing with small zoom level
Well, 100000 objects on the screen? We only need to draw 100000 objects at the same time if the view is demagnified. Otherwise all these objects would not fit on the screen. But when the view is demagnified, each single object occupies only a tiny area and you cannot recognize the details of the object anyway. Therefore it is not needed to draw all details. In this case, it is sufficient to draw only a tiny rectangle instead of the fully rendered text. Only when the view is magnified, we need to draw the text in detail, but in this case we never need to draw many objects. Hence, when the view is magnified, the slowdown caused by the few delegate drawing does not hurt so much, and when the view is demagnified, we don’t need to allocate many delegates. Here is the modified draw routine:
public void draw(Graphics dst, IlvTransformer t) {
IlvRect bbox = boundingBox(t);
if (bbox.width <= 2 || bbox.height <= 2) {
// draw only a tiny filled rectangle
dst.setColor(foreground);
int w = Math.max(1, (int)bbox.width);
int h = Math.max(1, (int)bbox.height);
dst.fillRect((int)bbox.x, (int)bbox.y, w, h);
} else if (bbox.width <= 4 || bbox.height <= 4) {
// draw border and inner, but no text
int w = Math.max(1, (int)bbox.width);
int h = Math.max(1, (int)bbox.height);
// fill on is hardcoded to true
dst.setPaint(background);
dst.fillRect((int)bbox.x, (int)bbox.y, w, h);
// stroke on is hardcoded to false, hence only the text label,
// not the border is drawn
dst.setColor(foreground);
int x = (int)bbox.x;
int middle = (int)(bbox.y + 0.5f * bbox.height);
dst.drawLine(x, middle, x + w, middle);
} else {
// draw the text via delegate
getDelegate(true).draw(dst, t);
}
}
This draw method has 3 levels of details:
- If the text object is drawn on an area smaller than 2×2 pixels, we draw a filled rectangle.
- If the text object is drawn on an area smaller than 4×4 pixels, we draw a filled background rectangle and a line inside representing the text.
- Otherwise, we draw the text object in full detail.
Results
I wrote a small application that displays 10000 text objects. When using IlvText directly, the application allocates 13 MB memory. When using LightText, it requires only 2.5 MB of memory. This technique hence allowed me to reduce the memory load by a factor of 5.2. When I zoom out, the LightText actually draws faster than IlvText. When I zoom in, only few objects get drawn, hence the slowdown of LightText is in this case neglectable.
The full code of the example is available here: lighttext.zip. It compiles with JViews 8.0 and 8.1. My small application has a check box at the top to choose between IlvText and LightText, and it displays the history of the memory footprint at the bottom. When you switch from IlvText to LightText, you can easily see how the memory footprint is reduced.

LightText is not just yet another text object. It is rather a programming technique, since it depends on your application which parameters of IlvText must be customizable and which can be hard coded with fixed values. The same idea can be applied to other IlvGraphic subclasses as well. JViews has plenty of features in general purpose IlvGraphic subclasses, but when memory is critical and the purpose is more special and less general, optimizing graphic classes might be worth the effort.
Tags: Code Optimization, diagram, JViews, Real-World examples, Text








July 3rd, 2008 at 5:33 am
I do have one question. Why not base your LightText on IlvGraphicHandle? Isn’t IlvGraphicHandle supposed to be the base for all delegated graphics objects?
July 3rd, 2008 at 5:41 am
IlvGraphicHandle keeps a permanent handle of the delegate. LightText allocates the delegate on the fly and gives it free for garbage collection after drawing. If I would use IlvGraphicHandle, then the delegate would not be garbage collected after drawing. Then I would loose the effect that is needs less memory.