/*
* Copyright 2017 Laszlo Balazs-Csiki
*
* This file is part of Pixelitor. Pixelitor is free software: you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License, version 3 as published by the Free
* Software Foundation.
*
* Pixelitor 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Pixelitor. If not, see <http://www.gnu.org/licenses/>.
*/
package pixelitor.filters;
import net.jafama.FastMath;
import pixelitor.filters.gui.ParamSet;
import pixelitor.filters.gui.RangeParam;
import pixelitor.filters.gui.ShowOriginal;
import pixelitor.utils.BasicProgressTracker;
import pixelitor.utils.ImageUtils;
import pixelitor.utils.ProgressTracker;
import pixelitor.utils.ReseedSupport;
import java.awt.AlphaComposite;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Random;
import static java.lang.Math.PI;
/**
* "Drunk Vision" filter inspired by "Fragment Blur" in paint.net
*/
public class DrunkVision extends FilterWithParametrizedGUI {
public static final String NAME = "Drunk Vision";
private final RangeParam drunkenness = new RangeParam("Drunkenness", 0, 20, 100);
private final RangeParam numEyes = new RangeParam("Number of Eyes", 2, 5, 42);
public DrunkVision() {
super(ShowOriginal.YES);
setParamSet(new ParamSet(
drunkenness,
numEyes
).withAction(ReseedSupport.createAction()));
}
@Override
public BufferedImage doTransform(BufferedImage src, BufferedImage dest) {
if (drunkenness.getValue() == 0) {
return src;
}
int numShiftedImages = numEyes.getValue() - 1;
ProgressTracker pt = new BasicProgressTracker(NAME, numShiftedImages);
dest = ImageUtils.copyImage(src);
Graphics2D g = dest.createGraphics();
ReseedSupport.reInitialize();
Random rand = ReseedSupport.getRand();
int maxDistance = (int) (drunkenness.getValueAsPercentage() * 0.2 * (src.getWidth() + src.getHeight()));
Point[] transformPoints = generateTransforms(numShiftedImages, maxDistance, rand);
for (int i = 0; i < numShiftedImages; i++) {
AffineTransform transform = AffineTransform.getTranslateInstance(transformPoints[i].x, transformPoints[i].y);
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f / (i + 2)));
g.drawImage(src, transform, null);
pt.unitDone();
}
pt.finish();
g.dispose();
return dest;
}
// generates the transforms for the images that are shifted
// from the original
private static Point[] generateTransforms(int numImages, int maxDistance, Random rand) {
Point[] retVal = new Point[numImages];
int i = 0;
double firstPointAngle = 0;
while (i < numImages) {
if (i == 0) {
// put it somewhere horizontal at distance maxDist
double r = rand.nextDouble();
if (r < 0.5) { // to the right
firstPointAngle = -0.25 + r;
} else { // to the left
firstPointAngle = PI - 0.75 + r;
}
retVal[i] = pointFormPolar(firstPointAngle, maxDistance);
} else if (i == 1) {
// put it more or less opposing the first point
double rangeStart = firstPointAngle + 3 * PI / 4;
double angle = rangeStart + PI * rand.nextDouble() / 2;
retVal[i] = pointFormPolar(angle, maxDistance);
} else if (i < 4) {
double rangeStart;
if (i == 2) {
rangeStart = firstPointAngle + PI / 4;
} else { // i == 3
rangeStart = firstPointAngle + 5 * PI / 4;
}
double angle = rangeStart + PI * rand.nextDouble() / 2;
retVal[i] = pointFormPolar(angle, maxDistance);
} else {
double randomAngle = rand.nextDouble() * PI * 2;
double minDistance = maxDistance / (double) i;
double distance = minDistance + (maxDistance - minDistance) * rand.nextDouble();
int shiftX = (int) (distance * FastMath.cos(randomAngle));
int shiftY = (int) (distance * FastMath.sin(randomAngle));
retVal[i] = new Point(shiftX, shiftY);
}
i++;
}
return retVal;
}
private static Point pointFormPolar(double angle, double dist) {
int x = (int) (dist * FastMath.cos(angle));
int y = (int) (dist * FastMath.sin(angle));
return new Point(x, y);
}
}