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

eu.hansolo.steelseries.tools.ContourGradientPaint Maven / Gradle / Ivy

package eu.hansolo.steelseries.tools;

import java.awt.Color;
import java.awt.Paint;
import java.awt.PaintContext;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.Transparency;
import java.awt.image.ColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.List;


/**
 * A paint class that creates a gradient that is a combination of four linear
 * gradient paints. Each of the gradients start at one of the four sides of
 * the given bounds rectangle and fill stop in the center of the boundary rectangle.
 * You could use it like the other gradient paints in the jdk (LinearGradientPaint and RadialGradientPaint).
 * You simply have to pass the boundary box of your element, a array of floats for the fractions and
 * a array of colors to the ContourGradientPaint. If you would like to create a diamond like gradient
 * you have to pass a boundary rectangle that is square (width == height).
 * Even if the name implies that it uses the contour of a shape it will always use the rectangular
 * boundary box of your shape!
 * @version 1.0
 * @author hansolo
 */
public final class ContourGradientPaint implements Paint {

    private static final Util UTIL = Util.INSTANCE;
    private final Rectangle2D BOUNDS;
    private final Float[] FRACTIONS;
    private final Color[] COLORS;
    private List colorLookup = new ArrayList(256);

    /**
     * Enhanced constructor which takes the FRACTIONS in degress from 0.0f to 360.0f and
     * also an GIVEN_OFFSET in degrees around the rotation CENTER
     * @param GIVEN_BOUNDS
     * @param GIVEN_FRACTIONS
     * @param GIVEN_COLORS
     * @throws IllegalArgumentException
     */
    public ContourGradientPaint(final Rectangle2D GIVEN_BOUNDS, final float[] GIVEN_FRACTIONS, final Color[] GIVEN_COLORS) throws IllegalArgumentException {
        // Set the values
        this.BOUNDS = GIVEN_BOUNDS;

        // Check that fractions and colors are of the same size
        if (GIVEN_FRACTIONS.length != GIVEN_COLORS.length) {
            throw new IllegalArgumentException("Fractions and colors must be equal in size");
        }

        List fractionList = new ArrayList(GIVEN_FRACTIONS.length);

        for (float fraction : GIVEN_FRACTIONS) {
            fractionList.add(fraction);
        }

        // Adjust fractions and colors array in the case where startvalue != 0.0f and/or endvalue != 1.0f
        List colorList = new ArrayList(GIVEN_COLORS.length);
        colorList.addAll(java.util.Arrays.asList(GIVEN_COLORS));

        // Assure that fractions start with 0.0f
        if (fractionList.get(0) != 0.0f) {
            fractionList.add(0, 0.0f);
            final Color TMP_COLOR = colorList.get(0);
            colorList.add(0, TMP_COLOR);
        }

        // Assure that fractions end with 1.0f
        if (fractionList.get(fractionList.size() - 1) != 1.0f) {
            fractionList.add(1.0f);
            colorList.add(GIVEN_COLORS[0]);
        }

        COLORS = new Color[colorList.size()];
        FRACTIONS = new Float[fractionList.size()];

        colorList.toArray(COLORS);
        fractionList.toArray(FRACTIONS);

        // Prepare color lookup table
        colorLookup.clear();
        colorLookup.addAll(prepareColorLookup());
    }

    private List prepareColorLookup() {
        final int SIZE = BOUNDS.getWidth() <= BOUNDS.getHeight() ? ((int) (BOUNDS.getWidth() / 2.0)) : ((int) (BOUNDS.getHeight() / 2.0));

        List tmpColorLookup = new ArrayList(SIZE);
        int relativeValue;
        for (int colorIndex = 0; colorIndex < COLORS.length - 1; colorIndex++) {
            relativeValue = 0;
            for (int value = (int) (FRACTIONS[colorIndex] * SIZE); value < (int) (FRACTIONS[colorIndex + 1] * SIZE); value++) {
                tmpColorLookup.add(UTIL.getColorFromFraction(COLORS[colorIndex], COLORS[colorIndex + 1], (int) ((FRACTIONS[colorIndex + 1] - FRACTIONS[colorIndex]) * SIZE), relativeValue));
                relativeValue++;

            }

        }

        return (ArrayList) tmpColorLookup;
    }

    @Override
    public java.awt.PaintContext createContext(final ColorModel COLOR_MODEL,
                                               final Rectangle DEVICE_BOUNDS,
                                               final Rectangle2D USER_BOUNDS,
                                               final AffineTransform TRANSFORM,
                                               final RenderingHints HINTS) {
        return new ContourGradientPaintContext();
    }

    @Override
    public int getTransparency() {
        return Transparency.TRANSLUCENT;
    }

    private final class ContourGradientPaintContext implements PaintContext {

        private final Point2D P1;
        private final Point2D P2;
        final GeneralPath SECTOR_A = new GeneralPath();
        final GeneralPath SECTOR_B = new GeneralPath();
        final GeneralPath SECTOR_C = new GeneralPath();
        final GeneralPath SECTOR_D = new GeneralPath();

