diff --git a/Sources/Kapture/CaptureController.swift b/Sources/Kapture/CaptureController.swift index 32f2ea1..95e0691 100644 --- a/Sources/Kapture/CaptureController.swift +++ b/Sources/Kapture/CaptureController.swift @@ -301,8 +301,11 @@ final class CaptureController: NSObject { } return event } - // Drag: grow the marquee + // Drag: grow the marquee — but only after real movement, so a + // click's micro-jitter keeps the hovered window eligible. guard let start = marqueeStart else { return nil } + let distance = hypot(appKitPoint.x - start.x, appKitPoint.y - start.y) + guard distance >= 4 else { return nil } let rect = NSRect( x: min(start.x, appKitPoint.x), y: min(start.y, appKitPoint.y), @@ -443,28 +446,43 @@ final class ScreenCapture { let detected = Self.detectCornerRadius(of: result) let radius = min(max((detected.map { $0 * 1.23 + 2.6 }) ?? (10 * scaleX), 4), 40) CaptureController.log("corners: detected=\(detected.map { String(format: "%.1f", $0) } ?? "nil") using=\(Int(radius))px") + // Anti-aliased gray mask: supersample 3x, downsample, then draw + // the image through it ONCE at 1x (no content resampling blur). let ss = 3 let W = result.width * ss, H = result.height * ss - guard let hiCtx = CGContext( + guard let hiMask = CGContext( data: nil, width: W, height: H, bitsPerComponent: 8, bytesPerRow: 0, - space: CGColorSpaceCreateDeviceRGB(), - bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue + space: CGColorSpaceCreateDeviceGray(), + bitmapInfo: CGImageAlphaInfo.none.rawValue ) else { return result } - hiCtx.addPath(CGPath(roundedRect: CGRect(x: 0, y: 0, width: W, height: H), cornerWidth: radius * CGFloat(ss), cornerHeight: radius * CGFloat(ss), transform: nil)) - hiCtx.clip() - hiCtx.interpolationQuality = .high - hiCtx.draw(result, in: CGRect(x: 0, y: 0, width: W, height: H)) - guard let hi = hiCtx.makeImage(), - let downCtx = CGContext( + hiMask.setFillColor(CGColor(gray: 1, alpha: 1)) + hiMask.fill(CGRect(x: 0, y: 0, width: W, height: H)) + hiMask.setFillColor(CGColor(gray: 0, alpha: 1)) + let cornerPath = CGMutablePath() + cornerPath.addRect(CGRect(x: 0, y: 0, width: W, height: H)) + cornerPath.addRoundedRect(in: CGRect(x: 0, y: 0, width: W, height: H), cornerWidth: radius * CGFloat(ss), cornerHeight: radius * CGFloat(ss), transform: .identity) + hiMask.addPath(cornerPath) + hiMask.fillPath(using: .evenOdd) + guard let hiMaskImg = hiMask.makeImage(), + let downMask = CGContext( + data: nil, width: result.width, height: result.height, + bitsPerComponent: 8, bytesPerRow: 0, + space: CGColorSpaceCreateDeviceGray(), + bitmapInfo: CGImageAlphaInfo.none.rawValue + ) else { return result } + downMask.interpolationQuality = .high + downMask.draw(hiMaskImg, in: CGRect(x: 0, y: 0, width: result.width, height: result.height)) + guard let mask1x = downMask.makeImage(), + let ctx = CGContext( data: nil, width: result.width, height: result.height, bitsPerComponent: 8, bytesPerRow: 0, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.premultipliedLast.rawValue ) else { return result } - downCtx.interpolationQuality = .high - downCtx.draw(hi, in: CGRect(x: 0, y: 0, width: result.width, height: result.height)) - if let masked = downCtx.makeImage() { + ctx.clip(to: CGRect(x: 0, y: 0, width: result.width, height: result.height), mask: mask1x) + ctx.draw(result, in: CGRect(x: 0, y: 0, width: result.width, height: result.height)) + if let masked = ctx.makeImage() { result = masked } } diff --git a/build-app.sh b/build-app.sh index 3676233..04ab41e 100755 --- a/build-app.sh +++ b/build-app.sh @@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST' CFBundleIconFile Icon CFBundleShortVersionString - 1.8.5 + 1.8.6 CFBundleVersion - 85 + 86 LSUIElement NSHighResolutionCapable