images, Dimension size ) {
- super( new ImageIcon( images.get( 0 ) ), size.width, size.height );
- this.images = images;
- }
-
- @Override
- protected Image getResolutionVariant( int destImageWidth, int destImageHeight ) {
- for( Image image : images ) {
+ // search for optimal image size
+ for( Image image : allImages ) {
if( destImageWidth <= image.getWidth( null ) &&
destImageHeight <= image.getHeight( null ) )
return image;
}
- return images.get( images.size() - 1 );
+ // use largest image
+ return allImages.get( allImages.size() - 1 );
}
}
diff --git a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ScaledImageIcon.java b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ScaledImageIcon.java
index cb503c31..0d683ac9 100644
--- a/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ScaledImageIcon.java
+++ b/flatlaf-core/src/main/java/com/formdev/flatlaf/util/ScaledImageIcon.java
@@ -77,7 +77,7 @@ debug*/
double scaleFactor = systemScaleFactor * userScaleFactor;
// paint input image icon if not necessary to scale
- if( scaleFactor == 1 && iconWidth == imageIcon.getIconWidth() && iconHeight == imageIcon.getIconHeight() ) {
+ if( scaleFactor == 1 && imageIcon != null && iconWidth == imageIcon.getIconWidth() && iconHeight == imageIcon.getIconHeight() ) {
imageIcon.paintIcon( c, g, x, y );
return;
}
diff --git a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatSVGUtils.java b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatSVGUtils.java
index f40d3d83..2ba944e6 100644
--- a/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatSVGUtils.java
+++ b/flatlaf-extras/src/main/java/com/formdev/flatlaf/extras/FlatSVGUtils.java
@@ -16,6 +16,7 @@
package com.formdev.flatlaf.extras;
+import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
@@ -23,8 +24,10 @@ import java.awt.image.BufferedImage;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
+import java.util.Collections;
import java.util.List;
import javax.swing.JWindow;
+import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import com.kitfox.svg.SVGCache;
import com.kitfox.svg.SVGDiagram;
import com.kitfox.svg.SVGException;
@@ -40,22 +43,51 @@ public class FlatSVGUtils
* Creates from the given SVG a list of icon images with different sizes that
* can be used for windows headers. The SVG should have a size of 16x16,
* otherwise it is scaled.
+ *
+ * If running on Java 9 or later and multi-resolution image support is available,
+ * then a single multi-resolution image is returned that creates images on demand
+ * for requested sizes from SVG.
+ * This has the advantage that only images for used sizes are created.
+ * Also if unusual sizes are requested (e.g. 18x18), then they are created from SVG.
*
* @param svgName the name of the SVG resource (a '/'-separated path)
- * @return list of icon images with different sizes (16x16, 24x24, 32x32, 48x48 and 64x64)
+ * @return list of icon images with different sizes (16x16, 20x20, 24x24, 28x28, 32x32, 48x48 and 64x64)
* @throws RuntimeException if failed to load or render SVG file
* @see JWindow#setIconImages(List)
*/
public static List createWindowIconImages( String svgName ) {
SVGDiagram diagram = loadSVG( svgName );
- return Arrays.asList(
- svg2image( diagram, 16, 16 ),
- svg2image( diagram, 24, 24 ),
- svg2image( diagram, 32, 32 ),
- svg2image( diagram, 48, 48 ),
- svg2image( diagram, 64, 64 )
- );
+ if( MultiResolutionImageSupport.isAvailable() ) {
+ // use a multi-resolution image that creates images on demand for requested sizes
+ return Collections.singletonList( MultiResolutionImageSupport.create( 0,
+ new Dimension[] {
+ // Listing all these sizes here is actually not necessary because
+ // any size is created on demand when
+ // MultiResolutionImage.getResolutionVariant(double destImageWidth, double destImageHeight)
+ // is invoked.
+ // This sizes are only used by MultiResolutionImage.getResolutionVariants().
+ new Dimension( 16, 16 ), // 100%
+ new Dimension( 20, 20 ), // 125%
+ new Dimension( 24, 24 ), // 150%
+ new Dimension( 28, 28 ), // 175%
+ new Dimension( 32, 32 ), // 200%
+ new Dimension( 48, 48 ), // 300%
+ new Dimension( 64, 64 ), // 400%
+ }, dim -> {
+ return svg2image( diagram, dim.width, dim.height );
+ } ) );
+ } else {
+ return Arrays.asList(
+ svg2image( diagram, 16, 16 ), // 100%
+ svg2image( diagram, 20, 20 ), // 125%
+ svg2image( diagram, 24, 24 ), // 150%
+ svg2image( diagram, 28, 28 ), // 175%
+ svg2image( diagram, 32, 32 ), // 200%
+ svg2image( diagram, 48, 48 ), // 300%
+ svg2image( diagram, 64, 64 ) // 400%
+ );
+ }
}
/**
diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java
index 54f6704c..41e7f04a 100644
--- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java
+++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.java
@@ -19,12 +19,16 @@ package com.formdev.flatlaf.testing;
import java.awt.*;
import java.awt.Dialog.ModalityType;
import java.awt.event.*;
+import java.awt.image.BufferedImage;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.*;
import com.formdev.flatlaf.FlatClientProperties;
+import com.formdev.flatlaf.ui.FlatUIUtils;
+import com.formdev.flatlaf.util.MultiResolutionImageSupport;
import net.miginfocom.swing.*;
/**
@@ -355,6 +359,32 @@ public class FlatWindowDecorationsTest
window.setIconImages( images );
else if( iconTestRandomRadioButton.isSelected() )
window.setIconImage( images.get( (int) (Math.random() * images.size()) ) );
+ else if( iconTestMRIRadioButton.isSelected() ) {
+ ArrayList sortedImages = new ArrayList<>( images );
+ sortedImages.sort( (image1, image2) -> {
+ return image1.getWidth( null ) - image2.getWidth( null );
+ } );
+ window.setIconImage( MultiResolutionImageSupport.create( 0, sortedImages.toArray( new Image[sortedImages.size()] ) ) );
+ } else if( iconTestDynMRIRadioButton.isSelected() ) {
+ window.setIconImage( MultiResolutionImageSupport.create( 0,
+ new Dimension[] {
+ new Dimension( 16, 16 ),
+ }, dim -> {
+ BufferedImage image = new BufferedImage( dim.width, dim.height, BufferedImage.TYPE_INT_ARGB );
+ Graphics2D g = image.createGraphics();
+ try {
+ g.setColor( Color.getHSBColor( (dim.width - 16) / 64f, 1, 0.8f ) );
+ g.fillRect( 0, 0, dim.width, dim.height );
+
+ g.setColor( Color.white );
+ g.setFont( new Font( "Dialog", Font.PLAIN, (int) (dim.width * 0.8) ) );
+ FlatUIUtils.drawString( this, g, String.valueOf( dim.width ), 0, dim.height - 2 );
+ } finally {
+ g.dispose();
+ }
+ return image;
+ } ) );
+ }
}
private JRootPane getWindowRootPane() {
@@ -403,6 +433,8 @@ public class FlatWindowDecorationsTest
iconNoneRadioButton = new JRadioButton();
iconTestAllRadioButton = new JRadioButton();
iconTestRandomRadioButton = new JRadioButton();
+ iconTestMRIRadioButton = new JRadioButton();
+ iconTestDynMRIRadioButton = new JRadioButton();
JButton openDialogButton = new JButton();
JButton openFrameButton = new JButton();
menuBar = new JMenuBar();
@@ -642,6 +674,8 @@ public class FlatWindowDecorationsTest
// rows
"[]" +
"[]" +
+ "[]" +
+ "[]" +
"[]"));
//---- iconNoneRadioButton ----
@@ -659,6 +693,16 @@ public class FlatWindowDecorationsTest
iconTestRandomRadioButton.setText("test random");
iconTestRandomRadioButton.addActionListener(e -> iconChanged());
panel2.add(iconTestRandomRadioButton, "cell 0 2");
+
+ //---- iconTestMRIRadioButton ----
+ iconTestMRIRadioButton.setText("test multi-resolution (Java 9+)");
+ iconTestMRIRadioButton.addActionListener(e -> iconChanged());
+ panel2.add(iconTestMRIRadioButton, "cell 0 3");
+
+ //---- iconTestDynMRIRadioButton ----
+ iconTestDynMRIRadioButton.setText("test dynamic multi-resolution (Java 9+)");
+ iconTestDynMRIRadioButton.addActionListener(e -> iconChanged());
+ panel2.add(iconTestDynMRIRadioButton, "cell 0 4");
}
add(panel2, "cell 1 8");
@@ -858,6 +902,8 @@ public class FlatWindowDecorationsTest
iconButtonGroup.add(iconNoneRadioButton);
iconButtonGroup.add(iconTestAllRadioButton);
iconButtonGroup.add(iconTestRandomRadioButton);
+ iconButtonGroup.add(iconTestMRIRadioButton);
+ iconButtonGroup.add(iconTestDynMRIRadioButton);
// JFormDesigner - End of component initialization //GEN-END:initComponents
}
@@ -892,6 +938,8 @@ public class FlatWindowDecorationsTest
private JRadioButton iconNoneRadioButton;
private JRadioButton iconTestAllRadioButton;
private JRadioButton iconTestRandomRadioButton;
+ private JRadioButton iconTestMRIRadioButton;
+ private JRadioButton iconTestDynMRIRadioButton;
private JMenuBar menuBar;
// JFormDesigner - End of variables declaration //GEN-END:variables
}
diff --git a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd
index 113f6d11..bc655f8e 100644
--- a/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd
+++ b/flatlaf-testing/src/main/java/com/formdev/flatlaf/testing/FlatWindowDecorationsTest.jfd
@@ -328,7 +328,7 @@ new FormModel {
} )
add( new FormContainer( "javax.swing.JPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$columnConstraints": "[fill]"
- "$rowConstraints": "[][][]"
+ "$rowConstraints": "[][][][][]"
"$layoutConstraints": "ltr,insets 0,hidemode 3,gap 0 0"
} ) {
name: "panel2"
@@ -366,6 +366,28 @@ new FormModel {
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 0 2"
} )
+ add( new FormComponent( "javax.swing.JRadioButton" ) {
+ name: "iconTestMRIRadioButton"
+ "text": "test multi-resolution (Java 9+)"
+ "$buttonGroup": new FormReference( "iconButtonGroup" )
+ auxiliary() {
+ "JavaCodeGenerator.variableLocal": false
+ }
+ addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "iconChanged", false ) )
+ }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
+ "value": "cell 0 3"
+ } )
+ add( new FormComponent( "javax.swing.JRadioButton" ) {
+ name: "iconTestDynMRIRadioButton"
+ "text": "test dynamic multi-resolution (Java 9+)"
+ "$buttonGroup": new FormReference( "iconButtonGroup" )
+ auxiliary() {
+ "JavaCodeGenerator.variableLocal": false
+ }
+ addEvent( new FormEvent( "java.awt.event.ActionListener", "actionPerformed", "iconChanged", false ) )
+ }, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
+ "value": "cell 0 4"
+ } )
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 8"
} )
@@ -386,7 +408,7 @@ new FormModel {
} )
}, new FormLayoutConstraints( null ) {
"location": new java.awt.Point( 0, 0 )
- "size": new java.awt.Dimension( 690, 440 )
+ "size": new java.awt.Dimension( 690, 495 )
} )
add( new FormContainer( "javax.swing.JMenuBar", new FormLayoutManager( class javax.swing.JMenuBar ) ) {
name: "menuBar"
@@ -552,18 +574,18 @@ new FormModel {
} )
} )
}, new FormLayoutConstraints( null ) {
- "location": new java.awt.Point( 0, 470 )
+ "location": new java.awt.Point( 0, 515 )
"size": new java.awt.Dimension( 255, 30 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "styleButtonGroup"
}, new FormLayoutConstraints( null ) {
- "location": new java.awt.Point( 0, 520 )
+ "location": new java.awt.Point( 0, 565 )
} )
add( new FormNonVisual( "javax.swing.ButtonGroup" ) {
name: "iconButtonGroup"
}, new FormLayoutConstraints( null ) {
- "location": new java.awt.Point( 0, 570 )
+ "location": new java.awt.Point( 0, 615 )
} )
}
}