001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer.imagery;
003
004import java.awt.Transparency;
005import java.awt.image.BufferedImage;
006import java.awt.image.LookupOp;
007import java.awt.image.ShortLookupTable;
008import java.util.Collections;
009import java.util.Map;
010
011import org.openstreetmap.josm.io.session.SessionAwareReadApply;
012import org.openstreetmap.josm.tools.ImageProcessor;
013import org.openstreetmap.josm.tools.Logging;
014import org.openstreetmap.josm.tools.Utils;
015
016/**
017 * An image processor which adjusts the gamma value of an image.
018 * @since 10547
019 */
020public class GammaImageProcessor implements ImageProcessor, SessionAwareReadApply {
021    private double gamma = 1.0;
022    final short[] gammaChange = new short[256];
023    private final LookupOp op3 = new LookupOp(
024            new ShortLookupTable(0, new short[][]{gammaChange, gammaChange, gammaChange}), null);
025    private final LookupOp op4 = new LookupOp(
026            new ShortLookupTable(0, new short[][]{gammaChange, gammaChange, gammaChange, gammaChange}), null);
027
028    /**
029     * Returns the currently set gamma value.
030     * @return the currently set gamma value
031     */
032    public double getGamma() {
033        return gamma;
034    }
035
036    /**
037     * Sets a new gamma value, {@code 1} stands for no correction.
038     * @param gamma new gamma value
039     */
040    public void setGamma(double gamma) {
041        this.gamma = gamma;
042        for (int i = 0; i < 256; i++) {
043            gammaChange[i] = (short) (255 * Math.pow(i / 255., gamma));
044        }
045    }
046
047    @Override
048    public BufferedImage process(BufferedImage image) {
049        if (gamma == 1.0) {
050            return image;
051        }
052        try {
053            final int bands = image.getRaster().getNumBands();
054            if (image.getType() != BufferedImage.TYPE_CUSTOM && bands == 3) {
055                return op3.filter(image, null);
056            } else if (image.getType() != BufferedImage.TYPE_CUSTOM && bands == 4) {
057                return op4.filter(image, null);
058            }
059        } catch (IllegalArgumentException ignore) {
060            Logging.trace(ignore);
061        }
062        final int type = image.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
063        final BufferedImage to = new BufferedImage(image.getWidth(), image.getHeight(), type);
064        to.getGraphics().drawImage(image, 0, 0, null);
065        return process(to);
066    }
067
068    @Override
069    public void applyFromPropertiesMap(Map<String, String> properties) {
070        String cStr = properties.get("gamma");
071        if (cStr != null) {
072            try {
073                setGamma(Double.parseDouble(cStr));
074            } catch (NumberFormatException e) {
075                Logging.trace(e);
076            }
077        }
078    }
079
080    @Override
081    public Map<String, String> toPropertiesMap() {
082        if (Utils.equalsEpsilon(gamma, 1.0))
083            return Collections.emptyMap();
084        else
085            return Collections.singletonMap("gamma", Double.toString(gamma));
086    }
087
088    @Override
089    public String toString() {
090        return "GammaImageProcessor [gamma=" + gamma + ']';
091    }
092}