added sources for Slick

Former-commit-id: 1647fa32ef6894bd7db44f741f07c2f4dcdf9054
Former-commit-id: 0e5810dcfbe1fd59b13e7cabe9f1e93c5542da2d
This commit is contained in:
Song Minjae
2016-12-30 23:29:12 +09:00
parent d1f01a203d
commit d3080ffb78
329 changed files with 58400 additions and 7 deletions

View File

@@ -0,0 +1,92 @@
package org.newdawn.slick.font.effects;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.Glyph;
/**
* Makes glyphs a solid color.
*
* @author Nathan Sweet <misc@n4te.com>
*/
public class ColorEffect implements ConfigurableEffect {
/** The colour that will be applied across the text */
private Color color = Color.white;
/**
* Default constructor for injection
*/
public ColorEffect() {
}
/**
* Create a new effect to colour the text
*
* @param color The colour to apply across the text
*/
public ColorEffect(Color color) {
this.color = color;
}
/**
* @see org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
*/
public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
g.setColor(color);
g.fill(glyph.getShape());
}
/**
* Get the colour being applied by this effect
*
* @return The colour being applied by this effect
*/
public Color getColor() {
return color;
}
/**
* Set the colour being applied by this effect
*
* @param color The colour being applied by this effect
*/
public void setColor(Color color) {
if (color == null) throw new IllegalArgumentException("color cannot be null.");
this.color = color;
}
/**
* @see java.lang.Object#toString()
*/
public String toString () {
return "Color";
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect#getValues()
*/
public List getValues() {
List values = new ArrayList();
values.add(EffectUtil.colorValue("Color", color));
return values;
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect#setValues(java.util.List)
*/
public void setValues(List values) {
for (Iterator iter = values.iterator(); iter.hasNext();) {
Value value = (Value)iter.next();
if (value.getName().equals("Color")) {
setColor((Color)value.getObject());
}
}
}
}

View File

@@ -0,0 +1,53 @@
package org.newdawn.slick.font.effects;
import java.util.List;
/**
* An effect that has a number of configuration values. This allows the effect to be configured in the Hiero GUI and to be saved
* and loaded to and from a file.
*
* @author Nathan Sweet <misc@n4te.com>
*/
public interface ConfigurableEffect extends Effect {
/**
* Returns the list of {@link Value}s for this effect. This list is not typically backed by the effect, so changes to the
* values will not take affect until {@link #setValues(List)} is called.
*/
public List getValues();
/**
* Sets the list of {@link Value}s for this effect.
*/
public void setValues(List values);
/**
* Represents a configurable value for an effect.
*/
static public interface Value {
/**
* Returns the name of the value.
*/
public String getName ();
/**
* Sets the string representation of the value.
*/
public void setString (String value);
/**
* Gets the string representation of the value.
*/
public String getString ();
/**
* Gets the object representation of the value.
*/
public Object getObject ();
/**
* Shows a dialog allowing a user to configure this value.
*/
public void showDialog ();
}
}

View File

@@ -0,0 +1,25 @@
package org.newdawn.slick.font.effects;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.Glyph;
/**
* A graphical effect that is applied to glyphs in a {@link UnicodeFont}.
*
* @author Nathan Sweet <misc@n4te.com>
*/
public interface Effect {
/**
* Called to draw the effect.
*
* @param image The image to draw into
* @param g The graphics context to use for applying the effect
* @param unicodeFont The font being rendered
* @param glyph The particular glyph being rendered
*/
public void draw (BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph);
}

View File

@@ -0,0 +1,368 @@
package org.newdawn.slick.font.effects;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import javax.swing.BorderFactory;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JColorChooser;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextArea;
import javax.swing.SpinnerNumberModel;
import org.newdawn.slick.font.GlyphPage;
import org.newdawn.slick.font.effects.ConfigurableEffect.Value;
/**
* Provides utility methods for effects.
*
* @author Nathan Sweet <misc@n4te.com>
*/
public class EffectUtil {
/** A graphics 2D temporary surface to be used when generating effects */
static private BufferedImage scratchImage = new BufferedImage(GlyphPage.MAX_GLYPH_SIZE, GlyphPage.MAX_GLYPH_SIZE,
BufferedImage.TYPE_INT_ARGB);
/**
* Returns an image that can be used by effects as a temp image.
*
* @return The scratch image used for temporary operations
*/
static public BufferedImage getScratchImage() {
Graphics2D g = (Graphics2D)scratchImage.getGraphics();
g.setComposite(AlphaComposite.Clear);
g.fillRect(0, 0, GlyphPage.MAX_GLYPH_SIZE, GlyphPage.MAX_GLYPH_SIZE);
g.setComposite(AlphaComposite.SrcOver);
g.setColor(java.awt.Color.white);
return scratchImage;
}
/**
* Prompts the user for a colour value
*
* @param name Thename of the value being configured
* @param currentValue The default value that should be selected
* @return The value selected
*/
static public Value colorValue(String name, Color currentValue) {
return new DefaultValue(name, EffectUtil.toString(currentValue)) {
public void showDialog () {
Color newColor = JColorChooser.showDialog(null, "Choose a color", EffectUtil.fromString(value));
if (newColor != null) value = EffectUtil.toString(newColor);
}
public Object getObject () {
return EffectUtil.fromString(value);
}
};
}
/**
* Prompts the user for int value
*
* @param name The name of the dialog to show
* @param currentValue The current value to be displayed
* @param description The help text to provide
* @return The value selected by the user
*/
static public Value intValue (String name, final int currentValue, final String description) {
return new DefaultValue(name, String.valueOf(currentValue)) {
public void showDialog () {
JSpinner spinner = new JSpinner(new SpinnerNumberModel(currentValue, Short.MIN_VALUE, Short.MAX_VALUE, 1));
if (showValueDialog(spinner, description)) value = String.valueOf(spinner.getValue());
}
public Object getObject () {
return Integer.valueOf(value);
}
};
}
/**
* Prompts the user for float value
*
* @param name The name of the dialog to show
* @param currentValue The current value to be displayed
* @param description The help text to provide
* @param min The minimum value to allow
* @param max The maximum value to allow
* @return The value selected by the user
*/
static public Value floatValue (String name, final float currentValue, final float min, final float max,
final String description) {
return new DefaultValue(name, String.valueOf(currentValue)) {
public void showDialog () {
JSpinner spinner = new JSpinner(new SpinnerNumberModel(currentValue, min, max, 0.1f));
if (showValueDialog(spinner, description)) value = String.valueOf(((Double)spinner.getValue()).floatValue());
}
public Object getObject () {
return Float.valueOf(value);
}
};
}
/**
* Prompts the user for boolean value
*
* @param name The name of the dialog to show
* @param currentValue The current value to be displayed
* @param description The help text to provide
* @return The value selected by the user
*/
static public Value booleanValue (String name, final boolean currentValue, final String description) {
return new DefaultValue(name, String.valueOf(currentValue)) {
public void showDialog () {
JCheckBox checkBox = new JCheckBox();
checkBox.setSelected(currentValue);
if (showValueDialog(checkBox, description)) value = String.valueOf(checkBox.isSelected());
}
public Object getObject () {
return Boolean.valueOf(value);
}
};
}
/**
* Prompts the user for a value that represents a fixed number of options.
* All options are strings.
*
* @param options The first array has an entry for each option. Each entry is either a String[1] that is both the display value
* and actual value, or a String[2] whose first element is the display value and second element is the actual value.
*
* @param name The name of the value being prompted for
* @param currentValue The current value to show as default
* @param description The description of the value
* @return The value selected by the user
*/
static public Value optionValue (String name, final String currentValue, final String[][] options, final String description) {
return new DefaultValue(name, currentValue.toString()) {
public void showDialog () {
int selectedIndex = -1;
DefaultComboBoxModel model = new DefaultComboBoxModel();
for (int i = 0; i < options.length; i++) {
model.addElement(options[i][0]);
if (getValue(i).equals(currentValue)) selectedIndex = i;
}
JComboBox comboBox = new JComboBox(model);
comboBox.setSelectedIndex(selectedIndex);
if (showValueDialog(comboBox, description)) value = getValue(comboBox.getSelectedIndex());
}
private String getValue (int i) {
if (options[i].length == 1) return options[i][0];
return options[i][1];
}
public String toString () {
for (int i = 0; i < options.length; i++)
if (getValue(i).equals(value)) return options[i][0].toString();
return "";
}
public Object getObject () {
return value;
}
};
}
/**
* Convers a color to a string.
*
* @param color The color to encode to a string
* @return The colour as a string
*/
static public String toString (Color color) {
if (color == null) throw new IllegalArgumentException("color cannot be null.");
String r = Integer.toHexString(color.getRed());
if (r.length() == 1) r = "0" + r;
String g = Integer.toHexString(color.getGreen());
if (g.length() == 1) g = "0" + g;
String b = Integer.toHexString(color.getBlue());
if (b.length() == 1) b = "0" + b;
return r + g + b;
}
/**
* Converts a string to a color.
*
* @param rgb The string encoding the colour
* @return The colour represented by the given encoded string
*/
static public Color fromString (String rgb) {
if (rgb == null || rgb.length() != 6) return Color.white;
return new Color(Integer.parseInt(rgb.substring(0, 2), 16), Integer.parseInt(rgb.substring(2, 4), 16), Integer.parseInt(rgb
.substring(4, 6), 16));
}
/**
* Provides generic functionality for an effect's configurable value.
*/
static private abstract class DefaultValue implements Value {
/** The value being held */
String value;
/** The key/name of the value */
String name;
/**
* Create a default value
*
* @param name The name of the value being configured
* @param value The value to use for the default
*/
public DefaultValue(String name, String value) {
this.value = value;
this.name = name;
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect.Value#setString(java.lang.String)
*/
public void setString(String value) {
this.value = value;
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect.Value#getString()
*/
public String getString() {
return value;
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect.Value#getName()
*/
public String getName() {
return name;
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
if (value == null) {
return "";
}
return value.toString();
}
/**
* Prompt the user for a value
*
* @param component The component to use as parent for the prompting dialog
* @param description The description of the value being prompted for
* @return True if the value was configured
*/
public boolean showValueDialog(final JComponent component, String description) {
ValueDialog dialog = new ValueDialog(component, name, description);
dialog.setTitle(name);
dialog.setLocationRelativeTo(null);
EventQueue.invokeLater(new Runnable() {
public void run () {
JComponent focusComponent = component;
if (focusComponent instanceof JSpinner)
focusComponent = ((JSpinner.DefaultEditor)((JSpinner)component).getEditor()).getTextField();
focusComponent.requestFocusInWindow();
}
});
dialog.setVisible(true);
return dialog.okPressed;
}
};
/**
* Provides generic functionality for a dialog to configure a value.
*/
static private class ValueDialog extends JDialog {
/** True if OK was pressed */
public boolean okPressed = false;
/**
* Create a new dialog to configure a specific value
*
* @param component The component to use as the parent of the dialog prompting the user
* @param name The name of the value being configured
* @param description The description of the value being configured
*/
public ValueDialog(JComponent component, String name, String description) {
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setLayout(new GridBagLayout());
setModal(true);
if (component instanceof JSpinner)
((JSpinner.DefaultEditor)((JSpinner)component).getEditor()).getTextField().setColumns(4);
JPanel descriptionPanel = new JPanel();
descriptionPanel.setLayout(new GridBagLayout());
getContentPane().add(
descriptionPanel,
new GridBagConstraints(0, 0, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH, new Insets(0, 0, 0,
0), 0, 0));
descriptionPanel.setBackground(Color.white);
descriptionPanel.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.black));
{
JTextArea descriptionText = new JTextArea(description);
descriptionPanel.add(descriptionText, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
descriptionText.setWrapStyleWord(true);
descriptionText.setLineWrap(true);
descriptionText.setBorder(BorderFactory.createEmptyBorder(0, 0, 0, 0));
descriptionText.setEditable(false);
}
JPanel panel = new JPanel();
getContentPane().add(
panel,
new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(5, 5, 0,
5), 0, 0));
panel.add(new JLabel(name + ":"));
panel.add(component);
JPanel buttonPanel = new JPanel();
getContentPane().add(
buttonPanel,
new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.NONE,
new Insets(0, 0, 0, 0), 0, 0));
{
JButton okButton = new JButton("OK");
buttonPanel.add(okButton);
okButton.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent evt) {
okPressed = true;
setVisible(false);
}
});
}
{
JButton cancelButton = new JButton("Cancel");
buttonPanel.add(cancelButton);
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed (ActionEvent evt) {
setVisible(false);
}
});
}
setSize(new Dimension(320, 175));
}
}
}

