fix: align text annotations in captures
This commit is contained in:
@@ -22,10 +22,11 @@ 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"`
|
||||
Tool string `json:"tool"`
|
||||
Color string `json:"color"`
|
||||
Points []Point `json:"points"`
|
||||
Text string `json:"text,omitempty"`
|
||||
FontSize float64 `json:"fontSize,omitempty"`
|
||||
}
|
||||
|
||||
type Point struct {
|
||||
@@ -35,12 +36,22 @@ type Point struct {
|
||||
|
||||
// ApplyAnnotations decodes a PNG, draws all annotations, and re-encodes it.
|
||||
func ApplyAnnotations(pngBytes []byte, annotations []Annotation, scale float64) ([]byte, error) {
|
||||
return ApplyAnnotationsWithScale(pngBytes, annotations, scale, scale)
|
||||
}
|
||||
|
||||
// ApplyAnnotationsWithScale decodes a PNG, draws all annotations using the
|
||||
// actual device-pixel scale of the captured image, and re-encodes it.
|
||||
func ApplyAnnotationsWithScale(pngBytes []byte, annotations []Annotation, scaleX, scaleY float64) ([]byte, error) {
|
||||
if len(annotations) == 0 {
|
||||
return pngBytes, nil
|
||||
}
|
||||
if scale <= 0 {
|
||||
scale = 1
|
||||
if scaleX <= 0 {
|
||||
scaleX = 1
|
||||
}
|
||||
if scaleY <= 0 {
|
||||
scaleY = scaleX
|
||||
}
|
||||
strokeScale := math.Max(scaleX, scaleY)
|
||||
|
||||
src, err := png.Decode(bytes.NewReader(pngBytes))
|
||||
if err != nil {
|
||||
@@ -55,16 +66,16 @@ func ApplyAnnotations(pngBytes []byte, annotations []Annotation, scale float64)
|
||||
if err != nil {
|
||||
c = color.RGBA{R: 59, G: 130, B: 246, A: 255}
|
||||
}
|
||||
width := int(math.Max(2, math.Round(3*scale)))
|
||||
width := int(math.Max(2, math.Round(3*strokeScale)))
|
||||
switch ann.Tool {
|
||||
case "pen":
|
||||
drawPolyline(dst, ann.Points, scale, width, c)
|
||||
drawPolyline(dst, ann.Points, scaleX, scaleY, width, c)
|
||||
case "rect":
|
||||
drawRectOutline(dst, ann.Points, scale, width, c)
|
||||
drawRectOutline(dst, ann.Points, scaleX, scaleY, width, c)
|
||||
case "ellipse":
|
||||
drawEllipseOutline(dst, ann.Points, scale, width, c)
|
||||
drawEllipseOutline(dst, ann.Points, scaleX, scaleY, width, c)
|
||||
case "text":
|
||||
drawTextAnnotation(dst, ann, scale, c)
|
||||
drawTextAnnotation(dst, ann, scaleX, scaleY, c)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,22 +103,22 @@ func parseHexColor(hex string) (color.RGBA, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func drawPolyline(img *image.RGBA, points []Point, scale float64, width int, c color.RGBA) {
|
||||
func drawPolyline(img *image.RGBA, points []Point, scaleX, scaleY float64, width int, c color.RGBA) {
|
||||
if len(points) == 1 {
|
||||
drawDot(img, scalePoint(points[0], scale), width, c)
|
||||
drawDot(img, scalePoint(points[0], scaleX, scaleY), width, c)
|
||||
return
|
||||
}
|
||||
for i := 1; i < len(points); i++ {
|
||||
drawLine(img, scalePoint(points[i-1], scale), scalePoint(points[i], scale), width, c)
|
||||
drawLine(img, scalePoint(points[i-1], scaleX, scaleY), scalePoint(points[i], scaleX, scaleY), width, c)
|
||||
}
|
||||
}
|
||||
|
||||
func drawRectOutline(img *image.RGBA, points []Point, scale float64, width int, c color.RGBA) {
|
||||
func drawRectOutline(img *image.RGBA, points []Point, scaleX, scaleY float64, width int, c color.RGBA) {
|
||||
if len(points) < 2 {
|
||||
return
|
||||
}
|
||||
a := scalePoint(points[0], scale)
|
||||
b := scalePoint(points[len(points)-1], scale)
|
||||
a := scalePoint(points[0], scaleX, scaleY)
|
||||
b := scalePoint(points[len(points)-1], scaleX, scaleY)
|
||||
x1, x2 := ordered(a.X, b.X)
|
||||
y1, y2 := ordered(a.Y, b.Y)
|
||||
drawLine(img, Point{X: x1, Y: y1}, Point{X: x2, Y: y1}, width, c)
|
||||
@@ -116,12 +127,12 @@ func drawRectOutline(img *image.RGBA, points []Point, scale float64, width int,
|
||||
drawLine(img, Point{X: x1, Y: y2}, Point{X: x1, Y: y1}, width, c)
|
||||
}
|
||||
|
||||
func drawEllipseOutline(img *image.RGBA, points []Point, scale float64, width int, c color.RGBA) {
|
||||
func drawEllipseOutline(img *image.RGBA, points []Point, scaleX, scaleY float64, width int, c color.RGBA) {
|
||||
if len(points) < 2 {
|
||||
return
|
||||
}
|
||||
a := scalePoint(points[0], scale)
|
||||
b := scalePoint(points[len(points)-1], scale)
|
||||
a := scalePoint(points[0], scaleX, scaleY)
|
||||
b := scalePoint(points[len(points)-1], scaleX, scaleY)
|
||||
x1, x2 := ordered(a.X, b.X)
|
||||
y1, y2 := ordered(a.Y, b.Y)
|
||||
rx := (x2 - x1) / 2
|
||||
@@ -180,8 +191,8 @@ func drawDot(img *image.RGBA, p Point, width int, c color.RGBA) {
|
||||
}
|
||||
}
|
||||
|
||||
func scalePoint(p Point, scale float64) Point {
|
||||
return Point{X: p.X * scale, Y: p.Y * scale}
|
||||
func scalePoint(p Point, scaleX, scaleY float64) Point {
|
||||
return Point{X: p.X * scaleX, Y: p.Y * scaleY}
|
||||
}
|
||||
|
||||
func ordered(a, b float64) (float64, float64) {
|
||||
@@ -191,7 +202,7 @@ func ordered(a, b float64) (float64, float64) {
|
||||
return b, a
|
||||
}
|
||||
|
||||
func drawTextAnnotation(img *image.RGBA, ann Annotation, scale float64, c color.RGBA) {
|
||||
func drawTextAnnotation(img *image.RGBA, ann Annotation, scaleX, scaleY float64, c color.RGBA) {
|
||||
if len(ann.Points) == 0 {
|
||||
return
|
||||
}
|
||||
@@ -199,16 +210,20 @@ func drawTextAnnotation(img *image.RGBA, ann Annotation, scale float64, c color.
|
||||
if text == "" {
|
||||
return
|
||||
}
|
||||
face := annotationFontFace(20 * scale)
|
||||
fontSize := ann.FontSize
|
||||
if fontSize <= 0 {
|
||||
fontSize = 20
|
||||
}
|
||||
face := annotationFontFace(fontSize * scaleY)
|
||||
if face == nil {
|
||||
face = basicfont.Face7x13
|
||||
}
|
||||
|
||||
origin := scalePoint(ann.Points[0], scale)
|
||||
origin := scalePoint(ann.Points[0], scaleX, scaleY)
|
||||
metrics := face.Metrics()
|
||||
lineHeight := metrics.Height
|
||||
if lineHeight <= 0 {
|
||||
lineHeight = fixed.I(int(math.Ceil(24 * scale)))
|
||||
lineHeight = fixed.I(int(math.Ceil(fontSize * 1.2 * scaleY)))
|
||||
}
|
||||
d := &xfont.Drawer{
|
||||
Dst: img,
|
||||
|
||||
Reference in New Issue
Block a user