7124244: [macosx] Shaped windows support
authorserb
Wed, 04 Jul 2012 14:38:14 +0400
changeset 13233 4d45f7ebc0d7
parent 13147 eada40c7d74d
child 13234 9437e5985d73
7124244: [macosx] Shaped windows support Reviewed-by: anthony, art
jdk/src/macosx/classes/sun/java2d/opengl/CGLLayer.java
jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java
jdk/src/macosx/classes/sun/lwawt/LWRepaintArea.java
jdk/src/macosx/classes/sun/lwawt/LWToolkit.java
jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java
jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java
jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java
jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java
jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java
jdk/src/macosx/native/sun/awt/AWTWindow.m
jdk/src/share/classes/javax/swing/RepaintManager.java
jdk/src/share/classes/sun/awt/SunToolkit.java
--- a/jdk/src/macosx/classes/sun/java2d/opengl/CGLLayer.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/classes/sun/java2d/opengl/CGLLayer.java	Wed Jul 04 14:38:14 2012 +0400
@@ -68,11 +68,12 @@
     }
 
     public boolean isOpaque() {
-        return peer.isOpaque();
+        return !peer.isTranslucent();
     }
 
     public int getTransparency() {
-        return (peer.isOpaque() ? Transparency.OPAQUE : Transparency.TRANSLUCENT);
+        return peer.isTranslucent() ? Transparency.TRANSLUCENT :
+               Transparency.OPAQUE;
     }
 
     public Object getDestination() {
--- a/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/LWComponentPeer.java	Wed Jul 04 14:38:14 2012 +0400
@@ -424,8 +424,7 @@
 
     @Override
     public final Graphics getGraphics() {
-        Graphics g = getWindowPeerOrSelf().isOpaque() ? getOnscreenGraphics()
-                                                      : getOffscreenGraphics();
+        final Graphics g = getOnscreenGraphics();
         if (g != null) {
             synchronized (getPeerTreeLock()){
                 applyConstrain(g);
@@ -443,13 +442,7 @@
         final LWWindowPeer wp = getWindowPeerOrSelf();
         return wp.getOnscreenGraphics(getForeground(), getBackground(),
                                       getFont());
-    }
 
-    public final Graphics getOffscreenGraphics() {
-        final LWWindowPeer wp = getWindowPeerOrSelf();
-
-        return wp.getOffscreenGraphics(getForeground(), getBackground(),
-                                       getFont());
     }
 
     private void applyConstrain(final Graphics g) {
@@ -463,7 +456,7 @@
     }
 
     //TODO Move this method to SG2D?
-    private void SG2DConstraint(final SunGraphics2D sg2d, Region r) {
+    void SG2DConstraint(final SunGraphics2D sg2d, Region r) {
         sg2d.constrainX = sg2d.transX;
         sg2d.constrainY = sg2d.transY;
 
@@ -710,7 +703,7 @@
         // Obtain the metrics from the offscreen window where this peer is
         // mostly drawn to.
         // TODO: check for "use platform metrics" settings
-        Graphics g = getWindowPeer().getOffscreenGraphics();
+        Graphics g = getWindowPeer().getGraphics();
         try {
             if (g != null) {
                 return g.getFontMetrics(f);
@@ -1011,14 +1004,33 @@
     @Override
     public final void applyShape(final Region shape) {
         synchronized (getStateLock()) {
-            region = shape;
+            if (region == shape || (region != null && region.equals(shape))) {
+                return;
+            }
+        }
+        applyShapeImpl(shape);
+    }
+
+    void applyShapeImpl(final Region shape) {
+        synchronized (getStateLock()) {
+            if (shape != null) {
+                region = Region.WHOLE_REGION.getIntersection(shape);
+            } else {
+                region = null;
+            }
         }
         repaintParent(getBounds());
     }
 
     protected final Region getRegion() {
         synchronized (getStateLock()) {
-            return region == null ? Region.getInstance(getSize()) : region;
+            return isShaped() ? region : Region.getInstance(getSize());
+        }
+    }
+
+    public boolean isShaped() {
+        synchronized (getStateLock()) {
+            return region != null;
         }
     }
 
@@ -1386,11 +1398,6 @@
         }
     }
 
-    // Just a helper method, thus final
-    protected final void flushOffscreenGraphics() {
-        flushOffscreenGraphics(getSize());
-    }
-
     protected static final void flushOnscreenGraphics(){
         final OGLRenderQueue rq = OGLRenderQueue.getInstance();
         rq.lock();
@@ -1401,36 +1408,6 @@
         }
     }
 
-    /*
-     * Flushes the given rectangle from the back buffer to the screen.
-     */
-    protected void flushOffscreenGraphics(Rectangle r) {
-        flushOffscreenGraphics(r.x, r.y, r.width, r.height);
-    }
-
-    private void flushOffscreenGraphics(int x, int y, int width, int height) {
-        Image bb = getWindowPeerOrSelf().getBackBuffer();
-        if (bb != null) {
-            // g is a screen Graphics from the delegate
-            final Graphics g = getOnscreenGraphics();
-
-            if (g != null && g instanceof Graphics2D) {
-                try {
-                    Graphics2D g2d = (Graphics2D)g;
-                    Point p = localToWindow(new Point(0, 0));
-                    Composite composite = g2d.getComposite();
-                    g2d.setComposite(AlphaComposite.Src);
-                    g.drawImage(bb, x, y, x + width, y + height, p.x + x,
-                            p.y + y, p.x + x + width, p.y + y + height,
-                            null);
-                    g2d.setComposite(composite);
-                } finally {
-                    g.dispose();
-                }
-            }
-        }
-    }
-
     /**
      * Used by ContainerPeer to skip all the paint events during layout.
      *
--- a/jdk/src/macosx/classes/sun/lwawt/LWRepaintArea.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/LWRepaintArea.java	Wed Jul 04 14:38:14 2012 +0400
@@ -58,9 +58,6 @@
 
     private static void flushBuffers(final LWComponentPeer peer) {
         if (peer != null) {
-            if (!peer.getWindowPeerOrSelf().isOpaque()) {
-                peer.flushOffscreenGraphics();
-            }
             peer.flushOnscreenGraphics();
         }
     }
--- a/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/LWToolkit.java	Wed Jul 04 14:38:14 2012 +0400
@@ -522,12 +522,6 @@
         postEvent(targetToAppContext(event.getSource()), event);
     }
 
-    // use peer's back buffer to implement non-opaque windows.
-    @Override
-    public boolean needUpdateWindow() {
-        return true;
-    }
-
     @Override
     public void grab(Window w) {
         if (w.getPeer() != null) {
--- a/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/LWWindowPeer.java	Wed Jul 04 14:38:14 2012 +0400
@@ -37,6 +37,7 @@
 import sun.java2d.*;
 import sun.java2d.loops.Blit;
 import sun.java2d.loops.CompositeType;
+import sun.java2d.pipe.Region;
 import sun.util.logging.PlatformLogger;
 
 public class LWWindowPeer
@@ -109,6 +110,8 @@
 
     private volatile boolean skipNextFocusChange;
 
+    private static final Color nonOpaqueBackground = new Color(0, 0, 0, 0);
+
     /**
      * Current modal blocker or null.
      *
@@ -169,6 +172,11 @@
         setAlwaysOnTop(getTarget().isAlwaysOnTop());
         updateMinimumSize();
 
+        final Shape shape = getTarget().getShape();
+        if (shape != null) {
+            applyShape(Region.getInstance(shape, null));
+        }
+
         final float opacity = getTarget().getOpacity();
         if (opacity < 1.0f) {
             setOpacity(opacity);
@@ -178,7 +186,7 @@
 
         updateInsets(platformWindow.getInsets());
         if (getSurfaceData() == null) {
-            replaceSurfaceData();
+            replaceSurfaceData(false);
         }
     }
 
@@ -280,7 +288,7 @@
             // "buffer", that's why numBuffers - 1
             assert numBuffers > 1;
 
-            replaceSurfaceData(numBuffers - 1, caps);
+            replaceSurfaceData(numBuffers - 1, caps, false);
         } catch (InvalidPipeException z) {
             throw new AWTException(z.toString());
         }
@@ -420,19 +428,30 @@
     public final void setOpaque(final boolean isOpaque) {
         if (this.isOpaque != isOpaque) {
             this.isOpaque = isOpaque;
-            getPlatformWindow().setOpaque(isOpaque);
-            replaceSurfaceData();
-            repaintPeer();
+            updateOpaque();
         }
     }
 
-    public final boolean isOpaque() {
-        return isOpaque;
+    private void updateOpaque() {
+        getPlatformWindow().setOpaque(!isTranslucent());
+        replaceSurfaceData(false);
+        repaintPeer();
     }
 
     @Override
     public void updateWindow() {
-        flushOffscreenGraphics();
+    }
+
+    public final boolean isTranslucent() {
+        synchronized (getStateLock()) {
+            return !isOpaque || isShaped();
+        }
+    }
+
+    @Override
+    final void applyShapeImpl(final Region shape) {
+        super.applyShapeImpl(shape);
+        updateOpaque();
     }
 
     @Override
@@ -587,7 +606,18 @@
                                                getFont());
         if (g != null) {
             try {
-                g.clearRect(0, 0, w, h);
+                if (g instanceof Graphics2D) {
+                    ((Graphics2D) g).setComposite(AlphaComposite.Src);
+                }
+                if (isTranslucent()) {
+                    g.setColor(nonOpaqueBackground);
+                    g.fillRect(0, 0, w, h);
+                }
+                if (g instanceof SunGraphics2D) {
+                    SG2DConstraint((SunGraphics2D) g, getRegion());
+                }
+                g.setColor(getBackground());
+                g.fillRect(0, 0, w, h);
             } finally {
                 g.dispose();
             }
@@ -894,35 +924,6 @@
         });
     }
 
-    /**
-     * This method returns a back buffer Graphics to render all the
-     * peers to. After the peer is painted, the back buffer contents
-     * should be flushed to the screen. All the target painting
-     * (Component.paint() method) should be done directly to the screen.
-     */
-    protected final Graphics getOffscreenGraphics(Color fg, Color bg, Font f) {
-        final Image bb = getBackBuffer();
-        if (bb == null) {
-            return null;
-        }
-        if (fg == null) {
-            fg = SystemColor.windowText;
-        }
-        if (bg == null) {
-            bg = SystemColor.window;
-        }
-        if (f == null) {
-            f = DEFAULT_FONT;
-        }
-        final Graphics2D g = (Graphics2D) bb.getGraphics();
-        if (g != null) {
-            g.setColor(fg);
-            g.setBackground(bg);
-            g.setFont(f);
-        }
-        return g;
-    }
-
     /*
      * May be called by delegate to provide SD to Java2D code.
      */
@@ -933,11 +934,16 @@
     }
 
     private void replaceSurfaceData() {
-        replaceSurfaceData(backBufferCount, backBufferCaps);
+        replaceSurfaceData(true);
+    }
+
+    private void replaceSurfaceData(boolean blit) {
+        replaceSurfaceData(backBufferCount, backBufferCaps, blit);
     }
 
     private void replaceSurfaceData(int newBackBufferCount,
-                                                 BufferCapabilities newBackBufferCaps) {
+                                    BufferCapabilities newBackBufferCaps,
+                                    boolean blit) {
         synchronized (surfaceDataLock) {
             final SurfaceData oldData = getSurfaceData();
             surfaceData = platformWindow.replaceSurfaceData();
@@ -950,7 +956,10 @@
             if (getSurfaceData() != null && oldData != getSurfaceData()) {
                 clearBackground(size.width, size.height);
             }
-            blitSurfaceData(oldData, getSurfaceData());
+
+            if (blit) {
+                blitSurfaceData(oldData, getSurfaceData());
+            }
 
             if (oldData != null && oldData != getSurfaceData()) {
                 // TODO: drop oldData for D3D/WGL pipelines
@@ -965,10 +974,15 @@
                 Graphics g = backBuffer.getGraphics();
                 try {
                     Rectangle r = getBounds();
-                    g.setColor(getBackground());
                     if (g instanceof Graphics2D) {
                         ((Graphics2D) g).setComposite(AlphaComposite.Src);
                     }
+                    g.setColor(nonOpaqueBackground);
+                    g.fillRect(0, 0, r.width, r.height);
+                    if (g instanceof SunGraphics2D) {
+                        SG2DConstraint((SunGraphics2D) g, getRegion());
+                    }
+                    g.setColor(getBackground());
                     g.fillRect(0, 0, r.width, r.height);
                     if (oldBB != null) {
                         // Draw the old back buffer to the new one
@@ -993,7 +1007,7 @@
                                           CompositeType.Src,
                                           dst.getSurfaceType());
             if (blit != null) {
-                blit.Blit(src, dst, ((Graphics2D) getGraphics()).getComposite(),
+                blit.Blit(src, dst, AlphaComposite.Src,
                           getRegion(), 0, 0, 0, 0, size.width, size.height);
             }
         }
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformEmbeddedFrame.java	Wed Jul 04 14:38:14 2012 +0400
@@ -117,7 +117,7 @@
         Rectangle r = peer.getBounds();
         Image im = null;
         if (!r.isEmpty()) {
-            int transparency = (peer.isOpaque() ? Transparency.OPAQUE : Transparency.TRANSLUCENT);
+            int transparency = peer.isTranslucent() ? Transparency.TRANSLUCENT : Transparency.OPAQUE;
             im = peer.getGraphicsConfiguration().createCompatibleImage(r.width, r.height, transparency);
         }
         return im;
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformView.java	Wed Jul 04 14:38:14 2012 +0400
@@ -64,7 +64,7 @@
     }
 
     public boolean isOpaque() {
-        return peer.isOpaque();
+        return !peer.isTranslucent();
     }
 
     /*
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/CPlatformWindow.java	Wed Jul 04 14:38:14 2012 +0400
@@ -737,6 +737,15 @@
             long clearColor = CWrapper.NSColor.clearColor();
             CWrapper.NSWindow.setBackgroundColor(getNSWindowPtr(), clearColor);
         }
+
+        //This is a temporary workaround. Looks like after 7124236 will be fixed
+        //the correct place for invalidateShadow() is CGLayer.drawInCGLContext.
+        SwingUtilities.invokeLater(new Runnable() {
+            @Override
+            public void run() {
+                invalidateShadow();
+            }
+        });
     }
 
     @Override
@@ -805,6 +814,10 @@
         nativeSetEnabled(getNSWindowPtr(), !blocked);
     }
 
+    public final void invalidateShadow(){
+        nativeRevalidateNSWindowShadow(getNSWindowPtr());
+    }
+
     // ----------------------------------------------------------------------
     //                          UTILITY METHODS
     // ----------------------------------------------------------------------
--- a/jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/classes/sun/lwawt/macosx/LWCToolkit.java	Wed Jul 04 14:38:14 2012 +0400
@@ -750,6 +750,11 @@
     }
 
     @Override
+    public boolean isWindowShapingSupported() {
+        return true;
+    }
+
+    @Override
     public boolean isWindowTranslucencySupported() {
         return true;
     }
@@ -759,6 +764,10 @@
         return true;
     }
 
+    public boolean isSwingBackbufferTranslucencySupported() {
+        return true;
+    }
+
     @Override
     public boolean enableInputMethodsForTextComponent() {
         return true;
--- a/jdk/src/macosx/native/sun/awt/AWTWindow.m	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/macosx/native/sun/awt/AWTWindow.m	Wed Jul 04 14:38:14 2012 +0400
@@ -941,14 +941,17 @@
 (JNIEnv *env, jclass clazz, jlong windowPtr)
 {
 JNF_COCOA_ENTER(env);
-AWT_ASSERT_NOT_APPKIT_THREAD;
 
     NSWindow *nsWindow = OBJC(windowPtr);
-    [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
-        AWT_ASSERT_APPKIT_THREAD;
+    if ([NSThread isMainThread]) {
+        [nsWindow invalidateShadow];
+    } else {
+        [JNFRunLoop performOnMainThreadWaiting:NO withBlock:^(){
+            AWT_ASSERT_APPKIT_THREAD;
 
-        [nsWindow invalidateShadow];
-    }];
+            [nsWindow invalidateShadow];
+        }];
+    }
 
 JNF_COCOA_EXIT(env);
 }
--- a/jdk/src/share/classes/javax/swing/RepaintManager.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/share/classes/javax/swing/RepaintManager.java	Wed Jul 04 14:38:14 2012 +0400
@@ -119,6 +119,11 @@
     // Whether or not a VolatileImage should be used for double-buffered painting
     static boolean volatileImageBufferEnabled = true;
     /**
+     * Type of VolatileImage which should be used for double-buffered
+     * painting.
+     */
+    private static final int volatileBufferType;
+    /**
      * Value of the system property awt.nativeDoubleBuffering.
      */
     private static boolean nativeDoubleBuffering;
@@ -204,6 +209,13 @@
             ((SunGraphicsEnvironment)ge).addDisplayChangedListener(
                     new DisplayChangedHandler());
         }
+        Toolkit tk = Toolkit.getDefaultToolkit();
+        if ((tk instanceof SunToolkit)
+                && ((SunToolkit) tk).isSwingBackbufferTranslucencySupported()) {
+            volatileBufferType = Transparency.TRANSLUCENT;
+        } else {
+            volatileBufferType = Transparency.OPAQUE;
+        }
     }
 
     /**
@@ -989,7 +1001,8 @@
             if (image != null) {
                 image.flush();
             }
-            image = config.createCompatibleVolatileImage(width, height);
+            image = config.createCompatibleVolatileImage(width, height,
+                                                         volatileBufferType);
             volatileMap.put(config, image);
         }
         return image;
@@ -1483,9 +1496,26 @@
                     for(y=clipY, maxy = clipY + clipH; y < maxy ; y += bh) {
                         osg.translate(-x, -y);
                         osg.setClip(x,y,bw,bh);
+                        if (volatileBufferType != Transparency.OPAQUE
+                                && osg instanceof Graphics2D) {
+                            final Graphics2D g2d = (Graphics2D) osg;
+                            final Color oldBg = g2d.getBackground();
+                            g2d.setBackground(c.getBackground());
+                            g2d.clearRect(x, y, bw, bh);
+                            g2d.setBackground(oldBg);
+                        }
                         c.paintToOffscreen(osg, x, y, bw, bh, maxx, maxy);
                         g.setClip(x, y, bw, bh);
-                        g.drawImage(image, x, y, c);
+                        if (volatileBufferType != Transparency.OPAQUE
+                                && g instanceof Graphics2D) {
+                            final Graphics2D g2d = (Graphics2D) g;
+                            final Composite oldComposite = g2d.getComposite();
+                            g2d.setComposite(AlphaComposite.Src);
+                            g2d.drawImage(image, x, y, c);
+                            g2d.setComposite(oldComposite);
+                        } else {
+                            g.drawImage(image, x, y, c);
+                        }
                         osg.translate(x, y);
                     }
                 }
--- a/jdk/src/share/classes/sun/awt/SunToolkit.java	Thu Jun 28 14:05:06 2012 +0400
+++ b/jdk/src/share/classes/sun/awt/SunToolkit.java	Wed Jul 04 14:38:14 2012 +0400
@@ -1985,6 +1985,13 @@
     }
 
     /**
+     * Returns true if swing backbuffer should be translucent.
+     */
+    public boolean isSwingBackbufferTranslucencySupported() {
+        return false;
+    }
+
+    /**
      * Returns whether or not a containing top level window for the passed
      * component is
      * {@link GraphicsDevice.WindowTranslucency#PERPIXEL_TRANSLUCENT PERPIXEL_TRANSLUCENT}.