/* 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.base.mathlib.MathLib; import org.jwildfire.image.Pixel; import org.jwildfire.image.SimpleImage; import org.jwildfire.image.WFImage; public class HSLTransformer extends Mesh2DTransformer { @Property(description = "Hue correction") @PropertyMin(-255) @PropertyMax(255) private int hue = 0; @Property(description = "Saturation correction") @PropertyMin(-255) @PropertyMax(255) private int saturation = 0; @Property(description = "Luminosity correction") @PropertyMin(-255) @PropertyMax(255) private int luminosity = 0; @Override protected void performPixelTransformation(WFImage pImg) { SimpleImage img = (SimpleImage) pImg; int width = pImg.getImageWidth(); int height = pImg.getImageHeight(); Pixel rgbPixel = new Pixel(); HSLPixel hslPixel = new HSLPixel(); double phue = (double) (this.hue) / 255.0; if (phue < (-1.0)) phue = -1; else if (phue > 1.0) phue = 1.0; double psaturation = (double) (this.saturation) / 255.0; if (psaturation < (-1.0)) psaturation = -1.0; else if (psaturation > 1.0) psaturation = 1.0; double pluminosity = (double) (this.luminosity) / 255.0; if (pluminosity < (-1.0)) pluminosity = -1; else if (pluminosity > 1.0) pluminosity = 1.0; for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { rgbPixel.setARGBValue(img.getARGBValue(j, i)); 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; hslPixel.saturation += psaturation; if (hslPixel.saturation < 0.0) hslPixel.saturation = 0.0; else if (hslPixel.saturation > 1.0) hslPixel.saturation = 1.0; 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; } hsl2rgb(hslPixel, rgbPixel); img.setRGB(j, i, rgbPixel); } } } public static void hsl2rgb(HSLPixel hslPixel, Pixel rgbPixel) { double luminosity = hslPixel.luminosity; double saturation = hslPixel.saturation; double hue = hslPixel.hue; double v = (luminosity <= 0.5) ? (luminosity * (1.0 + saturation)) : (luminosity + saturation - luminosity * saturation); if (v <= 0) { rgbPixel.r = 0; rgbPixel.g = 0; rgbPixel.b = 0; return; } hue *= 6.0; if (hue < 0.0) hue = 0.0; else if (hue > 6.0) hue = 6.0; double y = luminosity + luminosity - v; double x = y + (v - y) * (hue - (int) hue); double z = v - (v - y) * (hue - (int) hue); double r, g, b; // double h = hue; // if ((h >= 0.0) && (h < 1.0)) { // r = (1.0 - h) * v + h * z; // g = (1.0 - h) * x + h * v; // b = (1.0 - h) * y + h * y; // } // else if ((hue >= 1.0) && (hue < 2.0)) { // h -= 1.0; // r = (1.0 - h) * z + h * y; // g = (1.0 - h) * v + h * v; // b = (1.0 - h) * y + h * x; // } // else if ((hue >= 2.0) && (hue < 3.0)) { // h -= 2.0; // r = (1.0 - h) * y + h * y; // g = (1.0 - h) * v + h * z; // b = (1.0 - h) * x + h * v; // } // else if ((hue >= 3.0) && (hue < 4.0)) { // h -= 3.0; // r = (1.0 - h) * y + h * x; // g = (1.0 - h) * z + h * y; // b = (1.0 - h) * v + h * v; // } // else if ((hue >= 4.0) && (hue < 5.0)) { // h -= 4.0; // r = (1.0 - h) * x + h * v; // g = (1.0 - h) * y + h * y; // b = (1.0 - h) * v + h * z; // } // else { // h -= 5.0; // r = (1.0 - h) * v + h * v; // g = (1.0 - h) * y + h * x; // b = (1.0 - h) * z + h * y; // } switch ((int) hue) { case 0: r = v; g = x; b = y; break; case 1: r = z; g = v; b = y; break; case 2: r = y; g = v; b = x; break; case 3: r = y; g = z; b = v; break; case 4: r = x; g = y; b = v; break; case 5: r = v; g = y; b = z; break; default: // r = v; // g = x; // b = y; r = v; g = y; b = z; } rgbPixel.r = Tools.roundColor(r * 255.0); rgbPixel.g = Tools.roundColor(g * 255.0); rgbPixel.b = Tools.roundColor(b * 255.0); } private static double _max(double x, double y) { return (((x) > (y)) ? (x) : (y)); } private static double _min(double x, double y) { return (((x) < (y)) ? (x) : (y)); } public static void rgb2hsl(Pixel rgbPixel, HSLPixel hslPixel) { hslPixel.hue = 1.0; hslPixel.saturation = 0.0; double r = (double) rgbPixel.r / 255.0; double g = (double) rgbPixel.g / 255.0; double b = (double) rgbPixel.b / 255.0; double max = _max(r, _max(g, b)); double min = _min(r, _min(g, b)); /* if ((max - min) < 0.1) min = max; */ hslPixel.luminosity = (min + max) / 2.0; if (Math.abs(hslPixel.luminosity) <= MathLib.EPSILON) return; hslPixel.saturation = max - min; if (Math.abs(hslPixel.saturation) <= MathLib.EPSILON) return; hslPixel.saturation /= ((hslPixel.luminosity) <= 0.5) ? (min + max) : (2.0 - max - min); if (Math.abs(r - max) < MathLib.EPSILON) { hslPixel.hue = ((g == min) ? 5.0 + (max - b) / (max - min) : 1.0 - (max - g) / (max - min)); } else { if (Math.abs(g - max) < MathLib.EPSILON) { hslPixel.hue = ((b == min) ? 1.0 + (max - r) / (max - min) : 3.0 - (max - b) / (max - min)); } else { hslPixel.hue = ((r == min) ? 3.0 + (max - g) / (max - min) : 5.0 - (max - r) / (max - min)); } } hslPixel.hue /= 6.0; } @Override public void initDefaultParams(WFImage pImg) { hue = saturation = luminosity = 0; } public static class HSLPixel { public double hue; public double saturation; public double luminosity; } public int getHue() { return hue; } public void setHue(int hue) { this.hue = hue; } public int getSaturation() { return saturation; } public void setSaturation(int saturation) { this.saturation = saturation; } public int getLuminosity() { return luminosity; } public void setLuminosity(int luminosity) { this.luminosity = luminosity; } }