Skip to content
Merged
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
47 changes: 47 additions & 0 deletions vips/image_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,53 @@ func TestNewImageFromGoImage(t *testing.T) {
assert.Equal(t, []float64{255, 0, 0, 255}, pixel)
}

// TestGetPoint_RepeatedCalls verifies that GetPoint returns a Go-owned slice
// whose data remains valid across multiple calls. Previously, vipsGetPoint
// returned a slice backed by C memory and deferred a g_free on a nil pointer
// (captured before the C call), leaking the allocation.
func TestGetPoint_RepeatedCalls(t *testing.T) {
require.NoError(t, Startup(nil))

img, err := NewImageFromFile(resources + "png-24bit.png")
require.NoError(t, err)
defer img.Close()

// Call GetPoint multiple times and retain results.
// If the returned slice were backed by C memory, earlier results could
// be corrupted by later calls.
var results [][]float64
for i := 0; i < 10; i++ {
p, err := img.GetPoint(10, 10)
require.NoError(t, err)
results = append(results, p)
}

// All results should be identical and independently owned.
for i, p := range results {
assert.Equal(t, 3, len(p), "iteration %d", i)
assert.Equal(t, 255.0, p[0], "iteration %d", i)
assert.Equal(t, 255.0, p[1], "iteration %d", i)
assert.Equal(t, 255.0, p[2], "iteration %d", i)
}
}

// TestGetAsString_RoundTrip verifies that GetAsString correctly returns
// string metadata. Previously, vipsImageGetAsString deferred freeCString
// on a nil pointer (captured before the C call), leaking the allocated string.
func TestGetAsString_RoundTrip(t *testing.T) {
require.NoError(t, Startup(nil))

img, err := NewImageFromFile(resources + "jpg-24bit.jpg")
require.NoError(t, err)
defer img.Close()

img.SetString("test-field", "hello-world")

// GetAsString returns a formatted version of the field value.
result := img.GetAsString("test-field")
assert.Contains(t, result, "hello-world")
}

func TestNewImageFromGoImage_RoundTrip(t *testing.T) {
require.NoError(t, Startup(nil))

Expand Down
10 changes: 6 additions & 4 deletions vips/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,16 @@ func vipsFindTrim(in *C.VipsImage, threshold float64, backgroundColor *Color) (i
func vipsGetPoint(in *C.VipsImage, n int, x int, y int) ([]float64, error) {
incOpCounter("getpoint")
var out *C.double
defer gFreePointer(unsafe.Pointer(out))

if err := C.getpoint(in, &out, C.int(n), C.int(x), C.int(y)); err != 0 {
return nil, handleVipsError()
}

// maximum n is 4
return (*[4]float64)(unsafe.Pointer(out))[:n:n], nil
// Copy from C memory into a Go slice, then free the C allocation.
result := make([]float64, n)
copy(result, (*[4]float64)(unsafe.Pointer(out))[:n:n])
gFreePointer(unsafe.Pointer(out))
return result, nil
}

// https://www.libvips.org/API/current/libvips-arithmetic.html#vips-min
Expand Down Expand Up @@ -866,7 +868,7 @@ func vipsImageGetAsString(in *C.VipsImage, name string) string {
cField := C.CString(name)
defer freeCString(cField)
var cFieldValue *C.char
defer freeCString(cFieldValue)
defer func() { freeCString(cFieldValue) }()
if int(C.image_get_as_string(in, cField, &cFieldValue)) == 0 {
return C.GoString(cFieldValue)
}
Expand Down