Ignore:
Timestamp:
2016-07-17T13:07:14+02:00 (8 years ago)
Author:
Don-vip
Message:

fix #13159 - Move image processors out of imagery layer (patch by michael2402) - gsoc-core + fix checkstyle violations

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java

    r10467 r10547  
    99import java.awt.Component;
    1010import java.awt.GridBagLayout;
    11 import java.awt.Rectangle;
    12 import java.awt.RenderingHints;
    13 import java.awt.Transparency;
    1411import java.awt.event.ActionEvent;
    15 import java.awt.geom.Point2D;
    16 import java.awt.geom.Rectangle2D;
    1712import java.awt.image.BufferedImage;
    1813import java.awt.image.BufferedImageOp;
    19 import java.awt.image.ColorModel;
    20 import java.awt.image.ConvolveOp;
    21 import java.awt.image.DataBuffer;
    22 import java.awt.image.DataBufferByte;
    23 import java.awt.image.Kernel;
    24 import java.awt.image.LookupOp;
    25 import java.awt.image.ShortLookupTable;
    2614import java.util.ArrayList;
    2715import java.util.List;
     
    4634import org.openstreetmap.josm.data.preferences.IntegerProperty;
    4735import org.openstreetmap.josm.gui.MenuScroller;
     36import org.openstreetmap.josm.gui.layer.imagery.ImageryFilterSettings;
    4837import org.openstreetmap.josm.gui.widgets.UrlLabel;
    4938import org.openstreetmap.josm.tools.GBC;
     
    7665    protected double dy;
    7766
    78     protected GammaImageProcessor gammaImageProcessor = new GammaImageProcessor();
    79     protected SharpenImageProcessor sharpenImageProcessor = new SharpenImageProcessor();
    80     protected ColorfulImageProcessor collorfulnessImageProcessor = new ColorfulImageProcessor();
    81 
    8267    private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this);
     68
     69    private final ImageryFilterSettings filterSettings = new ImageryFilterSettings();
    8370
    8471    /**
     
    9683            icon = ImageProvider.get("imagery_small");
    9784        }
    98         addImageProcessor(collorfulnessImageProcessor);
    99         addImageProcessor(gammaImageProcessor);
    100         addImageProcessor(sharpenImageProcessor);
    101         sharpenImageProcessor.setSharpenLevel(1 + PROP_SHARPEN_LEVEL.get() / 2f);
     85        for (ImageProcessor processor : filterSettings.getProcessors()) {
     86            addImageProcessor(processor);
     87        }
     88        filterSettings.setSharpenLevel(1 + PROP_SHARPEN_LEVEL.get() / 2f);
    10289    }
    10390
     
    259246
    260247    /**
    261      * An image processor which adjusts the gamma value of an image.
    262      */
    263     public static class GammaImageProcessor implements ImageProcessor {
    264         private double gamma = 1;
    265         final short[] gammaChange = new short[256];
    266         private final LookupOp op3 = new LookupOp(
    267                 new ShortLookupTable(0, new short[][]{gammaChange, gammaChange, gammaChange}), null);
    268         private final LookupOp op4 = new LookupOp(
    269                 new ShortLookupTable(0, new short[][]{gammaChange, gammaChange, gammaChange, gammaChange}), null);
    270 
    271         /**
    272          * Returns the currently set gamma value.
    273          * @return the currently set gamma value
    274          */
    275         public double getGamma() {
    276             return gamma;
    277         }
    278 
    279         /**
    280          * Sets a new gamma value, {@code 1} stands for no correction.
    281          * @param gamma new gamma value
    282          */
    283         public void setGamma(double gamma) {
    284             this.gamma = gamma;
    285             for (int i = 0; i < 256; i++) {
    286                 gammaChange[i] = (short) (255 * Math.pow(i / 255., gamma));
    287             }
    288         }
    289 
    290         @Override
    291         public BufferedImage process(BufferedImage image) {
    292             if (gamma == 1) {
    293                 return image;
    294             }
    295             try {
    296                 final int bands = image.getRaster().getNumBands();
    297                 if (image.getType() != BufferedImage.TYPE_CUSTOM && bands == 3) {
    298                     return op3.filter(image, null);
    299                 } else if (image.getType() != BufferedImage.TYPE_CUSTOM && bands == 4) {
    300                     return op4.filter(image, null);
    301                 }
    302             } catch (IllegalArgumentException ignore) {
    303                 Main.trace(ignore);
    304             }
    305             final int type = image.getTransparency() == Transparency.OPAQUE ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB;
    306             final BufferedImage to = new BufferedImage(image.getWidth(), image.getHeight(), type);
    307             to.getGraphics().drawImage(image, 0, 0, null);
    308             return process(to);
    309         }
    310 
    311         @Override
    312         public String toString() {
    313             return "GammaImageProcessor [gamma=" + gamma + ']';
    314         }
    315     }
    316 
    317     /**
    318      * Sharpens or blurs the image, depending on the sharpen value.
    319      * <p>
    320      * A positive sharpen level means that we sharpen the image.
    321      * <p>
    322      * A negative sharpen level let's us blur the image. -1 is the most useful value there.
    323      *
    324      * @author Michael Zangl
    325      */
    326     public static class SharpenImageProcessor implements ImageProcessor {
    327         private float sharpenLevel;
    328         private ConvolveOp op;
    329 
    330         private static float[] KERNEL_IDENTITY = new float[] {
    331             0, 0, 0,
    332             0, 1, 0,
    333             0, 0, 0
    334         };
    335 
    336         private static float[] KERNEL_BLUR = new float[] {
    337             1f / 16, 2f / 16, 1f / 16,
    338             2f / 16, 4f / 16, 2f / 16,
    339             1f / 16, 2f / 16, 1f / 16
    340         };
    341 
    342         private static float[] KERNEL_SHARPEN = new float[] {
    343             -.5f, -1f, -.5f,
    344              -1f, 7, -1f,
    345             -.5f, -1f, -.5f
    346         };
    347 
    348         /**
    349          * Gets the current sharpen level.
    350          * @return The level.
    351          */
    352         public float getSharpenLevel() {
    353             return sharpenLevel;
    354         }
    355 
    356         /**
    357          * Sets the sharpening level.
    358          * @param sharpenLevel The level. Clamped to be positive or 0.
    359          */
    360         public void setSharpenLevel(float sharpenLevel) {
    361             if (sharpenLevel < 0) {
    362                 this.sharpenLevel = 0;
    363             } else {
    364                 this.sharpenLevel = sharpenLevel;
    365             }
    366 
    367             if (this.sharpenLevel < 0.95) {
    368                 op = generateMixed(this.sharpenLevel, KERNEL_IDENTITY, KERNEL_BLUR);
    369             } else if (this.sharpenLevel > 1.05) {
    370                 op = generateMixed(this.sharpenLevel - 1, KERNEL_SHARPEN, KERNEL_IDENTITY);
    371             } else {
    372                 op = null;
    373             }
    374         }
    375 
    376         private ConvolveOp generateMixed(float aFactor, float[] a, float[] b) {
    377             if (a.length != 9 || b.length != 9) {
    378                 throw new IllegalArgumentException("Illegal kernel array length.");
    379             }
    380             float[] values = new float[9];
    381             for (int i = 0; i < values.length; i++) {
    382                 values[i] = aFactor * a[i] + (1 - aFactor) * b[i];
    383             }
    384             return new ConvolveOp(new Kernel(3, 3, values), ConvolveOp.EDGE_NO_OP, null);
    385         }
    386 
    387         @Override
    388         public BufferedImage process(BufferedImage image) {
    389             if (op != null) {
    390                 return op.filter(image, null);
    391             } else {
    392                 return image;
    393             }
    394         }
    395 
    396         @Override
    397         public String toString() {
    398             return "SharpenImageProcessor [sharpenLevel=" + sharpenLevel + ']';
    399         }
    400     }
    401 
    402     /**
    403      * Adds or removes the colorfulness of the image.
    404      *
    405      * @author Michael Zangl
    406      */
    407     public static class ColorfulImageProcessor implements ImageProcessor {
    408         private ColorfulFilter op;
    409         private double colorfulness = 1;
    410 
    411         /**
    412          * Gets the colorfulness value.
    413          * @return The value
    414          */
    415         public double getColorfulness() {
    416             return colorfulness;
    417         }
    418 
    419         /**
    420          * Sets the colorfulness value. Clamps it to 0+
    421          * @param colorfulness The value
    422          */
    423         public void setColorfulness(double colorfulness) {
    424             if (colorfulness < 0) {
    425                 this.colorfulness = 0;
    426             } else {
    427                 this.colorfulness = colorfulness;
    428             }
    429 
    430             if (this.colorfulness < .95 || this.colorfulness > 1.05) {
    431                 op = new ColorfulFilter(this.colorfulness);
    432             } else {
    433                 op = null;
    434             }
    435         }
    436 
    437         @Override
    438         public BufferedImage process(BufferedImage image) {
    439             if (op != null) {
    440                 return op.filter(image, null);
    441             } else {
    442                 return image;
    443             }
    444         }
    445 
    446         @Override
    447         public String toString() {
    448             return "ColorfulImageProcessor [colorfulness=" + colorfulness + ']';
    449         }
    450     }
    451 
    452     private static class ColorfulFilter implements BufferedImageOp {
    453         private final double colorfulness;
    454 
    455         /**
    456          * Create a new colorful filter.
    457          * @param colorfulness The colorfulness as defined in the {@link ColorfulImageProcessor} class.
    458          */
    459         ColorfulFilter(double colorfulness) {
    460             this.colorfulness = colorfulness;
    461         }
    462 
    463         @Override
    464         public BufferedImage filter(BufferedImage src, BufferedImage dest) {
    465             if (src.getWidth() == 0 || src.getHeight() == 0) {
    466                 return src;
    467             }
    468 
    469             if (dest == null) {
    470                 dest = createCompatibleDestImage(src, null);
    471             }
    472             DataBuffer srcBuffer = src.getRaster().getDataBuffer();
    473             DataBuffer destBuffer = dest.getRaster().getDataBuffer();
    474             if (!(srcBuffer instanceof DataBufferByte) || !(destBuffer instanceof DataBufferByte)) {
    475                 Main.trace("Cannot apply color filter: Images do not use DataBufferByte.");
    476                 return src;
    477             }
    478 
    479             int type = src.getType();
    480             if (type != dest.getType()) {
    481                 Main.trace("Cannot apply color filter: Src / Dest differ in type (" + type + '/' + dest.getType() + ')');
    482                 return src;
    483             }
    484             int redOffset, greenOffset, blueOffset, alphaOffset = 0;
    485             switch (type) {
    486             case BufferedImage.TYPE_3BYTE_BGR:
    487                 blueOffset = 0;
    488                 greenOffset = 1;
    489                 redOffset = 2;
    490                 break;
    491             case BufferedImage.TYPE_4BYTE_ABGR:
    492             case BufferedImage.TYPE_4BYTE_ABGR_PRE:
    493                 blueOffset = 1;
    494                 greenOffset = 2;
    495                 redOffset = 3;
    496                 break;
    497             case BufferedImage.TYPE_INT_ARGB:
    498             case BufferedImage.TYPE_INT_ARGB_PRE:
    499                 redOffset = 0;
    500                 greenOffset = 1;
    501                 blueOffset = 2;
    502                 alphaOffset = 3;
    503                 break;
    504             default:
    505                 Main.trace("Cannot apply color filter: Source image is of wrong type (" + type + ").");
    506                 return src;
    507             }
    508             doFilter((DataBufferByte) srcBuffer, (DataBufferByte) destBuffer, redOffset, greenOffset, blueOffset,
    509                     alphaOffset, src.getAlphaRaster() != null);
    510             return dest;
    511         }
    512 
    513         private void doFilter(DataBufferByte src, DataBufferByte dest, int redOffset, int greenOffset, int blueOffset,
    514                 int alphaOffset, boolean hasAlpha) {
    515             byte[] srcPixels = src.getData();
    516             byte[] destPixels = dest.getData();
    517             if (srcPixels.length != destPixels.length) {
    518                 Main.trace("Cannot apply color filter: Source/Dest lengths differ.");
    519                 return;
    520             }
    521             int entries = hasAlpha ? 4 : 3;
    522             for (int i = 0; i < srcPixels.length; i += entries) {
    523                 int r = srcPixels[i + redOffset] & 0xff;
    524                 int g = srcPixels[i + greenOffset] & 0xff;
    525                 int b = srcPixels[i + blueOffset] & 0xff;
    526                 double luminosity = r * .21d + g * .72d + b * .07d;
    527                 destPixels[i + redOffset] = mix(r, luminosity);
    528                 destPixels[i + greenOffset] = mix(g, luminosity);
    529                 destPixels[i + blueOffset] = mix(b, luminosity);
    530                 if (hasAlpha) {
    531                     destPixels[i + alphaOffset] = srcPixels[i + alphaOffset];
    532                 }
    533             }
    534         }
    535 
    536         private byte mix(int color, double luminosity) {
    537             int val = (int) (colorfulness * color + (1 - colorfulness) * luminosity);
    538             if (val < 0) {
    539                 return 0;
    540             } else if (val > 0xff) {
    541                 return (byte) 0xff;
    542             } else {
    543                 return (byte) val;
    544             }
    545         }
    546 
    547         @Override
    548         public Rectangle2D getBounds2D(BufferedImage src) {
    549             return new Rectangle(src.getWidth(), src.getHeight());
    550         }
    551 
    552         @Override
    553         public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) {
    554             return new BufferedImage(src.getWidth(), src.getHeight(), src.getType());
    555         }
    556 
    557         @Override
    558         public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) {
    559             return (Point2D) srcPt.clone();
    560         }
    561 
    562         @Override
    563         public RenderingHints getRenderingHints() {
    564             return null;
    565         }
    566 
    567     }
    568 
    569     /**
    570      * Returns the currently set gamma value.
    571      * @return the currently set gamma value
    572      */
    573     public double getGamma() {
    574         return gammaImageProcessor.getGamma();
    575     }
    576 
    577     /**
    578      * Sets a new gamma value, {@code 1} stands for no correction.
    579      * @param gamma new gamma value
    580      */
    581     public void setGamma(double gamma) {
    582         gammaImageProcessor.setGamma(gamma);
    583     }
    584 
    585     /**
    586      * Gets the current sharpen level.
    587      * @return The sharpen level.
    588      */
    589     public double getSharpenLevel() {
    590         return sharpenImageProcessor.getSharpenLevel();
    591     }
    592 
    593     /**
    594      * Sets the sharpen level for the layer.
    595      * <code>1</code> means no change in sharpness.
    596      * Values in range 0..1 blur the image.
    597      * Values above 1 are used to sharpen the image.
    598      * @param sharpenLevel The sharpen level.
    599      */
    600     public void setSharpenLevel(double sharpenLevel) {
    601         sharpenImageProcessor.setSharpenLevel((float) sharpenLevel);
    602     }
    603 
    604     /**
    605      * Gets the colorfulness of this image.
    606      * @return The colorfulness
    607      */
    608     public double getColorfulness() {
    609         return collorfulnessImageProcessor.getColorfulness();
    610     }
    611 
    612     /**
    613      * Sets the colorfulness of this image.
    614      * 0 means grayscale.
    615      * 1 means normal colorfulness.
    616      * Values greater than 1 are allowed.
    617      * @param colorfulness The colorfulness.
    618      */
    619     public void setColorfulness(double colorfulness) {
    620         collorfulnessImageProcessor.setColorfulness(colorfulness);
     248     * Gets the settings for the filter that is applied to this layer.
     249     * @return The filter settings.
     250     * @since 10547
     251     */
     252    public ImageryFilterSettings getFilterSettings() {
     253        return filterSettings;
    621254    }
    622255
Note: See TracChangeset for help on using the changeset viewer.