001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.imagery;
003
004import java.awt.image.BufferedImage;
005import java.awt.image.ConvolveOp;
006import java.awt.image.Kernel;
007
008import org.openstreetmap.josm.gui.layer.ImageProcessor;
009
010/**
011 * Sharpens or blurs the image, depending on the sharpen value.
012 * <p>
013 * A positive sharpen level means that we sharpen the image.
014 * <p>
015 * A negative sharpen level let's us blur the image. -1 is the most useful value there.
016 *
017 * @author Michael Zangl
018 * @since 10547
019 */
020public class SharpenImageProcessor implements ImageProcessor {
021    private float sharpenLevel = 1;
022    private ConvolveOp op;
023
024    private static float[] KERNEL_IDENTITY = new float[] {
025        0, 0, 0,
026        0, 1, 0,
027        0, 0, 0
028    };
029
030    private static float[] KERNEL_BLUR = new float[] {
031        1f / 16, 2f / 16, 1f / 16,
032        2f / 16, 4f / 16, 2f / 16,
033        1f / 16, 2f / 16, 1f / 16
034    };
035
036    private static float[] KERNEL_SHARPEN = new float[] {
037        -.5f, -1f, -.5f,
038         -1f, 7, -1f,
039        -.5f, -1f, -.5f
040    };
041
042    /**
043     * Gets the current sharpen level.
044     * @return The level.
045     */
046    public float getSharpenLevel() {
047        return sharpenLevel;
048    }
049
050    /**
051     * Sets the sharpening level.
052     * @param sharpenLevel The level. Clamped to be positive or 0.
053     */
054    public void setSharpenLevel(float sharpenLevel) {
055        if (sharpenLevel < 0) {
056            this.sharpenLevel = 0;
057        } else {
058            this.sharpenLevel = sharpenLevel;
059        }
060
061        if (this.sharpenLevel < 0.95) {
062            op = generateMixed(this.sharpenLevel, KERNEL_IDENTITY, KERNEL_BLUR);
063        } else if (this.sharpenLevel > 1.05) {
064            op = generateMixed(this.sharpenLevel - 1, KERNEL_SHARPEN, KERNEL_IDENTITY);
065        } else {
066            op = null;
067        }
068    }
069
070    private static ConvolveOp generateMixed(float aFactor, float[] a, float[] b) {
071        if (a.length != 9 || b.length != 9) {
072            throw new IllegalArgumentException("Illegal kernel array length.");
073        }
074        float[] values = new float[9];
075        for (int i = 0; i < values.length; i++) {
076            values[i] = aFactor * a[i] + (1 - aFactor) * b[i];
077        }
078        return new ConvolveOp(new Kernel(3, 3, values), ConvolveOp.EDGE_NO_OP, null);
079    }
080
081    @Override
082    public BufferedImage process(BufferedImage image) {
083        if (op != null) {
084            return op.filter(image, null);
085        } else {
086            return image;
087        }
088    }
089
090    @Override
091    public String toString() {
092        return "SharpenImageProcessor [sharpenLevel=" + sharpenLevel + ']';
093    }
094}