1 /* 2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.apple.laf; 27 28 import java.awt.*; 29 import java.awt.image.*; 30 import java.util.HashMap; 31 32 import com.apple.laf.AquaImageFactory.RecyclableSlicedImageControl; 33 import com.apple.laf.AquaImageFactory.NineSliceMetrics; 34 import com.apple.laf.AquaImageFactory.SlicedImageControl; 35 36 import sun.awt.image.*; 37 import sun.java2d.*; 38 import sun.print.*; 39 import apple.laf.*; 40 import apple.laf.JRSUIConstants.Widget; 41 import apple.laf.JRSUIUtils.NineSliceMetricsProvider; 42 43 abstract class AquaPainter <T extends JRSUIState> { 44 static <T extends JRSUIState> AquaPainter<T> create(final T state) { 45 return new AquaSingleImagePainter<T>(state); 46 } 47 48 static <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) { 49 return AquaPainter.create(state, minWidth, minHeight, westCut, eastCut, northCut, southCut, true); 50 } 51 52 static <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) { 53 return AquaPainter.create(state, minWidth, minHeight, westCut, eastCut, northCut, southCut, useMiddle, true, true); 54 } 55 56 static <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) { 57 return create(state, new NineSliceMetricsProvider() { 58 @Override 59 public NineSliceMetrics getNineSliceMetricsForState(JRSUIState state) { 60 return new NineSliceMetrics(minWidth, minHeight, westCut, eastCut, northCut, southCut, useMiddle, stretchHorizontally, stretchVertically); 61 } 62 }); 63 } 64 65 static <T extends JRSUIState> AquaPainter<T> create(final T state, final NineSliceMetricsProvider metricsProvider) { 66 return new AquaNineSlicingImagePainter<T>(state, metricsProvider); 67 } 68 69 abstract void paint(final Graphics2D g, final T stateToPaint, final Component c); 70 71 final Rectangle boundsRect = new Rectangle(); 72 final JRSUIControl control; 73 T state; 74 public AquaPainter(final JRSUIControl control, final T state) { 75 this.control = control; 76 this.state = state; 77 } 78 79 JRSUIControl getControl() { 80 control.set(state = (T)state.derive()); 81 return control; 82 } 83 84 void paint(final Graphics g, final Component c, final int x, final int y, final int w, final int h) { 85 boundsRect.setBounds(x, y, w, h); 86 87 final T nextState = (T)state.derive(); 88 final Graphics2D g2d = getGraphics2D(g); 89 if (g2d != null) paint(g2d, nextState, c); 90 state = nextState; 91 } 92 93 static class AquaNineSlicingImagePainter<T extends JRSUIState> extends AquaPainter<T> { 94 protected final HashMap<T, RecyclableJRSUISlicedImageControl> slicedControlImages; 95 protected final NineSliceMetricsProvider metricsProvider; 96 97 public AquaNineSlicingImagePainter(final T state) { 98 this(state, null); 99 } 100 101 public AquaNineSlicingImagePainter(final T state, final NineSliceMetricsProvider metricsProvider) { 102 super(new JRSUIControl(false), state); 103 this.metricsProvider = metricsProvider; 104 slicedControlImages = new HashMap<T, RecyclableJRSUISlicedImageControl>(); 105 } 106 107 @Override 108 void paint(final Graphics2D g, final T stateToPaint, final Component c) { 109 if (metricsProvider == null) { 110 AquaSingleImagePainter.paintFromSingleCachedImage(g, control, stateToPaint, c, boundsRect); 111 return; 112 } 113 114 RecyclableJRSUISlicedImageControl slicesRef = slicedControlImages.get(stateToPaint); 115 if (slicesRef == null) { 116 final NineSliceMetrics metrics = metricsProvider.getNineSliceMetricsForState(stateToPaint); 117 if (metrics == null) { 118 AquaSingleImagePainter.paintFromSingleCachedImage(g, control, stateToPaint, c, boundsRect); 119 return; 120 } 121 slicesRef = new RecyclableJRSUISlicedImageControl(control, stateToPaint, metrics); 122 slicedControlImages.put(stateToPaint, slicesRef); 123 } 124 final SlicedImageControl slices = slicesRef.get(); 125 slices.paint(g, boundsRect.x, boundsRect.y, boundsRect.width, boundsRect.height); 126 } 127 } 128 129 static class AquaSingleImagePainter<T extends JRSUIState> extends AquaPainter<T> { 130 public AquaSingleImagePainter(final T state) { 131 super(new JRSUIControl(false), state); 132 } 133 134 @Override 135 void paint(Graphics2D g, T stateToPaint, Component c) { 136 paintFromSingleCachedImage(g, control, stateToPaint, c, boundsRect); 137 } 138 139 static void paintFromSingleCachedImage(final Graphics2D g, final JRSUIControl control, final JRSUIState controlState, final Component c, final Rectangle boundsRect) { 140 Rectangle clipRect = g.getClipBounds(); 141 Rectangle intersection = boundsRect.intersection(clipRect); 142 if (intersection.width <= 0 || intersection.height <= 0) return; 143 144 int imgX1 = intersection.x - boundsRect.x; 145 int imgY1 = intersection.y - boundsRect.y; 146 147 final GraphicsConfiguration config = g.getDeviceConfiguration(); 148 final ImageCache cache = ImageCache.getInstance(); 149 BufferedImage image = (BufferedImage)cache.getImage(config, boundsRect.width, boundsRect.height, controlState); 150 if (image == null) { 151 image = new BufferedImage(boundsRect.width, boundsRect.height, BufferedImage.TYPE_INT_ARGB_PRE); 152 cache.setImage(image, config, boundsRect.width, boundsRect.height, controlState); 153 } else { 154 g.drawImage(image, intersection.x, intersection.y, intersection.x + intersection.width, intersection.y + intersection.height, 155 imgX1, imgY1, imgX1 + intersection.width, imgY1 + intersection.height, null); 156 return; 157 } 158 159 final WritableRaster raster = image.getRaster(); 160 final DataBufferInt buffer = (DataBufferInt)raster.getDataBuffer(); 161 162 control.set(controlState); 163 control.paint(SunWritableRaster.stealData(buffer, 0), 164 image.getWidth(), image.getHeight(), 0, 0, boundsRect.width, boundsRect.height); 165 SunWritableRaster.markDirty(buffer); 166 167 g.drawImage(image, intersection.x, intersection.y, intersection.x + intersection.width, intersection.y + intersection.height, 168 imgX1, imgY1, imgX1 + intersection.width, imgY1 + intersection.height, null); 169 } 170 } 171 172 static class RecyclableJRSUISlicedImageControl extends RecyclableSlicedImageControl { 173 final JRSUIControl control; 174 final JRSUIState state; 175 176 public RecyclableJRSUISlicedImageControl(final JRSUIControl control, final JRSUIState state, final NineSliceMetrics metrics) { 177 super(metrics); 178 this.control = control; 179 this.state = state; 180 } 181 182 @Override 183 protected Image createTemplateImage(int width, int height) { 184 BufferedImage image = new BufferedImage(metrics.minW, metrics.minH, BufferedImage.TYPE_INT_ARGB_PRE); 185 186 final WritableRaster raster = image.getRaster(); 187 final DataBufferInt buffer = (DataBufferInt)raster.getDataBuffer(); 188 189 control.set(state); 190 control.paint(SunWritableRaster.stealData(buffer, 0), metrics.minW, metrics.minH, 0, 0, metrics.minW, metrics.minH); 191 192 SunWritableRaster.markDirty(buffer); 193 194 return image; 195 } 196 } 197 198 protected Graphics2D getGraphics2D(final Graphics g) { 199 try { 200 return (SunGraphics2D)g; // doing a blind try is faster than checking instanceof 201 } catch (Exception e) { 202 if (g instanceof PeekGraphics) { 203 // if it is a peek just dirty the region 204 g.fillRect(boundsRect.x, boundsRect.y, boundsRect.width, boundsRect.height); 205 } else if (g instanceof ProxyGraphics2D) { 206 final ProxyGraphics2D pg = (ProxyGraphics2D)g; 207 final Graphics2D g2d = pg.getDelegate(); 208 if (g2d instanceof SunGraphics2D) { return (SunGraphics2D)g2d; } 209 } else if (g instanceof Graphics2D) { 210 return (Graphics2D) g; 211 } 212 } 213 214 return null; 215 } 216 }