Make exported stroke thickness match the preview

Strokes were sized in view points but exported in image pixels, so on
retina captures the saved/copied annotation looked several times thinner.
Stroke widths are now converted into image pixels when drawn and scaled
back for the preview, keeping preview and export identical.

💘 Generated with Crush

Assisted-by: Crush:deepseek-v4-flash-0731
This commit is contained in:
2026-09-23 17:28:38 +08:00
parent 195c6a9b1f
commit 26700c59f4
2 changed files with 11 additions and 9 deletions
+9 -7
View File
@@ -195,14 +195,15 @@ final class CaptureModel: ObservableObject {
return rep.cgImage ?? image return rep.cgImage ?? image
} }
func addStroke(_ points: [CGPoint]) { /// `widthPx` is the stroke thickness in IMAGE PIXELS (the preview
/// converts the slider value so exports match what was drawn).
func addStroke(_ points: [CGPoint], widthPx: CGFloat) {
guard points.count > 1 else { return } guard points.count > 1 else { return }
if tool == .redact { if tool == .redact {
// Redaction: a filled black bar from start to end, as thick as the // Redaction: a filled black bar from start to end.
// current stroke width (drawn as a rect for clean edges). strokes.append(Stroke(points: points, width: max(widthPx, 12), color: .black, isRedaction: true))
strokes.append(Stroke(points: points, width: max(strokeWidth, 12), color: .black, isRedaction: true))
} else { } else {
strokes.append(Stroke(points: points, width: strokeWidth, color: drawColor)) strokes.append(Stroke(points: points, width: widthPx, color: drawColor))
} }
redoStack = [] redoStack = []
} }
@@ -575,7 +576,8 @@ struct ScreenshotPane: View {
for point in stroke.points.dropFirst() { for point in stroke.points.dropFirst() {
path.addLine(to: CGPoint(x: origin.x + point.x * scale, y: origin.y + point.y * scale)) path.addLine(to: CGPoint(x: origin.x + point.x * scale, y: origin.y + point.y * scale))
} }
context.stroke(path, with: .color(Color(nsColor: stroke.color)), lineWidth: stroke.width) // stroke.width is in image pixels; scale to view points
context.stroke(path, with: .color(Color(nsColor: stroke.color)), lineWidth: stroke.width * scale)
} }
} }
} }
@@ -636,7 +638,7 @@ struct ScreenshotPane: View {
.onEnded { _ in .onEnded { _ in
if model.tool != .select { if model.tool != .select {
if currentStroke.count > 1 { if currentStroke.count > 1 {
model.addStroke(currentStroke) model.addStroke(currentStroke, widthPx: model.strokeWidth / max(scale, 0.01))
} }
currentStroke = [] currentStroke = []
} else { } else {
+2 -2
View File
@@ -108,9 +108,9 @@ cat > "$APP/Contents/Info.plist" <<'PLIST'
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string>Icon</string> <string>Icon</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.9.1</string> <string>1.9.2</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>91</string> <string>92</string>
<key>LSUIElement</key> <key>LSUIElement</key>
<true/> <true/>
<key>NSHighResolutionCapable</key> <key>NSHighResolutionCapable</key>