/*
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.EPSILON;
import static org.jwildfire.base.mathlib.MathLib.fabs;
import static org.jwildfire.base.mathlib.MathLib.sqrt;
import java.util.Map;
import java.util.WeakHashMap;
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 AbstractColorMapWFFunc extends VariationFunc {
private static final long serialVersionUID = 1L;
public static final String PARAM_SCALEX = "scale_x";
public static final String PARAM_SCALEY = "scale_y";
private static final String PARAM_SCALEZ = "scale_z";
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 PARAM_RESETZ = "reset_z";
private static final String PARAM_IS_SEQUENCE = "is_sequence";
private static final String PARAM_SEQUENCE_START = "sequence_start";
private static final String PARAM_SEQUENCE_DIGITS = "sequence_digits";
private static final String PARAM_ALPHA_MODE = "alpha_mode";
private static final String RESSOURCE_IMAGE_FILENAME = "image_filename";
public static final String RESSOURCE_INLINED_IMAGE = "inlined_image";
public static final String RESSOURCE_IMAGE_SRC = "image_src";
public static final String RESSOURCE_IMAGE_DESC_SRC = "image_desc_src";
private static final String[] paramNames = { PARAM_SCALEX, PARAM_SCALEY, PARAM_SCALEZ, PARAM_OFFSETX, PARAM_OFFSETY, PARAM_OFFSETZ, PARAM_TILEX, PARAM_TILEY, PARAM_RESETZ, PARAM_IS_SEQUENCE, PARAM_SEQUENCE_START, PARAM_SEQUENCE_DIGITS, PARAM_ALPHA_MODE };
private static final String[] ressourceNames = { RESSOURCE_IMAGE_FILENAME, RESSOURCE_INLINED_IMAGE, RESSOURCE_IMAGE_DESC_SRC, RESSOURCE_IMAGE_SRC };
private double scaleX = 1.0;
private double scaleY = 1.0;
private double scaleZ = 0.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 int resetZ = 1;
private String imageFilename = null;
private byte[] inlinedImage = null;
private String imageDescSrc = null;
private String imageSrc = null;
private int inlinedImageHash = 0;
private int is_sequence = 0;
private int sequence_start = 1;
private int sequence_digits = 4;
private int alpha_mode = 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;
}
}
if (ix >= 0 && ix < imgWidth && iy >= 0 && iy < imgHeight) {
if (colorMap instanceof SimpleImage) {
toolPixel.setARGBValue(((SimpleImage) colorMap).getARGBValue(
ix, iy));
if (alpha_mode == 1) {
pVarTP.withAlpha = true;
pVarTP.alpha = (double) toolPixel.r / 255.0 * 10.0;
}
else {
pVarTP.rgbColor = true;
pVarTP.redColor = toolPixel.r;
pVarTP.greenColor = toolPixel.g;
pVarTP.blueColor = toolPixel.b;
}
}
else {
((SimpleHDRImage) colorMap).getRGBValues(rgbArray, ix, iy);
if (alpha_mode == 1) {
pVarTP.withAlpha = true;
pVarTP.alpha = rgbArray[0] / 255.0 * 10.0;
}
else {
pVarTP.rgbColor = true;
pVarTP.redColor = rgbArray[0];
pVarTP.greenColor = rgbArray[0];
pVarTP.blueColor = rgbArray[0];
}
}
}
else {
if (alpha_mode == 1) {
pVarTP.withAlpha = true;
pVarTP.alpha = 0;
}
else {
pVarTP.rgbColor = true;
pVarTP.redColor = 0;
pVarTP.greenColor = 0;
pVarTP.blueColor = 0;
}
}
double dz = this.offsetZ;
if (fabs(scaleZ) > EPSILON) {
double intensity = (0.299 * pVarTP.redColor + 0.588 * pVarTP.greenColor + 0.113 * pVarTP.blueColor) / 255.0;
dz += scaleZ * intensity;
}
if (resetZ != 0) {
pVarTP.z = dz;
}
else {
pVarTP.z += dz;
}
pVarTP.color = getColorIdx(pVarTP.redColor, pVarTP.greenColor, pVarTP.blueColor);
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { scaleX, scaleY, scaleZ, offsetX, offsetY, offsetZ, tileX, tileY, resetZ, is_sequence, sequence_start, sequence_digits, alpha_mode };
}
@Override
public void setParameter(String pName, double pValue) {
if (PARAM_SCALEX.equalsIgnoreCase(pName))
scaleX = pValue;
else if (PARAM_SCALEY.equalsIgnoreCase(pName))
scaleY = pValue;
else if (PARAM_SCALEZ.equalsIgnoreCase(pName))
scaleZ = 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 if (PARAM_RESETZ.equalsIgnoreCase(pName))
resetZ = Tools.FTOI(pValue);
else if (PARAM_IS_SEQUENCE.equalsIgnoreCase(pName)) {
is_sequence = Tools.FTOI(pValue);
clearCurrColorMap();
}
else if (PARAM_SEQUENCE_START.equalsIgnoreCase(pName)) {
sequence_start = Tools.FTOI(pValue);
clearCurrColorMap();
}
else if (PARAM_SEQUENCE_DIGITS.equalsIgnoreCase(pName)) {
sequence_digits = Tools.FTOI(pValue);
clearCurrColorMap();
}
else if (PARAM_ALPHA_MODE.equalsIgnoreCase(pName))
alpha_mode = limitIntVal(Tools.FTOI(pValue), 0, 1);
else
throw new IllegalArgumentException(pName);
}
private WFImage colorMap;
private RenderColor[] renderColors;
private Map<RenderColor, Double> colorIdxMap = new WeakHashMap<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);
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;
}
}
return (double) nearestIdx / (double) (renderColors.length - 1);
}
*/
@Override
public void init(FlameTransformationContext pContext, Layer pLayer, XForm pXForm, double pAmount) {
colorMap = null;
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(getCurrImageFilename(pContext));
}
catch (Exception e) {
e.printStackTrace();
}
}
if (colorMap == null) {
colorMap = getDfltImage();
}
imgWidth = colorMap.getImageWidth();
imgHeight = colorMap.getImageHeight();
}
private String getCurrImageFilename(FlameTransformationContext pContext) {
if (is_sequence > 0) {
int frame = pContext.getFrame() - 1 + sequence_start;
String baseFilename;
String fileExt;
int p = imageFilename.lastIndexOf(".");
if (p < 0 || p <= sequence_digits || p == imageFilename.length() - 1)
return imageFilename;
baseFilename = imageFilename.substring(0, p - sequence_digits);
fileExt = imageFilename.substring(p, imageFilename.length());
String number = String.valueOf(frame);
while (number.length() < sequence_digits) {
number = "0" + number;
}
return baseFilename + number + fileExt;
}
else {
return imageFilename;
}
}
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, (imageDescSrc != null ? imageDescSrc.getBytes() : null), (imageSrc != null ? imageSrc.getBytes() : null) };
}
@Override
public void setRessource(String pName, byte[] pValue) {
if (RESSOURCE_IMAGE_FILENAME.equalsIgnoreCase(pName)) {
imageFilename = pValue != null ? new String(pValue) : "";
clearCurrColorMap();
}
else if (RESSOURCE_INLINED_IMAGE.equalsIgnoreCase(pName)) {
inlinedImage = pValue;
inlinedImageHash = RessourceManager.calcHashCode(inlinedImage);
clearCurrColorMap();
}
else if (RESSOURCE_IMAGE_DESC_SRC.equalsIgnoreCase(pName)) {
imageDescSrc = pValue != null ? new String(pValue) : "";
}
else if (RESSOURCE_IMAGE_SRC.equalsIgnoreCase(pName)) {
imageSrc = pValue != null ? new String(pValue) : "";
}
else
throw new IllegalArgumentException(pName);
}
private void clearCurrColorMap() {
colorMap = null;
colorIdxMap.clear();
}
@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 if (RESSOURCE_IMAGE_DESC_SRC.equalsIgnoreCase(pName)) {
return RessourceType.HREF;
}
else if (RESSOURCE_IMAGE_SRC.equalsIgnoreCase(pName)) {
return RessourceType.HREF;
}
else
throw new IllegalArgumentException(pName);
}
}