/*
JWildfire - an image and animation processor written in Java
Copyright (C) 1995-2016 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.render.postdof;
import java.util.ArrayList;
import java.util.List;
import org.jwildfire.base.Tools;
import org.jwildfire.base.mathlib.MathLib;
import org.jwildfire.create.tina.base.Flame;
import org.jwildfire.create.tina.random.AbstractRandomGenerator;
import org.jwildfire.create.tina.random.MarsagliaRandomGenerator;
import org.jwildfire.create.tina.render.filter.FilterKernel;
public class PostDOFCalculator {
private final PostDOFBuffer buffer;
private final double imgSize;
private final FilterKernel bokehKernel;
private final List<PostDOFFilteredSample> filteredSamples;
private final double kernelScale;
private final double bokehIntensity;
private final double bokehBrightness;
private final double bokehSize;
private final double bokehActivation;
private final AbstractRandomGenerator randGen;
public PostDOFCalculator(PostDOFBuffer buffer, Flame flame) {
super();
this.buffer = buffer;
imgSize = MathLib.sqrt(MathLib.sqr(buffer.getWidth()) + MathLib.sqr(buffer.getHeight()));
bokehKernel = flame.getSolidRenderSettings().getPostBokehFilterKernel().createFilterInstance();
bokehIntensity = flame.getSolidRenderSettings().getPostBokehIntensity() * 1000.0 / imgSize;
bokehBrightness = flame.getSolidRenderSettings().getPostBokehBrightness();
bokehSize = flame.getSolidRenderSettings().getPostBokehSize();
bokehActivation = flame.getSolidRenderSettings().getPostBokehActivation();
randGen = new MarsagliaRandomGenerator();
filteredSamples = new ArrayList<>();
kernelScale = 1.0;
}
public void addSample(int x, int y, float r, float g, float b, double dofDist, double z) {
if (r > 0 || g > 0 || b > 0) {
PostDOFSample sample = new PostDOFSample(x, y, (float) z, (float) dofDist, r, g, b);
processSample(sample);
}
}
private void processSample(PostDOFSample sample) {
double plainRadius = MathLib.fabs(sample.getDofDist()) * 10.0;
double radius = Tools.FTOI(MathLib.fabs(sample.getDofDist()) * 10.0);
if (radius < 2.0) {
radius = 2.0;
}
if (radius > 0.0) {
filteredSamples.clear();
if (randGen.random() > 1.0 - bokehIntensity && calcBrightness(sample.getR(), sample.getG(), sample.getB()) >= bokehActivation) {
double intensity = (randGen.random() + 1.0) / 5.0;
sample.setR(Tools.FTOI(sample.getR() * intensity * radius * radius * bokehBrightness));
sample.setG(Tools.FTOI(sample.getG() * intensity * radius * radius * bokehBrightness));
sample.setB(Tools.FTOI(sample.getB() * intensity * radius * radius * bokehBrightness));
radius *= bokehSize * (1.0 + randGen.random() * bokehSize);
}
else {
radius *= (1.0 + (0.5 - randGen.random()) * 0.1);
}
double scaledInvRadius = 1.0 / plainRadius * kernelScale * bokehKernel.getSpatialSupport();
double intensitySum = 0.0;
// double stepSize = (radius < 5.0) ? 0.5 : 1.0;
double stepSize;
if (radius < 2.0) {
stepSize = 0.0625;
}
else if (radius < 3.0) {
stepSize = 0.125;
}
else if (radius < 4.0) {
stepSize = 0.25;
}
else if (radius < 5.0) {
stepSize = 0.375;
}
else if (radius < 6.0) {
stepSize = 0.5;
}
else if (radius < 7.0) {
stepSize = 0.625;
}
else if (radius < 8.0) {
stepSize = 0.75;
}
else if (radius < 9.0) {
stepSize = 0.875;
}
else {
stepSize = 1.0;
}
stepSize *= 0.5;
int maxSteps = (int) imgSize;
if ((radius / stepSize) > maxSteps) {
stepSize = radius / (double) maxSteps;
}
for (double i = -radius; i < radius + MathLib.EPSILON; i += stepSize) {
double i_square = MathLib.sqr(i * scaledInvRadius);
double dstX = sample.getX() + i;
for (double j = -radius; j < radius + MathLib.EPSILON; j += stepSize) {
double dstY = sample.getY() + j;
double r = MathLib.sqrt(i_square + MathLib.sqr(j * scaledInvRadius));
double intensity = bokehKernel.getFilterCoeff(r);
if (intensity > MathLib.EPSILON) {
intensitySum += intensity;
filteredSamples.add(new PostDOFFilteredSample(Tools.FTOI(dstX), Tools.FTOI(dstY), sample, intensity));
}
}
}
if (filteredSamples.size() != 0) {
buffer.addSamples(filteredSamples, intensitySum);
filteredSamples.clear();
}
else {
filteredSamples.add(new PostDOFFilteredSample(sample.getX(), sample.getY(), sample, 1.0));
buffer.addSamples(filteredSamples, 1.0);
filteredSamples.clear();
}
}
else {
filteredSamples.add(new PostDOFFilteredSample(sample.getX(), sample.getY(), sample, 1.0));
buffer.addSamples(filteredSamples, 1.0);
filteredSamples.clear();
}
}
private double calcBrightness(float r, float g, float b) {
return r * 0.2990 / 255.0 + g * 0.5880 / 255.0 + b * 0.1130 / 255.0;
}
}