/*
JWildfire - an image and animation processor written in Java
Copyright (C) 1995-2012 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.create.tina.variation;
import static org.jwildfire.base.mathlib.MathLib.M_2PI;
import static org.jwildfire.base.mathlib.MathLib.cos;
import static org.jwildfire.base.mathlib.MathLib.sin;
import static org.jwildfire.base.mathlib.MathLib.sqrt;
import java.util.HashMap;
import java.util.Map;
import org.jwildfire.base.Tools;
import org.jwildfire.create.GradientCreator;
import org.jwildfire.create.tina.base.Layer;
import org.jwildfire.create.tina.base.XForm;
import org.jwildfire.create.tina.base.XYZPoint;
import org.jwildfire.create.tina.palette.RenderColor;
import org.jwildfire.image.Pixel;
import org.jwildfire.image.SimpleHDRImage;
import org.jwildfire.image.SimpleImage;
import org.jwildfire.image.WFImage;
public abstract class AbstractDisplacementMapWFFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
private static final String PARAM_MODE = "mode";
private static final String PARAM_COLOR_MODE = "color_mode";
private static final String PARAM_BIAS = "bias";
private static final String PARAM_SCALEX = "scale_x";
private static final String PARAM_SCALEY = "scale_y";
private static final String PARAM_OFFSETX = "offset_x";
private static final String PARAM_OFFSETY = "offset_y";
private static final String PARAM_OFFSETZ = "offset_z";
private static final String PARAM_TILEX = "tile_x";
private static final String PARAM_TILEY = "tile_y";
private static final String RESSOURCE_INLINED_IMAGE = "inlined_image";
private static final String RESSOURCE_IMAGE_FILENAME = "image_filename";
private static final String[] paramNames = { PARAM_MODE, PARAM_COLOR_MODE, PARAM_BIAS, PARAM_SCALEX, PARAM_SCALEY, PARAM_OFFSETX, PARAM_OFFSETY, PARAM_OFFSETZ, PARAM_TILEX, PARAM_TILEY };
private static final String[] ressourceNames = { RESSOURCE_IMAGE_FILENAME, RESSOURCE_INLINED_IMAGE };
private static final int MODE_ROTATE = 0;
private static final int MODE_TRANSLATE = 1;
private static final int MODE_SCALE = 2;
private static final int MODE_SCISSOR = 3;
private static final int COLOR_MODE_IGNORE = 0;
private static final int COLOR_MODE_INHERIT = 1;
private int mode = MODE_ROTATE;
private int colorMode = COLOR_MODE_IGNORE;
private double bias = 0.0;
private double scaleX = 1.0;
private double scaleY = 1.0;
private double offsetX = 0.0;
private double offsetY = 0.0;
private double offsetZ = 0.0;
private int tileX = 1;
private int tileY = 1;
private String imageFilename = null;
private byte[] inlinedImage = null;
private int inlinedImageHash = 0;
// derived params
private int imgWidth, imgHeight;
private Pixel toolPixel = new Pixel();
private float[] rgbArray = new float[3];
public void transform(FlameTransformationContext pContext, XForm pXForm, XYZPoint pAffineTP, XYZPoint pVarTP, double pAmount, double pInputX, double pInputY) {
double x = (pInputX - (offsetX + 0.5) + 1.0) / scaleX * (double) (imgWidth - 1);
double y = (pInputY - (offsetY + 0.5) + 1.0) / scaleY * (double) (imgHeight - 1);
int ix = Tools.FTOI(x);
int iy = Tools.FTOI(y);
if (this.tileX == 1) {
if (ix < 0) {
int nx = ix / imgWidth - 1;
ix -= nx * imgWidth;
}
else if (ix >= imgWidth) {
int nx = ix / imgWidth;
ix -= nx * imgWidth;
}
}
if (this.tileY == 1) {
if (iy < 0) {
int ny = iy / imgHeight - 1;
iy -= ny * imgHeight;
}
else if (iy >= imgHeight) {
int ny = iy / imgHeight;
iy -= ny * imgHeight;
}
}
double r, g, b;
if (ix >= 0 && ix < imgWidth && iy >= 0 && iy < imgHeight) {
if (colorMap instanceof SimpleImage) {
toolPixel.setARGBValue(((SimpleImage) colorMap).getARGBValue(
ix, iy));
r = (double) toolPixel.r / 255.0;
g = (double) toolPixel.g / 255.0;
b = (double) toolPixel.b / 255.0;
}
else {
((SimpleHDRImage) colorMap).getRGBValues(rgbArray, ix, iy);
r = rgbArray[0];
g = rgbArray[0];
b = rgbArray[0];
}
}
else {
return;
}
switch (mode) {
case MODE_TRANSLATE: {
double amountX = (r - 0.5) * pAmount;
double amountY = (g - 0.5) * pAmount;
pVarTP.x += amountX;
pVarTP.y += amountY;
}
break;
case MODE_SCALE: {
double intensity = calcIntensity(r, g, b) - bias;
if (intensity > 0.0) {
double scl = 1.0 + (intensity - 0.5) * pAmount;
pVarTP.x *= scl;
pVarTP.y *= scl;
}
}
break;
case MODE_SCISSOR: {
double amountX = (r - 0.5) * pAmount;
double amountY = (g - 0.5) * pAmount;
double newx = pVarTP.x * amountX * amountY + pVarTP.y * amountY;
double newy = pVarTP.x * amountY - pVarTP.y * amountX * amountY;
pVarTP.x = newx;
pVarTP.y = newy;
}
break;
case MODE_ROTATE:
default: {
double intensity = calcIntensity(r, g, b) - bias;
if (intensity > 0.0) {
double angle = intensity * M_2PI * pAmount;
double sina = sin(angle);
double cosa = cos(angle);
double xnew = pVarTP.x * cosa - pVarTP.y * sina;
double ynew = pVarTP.x * sina + pVarTP.y * cosa;
pVarTP.x = xnew;
pVarTP.y = ynew;
}
}
}
switch (colorMode) {
case COLOR_MODE_INHERIT: {
pVarTP.rgbColor = true;
pVarTP.redColor = r;
pVarTP.greenColor = g;
pVarTP.blueColor = b;
pVarTP.color = getColorIdx(r, g, b);
}
break;
}
}
private double calcIntensity(double red, double green, double blue) {
return (0.299 * red + 0.588 * green + 0.113 * blue);
}
private Map<RenderColor, Double> colorIdxMap = new HashMap<RenderColor, Double>();
private double getColorIdx(double pR, double pG, double pB) {
RenderColor pColor = new RenderColor(pR, pG, pB);
Double res = colorIdxMap.get(pColor);
if (res == null) {
int nearestIdx = 0;
RenderColor color = renderColors[0];
double dr, dg, db;
dr = (color.red - pR);
dg = (color.green - pG);
db = (color.blue - pB);
double nearestDist = sqrt(dr * dr + dg * dg + db * db);
for (int i = 1; i < renderColors.length; i++) {
color = renderColors[i];
dr = (color.red - pR);
dg = (color.green - pG);
db = (color.blue - pB);
double dist = sqrt(dr * dr + dg * dg + db * db);
if (dist < nearestDist) {
nearestDist = dist;
nearestIdx = i;
}
}
res = (double) nearestIdx / (double) (renderColors.length - 1);
colorIdxMap.put(pColor, res);
}
return res;
}
/*
private double getColorIdx(double pR, double pG, double pB) {
int nearestIdx = 0;
RenderColor color = renderColors[0];
double dr, dg, db;
dr = (color.red - pR);
dg = (color.green - pG);
db = (color.blue - pB);
// double nearestDist = sqrt(dr * dr + dg * dg + db * db);
double nearestDist = fabs(dr) + fabs(dg) + fabs(db);
for (int i = 1; i < renderColors.length; i++) {
color = renderColors[i];
dr = (color.red - pR);
dg = (color.green - pG);
db = (color.blue - pB);
// double dist = sqrt(dr * dr + dg * dg + db * db);
double dist = fabs(dr) + fabs(dg) + fabs(db);
if (dist < nearestDist) {
nearestDist = dist;
nearestIdx = i;
}
}
return (double) nearestIdx / (double) (renderColors.length - 1);
}
*/
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { mode, colorMode, bias, scaleX, scaleY, offsetX, offsetY, offsetZ, tileX, tileY };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_MODE.equalsIgnoreCase(pName))
mode = Tools.FTOI(pValue);
else if (PARAM_COLOR_MODE.equalsIgnoreCase(pName))
colorMode = Tools.FTOI(pValue);
else if (PARAM_BIAS.equalsIgnoreCase(pName))
bias = pValue;
else if (PARAM_SCALEX.equalsIgnoreCase(pName))
scaleX = pValue;
else if (PARAM_SCALEY.equalsIgnoreCase(pName))
scaleY = pValue;
else if (PARAM_OFFSETX.equalsIgnoreCase(pName))
offsetX = pValue;
else if (PARAM_OFFSETY.equalsIgnoreCase(pName))
offsetY = pValue;
else if (PARAM_OFFSETZ.equalsIgnoreCase(pName))
offsetZ = pValue;
else if (PARAM_TILEX.equalsIgnoreCase(pName))
tileX = Tools.FTOI(pValue);
else if (PARAM_TILEY.equalsIgnoreCase(pName))
tileY = Tools.FTOI(pValue);
else
throw new IllegalArgumentException(pName);
}
private WFImage colorMap;
private RenderColor[] renderColors;
@Override
public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
colorMap = null;
// renderColors = pContext.getFlameRenderer().getColorMap();
// TODO optimize
renderColors = pLayer.getPalette().createRenderPalette(pContext.getFlameRenderer().getFlame().getWhiteLevel());
if (inlinedImage != null) {
try {
colorMap = RessourceManager.getImage(inlinedImageHash, inlinedImage);
}
catch (Exception e) {
e.printStackTrace();
}
}
else if (imageFilename != null && imageFilename.length() > 0) {
try {
colorMap = RessourceManager.getImage(imageFilename);
}
catch (Exception e) {
e.printStackTrace();
}
}
if (colorMap == null) {
colorMap = getDfltImage();
}
imgWidth = colorMap.getImageWidth();
imgHeight = colorMap.getImageHeight();
}
private static SimpleImage dfltImage = null;
private synchronized SimpleImage getDfltImage() {
if (dfltImage == null) {
GradientCreator creator = new GradientCreator();
dfltImage = creator.createImage(256, 256);
}
return dfltImage;
}
@Override
public String[] getRessourceNames() {
return ressourceNames;
}
@Override
public byte[][] getRessourceValues() {
return new byte[][] { (imageFilename != null ? imageFilename.getBytes() : null), inlinedImage };
}
@Override
public void setRessource(String pName, byte[] pValue) {
if (RESSOURCE_IMAGE_FILENAME.equalsIgnoreCase(pName)) {
imageFilename = pValue != null ? new String(pValue) : "";
colorMap = null;
colorIdxMap.clear();
}
else if (RESSOURCE_INLINED_IMAGE.equalsIgnoreCase(pName)) {
inlinedImage = pValue;
inlinedImageHash = RessourceManager.calcHashCode(inlinedImage);
colorMap = null;
colorIdxMap.clear();
}
else
throw new IllegalArgumentException(pName);
}
@Override
public RessourceType getRessourceType(String pName) {
if (RESSOURCE_IMAGE_FILENAME.equalsIgnoreCase(pName)) {
return RessourceType.IMAGE_FILENAME;
}
else if (RESSOURCE_INLINED_IMAGE.equalsIgnoreCase(pName)) {
return RessourceType.IMAGE_FILE;
}
else
throw new IllegalArgumentException(pName);
}
}