This is an automated email from the ASF dual-hosted git repository. ebakke pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/netbeans.git
commit 23ccd9d64f7e4a30316444b56eeea330c161f916 Author: Eirik Bakke <[email protected]> AuthorDate: Thu Jul 25 04:45:33 2019 -0400 Simplify/improve the HiDPI splash screen support code. Details: * Use the shared caching logic for scaled HiDPI image variants now available by subclassing from org.openide.util.CachedHiDPIIcon. * Properly wait for the image to load before scaling (via ImageIcon's MediaTracker). * Rename ScaledBitmapIcon.width/height to sourceWidth/sourceHeight. * Document some known image scaling issues on Java. --- .../netbeans/core/startup/ScaledBitmapIcon.java | 127 +++++++-------------- 1 file changed, 42 insertions(+), 85 deletions(-) diff --git a/platform/core.startup/src/org/netbeans/core/startup/ScaledBitmapIcon.java b/platform/core.startup/src/org/netbeans/core/startup/ScaledBitmapIcon.java index dec4ee0..2f222f4 100644 --- a/platform/core.startup/src/org/netbeans/core/startup/ScaledBitmapIcon.java +++ b/platform/core.startup/src/org/netbeans/core/startup/ScaledBitmapIcon.java @@ -19,110 +19,67 @@ package org.netbeans.core.startup; import java.awt.Component; -import java.awt.Graphics; import java.awt.Graphics2D; -import java.awt.GraphicsConfiguration; import java.awt.Image; import java.awt.RenderingHints; -import java.awt.Transparency; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.awt.image.ColorModel; import javax.swing.Icon; +import javax.swing.ImageIcon; +import org.openide.util.CachedHiDPIIcon; +import org.openide.util.Parameters; -/* Package-private for now. At some later point it might be useful to expose the DPI-based caching -functionality in this class as a more general utility. */ /** * An Icon implementation that scales a source image to the specified dimensions. Can be used to * produce sharp images on HiDPI displays, without relying on MultiResolutionImage, which exists * only since JDK 9. This also sidesteps https://bugs.openjdk.java.net/browse/JDK-8212226 on - * Windows. For HiDPI displays, the source image's dimensions should be at least double those of the - * icon's logical dimensions. A double-resolution source image will automatically be scaled down - * to 1x, 1.5x, or other HiDPI scaling factors as needed. + * Windows. It's recommended to use a source image with dimensions that are exactly twice those of + * the icon's logical dimensions. A double-resolution source image will automatically be scaled + * down to 1x, 1.5x, or other HiDPI scaling factors as needed. */ -final class ScaledBitmapIcon implements Icon { - private final Map<Double,Image> cache = new ConcurrentHashMap<>(); +final class ScaledBitmapIcon extends CachedHiDPIIcon { private final Image sourceImage; - private final int width; - private final int height; + private final int sourceWidth; + private final int sourceHeight; public ScaledBitmapIcon(Image sourceImage, int width, int height) { - if (sourceImage == null) - throw new NullPointerException(); - if (width <= 0) - throw new IllegalArgumentException(); - if (height <= 0) - throw new IllegalArgumentException(); + super(width, height); + Parameters.notNull("sourceImage", sourceImage); this.sourceImage = sourceImage; - this.width = width; - this.height = height; - } - - private Image getScaledImage(GraphicsConfiguration gc, double dpiScaling) { - Image ret = cache.get(dpiScaling); - if (ret != null) { - return ret; - } - final BufferedImage img = gc.createCompatibleImage( - (int) Math.ceil(getIconWidth() * dpiScaling), - (int) Math.ceil(getIconHeight() * dpiScaling), Transparency.TRANSLUCENT); - final double sourceWidth = sourceImage.getWidth(null); - final double sourceHeight = sourceImage.getHeight(null); - if (sourceWidth >= 1 && sourceHeight >= 1) { - final Graphics2D imgG = (Graphics2D) img.getGraphics(); - try { - imgG.setTransform(AffineTransform.getScaleInstance( - dpiScaling * getIconWidth() / sourceWidth, - dpiScaling * getIconHeight() / sourceHeight)); - imgG.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - imgG.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); - imgG.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); - imgG.drawImage(sourceImage, 0, 0, null); - } finally { - imgG.dispose(); - } - if (dpiScaling <= 3.0) - cache.put(dpiScaling, img); - } - return img; + /* Like ImageIcon, we block until the image is fully loaded. Just rely on ImageIcon's + implementation here (it will ll get a MediaTracker, call waitForID etc.). */ + Icon imageIcon = new ImageIcon(sourceImage); + sourceWidth = imageIcon.getIconWidth(); + sourceHeight = imageIcon.getIconHeight(); } @Override - public void paintIcon(Component c, Graphics g0, int x, int y) { - final Graphics2D g = (Graphics2D) g0; - final AffineTransform oldTransform = g.getTransform(); - g.translate(x, y); - final AffineTransform tx = g.getTransform(); - - final double dpiScaling; - final int txType = tx.getType(); - if (txType == AffineTransform.TYPE_UNIFORM_SCALE || - txType == (AffineTransform.TYPE_UNIFORM_SCALE | AffineTransform.TYPE_TRANSLATION)) - { - dpiScaling = tx.getScaleX(); - } else { - dpiScaling = 1.0; - } - // Scale the image down to its logical dimensions, then draw it at the device pixel boundary. - Image scaledImage = getScaledImage(g.getDeviceConfiguration(), dpiScaling); - if (dpiScaling != 1.0) { - AffineTransform tx2 = g.getTransform(); - g.setTransform(new AffineTransform(1, 0, 0, 1, - (int) tx2.getTranslateX(), - (int) tx2.getTranslateY())); + protected Image createAndPaintImage( + Component c, ColorModel colorModel, int deviceWidth, int deviceHeight, double scale) + { + final BufferedImage img = createBufferedImage(colorModel, deviceWidth, deviceHeight); + if (sourceWidth > 0 && sourceHeight > 0) { + final Graphics2D g = img.createGraphics(); + try { + /* Despite these hints, downscaling by more than 2x tends to give low-quality + results compared to image resizing in, say, Photoshop or IrfanView. This is a known + quality/performance trade-off in Java2D; see + https://stackoverflow.com/questions/24745147/java-resize-image-without-losing-quality . + Our Javadoc recommendation to use a scaling of exactly 2x should avoid these + problems. */ + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); + g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); + g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); + g.setTransform(AffineTransform.getScaleInstance( + scale * getIconWidth() / (double) sourceWidth, + scale * getIconHeight() / (double) sourceHeight)); + g.drawImage(sourceImage, 0, 0, null); + } finally { + g.dispose(); + } } - g.drawImage(scaledImage, 0, 0, null); - g.setTransform(oldTransform); - } - - @Override - public int getIconWidth() { - return width; - } - - @Override - public int getIconHeight() { - return height; + return img; } } --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected] For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists
