gdx-studio

#libgdx#java#desktop

git clone https://git.pyrossh.dev/gdx-studio

An IDE for creating Games using libgdx and Java supported on all platforms Android, iOS, Desktop


src_libs/com/badlogic/gdx/tools/hiero/unicodefont/effects/ShadowEffect.java
a62d533 1
/*******************************************************************************
a62d533 2
 * Copyright 2011 See AUTHORS file.
a62d533 3
 * 
a62d533 4
 * Licensed under the Apache License, Version 2.0 (the "License");
a62d533 5
 * you may not use this file except in compliance with the License.
a62d533 6
 * You may obtain a copy of the License at
a62d533 7
 * 
a62d533 8
 *   http://www.apache.org/licenses/LICENSE-2.0
a62d533 9
 * 
a62d533 10
 * Unless required by applicable law or agreed to in writing, software
a62d533 11
 * distributed under the License is distributed on an "AS IS" BASIS,
a62d533 12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
a62d533 13
 * See the License for the specific language governing permissions and
a62d533 14
 * limitations under the License.
a62d533 15
 ******************************************************************************/
a62d533 16
a62d533 17
package com.badlogic.gdx.tools.hiero.unicodefont.effects;
a62d533 18
a62d533 19
import java.awt.AlphaComposite;
a62d533 20
import java.awt.Color;
a62d533 21
import java.awt.Composite;
a62d533 22
import java.awt.Graphics2D;
a62d533 23
import java.awt.RenderingHints;
a62d533 24
import java.awt.image.BufferedImage;
a62d533 25
import java.awt.image.ConvolveOp;
a62d533 26
import java.awt.image.Kernel;
a62d533 27
import java.util.ArrayList;
a62d533 28
import java.util.Iterator;
a62d533 29
import java.util.List;
a62d533 30
a62d533 31
import com.badlogic.gdx.tools.hiero.unicodefont.Glyph;
a62d533 32
import com.badlogic.gdx.tools.hiero.unicodefont.UnicodeFont;
a62d533 33
a62d533 34
/** @author Nathan Sweet */
a62d533 35
public class ShadowEffect implements ConfigurableEffect {
a62d533 36
	/** The numberof kernels to apply */
a62d533 37
	public static final int NUM_KERNELS = 16;
a62d533 38
	/** The blur kernels applied across the effect */
a62d533 39
	public static final float[][] GAUSSIAN_BLUR_KERNELS = generateGaussianBlurKernels(NUM_KERNELS);
a62d533 40
a62d533 41
	private Color color = Color.black;
a62d533 42
	private float opacity = 0.6f;
a62d533 43
	private float xDistance = 2, yDistance = 2;
a62d533 44
	private int blurKernelSize = 0;
a62d533 45
	private int blurPasses = 1;
a62d533 46
a62d533 47
	public ShadowEffect () {
a62d533 48
	}
a62d533 49
a62d533 50
	public ShadowEffect (Color color, int xDistance, int yDistance, float opacity) {
a62d533 51
		this.color = color;
a62d533 52
		this.xDistance = xDistance;
a62d533 53
		this.yDistance = yDistance;
a62d533 54
		this.opacity = opacity;
a62d533 55
	}
a62d533 56
a62d533 57
	public void draw (BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
a62d533 58
		g = (Graphics2D)g.create();
a62d533 59
		g.translate(xDistance, yDistance);
a62d533 60
		g.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), Math.round(opacity * 255)));
a62d533 61
		g.fill(glyph.getShape());
a62d533 62
a62d533 63
		// Also shadow the outline, if one exists.
a62d533 64
		for (Iterator iter = unicodeFont.getEffects().iterator(); iter.hasNext();) {
a62d533 65
			Effect effect = (Effect)iter.next();
a62d533 66
			if (effect instanceof OutlineEffect) {
a62d533 67
				Composite composite = g.getComposite();
a62d533 68
				g.setComposite(AlphaComposite.Src); // Prevent shadow and outline shadow alpha from combining.
a62d533 69
a62d533 70
				g.setStroke(((OutlineEffect)effect).getStroke());
a62d533 71
				g.draw(glyph.getShape());
a62d533 72
a62d533 73
				g.setComposite(composite);
a62d533 74
				break;
a62d533 75
			}
a62d533 76
		}
a62d533 77
a62d533 78
		g.dispose();
a62d533 79
		if (blurKernelSize > 1 && blurKernelSize < NUM_KERNELS && blurPasses > 0) blur(image);
