feat: add annotation tool settings menus

This commit is contained in:
2026-07-08 16:20:51 +08:00
parent 7b290d9300
commit 18a9f53062
6 changed files with 587 additions and 149 deletions
+11 -6
View File
@@ -22,11 +22,12 @@ import (
// Annotation describes a user-drawn mark relative to the selected screenshot.
// Coordinates are logical pixels in the same coordinate system as the overlay.
type Annotation struct {
Tool string `json:"tool"`
Color string `json:"color"`
Points []Point `json:"points"`
Text string `json:"text,omitempty"`
FontSize float64 `json:"fontSize,omitempty"`
Tool string `json:"tool"`
Color string `json:"color"`
Points []Point `json:"points"`
Text string `json:"text,omitempty"`
StrokeWidth float64 `json:"strokeWidth,omitempty"`
FontSize float64 `json:"fontSize,omitempty"`
}
type Point struct {
@@ -66,7 +67,11 @@ func ApplyAnnotationsWithScale(pngBytes []byte, annotations []Annotation, scaleX
if err != nil {
c = color.RGBA{R: 59, G: 130, B: 246, A: 255}
}
width := int(math.Max(2, math.Round(3*strokeScale)))
strokeWidth := ann.StrokeWidth
if strokeWidth <= 0 {
strokeWidth = 3
}
width := int(math.Max(1, math.Round(strokeWidth*strokeScale)))
switch ann.Tool {
case "pen":
drawPolyline(dst, ann.Points, scaleX, scaleY, width, c)
+31
View File
@@ -140,3 +140,34 @@ func TestApplyAnnotationsWithScaleDrawsTextAtDevicePosition(t *testing.T) {
t.Fatalf("expected text to be drawn near scaled x=100, min red x=%d", minX)
}
}
func TestApplyAnnotationsUsesStrokeWidth(t *testing.T) {
src := image.NewRGBA(image.Rect(0, 0, 80, 80))
draw.Draw(src, src.Bounds(), &image.Uniform{C: color.White}, image.Point{}, draw.Src)
var buf bytes.Buffer
if err := png.Encode(&buf, src); err != nil {
t.Fatalf("encode source: %v", err)
}
out, err := ApplyAnnotations(buf.Bytes(), []Annotation{
{
Tool: "rect",
Color: "#ef4444",
Points: []Point{{X: 20, Y: 20}, {X: 60, Y: 60}},
StrokeWidth: 8,
},
}, 1)
if err != nil {
t.Fatalf("apply annotations: %v", err)
}
img, err := png.Decode(bytes.NewReader(out))
if err != nil {
t.Fatalf("decode output: %v", err)
}
got := color.RGBAModel.Convert(img.At(20, 24)).(color.RGBA)
if got.R < 180 || got.G > 180 || got.B > 180 {
t.Fatalf("expected thick rectangle stroke to cover y=24, got %#v", got)
}
}