/*
* 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.painter;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.EnumSet;
import java.util.Set;
import javax.swing.CellRendererPane;
import org.pushingpixels.lafwidget.LafWidgetUtilities;
import org.pushingpixels.lafwidget.contrib.intellij.UIUtil;
import org.pushingpixels.substance.api.SubstanceColorScheme;
import org.pushingpixels.substance.api.SubstanceConstants.Side;
import org.pushingpixels.substance.api.painter.border.SubstanceBorderPainter;
import org.pushingpixels.substance.api.painter.highlight.SubstanceHighlightPainter;
import org.pushingpixels.substance.internal.utils.HashMapKey;
import org.pushingpixels.substance.internal.utils.LazyResettableHashMap;
import org.pushingpixels.substance.internal.utils.SubstanceCoreUtilities;
import org.pushingpixels.substance.internal.utils.SubstanceSizeUtils;
/**
* Contains utility methods related to highlight painters. This class is for
* internal use only.
*
* @author Kirill Grouchnikov
*/
public class HighlightPainterUtils {
/**
* Cache for small objects.
*/
protected final static LazyResettableHashMap<BufferedImage> smallCache = new LazyResettableHashMap<BufferedImage>(
"SubstanceHighlightUtils");
/**
* Paints the highlight for the specified component.
*
* @param g
* Graphic context.
* @param rendererPane
* Renderer pane. Can be <code>null</code>.
* @param c
* Component.
* @param rect
* Rectangle to highlight.
* @param borderAlpha
* Border alpha.
* @param openSides
* The sides specified in this set will not be painted. Can be
* <code>null</code> or empty.
* @param fillScheme
* The fill color scheme.
* @param borderScheme
* The border color scheme.
*/
public static void paintHighlight(Graphics g,
CellRendererPane rendererPane, Component c, Rectangle rect,
float borderAlpha, Set<Side> openSides,
SubstanceColorScheme fillScheme, SubstanceColorScheme borderScheme) {
// fix for bug 65
if ((rect.width <= 0) || (rect.height <= 0))
return;
Component compForQuerying = (rendererPane != null) ? rendererPane : c;
SubstanceHighlightPainter highlightPainter = SubstanceCoreUtilities
.getSkin(compForQuerying).getHighlightPainter();
SubstanceBorderPainter highlightBorderPainter = SubstanceCoreUtilities
.getHighlightBorderPainter(compForQuerying);
Graphics2D g2d = (Graphics2D) g.create(rect.x, rect.y, rect.width,
rect.height);
if (openSides == null) {
openSides = EnumSet.noneOf(Side.class);
}
if (rect.width * rect.height < 100000) {
String openKey = "";
for (Side oSide : openSides) {
openKey += oSide.name() + "-";
}
HashMapKey key = SubstanceCoreUtilities.getHashKey(highlightPainter
.getDisplayName(), highlightBorderPainter.getDisplayName(),
rect.width, rect.height, fillScheme.getDisplayName(),
borderScheme.getDisplayName(), borderAlpha, openKey);
BufferedImage result = smallCache.get(key);
if (result == null) {
result = createHighlighterImage(c, rect, borderAlpha,
openSides, fillScheme, borderScheme, highlightPainter,
highlightBorderPainter);
smallCache.put(key, result);
}
int scaleFactor = UIUtil.getScaleFactor();
g2d.drawImage(result, 0, 0, result.getWidth() / scaleFactor,
result.getHeight() / scaleFactor, null);
}
}
private static BufferedImage createHighlighterImage(Component c,
Rectangle rect, float borderAlpha, Set<Side> openSides,
SubstanceColorScheme currScheme,
SubstanceColorScheme currBorderScheme,
SubstanceHighlightPainter highlightPainter,
SubstanceBorderPainter highlightBorderPainter) {
BufferedImage result = SubstanceCoreUtilities.getBlankImage(rect.width,
rect.height);
Graphics2D resGraphics = result.createGraphics();
highlightPainter.paintHighlight(resGraphics, c, rect.width,
rect.height, currScheme);
paintHighlightBorder(resGraphics, c, rect.width, rect.height,
borderAlpha, openSides, highlightBorderPainter,
currBorderScheme);
resGraphics.dispose();
return result;
}
/**
* Paints the highlight border for the specified component.
*
* @param graphics
* Graphic context.
* @param comp
* Component.
* @param width
* Border width.
* @param height
* Border width.
* @param borderAlpha
* Border alpha.
* @param openSides
* The sides specified in this set will not be painted. Can be
* <code>null</code> or empty.
* @param highlightBorderPainter
* Border painter for the highlights.
* @param borderColorScheme
* The border color scheme.
*/
private static void paintHighlightBorder(Graphics2D graphics,
Component comp, int width, int height, float borderAlpha,
Set<Side> openSides, SubstanceBorderPainter highlightBorderPainter,
SubstanceColorScheme borderColorScheme) {
if (borderAlpha <= 0.0f)
return;
int openDelta = 3 + (int) (Math.ceil(3.0 * SubstanceSizeUtils.getBorderStrokeWidth()));
int deltaLeft = openSides.contains(Side.LEFT) ? openDelta : 0;
int deltaRight = openSides.contains(Side.RIGHT) ? openDelta : 0;
int deltaTop = openSides.contains(Side.TOP) ? openDelta : 0;
int deltaBottom = openSides.contains(Side.BOTTOM) ? openDelta : 0;
float borderDelta = SubstanceSizeUtils.getBorderStrokeWidth();
Shape contour = new Rectangle2D.Float(borderDelta / 2.0f, borderDelta / 2.0f, width
+ deltaLeft + deltaRight - borderDelta, height
+ deltaTop + deltaBottom - borderDelta);
Graphics2D g2d = (Graphics2D) graphics.create();
g2d.translate(-deltaLeft, -deltaTop);
g2d.setComposite(LafWidgetUtilities.getAlphaComposite(null,
borderAlpha, graphics));
Shape contourInner = new Rectangle2D.Float(1.5f * borderDelta, 1.5f * borderDelta,
width + deltaLeft + deltaRight - 3 * borderDelta,
height + deltaTop + deltaBottom - 3 * borderDelta);
highlightBorderPainter.paintBorder(g2d, comp, width + deltaLeft
+ deltaRight, height + deltaTop + deltaBottom, contour,
contourInner, borderColorScheme);
g2d.dispose();
}
/**
* Returns the memory usage string.
*
* @return Memory usage string.
*/
public static String getMemoryUsage() {
StringBuffer sb = new StringBuffer();
sb.append("SubstanceHighlightUtils: \n");
sb.append("\t" + smallCache.size() + " smalls");
return sb.toString();
}
}