Можно ли дать приложениям AWT четкие значки панели задач в Windows 10 - PullRequest
13 голосов
/ 15 марта 2019

Я пытаюсь установить значок приложения Java AWT, чтобы оно отображалось в собственном разрешении на панели задач Windows 10 (в том числе, когда масштабирование рабочего стола установлено выше 100%). Похоже, что по умолчанию, если в исполняемый файл встраивается значок, содержащий несколько размеров, Windows, похоже, выбирает размер, превышающий фактический размер значков панели задач, и уменьшает его (при масштабе 100% размер 32-пиксельного значка изменяется до 24, даже если указан 24-пиксельный значок , и аналогично для других масштабов.)

Я решил эту проблему для приложений C ++ MFC, загрузив в качестве ресурса значок правильного размера и отправив в окно сообщение WM_SETICON, что приводит к появлению симпатичного острого значка на панели задач и в диалоговом окне alt-tab.

smallIcon = (HICON)LoadImage( myInstance, MAKEINTRESOURCE(smallIconRes), IMAGE_ICON, smallIconSize, smallIconSize, LR_DEFAULTCOLOR );
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)smallIcon);

bigIcon   = (HICON)LoadImage( myInstance, MAKEINTRESOURCE(bigIconRes),   IMAGE_ICON, bigIconSize,   bigIconSize,   LR_DEFAULTCOLOR );
SendMessage(hWnd, WM_SETICON, ICON_BIG,   (LPARAM)bigIcon); 

Этот подход, похоже, не работает для приложений Java - сообщение WM_SETICON с wParam, установленным в ICON_SMALL, работает нормально, но эквивалент ICON_BIG игнорируется.

Если я пытаюсь использовать Java API для установки иконки, сделав это

    List<Image> icons = new ArrayList<Image>();
    icons.add(windowIcons.getIcon(20)); // small icons are 20x20 pixels
    icons.add(windowIcons.getIcon(30)); // large are 30x30 at 125% scale
    setIconImages(icons);

используется правильный значок, но он выглядит размытым, как будто что-то изменило его размер до «ожидаемого» размера, а затем изменило его размер обратно. Слева вот как это выглядит, справа - содержимое файла значка.

Java application's icon vs. how it should look

Итак, мой вопрос: что я могу сделать в этом приложении Java, чтобы заставить Windows отображать значок, который я даю, на панели задач, не масштабируя и не размывая детали?

Ответы [ 2 ]

2 голосов
/ 23 марта 2019

Существует действительно функция масштабирования, называемая getScaledIconImage() в sun.awt.SunToolkit , которая всегда используется при установке значков. Вы должны обойти эту функцию, чтобы получить значок без печати. Так что вам нужна замена для java.awt.Window.setIconImages() метода.

Предоставлено несколько изображений значков Icon16x16.png, Icon24x24.png и т. Д. Это пример customSetIconImages(), который помещает четкий значок 24x24 пикселей на панель задач Windows 10.

import java.awt.*;
import java.awt.event.*;
import java.awt.image.*;
import javax.swing.ImageIcon;
import java.awt.peer.WindowPeer;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;

@SuppressWarnings("serial")
public class MyFrame extends Frame implements WindowListener {

    final Image i16, i24, i32, i48;

    MyFrame() throws Exception {

        i16 = Toolkit.getDefaultToolkit().getImage("Icon16x16.png");
        i24 = Toolkit.getDefaultToolkit().getImage("Icon24x24.png");
        i32 = Toolkit.getDefaultToolkit().getImage("Icon32x32.png");
        i48 = Toolkit.getDefaultToolkit().getImage("Icon48x48.png");

        addWindowListener(this);
        setSize(500,300);
        setTitle("Unaliased icon example");
        setLayout(new FlowLayout());
        setVisible(true);
    }

    public synchronized void customSetIconImages(java.util.List<Image> icons) throws Exception {
        Field windowIcons = Class.forName("java.awt.Window").getDeclaredField("icons");
        windowIcons.setAccessible(true);
        windowIcons.set(this, new ArrayList<Image>(icons));

        if (getPeer() != null)
            updateIconImages(i24, 24, 24, i24, 24, 24);

        firePropertyChange("iconImage", null, null);
    }

    public void updateIconImages(Image big, int bw, int bh, Image small, int sw, int sh) throws Exception {
        DataBufferInt iconData = getUnscaledIconData(big, bw, bh);
        DataBufferInt iconSmData = getUnscaledIconData(small, sw, sh);

        WindowPeer peer = (WindowPeer) getPeer();
        Method setIconImagesData = Class.forName("sun.awt.windows.WWindowPeer").getDeclaredMethod("setIconImagesData", int[].class, int.class, int.class, int[].class, int.class, int.class);
        setIconImagesData.setAccessible(true);
        setIconImagesData.invoke(peer, iconData.getData(), bw, bh, iconSmData.getData(), sw, sh);
    }

    public static DataBufferInt getUnscaledIconData(Image image, int w, int h) {
        Image temporary = new ImageIcon(image).getImage();
        BufferedImage buffImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = buffImage.createGraphics();
        g2d.drawImage(temporary, 0, 0, null);
        g2d.dispose();
        Raster raster = buffImage.getRaster();
        DataBuffer buffer = raster.getDataBuffer();
        return (DataBufferInt) buffer;
    }

    @Override
    public void windowOpened(WindowEvent arg0) {
        try {
            customSetIconImages(Arrays.asList(i24));
        } catch (Exception e) {
            System.err.println(e.getClass().getName()+" "+e.getMessage());
        }
    }

    @Override
    public void windowActivated(WindowEvent arg0) {
    }

    @Override
    public void windowClosed(WindowEvent arg0) {
    }

    @Override
    public void windowClosing(WindowEvent arg0) {
        dispose();
    }

    @Override
    public void windowDeactivated(WindowEvent arg0) {
    }

    @Override
    public void windowDeiconified(WindowEvent arg0) {
    }

    @Override
    public void windowIconified(WindowEvent arg0) {
    }

    public static void main(String args[]) throws Exception {
        MyFrame fr = new MyFrame();
    }
}

Как сказал @ df778899, внутри sun.awt.windows.WWindowPeer есть четыре частных собственных метода, которые вы можете вызвать, чтобы определить размер системных значков. Вы можете комбинировать информацию, возвращаемую этими методами, с вашей собственной версией getScaledIconImage(), которая выполняет сглаживание или нет по вашему желанию.

Наконец, обратите внимание, что это очень грязный хак только для того, чтобы получить непроверенную иконку. Я тестировал только в Java 8 и Windows 10. И есть большие шансы, что он не будет работать в более новых версиях Java.

2 голосов
/ 22 марта 2019

Это не тот ответ, на который вы надеетесь, но это похоже на проблему на уровне JDK.

Значки окна обрабатываются классом sun.awt.windows.WWindowPeer, что, в свою очередь, делаетнемного вызовов нативных методов, но этого достаточно, чтобы увидеть в источнике, чтобы указать на проблему.Пожалуйста, прочитайте важный бит здесь .

По сути, независимо от того, сколько размеров изображений предусмотрено, он выберет только два размера - для WWindowPeer.getSysIconWidth() и getSysSmIconWidth() - дляперейти к собственному методу setIconImagesData().

Методы getSysIconWidth() и getSysSmIconWidth() также являются нативными, но можно напрямую проверить их возвращаемые значения:

JFrame frame = new JFrame();
runOnPeer(frame, "getSysIconWidth");
runOnPeer(frame, "getSysIconHeight");
runOnPeer(frame, "getSysSmIconWidth");
runOnPeer(frame, "getSysSmIconHeight");

private void runOnPeer(JFrame frame, String methodName) {

    //JDK8 style
    //ComponentPeer peer = frame.getPeer();

    //JDK11 style
    Field peerField = Component.class.getDeclaredField("peer");
    peerField.setAccessible(true);
    Object peer = peerField.get(frame);

    Method method = Class.forName("sun.awt.windows.WWindowPeer")
            .getDeclaredMethod(methodName);
    method.setAccessible(true);
    System.out.println(methodName + "()=" + method.invoke(peer));
}

..... который возвращает это в Windows 10 ...

getSysIconWidth()=32
getSysIconHeight()=32
getSysSmIconWidth()=16
getSysSmIconHeight()=16

Как вы говорите, очевидно, что один из этих размеров изображения затем масштабируется для панели задач.

...