/* 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.create; import java.awt.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; import java.util.Random; 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.swing.Buffer; import org.jwildfire.swing.NonHDRImageBufferComboBoxEditor; import com.l2fprod.common.beans.editor.ComboBoxPropertyEditor; public class CloudCreator extends ImageCreator { public enum Mode { CLOUDS, PLASMA } public enum ColorMode { GREY, COLOR, BASE_COLOR } public enum BGMode { COLOR, IMAGE } @Property(description = "Cloud coverage") @PropertyMin(0.0) @PropertyMax(1.0) private double cover = 0.5; @Property(description = "Cloud sharpness") @PropertyMin(0.0) @PropertyMax(1.0) private double sharpness = 0.975; @Property(description = "Octave persistence") @PropertyMin(0.0) @PropertyMax(1.0) private double persistence = 0.45; @Property(description = "Initial frequency (size of the pixel map)") @PropertyMin(2) private int initialFrequency = 5; @Property(description = "Number of octaves") @PropertyMin(2) private int octaves = 7; @Property(description = "Seed for the random number generator") @PropertyMin(0) private int seed = 123; @Property(description = "Cloud colors", editorClass = ColorModeEditor.class) private ColorMode colorMode = ColorMode.GREY; @Property(description = "Cloud base color (colorMode=BASE_COLOR)") private Color cloudColor = new Color(255, 51, 0); @Property(description = "Mode", editorClass = ModeEditor.class) private Mode mode = Mode.CLOUDS; @Property(description = "Background image (mode==CLOUDS)", editorClass = NonHDRImageBufferComboBoxEditor.class) private Buffer backgroundImg; @Property(description = "Background mode (mode==CLOUDS)", editorClass = BGModeEditor.class) private BGMode bgMode = BGMode.COLOR; @Property(description = "Background color (bgMode=COLOR)") private Color bgColor = new Color(153, 186, 255); @Override protected void fillImage(SimpleImage res) { int width = res.getImageWidth(); int height = res.getImageHeight(); Random rnd = new Random(); rnd.setSeed(this.seed); double cover = 1.0 - this.cover; double aspect = (double) width / (double) height; int frequency = this.initialFrequency; int frequencyX = (int) (frequency * aspect + 0.5); int frequencyY = (int) (frequency * aspect + 0.5); double alphaInt = 1.0f; BufferedImage mainImage = res.getBufferedImg(); Graphics2D mainGr = mainImage.createGraphics(); for (int i = 0; i < this.octaves; i++) { // create a small random image BufferedImage rndMap = new BufferedImage(frequencyX, frequencyY, BufferedImage.TYPE_INT_ARGB); { Graphics2D g = rndMap.createGraphics(); try { switch (colorMode) { case COLOR: for (int x = 0; x < frequencyX; x++) { for (int y = 0; y < frequencyY; y++) { int rVal = rnd.nextInt(255); int gVal = rnd.nextInt(255); int bVal = rnd.nextInt(255); g.setColor(new Color(rVal, gVal, bVal, (int) (255.0 * alphaInt + 0.5))); g.fillRect(x, y, 1, 1); } } break; case GREY: for (int x = 0; x < frequencyX; x++) { for (int y = 0; y < frequencyY; y++) { int val = rnd.nextInt(255); g.setColor(new Color(val, val, val, (int) (255.0 * alphaInt + 0.5))); g.fillRect(x, y, 1, 1); } } break; case BASE_COLOR: int rBase = this.cloudColor.getRed(); int gBase = this.cloudColor.getGreen(); int bBase = this.cloudColor.getBlue(); for (int x = 0; x < frequencyX; x++) { for (int y = 0; y < frequencyY; y++) { int val = rnd.nextInt(255); int rVal = (rBase * val) / 255; int gVal = (gBase * val) / 255; int bVal = (bBase * val) / 255; g.setColor(new Color(rVal, gVal, bVal, (int) (255.0 * alphaInt + 0.5))); g.fillRect(x, y, 1, 1); } } break; } } finally { g.dispose(); } } // scale up the image using Java-built-in interpolation { BufferedImage scaledRndMap = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics2D g = scaledRndMap.createGraphics(); try { g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); g.drawImage(rndMap, 0, 0, width, height, 0, 0, frequencyX, frequencyY, null); } finally { g.dispose(); } mainGr.drawImage(scaledRndMap, null, 0, 0); } alphaInt *= this.persistence; frequency += frequency; frequencyX = (int) (frequency * aspect + 0.5); frequencyY = (int) (frequency * aspect + 0.5); } // apply an exponential filter to let the noise more look like clouds if (mode == Mode.CLOUDS) { final double rWeight = 0.2990; final double gWeight = 0.5880; final double bWeight = 0.1130; SimpleImage bgImg = (this.backgroundImg != null) ? backgroundImg.getImage() : null; Pixel pixel = new Pixel(); Pixel bgPixel = new Pixel(); for (int i = 0; i < mainImage.getWidth(); i++) { for (int j = 0; j < mainImage.getHeight(); j++) { pixel.setARGBValue(res.getARGBValue(i, j)); double lum = pixel.r * rWeight + pixel.g * gWeight + pixel.b * bWeight; double c = lum - (cover * 255); if (c < 0) c = 0; int iVal = Tools.roundColor(255.0 - (Math.pow(this.sharpness, c) * 255.0)); int bgRed = 0, bgGreen = 0, bgBlue = 0; switch (bgMode) { case IMAGE: if (bgImg != null) { bgPixel.setARGBValue(bgImg.getARGBValueIgnoreBounds(i, j)); bgRed = bgPixel.r; bgGreen = bgPixel.g; bgBlue = bgPixel.b; } break; case COLOR: bgRed = this.bgColor.getRed(); bgGreen = this.bgColor.getGreen(); bgBlue = this.bgColor.getBlue(); break; } switch (colorMode) { case GREY: pixel.r = expose(iVal + 1.5 * bgRed); pixel.g = expose(iVal + 1.5 * bgGreen); pixel.b = expose(iVal + 1.5 * bgBlue); break; default: pixel.r = expose((iVal * pixel.r) / 255 + 1.5 * bgRed); pixel.g = expose((iVal * pixel.g) / 255 + 1.5 * bgGreen); pixel.b = expose((iVal * pixel.b) / 255 + 1.5 * bgBlue); break; } res.setRGB(i, j, pixel); } } } } public double getCover() { return cover; } public void setCover(double cover) { this.cover = cover; } public double getSharpness() { return sharpness; } public void setSharpness(double sharpness) { this.sharpness = sharpness; } public double getPersistence() { return persistence; } public void setPersistence(double persistence) { this.persistence = persistence; } public int getInitialFrequency() { return initialFrequency; } public void setInitialFrequency(int initialFrequency) { this.initialFrequency = initialFrequency; } public int getOctaves() { return octaves; } public void setOctaves(int octaves) { this.octaves = octaves; } public int getSeed() { return seed; } public void setSeed(int seed) { this.seed = seed; } private int expose(double light) { double exposure = -0.002; return Tools.roundColor((1.0 - Math.exp(light * exposure)) * 255.0 + 100); } public static class ColorModeEditor extends ComboBoxPropertyEditor { public ColorModeEditor() { super(); setAvailableValues(new ColorMode[] { ColorMode.GREY, ColorMode.COLOR, ColorMode.BASE_COLOR }); } } public static class BGModeEditor extends ComboBoxPropertyEditor { public BGModeEditor() { super(); setAvailableValues(new BGMode[] { BGMode.COLOR, BGMode.IMAGE }); } } public static class ModeEditor extends ComboBoxPropertyEditor { public ModeEditor() { super(); setAvailableValues(new Mode[] { Mode.CLOUDS, Mode.PLASMA }); } } public ColorMode getColorMode() { return colorMode; } public void setColorMode(ColorMode colorMode) { this.colorMode = colorMode; } public Color getCloudColor() { return cloudColor; } public void setCloudColor(Color cloudColor) { this.cloudColor = cloudColor; } public Mode getMode() { return mode; } public void setMode(Mode mode) { this.mode = mode; } public Buffer getBackgroundImg() { return backgroundImg; } public void setBackgroundImg(Buffer backgroundImg) { this.backgroundImg = backgroundImg; } public BGMode getBgMode() { return bgMode; } public void setBgMode(BGMode bgMode) { this.bgMode = bgMode; } public Color getBgColor() { return bgColor; } public void setBgColor(Color bgColor) { this.bgColor = bgColor; } }