a62d533 80
	}
a62d533 81
a62d533 82
	private void blur (BufferedImage image) {
a62d533 83
		float[] matrix = GAUSSIAN_BLUR_KERNELS[blurKernelSize - 1];
a62d533 84
		Kernel gaussianBlur1 = new Kernel(matrix.length, 1, matrix);
a62d533 85
		Kernel gaussianBlur2 = new Kernel(1, matrix.length, matrix);
a62d533 86
		RenderingHints hints = new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
a62d533 87
		ConvolveOp gaussianOp1 = new ConvolveOp(gaussianBlur1, ConvolveOp.EDGE_NO_OP, hints);
a62d533 88
		ConvolveOp gaussianOp2 = new ConvolveOp(gaussianBlur2, ConvolveOp.EDGE_NO_OP, hints);
a62d533 89
		BufferedImage scratchImage = EffectUtil.getScratchImage();
a62d533 90
		for (int i = 0; i < blurPasses; i++) {
a62d533 91
			gaussianOp1.filter(image, scratchImage);
a62d533 92
			gaussianOp2.filter(scratchImage, image);
a62d533 93
		}
a62d533 94
	}
a62d533 95
a62d533 96
	public Color getColor () {
a62d533 97
		return color;
a62d533 98
	}
a62d533 99
a62d533 100
	public void setColor (Color color) {
a62d533 101
		this.color = color;
a62d533 102
	}
a62d533 103
a62d533 104
	public float getXDistance () {
a62d533 105
		return xDistance;
a62d533 106
	}
a62d533 107
a62d533 108
	/** Sets the pixels to offset the shadow on the x axis. The glyphs will need padding so the shadow doesn't get clipped. */
a62d533 109
	public void setXDistance (float distance) {
a62d533 110
		xDistance = distance;
a62d533 111
	}
a62d533 112
a62d533 113
	public float getYDistance () {
a62d533 114
		return yDistance;
a62d533 115
	}
a62d533 116
a62d533 117
	/** Sets the pixels to offset the shadow on the y axis. The glyphs will need padding so the shadow doesn't get clipped. */
a62d533 118
	public void setYDistance (float distance) {
a62d533 119
		yDistance = distance;
a62d533 120
	}
a62d533 121
a62d533 122
	public int getBlurKernelSize () {
a62d533 123
		return blurKernelSize;
a62d533 124
	}
a62d533 125
a62d533 126
	/** Sets how many neighboring pixels are used to blur the shadow. Set to 0 for no blur. */
a62d533 127
	public void setBlurKernelSize (int blurKernelSize) {
a62d533 128
		this.blurKernelSize = blurKernelSize;
a62d533 129
	}
a62d533 130
a62d533 131
	public int getBlurPasses () {
a62d533 132
		return blurPasses;
a62d533 133
	}
a62d533 134
a62d533 135
	/** Sets the number of times to apply a blur to the shadow. Set to 0 for no blur. */
a62d533 136
	public void setBlurPasses (int blurPasses) {
a62d533 137
		this.blurPasses = blurPasses;
a62d533 138
	}
a62d533 139
a62d533 140
	public float getOpacity () {
a62d533 141
		return opacity;
a62d533 142
	}
a62d533 143
a62d533 144
	public void setOpacity (float opacity) {
a62d533 145
		this.opacity = opacity;
a62d533 146
	}
a62d533 147
a62d533 148
	public String toString () {
a62d533 149
		return "Shadow";
a62d533 150
	}
a62d533 151
a62d533 152
	public List getValues () {
a62d533 153
		List values = new ArrayList();
a62d533 154
		values.add(EffectUtil.colorValue("Color", color));
a62d533 155
		values.add(EffectUtil.floatValue("Opacity", opacity, 0, 1, "This setting sets the translucency of the shadow."));
a62d533 156
		values.add(EffectUtil.floatValue("X distance", xDistance, -99, 99, "This setting is the amount of pixels to offset the "
a62d533 157
			+ "shadow on the x axis. The glyphs will need padding so the shadow doesn't get clipped."));
a62d533 158
		values.add(EffectUtil.floatValue("Y distance", yDistance, -99, 99, "This setting is the amount of pixels to offset the "
a62d533 159
			+ "shadow on the y axis. The glyphs will need padding so the shadow doesn't get clipped."));
a62d533 160
a62d533 161
		List options = new ArrayList();
a62d533 162
		options.add(new String[] {"None", "0"});
a62d533 163
		for (int i = 2; i < NUM_KERNELS; i++)
a62d533 164
			options.add(new String[] {String.valueOf(i)});
a62d533 165
		String[][] optionsArray = (String[][])options.toArray(new String[options.size()][]);
a62d533 166
		values.add(EffectUtil.optionValue("Blur kernel size", String.valueOf(blurKernelSize), optionsArray,
a62d533 167
			"This setting controls how many neighboring pixels are used to blur the shadow. Set to \"None\" for no blur."));
a62d533 168
a62d533 169
		values.add(EffectUtil.intValue("Blur passes", blurPasses,
a62d533 170
			"The setting is the number of times to apply a blur to the shadow. Set to \"0\" for no blur."));
a62d533 171
		return values;
a62d533 172
	}
a62d533 173
a62d533 174
	public void setValues (List values) {
a62d533 175
		for (Iterator iter = values.iterator(); iter.hasNext();) {
a62d533 176
			Value value = (Value)iter.next();
a62d533 177
			if (value.getName().equals("Color")) {
a62d533 178
				color = (Color)value.getObject();
a62d533 179
			} else if (value.getName().equals("Opacity")) {
a62d533 180
				opacity = ((Float)value.getObject()).floatValue();
a62d533 181
			} else if (value.getName().equals("X distance")) {
a62d533 182
				xDistance = ((Float)value.getObject()).floatValue();
a62d533 183
			} else if (value.getName().equals("Y distance")) {
a62d533 184
				yDistance = ((Float)value.getObject()).floatValue();
a62d533 185
			} else if (value.getName().equals("Blur kernel size")) {
a62d533 186
				blurKernelSize = Integer.parseInt((String)value.getObject());
a62d533 187
			} else if (value.getName().equals("Blur passes")) {
a62d533 188
				blurPasses = ((Integer)value.getObject()).intValue();
a62d533 189
			}
a62d533 190
		}
a62d533 191
	}
a62d533 192
a62d533 193
	/** Generate the blur kernels which will be repeatedly applied when blurring images
a62d533 194
	 * 
a62d533 195
	 * @param level The number of kernels to generate
a62d533 196
	 * @return The kernels generated */
a62d533 197
	private static float[][] generateGaussianBlurKernels (int level) {
a62d533 198
		float[][] pascalsTriangle = generatePascalsTriangle(level);
a62d533 199
		float[][] gaussianTriangle = new float[pascalsTriangle.length][];
a62d533 200
		for (int i = 0; i < gaussianTriangle.length; i++) {
a62d533 201
			float total = 0.0f;
a62d533 202
			gaussianTriangle[i] = new float[pascalsTriangle[i].length];
a62d533 203
			for (int j = 0; j < pascalsTriangle[i].length; j++)
a62d533 204
				total += pascalsTriangle[i][j];
a62d533 205
			float coefficient = 1 / total;
a62d533 206
			for (int j = 0; j < pascalsTriangle[i].length; j++)
a62d533 207
				gaussianTriangle[i][j] = coefficient * pascalsTriangle[i][j];
a62d533 208
		}
a62d533 209
		return gaussianTriangle;
a62d533 210
	}
a62d533 211
a62d533 212
	/** Generate Pascal's triangle
a62d533 213
	 * 
a62d533 214
	 * @param level The level of the triangle to generate
a62d533 215
	 * @return The Pascal's triangle kernel */
a62d533 216
	private static float[][] generatePascalsTriangle (int level) {
a62d533 217
		if (level < 2) level = 2;
a62d533 218
		float[][] triangle = new float[level][];
a62d533 219
		triangle[0] = new float[1];
a62d533 220
		triangle[1] = new float[2];
a62d533 221
		triangle[0][0] = 1.0f;
a62d533 222
		triangle[1][0] = 1.0f;
a62d533 223
		triangle[1][1] = 1.0f;
a62d533 224
		for (int i = 2; i < level; i++) {
a62d533 225
			triangle[i] = new float[i + 1];
a62d533 226
			triangle[i][0] = 1.0f;
a62d533 227
			triangle[i][i] = 1.0f;
a62d533 228
			for (int j = 1; j < triangle[i].length - 1; j++)
a62d533 229
				triangle[i][j] = triangle[i - 1][j - 1] + triangle[i - 1][j];
a62d533 230
		}
a62d533 231
		return triangle;
a62d533 232
	}
a62d533 233
}