All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jdesktop.swingx.util.PaintUtils Maven / Gradle / Ivy

There is a newer version: 1.7.2
Show newest version
/*
 * $Id: PaintUtils.java 4193 2012-06-27 19:42:05Z kschaefe $
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

package org.jdesktop.swingx.util;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.LinearGradientPaint;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.TexturePaint;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * A collection of utilities for working with Paints and Colors.
 *
 * @author Mark Davidson
 * @author [email protected]
 * @author Karl George Schaefer
 */
@SuppressWarnings("nls")
public class PaintUtils {

    private static final Logger LOG = Logger.getLogger(PaintUtils.class.getName());

    public static final GradientPaint BLUE_EXPERIENCE = new GradientPaint(
        new Point2D.Double(0, 0),
        new Color(168, 204, 241),
        new Point2D.Double(0, 1),
        new Color(44, 61, 146)
    );
    public static final GradientPaint MAC_OSX_SELECTED = new GradientPaint(
        new Point2D.Double(0, 0),
        new Color(81, 141, 236),
        new Point2D.Double(0, 1),
        new Color(36, 96, 192)
    );
    public static final GradientPaint MAC_OSX = new GradientPaint(
        new Point2D.Double(0, 0),
        new Color(167, 210, 250),
        new Point2D.Double(0, 1),
        new Color(99, 147, 206)
    );
    public static final GradientPaint AERITH = new GradientPaint(
        new Point2D.Double(0, 0),
        Color.WHITE,
        new Point2D.Double(0, 1),
        new Color(64, 110, 161)
    );
    public static final GradientPaint GRAY = new GradientPaint(
        new Point2D.Double(0, 0),
        new Color(226, 226, 226),
        new Point2D.Double(0, 1),
        new Color(250, 248, 248)
    );
    public static final GradientPaint RED_XP = new GradientPaint(
        new Point2D.Double(0, 0),
        new Color(236, 81, 81),
        new Point2D.Double(0, 1),
        new Color(192, 36, 36)
    );
    public static final GradientPaint NIGHT_GRAY = new GradientPaint(
        new Point2D.Double(0, 0),
        new Color(102, 111, 127),
        new Point2D.Double(0, 1),
        new Color(38, 45, 61)
    );
    public static final GradientPaint NIGHT_GRAY_LIGHT = new GradientPaint(
        new Point2D.Double(0, 0),
        new Color(129, 138, 155),
        new Point2D.Double(0, 1),
        new Color(58, 66, 82)
    );

    //originally included in LinearGradientPainter
    public static final Paint ORANGE_DELIGHT = new LinearGradientPaint(
        new Point2D.Double(0, 0),
        new Point2D.Double(1, 0),
        new float[] {0f, .5f, .51f, 1f},
        new Color[] {
            new Color(248, 192, 75),
            new Color(253, 152, 6),
            new Color(243, 133, 0),
            new Color(254, 124, 0)
        }
    );

    //originally included in LinearGradientPainter
    public static final Paint BLACK_STAR = new LinearGradientPaint(
        new Point2D.Double(0, 0),
        new Point2D.Double(1, 0),
        new float[] {0f, .5f, .51f, 1f},
        new Color[] {
            new Color(54, 62, 78),
            new Color(32, 39, 55),
            new Color(74, 82, 96),
            new Color(123, 132, 145)
        }
    );

    private PaintUtils() {
    }

