Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ go 1.23.0

require (
github.com/corona10/goimagehash v1.1.0
github.com/disintegration/imaging v1.6.2
github.com/flopp/go-findfont v0.1.0
github.com/fogleman/gg v1.3.0
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
Expand Down
4 changes: 0 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
github.com/corona10/goimagehash v1.1.0 h1:teNMX/1e+Wn/AYSbLHX8mj+mF9r60R1kBeqE9MkoYwI=
github.com/corona10/goimagehash v1.1.0/go.mod h1:VkvE0mLn84L4aF8vCb6mafVajEb6QYMHl2ZJLn0mOGI=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/goccy/go-findfont v0.0.0-20250109093214-c2e12b298c75 h1:XIuIPArJ/7VuKj7uygrjl7yBYP+sTa58ljJzOxU4qDc=
Expand All @@ -12,9 +10,7 @@ github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/tetratelabs/wazero v1.10.1 h1:2DugeJf6VVk58KTPszlNfeeN8AhhpwcZqkJj2wwFuH8=
github.com/tetratelabs/wazero v1.10.1/go.mod h1:DRm5twOQ5Gr1AoEdSi0CLjDQF1J9ZAuyqFIjl1KKfQU=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
24 changes: 22 additions & 2 deletions gvc/image_renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import (
"strings"
"sync"

"github.com/disintegration/imaging"
"github.com/flopp/go-findfont"
"github.com/fogleman/gg"
"github.com/goccy/go-graphviz/internal/wasm"
"github.com/golang/freetype/truetype"
"golang.org/x/image/draw"
"golang.org/x/image/font"
"golang.org/x/image/font/gofont/goregular"
"golang.org/x/image/font/opentype"
Expand Down Expand Up @@ -395,7 +395,7 @@ func (r *ImageRenderer) LoadImage(ctx context.Context, job *Job, shape *UserShap
bottomRightY := bf.UR().Y()
width := bottomRightX - topLeftX
height := bottomRightY - topLeftY
img = imaging.Resize(img, int(width), int(height), imaging.Lanczos)
img = resizeImage(img, int(width), int(height))
xPAD := defaultXPAD / 2.0
yPAD := defaultYPAD / 2.0
posX := (topLeftX + xPAD) * job.Scale().X()
Expand All @@ -410,6 +410,26 @@ func (r *ImageRenderer) LoadImage(ctx context.Context, job *Job, shape *UserShap
return nil
}

// resizeImage scales img to width x height with a Catmull-Rom kernel. A
// zero width or height is derived from the other one so the aspect ratio
// is preserved; when both are zero the image is returned as is.
func resizeImage(img image.Image, width, height int) image.Image {
srcBounds := img.Bounds()
srcW, srcH := srcBounds.Dx(), srcBounds.Dy()
if srcW == 0 || srcH == 0 || (width <= 0 && height <= 0) {
return img
}
if width <= 0 {
width = max(1, int(float64(height)*float64(srcW)/float64(srcH)+0.5))
}
if height <= 0 {
height = max(1, int(float64(width)*float64(srcH)/float64(srcW)+0.5))
}
dst := image.NewNRGBA(image.Rect(0, 0, width, height))
draw.CatmullRom.Scale(dst, dst.Bounds(), img, srcBounds, draw.Src, nil)
return dst
}

type FontLoader func(ctx context.Context, job *Job, font *TextFont) (font.Face, error)

var (
Expand Down
44 changes: 44 additions & 0 deletions gvc/image_renderer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package gvc

import (
"image"
"image/color"
"testing"
)

func TestResizeImage(t *testing.T) {
src := image.NewNRGBA(image.Rect(0, 0, 40, 20))
for y := 0; y < 20; y++ {
for x := 0; x < 40; x++ {
src.Set(x, y, color.NRGBA{R: 200, G: 30, B: 30, A: 255})
}
}

tests := []struct {
name string
width int
height int
wantW, wantH int
}{
{name: "both dimensions", width: 10, height: 10, wantW: 10, wantH: 10},
{name: "width only keeps the aspect ratio", width: 20, height: 0, wantW: 20, wantH: 10},
{name: "height only keeps the aspect ratio", width: 0, height: 5, wantW: 10, wantH: 5},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := resizeImage(src, tt.width, tt.height)
b := got.Bounds()
if b.Dx() != tt.wantW || b.Dy() != tt.wantH {
t.Fatalf("size = %dx%d, want %dx%d", b.Dx(), b.Dy(), tt.wantW, tt.wantH)
}
c := color.NRGBAModel.Convert(got.At(b.Min.X+b.Dx()/2, b.Min.Y+b.Dy()/2)).(color.NRGBA)
if c.R < 190 || c.A != 255 {
t.Fatalf("center pixel = %+v, want the source colour", c)
}
})
}

if got := resizeImage(src, 0, 0); got != image.Image(src) {
t.Fatal("a zero size must return the image unchanged")
}
}