Skip to content
This repository was archived by the owner on Jul 9, 2025. It is now read-only.

Commit 828da5e

Browse files
author
Andre Reinald
committed
Bug 675410 - Fix bottom rounded corners, hopefully in the same openGL context as the rest of the window. r=mstange, r=bgirard
1 parent d2a420e commit 828da5e

4 files changed

Lines changed: 211 additions & 26 deletions

File tree

widget/cocoa/nsChildView.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,9 @@ class nsChildView : public nsBaseWidget,
558558
return widget.forget();
559559
}
560560

561+
void MaybeDrawResizeIndicator(mozilla::layers::LayerManagerOGL* aManager, nsIntRect aRect);
562+
void MaybeDrawRoundedBottomCorners(mozilla::layers::LayerManagerOGL* aManager, nsIntRect aRect);
563+
561564
nsIWidget* GetWidgetForListenerEvents();
562565

563566
protected:
@@ -577,6 +580,7 @@ class nsChildView : public nsBaseWidget,
577580

578581
nsRefPtr<gfxASurface> mTempThebesSurface;
579582
nsRefPtr<mozilla::gl::TextureImage> mResizerImage;
583+
nsRefPtr<mozilla::gl::TextureImage> mCornerMaskImage;
580584

581585
nsRefPtr<gfxQuartzSurface> mTitlebarSurf;
582586
gfxSize mTitlebarSize;
@@ -587,6 +591,8 @@ class nsChildView : public nsBaseWidget,
587591
// ** We'll need to reinitialize this if the backing resolution changes. **
588592
CGFloat mBackingScaleFactor;
589593

594+
bool mFailedResizerImage;
595+
bool mFailedCornerMaskImage;
590596
bool mVisible;
591597
bool mDrawing;
592598
bool mPluginDrawing;

widget/cocoa/nsChildView.mm

Lines changed: 201 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
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"
@@ -72,7 +73,6 @@
7273
using namespace mozilla::layers;
7374
using namespace mozilla::gl;
7475
using namespace mozilla::widget;
75-
using namespace mozilla;
7676

7777
#undef DEBUG_UPDATE
7878
#undef INVALIDATE_DEBUGGING // flash areas as they are invalidated
@@ -89,6 +89,9 @@
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, &region);
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+
18201864
static void
18211865
DrawResizer(CGContextRef aCtx)
18221866
{
@@ -1851,39 +1895,42 @@ inline uint16_t COLOR8TOCOLOR16(uint8_t color8)
18511895
}
18521896

18531897
void
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

19142043
void
@@ -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) {

widget/cocoa/nsCocoaWindow.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,10 @@ typedef struct _nsCocoaWindowList {
108108

109109
// If we set the window's stylemask to be textured, the corners on the bottom of
110110
// the window are rounded by default. We use this private method to make
111-
// the corners square again, a la Safari.
111+
// the corners square again, a la Safari. Starting with 10.7, all windows have
112+
// rounded bottom corners, so this call doesn't have any effect there.
112113
- (void)setBottomCornerRounded:(BOOL)rounded;
114+
- (BOOL)bottomCornerRounded;
113115

114116
@end
115117

widget/cocoa/nsCocoaWindow.mm

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2813,7 +2813,7 @@ - (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle back
28132813
// setBottomCornerRounded: is a private API call, so we check to make sure
28142814
// we respond to it just in case.
28152815
if ([self respondsToSelector:@selector(setBottomCornerRounded:)])
2816-
[self setBottomCornerRounded:NO];
2816+
[self setBottomCornerRounded:nsCocoaFeatures::OnLionOrLater()];
28172817

28182818
[self setAutorecalculatesContentBorderThickness:NO forEdge:NSMaxYEdge];
28192819
[self setContentBorderThickness:0.0f forEdge:NSMaxYEdge];

0 commit comments

Comments
 (0)