Skip to content

Commit

Permalink
FileChooser: scale file icons (issue #100)
Browse files Browse the repository at this point in the history
  • Loading branch information
DevCharly committed May 16, 2020
1 parent e75caf5 commit f632c35
Show file tree
Hide file tree
Showing 6 changed files with 216 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
import java.awt.Dimension;
import java.io.File;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileView;
import javax.swing.plaf.ComponentUI;
import javax.swing.plaf.metal.MetalFileChooserUI;
import com.formdev.flatlaf.util.ScaledImageIcon;
import com.formdev.flatlaf.util.UIScale;

/**
Expand Down Expand Up @@ -157,19 +159,32 @@ private class FlatFileView
{
@Override
public Icon getIcon( File f ) {
// get cached icon
Icon icon = getCachedIcon( f );
if( icon != null )
return icon;

// get system icon
if( f != null ) {
icon = getFileChooser().getFileSystemView().getSystemIcon( f );

if( icon != null ) {
if( icon instanceof ImageIcon )
icon = new ScaledImageIcon( (ImageIcon) icon );
cacheIcon( f, icon );
return icon;
}
}

return super.getIcon( f );
// get default icon
icon = super.getIcon( f );

if( icon instanceof ImageIcon ) {
icon = new ScaledImageIcon( (ImageIcon) icon );
cacheIcon( f, icon );
}

return icon;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package com.formdev.flatlaf.util;

import java.awt.Image;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;

/**
Expand All @@ -41,4 +43,12 @@ public static Image create( int baseImageIndex, Image... resolutionVariants ) {
public static Image map( Image image, Function<Image, Image> mapper ) {
return mapper.apply( image );
}

public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
return image;
}

public static List<Image> getResolutionVariants( Image image ) {
return Collections.singletonList( image );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
/*
* 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.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import javax.swing.Icon;
import javax.swing.ImageIcon;

/**
* Scales the given image icon using the system and user scale factors and
* paints the icon at system scale factor 1x. This gives best scaling quality.
* If the given image icon supports multiple resolutions, the best resolution
* variant is used. The last scaled image is cached for faster repainting.
*
* @author Karl Tauber
*/
public class ScaledImageIcon
implements Icon
{
private final ImageIcon imageIcon;

private double lastSystemScaleFactor;
private float lastUserScaleFactor;
private Image lastImage;

public ScaledImageIcon( ImageIcon imageIcon ) {
this.imageIcon = imageIcon;
}

@Override
public int getIconWidth() {
return UIScale.scale( imageIcon.getIconWidth() );
}

@Override
public int getIconHeight() {
return UIScale.scale( imageIcon.getIconHeight() );
}

@Override
public void paintIcon( Component c, Graphics g, int x, int y ) {
/*debug
g.setColor( Color.red );
g.drawRect( x, y, getIconWidth(), getIconHeight() );
debug*/

// scale factor
double systemScaleFactor = UIScale.getSystemScaleFactor( (Graphics2D) g );
float userScaleFactor = UIScale.getUserScaleFactor();
double scaleFactor = systemScaleFactor * userScaleFactor;

// paint input image icon if not necessary to scale
if( scaleFactor == 1 ) {
imageIcon.paintIcon( c, g, x, y );
return;
}

// paint cached scaled icon
if( systemScaleFactor == lastSystemScaleFactor &&
userScaleFactor == lastUserScaleFactor &&
lastImage != null )
{
paintLastImage( g, x, y );
return;
}

// destination image size
int destImageWidth = (int) Math.round( imageIcon.getIconWidth() * scaleFactor );
int destImageHeight = (int) Math.round( imageIcon.getIconHeight() * scaleFactor );

// get resolution variant of image if it is a multi-resolution image
Image image = MultiResolutionImageSupport.getResolutionVariant(
imageIcon.getImage(), destImageWidth, destImageHeight );

// size of image
int imageWidth = image.getWidth( null );
int imageHeight = image.getHeight( null );

// scale image if necessary to destination size
if( imageWidth != destImageWidth || imageHeight != destImageHeight ) {
// determine scaling method; default is "quality"
Object scalingInterpolation = RenderingHints.VALUE_INTERPOLATION_BICUBIC;
float imageScaleFactor = (float) destImageWidth / (float) imageWidth;
if( ((int) imageScaleFactor) == imageScaleFactor &&
imageScaleFactor > 1f &&
imageWidth <= 16 &&
imageHeight <= 16 )
{
// use "speed" scaling for small icons if the scale factor is an integer
// to avoid blurred icons
scalingInterpolation = RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR;
}

// scale image
BufferedImage bufferedImage = image2bufferedImage( image );
image = scaleImage( bufferedImage, destImageWidth, destImageHeight, scalingInterpolation );
}

// cache image
lastSystemScaleFactor = systemScaleFactor;
lastUserScaleFactor = userScaleFactor;
lastImage = image;

// paint image
paintLastImage( g, x, y );
}

private void paintLastImage( Graphics g, int x, int y ) {
if( lastSystemScaleFactor > 1 ) {
HiDPIUtils.paintAtScale1x( (Graphics2D) g, x, y, 100, 100, // width and height are not used
(g2, x2, y2, width2, height2, scaleFactor2) -> {
g2.drawImage( lastImage, x2, y2, null );
} );
} else
g.drawImage( lastImage, x, y, null );
}

/**
* Scales the given image to the target dimensions.
*
* This is the same what imgscalr library (https://github.com/rkalla/imgscalr)
* would do when invoking Scalr.resize().
*/
private BufferedImage scaleImage( BufferedImage image, int targetWidth, int targetHeight,
Object scalingInterpolation )
{
BufferedImage bufferedImage = new BufferedImage( targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB );
Graphics2D g = bufferedImage.createGraphics();
try {
g.setRenderingHint( RenderingHints.KEY_INTERPOLATION, scalingInterpolation );
g.drawImage( image, 0, 0, targetWidth, targetHeight, null );
} finally {
g.dispose();
}
return bufferedImage;

}

private BufferedImage image2bufferedImage( Image image ) {
if( image instanceof BufferedImage )
return (BufferedImage) image;

BufferedImage bufferedImage = new BufferedImage( image.getWidth( null ),
image.getHeight( null ), BufferedImage.TYPE_INT_ARGB );
Graphics2D g = bufferedImage.createGraphics();
try {
g.drawImage( image, 0, 0, null );
} finally {
g.dispose();
}
return bufferedImage;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.awt.image.BaseMultiResolutionImage;
import java.awt.image.MultiResolutionImage;
import java.util.ArrayList;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.function.Function;
Expand Down Expand Up @@ -51,6 +52,18 @@ public static Image map( Image image, Function<Image, Image> mapper ) {
: mapper.apply( image );
}

public static Image getResolutionVariant( Image image, int destImageWidth, int destImageHeight ) {
return (image instanceof MultiResolutionImage)
? ((MultiResolutionImage)image).getResolutionVariant( destImageWidth, destImageHeight )
: image;
}

public static List<Image> getResolutionVariants( Image image ) {
return (image instanceof MultiResolutionImage)
? ((MultiResolutionImage)image).getResolutionVariants()
: Collections.singletonList( image );
}

//---- class MappedMultiResolutionImage -----------------------------------

private static class MappedMultiResolutionImage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ private void initComponents() {
"[]",
// rows
"[top]" +
"[top]" +
"[grow,top]" +
"[]"));

//---- colorChooserLabel ----
Expand All @@ -73,7 +73,7 @@ private void initComponents() {
//---- fileChooserLabel ----
fileChooserLabel.setText("JFileChooser:");
add(fileChooserLabel, "cell 0 1");
add(fileChooser1, "cell 1 1");
add(fileChooser1, "cell 1 1,growy");

//---- label1 ----
label1.setText("icons:");
Expand Down
Original file line number Diff line number Diff line change
@@ -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 {
contentType: "form/swing"
Expand All @@ -9,7 +9,7 @@ new FormModel {
add( new FormContainer( "com.formdev.flatlaf.testing.FlatTestPanel", new FormLayoutManager( class net.miginfocom.swing.MigLayout ) {
"$layoutConstraints": "ltr,insets dialog,hidemode 3"
"$columnConstraints": "[][]"
"$rowConstraints": "[top][top][]"
"$rowConstraints": "[top][grow,top][]"
} ) {
name: "this"
add( new FormComponent( "javax.swing.JLabel" ) {
Expand All @@ -32,7 +32,7 @@ new FormModel {
add( new FormComponent( "javax.swing.JFileChooser" ) {
name: "fileChooser1"
}, new FormLayoutConstraints( class net.miginfocom.layout.CC ) {
"value": "cell 1 1"
"value": "cell 1 1,growy"
} )
add( new FormComponent( "javax.swing.JLabel" ) {
name: "label1"
Expand Down

0 comments on commit f632c35

Please sign in to comment.