/* * 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.filters.gui; import com.bric.swing.GradientSlider; import com.jhlabs.image.Colormap; import com.jhlabs.image.ImageMath; import pixelitor.colors.ColorUtils; import javax.swing.*; import java.awt.Color; import java.awt.Dimension; import java.awt.Rectangle; import java.beans.PropertyChangeEvent; import static pixelitor.filters.gui.RandomizePolicy.ALLOW_RANDOMIZE; /** * Represents a gradient. (Note that unlike other filter parameter implementations, * this is not really a model for the GradientSlider GUI component, * the actual value is stored only inside the GradientSlider) */ public class GradientParam extends AbstractFilterParam { private static final String GRADIENT_SLIDER_USE_BEVEL = "GradientSlider.useBevel"; private GradientSlider gradientSlider; private final float[] defaultThumbPositions; private final Color[] defaultColors; private boolean trigger = true; // whether the running of the filter should be triggered public GradientParam(String name, Color firstColor, Color secondColor) { this(name, new float[]{0.0f, 1.0f}, new Color[]{firstColor, secondColor}); } public GradientParam(String name, float[] defaultThumbPositions, Color[] defaultColors) { this(name, defaultThumbPositions, defaultColors, ALLOW_RANDOMIZE); } public GradientParam(String name, float[] defaultThumbPositions, Color[] defaultColors, RandomizePolicy randomizePolicy) { super(name, randomizePolicy); this.defaultThumbPositions = defaultThumbPositions; this.defaultColors = defaultColors; // has to be created in the constructor because getValue() can be called early createGradientSlider(defaultThumbPositions, defaultColors); } private void createGradientSlider(float[] defaultThumbPositions, Color[] defaultColors) { gradientSlider = new GradientSlider(GradientSlider.HORIZONTAL, defaultThumbPositions, defaultColors); gradientSlider.addPropertyChangeListener(evt -> { if(shouldStartFilter(evt)) { adjustmentListener.paramAdjusted(); } }); gradientSlider.putClientProperty(GRADIENT_SLIDER_USE_BEVEL, "true"); // if there other controls in the dialog, they will determine the horizontal size gradientSlider.setPreferredSize(new Dimension(250, 30)); } private boolean shouldStartFilter(PropertyChangeEvent evt) { if (evt.getPropertyName().equals(GRADIENT_SLIDER_USE_BEVEL)) { return false; } if (evt.getPropertyName().equals("enabled")) { return false; } if (trigger && !gradientSlider.isValueAdjusting() && adjustmentListener != null) { String propertyName = evt.getPropertyName(); if (!"ancestor".equals(propertyName)) { if (!"selected thumb".equals(propertyName)) { return true; } } } return false; } @Override public JComponent createGUI() { return gradientSlider; } public Colormap getValue() { return v -> { Color c = (Color) gradientSlider.getValue(v); if (c == null) { throw new IllegalStateException("null color for v = " + v); } return c.getRGB(); }; } @Override public int getNrOfGridBagCols() { return 2; } @Override public void randomize() { if (randomizePolicy.allowRandomize()) { Color[] randomColors = new Color[defaultThumbPositions.length]; for (int i = 0; i < randomColors.length; i++) { randomColors[i] = ColorUtils.getRandomColor(false); } trigger = false; gradientSlider.setValues(defaultThumbPositions, randomColors); trigger = true; } } @Override public boolean isSetToDefault() { if (areThumbPositionsChanged()) { return false; } if (areColorsChanged()) { return false; } return true; } private boolean areThumbPositionsChanged() { float[] thumbPositions = gradientSlider.getThumbPositions(); if(thumbPositions.length != defaultThumbPositions.length) { return true; } for (int i = 0; i < thumbPositions.length; i++) { if (thumbPositions[i] != defaultThumbPositions[i]) { return true; } } return false; } private boolean areColorsChanged() { Object[] values = gradientSlider.getValues(); if (values.length != defaultColors.length) { return true; } for (int i = 0; i < defaultColors.length; i++) { if (!defaultColors[i].equals(values[i])) { return true; } } return false; } @Override public void reset(boolean triggerAction) { if (triggerAction) { gradientSlider.setValues(defaultThumbPositions, defaultColors); } else { trigger = false; gradientSlider.setValues(defaultThumbPositions, defaultColors); trigger = true; } } @Override void setEnabled(boolean b) { if (gradientSlider != null) { gradientSlider.setEnabled(b); } } @Override public void considerImageSize(Rectangle bounds) { } @Override public boolean canBeAnimated() { return true; } @Override public ParamState copyState() { return new GState(gradientSlider.getThumbPositions(), gradientSlider.getColors()); } @Override public void setState(ParamState state) { GState gr = (GState) state; trigger = false; createGradientSlider(gr.thumbPositions, gr.colors); trigger = true; } private static class GState implements ParamState { final float[] thumbPositions; final Color[] colors; public GState(float[] thumbPositions, Color[] colors) { this.thumbPositions = thumbPositions; this.colors = colors; } @Override public ParamState interpolate(ParamState endState, double progress) { // This will not work if the number of thumbs changes GState grEndState = (GState) endState; float[] interpolatedPositions = getInterpolatedPositions((float) progress, grEndState); Color[] interpolatedColors = getInterpolatedColors((float) progress, grEndState); return new GState(interpolatedPositions, interpolatedColors); } private float[] getInterpolatedPositions(float progress, GState grEndState) { float[] interpolatedPositions = new float[thumbPositions.length]; for (int i = 0; i < thumbPositions.length; i++) { float initial = thumbPositions[i]; float end = grEndState.thumbPositions[i]; float interpolated = ImageMath.lerp(progress, initial, end); interpolatedPositions[i] = interpolated; } return interpolatedPositions; } private Color[] getInterpolatedColors(float progress, GState grEndState) { Color[] interpolatedColors = new Color[colors.length]; for (int i = 0; i < colors.length; i++) { Color initial = colors[i]; Color end = grEndState.colors[i]; Color interpolated = ColorUtils.interpolateColor(initial, end, progress); interpolatedColors[i] = interpolated; } return interpolatedColors; } } @Override public String toString() { return String.format("%s[name = '%s']", getClass().getSimpleName(), getName()); } }