/* 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.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 BalancingGradientTransformer extends Mesh2DTransformer { public enum Mode { RED, GREEN, BLUE, CONTRAST, BRIGHTNESS, GAMMA, SATURATION } public enum Transition { RADIAL, PARALLEL } @Property(description = "Balancing mode", editorClass = ModeEditor.class) private Mode mode = Mode.BRIGHTNESS; @Property(description = "X-coordinate of effect source") private int x1; @Property(description = "Y-coordinate of effect source") private int y1; @Property(description = "Effect value at the effect source") private int value1; @Property(description = "Transition mode", editorClass = TransitionEditor.class) private Transition transition = Transition.RADIAL; @Property(description = "X-coordinate of effect destination (parallel transition)") private int x2; @Property(description = "Y-coordinate of effect destination (parallel transition)") private int y2; @Property(description = "Effect value at the effect destination") private int value2; @Property(description = "Effect radius (radial transition)") private int radius; @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 pixel = new Pixel(); final double brMAX = 256.0; switch (this.mode) { case RED: 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; int ival = v1 + (int) (dv * prj + 0.5); int tt = (int) ((double) ival / (double) brMAX * (double) 255.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); pixel.r += tt; if (pixel.r < 0) pixel.r = 0; else if (pixel.r > 255) pixel.r = 255; img.setRGB(j, i, pixel); } } } 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; int ival = v1 + (int) (dv * prj + 0.5); int tt = (int) ((double) ival / (double) brMAX * (double) 255.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); pixel.r += tt; if (pixel.r < 0) pixel.r = 0; else if (pixel.r > 255) pixel.r = 255; img.setRGB(j, i, pixel); } } } break; case GREEN: 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; int ival = v1 + (int) (dv * prj + 0.5); int tt = (int) ((double) ival / (double) brMAX * (double) 255.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); pixel.g += tt; if (pixel.g < 0) pixel.g = 0; else if (pixel.g > 255) pixel.g = 255; img.setRGB(j, i, pixel); } } } 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; int ival = v1 + (int) (dv * prj + 0.5); int tt = (int) ((double) ival / (double) brMAX * (double) 255.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); pixel.g += tt; if (pixel.g < 0) pixel.g = 0; else if (pixel.g > 255) pixel.g = 255; img.setRGB(j, i, pixel); } } } break; case BLUE: 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; int ival = v1 + (int) (dv * prj + 0.5); int tt = (int) ((double) ival / (double) brMAX * (double) 255.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); pixel.b += tt; if (pixel.b < 0) pixel.b = 0; else if (pixel.b > 255) pixel.b = 255; img.setRGB(j, i, pixel); } } } 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; int ival = v1 + (int) (dv * prj + 0.5); int tt = (int) ((double) ival / (double) brMAX * (double) 255.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); pixel.b += tt; if (pixel.b < 0) pixel.b = 0; else if (pixel.b > 255) pixel.b = 255; img.setRGB(j, i, pixel); } } } break; case BRIGHTNESS: 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; int ival = v1 + (int) (dv * prj + 0.5); int tt = (int) ((double) ival / (double) brMAX * (double) 255.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); pixel.r += tt; if (pixel.r < 0) pixel.r = 0; else if (pixel.r > 255) pixel.r = 255; pixel.g += tt; if (pixel.g < 0) pixel.g = 0; else if (pixel.g > 255) pixel.g = 255; pixel.b += tt; if (pixel.b < 0) pixel.b = 0; else if (pixel.b > 255) pixel.b = 255; img.setRGB(j, i, pixel); } } } 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; int ival = v1 + (int) (dv * prj + 0.5); int tt = (int) ((double) ival / (double) brMAX * (double) 255.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); pixel.r += tt; if (pixel.r < 0) pixel.r = 0; else if (pixel.r > 255) pixel.r = 255; pixel.g += tt; if (pixel.g < 0) pixel.g = 0; else if (pixel.g > 255) pixel.g = 255; pixel.b += tt; if (pixel.b < 0) pixel.b = 0; else if (pixel.b > 255) pixel.b = 255; img.setRGB(j, i, pixel); } } } break; case CONTRAST: 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; int ival = v1 + (int) (dv * prj + 0.5); double scale = (double) (ival) / brMAX; int sc = (int) (scale * (double) Tools.VPREC + 0.5); int dc; pixel.setARGBValue(img.getARGBValue(j, i)); if (ival > 0) { dc = (int) (((int) (pixel.r - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; dc = pixel.r + dc; if (dc < 0) dc = 0; else if (dc > 255) dc = 255; pixel.r = dc; dc = (int) (((int) (pixel.g - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; dc = pixel.g + dc; if (dc < 0) dc = 0; else if (dc > 255) dc = 255; pixel.g = dc; dc = (int) ((int) ((pixel.b - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; dc = pixel.b + dc; if (dc < 0) dc = 0; else if (dc > 255) dc = 255; pixel.b = dc; } else { int val; dc = (int) (((int) (pixel.r - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; val = pixel.r + dc; if (pixel.r < 127) { if (val > 127) val = 127; } else { if (val < 127) val = 127; } pixel.r = val; dc = (int) ((int) ((pixel.g - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; val = pixel.g + dc; if (pixel.g < 127) { if (val > 127) val = 127; } else { if (val < 127) val = 127; } pixel.g = val; dc = (int) ((int) ((pixel.b - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; val = pixel.b + dc; if (pixel.b < 127) { if (val > 127) val = 127; } else { if (val < 127) val = 127; } pixel.b = val; } img.setRGB(j, i, pixel); } } } 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; int ival = v1 + (int) (dv * prj + 0.5); double scale = (double) (ival) / brMAX; int sc = (int) (scale * (double) Tools.VPREC + 0.5); int dc; pixel.setARGBValue(img.getARGBValue(j, i)); if (ival > 0) { dc = (int) (((int) (pixel.r - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; dc = pixel.r + dc; if (dc < 0) dc = 0; else if (dc > 255) dc = 255; pixel.r = dc; dc = (int) (((int) (pixel.g - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; dc = pixel.g + dc; if (dc < 0) dc = 0; else if (dc > 255) dc = 255; pixel.g = dc; dc = (int) ((int) ((pixel.b - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; dc = pixel.b + dc; if (dc < 0) dc = 0; else if (dc > 255) dc = 255; pixel.b = dc; } else { int val; dc = (int) (((int) (pixel.r - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; val = pixel.r + dc; if (pixel.r < 127) { if (val > 127) val = 127; } else { if (val < 127) val = 127; } pixel.r = val; dc = (int) ((int) ((pixel.g - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; val = pixel.g + dc; if (pixel.g < 127) { if (val > 127) val = 127; } else { if (val < 127) val = 127; } pixel.g = val; dc = (int) ((int) ((pixel.b - (int) 127) * sc) >> Tools.SPREC); if (dc < (-255)) dc = (-255); else if (dc > 255) dc = 255; val = pixel.b + dc; if (pixel.b < 127) { if (val > 127) val = 127; } else { if (val < 127) val = 127; } pixel.b = val; } img.setRGB(j, i, pixel); } } } break; case GAMMA: { final double max = 255.0; 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; int ival = v1 + (int) (dv * prj + 0.5); double g = (double) 512.0 / (512.0 + (double) ival); pixel.setARGBValue(img.getARGBValue(j, i)); int val; val = pixel.r; if (val == 0) val = 0; else val = (int) (max * Math.pow((double) val / 255.0, g) + 0.5); pixel.r = val; val = pixel.g; if (val == 0) val = 0; else val = (int) (max * Math.pow((double) val / 255.0, g) + 0.5); pixel.g = val; val = pixel.b; if (val == 0) val = 0; else val = (int) (max * Math.pow((double) val / 255.0, g) + 0.5); pixel.b = val; img.setRGB(j, i, pixel); } } } 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; int ival = v1 + (int) (dv * prj + 0.5); double g = (double) 512.0 / (512.0 + (double) ival); pixel.setARGBValue(img.getARGBValue(j, i)); int val; val = pixel.r; if (val == 0) val = 0; else val = (int) (max * Math.pow((double) val / 255.0, g) + 0.5); pixel.r = val; val = pixel.g; if (val == 0) val = 0; else val = (int) (max * Math.pow((double) val / 255.0, g) + 0.5); pixel.g = val; val = pixel.b; if (val == 0) val = 0; else val = (int) (max * Math.pow((double) val / 255.0, g) + 0.5); pixel.b = val; img.setRGB(j, i, pixel); } } } } break; case SATURATION: { int rs = 2990; int gs = 5880; int bs = 1130; rs = (rs * Tools.VPREC) / 10000; gs = (gs * Tools.VPREC) / 10000; bs = (bs * Tools.VPREC) / 10000; 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; int ival = v1 + (int) (dv * prj + 0.5); int scl = (int) ((double) ival / 255.0 * 1024.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); int rv = pixel.r; int gv = pixel.g; int bv = pixel.b; int avg = (rs * rv + gs * gv + bs * bv) >> Tools.SPREC; rv += ((rv - avg) * scl) >> Tools.SPREC; if (rv < 0) rv = 0; else if (rv > 255) rv = 255; pixel.r = rv; gv += ((gv - avg) * scl) >> Tools.SPREC; if (gv < 0) gv = 0; else if (gv > 255) gv = 255; pixel.g = gv; bv += ((bv - avg) * scl) >> Tools.SPREC; if (bv < 0) bv = 0; else if (bv > 255) bv = 255; pixel.b = bv; img.setRGB(j, i, pixel); } } } 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; int ival = v1 + (int) (dv * prj + 0.5); int scl = (int) ((double) ival / 255.0 * 1024.0 + 0.5); pixel.setARGBValue(img.getARGBValue(j, i)); int rv = pixel.r; int gv = pixel.g; int bv = pixel.b; int avg = (rs * rv + gs * gv + bs * bv) >> Tools.SPREC; rv += ((rv - avg) * scl) >> Tools.SPREC; if (rv < 0) rv = 0; else if (rv > 255) rv = 255; pixel.r = rv; gv += ((gv - avg) * scl) >> Tools.SPREC; if (gv < 0) gv = 0; else if (gv > 255) gv = 255; pixel.g = gv; bv += ((bv - avg) * scl) >> Tools.SPREC; if (bv < 0) bv = 0; else if (bv > 255) bv = 255; pixel.b = bv; img.setRGB(j, i, pixel); } } } } 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); baseRadius = 0; value1 = 120; value2 = -180; mode = Mode.BLUE; transition = Transition.RADIAL; } public static class ModeEditor extends ComboBoxPropertyEditor { public ModeEditor() { super(); setAvailableValues(new Mode[] { Mode.RED, Mode.GREEN, Mode.BLUE, Mode.CONTRAST, Mode.BRIGHTNESS, Mode.GAMMA, Mode.SATURATION }); } } 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; } @Override protected boolean allowShowStats() { return false; } }