1 /* 2 * Copyright (c) 2013, 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 sun.swing; 27 28 import java.awt.BorderLayout; 29 import java.awt.Color; 30 import java.awt.Component; 31 import java.awt.Container; 32 import java.awt.EventQueue; 33 import java.awt.Graphics; 34 import java.awt.Graphics2D; 35 import java.awt.Rectangle; 36 import java.awt.image.BufferedImage; 37 import java.awt.image.DataBufferInt; 38 import java.security.AccessController; 39 40 import javax.swing.JLayeredPane; 41 import javax.swing.JPanel; 42 import javax.swing.JRootPane; 43 import javax.swing.LayoutFocusTraversalPolicy; 44 import javax.swing.RootPaneContainer; 45 46 import sun.awt.LightweightFrame; 47 import sun.security.action.GetPropertyAction; 48 49 /** 50 * The frame serves as a lightweight container which paints its content 51 * to an offscreen image and provides access to the image's data via the 52 * {@link LightweightContent} interface. Note, that it may not be shown 53 * as a standalone toplevel frame. Its purpose is to provide functionality 54 * for lightweight embedding. 55 * 56 * @author Artem Ananiev 57 * @author Anton Tarasov 58 */ 59 public final class JLightweightFrame extends LightweightFrame implements RootPaneContainer { 60 61 private final JRootPane rootPane = new JRootPane(); 62 63 private LightweightContent content; 64 65 private Component component; 66 private JPanel contentPane; 67 68 private BufferedImage bbImage; 69 70 /** 71 * {@code copyBufferEnabled}, true by default, defines the following strategy. 72 * A duplicating (copy) buffer is created for the original pixel buffer. 73 * The copy buffer is synchronized with the original buffer every time the 74 * latter changes. {@code JLightweightFrame} passes the copy buffer array 75 * to the {@link LightweightContent#imageBufferReset} method. The code spot 76 * which synchronizes two buffers becomes the only critical section guarded 77 * by the lock (managed with the {@link LightweightContent#paintLock()}, 78 * {@link LightweightContent#paintUnlock()} methods). 79 */ 80 private boolean copyBufferEnabled; 81 private int[] copyBuffer; 82 83 /** 84 * Constructs a new, initially invisible {@code JLightweightFrame} 85 * instance. 86 */ 87 public JLightweightFrame() { 88 super(); 89 copyBufferEnabled = "true".equals(AccessController. 90 doPrivileged(new GetPropertyAction("jlf.copyBufferEnabled", "true"))); 91 92 add(rootPane, BorderLayout.CENTER); 93 setFocusTraversalPolicy(new LayoutFocusTraversalPolicy()); 94 if (getGraphicsConfiguration().isTranslucencyCapable()) { 95 setBackground(new Color(0, 0, 0, 0)); 96 } 97 } 98 99 /** 100 * Sets the {@link LightweightContent} instance for this frame. 101 * The {@code JComponent} object returned by the 102 * {@link LightweightContent#getComponent()} method is immediately 103 * added to the frame's content pane. 104 * 105 * @param content the {@link LightweightContent} instance 106 */ 107 public void setContent(LightweightContent content) { 108 this.content = content; 109 this.component = content.getComponent(); 110 111 initInterior(); 112 } 113 114 @Override 115 public Graphics getGraphics() { 116 if (bbImage == null) return null; 117 118 Graphics2D g = bbImage.createGraphics(); 119 g.setBackground(getBackground()); 120 g.setColor(getForeground()); 121 g.setFont(getFont()); 122 return g; 123 } 124 125 /** 126 * {@inheritDoc} 127 * 128 * @see LightweightContent#focusGrabbed() 129 */ 130 @Override 131 public void grabFocus() { 132 if (content != null) content.focusGrabbed(); 133 } 134 135 /** 136 * {@inheritDoc} 137 * 138 * @see LightweightContent#focusUngrabbed() 139 */ 140 @Override 141 public void ungrabFocus() { 142 if (content != null) content.focusUngrabbed(); 143 } 144 145 private void syncCopyBuffer(boolean reset, int x, int y, int w, int h) { 146 content.paintLock(); 147 try { 148 int[] srcBuffer = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); 149 if (reset) { 150 copyBuffer = new int[srcBuffer.length]; 151 } 152 int linestride = bbImage.getWidth(); 153 154 for (int i=0; i<h; i++) { 155 int from = (y + i) * linestride + x; 156 System.arraycopy(srcBuffer, from, copyBuffer, from, w); 157 } 158 } finally { 159 content.paintUnlock(); 160 } 161 } 162 163 private void initInterior() { 164 contentPane = new JPanel() { 165 @Override 166 public void paint(Graphics g) { 167 if (!copyBufferEnabled) { 168 content.paintLock(); 169 } 170 try { 171 super.paint(g); 172 173 final Rectangle clip = g.getClipBounds() != null ? 174 g.getClipBounds() : 175 new Rectangle(0, 0, contentPane.getWidth(), contentPane.getHeight()); 176 177 clip.x = Math.max(0, clip.x); 178 clip.y = Math.max(0, clip.y); 179 clip.width = Math.min(contentPane.getWidth(), clip.width); 180 clip.height = Math.min(contentPane.getHeight(), clip.height); 181 182 EventQueue.invokeLater(new Runnable() { 183 @Override 184 public void run() { 185 if (copyBufferEnabled) { 186 syncCopyBuffer(false, clip.x, clip.y, clip.width, clip.height); 187 } 188 content.imageUpdated(clip.x, clip.y, clip.width, clip.height); 189 } 190 }); 191 } finally { 192 if (!copyBufferEnabled) { 193 content.paintUnlock(); 194 } 195 } 196 } 197 @Override 198 protected boolean isPaintingOrigin() { 199 return true; 200 } 201 }; 202 contentPane.setLayout(new BorderLayout()); 203 contentPane.add(component); 204 setContentPane(contentPane); 205 } 206 207 @SuppressWarnings("deprecation") 208 @Override public void reshape(int x, int y, int width, int height) { 209 super.reshape(x, y, width, height); 210 211 if (width == 0 || height == 0) { 212 return; 213 } 214 if (!copyBufferEnabled) { 215 content.paintLock(); 216 } 217 try { 218 if ((bbImage == null) || (width != bbImage.getWidth()) || (height != bbImage.getHeight())) { 219 boolean createBB = true; 220 int newW = width; 221 int newH = height; 222 if (bbImage != null) { 223 int oldW = bbImage.getWidth(); 224 int oldH = bbImage.getHeight(); 225 if ((oldW >= newW) && (oldH >= newH)) { 226 createBB = false; 227 } else { 228 if (oldW >= newW) { 229 newW = oldW; 230 } else { 231 newW = Math.max((int)(oldW * 1.2), width); 232 } 233 if (oldH >= newH) { 234 newH = oldH; 235 } else { 236 newH = Math.max((int)(oldH * 1.2), height); 237 } 238 } 239 } 240 if (createBB) { 241 BufferedImage oldBB = bbImage; 242 bbImage = new BufferedImage(newW, newH, BufferedImage.TYPE_INT_ARGB_PRE); 243 if (oldBB != null) { 244 Graphics g = bbImage.getGraphics(); 245 try { 246 g.drawImage(oldBB, 0, 0, newW, newH, null); 247 } finally { 248 g.dispose(); 249 oldBB.flush(); 250 } 251 } 252 int[] pixels = ((DataBufferInt)bbImage.getRaster().getDataBuffer()).getData(); 253 if (copyBufferEnabled) { 254 syncCopyBuffer(true, 0, 0, width, height); 255 pixels = copyBuffer; 256 } 257 content.imageBufferReset(pixels, 0, 0, width, height, bbImage.getWidth()); 258 return; 259 } 260 } 261 content.imageReshaped(0, 0, width, height); 262 263 } finally { 264 if (!copyBufferEnabled) { 265 content.paintUnlock(); 266 } 267 } 268 } 269 270 @Override 271 public JRootPane getRootPane() { 272 return rootPane; 273 } 274 275 @Override 276 public void setContentPane(Container contentPane) { 277 getRootPane().setContentPane(contentPane); 278 } 279 280 @Override 281 public Container getContentPane() { 282 return getRootPane().getContentPane(); 283 } 284 285 @Override 286 public void setLayeredPane(JLayeredPane layeredPane) { 287 getRootPane().setLayeredPane(layeredPane); 288 } 289 290 @Override 291 public JLayeredPane getLayeredPane() { 292 return getRootPane().getLayeredPane(); 293 } 294 295 @Override 296 public void setGlassPane(Component glassPane) { 297 getRootPane().setGlassPane(glassPane); 298 } 299 300 @Override 301 public Component getGlassPane() { 302 return getRootPane().getGlassPane(); 303 } 304 }