/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2015 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.image; import org.jwildfire.base.Prefs; import org.jwildfire.base.Tools; import org.jwildfire.base.mathlib.MathLib; import org.jwildfire.image.Pixel; import org.jwildfire.image.SimpleImage; public class PostFilterImageThread extends AbstractImageRenderThread { private final int startRow, endRow; private final SimpleImage input, output; private final boolean showHits; private final Pixel toolPixel1 = new Pixel(); private final Pixel toolPixel2 = new Pixel(); private final double threshold; int hits = 0; public static class PixelValue { public int argb; public double luminosity; public PixelValue(int pArgb, double pLuminosity) { argb = pArgb; luminosity = pLuminosity; } } public PostFilterImageThread(int pStartRow, int pEndRow, SimpleImage pInput, SimpleImage pOutput, double pThreshold) { input = pInput; output = pOutput; startRow = pStartRow; endRow = pEndRow; threshold = -MathLib.log(pThreshold) / 2.0; showHits = Prefs.getPrefs().isDevelopmentMode(); } @Override public void run() { setDone(false); try { doFilter3(); } finally { setDone(true); } } private void doFilter3() { hits = 0; for (int y = startRow; y < endRow; y++) { for (int x = 0; x < input.getImageWidth(); x++) { PixelValue pCenter = getPixel(input, toolPixel1, x, y); PixelValue pLeft = getPixel(input, toolPixel1, x - 1, y); PixelValue pRight = getPixel(input, toolPixel1, x + 1, y); PixelValue pUp = getPixel(input, toolPixel1, x, y - 1); PixelValue pDown = getPixel(input, toolPixel1, x, y + 1); boolean left = hasHighContrast(pCenter, pLeft); boolean right = hasHighContrast(pCenter, pRight); boolean up = hasHighContrast(pCenter, pUp); boolean down = hasHighContrast(pCenter, pDown); if ((left && right && (up || down)) || (up && down || (left || right))) { hits++; if (showHits) { output.setARGB(x, y, 255, 0, 255, 0); if (x < input.getImageWidth() - 1) output.setARGB(x + 1, y, 255, 0, 255, 0); if (x > 0) output.setARGB(x - 1, y, 255, 0, 255, 0); if (y < input.getImageHeight() - 1) output.setARGB(x, y + 1, 255, 0, 255, 0); if (y > 0) output.setARGB(x, y - 1, 255, 0, 255, 0); } else { doGaussian8(y, x, pCenter, pLeft, pRight, pUp, pDown); } } } } } private boolean hasHighContrast(PixelValue pA, PixelValue pB) { toolPixel1.setARGBValue(pA.argb); toolPixel2.setARGBValue(pB.argb); double dist = MathLib.sqrt(MathLib.sqr(toolPixel1.r - toolPixel2.r) + MathLib.sqr(toolPixel1.g - toolPixel2.g) + MathLib.sqr(toolPixel1.b - toolPixel2.b)) / 255.0; return dist > threshold; // return MathLib.fabs(pA.luminosity - pB.luminosity) > threshold; } // public void doFilter4() { // hits = 0; // for (int y = startRow; y < endRow; y++) { // for (int x = 0; x < input.getImageWidth(); x++) { // PixelValue pCenter = getPixel(input, toolPixel1, x, y); // PixelValue pLeft = getPixel(input, toolPixel1, x - 1, y); // if (hasHighContrast(pCenter, pLeft)) { // PixelValue pRight = getPixel(input, toolPixel1, x + 1, y); // if (hasHighContrast(pCenter, pRight)) { // PixelValue pUp = getPixel(input, toolPixel1, x, y - 1); // if (hasHighContrast(pCenter, pUp)) { // PixelValue pDown = getPixel(input, toolPixel1, x, y + 1); // if (hasHighContrast(pCenter, pDown)) { // hits++; // if (showHits) { // output.setARGB(x, y, 255, 0, 255, 0); // if (x < input.getImageWidth() - 1) // output.setARGB(x + 1, y, 255, 0, 255, 0); // if (x > 0) // output.setARGB(x - 1, y, 255, 0, 255, 0); // if (y < input.getImageHeight() - 1) // output.setARGB(x, y + 1, 255, 0, 255, 0); // if (y > 0) // output.setARGB(x, y - 1, 255, 0, 255, 0); // } // else { // doMedian4(y, x, pCenter, pLeft, pRight, pUp, pDown); // } // } // } // } // } // // } // } // } // private void doMedian8(int y, int x, PixelValue pCenter, PixelValue pLeft, PixelValue pRight, PixelValue pUp, PixelValue pDown) { // List<PixelValue> pixels = new ArrayList<PixelValue>(); // pixels.add(pCenter); // pixels.add(pLeft); // pixels.add(pRight); // pixels.add(pUp); // pixels.add(pDown); // pixels.add(getPixel(input, toolPixel1, x + 1, y + 1)); // pixels.add(getPixel(input, toolPixel1, x + 1, y - 1)); // pixels.add(getPixel(input, toolPixel1, x - 1, y + 1)); // pixels.add(getPixel(input, toolPixel1, x - 1, y - 1)); // Collections.sort(pixels, new Comparator<PixelValue>() { // // @Override // public int compare(PixelValue o1, PixelValue o2) { // if (o1.luminosity > o2.luminosity) { // return 1; // } // else if (o1.luminosity < o2.luminosity) { // return -1; // } // else { // return 0; // } // } // }); // PixelValue newCentre = pixels.get(pixels.size() / 2); // output.setARGB(x, y, newCentre.argb); // } private void doGaussian8(int y, int x, PixelValue pCenter, PixelValue pLeft, PixelValue pRight, PixelValue pUp, PixelValue pDown) { double w0 = 0.32; double w1 = 0.11; double w2 = 0.06; PixelValue p1 = getPixel(input, toolPixel1, x + 1, y + 1); PixelValue p2 = getPixel(input, toolPixel1, x + 1, y - 1); PixelValue p3 = getPixel(input, toolPixel1, x - 1, y + 1); PixelValue p4 = getPixel(input, toolPixel1, x - 1, y - 1); double r = toolPixel1.setARGBValue(pCenter.argb).getR() * w0 + (toolPixel1.setARGBValue(pLeft.argb).getR() + toolPixel1.setARGBValue(pRight.argb).getR() + toolPixel1.setARGBValue(pUp.argb).getR() + toolPixel1.setARGBValue(pDown.argb).getR()) * w1 + (toolPixel1.setARGBValue(p1.argb).getR() + toolPixel1.setARGBValue(p2.argb).getR() + toolPixel1.setARGBValue(p3.argb).getR() + toolPixel1.setARGBValue(p4.argb).getR()) * w2; double g = toolPixel1.setARGBValue(pCenter.argb).getG() * w0 + (toolPixel1.setARGBValue(pLeft.argb).getG() + toolPixel1.setARGBValue(pRight.argb).getG() + toolPixel1.setARGBValue(pUp.argb).getG() + toolPixel1.setARGBValue(pDown.argb).getG()) * w1 + (toolPixel1.setARGBValue(p1.argb).getG() + toolPixel1.setARGBValue(p2.argb).getG() + toolPixel1.setARGBValue(p3.argb).getG() + toolPixel1.setARGBValue(p4.argb).getG()) * w2; double b = toolPixel1.setARGBValue(pCenter.argb).getR() * w0 + (toolPixel1.setARGBValue(pLeft.argb).getB() + toolPixel1.setARGBValue(pRight.argb).getB() + toolPixel1.setARGBValue(pUp.argb).getB() + toolPixel1.setARGBValue(pDown.argb).getB()) * w1 + (toolPixel1.setARGBValue(p1.argb).getB() + toolPixel1.setARGBValue(p2.argb).getB() + toolPixel1.setARGBValue(p3.argb).getB() + toolPixel1.setARGBValue(p4.argb).getB()) * w2; toolPixel1.setARGB(255, Tools.roundColor(r), Tools.roundColor(g), Tools.roundColor(b)); output.setARGB(x, y, toolPixel1.getARGBValue()); } private PixelValue getPixel(SimpleImage img, Pixel rgbPixel, int x, int y) { int argb = img.getARGBValueIgnoreBounds(x, y); rgbPixel.setARGBValue(argb); return new PixelValue(argb, (rgbPixel.r * 0.299 + rgbPixel.g * 0.5880 + rgbPixel.b * 0.1130) / 255.0); } }