1 /******************************************************************************* 2 * Copyright 2011 See AUTHORS file. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 ******************************************************************************/ 16 package com.badlogic.gdx.tools.hiero.unicodefont.effects; 17 18 import java.awt.Color; 19 import java.awt.Graphics2D; 20 import java.awt.RenderingHints; 21 import java.awt.geom.AffineTransform; 22 import java.awt.image.BufferedImage; 23 import java.util.ArrayList; 24 import java.util.Iterator; 25 import java.util.List; 26 27 import com.badlogic.gdx.tools.distancefield.DistanceFieldGenerator; 28 import com.badlogic.gdx.tools.hiero.unicodefont.Glyph; 29 import com.badlogic.gdx.tools.hiero.unicodefont.UnicodeFont; 30 31 /** 32 * A filter to create a distance field. The resulting font can be rendered 33 * with a simple custom shader to draw bitmap fonts that remain crisp 34 * even under high magnification. 35 * 36 * <p> An example of the use of such a font is included in the libgdx test suite 37 * under the name {@code BitmapFontDistanceFieldTest}. 38 * 39 * @see DistanceFieldGenerator 40 * 41 * @author Thomas ten Cate 42 */ 43 public class DistanceFieldEffect implements ConfigurableEffect 44 { 45 private Color color = Color.WHITE; 46 private int scale = 1; 47 private float spread = 1; 48 49 /** 50 * Draws the glyph to the given image, upscaled by a factor of {@link #scale}. 51 * 52 * @param image the image to draw to 53 * @param glyph the glyph to draw 54 */ 55 private void drawGlyph(BufferedImage image, Glyph glyph) { 56 Graphics2D inputG = (Graphics2D) image.getGraphics(); 57 inputG.setTransform(AffineTransform.getScaleInstance(scale, scale)); 58 // We don't really want anti-aliasing (we'll discard it anyway), 59 // but accurate positioning might improve the result slightly 60 inputG.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); 61 inputG.setColor(Color.WHITE); 62 inputG.fill(glyph.getShape()); 63 } 64 65 @Override 66 public void draw(BufferedImage image, Graphics2D g, UnicodeFont unicodeFont, Glyph glyph) { 67 BufferedImage input = new BufferedImage( 68 scale * glyph.getWidth(), 69 scale * glyph.getHeight(), 70 BufferedImage.TYPE_BYTE_BINARY); 71 drawGlyph(input, glyph); 72 73 DistanceFieldGenerator generator = new DistanceFieldGenerator(); 74 generator.setColor(color); 75 generator.setDownscale(scale); 76 // We multiply spread by the scale, so that changing scale will only affect accuracy 77 // and not spread in the output image. 78 generator.setSpread(scale * spread); 79 BufferedImage distanceField = generator.generateDistanceField(input); 80 81 g.drawImage(distanceField, new AffineTransform(), null); 82 } 83 84 @Override 85 public String toString() { 86 return "Distance field"; 87 } 88 89 @Override 90 public List getValues() { 91 List values = new ArrayList(); 92 values.add(EffectUtil.colorValue("Color", color)); 93 values.add(EffectUtil.intValue("Scale", scale, "The distance field is computed from an image larger than the output glyph by this factor. Set this to a higher value for more accuracy, but slower font generation.")); 94 values.add(EffectUtil.floatValue("Spread", spread, 1.0f, Float.MAX_VALUE, "The maximum distance from edges where the effect of the distance field is seen. Set this to about half the width of lines in your output font.")); 95 return values; 96 } 97 98 @Override 99 public void setValues(List values) { 100 for (Iterator iter = values.iterator(); iter.hasNext();) { 101 Value value = (Value)iter.next(); 102 if ("Color".equals(value.getName())) { 103 color = (Color)value.getObject(); 104 } else if ("Scale".equals(value.getName())) { 105 scale = Math.max(1, (Integer)value.getObject()); 106 } else if ("Spread".equals(value.getName())) { 107 spread = Math.max(0, (Float)value.getObject()); 108 } 109 } 110 111 } 112 }