public void CanDrawRoundRectDifference() { using var outer = new SKRoundRect(SKRect.Create(50, 50, 200, 200), 20); using var inner = new SKRoundRect(SKRect.Create(100, 100, 100, 100), 20); using var paint = new SKPaint(); SKColor[] diff; using (var bmp = new SKBitmap(new SKImageInfo(300, 300))) using (var canvas = new SKCanvas(bmp)) { canvas.Clear(SKColors.Transparent); canvas.DrawRoundRectDifference(outer, inner, paint); diff = bmp.Pixels; } SKColor[] paths; using (var bmp = new SKBitmap(new SKImageInfo(300, 300))) using (var canvas = new SKCanvas(bmp)) using (var path = new SKPath()) { canvas.Clear(SKColors.Transparent); path.AddRoundRect(outer); path.AddRoundRect(inner); path.FillType = SKPathFillType.EvenOdd; canvas.DrawPath(path, paint); paths = bmp.Pixels; } Assert.Equal(paths, diff); }
/// <inheritdoc /> public void DrawRectangle(IBrush brush, IPen pen, RoundedRect rect, BoxShadows boxShadows = default) { if (rect.Rect.Height <= 0 || rect.Rect.Width <= 0) { return; } // Arbitrary chosen values // On OSX Skia breaks OpenGL context when asked to draw, e. g. (0, 0, 623, 6666600) rect if (rect.Rect.Height > 8192 || rect.Rect.Width > 8192) { boxShadows = default; } var rc = rect.Rect.ToSKRect(); var isRounded = rect.IsRounded; var needRoundRect = rect.IsRounded || (boxShadows.HasInsetShadows); using var skRoundRect = needRoundRect ? new SKRoundRect() : null; if (needRoundRect) { skRoundRect.SetRectRadii(rc, new[] { rect.RadiiTopLeft.ToSKPoint(), rect.RadiiTopRight.ToSKPoint(), rect.RadiiBottomRight.ToSKPoint(), rect.RadiiBottomLeft.ToSKPoint(), }); } foreach (var boxShadow in boxShadows) { if (!boxShadow.IsEmpty && !boxShadow.IsInset) { using (var shadow = BoxShadowFilter.Create(_boxShadowPaint, boxShadow, _currentOpacity)) { var spread = (float)boxShadow.Spread; if (boxShadow.IsInset) { spread = -spread; } Canvas.Save(); if (isRounded) { using var shadowRect = new SKRoundRect(skRoundRect); if (spread != 0) { shadowRect.Inflate(spread, spread); } Canvas.ClipRoundRect(skRoundRect, shadow.ClipOperation, true); var oldTransform = Transform; Transform = oldTransform * Matrix.CreateTranslation(boxShadow.OffsetX, boxShadow.OffsetY); Canvas.DrawRoundRect(shadowRect, shadow.Paint); Transform = oldTransform; } else { var shadowRect = rc; if (spread != 0) { shadowRect.Inflate(spread, spread); } Canvas.ClipRect(rc, shadow.ClipOperation); var oldTransform = Transform; Transform = oldTransform * Matrix.CreateTranslation(boxShadow.OffsetX, boxShadow.OffsetY); Canvas.DrawRect(shadowRect, shadow.Paint); Transform = oldTransform; } Canvas.Restore(); } } } if (brush != null) { using (var paint = CreatePaint(_fillPaint, brush, rect.Rect.Size)) { if (isRounded) { Canvas.DrawRoundRect(skRoundRect, paint.Paint); } else { Canvas.DrawRect(rc, paint.Paint); } } } foreach (var boxShadow in boxShadows) { if (!boxShadow.IsEmpty && boxShadow.IsInset) { using (var shadow = BoxShadowFilter.Create(_boxShadowPaint, boxShadow, _currentOpacity)) { var spread = (float)boxShadow.Spread; var offsetX = (float)boxShadow.OffsetX; var offsetY = (float)boxShadow.OffsetY; var outerRect = AreaCastingShadowInHole(rc, (float)boxShadow.Blur, spread, offsetX, offsetY); Canvas.Save(); using var shadowRect = new SKRoundRect(skRoundRect); if (spread != 0) { shadowRect.Deflate(spread, spread); } Canvas.ClipRoundRect(skRoundRect, shadow.ClipOperation, true); var oldTransform = Transform; Transform = oldTransform * Matrix.CreateTranslation(boxShadow.OffsetX, boxShadow.OffsetY); using (var outerRRect = new SKRoundRect(outerRect)) Canvas.DrawRoundRectDifference(outerRRect, shadowRect, shadow.Paint); Transform = oldTransform; Canvas.Restore(); } } } if (pen?.Brush != null) { using (var paint = CreatePaint(_strokePaint, pen, rect.Rect.Size)) { if (isRounded) { Canvas.DrawRoundRect(skRoundRect, paint.Paint); } else { Canvas.DrawRect(rc, paint.Paint); } } } }