Path: blob/master/src/java.desktop/macosx/classes/com/apple/laf/AquaImageFactory.java
41154 views
/*1* Copyright (c) 2011, 2021, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package com.apple.laf;2627import java.awt.*;28import java.awt.image.BufferedImage;29import java.security.PrivilegedAction;3031import javax.swing.*;32import javax.swing.plaf.*;3334import sun.lwawt.macosx.LWCToolkit;35import apple.laf.JRSUIConstants.AlignmentHorizontal;36import apple.laf.JRSUIConstants.AlignmentVertical;37import apple.laf.JRSUIConstants.Direction;38import apple.laf.JRSUIConstants.State;39import apple.laf.JRSUIConstants.Widget;40import apple.laf.*;4142import com.apple.eio.FileManager;43import com.apple.laf.AquaIcon.InvertableIcon;44import com.apple.laf.AquaIcon.JRSUIControlSpec;45import com.apple.laf.AquaIcon.SystemIcon;46import com.apple.laf.AquaUtils.RecyclableObject;47import com.apple.laf.AquaUtils.RecyclableSingleton;48import sun.awt.image.MultiResolutionCachedImage;49import sun.lwawt.macosx.CImage;5051public class AquaImageFactory {52public static IconUIResource getConfirmImageIcon() {53// public, because UIDefaults.ProxyLazyValue uses reflection to get this value5455return new IconUIResource(new AquaIcon.CachingScalingIcon(kAlertIconSize, kAlertIconSize) {56Image createImage() {57return getGenericJavaIcon();58}59});60}6162public static IconUIResource getCautionImageIcon() {63// public, because UIDefaults.ProxyLazyValue uses reflection to get this value64return getAppIconCompositedOn(AquaIcon.SystemIcon.getCautionIcon());65}6667public static IconUIResource getStopImageIcon() {68// public, because UIDefaults.ProxyLazyValue uses reflection to get this value69return getAppIconCompositedOn(AquaIcon.SystemIcon.getStopIcon());70}7172public static IconUIResource getLockImageIcon() {73// public, because UIDefaults.ProxyLazyValue uses reflection to get this value74if (JRSUIUtils.Images.shouldUseLegacySecurityUIPath()) {75final Image lockIcon = CImage.createImageFromFile("/System/Library/CoreServices/SecurityAgent.app/Contents/Resources/Security.icns", kAlertIconSize, kAlertIconSize);76return getAppIconCompositedOn(lockIcon);77}7879final Image lockIcon = Toolkit.getDefaultToolkit().getImage("NSImage://NSSecurity");80return getAppIconCompositedOn(lockIcon);81}8283@SuppressWarnings("removal")84static Image getGenericJavaIcon() {85return java.security.AccessController.doPrivileged(new PrivilegedAction<Image>() {86public Image run() {87return com.apple.eawt.Application.getApplication().getDockIconImage();88}89});90}9192@SuppressWarnings("removal")93static String getPathToThisApplication() {94return java.security.AccessController.doPrivileged(new PrivilegedAction<String>() {95public String run() {96return FileManager.getPathToApplicationBundle();97}98});99}100101static IconUIResource getAppIconCompositedOn(final SystemIcon systemIcon) {102systemIcon.setSize(kAlertIconSize, kAlertIconSize);103return getAppIconCompositedOn(systemIcon.createImage());104}105106private static final int kAlertIconSize = 64;107static IconUIResource getAppIconCompositedOn(final Image background) {108109if (background instanceof MultiResolutionCachedImage) {110int width = background.getWidth(null);111Image mrIconImage = ((MultiResolutionCachedImage) background).map(112rv -> getAppIconImageCompositedOn(rv, rv.getWidth(null) / width));113return new IconUIResource(new ImageIcon(mrIconImage));114}115116BufferedImage iconImage = getAppIconImageCompositedOn(background, 1);117return new IconUIResource(new ImageIcon(iconImage));118}119120static BufferedImage getAppIconImageCompositedOn(final Image background, int scaleFactor) {121122final int scaledAlertIconSize = kAlertIconSize * scaleFactor;123final int kAlertSubIconSize = (int) (scaledAlertIconSize * 0.5);124final int kAlertSubIconInset = scaledAlertIconSize - kAlertSubIconSize;125final Icon smallAppIconScaled = new AquaIcon.CachingScalingIcon(126kAlertSubIconSize, kAlertSubIconSize) {127Image createImage() {128return getGenericJavaIcon();129}130};131132final BufferedImage image = new BufferedImage(scaledAlertIconSize,133scaledAlertIconSize, BufferedImage.TYPE_INT_ARGB_PRE);134final Graphics g = image.getGraphics();135g.drawImage(background, 0, 0,136scaledAlertIconSize, scaledAlertIconSize, null);137if (g instanceof Graphics2D) {138// improves icon rendering quality in Quartz139((Graphics2D) g).setRenderingHint(RenderingHints.KEY_RENDERING,140RenderingHints.VALUE_RENDER_QUALITY);141}142143smallAppIconScaled.paintIcon(null, g,144kAlertSubIconInset, kAlertSubIconInset);145g.dispose();146147return image;148}149150public static IconUIResource getTreeFolderIcon() {151// public, because UIDefaults.ProxyLazyValue uses reflection to get this value152return AquaIcon.SystemIcon.getFolderIconUIResource();153}154155public static IconUIResource getTreeOpenFolderIcon() {156// public, because UIDefaults.ProxyLazyValue uses reflection to get this value157return AquaIcon.SystemIcon.getOpenFolderIconUIResource();158}159160public static IconUIResource getTreeDocumentIcon() {161// public, because UIDefaults.ProxyLazyValue uses reflection to get this value162return AquaIcon.SystemIcon.getDocumentIconUIResource();163}164165public static UIResource getTreeExpandedIcon() {166// public, because UIDefaults.ProxyLazyValue uses reflection to get this value167return AquaIcon.getIconFor(new JRSUIControlSpec() {168public void initIconPainter(final AquaPainter<? extends JRSUIState> painter) {169painter.state.set(Widget.DISCLOSURE_TRIANGLE);170painter.state.set(State.ACTIVE);171painter.state.set(Direction.DOWN);172painter.state.set(AlignmentHorizontal.CENTER);173painter.state.set(AlignmentVertical.CENTER);174}175}, 20, 20);176}177178public static UIResource getTreeCollapsedIcon() {179// public, because UIDefaults.ProxyLazyValue uses reflection to get this value180return AquaIcon.getIconFor(new JRSUIControlSpec() {181public void initIconPainter(final AquaPainter<? extends JRSUIState> painter) {182painter.state.set(Widget.DISCLOSURE_TRIANGLE);183painter.state.set(State.ACTIVE);184painter.state.set(Direction.RIGHT);185painter.state.set(AlignmentHorizontal.CENTER);186painter.state.set(AlignmentVertical.CENTER);187}188}, 20, 20);189}190191public static UIResource getTreeRightToLeftCollapsedIcon() {192// public, because UIDefaults.ProxyLazyValue uses reflection to get this value193return AquaIcon.getIconFor(new JRSUIControlSpec() {194public void initIconPainter(final AquaPainter<? extends JRSUIState> painter) {195painter.state.set(Widget.DISCLOSURE_TRIANGLE);196painter.state.set(State.ACTIVE);197painter.state.set(Direction.LEFT);198painter.state.set(AlignmentHorizontal.CENTER);199painter.state.set(AlignmentVertical.CENTER);200}201}, 20, 20);202}203204static class NamedImageSingleton extends RecyclableSingleton<Image> {205final String namedImage;206207NamedImageSingleton(final String namedImage) {208this.namedImage = namedImage;209}210211@Override212protected Image getInstance() {213return getNSIcon(namedImage);214}215}216217static class IconUIResourceSingleton extends RecyclableSingleton<IconUIResource> {218final NamedImageSingleton holder;219220public IconUIResourceSingleton(final NamedImageSingleton holder) {221this.holder = holder;222}223224@Override225protected IconUIResource getInstance() {226return new IconUIResource(new ImageIcon(holder.get()));227}228}229230@SuppressWarnings("serial") // Superclass is not serializable across versions231static class InvertableImageIcon extends ImageIcon implements InvertableIcon, UIResource {232Icon invertedImage;233public InvertableImageIcon(final Image image) {234super(image);235}236237@Override238public Icon getInvertedIcon() {239if (invertedImage != null) return invertedImage;240return invertedImage = new IconUIResource(new ImageIcon(AquaUtils.generateLightenedImage(getImage(), 100)));241}242}243244private static final NamedImageSingleton northArrow = new NamedImageSingleton("NSMenuScrollUp");245private static final IconUIResourceSingleton northArrowIcon = new IconUIResourceSingleton(northArrow);246private static final NamedImageSingleton southArrow = new NamedImageSingleton("NSMenuScrollDown");247private static final IconUIResourceSingleton southArrowIcon = new IconUIResourceSingleton(southArrow);248private static final NamedImageSingleton westArrow = new NamedImageSingleton("NSMenuSubmenuLeft");249private static final IconUIResourceSingleton westArrowIcon = new IconUIResourceSingleton(westArrow);250private static final NamedImageSingleton eastArrow = new NamedImageSingleton("NSMenuSubmenu");251private static final IconUIResourceSingleton eastArrowIcon = new IconUIResourceSingleton(eastArrow);252253static Image getArrowImageForDirection(final int direction) {254switch(direction) {255case SwingConstants.NORTH: return northArrow.get();256case SwingConstants.SOUTH: return southArrow.get();257case SwingConstants.EAST: return eastArrow.get();258case SwingConstants.WEST: return westArrow.get();259}260return null;261}262263static Icon getArrowIconForDirection(int direction) {264switch(direction) {265case SwingConstants.NORTH: return northArrowIcon.get();266case SwingConstants.SOUTH: return southArrowIcon.get();267case SwingConstants.EAST: return eastArrowIcon.get();268case SwingConstants.WEST: return westArrowIcon.get();269}270return null;271}272273public static Icon getMenuArrowIcon() {274return new InvertableImageIcon(AquaUtils.generateLightenedImage(eastArrow.get(), 25));275}276277public static Icon getMenuItemCheckIcon() {278return new InvertableImageIcon(AquaUtils.generateLightenedImage(279getNSIcon("NSMenuItemSelection"), 25));280}281282public static Icon getMenuItemDashIcon() {283return new InvertableImageIcon(AquaUtils.generateLightenedImage(284getNSIcon("NSMenuMixedState"), 25));285}286287private static Image getNSIcon(String imageName) {288Image icon = Toolkit.getDefaultToolkit()289.getImage("NSImage://" + imageName);290return icon;291}292293public static class NineSliceMetrics {294public final int wCut, eCut, nCut, sCut;295public final int minW, minH;296public final boolean showMiddle, stretchH, stretchV;297298public NineSliceMetrics(final int minWidth, final int minHeight, final int westCut, final int eastCut, final int northCut, final int southCut) {299this(minWidth, minHeight, westCut, eastCut, northCut, southCut, true);300}301302public NineSliceMetrics(final int minWidth, final int minHeight, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean showMiddle) {303this(minWidth, minHeight, westCut, eastCut, northCut, southCut, showMiddle, true, true);304}305306public NineSliceMetrics(final int minWidth, final int minHeight, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean showMiddle, final boolean stretchHorizontally, final boolean stretchVertically) {307this.wCut = westCut; this.eCut = eastCut; this.nCut = northCut; this.sCut = southCut;308this.minW = minWidth; this.minH = minHeight;309this.showMiddle = showMiddle; this.stretchH = stretchHorizontally; this.stretchV = stretchVertically;310}311}312313/*314* A "paintable" which holds nine images, which represent a sliced up initial315* image that can be streched from its middles.316*/317public static class SlicedImageControl {318final BufferedImage NW, N, NE;319final BufferedImage W, C, E;320final BufferedImage SW, S, SE;321322final NineSliceMetrics metrics;323324final int totalWidth, totalHeight;325final int centerColWidth, centerRowHeight;326327public SlicedImageControl(final Image img, final int westCut, final int eastCut, final int northCut, final int southCut) {328this(img, westCut, eastCut, northCut, southCut, true);329}330331public SlicedImageControl(final Image img, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean useMiddle) {332this(img, westCut, eastCut, northCut, southCut, useMiddle, true, true);333}334335public SlicedImageControl(final Image img, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean useMiddle, final boolean stretchHorizontally, final boolean stretchVertically) {336this(img, new NineSliceMetrics(img.getWidth(null), img.getHeight(null), westCut, eastCut, northCut, southCut, useMiddle, stretchHorizontally, stretchVertically));337}338339public SlicedImageControl(final Image img, final NineSliceMetrics metrics) {340this.metrics = metrics;341342if (img.getWidth(null) != metrics.minW || img.getHeight(null) != metrics.minH) {343throw new IllegalArgumentException("SlicedImageControl: template image and NineSliceMetrics don't agree on minimum dimensions");344}345346totalWidth = metrics.minW;347totalHeight = metrics.minH;348centerColWidth = totalWidth - metrics.wCut - metrics.eCut;349centerRowHeight = totalHeight - metrics.nCut - metrics.sCut;350351NW = createSlice(img, 0, 0, metrics.wCut, metrics.nCut);352N = createSlice(img, metrics.wCut, 0, centerColWidth, metrics.nCut);353NE = createSlice(img, totalWidth - metrics.eCut, 0, metrics.eCut, metrics.nCut);354W = createSlice(img, 0, metrics.nCut, metrics.wCut, centerRowHeight);355C = metrics.showMiddle ? createSlice(img, metrics.wCut, metrics.nCut, centerColWidth, centerRowHeight) : null;356E = createSlice(img, totalWidth - metrics.eCut, metrics.nCut, metrics.eCut, centerRowHeight);357SW = createSlice(img, 0, totalHeight - metrics.sCut, metrics.wCut, metrics.sCut);358S = createSlice(img, metrics.wCut, totalHeight - metrics.sCut, centerColWidth, metrics.sCut);359SE = createSlice(img, totalWidth - metrics.eCut, totalHeight - metrics.sCut, metrics.eCut, metrics.sCut);360}361362static BufferedImage createSlice(final Image img, final int x, final int y, final int w, final int h) {363if (w == 0 || h == 0) return null;364365final BufferedImage slice = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB_PRE);366final Graphics2D g2d = slice.createGraphics();367g2d.drawImage(img, 0, 0, w, h, x, y, x + w, y + h, null);368g2d.dispose();369370return slice;371}372373public void paint(final Graphics g, final int x, final int y, final int w, final int h) {374g.translate(x, y);375376if (w < totalWidth || h < totalHeight) {377paintCompressed(g, w, h);378} else {379paintStretchedMiddles(g, w, h);380}381382g.translate(-x, -y);383}384385void paintStretchedMiddles(final Graphics g, final int w, final int h) {386int baseX = metrics.stretchH ? 0 : ((w / 2) - (totalWidth / 2));387int baseY = metrics.stretchV ? 0 : ((h / 2) - (totalHeight / 2));388int adjustedWidth = metrics.stretchH ? w : totalWidth;389int adjustedHeight = metrics.stretchV ? h : totalHeight;390391if (NW != null) g.drawImage(NW, baseX, baseY, null);392if (N != null) g.drawImage(N, baseX + metrics.wCut, baseY, adjustedWidth - metrics.eCut - metrics.wCut, metrics.nCut, null);393if (NE != null) g.drawImage(NE, baseX + adjustedWidth - metrics.eCut, baseY, null);394if (W != null) g.drawImage(W, baseX, baseY + metrics.nCut, metrics.wCut, adjustedHeight - metrics.nCut - metrics.sCut, null);395if (C != null) g.drawImage(C, baseX + metrics.wCut, baseY + metrics.nCut, adjustedWidth - metrics.eCut - metrics.wCut, adjustedHeight - metrics.nCut - metrics.sCut, null);396if (E != null) g.drawImage(E, baseX + adjustedWidth - metrics.eCut, baseY + metrics.nCut, metrics.eCut, adjustedHeight - metrics.nCut - metrics.sCut, null);397if (SW != null) g.drawImage(SW, baseX, baseY + adjustedHeight - metrics.sCut, null);398if (S != null) g.drawImage(S, baseX + metrics.wCut, baseY + adjustedHeight - metrics.sCut, adjustedWidth - metrics.eCut - metrics.wCut, metrics.sCut, null);399if (SE != null) g.drawImage(SE, baseX + adjustedWidth - metrics.eCut, baseY + adjustedHeight - metrics.sCut, null);400401/*402if (NW != null) {g.setColor(Color.GREEN); g.fillRect(baseX, baseY, NW.getWidth(), NW.getHeight());}403if (N != null) {g.setColor(Color.RED); g.fillRect(baseX + metrics.wCut, baseY, adjustedWidth - metrics.eCut - metrics.wCut, metrics.nCut);}404if (NE != null) {g.setColor(Color.BLUE); g.fillRect(baseX + adjustedWidth - metrics.eCut, baseY, NE.getWidth(), NE.getHeight());}405if (W != null) {g.setColor(Color.PINK); g.fillRect(baseX, baseY + metrics.nCut, metrics.wCut, adjustedHeight - metrics.nCut - metrics.sCut);}406if (C != null) {g.setColor(Color.ORANGE); g.fillRect(baseX + metrics.wCut, baseY + metrics.nCut, adjustedWidth - metrics.eCut - metrics.wCut, adjustedHeight - metrics.nCut - metrics.sCut);}407if (E != null) {g.setColor(Color.CYAN); g.fillRect(baseX + adjustedWidth - metrics.eCut, baseY + metrics.nCut, metrics.eCut, adjustedHeight - metrics.nCut - metrics.sCut);}408if (SW != null) {g.setColor(Color.MAGENTA); g.fillRect(baseX, baseY + adjustedHeight - metrics.sCut, SW.getWidth(), SW.getHeight());}409if (S != null) {g.setColor(Color.DARK_GRAY); g.fillRect(baseX + metrics.wCut, baseY + adjustedHeight - metrics.sCut, adjustedWidth - metrics.eCut - metrics.wCut, metrics.sCut);}410if (SE != null) {g.setColor(Color.YELLOW); g.fillRect(baseX + adjustedWidth - metrics.eCut, baseY + adjustedHeight - metrics.sCut, SE.getWidth(), SE.getHeight());}411*/412}413414void paintCompressed(final Graphics g, final int w, final int h) {415final double heightRatio = h > totalHeight ? 1.0 : (double)h / (double)totalHeight;416final double widthRatio = w > totalWidth ? 1.0 : (double)w / (double)totalWidth;417418final int northHeight = (int)(metrics.nCut * heightRatio);419final int southHeight = (int)(metrics.sCut * heightRatio);420final int centerHeight = h - northHeight - southHeight;421422final int westWidth = (int)(metrics.wCut * widthRatio);423final int eastWidth = (int)(metrics.eCut * widthRatio);424final int centerWidth = w - westWidth - eastWidth;425426if (NW != null) g.drawImage(NW, 0, 0, westWidth, northHeight, null);427if (N != null) g.drawImage(N, westWidth, 0, centerWidth, northHeight, null);428if (NE != null) g.drawImage(NE, w - eastWidth, 0, eastWidth, northHeight, null);429if (W != null) g.drawImage(W, 0, northHeight, westWidth, centerHeight, null);430if (C != null) g.drawImage(C, westWidth, northHeight, centerWidth, centerHeight, null);431if (E != null) g.drawImage(E, w - eastWidth, northHeight, eastWidth, centerHeight, null);432if (SW != null) g.drawImage(SW, 0, h - southHeight, westWidth, southHeight, null);433if (S != null) g.drawImage(S, westWidth, h - southHeight, centerWidth, southHeight, null);434if (SE != null) g.drawImage(SE, w - eastWidth, h - southHeight, eastWidth, southHeight, null);435}436}437438public abstract static class RecyclableSlicedImageControl extends RecyclableObject<SlicedImageControl> {439final NineSliceMetrics metrics;440441public RecyclableSlicedImageControl(final NineSliceMetrics metrics) {442this.metrics = metrics;443}444445@Override446protected SlicedImageControl create() {447return new SlicedImageControl(createTemplateImage(metrics.minW, metrics.minH), metrics);448}449450protected abstract Image createTemplateImage(final int width, final int height);451}452453// when we use SystemColors, we need to proxy the color with something that implements UIResource,454// so that it will be uninstalled when the look and feel is changed.455@SuppressWarnings("serial") // JDK implementation class456private static class SystemColorProxy extends Color implements UIResource {457final Color color;458public SystemColorProxy(final Color color) {459super(color.getRGB());460this.color = color;461}462463public int getRGB() {464return color.getRGB();465}466}467468public static Color getWindowBackgroundColorUIResource() {469//return AquaNativeResources.getWindowBackgroundColorUIResource();470return new SystemColorProxy(SystemColor.window);471}472473public static Color getTextSelectionBackgroundColorUIResource() {474return new SystemColorProxy(SystemColor.textHighlight);475}476477public static Color getTextSelectionForegroundColorUIResource() {478return new SystemColorProxy(SystemColor.textHighlightText);479}480481public static Color getSelectionBackgroundColorUIResource() {482return new SystemColorProxy(SystemColor.controlHighlight);483}484485public static Color getSelectionForegroundColorUIResource() {486return new SystemColorProxy(SystemColor.controlLtHighlight);487}488489public static Color getFocusRingColorUIResource() {490return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.KEYBOARD_FOCUS_COLOR));491}492493public static Color getSelectionInactiveBackgroundColorUIResource() {494return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.INACTIVE_SELECTION_BACKGROUND_COLOR));495}496497public static Color getSelectionInactiveForegroundColorUIResource() {498return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.INACTIVE_SELECTION_FOREGROUND_COLOR));499}500501public static Color getSelectedControlColorUIResource() {502return new SystemColorProxy(LWCToolkit.getAppleColor(LWCToolkit.SELECTED_CONTROL_TEXT_COLOR));503}504}505506507