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

com.badlogic.gdx.tools.hiero.unicodefont.effects.ShadowEffect Maven / Gradle / Ivy

The newest version!
/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

package com.badlogic.gdx.tools.hiero.unicodefont.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.Iterator;
import java.util.List;

import com.badlogic.gdx.tools.hiero.unicodefont.Glyph;
import com.badlogic.gdx.tools.hiero.unicodefont.UnicodeFont;

/** @author Nathan Sweet */
public class ShadowEffect implements ConfigurableEffect {
	/** The numberof 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);

	private Color color = Color.black;
	private float opacity = 0.6f;
	private float xDistance = 2, yDistance = 2;
	private int blurKernelSize = 0;
	private int blurPasses = 1;

	public ShadowEffect () {
	}

	public ShadowEffect (Color color, int xDistance, int yDistance, float opacity) {
		this.color = color;
		this.xDistance = xDistance;
		this.yDistance = yDistance;
		this.opacity = opacity;
	}

	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 (Iterator iter = unicodeFont.getEffects().iterator(); iter.hasNext();) {
			Effect effect = (Effect)iter.next();
			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);
	}

	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);
		}
	}

	public Color getColor () {
		return color;
	}

	public void setColor (Color color) {
		this.color = color;
	}

	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. */
	public void setXDistance (float distance) {
		xDistance = distance;
	}

	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. */
	public void setYDistance (float distance) {
		yDistance = distance;
	}

	public int getBlurKernelSize () {
		return blurKernelSize;
	}

	/** Sets how many neighboring pixels are used to blur the shadow. Set to 0 for no blur. */
	public void setBlurKernelSize (int blurKernelSize) {
		this.blurKernelSize = blurKernelSize;
	}

	public int getBlurPasses () {
		return blurPasses;
	}

	/** Sets the number of times to apply a blur to the shadow. Set to 0 for no blur. */
	public void setBlurPasses (int blurPasses) {
		this.blurPasses = blurPasses;
	}

	public float getOpacity () {
		return opacity;
	}

	public void setOpacity (float opacity) {
		this.opacity = opacity;
	}

	public String toString () {
		return "Shadow";
	}

	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, -99, 99, "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, -99, 99, "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 = (String[][])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;
	}

	public void setValues (List values) {
		for (Iterator iter = values.iterator(); iter.hasNext();) {
			Value value = (Value)iter.next();
			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