Changeset 10142 in josm for trunk/src


Ignore:
Timestamp:
2016-04-12T23:08:55+02:00 (8 years ago)
Author:
Don-vip
Message:

fix #12752 - Add more image filters (patch by michael2402, modified)

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

Legend:

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

    r10011 r10142  
    532532        protected static final int SLIDER_STEPS = 100;
    533533        private static final double MAX_GAMMA_FACTOR = 2;
     534        private static final double MAX_SHARPNESS_FACTOR = 2;
     535        private static final double MAX_COLORFUL_FACTOR = 2;
    534536        private final LayerListModel model;
    535537        private final JPopupMenu popup;
    536         private JSlider opacitySlider;
    537         private JSlider gammaSlider;
    538538        private SideButton sideButton;
    539539        private JCheckBox visibilityCheckbox;
     540        final OpacitySlider opacitySlider = new OpacitySlider();
     541        private final ArrayList<FilterSlider<?>> sliders = new ArrayList<>();
    540542
    541543        /**
     
    560562                @Override
    561563                public void stateChanged(ChangeEvent e) {
    562                     setVisible(visibilityCheckbox.isSelected());
     564                    setVisibleFlag(visibilityCheckbox.isSelected());
    563565                }
    564566            });
    565567            content.add(visibilityCheckbox, GBC.eop());
    566568
    567             content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "transparency")), GBC.std().span(1, 2).insets(0, 0, 5, 0));
    568             content.add(new JLabel(tr("Opacity")), GBC.eol());
    569             opacitySlider = new JSlider(JSlider.HORIZONTAL);
    570             opacitySlider.setMaximum(SLIDER_STEPS);
    571             opacitySlider.addChangeListener(new ChangeListener() {
    572                 @Override
    573                 public void stateChanged(ChangeEvent e) {
    574                     setOpacityValue(readOpacityValue(), opacitySlider.getValueIsAdjusting());
    575                 }
    576             });
    577             opacitySlider.setToolTipText(tr("Adjust opacity of the layer."));
    578             content.add(opacitySlider, GBC.eop());
    579 
    580             content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "gamma")), GBC.std().span(1, 2).insets(0, 0, 5, 0));
    581             content.add(new JLabel(tr("Gamma")), GBC.eol());
    582             gammaSlider = new JSlider(JSlider.HORIZONTAL);
    583             gammaSlider.setMaximum(SLIDER_STEPS);
    584             gammaSlider.addChangeListener(new ChangeListener() {
    585                 @Override
    586                 public void stateChanged(ChangeEvent e) {
    587                     setGammaValue(readGammaValue());
    588                 }
    589             });
    590             gammaSlider.setToolTipText(tr("Adjust gamma value of the layer."));
    591             content.add(gammaSlider, GBC.eol());
    592         }
    593 
    594         protected double readOpacityValue() {
    595             return (double) opacitySlider.getValue() / SLIDER_STEPS;
    596         }
    597 
    598         protected double readGammaValue() {
    599             return (double) gammaSlider.getValue() / SLIDER_STEPS * MAX_GAMMA_FACTOR;
    600         }
    601 
    602         protected void setVisible(boolean visible) {
     569            addSlider(content, opacitySlider);
     570            addSlider(content, new ColorfulnessSlider());
     571            addSlider(content, new GammaFilterSlider());
     572            addSlider(content, new SharpnessSlider());
     573        }
     574
     575        private void addSlider(JPanel content, FilterSlider<?> slider) {
     576            content.add(new JLabel(slider.getIcon()), GBC.std().span(1, 2).insets(0, 0, 5, 0));
     577            content.add(new JLabel(slider.getLabel()), GBC.eol());
     578            content.add(slider, GBC.eop());
     579            sliders.add(slider);
     580        }
     581
     582        protected void setVisibleFlag(boolean visible) {
    603583            for (Layer l : model.getSelectedLayers()) {
    604584                l.setVisible(visible);
    605585            }
    606586            updateValues();
    607         }
    608 
    609         protected void setOpacityValue(double value, boolean adjusting) {
    610             if (value <= 0 && !adjusting) {
    611                 setVisible(false);
    612             } else {
    613                 for (Layer l : model.getSelectedLayers()) {
    614                     l.setOpacity(value);
    615                 }
    616             }
    617         }
    618 
    619         protected void setGammaValue(double value) {
    620             for (ImageryLayer imageryLayer : Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class)) {
    621                 imageryLayer.setGamma(value);
    622             }
    623587        }
    624588
     
    648612            visibilityCheckbox.setSelected(allVisible && !allHidden);
    649613
    650             updateOpacitySlider(layers, allHidden);
    651 
    652             updateGammaSlider(layers, allHidden);
    653         }
    654 
    655         private void updateGammaSlider(List<Layer> layers, boolean allHidden) {
    656             Collection<ImageryLayer> gammaLayers = Utils.filteredCollection(layers, ImageryLayer.class);
    657             if (gammaLayers.isEmpty() || allHidden) {
    658                 gammaSlider.setEnabled(false);
    659             } else {
    660                 gammaSlider.setEnabled(true);
    661                 double gamma = gammaLayers.iterator().next().getGamma();
    662                 gammaSlider.setValue((int) (gamma * SLIDER_STEPS / MAX_GAMMA_FACTOR));
    663             }
    664         }
    665 
    666         private void updateOpacitySlider(List<Layer> layers, boolean allHidden) {
    667             if (layers.isEmpty() || allHidden) {
    668                 opacitySlider.setEnabled(false);
    669             } else {
    670                 opacitySlider.setEnabled(true);
     614            for (FilterSlider<?> slider : sliders) {
     615                slider.updateSlider(layers, allHidden);
     616            }
     617        }
     618
     619        @Override
     620        public boolean supportLayers(List<Layer> layers) {
     621            return !layers.isEmpty();
     622        }
     623
     624        @Override
     625        public Component createMenuComponent() {
     626            return new JMenuItem(this);
     627        }
     628
     629        @Override
     630        public void updateEnabledState() {
     631            setEnabled(!model.getSelectedLayers().isEmpty());
     632        }
     633
     634        /**
     635         * Sets the corresponding side button.
     636         * @param sideButton the corresponding side button
     637         */
     638        void setCorrespondingSideButton(SideButton sideButton) {
     639            this.sideButton = sideButton;
     640        }
     641
     642        /**
     643         * This is a slider for a filter value.
     644         * @author Michael Zangl
     645         *
     646         * @param <T> The layer type.
     647         */
     648        private abstract class FilterSlider<T extends Layer> extends JSlider {
     649            private final double minValue;
     650            private final double maxValue;
     651            private final Class<T> layerClassFilter;
     652
     653            /**
     654             * Create a new filter slider.
     655             * @param minValue The minimum value to map to the left side.
     656             * @param maxValue The maximum value to map to the right side.
     657             * @param layerClassFilter The type of layer influenced by this filter.
     658             */
     659            FilterSlider(double minValue, double maxValue, Class<T> layerClassFilter) {
     660                super(JSlider.HORIZONTAL);
     661                this.minValue = minValue;
     662                this.maxValue = maxValue;
     663                this.layerClassFilter = layerClassFilter;
     664                setMaximum(SLIDER_STEPS);
     665                int tick = convertFromRealValue(1);
     666                setMinorTickSpacing(tick);
     667                setMajorTickSpacing(tick);
     668                setPaintTicks(true);
     669
     670                addChangeListener(new ChangeListener() {
     671                    @Override
     672                    public void stateChanged(ChangeEvent e) {
     673                        onStateChanged();
     674                    }
     675                });
     676            }
     677
     678            /**
     679             * Called whenever the state of the slider was changed.
     680             * @see #getValueIsAdjusting()
     681             * @see #getRealValue()
     682             */
     683            protected void onStateChanged() {
     684                Collection<T> layers = filterLayers(model.getSelectedLayers());
     685                for (T layer : layers) {
     686                    applyValueToLayer(layer);
     687                }
     688            }
     689
     690            protected void applyValueToLayer(T layer) {
     691            }
     692
     693            protected double getRealValue() {
     694                return convertToRealValue(getValue());
     695            }
     696
     697            protected double convertToRealValue(int value) {
     698                double s = (double) value / SLIDER_STEPS;
     699                return s * maxValue + (1-s) * minValue;
     700            }
     701
     702            protected void setRealValue(double value) {
     703                setValue(convertFromRealValue(value));
     704            }
     705
     706            protected int convertFromRealValue(double value) {
     707                int i = (int) ((value - minValue) / (maxValue - minValue) * SLIDER_STEPS + .5);
     708                if (i < getMinimum()) {
     709                    return getMinimum();
     710                } else if (i > getMaximum()) {
     711                    return getMaximum();
     712                } else {
     713                    return i;
     714                }
     715            }
     716
     717            public abstract ImageIcon getIcon();
     718
     719            public abstract String getLabel();
     720
     721            public void updateSlider(List<Layer> layers, boolean allHidden) {
     722                Collection<? extends Layer> usedLayers = filterLayers(layers);
     723                if (usedLayers.isEmpty() || allHidden) {
     724                    setEnabled(false);
     725                } else {
     726                    setEnabled(true);
     727                    updateSliderWhileEnabled(usedLayers, allHidden);
     728                }
     729            }
     730
     731            protected Collection<T> filterLayers(List<Layer> layers) {
     732                return Utils.filteredCollection(layers, layerClassFilter);
     733            }
     734
     735            protected abstract void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden);
     736        }
     737
     738        /**
     739         * This slider allows you to change the opacity of a layer.
     740         *
     741         * @author Michael Zangl
     742         * @see Layer#setOpacity(double)
     743         */
     744        class OpacitySlider extends FilterSlider<Layer> {
     745            /**
     746             * Creaate a new {@link OpacitySlider}.
     747             */
     748            OpacitySlider() {
     749                super(0, 1, Layer.class);
     750                setToolTipText(tr("Adjust opacity of the layer."));
     751
     752            }
     753
     754            @Override
     755            protected void onStateChanged() {
     756                if (getRealValue() <= 0.001 && !getValueIsAdjusting()) {
     757                    setVisibleFlag(false);
     758                } else {
     759                    super.onStateChanged();
     760                }
     761            }
     762
     763            @Override
     764            protected void applyValueToLayer(Layer layer) {
     765                layer.setOpacity(getRealValue());
     766            }
     767
     768            @Override
     769            protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
    671770                double opacity = 0;
    672                 for (Layer l : layers) {
     771                for (Layer l : usedLayers) {
    673772                    opacity += l.getOpacity();
    674773                }
    675                 opacity /= layers.size();
     774                opacity /= usedLayers.size();
    676775                if (opacity == 0) {
    677776                    opacity = 1;
    678                     setOpacityValue(opacity, false);
    679                 }
    680                 opacitySlider.setValue((int) (opacity * SLIDER_STEPS));
    681             }
    682         }
    683 
    684         @Override
    685         public boolean supportLayers(List<Layer> layers) {
    686             return !layers.isEmpty();
    687         }
    688 
    689         @Override
    690         public Component createMenuComponent() {
    691             return new JMenuItem(this);
    692         }
    693 
    694         @Override
    695         public void updateEnabledState() {
    696             setEnabled(!model.getSelectedLayers().isEmpty());
    697         }
    698 
    699         /**
    700          * Sets the corresponding side button.
    701          * @param sideButton the corresponding side button
    702          */
    703         void setCorrespondingSideButton(SideButton sideButton) {
    704             this.sideButton = sideButton;
     777                    setVisibleFlag(true);
     778                }
     779                setRealValue(opacity);
     780            }
     781
     782            @Override
     783            public String getLabel() {
     784                return tr("Opacity");
     785            }
     786
     787            @Override
     788            public ImageIcon getIcon() {
     789                return ImageProvider.get("dialogs/layerlist", "transparency");
     790            }
     791
     792            @Override
     793            public String toString() {
     794                return "OpacitySlider [getRealValue()=" + getRealValue() + "]";
     795            }
     796        }
     797
     798        /**
     799         * This slider allows you to change the gamma value of a layer.
     800         *
     801         * @author Michael Zangl
     802         * @see ImageryLayer#setGamma(double)
     803         */
     804        private class GammaFilterSlider extends FilterSlider<ImageryLayer> {
     805
     806            /**
     807             * Create a new {@link GammaFilterSlider}
     808             */
     809            GammaFilterSlider() {
     810                super(0, MAX_GAMMA_FACTOR, ImageryLayer.class);
     811                setToolTipText(tr("Adjust gamma value of the layer."));
     812            }
     813
     814            @Override
     815            protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
     816                double gamma = ((ImageryLayer) usedLayers.iterator().next()).getGamma();
     817                setRealValue(gamma);
     818            }
     819
     820            @Override
     821            protected void applyValueToLayer(ImageryLayer layer) {
     822                layer.setGamma(getRealValue());
     823            }
     824
     825            @Override
     826            public ImageIcon getIcon() {
     827               return ImageProvider.get("dialogs/layerlist", "gamma");
     828            }
     829
     830            @Override
     831            public String getLabel() {
     832                return tr("Gamma");
     833            }
     834        }
     835
     836        /**
     837         * This slider allows you to change the sharpness of a layer.
     838         *
     839         * @author Michael Zangl
     840         * @see ImageryLayer#setSharpenLevel(double)
     841         */
     842        private class SharpnessSlider extends FilterSlider<ImageryLayer> {
     843
     844            /**
     845             * Creates a new {@link SharpnessSlider}
     846             */
     847            SharpnessSlider() {
     848                super(0, MAX_SHARPNESS_FACTOR, ImageryLayer.class);
     849                setToolTipText(tr("Adjust sharpness/blur value of the layer."));
     850            }
     851
     852            @Override
     853            protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
     854                setRealValue(((ImageryLayer) usedLayers.iterator().next()).getSharpenLevel());
     855            }
     856
     857            @Override
     858            protected void applyValueToLayer(ImageryLayer layer) {
     859                layer.setSharpenLevel(getRealValue());
     860            }
     861
     862            @Override
     863            public ImageIcon getIcon() {
     864               return ImageProvider.get("dialogs/layerlist", "sharpness");
     865            }
     866
     867            @Override
     868            public String getLabel() {
     869                return tr("Sharpness");
     870            }
     871        }
     872
     873        /**
     874         * This slider allows you to change the colorfulness of a layer.
     875         *
     876         * @author Michael Zangl
     877         * @see ImageryLayer#setColorfulness(double)
     878         */
     879        private class ColorfulnessSlider extends FilterSlider<ImageryLayer> {
     880
     881            /**
     882             * Create a new {@link ColorfulnessSlider}
     883             */
     884            ColorfulnessSlider() {
     885                super(0, MAX_COLORFUL_FACTOR, ImageryLayer.class);
     886                setToolTipText(tr("Adjust colorfulness of the layer."));
     887            }
     888
     889            @Override
     890            protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) {
     891                setRealValue(((ImageryLayer) usedLayers.iterator().next()).getColorfulness());
     892            }
     893
     894            @Override
     895            protected void applyValueToLayer(ImageryLayer layer) {
     896                layer.setColorfulness(getRealValue());
     897            }
     898
     899            @Override
     900            public ImageIcon getIcon() {
     901               return ImageProvider.get("dialogs/layerlist", "colorfulness");
     902            }
     903
     904            @Override
     905            public String getLabel() {
     906                return tr("Colorfulness");
     907            }
    705908        }
    706909    }
  • trunk/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java

    r10001 r10142  
    259259    }
    260260
     261    @Override
     262    public void setSharpenLevel(double sharpenLevel) {
     263        super.setSharpenLevel(sharpenLevel);
     264        redraw();
     265    }
     266
     267    @Override
     268    public void setColorfulness(double colorfulness) {
     269        super.setColorfulness(colorfulness);
     270        redraw();
     271    }
     272
    261273    /**
    262274     * Marks layer as needing redraw on offset change
  • trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java

    r9983 r10142  
    99import java.awt.Component;
    1010import java.awt.GridBagLayout;
     11import java.awt.Rectangle;
     12import java.awt.RenderingHints;
    1113import java.awt.Transparency;
    1214import java.awt.event.ActionEvent;
     15import java.awt.geom.Point2D;
     16import java.awt.geom.Rectangle2D;
    1317import java.awt.image.BufferedImage;
    1418import java.awt.image.BufferedImageOp;
     19import java.awt.image.ColorModel;
    1520import java.awt.image.ConvolveOp;
     21import java.awt.image.DataBuffer;
     22import java.awt.image.DataBufferByte;
    1623import java.awt.image.Kernel;
    1724import java.awt.image.LookupOp;
     
    7077
    7178    protected GammaImageProcessor gammaImageProcessor = new GammaImageProcessor();
     79    protected SharpenImageProcessor sharpenImageProcessor = new SharpenImageProcessor();
     80    protected ColorfulImageProcessor collorfulnessImageProcessor = new ColorfulImageProcessor();
    7281
    7382    private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this);
     
    8796            icon = ImageProvider.get("imagery_small");
    8897        }
    89         addImageProcessor(createSharpener(PROP_SHARPEN_LEVEL.get()));
     98        addImageProcessor(collorfulnessImageProcessor);
    9099        addImageProcessor(gammaImageProcessor);
     100        addImageProcessor(sharpenImageProcessor);
     101        sharpenImageProcessor.setSharpenLevel(1 + PROP_SHARPEN_LEVEL.get() / 2f);
    91102    }
    92103
     
    233244    }
    234245
    235     public ImageProcessor createSharpener(int sharpenLevel) {
    236         final Kernel kernel;
    237         if (sharpenLevel == 1) {
    238             kernel = new Kernel(3, 3, new float[]{-0.25f, -0.5f, -0.25f, -0.5f, 4, -0.5f, -0.25f, -0.5f, -0.25f});
    239         } else if (sharpenLevel == 2) {
    240             kernel = new Kernel(3, 3, new float[]{-0.5f, -1, -0.5f, -1, 7, -1, -0.5f, -1, -0.5f});
    241         } else {
    242             return null;
    243         }
    244         BufferedImageOp op = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
    245         return createImageProcessor(op, false);
    246     }
    247 
    248246    /**
    249247     * An image processor which adjusts the gamma value of an image.
     
    301299
    302300    /**
     301     * Sharpens or blurs the image, depending on the sharpen value.
     302     * <p>
     303     * A positive sharpen level means that we sharpen the image.
     304     * <p>
     305     * A negative sharpen level let's us blur the image. -1 is the most useful value there.
     306     *
     307     * @author Michael Zangl
     308     */
     309    public static class SharpenImageProcessor implements ImageProcessor {
     310        private float sharpenLevel = 0;
     311        private ConvolveOp op;
     312
     313        private static float[] KERNEL_IDENTITY = new float[] {
     314            0, 0, 0,
     315            0, 1, 0,
     316            0, 0, 0
     317        };
     318
     319        private static float[] KERNEL_BLUR = new float[] {
     320            1f / 16, 2f / 16, 1f / 16,
     321            2f / 16, 4f / 16, 2f / 16,
     322            1f / 16, 2f / 16, 1f / 16
     323        };
     324
     325        private static float[] KERNEL_SHARPEN = new float[] {
     326            -.5f, -1f, -.5f,
     327             -1f,  7,  -1f,
     328            -.5f, -1f, -.5f
     329        };
     330
     331        /**
     332         * Gets the current sharpen level.
     333         * @return The level.
     334         */
     335        public float getSharpenLevel() {
     336            return sharpenLevel;
     337        }
     338
     339        /**
     340         * Sets the sharpening level.
     341         * @param sharpenLevel The level. Clamped to be positive or 0.
     342         */
     343        public void setSharpenLevel(float sharpenLevel) {
     344            if (sharpenLevel < 0) {
     345                this.sharpenLevel = 0;
     346            } else {
     347                this.sharpenLevel = sharpenLevel;
     348            }
     349
     350            if (this.sharpenLevel < 0.95) {
     351                op = generateMixed(this.sharpenLevel, KERNEL_IDENTITY, KERNEL_BLUR);
     352            } else if (this.sharpenLevel > 1.05) {
     353                op = generateMixed(this.sharpenLevel - 1, KERNEL_SHARPEN, KERNEL_IDENTITY);
     354            } else {
     355                op = null;
     356            }
     357        }
     358
     359        private ConvolveOp generateMixed(float aFactor, float[] a, float[] b) {
     360            if (a.length != 9 || b.length != 9) {
     361                throw new IllegalArgumentException("Illegal kernel array length.");
     362            }
     363            float[] values = new float[9];
     364            for (int i = 0; i < values.length; i++) {
     365                values[i] = aFactor * a[i] + (1 - aFactor) * b[i];
     366            }
     367            return new ConvolveOp(new Kernel(3, 3, values), ConvolveOp.EDGE_NO_OP, null);
     368        }
     369
     370        @Override
     371        public BufferedImage process(BufferedImage image) {
     372            if (op != null) {
     373                return op.filter(image, null);
     374            } else {
     375                return image;
     376            }
     377        }
     378
     379        @Override
     380        public String toString() {
     381            return "SharpenImageProcessor [sharpenLevel=" + sharpenLevel + "]";
     382        }
     383    }
     384
     385    /**
     386     * Adds or removes the colorfulness of the image.
     387     *
     388     * @author Michael Zangl
     389     */
     390    public static class ColorfulImageProcessor implements ImageProcessor {
     391        private ColorfulFilter op = null;
     392        private double colorfulness = 1;
     393
     394        /**
     395         * Gets the colorfulness value.
     396         * @return The value
     397         */
     398        public double getColorfulness() {
     399            return colorfulness;
     400        }
     401
     402        /**
     403         * Sets the colorfulness value. Clamps it to 0+
     404         * @param colorfulness The value
     405         */
     406        public void setColorfulness(double colorfulness) {
     407            if (colorfulness < 0) {
     408                this.colorfulness = 0;
     409            } else {
     410                this.colorfulness = colorfulness;
     411            }
     412
     413            if (this.colorfulness < .95 || this.colorfulness > 1.05) {
     414                op = new ColorfulFilter(this.colorfulness);
     415            } else {
     416                op = null;
     417            }
     418        }
     419
     420        @Override
     421        public BufferedImage process(BufferedImage image) {
     422            if (op != null) {
     423                return op.filter(image, null);
     424            } else {
     425                return image;
     426            }
     427        }
     428
     429        @Override
     430        public String toString() {
     431            return "ColorfulImageProcessor [colorfulness=" + colorfulness + "]";
     432        }
     433    }
     434
     435    private static class ColorfulFilter implements BufferedImageOp {
     436        private final double colorfulness;
     437
     438        /**
     439         * Create a new colorful filter.
     440         * @param colorfulness The colorfulness as defined in the {@link ColorfulImageProcessor} class.
     441         */
     442        ColorfulFilter(double colorfulness) {
     443            this.colorfulness = colorfulness;
     444        }
     445
     446        @Override
     447        public BufferedImage filter(BufferedImage src, BufferedImage dest) {
     448            if (src.getWidth() == 0 || src.getHeight() == 0) {
     449                return src;
     450            }
     451
     452            if (dest == null) {
     453                dest = createCompatibleDestImage(src, null);
     454            }
     455            DataBuffer srcBuffer = src.getRaster().getDataBuffer();
     456            DataBuffer destBuffer = dest.getRaster().getDataBuffer();
     457            if (!(srcBuffer instanceof DataBufferByte) || !(destBuffer instanceof DataBufferByte)) {
     458                Main.trace("Cannot apply color filter: Images do not use DataBufferByte.");
     459                return src;
     460            }
     461
     462            int type = src.getType();
     463            if (type != dest.getType()) {
     464                Main.trace("Cannot apply color filter: Src / Dest differ in type (" + type + "/" + dest.getType() + ")");
     465                return src;
     466            }
     467            int redOffset, greenOffset, blueOffset, alphaOffset = 0;
     468            switch (type) {
     469            case BufferedImage.TYPE_3BYTE_BGR:
     470                blueOffset = 0;
     471                greenOffset = 1;
     472                redOffset = 2;
     473                break;
     474            case BufferedImage.TYPE_4BYTE_ABGR:
     475            case BufferedImage.TYPE_4BYTE_ABGR_PRE:
     476                blueOffset = 1;
     477                greenOffset = 2;
     478                redOffset = 3;
     479                break;
     480            case BufferedImage.TYPE_INT_ARGB:
     481            case BufferedImage.TYPE_INT_ARGB_PRE:
     482                redOffset = 0;
     483                greenOffset = 1;
     484                blueOffset = 2;
     485                alphaOffset = 3;
     486                break;
     487            default:
     488                Main.trace("Cannot apply color filter: Source image is of wrong type (" + type + ").");
     489                return src;
     490            }
     491            doFilter((DataBufferByte) srcBuffer, (DataBufferByte) destBuffer, redOffset, greenOffset, blueOffset,
     492                    alphaOffset, src.getAlphaRaster() != null);
     493            return dest;
     494        }
     495
     496        private void doFilter(DataBufferByte src, DataBufferByte dest, int redOffset, int greenOffset, int blueOffset,
     497                int alphaOffset, boolean hasAlpha) {
     498            byte[] srcPixels = src.getData();
     499            byte[] destPixels = dest.getData();
     500            if (srcPixels.length != destPixels.length) {
     501                Main.trace("Cannot apply color filter: Source/Dest lengths differ.");
     502                return;
     503            }
     504            int entries = hasAlpha ? 4 : 3;
     505            for (int i = 0; i < srcPixels.length; i += entries) {
     506                int r = srcPixels[i + redOffset] & 0xff;
     507                int g = srcPixels[i + greenOffset] & 0xff;
     508                int b = srcPixels[i + blueOffset] & 0xff;
     509                float luminosity = r * .21f + g * .72f + b * .07f;
     510                destPixels[i + redOffset] = mix(r, luminosity);
     511                destPixels[i + greenOffset] = mix(g, luminosity);
     512                destPixels[i + blueOffset] = mix(b, luminosity);
     513                if (hasAlpha) {
     514                    destPixels[i + alphaOffset] = srcPixels[i + alphaOffset];
     515                }
     516            }
     517        }
     518
     519        private byte mix(int color, float luminosity) {
     520            int val = (int) (colorfulness * color +  (1 - colorfulness) * luminosity);
     521            if (val < 0) {
     522                return 0;
     523            } else if (val > 0xff) {
     524                return (byte) 0xff;
     525            } else {
     526                return (byte) val;
     527            }
     528        }
     529
     530        @Override
     531        public Rectangle2D getBounds2D(BufferedImage src) {
     532            return new Rectangle(src.getWidth(), src.getHeight());
     533        }
     534
     535        @Override
     536        public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) {
     537            return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
     538        }
     539
     540        @Override
     541        public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
     542            return (Point2D) srcPt.clone();
     543        }
     544
     545        @Override
     546        public RenderingHints getRenderingHints() {
     547            return null;
     548        }
     549
     550    }
     551
     552    /**
    303553     * Returns the currently set gamma value.
    304554     * @return the currently set gamma value
     
    314564    public void setGamma(double gamma) {
    315565        gammaImageProcessor.setGamma(gamma);
     566    }
     567
     568    /**
     569     * Gets the current sharpen level.
     570     * @return The sharpen level.
     571     */
     572    public double getSharpenLevel() {
     573        return sharpenImageProcessor.getSharpenLevel();
     574    }
     575
     576    /**
     577     * Sets the sharpen level for the layer.
     578     * <code>1</code> means no change in sharpness.
     579     * Values in range 0..1 blur the image.
     580     * Values above 1 are used to sharpen the image.
     581     * @param sharpenLevel The sharpen level.
     582     */
     583    public void setSharpenLevel(double sharpenLevel) {
     584        sharpenImageProcessor.setSharpenLevel((float) sharpenLevel);
     585    }
     586
     587    /**
     588     * Gets the colorfulness of this image.
     589     * @return The colorfulness
     590     */
     591    public double getColorfulness() {
     592        return collorfulnessImageProcessor.getColorfulness();
     593    }
     594
     595    /**
     596     * Sets the colorfulness of this image.
     597     * 0 means grayscale.
     598     * 1 means normal colorfulness.
     599     * Values greater than 1 are allowed.
     600     * @param colorfulness The colorfulness.
     601     */
     602    public void setColorfulness(double colorfulness) {
     603        collorfulnessImageProcessor.setColorfulness(colorfulness);
    316604    }
    317605
Note: See TracChangeset for help on using the changeset viewer.