Ticket #12752: patch-image-sharpness.patch
File patch-image-sharpness.patch, 33.2 KB (added by , 9 years ago) |
---|
-
src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java
diff --git a/images/dialogs/layerlist/colorfulness.png b/images/dialogs/layerlist/colorfulness.png new file mode 100644 index 0000000..490f35a Binary files /dev/null and b/images/dialogs/layerlist/colorfulness.png differ diff --git a/images/dialogs/layerlist/colorfulness.xcf b/images/dialogs/layerlist/colorfulness.xcf new file mode 100644 index 0000000..031cfae Binary files /dev/null and b/images/dialogs/layerlist/colorfulness.xcf differ diff --git a/images/dialogs/layerlist/gamma.png b/images/dialogs/layerlist/gamma.png index f67ada4..3afee11 100644 Binary files a/images/dialogs/layerlist/gamma.png and b/images/dialogs/layerlist/gamma.png differ diff --git a/images/dialogs/layerlist/gamma.xcf b/images/dialogs/layerlist/gamma.xcf new file mode 100644 index 0000000..8d8e59f Binary files /dev/null and b/images/dialogs/layerlist/gamma.xcf differ diff --git a/images/dialogs/layerlist/sharpness.png b/images/dialogs/layerlist/sharpness.png new file mode 100644 index 0000000..b381935 Binary files /dev/null and b/images/dialogs/layerlist/sharpness.png differ diff --git a/images/dialogs/layerlist/sharpness.xcf b/images/dialogs/layerlist/sharpness.xcf new file mode 100644 index 0000000..3e50dd5 Binary files /dev/null and b/images/dialogs/layerlist/sharpness.xcf differ diff --git a/images/dialogs/layerlist/transparency.png b/images/dialogs/layerlist/transparency.png index 3149c9d..0498ca1 100644 Binary files a/images/dialogs/layerlist/transparency.png and b/images/dialogs/layerlist/transparency.png differ diff --git a/images/dialogs/layerlist/transparency.xcf b/images/dialogs/layerlist/transparency.xcf new file mode 100644 index 0000000..906a920 Binary files /dev/null and b/images/dialogs/layerlist/transparency.xcf differ diff --git a/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java b/src/org/openstreetmap/josm/gui/dialogs/LayerListDialog.java index 41cad51..82901e9 100644
a b public class LayerListDialog extends ToggleDialog { 531 531 public static final class LayerVisibilityAction extends AbstractAction implements IEnabledStateUpdating, LayerAction { 532 532 protected static final int SLIDER_STEPS = 100; 533 533 private static final double MAX_GAMMA_FACTOR = 2; 534 private static final double MAX_SHARPNESS_FACTOR = 2; 535 private static final double MAX_COLORFUL_FACTOR = 2; 534 536 private final LayerListModel model; 535 537 private final JPopupMenu popup; 536 private JSlider opacitySlider;537 private JSlider gammaSlider;538 538 private SideButton sideButton; 539 539 private JCheckBox visibilityCheckbox; 540 final OpacitySlider opacitySlider = new OpacitySlider(); 541 private final ArrayList<FilterSlider<?>> sliders = new ArrayList<>(); 540 542 541 543 /** 542 544 * Creates a new {@link LayerVisibilityAction} … … public class LayerListDialog extends ToggleDialog { 559 561 visibilityCheckbox.addChangeListener(new ChangeListener() { 560 562 @Override 561 563 public void stateChanged(ChangeEvent e) { 562 setVisible (visibilityCheckbox.isSelected());564 setVisibleFlag(visibilityCheckbox.isSelected()); 563 565 } 564 566 }); 565 567 content.add(visibilityCheckbox, GBC.eop()); 566 568 567 content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "transparency")), GBC.std().span(1, 2).insets(0, 0, 5, 0)); 568 content.add(new JLabel(tr("Opacity")), GBC.eol()); 569 opacitySlider = new JSlider(JSlider.HORIZONTAL); 570 opacitySlider.setMaximum(SLIDER_STEPS); 571 opacitySlider.addChangeListener(new ChangeListener() { 572 @Override 573 public void stateChanged(ChangeEvent e) { 574 setOpacityValue(readOpacityValue(), opacitySlider.getValueIsAdjusting()); 575 } 576 }); 577 opacitySlider.setToolTipText(tr("Adjust opacity of the layer.")); 578 content.add(opacitySlider, GBC.eop()); 579 580 content.add(new JLabel(ImageProvider.get("dialogs/layerlist", "gamma")), GBC.std().span(1, 2).insets(0, 0, 5, 0)); 581 content.add(new JLabel(tr("Gamma")), GBC.eol()); 582 gammaSlider = new JSlider(JSlider.HORIZONTAL); 583 gammaSlider.setMaximum(SLIDER_STEPS); 584 gammaSlider.addChangeListener(new ChangeListener() { 585 @Override 586 public void stateChanged(ChangeEvent e) { 587 setGammaValue(readGammaValue()); 588 } 589 }); 590 gammaSlider.setToolTipText(tr("Adjust gamma value of the layer.")); 591 content.add(gammaSlider, GBC.eol()); 592 } 593 594 protected double readOpacityValue() { 595 return (double) opacitySlider.getValue() / SLIDER_STEPS; 569 addSlider(content, opacitySlider); 570 addSlider(content, new ColorfulnessSlider()); 571 addSlider(content, new GammaFilterSlider()); 572 addSlider(content, new SharpnessSlider()); 596 573 } 597 574 598 protected double readGammaValue() { 599 return (double) gammaSlider.getValue() / SLIDER_STEPS * MAX_GAMMA_FACTOR; 575 private void addSlider(JPanel content, FilterSlider<?> slider) { 576 content.add(new JLabel(slider.getIcon()), GBC.std().span(1, 2).insets(0, 0, 5, 0)); 577 content.add(new JLabel(slider.getLabel()), GBC.eol()); 578 content.add(slider, GBC.eop()); 579 sliders.add(slider); 600 580 } 601 581 602 protected void setVisible (boolean visible) {582 protected void setVisibleFlag(boolean visible) { 603 583 for (Layer l : model.getSelectedLayers()) { 604 584 l.setVisible(visible); 605 585 } 606 586 updateValues(); 607 587 } 608 588 609 protected void setOpacityValue(double value, boolean adjusting) {610 if (value <= 0 && !adjusting) {611 setVisible(false);612 } else {613 for (Layer l : model.getSelectedLayers()) {614 l.setOpacity(value);615 }616 }617 }618 619 protected void setGammaValue(double value) {620 for (ImageryLayer imageryLayer : Utils.filteredCollection(model.getSelectedLayers(), ImageryLayer.class)) {621 imageryLayer.setGamma(value);622 }623 }624 625 589 @Override 626 590 public void actionPerformed(ActionEvent e) { 627 591 updateValues(); … … public class LayerListDialog extends ToggleDialog { 647 611 // TODO: Indicate tristate. 648 612 visibilityCheckbox.setSelected(allVisible && !allHidden); 649 613 650 updateOpacitySlider(layers, allHidden); 651 652 updateGammaSlider(layers, allHidden); 653 } 654 655 private void updateGammaSlider(List<Layer> layers, boolean allHidden) { 656 Collection<ImageryLayer> gammaLayers = Utils.filteredCollection(layers, ImageryLayer.class); 657 if (gammaLayers.isEmpty() || allHidden) { 658 gammaSlider.setEnabled(false); 659 } else { 660 gammaSlider.setEnabled(true); 661 double gamma = gammaLayers.iterator().next().getGamma(); 662 gammaSlider.setValue((int) (gamma * SLIDER_STEPS / MAX_GAMMA_FACTOR)); 663 } 664 } 665 666 private void updateOpacitySlider(List<Layer> layers, boolean allHidden) { 667 if (layers.isEmpty() || allHidden) { 668 opacitySlider.setEnabled(false); 669 } else { 670 opacitySlider.setEnabled(true); 671 double opacity = 0; 672 for (Layer l : layers) { 673 opacity += l.getOpacity(); 674 } 675 opacity /= layers.size(); 676 if (opacity == 0) { 677 opacity = 1; 678 setOpacityValue(opacity, false); 679 } 680 opacitySlider.setValue((int) (opacity * SLIDER_STEPS)); 614 for (FilterSlider<?> slider : sliders) { 615 slider.updateSlider(layers, allHidden); 681 616 } 682 617 } 683 618 … … public class LayerListDialog extends ToggleDialog { 703 638 void setCorrespondingSideButton(SideButton sideButton) { 704 639 this.sideButton = sideButton; 705 640 } 641 642 /** 643 * This is a slider for a filter value. 644 * @author Michael Zangl 645 * 646 * @param <T> The layer type. 647 */ 648 private abstract class FilterSlider<T extends Layer> extends JSlider { 649 private final double minValue; 650 private final double maxValue; 651 private final Class<T> layerClassFilter; 652 653 /** 654 * Create a new filter slider. 655 * @param minValue The minimum value to map to the left side. 656 * @param maxValue The maximum value to map to the right side. 657 * @param layerClassFilter The type of layer influenced by this filter. 658 */ 659 FilterSlider(double minValue, double maxValue, Class<T> layerClassFilter) { 660 super(JSlider.HORIZONTAL); 661 this.minValue = minValue; 662 this.maxValue = maxValue; 663 this.layerClassFilter = layerClassFilter; 664 setMaximum(SLIDER_STEPS); 665 int tick = convertFromRealValue(1); 666 setMinorTickSpacing(tick); 667 setMajorTickSpacing(tick); 668 setPaintTicks(true); 669 670 addChangeListener(new ChangeListener() { 671 @Override 672 public void stateChanged(ChangeEvent e) { 673 onStateChanged(); 674 } 675 }); 676 } 677 678 /** 679 * Called whenever the state of the slider was changed. 680 * @see #getValueIsAdjusting() 681 * @see #getRealValue() 682 */ 683 protected void onStateChanged() { 684 Collection<T> layers = filterLayers(model.getSelectedLayers()); 685 for (T layer : layers) { 686 applyValueToLayer(layer); 687 } 688 } 689 690 protected void applyValueToLayer(T layer) { 691 } 692 693 protected double getRealValue() { 694 return convertToRealValue(getValue()); 695 } 696 697 protected double convertToRealValue(int value) { 698 double s = (double) value / SLIDER_STEPS; 699 return s * maxValue + (1-s) * minValue; 700 } 701 702 protected void setRealValue(double value) { 703 setValue(convertFromRealValue(value)); 704 } 705 706 protected int convertFromRealValue(double value) { 707 int i = (int) ((value - minValue) / (maxValue - minValue) * SLIDER_STEPS + .5); 708 if (i < getMinimum()) { 709 return getMinimum(); 710 } else if (i > getMaximum()) { 711 return getMaximum(); 712 } else { 713 return i; 714 } 715 } 716 717 public abstract ImageIcon getIcon(); 718 719 public abstract String getLabel(); 720 721 public void updateSlider(List<Layer> layers, boolean allHidden) { 722 Collection<? extends Layer> usedLayers = filterLayers(layers); 723 if (usedLayers.isEmpty() || allHidden) { 724 setEnabled(false); 725 } else { 726 setEnabled(true); 727 updateSliderWhileEnabled(usedLayers, allHidden); 728 } 729 } 730 731 protected Collection<T> filterLayers(List<Layer> layers) { 732 return Utils.filteredCollection(layers, layerClassFilter); 733 } 734 735 protected abstract void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden); 736 } 737 738 /** 739 * This slider allows you to change the opacity of a layer. 740 * 741 * @author Michael Zangl 742 * @see Layer#setOpacity(double) 743 */ 744 class OpacitySlider extends FilterSlider<Layer> { 745 /** 746 * Creaate a new {@link OpacitySlider}. 747 */ 748 OpacitySlider() { 749 super(0, 1, Layer.class); 750 setToolTipText(tr("Adjust opacity of the layer.")); 751 752 } 753 754 @Override 755 protected void onStateChanged() { 756 if (getRealValue() <= 0.001 && !getValueIsAdjusting()) { 757 setVisibleFlag(false); 758 } else { 759 super.onStateChanged(); 760 } 761 } 762 763 @Override 764 protected void applyValueToLayer(Layer layer) { 765 layer.setOpacity(getRealValue()); 766 } 767 768 @Override 769 protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) { 770 double opacity = 0; 771 for (Layer l : usedLayers) { 772 opacity += l.getOpacity(); 773 } 774 opacity /= usedLayers.size(); 775 if (opacity == 0) { 776 opacity = 1; 777 setVisibleFlag(true); 778 } 779 setRealValue(opacity); 780 } 781 782 @Override 783 public String getLabel() { 784 return tr("Opacity"); 785 } 786 787 @Override 788 public ImageIcon getIcon() { 789 return ImageProvider.get("dialogs/layerlist", "transparency"); 790 } 791 792 @Override 793 public String toString() { 794 return "OpacitySlider [getRealValue()=" + getRealValue() + "]"; 795 } 796 } 797 798 /** 799 * This slider allows you to change the gamma value of a layer. 800 * 801 * @author Michael Zangl 802 * @see ImageryLayer#setGamma(double) 803 */ 804 private class GammaFilterSlider extends FilterSlider<ImageryLayer> { 805 806 /** 807 * Create a new {@link GammaFilterSlider} 808 */ 809 GammaFilterSlider() { 810 super(0, MAX_GAMMA_FACTOR, ImageryLayer.class); 811 setToolTipText(tr("Adjust gamma value of the layer.")); 812 } 813 814 @Override 815 protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) { 816 double gamma = ((ImageryLayer) usedLayers.iterator().next()).getGamma(); 817 setRealValue(gamma); 818 } 819 820 @Override 821 protected void applyValueToLayer(ImageryLayer layer) { 822 layer.setGamma(getRealValue()); 823 } 824 825 @Override 826 public ImageIcon getIcon() { 827 return ImageProvider.get("dialogs/layerlist", "gamma"); 828 } 829 830 @Override 831 public String getLabel() { 832 return tr("Gamma"); 833 } 834 } 835 836 /** 837 * This slider allows you to change the sharpness of a layer. 838 * 839 * @author Michael Zangl 840 * @see ImageryLayer#setSharpenLevel(double) 841 */ 842 private class SharpnessSlider extends FilterSlider<ImageryLayer> { 843 844 /** 845 * Creates a new {@link SharpnessSlider} 846 */ 847 SharpnessSlider() { 848 super(0, MAX_SHARPNESS_FACTOR, ImageryLayer.class); 849 setToolTipText(tr("Adjust sharpness/blur value of the layer.")); 850 } 851 852 @Override 853 protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) { 854 setRealValue(((ImageryLayer) usedLayers.iterator().next()).getSharpenLevel()); 855 } 856 857 @Override 858 protected void applyValueToLayer(ImageryLayer layer) { 859 layer.setSharpenLevel(getRealValue()); 860 } 861 862 @Override 863 public ImageIcon getIcon() { 864 return ImageProvider.get("dialogs/layerlist", "sharpness"); 865 } 866 867 @Override 868 public String getLabel() { 869 return tr("Sharpness"); 870 } 871 } 872 873 /** 874 * This slider allows you to change the colorfulness of a layer. 875 * 876 * @author Michael Zangl 877 * @see ImageryLayer#setColorfulness(double) 878 */ 879 private class ColorfulnessSlider extends FilterSlider<ImageryLayer> { 880 881 /** 882 * Create a new {@link ColorfulnessSlider} 883 */ 884 ColorfulnessSlider() { 885 super(0, MAX_COLORFUL_FACTOR, ImageryLayer.class); 886 setToolTipText(tr("Adjust colorfulness of the layer.")); 887 } 888 889 @Override 890 protected void updateSliderWhileEnabled(Collection<? extends Layer> usedLayers, boolean allHidden) { 891 setRealValue(((ImageryLayer) usedLayers.iterator().next()).getColorfulness()); 892 } 893 894 @Override 895 protected void applyValueToLayer(ImageryLayer layer) { 896 layer.setColorfulness(getRealValue()); 897 } 898 899 @Override 900 public ImageIcon getIcon() { 901 return ImageProvider.get("dialogs/layerlist", "colorfulness"); 902 } 903 904 @Override 905 public String getLabel() { 906 return tr("Colorfulness"); 907 } 908 } 706 909 } 707 910 708 911 /** -
src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java
diff --git a/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java b/src/org/openstreetmap/josm/gui/layer/AbstractTileSourceLayer.java index b52d9df..6467fe7 100644
a b implements ImageObserver, TileLoaderListener, ZoomChangeListener { 258 258 redraw(); 259 259 } 260 260 261 @Override 262 public void setSharpenLevel(double sharpenLevel) { 263 super.setSharpenLevel(sharpenLevel); 264 redraw(); 265 } 266 267 @Override 268 public void setColorfulness(double colorfulness) { 269 super.setColorfulness(colorfulness); 270 redraw(); 271 } 272 261 273 /** 262 274 * Marks layer as needing redraw on offset change 263 275 */ -
src/org/openstreetmap/josm/gui/layer/ImageryLayer.java
diff --git a/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java b/src/org/openstreetmap/josm/gui/layer/ImageryLayer.java index d1eb563..5b3f7a7 100644
a b import static org.openstreetmap.josm.tools.I18n.trc; 8 8 import java.awt.Color; 9 9 import java.awt.Component; 10 10 import java.awt.GridBagLayout; 11 import java.awt.Rectangle; 12 import java.awt.RenderingHints; 11 13 import java.awt.Transparency; 12 14 import java.awt.event.ActionEvent; 15 import java.awt.geom.Point2D; 16 import java.awt.geom.Rectangle2D; 13 17 import java.awt.image.BufferedImage; 14 18 import java.awt.image.BufferedImageOp; 19 import java.awt.image.ColorModel; 15 20 import java.awt.image.ConvolveOp; 21 import java.awt.image.DataBuffer; 22 import java.awt.image.DataBufferByte; 16 23 import java.awt.image.Kernel; 17 24 import java.awt.image.LookupOp; 18 25 import java.awt.image.ShortLookupTable; … … public abstract class ImageryLayer extends Layer { 69 76 protected double dy; 70 77 71 78 protected GammaImageProcessor gammaImageProcessor = new GammaImageProcessor(); 79 protected SharpenImageProcessor sharpenImageProcessor = new SharpenImageProcessor(); 80 protected ColorfulImageProcessor collorfulnessImageProcessor = new ColorfulImageProcessor(); 72 81 73 82 private final ImageryAdjustAction adjustAction = new ImageryAdjustAction(this); 74 83 … … public abstract class ImageryLayer extends Layer { 86 95 if (icon == null) { 87 96 icon = ImageProvider.get("imagery_small"); 88 97 } 89 addImageProcessor(c reateSharpener(PROP_SHARPEN_LEVEL.get()));98 addImageProcessor(collorfulnessImageProcessor); 90 99 addImageProcessor(gammaImageProcessor); 100 addImageProcessor(sharpenImageProcessor); 101 sharpenImageProcessor.setSharpenLevel(1 + PROP_SHARPEN_LEVEL.get() / 2f); 91 102 } 92 103 93 104 public double getPPD() { … … public abstract class ImageryLayer extends Layer { 232 243 return hasBookmarks ? subMenu : adjustMenuItem; 233 244 } 234 245 246 // Can soon be removed. Replaced by sharpen image processor. 247 @Deprecated 235 248 public ImageProcessor createSharpener(int sharpenLevel) { 236 249 final Kernel kernel; 237 250 if (sharpenLevel == 1) { … … public abstract class ImageryLayer extends Layer { 300 313 } 301 314 302 315 /** 316 * Sharpens or blurs the image, depending on the sharpen value. 317 * <p> 318 * A positive sharpen level means that we sharpen the image. 319 * <p> 320 * A negative sharpen level let's us blur the image. -1 is the most useful value there. 321 * 322 * @author Michael Zangl 323 */ 324 public static class SharpenImageProcessor implements ImageProcessor { 325 private float sharpenLevel = 0; 326 private ConvolveOp op; 327 328 private static float[] KERNEL_IDENTITY = new float[] { 329 0, 0, 0, 330 0, 1, 0, 331 0, 0, 0 332 }; 333 334 private static float[] KERNEL_BLUR = new float[] { 335 1f / 16, 2f / 16, 1f / 16, 336 2f / 16, 4f / 16, 2f / 16, 337 1f / 16, 2f / 16, 1f / 16 338 }; 339 340 private static float[] KERNEL_SHARPEN = new float[] { 341 -.5f, -1f, -.5f, 342 -1f, 7, -1f, 343 -.5f, -1f, -.5f 344 }; 345 346 /** 347 * Gets the current sharpen level. 348 * @return The level. 349 */ 350 public float getSharpenLevel() { 351 return sharpenLevel; 352 } 353 354 /** 355 * Sets the sharpening level. 356 * @param sharpenLevel The level. Clamped to be positive or 0. 357 */ 358 public void setSharpenLevel(float sharpenLevel) { 359 if (sharpenLevel < 0) { 360 this.sharpenLevel = 0; 361 } else { 362 this.sharpenLevel = sharpenLevel; 363 } 364 365 if (this.sharpenLevel < 0.95) { 366 op = generateMixed(this.sharpenLevel, KERNEL_IDENTITY, KERNEL_BLUR); 367 } else if (this.sharpenLevel > 1.05) { 368 op = generateMixed(this.sharpenLevel - 1, KERNEL_SHARPEN, KERNEL_IDENTITY); 369 } else { 370 op = null; 371 } 372 } 373 374 private ConvolveOp generateMixed(float aFactor, float[] a, float[] b) { 375 if (a.length != 9 || b.length != 9) { 376 throw new IllegalArgumentException("Illegal kernel array length."); 377 } 378 float[] values = new float[9]; 379 for (int i = 0; i < values.length; i++) { 380 values[i] = aFactor * a[i] + (1 - aFactor) * b[i]; 381 } 382 return new ConvolveOp(new Kernel(3, 3, values), ConvolveOp.EDGE_NO_OP, null); 383 } 384 385 @Override 386 public BufferedImage process(BufferedImage image) { 387 if (op != null) { 388 return op.filter(image, null); 389 } else { 390 return image; 391 } 392 } 393 394 @Override 395 public String toString() { 396 return "SharpenImageProcessor [sharpenLevel=" + sharpenLevel + "]"; 397 } 398 } 399 400 /** 401 * Adds or removes the colorfulness of the image. 402 * 403 * @author Michael Zangl 404 */ 405 public static class ColorfulImageProcessor implements ImageProcessor { 406 private ColorfulFilter op = null; 407 private double colorfulness = 1; 408 409 /** 410 * Gets the colorfulness value. 411 * @return The value 412 */ 413 public double getColorfulness() { 414 return colorfulness; 415 } 416 417 /** 418 * Sets the colorfulness value. Clamps it to 0+ 419 * @param colorfulness The value 420 */ 421 public void setColorfulness(double colorfulness) { 422 if (colorfulness < 0) { 423 this.colorfulness = 0; 424 } else { 425 this.colorfulness = colorfulness; 426 } 427 428 if (this.colorfulness < .95 || this.colorfulness > 1.05) { 429 op = new ColorfulFilter(this.colorfulness); 430 } else { 431 op = null; 432 } 433 } 434 435 @Override 436 public BufferedImage process(BufferedImage image) { 437 if (op != null) { 438 return op.filter(image, null); 439 } else { 440 return image; 441 } 442 } 443 444 @Override 445 public String toString() { 446 return "ColorfulImageProcessor [colorfulness=" + colorfulness + "]"; 447 } 448 } 449 450 private static class ColorfulFilter implements BufferedImageOp { 451 private final double colorfulness; 452 453 /** 454 * Create a new colorful filter. 455 * @param colorfulness The colorfulness as defined in the {@link ColorfulImageProcessor} class. 456 */ 457 ColorfulFilter(double colorfulness) { 458 this.colorfulness = colorfulness; 459 } 460 461 @Override 462 public BufferedImage filter(BufferedImage src, BufferedImage dest) { 463 if (src.getWidth() == 0 || src.getHeight() == 0) { 464 return src; 465 } 466 467 if (dest == null) { 468 dest = createCompatibleDestImage(src, null); 469 } 470 DataBuffer srcBuffer = src.getRaster().getDataBuffer(); 471 DataBuffer destBuffer = dest.getRaster().getDataBuffer(); 472 if (!(srcBuffer instanceof DataBufferByte) || !(destBuffer instanceof DataBufferByte)) { 473 Main.trace("Cannot apply color filter: Images do not use DataBufferByte."); 474 return src; 475 } 476 477 int type = src.getType(); 478 if (type != dest.getType()) { 479 Main.trace("Cannot apply color filter: Src / Dest differ in type (" + type + "/" + dest.getType() + ")"); 480 return src; 481 } 482 int redOffset, greenOffset, blueOffset, alphaOffset = 0; 483 switch (type) { 484 case BufferedImage.TYPE_3BYTE_BGR: 485 blueOffset = 0; 486 greenOffset = 1; 487 redOffset = 2; 488 break; 489 case BufferedImage.TYPE_4BYTE_ABGR: 490 case BufferedImage.TYPE_4BYTE_ABGR_PRE: 491 blueOffset = 1; 492 greenOffset = 2; 493 redOffset = 3; 494 break; 495 case BufferedImage.TYPE_INT_ARGB: 496 case BufferedImage.TYPE_INT_ARGB_PRE: 497 redOffset = 0; 498 greenOffset = 1; 499 blueOffset = 2; 500 alphaOffset = 3; 501 break; 502 default: 503 Main.trace("Cannot apply color filter: Source image is of wrong type (" + type + ")."); 504 return src; 505 } 506 doFilter((DataBufferByte) srcBuffer, (DataBufferByte) destBuffer, redOffset, greenOffset, blueOffset, 507 alphaOffset, src.getAlphaRaster() != null); 508 return dest; 509 } 510 511 private void doFilter(DataBufferByte src, DataBufferByte dest, int redOffset, int greenOffset, int blueOffset, 512 int alphaOffset, boolean hasAlpha) { 513 byte[] srcPixels = src.getData(); 514 byte[] destPixels = dest.getData(); 515 if (srcPixels.length != destPixels.length) { 516 Main.trace("Cannot apply color filter: Source/Dest lengths differ."); 517 return; 518 } 519 int entries = hasAlpha ? 4 : 3; 520 for (int i = 0; i < srcPixels.length; i += entries) { 521 int r = srcPixels[i + redOffset] & 0xff; 522 int g = srcPixels[i + greenOffset] & 0xff; 523 int b = srcPixels[i + blueOffset] & 0xff; 524 float luminosity = r * .21f + g * .72f + b * .07f; 525 destPixels[i + redOffset] = mix(r, luminosity); 526 destPixels[i + greenOffset] = mix(g, luminosity); 527 destPixels[i + blueOffset] = mix(b, luminosity); 528 if (hasAlpha) { 529 destPixels[i + alphaOffset] = srcPixels[i + alphaOffset]; 530 } 531 } 532 } 533 534 private byte mix(int color, float luminosity) { 535 int val = (int) (colorfulness * color + (1 - colorfulness) * luminosity); 536 if (val < 0) { 537 return 0; 538 } else if (val > 0xff) { 539 return (byte) 0xff; 540 } else { 541 return (byte) val; 542 } 543 } 544 545 @Override 546 public Rectangle2D getBounds2D(BufferedImage src) { 547 return new Rectangle(src.getWidth(), src.getHeight()); 548 } 549 550 @Override 551 public BufferedImage createCompatibleDestImage(BufferedImage src, ColorModel destCM) { 552 return new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); 553 } 554 555 @Override 556 public Point2D getPoint2D(Point2D srcPt, Point2D dstPt) { 557 return (Point2D) srcPt.clone(); 558 } 559 560 @Override 561 public RenderingHints getRenderingHints() { 562 return null; 563 } 564 565 } 566 567 /** 303 568 * Returns the currently set gamma value. 304 569 * @return the currently set gamma value 305 570 */ … … public abstract class ImageryLayer extends Layer { 316 581 } 317 582 318 583 /** 584 * Gets the current sharpen level. 585 * @return The sharpen level. 586 */ 587 public double getSharpenLevel() { 588 return sharpenImageProcessor.getSharpenLevel(); 589 } 590 591 /** 592 * Sets the sharpen level for the layer. 593 * <code>1</code> means no change in sharpness. 594 * Values in range 0..1 blur the image. 595 * Values above 1 are used to sharpen the image. 596 * @param sharpenLevel The sharpen level. 597 */ 598 public void setSharpenLevel(double sharpenLevel) { 599 sharpenImageProcessor.setSharpenLevel((float) sharpenLevel); 600 } 601 602 /** 603 * Gets the colorfulness of this image. 604 * @return The colorfulness 605 */ 606 public double getColorfulness() { 607 return collorfulnessImageProcessor.getColorfulness(); 608 } 609 610 /** 611 * Sets the colorfulness of this image. 612 * 0 means grayscale. 613 * 1 means normal colorfulness. 614 * Values greater than 1 are allowed. 615 * @param colorfulness The colorfulness. 616 */ 617 public void setColorfulness(double colorfulness) { 618 collorfulnessImageProcessor.setColorfulness(colorfulness); 619 } 620 621 /** 319 622 * This method adds the {@link ImageProcessor} to this Layer if it is not {@code null}. 320 623 * 321 624 * @param processor that processes the image -
test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java
diff --git a/test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java b/test/unit/org/openstreetmap/josm/gui/dialogs/LayerListDialogTest.java index afede87..9157742 100644
a b public class LayerListDialogTest { 46 46 47 47 // now check values 48 48 action.updateValues(); 49 assertEquals(1.0, action.readOpacityValue(), 1e-15); 50 assertEquals(1.0, action.readGammaValue(), 1e-15); 49 assertEquals(1.0, action.opacitySlider.getRealValue(), 1e-15); 51 50 52 action.setOpacityValue(.5, false); 53 action.setGammaValue(1.5); 51 action.opacitySlider.setRealValue(.5); 54 52 action.updateValues(); 55 53 56 assertEquals(0.5, action.readOpacityValue(), 1e-15); 57 assertEquals(1.5, action.readGammaValue(), 1e-15); 54 assertEquals(0.5, action.opacitySlider.getRealValue(), 1e-15); 58 55 59 action.setVisible (false);56 action.setVisibleFlag(false); 60 57 action.updateValues(); 61 58 assertFalse(layer.isVisible()); 62 59 63 action.setVisible (true);60 action.setVisibleFlag(true); 64 61 action.updateValues(); 65 62 assertTrue(layer.isVisible()); 66 63 67 64 // layer stays visible during adjust 68 action.setOpacityValue(0, true); 65 action.opacitySlider.setValueIsAdjusting(true); 66 action.opacitySlider.setRealValue(0); 69 67 assertEquals(0, layer.getOpacity(), 1e-15); 70 68 layer.setOpacity(.1); // to make layer.isVisible work 71 69 assertTrue(layer.isVisible()); 72 70 layer.setOpacity(0); 73 71 74 action.setOpacityValue(0, false); 72 action.opacitySlider.setValueIsAdjusting(false); 73 action.opacitySlider.setRealValue(0); 75 74 assertEquals(0, layer.getOpacity(), 1e-15); 76 75 layer.setOpacity(.1); // to make layer.isVisible work 77 76 assertFalse(layer.isVisible()); … … public class LayerListDialogTest { 79 78 action.updateValues(); 80 79 81 80 // Opacity reset when it was 0 and user set layer to visible. 82 action.setVisible (true);81 action.setVisibleFlag(true); 83 82 action.updateValues(); 84 assertEquals(1.0, action. readOpacityValue(), 1e-15);83 assertEquals(1.0, action.opacitySlider.getRealValue(), 1e-15); 85 84 assertEquals(1.0, layer.getOpacity(), 1e-15); 86 85 87 86 } finally {