8151385: [hidpi] JOptionPane-Icons only partially visible when using Windows 10 L&F

Reviewed-by: serb, alexsch
This commit is contained in:
Hendrik Schreiber 2016-06-14 11:33:56 +03:00
parent 0c483d8e20
commit 6d82cbce2a
4 changed files with 157 additions and 19 deletions

View File

@ -27,7 +27,9 @@ package sun.awt.shell;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.AbstractMultiResolutionImage;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
@ -982,11 +984,12 @@ final class Win32ShellFolder2 extends ShellFolder {
// Return the bits from an HICON. This has a side effect of setting
// the imageHash variable for efficient caching / comparing.
private static native int[] getIconBits(long hIcon, int iconSize);
private static native int[] getIconBits(long hIcon);
// Dispose the HICON
private static native void disposeIcon(long hIcon);
static native int[] getStandardViewButton0(int iconIndex);
// Get buttons from native toolbar implementation.
static native int[] getStandardViewButton0(int iconIndex, boolean small);
// Should be called from the COM thread
private long getIShellIcon() {
@ -1000,12 +1003,17 @@ final class Win32ShellFolder2 extends ShellFolder {
private static Image makeIcon(long hIcon, boolean getLargeIcon) {
if (hIcon != 0L && hIcon != -1L) {
// Get the bits. This has the side effect of setting the imageHash value for this object.
int size = getLargeIcon ? 32 : 16;
int[] iconBits = getIconBits(hIcon, size);
final int[] iconBits = getIconBits(hIcon);
if (iconBits != null) {
BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
// icons are always square
final int size = (int) Math.sqrt(iconBits.length);
final int baseSize = getLargeIcon ? 32 : 16;
final BufferedImage img =
new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
img.setRGB(0, 0, size, size, iconBits, 0, size);
return img;
return size == baseSize
? img
: new MultiResolutionIconImage(baseSize, img);
}
}
return null;
@ -1298,4 +1306,39 @@ final class Win32ShellFolder2 extends ShellFolder {
});
}
static class MultiResolutionIconImage extends AbstractMultiResolutionImage {
final int baseSize;
final Image resolutionVariant;
public MultiResolutionIconImage(int baseSize, Image resolutionVariant) {
this.baseSize = baseSize;
this.resolutionVariant = resolutionVariant;
}
@Override
public int getWidth(ImageObserver observer) {
return baseSize;
}
@Override
public int getHeight(ImageObserver observer) {
return baseSize;
}
@Override
protected Image getBaseImage() {
return resolutionVariant;
}
@Override
public Image getResolutionVariant(double width, double height) {
return resolutionVariant;
}
@Override
public List<Image> getResolutionVariants() {
return Arrays.asList(resolutionVariant);
}
}
}

View File

@ -27,6 +27,7 @@ package sun.awt.shell;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.BaseMultiResolutionImage;
import java.io.File;
import java.io.FileNotFoundException;
@ -116,13 +117,21 @@ final class Win32ShellFolderManager2 extends ShellFolderManager {
return result;
}
BufferedImage img = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
final int[] iconBits = Win32ShellFolder2
.getStandardViewButton0(iconIndex, true);
if (iconBits != null) {
// icons are always square
final int size = (int) Math.sqrt(iconBits.length);
final BufferedImage img =
new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
img.setRGB(0, 0, size, size, iconBits, 0, size);
img.setRGB(0, 0, 16, 16, Win32ShellFolder2.getStandardViewButton0(iconIndex), 0, 16);
STANDARD_VIEW_BUTTONS[iconIndex] = (size == 16)
? img
: new MultiResolutionIconImage(16, img);
}
STANDARD_VIEW_BUTTONS[iconIndex] = img;
return img;
return STANDARD_VIEW_BUTTONS[iconIndex];
}
// Special folders

View File

@ -930,19 +930,43 @@ JNIEXPORT void JNICALL Java_sun_awt_shell_Win32ShellFolder2_disposeIcon
/*
* Class: sun_awt_shell_Win32ShellFolder2
* Method: getIconBits
* Signature: (JI)[I
* Signature: (J)[I
*/
JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconBits
(JNIEnv* env, jclass cls, jlong hicon, jint iconSize)
(JNIEnv* env, jclass cls, jlong hicon)
{
const int MAX_ICON_SIZE = 128;
int iconSize = 0;
jintArray iconBits = NULL;
BITMAP bmp;
memset(&bmp, 0, sizeof(BITMAP));
// Get the icon info
ICONINFO iconInfo;
if (fn_GetIconInfo((HICON)hicon, &iconInfo)) {
// Get the screen DC
HDC dc = GetDC(NULL);
if (dc != NULL) {
// find out the icon size in order to deal with different sizes
// delivered depending on HiDPI mode or SD DPI mode.
if (iconInfo.hbmColor) {
const int nWrittenBytes = GetObject(iconInfo.hbmColor, sizeof(bmp), &bmp);
if(nWrittenBytes > 0) {
iconSize = bmp.bmWidth;
}
} else if (iconInfo.hbmMask) {
// Icon has no color plane, image data stored in mask
const int nWrittenBytes = GetObject(iconInfo.hbmMask, sizeof(bmp), &bmp);
if (nWrittenBytes > 0) {
iconSize = bmp.bmWidth;
}
}
// limit iconSize to MAX_ICON_SIZE, so that the colorBits and maskBits
// arrays are big enough.
// (logic: rather show bad icons than overrun the array size)
iconSize = iconSize > MAX_ICON_SIZE ? MAX_ICON_SIZE : iconSize;
// Set up BITMAPINFO
BITMAPINFO bmi;
memset(&bmi, 0, sizeof(BITMAPINFO));
@ -954,7 +978,7 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconBits
bmi.bmiHeader.biCompression = BI_RGB;
// Extract the color bitmap
int nBits = iconSize * iconSize;
long colorBits[1024];
long colorBits[MAX_ICON_SIZE * MAX_ICON_SIZE];
GetDIBits(dc, iconInfo.hbmColor, 0, iconSize, colorBits, &bmi, DIB_RGB_COLORS);
// XP supports alpha in some icons, and depending on device.
// This should take precedence over the icon mask bits.
@ -969,7 +993,7 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconBits
}
if (!hasAlpha) {
// Extract the mask bitmap
long maskBits[1024];
long maskBits[MAX_ICON_SIZE * MAX_ICON_SIZE];
GetDIBits(dc, iconInfo.hbmMask, 0, iconSize, maskBits, &bmi, DIB_RGB_COLORS);
// Copy the mask alphas into the color bits
for (int i = 0; i < nBits; i++) {
@ -1001,10 +1025,10 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getIconBits
/*
* Class: sun_awt_shell_Win32ShellFolder2
* Method: getStandardViewButton0
* Signature: (I)[I
* Signature: (IZ)[I
*/
JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getStandardViewButton0
(JNIEnv* env, jclass cls, jint iconIndex)
(JNIEnv* env, jclass cls, jint iconIndex, jboolean smallIcon)
{
jintArray result = NULL;
@ -1014,7 +1038,8 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getStandardView
NULL, NULL, NULL, NULL);
if (hWndToolbar != NULL) {
SendMessage(hWndToolbar, TB_LOADIMAGES, (WPARAM)IDB_VIEW_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);
WPARAM size = smallIcon ? (WPARAM)IDB_VIEW_SMALL_COLOR : (WPARAM)IDB_VIEW_LARGE_COLOR;
SendMessage(hWndToolbar, TB_LOADIMAGES, size, (LPARAM)HINST_COMMCTRL);
HIMAGELIST hImageList = (HIMAGELIST) SendMessage(hWndToolbar, TB_GETIMAGELIST, 0, 0);
@ -1022,7 +1047,7 @@ JNIEXPORT jintArray JNICALL Java_sun_awt_shell_Win32ShellFolder2_getStandardView
HICON hIcon = ImageList_GetIcon(hImageList, iconIndex, ILD_TRANSPARENT);
if (hIcon != NULL) {
result = Java_sun_awt_shell_Win32ShellFolder2_getIconBits(env, cls, ptr_to_jlong(hIcon), 16);
result = Java_sun_awt_shell_Win32ShellFolder2_getIconBits(env, cls, ptr_to_jlong(hIcon));
DestroyIcon(hIcon);
}

View File

@ -0,0 +1,61 @@
/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test
* @bug 8151385
* @summary JOptionPane icons are cropped on Windows 10 with HiDPI display
* @author Hendrik Schreiber
* @requires os.family == "windows"
* @modules java.desktop/sun.awt.shell
* @run main BadHiDPIIcon
*/
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.awt.image.MultiResolutionImage;
import sun.awt.shell.ShellFolder;
public class BadHiDPIIcon {
public static void main(String[] args) {
// the error icon is round and in all four corner transparent
// we check that all corners are identical
Image icon = (Image) ShellFolder.get("optionPaneIcon Error");
final BufferedImage image = getBufferedImage(icon);
final int upperLeft = image.getRGB(0, 0);
final int upperRight = image.getRGB(image.getWidth() - 1, 0);
final int lowerLeft = image.getRGB(0, image.getHeight() - 1);
final int lowerRight = image.getRGB(image.getWidth() - 1, image.getHeight() - 1);
if (upperLeft != upperRight || upperLeft != lowerLeft || upperLeft != lowerRight) {
throw new RuntimeException("optionPaneIcon Error is not a round icon with transparent background.");
}
}
private static BufferedImage getBufferedImage(Image image) {
if (image instanceof MultiResolutionImage) {
MultiResolutionImage mrImage = (MultiResolutionImage) image;
return (BufferedImage) mrImage.getResolutionVariant(32, 32);
}
return (BufferedImage) image;
}
}