/* JWildfire - an image and animation processor written in Java Copyright (C) 1995-2011 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.transform; import org.jwildfire.base.Property; import org.jwildfire.base.PropertyMax; import org.jwildfire.base.PropertyMin; import org.jwildfire.base.Tools; import org.jwildfire.image.Pixel; import org.jwildfire.image.SimpleImage; import org.jwildfire.image.WFImage; import com.l2fprod.common.beans.editor.ComboBoxPropertyEditor; public class WindTransformer extends Mesh2DTransformer { public enum Direction { LEFT, RIGHT } @Property(description = "Probability of the effect") @PropertyMin(0) @PropertyMax(100) private int probability = 20; @Property(description = "Seed for the random number generator") @PropertyMin(0) private int seed = 123; @Property(description = "Intensity of the effect") private int intensity = 40; @Property(description = "Wind direction", editorClass = DirectionEditor.class) private Direction direction = Direction.RIGHT; @Override protected void performPixelTransformation(WFImage pImg) { SimpleImage img = (SimpleImage) pImg; int width = pImg.getImageWidth(); int height = pImg.getImageHeight(); int contrast = 0; short smask[][] = new short[height][width]; int rs = 2990; int gs = 5880; int bs = 1130; rs = (rs * Tools.VPREC) / 10000; gs = (gs * Tools.VPREC) / 10000; bs = (bs * Tools.VPREC) / 10000; Pixel p0 = new Pixel(); Pixel p1 = new Pixel(); Tools.srand123(this.seed); /* flip the image */ if (direction == Direction.LEFT) { srcImg = srcImg.clone(); FlipTransformer ft = new FlipTransformer(); ft.setAxis(FlipTransformer.Axis.X); ft.transformImage(srcImg); ft.setAxis(FlipTransformer.Axis.X); ft.transformImage(img); } /** 1st line **/ for (int i = 0; i < width; i++) smask[0][i] = 1; /** lines 2...(height-2) **/ for (int i = 1; i < (height - 1); i++) { /* 1st pixel */ smask[i][0] = 1; /* process pixels 2..width-1 */ for (int j = 1; j < (width - 1); j++) { int dd = 0, int1; p1.setARGBValue(srcImg.getARGBValue(j - 1, i - 1)); int1 = (rs * p1.r + gs * p1.g + bs * p1.b) >> Tools.SPREC; dd += int1; p1.setARGBValue(srcImg.getARGBValue(j + 1, i - 1)); int1 = (rs * p1.r + gs * p1.g + bs * p1.b) >> Tools.SPREC; dd -= int1; p1.setARGBValue(srcImg.getARGBValue(j - 1, i)); int1 = (rs * p1.r + gs * p1.g + bs * p1.b) >> Tools.SPREC; dd += int1; p1.setARGBValue(srcImg.getARGBValue(j + 1, i)); int1 = (rs * p1.r + gs * p1.g + bs * p1.b) >> Tools.SPREC; dd -= int1; p1.setARGBValue(srcImg.getARGBValue(j - 1, i + 1)); int1 = (rs * p1.r + gs * p1.g + bs * p1.b) >> Tools.SPREC; dd += int1; p1.setARGBValue(srcImg.getARGBValue(j + 1, i + 1)); int1 = (rs * p1.r + gs * p1.g + bs * p1.b) >> Tools.SPREC; dd -= int1; if (dd < contrast) { smask[i][j] = 1; } } /* last pixel */ smask[i][width - 1] = 1; } /** last line **/ for (int i = 0; i < width; i++) smask[height - 1][i] = 1; /** accept only "thick" lines **/ for (int i = 1; i < (height - 1); i++) { for (int j = 1; j < (width - 1); j++) { if (smask[i][j] == 0) { if (smask[i][j + 1] != 0) smask[i][j] = 1; } } } /** create "thin" lines **/ for (int i = 1; i < (height - 1); i++) { nextLine: for (int j = (width - 2); j >= 1; j--) { if (smask[i][j] == 0) { int k = j - 1; while (smask[i][k] == 0) { smask[i][k] = 1; k--; if (k < 1) continue nextLine; } } } } /** remove isolated pixels **/ for (int i = 1; i < (height - 1); i++) { for (int j = 1; j < (width - 1); j++) { if (smask[i][j] == 0) { int dd = 0; dd += smask[i][j - 1]; dd += smask[i][j + 1]; dd += smask[i - 1][j]; dd += smask[i + 1][j]; dd += smask[i + 1][j - 1]; dd += smask[i + 1][j + 1]; dd += smask[i - 1][j - 1]; dd += smask[i - 1][j + 1]; if (dd == 8) smask[i][j] = 1; } } } /** apply the changes **/ int intensity0 = this.intensity; int intensity1 = intensity0 / 2; { double rprob = (double) ((double) 1.0 - (double) this.probability / (double) 100.0); for (int i = 0; i < height; i++) { cont: for (int j = (width - 1); j >= 0; j--) { if ((smask[i][j] == 0) && (Tools.drand() >= rprob)) { if (j < 1) { continue cont; } int intensity = intensity0 + (int) (Tools.drand() * intensity1 + 0.5); double dm = 100.0 / (double) (intensity - 1); int kmax = intensity; if ((j + kmax) >= width) kmax = width - j; for (int k = 0; k < kmax; k++) { int mix = (int) (100.0 - (double) k * dm + 0.5); int m1 = 100 - mix; int m2 = mix; p0.setARGBValue(srcImg.getARGBValue(j, i)); p1.setARGBValue(srcImg.getARGBValue(j + k, i)); int rv = ((int) ((int) p1.r * m1) + (int) p0.r * m2) / (int) 100; int gv = ((int) ((int) p1.g * m1) + (int) p0.g * m2) / (int) 100; int bv = ((int) ((int) p1.b * m1) + (int) p0.b * m2) / (int) 100; img.setRGB(j + k, i, rv, gv, bv); } } } } } /* flip the image */ if (direction == Direction.LEFT) { FlipTransformer ft = new FlipTransformer(); ft.setAxis(FlipTransformer.Axis.X); ft.transformImage(img); } } @Override public void initDefaultParams(WFImage pImg) { direction = Direction.RIGHT; intensity = 40; probability = 20; seed = 123; } public static class DirectionEditor extends ComboBoxPropertyEditor { public DirectionEditor() { super(); setAvailableValues(new Direction[] { Direction.LEFT, Direction.RIGHT }); } } public int getProbability() { return probability; } public void setProbability(int probability) { this.probability = probability; } public int getSeed() { return seed; } public void setSeed(int seed) { this.seed = seed; } public int getIntensity() { return intensity; } public void setIntensity(int intensity) { this.intensity = intensity; } public Direction getDirection() { return direction; } public void setDirection(Direction direction) { this.direction = direction; } }