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 }