com.github.mathiewz.slick.font.effects.ShadowEffect Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of modernized-slick Show documentation
Show all versions of modernized-slick Show documentation
The main purpose of this libraryis to modernize and maintain the slick2D library.
The newest version!
package com.github.mathiewz.slick.font.effects;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Composite;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.ArrayList;
import java.util.List;
import com.github.mathiewz.slick.UnicodeFont;
import com.github.mathiewz.slick.font.Glyph;
/**
* An effect to generate soft shadows beneath text
*
* @author Nathan Sweet
*/
public class ShadowEffect implements ConfigurableEffect {
/** The number of kernels to apply */
public static final int NUM_KERNELS = 16;
/** The blur kernels applied across the effect */
public static final float[][] GAUSSIAN_BLUR_KERNELS = generateGaussianBlurKernels(NUM_KERNELS);
/** The colour of the shadow to render */
private Color color = Color.black;
/** The transparency factor of the shadow */
private float opacity = 0.6f;
/** The distance on the x axis of the shadow from the text */
private float xDistance = 2;
/** The distance on the y axis of the shadow from the text */
private float yDistance = 2;
/** The size of the kernel used to blur the shadow */
private int blurKernelSize = 0;
/** The number of passes applied to create the blur */
private int blurPasses = 1;
/**
* Default constructor for injection
*/
public ShadowEffect() {
}
/**
* Create a new effect to apply a drop shadow to text
*
* @param color
* The colour of the shadow to generate
* @param xDistance
* The distance from the text on the x axis the shadow should be rendered
* @param yDistance
* The distance from the text on the y axis the shadow should be rendered
* @param opacity
* The transparency factor of the shadow
*/
public ShadowEffect(Color color, int xDistance, int yDistance, float opacity) {
this.color = color;
this.xDistance = xDistance;
this.yDistance = yDistance;
this.opacity = opacity;
}
/**
* @see com.github.mathiewz.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, com.github.mathiewz.slick.UnicodeFont, com.github.mathiewz.slick.font.Glyph)
*/
@Override
public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
g = (Graphics2D) g.create();
g.translate(xDistance, yDistance);
g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), Math.round(opacity * 255)));
g.fill(glyph.getShape());
// Also shadow the outline, if one exists.
for (Effect effect2 : unicodeFont.getEffects()) {
Effect effect = effect2;
if (effect instanceof OutlineEffect) {
Composite composite = g.getComposite();
g.setComposite(AlphaComposite.Src); // Prevent shadow and outline shadow alpha from combining.
g.setStroke(((OutlineEffect) effect).getStroke());
g.draw(glyph.getShape());
g.setComposite(composite);
break;
}
}
g.dispose();
if (blurKernelSize > 1 && blurKernelSize < NUM_KERNELS && blurPasses > 0) {
blur(image);
}
}
/**
* Apply blurring to the generate image
*
* @param image
* The image to be blurred
*/
private void blur(BufferedImage image) {
float[] matrix = GAUSSIAN_BLUR_KERNELS[blurKernelSize - 1];
Kernel gaussianBlur1 = new Kernel(matrix.length, 1, matrix);
Kernel gaussianBlur2 = new Kernel(1, matrix.length, matrix);
RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
ConvolveOp gaussianOp1 = new ConvolveOp(gaussianBlur1, ConvolveOp.EDGE_NO_OP, hints);
ConvolveOp gaussianOp2 = new ConvolveOp(gaussianBlur2, ConvolveOp.EDGE_NO_OP, hints);
BufferedImage scratchImage = EffectUtil.getScratchImage();
for (int i = 0; i < blurPasses; i++) {
gaussianOp1.filter(image, scratchImage);
gaussianOp2.filter(scratchImage, image);
}
}
/**
* Get the colour of the shadow generated
*
* @return The colour of the shadow generated
*/
public Color getColor() {
return color;
}
/**
* Set the colour of the shadow to be generated
*
* @param color
* The colour ofthe shadow to be generated
*/
public void setColor(Color color) {
this.color = color;
}
/**
* Get the distance on the X axis from the text the shadow should
* be generated at
*
* @return The distance on the X axis the shadow will be from the text
*/
public float getXDistance() {
return xDistance;
}
/**
* Sets the pixels to offset the shadow on the x axis. The glyphs will need padding so the
* shadow doesn't get clipped.
*
* @param distance
* The offset on the x axis
*/
public void setXDistance(float distance) {
xDistance = distance;
}
/**
* Get the distance on the Y axis from the text the shadow should
* be generated at
*
* @return The distance on the Y axis the shadow will be from the text
*/
public float getYDistance() {
return yDistance;
}
/**
* Sets the pixels to offset the shadow on the y axis. The glyphs will need
* padding so the shadow doesn't get clipped.
*
* @param distance
* The offset on the y axis
*/
public void setYDistance(float distance) {
yDistance = distance;
}
/**
* Get the size of the kernel used to apply the blur
*
* @return The blur kernel size
*/
public int getBlurKernelSize() {
return blurKernelSize;
}
/**
* Sets how many neighboring pixels are used to blur the shadow. Set to 0 for no blur.
*
* @param blurKernelSize
* The size of the kernel to apply the blur with
*/
public void setBlurKernelSize(int blurKernelSize) {
this.blurKernelSize = blurKernelSize;
}
/**
* Get the number of passes to apply the kernel for blurring
*
* @return The number of passes
*/
public int getBlurPasses() {
return blurPasses;
}
/**
* Sets the number of times to apply a blur to the shadow. Set to 0 for no blur.
*
* @param blurPasses
* The number of passes to apply when blurring
*/
public void setBlurPasses(int blurPasses) {
this.blurPasses = blurPasses;
}
/**
* Get the opacity of the shadow, i.e. how transparent it is
*
* @return The opacity of the shadow
*/
public float getOpacity() {
return opacity;
}
/**
* Set the opacity of the shadow, i.e. how transparent it is
*
* @param opacity
* The opacity of the shadow
*/
public void setOpacity(float opacity) {
this.opacity = opacity;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Shadow";
}
/**
* @see com.github.mathiewz.slick.font.effects.ConfigurableEffect#getValues()
*/
@Override
public List getValues() {
List values = new ArrayList<>();
values.add(EffectUtil.colorValue("Color", color));
values.add(EffectUtil.floatValue("Opacity", opacity, 0, 1, "This setting sets the translucency of the shadow."));
values.add(EffectUtil.floatValue("X distance", xDistance, Float.MIN_VALUE, Float.MAX_VALUE, "This setting is the amount of pixels to offset the shadow on the" + " x axis. The glyphs will need padding so the shadow doesn't get clipped."));
values.add(EffectUtil.floatValue("Y distance", yDistance, Float.MIN_VALUE, Float.MAX_VALUE, "This setting is the amount of pixels to offset the shadow on the" + " y axis. The glyphs will need padding so the shadow doesn't get clipped."));
List options = new ArrayList<>();
options.add(new String[] { "None", "0" });
for (int i = 2; i < NUM_KERNELS; i++) {
options.add(new String[] { String.valueOf(i) });
}
String[][] optionsArray = options.toArray(new String[options.size()][]);
values.add(EffectUtil.optionValue("Blur kernel size", String.valueOf(blurKernelSize), optionsArray, "This setting controls how many neighboring pixels are used to blur the shadow. Set to \"None\" for no blur."));
values.add(EffectUtil.intValue("Blur passes", blurPasses, "The setting is the number of times to apply a blur to the shadow. Set to \"0\" for no blur."));
return values;
}
/**
* @see com.github.mathiewz.slick.font.effects.ConfigurableEffect#setValues(java.util.List)
*/
@Override
public void setValues(List values) {
for (Value value2 : values) {
Value value = value2;
if (value.getName().equals("Color")) {
color = (Color) value.getObject();
} else if (value.getName().equals("Opacity")) {
opacity = ((Float) value.getObject()).floatValue();
} else if (value.getName().equals("X distance")) {
xDistance = ((Float) value.getObject()).floatValue();
} else if (value.getName().equals("Y distance")) {
yDistance = ((Float) value.getObject()).floatValue();
} else if (value.getName().equals("Blur kernel size")) {
blurKernelSize = Integer.parseInt((String) value.getObject());
} else if (value.getName().equals("Blur passes")) {
blurPasses = ((Integer) value.getObject()).intValue();
}
}
}
/**
* Generate the blur kernels which will be repeatedly applied when blurring images
*
* @param level
* The number of kernels to generate
* @return The kernels generated
*/
private static float[][] generateGaussianBlurKernels(int level) {
float[][] pascalsTriangle = generatePascalsTriangle(level);
float[][] gaussianTriangle = new float[pascalsTriangle.length][];
for (int i = 0; i < gaussianTriangle.length; i++) {
float total = 0.0f;
gaussianTriangle[i] = new float[pascalsTriangle[i].length];
for (int j = 0; j < pascalsTriangle[i].length; j++) {
total += pascalsTriangle[i][j];
}
float coefficient = 1 / total;
for (int j = 0; j < pascalsTriangle[i].length; j++) {
gaussianTriangle[i][j] = coefficient * pascalsTriangle[i][j];
}
}
return gaussianTriangle;
}
/**
* Generate Pascal's triangle
*
* @param level
* The level of the triangle to generate
* @return The Pascal's triangle kernel
*/
private static float[][] generatePascalsTriangle(int level) {
if (level < 2) {
level = 2;
}
float[][] triangle = new float[level][];
triangle[0] = new float[1];
triangle[1] = new float[2];
triangle[0][0] = 1.0f;
triangle[1][0] = 1.0f;
triangle[1][1] = 1.0f;
for (int i = 2; i < level; i++) {
triangle[i] = new float[i + 1];
triangle[i][0] = 1.0f;
triangle[i][i] = 1.0f;
for (int j = 1; j < triangle[i].length - 1; j++) {
triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
}
}
return triangle;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy