/* * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.plaf.nimbus; import java.awt.*;import java.awt.image.*;import java.lang.reflect.Method;import javax.swing.*;import javax.swing.plaf.UIResource;import javax.swing.Painter;import java.awt.print.PrinterGraphics; /** * Convenient base class for defining Painter instances for rendering a * region or component in Nimbus. * * @author Jasper Potts * @author Richard Bair */public abstract class AbstractRegionPainter implements Painter<JComponent> { /** * PaintContext, which holds a lot of the state needed for cache hinting and x/y value decoding * The data contained within the context is typically only computed once and reused over * multiple paint calls, whereas the other values (w, h, f, leftWidth, etc) are recomputed * for each call to paint. * * This field is retrieved from subclasses on each paint operation. It is up * to the subclass to compute and cache the PaintContext over multiple calls. */ private PaintContext ctx; /** * The scaling factor. Recomputed on each call to paint. */ private float f; /* Various metrics used for decoding x/y values based on the canvas size and stretching insets. On each call to paint, we first ask the subclass for the PaintContext. From the context we get the canvas size and stretching insets, and whether the algorithm should be "inverted", meaning the center section remains a fixed size and the other sections scale. We then use these values to compute a series of metrics (listed below) which are used to decode points in a specific axis (x or y). The leftWidth represents the distance from the left edge of the region to the first stretching inset, after accounting for any scaling factor (such as DPI scaling). The centerWidth is the distance between the leftWidth and the rightWidth. The rightWidth is the distance from the right edge, to the right inset (after scaling has been applied). The same logic goes for topHeight, centerHeight, and bottomHeight. The leftScale represents the proportion of the width taken by the left section. The same logic is applied to the other scales. The various widths/heights are used to decode control points. The various scales are used to decode bezier handles (or anchors). */ /** * The width of the left section. Recomputed on each call to paint. */ private float leftWidth; /** * The height of the top section. Recomputed on each call to paint. */ private float topHeight; /** * The width of the center section. Recomputed on each call to paint. */ private float centerWidth; /** * The height of the center section. Recomputed on each call to paint. */ private float centerHeight; /** * The width of the right section. Recomputed on each call to paint. */ private float rightWidth; /** * The height of the bottom section. Recomputed on each call to paint. */ private float bottomHeight; /** * The scaling factor to use for the left section. Recomputed on each call to paint. */ private float leftScale; /** * The scaling factor to use for the top section. Recomputed on each call to paint. */ private float topScale; /** * The scaling factor to use for the center section, in the horizontal * direction. Recomputed on each call to paint. */ private float centerHScale; /** * The scaling factor to use for the center section, in the vertical * direction. Recomputed on each call to paint. */ private float centerVScale; /** * The scaling factor to use for the right section. Recomputed on each call to paint. */ private float rightScale; /** * The scaling factor to use for the bottom section. Recomputed on each call to paint. */ private float bottomScale; /** * Create a new AbstractRegionPainter */ protected AbstractRegionPainter() { } /** * @inheritDoc */ @Override public final void paint(Graphics2D g, JComponent c, int w, int h) { //don't render if the width/height are too small if (w <= 0 || h <=0) return; Object[] extendedCacheKeys = getExtendedCacheKeys(c); ctx = getPaintContext(); PaintContext.CacheMode cacheMode = ctx == null ? PaintContext.CacheMode.NO_CACHING : ctx.cacheMode; if (cacheMode == PaintContext.CacheMode.NO_CACHING || !ImageCache.getInstance().isImageCachable(w, h) || g instanceof PrinterGraphics) { // no caching so paint directly paint0(g, c, w, h, extendedCacheKeys); } else if (cacheMode == PaintContext.CacheMode.FIXED_SIZES) { paintWithFixedSizeCaching(g, c, w, h, extendedCacheKeys); } else { // 9 Square caching paintWith9SquareCaching(g, ctx, c, w, h, extendedCacheKeys); } } /** * Get any extra attributes which the painter implementation would like * to include in the image cache lookups. This is checked for every call * of the paint(g, c, w, h) method. * * @param c The component on the current paint call * @return Array of extra objects to be included in the cache key */ protected Object[] getExtendedCacheKeys(JComponent c) { return null; } /** * <p>Gets the PaintContext for this painting operation. This method is called on every * paint, and so should be fast and produce no garbage. The PaintContext contains * information such as cache hints. It also contains data necessary for decoding * points at runtime, such as the stretching insets, the canvas size at which the * encoded points were defined, and whether the stretching insets are inverted.</p> * * <p> This method allows for subclasses to package the painting of different states * with possibly different canvas sizes, etc, into one AbstractRegionPainter implementation.</p> * * @return a PaintContext associated with this paint operation. */ protected abstract PaintContext getPaintContext(); /** * <p>Configures the given Graphics2D. Often, rendering hints or compositiing rules are * applied to a Graphics2D object prior to painting, which should affect all of the * subsequent painting operations. This method provides a convenient hook for configuring * the Graphics object prior to rendering, regardless of whether the render operation is * performed to an intermediate buffer or directly to the display.</p> * * @param g The Graphics2D object to configure. Will not be null. */ protected void configureGraphics(Graphics2D g) { g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } /** * Actually performs the painting operation. Subclasses must implement this method. * The graphics object passed may represent the actual surface being rendererd to, * or it may be an intermediate buffer. It has also been pre-translated. Simply render * the component as if it were located at 0, 0 and had a width of <code>width</code> * and a height of <code>height</code>. For performance reasons, you may want to read * the clip from the Graphics2D object and only render within that space. * * @param g The Graphics2D surface to paint to * @param c The JComponent related to the drawing event. For example, if the * region being rendered is Button, then <code>c</code> will be a * JButton. If the region being drawn is ScrollBarSlider, then the * component will be JScrollBar. This value may be null. * @param width The width of the region to paint. Note that in the case of * painting the foreground, this value may differ from c.getWidth(). * @param height The height of the region to paint. Note that in the case of * painting the foreground, this value may differ from c.getHeight(). * @param extendedCacheKeys The result of the call to getExtendedCacheKeys() */ protected abstract void doPaint(Graphics2D g, JComponent c, int width, int height, Object[] extendedCacheKeys); /** * Decodes and returns a float value representing the actual pixel location for * the given encoded X value. * * @param x an encoded x value (0...1, or 1...2, or 2...3) * @return the decoded x value * @throws IllegalArgumentException * if {@code x < 0} or {@code x > 3} */ protected final float decodeX(float x) { if (x >= 0 && x <= 1) { return x * leftWidth; } else if (x > 1 && x < 2) { return ((x-1) * centerWidth) + leftWidth; } else if (x >= 2 && x <= 3) { return ((x-2) * rightWidth) + leftWidth + centerWidth; } else { throw new IllegalArgumentException("Invalid x"); } } /** * Decodes and returns a float value representing the actual pixel location for * the given encoded y value. * * @param y an encoded y value (0...1, or 1...2, or 2...3) * @return the decoded y value * @throws IllegalArgumentException * if {@code y < 0} or {@code y > 3} */ protected final float decodeY(float y) { if (y >= 0 && y <= 1) { return y * topHeight; } else if (y > 1 && y < 2) { return ((y-1) * centerHeight) + topHeight; } else if (y >= 2 && y <= 3) { return ((y-2) * bottomHeight) + topHeight + centerHeight; } else { throw new IllegalArgumentException("Invalid y"); } } /** * Decodes and returns a float value representing the actual pixel location for * the anchor point given the encoded X value of the control point, and the offset * distance to the anchor from that control point. * * @param x an encoded x value of the bezier control point (0...1, or 1...2, or 2...3) * @param dx the offset distance to the anchor from the control point x * @return the decoded x location of the control point * @throws IllegalArgumentException * if {@code x < 0} or {@code x > 3} */ protected final float decodeAnchorX(float x, float dx) { if (x >= 0 && x <= 1) { return decodeX(x) + (dx * leftScale); } else if (x > 1 && x < 2) { return decodeX(x) + (dx * centerHScale); } else if (x >= 2 && x <= 3) { return decodeX(x) + (dx * rightScale); } else { throw new IllegalArgumentException("Invalid x"); } } /** * Decodes and returns a float value representing the actual pixel location for * the anchor point given the encoded Y value of the control point, and the offset * distance to the anchor from that control point. * * @param y an encoded y value of the bezier control point (0...1, or 1...2, or 2...3) * @param dy the offset distance to the anchor from the control point y * @return the decoded y position of the control point * @throws IllegalArgumentException * if {@code y < 0} or {@code y > 3} */ protected final float decodeAnchorY(float y, float dy) { if (y >= 0 && y <= 1) { return decodeY(y) + (dy * topScale); } else if (y > 1 && y < 2) { return decodeY(y) + (dy * centerVScale); } else if (y >= 2 && y <= 3) { return decodeY(y) + (dy * bottomScale); } else { throw new IllegalArgumentException("Invalid y"); } } /** * Decodes and returns a color, which is derived from a base color in UI * defaults. * * @param key A key corrosponding to the value in the UI Defaults table * of UIManager where the base color is defined * @param hOffset The hue offset used for derivation. * @param sOffset The saturation offset used for derivation. * @param bOffset The brightness offset used for derivation. * @param aOffset The alpha offset used for derivation. Between 0...255 * @return The derived color, whos color value will change if the parent * uiDefault color changes. */ protected final Color decodeColor(String key, float hOffset, float sOffset, float bOffset, int aOffset) { if (UIManager.getLookAndFeel() instanceof NimbusLookAndFeel){ NimbusLookAndFeel laf = (NimbusLookAndFeel) UIManager.getLookAndFeel(); return laf.getDerivedColor(key, hOffset, sOffset, bOffset, aOffset, true); } else { // can not give a right answer as painter sould not be used outside // of nimbus laf but do the best we can return Color.getHSBColor(hOffset,sOffset,bOffset); } } /** * Decodes and returns a color, which is derived from a offset between two * other colors. * * @param color1 The first color * @param color2 The second color * @param midPoint The offset between color 1 and color 2, a value of 0.0 is * color 1 and 1.0 is color 2; * @return The derived color */ protected final Color decodeColor(Color color1, Color color2, float midPoint) { return new Color(NimbusLookAndFeel.deriveARGB(color1, color2, midPoint)); } /** * Given parameters for creating a LinearGradientPaint, this method will * create and return a linear gradient paint. One primary purpose for this * method is to avoid creating a LinearGradientPaint where the start and * end points are equal. In such a case, the end y point is slightly * increased to avoid the overlap. * * @param x1 * @param y1 * @param x2 * @param y2 * @param midpoints * @param colors * @return a valid LinearGradientPaint. This method never returns null. * @throws NullPointerException * if {@code midpoints} array is null, * or {@code colors} array is null, * @throws IllegalArgumentException * if start and end points are the same points, * or {@code midpoints.length != colors.length}, * or {@code colors} is less than 2 in size, * or a {@code midpoints} value is less than 0.0 or greater than 1.0, * or the {@code midpoints} are not provided in strictly increasing order */ protected final LinearGradientPaint decodeGradient(float x1, float y1, float x2, float y2, float[] midpoints, Color[] colors) { if (x1 == x2 && y1 == y2) { y2 += .00001f; } return new LinearGradientPaint(x1, y1, x2, y2, midpoints, colors); } /** * Given parameters for creating a RadialGradientPaint, this method will * create and return a radial gradient paint. One primary purpose for this * method is to avoid creating a RadialGradientPaint where the radius * is non-positive. In such a case, the radius is just slightly * increased to avoid 0. * * @param x * @param y * @param r * @param midpoints * @param colors * @return a valid RadialGradientPaint. This method never returns null. * @throws NullPointerException * if {@code midpoints} array is null, * or {@code colors} array is null * @throws IllegalArgumentException * if {@code r} is non-positive, * or {@code midpoints.length != colors.length}, * or {@code colors} is less than 2 in size, * or a {@code midpoints} value is less than 0.0 or greater than 1.0, * or the {@code midpoints} are not provided in strictly increasing order */ protected final RadialGradientPaint decodeRadialGradient(float x, float y, float r, float[] midpoints, Color[] colors) { if (r == 0f) { r = .00001f; } return new RadialGradientPaint(x, y, r, midpoints, colors); } /** * Get a color property from the given JComponent. First checks for a * <code>getXXX()</code> method and if that fails checks for a client * property with key <code>property</code>. If that still fails to return * a Color then <code>defaultColor</code> is returned. * * @param c The component to get the color property from * @param property The name of a bean style property or client property * @param defaultColor The color to return if no color was obtained from * the component. * @return The color that was obtained from the component or defaultColor */ protected final Color getComponentColor(JComponent c, String property, Color defaultColor, float saturationOffset, float brightnessOffset, int alphaOffset) { Color color = null; if (c != null) { // handle some special cases for performance if ("background".equals(property)) { color = c.getBackground(); } else if ("foreground".equals(property)) { color = c.getForeground(); } else if (c instanceof JList && "selectionForeground".equals(property)) { color = ((JList) c).getSelectionForeground(); } else if (c instanceof JList && "selectionBackground".equals(property)) { color = ((JList) c).getSelectionBackground(); } else if (c instanceof JTable && "selectionForeground".equals(property)) { color = ((JTable) c).getSelectionForeground(); } else if (c instanceof JTable && "selectionBackground".equals(property)) { color = ((JTable) c).getSelectionBackground(); } else { String s = "get" + Character.toUpperCase(property.charAt(0)) + property.substring(1); try { Method method = c.getClass().getMethod(s); color = (Color) method.invoke(c); } catch (Exception e) { //don't do anything, it just didn't work, that's all. //This could be a normal occurance if you use a property //name referring to a key in clientProperties instead of //a real property } if (color == null) { Object value = c.getClientProperty(property); if (value instanceof Color) { color = (Color) value; } } } } // we return the defaultColor if the color found is null, or if // it is a UIResource. This is done because the color for the // ENABLED state is set on the component, but you don't want to use // that color for the over state. So we only respect the color // specified for the property if it was set by the user, as opposed // to set by us. if (color == null || color instanceof UIResource) { return defaultColor; } else if (saturationOffset != 0 || brightnessOffset != 0 || alphaOffset != 0) { float[] tmp = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null); tmp[1] = clamp(tmp[1] + saturationOffset); tmp[2] = clamp(tmp[2] + brightnessOffset); int alpha = clamp(color.getAlpha() + alphaOffset); return new Color((Color.HSBtoRGB(tmp[0], tmp[1], tmp[2]) & 0xFFFFFF) | (alpha <<24)); } else { return color; } } /** * A class encapsulating state useful when painting. Generally, instances of this * class are created once, and reused for each paint request without modification. * This class contains values useful when hinting the cache engine, and when decoding * control points and bezier curve anchors. */ protected static class PaintContext { protected static enum CacheMode { NO_CACHING, FIXED_SIZES, NINE_SQUARE_SCALE } private static Insets EMPTY_INSETS = new Insets(0, 0, 0, 0); private Insets stretchingInsets; private Dimension canvasSize; private boolean inverted; private CacheMode cacheMode; private double maxHorizontalScaleFactor;Lines 1–500 of 752. Load more
/* * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package javax.swing.plaf.nimbus; import java.awt.*; import java.awt.image.*; import java.lang.reflect.Method; import javax.swing.*; import javax.swing.plaf.UIResource; import javax.swing.Painter; import java.awt.print.PrinterGraphics; /** * Convenient base class for defining Painter instances for rendering a * region or component in Nimbus. * * @author Jasper Potts * @author Richard Bair */ public abstract class AbstractRegionPainter implements Painter<JComponent> { /** * PaintContext, which holds a lot of the state needed for cache hinting and x/y value decoding * The data contained within the context is typically only computed once and reused over * multiple paint calls, whereas the other values (w, h, f, leftWidth, etc) are recomputed * for each call to paint. * * This field is retrieved from subclasses on each paint operation. It is up * to the subclass to compute and cache the PaintContext over multiple calls. */ private PaintContext ctx; /** * The scaling factor. Recomputed on each call to paint. */ private float f; /* Various metrics used for decoding x/y values based on the canvas size and stretching insets. On each call to paint, we first ask the subclass for the PaintContext. From the context we get the canvas size and stretching insets, and whether the algorithm should be "inverted", meaning the center section remains a fixed size and the other sections scale. We then use these values to compute a series of metrics (listed below) which are used to decode points in a specific axis (x or y). The leftWidth represents the distance from the left edge of the region to the first stretching inset, after accounting for any scaling factor (such as DPI scaling). The centerWidth is the distance between the leftWidth and the rightWidth. The rightWidth is the distance from the right edge, to the right inset (after scaling has been applied). The same logic goes for topHeight, centerHeight, and bottomHeight. The leftScale represents the proportion of the width taken by the left section. The same logic is applied to the other scales. The various widths/heights are used to decode control points. The various scales are used to decode bezier handles (or anchors). */ /** * The width of the left section. Recomputed on each call to paint. */ private float leftWidth; /** * The height of the top section. Recomputed on each call to paint. */ private float topHeight; /** * The width of the center section. Recomputed on each call to paint. */ private float centerWidth; /** * The height of the center section. Recomputed on each call to paint. */ private float centerHeight; /** * The width of the right section. Recomputed on each call to paint. */ private float rightWidth; /** * The height of the bottom section. Recomputed on each call to paint. */ private float bottomHeight; /** * The scaling factor to use for the left section. Recomputed on each call to paint. */ private float leftScale; /** * The scaling factor to use for the top section. Recomputed on each call to paint. */ private float topScale; /** * The scaling factor to use for the center section, in the horizontal * direction. Recomputed on each call to paint. */ private float centerHScale; /** * The scaling factor to use for the center section, in the vertical * direction. Recomputed on each call to paint. */ private float centerVScale; /** * The scaling factor to use for the right section. Recomputed on each call to paint. */ private float rightScale; /** * The scaling factor to use for the bottom section. Recomputed on each call to paint. */ private float bottomScale; /** * Create a new AbstractRegionPainter */ protected AbstractRegionPainter() { } /** * @inheritDoc */ @Override public final void paint(Graphics2D g, JComponent c, int w, int h) { //don't render if the width/height are too small if (w <= 0 || h <=0) return; Object[] extendedCacheKeys = getExtendedCacheKeys(c); ctx = getPaintContext(); PaintContext.CacheMode cacheMode = ctx == null ? PaintContext.CacheMode.NO_CACHING : ctx.cacheMode; if (cacheMode == PaintContext.CacheMode.NO_CACHING || !ImageCache.getInstance().isImageCachable(w, h) || g instanceof PrinterGraphics) { // no caching so paint directly paint0(g, c, w, h, extendedCacheKeys); } else if (cacheMode == PaintContext.CacheMode.FIXED_SIZES) { paintWithFixedSizeCaching(g, c, w, h, extendedCacheKeys); } else { // 9 Square caching paintWith9SquareCaching(g, ctx, c, w, h, extendedCacheKeys); } } /** * Get any extra attributes which the painter implementation would like * to include in the image cache lookups. This is checked for every call * of the paint(g, c, w, h) method. * * @param c The component on the current paint call * @return Array of extra objects to be included in the cache key */ protected Object[] getExtendedCacheKeys(JComponent c) { return null; } /** * <p>Gets the PaintContext for this painting operation. This method is called on every * paint, and so should be fast and produce no garbage. The PaintContext contains * information such as cache hints. It also contains data necessary for decoding * points at runtime, such as the stretching insets, the canvas size at which the * encoded points were defined, and whether the stretching insets are inverted.</p> * * <p> This method allows for subclasses to package the painting of different states * with possibly different canvas sizes, etc, into one AbstractRegionPainter implementation.</p> * * @return a PaintContext associated with this paint operation. */ protected abstract PaintContext getPaintContext(); /** * <p>Configures the given Graphics2D. Often, rendering hints or compositiing rules are * applied to a Graphics2D object prior to painting, which should affect all of the * subsequent painting operations. This method provides a convenient hook for configuring * the Graphics object prior to rendering, regardless of whether the render operation is * performed to an intermediate buffer or directly to the display.</p> * * @param g The Graphics2D object to configure. Will not be null. */ protected void configureGraphics(Graphics2D g) { g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); } /** * Actually performs the painting operation. Subclasses must implement this method. * The graphics object passed may represent the actual surface being rendererd to, * or it may be an intermediate buffer. It has also been pre-translated. Simply render * the component as if it were located at 0, 0 and had a width of <code>width</code> * and a height of <code>height</code>. For performance reasons, you may want to read * the clip from the Graphics2D object and only render within that space. * * @param g The Graphics2D surface to paint to * @param c The JComponent related to the drawing event. For example, if the * region being rendered is Button, then <code>c</code> will be a * JButton. If the region being drawn is ScrollBarSlider, then the * component will be JScrollBar. This value may be null. * @param width The width of the region to paint. Note that in the case of * painting the foreground, this value may differ from c.getWidth(). * @param height The height of the region to paint. Note that in the case of * painting the foreground, this value may differ from c.getHeight(). * @param extendedCacheKeys The result of the call to getExtendedCacheKeys() */ protected abstract void doPaint(Graphics2D g, JComponent c, int width, int height, Object[] extendedCacheKeys); /** * Decodes and returns a float value representing the actual pixel location for * the given encoded X value. * * @param x an encoded x value (0...1, or 1...2, or 2...3) * @return the decoded x value * @throws IllegalArgumentException * if {@code x < 0} or {@code x > 3} */ protected final float decodeX(float x) { if (x >= 0 && x <= 1) { return x * leftWidth; } else if (x > 1 && x < 2) { return ((x-1) * centerWidth) + leftWidth; } else if (x >= 2 && x <= 3) { return ((x-2) * rightWidth) + leftWidth + centerWidth; } else { throw new IllegalArgumentException("Invalid x"); } } /** * Decodes and returns a float value representing the actual pixel location for * the given encoded y value. * * @param y an encoded y value (0...1, or 1...2, or 2...3) * @return the decoded y value * @throws IllegalArgumentException * if {@code y < 0} or {@code y > 3} */ protected final float decodeY(float y) { if (y >= 0 && y <= 1) { return y * topHeight; } else if (y > 1 && y < 2) { return ((y-1) * centerHeight) + topHeight; } else if (y >= 2 && y <= 3) { return ((y-2) * bottomHeight) + topHeight + centerHeight; } else { throw new IllegalArgumentException("Invalid y"); } } /** * Decodes and returns a float value representing the actual pixel location for * the anchor point given the encoded X value of the control point, and the offset * distance to the anchor from that control point. * * @param x an encoded x value of the bezier control point (0...1, or 1...2, or 2...3) * @param dx the offset distance to the anchor from the control point x * @return the decoded x location of the control point * @throws IllegalArgumentException * if {@code x < 0} or {@code x > 3} */ protected final float decodeAnchorX(float x, float dx) { if (x >= 0 && x <= 1) { return decodeX(x) + (dx * leftScale); } else if (x > 1 && x < 2) { return decodeX(x) + (dx * centerHScale); } else if (x >= 2 && x <= 3) { return decodeX(x) + (dx * rightScale); } else { throw new IllegalArgumentException("Invalid x"); } } /** * Decodes and returns a float value representing the actual pixel location for * the anchor point given the encoded Y value of the control point, and the offset * distance to the anchor from that control point. * * @param y an encoded y value of the bezier control point (0...1, or 1...2, or 2...3) * @param dy the offset distance to the anchor from the control point y * @return the decoded y position of the control point * @throws IllegalArgumentException * if {@code y < 0} or {@code y > 3} */ protected final float decodeAnchorY(float y, float dy) { if (y >= 0 && y <= 1) { return decodeY(y) + (dy * topScale); } else if (y > 1 && y < 2) { return decodeY(y) + (dy * centerVScale); } else if (y >= 2 && y <= 3) { return decodeY(y) + (dy * bottomScale); } else { throw new IllegalArgumentException("Invalid y"); } } /** * Decodes and returns a color, which is derived from a base color in UI * defaults. * * @param key A key corrosponding to the value in the UI Defaults table * of UIManager where the base color is defined * @param hOffset The hue offset used for derivation. * @param sOffset The saturation offset used for derivation. * @param bOffset The brightness offset used for derivation. * @param aOffset The alpha offset used for derivation. Between 0...255 * @return The derived color, whos color value will change if the parent * uiDefault color changes. */ protected final Color decodeColor(String key, float hOffset, float sOffset, float bOffset, int aOffset) { if (UIManager.getLookAndFeel() instanceof NimbusLookAndFeel){ NimbusLookAndFeel laf = (NimbusLookAndFeel) UIManager.getLookAndFeel(); return laf.getDerivedColor(key, hOffset, sOffset, bOffset, aOffset, true); } else { // can not give a right answer as painter sould not be used outside // of nimbus laf but do the best we can return Color.getHSBColor(hOffset,sOffset,bOffset); } } /** * Decodes and returns a color, which is derived from a offset between two * other colors. * * @param color1 The first color * @param color2 The second color * @param midPoint The offset between color 1 and color 2, a value of 0.0 is * color 1 and 1.0 is color 2; * @return The derived color */ protected final Color decodeColor(Color color1, Color color2, float midPoint) { return new Color(NimbusLookAndFeel.deriveARGB(color1, color2, midPoint)); } /** * Given parameters for creating a LinearGradientPaint, this method will * create and return a linear gradient paint. One primary purpose for this * method is to avoid creating a LinearGradientPaint where the start and * end points are equal. In such a case, the end y point is slightly * increased to avoid the overlap. * * @param x1 * @param y1 * @param x2 * @param y2 * @param midpoints * @param colors * @return a valid LinearGradientPaint. This method never returns null. * @throws NullPointerException * if {@code midpoints} array is null, * or {@code colors} array is null, * @throws IllegalArgumentException * if start and end points are the same points, * or {@code midpoints.length != colors.length}, * or {@code colors} is less than 2 in size, * or a {@code midpoints} value is less than 0.0 or greater than 1.0, * or the {@code midpoints} are not provided in strictly increasing order */ protected final LinearGradientPaint decodeGradient(float x1, float y1, float x2, float y2, float[] midpoints, Color[] colors) { if (x1 == x2 && y1 == y2) { y2 += .00001f; } return new LinearGradientPaint(x1, y1, x2, y2, midpoints, colors); } /** * Given parameters for creating a RadialGradientPaint, this method will * create and return a radial gradient paint. One primary purpose for this * method is to avoid creating a RadialGradientPaint where the radius * is non-positive. In such a case, the radius is just slightly * increased to avoid 0. * * @param x * @param y * @param r * @param midpoints * @param colors * @return a valid RadialGradientPaint. This method never returns null. * @throws NullPointerException * if {@code midpoints} array is null, * or {@code colors} array is null * @throws IllegalArgumentException * if {@code r} is non-positive, * or {@code midpoints.length != colors.length}, * or {@code colors} is less than 2 in size, * or a {@code midpoints} value is less than 0.0 or greater than 1.0, * or the {@code midpoints} are not provided in strictly increasing order */ protected final RadialGradientPaint decodeRadialGradient(float x, float y, float r, float[] midpoints, Color[] colors) { if (r == 0f) { r = .00001f; } return new RadialGradientPaint(x, y, r, midpoints, colors); } /** * Get a color property from the given JComponent. First checks for a * <code>getXXX()</code> method and if that fails checks for a client * property with key <code>property</code>. If that still fails to return * a Color then <code>defaultColor</code> is returned. * * @param c The component to get the color property from * @param property The name of a bean style property or client property * @param defaultColor The color to return if no color was obtained from * the component. * @return The color that was obtained from the component or defaultColor */ protected final Color getComponentColor(JComponent c, String property, Color defaultColor, float saturationOffset, float brightnessOffset, int alphaOffset) { Color color = null; if (c != null) { // handle some special cases for performance if ("background".equals(property)) { color = c.getBackground(); } else if ("foreground".equals(property)) { color = c.getForeground(); } else if (c instanceof JList && "selectionForeground".equals(property)) { color = ((JList) c).getSelectionForeground(); } else if (c instanceof JList && "selectionBackground".equals(property)) { color = ((JList) c).getSelectionBackground(); } else if (c instanceof JTable && "selectionForeground".equals(property)) { color = ((JTable) c).getSelectionForeground(); } else if (c instanceof JTable && "selectionBackground".equals(property)) { color = ((JTable) c).getSelectionBackground(); } else { String s = "get" + Character.toUpperCase(property.charAt(0)) + property.substring(1); try { Method method = c.getClass().getMethod(s); color = (Color) method.invoke(c); } catch (Exception e) { //don't do anything, it just didn't work, that's all. //This could be a normal occurance if you use a property //name referring to a key in clientProperties instead of //a real property } if (color == null) { Object value = c.getClientProperty(property); if (value instanceof Color) { color = (Color) value; } } } } // we return the defaultColor if the color found is null, or if // it is a UIResource. This is done because the color for the // ENABLED state is set on the component, but you don't want to use // that color for the over state. So we only respect the color // specified for the property if it was set by the user, as opposed // to set by us. if (color == null || color instanceof UIResource) { return defaultColor; } else if (saturationOffset != 0 || brightnessOffset != 0 || alphaOffset != 0) { float[] tmp = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null); tmp[1] = clamp(tmp[1] + saturationOffset); tmp[2] = clamp(tmp[2] + brightnessOffset); int alpha = clamp(color.getAlpha() + alphaOffset); return new Color((Color.HSBtoRGB(tmp[0], tmp[1], tmp[2]) & 0xFFFFFF) | (alpha <<24)); } else { return color; } } /** * A class encapsulating state useful when painting. Generally, instances of this * class are created once, and reused for each paint request without modification. * This class contains values useful when hinting the cache engine, and when decoding * control points and bezier curve anchors. */ protected static class PaintContext { protected static enum CacheMode { NO_CACHING, FIXED_SIZES, NINE_SQUARE_SCALE } private static Insets EMPTY_INSETS = new Insets(0, 0, 0, 0); private Insets stretchingInsets; private Dimension canvasSize; private boolean inverted; private CacheMode cacheMode; private double maxHorizontalScaleFactor;