/*
 * Copyright (c) 2005-2008 Substance 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 Substance Kirill Grouchnikov 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.jvnet.substance.utils;

import java.awt.*;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.geom.*;
import java.awt.image.*;

import javax.swing.*;
import javax.swing.text.JTextComponent;

import org.jvnet.lafwidget.animation.FadeKind;
import org.jvnet.lafwidget.layout.TransitionLayout;
import org.jvnet.substance.api.ComponentState;
import org.jvnet.substance.api.SubstanceColorScheme;
import org.jvnet.substance.api.ComponentState.ColorSchemeKind;
import org.jvnet.substance.api.SubstanceConstants.Side;
import org.jvnet.substance.colorscheme.ShiftColorScheme;
import org.jvnet.substance.painter.border.FlatBorderPainter;
import org.jvnet.substance.painter.border.SubstanceBorderPainter;
import org.jvnet.substance.painter.gradient.SimplisticGradientPainter;
import org.jvnet.substance.painter.gradient.SubstanceGradientPainter;
import org.jvnet.substance.utils.filters.*;
import org.jvnet.substance.watermark.SubstanceWatermark;

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

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

		/*
		 * (non-Javadoc)
		 * 
		 * @see
		 * org.jvnet.substance.painter.SubstanceGradientPainter#getDisplayName()
		 */
		@Override
		public String getDisplayName() {
			return "Simplistic Soft Border Reverse";
		}

		@Override
		public Color getTopFillColor(SubstanceColorScheme interpolationScheme1,
				SubstanceColorScheme interpolationScheme2, double cycleCoef,
				boolean useCyclePosAsInterpolation) {
			return super
					.getBottomFillColor(interpolationScheme1,
							interpolationScheme2, cycleCoef,
							useCyclePosAsInterpolation);
		}

		@Override
		public Color getBottomFillColor(
				SubstanceColorScheme interpolationScheme1,
				SubstanceColorScheme interpolationScheme2, double cycleCoef,
				boolean useCyclePosAsInterpolation) {
			return super
					.getTopFillColor(interpolationScheme1,
							interpolationScheme2, cycleCoef,
							useCyclePosAsInterpolation);
		}
	}

	/**
	 * 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 borderScheme1
	 *            First border color scheme.
	 * @param borderScheme2
	 *            Second border color scheme.
	 * @param cyclePos
	 *            Cycle position for interpolating the border color schemes.
	 */
	public static void paintBorder(Component c, Graphics2D graphics, int x,
			int y, int width, int height, float radius,
			SubstanceColorScheme borderScheme1,
			SubstanceColorScheme borderScheme2, float cyclePos) {

		SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
				.getBorderPainter(c);
		graphics.translate(x, y);
		int componentFontSize = SubstanceSizeUtils.getComponentFontSize(c);
		int borderDelta = (int) Math.floor(SubstanceSizeUtils
				.getBorderStrokeWidth(componentFontSize) / 2.0);
		Shape contour = SubstanceOutlineUtilities.getBaseOutline(width, height,
				radius, null, borderDelta);
		int borderThickness = (int) SubstanceSizeUtils
				.getBorderStrokeWidth(componentFontSize);
		GeneralPath contourInner = (c instanceof JTextComponent) ? null
				: SubstanceOutlineUtilities.getBaseOutline(width, height,
						radius, null, borderThickness + borderDelta);
		borderPainter.paintBorder(graphics, c, width, height, contour,
				contourInner, borderScheme1, borderScheme2, cyclePos,
				borderScheme1 != borderScheme2);
		graphics.translate(-x, -y);
	}

	/**
	 * Retrieves check mark image.
	 * 
	 * @param button
	 *            Button for the check mark.
	 * @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(AbstractButton button,
			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 Icon getArrowIcon(int fontSize, int direction,
			SubstanceColorScheme colorScheme) {
		float width = SubstanceSizeUtils.getArrowIconWidth(fontSize);
		float height = SubstanceSizeUtils.getArrowIconHeight(fontSize);
		if (direction == SwingConstants.CENTER)
			height *= 2;
		float strokeWidth = SubstanceSizeUtils.getArrowStrokeWidth(fontSize);
		return new ImageIcon(getArrow(width, height, strokeWidth, direction,
				colorScheme));
	}

	/**
	 * 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 Icon getArrowIcon(float width, float height,
			float strokeWidth, int direction, SubstanceColorScheme scheme) {
		return new ImageIcon(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
	 */
	public static BufferedImage getArrow(float width, float height,
			float strokeWidth, int direction, SubstanceColorScheme scheme) {
		BufferedImage arrowImage = SubstanceCoreUtilities.getBlankImage(
				(int) width, (int) height);

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

		// get graphics and set hints
		Graphics2D graphics = (Graphics2D) arrowImage.getGraphics();

		// graphics.setColor(Color.red);
		// graphics.fillRect(0, 0, width, height);

		graphics.translate(1, 1);
		width -= 2;
		height -= 2;
		graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);

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

		graphics.setColor(arrowColor);
		int cap = (width < 15) ? BasicStroke.CAP_BUTT : BasicStroke.CAP_ROUND;
		Stroke stroke = new BasicStroke(strokeWidth, cap,
				BasicStroke.JOIN_MITER);

		graphics.setStroke(stroke);

		int cushion = (int) strokeWidth / 2;
		if (direction == SwingConstants.CENTER) {
			BufferedImage top = getArrow(width, height / 2, strokeWidth,
					SwingConstants.NORTH, scheme);
			BufferedImage bottom = getArrow(width, height / 2, strokeWidth,
					SwingConstants.SOUTH, scheme);
			graphics.drawImage(top, 0, 1, null);
			graphics.drawImage(bottom, 0, (int) height / 2 - 1, null);
			return arrowImage;
		} else {
			GeneralPath gp = new GeneralPath();
			gp.moveTo(cushion, strokeWidth);
			gp.lineTo((float) 0.5 * (width - 1), height - 1 - cushion);
			gp.lineTo(width - 1 - cushion, strokeWidth);
			graphics.draw(gp);

			// graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
			// RenderingHints.VALUE_ANTIALIAS_OFF);
			// graphics.setStroke(new BasicStroke(1.0f, cap,
			// BasicStroke.JOIN_MITER));
			// graphics.setColor(Color.red);
			// graphics.drawRect(0, 0, (int)width - 1, (int)height - 1);

			int quadrantCounterClockwise = 0;
			switch (direction) {
			case SwingConstants.NORTH:
				quadrantCounterClockwise = 2;
				break;
			case SwingConstants.WEST:
				quadrantCounterClockwise = 1;
				break;
			case SwingConstants.SOUTH:
				quadrantCounterClockwise = 0;
				break;
			case SwingConstants.EAST:
				quadrantCounterClockwise = 3;
				break;
			}
			BufferedImage rotatedImage = SubstanceImageCreator.getRotated(
					arrowImage, quadrantCounterClockwise);

			return rotatedImage;
		}
	}

	/**
	 * Returns double arrow icon for the specified parameters.
	 * 
	 * @param fontSize
	 *            Font size.
	 * @param deltaWidth
	 *            Arrow width delta.
	 * @param deltaHeight
	 *            Arrow height delta.
	 * @param deltaStrokeWidth
	 *            Arrow stroke width delta.
	 * @param direction
	 *            Arrow direction.
	 * @param colorScheme
	 *            Color scheme for the arrow.
	 * @return Double arrow icon.
	 */
	public static Icon getDoubleArrowIcon(int fontSize, float deltaWidth,
			float deltaHeight, float deltaStrokeWidth, int direction,
			SubstanceColorScheme colorScheme) {
		float arrowWidth = SubstanceSizeUtils
				.getArrowIconWidth(SubstanceSizeUtils.getControlFontSize())
				+ deltaWidth;
		float arrowHeight = SubstanceSizeUtils
				.getArrowIconHeight(SubstanceSizeUtils.getControlFontSize())
				+ deltaHeight;
		float arrowStrokeWidth = SubstanceSizeUtils
				.getDoubleArrowStrokeWidth(SubstanceSizeUtils
						.getControlFontSize())
				+ deltaStrokeWidth;
		return getDoubleArrowIcon(arrowWidth, arrowHeight, arrowStrokeWidth,
				direction, colorScheme);
	}

	/**
	 * Retrieves arrow icon.
	 * 
	 * @param width
	 *            Arrow width.
	 * @param height
	 *            Arrow 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 Icon getDoubleArrowIcon(float width, float height,
			float strokeWidth, int direction, SubstanceColorScheme colorScheme) {
		int delta = 3 + SubstanceSizeUtils.getExtraPadding(SubstanceSizeUtils
				.getControlFontSize());
		BufferedImage downArrowImage = SubstanceCoreUtilities.getBlankImage(
				(int) width, (int) height + delta - 2);

		BufferedImage singleArrow = getArrow(width, Math.max(1, height - delta
				+ 2), strokeWidth, SwingConstants.SOUTH, colorScheme);

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

		graphics.drawImage(singleArrow, 0, 0, null);
		graphics.drawImage(singleArrow, 0, delta, null);

		int quadrantCounterClockwise = 0;
		switch (direction) {
		case SwingConstants.NORTH:
			quadrantCounterClockwise = 2;
			break;
		case SwingConstants.WEST:
			quadrantCounterClockwise = 1;
			break;
		case SwingConstants.SOUTH:
			quadrantCounterClockwise = 0;
			break;
		case SwingConstants.EAST:
			quadrantCounterClockwise = 3;
			break;
		}
		BufferedImage arrowImage = SubstanceImageCreator.getRotated(
				downArrowImage, quadrantCounterClockwise);

		return new ImageIcon(arrowImage);
	}

	/**
	 * Returns rotated image.
	 * 
	 * @param bi
	 *            Image to rotate.
	 * @param quadrantClockwise
	 *            Amount of quadrants to rotate in clockwise directio. The
	 *            rotation angle is 90 times this value.
	 * @return Rotated image.
	 */
	public static BufferedImage getRotated(BufferedImage bi,
			int quadrantClockwise) {
		quadrantClockwise = quadrantClockwise % 4;
		int width = bi.getWidth();
		int height = bi.getHeight();
		if ((quadrantClockwise == 1) || (quadrantClockwise == 3)) {
			width = bi.getHeight();
			height = bi.getWidth();
		}
		BufferedImage biRot = SubstanceCoreUtilities.getBlankImage(width,
				height);
		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();
		if (at != null)
			rotg.setTransform(at);
		rotg.drawImage(bi, 0, 0, null);
		rotg.dispose();
		return biRot;
	}

	/**
	 * Returns rotated image.
	 * 
	 * @param bi
	 *            Image to rotate.
	 * @param quadrantClockwise
	 *            Amount of quadrants to rotate in clockwise directio. The
	 *            rotation angle is 90 times this value.
	 * @return Rotated image.
	 */
	public static VolatileImage getRotated(final VolatileImage bi,
			int quadrantClockwise) {
		quadrantClockwise = quadrantClockwise % 4;
		int width = bi.getWidth();
		int height = bi.getHeight();
		if ((quadrantClockwise == 1) || (quadrantClockwise == 3)) {
			width = bi.getHeight();
			height = bi.getWidth();
		}
		VolatileImage biRot = SubstanceCoreUtilities.getBlankVolatileImage(
				width, height);
		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();
		if (at != null)
			rotg.setTransform(at);
		rotg.drawImage(bi, 0, 0, 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 toGreyscale(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);
		return new ImageIcon(new GrayscaleFilter().filter(result, null));
	}

	/**
	 * 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 ImageIcon(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 prevState
	 *            Previous component state.
	 * @param offsetX
	 *            Offset on X axis - should be positive in order to see the
	 *            entire radio button.
	 * @param scheme1
	 *            First color scheme.
	 * @param scheme2
	 *            Second color scheme.
	 * @param interpolationCyclePos10
	 *            Interpolation cycle.
	 * @param checkMarkVisibility
	 *            Checkmark 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,
			int dimension, ComponentState componentState,
			ComponentState prevState, int offsetX,
			SubstanceColorScheme scheme1, SubstanceColorScheme scheme2,
			float interpolationCyclePos10, float checkMarkVisibility) {

		// ComponentState.ColorSchemeKind kind = componentState
		// .getColorSchemeKind();

		float cyclePos = (scheme1 != scheme2) ? interpolationCyclePos10
				: componentState.getCyclePosition();

		SubstanceGradientPainter painter = SubstanceCoreUtilities
				.getGradientPainter(component);
		if (componentState.getColorSchemeKind() != ColorSchemeKind.CURRENT) {
			painter = SimplisticSoftBorderReverseGradientPainter.INSTANCE;
		}
		SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
				.getBorderPainter(component);

		float borderThickness = SubstanceSizeUtils
				.getBorderStrokeWidth(dimension);
		int delta = (int) (borderThickness - 0.6);
		// System.out.println(dimension + ":" + borderThickness + ":" + delta);

		// float fDelta = borderThickness / 2.0f;
		Shape contourBorder = new Ellipse2D.Float(delta, delta, dimension - 2
				* delta - 1, dimension - 2 * delta - 1);

		BufferedImage offBackground = SubstanceCoreUtilities.getBlankImage(
				dimension + offsetX, dimension);
		Graphics2D graphics = (Graphics2D) offBackground.getGraphics();
		float alpha = SubstanceColorSchemeUtilities.getAlpha(component,
				componentState);
		graphics.setComposite(AlphaComposite.getInstance(
				AlphaComposite.SRC_OVER, alpha));

		graphics.translate(offsetX, 0);
		painter.paintContourBackground(graphics, component, dimension,
				dimension, contourBorder, false, scheme1, scheme2, cyclePos,
				true, scheme1 != scheme2);

		Shape contourInner = new Ellipse2D.Float(delta + borderThickness, delta
				+ borderThickness, dimension - 2 * delta - 2 * borderThickness,
				dimension - 2 * delta - 2 * borderThickness);

		borderPainter.paintBorder(graphics, component, dimension, dimension,
				contourBorder, contourInner, SubstanceColorSchemeUtilities
						.getBorderColorScheme(component, componentState),
				SubstanceColorSchemeUtilities.getBorderColorScheme(component,
						prevState), cyclePos, scheme1 != scheme2);
		graphics.setComposite(AlphaComposite.SrcOver);
		// graphics.translate(-offsetX, 0);

		float rc = dimension / 2.0f;
		float radius = dimension / 4.5f;
		// graphics.translate(offsetX, 0);

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

		if (componentState.isKindActive(FadeKind.SELECTION)
				|| (checkMarkVisibility > 0.0)) {
			// mark
			Shape markOval = new Ellipse2D.Double(rc - radius, rc - radius,
					2 * radius, 2 * radius);

			graphics.setComposite(AlphaComposite.getInstance(
					AlphaComposite.SRC_OVER, alpha * checkMarkVisibility));
			drawRadioMark(
					graphics,
					SubstanceColorUtilities
							.getMarkColor(
									scheme1,
									componentState.getColorSchemeKind() != ColorSchemeKind.DISABLED),
					markOval);
			graphics.setComposite(AlphaComposite.getInstance(
					AlphaComposite.SRC_OVER, alpha * checkMarkVisibility
							* cyclePos));
			drawRadioMark(
					graphics,
					SubstanceColorUtilities
							.getMarkColor(
									scheme2,
									componentState.getColorSchemeKind() != ColorSchemeKind.DISABLED),
					markOval);
		} else {
			// draw ghost mark holder
			graphics.setPaint(new GradientPaint(rc + radius, rc - radius,
					scheme1.getDarkColor(), rc - radius, rc + radius, scheme1
							.getLightColor()));
			Shape markOval = new Ellipse2D.Double(rc - radius, rc - radius,
					2 * radius, 2 * radius);
			graphics.setComposite(AlphaComposite.getInstance(
					AlphaComposite.SRC_OVER, alpha * 0.3f));
			graphics.fill(markOval);
		}
		graphics.translate(-offsetX, 0);

		return offBackground;
	}

	/**
	 * Draws radio mark.
	 * 
	 * @param graphics
	 *            Graphics context.
	 * @param color
	 *            Radio mark color.
	 * @param markOval
	 *            Radio mark shape.
	 */
	private static void drawRadioMark(Graphics2D graphics, Color color,
			Shape markOval) {
		graphics.setColor(color);
		graphics.fill(markOval);
	}

	/**
	 * 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 prevState
	 *            Previous component state.
	 * @param colorScheme1
	 *            First color scheme.
	 * @param colorScheme2
	 *            Second color scheme.
	 * @param interpolationCyclePos
	 *            Interpolation cycle in 0.0-1.0 range.
	 * @param checkMarkVisibility
	 *            Checkmark visibility in 0.0-1.0 range.
	 * @param isCheckMarkFadingOut
	 *            if <code>true</code>, the value of
	 *            <code>interpolationCyclePos10</code> 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,
			int dimension, ComponentState componentState,
			ComponentState prevState, SubstanceColorScheme colorScheme1,
			SubstanceColorScheme colorScheme2, float interpolationCyclePos,
			float checkMarkVisibility, boolean isCheckMarkFadingOut) {

		int offset = SubstanceSizeUtils.getAdjustedSize(SubstanceSizeUtils
				.getComponentFontSize(button), 3, 9, 1, false);
		int delta = offset;
		float cornerRadius = SubstanceSizeUtils
				.getClassicButtonCornerRadius(SubstanceSizeUtils
						.getComponentFontSize(button));
		if (dimension <= 10) {
			offset = 2;
			cornerRadius = 2;
		}

		float cyclePos = (colorScheme1 != colorScheme2) ? interpolationCyclePos
				: componentState.getCyclePosition();

		int contourDim = dimension - delta;
		int borderDelta = (int) Math.floor(SubstanceSizeUtils
				.getBorderStrokeWidth(SubstanceSizeUtils
						.getComponentFontSize(button)) / 2.0);
		GeneralPath contour = SubstanceOutlineUtilities.getBaseOutline(
				contourDim, contourDim, cornerRadius, null, borderDelta);

		SubstanceGradientPainter painter = SubstanceCoreUtilities
				.getGradientPainter(button);
		if (componentState.getColorSchemeKind() != ColorSchemeKind.CURRENT) {
			painter = SimplisticSoftBorderReverseGradientPainter.INSTANCE;
		}
		SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
				.getBorderPainter(button);

		BufferedImage offBackground = SubstanceCoreUtilities.getBlankImage(
				dimension, dimension);
		Graphics2D graphics = (Graphics2D) offBackground.getGraphics();
		float alpha = SubstanceColorSchemeUtilities.getAlpha(button,
				componentState);
		graphics.setComposite(AlphaComposite.getInstance(
				AlphaComposite.SRC_OVER, alpha));

		graphics.translate(delta - 1, delta - 1);
		painter.paintContourBackground(graphics, button, contourDim,
				contourDim, contour, false, colorScheme1, colorScheme2,
				cyclePos, true, colorScheme1 != colorScheme2);

		// graphics.drawImage(background, 0, 0, null);
		int borderThickness = (int) SubstanceSizeUtils
				.getBorderStrokeWidth(dimension);
		GeneralPath contourInner = SubstanceOutlineUtilities.getBaseOutline(
				contourDim, contourDim, cornerRadius, null, borderThickness
						+ borderDelta);
		borderPainter.paintBorder(graphics, button, contourDim, contourDim,
				contour, contourInner, SubstanceColorSchemeUtilities
						.getBorderColorScheme(button, componentState),
				SubstanceColorSchemeUtilities.getBorderColorScheme(button,
						prevState), cyclePos, colorScheme1 != colorScheme2);
		graphics.translate(-delta, 1 - delta);
		if (componentState.isKindActive(FadeKind.SELECTION)
				|| (checkMarkVisibility > 0.0)) {
			if (isCheckMarkFadingOut) {
				graphics.setComposite(AlphaComposite.getInstance(
						AlphaComposite.SRC_OVER, alpha * checkMarkVisibility));
				checkMarkVisibility = 1.0f;
			}

			BufferedImage checkMark1 = SubstanceImageCreator.getCheckMark(
					button, dimension - 2 * offset / 3, componentState
							.isKindActive(FadeKind.ENABLE), colorScheme1,
					checkMarkVisibility);
			graphics.drawImage(checkMark1, 1 + 2 * offset / 3,
					(dimension < 14) ? 0 : -1, null);
			BufferedImage checkMark2 = SubstanceImageCreator.getCheckMark(
					button, dimension - 2 * offset / 3, componentState
							.isKindActive(FadeKind.ENABLE), colorScheme2,
					checkMarkVisibility);
			graphics.setComposite(AlphaComposite.getInstance(
					AlphaComposite.SRC_OVER, alpha * cyclePos));
			graphics.drawImage(checkMark2, 1 + 2 * offset / 3,
					(dimension < 14) ? 0 : -1, null);
		}

		return offBackground;
	}

	/**
	 * Retrieves composite background for the specified parameters. The
	 * composite background consists of three layers:
	 * <ol>
	 * <li>Layer that matches the <code>increased</code> state.
	 * <li>Layer that matches the <code>decreased</code> state.
	 * <li>Regular layer with rounded background.
	 * </ol>
	 * The layers are drawn in the following order:
	 * <ol>
	 * <li>The left half of the first layer
	 * <li>The right half of the second layer
	 * <li>The third layer
	 * </ol>
	 * Combined together, the layers create the image for scrollbar track with
	 * continuation of the arrow increase and decrease buttons.
	 * 
	 * @param component
	 *            Component.
	 * @param width
	 *            Image width.
	 * @param height
	 *            Image height.
	 * @param cornerRadius
	 *            Corner radius.
	 * @param decrButton
	 *            The <code>decrease</code> button.
	 * @param incrButton
	 *            The <code>increase</code> button.
	 * @param flipSides
	 *            If <code>true</code>, the drawn halves of the first and the
	 *            second layers above will be swapped.
	 * @return Composite background for the specified parameters.
	 */
	public static BufferedImage getCompositeRoundedBackground(
			JComponent component, int width, int height, int cornerRadius,
			AbstractButton decrButton, AbstractButton incrButton,
			boolean flipSides) {

		int delta = 3;

		BufferedImage result = SubstanceCoreUtilities.getBlankImage(width,
				height);
		if (decrButton != null) {
			Graphics2D graphics = (Graphics2D) result.getGraphics();
			if (!flipSides) {
				graphics.setClip(-delta, 0, width / 2, height);
				graphics.translate(-delta, 0);
			} else {
				graphics.setClip(width / 2, 0, width / 2 + 1, height);
			}
			PairwiseButtonBackgroundDelegate.updatePairwiseBackground(graphics,
					decrButton, width + 2 * delta, height,
					flipSides ? Side.RIGHT : Side.LEFT, true);
			graphics.dispose();
		}

		if (incrButton != null) {
			Graphics2D graphics = (Graphics2D) result.getGraphics();
			if (!flipSides) {
				graphics.setClip(width / 2, 0, width / 2 + 1, height);
			} else {
				graphics.setClip(-delta, 0, width / 2, height);
				graphics.translate(-delta, 0);
			}
			PairwiseButtonBackgroundDelegate.updatePairwiseBackground(graphics,
					incrButton, width + 2 * delta, height,
					flipSides ? Side.LEFT : Side.RIGHT, true);
			graphics.dispose();
		}

		return result;
	}

	/**
	 * 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.
	 */
	public static BufferedImage overlayEcho(BufferedImage image,
			float echoAlpha, int offsetX, int offsetY) {
		int width = image.getWidth();
		int height = image.getHeight();

		// blur the original image
		// ConvolveOp convolve = new ConvolveOp(new Kernel(3, 3, new float[] {
		// .4f, .4f, .4f, .4f, .0f, .4f, .4f, .4f, .4f }),
		// ConvolveOp.EDGE_NO_OP, null);
		offsetX = offsetY = 0;
		BufferedImage negated = getNegated(image);
		BufferedImage result = SubstanceCoreUtilities.getBlankImage(width,
				height);
		Graphics2D graphics = (Graphics2D) result.getGraphics().create();
		graphics.setComposite(AlphaComposite.getInstance(
				AlphaComposite.SRC_OVER, 0.2f * echoAlpha * echoAlpha
						* echoAlpha));
		graphics.drawImage(negated, offsetX - 1, offsetY - 1, null);
		graphics.drawImage(negated, offsetX + 1, offsetY - 1, null);
		graphics.drawImage(negated, offsetX - 1, offsetY + 1, null);
		graphics.drawImage(negated, offsetX + 1, offsetY + 1, null);
		graphics.setComposite(AlphaComposite.getInstance(
				AlphaComposite.SRC_OVER, 0.7f * echoAlpha * echoAlpha
						* echoAlpha));
		graphics.drawImage(negated, offsetX, offsetY - 1, null);
		graphics.drawImage(negated, offsetX, offsetY + 1, null);
		graphics.drawImage(negated, offsetX - 1, offsetY, null);
		graphics.drawImage(negated, offsetX + 1, offsetY, null);

		graphics.setComposite(AlphaComposite.getInstance(
				AlphaComposite.SRC_OVER, 1.0f));
		graphics.drawImage(image, 0, 0, null);

		graphics.dispose();
		return result;
	}

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

	/**
	 * Returns <code>minimize</code> icon.
	 * 
	 * @param iSize
	 *            Icon dimension.
	 * @param scheme
	 *            Color scheme for the icon.
	 * @return <code>Minimize</code> icon.
	 */
	public static Icon getMinimizeIcon(int iSize, SubstanceColorScheme scheme) {
		BufferedImage image = SubstanceCoreUtilities
				.getBlankImage(iSize, iSize);
		Graphics2D graphics = (Graphics2D) image.getGraphics();
		int start = (iSize / 4) - 2;
		int end = (3 * iSize / 4);// - 1;
		int size = end - start - 3;
		Color color = scheme.getForegroundColor();
		graphics.setColor(color);
		graphics.fillRect(start + 2, end - 2, size, 3);
		return new ImageIcon(SubstanceImageCreator.overlayEcho(image,
				SubstanceColorUtilities.getColorStrength(color), 1, 1));
	}

	/**
	 * Returns <code>restore</code> icon.
	 * 
	 * @param scheme
	 *            Color scheme for the icon.
	 * @return <code>Restore</code> icon.
	 */
	public static Icon getRestoreIcon(SubstanceColorScheme scheme) {
		int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
		BufferedImage image = SubstanceCoreUtilities
				.getBlankImage(iSize, iSize);
		Graphics2D graphics = (Graphics2D) image.getGraphics();
		int start = (iSize / 4) - 2;
		int end = (3 * iSize / 4) - 1;
		int size = end - start - 3;
		Color color = scheme.getForegroundColor();
		graphics.setColor(color);
		graphics.drawRect(start, end - size + 1, size, size);
		graphics.drawLine(start, end - size + 2, start + size, end - size + 2);
		graphics.fillRect(end - size, start + 1, size + 1, 2);
		graphics.drawLine(end, start + 1, end, start + size + 1);
		graphics.drawLine(start + size + 2, start + size + 1, end, start + size
				+ 1);
		return new ImageIcon(SubstanceImageCreator.overlayEcho(image,
				SubstanceColorUtilities.getColorStrength(color), 1, 1));
	}

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

	/**
	 * Returns <code>maximize</code> icon.
	 * 
	 * @param iSize
	 *            Icon dimension.
	 * @param scheme
	 *            Color scheme for the icon.
	 * @return <code>Maximize</code> icon.
	 */
	public static Icon getMaximizeIcon(int iSize, SubstanceColorScheme scheme) {
		BufferedImage image = SubstanceCoreUtilities
				.getBlankImage(iSize, iSize);
		Graphics2D graphics = (Graphics2D) image.getGraphics();
		int start = (iSize / 4) - 1;
		int end = iSize - start - 1;// (3 * iSize / 4);
		Color color = scheme.getForegroundColor();
		graphics.setColor(color);
		graphics.drawRect(start, start, end - start, end - start);
		graphics.drawLine(start, start + 1, end, start + 1);
		return new ImageIcon(SubstanceImageCreator.overlayEcho(image,
				SubstanceColorUtilities.getColorStrength(color), 1, 1));
	}

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

	/**
	 * Returns <code>close</code> icon.
	 * 
	 * @param iSize
	 *            Icon dimension.
	 * @param colorScheme
	 *            Color scheme for the icon.
	 * @return <code>Close</code> icon.
	 */
	public static Icon getCloseIcon(int iSize, SubstanceColorScheme colorScheme) {
		BufferedImage image = SubstanceCoreUtilities
				.getBlankImage(iSize, iSize);
		Graphics2D graphics = (Graphics2D) image.getGraphics();
		if (iSize < 15) {
			graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_ON);
		} else {
			graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
					RenderingHints.VALUE_ANTIALIAS_OFF);
		}
		int start = (iSize / 4);// - 1;
		int end = (3 * iSize / 4);

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

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

		graphics.setStroke(stroke);
		Color color = colorScheme.getForegroundColor();
		graphics.setColor(color);
		graphics.drawLine(start, start, end, end);
		graphics.drawLine(start, end, end, start);
		return new ImageIcon(SubstanceImageCreator.overlayEcho(image,
				SubstanceColorUtilities.getColorStrength(color), 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 <code>true</code>, the gradient will be vertical, if
	 *            <code>false</code>, the gradient will be horizontal.
	 */
	public static void paintRectangularBackground(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(TransitionLayout.getAlphaComposite(null,
					borderAlpha, graphics));

			paintSimpleBorder(g2d, width, height, colorScheme, colorScheme,
					0.0f);

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

	/**
	 * Paints simple border.
	 * 
	 * @param g2d
	 *            Graphics context.
	 * @param width
	 *            Border width.
	 * @param height
	 *            Border height.
	 * @param colorScheme
	 *            First color scheme.
	 * @param colorScheme2
	 *            Second color scheme.
	 * @param cyclePos
	 *            Interpolation cycle.
	 */
	public static void paintSimpleBorder(Graphics2D g2d, int width, int height,
			SubstanceColorScheme colorScheme,
			SubstanceColorScheme colorScheme2, float cyclePos) {
		int componentFontSize = SubstanceSizeUtils.getComponentFontSize(null);
		int borderDelta = (int) Math.floor(SubstanceSizeUtils
				.getBorderStrokeWidth(componentFontSize) / 2.0);
		float borderThickness = SubstanceSizeUtils
				.getBorderStrokeWidth(componentFontSize);

		g2d.setColor(SubstanceColorUtilities.getMidBorderColor(colorScheme,
				colorScheme2, cyclePos));
		g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
				RenderingHints.VALUE_STROKE_NORMALIZE);
		int joinKind = BasicStroke.JOIN_ROUND;
		int capKind = BasicStroke.CAP_BUTT;
		g2d.setStroke(new BasicStroke(borderThickness, capKind, joinKind));
		g2d.drawRect(borderDelta, borderDelta, width - 1 - 2 * borderDelta,
				height - 1 - 2 * borderDelta);
	}

	/**
	 * 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(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);
		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 = width / stripeSize;
				stripeOffset = stripeOffset % (2 * stripeSize);
				for (int stripe = -2; stripe <= stripeCount; stripe += 2) {
					int stripePos = stripe * stripeSize + stripeOffset;

					graphics.drawImage(stripeImage, stripePos, 0, null);
				}
			}
		} 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 = height / stripeSize;
				stripeOffset = stripeOffset % (2 * stripeSize);
				for (int stripe = -2; stripe <= stripeCount; stripe += 2) {
					int stripePos = stripe * stripeSize + stripeOffset;

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

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

			paintSimpleBorder(g2d, width, height, colorScheme, colorScheme,
					0.0f);
			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 result = SubstanceCoreUtilities.getBlankImage(width,
				height);
		Graphics2D graphics = (Graphics2D) result.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);

		float[] BLUR = { 0.10f, 0.10f, 0.10f, 0.10f, 0.30f, 0.10f, 0.10f,
				0.10f, 0.10f };
		ConvolveOp vBlurOp = new ConvolveOp(new Kernel(3, 3, BLUR));
		BufferedImage blurred = vBlurOp.filter(result, null);

		return blurred;
	}

	/**
	 * Returns drag bumps image.
	 * 
	 * @param c
	 *            Component.
	 * @param colorScheme
	 *            Color scheme.
	 * @param alwaysUseActive
	 *            Indicates whether the active color scheme should always be
	 *            used.
	 * @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 componentState
	 *            Split pane divider state.
	 * @param colorScheme1
	 *            First color scheme.
	 * @param colorScheme2
	 *            Second color scheme.
	 * @param interpolationCyclePos
	 *            Interpolation cycle.
	 */
	public static void paintSplitDividerBumpImage(Graphics g,
			SubstanceSplitPaneDivider divider, int x, int y, int width,
			int height, boolean isHorizontal, ComponentState componentState,
			SubstanceColorScheme colorScheme1,
			SubstanceColorScheme colorScheme2, float interpolationCyclePos) {
		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.setComposite(AlphaComposite.getInstance(
				AlphaComposite.SRC_OVER, 1.0f - interpolationCyclePos));
		Color markColor = SubstanceColorUtilities.getMarkColor(colorScheme1,
				divider.isEnabled());
		dotGraphics.setColor(markColor);
		dotGraphics.fillOval(0, 0, bumpDotDiameter, bumpDotDiameter);

		dotGraphics.setComposite(AlphaComposite.getInstance(
				AlphaComposite.SRC_OVER, interpolationCyclePos));
		Color markColor2 = SubstanceColorUtilities.getMarkColor(colorScheme2,
				divider.isEnabled());
		dotGraphics.setColor(markColor2);
		dotGraphics.fillOval(0, 0, bumpDotDiameter, bumpDotDiameter);

		dotGraphics.setComposite(AlphaComposite.getInstance(
				AlphaComposite.SRC_OVER, 0.4f));
		SubstanceBorderPainter borderPainter = SubstanceCoreUtilities
				.getBorderPainter(divider);
		borderPainter.paintBorder(dotGraphics, divider, width, height,
				new Ellipse2D.Float(0, 0, bumpDotDiameter - 1,
						bumpDotDiameter - 1), null, colorScheme1, colorScheme2,
				interpolationCyclePos, colorScheme1 != colorScheme2);

		graphics.setComposite(AlphaComposite.getInstance(
				AlphaComposite.SRC_OVER, 0.8f));
		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;
				graphics.drawImage(singleDot, cx, cy, null);
			}
		}
		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();
		// SubstanceCoreUtilities
		// .getActiveScheme(null) : SubstanceCoreUtilities
		// .getDefaultScheme(null);
		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.setColor(back1);
				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 colorScheme
	 *            Icon color scheme.
	 * @param isCollapsed
	 *            Collapsed state.
	 * @return Tree icon.
	 */
	public static BufferedImage getTreeIcon(JTree tree,
			SubstanceColorScheme colorScheme, boolean isCollapsed) {
		int fontSize = SubstanceSizeUtils.getComponentFontSize(tree);
		int dim = SubstanceSizeUtils.getTreeIconSize(fontSize);
		BufferedImage result = SubstanceCoreUtilities.getBlankImage(dim + 2,
				dim);
		Graphics2D graphics = (Graphics2D) result.getGraphics();

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

		Graphics2D g2 = (Graphics2D) graphics.create();
		g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_OFF);
		SimplisticGradientPainter gradPainter = new SimplisticSoftBorderReverseGradientPainter();
		FlatBorderPainter fbp = new FlatBorderPainter();

		int borderDelta = (int) Math.floor(SubstanceSizeUtils
				.getBorderStrokeWidth(fontSize) / 2.0);
		Shape contour = SubstanceOutlineUtilities.getBaseOutline(dim - 1,
				dim - 1, SubstanceSizeUtils.getClassicButtonCornerRadius(dim),
				null, borderDelta);

		g2.translate(0, 1);

		boolean isDark = colorScheme.isDark();

		SubstanceColorScheme fillScheme = new ShiftColorScheme(colorScheme,
				colorScheme.getExtraLightColor(), 0.7);

		gradPainter.paintContourBackground(g2, tree, dim - 1, dim - 1, contour,
				false, fillScheme, fillScheme, 0, false, false);
		SubstanceColorScheme borderScheme = new ShiftColorScheme(colorScheme,
				isDark ? colorScheme.getUltraLightColor() : colorScheme
						.getLightColor(), 0.95);
		fbp.paintBorder(g2, tree, dim - 1, dim - 1, contour, null,
				borderScheme, borderScheme, 0, false);

		g2.translate(-1, -1);

		Color signColor = isDark ? colorScheme.getUltraLightColor().brighter()
				.brighter() : colorScheme.getUltraDarkColor();
		if ((tree != null) && !tree.isEnabled())
			signColor = colorScheme.getForegroundColor();
		g2.setColor(signColor);
		int mid = dim / 2;
		int length = 5 * dim / 12;
		g2.drawLine(mid - length / 2, dim / 2, mid + length / 2, dim / 2);
		if (isCollapsed) {
			g2.drawLine(dim / 2, mid - length / 2, dim / 2, mid + length / 2);
		}
		g2.dispose();

		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.
	 */
	public 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);

		graphics.setPaint(new GradientPaint(0, baseTop / 2, lightColor,
				(int) (0.6 * width), baseTop, mainColor));
		graphics.fillRect(0, 0, width / 2, baseTop);
		graphics.setPaint(new GradientPaint(width, baseTop / 2, lightColor,
				(int) (0.4 * width), baseTop, mainColor));
		graphics.fillRect(width / 2, 0, width / 2, baseTop);

		graphics.setStroke(new BasicStroke((float) 1.3, 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() {
		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.setColor(new Color(240, 240, 240));
		graphics.fillRect(0, 0, iw, ih);

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

		graphics.setColor(new Color(190, 190, 190));
		graphics.drawRoundRect(0, 1, iw - 1, ih - 2, 4, 4);

		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 Icon getHexaMarker(int value, SubstanceColorScheme colorScheme) {
		BufferedImage result = SubstanceCoreUtilities.getBlankImage(9, 9);

		value %= 16;
		Color offColor = null;
		Color onColor = null;
		if (colorScheme == null) {
			return new ImageIcon(result);
		}
		boolean isDark = colorScheme.isDark();
		offColor = isDark ? colorScheme.getMidColor() : colorScheme
				.getMidColor().darker();
		onColor = isDark ? SubstanceColorUtilities.getInterpolatedColor(
				colorScheme.getUltraLightColor(), Color.white, 0.2)
				: colorScheme.getUltraDarkColor().darker();

		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 ImageIcon(result);
	}

	/**
	 * Returns big 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 Image getBigHexaMarker(int value,
			SubstanceColorScheme colorScheme) {
		int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
		BufferedImage result = SubstanceCoreUtilities.getBlankImage(iSize,
				iSize);

		value %= 16;
		Color offColor = null;
		Color onColor = null;
		if (colorScheme == null)
			return result;
		boolean isDark = colorScheme.isDark();
		offColor = isDark ? colorScheme.getMidColor() : colorScheme
				.getMidColor().darker();
		onColor = isDark ? SubstanceColorUtilities.getInterpolatedColor(
				colorScheme.getUltraLightColor(), Color.white, 0.2)
				: colorScheme.getUltraDarkColor().darker();

		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(9, 9, 6, 6);
		graphics.setColor(bit2 ? onColor : offColor);
		graphics.fillOval(9, 2, 6, 6);
		graphics.setColor(bit3 ? onColor : offColor);
		graphics.fillOval(2, 9, 6, 6);
		graphics.setColor(bit4 ? onColor : offColor);
		graphics.fillOval(2, 2, 6, 6);

		graphics.dispose();
		return result;
	}

	/**
	 * Returns search icon.
	 * 
	 * @param dimension
	 *            Icon dimension.
	 * @param colorScheme
	 *            Icon color scheme.
	 * @param leftToRight
	 *            LTR indication of the resulting icon.
	 * @return Search icon.
	 */
	public static Icon getSearchIcon(int dimension,
			SubstanceColorScheme colorScheme, boolean leftToRight) {
		BufferedImage result = SubstanceCoreUtilities.getBlankImage(dimension,
				dimension);

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

		Color color = SubstanceColorUtilities.getMarkColor(colorScheme, true);
		graphics.setColor(color);

		graphics.setStroke(new BasicStroke(1.5f));
		if (leftToRight) {
			int xc = (int) (0.6 * dimension);
			int yc = (int) (0.45 * dimension);
			int r = (int) (0.3 * dimension);

			graphics.drawOval(xc - r, yc - r, 2 * r, 2 * r);

			graphics.setStroke(new BasicStroke(3.0f));
			GeneralPath handle = new GeneralPath();
			handle.moveTo((float) (xc - r / Math.sqrt(2.0)), (float) (yc + r
					/ Math.sqrt(2.0)));
			handle.lineTo(1.8f, dimension - 2.2f);
			graphics.draw(handle);
		} else {
			int xc = (int) (0.4 * dimension);
			int yc = (int) (0.45 * dimension);
			int r = (int) (0.3 * dimension);

			graphics.drawOval(xc - r, yc - r, 2 * r, 2 * r);

			graphics.setStroke(new BasicStroke(3.0f));
			GeneralPath handle = new GeneralPath();
			handle.moveTo((float) (xc + r / Math.sqrt(2.0)), (float) (yc + r
					/ Math.sqrt(2.0)));
			handle.lineTo(dimension - 2.5f, dimension - 2.2f);
			graphics.draw(handle);
		}

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

	/**
	 * Returns an icon that matches the specified watermark.
	 * 
	 * @param watermark
	 *            Watermark instance.
	 * @return Icon that matches the specified watermark.
	 */
	public static Icon getWatermarkIcon(SubstanceWatermark watermark) {
		int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
		BufferedImage result = SubstanceCoreUtilities.getBlankImage(iSize,
				iSize);
		Graphics2D graphics = (Graphics2D) result.getGraphics().create();
		graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);

		watermark.previewWatermark(graphics, 0, 0, iSize, iSize);
		graphics.dispose();
		return new ImageIcon(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) {
		int extraPadding = SubstanceSizeUtils
				.getExtraPadding(SubstanceSizeUtils.getControlFontSize());
		BufferedImage result = SubstanceCoreUtilities.getBlankImage(
				6 + 2 * extraPadding, 8 + 2 * extraPadding);

		Color fore = scheme.getForegroundColor();
		Color fill = new Color(208, 208, 48);

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

		graphics.setColor(fill);
		// lock fill
		graphics.fillRect(1, 2 + extraPadding, 4 + 2 * extraPadding,
				4 + 2 * extraPadding);
		graphics.setColor(fore);
		// lock left side
		graphics.drawLine(0, 3 + extraPadding, 0, 7 + 2 * extraPadding);
		// lock right side
		graphics.drawLine(5 + 2 * extraPadding, 3 + extraPadding,
				5 + 2 * extraPadding, 7 + 2 * extraPadding);
		// lock bottom side
		graphics.drawLine(0, 7 + 2 * extraPadding, 5 + 2 * extraPadding,
				7 + 2 * extraPadding);
		// lock top side
		graphics.drawLine(1, 2 + extraPadding, 4 + 2 * extraPadding,
				2 + extraPadding);
		// lock left handle
		graphics.drawLine(1 + extraPadding, 1, 1 + extraPadding,
				2 + extraPadding);
		// lock right handle
		graphics.drawLine(4 + extraPadding, 1, 4 + extraPadding,
				2 + extraPadding);
		// lock top handle
		graphics.drawLine(2 + extraPadding, 0, 3 + extraPadding, 0);
		// lock keyhole
		graphics.fillRect(2 + extraPadding, 4 + extraPadding, 2,
				2 + extraPadding);

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

	/**
	 * Returns the negative of the specified image.
	 * 
	 * @param bi
	 *            Image.
	 * @return The negative of the specified image.
	 */
	public static BufferedImage getNegated(BufferedImage bi) {
		return new NegatedFilter().filter(bi, null);
	}

	/**
	 * 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) {
		int w = original.getIconWidth();
		int h = original.getIconHeight();
		BufferedImage origImage = SubstanceCoreUtilities.getBlankImage(w, h);
		original.paintIcon(comp, origImage.getGraphics(), 0, 0);

		return getColorSchemeImage(origImage, colorScheme);
	}

	/**
	 * 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 toSaturate
	 *            Indicates whether the resulting image should be saturated.
	 * @return Scheme-based version of the original icon.
	 */
	public static BufferedImage getColorSchemeImage(BufferedImage original,
			SubstanceColorScheme colorScheme) {
		return ColorSchemeFilter.getColorSchemeFilter(colorScheme).filter(
				original, null);
	}
}
