/* * 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.Composition; import pixelitor.filters.gui.ParamSet; import pixelitor.filters.gui.RangeParam; import pixelitor.filters.gui.ShowOriginal; import pixelitor.gui.ImageComponents; import pixelitor.history.History; import pixelitor.utils.ImageUtils; import java.awt.image.BufferedImage; /** * The Fade filter */ public class Fade extends FilterWithParametrizedGUI { private static final int FADE_MIN = 0; private static final int FADE_MAX = 100; private static final int FADE_INIT = 100; private final RangeParam opacityParam = new RangeParam("Opacity (%)", FADE_MIN, FADE_INIT, FADE_MAX ); // private BlendingModeParam blendingModeParam = new BlendingModeParam(BlendingMode.values()); public Fade() { super(ShowOriginal.YES); setParamSet(new ParamSet( opacityParam // blendingModeParam )); } @Override public BufferedImage doTransform(BufferedImage src, BufferedImage dest) { // the fade menu item must be active only if History.canFade() assert History.canFade(); BufferedImage previous = ImageComponents.getActiveComp() .map(Composition::getActiveDrawable) .flatMap(History::getPreviousEditForFade) .orElseThrow(() -> new IllegalStateException("no FadeableEdit")) .getBackupImage(); if(previous == null) { // soft reference expired return src; } dest = fade(previous, src, dest, opacityParam); return dest; } public void setOpacity(int newOpacity) { opacityParam.setValue(newOpacity); } public static BufferedImage fade(BufferedImage before, BufferedImage after, BufferedImage dest, RangeParam opacity) { if (opacity.getValue() == 100) { return after; } float fadeFactor = opacity.getValueAsPercentage(); // A simple AlphaComposite would not handle semitransparent pixels correctly int[] srcData = ImageUtils.getPixelsAsArray(after); int[] destData = ImageUtils.getPixelsAsArray(dest); int[] prevData = ImageUtils.getPixelsAsArray(before); int length = srcData.length; for (int i = 0; i < length; i++) { int rgb = srcData[i]; int a = (rgb >>> 24) & 0xFF; int r = (rgb >>> 16) & 0xFF; int g = (rgb >>> 8) & 0xFF; int b = (rgb) & 0xFF; int prevRGB = prevData[i]; int prevA = (prevRGB >>> 24) & 0xFF; int prevR = (prevRGB >>> 16) & 0xFF; int prevG = (prevRGB >>> 8) & 0xFF; int prevB = prevRGB & 0xFF; a = (int) (prevA + fadeFactor * (a - prevA)); r = (int) (prevR + fadeFactor * (r - prevR)); g = (int) (prevG + fadeFactor * (g - prevG)); b = (int) (prevB + fadeFactor * (b - prevB)); destData[i] = (a << 24) | (r << 16) | (g << 8) | b; } return dest; } @Override public boolean supportsGray() { return false; } }