        public ContourGradientPaintContext() {
            if (BOUNDS.getWidth() > BOUNDS.getHeight()) {
                this.P1 = new Point2D.Double(BOUNDS.getX() + BOUNDS.getHeight() / 2.0, BOUNDS.getY() + BOUNDS.getHeight() / 2.0);
                this.P2 = new Point2D.Double(BOUNDS.getMaxX() - BOUNDS.getHeight() / 2.0, BOUNDS.getMinY() + BOUNDS.getHeight() / 2.0);
            } else if (BOUNDS.getWidth() < BOUNDS.getHeight()) {
                this.P1 = new Point2D.Double(BOUNDS.getX() + BOUNDS.getWidth() / 2.0, BOUNDS.getY() + BOUNDS.getWidth() / 2.0);
                this.P2 = new Point2D.Double(BOUNDS.getX() + BOUNDS.getWidth() / 2.0, BOUNDS.getMaxY() - BOUNDS.getWidth() / 2.0);
            } else {
                this.P1 = new Point2D.Double(BOUNDS.getX() + BOUNDS.getWidth() / 2.0, BOUNDS.getY() + BOUNDS.getHeight() / 2.0);
                this.P2 = P1;
            }

            // Definition of the 4 sectors
            SECTOR_A.moveTo(BOUNDS.getMinX(), BOUNDS.getMaxY());
            SECTOR_A.lineTo(P1.getX(), P2.getY());
            SECTOR_A.lineTo(P1.getX(), P1.getY());
            SECTOR_A.lineTo(BOUNDS.getMinX(), BOUNDS.getMinY());
            SECTOR_A.closePath();

            SECTOR_B.moveTo(BOUNDS.getMinX(), BOUNDS.getMinY());
            SECTOR_B.lineTo(P1.getX(), P1.getY());
            SECTOR_B.lineTo(P2.getX(), P1.getY());
            SECTOR_B.lineTo(BOUNDS.getMaxX(), BOUNDS.getMinY());
            SECTOR_B.closePath();

            SECTOR_C.moveTo(BOUNDS.getMaxX(), BOUNDS.getMinY());
            SECTOR_C.lineTo(P2.getX(), P1.getY());
            SECTOR_C.lineTo(P2.getX(), P2.getY());
            SECTOR_C.lineTo(BOUNDS.getMaxX(), BOUNDS.getMaxY());
            SECTOR_C.closePath();

            SECTOR_D.moveTo(BOUNDS.getMaxX(), BOUNDS.getMaxY());
            SECTOR_D.lineTo(P2.getX(), P2.getY());
            SECTOR_D.lineTo(P1.getX(), P2.getY());
            SECTOR_D.lineTo(BOUNDS.getMinX(), BOUNDS.getMaxY());
            SECTOR_D.closePath();
        }

        @Override
        public void dispose() {
        }

        @Override
        public ColorModel getColorModel() {
            return ColorModel.getRGBdefault();
        }

        @Override
        public Raster getRaster(final int X, final int Y, final int TILE_WIDTH, final int TILE_HEIGHT) {
            // The moving point
            final Point P = new Point(0, 0);

            // Create raster for given colormodel
            final WritableRaster RASTER = getColorModel().createCompatibleWritableRaster(TILE_WIDTH, TILE_HEIGHT);

            // Create data array with place for red, green, blue and alpha values
            int[] data = new int[(TILE_WIDTH * TILE_HEIGHT * 4)];

            Color currentColor = new Color(0.0f, 0.0f, 0.0f, 0.0f);
            int currentRed = 0;
            int currentGreen = 0;
            int currentBlue = 0;
            int currentAlpha = 0;

            for (int tileY = 0; tileY < TILE_HEIGHT; tileY++) {
                for (int tileX = 0; tileX < TILE_WIDTH; tileX++) {
                    P.setLocation(X + tileX, Y + tileY);
                    if (SECTOR_A.contains(P)) {
                        if (X + tileX - BOUNDS.getBounds().x < colorLookup.size()) {
                            currentColor = colorLookup.get(X + tileX - BOUNDS.getBounds().x);
                        }
                    }

                    if (SECTOR_B.contains(P)) {
                        if (Y + tileY - BOUNDS.getBounds().y < colorLookup.size()) {
                            currentColor = colorLookup.get(Y + tileY - BOUNDS.getBounds().y);
                        }
                    }

                    if (SECTOR_C.contains(P)) {
                        if (colorLookup.size() - (X + tileX - SECTOR_A.getBounds().width - BOUNDS.getBounds().x - ((int) P2.getX() - (int) P1.getX())) < colorLookup.size()) {
                            currentColor = colorLookup.get(colorLookup.size() - (X + tileX - SECTOR_A.getBounds().width - BOUNDS.getBounds().x - ((int) P2.getX() - (int) P1.getX())));
                        }
                    }

                    if (SECTOR_D.contains(P)) {
                        if ((colorLookup.size() - (Y + tileY - SECTOR_B.getBounds().height - BOUNDS.getBounds().y - ((int) P2.getY() - (int) P1.getY()))) < colorLookup.size()) {
                            currentColor = colorLookup.get(colorLookup.size() - (Y + tileY - SECTOR_B.getBounds().height - BOUNDS.getBounds().y - ((int) P2.getY() - (int) P1.getY())));
                        }
                    }

                    // Split the current color in it's parts
                    currentRed = currentColor.getRed();
                    currentGreen = currentColor.getGreen();
                    currentBlue = currentColor.getBlue();
                    currentAlpha = currentColor.getAlpha();

                    // Fill data array with calculated color values
                    final int BASE = (tileY * TILE_WIDTH + tileX) * 4;
                    data[BASE + 0] = currentRed;
                    data[BASE + 1] = currentGreen;
                    data[BASE + 2] = currentBlue;
                    data[BASE + 3] = currentAlpha;
                }
            }

            // Fill the raster with the data
            RASTER.setPixels(0, 0, TILE_WIDTH, TILE_HEIGHT, data);

            return RASTER;
        }
    }

    @Override
    public String toString() {
        return "ContourGradientPaint";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy