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

org.pushingpixels.substance.internal.utils.SubstanceImageCreator Maven / Gradle / Ivy

There is a newer version: 4.5.0
Show newest version
/*
 * Copyright (c) 2005-2019 Radiance Kirill Grouchnikov. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  o Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  o Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 *  o Neither the name of the copyright holder nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.pushingpixels.substance.internal.utils;

import org.pushingpixels.neon.NeonCortex;
import org.pushingpixels.substance.api.ComponentState;
import org.pushingpixels.substance.api.SubstanceCortex;
import org.pushingpixels.substance.api.colorscheme.SubstanceColorScheme;
import org.pushingpixels.substance.api.painter.border.FlatBorderPainter;
import org.pushingpixels.substance.api.painter.border.SubstanceBorderPainter;
import org.pushingpixels.substance.api.painter.fill.SubstanceFillPainter;
import org.pushingpixels.substance.internal.painter.SimplisticFillPainter;
import org.pushingpixels.substance.internal.utils.filters.ColorFilter;
import org.pushingpixels.substance.internal.utils.filters.ColorSchemeFilter;
import org.pushingpixels.substance.internal.utils.filters.GrayscaleFilter;
import org.pushingpixels.substance.internal.utils.filters.TranslucentFilter;

import javax.swing.*;
import javax.swing.text.JTextComponent;
import java.awt.*;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.geom.*;
import java.awt.image.BufferedImage;

/**
 * Provides utility functions for creating various images for Substance  look and feel. This
 * class is for internal use only.
 * 
 * @author Kirill Grouchnikov
 */
public final class SubstanceImageCreator {
    /**
     * Custom fill painter for filling the checkmarks of checkboxes and radio buttons.
     * 
     * @author Kirill Grouchnikov
     */
    public static class SimplisticSoftBorderReverseFillPainter extends SimplisticFillPainter {
        /**
         * Singleton instance.
         */
        public static final SubstanceFillPainter INSTANCE = new SimplisticSoftBorderReverseFillPainter();

        /**
         * Private constructor.
         */
        private SimplisticSoftBorderReverseFillPainter() {
        }

        @Override
        public String getDisplayName() {
            return "Simplistic Soft Border Reverse";
        }

        @Override
        public Color getTopFillColor(SubstanceColorScheme fillScheme) {
            return super.getBottomFillColor(fillScheme);
        }

        @Override
        public Color getBottomFillColor(SubstanceColorScheme fillScheme) {
            return super.getTopFillColor(fillScheme);
        }
    }

    /**
     * Paints border instance of specified dimensions and status.
     * 
     * @param c
     *            Component.
     * @param graphics
     *            Graphics context.
     * @param x
     *            Component left X (in graphics context).
     * @param y
     *            Component top Y (in graphics context).
     * @param width
     *            Border width.
     * @param height
     *            Border height.
     * @param radius
     *            Border radius.
     * @param borderScheme
     *            Border color scheme.
     */
    public static void paintBorder(Component c, Graphics2D graphics, int x, float y, float width,
            float height, float radius, SubstanceColorScheme borderScheme) {

        SubstanceBorderPainter borderPainter = SubstanceCoreUtilities.getBorderPainter(c);
        graphics.translate(x, y);
        float borderDelta = SubstanceSizeUtils.getBorderStrokeWidth() / 2.0f;
        Shape contour = SubstanceOutlineUtilities.getBaseOutline(width, height, radius, null,
                borderDelta);
        float borderThickness = SubstanceSizeUtils.getBorderStrokeWidth();
        boolean skipInnerBorder = (c instanceof JTextComponent)
                || ((SwingUtilities.getAncestorOfClass(CellRendererPane.class, c) != null)
                        && (SwingUtilities.getAncestorOfClass(JFileChooser.class, c) != null));
        GeneralPath contourInner = skipInnerBorder ? null
                : SubstanceOutlineUtilities.getBaseOutline(width, height, radius - borderThickness,
                        null, borderThickness + borderDelta);
        borderPainter.paintBorder(graphics, c, width, height, contour, contourInner, borderScheme);
        graphics.translate(-x, -y);
    }

    /**
     * Retrieves check mark image.
     * 
     * @param dimension
     *            Check mark dimension.
     * @param isEnabled
     *            Enabled status.
     * @param scheme
     *            Color scheme for the check mark.
     * @param checkMarkVisibility
     *            Checkmark visibility in 0.0-1.0 range.
     * @return Check mark image.
     */
    private static BufferedImage getCheckMark(int dimension, boolean isEnabled,
            SubstanceColorScheme scheme, float checkMarkVisibility) {
        BufferedImage result = SubstanceCoreUtilities.getBlankImage(dimension, dimension);

        // get graphics and set hints
        Graphics2D graphics = (Graphics2D) result.getGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        // create curved checkbox path
        GeneralPath path = new GeneralPath();

        path.moveTo(0.25f * dimension, 0.5f * dimension);
        path.quadTo(0.37f * dimension, 0.6f * dimension, 0.47f * dimension, 0.8f * dimension);
        path.quadTo(0.55f * dimension, 0.5f * dimension, 0.85f * dimension, 0f);

        // compute the x-based clip for the visibility
        float xClipStart = 0.15f * dimension;
        float xClipEnd = 0.95f * dimension;
        float xClipRealEnd = xClipStart + (xClipEnd - xClipStart) * checkMarkVisibility;

        graphics.setClip(0, 0, (int) Math.ceil(xClipRealEnd), dimension);

        graphics.setColor(SubstanceColorUtilities.getMarkColor(scheme, isEnabled));
        Stroke stroke = new BasicStroke((float) 0.15 * dimension, BasicStroke.CAP_ROUND,
                BasicStroke.JOIN_ROUND);
        graphics.setStroke(stroke);
        graphics.draw(path);

        return result;
    }

    /**
     * Returns arrow icon for the specified parameters.
     * 
     * @param fontSize
     *            Font size.
     * @param direction
     *            Arrow direction.
     * @param colorScheme
     *            Arrow icon color scheme.
     * @return Arrow icon.
     */
    public static ImageWrapperIcon getArrowIcon(int fontSize, int direction,
            SubstanceColorScheme colorScheme) {
        float origWidth = SubstanceSizeUtils.getArrowIconWidth(fontSize);
        float origHeight = SubstanceSizeUtils.getArrowIconHeight(fontSize);
        float width = origWidth;
        float height = origHeight;
        if (direction == SwingConstants.CENTER)
            height *= 2;
        float strokeWidth = SubstanceSizeUtils.getArrowStrokeWidth(fontSize);
        ImageWrapperIcon result = new ImageWrapperIcon(
                getArrow(width, height, strokeWidth, direction, colorScheme));
        int finalWidth = (int) (Math.max(origWidth, origHeight) + 2);
        int finalHeight = (int) (Math.max(origWidth, height) + 2);
        result.setDimension(new Dimension(finalWidth, finalHeight));
        return result;
    }

    /**
     * Retrieves arrow icon.
     * 
     * @param width
     *            Arrow width.
     * @param height
     *            Arrow height.
     * @param strokeWidth
     *            Stroke width.
     * @param direction
     *            Arrow direction.
     * @param scheme
     *            Color scheme for the arrow.
     * @return Arrow image.
     * @see SwingConstants#NORTH
     * @see SwingConstants#WEST
     * @see SwingConstants#SOUTH
     * @see SwingConstants#EAST
     */
    public static ImageWrapperIcon getArrowIcon(float width, float height, float strokeWidth,
            int direction, SubstanceColorScheme scheme) {
        return new ImageWrapperIcon(getArrow(width, height, strokeWidth, direction, scheme));
    }

    /**
     * Retrieves arrow image.
     * 
     * @param width
     *            Arrow width.
     * @param height
     *            Arrow height.
     * @param strokeWidth
     *            Stroke width.
     * @param direction
     *            Arrow direction.
     * @param scheme
     *            Color scheme for the arrow.
     * @return Arrow image.
     * @see SwingConstants#NORTH
     * @see SwingConstants#WEST
     * @see SwingConstants#SOUTH
     * @see SwingConstants#EAST
     * @see SwingConstants#CENTER
     */
    private static BufferedImage getArrow(float width, float height, float strokeWidth,
            int direction, SubstanceColorScheme scheme) {
        if (direction == SwingConstants.EAST || direction == SwingConstants.WEST) {
            float tmp = width;
            width = height;
            height = tmp;
        }
        BufferedImage arrowImage = SubstanceCoreUtilities.getBlankImage((int) Math.ceil(width),
                (int) Math.ceil(height));

        // System.out.println(width + ":" + height + ":" + strokeWidth);

        // get graphics and set hints
        Graphics2D graphics = (Graphics2D) arrowImage.getGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                RenderingHints.VALUE_STROKE_PURE);
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        // graphics.setColor(Color.green);
        // graphics.drawRect(0, 0, (int) width -1 , (int) height - 1);

        Color arrowColor = SubstanceColorUtilities.getMarkColor(scheme, true);

        graphics.setColor(arrowColor);
        Stroke stroke = new BasicStroke(strokeWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
        graphics.setStroke(stroke);

        if (direction == SwingConstants.CENTER) {
            float smallHeight = (height - strokeWidth) / 2;
            BufferedImage top = getArrow(width, smallHeight, strokeWidth, SwingConstants.NORTH,
                    scheme);
            BufferedImage bottom = getArrow(width, smallHeight, strokeWidth, SwingConstants.SOUTH,
                    scheme);
            NeonCortex.drawImage(graphics, top, 0, 0);
            NeonCortex.drawImage(graphics, bottom, 0, (int) (height / 2.0));
            return arrowImage;
        } else {
            float cushion = strokeWidth / 2.0f;
            GeneralPath gp = new GeneralPath();
            switch (direction) {
                case SwingConstants.SOUTH:
                    gp.moveTo(cushion, cushion);
                    gp.lineTo(0.5f * (width), height - cushion - 1);
                    gp.lineTo(width - cushion, cushion);
                    break;
                case SwingConstants.NORTH:
                    gp.moveTo(cushion, height - cushion - 1);
                    gp.lineTo(0.5f * (width), cushion);
                    gp.lineTo(width - cushion, height - cushion - 1);
                    break;
                case SwingConstants.EAST:
                    gp.moveTo(cushion, cushion);
                    gp.lineTo(width - 1 - cushion, 0.5f * (height));
                    gp.lineTo(cushion, height - cushion);
                    break;
                case SwingConstants.WEST:
                    gp.moveTo(width - 1 - cushion, cushion);
                    gp.lineTo(cushion, 0.5f * (height));
                    gp.lineTo(width - 1 - cushion, height - cushion);
                    break;
            }
            graphics.draw(gp);

            return arrowImage;
        }
    }

    /**
     * Retrieves arrow icon.
     * 
     * @param fullWidth
     *            Full icon width.
     * @param fullHeight
     *            Full icon height.
     * @param strokeWidth
     *            Stroke width.
     * @param direction
     *            Arrow direction.
     * @param colorScheme
     *            Color scheme for the arrow.
     * @return Arrow image.
     * @see SwingConstants#NORTH
     * @see SwingConstants#WEST
     * @see SwingConstants#SOUTH
     * @see SwingConstants#EAST
     */
    public static ImageWrapperIcon getDoubleArrowIcon(float fullWidth,
            float fullHeight, float arrowGap, float strokeWidth, int direction,
            SubstanceColorScheme colorScheme) {
        boolean toggle = (direction == SwingConstants.WEST) || (direction == SwingConstants.EAST);
        int singleArrowWidth = toggle ? (int) fullHeight : (int) fullWidth;
        int singleArrowHeight = toggle ? (int) (fullWidth - arrowGap)
                : (int) (fullHeight - arrowGap);
        BufferedImage downArrowImage = SubstanceCoreUtilities.getBlankImage((int) fullWidth,
                (int) fullHeight);

        BufferedImage singleArrow = getArrow(singleArrowWidth, singleArrowHeight, strokeWidth,
                direction, colorScheme);

        // get graphics and set hints
        Graphics2D graphics = (Graphics2D) downArrowImage.getGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        // graphics.setColor(new Color(255, 0, 0, 128));
        // graphics.fillRect(0, 0, downArrowImage.getWidth(), downArrowImage.getHeight());

        if (!toggle) {
            NeonCortex.drawImage(graphics, singleArrow, 0, 0);
            NeonCortex.drawImage(graphics, singleArrow, 0, (int) arrowGap);
        } else {
            NeonCortex.drawImage(graphics, singleArrow, 0, 0);
            NeonCortex.drawImage(graphics, singleArrow, (int) arrowGap, 0);
        }

        return new ImageWrapperIcon(downArrowImage);
    }

    /**
     * Returns rotated image.
     * 
     * @param bi
     *            Image to rotate.
     * @param quadrantClockwise
     *            Amount of quadrants to rotate in clockwise direction. The rotation angle is 90
     *            times this value.
     * @return Rotated image.
     */
    public static BufferedImage getRotated(BufferedImage bi, int quadrantClockwise) {
        if (quadrantClockwise == 0) {
            return bi;
        }
        quadrantClockwise = quadrantClockwise % 4;
        int width = bi.getWidth();
        int height = bi.getHeight();
        if ((quadrantClockwise == 1) || (quadrantClockwise == 3)) {
            width = bi.getHeight();
            height = bi.getWidth();
        }
        double factor = NeonCortex.getScaleFactor();
        BufferedImage biRot = SubstanceCoreUtilities.getBlankImage((int) (width / factor),
                (int) (height / factor));
        AffineTransform at = null;
        switch (quadrantClockwise) {
            case 1:
                at = AffineTransform.getTranslateInstance(width, 0);
                at.rotate(Math.PI / 2);
                break;
            case 2:
                at = AffineTransform.getTranslateInstance(width, height);
                at.rotate(Math.PI);
                break;
            case 3:
                at = AffineTransform.getTranslateInstance(0, height);
                at.rotate(-Math.PI / 2);
        }
        Graphics2D rotg = biRot.createGraphics();
        rotg.scale(1.0f / factor, 1.0f / factor);
        if (at != null) {
            rotg.setTransform(at);
        }
        rotg.drawImage(bi, 0, 0, bi.getWidth(), bi.getHeight(), null);
        rotg.dispose();
        return biRot;
    }

    /**
     * Translated the specified icon to grey scale.
     * 
     * @param icon
     *            Icon.
     * @return Greyscale version of the specified icon.
     */
    public static Icon toGrayscale(Icon icon) {
        if (icon == null) {
            return null;
        }

        int width = icon.getIconWidth();
        int height = icon.getIconHeight();

        BufferedImage result = SubstanceCoreUtilities.getBlankImage(width, height);

        icon.paintIcon(null, result.getGraphics(), 0, 0);
        Icon resultIcon = new ImageWrapperIcon(new GrayscaleFilter().filter(result, null));
        return resultIcon;
    }

    /**
     * Makes the specified icon transparent.
     * 
     * @param c
     *            Component.
     * @param icon
     *            Icon.
     * @param alpha
     *            The alpha of the resulting image. The closer this value is to 0.0, the more
     *            transparent resulting image will be.
     * @return Transparent version of the specified icon.
     */
    public static Icon makeTransparent(Component c, Icon icon, double alpha) {
        if (icon == null) {
            return null;
        }

        int width = icon.getIconWidth();
        int height = icon.getIconHeight();

        BufferedImage result = SubstanceCoreUtilities.getBlankImage(width, height);
        icon.paintIcon(c, result.getGraphics(), 0, 0);
        return new ImageWrapperIcon(new TranslucentFilter(alpha).filter(result, null));
    }

    /**
     * Retrieves radio button of the specified size that matches the specified parameters.
     * 
     * @param component
     *            Component.
     * @param dimension
     *            Radio button dimension.
     * @param componentState
     *            Component state.
     * @param offsetX
     *            Offset on X axis - should be positive in order to see the entire radio button.
     * @param fillColorScheme
     *            Color scheme for the inner fill.
     * @param markColorScheme
     *            Color scheme for the check mark.
     * @param borderColorScheme
     *            Color scheme for the border.
     * @param checkMarkVisibility
     *            Check mark visibility in 0.0-1.0 range.
     * @return Radio button of the specified size that matches the specified parameters.
     */
    public static BufferedImage getRadioButton(JComponent component,
            SubstanceFillPainter fillPainter, SubstanceBorderPainter borderPainter, int dimension,
            ComponentState componentState, int offsetX, SubstanceColorScheme fillColorScheme,
            SubstanceColorScheme markColorScheme, SubstanceColorScheme borderColorScheme,
            float checkMarkVisibility, float alpha) {

        if (!componentState.isActive()) {
            fillPainter = SimplisticSoftBorderReverseFillPainter.INSTANCE;
        }

        float borderDelta = SubstanceSizeUtils.getBorderStrokeWidth();

        // float fDelta = borderThickness / 2.0f;
        Shape contourBorder = new Ellipse2D.Float(borderDelta / 2.0f, borderDelta / 2.0f,
                dimension - borderDelta, dimension - borderDelta);

        BufferedImage offBackground = SubstanceCoreUtilities.getBlankImage(dimension + offsetX,
                dimension);
        Graphics2D graphics = (Graphics2D) offBackground.getGraphics().create();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                RenderingHints.VALUE_STROKE_PURE);

        graphics.setComposite(getAlphaComposite(alpha));

        graphics.translate(offsetX, 0);
        fillPainter.paintContourBackground(graphics, component, dimension, dimension, contourBorder,
                false, fillColorScheme, true);

        Shape contourInner = new Ellipse2D.Float(1.5f * borderDelta, 1.5f * borderDelta,
                dimension - 3 * borderDelta, dimension - 3 * borderDelta);

        borderPainter.paintBorder(graphics, component, dimension, dimension, contourBorder,
                contourInner, borderColorScheme);
        graphics.setComposite(AlphaComposite.SrcOver);

        float rc = dimension / 2.0f;
        float radius = dimension / 4.5f;

        Shape markOval = new Ellipse2D.Double(rc - radius, rc - radius, 2 * radius, 2 * radius);
        if (checkMarkVisibility > 0.0) {
            // mark
            graphics.setComposite(getAlphaComposite(alpha * checkMarkVisibility));
            graphics.setColor(SubstanceColorUtilities.getMarkColor(markColorScheme,
                    !componentState.isDisabled()));
            graphics.fill(markOval);
        } else {
            // draw ghost mark holder
            graphics.setComposite(getAlphaComposite(alpha * 0.3f));
            graphics.setPaint(
                    new GradientPaint(rc + radius, rc - radius, fillColorScheme.getDarkColor(),
                            rc - radius, rc + radius, fillColorScheme.getLightColor()));
            graphics.fill(markOval);
        }
        graphics.dispose();

