Path: blob/master/src/java.desktop/macosx/classes/com/apple/laf/AquaPainter.java
41154 views
/*1* Copyright (c) 2011, 2014, 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.*;29import java.util.HashMap;3031import com.apple.laf.AquaImageFactory.RecyclableSlicedImageControl;32import com.apple.laf.AquaImageFactory.NineSliceMetrics;33import com.apple.laf.AquaImageFactory.SlicedImageControl;3435import sun.awt.image.*;36import sun.java2d.*;37import sun.print.*;38import apple.laf.*;39import apple.laf.JRSUIUtils.NineSliceMetricsProvider;40import sun.awt.image.ImageCache;4142abstract class AquaPainter <T extends JRSUIState> {43static <T extends JRSUIState> AquaPainter<T> create(final T state) {44return new AquaSingleImagePainter<>(state);45}4647static <T extends JRSUIState> AquaPainter<T> create(final T state, final int minWidth, final int minHeight, final int westCut, final int eastCut, final int northCut, final int southCut) {48return create(state, minWidth, minHeight, westCut, eastCut, northCut, southCut, true);49}5051static <T extends JRSUIState> AquaPainter<T> create(final T state, final int minWidth, final int minHeight, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean useMiddle) {52return create(state, minWidth, minHeight, westCut, eastCut, northCut, southCut, useMiddle, true, true);53}5455static <T extends JRSUIState> AquaPainter<T> create(final T state, final int minWidth, final int minHeight, final int westCut, final int eastCut, final int northCut, final int southCut, final boolean useMiddle, final boolean stretchHorizontally, final boolean stretchVertically) {56return create(state, new NineSliceMetricsProvider() {57@Override58public NineSliceMetrics getNineSliceMetricsForState(JRSUIState state) {59return new NineSliceMetrics(minWidth, minHeight, westCut, eastCut, northCut, southCut, useMiddle, stretchHorizontally, stretchVertically);60}61});62}6364static <T extends JRSUIState> AquaPainter<T> create(final T state, final NineSliceMetricsProvider metricsProvider) {65return new AquaNineSlicingImagePainter<>(state, metricsProvider);66}6768abstract void paint(Graphics2D g, T stateToPaint);6970final Rectangle boundsRect = new Rectangle();71final JRSUIControl control;72T state;73AquaPainter(final JRSUIControl control, final T state) {74this.control = control;75this.state = state;76}7778final JRSUIControl getControl() {79control.set(state = state.derive());80return control;81}8283final void paint(final Graphics g, final Component c, final int x,84final int y, final int w, final int h) {85boundsRect.setBounds(x, y, w, h);8687final T nextState = state.derive();88final Graphics2D g2d = getGraphics2D(g);89if (g2d != null) paint(g2d, nextState);90state = nextState;91}9293private static class AquaNineSlicingImagePainter<T extends JRSUIState>94extends AquaPainter<T> {9596private final HashMap<T, RecyclableJRSUISlicedImageControl> slicedControlImages;97private final NineSliceMetricsProvider metricsProvider;9899AquaNineSlicingImagePainter(final T state) {100this(state, null);101}102103AquaNineSlicingImagePainter(final T state, final NineSliceMetricsProvider metricsProvider) {104super(new JRSUIControl(false), state);105this.metricsProvider = metricsProvider;106slicedControlImages = new HashMap<>();107}108109@Override110void paint(final Graphics2D g, final T stateToPaint) {111if (metricsProvider == null) {112AquaSingleImagePainter.paintFromSingleCachedImage(g, control, stateToPaint, boundsRect);113return;114}115116RecyclableJRSUISlicedImageControl slicesRef = slicedControlImages.get(stateToPaint);117if (slicesRef == null) {118final NineSliceMetrics metrics = metricsProvider.getNineSliceMetricsForState(stateToPaint);119if (metrics == null) {120AquaSingleImagePainter.paintFromSingleCachedImage(g, control, stateToPaint, boundsRect);121return;122}123slicesRef = new RecyclableJRSUISlicedImageControl(control, stateToPaint, metrics);124slicedControlImages.put(stateToPaint, slicesRef);125}126final SlicedImageControl slices = slicesRef.get();127slices.paint(g, boundsRect.x, boundsRect.y, boundsRect.width, boundsRect.height);128}129}130131private static final class AquaSingleImagePainter<T extends JRSUIState>132extends AquaPainter<T> {133134AquaSingleImagePainter(final T state) {135super(new JRSUIControl(false), state);136}137138@Override139void paint(final Graphics2D g, final T stateToPaint) {140paintFromSingleCachedImage(g, control, stateToPaint, boundsRect);141}142143/**144* Paints a native control, which identified by its size and a set of145* additional arguments using a cached image.146*147* @param g Graphics to draw the control148* @param control the reference to the native control149* @param controlState the state of the native control150* @param bounds the rectangle where the native part should be drawn.151* Note: the focus can/will be drawn outside of this bounds.152*/153static void paintFromSingleCachedImage(final Graphics2D g,154final JRSUIControl control,155final JRSUIState controlState,156final Rectangle bounds) {157if (bounds.width <= 0 || bounds.height <= 0) {158return;159}160161int focus = 0;162if (controlState.is(JRSUIConstants.Focused.YES)) {163focus = JRSUIConstants.FOCUS_SIZE;164}165166final int imgX = bounds.x - focus;167final int imgY = bounds.y - focus;168final int imgW = bounds.width + (focus << 1);169final int imgH = bounds.height + (focus << 1);170final GraphicsConfiguration config = g.getDeviceConfiguration();171final ImageCache cache = ImageCache.getInstance();172final AquaPixelsKey key = new AquaPixelsKey(config, imgW, imgH,173bounds, controlState);174Image img = cache.getImage(key);175if (img == null) {176img = new MultiResolutionCachedImage(imgW, imgH,177(rvWidth, rvHeight) -> createImage(imgX, imgY,178rvWidth, rvHeight, bounds, control, controlState));179180if (!controlState.is(JRSUIConstants.Animating.YES)) {181cache.setImage(key, img);182}183}184185g.drawImage(img, imgX, imgY, imgW, imgH, null);186}187188private static Image createImage(int imgX, int imgY, int imgW, int imgH,189final Rectangle bounds,190final JRSUIControl control,191JRSUIState controlState) {192BufferedImage img = new BufferedImage(imgW, imgH,193BufferedImage.TYPE_INT_ARGB_PRE);194195final WritableRaster raster = img.getRaster();196final DataBufferInt buffer = (DataBufferInt) raster.getDataBuffer();197198control.set(controlState);199control.paint(SunWritableRaster.stealData(buffer, 0), imgW, imgH,200bounds.x - imgX, bounds.y - imgY, bounds.width,201bounds.height);202SunWritableRaster.markDirty(buffer);203return img;204}205}206207private static class AquaPixelsKey implements ImageCache.PixelsKey {208209private final int pixelCount;210private final int hash;211212// key parts213private final GraphicsConfiguration config;214private final int w;215private final int h;216private final Rectangle bounds;217private final JRSUIState state;218219AquaPixelsKey(final GraphicsConfiguration config,220final int w, final int h, final Rectangle bounds,221final JRSUIState state) {222this.pixelCount = w * h;223this.config = config;224this.w = w;225this.h = h;226this.bounds = bounds;227this.state = state;228this.hash = hash();229}230231@Override232public int getPixelCount() {233return pixelCount;234}235236private int hash() {237int hash = config != null ? config.hashCode() : 0;238hash = 31 * hash + w;239hash = 31 * hash + h;240hash = 31 * hash + bounds.hashCode();241hash = 31 * hash + state.hashCode();242return hash;243}244245@Override246public int hashCode() {247return hash;248}249250@Override251public boolean equals(Object obj) {252if (obj instanceof AquaPixelsKey) {253AquaPixelsKey key = (AquaPixelsKey) obj;254return config == key.config && w == key.w && h == key.h255&& bounds.equals(key.bounds) && state.equals(key.state);256}257return false;258}259}260261private static class RecyclableJRSUISlicedImageControl262extends RecyclableSlicedImageControl {263264private final JRSUIControl control;265private final JRSUIState state;266267RecyclableJRSUISlicedImageControl(final JRSUIControl control, final JRSUIState state, final NineSliceMetrics metrics) {268super(metrics);269this.control = control;270this.state = state;271}272273@Override274protected Image createTemplateImage(int width, int height) {275BufferedImage image = new BufferedImage(metrics.minW, metrics.minH, BufferedImage.TYPE_INT_ARGB_PRE);276277final WritableRaster raster = image.getRaster();278final DataBufferInt buffer = (DataBufferInt)raster.getDataBuffer();279280control.set(state);281control.paint(SunWritableRaster.stealData(buffer, 0), metrics.minW, metrics.minH, 0, 0, metrics.minW, metrics.minH);282283SunWritableRaster.markDirty(buffer);284285return image;286}287}288289private Graphics2D getGraphics2D(final Graphics g) {290try {291return (SunGraphics2D)g; // doing a blind try is faster than checking instanceof292} catch (Exception ignored) {293if (g instanceof PeekGraphics) {294// if it is a peek just dirty the region295g.fillRect(boundsRect.x, boundsRect.y, boundsRect.width, boundsRect.height);296} else if (g instanceof ProxyGraphics2D) {297final ProxyGraphics2D pg = (ProxyGraphics2D)g;298final Graphics2D g2d = pg.getDelegate();299if (g2d instanceof SunGraphics2D) {300return g2d;301}302} else if (g instanceof Graphics2D) {303return (Graphics2D) g;304}305}306307return null;308}309}310311312