/*
* Copyright 2016 Laszlo Balazs-Csiki
*
* This file is part of Pixelitor. Pixelitor is free software: you
* can redistribute it and/or modify it under the terms of the GNU
* General Public License, version 3 as published by the Free
* Software Foundation.
*
* Pixelitor 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Pixelitor. If not, see <http://www.gnu.org/licenses/>.
*/
package pixelitor.layers;
import com.jhlabs.image.PointFilter;
import net.jafama.FastMath;
import java.awt.Color;
class MaskFromColorRangeFilter extends PointFilter {
public static final int MODE_RGB = 1;
public static final int MODE_HSB = 2;
private int mode = MODE_HSB;
public static final int WHITE_PIXEL = 0xFF_FF_FF_FF;
public static final int BLACK_PIXEL = 0xFF_00_00_00;
private double toleranceMin;
private double toleranceMax;
private int refR, refG, refB;
private float refHue, refSat, refBri;
private boolean invert;
protected MaskFromColorRangeFilter(String filterName) {
super(filterName);
}
public void setColor(Color c) {
refR = c.getRed();
refG = c.getGreen();
refB = c.getBlue();
if (mode == MODE_HSB) {
float[] hsb = Color.RGBtoHSB(refR, refG, refB, null);
refHue = hsb[0];
refSat = hsb[1];
refBri = hsb[2];
}
}
public void setMode(int mode) {
this.mode = mode;
}
public void setTolerance(double tolerance, double fuzziness) {
// otherwise tolerance = 0 does not select exact matches - why?
double adjustedTolerance = tolerance + 0.1;
this.toleranceMin = adjustedTolerance * (1.0 - fuzziness);
this.toleranceMax = adjustedTolerance * (1.0 + fuzziness);
}
public void setInvert(boolean invert) {
this.invert = invert;
}
@Override
public int filterRGB(int x, int y, int rgb) {
double dist;
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
if (mode == MODE_RGB) {
int deltaR = r - refR;
int deltaG = g - refG;
int deltaB = b - refB;
dist = FastMath.sqrtQuick(deltaR * deltaR + deltaG * deltaG + deltaB * deltaB);
} else if (mode == MODE_HSB) {
float[] hsb = Color.RGBtoHSB(r, g, b, null);
float deltaHue = hsb[0] - refHue;
float deltaSat = hsb[1] - refSat;
float deltaBri = hsb[2] - refBri;
// hue is an angle
if (deltaHue > 0.5f) {
deltaHue = 1.0f - deltaHue;
} else if (deltaHue < -0.5f) {
deltaHue = 1.0f + deltaHue;
}
// int v = Math.abs((int) (deltaHue * 255 * 2));
// return 0xFF_00_00_00 | (v << 16) | (v << 8) | v;
dist = 150 * FastMath.sqrtQuick(deltaHue * deltaHue + deltaSat * deltaSat + deltaBri * deltaBri);
} else {
throw new IllegalStateException("mode = " + mode);
}
if (dist > toleranceMax) {
if (invert) {
return WHITE_PIXEL;
} else {
return BLACK_PIXEL;
}
} else if (dist < toleranceMin) {
if (invert) {
return BLACK_PIXEL;
} else {
return WHITE_PIXEL;
}
} else {
// linear interpolation
int v = (int) ((toleranceMax - dist) * 255 / (toleranceMax - toleranceMin));
if (invert) {
v = 255 - v;
}
return 0xFF_00_00_00 | (v << 16) | (v << 8) | v;
}
}
}