public static NSImage ToTransformedCorners(NSImage source, double topLeftCornerSize, double topRightCornerSize, double bottomLeftCornerSize, double bottomRightCornerSize, CornerTransformType cornersTransformType, double cropWidthRatio, double cropHeightRatio) { double sourceWidth = source.CGImage.Width; double sourceHeight = source.CGImage.Height; double desiredWidth = sourceWidth; double desiredHeight = sourceHeight; double desiredRatio = cropWidthRatio / cropHeightRatio; double currentRatio = sourceWidth / sourceHeight; if (currentRatio > desiredRatio) { desiredWidth = (cropWidthRatio * sourceHeight / cropHeightRatio); } else if (currentRatio < desiredRatio) { desiredHeight = (cropHeightRatio * sourceWidth / cropWidthRatio); } topLeftCornerSize = topLeftCornerSize * (desiredWidth + desiredHeight) / 2 / 100; topRightCornerSize = topRightCornerSize * (desiredWidth + desiredHeight) / 2 / 100; bottomLeftCornerSize = bottomLeftCornerSize * (desiredWidth + desiredHeight) / 2 / 100; bottomRightCornerSize = bottomRightCornerSize * (desiredWidth + desiredHeight) / 2 / 100; float cropX = (float)((sourceWidth - desiredWidth) / 2); float cropY = (float)((sourceHeight - desiredHeight) / 2); var colorSpace = CGColorSpace.CreateDeviceRGB(); const int bytesPerPixel = 4; int width = (int)desiredWidth; int height = (int)desiredHeight; var bytes = new byte[width * height * bytesPerPixel]; int bytesPerRow = bytesPerPixel * width; const int bitsPerComponent = 8; using (var context = new CGBitmapContext(bytes, width, height, bitsPerComponent, bytesPerRow, colorSpace, CGBitmapFlags.PremultipliedLast | CGBitmapFlags.ByteOrder32Big)) { context.BeginPath(); using (var path = new NSBezierPath()) { // TopLeft if (cornersTransformType.HasFlag(CornerTransformType.TopLeftCut)) { path.MoveTo(new CGPoint(0, topLeftCornerSize)); path.LineTo(new CGPoint(topLeftCornerSize, 0)); } else if (cornersTransformType.HasFlag(CornerTransformType.TopLeftRounded)) { path.MoveTo(new CGPoint(0, topLeftCornerSize)); path.QuadCurveToPoint(new CGPoint(topLeftCornerSize, 0), new CGPoint(0, 0)); } else { path.MoveTo(new CGPoint(0, 0)); } // TopRight if (cornersTransformType.HasFlag(CornerTransformType.TopRightCut)) { path.LineTo(new CGPoint(desiredWidth - topRightCornerSize, 0)); path.LineTo(new CGPoint(desiredWidth, topRightCornerSize)); } else if (cornersTransformType.HasFlag(CornerTransformType.TopRightRounded)) { path.LineTo(new CGPoint(desiredWidth - topRightCornerSize, 0)); path.QuadCurveToPoint(new CGPoint(desiredWidth, topRightCornerSize), new CGPoint(desiredWidth, 0)); } else { path.LineTo(new CGPoint(desiredWidth, 0)); } // BottomRight if (cornersTransformType.HasFlag(CornerTransformType.BottomRightCut)) { path.LineTo(new CGPoint(desiredWidth, desiredHeight - bottomRightCornerSize)); path.LineTo(new CGPoint(desiredWidth - bottomRightCornerSize, desiredHeight)); } else if (cornersTransformType.HasFlag(CornerTransformType.BottomRightRounded)) { path.LineTo(new CGPoint(desiredWidth, desiredHeight - bottomRightCornerSize)); path.QuadCurveToPoint(new CGPoint(desiredWidth - bottomRightCornerSize, desiredHeight), new CGPoint(desiredWidth, desiredHeight)); } else { path.LineTo(new CGPoint(desiredWidth, desiredHeight)); } // BottomLeft if (cornersTransformType.HasFlag(CornerTransformType.BottomLeftCut)) { path.LineTo(new CGPoint(bottomLeftCornerSize, desiredHeight)); path.LineTo(new CGPoint(0, desiredHeight - bottomLeftCornerSize)); } else if (cornersTransformType.HasFlag(CornerTransformType.BottomLeftRounded)) { path.LineTo(new CGPoint(bottomLeftCornerSize, desiredHeight)); path.QuadCurveToPoint(new CGPoint(0, desiredHeight - bottomLeftCornerSize), new CGPoint(0, desiredHeight)); } else { path.LineTo(new CGPoint(0, desiredHeight)); } path.ClosePath(); context.AddPath(path.ToCGPath()); context.Clip(); } var drawRect = new CGRect(-cropX, -cropY, sourceWidth, sourceHeight); context.DrawImage(drawRect, source.CGImage); using (var output = context.ToImage()) { return(new NSImage(output, CGSize.Empty)); } } }