View File

@@ -0,0 +1,62 @@
package org.newdawn.slick.font.effects;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.BufferedImageOp;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.Glyph;
/**
* Applys a {@link BufferedImageOp} filter to glyphs. Many filters can be found
* here: http://www.jhlabs.com/ip/filters/index.html
*
* @author Nathan Sweet <misc@n4te.com>
*/
public class FilterEffect implements Effect {
/** The filter to be applied */
private BufferedImageOp filter;
/**
* Default constructor for injection
*/
public FilterEffect () {
}
/**
* Create a new filtering effect based on a convolution operation
*
* @param filter The filter to apply
*/
public FilterEffect (BufferedImageOp filter) {
this.filter = filter;
}
/**
* @see org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
*/
public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
BufferedImage scratchImage = EffectUtil.getScratchImage();
filter.filter(image, scratchImage);
image.getGraphics().drawImage(scratchImage, 0, 0, null);
}
/**
* Get the filter being applied by this effect
*
* @return The filter being applied by this effect
*/
public BufferedImageOp getFilter() {
return filter;
}
/**
* Set the filter being applied by this effect
*
* @param filter The filter being used by this effect
*/
public void setFilter(BufferedImageOp filter) {
this.filter = filter;
}
}

View File

@@ -0,0 +1,195 @@
package org.newdawn.slick.font.effects;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.Glyph;
/**
* Paints glyphs with a gradient fill.
*
* @author Nathan Sweet <misc@n4te.com>
*/
public class GradientEffect implements ConfigurableEffect {
/** The top of gradients colour */
private Color topColor = Color.cyan;
/** The bottom of the gradient's colour */
private Color bottomColor = Color.blue;
/** The offset the gradient starts at */
private int offset = 0;
/** The scaling of the graident */
private float scale = 1;
/** True if the graident should cycle back and forth across the surface */
private boolean cyclic;
/**
* Default constructor for injection
*/
public GradientEffect() {
}
/**
* Create a new effect to apply a graident
*
* @param topColor The colour at the top of the graident
* @param bottomColor The colour at the bottom of the gradient
* @param scale The scale of the graident
*/
public GradientEffect(Color topColor, Color bottomColor, float scale) {
this.topColor = topColor;
this.bottomColor = bottomColor;
this.scale = scale;
}
/**
* @see org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
*/
public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
int ascent = unicodeFont.getAscent();
float height = (ascent) * scale;
float top = -glyph.getYOffset() + unicodeFont.getDescent() + offset + ascent / 2 - height / 2;
g.setPaint(new GradientPaint(0, top, topColor, 0, top + height, bottomColor, cyclic));
g.fill(glyph.getShape());
}
/**
* Get the colour at the top of the graident
*
* @return The colour at the top of the gradient
*/
public Color getTopColor() {
return topColor;
}
/**
* Set the colour at the top of the graident
*
* @param topColor The colour at the top of the graident
*/
public void setTopColor(Color topColor) {
this.topColor = topColor;
}
/**
* Get the colour at the bottom of the graident
*
* @return The colour at the bottom of the gradient
*/
public Color getBottomColor () {
return bottomColor;
}
/**
* Set the colour at the bottom of the graident
*
* @param bottomColor The colour at the bottom of the graident
*/
public void setBottomColor(Color bottomColor) {
this.bottomColor = bottomColor;
}
/**
* Get the offset the gradients starts at
*
* @return The offset the gradient starts at
*/
public int getOffset() {
return offset;
}
/**
* Sets the pixel offset to move the gradient up or down.
* The gradient is normally centered on the glyph.
*
* @param offset The offset the gradient is moved by
*/
public void setOffset (int offset) {
this.offset = offset;
}
/**
* Get the percentage scaling being applied to the gradient across the surface
*
* @return The scale of the graident
*/
public float getScale() {
return scale;
}
/**
* Changes the height of the gradient by a percentage. The gradient is
* normally the height of most glyphs in the font.
*
* @param scale The scale to apply
*/
public void setScale (float scale) {
this.scale = scale;
}
/**
* Check if the graident is repeating
*
* @return True if the gradient is repeating
*/
public boolean isCyclic() {
return cyclic;
}
/**
* If set to true, the gradient will repeat.
*
* @param cyclic True if the graident repeats
*/
public void setCyclic(boolean cyclic) {
this.cyclic = cyclic;
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return "Gradient";
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect#getValues()
*/
public List getValues() {
List values = new ArrayList();
values.add(EffectUtil.colorValue("Top color", topColor));
values.add(EffectUtil.colorValue("Bottom color", bottomColor));
values.add(EffectUtil.intValue("Offset", offset,
"This setting allows you to move the gradient up or down. The gradient is normally centered on the glyph."));
values.add(EffectUtil.floatValue("Scale", scale, 0, 1, "This setting allows you to change the height of the gradient by a"
+ "percentage. The gradient is normally the height of most glyphs in the font."));
values.add(EffectUtil.booleanValue("Cyclic", cyclic, "If this setting is checked, the gradient will repeat."));
return values;
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect#setValues(java.util.List)
*/
public void setValues(List values) {
for (Iterator iter = values.iterator(); iter.hasNext();) {
Value value = (Value)iter.next();
if (value.getName().equals("Top color")) {
topColor = (Color)value.getObject();
} else if (value.getName().equals("Bottom color")) {
bottomColor = (Color)value.getObject();
} else if (value.getName().equals("Offset")) {
offset = ((Integer)value.getObject()).intValue();
} else if (value.getName().equals("Scale")) {
scale = ((Float)value.getObject()).floatValue();
} else if (value.getName().equals("Cyclic")) {
cyclic = ((Boolean)value.getObject()).booleanValue();
}
}
}
}

View File

@@ -0,0 +1,178 @@
package org.newdawn.slick.font.effects;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Stroke;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.Glyph;
/**
* Strokes glyphs with an outline.
*
* @author Nathan Sweet <misc@n4te.com>
*/
public class OutlineEffect implements ConfigurableEffect {
/** The width of the outline in pixels */
private float width = 2;
/** The colour of the outline */
private Color color = Color.black;
/** The type of join at the line joins of the out line */
private int join = BasicStroke.JOIN_BEVEL;
/** The stroke used to draw the outline */
private Stroke stroke;
/**
* Default constructor for injection
*/
public OutlineEffect() {
}
/**
* Create a new effect to draw the outline of the text
*
* @param width The width of the outline
* @param color The colour of the outline
*/
public OutlineEffect(int width, Color color) {
this.width = width;
this.color = color;
}
/**
* @see org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
*/
public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) {
g = (Graphics2D)g.create();
if (stroke != null)
g.setStroke(stroke);
else
g.setStroke(getStroke());
g.setColor(color);
g.draw(glyph.getShape());
g.dispose();
}
/**
* Get the width of the outline being drawn
*
* @return The width of the outline being drawn
*/
public float getWidth() {
return width;
}
/**
* Sets the width of the outline. The glyphs will need padding so the
* outline doesn't get clipped.
*
* @param width The width of the outline being drawn
*/
public void setWidth (int width) {
this.width = width;
}
/**
* Get the colour of the outline being drawn
*
* @return The colour of the outline being drawn
*/
public Color getColor() {
return color;
}
/**
* Set the colour of the outline being drawn
*
* @param color The colour of the outline to draw
*/
public void setColor(Color color) {
this.color = color;
}
/**
* Get the join type as indicated by @see BasicStroke
*
* @return The join type between segments in the outline
*/
public int getJoin() {
return join;
}
/**
* Get the stroke being used to draw the outline
*
* @return The stroke being used to draw the outline
*/
public Stroke getStroke() {
if (stroke == null) {
return new BasicStroke(width, BasicStroke.CAP_SQUARE, join);
}
return stroke;
}
/**
* Sets the stroke to use for the outline. If this is set,
* the other outline settings are ignored.
*
* @param stroke The stroke to be used to draw the outline
*/
public void setStroke (Stroke stroke) {
this.stroke = stroke;
}
/**
* Sets how the corners of the outline are drawn. This is usually only noticeable
* at large outline widths.
*
* @param join One of: {@link BasicStroke#JOIN_BEVEL}, {@link BasicStroke#JOIN_MITER}, {@link BasicStroke#JOIN_ROUND}
*/
public void setJoin (int join) {
this.join = join;
}
/**
* @see java.lang.Object#toString()
*/
public String toString () {
return "Outline";
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect#getValues()
*/
public List getValues () {
List values = new ArrayList();
values.add(EffectUtil.colorValue("Color", color));
values.add(EffectUtil.floatValue("Width", width, 0.1f, 999, "This setting controls the width of the outline. "
+ "The glyphs will need padding so the outline doesn't get clipped."));
values.add(EffectUtil.optionValue("Join", String.valueOf(join), new String[][] { {"Bevel", BasicStroke.JOIN_BEVEL + ""},
{"Miter", BasicStroke.JOIN_MITER + ""}, {"Round", BasicStroke.JOIN_ROUND + ""}},
"This setting defines how the corners of the outline are drawn. "
+ "This is usually only noticeable at large outline widths."));
return values;
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect#setValues(java.util.List)
*/
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("Width")) {
width = ((Float)value.getObject()).floatValue();
} else if (value.getName().equals("Join")) {
join = Integer.parseInt((String)value.getObject());
}
}
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright 2006 Jerry Huxtable
*
* 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 org.newdawn.slick.font.effects;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.util.Iterator;
import java.util.List;
/**
* An effect that genrates a wobbly line around the outline of the text
*
* @author Jerry Huxtable
* @author Nathan Sweet <misc@n4te.com>
*/
public class OutlineWobbleEffect extends OutlineEffect {
/** How often the line wobbles */
private float detail = 1;
/** The amount of the line wobbles */
private float amplitude = 1;
/**
* Default constructor for injection
*/
public OutlineWobbleEffect () {
setStroke(new WobbleStroke());
}
/**
* Gets the detail of the wobble effect.
*
* @return The detail of the wobble effect
*/
public float getDetail() {
return detail;
}
/**
* Sets the detail of the wobble effect.
*
* @param detail The detail of the wobble effect
*/
public void setDetail(float detail) {
this.detail = detail;
}
/**
* Gets the amplitude of the wobble effect.
*
* @return The amplitude of the wobble effect
*/
public float getAmplitude() {
return amplitude;
}
/**
* Sets the amplitude of the wobble effect.
*
* @param amplitude The detail of the wobble effect
*/
public void setAmplitude(float amplitude) {
this.amplitude = amplitude;
}
/**
* Create a new effect that generates a wobbly line around the text
*
* @param width The width of the line
* @param color The colour of the line
*/
public OutlineWobbleEffect (int width, Color color) {
super(width, color);
}
/**
* @see org.newdawn.slick.font.effects.OutlineEffect#toString()
*/
public String toString() {
return "Outline (Wobble)";
}
/**
* @see org.newdawn.slick.font.effects.OutlineEffect#getValues()
*/
public List getValues() {
List values = super.getValues();
values.remove(2); // Remove "Join".
values.add(EffectUtil.floatValue("Detail", detail, 1, 50, "This setting controls how detailed the outline will be. "
+ "Smaller numbers cause the outline to have more detail."));
values.add(EffectUtil.floatValue("Amplitude", amplitude, 0.5f, 50, "This setting controls the amplitude of the outline."));
return values;
}
/**
* @see org.newdawn.slick.font.effects.OutlineEffect#setValues(java.util.List)
*/
public void setValues(List values) {
super.setValues(values);
for (Iterator iter = values.iterator(); iter.hasNext();) {
Value value = (Value)iter.next();
if (value.getName().equals("Detail")) {
detail = ((Float)value.getObject()).floatValue();
} else if (value.getName().equals("Amplitude")) {
amplitude = ((Float)value.getObject()).floatValue();
}
}
}
/**
* A stroke that generate a wobbly line
*
* @author Jerry Huxtable
* @author Nathan Sweet <misc@n4te.com>
*/
private class WobbleStroke implements Stroke {
/** The flattening factor of the stroke */
private static final float FLATNESS = 1;
/**
* @see java.awt.Stroke#createStrokedShape(java.awt.Shape)
*/
public Shape createStrokedShape (Shape shape) {
GeneralPath result = new GeneralPath();
shape = new BasicStroke(getWidth(), BasicStroke.CAP_SQUARE, getJoin()).createStrokedShape(shape);
PathIterator it = new FlatteningPathIterator(shape.getPathIterator(null), FLATNESS);
float points[] = new float[6];
float moveX = 0, moveY = 0;
float lastX = 0, lastY = 0;
float thisX = 0, thisY = 0;
int type = 0;
float next = 0;
while (!it.isDone()) {
type = it.currentSegment(points);
switch (type) {
case PathIterator.SEG_MOVETO:
moveX = lastX = randomize(points[0]);
moveY = lastY = randomize(points[1]);
result.moveTo(moveX, moveY);
next = 0;
break;
case PathIterator.SEG_CLOSE:
points[0] = moveX;
points[1] = moveY;
// Fall into....
case PathIterator.SEG_LINETO:
thisX = randomize(points[0]);
thisY = randomize(points[1]);
float dx = thisX - lastX;
float dy = thisY - lastY;
float distance = (float)Math.sqrt(dx * dx + dy * dy);
if (distance >= next) {
float r = 1.0f / distance;
while (distance >= next) {
float x = lastX + next * dx * r;
float y = lastY + next * dy * r;
result.lineTo(randomize(x), randomize(y));
next += detail;
}
}
next -= distance;
lastX = thisX;
lastY = thisY;
break;
}
it.next();
}
return result;
}
/**
* Get a random wobble factor
*
* @param x The position on the line
* @return The wobble factor
*/
private float randomize(float x) {
return x + (float)Math.random() * amplitude * 2 - 1;
}
}
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright 2006 Jerry Huxtable
*
* 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 org.newdawn.slick.font.effects;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.geom.FlatteningPathIterator;
import java.awt.geom.GeneralPath;
import java.awt.geom.PathIterator;
import java.util.Iterator;
import java.util.List;
/**
* An effect to generate a uniformly zigzaging line around text
*
* @author Jerry Huxtable
* @author Nathan Sweet <misc@n4te.com>
*/
public class OutlineZigzagEffect extends OutlineEffect {
/** The amount the line moves away from the text */
private float amplitude = 1;
/** How often the line zigs and zags */
private float wavelength = 3;
/**
* Default constructor for injection
*/
public OutlineZigzagEffect() {
setStroke(new ZigzagStroke());
}
/**
* Gets the wavelength of the wobble effect.
*
* @return The wavelength of the wobble effect
*/
public float getWavelength() {
return wavelength;
}
/**
* Sets the wavelength of the wobble effect.
*
* @param wavelength The wavelength of the wobble effect
*/
public void setWavelength(float wavelength) {
this.wavelength = wavelength;
}
/**
* Gets the amplitude of the wobble effect.
*
* @return The amplitude of the wobble effect
*/
public float getAmplitude() {
return amplitude;
}
/**
* Sets the amplitude of the wobble effect.
*
* @param amplitude The detail of the wobble effect
*/
public void setAmplitude(float amplitude) {
this.amplitude = amplitude;
}
/**
* Create a new effect to generate a zigzagging line around the text
*
* @param width The width of the line
* @param color The colour of the line
*/
public OutlineZigzagEffect(int width, Color color) {
super(width, color);
}
/**
* @see org.newdawn.slick.font.effects.OutlineEffect#toString()
*/
public String toString () {
return "Outline (Zigzag)";
}
/**
* @see org.newdawn.slick.font.effects.OutlineEffect#getValues()
*/
public List getValues() {
List values = super.getValues();
values.add(EffectUtil.floatValue("Wavelength", wavelength, 1, 100, "This setting controls the wavelength of the outline. "
+ "The smaller the value, the more segments will be used to draw the outline."));
values.add(EffectUtil.floatValue("Amplitude", amplitude, 0.5f, 50, "This setting controls the amplitude of the outline. "
+ "The bigger the value, the more the zigzags will vary."));
return values;
}
/**
* @see org.newdawn.slick.font.effects.OutlineEffect#setValues(java.util.List)
*/
public void setValues(List values) {
super.setValues(values);
for (Iterator iter = values.iterator(); iter.hasNext();) {
Value value = (Value)iter.next();
if (value.getName().equals("Wavelength")) {
wavelength = ((Float)value.getObject()).floatValue();
} else if (value.getName().equals("Amplitude")) {
amplitude = ((Float)value.getObject()).floatValue();
}
}
}
/**
* A stroke to generate zigzags
*
* @author Jerry Huxtable
* @author Nathan Sweet <misc@n4te.com>
*/
private class ZigzagStroke implements Stroke {
/** The flattening factor applied to the path iterator */
private static final float FLATNESS = 1;
/**
* @see java.awt.Stroke#createStrokedShape(java.awt.Shape)
*/
public Shape createStrokedShape (Shape shape) {
GeneralPath result = new GeneralPath();
PathIterator it = new FlatteningPathIterator(shape.getPathIterator(null), FLATNESS);
float points[] = new float[6];
float moveX = 0, moveY = 0;
float lastX = 0, lastY = 0;
float thisX = 0, thisY = 0;
int type = 0;
float next = 0;
int phase = 0;
while (!it.isDone()) {
type = it.currentSegment(points);
switch (type) {
case PathIterator.SEG_MOVETO:
moveX = lastX = points[0];
moveY = lastY = points[1];
result.moveTo(moveX, moveY);
next = wavelength / 2;
break;
case PathIterator.SEG_CLOSE:
points[0] = moveX;
points[1] = moveY;
// Fall into....
case PathIterator.SEG_LINETO:
thisX = points[0];
thisY = points[1];
float dx = thisX - lastX;
float dy = thisY - lastY;
float distance = (float)Math.sqrt(dx * dx + dy * dy);
if (distance >= next) {
float r = 1.0f / distance;
while (distance >= next) {
float x = lastX + next * dx * r;
float y = lastY + next * dy * r;
if ((phase & 1) == 0)
result.lineTo(x + amplitude * dy * r, y - amplitude * dx * r);
else
result.lineTo(x - amplitude * dy * r, y + amplitude * dx * r);
next += wavelength;
phase++;
}
}
next -= distance;
lastX = thisX;
lastY = thisY;
if (type == PathIterator.SEG_CLOSE) result.closePath();
break;
}
it.next();
}
return new BasicStroke(getWidth(), BasicStroke.CAP_SQUARE, getJoin()).createStrokedShape(result);
}
}
}

View File

@@ -0,0 +1,321 @@
package org.newdawn.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.Iterator;
import java.util.List;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.Glyph;
/**
* An effect to generate soft shadows beneath text
*
* @author Nathan Sweet <misc@n4te.com>
*/
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 org.newdawn.slick.font.effects.Effect#draw(java.awt.image.BufferedImage, java.awt.Graphics2D, org.newdawn.slick.UnicodeFont, org.newdawn.slick.font.Glyph)
*/
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);
}
/**
* 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()
*/
public String toString() {
return "Shadow";
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect#getValues()
*/
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 = (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;
}
/**
* @see org.newdawn.slick.font.effects.ConfigurableEffect#setValues(java.util.List)
*/
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;
}
}