/*
* Copyright (c) 2005-2016 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.pushingpixels.substance.internal.utils;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Component;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.LinearGradientPaint;
import java.awt.MultipleGradientPaint.CycleMethod;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import javax.swing.AbstractButton;
import javax.swing.CellRendererPane;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.JTree;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.text.JTextComponent;
import org.pushingpixels.lafwidget.LafWidgetUtilities;
import org.pushingpixels.lafwidget.contrib.intellij.UIUtil;
import org.pushingpixels.lafwidget.icon.HiDpiAwareIcon;
import org.pushingpixels.lafwidget.icon.HiDpiAwareIconUiResource;
import org.pushingpixels.lafwidget.utils.ColorFilter;
import org.pushingpixels.substance.api.ComponentState;
import org.pushingpixels.substance.api.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.api.watermark.SubstanceWatermark;
import org.pushingpixels.substance.internal.painter.SimplisticFillPainter;
import org.pushingpixels.substance.internal.svg.Locked;
import org.pushingpixels.substance.internal.svg.System_search;
import org.pushingpixels.substance.internal.utils.filters.ColorSchemeFilter;
import org.pushingpixels.substance.internal.utils.filters.GrayscaleFilter;
import org.pushingpixels.substance.internal.utils.filters.TranslucentFilter;
/**
* 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 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 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,
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 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(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 HiDpiAwareIcon 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);
HiDpiAwareIcon result = new HiDpiAwareIcon(getArrow(
width, height, strokeWidth, direction, colorScheme));
int finalWidth = (int) (Math.max(origWidth, origHeight) + 2);
int finalHeight = (int) (Math.max(origWidth, height) + 2);
result.setDimension(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 HiDpiAwareIcon getArrowIcon(float width, float height,
float strokeWidth, int direction, SubstanceColorScheme scheme) {
return new HiDpiAwareIcon(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) {
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.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
RenderingHints.VALUE_STROKE_PURE);
// graphics.setColor(Color.green);
// graphics.fillRect(0, 0, (int) width, (int) height);
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);
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);
int factor = UIUtil.getScaleFactor();
graphics.drawImage(top, 0, 0, top.getWidth() / factor, top.getHeight() / factor, null);
graphics.drawImage(bottom, 0, (int) (height / 2.0),
bottom.getWidth() / factor, bottom.getHeight() / factor, null);
return arrowImage;
} else {
float cushion = strokeWidth / 2.0f;
GeneralPath gp = new GeneralPath();
gp.moveTo(cushion, cushion);
gp.lineTo(0.5f * (width), height - 1 - cushion);
gp.lineTo(width - cushion, cushion);
graphics.draw(gp);
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, false);
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 HiDpiAwareIcon getDoubleArrowIconDelta(int fontSize, float deltaWidth,
float deltaHeight, float deltaStrokeWidth, int direction,
SubstanceColorScheme colorScheme) {
float arrowWidth = SubstanceSizeUtils.getArrowIconWidth(fontSize);
float arrowHeight = SubstanceSizeUtils.getArrowIconHeight(fontSize) + deltaHeight;
float arrowStrokeWidth = SubstanceSizeUtils.getDoubleArrowStrokeWidth(fontSize)
+ deltaStrokeWidth;
return getDoubleArrowIcon(fontSize, 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 HiDpiAwareIcon getDoubleArrowIcon(int fontSize, float width,
float height, float strokeWidth, int direction,
SubstanceColorScheme colorScheme) {
int delta = 3 + 2 * SubstanceSizeUtils.getExtraPadding(fontSize) / 3;
boolean toggle = (direction == SwingConstants.WEST)
|| (direction == SwingConstants.EAST);
if (toggle) {
float temp = width;
width = height;
height = temp;
}
BufferedImage downArrowImage = SubstanceCoreUtilities.getBlankImage(
(int) width, (int) height);
BufferedImage singleArrow = getArrow(width,
Math.max(1, height - delta), strokeWidth, SwingConstants.SOUTH,
colorScheme);
// get graphics and set hints
Graphics2D graphics = (Graphics2D) downArrowImage.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
int scaleFactor = UIUtil.getScaleFactor();
int arrowHeight = singleArrow.getHeight();
int arrowWidth = singleArrow.getWidth();
int bottomOfSecondArrow = (int) (height);
int topOfFirstArrow = bottomOfSecondArrow - arrowHeight / scaleFactor - delta;
graphics.drawImage(singleArrow, 0, topOfFirstArrow, arrowWidth / scaleFactor,
arrowHeight / scaleFactor, null);
graphics.drawImage(singleArrow, 0, topOfFirstArrow + delta, arrowWidth / scaleFactor,
arrowHeight / scaleFactor, 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, false);
return new HiDpiAwareIcon(arrowImage);
}
/**
* 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,
boolean respectScaleFactorDuringRotation) {
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();
}
int factor = UIUtil.getScaleFactor();
BufferedImage biRot = SubstanceCoreUtilities.getBlankImage(width / factor,
height / factor);
AffineTransform at = null;
int factorForRotation = respectScaleFactorDuringRotation ? factor : 1;
switch (quadrantClockwise) {
case 1:
at = AffineTransform.getTranslateInstance(width / factorForRotation, 0);
at.rotate(Math.PI / 2);
break;
case 2:
at = AffineTransform.getTranslateInstance(width / factorForRotation, height / factorForRotation);
at.rotate(Math.PI);
break;
case 3:
at = AffineTransform.getTranslateInstance(0, height / factorForRotation);
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 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);
Icon resultIcon = new HiDpiAwareIcon(new GrayscaleFilter().filter(result, null));
// System.out.println("Orig " + icon.getIconWidth() + "x" + icon.getIconHeight() + " -> " +
// resultIcon.getIconWidth() + "x" + resultIcon.getIconHeight());
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 HiDpiAwareIcon(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 <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,
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);
int factor = UIUtil.getScaleFactor();
graphics.drawImage(checkMark, 1 + 2 * xOffset / 3,
(dimension < 14) ? 0 : -1, checkMark.getWidth() / factor,
checkMark.getHeight() / factor, null);
}
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);
int factor = UIUtil.getScaleFactor();
int tweakedWidth = width / factor;
int tweakedHeight = 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 <code>minimize</code> icon.
*
* @param scheme
* Color scheme for the icon.
* @return <code>Minimize</code> icon.
*/
public static HiDpiAwareIcon getMinimizeIcon(SubstanceColorScheme scheme,
SubstanceColorScheme backgroundScheme) {
int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
return getMinimizeIcon(iSize, scheme, backgroundScheme);
}
/**
* Returns <code>minimize</code> icon.
*
* @param iSize
* Icon dimension.
* @param scheme
* Color scheme for the icon.
* @return <code>Minimize</code> icon.
*/
public static HiDpiAwareIcon getMinimizeIcon(int iSize, SubstanceColorScheme scheme,
SubstanceColorScheme backgroundScheme) {
BufferedImage image = SubstanceCoreUtilities
.getBlankImage(iSize, iSize);
Graphics2D graphics = (Graphics2D) 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 HiDpiAwareIcon(SubstanceImageCreator.overlayEcho(image,
noEcho ? 0 : SubstanceColorUtilities.getColorStrength(color),
echoColor, 1, 1));
}
/**
* Returns <code>restore</code> icon.
*
* @param scheme
* Color scheme for the icon.
* @return <code>Restore</code> icon.
*/
public static HiDpiAwareIcon getRestoreIcon(SubstanceColorScheme scheme,
SubstanceColorScheme backgroundScheme) {
int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
BufferedImage image = SubstanceCoreUtilities.getBlankImage(iSize, iSize);
Graphics2D graphics = (Graphics2D) 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 HiDpiAwareIcon(SubstanceImageCreator.overlayEcho(image,
noEcho ? 0 : SubstanceColorUtilities.getColorStrength(color),
echoColor, 1, 1));
}
/**
* Returns <code>maximize</code> icon.
*
* @param scheme
* Color scheme for the icon.
* @return <code>Maximize</code> icon.
*/
public static HiDpiAwareIcon getMaximizeIcon(SubstanceColorScheme scheme,
SubstanceColorScheme backgroundScheme) {
int iSize = SubstanceSizeUtils.getTitlePaneIconSize();
return getMaximizeIcon(iSize, scheme, backgroundScheme);
}
/**
* Returns <code>maximize</code> icon.
*
* @param iSize
* Icon dimension.
* @param scheme
* Color scheme for the icon.
* @return <code>Maximize</code> icon.
*/
public static HiDpiAwareIcon getMaximizeIcon(int iSize, SubstanceColorScheme scheme,
SubstanceColorScheme backgroundScheme) {
BufferedImage image = SubstanceCoreUtilities.getBlankImage(iSize, iSize);
Graphics2D graphics = (Graphics2D) 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 HiDpiAwareIcon(SubstanceImageCreator.overlayEcho(image,
noEcho ? 0 : SubstanceColorUtilities.getColorStrength(color),
echoColor, 1, 1));
}
/**
* Returns <code>close</code> icon.
*
* @param scheme
* Color scheme for the icon.
* @return <code>Close</code> icon.
*/
public static HiDpiAwareIcon getCloseIcon(SubstanceColorScheme scheme,
SubstanceColorScheme backgroundScheme) {
return SubstanceImageCreator.getCloseIcon(
SubstanceSizeUtils.getTitlePaneIconSize(), scheme,
backgroundScheme);
}
/**
* Returns <code>close</code> icon.
*
* @param iSize
* Icon dimension.
* @param colorScheme
* Color scheme for the icon.
* @return <code>Close</code> icon.
*/
public static HiDpiAwareIcon getCloseIcon(int iSize,
SubstanceColorScheme colorScheme,
SubstanceColorScheme backgroundScheme) {
BufferedImage image = SubstanceCoreUtilities.getBlankImage(iSize, iSize);
Graphics2D graphics = (Graphics2D) image.createGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
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 HiDpiAwareIcon(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 <code>true</code>, the gradient will be vertical, if
* <code>false</code>, 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(LafWidgetUtilities.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.
* @param cyclePos
* Interpolation cycle.
*/
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);
int scaleFactor = UIUtil.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 = scaleFactor * width / stripeSize;
stripeOffset = stripeOffset % (2 * stripeSize * scaleFactor);
for (int stripe = -2; stripe <= stripeCount; stripe += 2) {
int stripePos = stripe * stripeSize / scaleFactor + stripeOffset;
graphics.drawImage(stripeImage, stripePos, 0,
stripeImage.getWidth() / scaleFactor,
stripeImage.getHeight() / scaleFactor, 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 = scaleFactor * height / stripeSize;
stripeOffset = stripeOffset % (2 * stripeSize * scaleFactor);
for (int stripe = -2; stripe <= stripeCount; stripe += 2) {
int stripePos = stripe * stripeSize / scaleFactor + stripeOffset;
graphics.drawImage(stripeImage, 0, stripePos,
stripeImage.getWidth() / scaleFactor,
stripeImage.getHeight() / scaleFactor, null);
}
}
}
if (borderAlpha > 0.0f) {
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.setComposite(LafWidgetUtilities.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 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, 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(LafWidgetUtilities.getAlphaComposite(divider, 0.8f, g));
int scaleFactor = UIUtil.getScaleFactor();
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,
singleDot.getWidth() / scaleFactor,
singleDot.getHeight() / scaleFactor, 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();
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);
Graphics2D g2 = (Graphics2D) graphics.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_OFF);
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(g2, tree, dim, dim, contour,
false, fillScheme, false);
fbp.paintBorder(g2, tree, dim, dim, contour, null, borderScheme);
Color signColor = markScheme.getForegroundColor();
g2.setColor(signColor);
int mid = dim / 2;
int length = 7 * 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.
*/
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);
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(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() {
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);
int scaleFactor = UIUtil.getScaleFactor();
for (int i = 0; i < SubstanceImageCreator.crayonColors.length; i++) {
Color crayonColor = new Color(
0xff000000 | SubstanceImageCreator.crayonColors[i]);
BufferedImage crayonImage = SubstanceImageCreator.getSingleCrayon(
crayonColor, 22, 120);
graphics.drawImage(crayonImage, SubstanceImageCreator.crayonX(i),
SubstanceImageCreator.crayonY(i), crayonImage.getWidth() / scaleFactor,
crayonImage.getHeight() / scaleFactor, null);
}
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 HiDpiAwareIcon getHexaMarker(int value, SubstanceColorScheme colorScheme) {
BufferedImage result = SubstanceCoreUtilities.getBlankImage(9, 9);
value %= 16;
Color offColor = null;
Color onColor = null;
if (colorScheme == null) {
return new HiDpiAwareIcon(result);
}
boolean isDark = colorScheme.isDark();
offColor = isDark ? SubstanceColorUtilities.getInterpolatedColor(
colorScheme.getUltraLightColor(), Color.white, 0.7) : 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 HiDpiAwareIcon(result);
}
/**
* Returns search icon.
*
* @param dimension
* Icon dimension.
* @param colorScheme
* Icon color scheme.
* @param leftToRight
* LTR indication of the resulting icon.
* @return Search icon.
*/
private static BufferedImage createSearchIcon(int dimension,
SubstanceColorScheme colorScheme, boolean leftToRight) {
HiDpiAwareIcon hiDpiAwareIcon = new HiDpiAwareIcon(System_search.of(16, 16));
Color foregroundColor = SubstanceColorUtilities.getForegroundColor(colorScheme);
Color forColorization = SubstanceColorUtilities.getAlphaColor(foregroundColor, 160);
return hiDpiAwareIcon.colorize(forColorization).toImage();
}
public static HiDpiAwareIcon getSearchIcon(JComponent c, int dimension,
boolean leftToRight) {
SubstanceColorScheme colorScheme = SubstanceColorSchemeUtilities
.getColorScheme(c, ComponentState.ENABLED);
return new HiDpiAwareIcon(createSearchIcon(dimension, colorScheme, leftToRight));
}
public static HiDpiAwareIcon getSearchIconUiResource(int dimension,
SubstanceColorScheme colorScheme, boolean leftToRight) {
return new HiDpiAwareIconUiResource(createSearchIcon(dimension, colorScheme, leftToRight));
}
/**
* 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,
SubstanceColorSchemeUtilities.METALLIC_SKIN, 0, 0,
iSize, iSize);
graphics.dispose();
return new HiDpiAwareIcon(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;
BufferedImage result = SubstanceCoreUtilities.getBlankImage(size, size);
Locked.of(size, size).paintIcon(c, result.getGraphics(), 0, 0);
float brightnessFactor = scheme.isDark() ? 0.6f : -0.1f;
ColorSchemeFilter filter = ColorSchemeFilter.getColorSchemeFilter(scheme, brightnessFactor);
return new HiDpiAwareIcon(filter.filter(result, 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,
float originalBrightnessFactor) {
int w = original.getIconWidth();
int h = original.getIconHeight();
BufferedImage origImage = SubstanceCoreUtilities.getBlankImage(w, h);
Graphics2D g2d = (Graphics2D) origImage.getGraphics().create();
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 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, float originalBrightnessFactor) {
return ColorSchemeFilter.getColorSchemeFilter(colorScheme,
originalBrightnessFactor).filter(original, null);
}
}