/* 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.image.Pixel; import org.jwildfire.image.SimpleImage; import org.jwildfire.image.WFImage; import com.l2fprod.common.beans.editor.ComboBoxPropertyEditor; public class HSLGradientTransformer extends Mesh2DTransformer { public enum Mode { HUE, SATURATION, LUMINOSITY } public enum Transition { RADIAL, PARALLEL } @Property(description = "Balancing mode", editorClass = ModeEditor.class) private Mode mode = Mode.HUE; @Property(description = "X-coordinate of effect source") private int x1 = 64; @Property(description = "Y-coordinate of effect source") private int y1 = 51; @Property(description = "Effect value at the effect source") private int value1 = 64; @Property(description = "Transition mode", editorClass = TransitionEditor.class) private Transition transition = Transition.RADIAL; @Property(description = "X-coordinate of effect destination (parallel transition)") private int x2 = 256; @Property(description = "Y-coordinate of effect destination (parallel transition)") private int y2 = 204; @Property(description = "Effect value at the effect destination") private int value2 = 100; @Property(description = "Effect radius (radial transition)") private int radius = 120; @Property(description = "Base radius keeping the initial value (radial transition)") private int baseRadius; @Override protected void performPixelTransformation(WFImage pImg) { SimpleImage img = (SimpleImage) pImg; int width = pImg.getImageWidth(); int height = pImg.getImageHeight(); int x1 = this.x1; int y1 = this.y1; int v1 = this.value1; int x2 = this.x2; int y2 = this.y2; int v2 = this.value2; double rx = (double) (x2 - x1); double ry = (double) (y2 - y1); double dv = (double) (v2 - v1); double vlen; if (this.transition == Transition.PARALLEL) vlen = Math.sqrt(rx * rx + ry * ry); else vlen = this.radius - this.baseRadius; if (vlen < 0.0001) vlen = 0.0001; double vlenq = vlen * vlen; Pixel rgbPixel = new Pixel(); HSLTransformer.HSLPixel hslPixel = new HSLTransformer.HSLPixel(); switch (this.mode) { case HUE: if (this.transition == Transition.PARALLEL) { for (int i = 0; i < height; i++) { double ay = (double) (i - y1); for (int j = 0; j < width; j++) { double ax = (double) (j - x1); double prj = (ax * rx + ay * ry) / vlenq; if (prj < 0.0) prj = 0.0; else if (prj > 1.0) prj = 1.0; double phue = (v1 + dv * prj) / 255.0; if (phue < (-1.0)) phue = -1; else if (phue > 1.0) phue = 1.0; rgbPixel.setARGBValue(img.getARGBValue(j, i)); HSLTransformer.rgb2hsl(rgbPixel, hslPixel); if (hslPixel.hue != (-1.0)) { hslPixel.hue += phue; if (hslPixel.hue < 0.0) hslPixel.hue += 1.0; else if (hslPixel.hue > 1.0) hslPixel.hue -= 1.0; } HSLTransformer.hsl2rgb(hslPixel, rgbPixel); img.setRGB(j, i, rgbPixel); } } } else { for (int i = 0; i < height; i++) { double ay = (double) (i - y1); for (int j = 0; j < width; j++) { double ax = (double) (j - x1); double prj = (Math.sqrt(ax * ax + ay * ay) - baseRadius) / vlen; if (prj < 0.0) prj = 0.0; else if (prj > 1.0) prj = 1.0; double phue = (v1 + dv * prj) / 255.0; if (phue < (-1.0)) phue = -1; else if (phue > 1.0) phue = 1.0; rgbPixel.setARGBValue(img.getARGBValue(j, i)); HSLTransformer.rgb2hsl(rgbPixel, hslPixel); if (hslPixel.hue != (-1.0)) { hslPixel.hue += phue; if (hslPixel.hue < 0.0) hslPixel.hue += 1.0; else if (hslPixel.hue > 1.0) hslPixel.hue -= 1.0; } HSLTransformer.hsl2rgb(hslPixel, rgbPixel); img.setRGB(j, i, rgbPixel); } } } break; case SATURATION: if (this.transition == Transition.PARALLEL) { for (int i = 0; i < height; i++) { double ay = (double) (i - y1); for (int j = 0; j < width; j++) { double ax = (double) (j - x1); double prj = (ax * rx + ay * ry) / vlenq; if (prj < 0.0) prj = 0.0; else if (prj > 1.0) prj = 1.0; double psaturation = (v1 + dv * prj) / 255.0; if (psaturation < (-1.0)) psaturation = -1.0; else if (psaturation > 1.0) psaturation = 1.0; rgbPixel.setARGBValue(img.getARGBValue(j, i)); HSLTransformer.rgb2hsl(rgbPixel, hslPixel); hslPixel.saturation += psaturation; if (hslPixel.saturation < 0.0) hslPixel.saturation = 0.0; else if (hslPixel.saturation > 1.0) hslPixel.saturation = 1.0; HSLTransformer.hsl2rgb(hslPixel, rgbPixel); img.setRGB(j, i, rgbPixel); } } } else { for (int i = 0; i < height; i++) { double ay = (double) (i - y1); for (int j = 0; j < width; j++) { double ax = (double) (j - x1); double prj = (Math.sqrt(ax * ax + ay * ay) - baseRadius) / vlen; if (prj < 0.0) prj = 0.0; else if (prj > 1.0) prj = 1.0; double psaturation = (v1 + dv * prj) / 255.0; if (psaturation < (-1.0)) psaturation = -1.0; else if (psaturation > 1.0) psaturation = 1.0; rgbPixel.setARGBValue(img.getARGBValue(j, i)); HSLTransformer.rgb2hsl(rgbPixel, hslPixel); hslPixel.saturation += psaturation; if (hslPixel.saturation < 0.0) hslPixel.saturation = 0.0; else if (hslPixel.saturation > 1.0) hslPixel.saturation = 1.0; HSLTransformer.hsl2rgb(hslPixel, rgbPixel); img.setRGB(j, i, rgbPixel); } } } break; case LUMINOSITY: if (this.transition == Transition.PARALLEL) { for (int i = 0; i < height; i++) { double ay = (double) (i - y1); for (int j = 0; j < width; j++) { double ax = (double) (j - x1); double prj = (ax * rx + ay * ry) / vlenq; if (prj < 0.0) prj = 0.0; else if (prj > 1.0) prj = 1.0; double pluminosity = (v1 + dv * prj) / 255.0; if (pluminosity < (-1.0)) pluminosity = -1; else if (pluminosity > 1.0) pluminosity = 1.0; rgbPixel.setARGBValue(img.getARGBValue(j, i)); HSLTransformer.rgb2hsl(rgbPixel, hslPixel); hslPixel.luminosity += pluminosity; if (hslPixel.luminosity < 0.0) hslPixel.luminosity = 0.0; else if (hslPixel.luminosity > 1.0) hslPixel.luminosity = 1.0; HSLTransformer.hsl2rgb(hslPixel, rgbPixel); img.setRGB(j, i, rgbPixel); } } } else { for (int i = 0; i < height; i++) { double ay = (double) (i - y1); for (int j = 0; j < width; j++) { double ax = (double) (j - x1); double prj = (Math.sqrt(ax * ax + ay * ay) - baseRadius) / vlen; if (prj < 0.0) prj = 0.0; else if (prj > 1.0) prj = 1.0; double pluminosity = (v1 + dv * prj) / 255.0; if (pluminosity < (-1.0)) pluminosity = -1; else if (pluminosity > 1.0) pluminosity = 1.0; rgbPixel.setARGBValue(img.getARGBValue(j, i)); HSLTransformer.rgb2hsl(rgbPixel, hslPixel); hslPixel.luminosity += pluminosity; if (hslPixel.luminosity < 0.0) hslPixel.luminosity = 0.0; else if (hslPixel.luminosity > 1.0) hslPixel.luminosity = 1.0; HSLTransformer.hsl2rgb(hslPixel, rgbPixel); img.setRGB(j, i, rgbPixel); } } } break; } } @Override public void initDefaultParams(WFImage pImg) { int width = pImg.getImageWidth(); int height = pImg.getImageHeight(); double rr = Math.sqrt(width * width + height * height); x1 = (int) ((double) width / 2.1 + 0.5); y1 = (int) ((double) height / 1.9 + 0.5); x2 = width - (int) ((double) width / 7.0 + 0.5); y2 = height - (int) ((double) height / 9.0 + 0.5); radius = (int) (rr * 0.45 + 0.5); value1 = 120; value2 = -180; mode = Mode.HUE; transition = Transition.RADIAL; baseRadius = 0; } public static class ModeEditor extends ComboBoxPropertyEditor { public ModeEditor() { super(); setAvailableValues(new Mode[] { Mode.HUE, Mode.SATURATION, Mode.LUMINOSITY }); } } public static class TransitionEditor extends ComboBoxPropertyEditor { public TransitionEditor() { super(); setAvailableValues(new Transition[] { Transition.RADIAL, Transition.PARALLEL }); } } public Mode getMode() { return mode; } public void setMode(Mode mode) { this.mode = mode; } public int getX1() { return x1; } public void setX1(int x1) { this.x1 = x1; } public int getY1() { return y1; } public void setY1(int y1) { this.y1 = y1; } public int getValue1() { return value1; } public void setValue1(int value1) { this.value1 = value1; } public Transition getTransition() { return transition; } public void setTransition(Transition transition) { this.transition = transition; } public int getX2() { return x2; } public void setX2(int x2) { this.x2 = x2; } public int getY2() { return y2; } public void setY2(int y2) { this.y2 = y2; } public int getValue2() { return value2; } public void setValue2(int value2) { this.value2 = value2; } public int getRadius() { return radius; } public void setRadius(int radius) { this.radius = radius; } public int getBaseRadius() { return baseRadius; } public void setBaseRadius(int baseRadius) { this.baseRadius = baseRadius; } }