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)

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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.