diff options
-rw-r--r-- | cocoa/PlatCocoa.mm | 98 |
1 files changed, 84 insertions, 14 deletions
diff --git a/cocoa/PlatCocoa.mm b/cocoa/PlatCocoa.mm index a58b2c9c0..1a4e591bf 100644 --- a/cocoa/PlatCocoa.mm +++ b/cocoa/PlatCocoa.mm @@ -593,7 +593,56 @@ void SurfaceImpl::RoundedRectangle(PRectangle rc, ColourDesired fore, ColourDesi CGContextDrawPath( gc, kCGPathFillStroke ); } -void Scintilla::SurfaceImpl::AlphaRectangle(PRectangle rc, int /*cornerSize*/, ColourDesired fill, int alphaFill, +// DrawChamferedRectangle is a helper function for AlphaRectangle that either fills or strokes a +// rectangle with its corners chamfered at 45 degrees. +static void DrawChamferedRectangle(CGContextRef gc, PRectangle rc, int cornerSize, CGPathDrawingMode mode) { + // Points go clockwise, starting from just below the top left + CGPoint corners[4][2] = + { + { + { rc.left, rc.top + cornerSize }, + { rc.left + cornerSize, rc.top }, + }, + { + { rc.right - cornerSize - 1, rc.top }, + { rc.right - 1, rc.top + cornerSize }, + }, + { + { rc.right - 1, rc.bottom - cornerSize - 1 }, + { rc.right - cornerSize - 1, rc.bottom - 1 }, + }, + { + { rc.left + cornerSize, rc.bottom - 1 }, + { rc.left, rc.bottom - cornerSize - 1 }, + }, + }; + + // Align the points in the middle of the pixels + for( int i = 0; i < 4; ++ i ) + { + for( int j = 0; j < 2; ++ j ) + { + corners[i][j].x += 0.5; + corners[i][j].y += 0.5; + } + } + + // Move to the last point to begin the path + CGContextBeginPath( gc ); + CGContextMoveToPoint( gc, corners[3][1].x, corners[3][1].y ); + + for ( int i = 0; i < 4; ++ i ) + { + CGContextAddLineToPoint( gc, corners[i][0].x, corners[i][0].y ); + CGContextAddLineToPoint( gc, corners[i][1].x, corners[i][1].y ); + } + + // Close the path to enclose it for stroking and for filling, then draw it + CGContextClosePath( gc ); + CGContextDrawPath( gc, mode ); +} + +void Scintilla::SurfaceImpl::AlphaRectangle(PRectangle rc, int cornerSize, ColourDesired fill, int alphaFill, ColourDesired outline, int alphaOutline, int /*flags*/) { if ( gc ) { @@ -602,21 +651,42 @@ void Scintilla::SurfaceImpl::AlphaRectangle(PRectangle rc, int /*cornerSize*/, C rc.right = lround(rc.right); // Set the Fill color to match CGContextSetRGBFillColor( gc, fill.GetRed() / 255.0, fill.GetGreen() / 255.0, fill.GetBlue() / 255.0, alphaFill / 255.0 ); + CGContextSetRGBStrokeColor( gc, outline.GetRed() / 255.0, outline.GetGreen() / 255.0, outline.GetBlue() / 255.0, alphaOutline / 255.0 ); PRectangle rcFill = rc; - if ((fill == outline) && (alphaFill == alphaOutline)) { - // Optimization for simple case - CGRect rect = PRectangleToCGRect( rcFill ); - CGContextFillRect( gc, rect ); + if (cornerSize == 0) { + // A simple rectangle, no rounded corners + if ((fill == outline) && (alphaFill == alphaOutline)) { + // Optimization for simple case + CGRect rect = PRectangleToCGRect( rcFill ); + CGContextFillRect( gc, rect ); + } else { + rcFill.left += 1.0; + rcFill.top += 1.0; + rcFill.right -= 1.0; + rcFill.bottom -= 1.0; + CGRect rect = PRectangleToCGRect( rcFill ); + CGContextFillRect( gc, rect ); + CGContextAddRect( gc, CGRectMake( rc.left + 0.5, rc.top + 0.5, rc.Width() - 1, rc.Height() - 1 ) ); + CGContextStrokePath( gc ); + } } else { - rcFill.left += 1.0; - rcFill.top += 1.0; - rcFill.right -= 1.0; - rcFill.bottom -= 1.0; - CGRect rect = PRectangleToCGRect( rcFill ); - CGContextFillRect( gc, rect ); - CGContextSetRGBStrokeColor( gc, outline.GetRed() / 255.0, outline.GetGreen() / 255.0, outline.GetBlue() / 255.0, alphaOutline / 255.0 ); - CGContextAddRect( gc, CGRectMake( rc.left + 0.5, rc.top + 0.5, rc.Width() - 1, rc.Height() - 1 ) ); - CGContextStrokePath( gc ); + // Approximate rounded corners with 45 degree chamfers. + // Drawing real circular arcs often leaves some over- or under-drawn pixels. + if ((fill == outline) && (alphaFill == alphaOutline)) { + // Specializing this case avoids a few stray light/dark pixels in corners. + rcFill.left -= 0.5; + rcFill.top -= 0.5; + rcFill.right += 0.5; + rcFill.bottom += 0.5; + DrawChamferedRectangle( gc, rcFill, cornerSize, kCGPathFill ); + } else { + rcFill.left += 0.5; + rcFill.top += 0.5; + rcFill.right -= 0.5; + rcFill.bottom -= 0.5; + DrawChamferedRectangle( gc, rcFill, cornerSize-1, kCGPathFill ); + DrawChamferedRectangle( gc, rc, cornerSize, kCGPathStroke ); + } } } } |