Live Shapes transformations

Hi all,

I’m writing a vector based draw program. I use Shape as common type for my draw elements: Rectangle, Oval, and GeneralPath for freehand drawing. User can also move, rotate and resize such elements in wysiwyg fashion.
When he drags the mouse to modify an element, I update the canvas.
My two problems are:

[] the amount of cpu it takes to render elements to the canvas (30% CPU on win2k AthlonXP1900+ 512Mb)
[
] the gc that pauses every 4 seconds the rendering process (I’m using jdk 1.4.1_03)

Can you please help me ?

Here is the rotation code:


public final void rotate(Drawable d, double a) {
            DrawableState state = d.state;
            Rectangle actualWorkingBounds = state.actualWorkingBounds;
            state.rotation += a;
            trans.setToIdentity();
            trans.rotate(a, state.center.getX(), state.center.getY());
            d.working.transform(trans);
            actualWorkingBounds.setRect(d.working.getBounds());
            state.location.setLocation(actualWorkingBounds.getX(), actualWorkingBounds.getY());
      }

where d.working is a GeneralPath and trans is an AffineTransform.

Here is the rendering code (inside a Canvas subclass):


public final void repaintNow(Rectangle clip) {
   int x, y, w, h;
   Graphics2D g = buffer.createGraphics();
   g.setClip(clip.x,clip.y,clip.width,clip.height);
   g.setColor(Color.white);
   g.fillRect(clip.x,clip.y,clip.width,clip.height);
   g.setRenderingHints(renderingHints);
   for(int k = size; k >= 0 ; k--) {
      if(controller.getTotalBounds(draws[k]).intersects  (clip)) {
         draws[k].paint(g);
      }
   }
   g.dispose();
   getGraphics().drawImage(buffer, 0, 0, null);
   try{ Thread.sleep(14); } catch (Exception exn){}
}

where buffer is a BufferedImage created with:


buffer = getGraphicsConfiguration().createCompatibleImage(dimension.width, dimension.height);

When user drags I call the rotate code and the repaintNow one.

Thanks A LOT guys.

Many questions… hopefully leading to something helpful:

How many Shapes are you drawing when you see the 30% CPU load? How much does adding a shape affect this?
Have you tried incremental garbage collection? (-incgc)
Why do you have a sleep() in your repaintNow routine?
When are you calling repaintNow?
Are you using Swing?
Have you tried calling it only from paint(Graphics g) and simply requesting repaints when the user draws?
Have you disabled Swing’s double buffering for this component, since you are doing your own?

First, answers:

[] 300 shapes
[
] No, can you explain me what differences I’ll see if I use incgc ?
[] I call sleep because if I don’t, dragging takes 99% of cpu and I need some cpu (30%) to do other things (streaming audio/video)…
[
] I call repaintNow every time “mouseDragged” is called
[] yes I use Swing
[
] I’ve noticed that calling repaint is not as accurate as I need… sometimes the shape remains 2-3 pixels far from cursor when user stops dragging
[*] yes, I disabled it

Now, what I done to solve my problems:

[] now buffer is a VolatileImage (createCompatibleVolatileImage)
[
] I’ve removed all Point and Rectangle instances. Now I use only plain float variables
[] Now I drawImage only the dirty region from the buffer to the screen (I use the “drawImage” with 11 args)
[
] Now I use AWT (a Canvas)

with such modifications I’ve obtained 10% cpu with 300 arbitrary shapes (very complex freehanded shapes) and (surprise) the gc is sleeping for all the time.
I think that the use of Rectangle and Point was my real problem in the transformation methods (rotate, translate,…)

BUT, I’m not convinced that this is the BEST performance I can obtain… so if you want to give me some help, I’ll post the code.

Thanks a lot !

-incgcc would have spread out the GC work so you didn’t get that huge stop-the-world pause. It looks like that isn’t happening now so just keep it in mind if GC pauses return.

[quote]I’ve noticed that calling repaint is not as accurate as I need… sometimes the shape remains 2-3 pixels far from cursor when user stops dragging
[/quote]
This is something I have never seen. My first thought would be that there was a bug in your redraw code.
By using repaint() you might avoid a few paints because multiple repaint requests could be collapsed into a single one. This is specially true while the user is dragging the mouse and generating a lot of repaints.

Does the sleep cause the mouse motion to be less smooth or lagged?

You could do something else to put a cap on how often repaint is called so that drawing doesn’t hog the CPU from your other operations. Use a timer maybe to trigger repaints, stop the timer if there hasn’t been any changes to the shapes since the last repaint.

Have you profiled the code?
-Xprof

Can you upgrade to 1.4.2?

Getting rid of many temporary objects like Point and Rectangle is good in general, in terms of GC. You might also choose to reuse objects of this type, particularly with APIs that give you the option of supplying a Point to be filled in as opposed to needing to create a new one to return the data.

With buffer being Volatile you should be careful about the rendering hints you use… anti-aliasing might cause reads from the video memory which are very slow, it could be better to not try accelerating this image, depending on how it is used.

I’ll see if the “repaint” problem is a bug in my code… if so I’ll switch to repaint so that, as you told, multiple paints can collapse.

Well, 1.4.2 gives me 2 problems:

[] a bug in an image loading for filechooser (this bug prevents the application to start-up as I load everything on startup): I think it’s some corruption of rt.jar (I’ve downloaded it 3 times, so now I’ll wait 1.5… :frowning: )
[
] some strange bug in Socket streams: with the same code I have Exceptions if I use 1.4.2 and all goes right if I use 1.4.1_03… code is really tested and I’m quite sure is a jre bug (application terminates with an exception outside the jvm)

About the Point/Rectangle question… I think that accessing a plain variable is way faster than calling a method or accessing a public member. If you think how many times you have to do it in a 30 fps animation…
I know this difference is not the real reason of a performing application BUT if I can do it better I do that way.

In your opinion, can I take some advantage if I use Volatile only with non transparent/non antialiased shapes as a sort of “triple buffer”?

Last point…
I want to reimplement GeneralPath so that I can expose the vector of coordinates (I want to manipulate it one by one, as in every vector draw application!), but I cannot/I don’t want to reimplements 2 classes it uses: Crossings and Curves: these classes do the REAL work in methods like “intersects”… do you know a library (java2d compatible) that I can use ?

Real last point:
I’ve read many posts about BufferStrategy and it’s use in games, where the scene update occour in a loop and is not event-driven. Do you think such thing can help me ?

2^180 Thanks !

I honestly don’t know the answers to most of your other questions, but I was going to mention the idea of BufferStrategy and a game-loop style interface. The reason being that you can more easily control your frame rate and therefore make sure that you are giving enough time to the A/V Streaming. In fact the video that you are streaming might fit well with that style.

Volatile image might be a benefit if you don’t have the antialiasing… I would try to get some real measurements.

As for the GeneralPath - Would you be able to use a substitute even if you could make one? The insides of Shape aren’t going to construct one of your path objects for you. It’s also a final class so you can’t even make a subclass to peek at protected data or override some methods to intercept data.

your really need access to these members. If you made a class in the same package you could read them directly… but making stuff in the java.* package is a security problem.


    byte[] pointTypes;
    float[] pointCoords;
    int numTypes;
    int numCoords;

there might be a reflection trick to use to access those fields.

Probably best though to maintain the control points in your own data structure.

You’re right BUT GeneralPath is nothing more than an implementation of Shape… so if I have these functionalities of Crossings and Curves, I can can implement Shape by myself :))))

Well, I’ll do some testing about “repaint” and I’ll post the results.

thanks.

Ah, implement the Shape interface yourself. More work, but it could pay off.