/* * 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.ShowOriginal; import pixelitor.utils.ImageUtils; import java.awt.Color; import java.awt.image.BufferedImage; /** * Inverts only some of the RGB or HSB channels */ public class ChannelInvert extends FilterWithParametrizedGUI { public static final String NAME = "Channel Invert"; private static final int NOTHING = 0; private static final int RED_ONLY = 1; private static final int GREEN_ONLY = 2; private static final int BLUE_ONLY = 3; private static final int RED_GREEN = 4; private static final int RED_BLUE = 5; private static final int GREEN_BLUE = 6; private static final int RED_GREEN_BLUE = 7; private static final int HUE_ONLY = 8; private static final int SATURATION_ONLY = 9; private static final int BRI_ONLY = 10; private static final int HUE_SAT = 11; private static final int HUE_BRI = 12; private static final int SAT_BRI = 13; private static final int HUE_SAT_BRI = 14; private final Value[] invertChoices = { new Value("Nothing", NOTHING), new Value("Hue", HUE_ONLY), new Value("Saturation", SATURATION_ONLY), new Value("Brightness", BRI_ONLY), new Value("Hue and Saturation", HUE_SAT), new Value("Hue and Brightness", HUE_BRI), new Value("Saturation and Brightness", SAT_BRI), new Value("Hue, Saturation and Brightness", HUE_SAT_BRI), new Value("Red", RED_ONLY), new Value("Green", GREEN_ONLY), new Value("Blue", BLUE_ONLY), new Value("Red and Green", RED_GREEN), new Value("Red and Blue", RED_BLUE), new Value("Green and Blue", GREEN_BLUE), new Value("Red, Green and Blue", RED_GREEN_BLUE), }; private final IntChoiceParam invertTypeSelector = new IntChoiceParam("Invert Channel", invertChoices); public ChannelInvert() { super(ShowOriginal.YES); setParamSet(new ParamSet(invertTypeSelector)); } @Override public BufferedImage doTransform(BufferedImage src, BufferedImage dest) { int invertType = invertTypeSelector.getValue(); if (invertType == NOTHING) { return src; } if (invertType >= HUE_ONLY) { return invertHSB(invertType, src, dest); } int[] srcData = ImageUtils.getPixelsAsArray(src); int[] destData = ImageUtils.getPixelsAsArray(dest); for (int i = 0; i < destData.length; i++) { int srcPixel = srcData[i]; int alpha = srcPixel & 0xFF000000; if (alpha == 0) { destData[i] = srcPixel; } else { switch (invertType) { case RED_ONLY: destData[i] = srcPixel ^ 0x00FF0000; break; case GREEN_ONLY: destData[i] = srcPixel ^ 0x0000FF00; break; case BLUE_ONLY: destData[i] = srcPixel ^ 0x000000FF; break; case RED_GREEN: destData[i] = srcPixel ^ 0x00FFFF00; break; case RED_BLUE: destData[i] = srcPixel ^ 0x00FF00FF; break; case GREEN_BLUE: destData[i] = srcPixel ^ 0x0000FFFF; break; case RED_GREEN_BLUE: destData[i] = srcPixel ^ 0x00FFFFFF; break; } } } return dest; } private static BufferedImage invertHSB(int invertType, BufferedImage src, BufferedImage dest) { int[] srcData = ImageUtils.getPixelsAsArray(src); int[] destData = ImageUtils.getPixelsAsArray(dest); float[] hsb = {0.0f, 0.0f, 0.0f}; for (int i = 0; i < destData.length; i++) { int srcPixel = srcData[i]; int a = srcPixel & 0xFF000000; if (a == 0) { destData[i] = srcPixel; continue; } int r = (srcPixel >>> 16) & 0xFF; int g = (srcPixel >>> 8) & 0xFF; int b = (srcPixel) & 0xFF; hsb = Color.RGBtoHSB(r, g, b, hsb); int newRGB = 0; switch (invertType) { case HUE_ONLY: newRGB = Color.HSBtoRGB(0.5f + hsb[0], hsb[1], hsb[2]); break; case BRI_ONLY: newRGB = Color.HSBtoRGB(hsb[0], hsb[1], 1.0f - hsb[2]); break; case SATURATION_ONLY: newRGB = Color.HSBtoRGB(hsb[0], 1.0f - hsb[1], hsb[2]); break; case HUE_BRI: newRGB = Color.HSBtoRGB(0.5f + hsb[0], hsb[1], 1.0f - hsb[2]); break; case HUE_SAT: newRGB = Color.HSBtoRGB(0.5f + hsb[0], 1.0f - hsb[1], hsb[2]); break; case SAT_BRI: newRGB = Color.HSBtoRGB(hsb[0], 1.0f - hsb[1], 1.0f - hsb[2]); break; case HUE_SAT_BRI: newRGB = Color.HSBtoRGB(0.5f + hsb[0], 1.0f - hsb[1], 1.0f - hsb[2]); break; } // alpha is 255 here newRGB &= 0x00FFFFFF; // set alpha to 0 destData[i] = a | newRGB; // add the real alpha } return dest; } @Override public boolean supportsGray() { return false; } }