        return offBackground;
    }

    private static AlphaComposite getAlphaComposite(float alpha) {
        // Fix for "alpha value out of range"
        float finalAlpha = alpha;
        if (finalAlpha < 0.0f)
            finalAlpha = 0.0f;
        else if (finalAlpha > 1.0f)
            finalAlpha = 1.0f;
        return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, finalAlpha);
    }

    /**
     * Retrieves check box of the specified size that matches the specified component state.
     * 
     * @param button
     *            Button for the check mark.
     * @param dimension
     *            Check box size.
     * @param componentState
     *            Component state.
     * @param fillColorScheme
     *            Color scheme for the inner fill.
     * @param markColorScheme
     *            Color scheme for the check mark.
     * @param borderColorScheme
     *            Color scheme for the border.
     * @param checkMarkVisibility
     *            Check mark visibility in 0.0-1.0 range.
     * @param isCheckMarkFadingOut
     *            if true, the value of interpolationCyclePos10 is used as
     *            the alpha channel.
     * @return Check box of the specified size that matches the specified component state.
     */
    public static BufferedImage getCheckBox(AbstractButton button, SubstanceFillPainter fillPainter,
            SubstanceBorderPainter borderPainter, int dimension, ComponentState componentState,
            SubstanceColorScheme fillColorScheme, SubstanceColorScheme markColorScheme,
            SubstanceColorScheme borderColorScheme, float checkMarkVisibility,
            boolean isCheckMarkFadingOut, float alpha) {
        int xOffset = SubstanceSizeUtils
                .getAdjustedSize(SubstanceSizeUtils.getComponentFontSize(button), 2, 9, 1, false);
        int yOffset = xOffset + 1;
        int delta = xOffset;
        float cornerRadius = SubstanceSizeUtils
                .getClassicButtonCornerRadius(SubstanceSizeUtils.getComponentFontSize(button));
        if (dimension <= 10) {
            xOffset = 1;
            yOffset = 2;
            cornerRadius = 2;
        }

        int contourDim = dimension - delta;
        float borderDelta = SubstanceSizeUtils.getBorderStrokeWidth() / 2.0f;
        GeneralPath contour = SubstanceOutlineUtilities.getBaseOutline(contourDim, contourDim,
                cornerRadius, null, borderDelta);

        if (!componentState.isActive()) {
            fillPainter = SimplisticSoftBorderReverseFillPainter.INSTANCE;
        }

        BufferedImage offBackground = SubstanceCoreUtilities.getBlankImage(dimension, dimension);
        Graphics2D graphics = (Graphics2D) offBackground.getGraphics();
        graphics.setComposite(getAlphaComposite(alpha));

        graphics.translate(delta - 1, delta - 1);
        fillPainter.paintContourBackground(graphics, button, contourDim, contourDim, contour, false,
                fillColorScheme, true);

        float borderThickness = SubstanceSizeUtils.getBorderStrokeWidth();
        GeneralPath contourInner = SubstanceOutlineUtilities.getBaseOutline(contourDim, contourDim,
                cornerRadius - borderThickness, null, borderThickness + borderDelta);
        borderPainter.paintBorder(graphics, button, contourDim, contourDim, contour, contourInner,
                borderColorScheme);
        graphics.translate(-delta, 1 - delta);
        if (checkMarkVisibility > 0.0) {
            if (isCheckMarkFadingOut) {
                graphics.setComposite(getAlphaComposite(alpha * checkMarkVisibility));
                checkMarkVisibility = 1.0f;
            }

            BufferedImage checkMark = SubstanceImageCreator.getCheckMark(dimension - yOffset / 2,
                    !componentState.isDisabled(), markColorScheme, checkMarkVisibility);

            NeonCortex.drawImage(graphics, checkMark, 1 + 2 * xOffset / 3,
                    (dimension < 14) ? 0 : -1);
        }

        return offBackground;
    }

    /**
     * Overlays light-colored echo below the specified image.
     * 
     * @param image
     *            The input image.
     * @param echoAlpha
     *            Alpha channel for the echo image.
     * @param offsetX
     *            X offset of the echo.
     * @param offsetY
     *            Y offset of the echo.
     * @return Image with overlayed echo.
     */
    private static BufferedImage overlayEcho(BufferedImage image, float echoAlpha, Color echoColor,
            int offsetX, int offsetY) {
        int width = image.getWidth();
        int height = image.getHeight();

        offsetX = offsetY = 0;
        BufferedImage echo = new ColorFilter(echoColor).filter(image, null);
        double factor = NeonCortex.getScaleFactor();
        int tweakedWidth = (int) (width / factor);
        int tweakedHeight = (int) (height / factor);
        BufferedImage result = SubstanceCoreUtilities.getBlankImage(tweakedWidth, tweakedHeight);
        Graphics2D graphics = (Graphics2D) result.getGraphics().create();
        graphics.setComposite(getAlphaComposite(0.2f * echoAlpha * echoAlpha * echoAlpha));
        graphics.drawImage(echo, offsetX - 1, offsetY - 1, tweakedWidth, tweakedHeight, null);
        graphics.drawImage(echo, offsetX + 1, offsetY - 1, tweakedWidth, tweakedHeight, null);
        graphics.drawImage(echo, offsetX - 1, offsetY + 1, tweakedWidth, tweakedHeight, null);
        graphics.drawImage(echo, offsetX + 1, offsetY + 1, tweakedWidth, tweakedHeight, null);
        graphics.setComposite(getAlphaComposite(0.7f * echoAlpha * echoAlpha * echoAlpha));
        graphics.drawImage(echo, offsetX, offsetY - 1, tweakedWidth, tweakedHeight, null);
        graphics.drawImage(echo, offsetX, offsetY + 1, tweakedWidth, tweakedHeight, null);
        graphics.drawImage(echo, offsetX - 1, offsetY, tweakedWidth, tweakedHeight, null);
        graphics.drawImage(echo, offsetX + 1, offsetY, tweakedWidth, tweakedHeight, null);

        graphics.setComposite(getAlphaComposite(1.0f));
        graphics.drawImage(image, 0, 0, tweakedWidth, tweakedHeight, null);

        graphics.dispose();
        return result;
    }

    /**
     * Returns minimize icon.
     * 
     * @param scheme
     *            Color scheme for the icon.
     * @return Minimize icon.
     */
    public static ImageWrapperIcon getMinimizeIcon(SubstanceColorScheme scheme,
            SubstanceColorScheme backgroundScheme) {
        int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
        return getMinimizeIcon(iSize, scheme, backgroundScheme);
    }

    /**
     * Returns minimize icon.
     * 
     * @param iSize
     *            Icon dimension.
     * @param scheme
     *            Color scheme for the icon.
     * @return Minimize icon.
     */
    public static ImageWrapperIcon getMinimizeIcon(int iSize, SubstanceColorScheme scheme,
            SubstanceColorScheme backgroundScheme) {
        BufferedImage image = SubstanceCoreUtilities.getBlankImage(iSize, iSize);
        Graphics2D graphics = image.createGraphics();
        int start = iSize / 4 - 2;
        int end = 3 * iSize / 4;
        int size = end - start - 3;
        Color color = SubstanceColorUtilities.getMarkColor(scheme, true);
        graphics.setColor(color);
        graphics.fillRect(start + 2, end - 1, size, 3);
        graphics.dispose();

        Color echoColor = scheme.isDark() ? backgroundScheme.getUltraDarkColor()
                : backgroundScheme.getUltraLightColor();

        int fgStrength = SubstanceColorUtilities.getColorBrightness(color.getRGB());
        int echoStrength = SubstanceColorUtilities.getColorBrightness(echoColor.getRGB());
        boolean noEcho = Math.abs(fgStrength - echoStrength) < 48;
        return new ImageWrapperIcon(SubstanceImageCreator.overlayEcho(image,
                noEcho ? 0 : SubstanceColorUtilities.getColorStrength(color), echoColor, 1, 1));
    }

    /**
     * Returns restore icon.
     * 
     * @param scheme
     *            Color scheme for the icon.
     * @return Restore icon.
     */
    public static ImageWrapperIcon getRestoreIcon(SubstanceColorScheme scheme,
            SubstanceColorScheme backgroundScheme) {
        int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
        BufferedImage image = SubstanceCoreUtilities.getBlankImage(iSize, iSize);
        Graphics2D graphics = image.createGraphics();
        int start = iSize / 4 - 1;
        int end = iSize - start;
        int smallSquareSize = end - start - 3;
        Color color = SubstanceColorUtilities.getMarkColor(scheme, true);
        graphics.setColor(color);

        // "Main" rectangle
        int mainStartX = start;
        int mainStartY = end - smallSquareSize;
        // top (thicker)
        graphics.fillRect(mainStartX, mainStartY, smallSquareSize, 2);
        // left
        graphics.fillRect(mainStartX, mainStartY, 1, smallSquareSize);
        // right
        graphics.fillRect(mainStartX + smallSquareSize - 1, mainStartY, 1, smallSquareSize);
        // bottom
        graphics.fillRect(mainStartX, mainStartY + smallSquareSize - 1, smallSquareSize, 1);

        // "Secondary rectangle"
        int secondaryStartX = mainStartX + 3;
        int secondaryStartY = mainStartY - 3;
        // top (thicker)
        graphics.fillRect(secondaryStartX, secondaryStartY, smallSquareSize, 2);
        // right
        graphics.fillRect(secondaryStartX + smallSquareSize - 1, secondaryStartY, 1,
                smallSquareSize);
        // bottom (partial)
        graphics.fillRect(mainStartX + smallSquareSize + 1, secondaryStartY + smallSquareSize - 1,
                2, 1);

        graphics.dispose();

        Color echoColor = scheme.isDark() ? backgroundScheme.getUltraDarkColor()
                : backgroundScheme.getUltraLightColor();

        int fgStrength = SubstanceColorUtilities.getColorBrightness(color.getRGB());
        int echoStrength = SubstanceColorUtilities.getColorBrightness(echoColor.getRGB());
        boolean noEcho = Math.abs(fgStrength - echoStrength) < 48;
        return new ImageWrapperIcon(SubstanceImageCreator.overlayEcho(image,
                noEcho ? 0 : SubstanceColorUtilities.getColorStrength(color), echoColor, 1, 1));
    }

    /**
     * Returns maximize icon.
     * 
     * @param scheme
     *            Color scheme for the icon.
     * @return Maximize icon.
     */
    public static ImageWrapperIcon getMaximizeIcon(SubstanceColorScheme scheme,
            SubstanceColorScheme backgroundScheme) {
        int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
        return getMaximizeIcon(iSize, scheme, backgroundScheme);
    }

    /**
     * Returns maximize icon.
     * 
     * @param iSize
     *            Icon dimension.
     * @param scheme
     *            Color scheme for the icon.
     * @return Maximize icon.
     */
    public static ImageWrapperIcon getMaximizeIcon(int iSize, SubstanceColorScheme scheme,
            SubstanceColorScheme backgroundScheme) {
        BufferedImage image = SubstanceCoreUtilities.getBlankImage(iSize, iSize);
        Graphics2D graphics = image.createGraphics();
        int start = iSize / 4 - 1;
        int end = iSize - start;
        Color color = SubstanceColorUtilities.getMarkColor(scheme, true);
        graphics.setColor(color);
        // top (thicker)
        graphics.fillRect(start, start, end - start, 2);
        // left
        graphics.fillRect(start, start, 1, end - start);
        // right
        graphics.fillRect(end - 1, start, 1, end - start);
        // bottom
        graphics.fillRect(start, end - 1, end - start, 1);
        graphics.dispose();

        Color echoColor = scheme.isDark() ? backgroundScheme.getUltraDarkColor()
                : backgroundScheme.getUltraLightColor();

        int fgStrength = SubstanceColorUtilities.getColorBrightness(color.getRGB());
        int echoStrength = SubstanceColorUtilities.getColorBrightness(echoColor.getRGB());
        boolean noEcho = Math.abs(fgStrength - echoStrength) < 48;

        return new ImageWrapperIcon(SubstanceImageCreator.overlayEcho(image,
                noEcho ? 0 : SubstanceColorUtilities.getColorStrength(color), echoColor, 1, 1));
    }

    /**
     * Returns close icon.
     * 
     * @param scheme
     *            Color scheme for the icon.
     * @return Close icon.
     */
    public static ImageWrapperIcon getCloseIcon(SubstanceColorScheme scheme,
            SubstanceColorScheme backgroundScheme) {
        return SubstanceImageCreator.getCloseIcon(SubstanceSizeUtils.getTitlePaneIconSize(), scheme,
                backgroundScheme);
    }

    /**
     * Returns close icon.
     * 
     * @param iSize
     *            Icon dimension.
     * @param colorScheme
     *            Color scheme for the icon.
     * @return Close icon.
     */
    public static ImageWrapperIcon getCloseIcon(int iSize, SubstanceColorScheme colorScheme,
            SubstanceColorScheme backgroundScheme) {
        BufferedImage image = SubstanceCoreUtilities.getBlankImage(iSize, iSize);
        Graphics2D graphics = image.createGraphics();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        int start = iSize / 4;
        int end = iSize - start;

        // System.out.println(iSize + ":" + start + ":" + end);

        Stroke stroke = new BasicStroke(SubstanceSizeUtils.getCloseIconStrokeWidth(iSize),
                BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);

        graphics.setStroke(stroke);
        Color color = SubstanceColorUtilities.getMarkColor(colorScheme, true);
        graphics.setColor(color);
        graphics.drawLine(start, start, end, end);
        graphics.drawLine(start, end, end, start);
        graphics.dispose();

        Color echoColor = colorScheme.isDark() ? backgroundScheme.getUltraDarkColor()
                : backgroundScheme.getUltraLightColor();

        int fgStrength = SubstanceColorUtilities.getColorBrightness(color.getRGB());
        int echoStrength = SubstanceColorUtilities.getColorBrightness(echoColor.getRGB());
        boolean noEcho = Math.abs(fgStrength - echoStrength) < 48;

        return new ImageWrapperIcon(SubstanceImageCreator.overlayEcho(image,
                noEcho ? 0 : SubstanceColorUtilities.getColorStrength(color), echoColor, 1, 1));
    }

    /**
     * Paints rectangular gradient background.
     * 
     * @param g
     *            Graphic context.
     * @param startX
     *            Background starting X coord.
     * @param startY
     *            Background starting Y coord.
     * @param width
     *            Background width.
     * @param height
     *            Background height.
     * @param colorScheme
     *            Color scheme for the background.
     * @param borderAlpha
     *            Border alpha.
     * @param isVertical
     *            if true, the gradient will be vertical, if false, the
     *            gradient will be horizontal.
     */
    public static void paintRectangularBackground(Component c, Graphics g, int startX, int startY,
            int width, int height, SubstanceColorScheme colorScheme, float borderAlpha,
            boolean isVertical) {
        Graphics2D graphics = (Graphics2D) g.create();
        graphics.translate(startX, startY);

        if (!isVertical) {
            LinearGradientPaint paint = new LinearGradientPaint(0, 0, 0, height,
                    new float[] { 0.0f, 0.4f, 0.5f, 1.0f },
                    new Color[] { colorScheme.getUltraLightColor(), colorScheme.getLightColor(),
                                    colorScheme.getMidColor(), colorScheme.getUltraLightColor() },
                    CycleMethod.REPEAT);
            graphics.setPaint(paint);
            graphics.fillRect(0, 0, width, height);
        } else {
            LinearGradientPaint paint = new LinearGradientPaint(0, 0, width, 0,
                    new float[] { 0.0f, 0.4f, 0.5f, 1.0f },
                    new Color[] { colorScheme.getUltraLightColor(), colorScheme.getLightColor(),
                                    colorScheme.getMidColor(), colorScheme.getUltraLightColor() },
                    CycleMethod.REPEAT);
            graphics.setPaint(paint);
            graphics.fillRect(0, 0, width, height);
        }

        if (borderAlpha > 0.0f) {
            Graphics2D g2d = (Graphics2D) graphics.create();
            g2d.setComposite(WidgetUtilities.getAlphaComposite(null, borderAlpha, graphics));

            paintSimpleBorderAliased(c, g2d, width, height, colorScheme);

            g2d.dispose();
        }
        graphics.dispose();
    }

    /**
     * Paints simple border.
     * 
     * @param g2d
     *            Graphics context.
     * @param width
     *            Border width.
     * @param height
     *            Border height.
     * @param borderColorScheme
     *            Border color scheme.
     */
    public static void paintSimpleBorder(Component c, Graphics2D g2d, float width, float height,
            SubstanceColorScheme borderColorScheme) {
        float borderThickness = SubstanceSizeUtils.getBorderStrokeWidth();

        SubstanceBorderPainter borderPainter = SubstanceCoreUtilities.getBorderPainter(c);
        g2d.setColor(borderPainter.getRepresentativeColor(borderColorScheme));
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        int joinKind = BasicStroke.JOIN_ROUND;
        int capKind = BasicStroke.CAP_BUTT;
        g2d.setStroke(new BasicStroke(borderThickness, capKind, joinKind));
        g2d.draw(new Rectangle2D.Float(borderThickness / 2.0f, borderThickness / 2.0f,
                width - borderThickness, height - borderThickness));
    }

    public static void paintSimpleBorderAliased(Component c, Graphics2D g2d, int width, int height,
            SubstanceColorScheme colorScheme) {
        float borderThickness = SubstanceSizeUtils.getBorderStrokeWidth();

        g2d.setColor(SubstanceColorUtilities.getMidBorderColor(colorScheme));
        g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
        g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE);
        int joinKind = BasicStroke.JOIN_MITER;
        int capKind = BasicStroke.CAP_SQUARE;
        g2d.setStroke(new BasicStroke(borderThickness, capKind, joinKind));
        g2d.draw(new Rectangle2D.Float(borderThickness / 2.0f, borderThickness / 2.0f,
                width - borderThickness, height - borderThickness));
    }

    /**
     * Paints rectangular gradient background with spots and optional replicated stripe image.
     * 
     * @param g
     *            Graphics context.
     * @param startX
     *            X start coordinate.
     * @param startY
     *            Y start coordinate.
     * @param width
     *            Background width.
     * @param height
     *            Background height.
     * @param colorScheme
     *            Color scheme for the background.
     * @param stripeImage
     *            Stripe image to replicate.
     * @param stripeOffset
     *            Offset of the first stripe replication.
     * @param borderAlpha
     *            Border alpha.
     * @param isVertical
     *            Indication of horizontal / vertical orientation.
     */
    public static void paintRectangularStripedBackground(Component c, Graphics g, int startX,
            int startY, int width, int height, SubstanceColorScheme colorScheme,
            BufferedImage stripeImage, int stripeOffset, float borderAlpha, boolean isVertical) {
        Graphics2D graphics = (Graphics2D) g.create(startX, startY, width, height);
        double scaleFactor = NeonCortex.getScaleFactor();
        if (!isVertical) {
            LinearGradientPaint paint = new LinearGradientPaint(0, 0, 0, height,
                    new float[] { 0.0f, 0.2f, 0.5f, 0.8f, 1.0f },
                    new Color[] { colorScheme.getDarkColor(), colorScheme.getLightColor(),
                                    colorScheme.getMidColor(), colorScheme.getLightColor(),
                                    colorScheme.getDarkColor() },
                    CycleMethod.REPEAT);
            graphics.setPaint(paint);
            graphics.fillRect(0, 0, width, height);

            if (stripeImage != null) {
                int stripeSize = stripeImage.getHeight();
                int stripeCount = (int) (scaleFactor * width / stripeSize);
                stripeOffset = (int) (stripeOffset % (2 * stripeSize * scaleFactor));
                for (int stripe = -2; stripe <= stripeCount; stripe += 2) {
                    int stripePos = (int) (stripe * stripeSize / scaleFactor + stripeOffset);

                    NeonCortex.drawImage(graphics, stripeImage, stripePos, 0);
                }
            }
        } else {
            LinearGradientPaint paint = new LinearGradientPaint(0, 0, width, 0,
                    new float[] { 0.0f, 0.2f, 0.5f, 0.8f, 1.0f },
                    new Color[] { colorScheme.getDarkColor(), colorScheme.getLightColor(),
                                    colorScheme.getMidColor(), colorScheme.getLightColor(),
                                    colorScheme.getDarkColor() },
                    CycleMethod.REPEAT);
            graphics.setPaint(paint);
            graphics.fillRect(0, 0, width, height);

            if (stripeImage != null) {
                int stripeSize = stripeImage.getWidth();
                int stripeCount = (int) (scaleFactor * height / stripeSize);
                stripeOffset = (int) (stripeOffset % (2 * stripeSize * scaleFactor));
                for (int stripe = -2; stripe <= stripeCount; stripe += 2) {
                    int stripePos = (int) (stripe * stripeSize / scaleFactor + stripeOffset);

                    NeonCortex.drawImage(graphics, stripeImage, 0, stripePos);
                }
            }
        }

        if (borderAlpha > 0.0f) {
            Graphics2D g2d = (Graphics2D) graphics.create();
            g2d.setComposite(WidgetUtilities.getAlphaComposite(null, borderAlpha, graphics));

            paintSimpleBorderAliased(c, g2d, width, height, colorScheme);
            g2d.dispose();
        }
        graphics.dispose();
    }

    /**
     * Returns diagonal stripe image.
     * 
     * @param baseSize
     *            Stripe base in pixels.
     * @param color
     *            Stripe color.
     * @return Diagonal stripe image.
     */
    public static BufferedImage getStripe(int baseSize, Color color) {
        int width = (int) (1.8 * baseSize);
        int height = baseSize;
        BufferedImage intermediate = SubstanceCoreUtilities.getBlankImage(width, height);
        Graphics2D graphics = (Graphics2D) intermediate.getGraphics();

        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        Polygon polygon = new Polygon();
        polygon.addPoint(0, 0);
        polygon.addPoint(width - 1 - baseSize, 0);
        polygon.addPoint(width - 1, height - 1);
        polygon.addPoint(baseSize, height - 1);

        graphics.setColor(color);
        graphics.fillPolygon(polygon);
        graphics.drawPolygon(polygon);

        return intermediate;
    }

    /**
     * Returns drag bumps image.
     * 
     * @param c
     *            Component.
     * @param colorScheme
     *            Color scheme.
     * @param width
     *            Drag bumps width.
     * @param height
     *            Drag bumps height.
     * @param maxNumberOfStripes
     *            The maximum number of bump stripes (rows or columns).
     * @return Drag bumps image.
     */
    public static BufferedImage getDragImage(Component c, SubstanceColorScheme colorScheme,
            int width, int height, int maxNumberOfStripes) {
        BufferedImage result = SubstanceCoreUtilities.getBlankImage(width, height);
        Graphics2D graphics = (Graphics2D) result.getGraphics();

        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        boolean isDark = colorScheme.isDark();
        Color back1 = isDark ? colorScheme.getLightColor()
                : SubstanceColorUtilities.getInterpolatedColor(colorScheme.getLightColor(),
                        colorScheme.getDarkColor(), 0.8);
        Color back2 = isDark ? colorScheme.getExtraLightColor()
                : SubstanceColorUtilities.getInterpolatedColor(colorScheme.getMidColor(),
                        colorScheme.getDarkColor(), 0.4);
        Color fore = isDark ? colorScheme.getDarkColor() : colorScheme.getUltraLightColor();

        int componentFontSize = SubstanceSizeUtils.getComponentFontSize(c);
        int bumpDotDiameter = SubstanceSizeUtils.getDragBumpDiameter(componentFontSize);
        int bumpCellSize = (int) (1.5 * bumpDotDiameter + 1);
        int bumpRows = Math.max(1, height / bumpCellSize - 1);
        int bumpColumns = Math.max(1, (width - 2) / bumpCellSize);
        if (maxNumberOfStripes > 0) {
            if (height > width)
                bumpColumns = Math.min(bumpColumns, maxNumberOfStripes);
            else
                bumpRows = Math.min(bumpRows, maxNumberOfStripes);
        }

        int bumpRowOffset = (height - bumpCellSize * bumpRows) / 2;
        int bumpColOffset = 1 + (width - bumpCellSize * bumpColumns) / 2;

        for (int col = 0; col < bumpColumns; col++) {
            int cx = bumpColOffset + col * bumpCellSize;
            boolean isEvenCol = (col % 2 == 0);
            int offsetY = isEvenCol ? 0 : bumpDotDiameter;
            for (int row = 0; row < bumpRows; row++) {
                int cy = offsetY + bumpRowOffset + row * bumpCellSize;
                graphics.setColor(fore);
                graphics.fillOval(cx + 1, cy + 1, bumpDotDiameter, bumpDotDiameter);
                // graphics.setColor(back1);
                graphics.setPaint(new GradientPaint(cx, cy, back1, cx + bumpDotDiameter - 1,
                        cy + bumpDotDiameter - 1, back2));
                graphics.fillOval(cx, cy, bumpDotDiameter, bumpDotDiameter);
            }
        }
        return result;
    }

    /**
     * Paints the bump dots on the split pane dividers.
     * 
     * @param g
     *            Graphics context.
     * @param divider
     *            Split pane divider.
     * @param x
     *            X coordinate of the bump dots.
     * @param y
     *            Y coordinate of the bump dots.
     * @param width
     *            Width of the bump dots area.
     * @param height
     *            Height of the bump dots area.
     * @param isHorizontal
     *            Indicates whether the dots are horizontal.
     * @param colorScheme
     *            Color scheme.
     */
    public static void paintSplitDividerBumpImage(Graphics g, SubstanceSplitPaneDivider divider,
            int x, int y, int width, int height, boolean isHorizontal,
            SubstanceColorScheme colorScheme) {
        Graphics2D graphics = (Graphics2D) g.create();
        graphics.translate(x, y);

        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        int componentFontSize = SubstanceSizeUtils.getComponentFontSize(divider);
        int bumpDotDiameter = SubstanceSizeUtils.getBigDragBumpDiameter(componentFontSize);
        int bumpCellSize = (int) (1.5 * bumpDotDiameter + 1);
        int bumpRows = isHorizontal ? 1 : Math.max(1, height / bumpCellSize - 1);
        int bumpColumns = isHorizontal ? Math.max(1, (width - 2) / bumpCellSize) : 1;

        int bumpRowOffset = (height - bumpCellSize * bumpRows) / 2;
        int bumpColOffset = 1 + (width - bumpCellSize * bumpColumns) / 2;

        BufferedImage singleDot = SubstanceCoreUtilities.getBlankImage(bumpDotDiameter,
                bumpDotDiameter);
        Graphics2D dotGraphics = (Graphics2D) singleDot.getGraphics();
        dotGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        dotGraphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
                RenderingHints.VALUE_STROKE_PURE);

        Color markColor = SubstanceColorUtilities.getMarkColor(colorScheme, divider.isEnabled());
        dotGraphics.setColor(markColor);
        dotGraphics.fillOval(0, 0, bumpDotDiameter, bumpDotDiameter);

        dotGraphics.setComposite(getAlphaComposite(0.4f));
        SubstanceBorderPainter borderPainter = SubstanceCoreUtilities.getBorderPainter(divider);
        borderPainter.paintBorder(dotGraphics, divider, bumpDotDiameter, bumpDotDiameter,
                new Ellipse2D.Float(0, 0, bumpDotDiameter, bumpDotDiameter), null, colorScheme);

        graphics.setComposite(WidgetUtilities.getAlphaComposite(divider, 0.8f, g));
        for (int col = 0; col < bumpColumns; col++) {
            int cx = bumpColOffset + col * bumpCellSize;
            for (int row = 0; row < bumpRows; row++) {
                int cy = bumpRowOffset + row * bumpCellSize + (bumpCellSize - bumpDotDiameter) / 2;
                NeonCortex.drawImage(graphics, singleDot, cx, cy);
            }
        }
        graphics.dispose();
    }

    /**
     * Returns resize grip image.
     * 
     * @param c
     *            Component.
     * @param colorScheme
     *            Color scheme.
     * @param dimension
     *            Resize grip width.
     * @param isCrowded
     *            Indicates whether the grips should be painted closely.
     * @return Resize grip image.
     */
    public static BufferedImage getResizeGripImage(Component c, SubstanceColorScheme colorScheme,
            int dimension, boolean isCrowded) {
        BufferedImage result = SubstanceCoreUtilities.getBlankImage(dimension, dimension);
        Graphics2D graphics = (Graphics2D) result.getGraphics();

        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        boolean isDark = colorScheme.isDark();
        Color back1 = isDark ? colorScheme.getLightColor()
                : SubstanceColorUtilities.getInterpolatedColor(colorScheme.getLightColor(),
                        colorScheme.getDarkColor(), 0.8);
        Color back2 = isDark ? colorScheme.getExtraLightColor()
                : SubstanceColorUtilities.getInterpolatedColor(colorScheme.getMidColor(),
                        colorScheme.getDarkColor(), 0.4);
        Color fore = isDark ? colorScheme.getDarkColor() : colorScheme.getUltraLightColor();

        int bumpDotDiameter = SubstanceSizeUtils
                .getDragBumpDiameter(SubstanceSizeUtils.getComponentFontSize(c));
        int bumpCellSize = (int) (1.5 * bumpDotDiameter + 1);
        if (isCrowded)
            bumpCellSize--;
        int bumpLines = dimension / bumpCellSize;

        int bumpOffset = (dimension - bumpCellSize * bumpLines) / 2;

        for (int col = 0; col < bumpLines; col++) {
            int cx = bumpOffset + col * bumpCellSize;
            for (int row = (bumpLines - col - 1); row < bumpLines; row++) {
                int cy = bumpOffset + row * bumpCellSize;
                graphics.setColor(fore);
                graphics.fillOval(cx + 1, cy + 1, bumpDotDiameter, bumpDotDiameter);
                graphics.setPaint(new GradientPaint(cx, cy, back1, cx + bumpDotDiameter - 1,
                        cy + bumpDotDiameter - 1, back2));
                graphics.fillOval(cx, cy, bumpDotDiameter, bumpDotDiameter);
            }
        }
        return result;
    }

    /**
     * Retrieves tree icon.
     * 
     * @param tree
     *            Tree.
     * @param fillScheme
     *            Icon fill color scheme.
     * @param borderScheme
     *            Icon border color scheme.
     * @param isCollapsed
     *            Collapsed state.
     * @return Tree icon.
     */
    public static BufferedImage getTreeIcon(JTree tree, SubstanceColorScheme fillScheme,
            SubstanceColorScheme borderScheme, SubstanceColorScheme markScheme,
            boolean isCollapsed) {
        int fontSize = SubstanceSizeUtils.getComponentFontSize(tree);
        int dim = SubstanceSizeUtils.getTreeIconSize(fontSize);
        BufferedImage result = SubstanceCoreUtilities.getBlankImage(dim, dim);
        Graphics2D graphics = (Graphics2D) result.getGraphics();

        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_OFF);
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC);

        SubstanceFillPainter fillPainter = SimplisticSoftBorderReverseFillPainter.INSTANCE;
        SubstanceBorderPainter fbp = new FlatBorderPainter();

        float borderDelta = SubstanceSizeUtils.getBorderStrokeWidth() / 2.0f;
        Shape contour = SubstanceOutlineUtilities.getBaseOutline(dim, dim,
                SubstanceSizeUtils.getClassicButtonCornerRadius(dim) / 1.5f, null, borderDelta);

        fillPainter.paintContourBackground(graphics, tree, dim, dim, contour, false, fillScheme,
                false);
        fbp.paintBorder(graphics, tree, dim, dim, contour, null, borderScheme);

        Color signColor = markScheme.getForegroundColor();
        graphics.setColor(signColor);
        float mid = dim / 2;
        float length = 7 * dim / 12;
        // Horizontal stroke
        graphics.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
        graphics.draw(new Line2D.Float(mid - length / 2, mid, mid + length / 2, mid));
        if (isCollapsed) {
            // Vertical stroke
            graphics.draw(new Line2D.Float(mid, mid - length / 2, mid, mid + length / 2));
        }

        return result;
    }

    /**
     * Retrieves a single crayon of the specified color and dimensions for the crayon panel in color
     * chooser.
     * 
     * @param mainColor
     *            Crayon main color.
     * @param width
     *            Crayon width.
     * @param height
     *            Crayon height.
     * @return Crayon image.
     */
    private static BufferedImage getSingleCrayon(Color mainColor, int width, int height) {
        BufferedImage image = SubstanceCoreUtilities.getBlankImage(width, height);

        int baseTop = (int) (0.2 * height);

        Graphics2D graphics = (Graphics2D) image.getGraphics().create();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        int r = mainColor.getRed();
        int g = mainColor.getGreen();
        int b = mainColor.getBlue();
        // light coefficient
        double lc = 0.8;
        int lr = (int) (r + (255 - r) * lc);
        int lg = (int) (g + (255 - g) * lc);
        int lb = (int) (b + (255 - b) * lc);
        // dark coefficient
        double dc = 0.05;
        int dr = (int) ((1.0 - dc) * r);
        int dg = (int) ((1.0 - dc) * g);
        int db = (int) ((1.0 - dc) * b);

        Color lightColor = new Color(lr, lg, lb);
        Color darkColor = new Color(dr, dg, db);

        LinearGradientPaint paint = new LinearGradientPaint(0, 0, width, 0,
                new float[] { 0.0f, 0.3f, 0.5f, 0.9f, 1.0f },
                new Color[] { lightColor, darkColor, darkColor, lightColor, lightColor },
                CycleMethod.REPEAT);
        graphics.setPaint(paint);
        graphics.fillRect(0, baseTop, width, height);

        int dbwr = lr;
        int dbwg = lg;
        int dbwb = lb;
        int lbwr = 128 + dr / 4;
        int lbwg = 128 + dg / 4;
        int lbwb = 128 + db / 4;

        Color lightStripeColor = new Color(lbwr, lbwg, lbwb);
        Color darkStripeColor = new Color(dbwr, dbwg, dbwb);

        int stripeTop = (int) (0.35 * height);
        int stripeHeight = (int) (0.04 * height);
        LinearGradientPaint stripePaint = new LinearGradientPaint(0, 0, width, 0,
                new float[] { 0.0f, 0.3f, 0.5f, 0.9f, 1.0f },
                new Color[] { lightStripeColor, darkStripeColor, darkStripeColor, lightStripeColor,
                                lightStripeColor },
                CycleMethod.REPEAT);
        graphics.setPaint(stripePaint);
        graphics.fillRect(0, stripeTop, width, stripeHeight);

        graphics.setColor(lightStripeColor);
        graphics.drawRect(0, stripeTop, width - 1, stripeHeight);

        // create cap path
        GeneralPath capPath = new GeneralPath();
        capPath.moveTo(0.5f * width - 3, 4);
        capPath.quadTo(0.5f * width, 0, 0.5f * width + 3, 4);
        capPath.lineTo(width - 3, baseTop);
        capPath.lineTo(2, baseTop);
        capPath.lineTo(0.5f * width - 3, 4);

        graphics.setClip(capPath);

        RadialGradientPaint capPaint = new RadialGradientPaint(width / 2, baseTop, baseTop,
                width / 2, 4 * baseTop / 3, new float[] { 0.0f, 0.1f, 1.0f },
                new Color[] { mainColor, mainColor, lightColor }, CycleMethod.NO_CYCLE);
        graphics.setPaint(capPaint);
        graphics.fillRect(0, 0, width, baseTop);

        graphics.setStroke(new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));

        graphics.setClip(null);
        graphics.setColor(new Color(64 + dr / 2, 64 + dg / 2, 64 + db / 2, 200));
        graphics.drawRect(0, baseTop, width - 1, height - baseTop - 1);
        graphics.draw(capPath);

        graphics.dispose();

        return image;
    }

    /**
     * Crayon colors.
     */
    private final static int[] crayonColors = { 0x800000, // Cayenne
                    0x808000, // Asparagus
                    0x008000, // Clover
                    0x008080, // Teal
                    0x000080, // Midnight
                    0x800080, // Plum
                    0x7f7f7f, // Tin
                    0x808080, // Nickel

                    0x804000, // Mocha
                    0x408000, // Fern
                    0x008040, // Moss
                    0x004080, // Ocean
                    0x400080, // Eggplant
                    0x800040, // Maroon
                    0x666666, // Steel
                    0x999999, // Aluminium

                    0xff0000, // Maraschino
                    0xffff00, // Lemon
                    0x00ff00, // Spring
                    0x00ffff, // Turquoise
                    0x0000ff, // Blueberry
                    0xff00ff, // Magenta
                    0x4c4c4c, // Iron
                    0xb3b3b3, // Magnesium

                    0xff8000, // Tangerine
                    0x80ff00, // Lime
                    0x00ff80, // Sea Foam
                    0x0080ff, // Aqua
                    0x8000ff, // Grape
                    0xff0080, // Strawberry
                    0x333333, // Tungsten
                    0xcccccc, // Silver

                    0xff6666, // Salmon
                    0xffff66, // Banana
                    0x66ff66, // Flora
                    0x66ffff, // Ice
                    0x6666ff, // Orchid
                    0xff66ff, // Bubblegum
                    0x191919, // Lead
                    0xe6e6e6, // Mercury

                    0xffcc66, // Cantaloupe
                    0xccff66, // Honeydew
                    0x66ffcc, // Spindrift
                    0x66ccff, // Sky
                    0xcc66ff, // Lavender
                    0xff6fcf, // Carnation
                    0x000000, // Licorice
                    0xffffff, // Snow
    };

    /**
     * Retrieves crayon X offset.
     * 
     * @param i
     *            Crayon index.
     * @return Crayon X offset.
     */
    private static int crayonX(int i) {
        return (i % 8) * 22 + 4 + ((i / 8) % 2) * 11;
    }

    /**
     * Retrieves crayon Y offset.
     * 
     * @param i
     *            Crayon index.
     * @return Crayon Y offset.
     */
    private static int crayonY(int i) {
        return (i / 8) * 20 + 23;
    }

    /**
     * Retrieves crayons image for the crayon panel of color chooser.
     * 
     * @return Crayons image.
     */
    public static Image getCrayonsImage(Color fillColor) {
        int iw = 195;
        int ih = 208;
        Image image = SubstanceCoreUtilities.getBlankImage(iw, ih);
        Graphics2D graphics = (Graphics2D) image.getGraphics().create();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);
        graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC);

        graphics.setColor(fillColor);
        graphics.fillRect(0, 0, iw, ih);

        for (int i = 0; i < SubstanceImageCreator.crayonColors.length; i++) {
            Color crayonColor = new Color(0xff000000 | SubstanceImageCreator.crayonColors[i]);
            BufferedImage crayonImage = SubstanceImageCreator.getSingleCrayon(crayonColor, 22, 120);
            NeonCortex.drawImage(graphics, crayonImage, SubstanceImageCreator.crayonX(i),
                    SubstanceImageCreator.crayonY(i));
        }

        graphics.dispose();
        return image;
    }

    /**
     * Returns small icon representation of the specified integer value. The remainder of dividing
     * the integer by 16 is translated to four circles arranged in 2*2 grid.
     * 
     * @param value
     *            Integer value to represent.
     * @param colorScheme
     *            Icon color scheme.
     * @return Icon representation of the specified integer value.
     */
    public static ImageWrapperIcon getHexaMarker(int value,
            SubstanceColorScheme colorScheme) {
        BufferedImage result = SubstanceCoreUtilities.getBlankImage(9, 9);

        value %= 16;
        Color offColor = null;
        Color onColor = null;
        if (colorScheme == null) {
            return new ImageWrapperIcon(result);
        }
        boolean isDark = colorScheme.isDark();
        offColor = isDark
                ? SubstanceColorUtilities.getInterpolatedColor(colorScheme.getUltraLightColor(),
                        Color.white, 0.7)
                : SubstanceColorUtilities.deriveByBrightness(colorScheme.getMidColor(), -0.6f);
        onColor = isDark
                ? SubstanceColorUtilities.getInterpolatedColor(colorScheme.getUltraLightColor(),
                        Color.white, 0.2)
                : colorScheme.getForegroundColor();

        boolean bit1 = ((value & 0x1) != 0);
        boolean bit2 = ((value & 0x2) != 0);
        boolean bit3 = ((value & 0x4) != 0);
        boolean bit4 = ((value & 0x8) != 0);

        Graphics2D graphics = (Graphics2D) result.getGraphics().create();
        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                RenderingHints.VALUE_ANTIALIAS_ON);

        graphics.setColor(bit1 ? onColor : offColor);
        graphics.fillOval(5, 5, 4, 4);
        graphics.setColor(bit2 ? onColor : offColor);
        graphics.fillOval(5, 0, 4, 4);
        graphics.setColor(bit3 ? onColor : offColor);
        graphics.fillOval(0, 5, 4, 4);
        graphics.setColor(bit4 ? onColor : offColor);
        graphics.fillOval(0, 0, 4, 4);

        graphics.dispose();
        return new ImageWrapperIcon(result);
    }

    /**
     * Returns a lock icon that matches the specified scheme.
     * 
     * @param scheme
     *            Scheme instance.
     * @return Lock icon that matches the specified scheme.
     */
    public static Icon getSmallLockIcon(SubstanceColorScheme scheme, Component c) {
        int componentFontSize = SubstanceSizeUtils.getComponentFontSize(c);
        int extraPadding = SubstanceSizeUtils.getExtraPadding(componentFontSize);
        int size = 9 + 2 * extraPadding;

        return SubstanceCortex.GlobalScope.getIconPack().getLockIcon(size, scheme);
    }

    /**
     * Creates a new version of the specified icon that is rendered in the colors of the specified
     * color scheme.
     * 
     * @param comp
     *            Component.
     * @param original
     *            The original icon.
     * @param colorScheme
     *            Color scheme.
     * @return Scheme-based version of the original icon.
     */
    public static BufferedImage getColorSchemeImage(Component comp, Icon original,
            SubstanceColorScheme colorScheme, float originalBrightnessFactor) {
        int w = original.getIconWidth();
        int h = original.getIconHeight();
        if ((w == 0) || (h == 0)) {
            return null;
        }
        BufferedImage origImage = SubstanceCoreUtilities.getBlankImage(w, h);
        Graphics2D g2d = (Graphics2D) origImage.getGraphics().create();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
                RenderingHints.VALUE_INTERPOLATION_BICUBIC);
        original.paintIcon(comp, origImage.getGraphics(), 0, 0);
        g2d.dispose();

        BufferedImage result = getColorSchemeImage(origImage, colorScheme,
                originalBrightnessFactor);
        return result;
    }

    /**
     * Creates a new version of the specified image that is rendered in the colors of the specified
     * color scheme.
     * 
     * @param original
     *            The original image.
     * @param colorScheme
     *            Color scheme.
     * @param originalBrightnessFactor
     *            The original brightness factor.
     * @return Scheme-based version of the original icon.
     */
    public static BufferedImage getColorSchemeImage(BufferedImage original,
            SubstanceColorScheme colorScheme, float originalBrightnessFactor) {
        return ColorSchemeFilter.getColorSchemeFilter(colorScheme, originalBrightnessFactor)
                .filter(original, null);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy