Changeset 17740 in josm for trunk/src/org


Ignore:
Timestamp:
2021-04-11T11:29:10+02:00 (3 years ago)
Author:
simon04
Message:

fix #20659 - Add contrast/gamma/... on image viewer

Location:
trunk/src/org/openstreetmap/josm/gui
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/dialogs/layer/LayerVisibilityAction.java

    r16438 r17740  
    7070    private static final double DEFAULT_COLORFUL_FACTOR = 1;
    7171    private static final double MAX_COLORFUL_FACTOR = 2;
    72     private final LayerListModel model;
     72    private final Supplier<Collection<Layer>> layerSupplier;
     73    private final Supplier<Collection<ImageryFilterSettings>> filterSettingsSupplier;
    7374    private final JPopupMenu popup;
    7475    private SideButton sideButton;
     
    8586     */
    8687    public LayerVisibilityAction(LayerListModel model) {
    87         this.model = model;
     88        this(model::getSelectedLayers, () ->
     89                Utils.transform(Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class), ImageryLayer::getFilterSettings));
     90    }
     91
     92    /**
     93     * Creates a new {@link LayerVisibilityAction}
     94     * @param layerSupplier supplies the layers which should be affected
     95     * @param filterSettingsSupplier supplies the filter settings which should be affecgted
     96     */
     97    public LayerVisibilityAction(Supplier<Collection<Layer>> layerSupplier, Supplier<Collection<ImageryFilterSettings>> filterSettingsSupplier) {
     98        this.layerSupplier = layerSupplier;
     99        this.filterSettingsSupplier = filterSettingsSupplier;
    88100        popup = new JPopupMenu();
    89101        // prevent popup close on mouse wheel move
     
    103115        addContentEntry(new GammaFilterSlider());
    104116        addContentEntry(new SharpnessSlider());
    105         addContentEntry(new ColorSelector(model::getSelectedLayers));
     117        addContentEntry(new ColorSelector());
    106118    }
    107119
     
    112124
    113125    void setVisibleFlag(boolean visible) {
    114         for (Layer l : model.getSelectedLayers()) {
     126        for (Layer l : layerSupplier.get()) {
    115127            l.setVisible(visible);
    116128        }
     
    136148
    137149    void updateValues() {
    138         List<Layer> layers = model.getSelectedLayers();
    139 
    140         boolean allVisible = true;
    141         boolean allHidden = true;
    142         for (Layer l : layers) {
    143             allVisible &= l.isVisible();
    144             allHidden &= !l.isVisible();
    145         }
    146 
    147150        for (LayerVisibilityMenuEntry slider : sliders) {
    148             slider.updateLayers(layers, allVisible, allHidden);
     151            slider.updateLayers();
    149152        }
    150153    }
     
    162165    @Override
    163166    public void updateEnabledState() {
    164         setEnabled(!model.getSelectedLayers().isEmpty());
     167        setEnabled(!layerSupplier.get().isEmpty() || !filterSettingsSupplier.get().isEmpty());
    165168    }
    166169
     
    181184        /**
    182185         * Update the displayed value depending on the current layers
    183          * @param layers The layers
    184          * @param allVisible <code>true</code> if all layers are visible
    185          * @param allHidden <code>true</code> if all layers are hidden
    186          */
    187         void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden);
     186         */
     187        void updateLayers();
    188188
    189189        /**
     
    207207
    208208        @Override
    209         public void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden) {
     209        public void updateLayers() {
     210            Collection<Layer> layers = layerSupplier.get();
     211            boolean allVisible = layers.stream().allMatch(Layer::isVisible);
     212            boolean allHidden = layers.stream().noneMatch(Layer::isVisible);
     213
    210214            setEnabled(!layers.isEmpty());
    211215            // TODO: Indicate tristate.
     
    222226     * This is a slider for a filter value.
    223227     * @author Michael Zangl
    224      *
    225      * @param <T> The layer type.
    226      */
    227     private abstract class AbstractFilterSlider<T extends Layer> extends JPanel implements LayerVisibilityMenuEntry {
     228     */
     229    private abstract class AbstractFilterSlider extends JPanel implements LayerVisibilityMenuEntry {
    228230        private final double minValue;
    229231        private final double maxValue;
    230         private final Class<T> layerClassFilter;
    231232
    232233        protected final JSlider slider = new JSlider(JSlider.HORIZONTAL);
     
    237238         * @param maxValue The maximum value to map to the right side.
    238239         * @param defaultValue The default value for resetting.
    239          * @param layerClassFilter The type of layer influenced by this filter.
    240          */
    241         AbstractFilterSlider(double minValue, double maxValue, double defaultValue, Class<T> layerClassFilter) {
     240         */
     241        AbstractFilterSlider(double minValue, double maxValue, double defaultValue) {
    242242            super(new GridBagLayout());
    243243            this.minValue = minValue;
    244244            this.maxValue = maxValue;
    245             this.layerClassFilter = layerClassFilter;
    246245
    247246            add(new JLabel(getIcon()), GBC.std().span(1, 2).insets(0, 0, 5, 0));
     
    284283         * @see #getRealValue()
    285284         */
    286         protected void onStateChanged() {
    287             Collection<T> layers = filterLayers(model.getSelectedLayers());
    288             for (T layer : layers) {
    289                 applyValueToLayer(layer);
    290             }
    291         }
     285        protected abstract void onStateChanged();
    292286
    293287        protected void mouseWheelMoved(MouseWheelEvent e) {
     
    307301        }
    308302
    309         abstract void applyValueToLayer(T layer);
    310 
    311303        protected double getRealValue() {
    312304            return convertToRealValue(slider.getValue());
     
    330322
    331323        public abstract String getLabel();
    332 
    333         @Override
    334         public void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden) {
    335             Collection<? extends Layer> usedLayers = filterLayers(layers);
    336             setVisible(!usedLayers.isEmpty());
    337             if (usedLayers.stream().noneMatch(Layer::isVisible)) {
    338                 slider.setEnabled(false);
    339             } else {
    340                 slider.setEnabled(true);
    341                 updateSliderWhileEnabled(usedLayers, allHidden);
    342             }
    343         }
    344 
    345         protected Collection<T> filterLayers(List<Layer> layers) {
    346             return Utils.filteredCollection(layers, layerClassFilter);
    347         }
    348 
    349         protected abstract void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden);
    350324
    351325        @Override
     
    361335     * @see Layer#setOpacity(double)
    362336     */
    363     class OpacitySlider extends AbstractFilterSlider<Layer> {
     337    class OpacitySlider extends AbstractFilterSlider {
    364338        /**
    365339         * Create a new {@link OpacitySlider}.
    366340         */
    367341        OpacitySlider() {
    368             super(0, 1, DEFAULT_OPACITY, Layer.class);
     342            super(0, 1, DEFAULT_OPACITY);
    369343            setLabels("0%", "50%", "100%");
    370344            slider.setToolTipText(tr("Adjust opacity of the layer.") + " " + tr("Double click to reset."));
     
    376350                setVisibleFlag(false);
    377351            } else {
    378                 super.onStateChanged();
     352                for (Layer layer : layerSupplier.get()) {
     353                    layer.setOpacity(getRealValue());
     354                }
    379355            }
    380356        }
     
    382358        @Override
    383359        protected void mouseWheelMoved(MouseWheelEvent e) {
    384             if (!isEnabled() && !filterLayers(model.getSelectedLayers()).isEmpty() && e.getPreciseWheelRotation() < 0) {
     360            if (!isEnabled() && !layerSupplier.get().isEmpty() && e.getPreciseWheelRotation() < 0) {
    385361                // make layer visible and set the value.
    386362                // this allows users to use the mouse wheel to make the layer visible if it was hidden previously.
     
    393369
    394370        @Override
    395         protected void applyValueToLayer(Layer layer) {
    396             layer.setOpacity(getRealValue());
    397         }
    398 
    399         @Override
    400         protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
     371        public void updateLayers() {
     372            Collection<Layer> usedLayers = layerSupplier.get();
     373            setVisible(!usedLayers.isEmpty());
     374            if (usedLayers.stream().noneMatch(Layer::isVisible)) {
     375                slider.setEnabled(false);
     376                return;
     377            }
     378            slider.setEnabled(true);
    401379            double opacity = usedLayers.stream()
    402380                    .mapToDouble(Layer::getOpacity)
     
    432410     * @see ImageryFilterSettings#setGamma(double)
    433411     */
    434     private class GammaFilterSlider extends AbstractFilterSlider<ImageryLayer> {
     412    private class GammaFilterSlider extends AbstractFilterSlider {
    435413
    436414        /**
     
    438416         */
    439417        GammaFilterSlider() {
    440             super(-1, 1, DEFAULT_GAMMA_VALUE, ImageryLayer.class);
     418            super(-1, 1, DEFAULT_GAMMA_VALUE);
    441419            setLabels("0", "1", "∞");
    442420            slider.setToolTipText(tr("Adjust gamma value of the layer.") + " " + tr("Double click to reset."));
     
    444422
    445423        @Override
    446         protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
    447             double gamma = ((ImageryLayer) usedLayers.iterator().next()).getFilterSettings().getGamma();
    448             setRealValue(mapGammaToInterval(gamma));
    449         }
    450 
    451         @Override
    452         protected void applyValueToLayer(ImageryLayer layer) {
    453             layer.getFilterSettings().setGamma(mapIntervalToGamma(getRealValue()));
     424        public void updateLayers() {
     425            Collection<ImageryFilterSettings> settings = filterSettingsSupplier.get();
     426            setVisible(!settings.isEmpty());
     427            if (!settings.isEmpty()) {
     428                double gamma = settings.iterator().next().getGamma();
     429                setRealValue(mapGammaToInterval(gamma));
     430            }
     431        }
     432
     433        @Override
     434        protected void onStateChanged() {
     435            for (ImageryFilterSettings settings : filterSettingsSupplier.get()) {
     436                settings.setGamma(mapIntervalToGamma(getRealValue()));
     437            }
    454438        }
    455439
     
    492476     * @see ImageryFilterSettings#setSharpenLevel(double)
    493477     */
    494     private class SharpnessSlider extends AbstractFilterSlider<ImageryLayer> {
     478    private class SharpnessSlider extends AbstractFilterSlider {
    495479
    496480        /**
     
    498482         */
    499483        SharpnessSlider() {
    500             super(0, MAX_SHARPNESS_FACTOR, DEFAULT_SHARPNESS_FACTOR, ImageryLayer.class);
     484            super(0, MAX_SHARPNESS_FACTOR, DEFAULT_SHARPNESS_FACTOR);
    501485            setLabels(trc("image sharpness", "blurred"), trc("image sharpness", "normal"), trc("image sharpness", "sharp"));
    502486            slider.setToolTipText(tr("Adjust sharpness/blur value of the layer.") + " " + tr("Double click to reset."));
     
    504488
    505489        @Override
    506         protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
    507             setRealValue(((ImageryLayer) usedLayers.iterator().next()).getFilterSettings().getSharpenLevel());
    508         }
    509 
    510         @Override
    511         protected void applyValueToLayer(ImageryLayer layer) {
    512             layer.getFilterSettings().setSharpenLevel(getRealValue());
     490        public void updateLayers() {
     491            Collection<ImageryFilterSettings> settings = filterSettingsSupplier.get();
     492            setVisible(!settings.isEmpty());
     493            if (!settings.isEmpty()) {
     494                setRealValue(settings.iterator().next().getSharpenLevel());
     495            }
     496        }
     497
     498        @Override
     499        protected void onStateChanged() {
     500            for (ImageryFilterSettings settings : filterSettingsSupplier.get()) {
     501                settings.setSharpenLevel(getRealValue());
     502            }
    513503        }
    514504
     
    530520     * @see ImageryFilterSettings#setColorfulness(double)
    531521     */
    532     private class ColorfulnessSlider extends AbstractFilterSlider<ImageryLayer> {
     522    private class ColorfulnessSlider extends AbstractFilterSlider {
    533523
    534524        /**
     
    536526         */
    537527        ColorfulnessSlider() {
    538             super(0, MAX_COLORFUL_FACTOR, DEFAULT_COLORFUL_FACTOR, ImageryLayer.class);
     528            super(0, MAX_COLORFUL_FACTOR, DEFAULT_COLORFUL_FACTOR);
    539529            setLabels(trc("image colorfulness", "less"), trc("image colorfulness", "normal"), trc("image colorfulness", "more"));
    540530            slider.setToolTipText(tr("Adjust colorfulness of the layer.") + " " + tr("Double click to reset."));
     
    542532
    543533        @Override
    544         protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
    545             setRealValue(((ImageryLayer) usedLayers.iterator().next()).getFilterSettings().getColorfulness());
    546         }
    547 
    548         @Override
    549         protected void applyValueToLayer(ImageryLayer layer) {
    550             layer.getFilterSettings().setColorfulness(getRealValue());
     534        public void updateLayers() {
     535            Collection<ImageryFilterSettings> settings = filterSettingsSupplier.get();
     536            setVisible(!settings.isEmpty());
     537            if (!settings.isEmpty()) {
     538                setRealValue(settings.iterator().next().getColorfulness());
     539            }
     540        }
     541
     542        @Override
     543        protected void onStateChanged() {
     544            for (ImageryFilterSettings settings : filterSettingsSupplier.get()) {
     545                settings.setColorfulness(getRealValue());
     546            }
    551547        }
    552548
     
    566562     * @author Michael Zangl
    567563     */
    568     private static class ColorSelector extends JPanel implements LayerVisibilityMenuEntry {
    569 
    570         private static final Border NORMAL_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2);
    571         private static final Border SELECTED_BORDER = BorderFactory.createLineBorder(Color.BLACK, 2);
     564    private class ColorSelector extends JPanel implements LayerVisibilityMenuEntry {
     565
     566        private final Border NORMAL_BORDER = BorderFactory.createEmptyBorder(2, 2, 2, 2);
     567        private final Border SELECTED_BORDER = BorderFactory.createLineBorder(Color.BLACK, 2);
    572568
    573569        // TODO: Nicer color palette
    574         private static final Color[] COLORS = {
     570        private final Color[] COLORS = {
    575571                Color.RED,
    576572                Color.ORANGE,
     
    581577                Color.GRAY,
    582578        };
    583         private final Supplier<List<Layer>> layerSupplier;
    584579        private final HashMap<Color, JPanel> panels = new HashMap<>();
    585580
    586         ColorSelector(Supplier<List<Layer>> layerSupplier) {
     581        ColorSelector() {
    587582            super(new GridBagLayout());
    588             this.layerSupplier = layerSupplier;
    589583            add(new JLabel(tr("Color")), GBC.eol().insets(24 + 10, 0, 0, 0));
    590584            for (Color color : COLORS) {
     
    604598                @Override
    605599                public void mouseClicked(MouseEvent e) {
    606                     List<Layer> layers = layerSupplier.get();
     600                    Collection<Layer> layers = layerSupplier.get();
    607601                    for (Layer l : layers) {
    608602                        if (l instanceof GpxLayer) {
     
    623617
    624618        @Override
    625         public void updateLayers(List<Layer> layers, boolean allVisible, boolean allHidden) {
     619        public void updateLayers() {
     620            Collection<Layer> layers = layerSupplier.get();
    626621            List<Color> colors = layers.stream().filter(l -> l instanceof GpxLayer)
    627622                    .map(l -> ((GpxLayer) l).getColor())
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageDisplay.java

    r17553 r17740  
    3232import org.openstreetmap.josm.data.preferences.BooleanProperty;
    3333import org.openstreetmap.josm.data.preferences.DoubleProperty;
     34import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
     35import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings.FilterChangeListener;
    3436import org.openstreetmap.josm.spi.preferences.Config;
    3537import org.openstreetmap.josm.spi.preferences.PreferenceChangeEvent;
     
    3739import org.openstreetmap.josm.tools.Destroyable;
    3840import org.openstreetmap.josm.tools.ExifReader;
     41import org.openstreetmap.josm.tools.ImageProcessor;
    3942import org.openstreetmap.josm.tools.ImageProvider;
    4043import org.openstreetmap.josm.tools.Logging;
     
    4548 * Offers basic mouse interaction (zoom, drag) and on-screen text.
    4649 */
    47 public class ImageDisplay extends JComponent implements Destroyable, PreferenceChangedListener {
     50public class ImageDisplay extends JComponent implements Destroyable, PreferenceChangedListener, FilterChangeListener {
    4851
    4952    /** The file that is currently displayed */
     
    5154
    5255    /** The image currently displayed */
    53     private transient Image image;
     56    private transient BufferedImage image;
     57
     58    /**
     59     * Process the image before it is being displayed
     60     */
     61    private final ImageProcessor imageProcessor;
    5462
    5563    /** The image currently displayed */
     
    295303        @Override
    296304        public void run() {
    297             Image img;
     305            BufferedImage img;
    298306            try {
    299307                img = ImageIO.read(file);
     
    690698     */
    691699    public ImageDisplay() {
     700        this(image -> image);
     701    }
     702
     703    public ImageDisplay(ImageProcessor imageProcessor) {
    692704        addMouseListener(imgMouseListener);
    693705        addMouseWheelListener(imgMouseListener);
     
    695707        Config.getPref().addPreferenceChangeListener(this);
    696708        preferenceChanged(null);
     709        this.imageProcessor = imageProcessor;
     710        if (imageProcessor instanceof ImageryFilterSettings) {
     711            ((ImageryFilterSettings) imageProcessor).addFilterChangeListener(this);
     712        }
    697713    }
    698714
     
    703719        removeMouseMotionListener(imgMouseListener);
    704720        Config.getPref().removePreferenceChangeListener(this);
     721        if (imageProcessor instanceof ImageryFilterSettings) {
     722            ((ImageryFilterSettings) imageProcessor).removeFilterChangeListener(this);
     723        }
    705724    }
    706725
     
    744763
    745764    @Override
     765    public void filterChanged() {
     766        repaint();
     767    }
     768
     769    @Override
    746770    public void paintComponent(Graphics g) {
    747771        ImageEntry entry;
    748         Image image;
     772        BufferedImage image;
    749773        VisRect visibleRect;
    750774        boolean errorLoading;
     
    818842                    r.x = r.y = 0;
    819843                }
     844            }
     845
     846            if (image != null) {
     847                image = imageProcessor.process(image);
    820848            }
    821849
  • trunk/src/org/openstreetmap/josm/gui/layer/geoimage/ImageViewerDialog.java

    r17733 r17740  
    3636import org.openstreetmap.josm.gui.dialogs.DialogsPanel.Action;
    3737import org.openstreetmap.josm.gui.dialogs.ToggleDialog;
     38import org.openstreetmap.josm.gui.dialogs.layer.LayerVisibilityAction;
    3839import org.openstreetmap.josm.gui.layer.Layer;
    3940import org.openstreetmap.josm.gui.layer.LayerManager.LayerAddEvent;
     
    4344import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
    4445import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
     46import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
    4547import org.openstreetmap.josm.tools.ImageProvider;
    4648import org.openstreetmap.josm.tools.Logging;
     
    5355 */
    5456public final class ImageViewerDialog extends ToggleDialog implements LayerChangeListener, ActiveLayerChangeListener, ImageDataUpdateListener {
     57
     58    private final ImageryFilterSettings imageryFilterSettings = new ImageryFilterSettings();
    5559
    5660    private final ImageZoomAction imageZoomAction = new ImageZoomAction();
     
    6468    private final ImageLastAction imageLastAction = new ImageLastAction();
    6569    private final ImageCopyPathAction imageCopyPathAction = new ImageCopyPathAction();
    66 
    67     private final ImageDisplay imgDisplay = new ImageDisplay();
     70    private final LayerVisibilityAction visibilityAction = new LayerVisibilityAction(Collections::emptyList,
     71            () -> Collections.singleton(imageryFilterSettings));
     72
     73    private final ImageDisplay imgDisplay = new ImageDisplay(imageryFilterSettings);
    6874    private boolean centerView;
    6975
     
    162168        buttons.add(Box.createRigidArea(new Dimension(7, 0)));
    163169        buttons.add(btnCopyPath);
     170        buttons.add(Box.createRigidArea(new Dimension(7, 0)));
     171        buttons.add(new JButton(visibilityAction));
    164172
    165173        JPanel bottomPane = new JPanel(new GridBagLayout());
  • trunk/src/org/openstreetmap/josm/gui/layer/imagery/ImageryFilterSettings.java

    r15659 r17740  
    22package org.openstreetmap.josm.gui.layer.imagery;
    33
     4import java.awt.image.BufferedImage;
    45import java.util.Arrays;
    56import java.util.List;
     
    1314 * @since 10547
    1415 */
    15 public class ImageryFilterSettings {
     16public class ImageryFilterSettings implements ImageProcessor {
    1617
    1718    protected GammaImageProcessor gammaImageProcessor = new GammaImageProcessor();
     
    8586    }
    8687
     88    @Override
     89    public BufferedImage process(BufferedImage image) {
     90        for (ImageProcessor processor : getProcessors()) {
     91            image = processor.process(image);
     92        }
     93        return image;
     94    }
     95
    8796    /**
    8897     * Adds a filter change listener
Note: See TracChangeset for help on using the changeset viewer.