 |
BeanShell + 2D = Instant Graphics
Posted by zixle on July 11, 2006 at 03:45 PM | Comments (17)
Over the years I've worked on a number of projects that involved
various 2D rendering operations. The usual cycle for such work is to
tweak rendering code, compile, run, examine the results using a
magnifier, and repeat until I'm happy with it. This certainly works,
but takes a bit longer than is ideal. When I needed to do a lot of
graphics tweaking for this years 'Extreme GUI Makeover' talk, I
figured it was time to see if I could streamline this process. This
blog shows what I've concocted.
I was looking for something that would allow me to get rid of the usual
compile and run cycle; I want instant feedback! More often than not I
need to zoom in on the results. As such, a magnifier would have to be
included. In the end I want Java code, so alternative scripting
languages aren't an option.
Thankfully I stumbled upon Bean
Shell. BeanShell fit my needs perfectly! With BeanShell you can
evaluate blocks of Java code; just what I was looking for. Evaluating
raw Java code, for my purposes, is hardly interesting with out the
ability to supply state to the interpeter. BeanShell allows you to
supply state to the java code it executes. In this way I can pass a
graphics object to the interpeter that is available to the java code
that is executed. Putting all the pieces together enables me to
execute arbitrary Java code, rendering the results to an Image and
showing that Image using Swing components. Just what I wanted.
Here's the resulting application. For reasons I did not investigate
BeanShell won't run in the sandbox. As such, the application is signed
and needs to live outside the sandbox. Also note the application
stores the script in the file ~/.igeScript.
How does it work?
As you type in the text area a timer is started. When the timer fires
(~1 second) the script is evaluated in a background thread using
BeanShell. The intepreter is passed three variables: the width of the
image, the height of the image, and a graphics obtained from an image
of the specified size. If the interpeter successfully evaluates the
script, the image is rendered in two places. One shows the results at
actual size, the other scaled.
One of the first things you'll notice is the text area does no
completion for you. This is most definitely annoying! Ideally the
editor would behave just like that of NetBeans. As such, a better fit
would be to turn this into a NetBeans module. That way NetBeans would
provide all the completion, syntax highlighting, and error reporting
for free. Nice! If I have time, I'll do this; but don't hold your
breath;)
In my first take at the app I evaluated the script nearly immediately,
and on the EDT. This worked perfectly, until I typed in something
like:
while (x < y) {
}
As I was evaluating the script almost immediately, on the EDT, this
caused an infinite loop and I was screwed. This very rarely comes in
every day coding as you don't compile and run while typing. Not the
case here.
I was hoping BeanShell would allow me to stop the intepreter, but it
doesn't. To solve this problem I moved evaluating the script to a
separate thread. If the evaluation takes more than than 3 seconds, an
option pane is shown asking if the background thread should be
killed. To kill the background thread I use Thread.stop. This is
obviously bad, and can cause problems, but is better than causing the
app to be wedged...
Running the app
Try changing a couple of the variables at the top of the file to see
the results. For example, try changing TEXT_COLOR to
Color.RED and notice the effects render
immediately. Trying changing the KERNEL_SIZE and
BLUR_FACTOR to see the results on the drop shadow.
You can of course delete all the code and start a new.
Random other things
The app uses Hans's MultiSplitPane
to house the components. This makes it easy to resize the app to fit
your needs.
I'm also using an early prototype of beans
binding to keep the model in sync with the width, height, scale
and text area. I'm still working on the legal issues of releasing this
code. I'll do a blog once it's all straightened out.
I'm using a variant of the application framework I had been developing
in my blog series on architecting
applications. I will return to the application framework series,
but will stop development of the pieces that overlap with Hans's JSR
296: Swing appliction framework.
Full source can be found here.
-Scott
Bookmark blog post: del.icio.us Digg DZone Furl Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment
-
What happened to the architecture series? Last time we heard from it was back in March...
Posted by: kirillcool on July 11, 2006 at 03:55 PM
-
What happened to the architecture series?
I will try and get a blog out on cut/copy/paste for my next blog.
-Scott
Posted by: zixle on July 11, 2006 at 03:59 PM
-
Goes without saying - a very interesting technique... This would apply, of course, only for self-containing (or should i say simplistic) examples such as above. For real life scenarios, how would you test a method that is a part of pipeline? In such cases it's sometimes faster to compile, run and see the result. However, this tool can be used to create a good Java2D tutorial, especially for composites, brushes and gradients.
Posted by: kirillcool on July 11, 2006 at 04:40 PM
-
Totally rad. I've been lusting after this since you gave me a peek last month, so thanks for sharing with the world. One minor comment: on OS X, it looks like the "scaled" view is filtered by default. Could you add an explicit NEAREST_NEIGHBOR before scaling the image? That way it'll behave more like xmag, which is helpful if you're fiddling with individual pixels.
Chris
Posted by: campbell on July 11, 2006 at 05:00 PM
-
Its possible that the BeanShell is bombing in the sandbox because of its use of reflection. The SecurityManager could be saying 'no no no'.
leouser
Posted by: leouser on July 11, 2006 at 05:08 PM
-
Awesome! Come on Tim B., make this a NB module :-)
Posted by: rbair on July 11, 2006 at 05:43 PM
-
Awesome indeed. Too bad it doesn't start.
Click 1: The "Launch" button, in this web page
Click 2: The "Yes" button, in the certificate dialogue
...and then I get this:
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at com.sun.javaws.Launcher.executeApplication(Unknown Source)
at com.sun.javaws.Launcher.executeMainClass(Unknown Source)
at com.sun.javaws.Launcher.continueLaunch(Unknown Source)
at com.sun.javaws.Launcher.handleApplicationDesc(Unknown Source)
at com.sun.javaws.Launcher.handleLaunchFile(Unknown Source)
at com.sun.javaws.Launcher.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.StackOverflowError
at java.io.WinNTFileSystem.canonicalize0(Native Method)
at java.io.Win32FileSystem.canonicalize(Unknown Source)
at java.io.File.getCanonicalPath(Unknown Source)
at java.io.FilePermission$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.io.FilePermission.init(Unknown Source)
at java.io.FilePermission.(Unknown Source)
at java.lang.SecurityManager.checkRead(Unknown Source)
at java.io.File.exists(Unknown Source)
at sun.security.provider.PolicyFile$2.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at sun.security.provider.PolicyFile.initPolicyFile(Unknown Source)
at sun.security.provider.PolicyFile.initPolicyFile(Unknown Source)
at sun.security.provider.PolicyFile.init(Unknown Source)
at sun.security.provider.PolicyFile.(Unknown Source)
at sun.reflect.GeneratedConstructorAccessor16.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
at java.lang.reflect.Constructor.newInstance(Unknown Source)
at java.lang.Class.newInstance0(Unknown Source)
at java.lang.Class.newInstance(Unknown Source)
at java.security.Policy.getPolicyNoCheck(Unknown Source)
at java.security.ProtectionDomain.implies(Unknown Source)
at java.security.AccessControlContext.checkPermission(Unknown Source)
at java.security.AccessController.checkPermission(Unknown Source)
... and 1000 lines more of what seems to be a really huge stack trace
Posted by: misuasm on July 12, 2006 at 05:09 AM
-
misuasm,
Awesome indeed. Too bad it doesn't start.
What machine and VM are you running?
-Scott
Posted by: zixle on July 12, 2006 at 06:42 AM
-
This would apply, of course, only for self-containing (or should i say simplistic) examples such as above.
All true, which is what I primarily wanted this for. In an ideal world I could some how highlight a method, or chunk of methods in a component that were to be automatically rendered as I modify them. It would be tricky to identify all the dependencies, but certainly worthy of some investigation.
-Scott
Posted by: zixle on July 12, 2006 at 06:43 AM
-
Scott,
I am running Sun Java Version 1.5.0 (build 1.5.0_02-b09) on a Pentium4 machine with Windows 2000 SP4. I really don't understand why it crashes. Other apps work just fine. Running javaws and then launching the application from there doesn't work either.
Posted by: misuasm on July 12, 2006 at 06:55 AM
-
misuasm,
Could you email me the complete stack please. Email address is scott dot violet at sun dot com .
You might try upgrading to the latest 1.5.0 release. 1.5.0_02 is a bit dated by now.
Thanks!
-Scott
Posted by: zixle on July 12, 2006 at 08:33 AM
-
Wow! This is quite amazing application! You should donate your code to Netbeans 6.0! I can see how it can be an incredibly popular and powerful tool for Netbeans. When I first read the description I was quite skeptical...but I tried the webstart and it worked like a charm! Amazing!! I am gonna check out the source code!
Posted by: suryad on July 12, 2006 at 08:14 PM
-
Quite a cool demo :)
By the way, about Swing and latest Java (1.6.0) is it planned to update the Swing API and introduce Genericity everywhere (where applicable) ?
Example of this :
javax.swing.ComboBoxModel,
javax.swing.ListModel
item objet type should be parameter of class
java.util.EventObject :
source objet type should be parameter of class
java.beans.PropertyChangeEvent
value objet should be parameter of class
javax.swing.ComboBoxEditor
item objet type should be parameter of class
javax.swing.ListCellRenderer
javax.swing.Renderer
javax.swing.SpinnerModel
value objet type should be parameter of class
MutableComboBoxModel
element objet type should be parameter of class
etc... (lots and logs of classes including "Default" classes should be generified IMHO).
Best Rgs,
JB
Posted by: bjb on July 22, 2006 at 03:54 PM
-
bjb,
Thanks for the kind words! We did not have time to fullify generify Swing in mustang. Look for parts of that to happen for dolphin.
-Scott
Posted by: zixle on July 24, 2006 at 10:55 AM
-
Great tip!
We are using Beanshell to adjust the layout of Swing components in runtime. Such task, specially in rich client applications, demands a lot of change-compile-run cycles to reach a good visual quality as expected.
The hability to adjust layout in runtime is a sound productivity improvement.
Posted by: dlouzado on July 26, 2006 at 07:03 PM
-
I came to this page via the new Java 6 splashscreen blog, so that is why I revamp the discussion.
Have you got this designer in an application form or do I need to extract it out fo the JNLP? I would be great if I could start / use it stand alone and access my Swing libraries. (Got all these paint and draw methods in there.)
Posted by: tbee on February 05, 2007 at 03:50 AM
-
tbee,
Full source is available, so if you need to customize it, go for it!
-Scott
Posted by: zixle on February 05, 2007 at 09:04 AM
|