    /**
     * Resizes a gradient to fill the width and height available. If the
     * gradient is left to right it will be resized to fill the entire width.
     * If the gradient is top to bottom it will be resized to fill the entire
     * height. If the gradient is on an angle it will be resized to go from
     * one corner to the other of the rectangle formed by (0,0 -> width,height).
     * 

* This method can resize java.awt.GradientPaint, java.awt.LinearGradientPaint, * and the LinearGradientPaint implementation from Apache's Batik project. Note, * this method does not require the MultipleGradientPaint.jar from Apache to * compile or to run. MultipleGradientPaint.jar *is* required if you want * to resize the LinearGradientPaint from that jar. *

* Any paint passed into this method which is not a kind of gradient paint (like * a Color or TexturePaint) will be returned unmodified. It will not throw * an exception. If the gradient cannot be resized due to other errors the * original paint will be returned unmodified. It will not throw an * exception. */ public static Paint resizeGradient(Paint p, int width, int height) { if (p == null) return p; if (p instanceof GradientPaint) { GradientPaint gp = (GradientPaint) p; Point2D[] pts = new Point2D[2]; pts[0] = gp.getPoint1(); pts[1] = gp.getPoint2(); pts = adjustPoints(pts, width, height); return new GradientPaint(pts[0], gp.getColor1(), pts[1], gp.getColor2(), gp.isCyclic()); } if ("java.awt.LinearGradientPaint".equals(p.getClass().getName()) || "org.apache.batik.ext.awt.LinearGradientPaint".equals(p.getClass().getName())) { return resizeLinearGradient(p, width, height); } return p; } private static Paint resizeLinearGradient(Paint p, int width, int height) { try { Point2D[] pts = new Point2D[2]; pts[0] = (Point2D) invokeMethod(p, "getStartPoint"); pts[1] = (Point2D) invokeMethod(p, "getEndPoint"); pts = adjustPoints(pts, width, height); float[] fractions = (float[]) invokeMethod(p, "getFractions"); Color[] colors = (Color[]) invokeMethod(p, "getColors"); Constructor con = p.getClass().getDeclaredConstructor(Point2D.class, Point2D.class, float[].class, Color[].class); return (Paint) con.newInstance(pts[0], pts[1], fractions, colors); } catch (Exception ex) { LOG.log(Level.WARNING, ex.getMessage(), ex); } return p; } private static Object invokeMethod(Object p, String methodName) throws Exception { Method meth = p.getClass().getMethod(methodName); return meth.invoke(p); } private static Point2D[] adjustPoints(Point2D[] pts, int width, int height) { Point2D start = pts[0]; Point2D end = pts[1]; double angle = calcAngle(start, end); double e = 1; // if it is near 0 degrees if (Math.abs(angle) < Math.toRadians(e) || Math.abs(angle) > Math.toRadians(360 - e)) { start = new Point2D.Float(0, 0); end = new Point2D.Float(normalize(end.getX(), width), 0); } // near 45 double a2 = Math.toDegrees(angle); if (isNear(a2, 45, e)) { start = new Point2D.Float(0, 0); end = new Point2D.Float(normalize(end.getX(), width), normalize(end.getY(), height)); } // near 90 if (isNear(a2, 90, e)) { start = new Point2D.Float(0, 0); end = new Point2D.Float(0, normalize(end.getY(), height)); } // near 135 if (isNear(a2, 135, e)) { start = new Point2D.Float(normalize(start.getX(), width), 0); end = new Point2D.Float(0, normalize(end.getY(), height)); } // near 180 if (isNear(a2, 180, e)) { start = new Point2D.Float(normalize(start.getX(), width), 0); end = new Point2D.Float(0, 0); } // near 225 if (isNear(a2, 225, e)) { start = new Point2D.Float(normalize(start.getX(), width), normalize(start.getY(), height)); end = new Point2D.Float(0, 0); } // near 270 if (isNear(a2, 270, e)) { start = new Point2D.Float(0, normalize(start.getY(), height)); end = new Point2D.Float(0, 0); } // near 315 if (isNear(a2, 315, e)) { start = new Point2D.Float(0, normalize(start.getY(), height)); end = new Point2D.Float(normalize(end.getX(), width), 0); } return new Point2D[] {start, end}; } private static boolean isNear(double angle, double target, double error) { return Math.abs(target - Math.abs(angle)) < error; } private static float normalize(double original, float target) { if (original < 1f) { return target * (float) original; } return target; } private static double calcAngle(Point2D p1, Point2D p2) { double xOff = p2.getX() - p1.getX(); double yOff = p2.getY() - p1.getY(); double angle = Math.atan(yOff / xOff); if (xOff < 0) { angle += Math.PI; } if (angle < 0) { angle += 2 * Math.PI; } if (angle > 2 * Math.PI) { angle -= 2 * Math.PI; } return angle; } /** * Creates a new {@code Paint} that is a checkered effect using the colors {@link Color#GRAY * gray} and {@link Color#WHITE}. * * @return a the checkered paint */ public static Paint getCheckerPaint() { return getCheckerPaint(Color.WHITE, Color.GRAY, 20); } /** * Creates a new {@code Paint} that is a checkered effect using the specified colors. *

* While this method supports transparent colors, this implementation performs painting * operations using the second color after it performs operations using the first color. This * means that to create a checkered paint with a fully-transparent color, you MUST specify that * color first. * * @param c1 the first color * @param c2 the second color * @param size the size of the paint * @return a new {@code Paint} checkering the supplied colors */ public static Paint getCheckerPaint(Paint c1, Paint c2, int size) { BufferedImage img = GraphicsUtilities.createCompatibleTranslucentImage(size, size); Graphics2D g = img.createGraphics(); try { g.setPaint(c1); g.fillRect(0, 0, size, size); g.setPaint(c2); g.fillRect(0, 0, size / 2, size / 2); g.fillRect(size / 2, size / 2, size / 2, size / 2); } finally { g.dispose(); } return new TexturePaint(img, new Rectangle(0, 0, size, size)); } /** * Creates a {@code String} that represents the supplied color as a * hex-value RGB triplet, including the "#". The return value is suitable * for use in HTML. The alpha (transparency) channel is neither include nor * used in producing the string. * * @param color the color to convert * @return the hex {@code String} */ public static String toHexString(Color color) { return "#" + Integer.toHexString(color.getRGB() | 0xFF000000).substring(2); } /** * Returns a new color equal to the old one, except that there is no alpha * (transparency) channel. *

* This method is a convenience and has the same effect as {@code * setAlpha(color, 255)}. * * @param color the color to remove the alpha (transparency) from * @return a new non-transparent {@code Color} * @throws NullPointerException if {@code color} is {@code null} */ public static Color removeAlpha(Color color) { return setAlpha(color, 255); } /** * Returns a new color equal to the old one, except alpha (transparency) * channel is set to the new value. * * @param color the color to modify * @param alpha the new alpha (transparency) level. Must be an int between 0 * and 255 * @return a new alpha-applied {@code Color} * @throws IllegalArgumentException if {@code alpha} is not between 0 and 255 inclusive * @throws NullPointerException if {@code color} is {@code null} */ public static Color setAlpha(Color color, int alpha) { if (alpha < 0 || alpha > 255) { throw new IllegalArgumentException("invalid alpha value"); } return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); } /** * Returns a new color equal to the old one, except the saturation is set to * the new value. The new color will have the same alpha (transparency) as * the original color. *

* The color is modified using HSB calculations. The saturation must be a * float between 0 and 1. If 0 the resulting color will be gray. If 1 the * resulting color will be the most saturated possible form of the passed in * color. * * @param color the color to modify * @param saturation the saturation to use in the new color * @return a new saturation-applied {@code Color} * @throws IllegalArgumentException if {@code saturation} is not between 0 and 1 inclusive * @throws NullPointerException if {@code color} is {@code null} */ public static Color setSaturation(Color color, float saturation) { if (saturation < 0f || saturation > 1f) { throw new IllegalArgumentException("invalid saturation value"); } int alpha = color.getAlpha(); float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null); Color c = Color.getHSBColor(hsb[0], saturation, hsb[2]); return setAlpha(c, alpha); } /** * Returns a new color equal to the old one, except the brightness is set to * the new value. The new color will have the same alpha (transparency) as * the original color. *

* The color is modified using HSB calculations. The brightness must be a * float between 0 and 1. If 0 the resulting color will be black. If 1 the * resulting color will be the brightest possible form of the passed in * color. * * @param color the color to modify * @param brightness the brightness to use in the new color * @return a new brightness-applied {@code Color} * @throws IllegalArgumentException if {@code brightness} is not between 0 and 1 inclusive * @throws NullPointerException if {@code color} is {@code null} */ public static Color setBrightness(Color color, float brightness) { if (brightness < 0f || brightness > 1f) { throw new IllegalArgumentException("invalid brightness value"); } int alpha = color.getAlpha(); float[] hsb = Color.RGBtoHSB(color.getRed(), color.getGreen(), color.getBlue(), null); Color c = Color.getHSBColor(hsb[0], hsb[1], brightness); return setAlpha(c, alpha); } /** * Blends two colors to create a new color. The {@code origin} color is the * base for the new color and regardless of its alpha component, it is * treated as fully opaque (alpha 255). * * @param origin the base of the new color * @param over the alpha-enabled color to add to the {@code origin} color * @return a new color comprised of the {@code origin} and {@code over} * colors */ public static Color blend(Color origin, Color over) { if (over == null) { return origin; } if (origin == null) { return over; } int a = over.getAlpha(); int rb = (over.getRGB() & 0x00ff00ff) * (a + 1) + (origin.getRGB() & 0x00ff00ff) * (0xff - a) & 0xff00ff00; int g = (over.getRGB() & 0x0000ff00) * (a + 1) + (origin.getRGB() & 0x0000ff00) * (0xff - a) & 0x00ff0000; return new Color(over.getRGB() & 0xff000000 | (rb | g) >> 8); } /** * Interpolates a color. * * @param b the first color * @param a the second color * @param t the amount to interpolate * @return a new color */ public static Color interpolate(Color b, Color a, float t) { float[] acomp = a.getRGBComponents(null); float[] bcomp = b.getRGBComponents(null); float[] ccomp = new float[4]; // log.fine(("a comp "); // for(float f : acomp) { // log.fine((f); // } // for(float f : bcomp) { // log.fine((f); // } for (int i = 0; i < 4; i++) { ccomp[i] = acomp[i] + (bcomp[i] - acomp[i]) * t; } // for(float f : ccomp) { // log.fine((f); // } return new Color(ccomp[0], ccomp[1], ccomp[2], ccomp[3]); } /** * Computes an appropriate foreground color (either white or black) for the * given background color. * * @param bg the background color * @return {@code Color.WHITE} or {@code Color.BLACK} * @throws NullPointerException if {@code bg} is {@code null} */ public static Color computeForeground(Color bg) { float[] rgb = bg.getRGBColorComponents(null); float y = .3f * rgb[0] + .59f * rgb[1] + .11f * rgb[2]; return y > .5f ? Color.BLACK : Color.WHITE; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy