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}