/*
JWildfire - an image and animation processor written in Java
Copyright (C) 1995-2011 Andreas Maschke
This is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser
General Public License as published by the Free Software Foundation; either version 2.1 of the
License, or (at your option) any later version.
This software is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this software;
if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jwildfire.transform;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsEnvironment;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;
import org.jwildfire.base.Property;
import org.jwildfire.base.PropertyCategory;
import org.jwildfire.base.PropertyMin;
import org.jwildfire.base.Tools;
import org.jwildfire.image.SimpleImage;
import org.jwildfire.image.WFImage;
import com.l2fprod.common.beans.editor.ComboBoxPropertyEditor;
public class TextTransformer extends Mesh2DTransformer {
public enum FontStyle {
PLAIN, BOLD, ITALIC
}
public enum Mode {
NORMAL, OUTLINE
}
public enum HAlignment {
CENTRE, LEFT, RIGHT, NONE
}
public enum VAlignment {
CENTRE, TOP, BOTTOM, NONE
}
@Property(description = "1st row of text")
private String text1;
@Property(description = "2nd row of text")
private String text2;
@Property(description = "3rd row of text")
private String text3;
@Property(description = "4th row of text")
private String text4;
@Property(description = "5th row of text")
private String text5;
@Property(description = "6th row of text")
private String text6;
@Property(description = "7th row of text")
private String text7;
@Property(category = PropertyCategory.SECONDARY, description = "Horizontal alignment of the text", editorClass = HAlignmentEditor.class)
private HAlignment hAlign = HAlignment.CENTRE;
@Property(category = PropertyCategory.SECONDARY, description = "Vertical alignment of the text", editorClass = VAlignmentEditor.class)
private VAlignment vAlign = VAlignment.CENTRE;
@Property(description = "Name of the font", editorClass = FontNameEditor.class)
private String fontName;
@Property(description = "Size of the font")
private int fontSize;
@Property(description = "Offset to the base-line of the font")
private int baseLineOffset = 0;
@Property(description = "Style of the font", editorClass = FontStyleEditor.class, category = PropertyCategory.SECONDARY)
private FontStyle fontStyle;
@Property(description = "Text color")
private Color color;
@Property(description = "Text mode", editorClass = ModeEditor.class)
private Mode mode;
@Property(description = "Text position in x-direction (when no alignment")
private int posX;
@Property(description = "Text position in y-direction (when no alignment")
private int posY;
@PropertyMin(1)
@Property(description = "Outline width (mode=OUTLINE)", category = PropertyCategory.SECONDARY)
private int outlineWidth;
@Property(description = "Enable antialiasing")
private boolean antialiasing;
private class TextRenderInfo {
private Font font;
int maxWidth = 0, maxHeight = 0, maxRow = 0;
int yOffset = 0;
int areaHeight;
}
private TextRenderInfo createTextRenderInfo(SimpleImage pImg, List<TextRow> pRows) {
// create font and calculate the row sizes
TextRenderInfo res = new TextRenderInfo();
{
Graphics g = pImg.getGraphics();
{
int fontStyle;
switch (this.fontStyle) {
case BOLD:
fontStyle = Font.BOLD;
break;
case ITALIC:
fontStyle = Font.ITALIC;
break;
default:
fontStyle = Font.PLAIN;
break;
}
res.font = new Font(this.fontName, fontStyle, this.fontSize);
}
g.setFont(res.font);
FontMetrics fm = g.getFontMetrics();
res.yOffset = fm.getMaxAscent();
FontRenderContext frc = fm.getFontRenderContext();
for (TextRow row : pRows) {
Rectangle2D rect = res.font.getStringBounds(row.text, frc);
row.width = (int) (rect.getWidth() + 0.5);
row.height = (int) (rect.getHeight() + 0.5);
if (this.mode == Mode.OUTLINE) {
row.width += 2 * this.outlineWidth;
row.height += 2 * this.outlineWidth;
}
row.height += baseLineOffset;
if (row.width > res.maxWidth)
res.maxWidth = row.width;
if (row.height > res.maxHeight)
res.maxHeight = row.height;
if (row.row > res.maxRow)
res.maxRow = row.row;
}
}
res.areaHeight = (res.maxRow + 1) * res.maxHeight;
return res;
}
public Dimension calculateTextSize() {
SimpleImage img = new SimpleImage(320, 256);
List<TextRow> rows = getRows();
if ((rows == null) || (rows.size() < 1))
return new Dimension(0, 0);
TextRenderInfo renderInfo = createTextRenderInfo(img, rows);
return new Dimension(renderInfo.maxWidth, renderInfo.areaHeight);
}
@Override
protected void performPixelTransformation(WFImage pImg) {
SimpleImage img = (SimpleImage) pImg;
List<TextRow> rows = getRows();
if ((rows == null) || (rows.size() < 1))
return;
// calculate the offsets
// int areaWidth = maxWidth;
TextRenderInfo renderInfo = createTextRenderInfo(img, rows);
int width = pImg.getImageWidth();
int height = pImg.getImageHeight();
for (TextRow row : rows) {
switch (hAlign) {
case CENTRE:
row.left = (width - row.width) / 2;
break;
case LEFT:
row.left = 0;
break;
case RIGHT:
row.left = width - row.width;
break;
case NONE:
row.left = posX;
}
switch (vAlign) {
case CENTRE:
row.top = (height - renderInfo.areaHeight) / 2 + row.row * renderInfo.maxHeight;
break;
case TOP:
row.top = row.row * renderInfo.maxHeight;
break;
case BOTTOM:
row.top = height - (renderInfo.maxRow + 1 - row.row) * renderInfo.maxHeight;
break;
case NONE:
row.top = posY + row.row * renderInfo.maxHeight;
}
row.top += baseLineOffset;
}
// draw the text
int offLeft, offRight;
if (this.mode == Mode.OUTLINE) {
offLeft = offRight = this.outlineWidth;
}
else {
offLeft = offRight = 0;
}
switch (this.mode) {
case NORMAL: {
Graphics g = img.getGraphics();
g.setFont(renderInfo.font);
g.setColor(this.color);
Graphics2D g2d = (Graphics2D) g;
if (antialiasing) {
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
for (TextRow row : rows) {
g.drawString(row.text, offLeft + row.left, offRight + row.top + renderInfo.yOffset);
}
}
break;
case OUTLINE: {
SimpleImage textImg = new SimpleImage(width, height);
Graphics g = textImg.getGraphics();
g.setFont(renderInfo.font);
g.setColor(this.color);
Graphics2D g2d = (Graphics2D) g;
if (antialiasing) {
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
for (TextRow row : rows) {
g.drawString(row.text, offLeft + row.left, offRight + row.top + renderInfo.yOffset);
}
SimpleImage dilateImg = textImg.clone();
{
ErodeTransformer eT = new ErodeTransformer();
eT.setMode(ErodeTransformer.Mode.DILATE);
eT.setSize(2 * this.outlineWidth + 1);
eT.setShape(ErodeTransformer.Shape.DISK);
eT.transformImage(dilateImg);
}
{
AddTransformer aT = new AddTransformer();
aT.setMode(AddTransformer.Mode.SUBTRACT);
aT.setForegroundImage(textImg);
aT.transformImage(dilateImg);
}
{
AlphaComposeTransformer cT = new AlphaComposeTransformer();
cT.setForegroundImage(dilateImg);
SimpleImage alphaImg = dilateImg.clone();
new ColorToGrayTransformer().transformImage(alphaImg);
cT.setAlphaChannelImage(alphaImg);
cT.transformImage(img);
}
}
break;
}
// apply the text mask to the image
/*
pImg.setBufferedImage(textImg.getBufferedImg(), textImg.getImageWidth(),
textImg.getImageHeight());*/
}
private List<TextRow> getRows() {
List<TextRow> res = new ArrayList<TextRow>();
if ((text1 != null) && (text1.length() > 0))
res.add(new TextRow(0, text1));
if ((text2 != null) && (text2.length() > 0))
res.add(new TextRow(1, text2));
if ((text3 != null) && (text3.length() > 0))
res.add(new TextRow(2, text3));
if ((text4 != null) && (text4.length() > 0))
res.add(new TextRow(3, text4));
if ((text5 != null) && (text5.length() > 0))
res.add(new TextRow(4, text5));
if ((text6 != null) && (text6.length() > 0))
res.add(new TextRow(5, text6));
if ((text7 != null) && (text7.length() > 0))
res.add(new TextRow(6, text7));
return res;
}
private class TextRow {
public int row;
public String text;
public int width;
public int height;
public int left;
public int top;
public TextRow(int pRow, String pText) {
row = pRow;
text = pText;
}
}
@Override
public void initDefaultParams(WFImage pImg) {
text1 = Tools.APP_TITLE;
text2 = "is";
text3 = "cool :-)";
fontName = "Arial";
fontStyle = FontStyle.PLAIN;
fontSize = 120;
color = new Color(255, 0, 51);
mode = Mode.NORMAL;
outlineWidth = 3;
antialiasing = true;
hAlign = HAlignment.CENTRE;
vAlign = VAlignment.CENTRE;
}
public static class FontStyleEditor extends ComboBoxPropertyEditor {
public FontStyleEditor() {
super();
setAvailableValues(new FontStyle[] { FontStyle.PLAIN, FontStyle.BOLD, FontStyle.ITALIC });
}
}
public static class ModeEditor extends ComboBoxPropertyEditor {
public ModeEditor() {
super();
setAvailableValues(new Mode[] { Mode.NORMAL, Mode.OUTLINE });
}
}
private static String[] availableFontNames = null;
private static String[] getAvailableFontNames() {
if (availableFontNames == null) {
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
availableFontNames = ge.getAvailableFontFamilyNames();
}
return availableFontNames;
}
public static class FontNameEditor extends ComboBoxPropertyEditor {
public FontNameEditor() {
super();
setAvailableValues(getAvailableFontNames());
}
}
public String getFontName() {
return fontName;
}
public void setFontName(String fontName) {
this.fontName = fontName;
}
public int getFontSize() {
return fontSize;
}
public void setFontSize(int fontSize) {
this.fontSize = fontSize;
}
public FontStyle getFontStyle() {
return fontStyle;
}
public void setFontStyle(FontStyle fontStyle) {
this.fontStyle = fontStyle;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public Mode getMode() {
return mode;
}
public void setMode(Mode mode) {
this.mode = mode;
}
public int getOutlineWidth() {
return outlineWidth;
}
public void setOutlineWidth(int outlineWidth) {
this.outlineWidth = outlineWidth;
}
public boolean isAntialiasing() {
return antialiasing;
}
public void setAntialiasing(boolean antialiasing) {
this.antialiasing = antialiasing;
}
public String getText1() {
return text1;
}
public void setText1(String text1) {
this.text1 = text1;
}
public String getText2() {
return text2;
}
public void setText2(String text2) {
this.text2 = text2;
}
public String getText3() {
return text3;
}
public void setText3(String text3) {
this.text3 = text3;
}
public String getText4() {
return text4;
}
public void setText4(String text4) {
this.text4 = text4;
}
public String getText5() {
return text5;
}
public void setText5(String text5) {
this.text5 = text5;
}
public String getText6() {
return text6;
}
public void setText6(String text6) {
this.text6 = text6;
}
public String getText7() {
return text7;
}
public void setText7(String text7) {
this.text7 = text7;
}
public static class HAlignmentEditor extends ComboBoxPropertyEditor {
public HAlignmentEditor() {
super();
setAvailableValues(new HAlignment[] { HAlignment.CENTRE, HAlignment.LEFT, HAlignment.RIGHT, HAlignment.NONE });
}
}
public static class VAlignmentEditor extends ComboBoxPropertyEditor {
public VAlignmentEditor() {
super();
setAvailableValues(new VAlignment[] { VAlignment.CENTRE, VAlignment.TOP, VAlignment.BOTTOM, VAlignment.NONE });
}
}
public HAlignment getHAlign() {
return hAlign;
}
public void setHAlign(HAlignment hAlign) {
this.hAlign = hAlign;
}
public VAlignment getVAlign() {
return vAlign;
}
public void setVAlign(VAlignment vAlign) {
this.vAlign = vAlign;
}
public int getBaseLineOffset() {
return baseLineOffset;
}
public void setBaseLineOffset(int baseLineOffset) {
this.baseLineOffset = baseLineOffset;
}
public int getPosX() {
return posX;
}
public void setPosX(int posX) {
this.posX = posX;
}
public int getPosY() {
return posY;
}
public void setPosY(int posY) {
this.posY = posY;
}
}