You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by eb...@apache.org on 2019/11/27 01:23:31 UTC
[netbeans] 02/02: Simplify/improve the HiDPI splash screen support
code.
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 <eb...@ultorg.com>
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: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists