如何将图标转换为图像

Posted

技术标签:

【中文标题】如何将图标转换为图像【英文标题】:How can I convert an Icon to an Image 【发布时间】:2011-08-15 09:22:06 【问题描述】:

我正在尝试使用此代码将图标 (javax.swing.Icon) 转换为图像 (java.awt.Image):

private Image iconToImage(Icon icon)

    if(icon instanceof ImageIcon)
    
        return ((ImageIcon) icon).getImage();
    
    else
    
        BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_RGB);
        icon.paintIcon(null, image.getGraphics(), 0, 0);
        return image;
    

问题是,paintIcon 函数会在 image.getGraphics() 上抛出一个 NullPointerException

为了记录,icon 值是默认的CheckBox 图标(通过UIManager.getIcon("CheckBox.icon") 获得)

这里是抛出异常的详细信息:

Exception in thread "AWT-EventQueue-0" java.lang.NullPointerException
    at com.sun.java.swing.plaf.windows.WindowsIconFactory$CheckBoxIcon.paintIcon(WindowsIconFactory.java:306)
    at utils.WarningRenderer.iconToImage(WarningRenderer.java:50)
    at utils.WarningRenderer.<init>(WarningRenderer.java:38)
    at deliveryexpress.DeliveryExpressView.setWarnings(DeliveryExpressView.java:278)
    at deliveryexpress.DeliveryExpressView.updateLists(DeliveryExpressView.java:218)
    at deliveryexpress.DeliveryExpressView.access$1100(DeliveryExpressView.java:47)
    at deliveryexpress.DeliveryExpressView$5.addCheck(DeliveryExpressView.java:183)
    at org.japura.gui.model.DefaultListCheckModel.fireCheckListModelListeners(Unknown Source)
    at org.japura.gui.model.DefaultListCheckModel.fireAddCheckListModelListeners(Unknown Source)
    at org.japura.gui.model.DefaultListCheckModel.addCheck(Unknown Source)
    at org.japura.gui.CheckList$1.mouseClicked(Unknown Source)
    at java.awt.AWTEventMulticaster.mouseClicked(AWTEventMulticaster.java:253)
    at java.awt.Component.processMouseEvent(Component.java:6292)
    at javax.swing.JComponent.processMouseEvent(JComponent.java:3267)
    at java.awt.Component.processEvent(Component.java:6054)
    at java.awt.Container.processEvent(Container.java:2041)
    at java.awt.Component.dispatchEventImpl(Component.java:4652)
    at java.awt.Container.dispatchEventImpl(Container.java:2099)
    at java.awt.Component.dispatchEvent(Component.java:4482)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4577)
    at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4247)
    at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4168)
    at java.awt.Container.dispatchEventImpl(Container.java:2085)
    at java.awt.Window.dispatchEventImpl(Window.java:2478)
    at java.awt.Component.dispatchEvent(Component.java:4482)
    at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:644)
    at java.awt.EventQueue.access$000(EventQueue.java:85)
    at java.awt.EventQueue$1.run(EventQueue.java:603)
    at java.awt.EventQueue$1.run(EventQueue.java:601)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:98)
    at java.awt.EventQueue$2.run(EventQueue.java:617)
    at java.awt.EventQueue$2.run(EventQueue.java:615)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:614)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

如果您需要更多详细信息,请告诉我,我会编辑我的帖子以添加它们。

谢谢!

【问题讨论】:

完美的问题,给出了所有必要的细节:-) 【参考方案1】:

刚刚找到了一个代码 sn-p,如果您想更频繁地包装那些行为不端的 LAF 提供的图标,它可能会有所帮助:

/**
 * Some ui-icons misbehave in that they unconditionally class-cast to the 
 * component type they are mostly painted on. Consequently they blow up if 
 * we are trying to paint them anywhere else (f.i. in a renderer).  
 * 
 * This Icon is an adaption of a cool trick by Darryl Burke/Rob Camick found at
 * http://tips4java.wordpress.com/2008/12/18/icon-table-cell-renderer/#comment-120
 * 
 * The base idea is to instantiate a component of the type expected by the icon, 
 * let it paint into the graphics of a bufferedImage and create an ImageIcon from it.
 * In subsequent calls the ImageIcon is used. 
 * 
 */
public static class SafeIcon implements Icon 

    private Icon wrappee;
    private Icon standIn;

    public SafeIcon(Icon wrappee) 
        this.wrappee = wrappee;
    

    @Override
    public int getIconHeight() 
        return wrappee.getIconHeight();
    

    @Override
    public int getIconWidth() 
        return wrappee.getIconWidth();
    

    @Override
    public void paintIcon(Component c, Graphics g, int x, int y) 
        if (standIn == this) 
            paintFallback(c, g, x, y);
         else if (standIn != null) 
            standIn.paintIcon(c, g, x, y);
         else 
            try 
               wrappee.paintIcon(c, g, x, y); 
             catch (ClassCastException e) 
                createStandIn(e, x, y);
                standIn.paintIcon(c, g, x, y);
            
        
    

    /**
     * @param e
     */
    private void createStandIn(ClassCastException e, int x, int y) 
        try 
            Class<?> clazz = getClass(e);
            JComponent standInComponent = getSubstitute(clazz);
            standIn = createImageIcon(standInComponent, x, y);
         catch (Exception e1) 
            // something went wrong - fallback to this painting
            standIn = this;
         
    

    private Icon createImageIcon(JComponent standInComponent, int x, int y) 
        BufferedImage image = new BufferedImage(getIconWidth(),
                getIconHeight(), BufferedImage.TYPE_INT_ARGB);
          Graphics g = image.createGraphics();
          try 
              wrappee.paintIcon(standInComponent, g, 0, 0);
              return new ImageIcon(image);
           finally 
              g.dispose();
          
    

    /**
     * @param clazz
     * @throws IllegalAccessException 
     */
    private JComponent getSubstitute(Class<?> clazz) throws IllegalAccessException 
        JComponent standInComponent;
        try 
            standInComponent = (JComponent) clazz.newInstance();
         catch (InstantiationException e) 
            standInComponent = new AbstractButton() 

            ;
            ((AbstractButton) standInComponent).setModel(new DefaultButtonModel());
         
        return standInComponent;
    

    private Class<?> getClass(ClassCastException e) throws ClassNotFoundException 
        String className = e.getMessage();
        className = className.substring(className.lastIndexOf(" ") + 1);
        return Class.forName(className);

    

    private void paintFallback(Component c, Graphics g, int x, int y) 
        g.drawRect(x, y, getIconWidth(), getIconHeight());
        g.drawLine(x, y, x + getIconWidth(), y + getIconHeight());
        g.drawLine(x + getIconWidth(), y, x, y + getIconHeight());
    


要在您的 sn-p 中使用,只需传入任意组件:

    icon = new SafeIcon(icon);
    BufferedImage image = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_RGB);
    icon.paintIcon(new JPanel(), image.getGraphics(), 0, 0);

【讨论】:

谢谢,成功了!它不包括 alpha,但我会尝试自己解决这个问题:p @oliholz 可能遗漏了一些东西,但是使用支持 alpha 的类型(如 TYPE_INT_ARGB)创建 BufferedImage 有什么问题?【参考方案2】:

试试这个:

static Image iconToImage(Icon icon) 
   if (icon instanceof ImageIcon) 
      return ((ImageIcon)icon).getImage();
    
   else 
      int w = icon.getIconWidth();
      int h = icon.getIconHeight();
      GraphicsEnvironment ge = 
        GraphicsEnvironment.getLocalGraphicsEnvironment();
      GraphicsDevice gd = ge.getDefaultScreenDevice();
      GraphicsConfiguration gc = gd.getDefaultConfiguration();
      BufferedImage image = gc.createCompatibleImage(w, h);
      Graphics2D g = image.createGraphics();
      icon.paintIcon(null, g, 0, 0);
      g.dispose();
      return image;
   
 

一个完整的示例,我们将 laf 提供的图标转换为图像并将其用于 Windows 系统托盘。

import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import javax.swing.*;

