<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-406997977734348535</id><updated>2012-02-01T02:34:41.753-08:00</updated><category term='images'/><category term='toolkit'/><category term='prompt'/><category term='QTJ'/><category term='suggestion'/><category term='decode'/><category term='inspector'/><category term='Parametric graph'/><category term='measurement'/><category term='dial'/><category term='robot'/><category term='events'/><category term='scaling'/><category term='cocoa'/><category term='charcoal'/><category term='indication'/><category term='mouse'/><category term='distance'/><category term='drag'/><category term='imperial'/><category term='video'/><category term='jcolorchooser'/><category term='Mac 10.5'/><category term='layoutmanager'/><category term='gradient'/><category term='colorpicker'/><category term='transform'/><category term='paint'/><category term='decoder'/><category term='aqua'/><category term='JPEG'/><category term='graphics'/><category term='format'/><category term='memory'/><category term='unmanaged'/><category term='PixMap'/><category term='text'/><category term='panic'/><category term='mac'/><category term='Shape'/><category term='quadratic'/><category term='AlphaComposite'/><category term='power'/><category term='design'/><category term='java2d'/><category term='MOV'/><category term='Crossfade'/><category term='BufferedImage'/><category term='calligraphic'/><category term='exif'/><category term='flattening'/><category term='pencil'/><category term='Vista'/><category term='comment'/><category term='AWT'/><category term='gradients'/><category term='prompts'/><category term='length'/><category term='system properties'/><category term='interface'/><category term='AffineTransform'/><category term='internationalization'/><category term='serializable'/><category term='QuickTime'/><category term='Event Dispatch Thread'/><category term='encoder'/><category term='rectangle'/><category term='Composite'/><category term='animation'/><category term='codec'/><category term='search field'/><category term='animate'/><category term='transitions'/><category term='drag-and-drop'/><category term='image'/><category term='JTextArea'/><category term='menu'/><category term='usability'/><category term='antialias'/><category term='focus'/><category term='screen'/><category term='math'/><category term='JTextField'/><category term='artistic'/><category term='button'/><category term='degree'/><category term='freehand'/><category term='properties'/><category term='click'/><category term='color palette'/><category term='print'/><category term='slider'/><category term='Area'/><category term='custom stroke'/><category term='paintcontext'/><category term='slideshow'/><category term='JProgressBar'/><category term='frame'/><category term='JSpinner'/><category term='JOptionPane'/><category term='discussion'/><category term='angle'/><category term='dialog'/><category term='roundrect'/><category term='swing'/><category term='unit'/><category term='alignment'/><category term='JPG'/><category term='indicator'/><category term='bmp'/><category term='PathIterator'/><category term='halftone'/><category term='PixelGrabber'/><category term='print dialog'/><category term='JComponent'/><category term='cubic'/><category term='halftoning'/><category term='render'/><category term='window'/><category term='tiles'/><category term='metric'/><category term='performance'/><category term='MediaTracker'/><category term='pixels'/><category term='webcam'/><category term='megapixel'/><category term='toolbar'/><category term='vectorize'/><category term='thumbnail'/><category term='gui'/><category term='curve'/><category term='movie'/><category term='Color'/><category term='custom'/><category term='Graphics2D'/><category term='vector graphics'/><category term='colorpalette'/><category term='capture'/><category term='widget'/><category term='look-and-feel'/><category term='Optimization'/><category term='buttonUI'/><category term='articles'/><category term='plaf'/><category term='SystemColor'/><category term='shadow'/><category term='public'/><category term='QDGraphics'/><category term='XP'/><category term='bounds'/><category term='apple'/><category term='PNG'/><category term='load'/><category term='text components'/><category term='client property'/><category term='preference'/><category term='graph'/><category term='bevel'/><category term='effects'/><category term='picker'/><category term='find'/><category term='panel'/><category term='color chooser'/><category term='2D'/><category term='layers'/><category term='layout'/><category term='height'/><category term='managed'/><category term='vector'/><category term='UIManager'/><category term='NSImage'/><category term='idea'/><category term='bristle'/><category term='programming'/><category term='tutorial'/><category term='customize'/><category term='Java'/><category term='font'/><category term='blog'/><category term='AreaX'/><category term='clipping'/><category term='brush'/><category term='GeneralPath'/><category term='TexturePaint'/><category term='vectorizer'/><category term='runtime'/><category term='Shapes'/><category term='search'/><category term='input device'/><category term='stroke'/><category term='calligraphy'/><category term='bezier'/><category term='progress'/><category term='deadlock'/><title type='text'>Java Graphics: Code Snippets, Musings, and Discoveries</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>57</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-74630044016640289</id><published>2011-05-05T01:32:00.000-07:00</published><updated>2011-05-15T19:54:13.406-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scaling'/><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='image'/><category scheme='http://www.blogger.com/atom/ns#' term='megapixel'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='PNG'/><category scheme='http://www.blogger.com/atom/ns#' term='thumbnail'/><category scheme='http://www.blogger.com/atom/ns#' term='memory'/><category scheme='http://www.blogger.com/atom/ns#' term='JPG'/><category scheme='http://www.blogger.com/atom/ns#' term='JPEG'/><title type='text'>Images: Scaling JPEGs and PNGs</title><content type='html'>&lt;h3&gt;The Problem&lt;/H3&gt;Images -- especially JPEG images -- can be huge.&lt;br /&gt;&lt;p&gt;Last weekend for example: I went to a Demetri Martin booksigning.  I got my photo taken with the author with my phone.  I tried to upload it, and I was surprised to see it was 800+ KB, at about 2500 x 2000 pixels.&lt;br /&gt;&lt;p&gt;On the one hand: this is great, right?  I had no idea my phone had that level of resolution.&lt;br /&gt;&lt;p&gt;On the other hand: can your Java app effortlessly open up and work with this image?  Ours can't.  We have a painting application where the canvas size is around 1,000 pixels.  The problem is: we have to open the JPEG at its original size and then scale it down:&lt;br /&gt;&lt;br /&gt;(2500 pixels wide) x (2000 pixels tall) x (4 bytes*) = 19 MB&lt;br /&gt;&lt;p&gt;... but what we ultimately want is a 1000 x 800 pixel image: about 3 MB.  We have to churn through 22 MB of memory to get what we want.  And while this image is bigger than I expected, it's not "that big".  Our testing team found an image to test with that's 20,000 x 15,000 pixels.  That's 1.1 GB of memory uncompressed.  Our app can't spare 1.1 GB of RAM to open (or create a thumbnail of) an image.&lt;br /&gt;&lt;p&gt;(This article is a little longer than I'd like, but I promise it ends well.  Feel free to skip to the charts below if you're in a hurry.)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;*I used 4 bytes because although a JPEG is opaque and only requires 3 bytes, in our apps we nearly always convert it to a TYPE_INT_RGB.  This is backed by an int-based raster, so whether we use the 4th byte or not: it's allocated.&lt;br /&gt;&lt;h3&gt;The Context&lt;/H3&gt;I was still in college when Java 1.4 was released.  But even then I remembered discovering the &lt;code&gt;&lt;a href="http://download.oracle.com/javase/1.4.2/docs/api/javax/imageio/ImageIO.html"&gt;ImageIO&lt;/a&gt;&lt;/code&gt; classes: why would anyone use anything else?  In fact when I did start work a year or two later: I had to support some legacy Java 1.3 code.  Loading &lt;code&gt;&lt;a href="http://download.oracle.com/javase/1.4.2/docs/api/java/awt/Image.html"&gt;java.awt.Image&lt;/a&gt;&lt;/code&gt; objects instead of &lt;code&gt;&lt;a href="http://download.oracle.com/javase/1.4.2/docs/api/java/awt/image/BufferedImage.html"&gt;java.awt.image.BufferedImage&lt;/a&gt;&lt;/code&gt; objects seemed ridiculous: why would you want to use an &lt;code&gt;Image&lt;/code&gt; when you could use a &lt;code&gt;BufferedImage&lt;/code&gt;?&lt;br /&gt;&lt;p&gt;But now I've come to the conclusion that the &lt;code&gt;Image&lt;/code&gt; class is hugely underrated.  (And I'm surprised to see it still does its job so well, several years later when I really needed it.)&lt;br /&gt;&lt;h3&gt;The Existing Architecture&lt;/H3&gt;The &lt;code&gt;Image&lt;/code&gt; architecture is intentionally abstract -- and perhaps intentionally cryptic.  There are 3 important classes related to images:&lt;br /&gt;&lt;li&gt;&lt;code&gt;&lt;a href="http://download.oracle.com/javase/1.4.2/docs/api/java/awt/image/ImageObserver.html"&gt;ImageObserver&lt;/a&gt;&lt;/code&gt;: this simply receives updates &lt;i&gt;about&lt;/i&gt; an image.  Things like the width and height, and whether the image is fully loaded.&lt;br /&gt;&lt;li&gt;&lt;code&gt;&lt;a href="http://download.oracle.com/javase/1.4.2/docs/api/java/awt/image/ImageProducer.html"&gt;ImageProducer&lt;/a&gt;&lt;/code&gt;: this is the big hairy abstract entity that actually creates pixel data.  Every &lt;code&gt;Image&lt;/code&gt; has an &lt;code&gt;ImageProducer&lt;/code&gt; behind it.&lt;br /&gt;&lt;li&gt;&lt;code&gt;&lt;a href="http://download.oracle.com/javase/1.4.2/docs/api/java/awt/image/ImageConsumer.html"&gt;ImageConsumer&lt;/a&gt;&lt;/code&gt;: this listens to an &lt;code&gt;ImageProducer&lt;/code&gt; and makes sense of the incoming data.  We could use this to construct a &lt;code&gt;BufferedImage&lt;/code&gt;, for example.  This is the object we're interested in for this article.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The Solution&lt;/h3&gt;&lt;br /&gt;What I want to do now is devise an &lt;code&gt;ImageConsumer&lt;/code&gt; that does &lt;i&gt;not&lt;/i&gt; buffer an image in memory: instead it pipes the pixel data through efficiently and scales the image in one pass.&lt;br /&gt;&lt;p&gt;There is a very important red flag here: according to the very loose relationship between an &lt;code&gt;ImageProducer&lt;/code&gt; and an &lt;code&gt;ImageConsumer&lt;/code&gt;: the pixels may be delivered in &lt;i&gt;any order&lt;/i&gt;.  They could be tiles.  They could come in non-continuous rows.  You don't know.  It so happens in all my tests that the default JPEG decoder does deliver the pixel data in a single pass -- one row at a time.  But this is not guaranteed, and it may change at any time.  (This is a behind-the-scenes detail that you don't have any control over unless you write your own JPEG reader.)  I'll come back to this point later.&lt;br /&gt;&lt;p&gt;To address the vague contract of the &lt;code&gt;ImageConsumer&lt;/code&gt;: I wrote the &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/PixelIterator.html"&gt;PixelIterator&lt;/a&gt;&lt;/code&gt; for my own use a while ago: it promises pixel data will be delivered one consecutive row at a time.  (Either top-to-bottom or bottom-to-top.)  Then I used this class in &lt;a href="http://javagraphics.blogspot.com/2010/06/images-scaling-down.html"&gt;this article&lt;/a&gt; to dynamically scale images: the &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/ScalingIterator.html"&gt;ScalingIterator&lt;/a&gt;&lt;/code&gt; filters a large image and scales it on-the-fly to a smaller set of dimensions.  The resulting image is antialiased, although the rounding is vague and not strictly adhering to any set of interpolation rules.&lt;br /&gt;&lt;p&gt;So what is missing in this system is the class that converts a &lt;code&gt;java.awt.Image&lt;/code&gt; to a &lt;code&gt;PixelIterator&lt;/code&gt; so the &lt;code&gt;ScalingIterator&lt;/code&gt; can process it.  To fill this niche I wrote the &lt;code&gt;&lt;a href="https://java.net/svn/javagraphics~svn/trunk/src/com/bric/image/pixel/GenericImageSinglePassIterator.java"&gt;GenericImageSinglePassIterator&lt;/a&gt;&lt;/code&gt;.  The &lt;code&gt;ImageProducer&lt;/code&gt; passes data to this object in one thread, and as calls to &lt;code&gt;myIterator.next(...)&lt;/code&gt; are made this data is processed and passed on to the caller.  The &lt;code&gt;ImageProducer&lt;/code&gt; thread is blocked as pixel data is released until that data is requested: this guarantees data is not kept in memory unnecessarily.  (Either thread will timeout after 5 seconds of no feedback, though, and report than an error has occurred.)&lt;br /&gt;&lt;h3&gt;Results&lt;/h3&gt;&lt;br /&gt;I wrote a class called &lt;code&gt;&lt;a href="https://java.net/svn/javagraphics~svn/trunk/tests/com/bric/image/ScalingJPEGDemo.java"&gt;ScalingJPEGDemo&lt;/a&gt;&lt;/code&gt; to demonstrate this class.  It uses &lt;a href="https://java.net/svn/javagraphics~svn/trunk/tests/com/bric/image/resources/bridge3.jpg"&gt;this image&lt;/a&gt; from a recent vacation.  This image is 2750 x 2063 pixels, or 21 MB uncompressed.&lt;br /&gt;&lt;p&gt;I wrote a handful of other scaling methods to compare against.  In the &lt;code&gt;ScalingJPEGDemo&lt;/code&gt; class I defined this interface:&lt;br /&gt;&lt;pre&gt;static interface ScalingReader {&lt;br /&gt; public Image create(Dimension originalSize,Dimension maxSize) throws Throwable;&lt;br /&gt; &amp;nbsp; public String getName();&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;(The new code I developed for this project can handle incoming images much larger than 2750 x 2063, but some of the other contenders can't.)&lt;br /&gt;&lt;p&gt;These are the different readers I compared:&lt;br /&gt;&lt;img src="http://javagraphics.java.net/resources/singlePassReader100.png" align="right"&gt;&lt;h4&gt;SinglePassReader&lt;/h4&gt;&lt;p&gt;This is the new class I wrote (described above).  It is fast and antialiased, but most importantly it leaves a low memory footprint.&lt;br /&gt;&lt;pre&gt;ScalingReader singlePassReader = new ScalingReader() {&lt;br /&gt; public Image create(Dimension originalSize,Dimension maxSize) throws Throwable {&lt;br /&gt; &amp;nbsp; URL url = getImageURL();&lt;br /&gt; &amp;nbsp; return GenericImageSinglePassIterator.createScaledImage( url, maxSize);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getName() {&lt;br /&gt; &amp;nbsp; return "singlePassReader";&lt;br /&gt; }&lt;br /&gt;};&lt;/pre&gt;&lt;img src="http://javagraphics.java.net/resources/scalingGraphicsReader100.png" align="right"&gt;&lt;h4&gt;scalingGraphicsReader&lt;/h4&gt;&lt;p&gt;When I searched online, this is one of the two most commonly recommended approaches to scaling graphics.  This relies on the &lt;code&gt;Graphics2D&lt;/code&gt; interpolation rules to scale the incoming image.  The problem is: if the scaling factor is less than 50%, then the resulting image looks aliased.&lt;br /&gt;&lt;pre&gt;ScalingReader scalingGraphicsReader = new ScalingReader() {&lt;br /&gt; Component component = new JPanel();&lt;br /&gt; MediaTracker mediaTracker = new MediaTracker(component);&lt;br /&gt; public Image create(Dimension originalSize,Dimension maxSize) throws Throwable {&lt;br /&gt; &amp;nbsp; Dimension newSize = Scaling.scaleDimensionsProportionally(originalSize, maxSize);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; URL url = getImageURL();&lt;br /&gt; &amp;nbsp; Image image = Toolkit.getDefaultToolkit().createImage(url);&lt;br /&gt; &amp;nbsp; mediaTracker.addImage(image, 0);&lt;br /&gt; &amp;nbsp; mediaTracker.waitForAll();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; BufferedImage thumbnail = new BufferedImage(newSize.width, newSize.height, BufferedImage.TYPE_INT_ARGB);&lt;br /&gt; &amp;nbsp; Graphics2D g = thumbnail.createGraphics();&lt;br /&gt; &amp;nbsp; g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,  RenderingHints.VALUE_INTERPOLATION_BILINEAR);&lt;br /&gt; &amp;nbsp; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);&lt;br /&gt; &amp;nbsp; g.drawImage(image, 0, 0, newSize.width, newSize.height, null);&lt;br /&gt; &amp;nbsp; g.dispose();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; mediaTracker.removeImage(image);&lt;br /&gt; &amp;nbsp; image.flush();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; return thumbnail;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getName() {&lt;br /&gt; &amp;nbsp; return "scalingGraphicsReader";&lt;br /&gt; }&lt;br /&gt;};&lt;/pre&gt;&lt;img src="http://javagraphics.java.net/resources/transformReader100.png" align="right"&gt;&lt;h4&gt;transformReader&lt;/h4&gt;&lt;p&gt;This is practically the same as the previous model.  The only difference is this uses an &lt;code&gt;AffineTransform&lt;/code&gt;.  You never know how graphics pipelines process these requests, and over the years I've discovered some bizarre performance issues when you paint something two different ways.  However in this case: these two image-scaling methods performed identically.&lt;br /&gt;&lt;pre&gt;ScalingReader transformReader = new ScalingReader() {&lt;br /&gt; Component component = new JPanel();&lt;br /&gt; MediaTracker mediaTracker = new MediaTracker(component);&lt;br /&gt; public Image create(Dimension originalSize,Dimension maxSize) throws Throwable {&lt;br /&gt; &amp;nbsp; Dimension newSize = Scaling.scaleDimensionsProportionally(originalSize, maxSize);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; URL url = getImageURL();&lt;br /&gt; &amp;nbsp; Image image = Toolkit.getDefaultToolkit().createImage(url);&lt;br /&gt; &amp;nbsp; mediaTracker.addImage(image, 0);&lt;br /&gt; &amp;nbsp; mediaTracker.waitForAll();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; BufferedImage thumbnail = new BufferedImage(newSize.width, newSize.height, BufferedImage.TYPE_INT_ARGB);&lt;br /&gt; &amp;nbsp; Graphics2D g = thumbnail.createGraphics();&lt;br /&gt; &amp;nbsp; double scale = ((double)newSize.width)/((double)originalSize.width);&lt;br /&gt; &amp;nbsp; g.scale(scale, scale);&lt;br /&gt; &amp;nbsp; g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);&lt;br /&gt; &amp;nbsp; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);&lt;br /&gt; &amp;nbsp; g.drawImage(image, 0, 0, null);&lt;br /&gt; &amp;nbsp; g.dispose();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; mediaTracker.removeImage(image);&lt;br /&gt; &amp;nbsp; image.flush();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; return thumbnail;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getName() {&lt;br /&gt; &amp;nbsp; return "transformReader";&lt;br /&gt; }&lt;br /&gt;};&lt;/pre&gt;&lt;img src="http://javagraphics.java.net/resources/scaledInstanceReader100.png" align="right"&gt;&lt;h4&gt;scaledInstanceReader&lt;/h4&gt;&lt;p&gt;This is the approach most commonly insulted as "the old-fashioned way" to scale images.  But it's still a legitimate approach.  In my opinion it also looks better (because it is antialiased) than the two approaches above.&lt;br /&gt;&lt;pre&gt;ScalingReader scaledInstanceReader = new ScalingReader() {&lt;br /&gt; Component component = new JPanel();&lt;br /&gt; MediaTracker mediaTracker = new MediaTracker(component);&lt;br /&gt; public Image create(Dimension originalSize,Dimension maxSize) throws Throwable {&lt;br /&gt; &amp;nbsp; Dimension newSize = Scaling.scaleDimensionsProportionally(originalSize, maxSize);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; URL url = getImageURL();&lt;br /&gt; &amp;nbsp; Image image = Toolkit.getDefaultToolkit().createImage(url);&lt;br /&gt; &amp;nbsp; mediaTracker.addImage(image, 0);&lt;br /&gt; &amp;nbsp; mediaTracker.waitForID(0);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; Image image2 = image.getScaledInstance(newSize.width, newSize.height, Image.SCALE_SMOOTH);&lt;br /&gt; &amp;nbsp; mediaTracker.addImage(image2, 1);&lt;br /&gt; &amp;nbsp; mediaTracker.waitForID(1);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; mediaTracker.removeImage(image);&lt;br /&gt; &amp;nbsp; mediaTracker.removeImage(image2);&lt;br /&gt; &amp;nbsp; image.flush();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; return image2;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getName() {&lt;br /&gt; &amp;nbsp; return "scaledInstanceReader";&lt;br /&gt; }&lt;br /&gt;};&lt;/pre&gt;&lt;img src="http://javagraphics.java.net/resources/replicateScaleFilter100.png" align="right"&gt;&lt;h4&gt;replicateScaleFilter&lt;/h4&gt;&lt;p&gt;This is an interesting class I had never used before.  Although &lt;a href="http://download.oracle.com/javase/1.4.2/docs/api/java/awt/image/ReplicateScaleFilter.html"&gt;the API&lt;/a&gt; promises it will be an aliased image, I was interested to see how quickly it performed.&lt;br /&gt;&lt;pre&gt;ScalingReader replicateScaleFilter = new ScalingReader() {&lt;br /&gt; Component component = new JPanel();&lt;br /&gt; MediaTracker mediaTracker = new MediaTracker(component);&lt;br /&gt; public Image create(Dimension originalSize,Dimension maxSize) throws Throwable {&lt;br /&gt; &amp;nbsp; Dimension newSize = Scaling.scaleDimensionsProportionally(originalSize, maxSize);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; URL url = getImageURL();&lt;br /&gt; &amp;nbsp; Image image = Toolkit.getDefaultToolkit().createImage(url);&lt;br /&gt; &amp;nbsp; mediaTracker.addImage(image, 0);&lt;br /&gt; &amp;nbsp; mediaTracker.waitForAll();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; ImageProducer producer = image.getSource();&lt;br /&gt; &amp;nbsp; ImageFilter scalingFilter = new ReplicateScaleFilter(newSize.width, newSize.height);&lt;br /&gt; &amp;nbsp; ImageProducer scaledProducer = new FilteredImageSource(producer, scalingFilter);&lt;br /&gt; &amp;nbsp; Image image2 = Toolkit.getDefaultToolkit().createImage(scaledProducer);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; mediaTracker.addImage(image2, 1);&lt;br /&gt; &amp;nbsp; mediaTracker.waitForID(1);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; mediaTracker.removeImage(image);&lt;br /&gt; &amp;nbsp; mediaTracker.removeImage(image2);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; return image2;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getName() {&lt;br /&gt; &amp;nbsp; return "replicateScaleFilter";&lt;br /&gt; }&lt;br /&gt;};&lt;/pre&gt;&lt;img src="http://javagraphics.java.net/resources/areaAveragingScaleFilter100.png" align="right"&gt;&lt;h4&gt;areaAveragingScaleFilter&lt;/h4&gt;&lt;p&gt;This is similar to the &lt;code&gt;replicateScaleFilter&lt;/code&gt;, except this filter antialiases pixels.  It will be more expensive, but the results will be better.&lt;br /&gt;&lt;pre&gt;ScalingReader areaAveragingScaleFilter = new ScalingReader() {&lt;br /&gt; Component component = new JPanel();&lt;br /&gt; MediaTracker mediaTracker = new MediaTracker(component);&lt;br /&gt; public Image create(Dimension originalSize,Dimension maxSize) throws Throwable {&lt;br /&gt; &amp;nbsp; Dimension newSize = Scaling.scaleDimensionsProportionally(originalSize, maxSize);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; URL url = getImageURL();&lt;br /&gt; &amp;nbsp; Image image = Toolkit.getDefaultToolkit().createImage(url);&lt;br /&gt; &amp;nbsp; mediaTracker.addImage(image, 0);&lt;br /&gt; &amp;nbsp; mediaTracker.waitForAll();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; ImageProducer producer = image.getSource();&lt;br /&gt; &amp;nbsp; ImageFilter scalingFilter = new AreaAveragingScaleFilter(newSize.width, newSize.height);&lt;br /&gt; &amp;nbsp; ImageProducer scaledProducer = new FilteredImageSource(producer, scalingFilter);&lt;br /&gt; &amp;nbsp; Image image2 = Toolkit.getDefaultToolkit().createImage(scaledProducer);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; mediaTracker.addImage(image2, 1);&lt;br /&gt; &amp;nbsp; mediaTracker.waitForID(1);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; mediaTracker.removeImage(image);&lt;br /&gt; &amp;nbsp; mediaTracker.removeImage(image2);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; return image2;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getName() {&lt;br /&gt; &amp;nbsp; return "areaAveragingScaleFilter";&lt;br /&gt; }&lt;br /&gt;};&lt;/pre&gt;&lt;img src="http://javagraphics.java.net/resources/imageIOReader100.png" align="right"&gt;&lt;h4&gt;imageIOReader&lt;/h4&gt;&lt;p&gt;As far as I can tell: the &lt;code&gt;ImageIO&lt;/code&gt; package doesn't support &lt;i&gt;scaling&lt;/i&gt;, but it does support subsampling.  In addition to antialiasing, this presents a new problem none of the other readers face:&lt;br /&gt;&lt;p&gt;Subsampling only works in discrete intervals.  Suppose you want to subsample a 400 x 400 image with a subsample factor of 2: you'll get a 200 x 200 pixel image.  Now suppose you use a subsample factor of 4: you'll get a 100 x 100 pixel image.  Now suppose you wanted an image that was 150 x 150 pixels?  You're out of luck.  There is no subsample factor that gives you this result.  The reader below calculates a subsample factor, but still provides a &lt;code&gt;BufferedImage&lt;/code&gt; of the requested size: the remaining pixels are filled in with black.&lt;br /&gt;&lt;p&gt;The thumbnail to the right is "lucky", because it only introduces 1 row and column of black.  But in other cases, or with other pixel dimensions, this extra padding can be several dozen pixels.&lt;br /&gt;&lt;pre&gt;final ImageReader reader = (ImageReader)ioReaderList.get(a);&lt;br /&gt;ScalingReader imageIOReader = new ScalingReader() {&lt;br /&gt; public Image create(Dimension originalSize,Dimension maxSize) throws Throwable {&lt;br /&gt; &amp;nbsp; URL url = getImageURL();&lt;br /&gt; &amp;nbsp; Dimension newSize = Scaling.scaleDimensionsProportionally(originalSize, maxSize);&lt;br /&gt; &amp;nbsp; InputStream in = url.openStream();&lt;br /&gt; &amp;nbsp; try {&lt;br /&gt; &amp;nbsp; &amp;nbsp; ImageInputStream imageIn = ImageIO.createImageInputStream( in );&lt;br /&gt; &amp;nbsp; &amp;nbsp; reader.setInput(imageIn);&lt;br /&gt; &amp;nbsp; &amp;nbsp; int w = reader.getWidth(0);&lt;br /&gt; &amp;nbsp; &amp;nbsp; int h = reader.getHeight(0);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; &amp;nbsp; int sub = 1;&lt;br /&gt; &amp;nbsp; &amp;nbsp; if (w&gt;h)&lt;br /&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; sub = MathG.ceilInt(w/maxSize.getWidth());&lt;br /&gt; &amp;nbsp; &amp;nbsp; else&lt;br /&gt; &amp;nbsp; &amp;nbsp; &amp;nbsp; sub = MathG.ceilInt(h/maxSize.getHeight());&lt;br /&gt;&lt;br /&gt; &amp;nbsp; &amp;nbsp; BufferedImage bi = new BufferedImage(newSize.width, newSize.height,BufferedImage.TYPE_INT_RGB);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; &amp;nbsp; ImageReadParam param = reader.getDefaultReadParam();&lt;br /&gt; &amp;nbsp; &amp;nbsp; param.setSourceSubsampling(sub,sub,0,0);&lt;br /&gt; &amp;nbsp; &amp;nbsp; param.setDestination(bi);&lt;br /&gt; &amp;nbsp; &amp;nbsp; return reader.read(0,param);&lt;br /&gt; &amp;nbsp; } finally {&lt;br /&gt; &amp;nbsp; &amp;nbsp; in.close();&lt;br /&gt; &amp;nbsp; }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; public String getName() {&lt;br /&gt; &amp;nbsp; if(ioReaderList.size()==1) {&lt;br /&gt; &amp;nbsp; &amp;nbsp; return "imageIOReader";&lt;br /&gt; &amp;nbsp; } else {&lt;br /&gt; &amp;nbsp; &amp;nbsp; String s = reader.getClass().getName();&lt;br /&gt; &amp;nbsp; &amp;nbsp; if(s.indexOf('.')!=-1) {&lt;br /&gt; &amp;nbsp; &amp;nbsp; s = s.substring(s.lastIndexOf('.')+1);&lt;br /&gt; &amp;nbsp; &amp;nbsp; }&lt;br /&gt; &amp;nbsp; &amp;nbsp; return "imageIOReader&lt;"+s+"&gt;";&lt;br /&gt; &amp;nbsp; }&lt;br /&gt; }&lt;br /&gt;};&lt;/pre&gt;&lt;br /&gt;I tested these 7 methods with my test image, measuring the time and memory allocation each used.  The memory allocation measurement is shaky because I'm simply referring to &lt;code&gt;Runtime.getRuntime().freeMemory()&lt;/code&gt;, but it should give us a general idea.  (However an unplanned garbage collection can skew the results.)  In the tests I slowly incremented the destination image width by 100 pixels (from a 100 pixel wide copy to a 2000 pixel wide copy).&lt;br /&gt;&lt;p&gt;I collected results from 6 different environments (including XP, Mac, Windows 7, and Linux).  All the results &lt;a href="https://spreadsheets.google.com/ccc?key=0AijNZxvkIDdOdHRCM3VicmZCZ2lsSmd1V0hmdGZUc1E&amp;hl=en"&gt;here&lt;/a&gt;, but for brevity's sake I'll just show off the results from Windows 7.  (Not because it's remarkably better or worse than other OS's, but because I assume Windows 7 is incredibly common, and to show &lt;i&gt;all&lt;/i&gt; the results would bloat this article.)&lt;br /&gt;&lt;p&gt;Here is a graph of the time each method required:&lt;br /&gt;&lt;br /&gt;&lt;img src="https://spreadsheets.google.com/oimg?key=0AijNZxvkIDdOdHRCM3VicmZCZ2lsSmd1V0hmdGZUc1E&amp;oid=8&amp;zx=mh7ycskn0v0g" /&gt;&lt;br /&gt;&lt;p&gt;The slowest methods here are &lt;code&gt;areaAveragingScaleFilter&lt;/code&gt; and &lt;code&gt;scaledInstanceReader&lt;/code&gt;.  Based on this chart I'd suggest they're the same function (that is: I believe &lt;code&gt;Image.getScaledInstance()&lt;/code&gt; uses an &lt;code&gt;AreaAveragingFilter&lt;/code&gt;).  But other than my new class: these are the only two models that can antialias if you scale more than 50%.&lt;br /&gt;&lt;p&gt;Next in line is the &lt;code&gt;replicateScaleFilter&lt;/code&gt;.  This is the slowest of the aliased methods.&lt;br /&gt;&lt;p&gt;Next is the new class I wrote: it's around .8 seconds.  It's by no means the fastest method, but it is the fastest antialiased method.&lt;br /&gt;&lt;p&gt;The other results are a little garbled.  I'd suggest the &lt;code&gt;transformReader&lt;/code&gt; and the &lt;code&gt;scalingGraphicsReader&lt;/code&gt; are also identical under the hood, based on these numbers.  The &lt;code&gt;imageIOReader&lt;/code&gt; appears to jump around at specific intervals: I assume this relates to when the subsample factor increases.&lt;br /&gt;&lt;p&gt;But none of that is terribly important to me: I'm OK with asking the user to tolerate a 2-second delay when they give me a megapixel JPEG.  What I'm interested in is the memory allocation:&lt;br /&gt;&lt;br /&gt;&lt;img src="https://spreadsheets.google.com/oimg?key=0AijNZxvkIDdOdHRCM3VicmZCZ2lsSmd1V0hmdGZUc1E&amp;oid=9&amp;zx=4adw61k0ap9" /&gt;&lt;br /&gt;&lt;p&gt;Just like before: the &lt;code&gt;transformReader&lt;/code&gt; and &lt;code&gt;scalingGraphicsReader&lt;/code&gt; overlap so much its hard to notice there are 2 results.  And the &lt;code&gt;areaAveragingScaleFilter&lt;/code&gt; and &lt;code&gt;scaledInstanceReader&lt;/code&gt; results are also so close they also seem identical.&lt;br /&gt;&lt;p&gt;There are two clear winners in this chart: &lt;code&gt;imageIOReader&lt;/code&gt; and the new &lt;code&gt;singlePassReader&lt;/code&gt;.&lt;br /&gt;&lt;p&gt;The &lt;code&gt;imageIOReader&lt;/code&gt;, however, doesn't let you scale to arbitrary dimensions.  For example: when we attempt to scale the 2750 pixel wide image to 2000 pixels: we use a subsample factor of 2.  So the resulting image is 1375 pixels wide.  The remaining 625 pixels (approximately 30% of the width) are black.&lt;br /&gt;&lt;p&gt;That leaves the &lt;code&gt;singlePassReader&lt;/code&gt; as my winner.  Was this contest fair?  Probably not.  I created the tests, and I created a custom-tailored solution to pass the tests.  But I don't think my goals (low memory allocation and antialiasing) are asking too much; and I'm sharing this solution because I believe other developers might want the same results.&lt;br /&gt;&lt;p&gt;I could discuss the results of each OS in detail, but the broad conclusions are the same in each case.  Some other charts show strong dips in memory at certain levels: I assume this is unsolicited garbage collection at work.  This raises the interesting point that although other methods may churn through several MB of memory: they don't necessarily keep it all in use.  So that's promising.  I haven't tried to study exactly how much they would require if they had constant garbage collection.  (For example: if you were in an environment with a fixed amount of memory and frequent garbage collection, which methods could still scale the incoming image?)&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;These results are great.  This lets me efficiently create thumbnails and open images at a maximum resolution without burning through half my app's memory.&lt;br /&gt;&lt;p&gt;You can download the app that created the output above &lt;a href="http://javagraphics.java.net/jars/ScalingJPEGDemo.jar"&gt;here&lt;/a&gt;.  Note it includes a large embedded JPEG to test with.  You can download a smaller package that includes these classes &lt;a href="http://javagraphics.java.net/jars/Scaling.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;This article focused on talking about JPEGs, but the images all came from the abstract &lt;code&gt;Toolkit.createImage(...)&lt;/code&gt;, which supports JPEGs as well as PNGs.  And PNGs are stored in an orderly row-based model, so they should automatically work with this architecture, too.&lt;br /&gt;&lt;p&gt;However.  It is important to note that this code &lt;i&gt;is not guaranteed to work&lt;/i&gt; because it assumes the image data can be collected in a single pass.  Which is to say: I still definitely plan on using this class, but I'll always wrap it in try/catch clauses in case it fails.&lt;br /&gt;&lt;p&gt;Someday I'd like to also develop the &lt;code&gt;GenericImageRandomAccessIterator&lt;/code&gt;.  For this I'm envisioning a &lt;code&gt;RandomAccessFile&lt;/code&gt;-based model.  This will work with arbitrary passes, and it may require even less memory: but it will be noticeably slower because it relies on the file system.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-74630044016640289?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/74630044016640289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2011/05/images-scaling-jpegs-and-pngs.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/74630044016640289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/74630044016640289'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2011/05/images-scaling-jpegs-and-pngs.html' title='Images: Scaling JPEGs and PNGs'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-2464119862047980596</id><published>2011-02-28T19:09:00.000-08:00</published><updated>2011-04-05T23:51:56.390-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shadow'/><category scheme='http://www.blogger.com/atom/ns#' term='text'/><category scheme='http://www.blogger.com/atom/ns#' term='animation'/><category scheme='http://www.blogger.com/atom/ns#' term='calligraphy'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='effects'/><title type='text'>Text: Effects and Block Shadows</title><content type='html'>This weekend I revisited an old project: the &lt;a href="http://javagraphics.blogspot.com/2009/05/strokes-calligraphy-stroke.html"&gt;calligraphy stroke&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;I rewrote it using a handy unassuming class called the &lt;a href="http://javagraphics.java.net/doc/com/bric/awt/CalligraphyPathWriter.html"&gt;CalligraphyPathWriter&lt;/a&gt;.  This is fewer lines of code than the previous model, pipes the data more efficiently, and includes a couple of extra functions.&lt;br /&gt;&lt;p&gt;It no longer &lt;i&gt;only&lt;/i&gt; makes strokes.  The same basic notion of tracing a shape to apply a calligraphy-like stroke can be applied to creating drop shadows.  The only difference is a stroke straddles the path, and a drop shadow lies entirely to the side of the original shape.&lt;br /&gt;&lt;p&gt;The &lt;code&gt;CalligraphyPathWriter&lt;/code&gt; constructor includes two floats to define these offsets: if you use 0 and 30: then you're effectively adding a drop shadow to your shape.  If you use -5 and 5: then you're effectively creating a stroked outline of your shape.&lt;br /&gt;&lt;p&gt;You can also create a drop-shadow-like effect with code like this:&lt;br /&gt;&lt;pre&gt;Graphics2D g;&lt;br /&gt;Shape myShape;&lt;br /&gt;...&lt;br /&gt;double dx = Math.cos(angle);&lt;br /&gt;double dy = Math.sin(angle);&lt;br /&gt;for(int height = 0; height &amp;lt; 30; height++) {&lt;br /&gt; &amp;nbsp; g.translate(height*dx, height*dy);&lt;br /&gt; &amp;nbsp; g.fill(myShape);&lt;br /&gt; &amp;nbsp; g.translate(-height*dx, -height*dy);&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;... but if your shape or &lt;code&gt;java.awt.Paint&lt;/code&gt; are complex: this might be expensive.  (Also two of the four effects shown below can't be reproduced this way.)&lt;br /&gt;&lt;p&gt;The &lt;code&gt;CalligraphyPathWriter&lt;/code&gt; can reduce this down to:&lt;br /&gt;&lt;pre&gt;GeneralPath blockShadow = new GeneralPath();&lt;br /&gt;CalligraphyPathWriter writer = &lt;br /&gt; &amp;nbsp; &amp;nbsp; new CalligraphyPathWriter(angle, 0, height, blockShadow);&lt;br /&gt;writer.write(myShape);&lt;br /&gt;g.fill(blockShadow);&lt;/pre&gt;&lt;br /&gt;The call &lt;code&gt;write(myShape)&lt;/code&gt; iterates over the input shape and populates the &lt;code&gt;blockShadow&lt;/code&gt; path with the silhouette of the block shadow.&lt;br /&gt;&lt;h3&gt;Applying To Text&lt;/h3&gt;&lt;br /&gt;Here is a simple applet that creates animated text effects using these new block shadows:&lt;br /&gt;&lt;applet code="com/bric/awt/text/TextEffectDemo" codebase="http://javagraphics.java.net/jars/" width="500" height="280"&gt;     &lt;param name="archive" value="TextEffect-bin.jar" /&gt;&lt;/applet&gt;&lt;br /&gt;You can download the applet (source included) &lt;a href="http://javagraphics.java.net/jars/TextEffect.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;Type any text in the text field provided.  (Any text is acceptable, but it should probably be 20 characters or less?)  Then select any one of the buttons:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The "Outline" button uses the &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/awt/Scribbler.html"&gt;Scribbler&lt;/a&gt;&lt;/code&gt; class to randomize the texture of the text, fill and shadow.  A little random vertical offset is added, and lastly a light drop shadow is traced (but not filled) around each letter.&lt;br /&gt;&lt;li&gt;The "Punch" button separately punches each word forward with an obvious drop shadow.  (Also try writing a string with no spaces: then each character is punched.)&lt;br /&gt;&lt;li&gt;The "Wave" button fills &lt;i&gt;and&lt;/i&gt; traces the block shadow to reinforce the 3D-look.&lt;br /&gt;&lt;li&gt;The "Explode" uses another obvious drop shadow, but each character is assigned an angle based on how far away it is from the center.  (Then the height has to be divided by the sine of that angle so the baseline is constant.)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;My immediate goal this weekend was just to use block shadows for something fun.  In the long term I want to define an architecture similar to what I did for simple 2D-geometric &lt;a href="http://javagraphics.blogspot.com/2007/04/slideshows-transitions-swf.html"&gt;transitions&lt;/a&gt;.  This demo applet uses a very loosely defined &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/awt/text/TextEffect.html"&gt;TextEffect&lt;/a&gt;&lt;/code&gt; interface, but it needs a lot more work to support a variety of effects for different export formats.&lt;br /&gt;&lt;p&gt;Also in the future it would be nice to apply gradients to these block shadows.  A height-based gradient effect will be very easy.  An angle-based effect (which I think would be more popular) will be a little trickier, but is still manageable.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-2464119862047980596?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/2464119862047980596/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2011/02/text-effects-and-block-shadows.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2464119862047980596'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2464119862047980596'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2011/02/text-effects-and-block-shadows.html' title='Text: Effects and Block Shadows'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-667322249211342157</id><published>2011-02-25T22:47:00.000-08:00</published><updated>2011-02-25T23:42:55.791-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='toolkit'/><category scheme='http://www.blogger.com/atom/ns#' term='mouse'/><category scheme='http://www.blogger.com/atom/ns#' term='input device'/><category scheme='http://www.blogger.com/atom/ns#' term='click'/><category scheme='http://www.blogger.com/atom/ns#' term='events'/><title type='text'>Mouse Click Events: Adding Wiggle Room</title><content type='html'>&lt;code&gt;MouseListeners&lt;/code&gt; have a &lt;code&gt;mouseClicked()&lt;/code&gt; method, but it is only triggered if the mouse does not move between the mouse being pressed and later being released.&lt;br /&gt;&lt;p&gt;This is working as designed, but as touchpads and tablets become more available: I think this design is outdated.  It is very easy on certain devices to press the mouse, accidentally move the mouse, and then release the mouse.  (Even the word "mouse" here is outdated: we're dealing with fingers and tablet pens.)&lt;br /&gt;&lt;p&gt;The good news is: this does not affect buttons.  &lt;code&gt;JButtons&lt;/code&gt; use a more complicated model to trigger any associated &lt;code&gt;ActionListeners&lt;/code&gt; (based on &lt;code&gt;ButtonModels&lt;/code&gt;, armed and pressed states).  The issue I'm describing only relates to when &lt;code&gt;MouseListener.mouseClicked()&lt;/code&gt; is called.&lt;br /&gt;&lt;p&gt;Here is an applet that demonstrates the problem, and a potential solution:&lt;br /&gt;&lt;br&gt;&lt;APPLET CODE="com/bric/awt/ClickEventEnablerDemo" CODEBASE="http://javagraphics.java.net/jars/"  width=238 height=103&gt;     &lt;PARAM NAME="archive" VALUE="ClickEventEnabler.jar"&gt;&lt;br/&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;p&gt;When &lt;code&gt;mouseClicked()&lt;/code&gt; is called the color changes to green.  If it is called again then the color changes to yellow.&lt;br /&gt;&lt;p&gt;If you press the mouse down and move it a couple pixels before releasing: one label will highlight green and the other will not.  (Based on one mouse click neither label should turn yellow: if it does then two calls to &lt;code&gt;mouseClicked()&lt;/code&gt; are being made, and that's a serious bug.)&lt;br /&gt;&lt;p&gt;The pixel tolerance is a global static field.  I picked 10 as an arbitrary number; you can fine tune this as needed.&lt;br /&gt;&lt;h3&gt;Implementation&lt;/h3&gt;&lt;br /&gt;The &lt;a href="http://javagraphics.java.net/doc/com/bric/awt/ClickEventEnabler.html"&gt;ClickEventEnabler&lt;/a&gt; has only one method: &lt;code&gt;install()&lt;/code&gt;.&lt;br /&gt;&lt;p&gt;The current implementation just adds one &lt;code&gt;AWTEventListener&lt;/code&gt; to the Toolkit.  When &lt;code&gt;mousePressed()&lt;/code&gt; and later &lt;code&gt;mouseReleased()&lt;/code&gt; is called: we check to see if the distance between the mouse pressed and released location is too large.&lt;br /&gt;&lt;p&gt;If it is within an acceptable range, we create a &lt;code&gt;Runnable&lt;/code&gt; to invoke later.  If in the meantime a call &lt;code&gt;mouseClicked()&lt;/code&gt; comes in for the same component: the runnable is cancelled.  So if the user doesn't move the mouse, or if future Java implementations address this concern: this event listener won't send a redundant &lt;code&gt;mouseClicked()&lt;/code&gt; event.&lt;br /&gt;&lt;p&gt;If everything passes inspection: then we create our own &lt;code&gt;MouseEvent.MOUSE_CLICKED&lt;/code&gt; event and dispatch it ourselves.&lt;br /&gt;&lt;p&gt;So this requires top-level access to the event queue, but aside from possibly dispatching click events: it is very benign.&lt;br /&gt;&lt;p&gt;In the future: it might be useful to implement a similar change to recognize double-clicks.  For now I'm pilot testing this single-click solution.&lt;br /&gt;&lt;p&gt;The javadoc link above references the source code, but there's also a jar to download &lt;a href="http://javagraphics.java.net/jars/ClickEventEnabler.jar"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-667322249211342157?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/667322249211342157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2011/02/mouse-click-events-adding-wiggle-room.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/667322249211342157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/667322249211342157'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2011/02/mouse-click-events-adding-wiggle-room.html' title='Mouse Click Events: Adding Wiggle Room'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-7868716825269257505</id><published>2010-08-21T14:27:00.000-07:00</published><updated>2011-02-21T13:34:22.953-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Area'/><category scheme='http://www.blogger.com/atom/ns#' term='AreaX'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><title type='text'>Shapes: Creating an Alternative to the Area Class</title><content type='html'>&lt;img src="http://javagraphics.java.net/areax/resources/library.png" align="right"&gt;See the image on the right?&lt;br /&gt;&lt;p&gt;It's a piece of vector clip art from the collection we use at work.&lt;br /&gt;&lt;p&gt;If I use &lt;code&gt;java.awt.geom.Area&lt;/code&gt; objects to calculate its outline: it takes about 3 seconds.  But I figured out a way to calculate the same outline in about 120 milliseconds instead.*&lt;br /&gt;&lt;p&gt;This article may look deceptively short, but it's actually my longest article ever.  It's so long I had to &lt;a href="http://javagraphics.java.net/areax/"&gt;split it up into several pages&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;This article is also my last blog entry for 2010.  I have a nice collection of over 50 articles now, and it's time to branch out and explore other hobbies.  At least a little.  :)&lt;br /&gt;&lt;p&gt;I'm especially proud of this entry, though, and I wanted to go out with a grand finale.  So I hope you enjoy.&lt;br /&gt;&lt;br&gt;&lt;br /&gt;&lt;p&gt;*OK, so I'll admit this is a rare/exceptional example.  But overall I did see more than a 50% improvement in performance for calculating outlines!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-7868716825269257505?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/7868716825269257505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2010/08/shapes-creating-alternative-to-area.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7868716825269257505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7868716825269257505'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2010/08/shapes-creating-alternative-to-area.html' title='Shapes: Creating an Alternative to the Area Class'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-2633569330681434096</id><published>2010-06-22T21:05:00.000-07:00</published><updated>2011-02-21T13:32:51.598-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='vectorize'/><category scheme='http://www.blogger.com/atom/ns#' term='vectorizer'/><category scheme='http://www.blogger.com/atom/ns#' term='pencil'/><category scheme='http://www.blogger.com/atom/ns#' term='Shape'/><category scheme='http://www.blogger.com/atom/ns#' term='freehand'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><title type='text'>Shapes: Implementing a Freehand Pencil Tool (Vectorizers)</title><content type='html'>&lt;IMG SRC="http://javagraphics.java.net/resources/vectorizer_demo.gif" align="right"&gt;If you google &lt;a href="http://www.google.com/search?q=how+to+implement+a+pencil+tool+in+java"&gt;"how to implement a pencil tool in Java"&lt;/a&gt; you get a few results that basically tell you how to implement the right &lt;code&gt;MouseListeners&lt;/code&gt; to record the mouse points.  Then you have a giant polygon. (Or polyline, technically).&lt;br /&gt;&lt;p&gt;The problem is: this is ugly.  It's acceptable for beginners, or prototypes: but we can do better.&lt;br /&gt;&lt;p&gt;What I want to do is smooth out this data.  To replace the series of lines with attractive bezier curves.  To the right is an image of what I'm looking for.&lt;br /&gt;&lt;p&gt;I'm not entirely sure what to call this project.  (Is there an official word for this?  Does anyone know?)  It's a mix between "Vectorization" and "Beautification"?  For now I created a simple interface called &lt;a href="http://javagraphics.java.net/doc/com/bric/geom/Vectorizer.html"&gt;&lt;code&gt;Vectorizer&lt;/code&gt;&lt;/a&gt;:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;public interface Vectorizer {&lt;br /&gt; public void add(float x,float y,long t);&lt;br /&gt; public void reset();&lt;br /&gt; public GeneralPath getShape();&lt;br /&gt; public void getShape(GeneralPath path);&lt;br /&gt; public boolean isEmpty();&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;h3&gt;Results&lt;/h3&gt;&lt;br /&gt;I ended up with the following applet.&lt;br /&gt;&lt;br&gt;&lt;APPLET CODE="com/bric/geom/VectorizerDemo" CODEBASE="http://javagraphics.java.net/jars/"  width=444 height=515&gt;     &lt;PARAM NAME="archive" VALUE="Vectorizer.jar"&gt;&lt;br/&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;br&gt;You can download this applet &lt;a href="http://javagraphics.java.net/jars/Vectorizer.jar"&gt;here&lt;/a&gt; (source included).&lt;br /&gt;&lt;h3&gt;Implementation&lt;/h3&gt;&lt;br /&gt;The applet above demos the &lt;a href="http://javagraphics.java.net/doc/com/bric/geom/BasicVectorizer.html"&gt;&lt;code&gt;BasicVectorizer&lt;/code&gt;&lt;/a&gt;.  In pseudocode it goes likes this:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;currentPoint = 0;&lt;br /&gt;while more points remain:&lt;br /&gt; lastPoint = currentPoint + 1;&lt;br /&gt; create cubic arc from currentPoint to lastPoint&lt;br /&gt; while arc is within the allowed error:&lt;br /&gt;  lastPoint++;&lt;br /&gt;  create cubic arc from currentPoint to lastPoint&lt;br /&gt; write arc&lt;br /&gt; currentPoint = lastPoint;&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;This seems to work well for far-away points, but it starts to suffer when several points are clustered together.  The problem (for me) is it's hard to guess what the user is trying to do when the mouse moves in a small area: is the user drawing a sharp corner?  Just drawing slowly?  They might not be using a mouse at all -- maybe an assistive device doesn't track the way mice do?&lt;br /&gt;&lt;p&gt;So there's room for improvement, but it's a start.&lt;br /&gt;&lt;h3&gt;Other Resources&lt;/h3&gt;&lt;br /&gt;The only other open-source pencil tool I'm familiar with is Werner's in &lt;a href="http://www.jhotdraw.org/"&gt;JHotDraw&lt;/a&gt;.  (The license is LGPL.  I believe the classes involved are &lt;code&gt;org.jhotdraw.geom.Bezier&lt;/code&gt; and &lt;code&gt;org.jhotdraw.geom.BezierPath&lt;/code&gt;.)&lt;br /&gt;&lt;p&gt;Does anyone have other examples handy on the subject?&lt;br /&gt;&lt;p&gt;For now this meets my immediate needs, but maybe in the future I'll touch it up a little.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-2633569330681434096?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/2633569330681434096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2010/06/shapes-implementing-freehand-pencil.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2633569330681434096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2633569330681434096'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2010/06/shapes-implementing-freehand-pencil.html' title='Shapes: Implementing a Freehand Pencil Tool (Vectorizers)'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-329134431982658418</id><published>2010-06-13T18:03:00.000-07:00</published><updated>2011-05-17T01:22:59.194-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='encoder'/><category scheme='http://www.blogger.com/atom/ns#' term='image'/><category scheme='http://www.blogger.com/atom/ns#' term='bmp'/><category scheme='http://www.blogger.com/atom/ns#' term='decoder'/><category scheme='http://www.blogger.com/atom/ns#' term='thumbnail'/><title type='text'>Images: BMPs and Thumbnails</title><content type='html'>This article address the subject: how do I quickly create a BMP thumbnail?&lt;br /&gt;&lt;p&gt;In my &lt;a href="http://javagraphics.blogspot.com/2010/06/images-scaling-down.html"&gt;last article&lt;/a&gt; I presented an &lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/PixelIterator.html"&gt;architecture&lt;/a&gt; that included a memory-efficient model for scaling images.  This article will use that architecture to kick some scaling butt.&lt;br /&gt;&lt;p&gt;Surely (?) developers have come before me that tackled this issue, right?  This sounds like something &lt;code&gt;ImageIO&lt;/code&gt; might have addressed already, but when I googled &lt;a href="http://www.google.com/search?q=ImageIO+to+create+BMP+thumbnail"&gt;"ImageIO to create BMP thumbnail"&lt;/a&gt; I didn't find any promising leads.  (I did find &lt;a href="http://download.java.net/media/jai-imageio/javadoc/1.1/overview-summary.html"&gt;this page&lt;/a&gt; which mentions the BMP reader doesn't support thumbnails.)&lt;br /&gt;&lt;h3&gt;Reading BMPs&lt;/h3&gt;&lt;br /&gt;The trickiest part of this subject is: we need to be able to read BMPs.  Yes, there is existing code that can do this.  Mostly I'm thinking of &lt;code&gt;ImageIO&lt;/code&gt; classes, but I'm sure there are other options too.  The problem is: these interface with &lt;code&gt;BufferedImages&lt;/code&gt;, and my goal here is interface with my new &lt;code&gt;PixelIterator&lt;/code&gt; class.&lt;br /&gt;&lt;p&gt;So I dusted off the &lt;a href="http://www.fileformat.info/format/bmp/egff.htm"&gt;file specifications&lt;/a&gt; and wrote my own &lt;a href="http://javagraphics.java.net/doc/com/bric/image/bmp/BmpDecoderIterator.html"&gt;BMP decoder&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;As of this writing: this decoder is not fully featured.  It supports 1, 4, 8, 24 and 32-bit uncompressed BMPs, but nothing else.  It is my experience that the vast majority of BMPs in the world are uncompressed, though.&lt;br /&gt;&lt;p&gt;This functionality is nicely wrapped up in a few static classes in the  &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/image/bmp/BmpDecoder.html"&gt;BmpDecoder&lt;/a&gt;&lt;/code&gt;.&lt;br /&gt;&lt;h3&gt;Creating Thumbnails&lt;/h3&gt;&lt;br /&gt;With a &lt;code&gt;BytePixelIterator&lt;/code&gt; to work with, and the previously established &lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/ScalingIterator.html"&gt;ScalingIterator&lt;/a&gt;: it's incredibly easy to scale BMP data on-the-fly.&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;public static BufferedImage createThumbnail(InputStream bmp,&lt;br /&gt;Dimension maxSize) throws IOException {&lt;br /&gt; &amp;nbsp; PixelIterator i = BmpDecoderIterator.get(bmp);&lt;br /&gt; &amp;nbsp; int srcW = i.getWidth();&lt;br /&gt; &amp;nbsp; int srcH = i.getHeight();&lt;br /&gt;&lt;br /&gt; &amp;nbsp; float widthRatio = ((float)maxSize.width)/((float)srcW);&lt;br /&gt; &amp;nbsp; float heightRatio = ((float)maxSize.height)/((float)srcH);&lt;br /&gt; &amp;nbsp; float ratio = Math.min(widthRatio, heightRatio);&lt;br /&gt;&lt;br /&gt; &amp;nbsp; if(ratio&lt;1) {&lt;br /&gt; &amp;nbsp; &amp;nbsp; i = ScalingIterator.get(i, ratio);&lt;br /&gt; &amp;nbsp; }&lt;br /&gt; &amp;nbsp; return BufferedImageIterator.create( i, null );&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;h3&gt;Writing BMPs&lt;/h3&gt;&lt;p&gt;Also for good measure: I included a &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/image/bmp/BmpEncoder.html"&gt;BmpEncoder&lt;/a&gt;&lt;/code&gt;.  The motivation for this class was actually for a separate project I may discuss later, but since it's related I'll include it here.&lt;h3&gt;Results&lt;/h3&gt;&lt;p&gt;So far you might be underwhelmed by this article: there's nothing especially clever or innovative here.&lt;p&gt;But consider this example.  I have a folder on my computer called "bigpix".  (A QA guy at work put this folder together.)  I don't know where these files came from -- so I'm probably not legally allowed to distribute any of them -- but there's a file here called "ElPerroDelMar-Portratt.bmp".  It's 2,469 pixels by 3,234 pixels.&lt;p&gt;If I wanted to create a thumbnail of this file that fit inside a 128x128 image, my first reaction is simply to use &lt;code&gt;ImageIO&lt;/code&gt; to read the file and then scale that image to the appropriate size.  This requires creating an image that is 2469*3234*(3 colors) bytes (22 MB).  I just allocated 22 MB to create an eventual 48 KB image.&lt;p&gt;This is ridiculous.  And if the user is trying to navigate a folder of two dozen similar images (and you're creating thumbnails on-the-fly for all of them): you just used over 512 MB of RAM.&lt;p&gt;The &lt;a href="http://javagraphics.java.net/jars/Bmp.jar"&gt;Bmp.jar&lt;/a&gt; (source included) compares the cost of creating a thumbnail using the &lt;code&gt;ImageIO&lt;/code&gt; approach and the &lt;code&gt;BmpDecoder&lt;/code&gt; class.  Also (without scaling) it pits the encoding ability of &lt;code&gt;ImageIO&lt;/code&gt; against &lt;code&gt;BmpDecoder&lt;/code&gt;.  Here is the output when I ask the &lt;code&gt;Bmp.jar&lt;/code&gt; to work with the ElPerroDelMar image:&lt;blockquote&gt;&lt;pre&gt;OS = Mac OS X (10.5.8), i386&lt;br /&gt;Java Version = 1.5.0_24&lt;br /&gt;apple.awt.graphics.UseQuartz = false&lt;br /&gt;&lt;br /&gt;Running Thumbnail Tests...&lt;br /&gt;BmpThumbnail: 72 ms, 332944 bytes RAM&lt;br /&gt;ImageIO: 1065 ms, 23317200 bytes RAM&lt;br /&gt;&lt;br /&gt;Running Encode Tests...&lt;br /&gt;BmpEncoder: 585 ms, 332952 bytes RAM, 22987298 bytes file&lt;br /&gt;ImageIO: 649 ms, 14932160 bytes RAM, 22987326 bytes file&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;p&gt;As expected: the thumbnail generation is a huge improvement.  The &lt;code&gt;BmpDecoder&lt;/code&gt; takes less than 1.5% of the memory &lt;code&gt;ImageIO&lt;/code&gt; uses, and less than 7% of the time.&lt;p&gt;I was surprised (but pleased) to see such a substantial advantage in the encoding tests, too.  I'm not entirely sure what causes this, but the huge memory allocation suggests the &lt;code&gt;ImageIO&lt;/code&gt; classes are extracting all the image data before writing anything (instead of efficiently piping the image data).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-329134431982658418?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/329134431982658418/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2010/06/images-bmps-and-thumbnails.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/329134431982658418'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/329134431982658418'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2010/06/images-bmps-and-thumbnails.html' title='Images: BMPs and Thumbnails'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-8281756957059682591</id><published>2010-06-12T05:01:00.000-07:00</published><updated>2011-05-16T22:29:14.785-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scaling'/><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='image'/><category scheme='http://www.blogger.com/atom/ns#' term='java2d'/><category scheme='http://www.blogger.com/atom/ns#' term='antialias'/><category scheme='http://www.blogger.com/atom/ns#' term='thumbnail'/><title type='text'>Images: Scaling Down</title><content type='html'>It's easy to create thumbnails of &lt;code&gt;BufferedImages&lt;/code&gt;:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;BufferedImage thumb = new BufferedImage( (int)(source.getWidth()*scale), (int)(source.getHeight()*scale), BufferedImage.TYPE_INT_ARGB );&lt;br /&gt;Graphics2D g = thumb.createGraphics();&lt;br /&gt;g.scale(scaleFactor, scaleFactor);&lt;br /&gt;g.drawImage(source, 0, 0, null);&lt;br /&gt;g.dispose()&lt;/pre&gt;&lt;/blockquote&gt;... but when &lt;code&gt;scaleFactor&lt;/code&gt; is small enough, the image quality really suffers.  Sure I tried adding the right &lt;code&gt;RenderingHints&lt;/code&gt; -- interpolation, quality, etc -- but they only barely made an improvement.&lt;br /&gt;&lt;p&gt;Here is a screenshot of the example that brought this to my attention:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://javagraphics.java.net/resources/scaling_before.png"&gt;&lt;br /&gt;&lt;p&gt;As a sidenote: I should mention that this is only a problem using Sun's rendering engine.  If we use Quartz on Mac: the scaling is great.  However I swore of Quartz a long time ago (for &lt;a href="http://javagraphics.blogspot.com/2009/02/graphics2d-improving-performance.html"&gt;this&lt;/a&gt; and &lt;a href="http://javagraphics.blogspot.com/2007/04/managed-images.html"&gt;that&lt;/code&gt;&lt;/a&gt;, among other reasons).&lt;br /&gt;&lt;p&gt;So if I'm not using Quartz, and I don't like how &lt;code&gt;Java2D&lt;/code&gt; handles scaling: I wanted to explore other approaches to get better results.&lt;br /&gt;&lt;h3&gt;Filthy Rich Clients&lt;/h3&gt;&lt;p&gt;I really appreciate Laszlo pointing this out to me in the comments: Romain Guy already addressed the subject of smooth scaling in &lt;a href="http://filthyrichclients.org/"&gt;Filthy Rich Clients&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;He clearly identified the problem: bilinear interpolation starts to fall apart when the scaling factor is less than 50%.  (Which is often the case when you're creating thumbnails.)  But at 50% it still looks good.&lt;br /&gt;&lt;p&gt;His solution was brilliantly simple: just scale multiple times.  So if you want to scale to 25%: scale your source image at 50% twice.  The code he uses (without comments/javadocs) is less than 50 lines.  I found a copy &lt;a href="http://www.java2s.com/Open-Source/Java-Document/Swing-Library/swingx/org/jdesktop/swingx/graphics/GraphicsUtilities.java.htm"&gt;here&lt;/a&gt; under an LGPL or BSD license.&lt;br /&gt;&lt;p&gt;The downside to this approach, though, is it's very wasteful.  Consider creating a thumbnail of an image that is 4,000 x 3,000 pixels.  We'd first scale it to 2,000 x 1,500.  Then 1,000 x 750.  Then 500 x 375.  At 4 bytes per pixel: the intermediate two images add up to 14.3 MB of memory.  Although these would be quickly discarded for garbage collection: I'd rather not rely on a spare 14 MB of memory to get the job done.&lt;br /&gt;&lt;h3&gt;A Different Solution&lt;/h3&gt;&lt;p&gt;In this immediate context I'm only interested in scaling &lt;code&gt;BufferedImages&lt;/code&gt;.  However in the long-term I want a slightly more abstract model.  The &lt;a href="http://java.sun.com/javase/6/docs/api/index.html?java/awt/image/ImageProducer.html"&gt;ImageProducer&lt;/a&gt; class comes to mind, but it's &lt;i&gt;too&lt;/i&gt; abstract; I want something that iterates up or down an image in rows at a time.  Nothing more, nothing less.&lt;br /&gt;&lt;p&gt;So I wrote the &lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/PixelIterator.html"&gt;&lt;code&gt;PixelIterator&lt;/code&gt;&lt;/a&gt;, and it's two specific subclasses: &lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/BytePixelIterator.html"&gt;&lt;code&gt;BytePixelIterator&lt;/code&gt;&lt;/a&gt; and &lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/IntPixelIterator.html"&gt;&lt;code&gt;IntPixelIterator&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;These classes iterate over an image -- either top-to-bottom or bottom-to-top -- reading entire rows at a time.  The &lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/BufferedImageIterator.html"&gt;BufferedImageIterator&lt;/a&gt; iterates over the pixels in a &lt;code&gt;BufferedImage&lt;/code&gt; -- which is all we need for this example.&lt;br /&gt;&lt;p&gt;The &lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/ScalingIterator.html"&gt;ScalingIterator&lt;/a&gt; is what took the longest time to write in this project.  This is a subclass of &lt;code&gt;PixelIterator&lt;/code&gt; that acts as a filter for incoming data.  It reads one row from the source filter at a time, and then it combines color channels for the destination pixels.  Then optionally other rows are also read: eventually the sums are averaged into the filtered (scaled) row.  So it may read several rows from the source image and collapse them into one row, and it collapses multiple x-values (columns) into a single x-value.&lt;br /&gt;&lt;p&gt;This doesn't strictly follow any particular scaling algorithm, but it gets the job done quickly.  (And although the original implementation of this class only supported scaling &lt;i&gt;down&lt;/i&gt;, I've since revised it to scale up, too.)&lt;br /&gt;&lt;p&gt;The &lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/Scaling.html"&gt;&lt;code&gt;Scaling&lt;/code&gt;&lt;/a&gt; class offers a few convenient static methods for scaling that use the &lt;code&gt;ScalingIterator&lt;/code&gt;.&lt;br /&gt;&lt;h3&gt;Before and After&lt;/h3&gt;&lt;p&gt;Visually the results (when using the &lt;code&gt;Scaling&lt;/code&gt; class) are great:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://javagraphics.java.net/resources/scaling_compare.png"&gt;&lt;br /&gt;&lt;p&gt;Here's a closer look (the &lt;code&gt;Graphics2D&lt;/code&gt;-based version is on the left, the &lt;code&gt;Scaling&lt;/code&gt; version is on the right):&lt;br /&gt;&lt;br /&gt;&lt;img src="http://javagraphics.java.net/resources/scaling_compare2.png"&gt;&lt;br /&gt;&lt;br /&gt;The grass, the clouds, and the border are a lot less jagged when the &lt;code&gt;Scaling&lt;/code&gt; class is used.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Performance Comparison&lt;/h3&gt;&lt;p&gt;How does this class perform? I needed to compare my new &lt;code&gt;Scaling&lt;/code&gt; methods against other antialiased approaches.  Here are the three possible scaling models I came up with:&lt;br /&gt;&lt;li&gt;&lt;b&gt;Scaling&lt;/b&gt;: using the static methods in the &lt;a href="http://javagraphics.java.net/doc/com/bric/image/pixel/Scaling.html"&gt;&lt;code&gt;Scaling&lt;/code&gt;&lt;/a&gt; class.&lt;br /&gt;&lt;li&gt;&lt;b&gt;GraphicsUtilities&lt;/b&gt;: using Romain Guy's &lt;a href="http://www.java2s.com/Open-Source/Java-Document/Swing-Library/swingx/org/jdesktop/swingx/graphics/GraphicsUtilities.java.htm"&gt;GraphicsUtilities&lt;/a&gt; class.&lt;br /&gt;&lt;li&gt;&lt;b&gt;Image.getScaledInstance&lt;/b&gt;: calling &lt;a href="http://download.oracle.com/javase/6/docs/api/java/awt/Image.html#getScaledInstance%28int,%20int,%20int%29"&gt;Image.getScaledInstance()&lt;/a&gt; on a &lt;code&gt;BufferedImage&lt;/code&gt;.&lt;/li&gt;&lt;br /&gt;&lt;p&gt;The &lt;a href="http://java.net/projects/javagraphics/sources/svn/content/trunk/tests/com/bric/image/pixel/ScalingDemo.java"&gt;ScalingDemo&lt;code&gt;&lt;/code&gt;&lt;/a&gt; app compares these three methods by scaling an image of increasing size to 25%.  Here are the results on my Mac 10.5 machine running Java 1.6:&lt;br /&gt;&lt;script type="text/javascript" src="//ajax.googleapis.com/ajax/static/modules/gviz/1.0/chart.js"&gt; {"dataSourceUrl":"//spreadsheets.google.com/tq?key=0AijNZxvkIDdOdEdOV2l6dlB0elZ0T3hqZmhIajNKdEE&amp;transpose=0&amp;headers=1&amp;range=A2%3AD12&amp;gid=0&amp;pub=1","options":{"reverseCategories":false,"fontColor":"#fff","midColor":"#36c","backgroundColor":"#FFFFFF","pointSize":0,"headerColor":"#3d85c6","headerHeight":40,"is3D":false,"logScale":false,"hAxis":{"maxAlternation":1},"wmode":"opaque","title":"Performance of Antialiased Scaling","mapType":"hybrid","isStacked":false,"showTip":true,"displayAnnotations":true,"titleY":"Time (ms)","dataMode":"markers","titleX":"Width and Height (pixels)","colors":["#3366CC","#DC3912","#FF9900","#109618","#990099","#0099C6","#DD4477","#66AA00","#B82E2E","#316395"],"smoothLine":true,"maxColor":"#222","lineWidth":2,"labelPosition":"right","fontSize":"14px","hasLabelsColumn":true,"maxDepth":2,"interpolateNulls":false,"legend":"bottom","allowCollapse":true,"minColor":"#ccc","reverseAxis":false,"width":600,"height":371},"state":{},"chartType":"LineChart","chartName":"Chart 1"} &lt;/script&gt;&lt;br /&gt;&lt;p&gt;The &lt;code&gt;getScaledInstance()&lt;/code&gt; method is clearly the loser in this race.  This is unfortunately because it's the only method that's built-in, but developers have been warning against using this method for years: it's practically deprecated.&lt;br /&gt;&lt;p&gt;Of the other two methods: they're practically the same regarding speed. More importantly: I'm confident that the &lt;code&gt;Scaling&lt;/code&gt; approach is more efficient when it comes to memory allocation than the &lt;code&gt;GraphicsUtilities&lt;/code&gt; approach: so these facts combined validate this experiment for me.&lt;br /&gt;&lt;p&gt;The results here may not dazzle you at first, but this undertaking is a stepping stone for a couple of other scaling projects: see &lt;a href="http://javagraphics.blogspot.com/2011/05/images-scaling-jpegs-and-pngs.html"&gt;this article about JPEGs and PNGs&lt;/a&gt; and &lt;a href="http://javagraphics.blogspot.com/2010/06/images-bmps-and-thumbnails.html"&gt;this article about BMPs&lt;/a&gt; for details.&lt;br /&gt;&lt;p&gt;All the classes mentioned here are available in &lt;a href="http://javagraphics.java.net/jars/Scaling.jar"&gt;this jar&lt;/a&gt; (source included).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-8281756957059682591?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/8281756957059682591/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2010/06/images-scaling-down.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8281756957059682591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8281756957059682591'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2010/06/images-scaling-down.html' title='Images: Scaling Down'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-3633782622366558432</id><published>2010-03-28T16:18:00.000-07:00</published><updated>2011-02-21T12:48:19.028-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='interface'/><category scheme='http://www.blogger.com/atom/ns#' term='Shapes'/><category scheme='http://www.blogger.com/atom/ns#' term='bezier'/><title type='text'>Shapes: Bezier Control Points &amp; Data Models</title><content type='html'>This article explores a GUI that manipulates a shape with bezier shape segments.&lt;br /&gt;&lt;p&gt;The end result is the following applet.  To use this applet:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Click the "Create New Path" button.&lt;br /&gt;&lt;li&gt;Click and drag in the large empty &lt;code&gt;JComponent&lt;/code&gt; below the controls.&lt;br /&gt;&lt;li&gt;Repeat the previous step until you like your shape.&lt;br /&gt;&lt;li&gt;Double-click, or click "Create New Path" to leave path-creation mode.&lt;br /&gt;&lt;li&gt;Interact with the square and circular handles in the workspace.&lt;br /&gt;&lt;li&gt;Try fiddling with the scale and constraints.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br&gt;&lt;APPLET CODE="com/bric/geom/CubicPathDemo" CODEBASE="http://javagraphics.java.net/jars/"  width=420 height=586&gt;     &lt;PARAM NAME="archive" VALUE="CubicPath.jar"&gt;&lt;br/&gt;&lt;/APPLET&gt;&lt;br /&gt;You can download this jar (source included) &lt;a href="http://javagraphics.java.net/jars/CubicPath.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;h3&gt;Data Models&lt;/h3&gt;&lt;br /&gt;The &lt;a href="http://java.sun.com/javase/6/docs/api/java/awt/geom/GeneralPath.html"&gt;GeneralPath&lt;/a&gt; (or &lt;a href="http://java.sun.com/javase/6/docs/api/java/awt/geom/Path2D.html"&gt;Path2D&lt;/a&gt; in Java 1.6) is the simplest way to represent an arbitrary shape in Java.&lt;br /&gt;&lt;p&gt;However for the applet above we need something a lot more specific.  We need:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A mechanism to change curve data after it has been added.&lt;br /&gt;&lt;li&gt;A model that emphasizes a shape as a list of nodes, with optional control points before and after each node.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;Also the book &lt;a href="http://www.amazon.com/About-Face-2-0-Essentials-Interaction/dp/0764526413"&gt;About Face&lt;/a&gt; mentions:&lt;br /&gt;&lt;blockquote&gt;The closer the represented model comes to the user's mental model, the easier he will find the program to use and to understand.&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;In my case "the user" is actually the developer who wants to use the classes I write.  I want to obscure from the developer that a &lt;code&gt;PathIterator&lt;/code&gt; is really going to define a cubic segment #N with 3 points:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The second control point for the (n-1)th point.&lt;br /&gt;&lt;li&gt;The first control point for the nth point.&lt;br /&gt;&lt;li&gt;The nth point.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;To mimick the GUI presented in the applet I defined the &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/geom/CubicPath.html"&gt;CubicPath&lt;/a&gt;&lt;/code&gt;.  With this class you can iterate through the data in a shape in a couple of ways:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;for(int nodeIndex = 0; nodeIndex &amp;lt; myPath.getNodeCount(); nodeIndex++) {&lt;br /&gt; myPath.getNode(nodeIndex, dest);&lt;br /&gt; //do something&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;... or:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;for(int pathIndex = 0; pathIndex &amp;lt; myPath.getPathCount(); pathIndex++) {&lt;br /&gt; for(int nodeIndex = 0; nodeIndex &amp;lt; myPath.getNodeCount(); nodeIndex++) {&lt;br /&gt;  myPath.getNode(pathIndex, nodeIndex, dest);&lt;br /&gt;  //do something&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Similarly there are methods for &lt;code&gt;getNextControlForNode(nodeIndex)&lt;/code&gt; and &lt;code&gt;getPrevControlForNode()&lt;/code&gt;.&lt;br /&gt;&lt;p&gt;(In this case "next" means "defined after" and "prev" means "defined before".)&lt;br /&gt;&lt;p&gt;It is possible for control points to be &lt;code&gt;null&lt;/code&gt;.  In the absence of control points on either side of a segment: that segment will look like a line.&lt;br /&gt;&lt;p&gt;(Also, deep down the &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/geom/SimplifiedPathIterator.html"&gt;SimplifiedPathIterator&lt;/a&gt;&lt;/code&gt; is used so if possible a cubic segment &lt;i&gt;will&lt;/i&gt; be converted to a line in the &lt;code&gt;PathIterator&lt;/code&gt;.  But this is exactly the kind of implementation detail I'm trying to bury, so pretend I didn't say anything.)&lt;br /&gt;&lt;h3&gt;Scaling Factor&lt;/h3&gt;&lt;br /&gt;I applied the first draft of the &lt;code&gt;CubicPath&lt;/code&gt; to a project at work, and I was surprised that one my boss's first critiques was: the handles are too sensitive.  That is: in order to get certain interesting curves you have the drag the handles outside of the &lt;code&gt;JComponent&lt;/code&gt;.&lt;br /&gt;&lt;p&gt;At work we've modeled cubic-based shapes before, and a long time ago (in a very different, complicated architecture) we did in fact scale the vector the control points form with the actual end point.  I don't think we scaled it by very much, and I was impressed my boss immediately noticed this contrast in the two models.&lt;br /&gt;&lt;p&gt;So to address this problem I added the &lt;code&gt;scalingFactor&lt;/code&gt; field in the &lt;code&gt;CubicPath&lt;/code&gt;.  (See &lt;code&gt;setScaleFactor&lt;/code&gt; and &lt;code&gt;getScaleFactor&lt;/code&gt;).  I'd have to fumble around for the right words to mathematically/programmatically express exactly what it does: but if you play around with this feature in the applet above it should be pretty self explanatory.&lt;br /&gt;&lt;p&gt;It in no way affects the underlying shape; but it does affect the points returned by &lt;code&gt;getNextControlForNode()&lt;/code&gt; and &lt;code&gt;getPrevControlForNode()&lt;/code&gt;.  This lets you adjust the "sensitivity" of the handles.&lt;br /&gt;&lt;h3&gt;Continuous Curves&lt;/h3&gt;&lt;br /&gt;In order to maintain continuous curves: the two control points surrounding a node must be collinear.  Their magnitudes can vary, but their angle needs to be the same.&lt;br /&gt;&lt;p&gt;To help in this area I added a couple extra methods (see the "Constraint" feature in the applet to try these out):&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;setNextControlForNodeFromPrev(int nodeIndex,boolean includeDistance);&lt;br /&gt;setPrevControlForNodeFromNext(int nodeIndex,boolean includeDistance);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;So if you just defined the previous control point for a node, you can call &lt;code&gt;setNextControlForNodeFromPrev&lt;/code&gt; to make sure the next control point matches the previous (and vice versa).&lt;br /&gt;&lt;p&gt;These methods will always force the angles of the control points to match.  The boolean &lt;code&gt;includeDistance&lt;/code&gt; controls whether the magnitude is supposed to match, too.&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;Like all my blog articles: this may be revised in coming days/weeks after I first publish it.  But it is my hope that this is a solid interface for designing &lt;code&gt;java.awt.Shapes&lt;/code&gt; that will really simplify my life.  My goal is to never have to delve into this business again.  Hopefully if this meets your needs you can use it and save some time, too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-3633782622366558432?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/3633782622366558432/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2010/03/shapes-bezier-control-points-data.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3633782622366558432'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3633782622366558432'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2010/03/shapes-bezier-control-points-data.html' title='Shapes: Bezier Control Points &amp; Data Models'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-96104854160542006</id><published>2010-03-21T13:53:00.000-07:00</published><updated>2011-05-08T23:21:11.108-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='image'/><category scheme='http://www.blogger.com/atom/ns#' term='exif'/><category scheme='http://www.blogger.com/atom/ns#' term='thumbnail'/><category scheme='http://www.blogger.com/atom/ns#' term='JPEG'/><title type='text'>Images: Reading JPEG Thumbnails</title><content type='html'>&lt;p&gt;The problem: I want to create thumbnails for a folder of JPEGs.  Many of these JPEGs came from a digital camera, so they're several thousand pixels wide.  Loading the entire image and then scaling down to a 64x64 thumbnail sounds pretty wasteful: what else can I do?  I'm 99% sure most digital cameras these days are embedding thumbnails inside the JPEG files for just this kind of problem...&lt;br /&gt;&lt;br&gt;&lt;br /&gt;&lt;h3&gt;Discussion&lt;/h3&gt;&lt;br /&gt;As far as I can tell: &lt;code&gt;&lt;a href="http://java.sun.com/javase/6/docs/api/javax/imageio/ImageIO.html"&gt;ImageIO&lt;/a&gt;&lt;/code&gt; won't read JPEG thumbnails unless you have JAI installed?&lt;br /&gt;&lt;p&gt;I googled &lt;a href="http://www.google.com/search?q=java+example%3A+ImageIO+reading+thumbnail&amp;ie=utf-8&amp;oe=utf-8&amp;aq=t&amp;rls=org.mozilla:en-US:official&amp;client=firefox-a"&gt;"java example: ImageIO reading thumbnail"&lt;/a&gt; to see what I could find on the subject:&lt;br /&gt;&lt;p&gt;The &lt;a href="http://java.sun.com/j2se/1.5.0/docs/guide/imageio/spec/apps.fm3.html"&gt;first link&lt;/a&gt; was from Sun, and it said this:&lt;br /&gt;&lt;blockquote&gt;&lt;b&gt;3.3.4  Reading "Thumbnail" Images&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;Some image formats allow a small preview image (or multiple previews) to be stored alongside the main image. These "thumbnail" images are useful for identifying image files quickly, without the need to decode the entire image.&lt;br /&gt;&lt;br /&gt;Applications can determine how many thumbnail images associated with a particular image are available by calling:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;reader.getNumThumbnails(imageIndex);&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If a thumbnail image is present, it can be retrieved by calling:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;int thumbailIndex = 0;&lt;br /&gt;BufferedImage bi;&lt;br /&gt;bi = reader.readThumbnail(imageIndex, thumbnailIndex);&lt;/pre&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Right.  Good.  This makes sense.  The problem is: it doesn't seem to really work.&lt;br /&gt;&lt;p&gt;The &lt;a href="http://forums.java.net/jive/thread.jspa?messageID=373652"&gt;second link&lt;/a&gt; was much more insightful.  A developer wrote:&lt;br /&gt;&lt;blockquote&gt;I should have remarked that the JAI Image I/O Tools JPEG reader supports via the thumbnail method calls all thumbnails embedded in the JFIF APP0, JFXX APP0, and EXIF APP1 marker segments. Please see this javadoc for more information:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://download.java.net/media/jai-imageio/javadoc/1.1/overview-summary.html#JPEG&lt;br /&gt;"&gt;http://download.java.net/media/jai-imageio/javadoc/1.1/overview-summary.html#JPEG&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I think that the only thumbnails supported by the Java SE Image I/O JPEG reader via the thumbnail method calls are those in the JFIF and JFXX marker segments. If you are unable to use JAI Image I/O Tools for some reason you could however derive the EXIF thumbnail by parsing the contents of the "unknown" node in the image metadata corresponding to the EXIF APP1 marker segment.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;I think he just summed up the problem I was seeing.  If I run this code (without JAI installed), I get an exception:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;Iterator iterator = ImageIO.getImageReadersBySuffix("jpeg");&lt;br /&gt;while(iterator.hasNext()) {&lt;br /&gt; ImageReader reader = (ImageReader)iterator.next();&lt;br /&gt; try {&lt;br /&gt;  reader.setInput( ImageIO.createImageInputStream(jpeg) );&lt;br /&gt;  BufferedImage thumbnail = reader.readThumbnail(0, 0);&lt;br /&gt; } catch(Exception e) {&lt;br /&gt;  e.printStackTrace();&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;(The exception I get is: &lt;code&gt;java.lang.IndexOutOfBoundsException: No such thumbnail at com.sun.imageio.plugins.jpeg.JPEGImageReader.readThumbnail(JPEGImageReader.java:1354)&lt;/code&gt;.  Which makes sense based on what the second link said.)&lt;br /&gt;&lt;p&gt;Now I have nothing against &lt;code&gt;JAI&lt;/code&gt; itself.  But when I went to &lt;a href="http://java.sun.com/products/java-media/jai/downloads/download-1_1_2.html"&gt;grab a copy&lt;/a&gt; just now: the jar was over 5 MB!&lt;br /&gt;&lt;p&gt;This is ridiculous.  For widgets I present in this blog I'm not going to try to attach a 5 MB jar.  (Is that even legal?  I didn't look up the licensing.)  Or worse: I'm not going to insist that the user separately download other packages to get my software to run well.  It should run well straight out of the box.&lt;br /&gt;&lt;p&gt;I decided to try to extract the thumbnails myself.  I did not want to write a fully-functional JPEG decoder (what are the odds I could compete with OS-specific optimized code that already exists?), but I might benefit from my own metadata retrieval.&lt;br /&gt;&lt;h3&gt;Research&lt;/h3&gt;&lt;br /&gt;The first thing I needed was test cases.  Lots of and lots of test cases.  In addition to all the JPEGs lying around on my computer, I wanted some public domain images I could write unit tests against.  I explored Flickr, and found &lt;a href="http://www.flickr.com/commons/usage/"&gt;a collection of images&lt;/a&gt; with no known copyright.  I saved a few of them &lt;a href="http://java.net/projects/javagraphics/sources/svn/show/trunk/tests/com/bric/image/resources"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;Next I'd need to roll up my sleeves and study file specifications.  My first search result pointed to &lt;a href="http://www.w3.org/Graphics/JPEG/jfif3.pdf"&gt;this specifications PDF&lt;/a&gt;, but it is dated to 1992.  I did more research and found that &lt;a href="http://www.exif.org/Exif2-2.PDF"&gt;this PDF&lt;/a&gt; is a better fit.&lt;br /&gt;&lt;p&gt;A JPEG is (until the image data begins) clearly separated into discrete chunks of data.  So modeled after the &lt;code&gt;ZipInputStream&lt;/code&gt;, I wrote the &lt;a href="http://java.net/projects/javagraphics/sources/svn/content/trunk/src/com/bric/image/jpeg/JPEGMarkerInputStream.java"&gt;&lt;code&gt;JPEGMarkerInputStream&lt;/code&gt;&lt;/a&gt; to read each chunk separately.&lt;br /&gt;&lt;p&gt;According to the specifications: there are two types of markers that are supposed to provide all our thumbnails: APP0 and APP1.&lt;br /&gt;&lt;p&gt;The &lt;a href="http://download.java.net/media/jai-imageio/javadoc/1.1/overview-summary.html#JPEG"&gt;JAI documentation&lt;/a&gt; confirms this:&lt;br /&gt;&lt;blockquote&gt;The [JAI] JPEG reader supports thumbnails. These may be derived from JFIF APP0, JFXX APP0, or EXIF APP1 marker segments.&lt;/blockquote&gt;&lt;br /&gt;&lt;h4&gt;About the APP0 Block&lt;/h4&gt;&lt;br /&gt;This marker was introduced in the original 1992 specification.  It includes lots of basic metadata (resolution, version info), and can optionally include a thumbnail.  &lt;br /&gt;&lt;p&gt;However none of my sample images use this marker to embed a thumbnail.  I even branched out and scanned every JPEG on my computer: not one in over 11,000 JPEGs embedded a thumbnail this way.  Technically the package I introduce later should support these thumbnails, but since I have zero test cases to work with: I can't be certain I parse them correctly.&lt;br /&gt;&lt;p&gt;At this point I believe it is safe to say: these are (at best) extremely rare in the real world.&lt;br /&gt;&lt;h4&gt;About the APP1 Block&lt;/h4&gt;&lt;br /&gt;This block is the trove of information.  This includes designated (but optional) fields for everything you can imagine: width, resolution, aperture value, the encoding software name, copyright info, etc.  Also it includes a pointer to a mini-JPEG file (still in the APP1 block) to serve as a thumbnail.&lt;br /&gt;&lt;h4&gt;A Surprise Block&lt;/h4&gt;&lt;br /&gt;... but as I was exploring my test files, I found several other markers.  These were often application-specific.  I don't have specifications for most of these, and I didn't invest too much time in trying to figure them out.  However one started to stand out: the APP13 marker.&lt;br /&gt;&lt;p&gt;This marker is supposedly written by Adobe.  &lt;a href="http://www.ozhiker.com/electronics/pjmt/jpeg_info/app_segments.html"&gt;One page&lt;/a&gt; refers to this as the "Adobe IRB" block, where I assume (from googling) that "IRB" stands for "Image Resource Block".  What's interesting about this block is that it &lt;i&gt;also&lt;/i&gt; appears to contain a mini-JPEG.  In fact: if a test image contained only 1 thumbnail, it was several times more likely to contain a thumbnail in an APP13 block than an APP1 block.&lt;br /&gt;&lt;br&gt;&lt;img src="https://spreadsheets.google.com/spreadsheet/oimg?key=0AijNZxvkIDdOdDgyR3NxbGhjbGNtMFNoTTRuUlNrVUE&amp;oid=1&amp;zx=3xc3ge4cnayg" /&gt;&lt;br /&gt;&lt;br&gt;So I invested time in parsing this block, too.  (If I ignored this block I'd be missing nearly 1/3 of my possible thumbnails!)&lt;br /&gt;&lt;p&gt;To be fair: no one collection of test cases can represent "the majority" of JPEGs in the world.  It might be the case that my collection is extremely skewed, and on your computer not a single JPEG has an APP13 marker.  But still: this seemed like a lead worth following.&lt;br /&gt;&lt;p&gt;To be safe, I wanted to check to see if &lt;i&gt;other&lt;/i&gt; markers also existed that embedded mini-JPEG thumbnails.  I added a unit test that searched all my test files for the number of occurrences of the marker 0xFFD8 (the start-of-image marker): the results indicate that I'm recording all the mini-JPEGs embedded in my test cases. (But if a thumbnail is encoded in another format -- such as a PNG -- then I may still be missing it.)&lt;br /&gt;&lt;h3&gt;Results&lt;/h3&gt;&lt;br /&gt;Finally I put all this together in the &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/image/jpeg/JPEGMetaData.html"&gt;JPEGMetaData&lt;/a&gt;&lt;/code&gt; class.  You can download this class (source included) &lt;a href="http://javagraphics.java.net/jars/JPEGMetaData.jar"&gt;here&lt;/a&gt;  &lt;br /&gt;&lt;p&gt;I casually added methods to retrieve properties and comments, too, because they were trivial to parse.  Unfortunately what isn't easy to parse is: the dimensions of the actual image! (Or maybe someone reading this can cue me in to where to find this information?  For now I'll continue to use my &lt;code&gt;&lt;a href="https://java.net/svn/javagraphics~svn/trunk/src/com/bric/image/ImageSize.java"&gt;ImageSize&lt;/a&gt;&lt;/code&gt;&lt;br /&gt; class to fetch this information without actually loading the image.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-96104854160542006?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/96104854160542006/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2010/03/images-reading-jpeg-thumbnails.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/96104854160542006'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/96104854160542006'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2010/03/images-reading-jpeg-thumbnails.html' title='Images: Reading JPEG Thumbnails'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-2896828218322594079</id><published>2010-03-15T14:40:00.000-07:00</published><updated>2011-02-21T13:00:32.509-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PathIterator'/><category scheme='http://www.blogger.com/atom/ns#' term='Parametric graph'/><category scheme='http://www.blogger.com/atom/ns#' term='Shape'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='graph'/><category scheme='http://www.blogger.com/atom/ns#' term='bezier'/><title type='text'>Shapes: Parametric Equations (and Spirals!)</title><content type='html'>This article is split up in 2 parts:&lt;br /&gt;&lt;ol&gt;&lt;LI&gt;Converting a parametric graph into a &lt;code&gt;PathIterator&lt;/code&gt;&lt;br /&gt;&lt;LI&gt;An applet demoing a &lt;code&gt;Spiral2D&lt;/code&gt; class&lt;/LI&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h3&gt;Parametric Equations and PathIterators&lt;/h3&gt;&lt;br /&gt;Suppose you have a parametric graph expressed as &lt;code&gt;x(t)&lt;/code&gt; and &lt;code&gt;y(t)&lt;/code&gt;: how do you draw that in Java?&lt;br /&gt;&lt;P&gt;We need a &lt;code&gt;java.awt.geom.PathIterator&lt;/code&gt;.  With a &lt;code&gt;PathIterator&lt;/code&gt;, we can call:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;GeneralPath path = new GeneralPath();&lt;/code&gt;&lt;br /&gt;path.append(myPathIterator, false);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;(A separate blog article will cover actually making &lt;code&gt;java.awt.Shape&lt;/code&gt;... this article already has a lot of ground to cover, so that comes later.)&lt;br /&gt;&lt;P&gt;The graph needs to be broken up into segments for the &lt;code&gt;PathIterator&lt;/code&gt;.  Specifically we should use cubic path segments.  A cubic segment lets you supply 4 pieces of information.  In this case those four pieces of information should be:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;f(0) = v0&lt;br /&gt;f(1) = v1&lt;br /&gt;df/dt(0) = dv0&lt;br /&gt;df/dt(1) = dv1&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Having these 4 pieces lets us define the end points &lt;i&gt;and&lt;/i&gt; the angles/tangents at each end point.  Those angles are crucial to achieve a continuous-looking curve between segments.&lt;br /&gt;&lt;P&gt;Of course this is a parametric graph, so we have an x-equation and a y-equation -- and they can be treated in isolation.&lt;br /&gt;&lt;P&gt;The article &lt;a href="http://javagraphics.blogspot.com/2010/03/shapes-introduction.html"&gt;"Shapes: an Introduction"&lt;/a&gt; explains how to convert bezier control points into an algebraic expression:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;double ax = -lastX+3*x1-3*x2+x3;&lt;br /&gt;double bx = 3*lastX-6*x1+3*x2;&lt;br /&gt;double cx = -3*lastX+3*x1;&lt;br /&gt;double dx = lastX;&lt;br /&gt;//and x(t) = ax*(t^3)+bx*(t^2)+cx*t+dx&lt;br /&gt;//and dx/dt(t) = 3*ax*(t^2)+2*bx*t+cx&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;So this is how each cubic segment is structured.  Our task here is to calculate lastX, x1, x2 and x3.  Our givens are:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;x(0) = dx&lt;br /&gt;x(1) = ax+bx+cx+dx&lt;br /&gt;dx/dt(0) = cx&lt;br /&gt;dx/dt(1) = 3*ax+2*bx+cx&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Substitute our coefficients and reduce those expressions:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;x(0) = lastX&lt;br /&gt;x(1) = x3&lt;br /&gt;dx/dt(0) = -3*lastX+3*x1&lt;br /&gt;dx/dt(1) = -3*x2+3*x3&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Now in this case we'll know our parametric functions; what we need to find are the points to define the bezier curve.  So to isolate the x-variables:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;lastX = x(0)&lt;br /&gt;x3 = x(1)&lt;br /&gt;x1 = (dx/dt(0)+3*x(0))/3&lt;br /&gt;x2 = (3*x(1)-dx/dt(1))/3&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;I wrapped this up in a nice abstract class: the &lt;code&gt;&lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/geom/ParametricPathIterator.java?&amp;view=markup"&gt;ParametricPathIterator&lt;/a&gt;&lt;/code&gt;.  The core methods you need to implement are:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;protected abstract double getX(double t);&lt;br /&gt;protected abstract double getY(double t);&lt;br /&gt;protected abstract double getDX(double t);&lt;br /&gt;protected abstract double getDY(double t);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Also you're responsible for the methods &lt;code&gt;getMaxT()&lt;/code&gt; and &lt;code&gt;getNextT(double prevT)&lt;/code&gt;.&lt;br /&gt;&lt;br&gt;&lt;h3&gt;Spirals&lt;/h3&gt;&lt;br /&gt;All this originally came up when a coworker recently asked for a &lt;code&gt;Spiral2D&lt;/code&gt; class.  He found one from a programming book that provided an example, but:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;It was line-based, so it looked bad.&lt;br /&gt;&lt;li&gt;As per all examples in this book, the license to use this would be $500 for our company.  (OK, really this should be problem #1.)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;So having fleshed out the &lt;code&gt;ParametricPathIterator&lt;/code&gt;, this is a really easy undertaking.  What is a spiral?&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;x(t) = centerX+coilGap*t*cos(2*pi*t)&lt;br /&gt;y(t) = centerY+coilGap*t*sin(2*pi*t)&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;The derivative is a little trickier.  I had to refresh my memory on the &lt;a href="http://en.wikipedia.org/wiki/Product_rule"&gt;product rule&lt;/a&gt; to really get this right:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;dx/dt = coilGap*cos(2*pi*t)-2*pi*coilGap*t*sin(2*pi*t)&lt;br /&gt;dy/dt = coilGap*sin(2*pi*t)-2*pi*coilGap*t*cos(2*pi*t)&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Now in this case -- and in more complicated cases -- you don't necessarily need to calculate the derivative.  If you wanted to be lazy you could just write this method:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;protected double getDX(double t) {&lt;br /&gt;   double incr = .0000001;&lt;br /&gt;   return (getX(v+incr/2)-getX(v-incr/2))/incr;&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;This is easier to program, but it will introduce some rounding errors as values get really small.  Maybe there's not that much of a difference in practice?  That's up to you to decide.&lt;br /&gt;&lt;P&gt;So that's the math.  Codewise, these are the controls I wanted:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;center&lt;/b&gt; (Point2D): the center of this spiral.&lt;br /&gt;&lt;li&gt;&lt;b&gt;coilGap&lt;/b&gt; (double): the space between two coils.&lt;br /&gt;&lt;li&gt;&lt;b&gt;coils&lt;/b&gt; (double): the number of coil revolutions.&lt;br /&gt;&lt;li&gt;&lt;b&gt;clockwise&lt;/b&gt; (boolean): if this is false, we multiply the t expression in sine/cosine by negative one.&lt;br /&gt;&lt;li&gt;&lt;b&gt;offset&lt;/b&gt; (double): this is an angular offset in the sine/cosine.&lt;br /&gt;&lt;li&gt;&lt;b&gt;outward&lt;/b&gt; (boolean): this is a really subtle control.  If this is &lt;code&gt;true&lt;/code&gt; then the spiral will start at the center and spiral outward; if it is &lt;code&gt;false&lt;/code&gt; it starts outside and spirals inward.  This will not make a visual difference if you just want to "draw a spiral".&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;What's left to do?  Not much.  I need constructors, and a lot of getters and setters.  The only thing that I haven't mentioned are the abstract methods &lt;code&gt;getNextT()&lt;/code&gt; and &lt;code&gt;getMaxT()&lt;/code&gt;.  In this case these are really simple:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;protected double getMaxT() {&lt;br /&gt;   return coils;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;protected double getNextT(double t) {&lt;br /&gt;   //8 partitions in a coil&lt;br /&gt;   return t + 1.0/8.0;&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;The number of partitions was arbitrary, but I found that if used 4 partitions in coil it didn't look right.  A little more detail made a big visual difference.&lt;br /&gt;&lt;P&gt;Here is an applet that shows off this project:&lt;br /&gt;&lt;BR&gt;&lt;br /&gt;&lt;APPLET CODE="com/bric/geom/Spiral2DDemo" CODEBASE="http://javagraphics.java.net/jars/" width=450 height=420&gt;     &lt;PARAM NAME="archive" VALUE="Spiral2D.jar"&gt;&lt;br/&gt;&lt;/APPLET&gt;&lt;br /&gt;This applet (source included) is available &lt;a href="http://javagraphics.java.net/jars/Spiral2D.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;p.s. Try clicking in the applet to define the endpoint for the spiral.  (Also try using the shift key.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-2896828218322594079?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/2896828218322594079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2010/03/shapes-parametric-equations-and-spirals.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2896828218322594079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2896828218322594079'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2010/03/shapes-parametric-equations-and-spirals.html' title='Shapes: Parametric Equations (and Spirals!)'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-2321777579447517117</id><published>2010-03-11T18:43:00.000-08:00</published><updated>2010-03-21T01:10:58.105-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PathIterator'/><category scheme='http://www.blogger.com/atom/ns#' term='Shapes'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><category scheme='http://www.blogger.com/atom/ns#' term='bezier'/><title type='text'>Shapes: An Introduction</title><content type='html'>I try to sprinkle in some fun open-source code snippets in most of my articles -- but this article is different.  This article is simply an overview of the &lt;code&gt;java.awt.Shape&lt;/code&gt; object.  Several other articles build on (or assume knowledge of) what I cover here.&lt;br /&gt;&lt;h3&gt;What is a &lt;code&gt;java.awt.Shape&lt;/code&gt;?&lt;/h3&gt;&lt;br /&gt;When I refer to a "shape" in Java, I'm probably referring to the &lt;code&gt;&lt;a href="http://java.sun.com/javase/6/docs/api/java/awt/Shape.html"&gt;java.awt.Shape&lt;/a&gt;&lt;/code&gt; interface.  The most interesting method in this class is &lt;code&gt;getPathIterator(AffineTransform)&lt;/code&gt; -- the results of all the other methods can be derived from this method.&lt;br /&gt;&lt;h3&gt;OK, so what is a &lt;code&gt;java.awt.geom.PathIterator&lt;/code&gt;?&lt;/h3&gt;&lt;br /&gt;This is the heart of shape information in Java.  This iterator is similar to the human gesture of dragging a pen from one point in space to another.  A path is a series of segments, and there are 5 basic types of segments:&lt;br /&gt;&lt;ol&gt;&lt;LI&gt;SEG_MOVETO: this is the initial position of a path.  Each path must begin with this segment.  If a MOVETO segment is not followed by any other segments, though: that path is considered empty.  You might get a dot if you called &lt;code&gt;Graphics2D.draw(shape)&lt;/code&gt;, but you won't get anything if you call &lt;code&gt;Graphics2D.fill(shape)&lt;/code&gt;.&lt;br /&gt;&lt;LI&gt;SEG_LINETO: this segment requires an x-coordinate and a y-coordinate.  This connects a line from the last path location to the point indicated.&lt;br /&gt;&lt;LI&gt;SEG_QUADTO: this segment requires two points: the end point and a control point (more on control points later.)&lt;br /&gt;&lt;LI&gt;SEG_CUBICTO: this segment requires three points: two control points and an end point.&lt;br /&gt;&lt;LI&gt;SEG_CLOSE:  this is an optional segment that connects the previous segment to the SEG_MOVETO that began this shape.  Even if you draw 4 sides of a square, you need to add a SEG_CLOSE for some &lt;code&gt;java.awt.Strokes&lt;/code&gt; to correctly render the tips of all corners.&lt;/LI&gt;&lt;/ol&gt;&lt;br /&gt;&lt;P&gt;A single &lt;code&gt;PathIterator&lt;/code&gt; can contain any number of MOVETO's.  Each MOVETO is equivalent to lifting your pen off a piece of paper and repositioning it to start a new segment.&lt;br /&gt;&lt;h3&gt;Bezier Control Points&lt;/h3&gt;&lt;br /&gt;Quadratic and cubic segments use control points.  These are ingenious points that are used to tug an existing path in certain directions.  There's a great &lt;a href="http://en.wikipedia.org/wiki/B%C3%A9zier_curve"&gt;wikipedia page&lt;/a&gt; that describes this concept in detail (with diagrams and everything).  What I want to do is focus on the math.  I'll break this up into the quadratic and the cubic cases:&lt;br /&gt;&lt;h3&gt;Quadratic Parametric Equations&lt;/h3&gt;&lt;br /&gt;The &lt;a href="http://java.sun.com/javase/6/docs/api/java/awt/geom/PathIterator.html#SEG_QUADTO"&gt;javadocs&lt;/a&gt; are the place to start when you want to study the math here.  They describe a quadratic segment as:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;P(t) = B(2,0)*CP + B(2,1)*P1 + B(2,2)*P2&lt;br /&gt;       0 &lt;= t &lt;= 1&lt;br /&gt;&lt;br /&gt;B(n,m) = mth coefficient of nth degree Bernstein polynomial&lt;br /&gt;       = C(n,m) * t^(m) * (1 - t)^(n-m)&lt;br /&gt;C(n,m) = Combinations of n things, taken m at a time&lt;br /&gt;       = n! / (m! * (n-m)!)&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;This sounds a lot more complex than it actually is if you're willing to break it down.  We can get rid of C(n,m):&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;B(n,m) = mth coefficient of nth degree Bernstein polynomial&lt;br /&gt;       = [ ( n! / (m! * (n-m)!) ) * t^(m) * (1 - t)^(n-m) ]&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;And then expand the first equation:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;P(t) = [ ( 2! / (0! * (2-0)!) ) * t^(0) * (1 - t)^(2-0) ]* CP +&lt;br /&gt;       [ ( 2! / (1! * (2-1)!) ) * t^(1) * (1 - t)^(2-1) ] * P1 +&lt;br /&gt;       [ ( 2! / (2! * (2-2)!) ) * t^(2) * (1 - t)^(2-2) ] * P2&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Simplify everything:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;P(t) = (1 - 2*t + t^2) * CP +&lt;br /&gt;       [ (2*t - 2*t^2) ] * P1 +&lt;br /&gt;       [ t^(2) ] * P2&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;This is still not really useful to me, though.  What I want is an expression in terms of t:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;P(t) = (CP-2*P1+P2)*(t^2) +&lt;br /&gt;       (-2*CP+2*P1)*t +&lt;br /&gt;       CP&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;In my code this usually manifests itself something like this:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;double ax = lastX-2*x1+x2;&lt;br /&gt;double bx = -2*lastX+2*x1;&lt;br /&gt;double cx = lastX;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Likewise you can replace "x" with "y" to get the y coefficients.  From here you can treat your bezier segment as a good ole parametric equation.  You can always spot-check your math (in both the quadratic and cubic case below) by checking the values at t=0 and t=1.  At t=0 you get &lt;code&gt;lastX&lt;/code&gt; -- which is the right starting point -- and at t=1 you simply add all those terms together -- and the terms cancel to &lt;code&gt;x2&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;I hardly ever use quadratic segments, though.  If the user needs more than lines, the cubic segment gives you a lot more flexibility...&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Cubic Parametric Equations&lt;/h3&gt;&lt;br /&gt;Again, we'll start by looking at the &lt;a href="http://java.sun.com/javase/6/docs/api/java/awt/geom/PathIterator.html#SEG_CUBICTO"&gt;javadocs&lt;/a&gt; for a cubic curve:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;P(t) = B(3,0)*CP + B(3,1)*P1 + B(3,2)*P2 + B(3,3)*P3&lt;br /&gt;0 &lt;= t &lt;= 1&lt;br /&gt;&lt;br /&gt;B(n,m) = mth coefficient of nth degree Bernstein polynomial&lt;br /&gt;       = C(n,m) * t^(m) * (1 - t)^(n-m)&lt;br /&gt;C(n,m) = Combinations of n things, taken m at a time&lt;br /&gt;       = n! / (m! * (n-m)!)&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Expand that:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;P(t) = [ 3! / (0! * (3-0)!) * t^(0) * (1 - t)^(3-0) ] * CP +&lt;br /&gt;       [ 3! / (1! * (3-1)!) * t^(1) * (1 - t)^(3-1) ] * P1 +&lt;br /&gt;       [ 3! / (2! * (3-2)!) * t^(2) * (1 - t)^(3-2) ] * P2 +&lt;br /&gt;       [ 3! / (3! * (3-3)!) * t^(3) * (1 - t)^(3-3) ] * P3 &lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Now simplify:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;P(t) = [ (1 - t)^3 ] * CP +&lt;br /&gt;       [ 3 * t^(1) * (1 - t)^2 ] * P1 +&lt;br /&gt;       [ 3 * t^(2) * (1 - t)^1 ] * P2 +&lt;br /&gt;       [ t^(3) ] * P3 &lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Expand the exponents:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;P(t) = [ (1 - 3*t + 3*t^2 - t^3) ] * CP +&lt;br /&gt;       [ (3*t - 6*t^2 + 3*t^3) ] * P1 +&lt;br /&gt;       [ (3*t^2 - 3*t^3)^1 ] * P2 +&lt;br /&gt;       [ t^(3) ] * P3 &lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;But what we really want is the equation in terms of t:&lt;br /&gt;Lastly:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;P(t) = [-CP+3*P1-3*P2+P3]*(t^3) + &lt;br /&gt;       [3*CP-6*P1+3*P2]*(t^2) +&lt;br /&gt;       [-3*CP+3P1]*t +&lt;br /&gt;       CP&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;So now we have the cubic equation expressing this curve in terms of T.  Here is how I'd usually integrate this into my code:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;double ax = -lastX+3*x1-3*x2+x3;&lt;br /&gt;double bx = 3*lastX-6*x1+3*x2;&lt;br /&gt;double cx = -3*lastX+3*x1;&lt;br /&gt;double dx = lastX;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;... and you can likewise define &lt;code&gt;ay&lt;/code&gt;, &lt;code&gt;by&lt;/code&gt;, &lt;code&gt;cy&lt;/code&gt; and &lt;code&gt;dy&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;This concludes the introduction; look up other articles that are prefaced with "Shape" to see possible uses for this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-2321777579447517117?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/2321777579447517117/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2010/03/shapes-introduction.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2321777579447517117'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2321777579447517117'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2010/03/shapes-introduction.html' title='Shapes: An Introduction'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-968011846648632440</id><published>2010-01-14T01:18:00.000-08:00</published><updated>2011-02-21T13:01:42.608-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Color'/><category scheme='http://www.blogger.com/atom/ns#' term='colorpalette'/><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='color palette'/><category scheme='http://www.blogger.com/atom/ns#' term='colorpicker'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='jcolorchooser'/><title type='text'>Colors: A Good GUI for Selecting Colors</title><content type='html'>This article explores the question: what is a good GUI for picking a color?&lt;br /&gt;&lt;h3&gt;Dialogs&lt;/h3&gt;&lt;br /&gt;The first instinct of many developers will be to use a dialog.  And there is a dialog that comes standard with Java that does the trick.  It takes one line to invoke:&lt;br /&gt;&lt;BR&gt;&lt;code&gt;Color newColor = JColorChooser.showDialog(component, title, initialColor)&lt;/code&gt;&lt;br /&gt;&lt;BR&gt;&lt;a href="http://javagraphics.java.net/resources/jcolorchooser.jpg"&gt;&lt;img src="http://javagraphics.java.net/resources/jcolorchooser.jpg" width="830" height="257"&gt;&lt;/a&gt;&lt;br /&gt;&lt;BR&gt;I have several complaints about this dialog:&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;The default tab is poorly designed.  Look at all that wasted vertical space!  And I don't like the strong shadows that surrounds each cell.&lt;br /&gt;&lt;LI&gt;The colorful components are not keyboard accessible.  You can navigate the dialog using the keyboard if you eventually tab to a text field or spinner: but then you have to choose your color by numbers.  Do you like choosing a color by numbers?  It seems more natural to choose a color from a set of colors.&lt;br /&gt;&lt;LI&gt;The dialog uses tabs.  This is perhaps acceptable on the grounds that this is intended to be a universal component.  But a better design would only present one suitable model.  The user's goal here is to select a color.  In this dialog they can get distracted with all the different components.  Depending on how you count them, I see at least 23 different components the user can click here: this is overdoing it.&lt;/LI&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a href="http://javagraphics.java.net/resources/colorpicker.png"&gt;&lt;img src="http://javagraphics.java.net/resources/colorpicker.png" width="330" height="287" align="right"&gt;&lt;/a&gt;This prompted me to create &lt;A href="http://javagraphics.blogspot.com/2007/04/jcolorchooser-making-alternative.html"&gt;a new dialog&lt;/A&gt;.  It's default view features a color wheel.  You can navigate it with the keyboard.  There are no tabs, but it does offer subtle controls to change the view.  This is modeled after what Photoshop uses, and they must know a thing or two about how professional artists want to choose a color, right?&lt;br /&gt;&lt;P&gt;However most users are not professional artists, so this is still overkill.  Also when possible it is better to avoid dialogs.  The book &lt;a href="http://www.amazon.com/About-Face-Essentials-Interface-Design/dp/1568843224"&gt;About Face&lt;/a&gt; makes these comments about dialogs:&lt;br /&gt;&lt;blockquote&gt;"A dialog box is another room; have a good reason to go there.&lt;br /&gt;&lt;BR&gt;... Build functions into the window where they are used.&lt;br /&gt;&lt;BR&gt;... Putting functions in a dialog box emphasizes their separateness from the main task."&lt;/blockquote&gt;&lt;br /&gt;&lt;h3&gt;Palettes&lt;/h3&gt;&lt;br /&gt;Instead I would like to focus on palettes.  A grid of colors.  This could be presented in a small &lt;code&gt;JWindow&lt;/code&gt;, or it could be presented in a &lt;code&gt;JPopupMenu&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;Here size is everything.  The key difference in this concept and the dialogs mentioned before is that this is &lt;i&gt;small&lt;/i&gt;.  In nuanced jargon: this is a "transient", and the previous two dialogs are more "sovereign".  This is one component - not several.&lt;br /&gt;&lt;P&gt;Here is an applet that provides several consecutive drafts of different palettes:&lt;br /&gt;&lt;APPLET CODE="com/bric/plaf/demo/ColorPaletteDemo" CODEBASE="http://javagraphics.java.net/jars/" width=500 height=460&gt;     &lt;PARAM NAME="archive" VALUE="ColorPalette.jar"&gt;&lt;br/&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;This applet (source included) is available &lt;a href="http://javagraphics.java.net/jars/ColorPalette.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;P&gt;Here is a brief history of each draft:&lt;br /&gt;&lt;OL&gt;&lt;LI&gt;This was my first reaction to the words "pick a color".  Every pixel counts.  You have thousands of colors here to choose from.  Not every color is represented, but you get light and dark shades that really stand out.  (Compared to less saturated, muddy shades.)&lt;br /&gt;&lt;LI&gt;But quickly someone pointed out: this doesn't have black and white!  Those are very important colors.  As are shades of gray.  So the second draft included a special column for white/black/gray.&lt;br /&gt;&lt;LI&gt;We had this model deployed in a desktop application for a while.  Eventually the feedback came back to us: it's too many options.  It was nearly impossible to ever pick the same color.&lt;br /&gt;&lt;P&gt;The previous models were based on my instincts as a developer: if I give you more choices, that gives you more control.  More control is better, right?  But in fact people don't usually like too many choices.  Here is a quote from &lt;A href="http://www.amazon.com/Power-Persuasion-Were-Bought-Sold/dp/0471266345"&gt;The Power of Persuasion&lt;/A&gt;:&lt;br /&gt;&lt;blockquote&gt;"The greater the number of choices, the happier the consumer - so it's assumed.&lt;P&gt;In a recent series of experiments ...  a group of Columbia University chocolate lovers were given a choice between six different flavors of Godiva chocolates.  A second group was asked to select between thirty different flavors.  Subjects given the extensive flavor choices rated their selection as less tasty, less satisfying and less enjoyable than did the limited-choice group.  The thirty-flavor subjects expressed more regrets about their selection and were less likely to choose chocolate as payment for participating in the experiment."&lt;/blockquote&gt;In other words: suppose a user goes off in search of the color green.  But then they quickly see that there are 300 different shades to choose from!  This is a source of stress to the user.  Give them a few tasteful options and they'll actually be happier.&lt;LI&gt;Then we designed a product for a younger audience.  We wanted to thin the color pool down even more.  Also a common complaint was that users needed &lt;i&gt;brown&lt;/i&gt;.  So we added a second special column for brown.&lt;LI&gt;But for graphic designers and other artists: they really might want more than 60 colors.  This model uses keyboard modifiers to let the user get thousands of more choices.  There are modifiers for "flesh tones" and "earth tones", as well as to toggle to a continuous spectrum of colors.&lt;LI&gt;Another way to give more options is to add scrollbars.  A color is generally considered to have three dimensions (RGB, HSB, RYB, HSL, etc.).  A rectangle will only present two of these dimensions.  If you add a scrollbar, though: you can represent another dimension.  Also you can add a scrollbar for opacity.  And because I still like the modifier idea: you can press a modifier key to toggle between a continuous spectrum and a grid.  In theory this model offers &lt;i&gt;every&lt;/i&gt; color conceivable to the user.  (In practice it is subject to rounding, though.)&lt;LI&gt;But the HSB model is not very aesthetic.  Rob Camick has &lt;a href="http://tips4java.wordpress.com/2009/07/05/hsl-color/"&gt;a nice article&lt;/a&gt; about using the hue-saturation-luminance model instead.  Isn't it just all-around better looking?&lt;LI&gt;Lastly: yellow.  In HSB or HSL: the hue is still abstractly based on an RGB model (that is: the primary colors of light).  The problem here is if you split the RGB model into 6ths, you'll get: red, yellow, green, cyan, blue, purple.  What about orange?  You'll have to split the model into 12ths to see orange: it's a tiny sliver in an RGB-based spectrum.  Instead I added a special class called &lt;code&gt;HueDistribution&lt;/code&gt;, and I use it to give each of these colors equal weight: red, orange, yellow, green, cyan, blue, purple.&lt;/LI&gt;&lt;/OL&gt;&lt;br /&gt;&lt;P&gt;You'll notice in the applet these drafts all have funny names: the list presents them as &lt;code&gt;ColorPaletteUIs&lt;/code&gt;.  The component shown is a &lt;code&gt;com.bric.swing.ColorPalette&lt;/code&gt;, and it can be configured with several different look-and-feels.&lt;br /&gt;&lt;P&gt;Also the controls at the top of the applet are client properties that affect the look of the UI.  They are purely aesthetic - they make no functional difference.  Personally I hate selecting a color from uniform cells due to the &lt;a href="http://en.wikipedia.org/wiki/Contrast_effect"&gt;contrast effect&lt;/a&gt; of placing the cells side-by-side.  These options provide a stylized way to break up that effect.&lt;br /&gt;&lt;P&gt;I'd like to add this sidenote: these were intended for (and tested against) use in desktop apps: not applets.  I'd recommend downloading and running these jars as applications to really observe their behavior.  So far my limited testing shows these bugs:&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;On Vista in IE the alt modifier doesn't work.  (Well, it toggles IE's menu bar... so it is technically working...)&lt;br /&gt;&lt;LI&gt;On OpenSUSE in Firefox the popup color well (shown below) doesn't work.&lt;/LI&gt;&lt;/ul&gt;&lt;br /&gt;&lt;BR&gt;... there are probably many more.  But if these bugs only reproduce in applet form: I'm not too worried about them.&lt;br /&gt;&lt;h3&gt;Integrating Into A Window&lt;/h3&gt;&lt;br /&gt;Almost done.  But not quite.  Where should the &lt;code&gt;ColorPalette&lt;/code&gt; component live?  It is possible to put in in your main window, but you would only want to do that if your user constantly needed to select a color.  In a painting program, for example.&lt;br /&gt;&lt;P&gt;Or I'm developing a graphics application where I put two &lt;code&gt;ColorPalettes&lt;/code&gt; side-by-side: one contains the usual spectrum of colors, and the other contains translucent shades of white/black.  You might also want to provide a separate set of themed colors (if you were working on, say, a PowerPoint-like program).  Or a set of recently-used colors.&lt;br /&gt;&lt;P&gt;However in several applications you can't devote that much window space to the act of choosing a color.  Consider iWeb, for example: for your text, shadows, and other effects you need to choose a color, but the inspector is already crammed full of useful odds and ends&lt;br /&gt;&lt;P&gt;In this case what I'd want is a &lt;code&gt;ColorWell&lt;/code&gt;.  This is about the height of a &lt;code&gt;JLabel&lt;/code&gt; (and the width is arbitrary).  I gave it an Aqua-ish border to blend in on my Mac.&lt;br /&gt;&lt;P&gt;In order to keep it abstract I defined 4 client properties that are associated with an &lt;code&gt;ActionListener&lt;/code&gt;.  By default these are what these actions do:&lt;br /&gt;&lt;ol&gt;&lt;LI&gt;&lt;code&gt;downKeyAction&lt;/code&gt;: displays a &lt;code&gt;ColorPalette&lt;/code&gt; below the &lt;code&gt;ColorWell&lt;/code&gt;.&lt;br /&gt;&lt;LI&gt;&lt;code&gt;spaceKeyAction&lt;/code&gt;: provides a &lt;code&gt;ColorPicker&lt;/code&gt; dialog for your advanced user.&lt;br /&gt;&lt;LI&gt;&lt;code&gt;singleClickAction&lt;/code&gt;: same as &lt;code&gt;downKeyAction&lt;/code&gt;&lt;br /&gt;&lt;LI&gt;&lt;code&gt;doubleClickAction&lt;/code&gt;: same as &lt;code&gt;spaceKeyAction&lt;/code&gt;&lt;/LI&gt;&lt;/ol&gt;&lt;br /&gt;&lt;P&gt;So in conclusion: here is applet that contains everything: the &lt;code&gt;ColorWell&lt;/code&gt;, the &lt;code&gt;ColorPalette&lt;/code&gt;, and the &lt;a href="http://javagraphics.blogspot.com/2007/04/jcolorchooser-making-alternative.html"&gt;&lt;code&gt;ColorPicker&lt;/code&gt;&lt;/A&gt;:&lt;br /&gt;&lt;BR&gt;&lt;APPLET CODE="com/bric/swing/ColorDemo" CODEBASE="http://javagraphics.java.net/jars/" width=250 height=300&gt;     &lt;PARAM NAME="archive" VALUE="Colors.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;This applet (source included) is available &lt;a href="http://javagraphics.java.net/jars/Colors.jar"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-968011846648632440?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/968011846648632440/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2010/01/colors-good-gui-for-selecting-colors.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/968011846648632440'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/968011846648632440'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2010/01/colors-good-gui-for-selecting-colors.html' title='Colors: A Good GUI for Selecting Colors'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-1214030547596914858</id><published>2009-12-25T21:41:00.000-08:00</published><updated>2011-02-21T13:02:23.153-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='format'/><category scheme='http://www.blogger.com/atom/ns#' term='blog'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='discussion'/><category scheme='http://www.blogger.com/atom/ns#' term='idea'/><category scheme='http://www.blogger.com/atom/ns#' term='comment'/><category scheme='http://www.blogger.com/atom/ns#' term='suggestion'/><category scheme='http://www.blogger.com/atom/ns#' term='articles'/><category scheme='http://www.blogger.com/atom/ns#' term='public'/><title type='text'>Survey time!  Please take a minute to comment.</title><content type='html'>This time last year I merged all my blog projects into one codebase, and I moved that codebase to a &lt;a href="http://javagraphics.java.net/"&gt;dev.java.net&lt;/a&gt; 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.&lt;br /&gt;&lt;P&gt;Also I automated the process of building/updating the jars: one java app called &lt;code&gt;BlogUpdater&lt;/codE&gt; combs through the workspace and creates a jar for every java file with a &lt;code&gt;main()&lt;/code&gt; method.  As more and more projects share small common files this really helped me keep things up-to-date.&lt;br /&gt;&lt;h3&gt;Help Me Help You&lt;/h3&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;P&gt;Specific conversation starters could -- but don't have to -- include:&lt;br /&gt;&lt;li&gt;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?  &lt;A href="http://tips4java.wordpress.com/"&gt;Here&lt;/a&gt; is a similar repository that follows a very different format.  Also &lt;a href="http://explodingpixels.wordpress.com/"&gt;Ken's blog&lt;/a&gt; is much less cluttered.  Thoughts?&lt;br /&gt;&lt;li&gt;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.&lt;br /&gt;&lt;li&gt;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.&lt;br /&gt;&lt;li&gt;On a bigger, more abstract scale: does anyone have any suggestions for other ways to focus this time &amp; 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.&lt;br /&gt;&lt;li&gt;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.)&lt;br /&gt;&lt;li&gt;In the articles would you like more code samples?  More screenshots?  More diagrams?&lt;br /&gt;&lt;li&gt;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!&lt;/li&gt;&lt;br /&gt;&lt;P&gt;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 &lt;i&gt;you&lt;/i&gt; think first...&lt;br /&gt;&lt;P&gt;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.  :)&lt;br /&gt;&lt;p&gt;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?)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-1214030547596914858?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/1214030547596914858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/12/survey-time-please-take-minute-to.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1214030547596914858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1214030547596914858'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/12/survey-time-please-take-minute-to.html' title='Survey time!  Please take a minute to comment.'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-4815330153712305304</id><published>2009-12-13T18:57:00.000-08:00</published><updated>2011-02-21T13:03:15.113-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='search field'/><category scheme='http://www.blogger.com/atom/ns#' term='prompts'/><category scheme='http://www.blogger.com/atom/ns#' term='text'/><category scheme='http://www.blogger.com/atom/ns#' term='JTextField'/><category scheme='http://www.blogger.com/atom/ns#' term='search'/><category scheme='http://www.blogger.com/atom/ns#' term='prompt'/><category scheme='http://www.blogger.com/atom/ns#' term='roundrect'/><category scheme='http://www.blogger.com/atom/ns#' term='client property'/><title type='text'>Text: Prompts and Search Fields</title><content type='html'>Here's what I want to replicate:&lt;br /&gt;&lt;BR&gt;&lt;img src="http://javagraphics.java.net/resources/promptSearchField.png" /&gt;&lt;br /&gt;&lt;p&gt;The concept of a &lt;a href="http://java.sun.com/javase/6/docs/api/index.html?javax/swing/JTextField.html"&gt;text field&lt;/a&gt; is nothing new, but this undertaking requires two other components:&lt;br /&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;A &lt;code&gt;TextFieldUI&lt;/code&gt; with rounded corners, and a search icon.&lt;/li&gt;&lt;li&gt;&lt;a href="http://ui-patterns.com/pattern/InputPrompt"&gt;Prompting&lt;/a&gt;: gray text prompting the user with instructions or other useful information.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h3&gt;Apple's Solution&lt;/h3&gt;&lt;br /&gt;It's elegant.  It's simple:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;textField.putClientProperty( "JTextField.variant", "search");&lt;br /&gt;textField.putClientProperty( "JTextField.Search.Prompt", prompt);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;The first line is well documented &lt;a href="http://developer.apple.com/mac/library/technotes/tn2007/tn2196.html#JTEXTFIELD_VARIANT"&gt;here&lt;/a&gt;.  The second line is only casually mentioned &lt;a href="http://developer.apple.com/mac/library/releasenotes/CrossPlatform/JavaSnowLeopardUpdate1LeopardUpdate6RN/NewandNoteworthy/NewandNoteworthy.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;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 &lt;a href="http://javagraphics.blogspot.com/2009/08/buttons-new-uis.html"&gt;ButtonUI's&lt;/a&gt;, and an indeterminate &lt;a href="http://javagraphics.blogspot.com/2009/05/progress-spinny-widget.html"&gt;progress widget&lt;/a&gt;.  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.)&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;Creating a &lt;code&gt;RoundTextFieldUI&lt;/code&gt;&lt;/h3&gt;&lt;br /&gt;So a pure-Java solution requires making a new &lt;code&gt;TextFieldUI&lt;/code&gt;.  This wasn't so hard, if you extend the &lt;code&gt;BasicTextFieldUI&lt;/code&gt;. The key was figuring out I had to override the &lt;code&gt;getVisibleEditorRect()&lt;/code&gt; method.  This controls the rectangle the text appears in, so I can offset everything to draw the rounded rectangle.&lt;br /&gt;&lt;p&gt;Also by overriding &lt;code&gt;modelToView()&lt;/code&gt; I was able to tweak the caret height (the x-position and width I didn't change).&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/plaf/RoundTextFieldUI.java?view=markup"&gt;Here&lt;/a&gt; is the class I came up with.  It includes a client property &lt;code&gt;"useSearchIcon"&lt;/code&gt; to render a magnifying glass icon.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;That part was surprisingly simple.  Next I wanted to deal with prompting.&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;Using &lt;a href="http://code.google.com/p/xswingx/"&gt;XSwingX&lt;/a&gt;&lt;/h3&gt;&lt;br /&gt;This is a little project hosted at &lt;code&gt;code.google.com&lt;/code&gt;.  It has prompting built-in, and a very simple programming interface.  But it was surprisingly tricky to put a demo together.  It requires the &lt;a href="http://swingx.java.net/"&gt;SwingX&lt;/a&gt; package.  But not the latest version: there are some incompatibilities in the latest version of &lt;code&gt;SwingX&lt;/code&gt; integrated with &lt;code&gt;XSwingX&lt;/code&gt;.  We need version &lt;a href="http://swingx.java.net/servlets/ProjectDocumentList?folderID=8834&amp;amp;expandFolder=8834&amp;amp;folderID=6868"&gt;0.9.2&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;&lt;a href="http://javagraphics.java.net/resources/JXSearchTest.jar"&gt;Here&lt;/a&gt; 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.&lt;br /&gt;&lt;/p&gt;&lt;h3&gt;My Own Approach&lt;/h3&gt;&lt;br /&gt;I wanted to try something simple.  Something done in one, maybe two classes.  My first thought was to extend &lt;code&gt;JTextField&lt;/code&gt;, and shuffle the color/text based on when the text field has the focus:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;public class PromptTextField extends JTextField {&lt;br /&gt;       static FocusListener focusListener = new FocusListener() {&lt;br /&gt;             public void focusGained(FocusEvent e) {&lt;br /&gt;                   PromptTextField src = (PromptTextField)e.getSource();&lt;br /&gt;                   if(src.state==STATE_PROMPT) {&lt;br /&gt;                         src.setState(STATE_NORMAL);&lt;br /&gt;                   }&lt;br /&gt;             }&lt;br /&gt;  &lt;br /&gt;             public void focusLost(FocusEvent e) {&lt;br /&gt;                   PromptTextField src = (PromptTextField)e.getSource();&lt;br /&gt;                   if(src.getText().length()==0)&lt;br /&gt;                         src.setState(STATE_PROMPT);&lt;br /&gt;             }&lt;br /&gt;       };&lt;br /&gt;       private static final int STATE_UNDEFINED = 0;&lt;br /&gt;       private static final int STATE_NORMAL = 1;&lt;br /&gt;       private static final int STATE_PROMPT = 2;&lt;br /&gt; &lt;br /&gt;       private Color normalColor = SystemColor.textText;&lt;br /&gt;       protected Color promptColor = Color.gray;&lt;br /&gt;       protected String promptText;&lt;br /&gt;       private int state = STATE_UNDEFINED;&lt;br /&gt;&lt;br /&gt;       public PromptTextField(String text, String prompt, int columns) {&lt;br /&gt;             super(text, columns);&lt;br /&gt;             promptText = prompt;&lt;br /&gt;             normalColor = getForeground();&lt;br /&gt;             addFocusListener(focusListener);&lt;br /&gt;             if(text.length()==0) {&lt;br /&gt;                   setState(STATE_PROMPT);&lt;br /&gt;             } else {&lt;br /&gt;                   setState(STATE_NORMAL);&lt;br /&gt;             }&lt;br /&gt;       }&lt;br /&gt; &lt;br /&gt;       public String getText() {&lt;br /&gt;             if(state==STATE_PROMPT) {&lt;br /&gt;                   return "";&lt;br /&gt;             }&lt;br /&gt;             return super.getText();&lt;br /&gt;       }&lt;br /&gt; &lt;br /&gt;       private void setState(int s) {&lt;br /&gt;             if(s==state)&lt;br /&gt;                   return;&lt;br /&gt;             state = s;&lt;br /&gt;             if(s==STATE_PROMPT) {&lt;br /&gt;                   if(state==STATE_NORMAL &amp;amp;&amp;amp; getText().length()&gt;0)&lt;br /&gt;                         throw new IllegalArgumentException("the state should not be set to STATE_PROMPT if there is text already in the text field (\""+getText()+"\")");&lt;br /&gt;                   super.setForeground(promptColor);&lt;br /&gt;                   super.setText(promptText);&lt;br /&gt;             } else if(s==STATE_NORMAL) {&lt;br /&gt;                   super.setForeground(normalColor);&lt;br /&gt;                   super.setText("");&lt;br /&gt;             }&lt;br /&gt;       }&lt;br /&gt; &lt;br /&gt;       public void setText(String s) {&lt;br /&gt;             if(state==STATE_UNDEFINED) {&lt;br /&gt;                   super.setText(s);&lt;br /&gt;                   return;&lt;br /&gt;             }&lt;br /&gt;             if(s==null) s = "";&lt;br /&gt;             if(hasFocus() || s.length()&gt;0) {&lt;br /&gt;                   state = STATE_NORMAL;&lt;br /&gt;                   super.setForeground(normalColor);&lt;br /&gt;                   super.setText(s);&lt;br /&gt;             } else if(s.length()==0) {&lt;br /&gt;                   state = STATE_PROMPT;&lt;br /&gt;                   super.setForeground(promptColor);&lt;br /&gt;                   super.setText(promptText);&lt;br /&gt;             }&lt;br /&gt;       }&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;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 &lt;code&gt;getText()&lt;/code&gt; and &lt;code&gt;setText()&lt;/code&gt; methods.  This means the &lt;code&gt;getText()&lt;/code&gt; method might reflect something that has nothing in common with the data stored in &lt;code&gt;getDocument()&lt;/code&gt;.&lt;br /&gt;&lt;BR&gt;&lt;h3&gt;Rob Camick's Approach&lt;/h3&gt;&lt;br /&gt;Who is Rob Camick?  I don't know.  But now that I found &lt;a href="http://tips4java.wordpress.com/2009/11/29/text-prompt/"&gt;this article&lt;/a&gt; I'm going to pay closer attention.  He talks about another model to achieve prompting: add a component &lt;i&gt;over&lt;/i&gt; the text field.&lt;br /&gt;&lt;p&gt;It's simple.  It's not a "hack," it's just plain old clever.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;There's a problem, though: text placement.  In my case -- because I'm using a &lt;code&gt;TextFieldUI&lt;/code&gt; of my own creation -- it'll be hard to line up the prompted text exactly over the existing text.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;... unless I use a &lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/graphics/TextOnlyGraphics2D.java?view=markup"&gt;&lt;code&gt;TextOnlyGraphics2D&lt;/code&gt;&lt;/a&gt; and the same type of &lt;code&gt;TextFieldUI&lt;/code&gt;.&lt;br /&gt;&lt;/p&gt;&lt;blockquote&gt;&lt;pre&gt;public class TextFieldPrompt extends JTextField {&lt;br /&gt;     public TextFieldPrompt(JTextField parent,&lt;br /&gt;         Color promptColor,&lt;br /&gt;         String promptText) {&lt;br /&gt;         super(promptText);&lt;br /&gt;       &lt;br /&gt;         if(promptColor==null)&lt;br /&gt;             promptColor = Color.gray;&lt;br /&gt;       &lt;br /&gt;         parent.add(this);&lt;br /&gt;       &lt;br /&gt;         setFocusable(false);&lt;br /&gt;         setEditable(false);&lt;br /&gt;         setForeground(promptColor);&lt;br /&gt;         setOpaque(false);&lt;br /&gt;       &lt;br /&gt;         ... install listeners &amp;amp; UI ...&lt;br /&gt;     }&lt;br /&gt;   &lt;br /&gt;   &lt;br /&gt;     public void paint(Graphics g) {&lt;br /&gt;         Graphics2D g2 = (Graphics2D)g;&lt;br /&gt;         g2 = new TextOnlyGraphics2D( g2, null );&lt;br /&gt;         super.paint( g2 );&lt;br /&gt;     }&lt;br /&gt;   &lt;br /&gt;     public boolean contains(int x, int y) {&lt;br /&gt;         return false;&lt;br /&gt;     }&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;Here is an applet demonstrating the final model:&lt;br /&gt;&lt;APPLET CODE="com/bric/swing/PromptSearchDemo" CODEBASE="http://javagraphics.java.net/jars/" width=300 height=127&gt;     &lt;PARAM NAME="archive" VALUE="PromptSearch.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;This jar (source included) is available &lt;a href="http://javagraphics.java.net/jars/PromptSearch.jar"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-4815330153712305304?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/4815330153712305304/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/12/text-prompts-and-search-fields.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/4815330153712305304'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/4815330153712305304'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/12/text-prompts-and-search-fields.html' title='Text: Prompts and Search Fields'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-525950695113262473</id><published>2009-12-06T12:02:00.000-08:00</published><updated>2011-02-21T13:03:50.798-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='text'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='find'/><category scheme='http://www.blogger.com/atom/ns#' term='text components'/><category scheme='http://www.blogger.com/atom/ns#' term='search'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='effects'/><title type='text'>Text: Searching JTextComponents</title><content type='html'>This entry has to do with searching for a phrase in &lt;code&gt;JTextComponents&lt;/code&gt;.  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.)&lt;br /&gt;&lt;BR&gt;&lt;APPLET CODE="com/bric/swing/TextSearchDemo" CODEBASE="http://javagraphics.java.net/jars/" width=600 height=427&gt;     &lt;PARAM NAME="archive" VALUE="TextSearch.jar"&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;This jar (source included) is available &lt;a href="http://javagraphics.java.net/jars/TextSearch.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;h3&gt;Text Search Bars&lt;/h3&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;OL&gt;&lt;LI&gt;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.&lt;/LI&gt;&lt;LI&gt;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.&lt;/LI&gt;&lt;LI&gt;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.&lt;/LI&gt;&lt;LI&gt;Of course a &lt;i&gt;modal&lt;/i&gt; search dialog is worse.  Modal dialogs are discouraged &lt;a href="http://en.wikipedia.org/wiki/Dialog_box#Application_modal"&gt;here&lt;/a&gt;, among other places.&lt;/LI&gt;&lt;/OL&gt;&lt;br /&gt;&lt;P&gt;I modeled these search bars after the ones I saw in Firefox and Safari.  They include:&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;a search field&lt;br /&gt;&lt;LI&gt;next/previous buttons&lt;br /&gt;&lt;LI&gt;a checkbox for matching case&lt;br /&gt;&lt;LI&gt;a dismiss button&lt;br /&gt;&lt;LI&gt;a label including the occurrences of the search phrase&lt;br /&gt;&lt;LI&gt;a toggle button for "Highlight All"&lt;/LI&gt;&lt;/ul&gt;&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;H3&gt;Text Search Dialog&lt;/H3&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;H3&gt;Searching Text&lt;/H3&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;P&gt;My first instinct was to convert the document into a &lt;code&gt;String&lt;/code&gt;, and then use &lt;code&gt;.indexOf()&lt;/code&gt; and &lt;code&gt;.lastIndexOf()&lt;/code&gt; 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.&lt;br /&gt;&lt;P&gt;So instead I went spelunking around, and tried using the &lt;code&gt;Segment&lt;/code&gt; 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.&lt;br /&gt;&lt;P&gt;(Remember when we count the occurrences of the search phrase: we perform a bajillion searches, so this method needs to be relatively light.)&lt;br /&gt;&lt;P&gt;If anyone has any experience/suggestions on this subject, &lt;a href="http://javagraphics.java.net/source/browse/*checkout*/javagraphics/trunk/src/com/bric/swing/TextSearch.java"&gt;here&lt;/a&gt; is the code in question.&lt;br /&gt;&lt;H3&gt;The Visuals&lt;/H3&gt;&lt;br /&gt;When you use the Safari-style search bar you're introducing two extra layers of visual effects into your &lt;code&gt;JFrame&lt;/code&gt;:&lt;br /&gt;&lt;img src="http://javagraphics.java.net/resources/textLayersSmall.gif"&gt;&lt;br /&gt;The middle layer is known as the &lt;code&gt;TextHighlightSheet&lt;/code&gt;.  The topmost layer is a &lt;code&gt;SearchHighlight&lt;/code&gt;.  These layers have a few of things in common:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;They are both added to the &lt;A href="http://java.sun.com/j2se/1.4.2/docs/api/javax/swing/JLayeredPane.html"&gt;JLayeredPane&lt;/a&gt;.&lt;/li&gt;&lt;li&gt;They both are tied to their "parent" &lt;code&gt;JTextComponent&lt;/code&gt;.  (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 &lt;code&gt;AncestorListeners&lt;/code&gt; or &lt;code&gt;ComponentListeners&lt;/code&gt; are notified that the &lt;code&gt;TextComponent&lt;/code&gt; has moved around: these other layers have to immediately respond.&lt;/li&gt;&lt;li&gt;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.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Now I'll discuss each in a little more detail.&lt;br /&gt;&lt;br&gt;&lt;h3&gt;The TextHighlightSheet&lt;/h3&gt;&lt;br /&gt;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 &lt;code&gt;(0, 0)&lt;/code&gt; of the viewport, and then the index that is mapped to &lt;code&gt;(width, height)&lt;/code&gt;.  Then search only within those indices, and calculate the shapes of all the hits.&lt;br /&gt;&lt;P&gt;The hits are outlined in rectangles, and &lt;code&gt;Area&lt;/code&gt; objects are used to join overlapping rectangles.  Usually I avoid the &lt;code&gt;Area&lt;/code&gt; 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).&lt;br /&gt;&lt;P&gt;Finally a combination of the right clipping, translation, and a &lt;code&gt;TextOnlyGraphics2D&lt;/code&gt; let us call &lt;code&gt;parentTextComponent.paint(g)&lt;/code&gt; to render the text.&lt;br /&gt;&lt;P&gt;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 &lt;a href="http://javagraphics.java.net/doc/com/bric/swing/TextHighlightSheet.html"&gt;the javadocs&lt;/a&gt; if you really want to customize it.&lt;br /&gt;&lt;P&gt;The sheet has &lt;code&gt;isActive()&lt;/code&gt; and &lt;code&gt;setActive()&lt;/code&gt; methods to control whether it is visible.  The &lt;code&gt;TextSearchBar&lt;/code&gt; automatically follows one of two behaviors:&lt;br /&gt;&lt;ol&gt;&lt;LI&gt;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.&lt;br /&gt;&lt;LI&gt;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.&lt;/LI&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h3&gt;SearchHighlight&lt;/h3&gt;&lt;br /&gt;This is a very different component, and is subclassed directly into the look you're aiming for.  The &lt;code&gt;AbstractSearchHighlight&lt;/code&gt; subclasses into the &lt;code&gt;AquaSearchHighlight&lt;/code&gt;, the &lt;code&gt;FirefoxSearchHighlight&lt;/code&gt;, and the &lt;code&gt;NudgeSearchHighlight&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;When constructed this component creates a &lt;code&gt;BufferedImage&lt;/code&gt; of one particular hit of the search phrase.  Then a &lt;code&gt;javax.swing.Timer&lt;/code&gt; is activated, and this component is ready for action.&lt;br /&gt;&lt;P&gt;The subclasses can animate the transform and opacity of the image over the duration of the animation.&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;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 &lt;code&gt;JTextComponent&lt;/code&gt; 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.&lt;br /&gt;&lt;P&gt;Each effect is created by calling &lt;code&gt;TextSearch.highlight()&lt;/code&gt;.  This looks up the value for &lt;code&gt;UIManager.get("textSearchHighlightEffect")&lt;/code&gt;, 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 &lt;code&gt;com.bric.plaf.AbstractSearchHighlight&lt;/code&gt; if you don't want to.)&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;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.)&lt;br /&gt;&lt;P&gt;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 &lt;i&gt;end&lt;/i&gt; of the current selection... but continuous typing really needs a search that starts at the beginning of the current selection.  Another day, maybe...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-525950695113262473?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/525950695113262473/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/12/text-searching-jtextcomponents.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/525950695113262473'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/525950695113262473'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/12/text-searching-jtextcomponents.html' title='Text: Searching JTextComponents'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-5590026380506970720</id><published>2009-11-28T06:48:00.001-08:00</published><updated>2011-02-21T13:04:23.754-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='animation'/><category scheme='http://www.blogger.com/atom/ns#' term='gradients'/><category scheme='http://www.blogger.com/atom/ns#' term='paint'/><category scheme='http://www.blogger.com/atom/ns#' term='animate'/><category scheme='http://www.blogger.com/atom/ns#' term='halftoning'/><category scheme='http://www.blogger.com/atom/ns#' term='halftone'/><category scheme='http://www.blogger.com/atom/ns#' term='paintcontext'/><category scheme='http://www.blogger.com/atom/ns#' term='gradient'/><title type='text'>Gradients: a Halftone Gradient</title><content type='html'>&lt;P&gt;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.&lt;br /&gt;&lt;h3&gt;Halftone Gradients&lt;/h3&gt;&lt;br /&gt;I don't know why, but I've always wanted a gradient that looked like &lt;A href="http://en.wikipedia.org/wiki/Halftone"&gt;halftoning&lt;/a&gt;.&lt;br /&gt;&lt;P&gt;But for some reason whenever I thought of tackling it: I always envisioned myself writing a &lt;code&gt;PaintContext&lt;/code&gt; and coming up with some ugly code that involved lots of varying radii, and antialiasing things by hand, and doing clever things with &lt;code&gt;AffineTransforms&lt;/code&gt; 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.&lt;br /&gt;&lt;P&gt;My &lt;a href="http://javagraphics.blogspot.com/2009/11/gradients-boring-discussion.html"&gt;last blog entry&lt;/a&gt; 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.&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;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 &lt;code&gt;HalftoneGradient&lt;/code&gt; class:&lt;br /&gt;&lt;BR&gt;&lt;APPLET CODE="com/bric/awt/HalftoneGradientDemo" CODEBASE="http://javagraphics.java.net/jars/" width=400 height=427&gt;     &lt;PARAM NAME="archive" VALUE="HalftoneGradient.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;The jar (source included) is available &lt;a href="http://javagraphics.java.net/jars/HalftoneGradient.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;h3&gt;Your Basic Options&lt;/h3&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;P&gt;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 &lt;code&gt;t^.8&lt;/code&gt; 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 &lt;code&gt;color1&lt;/code&gt; to &lt;code&gt;color2&lt;/code&gt; should increase linearly with &lt;code&gt;t&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;Oh, and as an afterthought I added the "offset" property, and the "animate" checkbox.  How could I resist?  (Have you seen my &lt;a href="http://mickleness.deviantart.com/gallery/"&gt;art&lt;/a&gt;?  I love animating things.)&lt;br /&gt;&lt;h3&gt;Making a Tiled Pattern Not Tile&lt;/h3&gt;&lt;br /&gt;This paint delegates all the hard work to a transformed &lt;code&gt;TexturePaint&lt;/code&gt;, 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 &lt;code&gt;P1&lt;/code&gt; and &lt;code&gt;P2&lt;/code&gt;: how do I make it a solid color?&lt;br /&gt;&lt;P&gt;Originally I went to Sun's website and downloaded the source for the &lt;code&gt;TexturePaintContext&lt;/code&gt;.  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 &lt;code&gt;[0, Integer.MAX_VALUE]&lt;/code&gt;.  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 &lt;i&gt;few hours&lt;/i&gt;, so I moved on to plan B:&lt;br /&gt;&lt;P&gt;Instead I just rewrote the raster the &lt;code&gt;TexturePaintContext&lt;/code&gt; returns.  Is it the most efficient response?  No.  Does it work?  Yes.  The class is called &lt;code&gt;CoveredContext&lt;/code&gt;.  (It's non-public, although it's included in the jar mentioned above.)  In its original draft the hard work looked like this:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;public Raster getRaster(int x, int y, int w, int h) {&lt;br /&gt;     WritableRaster raster = (WritableRaster)context.getRaster(x, y, w, h);&lt;br /&gt;     ...&lt;br /&gt;     double[] matrix = new double[6];&lt;br /&gt;     transform.getMatrix(matrix);&lt;br /&gt;     for(int y2 = 0; y2 &amp;lt; h; y2++) {&lt;br /&gt;         raster.getDataElements(0, y2, w, 1, data);&lt;br /&gt;    &lt;br /&gt;         for(int x2 = 0; x2 &amp;lt; data.length; x2++) {&lt;br /&gt;             double x3 = x2+x;&lt;br /&gt;             double y3 = y2+y;&lt;br /&gt;&lt;br /&gt;             // apply the transform manually:&lt;br /&gt;             y3 = matrix[1]*x3+matrix[3]*y3+matrix[5];&lt;br /&gt;&lt;br /&gt;             if(y3 &amp;lt; 1) {&lt;br /&gt;                 data[x2] = color1;&lt;br /&gt;             } else if(y3 &amp;gt;= distance-1) {&lt;br /&gt;                 data[x2] = color2;&lt;br /&gt;             }&lt;br /&gt;         }&lt;br /&gt;    &lt;br /&gt;         raster.setDataElements(0, y2, w, 1, data);&lt;br /&gt;      }&lt;br /&gt;      return raster;&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;The &lt;A href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/awt/CoveredContext.java?view=markup"&gt;final draft&lt;/a&gt;, 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.&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;P&gt;I haven't tried to break this down and scrutinize performance yet.  This class first needs to demonstrate that it, in fact, useful...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-5590026380506970720?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/5590026380506970720/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/11/gradients-halftone-gradient.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/5590026380506970720'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/5590026380506970720'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/11/gradients-halftone-gradient.html' title='Gradients: a Halftone Gradient'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-4612922486665030157</id><published>2009-11-11T20:24:00.000-08:00</published><updated>2011-02-21T13:05:14.473-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gradients'/><category scheme='http://www.blogger.com/atom/ns#' term='paint'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='Optimization'/><title type='text'>Gradients: a Boring Discussion</title><content type='html'>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.&lt;br /&gt;&lt;P&gt;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 &lt;A HREF="http://www.amazon.com/Java-API-Graphics-Vincent-Hardy/dp/0130142662"&gt;Java 2D API Graphics book&lt;/A&gt;.)  Did our tricks work?  And if they did: do they still?&lt;br /&gt;&lt;P&gt;In this entry when I say "gradient" I'm referring to what is now known as a &lt;A HREF="http://java.sun.com/javase/6/docs/api/index.html?java/awt/MultipleGradientPaint.html"&gt;MultipleGradientPaint&lt;/A&gt;.  This is now standard in Java 1.6, but in earlier versions the only official Paint that came standard with Java is the &lt;A HREF="http://java.sun.com/j2se/1.5.0/docs/api/index.html?java/awt/GradientPaint.html"&gt;GradientPaint&lt;/A&gt; (a 2-color gradient).&lt;br /&gt;&lt;P&gt;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 &lt;A HREF="http://xmlgraphics.apache.org/batik/"&gt;Batik&lt;/A&gt; 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.&lt;br /&gt;&lt;h3&gt;A Simple Java 1.4-friendly Linear Gradient&lt;/h3&gt;&lt;br /&gt;So my first priority was to make a really simple alternative gradient to test against.&lt;br /&gt;&lt;P&gt;A few years ago &lt;A href="http://weblogs.java.net/blog/2006/09/26/java2d-gradients-performance"&gt;Romain Guy pointed out&lt;/A&gt; you could improve performance with horizontal and vertical gradients if you used a &lt;code&gt;TexturePaint&lt;/code&gt; instead of an actual gradient.  And last year I combined &lt;code&gt;TexturePaints&lt;/code&gt; with &lt;code&gt;AffineTransforms&lt;/code&gt; in &lt;A HREF="http://javagraphics.blogspot.com/2008/06/texturepaints-and-affinetransforms.html"&gt;this article&lt;/A&gt;.  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 &lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/awt/GradientTexturePaint.java?view=markup"&gt;GradientTexturePaint&lt;/a&gt;.&lt;br /&gt;&lt;P&gt;This paint creates a 1-pixel tall tiled image, and then finds the appropriate &lt;code&gt;AffineTransform&lt;/code&gt; 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:&lt;br /&gt;&lt;BR&gt;&lt;APPLET CODE="com/bric/awt/GradientTexturePaintDemo" CODEBASE="http://javagraphics.java.net/jars/" width=400 height=427&gt;     &lt;PARAM NAME="archive" VALUE="GradientTexturePaint.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;(You can download this demo, source included, &lt;A HREF="http://javagraphics.java.net/jars/GradientTexturePaint.jar"&gt;here&lt;/A&gt;.)&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;Under Quartz, however, you have to set the INTERPOLATION, &lt;i&gt;and&lt;/i&gt; 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.&lt;br /&gt;&lt;P&gt;Meanwhile, here is a screenshot contrasting the difference &lt;code&gt;RenderingHints&lt;/code&gt; make:&lt;br /&gt;&lt;BR&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/contrast_gradient_edge.png"&gt;&lt;br /&gt;&lt;P&gt;The biggest downside to this gradient is that it has to tile.  (Unlike the &lt;code&gt;GradientPaint&lt;/code&gt; class, which shows only one color beyond the two control points.)  Also, as we see later: it's performance isn't great.&lt;br /&gt;&lt;P&gt;But the advantages are:&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;It's Java 1.4 compatible.  (Java 1.3, probably?)&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;It requires no other classes.  It's very lightweight to add to your project.&lt;/LI&gt;&lt;/ul&gt;&lt;br /&gt;&lt;P&gt;So.  Problem #1 solved: I have at least one more gradient to compare.&lt;br /&gt;&lt;H3&gt;Comparing Gradients&lt;/H3&gt;&lt;br /&gt;Let's focus on linear gradients.  Here are the classes we can test against:&lt;br /&gt;&lt;ol&gt;&lt;LI&gt;&lt;code&gt;com.sun.glf.goodies.GradientPaintExt&lt;/code&gt; from Hardy's book.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;&lt;code&gt;com.bric.awt.GradientTexturePaint&lt;/code&gt; mentioned above.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;&lt;code&gt;org.apache.batik.ext.awt.LinearGradientPaint&lt;/code&gt; from Batik.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;&lt;code&gt;java.awt.LinearGradientPaint&lt;/code&gt; in Java 1.6+.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;&lt;code&gt;DelegatePaint&lt;/code&gt;: this is a small shell wrapped around the &lt;code&gt;java.awt.LinearGradientPaint&lt;/code&gt;.  I wanted to see if the &lt;code&gt;LinearGradientPaint&lt;/code&gt; was receiving special optimized treatment under-the-hood in the graphics pipeline.&lt;/LI&gt;&lt;/ol&gt;&lt;br /&gt;I put together several tests -- each test measures the median time and memory allocation required for an operation.  The tests include:&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;Creating &lt;code&gt;PaintContexts&lt;/code&gt;.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;Calling &lt;code&gt;Graphics2D.fillRect()&lt;/code&gt;.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;Calling &lt;code&gt;Graphics2D.fill(Ellipse2D)&lt;/code&gt;.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;Performing these operations through a small &lt;code&gt;AffineTransform&lt;/code&gt;.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;Also compare what it costs to recycle a gradient vs constructing it from scratch.&lt;/LI&gt;&lt;/ul&gt;&lt;br /&gt;The test I used is &lt;A HREF="http://javagraphics.java.net/jars/GradientTest.jar"&gt;here&lt;/A&gt; (source included).  &lt;A HREF="http://spreadsheets.google.com/pub?key=tbbtF8mRsdRIgyeZDxLrLKg&amp;output=html"&gt;Here&lt;/A&gt; are the results after running it in a variety of environments.&lt;br /&gt;&lt;P&gt;There's a lot of data.  Oiyee.  What does it mean?&lt;br /&gt;&lt;h4&gt;Creating Rasters vs Blitting&lt;/h4&gt;&lt;br /&gt;For starters: look at the first two lines.  This involves getting a &lt;code&gt;PaintContext&lt;/code&gt; and calling &lt;code&gt;PaintContext.getRaster()&lt;/code&gt;.  Notice that these are considerably faster than the next couple of lines that involve calling &lt;code&gt;Graphics2D.fillRect()&lt;/code&gt; for the same number of pixels.  This brought me to my first sad realization about &lt;code&gt;Paint&lt;/code&gt; 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 &lt;a href="http://www.randelshofer.ch/oop/graphics/index.html"&gt;creating a new Graphics2D pipeline&lt;/a&gt;, but it's a big undertaking.&lt;br /&gt;&lt;P&gt;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:&lt;br /&gt;&lt;BR&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/gradient_profile.png"&gt;&lt;br /&gt;&lt;P&gt;The vast majority of the time is spent blitting.  This seems especially unfortunate for simple calls to &lt;code&gt;fillRect()&lt;/code&gt; (because the "hard" work is already done, right?).  But it is what it is.&lt;br /&gt;&lt;h4&gt;Using Small &lt;code&gt;AffineTransforms&lt;/code&gt;&lt;/h4&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;P&gt;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 &lt;A HREF="http://javagraphics.java.net/doc/com/bric/graphics/OptimizedGraphics2D.html"&gt;OptimizedGraphics2D&lt;/A&gt; class.  Basically when using Quartz, and painting with anything other than a &lt;code&gt;java.awt.Color&lt;/code&gt;, we get the raster from a &lt;code&gt;PaintContext&lt;/code&gt; and then paint that as a &lt;code&gt;BufferedImage&lt;/code&gt;.  It looks something like this:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;PaintContext context = currentPaint.createContext(ColorModel.getRGBdefault(), &lt;br /&gt;    rect, rect, getTransform(), oldHints);&lt;br /&gt;BufferedImage scratchImage = getScratchImage(rect.width, rect.height);&lt;br /&gt;WritableRaster paintRaster = (WritableRaster)context.getRaster(rect.x, rect.y, rect.width, rect.height);&lt;br /&gt;BufferedImage newImage = new BufferedImage(context.getColorModel(), paintRaster, false, null);&lt;br /&gt;Graphics2D g = scratchImage.createGraphics();&lt;br /&gt;g.setComposite(AlphaComposite.Clear);&lt;br /&gt;g.fillRect(0, 0, rect.width, rect.height);&lt;br /&gt;g.translate(-rect.x, -rect.y);&lt;br /&gt;g.transform( myTransform );&lt;br /&gt;g.setColor(Color.black);&lt;br /&gt;g.setComposite(AlphaComposite.SrcOver);&lt;br /&gt;g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, &lt;br /&gt;    RenderingHints.VALUE_ANTIALIAS_ON);&lt;br /&gt;g.fill(s);&lt;br /&gt;g.setTransform(identityTransform);&lt;br /&gt;g.setComposite(AlphaComposite.SrcIn);&lt;br /&gt;g.drawImage(newImage, 0, 0, null);&lt;br /&gt;g.dispose();&lt;br /&gt;drawImage(scratchImage, &lt;br /&gt;    rect.x, rect.y, rect.x+rect.width, rect.y+rect.height, &lt;br /&gt;    0, 0, rect.width, rect.height, &lt;br /&gt;    null, null);&lt;br /&gt;newImage.flush();&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;So we're painting the mask in black, and then using a &lt;code&gt;SrcIn&lt;/code&gt; composite to render the fill of our paint.&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;Note that in these tests (in the &lt;code&gt;GradientTest&lt;/code&gt; app), there is a crucial line involved in testing:&lt;br /&gt;&lt;BR&gt;&lt;code&gt;OptimizedGraphics2D.testingOptimizations = true;&lt;/code&gt;&lt;br /&gt;&lt;P&gt;This means the code I mentioned above is &lt;i&gt;always being called&lt;/i&gt;.  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.)&lt;br /&gt;&lt;h4&gt;What About the Delegates?&lt;/h4&gt;&lt;br /&gt;Good news!  The delegates are worthless.  In Java 1.6 I wrapped the &lt;code&gt;awt.LinearGradientPaint&lt;/code&gt; in a custom &lt;code&gt;Paint&lt;/code&gt; object (and also wrapped their &lt;code&gt;PaintContext&lt;/code&gt;, too).  I know &lt;code&gt;java.awt.Colors&lt;/code&gt; and &lt;code&gt;java.awt.BasicStroke&lt;/code&gt; 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.&lt;br /&gt;&lt;h4&gt;What About Sharing?&lt;/h4&gt;&lt;br /&gt;Here "sharing" refers to using a static gradient, instead of constructing a new gradient object in each test.&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;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?)&lt;br /&gt;&lt;h4&gt;The Gradient Classes&lt;/h4&gt;&lt;br /&gt;One of the most obvious comparisons is to look at the columns side-by-side.  What stands out?&lt;br /&gt;&lt;P&gt;The first thing that pops out at me: the &lt;code&gt;GradientTexturePaint&lt;/code&gt; loses.  It's not horrible, but it's always behind the other classes in terms of speed.&lt;br /&gt;&lt;P&gt;Surprisingly in Java 1.6 the Batik class slightly outperform the &lt;code&gt;awt&lt;/code&gt; class.  I assumed the &lt;code&gt;awt&lt;/code&gt; class was, well, based-on the Batik model.  The code should be the same, right?  Apparently not.  Calling &lt;code&gt;fillRect()&lt;/code&gt; is 25% faster when you use Batik.&lt;br /&gt;&lt;P&gt;I find the memory allocation results are looking pretty shady here.  The &lt;code&gt;GradientTexturePaint&lt;/code&gt; 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.&lt;br /&gt;&lt;h4&gt;Managing Memory&lt;/h4&gt;&lt;br /&gt;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.)&lt;br /&gt;&lt;P&gt;The more I look at this, the more is looks like the Java2D architecture for Paints was poorly thought out.  A &lt;code&gt;Paint&lt;/code&gt; has one real method of interest: &lt;code&gt;createContext()&lt;/code&gt;.  (Already I'm scratching my head: why not make the method called &lt;code&gt;getContext()&lt;/code&gt;?  The distinction is that &lt;code&gt;create()&lt;/code&gt; requires making a new object, and &lt;code&gt;get()&lt;/code&gt; 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...)&lt;br /&gt;&lt;P&gt;A &lt;code&gt;PaintContext&lt;/code&gt; has a magic method called &lt;code&gt;getRaster()&lt;/code&gt;.  It returns a &lt;code&gt;Raster&lt;/code&gt; 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 &lt;code&gt;Raster&lt;/code&gt;?  Rasters are huge; this can really affect how fast memory is burned through.  Instead the method should accept a destination &lt;code&gt;WritableRaster&lt;/code&gt; 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 &lt;i&gt;then&lt;/i&gt; 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.)&lt;br /&gt;&lt;P&gt;However "good" paints do not create rasters constantly.  Batik paints, for example, have a static method: &lt;code&gt;getCachedRaster(ColorModel cm,int w,int h)&lt;/code&gt;.  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 &lt;code&gt;PaintContext&lt;/code&gt;: 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.&lt;br /&gt;&lt;h3&gt;Conclusions&lt;/h3&gt;&lt;br /&gt;I don't really have any conclusions; I just felt like this article should end with a "Conclusions" section.  :)&lt;br /&gt;&lt;P&gt;The only useful things to come out of this are:&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;I added a new trick to the &lt;code&gt;OptimizedGraphics2D&lt;/code&gt; class.  However as I phase out Quartz more and more, it doesn't seem that important.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;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.&lt;/LI&gt;&lt;/ul&gt;&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;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.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-4612922486665030157?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/4612922486665030157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/11/gradients-boring-discussion.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/4612922486665030157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/4612922486665030157'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/11/gradients-boring-discussion.html' title='Gradients: a Boring Discussion'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-1470062888663053085</id><published>2009-10-25T21:11:00.000-07:00</published><updated>2011-02-21T13:05:44.471-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AlphaComposite'/><category scheme='http://www.blogger.com/atom/ns#' term='images'/><category scheme='http://www.blogger.com/atom/ns#' term='Composite'/><category scheme='http://www.blogger.com/atom/ns#' term='Shapes'/><title type='text'>AlphaComposites: Which Does What?</title><content type='html'>It seems like every 4 months or so I come across a particular problem that I want to solve using &lt;a href="http://java.sun.com/javase/6/docs/api/java/awt/AlphaComposite.html"&gt;AlphaComposites&lt;/a&gt;.  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.&lt;br /&gt;&lt;p&gt;Here is an applet that demonstrates most of the possible variables at play:&lt;br /&gt;&lt;br&gt;&lt;APPLET CODE="com/bric/awt/CompositeDemo" CODEBASE="http://javagraphics.java.net/jars/" width=320 height=440&gt;     &lt;PARAM NAME="archive" VALUE="CompositeDemo.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;p&gt;This applet (source included) is available &lt;a href="http://javagraphics.java.net/jars/CompositeDemo.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;Also there are several &lt;code&gt;AlphaComposite&lt;/code&gt; 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 &lt;code&gt;BufferedImage&lt;/code&gt;, and then those images are rendered together.&lt;br /&gt;&lt;p&gt;That's all.  This is a clean, short article -- as much for my benefit as anyone else's.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-1470062888663053085?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/1470062888663053085/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/10/alphacomposites-which-does-what.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1470062888663053085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1470062888663053085'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/10/alphacomposites-which-does-what.html' title='AlphaComposites: Which Does What?'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-6422669710824510526</id><published>2009-08-21T09:53:00.000-07:00</published><updated>2011-02-21T13:06:23.129-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='plaf'/><category scheme='http://www.blogger.com/atom/ns#' term='buttonUI'/><category scheme='http://www.blogger.com/atom/ns#' term='focus'/><category scheme='http://www.blogger.com/atom/ns#' term='XP'/><category scheme='http://www.blogger.com/atom/ns#' term='Vista'/><category scheme='http://www.blogger.com/atom/ns#' term='look-and-feel'/><category scheme='http://www.blogger.com/atom/ns#' term='aqua'/><category scheme='http://www.blogger.com/atom/ns#' term='button'/><title type='text'>Buttons: New UI's</title><content type='html'>This article features some new &lt;code&gt;ButtonUIs&lt;/code&gt; I've been working on.&lt;br /&gt;&lt;p&gt;Coming from a strong background in Macs, the first UIs I wanted to emulate are the ones found &lt;a href="http://developer.apple.com/technotes/tn2007/tn2196.html#BUTTONS"&gt;here&lt;/a&gt;.  But I abstracted some common patterns and made a generic model that can easily be adapted to other looks, too.&lt;br /&gt;&lt;P&gt;Here's a demo applet showing off what I have so far.  The UI's are displayed in a &lt;code&gt;JInternalFrame&lt;/code&gt; so you can resize the window to see how they scale.  (They're originally presented at their preferred size.)&lt;br /&gt;&lt;BR&gt;&lt;APPLET CODE="com/bric/plaf/demo/FilledButtonUIDemo" CODEBASE="http://javagraphics.java.net/jars/" width=600 height=427&gt;     &lt;PARAM NAME="archive" VALUE="FilledButtonUI.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;This app (source included) is available &lt;a href="http://javagraphics.java.net/jars/FilledButtonUI.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;h3&gt;Why Bother?&lt;/h3&gt;&lt;br /&gt;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:&lt;br /&gt;&lt;LI&gt;These are cross-platform.  You can use them anywhere.&lt;br /&gt;&lt;LI&gt;These are pure Java.  This means you can override methods of interest.  Also if you find a bug you can go exploring...&lt;br /&gt;&lt;LI&gt;Resizing.  It turns out -- last I checked -- that Apple's buttons are vector-based.  (Try rendering them through an &lt;code&gt;AffineTransform&lt;/code&gt;!)  However: as &lt;code&gt;JComponents&lt;/code&gt; 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.&lt;br /&gt;&lt;LI&gt;Vertical segments.  Although horizontal segments are much more common/important: why not offer vertical segments?&lt;br /&gt;&lt;LI&gt;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.&lt;br /&gt;&lt;P&gt;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!&lt;br /&gt;&lt;h3&gt;Architecture&lt;/h3&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;P&gt;In this version the &lt;code&gt;&lt;A HREF="http://javagraphics.java.net/doc/com/bric/plaf/FilledButtonUI.html"&gt;FilledButtonUI&lt;/A&gt;&lt;/code&gt; 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.&lt;br /&gt;&lt;P&gt;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 &lt;Code&gt;getFocusPainting()&lt;/code&gt; method to return &lt;code&gt;PAINT_NO_FOCUS&lt;/code&gt;.  (This means that the &lt;Code&gt;FilledButtonUI&lt;/code&gt; isn't automatically painting focus for you, and you need to implement your own painting code somewhere else.)&lt;br /&gt;&lt;H3&gt;Client Properties&lt;/H3&gt;&lt;br /&gt;In the demo above all the buttons share the same instance of one &lt;code&gt;ButtonUI&lt;/code&gt;.  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:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;myButton.putClientProperty( HORIZONTAL_POSITION, LEFT );&lt;br /&gt;myButton.putClientProperty( VERTICAL_POSITION, BOTTOM );&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;(It is assumed if one of these properties is missing that the intended value is &lt;code&gt;ONLY&lt;/CODE&gt;.)&lt;br /&gt;&lt;P&gt;Also these UI's support an arbitrary shape.  To create a circular button you can call:&lt;br /&gt;&lt;br&gt;&lt;pre&gt;myButton.putClientProperty(SHAPE, new Ellipse2D.Float(0,0,100,100));&lt;/pre&gt;&lt;br /&gt;&lt;P&gt;(The shape will be scaled until the icon and text of a button fit inside it.)&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;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 &lt;code&gt;FilledButtonUI&lt;/code&gt; 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.&lt;br /&gt;&lt;H3&gt;ButtonClusters&lt;/H3&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;P&gt;The &lt;code&gt;&lt;A HREF="http://javagraphics.java.net/doc/com/bric/plaf/ButtonCluster.html"&gt;ButtonCluster&lt;/A&gt;&lt;/code&gt; 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 &lt;code&gt;ButtonCluster&lt;/code&gt; has no way of knowing if that is or is not the case; it assumes that part of your UI is unchanging.&lt;br /&gt;&lt;P&gt;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 &lt;code&gt;FilledButtonUI&lt;/code&gt; 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.&lt;br /&gt;&lt;P&gt;Also be sure to check out the static &lt;code&gt;install()&lt;/code&gt; methods in the javadoc: that may be the only method you need to call from your code to incorporate this project into your app.&lt;br /&gt;&lt;H3&gt;Embellishments&lt;/H3&gt;&lt;br /&gt;Extras.  Eye candy.  I added a few to the demo applet just to demonstrate how easy they are.&lt;br /&gt;&lt;P&gt;The blinking focus is built into the &lt;code&gt;FilledButtonUI&lt;/code&gt;, 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.&lt;br /&gt;&lt;P&gt;The other two extras (shimmer and zoom) are &lt;code&gt;&lt;a HREF="http://javagraphics.java.net/doc/com/bric/plaf/UIEffect.html"&gt;UIEffects&lt;/a&gt;&lt;/code&gt;.  This is a very simple but fun little class that the &lt;code&gt;FilledButtonUI&lt;/code&gt; 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 &lt;code&gt;FilledButtonUI&lt;/code&gt; subclasses.  (And remember Java2D's most basic drawing tools can &lt;a href="http://javagraphics.blogspot.com/2007/04/slideshows-transitions-swf.html"&gt;go a long way&lt;/a&gt; if you're creative.)&lt;br /&gt;&lt;h3&gt;Keyboard Focus&lt;/h3&gt;&lt;br /&gt;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 &lt;code&gt;&lt;a HREF="http://javagraphics.java.net/doc/com/bric/plaf/FocusArrowListener.html"&gt;FocusArrowListener&lt;/a&gt;&lt;/code&gt;.  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.  &lt;code&gt;JToolBars&lt;/code&gt; 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 &lt;code&gt;JToolBar&lt;/code&gt; the focus will wrap around to the other side.)&lt;br /&gt;&lt;P&gt;Again: thanks goes to Werner for some helpful tips on this element.&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;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.&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;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.&lt;br /&gt;&lt;P&gt;Feel free in the comments to talk about your favorite ButtonUI's from other sources/L&amp;F's, and what makes them your favorite.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-6422669710824510526?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/6422669710824510526/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/08/buttons-new-uis.html#comment-form' title='27 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6422669710824510526'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6422669710824510526'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/08/buttons-new-uis.html' title='Buttons: New UI&apos;s'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>27</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-2654180266196484535</id><published>2009-06-13T22:59:00.000-07:00</published><updated>2011-02-21T13:08:14.113-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pixels'/><category scheme='http://www.blogger.com/atom/ns#' term='layout'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='alignment'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='inspector'/><category scheme='http://www.blogger.com/atom/ns#' term='layoutmanager'/><title type='text'>Layouts: Designing an Inspector</title><content type='html'>This article covers a lot of ground... I guess the simplest introduction is: I recently needed to redesign some inspectors.  I wanted to make something easy for the other developers, and add an unprecedented level of precision/cleaniness to the GUI.  (By "unprecedented" I mean "unprecedented for our company"... not "unprecedented in the field of computer science.")&lt;br /&gt;&lt;BR&gt;&lt;h3&gt;Designing an Inspector&lt;/h3&gt;&lt;br /&gt;First off: how to design a model for laying out elements in the inspector?  First let's make an abstract layer.  This will offer several perks:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Developers won't have to fuss with the details of a &lt;code&gt;GridBagLayout&lt;/code&gt;, or a &lt;code&gt;GroupLayout&lt;/code&gt; (or whatever-else-you-like).  This saves them from extra work, and it minimizes copy-and-pasted code for common designs.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A restricted set of options will standardize the look of the inspector.  This is purely aesthetic, but I believe an aesthetic interface makes the software more likeable (and sometimes more usable).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Different &lt;code&gt;LayoutManagers&lt;/code&gt; can be used under the hood.  In my case I wanted to experiment with &lt;code&gt;GridBagLayouts&lt;/code&gt;, &lt;code&gt;GroupLayouts&lt;/code&gt;, and finally my own custom layout (&lt;code&gt;AlignedGridBagLayout&lt;/code&gt;).&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;So what should this inspector look like?  Here are examples from companies that (presumably?) put thousands of dollars into design:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://web.me.com/bricolage1/blog_resources/inspector1.png" /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I could write a whole article (or more) on the subtleties of what I like and don't like in these screenshots, but my conclusion would be: &lt;i&gt;I want something simpler&lt;/i&gt;.  I'm not saying Microsoft or Apple did anything especially wrong here, but I'm a smaller developer and can afford to be more of an idealist.  Specifically:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Icon tabs.  Boo.  Raskin ranted about icons in general in &lt;i&gt;The Humane Interface&lt;/i&gt;: basically we use them too much.  You have to teach people their meaning, which begs the question: why not just use words?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Unnecessary controls.  When I turn on a shadow, I usually want to change the offset and the opacity.  But not the other properties.  (The color?  Black.  The angle?  Bottom-right.  The blur?  "Some.")  Those options should exist, but they don't need to be in my main interface.  Use a collapsible panel -- or another panel entirely -- to navigate to these power-user options.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Inconsistent.  Each section has its own formatting rules.  This is probably to help cram as much as possible into a small space -- which is painful in itself.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;My new model is much simpler.  It also partitions the inspector into rows, but those rows have a suggested format: a right-aligned identifier, and a left-aligned control.  &lt;a href="http://javagraphics.java.net/doc/com/bric/inspector/InspectorLayout.html"&gt;Here&lt;/a&gt; is the Java interface I came up with.  (You can still implement custom panels by adding a component to a row and stretching it horizontally: this allows nested panels that can be composed however-you-want.  But this should be a last resort.)&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Here is an example of the type of inspector this would produce:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://web.me.com/bricolage1/blog_resources/inspector2.png" /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;In this model the "More" button can change the inspector to show more detailed options, or invoke a dialog.  Or the options could show up in the workspace itself (when you select a star shape in iWeb, for example, controls appear in a small &lt;a href="http://code.google.com/p/macwidgets/wiki/Examples"&gt;HUD-like panel&lt;/a&gt; just below your selection.)&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I haven't played sufficiently with &lt;a href="http://swingx.java.net/"&gt;collapsible panels&lt;/a&gt; (or "disclosure triangles", or whatever-else you want to call them).  That might also be a promising option to look into.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;This model is especially vulnerable to localization problems.  It might be necessary eventually to allow different margins for different groups of controls?  Ideally the identifiers should be brief in all localizations, but sometimes this may not be possible.&lt;br /&gt;&lt;/p&gt;&lt;BR&gt;&lt;h3&gt;Layout Implementation&lt;/h3&gt;&lt;br /&gt;The problem with simplifying an interface so elements align is: it's painfully obvious when elements &lt;i&gt;aren't&lt;/i&gt; precisely aligning.  On XP, Vista, and OpenSUSE this did not stand out as a striking problem, but on Mac it did.&lt;br /&gt;&lt;table&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;br /&gt;&lt;br /&gt;&lt;img src="http://web.me.com/bricolage1/blog_resources/inspector3.png" align="left" /&gt;To the left is a zoomed-in example of the problem.  In the Aqua look-and-feel a &lt;code&gt;JLabel&lt;/code&gt; and &lt;code&gt;JCheckBox&lt;/code&gt; have very different padding: so even though both components are right-aligned they may not look like it.&lt;br /&gt;&lt;p&gt;Meanwhile the controls on the right side are worse, and trickier.  My ideal is for all the pixels to be left-aligned.  But in this case the &lt;code&gt;JSpinner&lt;/code&gt; will add 2-3 extra pixels when it has the focus.  And the &lt;code&gt;JSlider&lt;/code&gt; allows several extra pixels so when the knob is at the far left it has lots of room.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Also (less noticeably) the baselines for some rows are inconsistent.  The spinner and its label have baselines that are 1 pixel off.  The combobox and its label are 2 or 3 pixels off.  This is not as urgent as the horizontal alignment, but it would also be nice to address.&lt;br /&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;When I mentioned these to Werner, he encouraged me to use a &lt;a href="http://java.sun.com/javase/6/docs/api/index.html?javax/swing/GroupLayout.html"&gt;&lt;code&gt;GroupLayout&lt;/code&gt;&lt;/a&gt; instead of a &lt;code&gt;GridBagLayout&lt;/code&gt;.  They offer the abstract &lt;a href="http://java.sun.com/javase/6/docs/api/index.html?javax/swing/LayoutStyle.html"&gt;&lt;code&gt;LayoutStyle&lt;/code&gt;&lt;/a&gt; class, and each look-and-feel can implement its own style guide to ease these rough edges.&lt;br /&gt;&lt;p&gt;(I argued that the &lt;code&gt;GroupLayout&lt;/code&gt; is only in Java 1.6, and I try to maintain Java 1.5 or 1.4 compatibility, and Werner pointed out this layout exists independently &lt;a href="http://swing-layout.java.net/"&gt;here&lt;/a&gt;.)&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Still, after implementing it (erg, to the best of my ability), I wasn't getting quite the results I wanted.  And as far as I could tell: I never could.  Also my GUIs often include custom UI elements.  (For example an &lt;a href="http://javagraphics.blogspot.com/2008/05/angles-need-gui-widget-for-angles.html"&gt;angle widget&lt;/a&gt;, or a &lt;a href="http://javagraphics.blogspot.com/2008/05/gradients-gui-widget-to-design-them.html"&gt;gradient slider&lt;/a&gt;.)  I would need a way for the &lt;code&gt;LayoutStyle&lt;/code&gt; to be made aware of these components.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;So I wanted a layout that could look at pixels.  I ended up extending the &lt;code&gt;GridBagLayout&lt;/code&gt; and created the &lt;a href="http://javagraphics.java.net/doc/com/bric/awt/AlignedGridBagLayout.html"&gt;&lt;code&gt;AlignedGridBagLayout&lt;/code&gt;&lt;/a&gt;.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The most important method in this subclass is:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;public void layoutContainer(Container parent) {&lt;/code&gt;&lt;br /&gt;    super.layoutContainer(parent);&lt;br /&gt;    nudgeVertically(parent);&lt;br /&gt;    nudgeHorizontally(parent);&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;The &lt;code&gt;nudgeVertically&lt;/code&gt; method looks at the baselines that are used when rendering a component, and if baselines are close but different: some components will be nudged up or down to make the baselines line up.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The &lt;code&gt;nudgeHorizontally&lt;/code&gt; method is trickier: it gets the padding of each component and nudges the component horizontally (pretending that padding doesn't exist).  The result is that things can now be absolutely left or right aligned.  (By "padding" I mean "the normally empty pixels inside the bounds of the &lt;code&gt;JComponent&lt;/code&gt;.)&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The tricky part is the phrase "it gets the padding".  I devoted 2 classes to this task:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/awt/PaddingInfo.html"&gt;&lt;code&gt;PaddingInfo&lt;/code&gt;&lt;/a&gt;: this paints a component in an offscreen image and measures the empty pixels around it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/awt/StoredPaddingInfo.html"&gt;&lt;code&gt;StoredPaddingInfo&lt;/code&gt;&lt;/a&gt;: this extends &lt;code&gt;PaddingInfo&lt;/code&gt;.  It refers to cached values of frequently-used components.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;It is the designers' responsibility, then, to leave large insets in the &lt;code&gt;GridBagLayout&lt;/code&gt;.  These insets are the only padding standing between your components, and suddenly these insets are where the focus of your spinner and the knob of your slider will be painted.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;This layout is still experimental, and at present I'm only ready to use it in very controlled situations (like inspectors).  I do &lt;i&gt;not&lt;/i&gt; recommend it for general use yet.  With that disclaimer out of the way, here's what the final version looks like on my Mac:&lt;br /&gt;&lt;br&gt;&lt;img src="http://web.me.com/bricolage1/blog_resources/inspector4.png"&gt;&lt;br /&gt;&lt;p&gt;Much better, right?  If you're using a Mac, there is a demo program &lt;a href="http://javagraphics.java.net/jars/Inspector.jar"&gt;here&lt;/a&gt; (source included) that creates the above screenshot.  Note this entire section (relating to the &lt;code&gt;AlignedGridBagLayout&lt;/code&gt; is &lt;i&gt;not necessary&lt;/i&gt; on other platforms.  This demo program should not be run on other platforms.  (In fact on Vista due to some strange rendering, erm, "issues": the demo looks atrocious as you mouse over different components.)&lt;br /&gt;&lt;P&gt;So as promised I covered a lot of subject material here.  If you have any constructive comments (or related links) about anything written here, feel free to leave them below.  Thanks for stopping by.  :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-2654180266196484535?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/2654180266196484535/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/06/layouts-designing-inspector.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2654180266196484535'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2654180266196484535'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/06/layouts-designing-inspector.html' title='Layouts: Designing an Inspector'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-7979680736174352340</id><published>2009-05-25T20:56:00.000-07:00</published><updated>2011-02-21T13:08:55.814-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='calligraphic'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='calligraphy'/><category scheme='http://www.blogger.com/atom/ns#' term='stroke'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='custom stroke'/><title type='text'>Strokes: a Calligraphy Stroke</title><content type='html'>This entry outlines how to approach programming a calligraphic &lt;code&gt;java.awt.Stroke&lt;/code&gt;.  Here is an applet demonstrating the finished product:&lt;br /&gt;&lt;br&gt;&lt;APPLET CODE="com/bric/awt/CalligraphyStrokeDemo" CODEBASE="http://javagraphics.java.net/jars/" width=283 height=374&gt;     &lt;PARAM NAME="archive" VALUE="CalligraphyStroke.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;You can download this applet (source included) &lt;A HREF="http://javagraphics.java.net/jars/CalligraphyStroke.jar"&gt;here&lt;/A&gt;.&lt;br /&gt;&lt;H4&gt;The Concept&lt;/H4&gt;Genuine &lt;A HREF="http://en.wikipedia.org/wiki/Calligraphy"&gt;calligraphy&lt;/A&gt; is an ancient and complex art form.  My implementation focuses on the most basic elements possible: tracing a path with a nib at a fixed angle and width.&lt;br /&gt;&lt;P&gt;A more complex implementation might include the ability to change the angle/width of the nib as the path progresses, the ability to paint in multiple colors, or the ability to aesthetically combine different symbols.  If anyone wants to tackle those elements: good luck.  Let me know how it goes. :)  Or if you want to pay me to tackle them: shoot me an email.&lt;br /&gt;&lt;H4&gt;The Algorithm&lt;/H4&gt;&lt;br /&gt;The algorithm is actually very simple.  Studying the case of the ellipse and the rectangle will be (mostly) sufficient.&lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;img src="http://javagraphics.java.net/resources/Ellipse.png" align="left"&gt;&lt;H5&gt;The Ellipse&lt;/H5&gt;The ellipse is the more interesting case.  To trace an ellipse with a calligraphic stroke we need to trace the shape twice: once offset (dx, dy) and another time offset (-dx, -dy).&lt;br /&gt;&lt;p&gt;For example, to render a stroke around the black ellipse to the left, we'll use the blue and red dotted outlines.&lt;br /&gt;&lt;p&gt;Let's call these outlines "tracks".  Suppose we start at the far right edge of the ellipse on the red track, moving clockwise:&lt;br /&gt;&lt;blockquote&gt;1. Trace down to the bottommost point.&lt;br /&gt;2. Switch to the blue track.&lt;br /&gt;3. Continue tracing clockwise on the blue track until you reach the topmost point.&lt;br /&gt;4. Switch to the red track.&lt;br /&gt;5. Continue tracing clockwise on the red track until you reach the end of the shape.&lt;br /&gt;6. Follow the green line segment to the blue track.  Now we're going to reverse directions.&lt;br /&gt;7. Trace the blue path counter-clockwise to the topmost point.&lt;br /&gt;8. Switch to the red track.&lt;br /&gt;9. Continue tracing counter-clockwise on the red track until you reach the bottommost point.&lt;br /&gt;10. Switch to the blue track.&lt;br /&gt;11. Continue tracing counter-clockwise on the blue track until you reach the starting point.&lt;br /&gt;12. Follow the green line segment to the red track.&lt;/blockquote&gt;&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;P&gt;The only thing you have to watch out for is when to switch tracks.  This is easy (for now): you switch tracks when the tangent is tangent to the nib.  The nib controls the offset (+dx,+dy), and in the example above is set at zero degrees.&lt;br /&gt;&lt;P&gt;How do you know when the slope is tangent to a particular angle?  In the example above it's easy to detect:&lt;br /&gt;&lt;blockquote&gt;1.  Convert the bezier curve to a polynomial expression:&lt;br /&gt; &amp;nbsp; &lt;code&gt;y = ay*(t^3)+by*(t^2)+cy*t+dy&lt;/code&gt;&lt;br /&gt;2.  Look at the derivative of that expression:&lt;br /&gt; &amp;nbsp; &lt;code&gt;y' = 3*ay*(t^2)+2*by*(t)+cy&lt;/code&gt;&lt;br /&gt;3.  Solve the derivative y' for zero.&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;So when the nib angle is non-zero ... well... shoot.  That's a tough one.  If the nib angle is "k", let's just rotate the entire system by -k.  Problem solved.&lt;br /&gt;&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;img src="http://javagraphics.java.net/resources/Rectangle.png" align="left"&gt;&lt;H5&gt;The Rectangle&lt;/H5&gt;We're not done yet, though.  Consider a rectangle where the nib angle is 45 degrees.  Here the slope of the segments are never tangent to the nib, but we still need to switch tracks twice (at the bottom-right corner and the top-left corner).&lt;br /&gt;&lt;P&gt;The rule to know when to switch is simple:&lt;br /&gt;&lt;blockquote&gt;1.  Rotate the shape by -k degrees.&lt;br /&gt;2.  Treat the incoming and outgoing slopes of each segment as euclidean vectors.&lt;br /&gt;3.  Switch tracks if the y vectors are on different sides of the x-axis.&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;So to get from one slope to another slope: we crossed the nib angle.  Crossing the nib angle means we have to switch tracks.  In cases where the angles are not continuous but do &lt;i&gt;not&lt;/i&gt; cross the nib angle: it's OK to continue tracing the same track.  (For example: see the bottom-left and top-right corners of the rectangle.)&lt;br /&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;H4&gt;Special Cases&lt;/H4&gt;Alternative title: "I Hate My Life"&lt;br /&gt;&lt;P&gt;The demo applet works, but those are simple cases.  Suppose you have a cubic &lt;code&gt;GeneralPath&lt;/code&gt;, but the first control point falls on the starting point of the curve (t=0), and the last control point falls on the end-point of the curve (t=1).  This path resembles a line since there is no curvature information... but it is expressed as a cubic curve.&lt;br /&gt;&lt;P&gt;(My first reaction when I read something like this is: "Oh come on.  You're just TRYING to be difficult.  You made this up, didn't you?"  Unfortunately I did not.  I stumbled across it accidentally, when I realized one of my applications represents all segments in certain shapes as cubic -- even if the segment is basically a line.)&lt;br /&gt;&lt;P&gt;Mathematically this raises some serious problems: when the control points exactly overlap the endpoints, then at t=0 and t=1: dy/dt and dx/dt both equal zero.  But the algorithm above identifies the slope as tangent when &lt;code&gt;dy/dt=0&lt;/code&gt;... so it thinks in these cases that the segment is tangent to the nib at the endpoints of this "line".&lt;br /&gt;&lt;P&gt;If you look at a graphic representation of these equations: there is definitely a tangent slope at t=0 and t=1.  How do I find out what it is?  Luckily I sometimes have coffee with a group of friends who are much smarter than I am, and I asked them.  One of them referred me to &lt;A HREF="http://en.wikipedia.org/wiki/L%27H%C3%B4pital%27s_rule"&gt;L'Hopital's Rule&lt;/A&gt;.  I vaguely recall learning this in high school, but I'd long since forgotten it: if dy/dt and dx/dt approach zero (at a certain t), then I can use their second derivative to calculate their behavior.&lt;br /&gt;&lt;P&gt;The good news is: this works!  Mathematically, I now can figure out the angle at t=0 and t=1.  The bad news is: this... doesn't actually help.&lt;br /&gt;&lt;P&gt;Consider a cubic curve from (0,0) to (50,50), with controls at (0,0) and (50,50).  If you consider the fact that the angle is a vector with a fixed direction: we still can't figure out that angle.  At t=0 L'Hopital's Rule will tell me the angle is .7853982 radians.  At t=1, the angle -2.3561945.  This is exactly &lt;i&gt;pi&lt;/i&gt; units away.  Sigh.  This makes sense: at those times the direction is completely undefined.  But it makes a huge difference: if the angle is offset by pi, then it will trigger the track-switching we use for rectangles.  Grrrr.&lt;br /&gt;&lt;P&gt;The bigger problem is: I don't understand what the "right question" is to ask.  I don't see how to approach this correctly.&lt;br /&gt;&lt;H4&gt;Partial Solution&lt;/H4&gt;For now I have a partial solution in place.&lt;br /&gt;&lt;P&gt;The &lt;A HREF="http://javagraphics.java.net/doc/com/bric/geom/SimplifiedPathIterator.html"&gt;SimplifiedPathIterator&lt;/A&gt; will convert a cubic segment to a linear segment if all the points are collinear.&lt;br /&gt;&lt;P&gt;This fixes my original problem, but it will still fail for this shape:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;GeneralPath path = new GeneralPath();&lt;/code&gt;&lt;br /&gt;&lt;code&gt;p.moveTo(0,0);&lt;/code&gt;&lt;br /&gt;&lt;code&gt;p.curveTo(0,20,50,50,50,50);&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;Here all the points are not collinear.  But if you try to get the angle at t=1, it will appear to be -2.6011732.  (Even though the angle at t=.95 is 0.5481237).  The class &lt;A HREF="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/geom/PathSegment.java?rev=129&amp;view=markup"&gt;PathSegment&lt;/A&gt; contains a very simple &lt;code&gt;main()&lt;/code&gt; demonstrating this problem.&lt;br /&gt;&lt;P&gt;I don't suppose anyone has any suggestions?  To restate the problem: "How do I know when a curve is tangent to a particular angle theta -- including cases where dy/dt=0 and dx/dt=0 in the curve?"&lt;br /&gt;&lt;P&gt;Also: if anyone has any sexier examples of this stroke, I'd be happy to reference them.  This has a lot of potential that this entry doesn't show off well enough.  (Also the &lt;A HREF="http://javagraphics.blogspot.com/2008/12/strokes-charcoal-stroke.html"&gt;CharcoalEffect&lt;/A&gt; can filter this stroke, too.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-7979680736174352340?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/7979680736174352340/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/05/strokes-calligraphy-stroke.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7979680736174352340'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7979680736174352340'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/05/strokes-calligraphy-stroke.html' title='Strokes: a Calligraphy Stroke'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-6979250004510127826</id><published>2009-05-17T22:18:00.000-07:00</published><updated>2011-02-21T13:09:23.797-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='math'/><category scheme='http://www.blogger.com/atom/ns#' term='Optimization'/><title type='text'>Math: Studying Performance</title><content type='html'>I'm revisiting a subject I wrote about nearly 2 years ago.  My original article talked about how to cut the time of the &lt;code&gt;Color.HSBtoRGB()&lt;/code&gt; in half, but it glazed over the underlying problem.  (John helped point out the real issue, but it took me a long time to write it up properly.)  The problem is:&lt;br /&gt;&lt;P&gt;The &lt;code&gt;java.lang.Math&lt;/code&gt; class is slow.  Now some of this is justified: it's accurate and consistent.  The &lt;a href="http://java.sun.com/javase/6/docs/api/java/lang/Math.html"&gt;javadocs&lt;/a&gt; talk about just how consistent these results are going to be across platforms.  Awesome, right?  But there's a catch: sometimes you don't need this accuracy.  In fact, I think in lots of cases the &lt;code&gt;Math&lt;/code&gt; class is delegating to the &lt;code&gt;StrictMath&lt;/code&gt; class, which the javadoc specifically cautions against.&lt;br /&gt;&lt;P&gt;Suppose you're writing an image filter, a gradient, a 3D something, etc.  The human eye doesn't care if your evaluation of a sine curve has an error less than 1 or 2 ulps: it just wants to see a continuous motion with certain types of acceleration/deceleration.  Do we need speed or accuracy more?  In this case: speed.&lt;br /&gt;&lt;P&gt;So I put together a class that approximates certain &lt;code&gt;Math&lt;/code&gt; functions.  You can see the &lt;a href="http://javagraphics.java.net/doc/com/bric/math/MathG.html"&gt;javadocs here&lt;/a&gt;.  You can download it &lt;A HREF="http://javagraphics.java.net/jars/MathG.jar"&gt;here&lt;/A&gt;.&lt;br /&gt;&lt;H3&gt;Ceil/Floor&lt;/H3&gt;&lt;br /&gt;The ceiling and floor functions are the biggest winners here.  (Using &lt;code&gt;MathG.floorInt()&lt;/code&gt; instead of &lt;code&gt;Math.floor()&lt;/code&gt; is the key to cutting the time of &lt;code&gt;Color.RGBtoHSB()&lt;/code&gt; in half.)&lt;br /&gt;&lt;P&gt;The &lt;code&gt;MathG&lt;/code&gt; implementation uses casting.  Here are some test results on various platforms:&lt;br /&gt;&lt;table border="0" frame="below"&gt;&lt;tr&gt;&lt;td&gt;Environment&lt;/td&gt;&lt;td&gt;Math.ceil() ms&lt;/td&gt;&lt;td&gt;MathG.floorInt() ms&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Windows XP 5.1, Java 1.6.0_13&lt;/td&gt;&lt;td&gt;172&lt;/td&gt;&lt;td&gt;16&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Linux 2.6.27.7-9-pae, Java 1.6.0_0&lt;/td&gt;&lt;td&gt;42&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;td&gt;Mac OS X 10.5.7, Java 1.6.0_07&lt;/td&gt;&lt;td&gt;31&lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;P&gt;(Note this should not be used for values greater than 1e10.)&lt;br /&gt;&lt;P&gt;In the javadocs you'll notice I added a "floorInt", "floorDouble", "ceilInt" and "ceilDouble" method.  The theory here was that casting is not free -- which I remember reading multiple times in college and random online articles -- so I expected to see a difference in the performance of those methods.  Turns out I didn't see any difference, though... so I'm not sure if casting is only expensive if go in the other direction (from double-&gt;int), or if I just wasn't measuring it correctly.&lt;br /&gt;&lt;h3&gt;Sin/Cos&lt;/h3&gt;These don't show nearly the same improvement, but there's still a notable gain.&lt;br /&gt;&lt;P&gt;For these I created two polynomial equations: one was of degree 5 and the other was of degree 7.  Using the higher degree polynomial results in a couple more digits of accuracy, but subtly lower performance.&lt;br /&gt;&lt;P&gt;Oleg chimed in in the comments and via email and pointed out that the results vary widely depending on the range of numbers tested.&lt;br /&gt;&lt;P&gt;I explored some more -- and improved the code a little -- and I found that I had reasonable success beating &lt;code&gt;Math.sin()&lt;/code&gt; as long as my argument was less than approximately &lt;code&gt;1e10&lt;/code&gt;. (After &lt;code&gt;1e10&lt;/code&gt; my results were still faster, but the error become unpredictably large.)&lt;br /&gt;&lt;P&gt;I added the private static method &lt;code&gt;testIncreasingMax()&lt;/code&gt; to &lt;code&gt;MathG&lt;/code&gt;, and on my Mac these are the results I received:&lt;br /&gt;&lt;BR&gt;&lt;img src="http://spreadsheets.google.com/pub?key=rqJlaHpstqPxpa-fS-v8W8Q&amp;oid=1&amp;output=image" /&gt;&lt;br /&gt;&lt;P&gt;Each sample include 1,000,000 tests, so all the methods concerned were churning out thousands of calculations per millisecond.  Each sample was also repeated 200 times and the median was taken as the final result (this was to avoid possible hiccups in the data).  The &lt;code&gt;MathG&lt;/code&gt; functions were amazingly consistent at 52 ms and 48 ms once the range of arguments reached [-200,200].&lt;br /&gt;&lt;P&gt;If you pass 1e10, then &lt;code&gt;MathG&lt;/code&gt; starts delegating to the &lt;code&gt;Math&lt;/code&gt; class to avoid painful errors.&lt;br /&gt;&lt;H3&gt;Sample Program Output&lt;/H3&gt;&lt;P&gt;The &lt;code&gt;main()&lt;/code&gt; method in &lt;code&gt;MathG&lt;/code&gt; runs a series of tests.  Here is some abbreviated output from that method on different platforms:&lt;br /&gt;&lt;blockquote&gt;&lt;BR&gt;&lt;block&gt;Running comparison of Math vs MathG on Windows XP 5.1, Java 1.6.0_13&lt;br /&gt; &amp;nbsp; MathG.sin01() median time: 109 ms&lt;br /&gt; &amp;nbsp; MathG.sin00004() median time: 125 ms&lt;br /&gt; &amp;nbsp; Math.sin() median time: 578 ms&lt;br /&gt; &amp;nbsp; MathG.cos01() median time: 109 ms&lt;br /&gt; &amp;nbsp; MathG.cos00004() median time: 125 ms&lt;br /&gt; &amp;nbsp; Math.cos() median time: 562 ms&lt;br /&gt; &amp;nbsp; MathG.floorDouble() median time: 16 ms&lt;br /&gt; &amp;nbsp; MathG.floorInt() median time: 16 ms&lt;br /&gt; &amp;nbsp; Math.floorDouble() median time: 172 ms&lt;br /&gt; &amp;nbsp; MathG.ceilDouble median time: 16 ms&lt;br /&gt; &amp;nbsp; MathG.ceilInt() median time: 16 ms&lt;br /&gt; &amp;nbsp; Math.ceilDouble() median time: 187 ms&lt;br /&gt;&lt;/block&gt;&lt;br /&gt;&lt;block&gt;Running comparison of Math vs MathG on Linux 2.6.27.7-9-pae, Java 1.6.0_0&lt;br /&gt; &amp;nbsp; MathG.sin01() median time: 52 ms&lt;br /&gt; &amp;nbsp; MathG.sin00004() median time: 47 ms&lt;br /&gt; &amp;nbsp; Math.sin() median time: 145 ms&lt;br /&gt; &amp;nbsp; MathG.cos01() median time: 51 ms&lt;br /&gt; &amp;nbsp; MathG.cos00004() median time: 49 ms&lt;br /&gt; &amp;nbsp; Math.cos() median time: 144 ms&lt;br /&gt; &amp;nbsp; MathG.floorDouble() median time: 10 ms&lt;br /&gt; &amp;nbsp; MathG.floorInt() median time: 11 ms&lt;br /&gt; &amp;nbsp; Math.floorDouble() median time: 42 ms&lt;br /&gt; &amp;nbsp; MathG.ceilDouble median time: 11 ms&lt;br /&gt; &amp;nbsp; MathG.ceilInt() median time: 11 ms&lt;br /&gt; &amp;nbsp; Math.ceilDouble() median time: 41 ms&lt;br /&gt;&lt;/block&gt;&lt;br /&gt;&lt;block&gt;Running comparison of Math vs MathG on Mac OS X 10.5.7, Java 1.6.0_07&lt;br /&gt; &amp;nbsp; MathG.sin01() median time: 59 ms&lt;br /&gt; &amp;nbsp; MathG.sin00004() median time: 66 ms&lt;br /&gt; &amp;nbsp; Math.sin() median time: 73 ms&lt;br /&gt; &amp;nbsp; MathG.cos01() median time: 60 ms&lt;br /&gt; &amp;nbsp; MathG.cos00004() median time: 66 ms&lt;br /&gt; &amp;nbsp; Math.cos() median time: 71 ms&lt;br /&gt; &amp;nbsp; MathG.floorDouble() median time: 0 ms&lt;br /&gt; &amp;nbsp; MathG.floorInt() median time: 0 ms&lt;br /&gt; &amp;nbsp; Math.floorDouble() median time: 31 ms&lt;br /&gt; &amp;nbsp; MathG.ceilDouble median time: 0 ms&lt;br /&gt; &amp;nbsp; MathG.ceilInt() median time: 0 ms&lt;br /&gt; &amp;nbsp; Math.ceilDouble() median time: 31 ms&lt;br /&gt;&lt;/block&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;I'm not pretending to be experienced in this field, but it is an interesting thing to play with.  Next time I write a really tight loop (implementing a gradient, for example), I'll see if using this class can make a dent in performance.  Also you might want to try running &lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/test/HSBTest.java?view=markup"&gt;this small app&lt;/a&gt; to see the effects of &lt;code&gt;Math.floor&lt;/code&gt; first hand.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-6979250004510127826?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/6979250004510127826/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/05/math-studying-performance.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6979250004510127826'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6979250004510127826'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/05/math-studying-performance.html' title='Math: Studying Performance'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-8570528972164461202</id><published>2009-05-17T20:51:00.000-07:00</published><updated>2011-02-21T13:09:42.967-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='indication'/><category scheme='http://www.blogger.com/atom/ns#' term='vector'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='progress'/><category scheme='http://www.blogger.com/atom/ns#' term='indicator'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='JProgressBar'/><title type='text'>Progress: A Spinny Widget</title><content type='html'>It's such a strange title: "Progress: a spinny widget."&lt;br /&gt;&lt;P&gt;But what else do you call it?  Apple calls it an "asynchronous progress indicator" ( &lt;A HREF="http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGControls/XHIGControls.html#//apple_ref/doc/uid/TP30000359-TPXREF208"&gt;[here]&lt;/a&gt; ).  I suppose that makes sense, but it's quite a mouthful.  I called it the "SpinningProgressBarUI".&lt;br /&gt;&lt;P&gt;I put together two flavors: one for Aqua, and one for "other".  (Although the Aqua flavor is quickly becoming ubiquitous in flash apps across the internet, so you might only want to use that?)  For now, though, I have one static call that creates a spinning progress indicator:&lt;br /&gt;&lt;BR&gt;&lt;code&gt;SpinningProgressBarUI.create()&lt;/code&gt;&lt;br /&gt;&lt;BR&gt;On Mac this implements the Aqua look, otherwise I get the spinning arrows.&lt;br /&gt;&lt;P&gt;Here's an applet demonstrating the UI's:&lt;br /&gt;&lt;P&gt;&lt;br /&gt;&lt;APPLET CODE="com/bric/plaf/SpinningProgressBarUIDemo" CODEBASE="http://javagraphics.java.net/jars/" width=300 height=510&gt;     &lt;PARAM NAME="archive" VALUE="SpinningProgressBarUI.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;In the comments below you'll notice Matt made a suggestion about using the rendering hint for strokes.  This did wonders for both UI's.  I added a toggle to this demo applet so you can see if there's a difference on your platform.  Also, for the sake of scrutinizing everything: I added a checkbox to slow down the period of each indicator.&lt;br /&gt;&lt;P&gt;If you're viewing this applet on a Mac, then you'll see three progress indicators.  The third (right-most) is Apple's widget.  You can implement this yourself by referring to the infamous &lt;A HREF="http://developer.apple.com/technotes/tn2007/tn2196.html"&gt;Apple Tech Note 2196&lt;/A&gt;.  As great as everything on that page is, for me it carries one serious flaw: it's all Mac-specific.  If I wanted Mac-specific code, I'd be programming in Cocoa.  I wrote this class so I'd have GUI elements that can work on all platforms in pure Java.&lt;br /&gt;&lt;P&gt;You can download this jar (source code included) &lt;A HREF="http://javagraphics.java.net/jars/SpinningProgressBarUI.jar"&gt;here&lt;/A&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-8570528972164461202?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/8570528972164461202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/05/progress-spinny-widget.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8570528972164461202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8570528972164461202'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/05/progress-spinny-widget.html' title='Progress: A Spinny Widget'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-7373773344222716614</id><published>2009-02-23T09:08:00.000-08:00</published><updated>2011-02-21T13:09:55.387-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Graphics2D'/><category scheme='http://www.blogger.com/atom/ns#' term='Optimization'/><title type='text'>Graphics2D: Improving Performance</title><content type='html'>This project actually began a few years ago when we noticed some huge performance problems on Macs with &lt;code&gt;Graphics2Ds&lt;/code&gt;.  To address these problems we made our own &lt;code&gt;Graphics2D&lt;/code&gt; that delegated certain instructions in special ways to get some major performance improvements.  The worst of the problems (observed back around 2005-ish) are no longer reproducing, but the idea of intercepting certain calls is still a good one.&lt;br /&gt;&lt;p&gt;Last weekend I sat down and rewrote this delegate &lt;code&gt;Graphics2D&lt;/code&gt;, and studied a few old and new tricks.  Old tricks include:&lt;br /&gt;&lt;br&gt;1. Studying &lt;code&gt;drawGlyphVector&lt;/code&gt;.  It turns out when Quartz is enabled on Mac and you're using a custom &lt;code&gt;Paint&lt;/code&gt; object: the performance is really bad.  It can be improved if you simply call: &lt;code&gt;fill(glyphVector.getOutline())&lt;/code&gt;.  This is unique to Quartz, and therefore unique to Macs.&lt;br /&gt;&lt;br&gt;2.  Drawing images.  In Java 1.4 if a custom &lt;code&gt;Paint&lt;/code&gt; object is active: drawing images takes longer than if a &lt;code&gt;Color&lt;/code&gt; paint is active.  (Crazy, right?  That was a fun one to debug.)  I seem to remember it being worse a few years ago, but even today we can shave off over 15% of the time used to draw an image if we set the current paint to a &lt;code&gt;Color&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;New tricks include:&lt;br /&gt;&lt;br&gt;1.  The &lt;code&gt;&lt;a href="http://javagraphics.blogspot.com/2007/04/shapes-clipping-to-rectangle.html"&gt;Clipper&lt;/a&gt;&lt;/code&gt; class.  When you call &lt;code&gt;clip(myShape)&lt;/code&gt;, you can improve the performance if the existing clipping &lt;i&gt;or&lt;/i&gt; the argument is a &lt;code&gt;Rectangle2D&lt;/code&gt;.  Is this a common case?  Maybe.  Are you painting to Swing components?  They clip to rectangles a lot...&lt;br /&gt;&lt;br&gt;2.  Filling shapes.  What do you think happens when you ask to fill a shape that lies completely outside the clipping?  Whatever is happening: it's mysterious and expensive.  If we just perform this simple check: &lt;code&gt;incomingShape.intersects(clipRect)==false&lt;/code&gt; then we can cut that operation down to 80% of its original time.  (This is only for shapes that are outside the clipping, though.)  Still, if you frequently repaint small portions of your window and lots of shapes are asked to paint: this could make a difference.&lt;br /&gt;&lt;br&gt;3.  To implement the optimization for &lt;code&gt;fill()&lt;/code&gt;, the clip rect needed to be cached.  This means we can also optimize &lt;code&gt;getClipBounds()&lt;/code&gt;.  This is a trivial optimization, because the original method was never expensive.  :)  But I managed to reduce the time &lt;code&gt;getClipBounds()&lt;/code&gt; takes by over 90% in most cases... so while trivial it's still a point of pride.  :)&lt;br /&gt;&lt;p&gt;&lt;a href="http://javagraphics.java.net/jars/OptimizedGraphics2D.jar"&gt;Here&lt;/a&gt; is a class that tests all these optimizations.  Note by default these optimizations are &lt;i&gt;not&lt;/i&gt; performed on all platforms/environments (because some only help in certain situations), but the test class makes sure they always run so you can observe their effect.&lt;br /&gt;&lt;p&gt;Below is a list of each optimization and its effect in several different environments (Vista and Linux listed at the bottom).  I'm most interested in Mac because it's been my experience that Mac has the most issues.&lt;br /&gt;&lt;br /&gt;Beginning tests on:&lt;br /&gt; &amp;nbsp OS = Mac OS X (10.5.6, i386)&lt;br /&gt; &amp;nbsp Java Version = 1.4.2_18&lt;br /&gt; &amp;nbsp apple.awt.graphics.UseQuartz = true&lt;br /&gt;&lt;pre&gt;TEST NAME                     NORMAL (ms) OPTIMIZED (ms) %      &lt;br /&gt;P drawGlyphVector() Test      7804        2087           26.74% &lt;br /&gt;P fill() Clipping Test        36          7              19.44% &lt;br /&gt;P drawImage() Background Test 30          25             83.33% &lt;br /&gt;P clip() Test                 73          62             84.93% &lt;br /&gt;P getClipBounds() Test        68          5              7.35%&lt;/pre&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Beginning tests on:&lt;br /&gt; &amp;nbsp OS = Mac OS X (10.5.6, i386)&lt;br /&gt; &amp;nbsp Java Version = 1.5.0_16&lt;br /&gt; &amp;nbsp apple.awt.graphics.UseQuartz = true&lt;br /&gt;&lt;pre&gt;TEST NAME                     NORMAL (ms) OPTIMIZED (ms) %      &lt;br /&gt;P drawGlyphVector() Test      7253        2084           28.73% &lt;br /&gt;P fill() Clipping Test        20          4              20.0%  &lt;br /&gt;F drawImage() Background Test 25          25             100.0% &lt;br /&gt;P clip() Test                 72          60             83.33% &lt;br /&gt;P getClipBounds() Test        51          5              9.8%&lt;/pre&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Beginning tests on:&lt;br /&gt; &amp;nbsp OS = Mac OS X (10.5.6, i386)&lt;br /&gt; &amp;nbsp Java Version = 1.5.0_16&lt;br /&gt; &amp;nbsp apple.awt.graphics.UseQuartz = false&lt;br /&gt;&lt;pre&gt;TEST NAME                     NORMAL (ms) OPTIMIZED (ms) %       &lt;br /&gt;F drawGlyphVector() Test      273         1297           475.09% &lt;br /&gt;P fill() Clipping Test        51          4              7.84%   &lt;br /&gt;F drawImage() Background Test 62          63             101.61% &lt;br /&gt;P clip() Test                 71          59             83.09%  &lt;br /&gt;P getClipBounds() Test        50          5              10.0%&lt;/pre&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Beginning tests on:&lt;br /&gt; &amp;nbsp OS = Mac OS X (10.5.6, x86_64)&lt;br /&gt; &amp;nbsp Java Version = 1.6.0_07&lt;br /&gt; &amp;nbsp apple.awt.graphics.UseQuartz = false&lt;br /&gt;&lt;pre&gt;TEST NAME                     NORMAL (ms) OPTIMIZED (ms) %       &lt;br /&gt;F drawGlyphVector() Test      189         888            469.84% &lt;br /&gt;P fill() Clipping Test        23          2              8.69%   &lt;br /&gt;F drawImage() Background Test 32          32             100.0%  &lt;br /&gt;P clip() Test                 66          45             68.18%  &lt;br /&gt;P getClipBounds() Test        21          2              9.52%&lt;/pre&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Beginning tests on:&lt;br /&gt; &amp;nbsp OS = Mac OS X (10.5.6, x86_64)&lt;br /&gt; &amp;nbsp Java Version = 1.6.0_07&lt;br /&gt; &amp;nbsp apple.awt.graphics.UseQuartz = true&lt;br /&gt;&lt;pre&gt;TEST NAME                     NORMAL (ms) OPTIMIZED (ms) %      &lt;br /&gt;P drawGlyphVector() Test      6177        1635           26.46% &lt;br /&gt;P fill() Clipping Test        14          2              14.28% &lt;br /&gt;F drawImage() Background Test 20          21             105.0% &lt;br /&gt;P clip() Test                 71          45             63.38% &lt;br /&gt;P getClipBounds() Test        21          2              9.52%&lt;/pre&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Beginning tests on:&lt;br /&gt; &amp;nbsp OS = Linux (2.6.27.7-9-pae, i386)&lt;br /&gt; &amp;nbsp Java Version = 1.6.0_0&lt;br /&gt;&lt;pre&gt;TEST NAME                     NORMAL (ms) OPTIMIZED (ms) %       &lt;br /&gt;F drawGlyphVector() Test      170         389            228.82% &lt;br /&gt;P fill() Clipping Test        22          1              4.54%   &lt;br /&gt;F drawImage() Background Test 76          77             101.31% &lt;br /&gt;P clip() Test                 76          55             72.36%  &lt;br /&gt;P getClipBounds() Test        29          2              6.89%&lt;/pre&gt;   &lt;br /&gt;&lt;hr&gt;&lt;br /&gt;Beginning tests on:&lt;br /&gt; &amp;nbsp OS = Windows Vista (6.0, x86)&lt;br /&gt; &amp;nbsp Java Version = 1.6.0_12&lt;br /&gt;&lt;pre&gt;TEST NAME                     NORMAL (ms) OPTIMIZED (ms) %       &lt;br /&gt;F drawGlyphVector() Test      156         1263           809.61% &lt;br /&gt;P fill() Clipping Test        31          0              0.0%    &lt;br /&gt;F drawImage() Background Test 47          47             100.0%  &lt;br /&gt;P clip() Test                 62          47             75.8%   &lt;br /&gt;P getClipBounds() Test        31          0              0.0%&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;So the next question is: what else can be improved?  Can &lt;code&gt;draw()&lt;/code&gt; be improved?  (That requires dealing with strokes, which makes it a little more complex.)  And as always I'm making the source code public: feedback is always welcome.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-7373773344222716614?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/7373773344222716614/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/02/graphics2d-improving-performance.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7373773344222716614'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7373773344222716614'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/02/graphics2d-improving-performance.html' title='Graphics2D: Improving Performance'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-1183975125389390235</id><published>2009-02-22T12:49:00.000-08:00</published><updated>2011-02-21T13:10:18.915-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='distance'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Shape'/><title type='text'>Shapes: Measuring Length</title><content type='html'>This is going to be a lackluster blog entry.  It's not going to sparkle or wow anyone... but don't let that fool you: it's very useful.&lt;br /&gt;&lt;p&gt;Actually the reason I'm writing this up as its own entry is that it's a crucial part of three custom &lt;code&gt;Strokes&lt;/code&gt; included in this blog:&lt;br /&gt;&lt;li&gt;The &lt;code&gt;&lt;a href="http://javagraphics.blogspot.com/2007/04/strokes-brush-stroke.html"&gt;BrushStroke&lt;/a&gt;&lt;/code&gt;.&lt;br /&gt;&lt;li&gt;The &lt;code&gt;&lt;a href="http://javagraphics.blogspot.com/2008/05/strokes-bristle-stroke.html"&gt;BristleStroke&lt;/a&gt;&lt;/code&gt;.&lt;br /&gt;&lt;li&gt;The &lt;code&gt;&lt;a href="http://javagraphics.blogspot.com/2008/12/strokes-charcoal-stroke.html"&gt;CharcoalStroke&lt;/a&gt;&lt;/code&gt;.&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The concept is very simple: given a &lt;code&gt;Shape&lt;/code&gt; object, we often need a way to talk about the length of that shape.  This includes being able to talk about that shape as fraction (from 0% to 100%) and in pixel lengths (iterate over 5 pixels at a time).&lt;br /&gt;&lt;p&gt;The &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/geom/MeasuredShape.html"&gt;MeasuredShape&lt;/a&gt;&lt;/code&gt; is designed to address these needs.&lt;br /&gt;&lt;p&gt;There are a couple of possible pitfalls with this class:&lt;br /&gt;&lt;br&gt;1.  In Java, a &lt;code&gt;Shape&lt;/code&gt; can have several subpaths.  If we want to discuss a shape in terms of fractions: it makes sense to break up each subpath into its own object.  So it is hazardous to use the constructors that simply take a &lt;code&gt;Shape&lt;/code&gt; argument, because they might throw &lt;code&gt;IllegalArgumentExceptions&lt;/code&gt;.&lt;br /&gt;&lt;br&gt;2.  Also, especially when talking about fractions, it doesn't make sense to talk about unclosed shapes.  So there is are two subtly different methods in the &lt;code&gt;MeasuredShape&lt;/code&gt; class: &lt;code&gt;getDistance()&lt;/code&gt; and &lt;code&gt;getOriginalDistance()&lt;/code&gt;.  If the path was closed when the &lt;code&gt;MeasuredShape&lt;/code&gt; was created: then these will be equal.  If the path was unclosed, then the &lt;i&gt;original&lt;/i&gt; distance represent the path that was passed to the constructor, and the current distance represents the length of the closed path.&lt;br /&gt;&lt;br /&gt;&lt;P&gt;Here is an applet demonstrating one fun method of this class:&lt;br /&gt;&lt;br&gt;This applet takes a circle and lets you adjust the percent drawn and the offset where the drawing begins.  (This is handled in just 1 method in the &lt;code&gt;MeasuredShape&lt;/code&gt; class.)&lt;br /&gt;&lt;br /&gt;&lt;APPLET CODE="com/bric/geom/MeasuredShapeDemo" CODEBASE="http://javagraphics.java.net/jars/" width=232 height=301&gt;     &lt;PARAM NAME="archive" VALUE="MeasuredShape.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;br /&gt;&lt;P&gt;The jar (source code included) is available &lt;a href="http://javagraphics.java.net/jars/MeasuredShape.jar"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-1183975125389390235?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/1183975125389390235/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2009/02/shapes-measuring-length.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1183975125389390235'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1183975125389390235'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2009/02/shapes-measuring-length.html' title='Shapes: Measuring Length'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-1180372164753080865</id><published>2008-12-18T10:33:00.000-08:00</published><updated>2011-02-21T13:10:55.023-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Graphics2D'/><category scheme='http://www.blogger.com/atom/ns#' term='vector graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='serializable'/><title type='text'>Graphics2D: Serializable Vector Graphics</title><content type='html'>This is an awkward blog entry, because it takes a lot of explaining.  Even the title was awkward... really "serialization" isn't the goal at all.  The goal is to provide a set of classes that can represent vector graphics.  This project is really meant to be a foundation for bigger and better projects: it's not a "final product" in itself.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;So with that introduction out of the way: I'd like to introduce the &lt;a href="http://javagraphics.java.net/doc/com/bric/graphics/GraphicsWriter.html"&gt;GraphicsWriter&lt;/a&gt; class.  This is a &lt;code&gt;Graphics2D&lt;/code&gt; object that you can paint to, and it will store your graphics as a series of drawing instructions.&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I already have at least three great uses for this class:&lt;br /&gt;&lt;br&gt;1.  Exporting Vector Graphics.  This is reminiscent of &lt;A HREF="http://xmlgraphics.apache.org/batik/"&gt;Batik's&lt;/A&gt; model: you simply paint your graphics to a special &lt;code&gt;Graphics2D&lt;/code&gt;, and then Batik can convert that into SVG files.  Likewise this will convert your code into a few basic types of &lt;a href="http://javagraphics.java.net/doc/com/bric/graphics/GraphicInstruction.html"&gt;instructions&lt;/a&gt;, and then all you have to do adapt those instructions to your exported file type.&lt;br /&gt;&lt;br&gt;2.  Debugging Runtime Graphics.  The current implementation is rough around the edges, but I've already used it several times to debug where graphics are coming from.  With &lt;a href="http://javagraphics.java.net/doc/com/bric/graphics/GraphicsWriterDebugger.html"&gt;this class&lt;/a&gt;, you can debug exactly where in the code a specific call is coming from to draw an image or a shape to the screen.  &lt;a href="http://javagraphics.java.net/resources/debugger_demo.GIF"&gt;Here&lt;/a&gt; is an example of how this class can help you study how an interface is drawn.&lt;br /&gt;&lt;br&gt;3.  Serialization.  Instead of just capturing the screen as a JPEG, you can actually serialize exactly how the screen is rendered.  For example if a beta-tester is seeing a weird artifact on their window that you can't reproduce: you can actually serialize what they're seeing and get a stack trace.&lt;br /&gt;&lt;p&gt;Like most projects, this is designed to meet my current needs and is not going to be a perfect fit for everyone out there.  Known bugs/flaws in the current implementation include: lack of XOR support, and lack of support for any &lt;code&gt;Composite&lt;/code&gt; that is not a &lt;code&gt;SourceOver&lt;/code&gt; composite.&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;This is still a work-in-progress.  (Especially the &lt;code&gt;TextBoxInstruction&lt;/code&gt;.)  But it is at least partially working.  In a separate project (which I may make public in coming weeks?) I'm successfully exporting graphics to PPTX files using this architecture.&lt;br /&gt;&lt;p&gt;Also it's worth noting that this was written to be flexible, but &lt;i&gt;not&lt;/i&gt; to be efficient.  For example, a performance-hungry usage might want to reconsider how many times &lt;code&gt;Area&lt;/code&gt; objects are used.  Also images are serialized as an int array.  If file size were a concern, a more complicated mechanism that kept track of the source image file might be appropriate (a 24KB JPEG could be 1,000KB int array...).&lt;br /&gt;&lt;p&gt;Any comments on the design of this model are welcome.  Also any ideas on ways to use this model besides exporting to PPTX are welcome.  (What vector file formats are still not Java-friendly?)&lt;br /&gt;&lt;p&gt;You can download this jar (source code included) &lt;a href="http://javagraphics.java.net/jars/GraphicsWriter.jar"&gt;here&lt;/a&gt;.  The &lt;code&gt;GraphicsWriterDebugger&lt;/code&gt; class is available &lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/graphics/GraphicsWriterDebugger.java?rev=43&amp;view=markup"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-1180372164753080865?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/1180372164753080865/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/12/graphics2d-serializable-vector-graphics.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1180372164753080865'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1180372164753080865'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/12/graphics2d-serializable-vector-graphics.html' title='Graphics2D: Serializable Vector Graphics'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-3464266224342628267</id><published>2008-12-13T07:07:00.000-08:00</published><updated>2011-02-21T13:11:29.152-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Shape'/><category scheme='http://www.blogger.com/atom/ns#' term='charcoal'/><category scheme='http://www.blogger.com/atom/ns#' term='stroke'/><category scheme='http://www.blogger.com/atom/ns#' term='custom stroke'/><category scheme='http://www.blogger.com/atom/ns#' term='effects'/><title type='text'>Strokes: a Charcoal Stroke</title><content type='html'>This stroke started out like &lt;a href="http://javagraphics.blogspot.com/2008/05/strokes-bristle-stroke.html"&gt;this one&lt;/a&gt;: I set out knowing exactly what I was going to do geometrically, but I wasn't quite sure how it would turn out visually.&lt;br /&gt;&lt;p&gt;The concept is very simple.  I want to trace along a shape, and frequently slice inward, creating several cracks, like this:&lt;br /&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/spike.jpg"&gt;&lt;br /&gt;&lt;p&gt;I wanted to do that, except several thousand times along the stroke of a shape.  It turns out (surprisingly?) that this effect has a relatively sophisticated look to it.  This applet demonstrates the new &lt;code&gt;CharcoalStroke&lt;/code&gt;:&lt;br /&gt;&lt;br&gt;&lt;APPLET CODE="com/bric/awt/CharcoalStrokeDemo" CODEBASE="http://javagraphics.java.net/jars/" width=269 height=410&gt;     &lt;PARAM NAME="archive" VALUE="CharcoalStroke.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;br&gt;&lt;h3&gt;Inner Workings&lt;/h3&gt;&lt;br /&gt;The effect is easy to explain, but implementing it requires some new tools.&lt;br /&gt;&lt;p&gt;The first problem is that we need to trace the segment from about t = 0 to t = a.  Then we add a crack.  Then resume tracing again at t = a+dt.  For a line this is no problem, but for cubic curves it's a lot harder.&lt;br /&gt;&lt;p&gt;Also we'll want some concept of &lt;i&gt;distances&lt;/i&gt;, because we'll want to space the cracks at varying intervals.  Some segments in a shape might be 100 pixels, and some segments might be 10: if we gave every segment the same number of cracks these shapes would look very unbalanced.&lt;br /&gt;&lt;p&gt;So the &lt;A HREF="http://javagraphics.java.net/doc/com/bric/geom/MeasuredShape.html"&gt;&lt;code&gt;MeasuredShape&lt;/code&gt;&lt;/A&gt; class takes care of those needs.  The &lt;code&gt;MeasuredShape&lt;/code&gt; gauges distance, and it can iterate over specific fractions of shape segments (whether they're linear, quadratic, or cubic).&lt;br /&gt;&lt;p&gt;Once we decide where to add cracks, we also need to figure out how deep to make them.  My first approach was to see how deep the crack &lt;i&gt;can&lt;/i&gt; be, and then pick a fraction of that value.  This had some questionable side-effects:&lt;br /&gt;&lt;br&gt;&lt;table cellpadding="10" cellspacing="10"&gt;&lt;tr&gt;&lt;td width=92&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/charcoalBad.jpg"&gt;&lt;/td&gt;&lt;td&gt;The purple and green lines highlight two very different crack depths.  The purple crack has many more pixels it can reach out to, so there's a dramatic shift when the curve is nearly tangent to the crack angle.&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;p&gt;Flor pointed this out over email, and we discussed possible improvements.  Our first reaction was to add another parameter when you define a &lt;code&gt;CharcoalStroke&lt;/code&gt;.  I ended up not liking this idea, because I wanted to keep the number constructor arguments to a minimum.  (Adding a new argument is clutter, just like adding an extra slider to your GUI is clutter.)&lt;br /&gt;&lt;p&gt;Instead he suggested we just make the maximum crack depth proportional to the stroke width.  This will work as long as we're dealing with &lt;code&gt;BasicStrokes&lt;/code&gt;, which is the default case.  So that improvement is courtesy of Flor.  :)&lt;br /&gt;&lt;h3&gt;The Charcoal Effect (not Stroke)&lt;/h3&gt;&lt;br /&gt;Although the entire goal of this project was a new &lt;i&gt;stroke&lt;/i&gt;: the actual effect can be applied to any shape.  The &lt;a href="http://javagraphics.java.net/doc/com/bric/awt/CharcoalEffect.html"&gt;&lt;code&gt;CharcoalEffect&lt;/code&gt;&lt;/a&gt; class can rewrite any shape with these tiny cracks. &lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;So here's a new stroke/effect for anyone who's interested.  The jar (source code included) is available &lt;A HREF="http://javagraphics.java.net/jars/CharcoalStroke.jar"&gt;here&lt;/A&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-3464266224342628267?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/3464266224342628267/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/12/strokes-charcoal-stroke.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3464266224342628267'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3464266224342628267'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/12/strokes-charcoal-stroke.html' title='Strokes: a Charcoal Stroke'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-4508571400505611081</id><published>2008-11-01T00:34:00.000-07:00</published><updated>2011-02-21T13:11:48.061-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='layers'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='interface'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='aqua'/><category scheme='http://www.blogger.com/atom/ns#' term='window'/><category scheme='http://www.blogger.com/atom/ns#' term='menu'/><category scheme='http://www.blogger.com/atom/ns#' term='apple'/><title type='text'>Windows: Adding a "Window" Menu</title><content type='html'>To make a GUI consistent with other Mac applications, I need to have a &lt;A HREF="http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGMenus/chapter_17_section_4.html#//apple_ref/doc/uid/TP30000356-TPXREF106"&gt;"Window" menu&lt;/A&gt;.&lt;br /&gt;&lt;p&gt;The basic concept is easy enough: get a list of Frames, and show the visible ones in the menu.  Technically you could use &lt;code&gt;&lt;A HREF="http://java.sun.com/javase/6/docs/api/java/awt/Frame.html#getFrames()"&gt;Frame.getFrames()&lt;/A&gt;&lt;/code&gt; to build your list of frames.  The catch is that this has no concept of a listener, so how do you know when a Frame is closed or reopened?&lt;br /&gt;&lt;p&gt;Instead of making a complicated web of listeners, I made the &lt;A HREF="http://javagraphics.java.net/doc/com/bric/window/WindowList.html"&gt;&lt;code&gt;WindowList&lt;/code&gt;&lt;/A&gt; class.  This class listens for &lt;code&gt;AWTEvents&lt;/code&gt;, and fires &lt;code&gt;ChangeListeners&lt;/code&gt; when windows are activated, closed, or iconified.  It can sort windows/frames two ways:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Time-based.  (What window was first?)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;By layer.  (What window is on top?)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;Also it keeps track of everything using &lt;code&gt;WeakReferences&lt;/code&gt;.  I'm not sure exactly how the deep dark inner workings of the AWT windows (and their peers) work: but hopefully nothing I write will cause a window or frame to linger in memory when everyone else has lost reference to it.&lt;br /&gt;&lt;h3&gt;The Bad Stuff&lt;/h3&gt;&lt;br /&gt;For such a small/simple menu, it's surprisingly tricky to implement.&lt;br /&gt;&lt;p&gt;For starters: there's the "Zoom" menu item.  This function is not really supported in Java, although you could make a hackish approach that toggles between recent window sizes.  The Apple guidelines specifically say: "This command should not expand the window to the full screen size."  So maximizing is out.  And really, the "Zoom" feature is out, too, until I make the JNI code to do it.&lt;br /&gt;&lt;p&gt;Then there are the icons.  When a frame is selected, it has a checkmark.  When a frame is iconified, it has a diamond.  When a frame has &lt;A HREF="http://developer.apple.com/technotes/tn2007/tn2196.html#WINDOW_DOCUMENTMODIFIED"&gt;unsaved changes&lt;/A&gt;, it has a bullet.  I started to implement my own &lt;code&gt;MenuItemUI&lt;/code&gt; that handles all these situations, but then I remembered: if you use the screen menubar on Mac, custom MenuItemUIs are not supported.&lt;br /&gt;&lt;p&gt;So I could still make/use a new UI on non-Macs, or on Macs when the screen menubar isn't be used: but that really defeats the point.  (Ironically I can make a more Aqua-like interface when I'm not constrained by Aqua.)&lt;br /&gt;&lt;p&gt;Also the guidelines say the "Minimize" menu item should change when the user presses the alt key to read "Minimize All".  Again: I wasn't able to implement this dynamic switch on Macs without a custom &lt;code&gt;MenuItemUI&lt;/code&gt;.&lt;br /&gt;&lt;h3&gt;The Good Stuff&lt;/h3&gt;&lt;br /&gt;But most importantly the Window menu lets you navigate your windows: and that we can do.&lt;br /&gt;&lt;p&gt;Also the "Minimize" menu item works.&lt;br /&gt;&lt;p&gt;The "Bring All to Front" menu item functionally works, but it's implemented 1 frame at a time, so you see the frames flicker to the front.  It's not perfect, but the end result is correct.&lt;br /&gt;&lt;p&gt;Also the Finder has a menu item "Cycle Through Windows".  Werner pointed out this is not in Apple's guidelines, but I think it's a handy tool.  It can be implemented in just one line:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;WindowList.getFrames(true, false, true)[0].toFront();&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;Unfortunately: the menu shortcut doesn't render correctly on Mac or Windows XP.  And this seems like the type of menu item that will only be useful if you can repetitively use the shortcut to cycle through your windows.  So I left it out of the final version, but you're welcome to add it yourself if you want to give it a new shortcut.&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;The core functionality is present, but there's still a lot of room for improvement.  The jar (source included) is available &lt;A HREF="http://javagraphics.java.net/jars/WindowMenu.jar"&gt;here&lt;/A&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-4508571400505611081?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/4508571400505611081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/11/windows-adding-window-menu.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/4508571400505611081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/4508571400505611081'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/11/windows-adding-window-menu.html' title='Windows: Adding a &quot;Window&quot; Menu'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-7871389611797677886</id><published>2008-11-01T00:33:00.000-07:00</published><updated>2011-02-21T13:12:33.675-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tiles'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='layout'/><category scheme='http://www.blogger.com/atom/ns#' term='print dialog'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='print'/><category scheme='http://www.blogger.com/atom/ns#' term='dialog'/><title type='text'>Print Dialogs: Laying Out Tiles</title><content type='html'>This entry talks about designing a dialog box to layout several tiles/cells on a page.&lt;br /&gt;&lt;br /&gt;The first question that needs to be asked is: is this necessary?&lt;br /&gt;&lt;br /&gt;I could argue that it's necessary because the program I'm working on has specific needs and we want to have full control of the GUI elements -- including a realistic preview of the printed page in the dialog -- but that's not the simplest answer.&lt;br /&gt;&lt;br /&gt;The simplest answer is that on Mac 10.5.4 the tiling mechanism is broken in Java 1.5 and 1.6.  If you run &lt;A HREF="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/print/PrintBug.java?view=markup"&gt;this test&lt;/A&gt; program (which relies on classes in the jar presented below), and set the print dialog to tile 4 images on a page: the tiling options are ignored.  And &lt;A HREF="http://javagraphics.java.net/resources/macPrintOutput.rtf"&gt;this gibberish&lt;/A&gt; is printed to &lt;code&gt;System.err&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;A New Dialog&lt;/h3&gt;&lt;br /&gt;So another approach to tiling is necessary.  In my case, we wanted a Java-based solution.  It could be well argued that other approaches, such as a JNI-based solution, might be as good or better, but we decided to invest in a new dialog.&lt;br /&gt;&lt;br /&gt;First, the definition of a "tile" is necessary.  For this I used a very simple interface:&lt;br /&gt;&lt;br&gt;&lt;code&gt;public interface Paintable {&lt;/code&gt;&lt;br /&gt;&lt;code&gt; &amp;nbsp; public int getWidth();&lt;/code&gt;&lt;br /&gt;&lt;code&gt; &amp;nbsp; public int getHeight();&lt;/code&gt;&lt;br /&gt;&lt;code&gt; &amp;nbsp; public void paint(Graphics2D g);&lt;/code&gt;&lt;br /&gt;&lt;code&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;It doesn't matter what the width and height are, or what they're measured in: because they are scaled to fit the available cells on the printed page as best they can.&lt;br /&gt;&lt;br /&gt;The layout is controlled by a &lt;A HREF="http://javagraphics.java.net/doc/com/bric/print/PrintLayout.html"&gt;&lt;code&gt;PrintLayout&lt;/code&gt;&lt;/A&gt; object.  The most important methods here are the methods &lt;code&gt;createPaintables()&lt;/code&gt; and &lt;code&gt;createPrintable()&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;This object also controls the number of tiles in each row and column, the margins on each side of the paper, the orientation, the paper size, and HTML-encoded headers and footers.  There will always be more features you can add to it, but it does a lot in its current state.&lt;br /&gt;&lt;br /&gt;The GUI is provided in a set of separate classes that create a &lt;A HREF="http://javagraphics.java.net/doc/com/bric/print/swing/PrintLayoutDialog.html"&gt;dialog&lt;/A&gt; like this one:&lt;br /&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/printLayout1.jpg"&gt;&lt;br /&gt;&lt;br /&gt;There are a few simply customizations you can make to this dialog, including expanding the "margins" section so it customizes each margin:&lt;br /&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/printLayout2.jpg"&gt;&lt;br /&gt;&lt;br /&gt;Or hiding the preview and using a more horizontal layout:&lt;br /&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/printLayout4.jpg"&gt;&lt;br /&gt;&lt;br /&gt;(This project builds on code found in several previous blog entries, including: &lt;A HREF="http://javagraphics.blogspot.com/2008/11/internationalization-measuring-lengths.html"&gt;LengthSpinners&lt;/A&gt;, the navigation controls in the &lt;A HREF="http://javagraphics.blogspot.com/2008/11/preferences-aqua-like-preferences.html"&gt;preferences&lt;/A&gt;, and a few excerpts of the &lt;A HREF="http://javagraphics.blogspot.com/2008/06/joptionpane-making-alternative.html"&gt;QDialog&lt;/A&gt; classes.)&lt;br /&gt;&lt;br /&gt;There are several issues these interfaces raise.  For example:&lt;br /&gt;&lt;BR&gt;1.  Mike pointed out they may be too complex, and maybe users shouldn't really define their own paper sizes.  He has a very valid point.  The reason those spinners were added, though, is because my next choice was to add a "Custom..." option to the paper combobox: and when it was selected a dialog box would prompt you to define your own paper size.  I hated the idea of this dialog creating a separate dialog, so instead of using labels to relay the paper dimensions, I used spinners.  The target user for this software is pretty advanced, but if your target user is, for example, middle school students: this might lead to some frustration and fruitless experimentation.&lt;br /&gt;&lt;BR&gt;2.  Also instead of spinners for the rows and columns, a simple combobox might be better.  It would offer "1", "2", "2x2", "4x4", and maybe a few other predetermined options.  This is a great idea, but I was surprised when I actually worked with a real-time preview with padding and margins how often 2x3 was actually the best fit, or 4x1.  But I'm torn on this subject, because this may be a situation where the user will never miss those controls if I don't offer them.  But once I saw them in use: they were pretty compelling.&lt;br /&gt;&lt;BR&gt;3.  Flor pointed out the components could use more visible grouping.  (Such as a TitledBorder, but with a more aesthetic border than Java uses by default.)  This is also a valid point, but in the end I simplified the original GUI and used vertical spacing to emphasize the clusters of controls.&lt;br /&gt;&lt;br /&gt;(Thanks to both Mike and Flor for their feedback.)  :)&lt;br /&gt;&lt;br /&gt;You may want to regroup the controls in several different ways, or change the appearance of the preview (its shadow, border, etc.).  Hopefully these are all aesthetic changes that won't require really digging deep into the &lt;code&gt;PrintLayout&lt;/code&gt; object itself.&lt;br /&gt;&lt;br /&gt;Also Mike pointed out icons would be useful for the portrait/landscape buttons.  I completely agree.  Anyone want to donate some icons for a public domain bit of code?  My first source for good, free icons is usually &lt;A HREF="http://www.famfamfam.com/lab/icons/silk/"&gt;Silk&lt;/A&gt;, but they would be too small in this case.  Plus they didn't have those icons.&lt;br /&gt;&lt;br /&gt;The jar (source code included) is available &lt;A HREF="http://javagraphics.java.net/jars/PrintLayoutDialog.jar"&gt;here&lt;/A&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-7871389611797677886?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/7871389611797677886/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/11/print-dialogs-laying-out-tiles.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7871389611797677886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7871389611797677886'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/11/print-dialogs-laying-out-tiles.html' title='Print Dialogs: Laying Out Tiles'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-3140568987289677782</id><published>2008-11-01T00:32:00.000-07:00</published><updated>2011-02-21T13:13:19.355-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='distance'/><category scheme='http://www.blogger.com/atom/ns#' term='metric'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='internationalization'/><category scheme='http://www.blogger.com/atom/ns#' term='measurement'/><category scheme='http://www.blogger.com/atom/ns#' term='imperial'/><category scheme='http://www.blogger.com/atom/ns#' term='JSpinner'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='length'/><category scheme='http://www.blogger.com/atom/ns#' term='unit'/><title type='text'>Internationalization: measuring lengths with JSpinners</title><content type='html'>What's a good way to let your user work with lengths?  (That's not a rhetorical question, I really am asking.)&lt;br /&gt;&lt;br /&gt;Suppose my user needs to specify the width and height of an object, in real-world terms.  I could make an interface like this:&lt;br /&gt;&lt;br /&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/lengthSpinner1.jpg"&gt;&lt;br /&gt;&lt;br /&gt;But to be a hip global citizen, I need to let users customize their units.  The above interface may be fine for an American audience, but I want my software to have a wider appeal.  (And even some American users -- depending on their background -- may balk at being constrained to inches.)&lt;br /&gt;&lt;br /&gt;So what now?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Option 1&lt;/b&gt;: I could simply add a &lt;code&gt;JComboBox&lt;/code&gt; after each spinner:&lt;br /&gt;&lt;br /&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/lengthSpinner2.jpg"&gt;&lt;br /&gt;&lt;br /&gt;But this doubles the number of controls.  It makes the GUI more cluttered than it really needs to be.  &lt;i&gt;This&lt;/i&gt; window isn't visually overwhelming, but suppose you had to specify 10 measurements or more?  Using 2 GUI controls for every 1 measurement will get messy.&lt;br /&gt;&lt;br /&gt;And odds are the user doesn't want to display some controls in inches and other controls in centimeters: they probably want all controls in the same unit.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Option 2&lt;/b&gt;: Use one &lt;code&gt;JComboBox&lt;/code&gt; above all other controls to specify units.&lt;br /&gt;&lt;br /&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/lengthSpinner3.jpg"&gt;&lt;br /&gt;&lt;br /&gt;This is better, but I'm still not convinced I like it.  Suppose a user opens this window to adjust the width and height.  This interface commits the cardinal sin of interrupting what the user wants to do and pestering them with questions first.  (I admit, this is a very small scale version of that offense, but it's still the same basic concept.)  The interface is saying, "Wait! I have some questions before we start."&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Option 3&lt;/b&gt;: Include the units in the spinner text field.&lt;br /&gt;&lt;br /&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/lengthSpinner4.jpg"&gt;&lt;br /&gt;&lt;br /&gt;I think of the 3 options, this is my favorite.  This doesn't introduce GUI clutter: there are still only 2 controls on the screen.&lt;br /&gt;&lt;br /&gt;Option 2 may be more economical, especially if you value mouse clicks over typing.  In Option 3 the user &lt;i&gt;cannot&lt;/i&gt; change units without typing.  (However, if you value typing over mouse clicks: this option is more direct.)&lt;br /&gt;&lt;br /&gt;More importantly: I think Option 3 is more intuitive.  By "intuitive" I mean "this most closely resembles how you would fill in this field on a piece of paper".  So it's the best of both worlds: it minimizes clutter, and it still flows in a natural human-oriented order.&lt;br /&gt;&lt;br /&gt;(In the next section I partially address the idea that the user will probably want &lt;i&gt;all&lt;/i&gt; controls in the same units, so he/she shouldn't have to specify them each time.)&lt;br /&gt;&lt;br /&gt;[ You could also argue listing the units in every spinner takes up precious pixels: we should specify the units once (similar to Option 2), and then not restate the units everywhere.  This follows the "redundancy-is-waste" and "screen-real-estate-is-precious" philosophies.  I respect those points of view, but I think in this case redundancy also offers safety.  You should be able to look at any value and instantly see what its units are, without having to find that one location in the interface where the units are specified.  As my high school physics teacher continually lectured us: a number without a unit is meaningless.  (And faulty assumptions about units can be &lt;A HREF="http://www.cnn.com/TECH/space/9909/30/mars.metric/"&gt;very expensive&lt;/A&gt;!) ]&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;The Java-side&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;All that's well and good, and it took about 10 minutes to write, collect and tweak screenshots, upload, etc.  But it's not supported in the default Swing toolkits.  So we're going to have to make it ourselves.&lt;br /&gt;&lt;br /&gt;It turns out (like so many projects...) that this is a bigger adventure than I'd expected.  But Swing does give you all the tools necessary to completely customize your spinners (and their models and editors), and I learned a lot about how the components interact along the way.&lt;br /&gt;&lt;br /&gt;First: we need a way to express &lt;A HREF="http://javagraphics.java.net/doc/com/bric/math/Length.html"&gt;lengths&lt;/A&gt;.  This includes several basic units (meters, inches, centimeters, feet, etc.), and the tools to easily convert in and out of different units.  Also this class needs to be localized, since the goal of this project is to accommodate a wide user base.&lt;br /&gt;&lt;br /&gt;In hindsight the rest was really easy -- now that I know exactly what was involved.  We need our own &lt;code&gt;SpinnerModel&lt;/code&gt; and editor, but it's awfully cumbersome to work with the spinner, its model, and its editor: so I only made the &lt;A HREF="http://javagraphics.java.net/doc/com/bric/swing/LengthSpinner.html"&gt;&lt;code&gt;LengthSpinner&lt;/code&gt;&lt;/A&gt; class public.  (It should integrate everything nicely so you don't need to worry about anything else.)&lt;br /&gt;&lt;br /&gt;The spinner also lets you specify an optional preference key.  If this is non-null, then the next time you reopen your application that preference key is consulted to get your preferred units of measurement for a spinner.  This is working under the assumption that most users will always want to work in metric or imperial units: so after switching once they'll never need to switch again.&lt;br /&gt;&lt;br /&gt;Also I added the &lt;A HREF="http://javagraphics.java.net/doc/com/bric/swing/LengthSpinnerGroup.html"&gt;&lt;code&gt;LengthSpinnerGroup&lt;/code&gt;&lt;/A&gt;.  This is a group of related &lt;code&gt;LengthSpinners&lt;/code&gt; that share the same unit.  So when one spinner changes units: all the others do too.  This is working under the assumption that the user isn't going to specify, for example, the width in inches and the height in centimeters.&lt;br /&gt;&lt;br /&gt;The jar is available &lt;A HREF="http://javagraphics.java.net/jars/LengthSpinner.jar"&gt;here&lt;/A&gt; (source included in the jar).&lt;br /&gt;&lt;br /&gt;As always I welcome comments/discussion about the jar and source code listed here.  Also I'd love to hear any comments/links/ideas about the question that started this entry: "What's a good way to let your user work with lengths?"&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-3140568987289677782?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/3140568987289677782/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/11/internationalization-measuring-lengths.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3140568987289677782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3140568987289677782'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/11/internationalization-measuring-lengths.html' title='Internationalization: measuring lengths with JSpinners'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-9008853642161938010</id><published>2008-11-01T00:31:00.000-07:00</published><updated>2011-02-21T13:14:07.523-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='frame'/><category scheme='http://www.blogger.com/atom/ns#' term='preference'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='aqua'/><category scheme='http://www.blogger.com/atom/ns#' term='dialog'/><title type='text'>Preferences: Aqua-like Preferences</title><content type='html'>Another fun recent commission involved making Aqua-like preferences.  This spirals into a surprising amount of decisions and discussions.&lt;br /&gt;&lt;P&gt;The first question is: is this a good idea?  A lot of developers are quick to emulate Apple simply because Apple happens to do ground-breaking UI work.  But their model is not universally the "best" model.  Apple's model in this case is very icon-centric, and icons are not necessarily a good fit for everyone.  Back in 2000, Jef Raskin pointed out in &lt;A HREF="http://en.wikipedia.org/wiki/The_Humane_Interface"&gt;&lt;I&gt;The Humane Interface&lt;/I&gt;&lt;/A&gt;:&lt;br /&gt;&lt;blockquote&gt;Icons contribute to visual attractiveness of an interface and, under the appropriate circumstances, can contribute to clarity; however, the failings of icons have become clearer with time.  For example, both the Mac and Windows 95 operating systems now provide aids to explain icons: When you point at the icon, a small text box appears that tells you you what the icon stands for.  The obvious reaction, which I have observed repeatedly when users first see this facility, is 'Why not just use the words in the first place?'  Why not indeed?&lt;/blockquote&gt;&lt;br /&gt;I think you can tastefully provide, for example, a JList on the left side of the window with text descriptions of the different preference panels.  Or, if you only need a few panels: a simple JTabbedPane should do fine.&lt;br /&gt;&lt;P&gt;That being said: if you're convinced the Aqua-look is right for you, then the next question worth asking is: should this be a dialog or a frame?  Here are some factors in that decision:&lt;br /&gt;&lt;table&gt;&lt;TR&gt;&lt;TD style="vertical-align: top;"&gt;&lt;H3&gt;Dialogs:&lt;/H3&gt;&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;Modal: a dialog can be modal.  This is probably the "traditional" way to approach preferences.  It's really easy to wait until a modal dialog is dismissed to commit changes.&lt;/LI&gt;&lt;br /&gt;&lt;LI&gt;No Menus: hurray!  Another 10 points for convenience.  Nobody -- including the Java architects -- expects a dialog to have its own menu bar.&lt;/LI&gt;&lt;br /&gt;&lt;LI&gt;Appearance: on Macs, a dialog cannot have the native-looking gradient that Aqua preference windows have.  You can emulate this gradient yourself: but this "look" might change in subsequent OS releases, so your application won't blend in.  Also you can't get rid of the dark gray line under the title bar, so that will always stand out if you're really detail-oriented.&lt;/LI&gt;&lt;/ul&gt;&lt;/TD&gt;&lt;TD&gt;&lt;H3&gt;Frames:&lt;/H3&gt;&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;Non-Modal: The "convenience" of modality is not an option in frames.  However it could be argued that this is better for the user's experience.  Also if your goal is to provide an Aqua-like experience, you need to use frames.&lt;/LI&gt;&lt;br /&gt;&lt;LI&gt;Menus: on Macs, you need to worry about menus.  It looks unprofessional if your menubar simply disappears when a certain frame is brought to the front.  So you might want to clone a JMenuBar from the main window in your application?  This could get complicated and hairy.&lt;/LI&gt;&lt;br /&gt;&lt;LI&gt;Appearance: on Macs, a frame can have the native-looking gray gradient.  This is officially known as "brushed-metal" for legacy's sake, although in Mac 10.5 it doesn't resemble brushed metal at all.&lt;/LI&gt;&lt;/ul&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/table&gt;&lt;br /&gt;&lt;P&gt;So in short: dialogs may be an easier choice to implement, but frames will provide the best emulation of an Aqua preference window.  So what I decided to do was implement these preferences simply as a &lt;code&gt;JPanel&lt;/code&gt;, and other developers could choose whether they wanted to present them in &lt;code&gt;JFrames&lt;/code&gt; or &lt;code&gt;JDialogs&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;Also in my case the goal was to emulate the "System Preferences" application.  In this model there is a table-of-contents page with several (dozen) buttons, and at the top of the window is a small header letting you navigate forward and backward.  However most desktop applications on Mac have fewer than 10 preference panels, so they use a much simpler model: the preference window is basically a &lt;code&gt;JTabbedPane&lt;/code&gt;, where the top of the window contains several buttons that manipulate which set of preferences are visible.&lt;br /&gt;&lt;BR&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/preferences.jpg"&gt;&lt;br /&gt;&lt;P&gt;So my implementation uses this line to supply either interface:&lt;br /&gt;&lt;code&gt;public void addButtonRow(AbstractButton[] buttons,JComponent[] components,String title);&lt;/code&gt;&lt;br /&gt;&lt;P&gt;If only 1 row is provided: you get the simple interface:&lt;br /&gt;&lt;BR&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/preferencePanel2.jpg"&gt;&lt;br /&gt;&lt;BR&gt;If multiple rows are provided, you get the "System Preferences"-style navigation:&lt;br /&gt;&lt;BR&gt;&lt;IMG SRC="http://javagraphics.java.net/resources/preferencePanel.jpg"&gt;&lt;br /&gt;&lt;P&gt;Lastly: what about eye candy?  (You can't set out to emulate something Apple does without trying to add some eye candy.)  In Apple's model, when a new panel is displayed two effects occur:&lt;br /&gt;&lt;LI&gt;Crossfade between panels.&lt;br /&gt;&lt;LI&gt;Animated resizing of the window.&lt;/LI&gt;&lt;br /&gt;&lt;P&gt;Fading is manageable.  It wasn't quite as easy as I expected, so I implemented this in &lt;A HREF="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/swing/FadingPanel.java?view=markup"&gt;a non-public class&lt;/A&gt; with a CardLayout.  Originally I just overrode a JPanel's &lt;code&gt;paint()&lt;/code&gt; method, but still sometimes components would flicker over the animation: so I ended up using a CardLayout, and switching to a blank panel whenever an animation was taking place.&lt;br /&gt;&lt;P&gt;Animated resizing is much trickier.  On Mac this is currently impossible without JNI, because if you programmatically resize a window the entire window flickers.  (I even tried &lt;A HREF="http://lists.apple.com/archives/Java-dev/2008/Sep/msg00482.html"&gt;the JNI approach&lt;/A&gt;: but it was harder than I expected.  And I don't have a strong background in JNI, so I decided to move on.)  Even on Windows, which is flicker-free, I never saw reasonable performance in this area.  So because of time constraints I eventually gave up on animated resizing.  Instead I added the method &lt;A HREF="http://javagraphics.java.net/doc/com/bric/swing/PreferencePanel.html#setFixedSize(boolean)"&gt;&lt;code&gt;setFixedSize(boolean)&lt;/code&gt;&lt;/A&gt; in the &lt;A HREF="http://javagraphics.java.net/doc/com/bric/swing/PreferencePanel.html"&gt;&lt;code&gt;PreferencePanel&lt;/code&gt;&lt;/A&gt; class: if this is called, the window never needs to be resized, and all the preference panels should be a similar size.&lt;br /&gt;&lt;P&gt;The end result is the &lt;A HREF="http://javagraphics.java.net/jars/PreferencePanel.jar"&gt;PreferencePanel.jar&lt;/A&gt;.  It lacks the animated-resizing, and the great search field that the "System Preference" has on Mac 10.5, but otherwise it's a handy little stand-alone component.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-9008853642161938010?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/9008853642161938010/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/11/preferences-aqua-like-preferences.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/9008853642161938010'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/9008853642161938010'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/11/preferences-aqua-like-preferences.html' title='Preferences: Aqua-like Preferences'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-1325270849177780670</id><published>2008-11-01T00:30:00.000-07:00</published><updated>2011-02-21T13:14:18.514-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='AlphaComposite'/><category scheme='http://www.blogger.com/atom/ns#' term='image'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Crossfade'/><category scheme='http://www.blogger.com/atom/ns#' term='Composite'/><title type='text'>Crossfades: What Is and Isn't Possible</title><content type='html'>Recently I tried to implement a crossfade between two translucent BufferedImages.  This turned into a whole mess of code and theories; none of which really give me what I'm looking for.  I'd like to thank Werner, Ken and Paul for their opinions on the subject.&lt;br /&gt;&lt;br /&gt;&lt;H3&gt;The Simple Case&lt;/H3&gt;&lt;br /&gt;If both images are opaque, then this exercise is easy:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;g.drawImage(img1, 0, 0, null);&lt;br /&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,t));&lt;br /&gt;g.drawImage(img2, 0, 0, null);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;This is probably how most slideshow transitions work.  It's elegant and simple, but it fails for translucency:&lt;br /&gt;&lt;P&gt;Suppose img2 has large transparent areas where img1 is opaque: in this case we need to fade out img1, in addition to fading in img2:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1-t));&lt;br /&gt;g.drawImage(img1, 0, 0, null);&lt;br /&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,t));&lt;br /&gt;g.drawImage(img2, 0, 0, null);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;This may not give the desired visual effect, though.  In areas where both img1 and img2 are opaque, at t = .5 the destination will have an opacity of only 75%.&lt;br /&gt;&lt;h3&gt;Background&lt;/h3&gt;&lt;br /&gt;Why 75%?  The short answer is: AlphaComposites are not designed to crossfade.  (In fact, they were &lt;A HREF="http://www.friedenhq.org/index.php5?title=Porter_Duff_Image_Compositing_Part_1"&gt;originally designed&lt;/A&gt; for Star Trek special effects, but that's another story.) The &lt;A HREF="http://java.sun.com/javase/6/docs/api/java/awt/AlphaComposite.html#SRC_OVER"&gt;javadocs&lt;/A&gt; explain the formula for SRC_OVER composites is:&lt;br /&gt;&lt;blockquote&gt;Ar = As + Ad*(1-As)&lt;/blockquote&gt;&lt;br /&gt;So in our case:&lt;br /&gt;&lt;blockquote&gt;As = t&lt;br /&gt;Ad = 1-t&lt;br /&gt;Ar = t + (1-t)*(1-t)&lt;br /&gt;Ar = t^2-2t+t+1&lt;br /&gt;Ar = t^2-t+1&lt;/blockquote&gt;&lt;br /&gt;This is a parabola where Ar is 1 at t = 0 and t = 1, but at its minimum Ar is only .75.  If you study the equations and how the composites work: it appears impossible to use AlphaComposites to crossfade two images so these requirements are met:&lt;br /&gt;&lt;ol&gt;&lt;LI&gt;The initial image is completely invisible at t = 1, and the final image is completely invisible at t = 0.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;If two pixels at the same (x,y) location have an alpha of k, then at all times during the crossfade the destination pixel should have an alpha of k.&lt;/LI&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h3&gt;Alternatives&lt;/H3&gt;&lt;br /&gt;So what is possible, then?  I put together a &lt;A HREF="http://javagraphics.java.net/jars/CrossfadeTest.jar"&gt;test program&lt;/A&gt; that compares different approaches to this problem:&lt;br /&gt;&lt;BR&gt;&lt;LI&gt;&lt;b&gt;Squared Composite&lt;/b&gt;: This builds on the simple implementation above, except img1 decreases more slowly and img2 increases faster.  The result is that there is more overlap, so the two alpha values will be &lt;i&gt;more&lt;/i&gt; opaque in the middle.  The function now had a minimum of Ar = .93-ish.&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1-t*t));&lt;br /&gt;g.drawImage(img1, 0, 0, null);&lt;br /&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1-(1-t)*(1-t)));&lt;br /&gt;g.drawImage(img2, 0, 0, null);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;.93 opacity isn't bad.  However that assumes we're working with &lt;i&gt;opaque&lt;/i&gt; pixels.  If we work with pixels whose alpha values are .5, then we're layering two reasonably-opaque pixels on top of each other, and the resulting pixel is visibly more opaque than it should be (at As = Ad = .5, for example, they would combine to about Ar = .6.)&lt;br /&gt;&lt;LI&gt;&lt;b&gt;Complicated Composite&lt;/b&gt;: One approach to this problem is to play around with other composites, and see if a combination of other composites can relieve this problem.  Here's one such combination:&lt;blockquote&gt;&lt;pre&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1-t)); &lt;br /&gt;g.drawImage(img1, 0, 0, null); &lt;br /&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,t)); &lt;br /&gt;g.drawImage(img2, 0, 0, null); &lt;br /&gt;float total = 1-t+t*t; &lt;br /&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_IN,&lt;br /&gt;  (float)(Math.pow(total,.2)))); &lt;br /&gt;g.drawImage(img1, 0, 0, null); &lt;br /&gt;g.drawImage(img2, 0, 0, null); &lt;br /&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,1-t)); &lt;br /&gt;g.drawImage(img1, 0, 0, null); &lt;br /&gt;g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,t)); &lt;br /&gt;g.drawImage(img2, 0, 0, null); &lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;This was found mainly through trial-and-error, but the basic concept is: at the end we apply the "simple composite" approach, but before that we prep the graphics destination with a ghosted copy of both images.  This helps soften (or compensate for) the shortcomings of the simple approach.&lt;br /&gt;&lt;P&gt;As discussed below: this approach works with reasonable accuracy, but it's expensive to calculate.&lt;br /&gt;&lt;LI&gt;&lt;b&gt;Raster-Based Approach&lt;/b&gt;: Forget AlphaComposites.  Go straight to the source.  Directly grab each pixel component and say:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;Cr = Cd*(1-t)+Cs*t&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;This has a 0% error rate: because it's exactly what we're looking for.  But it's also by my tests the most expensive approach.&lt;/LI&gt;&lt;br /&gt;&lt;h3&gt;Results&lt;/h3&gt;&lt;br /&gt;To compare each of these approaches, I measured the time they take to complete, the memory allocation, and the accuracy.  Now "accuracy" can be measured several ways: I chose to measure how much opacity changed for specific pixels that should have a constant opacity.  (If you're facing this problem you may need to rewrite the tests with a different definition of accuracy/error.)&lt;br /&gt;&lt;BR&gt;Here is a graph showing the error of each approach:&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pkKUQesZfkIyJkRTjCHDZeA&amp;oid=1&amp;output=image" /&gt;&lt;br /&gt;&lt;P&gt;The last two raster-based approaches have zero error, so they don't make an impression on the chart.  At its worst the complicated composite approach has an error of about .045.  In my experience this is hardly noticeable, especially compared with the .13 error the simplest approach shows.&lt;br /&gt;&lt;P&gt;But the performance of the most accurate approaches make them less desirable:&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pkKUQesZfkIyJkRTjCHDZeA&amp;oid=2&amp;output=image" /&gt;&lt;br /&gt;&lt;P&gt;Each figure here represents the time it takes to make 100 calls to each method, dealing with images that are 200x200 pixels in size.&lt;br /&gt;&lt;P&gt;And the memory performance also favors the simplest methods:&lt;br /&gt;&lt;img src="http://spreadsheets.google.com/pub?key=pkKUQesZfkIyJkRTjCHDZeA&amp;oid=3&amp;output=image" /&gt;&lt;br /&gt;&lt;P&gt;The difference in the two raster-based approaches is: one crossfades 1 row of pixels at a time, and other approach crossfades all pixels in the entire image in one pass.  As you can see, dealing with all the pixels at once saves a little time, but is much worse for memory.&lt;br /&gt;&lt;P&gt;Also: a word about memory.  Usually when we optimize "performance" we worry only about speed, but memory allocation can be equally important.  If developers are sloppy with memory, it has a direct impact on performance:&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;Excessive allocations (and not properly letting go of objects) can result in paging memory to run your application.  This can range from a "bad" user experience to "catastrophic", depending on what your program does.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;Also sloppy memory allocation means the garbage collector has to run more frequently.  On slower computers this can result in visible "burps" while the program is unusable.  True: you can configure the garbage collection to run at certain intervals or when the heap reaches certain levels to minimize this problem: but dealing with memory allocation responsibly is still the best practice.&lt;/LI&gt;&lt;/ul&gt;&lt;br /&gt;&lt;H3&gt;Conclusions&lt;/H3&gt;&lt;br /&gt;Ideally it'd be great to see a &lt;code&gt;CrossfadeComposite&lt;/code&gt; class.  This is effectively what the raster-based solution was, but my solution narrowly focused on specific types of images and ColorModels, and it would take some time/energy to extend it to a generic Composite class.&lt;br /&gt;&lt;P&gt;But even then: performance probably isn't going to be what we'd like.  (A custom composite in Java won't perform on par with AlphaComposites anyway: AlphaComposites are probably optimized for the platform's graphics pipelines.)&lt;br /&gt;&lt;P&gt;The best thing to do in the mean time is to avoid translucent images!  Opaque images are so much simpler.  In cases where translucency is unavoidable though: this page outlines a few different options.  You can pick which one is "less evil".&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-1325270849177780670?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/1325270849177780670/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/06/crossfades-what-is-and-isnt-possible.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1325270849177780670'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1325270849177780670'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/06/crossfades-what-is-and-isnt-possible.html' title='Crossfades: What Is and Isn&apos;t Possible'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-4713893359515975730</id><published>2008-11-01T00:28:00.000-07:00</published><updated>2011-02-21T13:14:36.557-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drag-and-drop'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='cocoa'/><category scheme='http://www.blogger.com/atom/ns#' term='toolbar'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='customize'/><title type='text'>Customize Toolbar: implementing Mac-like toolbars</title><content type='html'>&lt;p&gt;If you've used a Mac you're probably familiar with the "Customize Toolbar..." option available in most Cocoa applications.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;It's part eye-candy, yes, but it's also a consistent way to customize a program's GUI that is consistent across several different applications.  So kudos to that: the user's experience is simpler when things are consistent.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Can it be done it Java, without JNI?  Abso-frikkin-lutely!  Or, alternatively: "I don't see why not?"&lt;br /&gt;&lt;/p&gt;&lt;p&gt;I think I've developed this project about as far as I need to, although some developers may require more functionality (see below).  Here's a demo of what this can do:&lt;br /&gt;&lt;/p&gt;&lt;object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" id="Untitled" height="420" width="500"&gt;&lt;br /&gt;&lt;param name="movie" value="http://homepage.mac.com/bricolage1/blog_resources/CustomizedToolbar.swf"&gt;&lt;br /&gt;&lt;param name="loop" value="false"&gt;&lt;br /&gt;&lt;param name="quality" value="best"&gt;&lt;br /&gt;&lt;embed name="CustomizedToolbar.swf" src="http://homepage.mac.com/bricolage1/blog_resources/CustomizedToolbar.swf" loop="false" swliveconnect="true" quality="best" height="420" width="500"&gt;&lt;/embed&gt;&lt;br /&gt;&lt;/object&gt;&lt;p&gt;There are some elements that I think are improvements over Apple's implementation, and some elements that are not as good.  Some positive sides of my implementation:&lt;br /&gt;&lt;/p&gt;&lt;li&gt;Flexible spaces always paint the double-arrows, even in the toolbar.  This distinguishes the flexible spaces from the regular spaces.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;In the "default toolbar" widget: it clearly shows spaces.  If we're assuming the user understands how spaces work (which is a questionable assumption): it's better to show them where they really are.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;My implementation is completely WYSIWYG: as you drag, you see exactly where elements are going to land.  In Apple's implementation they shift a few pixels, depending on the context.&lt;/li&gt;&lt;br /&gt;&lt;p&gt;Downsides/bugs include:&lt;br /&gt;&lt;/p&gt;&lt;li&gt;On Mac when you drag a component out of the toolbar: the image slides back to its starting location.  I can't figure out how to avoid this "feature" that the OS uses to indicate a drag-and-drop operation is rejected.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The dialog is non-modal, and set to always float on top.  I think I had to do this to get the interaction I needed, but it makes a really awkward situation when you switch applications.  Hopefully users won't switch apps to often, though, in the middle of reordering their toolbar.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The dialog is not a sheet, and some users may miss the cool intro effect Cocoa sheets have.&lt;/li&gt;&lt;br /&gt;Also I didn't implement the name displays, and the combobox to show icons/names/both.  For now this feature lies outside my needs, and I'm not going to take the time to implement it.  If anyone else wants to add this: feel free to email me.&lt;br /&gt;&lt;br /&gt;The jar is &lt;a href="http://javagraphics.java.net/jars/CustomizedToolbar.jar"&gt;here&lt;/a&gt;.  The javadocs are &lt;a href="http://javagraphics.java.net/doc/com/bric/swing/toolbar/CustomizedToolbar.html"&gt;here&lt;/a&gt;.  As you can see: very few public methods/classes.  It should "just work" with very little effort on your part.  (Source code is included in the jar.)&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;h3&gt;Accessibility&lt;/h3&gt;&lt;p&gt;&lt;/p&gt;I feel like this model could be improved as far as accessibility is concerned.  This current implementation specifically calls:&lt;br /&gt;&lt;p&gt;&lt;code&gt;dialog.setFocusableWindowState(false)&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;If the dialog was allowed to have the keyboard focus: then on Macs using the brushed-metal look the parent frame would alternate a lot between the dark gray and light gray shades (representing a focused/unfocused window).  So that is worth avoiding... but at the same time: this dialog isn't keyboard traversable at all.  One thing Java usually excels at effortlessly is accessibility, and it seems like a real shame to not address that in this project.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;For example:&lt;br /&gt;&lt;br /&gt;In addition to a "Done" button, there could be a "Clear" button.  And each component in the dialog could be tabbed to: a single return key or double-click then adds that component to the rightmost edge of the toolbars components.  This would let users who may have trouble with drag-and-drop access this functionality.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;You could also argue users with poor mouse skills may not use the toolbar at all, since each of those buttons are important enough they probably have their own keyboard shortcuts.  But I'm not convinced that means accessibility is worth ignoring just for that argument.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;/p&gt;&lt;h3&gt;Environments&lt;/h3&gt;&lt;p&gt;&lt;/p&gt;I think it's interesting that I designed this with Java 1.3 compliance: so we could have been doing this &lt;i&gt;years&lt;/i&gt; ago.  And it's pure-java.  I only have Mac 10.5 and XP at my disposal: it works well on both, and it should work well on other platforms, too.&lt;br /&gt;&lt;p&gt;On platforms (such as XP) where &lt;code&gt;DragSource.isDragImageSupported()&lt;/code&gt; is false: this makes the component being dragged visible in the toolbar &lt;i&gt;while the drag is in progress&lt;/i&gt;.  This may make for some unusual slipping and sliding of the components, but I think it's a much more responsive operation overall.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-4713893359515975730?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/4713893359515975730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/06/customize-toolbar-implementing-mac-like.html#comment-form' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/4713893359515975730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/4713893359515975730'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/06/customize-toolbar-implementing-mac-like.html' title='Customize Toolbar: implementing Mac-like toolbars'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-7385719599610253689</id><published>2008-11-01T00:27:00.000-07:00</published><updated>2011-02-21T13:14:51.924-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Event Dispatch Thread'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='deadlock'/><category scheme='http://www.blogger.com/atom/ns#' term='AWT'/><category scheme='http://www.blogger.com/atom/ns#' term='panic'/><title type='text'>Event Dispatch Thread: Responding to Deadlocks</title><content type='html'>As I'm working on my latest project, I've had 3 cases now where I've encountered a locked event dispatch thread.  This means that my current session is toast.  There are only 2 things left to do at this point:&lt;br /&gt;&lt;ul&gt;&lt;LI&gt;As a developer, I look up its process number and call "kill -quit processID" in the terminal, so I can get a list of stack traces.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;Then I kill the process, either through Eclipse or by forcing a quit.&lt;/LI&gt;&lt;/ul&gt;&lt;br /&gt;&lt;P&gt;Then, having a list of stack traces, I set out to banish whatever caused the deadlock to occur in the first place.  This way no user/customer will ever have to face this horrible problem on their own, right?&lt;br /&gt;&lt;p&gt;Wrong.&lt;br /&gt;&lt;P&gt;There are 2 problems with this theory.  This assumes my programming is reliable and safe (which it obviously isn't if I had a deadlock in the first place).  Also this assumes my testing process is thorough and foolproof (this is nearly impossible: even if you could test every machine in existence, you can't test machines that have yet to be invented!  As computers speed up, and multiple processors (or similar devices) grow in popularity: race conditions that never mattered before may suddenly come to your attention.)  Therefore: the sad conclusion is that my software may fail for customers/users.  I solemnly swear to try my darndest to prevent such failures, but they may end up happening anyway.&lt;br /&gt;&lt;P&gt;So I wanted to build a series of safety nets.  First of all, I wanted to get a list of stack traces in the console (similar to calling "kill -quit pid" in the terminal).  When you have an angry customer on tech support, it's not advisable to guide him/her through entering this command manually.  Instead I print out this information to the console, and then (since I'm already outputting the console to a text file), I simply have the user send me that text file to study.&lt;br /&gt;&lt;P&gt;Monitoring for a locked AWT thread is simple.  Here's a rough sketch of how to do it:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;class AWTRunnable implements Runnable {&lt;br /&gt;   boolean flag;&lt;br /&gt; &lt;br /&gt;   public void run() {&lt;br /&gt;      flag = true;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Thread myThread = new Thread() {&lt;br /&gt;   public void run() {&lt;br /&gt;      AWTRunnable awtRunnable = new AWTRunnable();&lt;br /&gt;      while(true) {&lt;br /&gt;         idle();&lt;br /&gt;         awtRunnable.flag = false;&lt;br /&gt;         SwingUtilities.invokeLater(awtRunnable);&lt;br /&gt;         long start = System.currentTimeMillis();&lt;br /&gt;  while(awtRunnable.flag==false) {&lt;br /&gt;            idle();&lt;br /&gt;            long time = System.currentTimeMillis();&lt;br /&gt;            if(time-start&gt;acceptedDelay)&lt;br /&gt;               dumpThreadsAndPanic();&lt;br /&gt;         }&lt;br /&gt;      }&lt;br /&gt;   }&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;Already this is more feedback than before.  This is good progress.  But I think we can do more.  (This is where the controversial stuff comes up.)&lt;br /&gt;&lt;P&gt;If the AWT thread is locked, and we can detect this: is there something we can do to help the user?  Two ideas come to mind:&lt;br /&gt;&lt;ol&gt;&lt;LI&gt;Auto-save:  Then when the application is next launched, point out the software's noble attempts at saving face, and offer to give the user back their documents.&lt;/li&gt;&lt;br /&gt;&lt;LI&gt;Killing the event dispatch thread: this is unsafe, but if it works then the user may regain control of their application.&lt;/LI&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;So first I wrote the &lt;a href="http://javagraphics.java.net/doc/com/bric/debug/AWTPanicListener.html"&gt;&lt;code&gt;AWTPanicListener&lt;/code&gt;&lt;/a&gt;.  If you have a mechanism to autosave: you should definitely do that first.  Just call:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;AWTMonitor.installAWTListener("My Application Name", false);&lt;br /&gt;AWTMonitor.addPanicListener(myPanicListener);&lt;/pre&gt;&lt;/blockquote&gt;.&lt;br /&gt;&lt;p&gt;Option #2 is more interesting.  If you pass &lt;code&gt;true&lt;/code&gt; instead of &lt;code&gt;false&lt;/code&gt; in the example above: the &lt;code&gt;PanicDialogPrompt&lt;/code&gt; will also be installed.  This will result in a separate process launching that shows this dialog:&lt;br /&gt;&lt;BR&gt;&lt;IMG SRC="http://homepage.mac.com/bricolage1/blog_resources/unresponsive.png"&gt;&lt;br /&gt;&lt;P&gt;If the user clicks "Abort", then &lt;code&gt;Thread.stop()&lt;/code&gt; is called on the event dispatch thread.  (Java quickly regenerates a new event dispatch thread, so the program has a decent shot of surviving.)&lt;br /&gt;&lt;P&gt;I bounced this idea off a couple of colleagues, and they replied with a firm: &lt;b&gt;don't do this!&lt;/b&gt;  Werner probably summed up his objection best:&lt;br /&gt;&lt;blockquote&gt;Don't ever use Thread.stop().&lt;br /&gt;&lt;BR&gt;The JVM will get into an undefined state, and will crash badly. That's why this method has been deprecated already in the second release of the JVM (I think this happened in 1.0.1 or 1.1).&lt;br /&gt;&lt;BR&gt;Instead of 'will get into an undefined state' I should write 'is going to'. But there are many cases where the JVM 'gets lucky' and survives the stop.&lt;br /&gt;Even if Thread.stop() wouldn't damage the JVM. The AWT event dispatcher thread is vital to a Java application. Its impossible to know in what state the thread is, when the stop occurs. Expect native resources not being freed when the stop occurs, and worse. (Can there be anything worse? Because if native resources aren't freed, the application will crash sooner or later anyway).&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;Also we both agree the concept of launching another process is cumbersome and tacky, and that the dialog is intimidating to users (and it's never a good philosophy to intimidate users).&lt;br /&gt;&lt;P&gt;All that being said, however: while I was waiting on replies to that email thread I started working on other projects.  Before the end of the day I encountered my 4th deadlocked event dispatch thread.  The &lt;code&gt;AWTMonitor&lt;/code&gt; perked up, launched the extra process, and I was able to recover control of my program without forcing a quit.  It worked.&lt;br /&gt;&lt;p&gt;&lt;a href="http://javagraphics.java.net/jars/AWTMonitor.jar"&gt;Here&lt;/a&gt; is a demo jar (source included) that tests this concept.&lt;br /&gt;&lt;P&gt;So is this a safe, good idea?  No.  It risks crashing the JVM.  But you've already detected the session is likely ruined at this point: what's the harm in trying?  I compare this to an ejector seat in a plane: it may malfunction and kill something... but it will only be used when something is as good as dead already.&lt;br /&gt;&lt;p&gt;(Of course launching another process may take a little extra finagling based exactly on how your app is launched.  And it isn't doable in an applet, or other environments with restricted security.  But for a desktop app or your Eclipse environment: it should be possible to automate.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-7385719599610253689?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/7385719599610253689/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/06/event-dispatch-thread-responding-to.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7385719599610253689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7385719599610253689'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/06/event-dispatch-thread-responding-to.html' title='Event Dispatch Thread: Responding to Deadlocks'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-6373744903203633404</id><published>2008-11-01T00:26:00.000-07:00</published><updated>2011-02-21T13:15:16.558-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='AffineTransform'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='TexturePaint'/><category scheme='http://www.blogger.com/atom/ns#' term='paint'/><category scheme='http://www.blogger.com/atom/ns#' term='transform'/><title type='text'>TexturePaints and AffineTransforms</title><content type='html'>One of the things I love about &lt;A HREF="http://xmlgraphics.apache.org/batik/"&gt;Batik&lt;/A&gt; classes are the gradient paints.  Long before Java 6 had &lt;A HREF="http://java.sun.com/javase/6/docs/api/java/awt/MultipleGradientPaint.html"&gt;sophisticated paints&lt;/A&gt; with multiple colors: Batik had them.  Also the Batik classes can associate an &lt;code&gt;AffineTransform&lt;/code&gt; with their gradients.  This is a relatively advanced feature that I've really come to appreciate recently.  I even designed code around the assumption that my paint could have an &lt;code&gt;AffineTransform&lt;/code&gt; of its own... but then I realized my good old &lt;code&gt;TexturePaint&lt;/code&gt; can't do this.  (Even in Java 6.)&lt;br /&gt;&lt;P&gt;So I gritted my teeth and sat down to explore this problem, and to my amazement in about 2 minutes the "problem" was resolved.  In fact the resulting class is so simple I can list it here in this entry:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;&lt;br /&gt;public class TransformedTexturePaint extends TexturePaint {&lt;br /&gt;   AffineTransform transform;&lt;br /&gt;&lt;br /&gt;   public TransformedTexturePaint(BufferedImage txtr, &lt;br /&gt;       Rectangle2D anchor,&lt;br /&gt;       AffineTransform transform) {&lt;br /&gt;     super(txtr, anchor);&lt;br /&gt;     if(transform==null) transform = new AffineTransform();&lt;br /&gt;     this.transform = new AffineTransform(transform);&lt;br /&gt;   }&lt;br /&gt; &lt;br /&gt;   public AffineTransform getTransform() {&lt;br /&gt;     return transform;&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   public PaintContext createContext(ColorModel cm,&lt;br /&gt;       Rectangle deviceBounds,&lt;br /&gt;       Rectangle2D userBounds,&lt;br /&gt;       AffineTransform xform,&lt;br /&gt;       RenderingHints hints) {&lt;br /&gt;     AffineTransform newTransform = new AffineTransform(xform);&lt;br /&gt;     newTransform.concatenate(transform);&lt;br /&gt;     return super.createContext(cm, deviceBounds, userBounds, newTransform, hints);&lt;br /&gt;   }&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;This could possibly effect your performance if you rely on paints heavily.  Java pipelines usually have special code to handle standard AWT classes: I bet if you benchmarked the &lt;code&gt;TexturePaint&lt;/code&gt; against this object (even if this object just had an identity transform): the &lt;code&gt;TexturePaint&lt;/code&gt; will have a different average rendering time.&lt;br /&gt;&lt;p&gt;Here is an applet demoing this class:&lt;br /&gt;&lt;br&gt;&lt;APPLET CODE="com/bric/awt/TransformedTexturePaintDemo" CODEBASE="http://javagraphics.java.net/jars/" width=320 height=320&gt;     &lt;PARAM NAME="archive" VALUE="TransformedTexturePaint-bin.jar"&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;p&gt;You can download this applet (source included) &lt;A HREF="http://javagraphics.java.net/jars/TransformedTexturePaint.jar"&gt;here&lt;/A&gt;.  (But really the code about is what matters.)&lt;br /&gt;&lt;h3&gt;AffineTransform in General&lt;/h3&gt;&lt;br /&gt;Also in that jar is something that might be of general use: &lt;a href="http://javagraphics.java.net/doc/com/bric/geom/TransformUtils.html"&gt;&lt;code&gt;TransformUtils&lt;/code&gt;&lt;/a&gt;.  I'm assuming you, the savvy, sophisticated reader are generally familiar with &lt;A HREF="http://java.sun.com/javase/6/docs/api/java/awt/geom/AffineTransform.html"&gt;AffineTransforms&lt;/A&gt;.  They are the magic glue that hold &lt;code&gt;Java2D&lt;/code&gt; together.  But sometimes dealing with the matrices and the concatenations and the preconcatenations can be really daunting.  Sometimes its a lot easier to just say: "Look, I know what I want to transform these points &lt;i&gt;into&lt;/i&gt;, doesn't that count?"&lt;br /&gt;&lt;P&gt;So a few years ago I cooked up the &lt;code&gt;TransformUtils&lt;/code&gt; class to do just that.  You can specify 3 "before" points, and 3 "after" points, it returns the &lt;code&gt;AffineTransform&lt;/code&gt; that transforms the before into the after.  It's like magic.  Or more specifically: it's like performing gaussian elimination on a few simple equations.  But why worry about a matrix of equations when you can just use &lt;code&gt;TransformUtils&lt;/code&gt;?&lt;br /&gt;&lt;P&gt;Sometimes people (and by 'people', I mean 'bosses') may ask: why 3 points?  Well, because in 3 points there are 6 pieces of information (since each point has an X and a Y element).  An &lt;code&gt;AffineTransform&lt;/code&gt; has 6 variables in it.  So the two naturally fit together.  Often what bosses and clients end up asking about, though, is a &lt;A HREF="http://72.5.124.55/products/java-media/jai/forDevelopers/jai-apidocs/javax/media/jai/PerspectiveTransform.html"&gt;PerspectiveTransform&lt;/A&gt;.  This lets you perform much more complicated transformations: you can now achieve more 3D-looking effects.  These transforms involve 4 points, which is a much more natural number if your selection is, say, bordered with a rectangle.  The catch, however, is that this is a lot harder to compute.  In Java this generally requires &lt;code&gt;JAI&lt;/code&gt;, which is much slower than the built-in &lt;code&gt;Java2D&lt;/code&gt;.  Also, to my knowledge other environments (such as Flash) only really work with the 2D &lt;code&gt;AffineTransforms&lt;/code&gt;.  So the simple answer is: the 3D-type of transforms are not as widely supported, and require more resources to compute.  But they are definitely doable in Java.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-6373744903203633404?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/6373744903203633404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/06/texturepaints-and-affinetransforms.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6373744903203633404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6373744903203633404'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/06/texturepaints-and-affinetransforms.html' title='TexturePaints and AffineTransforms'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-853409991896799169</id><published>2008-11-01T00:25:00.000-07:00</published><updated>2011-02-21T13:15:34.363-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='XP'/><category scheme='http://www.blogger.com/atom/ns#' term='Vista'/><category scheme='http://www.blogger.com/atom/ns#' term='interface'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='dialog'/><category scheme='http://www.blogger.com/atom/ns#' term='JOptionPane'/><title type='text'>JOptionPane: making an alternative</title><content type='html'>The &lt;code&gt;JOptionPane&lt;/code&gt; is... nice.  But recently I needed something better.  I wanted to use &lt;A HREF="http://javagraphics.blogspot.com/2008/06/text-height-gui-layout-and-text-boxes.html"&gt;a text box that wrapped automatically at a fixed width&lt;/A&gt;.  And a help button.  And partitioned text (part in bold, part plain text).&lt;br /&gt;&lt;P&gt;At least, that's how this project started.  Then after reading what &lt;A HREF="http://msdn.microsoft.com/en-us/library/aa511268.aspx"&gt;Microsoft&lt;/A&gt; and &lt;A href="http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGWindows/chapter_18_section_7.html#//apple_ref/doc/uid/20000961-BACFBACB"&gt;Apple&lt;/A&gt; has to say on designing dialogs... and getting some feedback from a client and &lt;A HREF="http://blog.hslu.ch/rawcoder"&gt;Werner&lt;/A&gt;... I designed a new class &lt;A HREF="http://javagraphics.java.net/jars/QDialog.jar"&gt;QDialog&lt;/A&gt;.  It's certainly not perfect, but I'd encourage its use over &lt;code&gt;JOptionPane&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;The detail is best explained &lt;A HREF="http://javagraphics.java.net/doc/com/bric/swing/QDialog.html"&gt;in the javadocs&lt;/A&gt;.&lt;br /&gt;&lt;P&gt;More controversial touches include things like:&lt;br /&gt;&lt;LI&gt;If a "Cancel" button is not visible, then the close decoration does not dismiss the dialog.  (Because if an operation is not cancellable, what does the close decoration imply?)&lt;br /&gt;&lt;LI&gt;Also support is built-in for a "Do not show again" checkbox, and a "Always apply this decision" checkbox.  On the one hand, these are horrible design elements.  On the other hand, they are prevalent everywhere.  Please don't use them, but if you must use them: they're there.  :)&lt;br /&gt;&lt;P&gt;(Even Apple, the self-touted champion of design, includes this checkbox on my XP version of iTunes for an auto-update dialog.)&lt;br /&gt;&lt;/LI&gt;&lt;br /&gt;&lt;P&gt;And although not "controversial", there is a wealth of GUI component used in Vista (such as "command links") that are not present in other platforms, so it's a great challenge to make native components that truly blend in on all platforms.&lt;br /&gt;&lt;P&gt;Here are some screenshots of the &lt;code&gt;QDialog&lt;/code&gt; from Mac and XP:&lt;br /&gt;&lt;IMG SRC="http://homepage.mac.com/bricolage1/blog_resources/qdialog.jpg"&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-853409991896799169?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/853409991896799169/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/06/joptionpane-making-alternative.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/853409991896799169'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/853409991896799169'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/06/joptionpane-making-alternative.html' title='JOptionPane: making an alternative'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-6298400906637174789</id><published>2008-11-01T00:24:00.000-07:00</published><updated>2011-02-21T13:15:54.788-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pixels'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='layout'/><category scheme='http://www.blogger.com/atom/ns#' term='height'/><category scheme='http://www.blogger.com/atom/ns#' term='font'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='JTextArea'/><title type='text'>Text: Text Height and GUI Layout</title><content type='html'>I recently wanted to design an abstract, compact GUI that involved large text boxes.  It didn't take long to realize what I wanted was surprisingly complex:&lt;br /&gt;&lt;BR&gt;How do I deal with the height of text boxes?&lt;br /&gt;&lt;P&gt;To my knowledge, I can't simply ask a LayoutManager to add a JTextArea that has a fixed width (say, about 300 pixels?) and is &lt;i&gt;appropriately tall to show all the text inside it&lt;/i&gt;, but no more.&lt;br /&gt;&lt;P&gt;But there is a handy class called the &lt;code&gt;LineBreakMeasurer&lt;/code&gt;.  It's really easy to use, and it can take any &lt;code&gt;AttributedString&lt;/code&gt; and ration it off into separate lines.&lt;br /&gt;&lt;P&gt;So here's a method that can determine the approximate height of a &lt;code&gt;String&lt;/code&gt; if it's given a fixed width to render it in:&lt;br /&gt;&lt;BR&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;Hashtable attributes = new Hashtable();&lt;br /&gt;attributes.put( TextAttribute.FONT, getFont());&lt;br /&gt;String text = getText();&lt;br /&gt;&lt;br /&gt;FontRenderContext frc = isMac ? &lt;br /&gt;   new FontRenderContext(new AffineTransform(),true,true) :&lt;br /&gt;   new FontRenderContext(new AffineTransform(),false,false);&lt;br /&gt;&lt;br /&gt;String[] paragraphs = getParagraphs(text);&lt;br /&gt;int rows = 0;&lt;br /&gt;for(int a = 0; a &amp;lt paragraphs.length; a++) {&lt;br /&gt;   int textLength = paragraphs[a].length();&lt;br /&gt;   AttributedString attrString = new AttributedString( paragraphs[a], attributes);&lt;br /&gt;&lt;br /&gt;   LineBreakMeasurer lbm = new LineBreakMeasurer(attrString.getIterator(),frc);&lt;br /&gt;&lt;br /&gt;   int pos = 0;&lt;br /&gt;   while(pos &amp;lt textLength) {&lt;br /&gt;     pos = lbm.nextOffset(fixedWidth);&lt;br /&gt;     lbm.setPosition(pos);&lt;br /&gt;     rows++;&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;int extraPixels = 0;&lt;br /&gt;f(isXP) { //allow for descents&lt;br /&gt;   extraPixels = (int)(getFont().getLineMetrics("g", frc).getDescent()+1);&lt;br /&gt;}&lt;br /&gt;return new Dimension(fixedWidth, rows*rowHeight+extra);&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;The "extra" was added after trying this on XP, and realizing that the descent of g's and p's was being cut off.&lt;br /&gt;&lt;P&gt;Is this perfect?  Probably not.  But it's much better foundation than what I started with.  :)&lt;br /&gt;&lt;P&gt;I integrated this into the &lt;code&gt;&lt;a href="http://javagraphics.java.net/jars/FixedWidthTextArea.jar"&gt;FixedWidthTextArea&lt;/A&gt;&lt;/code&gt;, so now I have a &lt;code&gt;JTextArea&lt;/code&gt; that adjusts its preferred height every time its text is adjusted.  This could easily be extended to a more powerful component, but for now it meets my needs as-is.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-6298400906637174789?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/6298400906637174789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/06/text-height-gui-layout-and-text-boxes.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6298400906637174789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6298400906637174789'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/06/text-height-gui-layout-and-text-boxes.html' title='Text: Text Height and GUI Layout'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-3868515407444987023</id><published>2008-11-01T00:23:00.000-07:00</published><updated>2011-02-21T13:29:02.875-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mac 10.5'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='aqua'/><category scheme='http://www.blogger.com/atom/ns#' term='NSImage'/><title type='text'>Images: Accessing NSImages in Mac 10.5</title><content type='html'>This page further explores fun (and slightly secretive) resources available in Mac 10.5 in Java.&lt;br /&gt;&lt;P&gt;In Cocoa, the NSImage class &lt;A HREF="http://developer.apple.com/documentation/Cocoa/Reference/ApplicationKit/Classes/NSImage_Class/Reference/Reference.html#//apple_ref/doc/uid/20000344-BCIFACIA"&gt;has several constants&lt;/a&gt; that include standard Mac-ish images.  In Java you can access these images.  I don't know if this is "undocumented", or "poorly documented", or if I'm simply incompetent at finding the right technical note where it might be mentioned... but I never knew about this feature until I heard about it on Apple's java-dev list.&lt;br /&gt;&lt;P&gt;Accessing these images is very simple: suppose you want to access "NSImageNameSlideshowTemplate":&lt;br /&gt;&lt;BR&gt;&lt;PRE&gt;Image image = Toolkit.getDefaultToolkit().getImage("NSImage://NSSlideshowTemplate"))&lt;/PRE&gt;&lt;br /&gt;&lt;BR&gt;Now as of this writing, it so happens that the image returned will be a BufferedImage, but I would not directly assume that just in case that changes.&lt;br /&gt;&lt;P&gt;So the link mentioned above takes you to the official Apple page listing all the available images in Mac 10.5.&lt;br /&gt;&lt;P&gt;Below is an abbreviated list, including the dimensions of each graphic (in case you need to design icons that match those in a toolbar).  Also the list includes relevant comments from the Apple documentation when appropriate.&lt;br /&gt;&lt;P&gt;Note that Apple makes it very clear: you should only use these graphics for their intended purpose.  The graphic may change in future OS releases.  By using the abstract name, you're guaranteeing your application will use native-looking graphics.  But the graphic may change, the size may change, etc.&lt;br /&gt;&lt;br /&gt;&lt;P&gt;(Also &lt;A HREF="http://javagraphics.java.net/jars/DemoNSImage.jar"&gt;here&lt;/A&gt; is a little application that creates/displays the table mentioned below.)&lt;br /&gt;&lt;P&gt;Other related links include &lt;A HREF="http://developer.apple.com/technotes/tn2007/tn2196.html"&gt;this tech note&lt;/A&gt; from Apple discussing ways to Aqua-fy your JComponents.  Or &lt;A HREF="http://javagraphics.blogspot.com/2008/05/runtime-properties-kind-of-important.html"&gt;this  entry&lt;/A&gt; which talks about runtime-specific graphics.  If you need something platform independent, obviously the graphics listed on this page will not help you.  (I assume Apple will not sanction using their artwork on other platforms.)  So instead you should use graphics in the UIManager.  And/or you can look into libraries of open-source graphics such as the &lt;A HREF="http://www.famfamfam.com/lab/icons/silk/"&gt;Silk Icons&lt;/A&gt;.  Ken Orr also has a few standard &lt;A HREF="http://explodingpixels.wordpress.com/2008/07/03/leopard-style-icons/"&gt;icons&lt;/A&gt; that are safer to use in a cross-platform product that Apple's.&lt;br /&gt;&lt;br /&gt;&lt;P&gt;So here is the list of icons, their dimensions, and comments.  This list was last updated July 1st, 2008.  Apple may have a more up-to-date list somewhere.  Remember these are only available on 10.5, and should only be used for the function they describe.&lt;br /&gt;&lt;BR&gt;&lt;br /&gt;&lt;IMG SRC="http://homepage.mac.com/bricolage1/blog_resources/ns_table.png"&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-3868515407444987023?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/3868515407444987023/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/06/nsimage-accessing-images-in-mac-105.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3868515407444987023'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3868515407444987023'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/06/nsimage-accessing-images-in-mac-105.html' title='Images: Accessing NSImages in Mac 10.5'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-5180793069380631022</id><published>2008-11-01T00:22:00.000-07:00</published><updated>2011-02-21T13:28:55.101-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='screen'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='robot'/><category scheme='http://www.blogger.com/atom/ns#' term='capture'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='movie'/><title type='text'>Screen Capture: Recording Java Apps</title><content type='html'>Sooner or later I'll want to make tutorial movies demo'ing my new application for the unsuspecting public.&lt;br /&gt;&lt;br /&gt;&lt;P&gt;It turns out I can create those movies in Java pretty easily.  I made an application that ties into the RepaintManager (and listens for MouseEvents and ComponentEvents), and with that I'm able to get anywhere between 5 and 90 fps as I capture data using the Robot class.&lt;br /&gt;&lt;P&gt;Because I'm only capturing the parts of the window that are dirty, the size of your window doesn't matter: it's how much activity is taking place that counts.  (Well, that and your processor speed.  And RAM.)&lt;br /&gt;&lt;P&gt;Here is a demo of what this program can do:&lt;br /&gt;&lt;OBJECT CLASSID="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B" CODEBASE="http://www.apple.com/qtactivex/qtplugin.cab" WIDTH="340" HEIGHT="314"&gt;&lt;br /&gt;&lt;PARAM name="SRC" VALUE="http://homepage.mac.com/bricolage1/blog_resources/CaptureDemo.mov"&gt;&lt;br /&gt;&lt;PARAM name="AUTOPLAY" VALUE="false"&gt;&lt;br /&gt;&lt;PARAM name="CONTROLLER" VALUE="true"&gt;&lt;br /&gt;&lt;EMBED SRC="http://homepage.mac.com/bricolage1/blog_resources/CaptureDemo.mov" WIDTH="430" HEIGHT="314" &gt;&lt;br /&gt;&lt;/EMBED&gt;&lt;br /&gt;&lt;/OBJECT&gt;&lt;br /&gt;&lt;P&gt;Is it perfect?  No.  In my opinion the biggest two drawbacks are:&lt;br /&gt;&lt;LI&gt;I can't seem to capture the real cursor.  So I made one up.  This is not ideal, but it'll have to do for now.&lt;br /&gt;&lt;LI&gt;A real tutorial video will have narration.  I don't know off the top of my head if I can use iMovie to do this or not (can iMovie work with such a non-traditional movie?).  I could record this in Java, but it will add a lot of work and complexity to this project.&lt;/LI&gt;&lt;br /&gt;&lt;br /&gt;&lt;P&gt;Also the mouse location sometimes is recorded out-of-whack (there are at least 2 parts in the demo movie where the mouse "jumps" for a fraction of a second).&lt;br /&gt;&lt;br /&gt;&lt;P&gt;So... whatever it's shortcomings may be: &lt;A HREF="http://javagraphics.java.net/jars/ScreenCapture.jar"&gt;here&lt;/a&gt; is the demo program that captured the video above.  Note it is bundled with my &lt;A HREF="http://javagraphics.blogspot.com/2008/06/movies-writing-mov-files-without.html"&gt;previous blog entry&lt;/A&gt; so it can write MOV files, but you're welcome to remove those classes if there's another way you'd like to encode the animation.  (The animation is iterated through as a series of BufferedImages, so you can do whatever you like with it.)&lt;br /&gt;&lt;P&gt;(In the interest of full disclosure, I should point out the movie on this webpage was NOT directly created by this app.  To publish the movie for the web, I re-encoded the movie this application created using MPEG-4 level compression.)&lt;br /&gt;&lt;br /&gt;&lt;P&gt;Also a truly professional-looking tutorial movie will not really record every mouse movement the user makes: it will smooth out your gestures to give you a super-human grace.  (There will be no wobbly bits, no corrections, etc.).  I suppose that wouldn't be too hard to do in this package: so long as you knew exactly where the start/stop points for a mouse gesture were...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-5180793069380631022?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/5180793069380631022/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/06/screen-capture-recording-java-apps.html#comment-form' title='20 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/5180793069380631022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/5180793069380631022'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/06/screen-capture-recording-java-apps.html' title='Screen Capture: Recording Java Apps'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>20</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-5198985901270755836</id><published>2008-11-01T00:21:00.000-07:00</published><updated>2011-09-05T14:53:21.149-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='codec'/><category scheme='http://www.blogger.com/atom/ns#' term='MOV'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTime'/><category scheme='http://www.blogger.com/atom/ns#' term='movie'/><category scheme='http://www.blogger.com/atom/ns#' term='JPEG'/><title type='text'>Movies: writing MOV files without QuickTime</title><content type='html'>With QuickTime for Java officially deprecated: we need an alternative to create QuickTime mov files from within Java.&lt;br /&gt;&lt;h3&gt;Context&lt;/h3&gt;I generally approach this kind of problem in one of two roles: as an independent developer, or as an employee of a small company. In both roles: our budget is small, and open-source/BSD/creative commons/etc tools are our first pick. Aside from the formidable technological difficulties in encoding MPEG video, or H264, or whatever the current fad is: the legal/financial difficulties are even worse [for someone in my position].&lt;br /&gt;&lt;br /&gt;There is &lt;a href="http://www.mpegla.com/main/default.aspx"&gt;a group&lt;/a&gt; you're supposed to pay to license the patents involved in writing MPEGs. The last time I researched this (which was a while ago) the fine print is startling: there are so many intricate patents in this sort of format that nobody knows exactly who owns what, or if multiple (conflicting) patents exist. As a result: this group represents &lt;i&gt;many&lt;/i&gt; of the stakeholders, and they promise not to sue if you pay them their license fee. However it's always possible some upstart (from not in the group) will come along and point out a conflicting patent, and then you'd either have to pay them off too or risk being sued (or both).&lt;br /&gt;&lt;br /&gt;(&lt;a href="http://www.thisamericanlife.org/radio-archives/episode/441/when-patents-attack"&gt;Here&lt;/a&gt; is a fun commentary on the state of technological patents in America.)&lt;br /&gt;&lt;br /&gt;In response to this the &lt;a href="http://www.vorbis.com/"&gt;Ogg Vorbis&lt;/a&gt; project was formed: a patent-free alternative with quality compression. Last I checked, though: this is still not universally supported (which is a real pity). It is less than ideal to ask users to download a plug in to view their exported movie -- especially if that movie is intended to be published on websites for a diverse audience.&lt;br /&gt;&lt;br /&gt;There some other options developers can experiment with. &lt;a href="http://ffmpeg.org/"&gt;FFmpeg&lt;/a&gt; is an open-source implementation of MPEG codecs, however they haven't paid any of the licensing fees. As a result this is very much a use-at-your-own risk undertaking: they're providing you with the technical ability to read/write mpeg data, but if apply this in any commercial application you need to have your lawyers review it first. One workaround that some companies use, then, is to provide built-in &lt;i&gt;support&lt;/i&gt; for FFmpeg, but they do not distribute it. Then somehow (whether in documentation or through word of mouth) they mention to users that if they (the users) install this library: they can work with mpegs. I consider this unethical, but I'm not sure whether it is legal.&lt;br /&gt;&lt;h3&gt;Solution&lt;/h3&gt;My current solution is also less than ideal. This is something my 2001 Nikon digital camera could do, and now here it is in an open-source Java project: write a QuickTime mov file using JPEG-encoding.&lt;br /&gt;&lt;br /&gt;There are dozens of possible codecs to use inside a mov file -- and you can write your own if you're willing to write the QuickTime plugin. The JPEG encoding is exactly what it sounds like: each frame is encoded as a JPEG. This results in very poor compression (relative to other contemporary standards): but to my knowledge nobody is going to challenge it legally.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://javagraphics.java.net/jars/JPEGMovieAnimation.jar"&gt;Here&lt;/a&gt; is a jar that writes mov files entirely from Java code (source code included). The &lt;a href="http://javagraphics.java.net/doc/com/bric/qt/io/JPEGMovieAnimation.html"&gt;&lt;code&gt;JPEGMovieAnimation&lt;/code&gt;&lt;/a&gt; is a very simple class that writes frames sequentially to a movie file.&lt;br /&gt;&lt;br /&gt;I deciphered the file format from some Apple documentation and reverse engineering some sample mov files, but it was relatively straight-forward.&lt;br /&gt;&lt;br /&gt;In order to keep memory usage low: the frames are piped directly into the exported file. However this requires a second pass in writing the data: at the very end we have to clarify the size of certain data chunks in the header of the file. This is achieved using a &lt;code&gt;RandomAccessFile&lt;/code&gt; object, and because of this we have to write to a &lt;code&gt;File&lt;/code&gt; (instead of the more ideal &lt;code&gt;OutputStream&lt;/code&gt;).&lt;br /&gt;&lt;br /&gt;Some day I would like to the ability to encode audio along with the audio, but I haven't found the time to get to that yet. (The same patent discussion applies to audio formats, too, and I would probably have to use uncompressed audio.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-5198985901270755836?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/5198985901270755836/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/06/movies-writing-mov-files-without.html#comment-form' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/5198985901270755836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/5198985901270755836'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/06/movies-writing-mov-files-without.html' title='Movies: writing MOV files without QuickTime'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-6875416080422706399</id><published>2008-11-01T00:19:00.000-07:00</published><updated>2011-02-21T13:28:30.860-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='angle'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='dial'/><category scheme='http://www.blogger.com/atom/ns#' term='slider'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='widget'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='aqua'/><title type='text'>Angles: need GUI widget for angles?</title><content type='html'>I have a few graphical elements in my new program that need to let the user pick angles.  A &lt;FONT FACE="Courier"&gt;JSlider&lt;/FONT&gt; technically meets this need: you can adjust 1 value with a minimum and maximum value.&lt;br /&gt;&lt;br /&gt;&lt;P&gt;However, it's a slider.  It looks like a slider.  It doesn't preview the angle (is the left side 90 degrees?  180?).  And the left extreme is really equal to (or nearly equal to) the right extreme... but the GUI suggests they should be polar opposites.&lt;br /&gt;&lt;P&gt;So a new UI class is in order.  The underlying &lt;FONT FACE="Courier"&gt;JSlider&lt;/FONT&gt; is fine; we don't need to change that.  Enter the &lt;FONT FACE="Courier"&gt;AngleSliderUI&lt;/FONT&gt;.  This is a simple little UI that acts like the dials I use in iWeb.&lt;br /&gt;&lt;P&gt;To go one step further, I wrote the &lt;FONT FACE="Courier"&gt;AquaAngleSliderUI&lt;/FONT&gt; to emulate Aqua.  Note this is not a pixel-perfect replica, but it's good enough for my needs.  Also in iWeb I don't think the control supported keyboard traversal, so I had to improvise that part (I made the content always blue when the component has focus?).&lt;br /&gt;&lt;br /&gt;Here is an applet demoing the aqua and non-aqua version:&lt;br /&gt;&lt;br /&gt;&lt;APPLET CODE="com/bric/plaf/AngleSliderUIDemo" CODEBASE="http://javagraphics.java.net/jars/" width=334 height=123&gt;     &lt;PARAM NAME="archive" VALUE="AngleSliderUI.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;You can download this jar (source included) &lt;A HREF="http://javagraphics.java.net/jars/AngleSliderUI.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;P&gt;Actually Ken &lt;a href="http://explodingpixels.wordpress.com/2008/12/12/we-need-better-design-tools/"&gt;recently talked about&lt;/a&gt; the usefulness of vector-based UI elements, and I'm pleased to point out this aqua emulation is just a combination of linear gradients.&lt;br /&gt;&lt;P&gt;I'm not necessarily married to the non-Aqua design; I'm open to any suggestions.  In hindsight: it is awfully dark.  But when I designed it, it blended in well with the dark background and neighboring components.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-6875416080422706399?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/6875416080422706399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/05/angles-need-gui-widget-for-angles.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6875416080422706399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6875416080422706399'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/05/angles-need-gui-widget-for-angles.html' title='Angles: need GUI widget for angles?'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-2423055451454449353</id><published>2008-11-01T00:18:00.001-07:00</published><updated>2011-02-21T13:28:11.561-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='panel'/><category scheme='http://www.blogger.com/atom/ns#' term='bevel'/><title type='text'>DecoratedPanel: a sleeker looking JPanel</title><content type='html'>This is another old favorite.  I decided to polish it up and post it because a future entry might be an extension of this one.  :)&lt;br /&gt;&lt;br /&gt;&lt;P&gt;&lt;A HREF="http://javagraphics.java.net/jars/DecoratedPanel.jar"&gt;This&lt;/A&gt; is an extension of JPanel with a very specific "look".  Now it may be that you like this look but don't like the way the panel is designed; it would be easy enough to make a &lt;FONT FACE="courier"&gt;javax.swing.border.Border&lt;/FONT&gt; that achieved a similar look (minus the "glaze" element.)&lt;br /&gt;&lt;P&gt;Here is an applet demonstrating what this panel can do:&lt;br /&gt;&lt;br /&gt;&lt;APPLET CODE="com/bric/swing/DecoratedPanelDemo" CODEBASE="http://javagraphics.java.net/jars/" width=439 height=426&gt;     &lt;PARAM NAME="archive" VALUE="DecoratedPanel.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-2423055451454449353?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/2423055451454449353/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/05/decoratedpanel-sleeker-looking-jpanel.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2423055451454449353'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2423055451454449353'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/05/decoratedpanel-sleeker-looking-jpanel.html' title='DecoratedPanel: a sleeker looking JPanel'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-6468558759527447052</id><published>2008-11-01T00:18:00.000-07:00</published><updated>2008-11-16T16:36:43.787-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='runtime'/><category scheme='http://www.blogger.com/atom/ns#' term='system properties'/><category scheme='http://www.blogger.com/atom/ns#' term='properties'/><category scheme='http://www.blogger.com/atom/ns#' term='SystemColor'/><category scheme='http://www.blogger.com/atom/ns#' term='UIManager'/><title type='text'>Runtime Properties: kind of important</title><content type='html'>A few years ago I wrote the &lt;A HREF="http://homepage.mac.com/bricolage1/UIViewer.jar"&gt;UIViewer.jar&lt;/A&gt;.  I've always intended to write about it in my blog, but I kept telling little white lies to myself like, "But only after I update it."&lt;br /&gt;&lt;P&gt;Well no more lies.  I'm not updating it.  It is what it is, and (in its own amateur-ish way) what it is is great.&lt;br /&gt;&lt;P&gt;Very simply: this is a tool to browse runtime properties in Java.  It offers 3 lists of properties:&lt;br /&gt;&lt;LI&gt;UI Defaults: all the properties in the UIDefaults class&lt;br /&gt;&lt;LI&gt;SystemColors: the list of all the fields in the SystemColor class.&lt;br /&gt;&lt;LI&gt;System Properties: a list of all the properties in the System class.&lt;/LI&gt;&lt;br /&gt;&lt;br /&gt;&lt;P&gt;Here are some sample screenshots:&lt;br /&gt;&lt;BR&gt;&lt;IMG SRC="http://homepage.mac.com/bricolage1/blog_resources/uiviewer.jpg"&gt;&lt;br /&gt;&lt;BR&gt;&lt;IMG SRC="http://homepage.mac.com/bricolage1/blog_resources/uiviewer2.jpg"&gt;&lt;br /&gt;&lt;br /&gt;&lt;P&gt;Like most of my blog entries: I'm not going to work too hard to sell you on the idea.  Hopefully if this is something you could use, you already recognize it as such.  :)  Just last week I wanted to double-check what the value of "SystemColor.text" was compared to "SystemColor.textText" (do YOU know without checking?).  The week before I wanted to double-check what icons were available in the UIManager.  Etc.&lt;br /&gt;&lt;P&gt;&lt;br /&gt;&lt;P&gt;Now here's the bad news: I wrote this years ago, and have long since lost the source code for it.  So (unless someone volunteers to head up the project of re-writing this): there will be no updates to this jar.  But I consult it pretty regularly, so I thought it might be worth sharing.  :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-6468558759527447052?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/6468558759527447052/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/05/runtime-properties-kind-of-important.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6468558759527447052'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6468558759527447052'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/05/runtime-properties-kind-of-important.html' title='Runtime Properties: kind of important'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-6266047586898659696</id><published>2008-11-01T00:17:00.000-07:00</published><updated>2011-02-21T13:23:29.811-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='widget'/><category scheme='http://www.blogger.com/atom/ns#' term='JComponent'/><category scheme='http://www.blogger.com/atom/ns#' term='gradient'/><title type='text'>Gradients: a GUI widget to design them</title><content type='html'>Kudos to Werner and Sébastien who encouraged me to publish this, and gave me useful feedback on the design.&lt;br /&gt;&lt;P&gt;Around March I designed a JComponent to manipulate a gradient.  It's similar to a JSlider, except instead of 1 thumb it can have indefinitely many thumbs.  (But it must always have a minimum of 2 thumbs.)&lt;br /&gt;&lt;P&gt;Each thumb can have the keyboard focus, and when it has the focus the arrow keys nudge it in the appropriate direction.  Also this project by default is wired together with the &lt;A HREF="http://javagraphics.blogspot.com/2007/04/jcolorchooser-making-alternative.html"&gt;ColorPicker&lt;/A&gt; project, so if you double-click or right-click a thumb the ColorPicker package is used to let you select/edit your color.&lt;br /&gt;&lt;P&gt;New thumbs are added simply by clicking inside the track of the slider.  This is a property that you can turn off, if you really want to, but by default it is on.  You can remove thumbs either by dragging them outside of the track, or by hitting the delete/backspace key when that thumb has focus.  (Unless you are down to only 2 thumbs: then you can't remove any more.)&lt;br /&gt;&lt;P&gt;Here is a demo applet:&lt;br /&gt;&lt;table&gt;&lt;tr&gt;&lt;td&gt;&lt;APPLET CODE="com/bric/swing/GradientSliderDemo" CODEBASE="http://javagraphics.java.net/jars/" width=200 height=200&gt;     &lt;PARAM NAME="archive" VALUE="GradientSlider.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;/td&gt;&lt;td&gt;To use it, try clicking on any spot in the applet to create a new color/thumb.  Then you can right-click a thumb to trigger a popup to pick the exact color you want.  If you double-click or press the spacebar the &lt;code&gt;ColorPicker&lt;/code&gt; dialog should be invoked.&lt;P&gt;(As an applet, this demo may look a little unsightly when you invoke a popup or a dialog.  And the keyboard focus may not easily transfer back to the applet when the dialog is dismissed.  I recommend running this as a stand-alone app for a much better experience.)&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;br /&gt;&lt;P&gt;This demo (source included) is available &lt;a href="http://javagraphics.java.net/jars/GradientSlider.jar"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;P&gt;As you can see there are a few customizations you can make to its appearance, too:&lt;br /&gt;&lt;LI&gt;The track can be drawn in a simple bevel, or it can be painted in a rounded rectangle.&lt;br /&gt;&lt;LI&gt;The thumbs can fade in/out depending on whether the GradientSlider has focus or not.&lt;br /&gt;&lt;LI&gt;The sliders shown here are compact, but they will fill the space they're given.  So you can give a slider a preferred size and it will stretch appropriately.&lt;br /&gt;&lt;LI&gt;What else?  Tick marks, just like a JSlider.  In fact most of the method signatures are designed to really closely match the JSlider ( isInverted(), getOrientation(), setPaintTicks(), etc.)&lt;/LI&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-6266047586898659696?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/6266047586898659696/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/05/gradients-gui-widget-to-design-them.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6266047586898659696'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6266047586898659696'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/05/gradients-gui-widget-to-design-them.html' title='Gradients: a GUI widget to design them'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-3393075363965362314</id><published>2008-11-01T00:16:00.000-07:00</published><updated>2011-02-21T13:18:02.416-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='custom'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='stroke'/><category scheme='http://www.blogger.com/atom/ns#' term='custom stroke'/><category scheme='http://www.blogger.com/atom/ns#' term='bristle'/><title type='text'>Strokes: a bristle stroke</title><content type='html'>By the way: this entry -- and probably several others -- is due in part to the correspondence I have with &lt;A HREF="http://blog.hslu.ch/rawcoder"&gt;Werner R&lt;/A&gt;.  I encourage you to visit his site(s).  And for that matter I encourage you to write me like he does!  It influences the frequency and subject matter of what I post.  :)&lt;br /&gt;&lt;P&gt;So I'm still playing with strokes.  &lt;A HREF="http://javagraphics.blogspot.com/2007/04/strokes-brush-stroke.html"&gt;Last time&lt;/A&gt; I set out with a vision: I wanted to make something that emulated a physical brush against paper.  One thing I discovered was that you can get a wealth of texture just by antialiasing very fine shapes.&lt;br /&gt;&lt;P&gt;So this time I set out with a particular &lt;i&gt;implementation&lt;/i&gt; in mind (I didn't know exactly what the visual effect would be).  This stroke "splatters" tiny shapes (like shrapnel?) over your path.  The frequency of the shapes is a little greater in the middle of the stroke, and it tapers off towards the edges (depending on the thickness you've designated).  Here is an applet demonstrating this new stroke:&lt;br /&gt;&lt;br /&gt;&lt;APPLET CODE="com/bric/awt/BristleStrokeDemo" CODEBASE="http://javagraphics.net/jars/" width=269 height=371&gt;     &lt;PARAM NAME="archive" VALUE="BristleStroke.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;br /&gt;&lt;P&gt;This stroke is available &lt;A HREF="http://javagraphics.java.net/jars/BristleStroke.jar"&gt;here&lt;/A&gt;.  As of this writing, it's biggest flaw is probably how it handles joins (try making a square in this stroke).  If anyone has any suggestions (or improvements), please get in touch in with me.&lt;br /&gt;&lt;P&gt;As I play with making strokes, two thoughts keep coming up:&lt;br /&gt;&lt;P&gt;1.  Is a "stroke" really what I want?  Because a stroke is just a shape.  Do I really want something more flexible?  For example:&lt;br /&gt;&lt;BR&gt;&lt;FONT FACE="Courier"&gt;interface Outline {&lt;br /&gt;&lt;BR&gt; &amp;nbsp &amp;nbsp public void paintOutline(Graphics2D g,Shape shape);&lt;br /&gt;&lt;BR&gt;}&lt;/FONT&gt;&lt;br /&gt;&lt;P&gt;This would allow for a lot more.  Imagine adding some varying opacities and/or colors into these.  (Or images?)  By using the Stroke class you're limited to 1 shape, and 1 fill.&lt;br /&gt;&lt;P&gt;2.  What's an efficient way to paint the stroke on the inside or outside of a shape?  So far in both strokes I straddle the edge of the shape - which is what the BasicStroke also does.  But several painting programs offer the ability to paint the stroke completely on the outside or in the inside of the shape.&lt;br /&gt;&lt;P&gt;You can achieve this with Areas, or even clipping, but is there a (efficient?) geometric way to approach this?  It's easy once you're working on the inner magic of strokes to focus on the area that is clockwise or counterclockwise to the tangent direction fo the shape: but is that area inside or outside the shape?  And if the shape intersects itself (as it does in a figure "8"), then what &lt;i&gt;was&lt;/i&gt; inside is now outside, and vice versa.&lt;br /&gt;&lt;P&gt;And &lt;i&gt;that&lt;/i&gt; ties into the design question: why not program a simple check to see which side of the shape's edge is "inside" or "outside" of the shape and use that value -- knowing that it will fail for cases like a figure "8".  Figure 8's are arguably not that common in graphics.  So now it's a case of making a really well-designed, theoretically wonderful implementation, or making an efficient, fast implementation that will satisfy 98% of the user's needs.&lt;br /&gt;&lt;P&gt;&lt;br /&gt;&lt;P&gt;So where does that leave me in conclusion?  No where, really.  But asking questions is half the fun.  :)  In the mean time I hope the BristleStroke might prove useful to someone...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-3393075363965362314?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/3393075363965362314/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2008/05/strokes-bristle-stroke.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3393075363965362314'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3393075363965362314'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2008/05/strokes-bristle-stroke.html' title='Strokes: a bristle stroke'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-1777083296048809465</id><published>2008-11-01T00:15:00.000-07:00</published><updated>2011-02-21T13:18:18.282-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='artistic'/><category scheme='http://www.blogger.com/atom/ns#' term='custom'/><category scheme='http://www.blogger.com/atom/ns#' term='brush'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='stroke'/><category scheme='http://www.blogger.com/atom/ns#' term='paint'/><category scheme='http://www.blogger.com/atom/ns#' term='render'/><category scheme='http://www.blogger.com/atom/ns#' term='custom stroke'/><title type='text'>Strokes: a brush stroke</title><content type='html'>When I set out to explore the subject of custom strokes, I was surprised to find only a few resources on the subject:&lt;br /&gt;&lt;li&gt;Jerry Huxtable has an &lt;a href="http://www.jhlabs.com/java/java2d/strokes/"&gt;excellent page&lt;/a&gt; detailing a few specialized strokes.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Vincent Hardy's &lt;a href="http://www.amazon.com/Java-API-Graphics-Vincent-Hardy/dp/0130142662"&gt;book&lt;/a&gt; contains a nice wave stroke.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;David Flanagan covered the topic in "Java Examples in a Nutshell" (see &lt;a href="http://www.java2s.com/Code/Java/2D-Graphics-GUI/CustomStrokes.htm"&gt;this page&lt;/a&gt; for details).&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;But none of those examples had the hand-made feel I was aiming for recently in a particular project.  So I decided to make my own stroke that resembled an artist's paint brush.  Here's what I ended up with:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;applet code="com/bric/awt/BrushStrokeDemo" codebase="http://javagraphics.java.net/jars/" height="371" width="269"&gt;     &lt;param name="archive" value="BrushStroke.jar"&gt;&lt;br /&gt;&lt;/applet&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The rendering time is very reasonable, and the implementation is surprisingly simple: just trace the path several times with a thin stroke, each stroke being a little bit offset to the path itself.&lt;br /&gt;&lt;/p&gt;&lt;p&gt;OK, it is a little bit more complicated than that, but that's the basic concept.  I'm sure there's still room for improvement, but &lt;a href="http://javagraphics.java.net/jars/BrushStroke.jar"&gt;here&lt;/a&gt; is the current release of the BrushStroke class.  I'd love some feedback.  Or comments.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-1777083296048809465?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/1777083296048809465/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/strokes-brush-stroke.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1777083296048809465'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/1777083296048809465'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/strokes-brush-stroke.html' title='Strokes: a brush stroke'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-7425657389117116244</id><published>2008-11-01T00:14:00.000-07:00</published><updated>2011-02-21T13:18:36.358-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='drag'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='window'/><title type='text'>Windows: dragging made easy</title><content type='html'>I was studying an interesting GUI question recently relating to Macs and Java, and I came across &lt;A HREF="http://developer.apple.com/technotes/tn2007/tn2196.html"&gt;this page&lt;/A&gt; discussing the cool new properties in Java 1.5 on Mac OS 10.5.&lt;br /&gt;&lt;P&gt;Several of the properties I love.  I wish they could be available for earlier OS's and Java versions, but the search field variants, the window's document file, etc. will  all be welcome additions to my projects.&lt;br /&gt;&lt;P&gt;One property, however, really caught me off guard.  The &lt;A HREF="http://developer.apple.com/technotes/tn2007/tn2196.html#APPLE_AWT_DRAGGABLEWINDOWBACKGROUND"&gt;apple.awt.draggableWindowBackground&lt;/A&gt; property lets the user drag the window with simple click-and-drag gestures.  The funny thing is, we've always been able to do this.  :)  At least, I think we have.  &lt;A HREF="http://javagraphics.java.net/jars/WindowDragger.jar"&gt;Here&lt;/A&gt; is a simple Java class that achieves the same thing, I think... but this class should work on all OS's and in earlier Java versions.  (&lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/window/WindowDragger.java?view=markup"&gt;Here's&lt;/a&gt; the source code.)&lt;br /&gt;&lt;P&gt;If anyone notices a difference in Apple's system property vs that class's behavior, please let me know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-7425657389117116244?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/7425657389117116244/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/windows-dragging-made-easy.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7425657389117116244'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7425657389117116244'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/windows-dragging-made-easy.html' title='Windows: dragging made easy'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-3449552877257185611</id><published>2008-11-01T00:13:00.000-07:00</published><updated>2011-09-05T13:31:57.088-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webcam'/><category scheme='http://www.blogger.com/atom/ns#' term='video'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='swing'/><category scheme='http://www.blogger.com/atom/ns#' term='capture'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTime'/><title type='text'>QTJ: Video Capture in Swing Components</title><content type='html'>&lt;h3&gt;Editor's Note:&lt;/h3&gt;&lt;b&gt;&lt;br /&gt;QuickTime for Java has been deprecated for years now, and so this article is deprecated along with it. I have removed the source code mentioned in this article from my &lt;a href="http://javagraphics.java.net/"&gt;source code repository&lt;/a&gt;. However the link to the jar in this article now to refer to an older SVN revision that should continue to function indefinitely if you feel nostalgic and want to review this project.&lt;/b&gt;&lt;br /&gt;&lt;hr /&gt;Although Chris Adamson has pointed out &lt;a href="http://developer.apple.com/quicktime/qtjava/index.html"&gt;QuickTime for Java's&lt;/a&gt; days are probably numbered: we still use it.  For some things we still have little choice.&lt;br /&gt;However: I lost faith in QTJ's on-screen components a long time ago.  Mixing heavyweight components in our swing frames seems unnecessary.  And if memory serves most of Apple's &lt;a href="http://developer.apple.com/samplecode/Java/idxQuickTime-date.html"&gt;code samples using QTJ&lt;/a&gt; are deprecated anyway.  (Of course I haven't checked in over a year, but that probably doesn't matter since their code samples were last updated in 2006.)&lt;br /&gt;So... now that I've vented about that: on with the good news.&lt;br /&gt;The good news it that it doesn't take much to render video from a camera to your screen.  A sequence grabber, a video channel, and a &lt;a href="http://javagraphics.blogspot.com/2007/04/qtj-from-qdgraphics-to-bufferedimage.html"&gt;way to transfer data from a QDGraphics to a BufferedImage&lt;/a&gt;... and &lt;a href="http://java.net/projects/javagraphics/sources/svn/content/trunk/www/jars/Playback.jar?rev=1339"&gt;voila&lt;/a&gt;!  A small jar that captures video data from your camera and displays it on screen.  (It's like Photo Booth but without any cool effects.)&lt;br /&gt;This is not a robust program, so if you don't have a QT-recgonizable camera plugged in when the program launches, or if you're in an environment that doesn't support QTJ: you're out of luck.  But hopefully it provides a working model for capturing image data to anyone who's interested.&lt;br /&gt;On my 2.2 GHz computer: it takes less than 20 milliseconds to let the sequence grabber idle AND copy this to the BufferedImage.  (This time is output in the console so you can measure your own mileage.)&lt;br /&gt;Be warned that on slower machines this may monopolize your CPU if you're not careful... so be sure to test this thoroughly on whatever machines you might be deploying on.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-3449552877257185611?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/3449552877257185611/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/qtj-video-capture-in-swing-components.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3449552877257185611'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3449552877257185611'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/qtj-video-capture-in-swing-components.html' title='QTJ: Video Capture in Swing Components'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-8373055596033649111</id><published>2008-11-01T00:12:00.000-07:00</published><updated>2011-02-21T13:19:24.124-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='colorpicker'/><category scheme='http://www.blogger.com/atom/ns#' term='picker'/><category scheme='http://www.blogger.com/atom/ns#' term='jcolorchooser'/><category scheme='http://www.blogger.com/atom/ns#' term='dialog'/><category scheme='http://www.blogger.com/atom/ns#' term='color chooser'/><title type='text'>Colors: a Color Dialog</title><content type='html'>Don't get me wrong: I'm glad the good folks at Sun included the JColorChooser.  And I've definitely used it from time to time.&lt;br /&gt;&lt;P&gt;But recently I sat down and designed a nice, Photoshop-style color chooser.  Here it is in applet form:&lt;br /&gt;&lt;APPLET CODE="com/bric/swing/ColorPickerDemo" CODEBASE="http://javagraphics.java.net/jars/" width=526 height=384&gt;     &lt;PARAM NAME="archive" VALUE="ColorPicker.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;It's hard to say after years of working with a computer if this is "intuitive", since I'm definitely biased in this area... but I think the color wheel really appeals to a lot of people, and they know where to go to find the color they need.&lt;br /&gt;&lt;P&gt;The whole dialog is very responsive, keyboard accessible, localizable, etc.  You can invoke it in 1 static line:&lt;br /&gt;&lt;P&gt;&lt;PRE&gt;Color newColor = ColorPicker.showDialog(myFrame, originalColor);&lt;/PRE&gt;&lt;br /&gt;&lt;br /&gt;&lt;P&gt;... or!  You can customize the panel, and put it in a GUI without a dialog.  Here are some screenshots of miniature versions:&lt;br /&gt;&lt;P&gt;&lt;IMG SRC="http://web.me.com/bricolage1/blog_resources/colorpicker2.jpg"&gt;&lt;br /&gt;&lt;br /&gt;&lt;P&gt;The demo app (source code included) is available &lt;A HREF="http://javagraphics.java.net/jars/ColorPicker.jar"&gt;here&lt;/A&gt;.  (This project was originally packaged as part of Tim Boudreau's handy &lt;code&gt;&lt;a href="http://colorchooser.java.net/"&gt;ColorChooser&lt;/code&gt;&lt;/a&gt; project, but the latest copy of this project is now hosted with &lt;a href="http://javagraphics.java.net"&gt;the rest of my blog's code&lt;/A&gt;.)&lt;br /&gt;&lt;P&gt;Also, as a footnote: any discussion of color choosing should point out the &lt;A HREF="http://www.randelshofer.ch/quaqua/download.html"&gt;Quaqua&lt;/A&gt; project includes a color chooser that emulates the Mac look and feel.&lt;br /&gt;&lt;P&gt;Personally I'd recommend also including some sort &lt;a href="http://javagraphics.java.net/doc/com/bric/swing/ColorWell.html"&gt;color well&lt;/a&gt; in your UI.  Dialogs are pretty clunky and really interrupt the flow of your UI and user experience.  Tim's project mentioned above is a good start.  I have &lt;a href="http://javagraphics.blogspot.com/2010/01/colors-good-gui-for-selecting-colors.html"&gt;another article&lt;/a&gt; that talks about the subject of color choosing widgets more broadly.&lt;br /&gt;&lt;P&gt;P.S. Like most of my more popular blog entries: this has undergone several revisions.  As a result some of the comments below may not be written about the most current version.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-8373055596033649111?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/8373055596033649111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/jcolorchooser-making-alternative.html#comment-form' title='23 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8373055596033649111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8373055596033649111'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/jcolorchooser-making-alternative.html' title='Colors: a Color Dialog'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>23</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-7464557247518134782</id><published>2008-11-01T00:11:00.000-07:00</published><updated>2011-02-21T13:20:09.672-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Area'/><category scheme='http://www.blogger.com/atom/ns#' term='clipping'/><category scheme='http://www.blogger.com/atom/ns#' term='Graphics2D'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='rectangle'/><category scheme='http://www.blogger.com/atom/ns#' term='GeneralPath'/><title type='text'>Shapes: Clipping to a Rectangle</title><content type='html'>So recently I found myself wanting to clip an arbitrary &lt;code&gt;java.awt.Shape&lt;/code&gt; to a &lt;code&gt;Rectangle2D&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;When I say "clip" I don't mean I wanted to render a shape on a &lt;code&gt;Graphics2D&lt;/code&gt; &lt;i&gt;through&lt;/i&gt; a clipping.  That would be pretty easy: &lt;blockquote&gt;&lt;code&gt;Graphics2D g = ...&lt;br /&gt;g.setClip(new Rectangle2D.Float(x,y,w,h));&lt;br /&gt;g.fill(myArbitraryShape);&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;That's not what I want to do. I want to actually create a &lt;code&gt;GeneralPath&lt;/code&gt; that represents an arbitrary &lt;code&gt;java.awt.Shape&lt;/code&gt; that has been clipped to a &lt;code&gt;Rectangle2D&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;So I did the only thing I knew how to do:&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;Area area = new Area(myArbitraryShape);&lt;br /&gt;Rectangle2D rect = new Rectangle2D.Float(x,y,w,h);&lt;br /&gt;area.intersect(new Area(rect));&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;And this worked perfectly. But using a &lt;code&gt;java.awt.geom.Area&lt;/code&gt; is expensive. The &lt;code&gt;Area&lt;/code&gt; class is very reliable and accurate... but I'm effectively calling in the National Guard when really all I need is an off-duty policeman.&lt;br /&gt;&lt;P&gt;As I created paths with more and more segments, this is the kind of performance the &lt;code&gt;Area&lt;/code&gt; class was giving me:&lt;br /&gt;&lt;BR&gt;&lt;img src="http://javagraphics.java.net/resources/clipper_results.jpg"&gt;&lt;br /&gt;&lt;P&gt;When I asked for paths with 35 segments or more: I received &lt;code&gt;OutOfMemoryErrors&lt;/code&gt;.&lt;br /&gt;&lt;P&gt;So this weekend I set off in search of a new alternative.  I ended up creating the &lt;a href="http://javagraphics.java.net/jars/Clipper.jar"&gt;&lt;code&gt;Clipper&lt;/code&gt;&lt;/a&gt; class.  It only contains a few public static methods to help clip a shape to a rectangle.&lt;br /&gt;&lt;P&gt;It returns nearly immediately after crunching through your shape and calculating -- curves and all -- a new &lt;code&gt;GeneralPath&lt;/code&gt; that lies exactly inside the rectangle you specified.&lt;br /&gt;&lt;P&gt;Here is a demo applet:&lt;br /&gt;&lt;APPLET CODE="com/bric/geom/ClipperDemo" CODEBASE="http://javagraphics.java.net/jars/" width=360 height=360&gt;     &lt;PARAM NAME="archive" VALUE="Clipper.jar"&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;(If you download the jar and run it as an app it will also run a few numbers (time and memory allocation) and output that to the console.)&lt;br /&gt;&lt;h3&gt;How It Works&lt;/h3&gt;&lt;br /&gt;It is uses an algorithm that is different from both the &lt;a href="http://en.wikipedia.org/wiki/Sutherland-Hodgman_clipping_algorithm"&gt;Sutherland-Hodgman clipping algorithm&lt;/a&gt; and the &lt;a href="http://en.wikipedia.org/wiki/Weiler-Atherton_clipping_algorithm"&gt;Weiler-Atherton clipping algorithm&lt;/a&gt;.&lt;br /&gt;&lt;P&gt;The &lt;code&gt;PathIterator&lt;/code&gt; from the source shape is traced exactly once, but as it is traced it is filtered.  If a curve, or a segment of a curve, lies outside the clipping rectangle: it is flattened against the rectangle:&lt;br /&gt;&lt;BR&gt;&lt;img src="http://javagraphics.java.net/resources/clipping.gif"&gt;&lt;br /&gt;&lt;P&gt;This gets slightly tricky for more complicated shapes, but as long as you trace back-and-forth over the same vertical/horizontal lines: the result is the same:&lt;br /&gt;&lt;BR&gt;&lt;img src="http://javagraphics.java.net/resources/clipping2.gif"&gt;&lt;br /&gt;&lt;P&gt;The &lt;code&gt;Clipper&lt;/code&gt; class always eliminates redundant line segments with a width or height of zero.&lt;br /&gt;&lt;P&gt;On my giant to-do list of things-that-I-may-never-make-the-time-for, I'd like to compare this against the previous algorithms mentioned... but so far I haven't had the chance.  It seems like this should have an advantage order-wise, because it doesn't have to cache anything, or re-traverse the clipping rectangle or the input shape.  In short: it was designed to work with a &lt;code&gt;PathIterator&lt;/code&gt; in one pass.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-7464557247518134782?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/7464557247518134782/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/shapes-clipping-to-rectangle.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7464557247518134782'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7464557247518134782'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/shapes-clipping-to-rectangle.html' title='Shapes: Clipping to a Rectangle'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-7479310984722791569</id><published>2008-11-01T00:10:00.000-07:00</published><updated>2008-11-16T16:41:34.699-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='power'/><category scheme='http://www.blogger.com/atom/ns#' term='quadratic'/><category scheme='http://www.blogger.com/atom/ns#' term='degree'/><category scheme='http://www.blogger.com/atom/ns#' term='cubic'/><category scheme='http://www.blogger.com/atom/ns#' term='curve'/><category scheme='http://www.blogger.com/atom/ns#' term='flattening'/><category scheme='http://www.blogger.com/atom/ns#' term='Shapes'/><category scheme='http://www.blogger.com/atom/ns#' term='bezier'/><title type='text'>Shapes: flattened cubic curves</title><content type='html'>&lt;span style="font-family:verdana;"&gt;Do you have a curve that uses cubic curves but an a&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;lgorithm that only handles quadratic curves?&lt;/span&gt;&lt;br /&gt;&lt;BR&gt;&lt;p style="font-family: verdana;"&gt;(OK, I admit that intro felt a little bit like an infomercial... but what can you do?)&lt;br /&gt;&lt;/p&gt;&lt;BR&gt;&lt;p style="font-family: verdana;"&gt;I developed the &lt;a style="font-family: courier new;" href="http://homepage.mac.com/bricolage1/samples/QuadraticFlattenedPathIterator.java"&gt;QuadraticFlattenedPathIterator.java&lt;/a&gt; to flatten a &lt;span style="font-family:courier new;"&gt;PathIterator&lt;/span&gt; with cubic data into a path with only quadratic data.  The implementation is very simple: we calculate all the critical points of the cubic curve, and then based on the approximate length of those segments we divide that curve into a few smaller quadratic curves.&lt;/p&gt;&lt;BR&gt;&lt;p style="font-family: verdana;"&gt;I made a &lt;a href="http://homepage.mac.com/bricolage1/samples/QFPITests.java"&gt;test application&lt;/a&gt; to demonstrate the new iterator.  You can closely study the approximations with the real curves to see that the curves are pretty darn close.  Antialiasing is really the biggest difference in the two.  My naked eye can hardly tell any difference.  (I had to open up a screen magnifier app to really see the antialiasing change in certain cases.)&lt;br /&gt;&lt;/p&gt;&lt;BR&gt;&lt;p style="font-family: verdana;"&gt;This is just a stepping stone to &lt;a href="http://javagraphics.blogspot.com/2007/04/shapes-studying-performance.html"&gt;bigger and better&lt;/a&gt; projects.  :)  More to come in coming weeks.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-7479310984722791569?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/7479310984722791569/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/06/shapes-flattened-cubic-curves.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7479310984722791569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7479310984722791569'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/06/shapes-flattened-cubic-curves.html' title='Shapes: flattened cubic curves'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-3983443037642120702</id><published>2008-11-01T00:09:00.000-07:00</published><updated>2011-02-21T13:20:48.243-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='bounds'/><category scheme='http://www.blogger.com/atom/ns#' term='Area'/><category scheme='http://www.blogger.com/atom/ns#' term='Shapes'/><category scheme='http://www.blogger.com/atom/ns#' term='GeneralPath'/><title type='text'>Shapes: calculating bounds</title><content type='html'>Sometimes I want to know the exact bounds of a &lt;code&gt;&lt;a href="http://java.sun.com/javase/6/docs/api/java/awt/Shape.html"&gt;java.awt.Shape&lt;/a&gt;&lt;/code&gt;.  What's an efficient way to calculate this?&lt;br /&gt;&lt;p&gt;Well that depends.  There is the &lt;code&gt;Shape.getBounds()&lt;/code&gt; method, to start with.  This works most of the time.  But it comes with a catch.  According to the API:&lt;br /&gt;&lt;br&gt;&lt;blockquote&gt;"Note that there is no guarantee that the returned &lt;code"&gt;Rectangle2D&lt;/code&gt; is the smallest bounding box that encloses the &lt;code&gt;Shape&lt;/code&gt;, only that the &lt;code&gt;Shape&lt;/code&gt; lies entirely within the indicated &lt;code&gt;Rectangle2D&lt;/code&gt;."&lt;/code&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br&gt;That basically said the &lt;code&gt;Rectangle2D&lt;/code&gt; returned by &lt;code&gt;getBounds()&lt;/code&gt; may be too large.  It will never be smaller than your &lt;code&gt;Shape&lt;/code&gt;, but it may be larger.&lt;br /&gt;&lt;p&gt;So next I turned to the &lt;code&gt;&lt;a href="http://java.sun.com/javase/6/docs/api/java/awt/geom/Area.html"&gt;java.awt.geom.Area&lt;/a&gt;&lt;/code&gt; class.  An &lt;code&gt;Area&lt;/code&gt; object is guaranteed to have an accurate calculation for &lt;code&gt;getBounds()&lt;/code&gt;.&lt;br /&gt;&lt;p&gt;But the catch is the &lt;code&gt;Area&lt;/code&gt; class is notoriously slow.  I would recommend only using it as a last resort, or for extremely simple shapes.&lt;br /&gt;&lt;p&gt;So a &lt;code&gt;GeneralPath&lt;/code&gt; may not return accurate bounds.  And an &lt;code&gt;Area&lt;/code&gt; may not calculate bounds efficiently.  So let's come up with another solution...&lt;br /&gt;&lt;h3&gt;How to Calculate a Shape's Bounds&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;A shape is just a series of parametric equations.  (&lt;a href="http://javagraphics.blogspot.com/2010/03/shapes-introduction.html"&gt;Here&lt;/a&gt; is a refresher on the subject.)&lt;br /&gt;&lt;p&gt;If you want to get the bounds of a shape, you can just iterate over its segments and add together the bounds of each segment.  Here is pseudo-code for this process:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;public static Rectangle2D getBounds() {&lt;br /&gt; Rectangle2D bounds;&lt;br /&gt; float[] f = new float[6];&lt;br /&gt; &lt;br /&gt; float lastX = 0;&lt;br /&gt; float lastY = 0;&lt;br /&gt;&lt;br /&gt; float a, b, c, d;&lt;br /&gt;&lt;br /&gt; float t, x, y, det;&lt;br /&gt; while(i.isDone()==false) {&lt;br /&gt;  int k = i.currentSegment(f);&lt;br /&gt;  if(k==PathIterator.SEG_MOVETO) {&lt;br /&gt;   bounds.add(f[0], f[1]);&lt;br /&gt;   lastX = f[0];&lt;br /&gt;   lastY = f[1];&lt;br /&gt;  } else if(k==PathIterator.SEG_CLOSE) {&lt;br /&gt;   //as long as the last segment and the moveTo were&lt;br /&gt;   //recorded: we're fine.&lt;br /&gt;  } else if(k==PathIterator.SEG_LINETO) {&lt;br /&gt;   //add the end point:&lt;br /&gt;   bounds.add(f[0], f[1]);&lt;br /&gt;&lt;br /&gt;   lastX = f[0];&lt;br /&gt;   lastY = f[1];&lt;br /&gt;  } else if(k==PathIterator.SEG_QUADTO) {&lt;br /&gt;   //add the end point:&lt;br /&gt;   bounds.add(f[2], f[3]);&lt;br /&gt;&lt;br /&gt;   //this curve might have extrema:&lt;br /&gt;   a = lastX-2*f[0]+f[2];&lt;br /&gt;   b = -2*lastX+2*f[0];&lt;br /&gt;   c = lastX;&lt;br /&gt;&lt;br /&gt;   t = -b/(2*a);&lt;br /&gt;   if(t&gt;0 &amp;&amp; t&lt;1) {&lt;br /&gt;    bounds.addX(a*t*t+b*t+c);&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   a = lastY-2*f[1]+f[3];&lt;br /&gt;   b = -2*lastY+2*f[1];&lt;br /&gt;   c = lastY;&lt;br /&gt;   t = -b/(2*a);&lt;br /&gt;   if(t&gt;0 &amp;&amp; t&lt;1) {&lt;br /&gt;    bounds.addY(a*t*t+b*t+c);&lt;br /&gt;   }&lt;br /&gt;   lastX = f[2];&lt;br /&gt;   lastY = f[3];&lt;br /&gt;  } else if(k==PathIterator.SEG_CUBICTO) {&lt;br /&gt;   //add the end point:&lt;br /&gt;   bounds.add(f[4], f[5]);&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;   //this curve might have extrema:&lt;br /&gt;&lt;br /&gt;   //f = a*t*t*t+b*t*t+c*t+d&lt;br /&gt;   //df/dt = 3*a*t*t+2*b*t+c&lt;br /&gt;   &lt;br /&gt;   //A = 3*a, B = 2*b, C = c&lt;br /&gt;   //t = [-B+-sqrt(B^2-4*A*C)]/(2A)&lt;br /&gt;   //t = (-2*b+-sqrt(4*b*b-12*a*c)]/(6*a)&lt;br /&gt;&lt;br /&gt;   a = -lastX+3*f[0]-3*f[2]+f[4];&lt;br /&gt;   b = 3*lastX-6*f[0]+3*f[2];&lt;br /&gt;   c = -3*lastX+3*f[0];&lt;br /&gt;   d = lastX;&lt;br /&gt;&lt;br /&gt;   det = (4*b*b-12*a*c);&lt;br /&gt;   if(det&lt;0) {&lt;br /&gt;    //there are no solutions!  nothing to do here&lt;br /&gt;   } else if(det==0) {&lt;br /&gt;    //there is 1 solution&lt;br /&gt;    t = -2*b/(6*a);&lt;br /&gt;    if(t&gt;0 &amp;&amp; t&lt;1) {&lt;br /&gt;     x = a*t*t*t+b*t*t+c*t+d;&lt;br /&gt;     bounds.addX(x);&lt;br /&gt;    }&lt;br /&gt;   } else {&lt;br /&gt;    //there are 2 solutions:&lt;br /&gt;    det = (float)Math.sqrt(det);&lt;br /&gt;    t = (-2*b+det)/(6*a);&lt;br /&gt;    if(t&gt;0 &amp;&amp; t&lt;1) {&lt;br /&gt;     x = a*t*t*t+b*t*t+c*t+d;&lt;br /&gt;     bounds.addX(x);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    t = (-2*b-det)/(6*a);&lt;br /&gt;    if(t&gt;0 &amp;&amp; t&lt;1) {&lt;br /&gt;     x = a*t*t*t+b*t*t+c*t+d;&lt;br /&gt;     bounds.addX(x);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   //do the same thing for y:&lt;br /&gt;&lt;br /&gt;   a = -lastY+3*f[1]-3*f[3]+f[5];&lt;br /&gt;   b = 3*lastY-6*f[1]+3*f[3];&lt;br /&gt;   c = -3*lastY+3*f[1];&lt;br /&gt;   d = lastY;&lt;br /&gt;   &lt;br /&gt;   det = (4*b*b-12*a*c);&lt;br /&gt;   if(det&lt;0) {&lt;br /&gt;    //there are no solutions!  nothing to do here&lt;br /&gt;   } else if(det==0) {&lt;br /&gt;    //there is 1 solution&lt;br /&gt;    t = -2*b/(6*a);&lt;br /&gt;    if(t&gt;0 &amp;&amp; t&lt;1) {&lt;br /&gt;     y = a*t*t*t+b*t*t+c*t+d;&lt;br /&gt;     bounds.addY(y);&lt;br /&gt;    }&lt;br /&gt;   } else {&lt;br /&gt;    //there are 2 solutions:&lt;br /&gt;    det = (float)Math.sqrt(det);&lt;br /&gt;    t = (-2*b+det)/(6*a);&lt;br /&gt;    if(t&gt;0 &amp;&amp; t&lt;1) {&lt;br /&gt;     y = a*t*t*t+b*t*t+c*t+d;&lt;br /&gt;     bounds.addY(y);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    t = (-2*b-det)/(6*a);&lt;br /&gt;    if(t&gt;0 &amp;&amp; t&lt;1) {&lt;br /&gt;     y = a*t*t*t+b*t*t+c*t+d;&lt;br /&gt;     bounds.addY(y);&lt;br /&gt;    }&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   lastX = f[4];&lt;br /&gt;   lastY = f[5];&lt;br /&gt;  }&lt;br /&gt;  i.next();&lt;br /&gt; }&lt;br /&gt; &lt;br /&gt; return bounds;&lt;br /&gt;}&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;P&gt;That's all.  This is all nicely wrapped up in static methods in &lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/geom/ShapeBounds.java?view=markup"&gt;this class&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;Also I put together a &lt;a href="http://javagraphics.java.net/jars/ShapeBoundsDemo.jar"&gt;jar&lt;/a&gt; that compares this class against the &lt;code&gt;Area&lt;/code&gt;:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;time for a 5-curve using ShapeBounds.getBounds(): 0 ms&lt;br /&gt;time for a 5-curve using an Area: 2 ms&lt;br /&gt;time for a 15-curve using ShapeBounds.getBounds(): 0 ms&lt;br /&gt;time for a 15-curve using an Area: 16 ms&lt;br /&gt;time for a 25-curve using ShapeBounds.getBounds(): 0 ms&lt;br /&gt;time for a 25-curve using an Area: 66 ms&lt;br /&gt;time for a 35-curve using ShapeBounds.getBounds(): 0 ms&lt;br /&gt;time for a 35-curve using an Area: 153 ms&lt;br /&gt;time for a 45-curve using ShapeBounds.getBounds(): 0 ms&lt;br /&gt;time for a 45-curve using an Area: 297 ms&lt;br /&gt;time for a 55-curve using ShapeBounds.getBounds(): 0 ms&lt;br /&gt;time for a 55-curve using an Area: 530 ms&lt;br /&gt;time for a 65-curve using ShapeBounds.getBounds(): 0 ms&lt;br /&gt;time for a 65-curve using an Area: 833 ms&lt;br /&gt;&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;This is what I meant by "notoriously" slow.  Now there might be other unavoidable reasons why you need an &lt;code&gt;Area&lt;/code&gt;, and that's fine: but don't resort to an &lt;code&gt;Area&lt;/code&gt; just to get accurate shape bounds.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-3983443037642120702?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/3983443037642120702/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/05/shapes-calculating-bounds.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3983443037642120702'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/3983443037642120702'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/05/shapes-calculating-bounds.html' title='Shapes: calculating bounds'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-8288915376832336059</id><published>2008-11-01T00:08:00.000-07:00</published><updated>2011-02-21T13:21:05.711-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='PixelGrabber'/><category scheme='http://www.blogger.com/atom/ns#' term='image'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='BufferedImage'/><title type='text'>PixelGrabber: Studying Performance</title><content type='html'>&lt;span style="font-family:verdana;"&gt;&lt;span style="font-family:verdana;"&gt;I think the first time I started looking at the &lt;a style="font-family: courier new;" href="http://java.sun.com/javase/6/docs/api/java/awt/image/PixelGrabber.html"&gt;java.awt.image.PixelGrabber&lt;/a&gt;&lt;/span&gt; class was when I started pulling apart an image encoder utility.&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;&lt;p&gt;It's a nice idea: let's take any abstract &lt;span style="font-family:courier new;"&gt;java.awt.Image&lt;/span&gt; and convert it to RGB pixels.  Sounds very useful.&lt;/p&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;p&gt;But it's too abstract.  (Yes, I said it: things in Java can easily become too abstract.)  It's great that the &lt;span style="font-family:courier new;"&gt;PixelGrabber&lt;/span&gt; works on any image under the sun, but 95% of the images I work with anymore are &lt;span style="font-family:courier new;"&gt;BufferedImages&lt;/span&gt;.  So why not take that into account when we grab the pixels?&lt;/p&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;p&gt;Also the &lt;span style="font-family:courier new;"&gt;PixelGrabber&lt;/span&gt; adds an &lt;span style="font-family:courier new;"&gt;ImageConsumer&lt;/span&gt; to my &lt;span style="font-family:courier new;"&gt;BufferedImage&lt;/span&gt;.  It says so plainly in the javadocs.  Already mental sirens should start going off: this is not a good idea.   I don't want to be that guy who quotes himself, so I'll just &lt;a href="http://javagraphics.blogspot.com/2007/04/managed-images.html"&gt;link to myself&lt;/a&gt; instead.  ;)  The point is: &lt;span style="font-weight: bold;"&gt;never &lt;/span&gt;add an &lt;span style="font-family:courier new;"&gt;ImageConsumer&lt;/span&gt; to a &lt;span style="font-family:courier new;"&gt;BufferedImage&lt;/span&gt;.  (Unless you know what you're doing, that is.)&lt;br /&gt;&lt;/p&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;p&gt;Suppose instead we create a &lt;span style="font-family:courier new;"&gt;BufferedImage&lt;/span&gt; of type &lt;span style="font-family:courier new;"&gt;TYPE_INT_ARGB&lt;/span&gt; and call the following:&lt;/p&gt;&lt;/span&gt;&lt;blockquote style="font-family: courier new;"&gt;pixels = bi.getRaster().getDataElements(0,0,w,h,null);&lt;/blockquote&gt;&lt;span style="font-family:verdana;"&gt;&lt;p&gt;This gives you all the pixels and doesn't unmanage your image.  And in my limited testing it achieves exactly the same performance as the &lt;span style="font-family:courier new;"&gt;PixelGrabber&lt;/span&gt; class.&lt;/p&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;separator&gt;&lt;br /&gt;&lt;/separator&gt;&lt;p&gt;But I think there's still room for improvement, depending on what you're trying to achieve:&lt;/p&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;p&gt;In my case, I was trying to encode an image 1 row of pixels at a time.  So why not try extracting 1 row of pixels at a time?  It's not the most revolutionary idea, but it turns out it made a big performance improvement.  And it makes sense, too: if I have an 800x600 image (which is ridiculously modest in the age of digital cameras), then I know somewhere I already have 480,000 integers in memory.  If I want to write that out to a file, do I really need to create another array of 480,000 integers?  Doesn't that sound expensive?&lt;/p&gt;&lt;/span&gt;&lt;span style="font-family:verdana;"&gt;&lt;p&gt;So I made a class called the &lt;a style="font-family: courier new;" href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/image/ARGBPixelGrabber.java?view=markup"&gt;ARGBPixelGrabber&lt;/a&gt; (or &lt;a href="http://javagraphics.java.net/jars/ARGBPixelGrabber.jar"&gt;here&lt;/a&gt; it is as a jar).  It is optimized to deal with 9 types of &lt;span style="font-family:courier new;"&gt;BufferedImages&lt;/span&gt;:&lt;/p&gt;&lt;/span&gt;&lt;/span&gt;&lt;ul style="font-family: courier new;"&gt;&lt;li&gt;TYPE_INT_RGB&lt;/li&gt;&lt;li&gt;TYPE_INT_ARGB&lt;/li&gt;&lt;li&gt;TYPE_INT_ARGB_PRE&lt;/li&gt;&lt;li&gt;TYPE_INT_BGR&lt;/li&gt;&lt;li&gt;TYPE_3BYTE_BGR&lt;/li&gt;&lt;li&gt;TYPE_4BYTE_ABGR&lt;/li&gt;&lt;li&gt;TYPE_4BYTE_ABGR_PRE&lt;/li&gt;&lt;li&gt;TYPE_BYTE_GRAY&lt;/li&gt;&lt;li&gt;TYPE_BYTE_INDEXED&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-family:verdana;"&gt;This class includes a &lt;span style="font-family:courier new;"&gt;main()&lt;/span&gt; method and a little unit test to clock the median performance of a few different methods.  The output on my Mac OS 10.4.9 machine is:&lt;/span&gt;&lt;br /&gt;&lt;blockquote style="font-family: courier new;"&gt;PixelGrabber.grabPixels: 17 ms&lt;br /&gt;ARGBPixelGrabber.grabPixels: 9 ms&lt;br /&gt;ARGBPixelGrabber.next: 4 ms&lt;/blockquote&gt;&lt;span style="font-family:verdana;"&gt;Not bad, eh?  In the first case an ordinary &lt;span style="font-family:courier new;"&gt;PixelGrabber&lt;/span&gt; is used to fetch the contents of the image data.  Not only is this the slowest approach, but it will also unmanage my &lt;span style="font-family:courier new;"&gt;BufferedImage&lt;/span&gt;.&lt;br /&gt;&lt;p&gt;In the second case I use the &lt;span style="font-family:courier new;"&gt;ARGBPixelGrabber&lt;/span&gt; and recycle the same destination array repeatedly.  That is the key: if I ask for a new array every time I'll see the same performance as the &lt;span style="font-family:courier new;"&gt;PixelGrabber&lt;/span&gt; class.  (Now you may point out I didn't test the &lt;span style="font-family:courier new;"&gt;PixelGrabber&lt;/span&gt; constructor that accepts an int array.  You are correct.  That probably would have helped.  But I didn't want to search my memory for what "scansize" was.  Besides, that's quibbling over a minor point when you look at the 3rd case.)&lt;br /&gt;&lt;/p&gt;&lt;p&gt;In the third case I ask for each row separately by calling next().  Now I can blaze through my image in 4 milliseconds.  So this is definitely the choice for me.&lt;br /&gt;&lt;separator&gt;&lt;br /&gt;&lt;span style="font-family:verdana;"&gt;&lt;p&gt;All that being said: your needs may be different than mine.  &lt;/p&gt;&lt;/span&gt;But the point is: don't be afraid to roll up your sleeves and reach inside your images.  You may be surprised at what you can do.  Hopefully the &lt;code&gt;ARGBPixelGrabber&lt;/code&gt; can be either useful or educational for someone reading this.&lt;br /&gt;&lt;/separator&gt;&lt;/p&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-8288915376832336059?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/8288915376832336059/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/pixelgrabber-studying-performance.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8288915376832336059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8288915376832336059'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/pixelgrabber-studying-performance.html' title='PixelGrabber: Studying Performance'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-8984657142571519447</id><published>2008-11-01T00:07:00.000-07:00</published><updated>2011-09-05T14:58:36.083-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='image'/><category scheme='http://www.blogger.com/atom/ns#' term='QTJ'/><category scheme='http://www.blogger.com/atom/ns#' term='PixMap'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='decode'/><category scheme='http://www.blogger.com/atom/ns#' term='decoder'/><category scheme='http://www.blogger.com/atom/ns#' term='BufferedImage'/><category scheme='http://www.blogger.com/atom/ns#' term='QuickTime'/><category scheme='http://www.blogger.com/atom/ns#' term='QDGraphics'/><title type='text'>QTJ: from QDGraphics to BufferedImage</title><content type='html'>&lt;h3&gt;Editor's Note:&lt;/h3&gt;&lt;b&gt;&lt;br /&gt;QuickTime for Java has been deprecated for years now, and so this article is deprecated along with it. I have removed the source code mentioned in this article from my &lt;a href="http://javagraphics.java.net/"&gt;source code repository&lt;/a&gt;. However the link to the jar in this article now to refer to an older SVN revision that should continue to function indefinitely if you feel nostalgic and want to review this project.&lt;/b&gt;&lt;br /&gt;&lt;hr /&gt;&lt;span style="font-family: verdana;"&gt;In at least a couple of applications now I've needed to make a really streamlined mechanism to pipe QuickTime images into &lt;span style="font-family: courier new;"&gt;BufferedImages&lt;/span&gt;.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: verdana;"&gt;&lt;br /&gt;&lt;br /&gt;The &lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/qt/QTJImage.java?&amp;amp;view=markup" style="font-family: courier new;"&gt;QTJImage&lt;/a&gt; reaches inside &lt;span style="font-family: courier new;"&gt;PixMaps&lt;/span&gt; and actually converts data into an ARGB &lt;span style="font-family: courier new;"&gt;BufferedImage&lt;/span&gt;.  I don't have exact numbers handy, but when it was being developed I remember it offered both the best performance and the minimum memory allocation.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-family: verdana;"&gt;Here I offer it for public use/scrutiny.  It's served me pretty well for the last few years.  If someone wants to run some benchmarks on it: that would be great.&lt;br /&gt;&lt;br /&gt;As always: I'd love to hear ideas for improvement.  Or success stories.&lt;br /&gt;&lt;br /&gt;Also, it's worth pointing out Apple has a &lt;a href="http://lists.apple.com/mailman/listinfo/quicktime-java"&gt;mailing list&lt;/a&gt; specifically for QuickTime for Java related issues.  It doesn't get that much love these days.  But some intelligent people are on there, and every-so-often it is graced by an Apple employee, too.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: verdana;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: verdana;"&gt;Footnote: for anyone interested in a stand-alone demo of this, I just added the &lt;a href="http://java.net/projects/javagraphics/sources/svn/content/trunk/src/com/bric/qt/QTAnimationReader.java?rev=1339"&gt;QTAnimationReader&lt;/a&gt; to the server.  This uses the &lt;code&gt;QTJImage&lt;/code&gt; to iterate over an entire movie.  &lt;a href="http://java.net/projects/javagraphics/sources/svn/content/trunk/www/jars/QTAnimationReader.jar?rev=1339"&gt;Here&lt;/a&gt; is a jar that demos this class.&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: verdana;"&gt;Extra footnote: In 2009 Apple is scheduled to officially deprecate QuickTime for Java when QuickTime X is released with Mac OS 10.6.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-8984657142571519447?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/8984657142571519447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/qtj-from-qdgraphics-to-bufferedimage.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8984657142571519447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/8984657142571519447'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/qtj-from-qdgraphics-to-bufferedimage.html' title='QTJ: from QDGraphics to BufferedImage'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-2171505732580302856</id><published>2008-11-01T00:05:00.000-07:00</published><updated>2011-02-21T13:21:40.157-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='managed'/><category scheme='http://www.blogger.com/atom/ns#' term='unmanaged'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='BufferedImage'/><title type='text'>Images: Managed Images</title><content type='html'>You'd think the friendly folks at Sun might mention this... but it's not really talked about it.  In fact, it's so not talked about that I still don't have a firm understanding of what it is.  Once I googled it and found a nifty little diagram explaining the idea, but I've never seen that diagram again.&lt;br /&gt;&lt;p&gt;Here's my understanding of the concept:&lt;br /&gt;&lt;p&gt;&lt;blockquote&gt;&lt;b&gt;managed image&lt;/b&gt;: this is a &lt;code&gt;BufferedImage&lt;/code&gt; that is optimized to run efficiently in memory.&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;Of course by default your images are managed.  But there are 2 seemingly innocent ways to unmanage them.  Once an image becomes unmanaged: there is no way to bring it back to its managed state.  They are blacklisted.  They are on the terrorist watchlist of your JVM.  Rendering to an unmanaged image can be a factor of over 100x slower than rendering to a managed image.&lt;br /&gt;&lt;p&gt;The two ways I know to unmanage an image are:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Access the raster's &lt;code&gt;DataBuffer&lt;/code&gt;.  For example:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;myBufferedImage.getRaster().getDataBuffer();&lt;/pre&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Add an &lt;code&gt;ImageConsumer&lt;/code&gt; to a &lt;code&gt;BufferedImage's&lt;/code&gt; &lt;code&gt;ImageProducer&lt;/code&gt;.  For example:&lt;blockquote&gt;&lt;pre&gt;myBufferedImage.getSource().addImageConsumer(c);&lt;/pre&gt;&lt;/blockquote&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;Now that I've scared you into listening, here's the great news: this problem has been &lt;i&gt;mostly&lt;/i&gt; taken care of in recent years.  Based on my own experience and the comments in this article: this only appears to be a problem if you're using a Mac with Quartz rendering.&lt;br /&gt;&lt;p&gt;To study this further yourself, I recommend running &lt;a href="http://javagraphics.java.net/source/browse/javagraphics/trunk/src/com/bric/test/ManagedImageTest.java?rev=750&amp;view=markup"&gt;this sample program&lt;/a&gt;.&lt;br /&gt;&lt;p&gt;Here is sample output for that program from a few years ago, running Mac 10.4.9 PPC and Java 1.4:&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;Regular Test Time (1): 6&lt;br /&gt;Unmanaged Test Time (1): 11494&lt;br /&gt;Regular Test Time (2): 5&lt;br /&gt;Unmanaged Test Time (2): 11384&lt;br /&gt;Unmanaged Test time (2): 10129&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;So regardless of how I unmanaged my image, the results are about the same: my performance change to about 2000 times slower than it should be.&lt;br /&gt;&lt;p&gt;Also note that if you remove the &lt;code&gt;ImageConsumer&lt;/code&gt; it doesn't matter: the damage has already been done.&lt;br /&gt;&lt;p&gt;In a way this makes sense: if I have the &lt;code&gt;DataBuffer&lt;/code&gt; in hand, then I may be hijacking the image contents.  So Java has to constantly check to make sure that the &lt;code&gt;DataBuffer&lt;/code&gt; and the &lt;code&gt;Graphics2D&lt;/code&gt; match up.  Also if I have an &lt;code&gt;ImageConsumer&lt;/code&gt; listening to the &lt;code&gt;ImageProducer&lt;/code&gt;: this adds a level of complexity to how the image data is manipulated.&lt;br /&gt;&lt;p&gt;Here are tips for avoiding any problems.  Even though the threat is largely passed, I still follow these practices out of habit/safety:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;If you never need to interact with the &lt;code&gt;Graphics2D&lt;/code&gt; of an image, then you shouldn't see a performance problem.  That is: suppose you just wanted to define an image pixel-by-pixel, but you never wanted to use a &lt;code"&gt;Graphics2D&lt;/code&gt; to manipulate that image.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Suppose you do want to control the pixels in an image?  Not a problem: use &lt;code&gt;bi.getRaster().getDataElements()&lt;/code&gt; and &lt;code&gt;bi.getRaster().setDataElements()&lt;/code&gt;.  Just don't ask for the &lt;code&gt;DataBuffer&lt;/code&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;As a footnote, Jerry recently pointed out that calling &lt;code"&gt;myBufferedImage.setRGB()&lt;/code&gt; used to unmanage an image.  This does not appear to be the case in Java 1.4 or later.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-2171505732580302856?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/2171505732580302856/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/managed-images.html#comment-form' title='16 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2171505732580302856'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/2171505732580302856'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/managed-images.html' title='Images: Managed Images'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>16</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-6678147348595380060</id><published>2008-11-01T00:03:00.000-07:00</published><updated>2011-02-21T13:21:56.215-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='image'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='load'/><category scheme='http://www.blogger.com/atom/ns#' term='BufferedImage'/><category scheme='http://www.blogger.com/atom/ns#' term='MediaTracker'/><title type='text'>Images: studying MediaTracker</title><content type='html'>So I've been using the &lt;code&gt;MediaTracker&lt;/code&gt; for years now.  And it works.  Kinda sorta.  Like a trolley cart at the grocery store: it goes off and does its own thing sometimes, but it always get the job done.&lt;br /&gt;&lt;p&gt;I spent the last 24 hours working on a new class to load images.  I gave it the subtle name &lt;code&gt;&lt;a href="http://javagraphics.java.net/jars/ImageLoader.jar"&gt;ImageLoader&lt;/a&gt;&lt;/code&gt;.  (Source code included in the jar.)&lt;br /&gt;&lt;p&gt;Originally -- back when I first put this together -- the &lt;code&gt;ImageLoader&lt;/code&gt; would generally take 1% to 50% of the memory the &lt;code&gt;MediaTracker&lt;/code&gt; required for the same job.  Since then it looks like the &lt;code&gt;MediaTracker&lt;/code&gt; cleaned up its act: but this class still outperforms it.  (Just not by as much.)&lt;br /&gt;&lt;p&gt;Note this operates by adding an &lt;code&gt;ImageConsumer&lt;/code&gt; to an image, which can have the devastating effect of unmanaging an image (see &lt;a href="http://javagraphics.blogspot.com/2007/04/managed-images.html"&gt;this article&lt;/a&gt; for more information).  So if you intend to edit the original &lt;code&gt;Image&lt;/code&gt; in the future, using this class could be dangerous.  If, however, you never need to edit the original image; or if you plan on using the &lt;code&gt;ImageLoader&lt;/code&gt; just to convert an abstract &lt;code&gt;java.awt.Image&lt;/code&gt; into a &lt;code&gt;BufferedImaged&lt;/code&gt;: this class will do fine.&lt;br /&gt;&lt;p&gt;Here is some sample output from this program under two different configurations (reading the same image):&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;OS = Mac OS X (10.5.8), i386&lt;br /&gt;Java Version = 1.4.2_22&lt;br /&gt;apple.awt.graphics.UseQuartz = false&lt;br /&gt;ImageLoader Tests:&lt;br /&gt;File: /Users/bricolage1/Pictures/chinese-leopard_1382004i.jpg&lt;br /&gt;Dimensions: 560x380&lt;br /&gt; Creating a BufferedImage:&lt;br /&gt;MediaTracker took 30 ms, 2919272 bytes&lt;br /&gt;ImageLoader took 23 ms, 2023776 bytes (creating TYPE_INT_ARGB)&lt;br /&gt;(So ImageLoader took 76% of the time, and 69% of the memory)&lt;br /&gt;ImageIO took 125 ms, 829272 bytes (creating TYPE_3BYTE_BGR)&lt;br /&gt;(So ImageLoader took 18% of the time, and 244% of the memory)&lt;/pre&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;pre&gt;OS = Mac OS X (10.5.8), x86_64&lt;br /&gt;Java Version = 1.6.0_17&lt;br /&gt;apple.awt.graphics.UseQuartz = false&lt;br /&gt;ImageLoader Tests:&lt;br /&gt;File: /Users/bricolage1/Pictures/chinese-leopard_1382004i.jpg&lt;br /&gt;Dimensions: 560x380&lt;br /&gt; Creating a BufferedImage:&lt;br /&gt;MediaTracker took 17 ms, 3033536 bytes&lt;br /&gt;ImageLoader took 16 ms, 2171128 bytes (creating TYPE_INT_ARGB)&lt;br /&gt;(So ImageLoader took 94% of the time, and 71% of the memory)&lt;br /&gt;ImageIO took 84 ms, 7224784 bytes (creating TYPE_3BYTE_BGR)&lt;br /&gt;(So ImageLoader took 19% of the time, and 30% of the memory)&lt;/pre&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-6678147348595380060?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/6678147348595380060/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/images-studying-mediatracker.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6678147348595380060'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/6678147348595380060'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/images-studying-mediatracker.html' title='Images: studying MediaTracker'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-406997977734348535.post-7779815592452765139</id><published>2008-11-01T00:01:00.000-07:00</published><updated>2011-02-21T13:23:01.319-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='transitions'/><category scheme='http://www.blogger.com/atom/ns#' term='slideshow'/><category scheme='http://www.blogger.com/atom/ns#' term='graphics'/><category scheme='http://www.blogger.com/atom/ns#' term='BufferedImage'/><category scheme='http://www.blogger.com/atom/ns#' term='2D'/><category scheme='http://www.blogger.com/atom/ns#' term='effects'/><title type='text'>Slideshows: Transitions &amp; SWFs</title><content type='html'>[This article replaces the now deprecated project at &lt;a href="http://transitions.java.net"&gt;&lt;code&gt;transitions.java.net&lt;/A&gt;&lt;/code&gt;.  That project has been integrated into my blog's source code repository.]&lt;br /&gt;&lt;P&gt;On the one hand: the Java2D architecture is a little bit limiting (as of Java 1.5 you still had to use outside code to get multiple-color gradients or perspective transforms).  But on the other hand: we developers aren't really using the options that we had access to.  Was this a design problem?  Possibly.  Also I'd suggest it's largely a training problem: developers usually pick Java for its flexibility and webiness, but they underestimate its graphic abilities.  Maybe &lt;A HREF="http://javafx.com/"&gt;JavaFX&lt;/A&gt; will end up addressing these problems?&lt;br /&gt;&lt;P&gt;Anyway.  Now I'm just rambling.  If you're reading this article it's probably to get useful/fun code, and not to hear me philosophize.  Back on topic: designing a slideshow of images.  The first thing to do is to set up a little architecture:&lt;br /&gt;&lt;h3&gt;Transitions and Transition2Ds&lt;/h3&gt;&lt;br /&gt;In its most generic form, I defined an interface called &lt;code&gt;Transition&lt;/code&gt;.  It has only 1 method:&lt;br /&gt;&lt;BR&gt;&lt;code&gt;public void paint(Graphics2D g,&lt;br /&gt; &amp;nbsp; BufferedImage frameA,&lt;br /&gt; &amp;nbsp; BufferedImage frameB,&lt;br /&gt; &amp;nbsp; float progress);&lt;/code&gt;&lt;br /&gt;&lt;P&gt;It's so generic that it's near useless.  I suppose you could paint frames of a &lt;code&gt;Transition&lt;/code&gt; object and then export those frames as still images, or &lt;a href="http://javagraphics.blogspot.com/2008/06/movies-writing-mov-files-without.html"&gt;write a movie&lt;/a&gt;, but we can probably define a better model.&lt;br /&gt;&lt;P&gt;So I created an implementation of this interface: the &lt;code&gt;&lt;a href="http://javagraphics.java.net/doc/com/bric/image/transition/Transition2D.html"&gt;Transition2D&lt;/a&gt;&lt;/code&gt; is an abstract class that narrows down the playing field a little bit.  This class contains the method:&lt;br /&gt;&lt;BR&gt;&lt;code&gt;public abstract Transition2DInstruction[] getInstructions(&lt;br /&gt; &amp;nbsp; float progress,&lt;br /&gt; &amp;nbsp; Dimension size);&lt;/code&gt;&lt;br /&gt;&lt;P&gt;This class describes a frame as a series of 2D instructions.  This means it can be represented as vector art -- this is much more useful than the generic &lt;code&gt;Transition&lt;/code&gt; interface mentioned above.  There are two subclasses of the abstract &lt;A HREF="http://javagraphics.java.net/doc/com/bric/image/transition/Transition2DInstruction.html"&gt;&lt;codE&gt;Transitioned2DInstruction&lt;/code&gt;&lt;/A&gt;:&lt;br /&gt;&lt;LI&gt;&lt;A HREF="http://javagraphics.java.net/doc/com/bric/image/transition/ShapeInstruction.html"&gt;&lt;codE&gt;ShapeInstruction&lt;/code&gt;&lt;/A&gt;: to draw a simple shape with a solid color for a fill and stroke, and a fixed stroke width.&lt;br /&gt;&lt;LI&gt;&lt;A HREF="http://javagraphics.java.net/doc/com/bric/image/transition/ImageInstruction.html"&gt;&lt;codE&gt;ImageInstruction&lt;/code&gt;&lt;/A&gt;: to draw an image.  This instruction includes the opacity, clipping and transform.  Also each &lt;code&gt;ImageInstruction&lt;/code&gt; identifies whether this instruction is drawing the beginning or ending image.  (That is: in a slideshow you have to transition from "Frame A" to "Frame B".  Does this instruction relate to drawing A or B?)&lt;/LI&gt;&lt;br /&gt;&lt;h3&gt;Examples&lt;/h3&gt;&lt;br /&gt;So those are the basic mechanics of this model.  Having outlined the architecture I played around for several weeks in my spare time and made some fun &lt;code&gt;Transition2D&lt;/code&gt; examples:&lt;br /&gt;&lt;APPLET CODE="com/bric/image/transition/app/Transition2DDemo" CODEBASE="http://javagraphics.java.net/jars/" width=434 height=549&gt;     &lt;PARAM NAME="archive" VALUE="Transition2D.jar"&gt;&lt;br /&gt;&lt;/APPLET&gt;&lt;br /&gt;&lt;P&gt;You can download this applet (source included) &lt;A HREF="http://javagraphics.java.net/jars/Transition2D.jar"&gt;here&lt;/A&gt;.  If the applet appears broken or choppy: please try downloading the jar and launching that.&lt;br /&gt;&lt;P&gt;I'd like to say I designed all of these transitions with precision and foresight, but that's not true at all.  Most of them (especially the best ones) are accidents.  I may have started out with some sort of goal in mind, but through a series of mathematical mistakes I ended up with something completely different from what I was envisioning.  Still: I'm very pleased with all of them.&lt;br /&gt;&lt;P&gt;There are 2 separate packages of &lt;code&gt;Transition2Ds&lt;/code&gt;:&lt;br /&gt;&lt;LI&gt;&lt;code&gt;com.bric.image.transition.vanilla&lt;/code&gt;: These are all based on transitions we previously used at work.  Tech4Learning gave permission to reuse them in this project.&lt;br /&gt;&lt;LI&gt;&lt;code&gt;com.bric.image.transition.spunk&lt;/code&gt;: These [new] transitions are much more playful (maybe even juvenile?) and all my own creation.  Some are obviously based on existing models (like "Swivel" and "Zoom"), but they were all written from scratch.&lt;/LI&gt;&lt;br /&gt;&lt;H3&gt;Writing SWFs&lt;/H3&gt;&lt;br /&gt;The &lt;code&gt;Transition2DInstruction&lt;/code&gt; objects were designed so they just happened to be very SWF-friendly.  If you take a series of instructions add a splash of Flagstone's excellent SWF-writing project &lt;A HREF="http://www.flagstonesoftware.com/transform/"&gt;Transform&lt;/A&gt;, and you can export SWF slideshows like this one:&lt;br /&gt;&lt;object CLASSID="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" CODEBASE="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0" ID="Untitled" WIDTH="320" HEIGHT="240"&gt;&lt;br /&gt;&lt;param name="loop" value="true"&gt;&lt;br /&gt;&lt;param name="quality" value="best"&gt;&lt;br /&gt;&lt;embed NAME="Demo" SRC="http://javagraphics.java.net/resources/carrie.swf" WIDTH=320 HEIGHT=240 LOOP=true SWLIVECONNECT=true QUALITY=best&gt;&lt;/embed&gt;&lt;br /&gt;&lt;/object&gt;&lt;br /&gt;&lt;P&gt;(These photos were generously donated by a wonderful &lt;A HREF="http://carrierrichardson.com/"&gt;photographer and friend&lt;/A&gt;.)&lt;br /&gt;&lt;P&gt;By the way: this SWF file doesn't show for me on all browsers.  Anyone know why?&lt;br /&gt;&lt;P&gt;Although there is SWF-writing code in the &lt;A HREF="http://javagraphics.java.net/"&gt;SVN codebase&lt;/A&gt; you can study, I wouldn't advertise any of it as extremely robust.  If you want play/stop controls, audio, and other really basic Flash-ish features: you'll need to write your own project.  This demo is just that: a demo.  A fun proof-of-concept.&lt;br /&gt;&lt;H3&gt;Conclusion&lt;/H3&gt;&lt;br /&gt;So... enjoy!  Also at someone's request I wrote the &lt;A HREF="http://javagraphics.java.net/jars/SimplestTransitionDemo.jar"&gt;SimplestTransitionDemo.jar&lt;/A&gt; a while ago if you want to slowly dig around and see how everything works.&lt;br /&gt;&lt;P&gt;If you have created your own transitions -- whether they're 2D or not -- I'd love to see them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/406997977734348535-7779815592452765139?l=javagraphics.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://javagraphics.blogspot.com/feeds/7779815592452765139/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://javagraphics.blogspot.com/2007/04/slideshows-transitions-swf.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7779815592452765139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/406997977734348535/posts/default/7779815592452765139'/><link rel='alternate' type='text/html' href='http://javagraphics.blogspot.com/2007/04/slideshows-transitions-swf.html' title='Slideshows: Transitions &amp; SWFs'/><author><name>Jeremy</name><uri>http://www.blogger.com/profile/02052809704994900346</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://4.bp.blogspot.com/_RNjBLxshIIk/SPVxdGaQFDI/AAAAAAAAAAs/rbnLirgymUk/S220/jeremy.jpg'/></author><thr:total>4</thr:total></entry></feed>
