/*
* Copyright 2017 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.filters;
import pixelitor.filters.gui.IntChoiceParam;
import pixelitor.filters.gui.IntChoiceParam.Value;
import pixelitor.filters.gui.ParamSet;
import pixelitor.filters.gui.RangeParam;
import pixelitor.filters.gui.ShowOriginal;
import pixelitor.utils.ImageUtils;
import java.awt.image.BufferedImage;
import static pixelitor.filters.gui.RandomizePolicy.IGNORE_RANDOMIZE;
/**
* Solarize
*/
public class Solarize extends FilterWithParametrizedGUI {
public static final String NAME = "Solarize";
private static final int TYPE_CLASSIC = 1; // pixels above the threshold level are inverted + contrast is maximized
private static final int TYPE_INVERTED = 2; // upside down: corresponds to a V-shaped curves adjustment
private final RangeParam redThreshold = new RangeParam("Red Threshold", 0, 128, 255);
private final RangeParam greenThreshold = new RangeParam("Green Threshold", 0, 128, 255);
private final RangeParam blueThreshold = new RangeParam("Blue Threshold", 0, 128, 255);
private final IntChoiceParam type = new IntChoiceParam("Type", new Value[]{
new Value("Classic", TYPE_CLASSIC),
new Value("Upside Down Curve", TYPE_INVERTED)
}, IGNORE_RANDOMIZE);
public Solarize() {
super(ShowOriginal.YES);
setParamSet(new ParamSet(
type,
redThreshold,
greenThreshold,
blueThreshold
));
}
@Override
public BufferedImage doTransform(BufferedImage src, BufferedImage dest) {
int[] srcData = ImageUtils.getPixelsAsArray(src);
int[] destData = ImageUtils.getPixelsAsArray(dest);
float redThr = redThreshold.getValueAsFloat();
float greenThr = greenThreshold.getValueAsFloat();
float blueThr = blueThreshold.getValueAsFloat();
float m1Red = 255.0f / redThr;
float m2Red = 255.0f / (255.0f - redThr);
float m1Green = 255.0f / greenThr;
float m2Green = 255.0f / (255.0f - greenThr);
float m1Blue = 255.0f / blueThr;
float m2Blue = 255.0f / (255.0f - blueThr);
int[] redLookup = new int[256];
int[] greenLookup = new int[256];
int[] blueLookup = new int[256];
int solarizeType = type.getValue();
if (solarizeType == TYPE_CLASSIC) {
for (int i = 0; i < 256; i++) {
if (i > redThr) {
redLookup[i] = 255 - (int) (m2Red * (i - redThr));
} else {
redLookup[i] = 255 - (int) (m1Red * (redThr - i));
}
if (i > greenThr) {
greenLookup[i] = 255 - (int) (m2Green * (i - greenThr));
} else {
greenLookup[i] = 255 - (int) (m1Green * (greenThr - i));
}
if (i > blueThr) {
blueLookup[i] = 255 - (int) (m2Blue * (i - blueThr));
} else {
blueLookup[i] = 255 - (int) (m1Blue * (blueThr - i));
}
}
} else if (solarizeType == TYPE_INVERTED) {
for (int i = 0; i < 256; i++) {
if (i > redThr) {
redLookup[i] = (int) (m2Red * (i - redThr));
} else {
redLookup[i] = (int) (m1Red * (redThr - i));
}
if (i > greenThr) {
greenLookup[i] = (int) (m2Green * (i - greenThr));
} else {
greenLookup[i] = (int) (m1Green * (greenThr - i));
}
if (i > blueThr) {
blueLookup[i] = (int) (m2Blue * (i - blueThr));
} else {
blueLookup[i] = (int) (m1Blue * (blueThr - i));
}
}
}
for (int i = 0, destDataLength = destData.length; i < destDataLength; i++) {
int rgb = srcData[i];
int a = (rgb >>> 24) & 0xFF;
if (a == 0) {
destData[i] = 0;
} else {
int r = (rgb >>> 16) & 0xFF;
int g = (rgb >>> 8) & 0xFF;
int b = (rgb) & 0xFF;
int newR = redLookup[r];
int newG = greenLookup[g];
int newB = blueLookup[b];
destData[i] = (a << 24) | (newR << 16) | (newG << 8) | newB;
}
}
return dest;
}
@Override
public boolean supportsGray() {
return false;
}
}