Saturday, December 26, 2009

Survey time! Please take a minute to comment.

This time last year I merged all my blog projects into one codebase, and I moved that codebase to a dev.java.net project. I couldn't be happier: this was a great move. It's backed up, it's easy to check out the workspace on other machines, etc.

Also I automated the process of building/updating the jars: one java app called BlogUpdater combs through the workspace and creates a jar for every java file with a main() method. As more and more projects share small common files this really helped me keep things up-to-date.

Help Me Help You


Now it's that time of year again where I want to step back and look at how I do what I do. What can be improved? Both for me and for you. Any feedback is welcome. Feel free to email me or post it here as a comment.

Specific conversation starters could -- but don't have to -- include:

  • Currently I list every blog article in a list on the left. Is this a good format? There are over 40 things in this list. By this time next year: there may be 50 or 60? Here is a similar repository that follows a very different format. Also Ken's blog is much less cluttered. Thoughts?
  • What subjects would you like to hear about? Writing these things up takes time... if I can cater to what you're interested in it will be a win-win for both of us.
  • In a given project: what do you want to hear about? I don't have a fixed template: so far I've just rambled about whatever I think is interesting. This might be performance, how to render something, architecture, etc.
  • On a bigger, more abstract scale: does anyone have any suggestions for other ways to focus this time & energy? Maybe I could join forces with other bloggers? (Who did you have in mind?) Or I could work on specific open-source java projects my skill set might lend itself to? etc. If a great suggestion comes up: I might just freeze this blog the way it is and pick up that other project.
  • Does it bother anyone that I don't have a solid versioning system? (That is: if you use the ColorPicker project, there is no version number associated with it.)
  • In the articles would you like more code samples? More screenshots? More diagrams?
  • Does anything about my programming style prevent you from integrating my jars into your own projects? Anything in the java code that you'd like to see changed? Don't be shy!

  • I recently sketched out a list of big changes I wanted to make to my blog and its presentation; but then I decided it might be much more useful to just ask you, my readers, what you think first...

    p.s. if you like things just the way they are, and would like to see more of "the same" articles: please say that too. The only way I'll gauge the majority opinion is if you speak up. :)

    Thanks for reading, and thanks for the 9,000+ hits I've received this year. (Give or take a few thousand hits that might just be search engine bots?)

    Monday, December 14, 2009

    Text: Prompts and Search Fields

    Here's what I want to replicate:


    The concept of a text field is nothing new, but this undertaking requires two other components:

    1. A TextFieldUI with rounded corners, and a search icon.
    2. Prompting: gray text prompting the user with instructions or other useful information.

    Apple's Solution


    It's elegant. It's simple:
    textField.putClientProperty( "JTextField.variant", "search");
    textField.putClientProperty( "JTextField.Search.Prompt", prompt);

    The first line is well documented here. The second line is only casually mentioned here.

    It's a great solution -- if you're only trying to support Macs. What I want is a cross-platform solution. This is the same problem that triggered my new ButtonUI's, and an indeterminate progress widget. If I may get on my soap box for a minute: some of the client properties Apple's Java team implements are really not a good use of their time. To be fair: some properties they implement are great (window opacity, types of title bars, window decorations, etc). Meanwhile there are JVM-crashing bugs, accessibility bugs, etc, that Apple's team can address but I cannot... so why implement properties like this? (end of rant.)

    Creating a RoundTextFieldUI


    So a pure-Java solution requires making a new TextFieldUI. This wasn't so hard, if you extend the BasicTextFieldUI. The key was figuring out I had to override the getVisibleEditorRect() method. This controls the rectangle the text appears in, so I can offset everything to draw the rounded rectangle.

    Also by overriding modelToView() I was able to tweak the caret height (the x-position and width I didn't change).

    Here is the class I came up with. It includes a client property "useSearchIcon" to render a magnifying glass icon.

    That part was surprisingly simple. Next I wanted to deal with prompting.

    Using XSwingX


    This is a little project hosted at code.google.com. It has prompting built-in, and a very simple programming interface. But it was surprisingly tricky to put a demo together. It requires the SwingX package. But not the latest version: there are some incompatibilities in the latest version of SwingX integrated with XSwingX. We need version 0.9.2.

    Here is a jar that includes all the necessary classes to get the job done. But if you start counting: that comes to 32 classes. Also if you start looking at the source code: the word "hack" keeps coming up. It doesn't seem like a great solution.

    My Own Approach


    I wanted to try something simple. Something done in one, maybe two classes. My first thought was to extend JTextField, and shuffle the color/text based on when the text field has the focus:

    public class PromptTextField extends JTextField {
    static FocusListener focusListener = new FocusListener() {
    public void focusGained(FocusEvent e) {
    PromptTextField src = (PromptTextField)e.getSource();
    if(src.state==STATE_PROMPT) {
    src.setState(STATE_NORMAL);
    }
    }

    public void focusLost(FocusEvent e) {
    PromptTextField src = (PromptTextField)e.getSource();
    if(src.getText().length()==0)
    src.setState(STATE_PROMPT);
    }
    };
    private static final int STATE_UNDEFINED = 0;
    private static final int STATE_NORMAL = 1;
    private static final int STATE_PROMPT = 2;

    private Color normalColor = SystemColor.textText;
    protected Color promptColor = Color.gray;
    protected String promptText;
    private int state = STATE_UNDEFINED;

    public PromptTextField(String text, String prompt, int columns) {
    super(text, columns);
    promptText = prompt;
    normalColor = getForeground();
    addFocusListener(focusListener);
    if(text.length()==0) {
    setState(STATE_PROMPT);
    } else {
    setState(STATE_NORMAL);
    }
    }

    public String getText() {
    if(state==STATE_PROMPT) {
    return "";
    }
    return super.getText();
    }

    private void setState(int s) {
    if(s==state)
    return;
    state = s;
    if(s==STATE_PROMPT) {
    if(state==STATE_NORMAL && getText().length()>0)
    throw new IllegalArgumentException("the state should not be set to STATE_PROMPT if there is text already in the text field (\""+getText()+"\")");
    super.setForeground(promptColor);
    super.setText(promptText);
    } else if(s==STATE_NORMAL) {
    super.setForeground(normalColor);
    super.setText("");
    }
    }

    public void setText(String s) {
    if(state==STATE_UNDEFINED) {
    super.setText(s);
    return;
    }
    if(s==null) s = "";
    if(hasFocus() || s.length()>0) {
    state = STATE_NORMAL;
    super.setForeground(normalColor);
    super.setText(s);
    } else if(s.length()==0) {
    state = STATE_PROMPT;
    super.setForeground(promptColor);
    super.setText(promptText);
    }
    }
    }

    I was impressed that, well, that this worked. It's very minimalist. However it's also not very good: it's an awkward hack that overrides the getText() and setText() methods. This means the getText() method might reflect something that has nothing in common with the data stored in getDocument().

    Rob Camick's Approach


    Who is Rob Camick? I don't know. But now that I found this article I'm going to pay closer attention. He talks about another model to achieve prompting: add a component over the text field.

    It's simple. It's not a "hack," it's just plain old clever.

    There's a problem, though: text placement. In my case -- because I'm using a TextFieldUI of my own creation -- it'll be hard to line up the prompted text exactly over the existing text.

    ... unless I use a TextOnlyGraphics2D and the same type of TextFieldUI.

    public class TextFieldPrompt extends JTextField {
    public TextFieldPrompt(JTextField parent,
    Color promptColor,
    String promptText) {
    super(promptText);

    if(promptColor==null)
    promptColor = Color.gray;

    parent.add(this);

    setFocusable(false);
    setEditable(false);
    setForeground(promptColor);
    setOpaque(false);

    ... install listeners & UI ...
    }


    public void paint(Graphics g) {
    Graphics2D g2 = (Graphics2D)g;
    g2 = new TextOnlyGraphics2D( g2, null );
    super.paint( g2 );
    }

    public boolean contains(int x, int y) {
    return false;
    }
    }

    Conclusion


    Here is an applet demonstrating the final model:


    This jar (source included) is available here.

    Sunday, December 6, 2009

    Text: Searching JTextComponents

    This entry has to do with searching for a phrase in JTextComponents. There's a lot of ground to cover, so I'll start you off with the demo and then go into details. (Give this applet a few seconds to load... it has a lot of text to read.)


    This jar (source included) is available here.

    Text Search Bars


    What is the point of a search bar? This is an interface decision more than a technical one. A search bar has several advantages over a search dialog:
    1. A dialog will physically be in the wrong location. It will start out centered, which probably covers part of the text the user wants to search.
    2. Providing a search bar in a fixed location will help users visually scan the interface more easily. They'll know where to look. It's a matter of identifying a fixed point vs searching for it.
    3. Dialogs are usually (and perhaps originally) designed for the convenience of programmers. You can cram a program full of thousands of dialogs... but the screen real estate is unlimited. It is much harder to create a well-balanced window with just the right amount of essential interface controls without becoming too cluttered. It takes more work, and we usually don't bother.
    4. Of course a modal search dialog is worse. Modal dialogs are discouraged here, among other places.

    I modeled these search bars after the ones I saw in Firefox and Safari. They include:

    • a search field
    • next/previous buttons
    • a checkbox for matching case
    • a dismiss button
    • a label including the occurrences of the search phrase
    • a toggle button for "Highlight All"

    The order (and presence) varies, but those are the basics. Safari automatically highlights all, and Firefox uses a toggle button. The anchor varies in both toolbars, but the components are still listed in about that order.

    Text Search Dialog


    Of course sometimes a dialog might be appropriate: that's up to you to decide. If searching is a feature so rarely used that it doesn't make sense to keep it in the main interface: that's justifiable. Or if your software is so versatile that searching is nothing but an auxiliary function? That's OK too.

    So I included a minimal dialog. It automatically performs case insensitive, wrap-around searches. It's not modal, but it does dismiss itself when the user hits the return key.

    No matter which model you use (a search dialog or a search bar), you probably want to also include typical menu shortcuts for find, find again, and find previous.

    Searching Text


    Most of the rest of this project was GUI work: so I was in my prime. The actual methods that do the searching/counting, though... I admit I'm pretty clueless about.

    My first instinct was to convert the document into a String, and then use .indexOf() and .lastIndexOf() to search for everything. Now I have absolutely no proof for what I'm about to say, but that sure feels like a horrible approach. Especially when we have to convert the entire string to a specific case to avoid case sensitivity.

    So instead I went spelunking around, and tried using the Segment class. Now the method is much hairier, but it walks through chunks of characters at a time to find the search phrase: at no time do I have an extra copy of the whole document floating around.

    (Remember when we count the occurrences of the search phrase: we perform a bajillion searches, so this method needs to be relatively light.)

    If anyone has any experience/suggestions on this subject, here is the code in question.

    The Visuals


    When you use the Safari-style search bar you're introducing two extra layers of visual effects into your JFrame:

    The middle layer is known as the TextHighlightSheet. The topmost layer is a SearchHighlight. These layers have a few of things in common:
    1. They are both added to the JLayeredPane.
    2. They both are tied to their "parent" JTextComponent. (It's not technically their parent component as far as the Swing hierarchy is concerned, but it's useful to think of them as bound to that text component.) When AncestorListeners or ComponentListeners are notified that the TextComponent has moved around: these other layers have to immediately respond.
    3. They're both animated. Or at least they can be. The middle sheet may fade in/out, and the highlight can have any number of customized animations.

    Now I'll discuss each in a little more detail.

    The TextHighlightSheet


    This is the more clever component: it has to highlight all the occurrences of the search phrase, without looking at irrelevant hits. So it will first calculate the index that is mapped to the point (0, 0) of the viewport, and then the index that is mapped to (width, height). Then search only within those indices, and calculate the shapes of all the hits.

    The hits are outlined in rectangles, and Area objects are used to join overlapping rectangles. Usually I avoid the Area class like the plague, but combined rectangles shouldn't be a hassle: they're small, and they contain only lines. Paint those rectangles white, and then paint everything around them in a translucent black (to add the overall shadow).

    Finally a combination of the right clipping, translation, and a TextOnlyGraphics2D let us call parentTextComponent.paint(g) to render the text.

    The Firefox search bar uses the same component as the Safari search bar. (Although in the Firefox bar you have to explicitly turn it on with the "Highlight All" button.) The Firefox sheet, however, doesn't have a translucent shadow. Also it uses different padding and colors. All of this is covered in the javadocs if you really want to customize it.

    The sheet has isActive() and setActive() methods to control whether it is visible. The TextSearchBar automatically follows one of two behaviors:

    1. If the "Highlight All" button is visible: then that button is the only factor controlling whether the sheet is active. This is how Firefox behaves.
    2. If the "Highlight All" button is invisible: then the sheet is made active only when the search field has the keyboard focus. This is how Safari behaves.

    SearchHighlight


    This is a very different component, and is subclassed directly into the look you're aiming for. The AbstractSearchHighlight subclasses into the AquaSearchHighlight, the FirefoxSearchHighlight, and the NudgeSearchHighlight.

    When constructed this component creates a BufferedImage of one particular hit of the search phrase. Then a javax.swing.Timer is activated, and this component is ready for action.

    The subclasses can animate the transform and opacity of the image over the duration of the animation.

    It is important that the sheet and the effect be designed to visually complement each other. For example: the highlight sheet for the Safari search bar is designed to work with the Aqua or the Nudge effects, but the Firefox effect uses smaller padding around the hit phrases.

    Also the Firefox effect cheats and uses an infinite duration, so the effect never really goes away. I'm not sure exactly what should happen in this scenario if the JTextComponent is editable? (Any thoughts?) I guess the effect should go away as soon you modify the document such that that particular hit phrase is changed? This is not an issue in the Safari/Nudge models, simply because the animation will quickly dispose of itself. So this is a bug in the Firefox effect, but I'm not sure what the ideal behavior is.

    Each effect is created by calling TextSearch.highlight(). This looks up the value for UIManager.get("textSearchHighlightEffect"), and instantiates the class name provided. I won't go into detail here, but the point is: you can customize this if you want. (You don't even have to extend the com.bric.plaf.AbstractSearchHighlight if you don't want to.)

    Conclusion


    In v1.0 of this project I just included the search bars and the effects. In v2.0 I added the middle sheet. If I get around to a v3.0: what next? (Aside from existing bugs.)

    Speaking of bugs: I suppose you also need to be able to perform a revised search with every key stroke in the search field. This is how Firefox behaves. This is slightly different from hitting the enter key in the text field, because that will search for the next occurrence starting at the end of the current selection... but continuous typing really needs a search that starts at the beginning of the current selection. Another day, maybe...

    Saturday, November 28, 2009

    Gradients: a Halftone Gradient

    So much to do: so little time! I have 3 other fun subjects to blog about... all of them in varying states of completion. But this is the most finished, so it's the first to write up.

    Halftone Gradients


    I don't know why, but I've always wanted a gradient that looked like halftoning.

    But for some reason whenever I thought of tackling it: I always envisioned myself writing a PaintContext and coming up with some ugly code that involved lots of varying radii, and antialiasing things by hand, and doing clever things with AffineTransforms and dot products that I'm not really well-versed in. And maybe that is the better/more efficient way to approach this subject... but there's a much easier way.

    My last blog entry used transformed tiles to achieve a multiple-color gradient. If you look at a halftone gradient as a tiling pattern, then suddenly it becomes a piece of cake. I just need to create a tall, skinny tile that transitions from one color to another.

    If I were a better blog author, I might try inserting a diagram here. I'd demonstrate exactly what the tile looks like, and maybe discuss the concept of halftoning a little bit more.

    However I'm not a better author. I'm much too lazy. Plus (and I'm not *entirely* bs-ing here): I really do believe a hands-on interactive model will explain this project more thoroughly than diagrams ever will. So... here's a demo applet showing off the HalftoneGradient class:



    The jar (source included) is available here.

    Your Basic Options


    Although I started with circles, it's even easier to make a tiled pattern using diamonds or isosceles triangles. The size of each shape scales in proportion to the width of the tile, and where in the gradient that shape is drawn.

    I'm unsure, though, if the circles are done correctly. I didn't find an exact algorithm for describing how halftoning should work -- but I admit I didn't look too closely. If a gradient is expressed in terms of t=[0,1]: does the gradient at t = .5 really look correct? Is that a half-way point between the two colors? For the circles I made the radius related to t^.8 to try to improve the balance. There is surely a more scientific way to approach this subject, but I didn't have the interest or patience. Does anyone have any experience with this subject? Basically the proportion of color1 to color2 should increase linearly with t.

    Oh, and as an afterthought I added the "offset" property, and the "animate" checkbox. How could I resist? (Have you seen my art? I love animating things.)

    Making a Tiled Pattern Not Tile


    This paint delegates all the hard work to a transformed TexturePaint, so it should be tiling like mad when the "Cycle" checkbox is unselected, right? This was probably the hardest part of the project: if a point lies outside of the strip created between P1 and P2: how do I make it a solid color?

    Originally I went to Sun's website and downloaded the source for the TexturePaintContext. I was determined to roll up my sleeves and make an adaptation that solved this. It didn't take me long to realize I was waaaaaaay over my head, though, as I looked through their source code. Aside from subtle problems (like referencing non-public classes to manipulate rasters), they somehow eliminated floats/doubles. Everything was int-based, on a scale of [0, Integer.MAX_VALUE]. While probably more efficient: this hurt my head. If you locked me in a dark prison cell with just bagels and a laptop: maybe in a few days I could figure this out. But I wanted to figure this out in a few hours, so I moved on to plan B:

    Instead I just rewrote the raster the TexturePaintContext returns. Is it the most efficient response? No. Does it work? Yes. The class is called CoveredContext. (It's non-public, although it's included in the jar mentioned above.) In its original draft the hard work looked like this:

    public Raster getRaster(int x, int y, int w, int h) {
    WritableRaster raster = (WritableRaster)context.getRaster(x, y, w, h);
    ...
    double[] matrix = new double[6];
    transform.getMatrix(matrix);
    for(int y2 = 0; y2 < h; y2++) {
    raster.getDataElements(0, y2, w, 1, data);

    for(int x2 = 0; x2 < data.length; x2++) {
    double x3 = x2+x;
    double y3 = y2+y;

    // apply the transform manually:
    y3 = matrix[1]*x3+matrix[3]*y3+matrix[5];

    if(y3 < 1) {
    data[x2] = color1;
    } else if(y3 >= distance-1) {
    data[x2] = color2;
    }
    }

    raster.setDataElements(0, y2, w, 1, data);
    }
    return raster;
    }

    The final draft, though, is much more mathematically clever. (It calculates the two x-points in a row of pixels that mark the beginning and end of the gradient, and flood fills everything beyond those points.) But it's basically the same approach in spirit. It involves many more special cases, though.

    Conclusion


    So there you have it. Very painless. The code, including comments and javadoc, probably comes to less than 500 lines of code. Eventually I plan to integrate this into some other projects I'm working on, but that will have to wait.

    I haven't tried to break this down and scrutinize performance yet. This class first needs to demonstrate that it, in fact, useful...

    Thursday, November 12, 2009

    Gradients: a Boring Discussion

    This really is a boring entry. In most of my articles I tried to include some sexy snippets of code, but this article is just an exploration of gradients and paints. There are no exciting conclusions. But I learned a lot, and in case you find yourself researching the same topic: here's what I found.

    The catalyst that started all this was when I started studying some old code at work. A few years ago we devised a series of complicated mechanisms to help optimize rendering gradients. (Most of them were somehow based on Vincent Hardy's Java 2D API Graphics book.) Did our tricks work? And if they did: do they still?

    In this entry when I say "gradient" I'm referring to what is now known as a MultipleGradientPaint. This is now standard in Java 1.6, but in earlier versions the only official Paint that came standard with Java is the GradientPaint (a 2-color gradient).

    One problem here is that there are, to my knowledge, only a couple of gradients to work with. There are the linear and radial gradients in Hardy's book. Then later on Batik came along with linear and radial gradients designed for SVG. Funny thing is: Vincent Hardy helped write these, too. (Along with Nicholas Talian, Jim Graham, and Jerry Evans.) Which makes sense, but for the sake of this article I'd like to have some really distinct classes to compare.

    A Simple Java 1.4-friendly Linear Gradient


    So my first priority was to make a really simple alternative gradient to test against.

    A few years ago Romain Guy pointed out you could improve performance with horizontal and vertical gradients if you used a TexturePaint instead of an actual gradient. And last year I combined TexturePaints with AffineTransforms in this article. So if you consider a linear gradient as basically a repeating tiled image... and you can transform that tile... then voila! You can make a GradientTexturePaint.

    This paint creates a 1-pixel tall tiled image, and then finds the appropriate AffineTransform to make that tile stretch the two control points you provide. Here is a demo. Use the slider at the top to control the colors, and the two white circles adjust the endpoints of the gradient:



    (You can download this demo, source included, here.)

    You can right-click the preview area to change the rendering hints. Using Sun's pipeline there is a visible difference when the interpolation hints are set to BILINEAR or BICUBIC. For convenience I also made the ANTIALIASING key affect the interpolation, too.

    Under Quartz, however, you have to set the INTERPOLATION, and define the COLOR_RENDERING. It doesn't matter what it is (SPEED or QUALITY), but it has to be defined. But the code I added to intercept the ANTIALIAS hint doesn't work... ugh. It's a wreck. Quartz is forever a mystery.

    Meanwhile, here is a screenshot contrasting the difference RenderingHints make:


    The biggest downside to this gradient is that it has to tile. (Unlike the GradientPaint class, which shows only one color beyond the two control points.) Also, as we see later: it's performance isn't great.

    But the advantages are:

    • It's Java 1.4 compatible. (Java 1.3, probably?)

    • It requires no other classes. It's very lightweight to add to your project.

    So. Problem #1 solved: I have at least one more gradient to compare.

    Comparing Gradients


    Let's focus on linear gradients. Here are the classes we can test against:
    1. com.sun.glf.goodies.GradientPaintExt from Hardy's book.

    2. com.bric.awt.GradientTexturePaint mentioned above.

    3. org.apache.batik.ext.awt.LinearGradientPaint from Batik.

    4. java.awt.LinearGradientPaint in Java 1.6+.

    5. DelegatePaint: this is a small shell wrapped around the java.awt.LinearGradientPaint. I wanted to see if the LinearGradientPaint was receiving special optimized treatment under-the-hood in the graphics pipeline.

    I put together several tests -- each test measures the median time and memory allocation required for an operation. The tests include:
    • Creating PaintContexts.

    • Calling Graphics2D.fillRect().

    • Calling Graphics2D.fill(Ellipse2D).

    • Performing these operations through a small AffineTransform.

    • Also compare what it costs to recycle a gradient vs constructing it from scratch.

    The test I used is here (source included). Here are the results after running it in a variety of environments.

    There's a lot of data. Oiyee. What does it mean?

    Creating Rasters vs Blitting


    For starters: look at the first two lines. This involves getting a PaintContext and calling PaintContext.getRaster(). Notice that these are considerably faster than the next couple of lines that involve calling Graphics2D.fillRect() for the same number of pixels. This brought me to my first sad realization about Paint objects: the bottleneck in performance is deep in the graphics pipeline. This makes sense, but it is frustrating because it's harder to modify that behavior. Werner has taken on the task of creating a new Graphics2D pipeline, but it's a big undertaking.

    Where does the rest of the time go? In the Sun pipeline (that is, when Quartz is not active), the time may look like this:


    The vast majority of the time is spent blitting. This seems especially unfortunate for simple calls to fillRect() (because the "hard" work is already done, right?). But it is what it is.

    Using Small AffineTransforms


    What about those lines that usually take 1-3 milliseconds? The tests with "reduced" in the name? These paint both rectangles and ellipses through a transform that scales the x and y coordinates by 0.2.

    The catch here is when Quartz is active: it doesn't do too well. This is where the "optimized" keyword comes in. Based on this research -- and some of the old code we had lying around work -- I added another method to the OptimizedGraphics2D class. Basically when using Quartz, and painting with anything other than a java.awt.Color, we get the raster from a PaintContext and then paint that as a BufferedImage. It looks something like this:


    PaintContext context = currentPaint.createContext(ColorModel.getRGBdefault(),
    rect, rect, getTransform(), oldHints);
    BufferedImage scratchImage = getScratchImage(rect.width, rect.height);
    WritableRaster paintRaster = (WritableRaster)context.getRaster(rect.x, rect.y, rect.width, rect.height);
    BufferedImage newImage = new BufferedImage(context.getColorModel(), paintRaster, false, null);
    Graphics2D g = scratchImage.createGraphics();
    g.setComposite(AlphaComposite.Clear);
    g.fillRect(0, 0, rect.width, rect.height);
    g.translate(-rect.x, -rect.y);
    g.transform( myTransform );
    g.setColor(Color.black);
    g.setComposite(AlphaComposite.SrcOver);
    g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
    RenderingHints.VALUE_ANTIALIAS_ON);
    g.fill(s);
    g.setTransform(identityTransform);
    g.setComposite(AlphaComposite.SrcIn);
    g.drawImage(newImage, 0, 0, null);
    g.dispose();
    drawImage(scratchImage,
    rect.x, rect.y, rect.x+rect.width, rect.y+rect.height,
    0, 0, rect.width, rect.height,
    null, null);
    newImage.flush();

    So we're painting the mask in black, and then using a SrcIn composite to render the fill of our paint.

    I'd recommend avoiding Quartz if you can. This is one of a handful of surprises we've found over the years, and Apple has been steadily pushing the Sun pipeline for some time now.

    Note that in these tests (in the GradientTest app), there is a crucial line involved in testing:

    OptimizedGraphics2D.testingOptimizations = true;

    This means the code I mentioned above is always being called. But in regular usage that method will only be called when Quartz is active. (The OptimizedGraphics2D is very careful about when it employs its special tricks.)

    What About the Delegates?


    Good news! The delegates are worthless. In Java 1.6 I wrapped the awt.LinearGradientPaint in a custom Paint object (and also wrapped their PaintContext, too). I know java.awt.Colors and java.awt.BasicStroke used to get special optimized treatment in Quartz (and maybe in Sun's pipeline?)... so I wanted to see if that was the case here. It isn't. This is great: it means what we see is what we get. And it means we can ignore those columns of data.

    What About Sharing?


    Here "sharing" refers to using a static gradient, instead of constructing a new gradient object in each test.

    In the "Java 1.5" and "Java 1.6" tabs, there is practically no difference in the time it takes between sharing and non-sharing tests. This is also welcome news. It means the Paint objects aren't doing any clever caching of their own, and it makes developers' lives easier.

    However. When Quartz is active, there's a speed improvement gained by sharing the gradient object. And the memory allocation? Wow. 0 bytes in some cases? Something special is going on here. And I have no idea what it is. Moving on. (Seriously, how am I supposed to cover all this material?)

    The Gradient Classes


    One of the most obvious comparisons is to look at the columns side-by-side. What stands out?

    The first thing that pops out at me: the GradientTexturePaint loses. It's not horrible, but it's always behind the other classes in terms of speed.

    Surprisingly in Java 1.6 the Batik class slightly outperform the awt class. I assumed the awt class was, well, based-on the Batik model. The code should be the same, right? Apparently not. Calling fillRect() is 25% faster when you use Batik.

    I find the memory allocation results are looking pretty shady here. The GradientTexturePaint takes 0 bytes to run some of these tests? I wouldn't be surprised if the tests/measured are somehow flawed. This doesn't make sense.

    Managing Memory


    Usually when I measure memory allocation, I add lines that call garbage collection before a sensitive block of code. But it turns out this can dramatically skew some results in this case: because of how some gradients are cached. (WeakReferences can get involved! Calling garbage collection unnecessarily destroys the cached raster they refer to.)

    The more I look at this, the more is looks like the Java2D architecture for Paints was poorly thought out. A Paint has one real method of interest: createContext(). (Already I'm scratching my head: why not make the method called getContext()? The distinction is that create() requires making a new object, and get() might simply return an existing object. This could have huge implications if you paint thousands of lines at varying transforms. But this is trivial compared to the next part...)

    A PaintContext has a magic method called getRaster(). It returns a Raster object. It looks simple enough, until you consider how it is used: you might be painting millions of shapes! Is each call to this method creating a new Raster? Rasters are huge; this can really affect how fast memory is burned through. Instead the method should accept a destination WritableRaster as an argument. This avoids the cost of creating new objects constantly. Also it clearly defines the role of the method. With the existing signature: I don't know what happens to a raster object when I hand it to the caller. Am I free to reuse it? Am I responsible for disposing it? The graphics pipeline may want to hang on to it until a certain number of rasters queue up, and then process it. (This would be especially useful if opaque shapes were rendering on top of each other: it may be the case that the first shape is completely obscured by latter shapes, so its raster is irrelevant. I'm not saying this is done: just that it's possible.)

    However "good" paints do not create rasters constantly. Batik paints, for example, have a static method: getCachedRaster(ColorModel cm,int w,int h). As long as you're asking for a width and height greater than 32 pixels: one static raster will be continuously recycled. They've been doing this for years and the world doesn't fall apart, so apparently it's a safe thing to do. So if you're writing your own PaintContext: recycle your rasters. Look at Batik's example. (They also dispose/release rasters in a clever way.) Their example is important because it's efficient, and because it obviously works with the Sun pipeline.

    Conclusions


    I don't really have any conclusions; I just felt like this article should end with a "Conclusions" section. :)

    The only useful things to come out of this are:

    • I added a new trick to the OptimizedGraphics2D class. However as I phase out Quartz more and more, it doesn't seem that important.

    • I made a light multiple-color gradient for Java 1.4. Since I strive to maintain backwards compatibility in most of my projects: this may quickly end up in my custom UI components.

    If you aren't worried about backwards compatibility: Java 1.6's gradient paints look perfectly sound. The Batik classes outperformed them in some cases, but overall I think they're a perfectly good choice if you're shopping around.

    I apologize for the haphazard way material was presented here, but there really is so much ground to cover. I could spent hours (and pages) studying the subtleties of the numbers I collected; it's hard to figure out exactly what is important and how to present it.

    If you'd like to see me add more tests, or have insight into the results already mentioned here: please be in touch. If you have specific questions that might be hard to answer through comments please email me directly.

    Monday, October 26, 2009

    AlphaComposites: Which Does What?

    It seems like every 4 months or so I come across a particular problem that I want to solve using AlphaComposites. The problem is I'm a very visual person; the equations listed in the javadocs don't help me visualize what each composite actually looks like.

    Here is an applet that demonstrates most of the possible variables at play:



    This applet (source included) is available here.

    Also there are several AlphaComposite types where it makes a huge difference whether you're rendering images vs shapes. In this applet if "Use Images" is selected then the shapes you see are first rendered in a BufferedImage, and then those images are rendered together.

    That's all. This is a clean, short article -- as much for my benefit as anyone else's.

    Friday, August 21, 2009

    Buttons: New UI's

    This article features some new ButtonUIs I've been working on.

    Coming from a strong background in Macs, the first UIs I wanted to emulate are the ones found here. But I abstracted some common patterns and made a generic model that can easily be adapted to other looks, too.

    Here's a demo applet showing off what I have so far. The UI's are displayed in a JInternalFrame so you can resize the window to see how they scale. (They're originally presented at their preferred size.)



    This app (source included) is available here.

    Why Bother?


    Why bother to make these UI's when Apple provided some convenient client properties to get the same buttons in Java? I have several possible reasons:
  • These are cross-platform. You can use them anywhere.
  • These are pure Java. This means you can override methods of interest. Also if you find a bug you can go exploring...
  • Resizing. It turns out -- last I checked -- that Apple's buttons are vector-based. (Try rendering them through an AffineTransform!) However: as JComponents they don't always resize well. (Especially the segmented variety.) If you have an icon that's just a couple of pixels larger than allowed: then those couple of pixels may just dangle off the edge.
  • Vertical segments. Although horizontal segments are much more common/important: why not offer vertical segments?
  • Java 1.4 compatible. (Probably even Java 1.3 compatible -- I haven't checked?) At work we strive to maintain backward compatibility for several years, so this is an advantage for us.

    As a disclaimer I should add: it is not my intention to make pixel-perfect replicas of any UI's. Instead my goal is to be "passable". There may be some improvements, and there be some regressions. If you have questions or comments, please let me know. I may be able to defend certain design decisions, or you may convince me to change something. :) But speaking of pixel-precision: I want to thank Werner for giving me some great advice for certain rendering problems I was having!

    Architecture


    After some discussion with Thasso, I decided to simplify the original architecture. Before there was too much emphasis on how to let subclasses define their own shape, and not enough flexibility with the fill.

    In this version the FilledButtonUI is the parent of all my other ButtonUIs. This object is responsible for the shape of the button area; the subclasses are only interested in the fill and in rendering.

    All the UI's can automatically receive Mac-like focus-rings either inside or outside the shape of the button (or both). In the demo, most of the rings are usually outside except in the 3x3 block of buttons: here the focus needs to be painted internally, because external focus wouldn't show in the the centermost cell. If you want to render a customized type of focus, you can override the getFocusPainting() method to return PAINT_NO_FOCUS. (This means that the FilledButtonUI isn't automatically painting focus for you, and you need to implement your own painting code somewhere else.)

    Client Properties


    In the demo above all the buttons share the same instance of one ButtonUI. The UI looks at client properties in the button to distinguish the shape. To set up a button on the bottom-left corner of a grid of buttons, you can call:
    myButton.putClientProperty( HORIZONTAL_POSITION, LEFT );
    myButton.putClientProperty( VERTICAL_POSITION, BOTTOM );

    (It is assumed if one of these properties is missing that the intended value is ONLY.)

    Also these UI's support an arbitrary shape. To create a circular button you can call:

    myButton.putClientProperty(SHAPE, new Ellipse2D.Float(0,0,100,100));

    (The shape will be scaled until the icon and text of a button fit inside it.)

    In the comments below Andre requested something similar to this feature. Personally I only really see myself using this feature for circles, but it is up to your discretion to decide if you think other shapes are a good fit for your UI.

    Thasso pointed out the current implementation requires that buttons be mutually exclusive in their layout: if two buttons are supposed to be nestled together in, say, a diamond-like pattern, then the FilledButtonUI is probably not a good choice. While that sounds like a fun design challenge to address (as far as programming is concerned), this project is already very large in scope and I don't want to take on more than I can handle.

    ButtonClusters


    You probably don't want to comb through your UI and set up client properties for each toolbar to get the correct segment positions. And even if you did: supposed you have a toolbar with buttons A, B, C and D, but then in some situations you made D invisible. At this point button C needs to become the right-most button in the set, instead of being a middle button. Updating this constantly seems like a real chore.

    The ButtonCluster object automates this for you. It works under a couple of assumptions, though. It does not directly affect the layout of your buttons: it assumes when you pass it an array of buttons that they will always be adjacent in your UI, and that they will be presented in that order. A ButtonCluster has no way of knowing if that is or is not the case; it assumes that part of your UI is unchanging.

    When you create a cluster you can ask it to be standardized or not. (This is largely in response to Michael's comments below.) If this boolean is true, then the FilledButtonUI will arrange each button in a cluster to have approximately the same width and height. This will result in larger GUI components, but with a more balanced look.

    Also be sure to check out the static install() methods in the javadoc: that may be the only method you need to call from your code to incorporate this project into your app.

    Embellishments


    Extras. Eye candy. I added a few to the demo applet just to demonstrate how easy they are.

    The blinking focus is built into the FilledButtonUI, and probably required about 20 lines of code. This is a feature a user once requested for accessibility reasons: users who can't easily use a mouse largely rely on keyboard focus to navigate software, but sometimes the focus is so subtle in a crowded interface that it's hard to spot. The blinking really helps the human eye to find the area of interest.

    The other two extras (shimmer and zoom) are UIEffects. This is a very simple but fun little class that the FilledButtonUI supports when rendering a button. In a way this model is my attempt to compensate for not having multiple inheritance: I can make easy little effects that I can apply to any and all FilledButtonUI subclasses. (And remember Java2D's most basic drawing tools can go a long way if you're creative.)

    Keyboard Focus


    Speaking of keyboard focus, that reminds me (can you tell that writing this article is very much a stream-of-consciousness process?)... I'm also very pleased with the FocusArrowListener. This listens for arrow keys, and shifts the focus accordingly when a key is pressed. It's a very simple concept, but I think it should be applied to most GUI elements that do not already have defined behaviors for the arrow keys. JToolBars already do something very similar: pressing the the left and right arrow keys is generally equivalent to pressing tab and shift-tab. (Except when you get to one extreme of a JToolBar the focus will wrap around to the other side.)

    Again: thanks goes to Werner for some helpful tips on this element.

    Conclusion


    The incentive for this project was to get some consistent aesthetic UI's to use in our desktop applications. With that goal in mind I put together what I hope is a relatively adaptable model for all sorts of other button UI's to work from.

    I've spent a lot of time on this in the last few weeks and I need to move on for now. If you have improvements/fixes you want to make: the code is in the jar (and available on an SVN server), so you're welcome to do whatever is necessary. And if you email me afterwards I might be able to incorporate the changes into the official release.

    As of this writing the features I would most like to add are HTML-support (in text) and a FadingUIEffect. Neither of these should require overhauling anything major: but they are outside my current needs.

    Feel free in the comments to talk about your favorite ButtonUI's from other sources/L&F's, and what makes them your favorite.