You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pivot.apache.org by tv...@apache.org on 2009/03/26 00:12:17 UTC

svn commit: r758461 [29/47] - in /incubator/pivot/branches: ./ 1.1/ 1.1/charts-test/ 1.1/charts-test/src/ 1.1/charts-test/src/pivot/ 1.1/charts-test/src/pivot/charts/ 1.1/charts-test/src/pivot/charts/test/ 1.1/charts/ 1.1/charts/lib/ 1.1/charts/src/ 1....

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/BlurDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/BlurDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/BlurDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/BlurDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.awt.image.ConvolveOp;
+import java.awt.image.Kernel;
+
+import pivot.wtk.Component;
+import pivot.wtk.Bounds;
+
+/**
+ * Decorator that applies a blur to a component.
+ * <p>
+ * Blurs are given an integer magnitude, which represents the intensity of
+ * the blur. This value translates to a grid of pixels (<tt>blurMagnitude^2</tt>),
+ * where each pixel value is calculated by consulting its neighboring pixels
+ * according to the grid. Because of this, note that you will get "prettier"
+ * blurring if you choose odd values for the blur magnitude; this allows the
+ * pixel in question to reside at the center of the grid, thus preventing any
+ * arbitrary shifting of pixels. Also note that the greater the intensity of
+ * the blur, the greater the intensity of the calculations necessary to
+ * accomplish the blur (and the longer it will take to perform the blur).
+ * <p>
+ * TODO Increase size of buffered image to account for edge conditions of the
+ * blur.
+ * <p>
+ * TODO Use unequal values in the blur kernel to make pixels that are farther
+ * away count less towards the blur.
+ *
+ * @author tvolkert
+ */
+public class BlurDecorator implements Decorator {
+    private int blurMagnitude;
+
+    private Graphics2D graphics = null;
+
+    private BufferedImage bufferedImage = null;
+    private Graphics2D bufferedImageGraphics = null;
+
+    /**
+     * Creates a <tt>BlurDecorator</tt> with the default blur magnitude.
+     *
+     * @see #BlurDecorator(int)
+     */
+    public BlurDecorator() {
+        this(9);
+    }
+
+    /**
+     * Creates a <tt>BlurDecorator</tt> with the specified blur magnitude.
+     *
+     * @param blurMagnitude
+     * The intensity of the blur.
+     */
+    public BlurDecorator(int blurMagnitude) {
+        this.blurMagnitude = blurMagnitude;
+    }
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        this.graphics = graphics;
+
+        int width = component.getWidth();
+        int height = component.getHeight();
+
+        if (bufferedImage == null
+            || bufferedImage.getWidth() != width
+            || bufferedImage.getHeight() != height) {
+            bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+        }
+
+        bufferedImageGraphics = bufferedImage.createGraphics();
+        bufferedImageGraphics.setClip(graphics.getClip());
+
+        return bufferedImageGraphics;
+    }
+
+    public void update() {
+        bufferedImageGraphics.dispose();
+        bufferedImage.flush();
+
+        float[] kernel = new float[blurMagnitude * blurMagnitude];
+        for (int i = 0, n = kernel.length; i < n; i++) {
+            kernel[i] = 1f / n;
+        }
+
+        ConvolveOp blur = new ConvolveOp(new Kernel(blurMagnitude, blurMagnitude,
+            kernel), ConvolveOp.EDGE_NO_OP, null);
+        bufferedImage = blur.filter(bufferedImage, null);
+
+        this.graphics.drawImage(bufferedImage, 0, 0, null);
+    }
+
+    public Bounds getBounds(Component component) {
+        return new Bounds(0, 0, component.getWidth(), component.getHeight());
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return new AffineTransform();
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ClipDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ClipDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ClipDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ClipDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+
+import pivot.wtk.Bounds;
+import pivot.wtk.Component;
+
+/**
+ * Decorator that adds a rectangular region to the current clip.
+ *
+ * @author gbrown
+ */
+public class ClipDecorator implements Decorator {
+	private int x = 0;
+	private int y = 0;
+	private int width = 0;
+	private int height = 0;
+
+	public int getX() {
+		return x;
+	}
+
+	public void setX(int x) {
+		this.x = x;
+	}
+
+	public int getY() {
+		return y;
+	}
+
+	public void setY(int y) {
+		this.y = y;
+	}
+
+	public int getWidth() {
+		return width;
+	}
+
+	public void setWidth(int width) {
+		this.width = width;
+	}
+
+	public int getHeight() {
+		return height;
+	}
+
+	public void setHeight(int height) {
+		this.height = height;
+	}
+
+	public Graphics2D prepare(Component component, Graphics2D graphics) {
+		graphics.clipRect(x, y, width, height);
+		return graphics;
+	}
+
+	public void update() {
+		// No-op
+	}
+
+    public Bounds getBounds(Component component) {
+        return new Bounds(x, y, width, height);
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return new AffineTransform();
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/Decorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/Decorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/Decorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/Decorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+
+import pivot.wtk.Bounds;
+import pivot.wtk.Component;
+
+/**
+ * Interface defining a component "decorator". Decorators allow a caller to
+ * attach additional visual effects to a component.
+ *
+ * @author gbrown
+ */
+public interface Decorator {
+    /**
+     * Prepares the graphics context into which the component or prior
+     * decorator will paint. This method is called immediately prior to
+     * {@link Component#paint(Graphics2D)}; decorators are called in
+     * descending order.
+     *
+     * @param component
+     * @param graphics
+     *
+     * @return
+     * The graphics context that should be used by the component or prior
+     * decorators.
+     */
+    public Graphics2D prepare(Component component, Graphics2D graphics);
+
+    /**
+     * Updates the graphics context into which the component or prior
+     * decorator was painted. This method is called immediately after
+     * {@link Component#paint(Graphics2D)}; decorators are called in
+     * ascending order.
+     */
+    public void update();
+
+    /**
+     * Returns the bounding area of the decorator.
+     *
+     * @param component
+     *
+     * @return
+     * The decorator's bounds, relative to the component's origin.
+     */
+    public Bounds getBounds(Component component);
+
+    /**
+     * Returns the transformation the decorator applies to the component's
+     * coordinate space.
+     *
+     * @return
+     * The decorator's transform.
+     */
+    public AffineTransform getTransform(Component component);
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/DropShadowDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/DropShadowDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/DropShadowDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/DropShadowDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,397 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Contains code and concepts from ShadowRenderer.java v1.6,
+ * copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
+ * Santa Clara, California 95054, U.S.A. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.awt.image.Raster;
+import java.awt.image.WritableRaster;
+import pivot.wtk.Component;
+import pivot.wtk.Bounds;
+
+/**
+ * Decorator that adds a drop shadows to a component.
+ *
+ * @author gbrown
+ * @author tvolkert
+ * @author eryzhikov
+ * @author Romain Guy
+ * @author Sebastien Petrucci
+ */
+public class DropShadowDecorator implements Decorator {
+    private int blurRadius;
+    private int xOffset;
+    private int yOffset;
+
+    private Color shadowColor = Color.BLACK;
+    private float shadowOpacity = 0.33f;
+
+    private Graphics2D graphics = null;
+    private BufferedImage componentImage = null;
+    private Graphics2D componentImageGraphics = null;
+
+    public DropShadowDecorator() {
+        this(5, 5, 5);
+    }
+
+    public DropShadowDecorator(int blurRadius, int xOffset, int yOffset) {
+        this.blurRadius = blurRadius;
+        this.xOffset = xOffset;
+        this.yOffset = yOffset;
+    }
+
+    /**
+     * Returns the color used to draw the shadow.
+     *
+     * @return
+     * The color used to draw the shadow.
+     */
+    public Color getShadowColor() {
+        return shadowColor;
+    }
+
+    /**
+     * Sets the color used to draw the shadow.
+     *
+     * @param shadowColor
+     * The color used to draw the shadow.
+     */
+    public void setShadowColor(Color shadowColor) {
+        this.shadowColor = shadowColor;
+    }
+
+    /**
+     * Sets the color used to draw the shadow.
+     *
+     * @param shadowColor
+     * The color used to draw the shadow.
+     */
+    public final void setShadowColor(String shadowColor) {
+        if (shadowColor == null) {
+            throw new IllegalArgumentException("shadowColor is null.");
+        }
+
+        setShadowColor(Color.decode(shadowColor));
+    }
+
+    /**
+     * Returns the opacity used to draw the shadow.
+     *
+     * @return
+     * The color used to draw the shadow.
+     */
+    public float getShadowOpacity() {
+        return shadowOpacity;
+    }
+
+    /**
+     * Sets the opacity used to draw the shadow.
+     *
+     * @param shadowOpacity
+     * The opacity used to draw the shadow.
+     */
+    public void setShadowOpacity(float shadowOpacity) {
+        this.shadowOpacity = shadowOpacity;
+    }
+
+    /**
+     * Returns the blur radius used to draw the shadow.
+     *
+     * @return
+     * The blur radius used to draw the shadow.
+     */
+    public int getBlurRadius() {
+        return blurRadius;
+    }
+
+    /**
+     * Sets the blur radius used to draw the shadow.
+     *
+     * @param blurRadius
+     * The blur radius used to draw the shadow.
+     */
+    public void setBlurRadius(int blurRadius) {
+        this.blurRadius = blurRadius;
+    }
+
+    /**
+     * Returns the amount that the drop shadow will be offset along the x axis.
+     *
+     * @return
+     * The x offset used to draw the shadow
+     */
+    public int getXOffset() {
+        return xOffset;
+    }
+
+    /**
+     * Sets the amount that the drop shadow will be offset along the x axis.
+     *
+     * @param xOffset
+     * The x offset used to draw the shadow
+     */
+    public void setXOffset(int xOffset) {
+        this.xOffset = xOffset;
+    }
+
+    /**
+     * Returns the amount that the drop shadow will be offset along the y axis.
+     *
+     * @return
+     * The y offset used to draw the shadow
+     */
+    public int getYOffset() {
+        return yOffset;
+    }
+
+    /**
+     * Sets the amount that the drop shadow will be offset along the y axis.
+     *
+     * @param yOffset
+     * The y offset used to draw the shadow
+     */
+    public void setYOffset(int yOffset) {
+        this.yOffset = yOffset;
+    }
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        this.graphics = graphics;
+
+        int width = component.getWidth();
+        int height = component.getHeight();
+
+        if (width > 0
+            && height > 0) {
+            if (componentImage == null
+                || componentImage.getWidth() != width
+                || componentImage.getHeight() != height) {
+                componentImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+            }
+
+            componentImageGraphics = componentImage.createGraphics();
+            componentImageGraphics.setClip(graphics.getClip());
+
+            componentImageGraphics.setComposite(AlphaComposite.Clear);
+            componentImageGraphics.fillRect(0, 0, componentImage.getWidth(), componentImage.getHeight());
+
+            componentImageGraphics.setComposite(AlphaComposite.SrcOver);
+
+            graphics = componentImageGraphics;
+        }
+
+        return graphics;
+    }
+
+    public void update() {
+        if (componentImage != null) {
+            componentImageGraphics.dispose();
+
+            BufferedImage shadowImage = createShadow(componentImage);
+
+            graphics.drawImage(shadowImage, xOffset - blurRadius, yOffset - blurRadius, null);
+            graphics.drawImage(componentImage, 0, 0, null);
+        }
+    }
+
+    public Bounds getBounds(Component component) {
+        return new Bounds(xOffset - blurRadius, yOffset - blurRadius,
+            component.getWidth() + blurRadius * 2,
+            component.getHeight() + blurRadius * 2);
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return new AffineTransform();
+    }
+
+    /**
+     * Generates the shadow for a given picture and the current properties of
+     * the decorator. The generated image dimensions are computed as follows:
+     *
+     * <pre>
+     * width = imageWidth + 2 * blurRadius
+     * height = imageHeight + 2 * blurRadius
+     * </pre>
+     *
+     * @param image
+     * The image from which the shadow will be cast.
+     *
+     * @return
+     * An image containing the generated shadow.
+     */
+    private BufferedImage createShadow(BufferedImage src) {
+        int shadowSize = blurRadius * 2;
+
+        int srcWidth = src.getWidth();
+        int srcHeight = src.getHeight();
+
+        int dstWidth = srcWidth + shadowSize;
+        int dstHeight = srcHeight + shadowSize;
+
+        int left = blurRadius;
+        int right = shadowSize - left;
+
+        int yStop = dstHeight - right;
+
+        int shadowRgb = shadowColor.getRGB() & 0x00FFFFFF;
+        int[] aHistory = new int[shadowSize];
+        int historyIdx;
+
+        int aSum;
+
+        BufferedImage dst = new BufferedImage(dstWidth, dstHeight,
+            BufferedImage.TYPE_INT_ARGB);
+
+        int[] dstBuffer = new int[dstWidth * dstHeight];
+        int[] srcBuffer = new int[srcWidth * srcHeight];
+
+        Raster srcRaster = src.getRaster();
+        srcRaster.getDataElements(0, 0, srcWidth, srcHeight, srcBuffer);
+
+        int lastPixelOffset = right * dstWidth;
+        float hSumDivider = 1.0f / shadowSize;
+        float vSumDivider = shadowOpacity / shadowSize;
+
+        int[] hSumLookup = new int[256 * shadowSize];
+        for (int i = 0; i < hSumLookup.length; i++) {
+            hSumLookup[i] = (int) (i * hSumDivider);
+        }
+
+        int[] vSumLookup = new int[256 * shadowSize];
+        for (int i = 0; i < vSumLookup.length; i++) {
+            vSumLookup[i] = (int) (i * vSumDivider);
+        }
+
+        int srcOffset;
+
+        // Horizontal pass: extract the alpha mask from the source picture and
+        // blur it into the destination picture
+        for (int srcY = 0, dstOffset = left * dstWidth; srcY < srcHeight; srcY++) {
+            // First pixels are empty
+            for (historyIdx = 0; historyIdx < shadowSize;) {
+                aHistory[historyIdx++] = 0;
+            }
+
+            aSum = 0;
+            historyIdx = 0;
+            srcOffset = srcY * srcWidth;
+
+            // Compute the blur average with pixels from the source image
+            for (int srcX = 0; srcX < srcWidth; srcX++) {
+                int a = hSumLookup[aSum];
+                // Store the alpha value only; the shadow color will be added
+                // in the next pass
+                dstBuffer[dstOffset++] = a << 24;
+
+                // Substract the oldest pixel from the sum
+                aSum -= aHistory[historyIdx];
+
+                // Extract the new pixel and store its value into history...
+                a = srcBuffer[srcOffset + srcX] >>> 24;
+                aHistory[historyIdx] = a;
+
+                // ...and add its value to the sum
+                aSum += a;
+
+                if (++historyIdx >= shadowSize) {
+                    historyIdx -= shadowSize;
+                }
+            }
+
+            // Blur the end of the row - no new pixels to grab
+            for (int i = 0; i < shadowSize; i++) {
+                int a = hSumLookup[aSum];
+                dstBuffer[dstOffset++] = a << 24;
+
+                // Substract the oldest pixel from the sum...and nothing new
+                // to add!
+                aSum -= aHistory[historyIdx];
+
+                if (++historyIdx >= shadowSize) {
+                    historyIdx -= shadowSize;
+                }
+            }
+        }
+
+        // Vertical pass
+        for (int x = 0, bufferOffset = 0; x < dstWidth; x++, bufferOffset = x) {
+            aSum = 0;
+
+            // First pixels are empty...
+            for (historyIdx = 0; historyIdx < left;) {
+                aHistory[historyIdx++] = 0;
+            }
+
+            // ...and then they come from the dstBuffer
+            for (int y = 0; y < right; y++, bufferOffset += dstWidth) {
+                // Extract alpha and store into history...
+                int a = dstBuffer[bufferOffset] >>> 24;
+                aHistory[historyIdx++] = a;
+
+                // ...and add to sum
+                aSum += a;
+            }
+
+            bufferOffset = x;
+            historyIdx = 0;
+
+            // Compute the blur avera`ge with pixels from the previous pass
+            for (int y = 0; y < yStop; y++, bufferOffset += dstWidth) {
+                // Store alpha value + shadow color
+                int a = vSumLookup[aSum];
+                dstBuffer[bufferOffset] = a << 24 | shadowRgb;
+
+                // Substract the oldest pixel from the sum
+                aSum -= aHistory[historyIdx];
+
+                // Extract the new pixel and store its value into history...
+                a = dstBuffer[bufferOffset + lastPixelOffset] >>> 24;
+                aHistory[historyIdx] = a;
+
+                // ... and add its value to the sum
+                aSum += a;
+
+                if (++historyIdx >= shadowSize) {
+                    historyIdx -= shadowSize;
+                }
+            }
+
+            // Blur the end of the column - no pixels to grab anymore
+            for (int y = yStop; y < dstHeight; y++, bufferOffset += dstWidth) {
+                int a = vSumLookup[aSum];
+                dstBuffer[bufferOffset] = a << 24 | shadowRgb;
+
+                // Substract the oldest pixel from the sum
+                aSum -= aHistory[historyIdx];
+
+                if (++historyIdx >= shadowSize) {
+                    historyIdx -= shadowSize;
+                }
+            }
+        }
+
+        WritableRaster dstRaster = dst.getRaster();
+        dstRaster.setDataElements(0, 0, dstWidth, dstHeight, dstBuffer);
+
+        return dst;
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/FadeDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/FadeDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/FadeDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/FadeDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,55 @@
+package pivot.wtk.effects;
+
+import java.awt.AlphaComposite;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+
+import pivot.wtk.Component;
+import pivot.wtk.Bounds;
+
+/**
+ * Decorator that applies an opacity to a component.
+ *
+ * @author gbrown
+ */
+public class FadeDecorator implements Decorator {
+    private float opacity;
+
+    public FadeDecorator() {
+        this(0.5f);
+    }
+
+    public FadeDecorator(float opacity) {
+        this.opacity = opacity;
+    }
+
+    public float getOpacity() {
+        return opacity;
+    }
+
+    public void setOpacity(float opacity) {
+        if (opacity < 0f
+            || opacity > 1f) {
+            throw new IllegalArgumentException("opacity must be a value between 0 and 1, inclusive.");
+        }
+
+        this.opacity = opacity;
+    }
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
+        return graphics;
+    }
+
+    public void update() {
+        // No-op
+    }
+
+    public Bounds getBounds(Component component) {
+        return new Bounds(0, 0, component.getWidth(), component.getHeight());
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return new AffineTransform();
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/GrayscaleDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/GrayscaleDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/GrayscaleDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/GrayscaleDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+
+import pivot.wtk.Component;
+import pivot.wtk.Bounds;
+
+/**
+ * Decorator that applies a grayscale conversion to a component.
+ *
+ * @author tvolkert
+ * @author gbrown
+ */
+public class GrayscaleDecorator implements Decorator {
+    private Graphics2D graphics = null;
+
+    private BufferedImage bufferedImage = null;
+    private Graphics2D bufferedImageGraphics = null;
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        this.graphics = graphics;
+
+        int width = component.getWidth();
+        int height = component.getHeight();
+
+        if (bufferedImage == null
+            || bufferedImage.getWidth() < width
+            || bufferedImage.getHeight() < height) {
+            bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
+        }
+
+        bufferedImageGraphics = bufferedImage.createGraphics();
+        bufferedImageGraphics.setClip(graphics.getClip());
+
+        return bufferedImageGraphics;
+    }
+
+    public void update() {
+        bufferedImageGraphics.dispose();
+        bufferedImage.flush();
+
+        graphics.drawImage(bufferedImage, 0, 0, null);
+    }
+
+    public Bounds getBounds(Component component) {
+        return new Bounds(0, 0, component.getWidth(), component.getHeight());
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return new AffineTransform();
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ReflectionDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ReflectionDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ReflectionDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ReflectionDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,81 @@
+package pivot.wtk.effects;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.GradientPaint;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+
+import pivot.wtk.Component;
+import pivot.wtk.Bounds;
+
+/**
+ * Decorator that paints a reflection of a component.
+ * <p>
+ * TODO Make gradient properties configurable.
+ * <p>
+ * TODO Add a shear value.
+ *
+ * @author gbrown
+ */
+public class ReflectionDecorator implements Decorator {
+    private Graphics2D graphics = null;
+    private Component component = null;
+
+    private BufferedImage componentImage = null;
+    private Graphics2D componentImageGraphics = null;
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        this.graphics = graphics;
+        this.component = component;
+
+        int width = component.getWidth();
+        int height = component.getHeight();
+
+        componentImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+        componentImageGraphics = componentImage.createGraphics();
+
+        // Clear the image background
+        componentImageGraphics.setComposite(AlphaComposite.Clear);
+        componentImageGraphics.fillRect(0, 0, componentImage.getWidth(), componentImage.getHeight());
+
+        componentImageGraphics.setComposite(AlphaComposite.SrcOver);
+
+        return componentImageGraphics;
+    }
+
+    public void update() {
+        // Draw the component
+        graphics.drawImage(componentImage, 0, 0, null);
+
+        // Draw the reflection
+        int width = componentImage.getWidth();
+        int height = componentImage.getHeight();
+
+        GradientPaint mask = new GradientPaint(0, height / 4, new Color(1.0f, 1.0f, 1.0f, 0.0f),
+            0, height, new Color(1.0f, 1.0f, 1.0f, 0.5f));
+        componentImageGraphics.setPaint(mask);
+
+        componentImageGraphics.setComposite(AlphaComposite.DstIn);
+        componentImageGraphics.fillRect(0, 0, width, height);
+
+        componentImageGraphics.dispose();
+        componentImage.flush();
+
+        graphics.transform(getTransform(component));
+
+        graphics.drawImage(componentImage, 0, 0, null);
+    }
+
+    public Bounds getBounds(Component component) {
+        return new Bounds(0, 0, component.getWidth(), component.getHeight() * 2);
+    }
+
+    public AffineTransform getTransform(Component component) {
+        AffineTransform transform = AffineTransform.getScaleInstance(1.0, -1.0);
+        transform.translate(0, -(component.getHeight() * 2));
+
+        return transform;
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/SaturationDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/SaturationDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/SaturationDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/SaturationDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Contains code and concepts from ShadowRenderer.java v1.6,
+ * copyright 2006 Sun Microsystems, Inc., 4150 Network Circle,
+ * Santa Clara, California 95054, U.S.A. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.awt.image.BufferedImage;
+import java.awt.image.WritableRaster;
+import pivot.wtk.Component;
+import pivot.wtk.Bounds;
+
+/**
+ * Decorator that modifies the color saturation of a component.
+ *
+ * @author tvolkert
+ */
+public class SaturationDecorator implements Decorator {
+    private float multiplier;
+
+    private Graphics2D graphics = null;
+
+    private BufferedImage componentImage = null;
+    private Graphics2D componentGraphics = null;
+
+    public SaturationDecorator() {
+        this(1.0f);
+    }
+
+    public SaturationDecorator(float multiplier) {
+        this.multiplier = multiplier;
+    }
+
+    public float getMultiplier() {
+        return multiplier;
+    }
+
+    public void setMultiplier(float multiplier) {
+        this.multiplier = multiplier;
+    }
+
+    public void setMultiplier(Number multiplier) {
+        if (multiplier == null) {
+            throw new IllegalArgumentException("Multiplier is null.");
+        }
+
+        setMultiplier(multiplier.floatValue());
+    }
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        int x = 0;
+        int y = 0;
+        int width = component.getWidth();
+        int height = component.getHeight();
+
+        java.awt.Rectangle clipBounds = graphics.getClipBounds();
+        if (clipBounds != null) {
+            x = clipBounds.x;
+            y = clipBounds.y;
+            width = clipBounds.width;
+            height = clipBounds.height;
+        }
+
+        componentImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+
+        this.graphics = graphics;
+
+        componentGraphics = componentImage.createGraphics();
+        componentGraphics.translate(-x, -y);
+        componentGraphics.setClip(graphics.getClip());
+
+        return componentGraphics;
+    }
+
+    /**
+     * Adjusts the saturation of the component image and draws the resulting
+     * image using the component's graphics.
+     */
+    public void update() {
+        int width = componentImage.getWidth();
+        int height = componentImage.getHeight();
+
+        int[] buffer = new int[width * height];
+
+        WritableRaster raster = componentImage.getRaster();
+        raster.getDataElements(0, 0, width, height, buffer);
+
+        float[] hsb = new float[3];
+
+        for (int i = 0; i < height; i++) {
+            for (int j = 0; j < width; j++) {
+                int srcRGB = buffer[i * width + j];
+
+                // Adjust color saturation
+                Color.RGBtoHSB((srcRGB >> 16) & 0xff,
+                    (srcRGB >> 8) & 0xff, srcRGB & 0xff, hsb);
+                int dstRGB = Color.HSBtoRGB(hsb[0],
+                    Math.min(Math.max(hsb[1] * multiplier, 0f), 1f), hsb[2]);
+
+                // Preserve the source alpha channel
+                dstRGB = (srcRGB & 0xff000000) | (dstRGB & 0xffffff);
+
+                buffer[i * width + j] = dstRGB;
+            }
+        }
+
+        raster.setDataElements(0, 0, width, height, buffer);
+
+        int x = 0;
+        int y = 0;
+
+        java.awt.Rectangle clipBounds = componentGraphics.getClipBounds();
+        if (clipBounds != null) {
+            x = clipBounds.x;
+            y = clipBounds.y;
+        }
+
+        componentGraphics.dispose();
+        graphics.drawImage(componentImage, x, y, null);
+    }
+
+    public Bounds getBounds(Component component) {
+        return new Bounds(0, 0, component.getWidth(), component.getHeight());
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return new AffineTransform();
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ScaleDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ScaleDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ScaleDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ScaleDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,373 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.geom.AffineTransform;
+
+import pivot.wtk.Bounds;
+import pivot.wtk.Component;
+import pivot.wtk.Container;
+import pivot.wtk.HorizontalAlignment;
+import pivot.wtk.Platform;
+import pivot.wtk.VerticalAlignment;
+
+/**
+ * Decorator that scales the painting of a component along the X and/or Y axes.
+ *
+ * @author tvolkert
+ */
+public class ScaleDecorator implements Decorator {
+    private float scaleX;
+    private float scaleY;
+    private HorizontalAlignment horizontalAlignment = HorizontalAlignment.CENTER;
+    private VerticalAlignment verticalAlignment = VerticalAlignment.CENTER;
+
+    /**
+     * Creates a new <tt>ScaleDecorator</tt> with the default <tt>scaleX</tt>
+     * <tt>scaleY</tt> values of <tt>1</tt>.
+     */
+    public ScaleDecorator() {
+        this(1f, 1f);
+    }
+
+    /**
+     * Creates a new <tt>ScaleDecorator</tt> with the specified <tt>scaleX</tt>
+     * and <tt>scaleY</tt> values.
+     *
+     * @param scaleX
+     * The amount to scale the component's x-axis
+     *
+     * @param scaleY
+     * The amount to scale the component's y-axis
+     */
+    public ScaleDecorator(float scaleX, float scaleY) {
+        if (scaleX <= 0
+            || scaleY <= 0) {
+            throw new IllegalArgumentException("Scale values must be positive.");
+        }
+
+        this.scaleX = scaleX;
+        this.scaleY = scaleY;
+    }
+
+    /**
+     * Gets the amount by which drawing operations will be scaled along the
+     * x-axis.
+     *
+     * @return
+     * The amount to scale the component's x-axis
+     */
+    public float getScaleX() {
+        return scaleX;
+    }
+
+    /**
+     * Sets the amount by which drawing operations will be scaled along the
+     * x-axis.
+     *
+     * @param scaleX
+     * The amount to scale the component's x-axis
+     */
+    public void setScaleX(float scaleX) {
+        if (scaleX <= 0) {
+            throw new IllegalArgumentException("scaleX must be positive.");
+        }
+
+        this.scaleX = scaleX;
+    }
+
+    /**
+     * Sets the amount by which drawing operations will be scaled along the
+     * x-axis.
+     *
+     * @param scaleX
+     * The amount to scale the component's x-axis
+     */
+    public void setScaleX(Number scaleX) {
+        if (scaleX == null) {
+            throw new IllegalArgumentException("scaleX is null.");
+        }
+
+        setScaleX(scaleX.floatValue());
+    }
+
+    /**
+     * Gets the amount by which drawing operations will be scaled along the
+     * y-axis.
+     *
+     * @return
+     * The amount to scale the component's y-axis
+     */
+    public float getScaleY() {
+        return scaleY;
+    }
+
+    /**
+     * Sets the amount by which drawing operations will be scaled along the
+     * y-axis.
+     *
+     * @param scaleY
+     * The amount to scale the component's y-axis
+     */
+    public void setScaleY(float scaleY) {
+        if (scaleY <= 0) {
+            throw new IllegalArgumentException("scaleY must be positive.");
+        }
+
+        this.scaleY = scaleY;
+    }
+
+    /**
+     * Sets the amount by which drawing operations will be scaled along the
+     * y-axis.
+     *
+     * @param scaleY
+     * The amount to scale the component's y-axis
+     */
+    public void setScaleY(Number scaleY) {
+        if (scaleY == null) {
+            throw new IllegalArgumentException("scaleY is null.");
+        }
+
+        setScaleY(scaleY.floatValue());
+    }
+
+    /**
+     * Gets the horizontal alignment of the decorator. A left alignment will
+     * paint the component's left edge at the component's x-coordinate. A right
+     * alignment will paint the component's right edge along the right side
+     * of the component's bounding box. A center or justified alignment will
+     * paint the scaled component centered with respect to the component's
+     * bounding box.
+     *
+     * @return
+     * The horizontal alignment
+     */
+    public HorizontalAlignment getHorizontalAlignment() {
+        return horizontalAlignment;
+    }
+
+    /**
+     * Sets the horizontal alignment of the decorator. A left alignment will
+     * paint the component's left edge at the component's x-coordinate. A right
+     * alignment will paint the component's right edge along the right side
+     * of the component's bounding box. A center or justified alignment will
+     * paint the scaled component centered with respect to the component's
+     * bounding box.
+     *
+     * @param horizontalAlignment
+     * The horizontal alignment
+     */
+    public void setHorizontalAlignment(HorizontalAlignment horizontalAlignment) {
+        if (horizontalAlignment == null) {
+            throw new IllegalArgumentException("horizontalAlignment is null.");
+        }
+
+        this.horizontalAlignment = horizontalAlignment;
+    }
+
+    /**
+     * Sets the horizontal alignment of the decorator. A left alignment will
+     * paint the component's left edge at the component's x-coordinate. A right
+     * alignment will paint the component's right edge along the right side
+     * of the component's bounding box. A center or justified alignment will
+     * paint the scaled component centered with respect to the component's
+     * bounding box.
+     *
+     * @param horizontalAlignment
+     * The horizontal alignment
+     */
+    public final void setHorizontalAlignment(String horizontalAlignment) {
+        if (horizontalAlignment == null) {
+            throw new IllegalArgumentException("horizontalAlignment is null.");
+        }
+
+        setHorizontalAlignment(HorizontalAlignment.decode(horizontalAlignment));
+    }
+
+    /**
+     * Gets the vertical alignment of the decorator. A top alignment will
+     * paint the component's top edge at the component's y-coordinate. A bottom
+     * alignment will paint the component's bottom edge along the bottom side
+     * of the component's bounding box. A center or justified alignment will
+     * paint the scaled component centered with respect to the component's
+     * bounding box.
+     *
+     * @return
+     * The vertical alignment
+     */
+    public VerticalAlignment getVerticalAlignment() {
+        return verticalAlignment;
+    }
+
+    /**
+     * Sets the vertical alignment of the decorator. A top alignment will
+     * paint the component's top edge at the component's y-coordinate. A bottom
+     * alignment will paint the component's bottom edge along the bottom side
+     * of the component's bounding box. A center or justified alignment will
+     * paint the scaled component centered with respect to the component's
+     * bounding box.
+     *
+     * @param verticalAlignment
+     * The vertical alignment
+     */
+    public void setVerticalAlignment(VerticalAlignment verticalAlignment) {
+        if (verticalAlignment == null) {
+            throw new IllegalArgumentException("verticalAlignment is null.");
+        }
+
+        this.verticalAlignment = verticalAlignment;
+    }
+
+    /**
+     * Sets the vertical alignment of the decorator. A top alignment will
+     * paint the component's top edge at the component's y-coordinate. A bottom
+     * alignment will paint the component's bottom edge along the bottom side
+     * of the component's bounding box. A center or justified alignment will
+     * paint the scaled component centered with respect to the component's
+     * bounding box.
+     *
+     * @param verticalAlignment
+     * The vertical alignment
+     */
+    public final void setVerticalAlignment(String verticalAlignment) {
+        if (verticalAlignment == null) {
+            throw new IllegalArgumentException("verticalAlignment is null.");
+        }
+
+        setVerticalAlignment(VerticalAlignment.decode(verticalAlignment));
+    }
+
+    /**
+     * Gets the x translation that will be applied with respect to the
+     * specified component, given this decorator's <tt>scaleX</tt> and
+     * <tt>horizontalAlignment</tt> properties.
+     *
+     * @param component
+     * The component being decorated
+     *
+     * @return
+     * The amount to translate x-coordinate actions when decorating this
+     * component
+     */
+    private int getTranslatedX(Component component) {
+        int width = component.getWidth();
+        int translatedWidth = (int)Math.ceil(width * scaleX);
+
+        int tx;
+
+        switch (horizontalAlignment) {
+        case LEFT:
+            tx = 0;
+            break;
+        case RIGHT:
+            tx = width - translatedWidth;
+            break;
+        default:
+            tx = (width - translatedWidth) / 2;
+            break;
+        }
+
+        return tx;
+    }
+
+    /**
+     * Gets the y translation that will be applied with respect to the
+     * specified component, given this decorator's <tt>scaleY</tt> and
+     * <tt>verticalAlignment</tt> properties.
+     *
+     * @param component
+     * The component being decorated
+     *
+     * @return
+     * The amount to translate y-coordinate actions when decorating this
+     * component
+     */
+    private int getTranslatedY(Component component) {
+        int height = component.getHeight();
+        int translatedHeight = (int)Math.ceil(height * scaleY);
+
+        int ty;
+
+        switch (verticalAlignment) {
+        case TOP:
+            ty = 0;
+            break;
+        case BOTTOM:
+            ty = height - translatedHeight;
+            break;
+        default:
+            ty = (height - translatedHeight) / 2;
+            break;
+        }
+
+        return ty;
+    }
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+            RenderingHints.VALUE_ANTIALIAS_ON);
+        graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
+            Platform.getTextAntialiasingHint());
+
+        int tx = getTranslatedX(component);
+        int ty = getTranslatedY(component);
+
+        if (tx != 0 || ty != 0) {
+            graphics.translate(tx, ty);
+        }
+
+        graphics.scale(scaleX, scaleY);
+
+        return graphics;
+    }
+
+    public void update() {
+        // No-op
+    }
+
+    public void repaint(Component component, int x, int y, int width, int height) {
+        Container parent = component.getParent();
+
+        if (parent != null) {
+            int tx = getTranslatedX(component);
+            int ty = getTranslatedY(component);
+
+            x = (int)((x * scaleX) + component.getX() + tx);
+            y = (int)((y * scaleY) + component.getY() + ty);
+            width = (int)Math.ceil(width * scaleX);
+            height = (int)Math.ceil(height * scaleY);
+
+            parent.repaint(x, y, width, height);
+        }
+    }
+
+    public Bounds getBounds(Component component) {
+        int width = (int)Math.ceil(component.getWidth() * scaleX);
+        int height = (int)Math.ceil(component.getHeight() * scaleY);
+
+        int tx = getTranslatedX(component);
+        int ty = getTranslatedY(component);
+
+        return new Bounds(tx, ty, width, height);
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return AffineTransform.getScaleInstance(scaleX, scaleY);
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ShadeDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ShadeDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ShadeDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/ShadeDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.AlphaComposite;
+import java.awt.Color;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+
+import pivot.wtk.Component;
+import pivot.wtk.Bounds;
+
+/**
+ * Decorator that applies a "shade" to a component. The shade is a rectangle
+ * of the same size as the component that is painted over the component using a
+ * given color and opacity value.
+ *
+ * @author tvolkert
+ */
+public class ShadeDecorator implements Decorator {
+    private float opacity;
+    private Color color;
+
+    private Graphics2D graphics;
+    private Component component;
+
+    /**
+     * Creates a new <tt>ShadeDecorator</tt> with the default opacity and
+     * shade color.
+     */
+    public ShadeDecorator() {
+        this(0.33f, Color.BLACK);
+    }
+
+    /**
+     * Creates a new <tt>ShadeDecorator</tt> with the specified opacity and
+     * shade color.
+     *
+     * @param opacity
+     * The opacity of the shade, between 0 and 1, exclusive.
+     *
+     * @param color
+     * The color of the shade.
+     */
+    public ShadeDecorator(float opacity, Color color) {
+        if (opacity <= 0 || opacity >= 1) {
+            throw new IllegalArgumentException("opacity must be between 0 and 1, exclusive.");
+        }
+
+        if (color == null) {
+            throw new IllegalArgumentException("color is null.");
+        }
+
+        this.opacity = opacity;
+        this.color = color;
+    }
+
+    public float getOpacity() {
+        return opacity;
+    }
+
+    public void setOpacity(float opacity) {
+        this.opacity = opacity;
+    }
+
+    public void setOpacity(Number opacity) {
+        if (opacity == null) {
+            throw new IllegalArgumentException("opacity is null.");
+        }
+
+        setOpacity(opacity.floatValue());
+    }
+
+    public Color getColor() {
+        return color;
+    }
+
+    public void setColor(Color color) {
+        if (color == null) {
+            throw new IllegalArgumentException("color is null.");
+        }
+
+        this.color = color;
+    }
+
+    /**
+     *
+     */
+    public final void setColor(String color) {
+        if (color == null) {
+            throw new IllegalArgumentException("color is null.");
+        }
+
+        setColor(Color.decode(color));
+    }
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        this.graphics = graphics;
+        this.component = component;
+
+        return graphics;
+    }
+
+    public void update() {
+        graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, opacity));
+        graphics.setColor(color);
+        graphics.fillRect(0, 0, component.getWidth(), component.getHeight());
+    }
+
+    public Bounds getBounds(Component component) {
+        return new Bounds(0, 0, component.getWidth(), component.getHeight());
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return new AffineTransform();
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TagDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TagDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TagDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TagDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+
+import pivot.wtk.Bounds;
+import pivot.wtk.Component;
+import pivot.wtk.HorizontalAlignment;
+import pivot.wtk.VerticalAlignment;
+import pivot.wtk.Visual;
+
+/**
+ * Decorator that allows a caller to attach a "tag" visual to a component.
+ *
+ * @author gbrown
+ */
+public class TagDecorator implements Decorator {
+    private Visual tag;
+    private HorizontalAlignment horizontalAlignment;
+    private VerticalAlignment verticalAlignment;
+    private int xOffset;
+    private int yOffset;
+
+    private Graphics2D graphics = null;
+    private Bounds bounds = null;
+
+    public TagDecorator() {
+        this(null);
+    }
+
+    public TagDecorator(Visual tag) {
+        this(tag, HorizontalAlignment.RIGHT, VerticalAlignment.TOP, 0, 0);
+    }
+
+    public TagDecorator(Visual tag,
+        HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment,
+        int xOffset, int yOffset) {
+        this.tag = tag;
+        this.horizontalAlignment = horizontalAlignment;
+        this.verticalAlignment = verticalAlignment;
+        this.xOffset = xOffset;
+        this.yOffset = yOffset;
+    }
+
+    public Visual getTag() {
+        return tag;
+    }
+
+    public void setTag(Visual tag) {
+        this.tag = tag;
+    }
+
+    public HorizontalAlignment getHorizontalAlignment() {
+        return horizontalAlignment;
+    }
+
+    public void setHorizontalAlignment(HorizontalAlignment horizontalAlignment) {
+        if (horizontalAlignment == null) {
+            throw new IllegalArgumentException("horizontalAlignment is null.");
+        }
+
+        this.horizontalAlignment = horizontalAlignment;
+    }
+
+    public VerticalAlignment getVerticalAlignment() {
+        return verticalAlignment;
+    }
+
+    public void setVerticalAlignment(VerticalAlignment verticalAlignment) {
+        if (verticalAlignment == null) {
+            throw new IllegalArgumentException("verticalAlignment is null.");
+        }
+
+        this.verticalAlignment = verticalAlignment;
+    }
+
+    public int getXOffset() {
+        return xOffset;
+    }
+
+    public void setXOffset(int xOffset) {
+        this.xOffset = xOffset;
+    }
+
+    public int getYOffset() {
+        return yOffset;
+    }
+
+    public void setYOffset(int yOffset) {
+        this.yOffset = yOffset;
+    }
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        if (tag != null) {
+            bounds = getBounds(component);
+            this.graphics = graphics;
+        }
+
+        return graphics;
+    }
+
+    public void update() {
+        if (tag != null) {
+            graphics.translate(bounds.x, bounds.y);
+            tag.paint(graphics);
+        }
+    }
+
+    public Bounds getBounds(Component component) {
+        Bounds bounds;
+
+        if (tag == null) {
+            bounds = null;
+        } else {
+            bounds = new Bounds();
+
+            switch (horizontalAlignment) {
+                case LEFT: {
+                    bounds.x = xOffset;
+                    break;
+                }
+
+                case RIGHT: {
+                    bounds.x = component.getWidth() - tag.getWidth() + xOffset;
+                    break;
+                }
+
+                case CENTER: {
+                    bounds.x = (component.getWidth() - tag.getWidth()) / 2 + xOffset;
+                    break;
+                }
+            }
+
+            switch (verticalAlignment) {
+                case TOP: {
+                    bounds.y = yOffset;
+                    break;
+                }
+
+                case BOTTOM: {
+                    bounds.y = component.getHeight() - tag.getHeight() + yOffset;
+                    break;
+                }
+
+                case CENTER: {
+                    bounds.y = (component.getHeight() - tag.getHeight()) / 2 + yOffset;
+                    break;
+                }
+            }
+
+            bounds.width = tag.getWidth();
+            bounds.height = tag.getHeight();
+        }
+
+        return bounds;
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return new AffineTransform();
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/Transition.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/Transition.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/Transition.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/Transition.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,282 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import pivot.wtk.ApplicationContext;
+
+/**
+ * Abstract base class for "transitions", which are animated application
+ * effects.
+ *
+ * @author gbrown
+ * @author tvolkert
+ */
+public abstract class Transition {
+    private int duration;
+    private int rate;
+    private boolean repeat;
+
+    private TransitionListener transitionListener;
+
+    private long startTime = 0;
+    private long currentTime = 0;
+    private ApplicationContext.ScheduledCallback transitionCallback = null;
+
+    public static final int DEFAULT_DURATION = 0;
+    public static final int DEFAULT_RATE = 15;
+
+    private final Runnable updateCallback = new Runnable() {
+        public void run() {
+            currentTime = System.currentTimeMillis();
+
+            long endTime = startTime + duration;
+            if (currentTime >= endTime) {
+                if (repeat) {
+                    startTime = endTime;
+                } else {
+                    currentTime = endTime;
+                    stop();
+
+                    if (transitionListener != null) {
+                        transitionListener.transitionCompleted(Transition.this);
+                    }
+                }
+            }
+
+            update();
+        }
+    };
+
+    /**
+     * Creates a new, non-repeating transition with the default duration
+     * and rate.
+     */
+    public Transition() {
+        this(DEFAULT_DURATION, DEFAULT_RATE, false);
+    }
+
+    /**
+     * Creates a new transition with the given duration, rate, and repeat.
+     *
+     * @param duration
+     * Transition duration, in milliseconds.
+     *
+     * @param rate
+     * Transition rate, in frames per second.
+     */
+    public Transition(int duration, int rate, boolean repeat) {
+        if (duration <= 0) {
+            throw new IllegalArgumentException("duration must be positive.");
+        }
+
+        this.duration = duration;
+        this.rate = rate;
+        this.repeat = repeat;
+    }
+
+    /**
+     * Returns the transition duration.
+     *
+     * @return
+     * The duration of the transition, in milliseconds.
+     *
+     * @see #setDuration(int)
+     */
+    public int getDuration() {
+        return duration;
+    }
+
+    /**
+     * Sets the transition duration, the length of time the transition is
+     * scheduled to run.
+     *
+     * @param duration
+     * The duration of the transition, in milliseconds.
+     */
+    public void setDuration(int duration) {
+        if (duration < 0) {
+            throw new IllegalArgumentException("duration is negative.");
+        }
+
+        if (transitionCallback != null) {
+            throw new IllegalStateException("Transition is currently running.");
+        }
+
+        this.duration = duration;
+    }
+
+    /**
+     * Returns the transition rate.
+     *
+     * @return
+     * The rate of the transition, in frames per second.
+     *
+     * @see #setRate(int)
+     */
+    public int getRate() {
+        return rate;
+    }
+
+    /**
+     * Sets the transition rate, the number of times the transition will be
+     * updated within the span of one second.
+     *
+     * @param rate
+     * The transition rate, in frames per second.
+     */
+    public void setRate(int rate) {
+        if (rate < 0) {
+            throw new IllegalArgumentException("rate is negative.");
+        }
+
+        if (transitionCallback != null) {
+            throw new IllegalStateException("Transition is currently running.");
+        }
+
+        this.rate = rate;
+    }
+
+    /**
+     * Returns the transition interval, the number of milliseconds between
+     * updates.
+     *
+     * @return
+     * The transition interval, in milliseconds.
+     */
+    public int getInterval() {
+        return (int)((1f / (float)rate) * 1000);
+    }
+
+    /**
+     * Returns the time at which the transition was started.
+     *
+     * @return
+     * The transition's start time.
+     */
+    public long getStartTime() {
+        return startTime;
+    }
+
+    /**
+     * Returns the last time the transition was updated.
+     *
+     * @return
+     * The most recent update time.
+     */
+    public long getCurrentTime() {
+        return currentTime;
+    }
+
+    /**
+     * Returns the elapsed time since the transition started.
+     *
+     * @return
+     * Returns the amount of time that has passed since the transition
+     * was started.
+     */
+    public int getElapsedTime() {
+        return (int)(currentTime - startTime);
+    }
+
+    /**
+     * Returns the percentage of the transition that has completed.
+     *
+     * @return
+     * A value between 0 and 1, inclusive, representing the transition's
+     * percent complete.
+     */
+    public float getPercentComplete() {
+        return (float)(currentTime - startTime) / (float)(duration);
+    }
+
+    /**
+     * Tells whether or not the transition is currently running.
+     *
+     * @return
+     * <tt>true</tt> if the transition is currently running; <tt>false</tt> if
+     * it is not
+     */
+    public boolean isRunning() {
+        return (transitionCallback != null);
+    }
+
+    /**
+     * Starts the transition. Calls {@link #update()} to establish the
+     * initial state and starts a timer that will repeatedly call
+     * {@link #update()} at the current rate.
+     */
+    public final void start() {
+        start(null);
+    }
+
+    /**
+     * Starts the transition. Calls {@link #update()} to establish the
+     * initial state and starts a timer that will repeatedly call
+     * {@link #update()} at the current rate. The specified
+     * <tt>TransitionListener</tt> will be notified when the transition
+     * completes.
+     *
+     * @param transitionListener
+     * The listener to get notified when the transition completes, or
+     * <tt>null</tt> if no notification is necessary
+     */
+    public void start(TransitionListener transitionListener) {
+        if (transitionCallback != null) {
+            throw new IllegalStateException("Transition is currently running.");
+        }
+
+        this.transitionListener = transitionListener;
+
+        startTime = System.currentTimeMillis();
+        currentTime = startTime;
+
+        transitionCallback = ApplicationContext.scheduleRecurringCallback(updateCallback,
+            getInterval());
+
+        update();
+    }
+
+    /**
+     * Stops the transition. Does not fire a
+     * {@link TransitionListener#transitionCompleted(Transition)} event.
+     */
+    public void stop() {
+        if (transitionCallback != null) {
+            transitionCallback.cancel();
+        }
+
+        transitionCallback = null;
+    }
+
+    /**
+     * "Fast-forwards" to the end of the transition and fires a
+     * {@link TransitionListener#transitionCompleted(Transition)} event.
+     */
+    public void end() {
+    	if (transitionCallback != null) {
+        	currentTime = startTime + duration;
+        	stop();
+        	update();
+        	transitionListener.transitionCompleted(this);
+    	}
+    }
+
+    /**
+     * Called repeatedly while the transition is running to update the
+     * transition's state.
+     */
+    protected abstract void update();
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TransitionListener.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TransitionListener.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TransitionListener.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TransitionListener.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+/**
+ * Transition listener interface.
+ *
+ * @author tvolkert
+ */
+public interface TransitionListener {
+    /**
+     * Called when a transition has completed.
+     *
+     * @param transition
+     */
+    public void transitionCompleted(Transition transition);
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TranslationDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TranslationDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TranslationDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/TranslationDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+
+import pivot.wtk.Bounds;
+import pivot.wtk.Component;
+import pivot.wtk.Point;
+
+/**
+ * Decorator that translates the paint origin of its component.
+ *
+ * @author gbrown
+ */
+public class TranslationDecorator implements Decorator {
+    private int x = 0;
+    private int y = 0;
+    private boolean clip = false;
+
+    public TranslationDecorator() {
+    }
+
+    public TranslationDecorator(boolean clip) {
+        setClip(clip);
+    }
+
+    public TranslationDecorator(int x, int y, boolean clip) {
+        setOffset(x, y);
+        setClip(clip);
+    }
+
+    public int getX() {
+        return x;
+    }
+
+    public void setX(int x) {
+        this.x = x;
+    }
+
+    public int getY() {
+        return y;
+    }
+
+    public void setY(int y) {
+        this.y = y;
+    }
+
+    public Point getOffset() {
+        return new Point(x, y);
+    }
+
+    public void setOffset(Point offset) {
+        if (offset == null) {
+            throw new IllegalArgumentException("offset is null.");
+        }
+
+        setOffset(offset.x, offset.y);
+    }
+
+    public void setOffset(int x, int y) {
+        this.x = x;
+        this.y = y;
+    }
+
+    public boolean getClip() {
+        return clip;
+    }
+
+    public void setClip(boolean clip) {
+        this.clip = clip;
+    }
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        graphics.translate(x, y);
+        return graphics;
+    }
+
+    public void update() {
+        // No-op
+    }
+
+    public Bounds getBounds(Component component) {
+        Bounds bounds = new Bounds(x, y, component.getWidth(), component.getHeight());
+
+        if (clip) {
+            bounds.intersect(component.getBounds());
+        }
+
+        return bounds;
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return AffineTransform.getTranslateInstance(x, y);
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/WatermarkDecorator.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/WatermarkDecorator.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/WatermarkDecorator.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/WatermarkDecorator.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects;
+
+import java.awt.AlphaComposite;
+import java.awt.Font;
+import java.awt.Graphics2D;
+import java.awt.geom.AffineTransform;
+import java.net.URL;
+
+import pivot.wtk.Component;
+import pivot.wtk.FlowPane;
+import pivot.wtk.ImageView;
+import pivot.wtk.Label;
+import pivot.wtk.Orientation;
+import pivot.wtk.Bounds;
+import pivot.wtk.VerticalAlignment;
+import pivot.wtk.media.Image;
+
+/**
+ * Decorator that paints a watermark effect over a component.
+ *
+ * @author tvolkert
+ */
+public class WatermarkDecorator implements Decorator {
+    private float opacity = 0.075f;
+    private double theta = Math.PI / 4;
+
+    private FlowPane flowPane = new FlowPane(Orientation.HORIZONTAL);
+    private ImageView imageView = new ImageView();
+    private Label label = new Label();
+
+    private Component component = null;
+    private Graphics2D graphics = null;
+
+    /**
+     * Cretes a new <tt>WatermarkDecorator</tt> with no text or image.
+     */
+    public WatermarkDecorator() {
+        this(null, null);
+    }
+
+    /**
+     * Cretes a new <tt>WatermarkDecorator</tt> with the specified string as
+     * its text and no image.
+     *
+     * @param text
+     * The text to paint over the decorated component
+     */
+    public WatermarkDecorator(String text) {
+        this(text, null);
+    }
+
+    /**
+     * Cretes a new <tt>WatermarkDecorator</tt> with no text and the specified
+     * image.
+     *
+     * @param image
+     * The image to paint over the decorated component
+     */
+    public WatermarkDecorator(Image image) {
+        this(null, image);
+    }
+
+    /**
+     * Cretes a new <tt>WatermarkDecorator</tt> with the specified text and
+     * image.
+     *
+     * @param text
+     * The text to paint over the decorated component
+     *
+     * @param image
+     * The image to paint over the decorated component
+     */
+    public WatermarkDecorator(String text, Image image) {
+        flowPane.add(imageView);
+        flowPane.add(label);
+
+        flowPane.getStyles().put("verticalAlignment", VerticalAlignment.CENTER);
+        imageView.getStyles().put("opacity", opacity);
+
+        Font font = (Font)label.getStyles().get("font");
+        label.getStyles().put("font", font.deriveFont(Font.BOLD, 60));
+
+        label.setText(text);
+        imageView.setImage(image);
+
+        validate();
+    }
+
+    /**
+     * Gets the text that will be painted over this decorator's component.
+     *
+     * @return
+     * This decorator's text
+     */
+    public String getText() {
+        return label.getText();
+    }
+
+    /**
+     * Sets the text that will be painted over this decorator's component.
+     *
+     * @param text
+     * This decorator's text
+     */
+    public void setText(String text) {
+        label.setText(text);
+        validate();
+    }
+
+    /**
+     * Gets the font that will be used when painting this decorator's text.
+     *
+     * @return
+     * This decorator's font
+     */
+    public Font getFont() {
+        return (Font)label.getStyles().get("font");
+    }
+
+    /**
+     * Sets the font that will be used when painting this decorator's text.
+     *
+     * @param font
+     * This decorator's font
+     */
+    public void setFont(Font font) {
+        label.getStyles().put("font", font);
+        validate();
+    }
+
+    /**
+     * Sets the font that will be used when painting this decorator's text.
+     *
+     * @param font
+     * This decorator's font
+     */
+    public final void setFont(String font) {
+        setFont(Font.decode(font));
+    }
+
+    /**
+     * Gets the image that will be painted over this decorator's component.
+     *
+     * @return
+     * This decorator's image
+     */
+    public Image getImage() {
+        return imageView.getImage();
+    }
+
+    /**
+     * Sets the image that will be painted over this decorator's component.
+     *
+     * @param image
+     * This decorator's image
+     */
+    public void setImage(Image image) {
+        imageView.setImage(image);
+        validate();
+    }
+
+    /**
+     * Sets the image that will be painted over this decorator's component.
+     *
+     * @param image
+     * This decorator's image
+     */
+    public void setImage(URL image) {
+        if (image == null) {
+            throw new IllegalArgumentException("image is null.");
+        }
+
+        setImage(Image.load(image));
+    }
+
+    /**
+     * Sets the image that will be painted over this decorator's component.
+     *
+     * @param image
+     * This decorator's image
+     */
+    public void setImage(String image) {
+        if (image == null) {
+            throw new IllegalArgumentException("image is null.");
+        }
+
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        setImage(classLoader.getResource(image));
+    }
+
+    /**
+     * Gets the opacity of the watermark.
+     *
+     * @return
+     * This decorator's opacity
+     */
+    public float getOpacity() {
+        return opacity;
+    }
+
+    /**
+     * Sets the opacity of the watermark.
+     *
+     * @param opacity
+     * This decorator's opacity
+     */
+    public void setOpacity(float opacity) {
+        this.opacity = opacity;
+        imageView.getStyles().put("opacity", opacity);
+    }
+
+    /**
+     * Gets the angle at the watermark will be painted, in radians.
+     *
+     * @return
+     * This decorator's watermark angle
+     */
+    public double getTheta() {
+        return theta;
+    }
+
+    /**
+     * Sets the angle at the watermark will be painted, in radians. This value
+     * must lie between <tt>0</tt> and <tt>PI / 2</tt> (inclusive).
+     *
+     * @param theta
+     * This decorator's watermark angle
+     */
+    public void setTheta(double theta) {
+        if (theta < 0
+            || theta > Math.PI / 2) {
+            throw new IllegalArgumentException("Theta must be between 0 nd PI / 2.");
+        }
+
+        this.theta = theta;
+    }
+
+    /**
+     * Sets this decorator's flow pane to its preferred size and validates it.
+     */
+    private void validate() {
+        flowPane.setSize(flowPane.getPreferredSize());
+        flowPane.validate();
+    }
+
+    public Graphics2D prepare(Component component, Graphics2D graphics) {
+        this.component = component;
+        this.graphics = graphics;
+
+        return graphics;
+    }
+
+    public void update() {
+        int width = component.getWidth();
+        int height = component.getHeight();
+
+        double sinTheta = Math.sin(theta);
+        double cosTheta = Math.cos(theta);
+
+        Graphics2D watermarkGraphics = (Graphics2D)graphics.create();
+        watermarkGraphics.setComposite(AlphaComposite.getInstance
+            (AlphaComposite.SRC_OVER, opacity));
+        watermarkGraphics.rotate(theta);
+
+        // Calculate the separation in between each repetition of the watermark
+        int dX = (int)(1.5 * flowPane.getWidth());
+        int dY = 2 * flowPane.getHeight();
+
+        // Prepare the origin of our graphics context
+        int x = 0;
+        int y = (int)(-width * sinTheta);
+        watermarkGraphics.translate(x, y);
+
+        for (int yStop = (int)(height * cosTheta), p = 0; y < yStop; y += dY, p = 1 - p) {
+            for (int xStop = (int)(height * sinTheta + width * cosTheta); x < xStop; x += dX) {
+                flowPane.paint(watermarkGraphics);
+                watermarkGraphics.translate(dX, 0);
+            }
+
+            // Move X origin back to its starting position & Y origin down
+            watermarkGraphics.translate(-x, dY);
+            x = 0;
+
+            // Shift the x back and forth to add randomness feel to pattern
+            watermarkGraphics.translate((int)((0.5f - p) * flowPane.getWidth()), 0);
+        }
+
+        watermarkGraphics.dispose();
+    }
+
+    public Bounds getBounds(Component component) {
+        return new Bounds(0, 0, component.getWidth(), component.getHeight());
+    }
+
+    public AffineTransform getTransform(Component component) {
+        return new AffineTransform();
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Circular.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Circular.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Circular.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Circular.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2003 Robert Penner, all rights reserved.
+ *
+ * This work is subject to the terms in
+ * http://www.robertpenner.com/easing_terms_of_use.html.
+ *
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects.easing;
+
+/**
+ * Circular easing operation.
+ *
+ * @author Robert Penner
+ * @author gbrown
+ */
+public class Circular implements Easing {
+    public float easeIn(float time, float begin, float change, float duration) {
+        return -change * ((float)Math.sqrt(1f - (time /= duration) * time) - 1f) + begin;
+    }
+
+    public float easeOut(float time, float begin, float change, float duration) {
+        return change * (float)Math.sqrt(1f - (time = time / duration - 1f) * time) + begin;
+    }
+
+    public float easeInOut(float time, float begin, float change, float duration) {
+        if ((time /= duration / 2f) < 1f) {
+            return -change / 2f * ((float)Math.sqrt(1f - time * time) - 1f) + begin;
+        } else {
+            return change / 2f * ((float)Math.sqrt(1f - (time -= 2f) * time) + 1f) + begin;
+        }
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Cubic.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Cubic.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Cubic.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Cubic.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2003 Robert Penner, all rights reserved.
+ *
+ * This work is subject to the terms in
+ * http://www.robertpenner.com/easing_terms_of_use.html.
+ *
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects.easing;
+
+/**
+ * Cubic easing operation.
+ *
+ * @author Robert Penner
+ * @author gbrown
+ */
+public class Cubic implements Easing {
+    public float easeIn(float time, float begin, float change, float duration) {
+        return change * (time /= duration) * time * time + begin;
+    }
+
+    public float easeOut(float time, float begin, float change, float duration) {
+        return change * ((time = time / duration - 1f) * time * time + 1f) + begin;
+    }
+
+    public float easeInOut(float time, float begin, float change, float duration) {
+        if ((time /= duration / 2f) < 1f) {
+            return change / 2f * time * time * time + begin;
+        } else {
+            return change / 2f * ((time -= 2f) * time * time + 2f) + begin;
+        }
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Easing.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Easing.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Easing.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Easing.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2003 Robert Penner, all rights reserved.
+ *
+ * This work is subject to the terms in
+ * http://www.robertpenner.com/easing_terms_of_use.html.
+ *
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects.easing;
+
+/**
+ * Base interface for easing operations.
+ *
+ * @author Robert Penner
+ * @author gbrown
+ */
+public interface Easing {
+    public float easeIn(float time, float begin, float change, float duration);
+    public float easeOut(float time, float begin, float change, float duration);
+    public float easeInOut(float time, float begin, float change, float duration);
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Exponential.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Exponential.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Exponential.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Exponential.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2003 Robert Penner, all rights reserved.
+ *
+ * This work is subject to the terms in
+ * http://www.robertpenner.com/easing_terms_of_use.html.
+ *
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects.easing;
+
+/**
+ * Exponential easing operation.
+ *
+ * @author Robert Penner
+ * @author gbrown
+ */
+public class Exponential implements Easing {
+    public float easeIn(float time, float begin, float change, float duration) {
+        return (time == 0) ? begin
+            : change * (float)Math.pow(2, 10 * (time / duration - 1f)) + begin;
+    }
+
+    public float easeOut(float time, float begin, float change, float duration) {
+        return (time == duration) ? begin + change
+            : change * ((float)-Math.pow(2, -10 * time / duration) + 1f) + begin;
+    }
+
+    public float easeInOut(float time, float begin, float change, float duration) {
+        if (time == 0) {
+            return begin;
+        }
+
+        if (time == duration) {
+            return begin + change;
+        }
+
+        if ((time /= duration / 2f) < 1) {
+            return change / 2f * (float)Math.pow(2, 10 * (time - 1)) + begin;
+        }
+
+        return change / 2f * ((float)-Math.pow(2, -10 * --time) + 2) + begin;
+    }
+}

Added: incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Linear.java
URL: http://svn.apache.org/viewvc/incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Linear.java?rev=758461&view=auto
==============================================================================
--- incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Linear.java (added)
+++ incubator/pivot/branches/1.1/wtk/src/pivot/wtk/effects/easing/Linear.java Wed Mar 25 23:08:38 2009
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2003 Robert Penner, all rights reserved.
+ *
+ * This work is subject to the terms in
+ * http://www.robertpenner.com/easing_terms_of_use.html.
+ *
+ * Copyright (c) 2008 VMware, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package pivot.wtk.effects.easing;
+
+/**
+ * Linear easing operation.
+ *
+ * @author Robert Penner
+ * @author gbrown
+ */
+public class Linear implements Easing {
+    public float easeIn(float time, float begin, float change, float duration) {
+        return change * time / duration + begin;
+    }
+
+    public float easeOut(float time, float begin, float change, float duration) {
+        return change * time / duration + begin;
+    }
+
+    public float easeInOut(float time, float begin, float change, float duration) {
+        return change * time / duration + begin;
+    }
+}