support multi-resolution images in disabled icons on Java 9+ (e.g. @2x icons on macOS) (issue #70)

This commit is contained in:
Karl Tauber
2020-04-24 17:07:30 +02:00
parent 0141dfbea2
commit c9c703fe98
10 changed files with 195 additions and 5 deletions

View File

@@ -23,6 +23,11 @@ plugins {
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) { if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
sourceSets { sourceSets {
create( "java9" ) {
java {
setSrcDirs( listOf( "src/main/java9" ) )
}
}
create( "module-info" ) { create( "module-info" ) {
java { java {
// include "src/main/java" here to get compile errors if classes are // include "src/main/java" here to get compile errors if classes are
@@ -52,6 +57,12 @@ tasks {
archiveBaseName.set( "flatlaf" ) archiveBaseName.set( "flatlaf" )
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) { if( JavaVersion.current() >= JavaVersion.VERSION_1_9 ) {
manifest.attributes( "Multi-Release" to "true" )
into( "META-INF/versions/9" ) {
from( sourceSets["java9"].output )
}
from( sourceSets["module-info"].output ) { from( sourceSets["module-info"].output ) {
include( "module-info.class" ) include( "module-info.class" )
} }

View File

@@ -41,6 +41,7 @@ import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.swing.AbstractButton; import javax.swing.AbstractButton;
@@ -64,6 +65,7 @@ import javax.swing.plaf.basic.BasicLookAndFeel;
import javax.swing.text.StyleContext; import javax.swing.text.StyleContext;
import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.HTMLEditorKit;
import com.formdev.flatlaf.util.GrayFilter; import com.formdev.flatlaf.util.GrayFilter;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SystemInfo; import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
@@ -136,9 +138,14 @@ public abstract class FlatLaf
: new GrayFilter( 25, -25, 100 ); : new GrayFilter( 25, -25, 100 );
} }
ImageFilter filter = (ImageFilter) grayFilter;
Function<Image, Image> mapper = img -> {
ImageProducer producer = new FilteredImageSource( img.getSource(), filter );
return Toolkit.getDefaultToolkit().createImage( producer );
};
Image image = ((ImageIcon)icon).getImage(); Image image = ((ImageIcon)icon).getImage();
ImageProducer producer = new FilteredImageSource( image.getSource(), (ImageFilter) grayFilter ); return new ImageIconUIResource( MultiResolutionImageSupport.map( image, mapper ) );
return new ImageIconUIResource( Toolkit.getDefaultToolkit().createImage( producer ) );
} }
return null; return null;

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Image;
import java.util.function.Function;
/**
* Support for multi-resolution images available since Java 9.
*
* @author Karl Tauber
*/
public class MultiResolutionImageSupport
{
public static boolean isAvailable() {
return false;
}
public static boolean isMultiResolutionImage( Image image ) {
return false;
}
public static Image create( int baseImageIndex, Image... resolutionVariants ) {
return resolutionVariants[baseImageIndex];
}
public static Image map( Image image, Function<Image, Image> mapper ) {
return mapper.apply( image );
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2020 FormDev Software GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.formdev.flatlaf.util;
import java.awt.Image;
import java.awt.image.AbstractMultiResolutionImage;
import java.awt.image.BaseMultiResolutionImage;
import java.awt.image.MultiResolutionImage;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.function.Function;
import javax.swing.ImageIcon;
/**
* Support for multi-resolution images available since Java 9.
*
* @author Karl Tauber
*/
public class MultiResolutionImageSupport
{
public static boolean isAvailable() {
return true;
}
public static boolean isMultiResolutionImage( Image image ) {
return image instanceof MultiResolutionImage;
}
public static Image create( int baseImageIndex, Image... resolutionVariants ) {
return new BaseMultiResolutionImage( baseImageIndex, resolutionVariants );
}
public static Image map( Image image, Function<Image, Image> mapper ) {
return image instanceof MultiResolutionImage
? new MappedMultiResolutionImage( image, mapper )
: mapper.apply( image );
}
//---- class MappedMultiResolutionImage -----------------------------------
private static class MappedMultiResolutionImage
extends AbstractMultiResolutionImage
{
private final Image mrImage;
private final Function<Image, Image> mapper;
private final IdentityHashMap<Image, Image> cache = new IdentityHashMap<>();
MappedMultiResolutionImage( Image mrImage, Function<Image, Image> mapper ) {
assert mrImage instanceof MultiResolutionImage;
this.mrImage = mrImage;
this.mapper = mapper;
}
@Override
public Image getResolutionVariant( double destImageWidth, double destImageHeight ) {
Image variant = ((MultiResolutionImage)mrImage).getResolutionVariant( destImageWidth, destImageHeight );
return mapAndCacheImage( variant );
}
@Override
public List<Image> getResolutionVariants() {
List<Image> variants = ((MultiResolutionImage)mrImage).getResolutionVariants();
List<Image> mappedVariants = new ArrayList<>();
for( Image image : variants )
mappedVariants.add( mapAndCacheImage( image ) );
return mappedVariants;
}
@Override
protected Image getBaseImage() {
return mapAndCacheImage( mrImage );
}
private Image mapAndCacheImage( Image image ) {
return cache.computeIfAbsent( image, img -> {
return new ImageIcon( mapper.apply( img ) ).getImage();
} );
}
}
}

View File

@@ -43,6 +43,9 @@ tasks {
manifest { manifest {
attributes( "Main-Class" to "com.formdev.flatlaf.demo.FlatLafDemo" ) attributes( "Main-Class" to "com.formdev.flatlaf.demo.FlatLafDemo" )
if( JavaVersion.current() >= JavaVersion.VERSION_1_9 )
attributes( "Multi-Release" to "true" )
} }
exclude( "module-info.class" ) exclude( "module-info.class" )

View File

@@ -101,6 +101,7 @@ class MoreComponentsPanel
JButton button7 = new JButton(); JButton button7 = new JButton();
JButton button8 = new JButton(); JButton button8 = new JButton();
JToggleButton toggleButton6 = new JToggleButton(); JToggleButton toggleButton6 = new JToggleButton();
JButton button1 = new JButton();
//======== this ======== //======== this ========
setLayout(new MigLayout( setLayout(new MigLayout(
@@ -380,6 +381,11 @@ class MoreComponentsPanel
toggleButton6.setIcon(UIManager.getIcon("Tree.leafIcon")); toggleButton6.setIcon(UIManager.getIcon("Tree.leafIcon"));
toggleButton6.setSelected(true); toggleButton6.setSelected(true);
toolBar1.add(toggleButton6); toolBar1.add(toggleButton6);
//---- button1 ----
button1.setIcon(new ImageIcon(getClass().getResource("/com/formdev/flatlaf/demo/icons/intellij-showWriteAccess.png")));
button1.setEnabled(false);
toolBar1.add(button1);
} }
add(toolBar1, "cell 1 10 3 1,growx"); add(toolBar1, "cell 1 10 3 1,growx");
// JFormDesigner - End of component initialization //GEN-END:initComponents // JFormDesigner - End of component initialization //GEN-END:initComponents

View File

@@ -1,4 +1,4 @@
JFDML JFormDesigner: "7.0.0.0.194" Java: "11.0.2" encoding: "UTF-8" JFDML JFormDesigner: "7.0.1.0.272" Java: "13.0.2" encoding: "UTF-8"
new FormModel { new FormModel {
contentType: "form/swing" contentType: "form/swing"
@@ -355,6 +355,11 @@ new FormModel {
"icon": new com.jformdesigner.model.SwingIcon( 2, "Tree.leafIcon" ) "icon": new com.jformdesigner.model.SwingIcon( 2, "Tree.leafIcon" )
"selected": true "selected": true
} ) } )
add( new FormComponent( "javax.swing.JButton" ) {
name: "button1"
"icon": new com.jformdesigner.model.SwingIcon( 0, "/com/formdev/flatlaf/demo/icons/intellij-showWriteAccess.png" )
"enabled": false
} )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) { }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 10 3 1,growx" "value": "cell 1 10 3 1,growx"
} ) } )

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

View File

@@ -27,8 +27,11 @@ import java.awt.image.FilteredImageSource;
import java.awt.image.ImageProducer; import java.awt.image.ImageProducer;
import java.awt.image.RGBImageFilter; import java.awt.image.RGBImageFilter;
import java.beans.*; import java.beans.*;
import java.net.URL;
import javax.swing.*; import javax.swing.*;
import com.formdev.flatlaf.FlatLaf; import com.formdev.flatlaf.FlatLaf;
import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.formdev.flatlaf.util.SystemInfo;
import com.formdev.flatlaf.util.UIScale; import com.formdev.flatlaf.util.UIScale;
import net.miginfocom.swing.*; import net.miginfocom.swing.*;
@@ -575,8 +578,23 @@ public class FlatDisabledIconsTest
private final ImageIcon darkIcon; private final ImageIcon darkIcon;
LightOrDarkIcon( String lightIconName, String darkIconName ) { LightOrDarkIcon( String lightIconName, String darkIconName ) {
this.lightIcon = new ImageIcon( getClass().getResource( lightIconName ) ); this.lightIcon = loadIcon( lightIconName );
this.darkIcon = new ImageIcon( getClass().getResource( darkIconName ) ); this.darkIcon = loadIcon( darkIconName );
}
private static ImageIcon loadIcon( String iconName ) {
ImageIcon icon = new ImageIcon( LightOrDarkIcon.class.getResource( iconName ) );
if( SystemInfo.IS_MAC || !MultiResolutionImageSupport.isAvailable() || !iconName.endsWith( ".png" ) )
return icon;
String iconName2x = iconName.replace( ".png", "@2x.png" );
URL url2x = LightOrDarkIcon.class.getResource( iconName2x );
if( url2x == null )
return icon;
ImageIcon icon2x = new ImageIcon( url2x );
return new ImageIcon( MultiResolutionImageSupport.create( 0, icon.getImage(), icon2x.getImage() ) );
} }
private ImageIcon getCurrentIcon() { private ImageIcon getCurrentIcon() {