5252#include " Layers.h"
5353#include " LayerManagerOGL.h"
5454#include " GLTextureImage.h"
55+ #include " GLContext.h"
5556#include " mozilla/layers/CompositorCocoaWidgetHelper.h"
5657#ifdef ACCESSIBILITY
5758#include " nsAccessibilityService.h"
7273using namespace mozilla::layers;
7374using namespace mozilla::gl;
7475using namespace mozilla::widget;
75- using namespace mozilla;
7676
7777#undef DEBUG_UPDATE
7878#undef INVALIDATE_DEBUGGING // flash areas as they are invalidated
8989 CG_EXTERN void CGContextResetCTM (CGContextRef);
9090 CG_EXTERN void CGContextSetCTM (CGContextRef, CGAffineTransform);
9191 CG_EXTERN void CGContextResetClip (CGContextRef);
92+
93+ typedef CFTypeRef CGSRegionObj;
94+ CGError CGSNewRegionWithRect (const CGRect *rect, CGSRegionObj *outRegion);
9295}
9396
9497// defined in nsMenuBarX.mm
@@ -138,6 +141,10 @@ - (BOOL)isUsingOpenGL;
138141- (void )drawUsingOpenGL ;
139142- (void )drawUsingOpenGLCallback ;
140143
144+ - (BOOL )hasRoundedBottomCorners ;
145+ - (CGFloat)bottomCornerRadius ;
146+ - (void )maybeClearBottomCorners ;
147+
141148// Called using performSelector:withObject:afterDelay:0 to release
142149// aWidgetArray (and its contents) the next time through the run loop.
143150- (void )releaseWidgets : (NSArray *)aWidgetArray ;
@@ -156,6 +163,28 @@ - (BOOL)inactiveWindowAcceptsMouseEvent:(NSEvent*)aEvent;
156163
157164@end
158165
166+ @interface NSView (NSThemeFrameCornerRadius)
167+ - (float )roundedCornerRadius ;
168+ @end
169+
170+ // Starting with 10.7 the bottom corners of all windows are rounded.
171+ // Unfortunately, the standard rounding that OS X applies to OpenGL views
172+ // does not use anti-aliasing and looks very crude. Since we want a smooth,
173+ // anti-aliased curve, we'll draw it ourselves.
174+ // Additionally, we need to turn off the OS-supplied rounding because it
175+ // eats into our corner's curve. We do that by overriding an NSSurface method.
176+ @interface NSSurface @end
177+
178+ @implementation NSSurface (DontCutOffCorners)
179+ - (CGSRegionObj)_createRoundedBottomRegionForRect : (CGRect)rect
180+ {
181+ // Create a normal rect region without rounded bottom corners.
182+ CGSRegionObj region;
183+ CGSNewRegionWithRect (&rect, ®ion);
184+ return region;
185+ }
186+ @end
187+
159188#pragma mark -
160189
161190/* Convenience routines to go from a gecko rect to cocoa NSRects and back
@@ -211,6 +240,8 @@ void EnsureLogInitialized()
211240, mParentView(nullptr )
212241, mParentWidget(nullptr )
213242, mBackingScaleFactor(0.0 )
243+ , mFailedResizerImage(false )
244+ , mFailedCornerMaskImage(false )
214245, mVisible(false )
215246, mDrawing(false )
216247, mPluginDrawing(false )
@@ -235,10 +266,11 @@ void EnsureLogInitialized()
235266 kid = kid->GetPrevSibling ();
236267 childView->ResetParent ();
237268 }
238-
269+
239270 NS_WARN_IF_FALSE (mOnDestroyCalled, " nsChildView object destroyed without calling Destroy()" );
240271
241272 mResizerImage = nullptr ;
273+ mCornerMaskImage = nullptr ;
242274
243275 // An nsChildView object that was in use can be destroyed without Destroy()
244276 // ever being called on it. So we also need to do a quick, safe cleanup
@@ -1817,6 +1849,18 @@ inline uint16_t COLOR8TOCOLOR16(uint8_t color8)
18171849 return mTempThebesSurface;
18181850}
18191851
1852+ void
1853+ nsChildView::DrawWindowOverlay (LayerManager* aManager, nsIntRect aRect)
1854+ {
1855+ if (!aManager) {
1856+ return ;
1857+ }
1858+
1859+ LayerManagerOGL *manager = static_cast<LayerManagerOGL *>(aManager);
1860+ MaybeDrawResizeIndicator (manager, aRect);
1861+ MaybeDrawRoundedBottomCorners (manager, aRect);
1862+ }
1863+
18201864static void
18211865DrawResizer (CGContextRef aCtx)
18221866{
@@ -1851,39 +1895,42 @@ inline uint16_t COLOR8TOCOLOR16(uint8_t color8)
18511895}
18521896
18531897void
1854- nsChildView::DrawWindowOverlay (LayerManager * aManager, nsIntRect aRect)
1898+ nsChildView::MaybeDrawResizeIndicator (LayerManagerOGL * aManager, nsIntRect aRect)
18551899{
1856- if (!ShowsResizeIndicator (nullptr )) {
1857- return ;
1858- }
1859-
1860- nsRefPtr<LayerManagerOGL> manager (static_cast<LayerManagerOGL*>(aManager));
1861- if (!manager) {
1900+ nsIntRect resizeRect;
1901+ if (!ShowsResizeIndicator (&resizeRect) || mFailedResizerImage) {
18621902 return ;
18631903 }
18641904
18651905 if (!mResizerImage) {
1866- mResizerImage = TextureImage::Create (manager-> gl (),
1867- nsIntSize (15 , 15 ),
1868- gfxASurface::CONTENT_COLOR_ALPHA ,
1869- LOCAL_GL_CLAMP_TO_EDGE ,
1870- TextureImage::UseNearestFilter);
1906+ mResizerImage =
1907+ aManager-> gl ()-> CreateTextureImage ( nsIntSize (resizeRect. width , resizeRect. height ),
1908+ gfxASurface::CONTENT_COLOR_ALPHA ,
1909+ LOCAL_GL_CLAMP_TO_EDGE ,
1910+ TextureImage::UseNearestFilter);
18711911
18721912 // Creation of texture images can fail.
18731913 if (!mResizerImage)
18741914 return ;
18751915
1876- nsIntRegion update (nsIntRect (0 , 0 , 15 , 15 ));
1916+ nsIntRegion update (nsIntRect (0 , 0 , resizeRect. width , resizeRect. height ));
18771917 gfxASurface *asurf = mResizerImage->BeginUpdate (update);
18781918 if (!asurf) {
18791919 mResizerImage = nullptr ;
18801920 return ;
18811921 }
18821922
1883- NS_ABORT_IF_FALSE (asurf->GetType () == gfxASurface::SurfaceTypeQuartz,
1884- "BeginUpdate must return a Quartz surface!");
1885-
1923+ // is the CairoSurface of a non QuartzSurface usable in the gfxQuartzSurface constructor ?
1924+ // answer seems to be NO (see comments on bug 675410
1925+ // this should not work for instance: new gfxQuartzSurface(asurf->CairoSurface(), false))
1926+ if (asurf->GetType () != gfxASurface::SurfaceTypeQuartz) {
1927+ NS_WARN_IF_FALSE (FALSE , " mResizerImage's surface is not Quartz" );
1928+ mResizerImage = nullptr ;
1929+ mFailedResizerImage = true ;
1930+ return ;
1931+ }
18861932 nsRefPtr<gfxQuartzSurface> image = static_cast<gfxQuartzSurface*>(asurf);
1933+
18871934 DrawResizer (image->GetCGContext ());
18881935
18891936 mResizerImage->EndUpdate ();
@@ -1897,18 +1944,100 @@ inline uint16_t COLOR8TOCOLOR16(uint8_t color8)
18971944 TextureImage::ScopedBindTexture texBind (mResizerImage, LOCAL_GL_TEXTURE0 );
18981945
18991946 ShaderProgramOGL *program =
1900- manager ->GetProgram (mResizerImage->GetShaderProgramType ());
1947+ aManager ->GetProgram (mResizerImage->GetShaderProgramType (), nullptr );
19011948 program->Activate ();
1902- program->SetLayerQuadRect (nsIntRect (bottomX - 15 ,
1903- bottomY - 15 ,
1904- 15 ,
1905- 15 ));
1949+ program->SetLayerQuadRect (nsIntRect (bottomX - resizeIndicatorWidth ,
1950+ bottomY - resizeIndicatorHeight ,
1951+ resizeIndicatorWidth ,
1952+ resizeIndicatorHeight ));
19061953 program->SetLayerTransform (gfx3DMatrix ());
19071954 program->SetLayerOpacity (1.0 );
19081955 program->SetRenderOffset (nsIntPoint (0 ,0 ));
19091956 program->SetTextureUnit (0 );
19101957
1911- manager->BindAndDrawQuad (program);
1958+ aManager->BindAndDrawQuad (program);
1959+ }
1960+
1961+ static void
1962+ DrawTopLeftCornerMask (CGContextRef aCtx, int aRadius)
1963+ {
1964+ CGContextSetRGBFillColor (aCtx, 1.0 , 1.0 , 1.0 , 1.0 );
1965+ CGContextFillEllipseInRect (aCtx, CGRectMake (0 , 0 , aRadius * 2 , aRadius * 2 ));
1966+ }
1967+
1968+ void
1969+ nsChildView::MaybeDrawRoundedBottomCorners (LayerManagerOGL* aManager, nsIntRect aRect)
1970+ {
1971+ if (![mView isKindOfClass: [ChildView class ]] ||
1972+ ![(ChildView*)mView hasRoundedBottomCorners ] ||
1973+ mFailedCornerMaskImage)
1974+ return ;
1975+
1976+ CGFloat cornerRadius = [(ChildView*)mView bottomCornerRadius ];
1977+ int devPixelCornerRadius = cornerRadius * BackingScaleFactor ();
1978+
1979+ if (!mCornerMaskImage) {
1980+ mCornerMaskImage =
1981+ aManager->gl ()->CreateTextureImage (nsIntSize (devPixelCornerRadius, devPixelCornerRadius),
1982+ gfxASurface::CONTENT_COLOR_ALPHA ,
1983+ LOCAL_GL_CLAMP_TO_EDGE ,
1984+ TextureImage::UseNearestFilter);
1985+
1986+ // Creation of texture images can fail.
1987+ if (!mCornerMaskImage)
1988+ return ;
1989+
1990+ nsIntRegion update (nsIntRect (0 , 0 , devPixelCornerRadius, devPixelCornerRadius));
1991+ gfxASurface *asurf = mCornerMaskImage->BeginUpdate (update);
1992+ if (!asurf) {
1993+ mCornerMaskImage = nullptr ;
1994+ return ;
1995+ }
1996+
1997+ // is the CairoSurface of a non QuartzSurface usable in the gfxQuartzSurface constructor ?
1998+ // answer seems to be NO (see comments on bug 675410
1999+ // this should not work for instance: new gfxQuartzSurface(asurf->CairoSurface(), false))
2000+ if (asurf->GetType () != gfxASurface::SurfaceTypeQuartz) {
2001+ NS_WARN_IF_FALSE (FALSE , " mCornerMaskImage's surface is not Quartz" );
2002+ mCornerMaskImage = nullptr ;
2003+ mFailedCornerMaskImage = true ;
2004+ return ;
2005+ }
2006+ nsRefPtr<gfxQuartzSurface> image = static_cast<gfxQuartzSurface*>(asurf);
2007+
2008+ DrawTopLeftCornerMask (image->GetCGContext (), devPixelCornerRadius);
2009+
2010+ mCornerMaskImage->EndUpdate ();
2011+ }
2012+
2013+ NS_ABORT_IF_FALSE (mCornerMaskImage, " Must have a texture allocated by now!" );
2014+
2015+ TextureImage::ScopedBindTexture texBind (mCornerMaskImage, LOCAL_GL_TEXTURE0 );
2016+
2017+ ShaderProgramOGL *program = aManager->GetProgram (mCornerMaskImage->GetShaderProgramType (), nullptr );
2018+ program->Activate ();
2019+ program->SetLayerQuadRect (nsIntRect (0 , 0 , // aRect.x, aRect.y,
2020+ devPixelCornerRadius,
2021+ devPixelCornerRadius));
2022+ program->SetLayerOpacity (1.0 );
2023+ program->SetRenderOffset (nsIntPoint (0 ,0 ));
2024+ program->SetTextureUnit (0 );
2025+
2026+ // Use operator destination in: multiply all 4 channels with source alpha.
2027+ aManager->gl ()->fBlendFuncSeparate (LOCAL_GL_ZERO , LOCAL_GL_SRC_ALPHA ,
2028+ LOCAL_GL_ZERO , LOCAL_GL_SRC_ALPHA );
2029+
2030+ // Draw; first bottom left, then bottom right.
2031+ program->SetLayerTransform (gfx3DMatrix::ScalingMatrix (1 , -1 , 1 ) *
2032+ gfx3DMatrix::Translation (0 , aRect.height , 0 ));
2033+ aManager->BindAndDrawQuad (program);
2034+ program->SetLayerTransform (gfx3DMatrix::ScalingMatrix (-1 , -1 , 1 ) *
2035+ gfx3DMatrix::Translation (aRect.width , aRect.height , 0 ));
2036+ aManager->BindAndDrawQuad (program);
2037+
2038+ // Reset blend mode.
2039+ aManager->gl ()->fBlendFuncSeparate (LOCAL_GL_ONE , LOCAL_GL_ONE_MINUS_SRC_ALPHA ,
2040+ LOCAL_GL_ONE , LOCAL_GL_ONE );
19122041}
19132042
19142043void
@@ -2577,6 +2706,13 @@ - (void)drawRect:(NSRect)aRect inContext:(CGContextRef)aContext alternate:(BOOL)
25772706 // Paints that come through here are triggered by something that Cocoa
25782707 // controls, for example by window resizing or window focus changes.
25792708
2709+ // Since our window is usually declared as opaque, the window's pixel
2710+ // buffer may now contain garbage which we need to prevent from reaching
2711+ // the screen. The only place where garbage can show is in the bottom
2712+ // corners - the rest of the window is covered by our OpenGL surface.
2713+ // So we need to clear the pixel buffer contents in the corners.
2714+ [self maybeClearBottomCorners ];
2715+
25802716 // Do GL composition and return.
25812717 [self drawUsingOpenGL ];
25822718 return ;
@@ -2728,6 +2864,47 @@ - (void)drawUsingOpenGLCallback
27282864 }
27292865}
27302866
2867+ - (BOOL )hasRoundedBottomCorners
2868+ {
2869+ return [[self window ] respondsToSelector: @selector (bottomCornerRounded )] &&
2870+ [[self window ] bottomCornerRounded ];
2871+ }
2872+
2873+ - (CGFloat)bottomCornerRadius
2874+ {
2875+ if (![self hasRoundedBottomCorners ])
2876+ return 0 .0f ;
2877+ NSView * borderView = [[[self window ] contentView ] superview ];
2878+ if (!borderView || ![borderView respondsToSelector: @selector (roundedCornerRadius )])
2879+ return 4 .0f ;
2880+ return [borderView roundedCornerRadius ];
2881+ }
2882+
2883+ // Accelerated windows have two NSSurfaces:
2884+ // (1) The window's pixel buffer in the back and
2885+ // (2) the OpenGL view in the front.
2886+ // These two surfaces are composited by the window manager. Drawing with the
2887+ // usual CGContext functions ends up in (1).
2888+ // When our window has rounded bottom corners, the OpenGL view has transparent
2889+ // pixels in the corners. In these places the contents of the window's pixel
2890+ // buffer can show through. So we need to make sure that the pixel buffer is
2891+ // transparent in the corners so that no garbage reaches the screen.
2892+ // The contents of the pixel buffer in the rest of the window don't matter
2893+ // because they're covered by the OpenGL view.
2894+ // Making the bottom corners transparent works even though our window is
2895+ // declared "opaque" (in the NSWindow's isOpaque method).
2896+ - (void )maybeClearBottomCorners
2897+ {
2898+ if (![self hasRoundedBottomCorners ])
2899+ return ;
2900+
2901+ int radius = [self bottomCornerRadius ];
2902+ int w = [self bounds ].size .width , h = [self bounds ].size .height ;
2903+ [[NSColor clearColor ] set ];
2904+ NSRectFill (NSMakeRect (0 , h - radius, radius, radius));
2905+ NSRectFill (NSMakeRect (w - radius, h - radius, radius, radius));
2906+ }
2907+
27312908- (void )releaseWidgets : (NSArray *)aWidgetArray
27322909{
27332910 if (!aWidgetArray) {
0 commit comments