public class SysTrayDemo 
    protected static TrayIcon trayIcon;
    private static PopupMenu createTrayMenu() 
        ActionListener exitListener = new ActionListener() 
            public void actionPerformed(ActionEvent e) 
                System.out.println("Bye from the tray");
                System.exit(0);
            
        ;

        ActionListener executeListener = new ActionListener() 
            public void actionPerformed(ActionEvent e) 
                JOptionPane.showMessageDialog
                   (null, "Popup from the action on the systray!",
                    "User action", JOptionPane.INFORMATION_MESSAGE);
                trayIcon.displayMessage
                   ("Done", "You can do it again if you want!", 
                    TrayIcon.MessageType.INFO);
            
        ;

        PopupMenu menu = new PopupMenu();
        MenuItem execItem = new MenuItem("Action...");
        execItem.addActionListener(executeListener);
        menu.add(execItem);

        MenuItem exitItem = new MenuItem("Exit");
        exitItem.addActionListener(exitListener);
        menu.add(exitItem);
        return menu;
    

    /**
     * using a built-in icon
     * we need to convert the icon to an Image
     */
    private static TrayIcon createTrayIconFromBuiltInIcon() 
        Icon icon = UIManager.getIcon("OptionPane.warningIcon");
        PopupMenu popup = createTrayMenu();
        Image image = iconToImage(icon);
        TrayIcon ti = new TrayIcon(image, "Java System Tray Demo", popup);
        ti.setImageAutoSize(true);
        return ti;
    

    static Image iconToImage(Icon icon) 
          if (icon instanceof ImageIcon) 
              return ((ImageIcon)icon).getImage();
           else 
              int w = icon.getIconWidth();
              int h = icon.getIconHeight();
              GraphicsEnvironment ge = 
                GraphicsEnvironment.getLocalGraphicsEnvironment();
              GraphicsDevice gd = ge.getDefaultScreenDevice();
              GraphicsConfiguration gc = gd.getDefaultConfiguration();
              BufferedImage image = gc.createCompatibleImage(w, h);
              Graphics2D g = image.createGraphics();
              icon.paintIcon(null, g, 0, 0);
              g.dispose();
              return image;
          
      

    public static void main(String[] args) throws Exception 
        if (!SystemTray.isSupported()) 
            System.out.println
               ("System tray not supported on this platform");
            System.exit(1);
        

        try 
            SystemTray sysTray = SystemTray.getSystemTray();
            trayIcon = createTrayIconFromBuiltInIcon();
            sysTray.add(trayIcon);
            trayIcon.displayMessage("Ready",
                "Tray icon started and tready", TrayIcon.MessageType.INFO);
        
        catch (AWTException e) 
            System.out.println("Unable to add icon to the system tray");
            System.exit(1);
        
    

【讨论】:

一个小时前已经评论过,再做一次:您认为这将如何帮助解决异常的原因,即 laf 提供的图标无法处理空组件? ...这个错误的答案怎么会卷土重来? @kleopatra,可能是因为它工作正常,如我的测试用例所示。 所以你很幸运使用了 OptionPane.warningIcon ;-) 尝试使用 checkbox.icon (metal-/windowsLAF),这是要解决的问题... 很高兴知道,@kleopatra。其他一些人也出现了同样的问题(CheckBoxMenuItem),但大多数都可以。我给你+1 ;-) @RealHowTo 您测试了多少 LAF?试试 Windows 经典版。【参考方案3】:

试试这个:

icon.paintIcon(new JCheckBox(), image.getGraphics(), 0, 0);

我无法准确解释为什么它需要JCheckBox。也许它因图标而异? NullPointerException 来自MetalIconFactory 中的这一行,用于"CheckBox.icon"

ButtonModel model = ((JCheckBox)c).getModel();

【讨论】:

仅供参考:与状态相关的图标绘制在 windowsCheckBoxIcon 中类似。这些图标实现非常糟糕,因为它们假定给定组件是 JCheckBox 类型。按照约定,paintIcon必须处理任意组件类型,包括根本没有

以上是关于如何将图标转换为图像的主要内容,如果未能解决你的问题,请参考以下文章

如何从PNG图像制作字体图标?

使用 <path> 标签将图标图像转换为 svg 的最佳方法

转换为png时如何更改图像中的单一颜色?

图像到图标的转换

如何将背景转换为透明? [关闭]

如何在 Mac 上将图像转换为文本?