/*
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.HashMap;
import java.util.Map;
import org.jwildfire.base.Tools;
import org.jwildfire.base.mathlib.GfxMathLib;
import org.jwildfire.base.mathlib.MathLib;
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_DC_COLOR = "dc_color";
private static final String PARAM_BLEND_COLORMAP = "blend_colormap";
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 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_DC_COLOR, PARAM_BLEND_COLORMAP, PARAM_IS_SEQUENCE, PARAM_SEQUENCE_START, PARAM_SEQUENCE_DIGITS };
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 int dc_color = 1;
private int blend_colormap = 0;
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;
// 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 - 2);
double y = (pInputY - (offsetY + 0.5) + 1.0) / scaleY * (double) (imgHeight - 2);
int ix, iy;
if (blend_colormap > 0) {
ix = (int) MathLib.trunc(x);
iy = (int) MathLib.trunc(y);
}
else {
ix = Tools.FTOI(x);
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) {
if (blend_colormap > 0) {
double iufrac = MathLib.frac(x);
double ivfrac = MathLib.frac(y);
toolPixel.setARGBValue(((SimpleImage) colorMap).getARGBValue(
ix, iy));
int lur = toolPixel.r;
int lug = toolPixel.g;
int lub = toolPixel.b;
toolPixel.setARGBValue(((SimpleImage) colorMap).getARGBValue(
ix + 1, iy));
int rur = toolPixel.r;
int rug = toolPixel.g;
int rub = toolPixel.b;
toolPixel.setARGBValue(((SimpleImage) colorMap).getARGBValue(
ix, iy + 1));
int lbr = toolPixel.r;
int lbg = toolPixel.g;
int lbb = toolPixel.b;
toolPixel.setARGBValue(((SimpleImage) colorMap).getARGBValue(
ix + 1, iy + 1));
int rbr = toolPixel.r;
int rbg = toolPixel.g;
int rbb = toolPixel.b;
r = GfxMathLib.blerp(lur, rur, lbr, rbr, iufrac, ivfrac);
g = GfxMathLib.blerp(lug, rug, lbg, rbg, iufrac, ivfrac);
b = GfxMathLib.blerp(lub, rub, lbb, rbb, iufrac, ivfrac);
}
else {
toolPixel.setARGBValue(((SimpleImage) colorMap).getARGBValue(
ix, iy));
r = toolPixel.r;
g = toolPixel.g;
b = toolPixel.b;
}
if (dc_color > 0) {
pVarTP.rgbColor = true;
pVarTP.redColor = r;
pVarTP.greenColor = g;
pVarTP.blueColor = b;
}
}
else {
if (blend_colormap > 0) {
double iufrac = MathLib.frac(x);
double ivfrac = MathLib.frac(y);
((SimpleHDRImage) colorMap).getRGBValues(rgbArray, ix, iy);
double lur = rgbArray[0];
double lug = rgbArray[1];
double lub = rgbArray[2];
((SimpleHDRImage) colorMap).getRGBValues(rgbArray, ix + 1, iy);
double rur = rgbArray[0];
double rug = rgbArray[1];
double rub = rgbArray[2];
((SimpleHDRImage) colorMap).getRGBValues(rgbArray, ix, iy + 1);
double lbr = rgbArray[0];
double lbg = rgbArray[1];
double lbb = rgbArray[2];
((SimpleHDRImage) colorMap).getRGBValues(rgbArray, ix + 1, iy + 1);
double rbr = rgbArray[0];
double rbg = rgbArray[1];
double rbb = rgbArray[2];
r = GfxMathLib.blerp(lur, rur, lbr, rbr, iufrac, ivfrac);
g = GfxMathLib.blerp(lug, rug, lbg, rbg, iufrac, ivfrac);
b = GfxMathLib.blerp(lub, rub, lbb, rbb, iufrac, ivfrac);
}
else {
((SimpleHDRImage) colorMap).getRGBValues(rgbArray, ix, iy);
r = rgbArray[0];
g = rgbArray[1];
b = rgbArray[2];
}
if (dc_color > 0) {
pVarTP.rgbColor = true;
pVarTP.redColor = r;
pVarTP.greenColor = g;
pVarTP.blueColor = b;
}
}
}
else {
r = g = b = 0.0;
if (dc_color > 0) {
pVarTP.rgbColor = true;
pVarTP.redColor = r;
pVarTP.greenColor = g;
pVarTP.blueColor = b;
}
}
double dz = this.offsetZ;
if (fabs(scaleZ) > EPSILON) {
double intensity = (0.299 * r + 0.588 * g + 0.113 * b) / 255.0;
dz += scaleZ * intensity;
}
if (resetZ != 0) {
pVarTP.z = dz;
}
else {
pVarTP.z += dz;
}
if (dc_color > 0) {
pVarTP.color = getColorIdx(Tools.FTOI(r), Tools.FTOI(g), Tools.FTOI(b));
}
}
@Override
public String[] getParameterNames() {
return paramNames;
}
@Override
public Object[] getParameterValues() {
return new Object[] { scaleX, scaleY, scaleZ, offsetX, offsetY, offsetZ, tileX, tileY, resetZ, dc_color, blend_colormap, is_sequence, sequence_start, sequence_digits };
}
@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_DC_COLOR.equalsIgnoreCase(pName))
dc_color = Tools.FTOI(pValue);
else if (PARAM_BLEND_COLORMAP.equalsIgnoreCase(pName))
blend_colormap = 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
throw new IllegalArgumentException(pName);
}
private WFImage colorMap;
private RenderColor[] renderColors;
private Map<RenderColor, Double> colorIdxMap = new HashMap<RenderColor, Double>();
private double getColorIdx(int pR, int pG, int 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;
}
@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);
}
}