Skip to content

Commit 106ad1e

Browse files
authored
fix: image type unknown from memory (#19)
* more test cases * more test cases * fix: image type unknown from memory * fix tabs * readme * readme * readme * readme * build: update generated vips package
1 parent 035610d commit 106ad1e

File tree

7 files changed

+1563
-156
lines changed

7 files changed

+1563
-156
lines changed

README.md

Lines changed: 40 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
# vipsgen
22

3+
[![Go Reference](https://pkg.go.dev/badge/github.com/cshum/vipsgen.svg)](https://pkg.go.dev/github.com/cshum/vipsgen)
4+
![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/cshum/vipsgen)
5+
[![CI](https://github.com/cshum/vipsgen/actions/workflows/ci.yml/badge.svg)](https://github.com/cshum/vipsgen/actions/workflows/ci.yml)
6+
37
vipsgen is a Go binding generator for [libvips](https://github.com/libvips/libvips) - a fast and efficient image processing library.
48

5-
Existing Go libvips bindings rely on manually written code that is often incomplete, error-prone, and difficult to maintain as libvips evolves. vipsgen aims to solve this problem by using GObject introspection to automatically generate type-safe, efficient, and fully documented Go bindings that adapt to your specific libvips installation.
9+
Existing Go libvips bindings rely on manually written code that is often incomplete, error-prone, and difficult to maintain as libvips evolves. vipsgen aims to solve this problem by generating type-safe, robust, and fully documented Go bindings using GObject introspection.
610

7-
vipsgen provides a pre-generated library you can import directly `github.com/cshum/vipsgen/vips`. Also allows code generation via `vipsgen` command for your specific libvips installation.
11+
vipsgen provides a pre-generated library you can import directly `github.com/cshum/vipsgen/vips`. Also allows code generation via `vipsgen` command that adapts to your specific libvips installation.
812

9-
- **Coverage**: Comprehensive bindings that cover most of the libvips operations, with allowing custom code
13+
- **Coverage**: Comprehensive bindings for over 200 libvips operations
1014
- **Type-Safe**: Generates proper Go types for libvips enums and structs
11-
- **Idiomatic**: Creates Go style code that feels natural to use
15+
- **Idiomatic**: Creates clear Go style code that feels natural to use
1216
- **Streaming**: Includes `VipsSource` bindings with `io.ReadCloser` integration for streaming
1317

1418
## Quick Start
@@ -31,52 +35,68 @@ Use the package directly:
3135
go get -u github.com/cshum/vipsgen/vips
3236
```
3337

38+
vipsgen provides rich options for fine-tuning image operations. Each operation can accept a nil value for default options, or customize optional arguments with specific option structs:
39+
3440
```go
3541
package main
3642

3743
import (
3844
"fmt"
3945
"log"
4046
"net/http"
41-
"os"
47+
4248
"github.com/cshum/vipsgen/vips"
4349
)
4450

4551
func main() {
46-
// Fetch an image from URL
52+
// Fetch an image from http.Get
4753
resp, err := http.Get("https://raw.githubusercontent.com/cshum/imagor/master/testdata/gopher.png")
4854
if err != nil {
4955
log.Fatalf("Failed to fetch image: %v", err)
5056
}
57+
defer resp.Body.Close()
5158

5259
// Create source from io.ReadCloser
5360
source := vips.NewSource(resp.Body)
5461
defer source.Close() // source needs to remain available during image lifetime
5562

56-
// Load image from source
57-
image, err := vips.NewImageFromSource(source, nil)
63+
// Shrink-on-load via creating image from thumbnail source with options
64+
image, err := vips.NewThumbnailSource(source, 800, &vips.ThumbnailSourceOptions{
65+
Height: 1000,
66+
FailOn: vips.FailOnError, // Fail on first error
67+
})
5868
if err != nil {
5969
log.Fatalf("Failed to load image: %v", err)
6070
}
6171
defer image.Close() // always close images to free memory
6272

63-
// Resize the image
64-
if err := image.Resize(0.5, nil); err != nil {
65-
log.Fatalf("Failed to resize image: %v", err)
73+
// Add a yellow border using vips_embed
74+
border := 10
75+
if err := image.Embed(
76+
border, border,
77+
image.Width()+border*2,
78+
image.Height()+border*2,
79+
&vips.EmbedOptions{
80+
Extend: vips.ExtendBackground, // extend with colour from the background property
81+
Background: []float64{255, 255, 0, 255}, // Yellow border
82+
},
83+
); err != nil {
84+
log.Fatalf("Failed to add border: %v", err)
6685
}
6786

68-
// Save the result as WebP
69-
buf, err := image.WebpsaveBuffer(nil)
87+
fmt.Printf("Processed image: %dx%d\n", image.Width(), image.Height())
88+
89+
// Save the result as WebP file with options
90+
err = image.Webpsave("resized-gopher.webp", &vips.WebpsaveOptions{
91+
Q: 85, // Quality factor (0-100)
92+
Lossless: false, // Use lossy compression
93+
Effort: 4, // Compression effort (0-6)
94+
SmartSubsample: true, // Better chroma subsampling
95+
})
7096
if err != nil {
7197
log.Fatalf("Failed to save image as WebP: %v", err)
7298
}
73-
74-
// Write to file
75-
if err := os.WriteFile("resized-gopher.webp", buf, 0666); err != nil {
76-
log.Fatalf("Failed to write file: %v", err)
77-
}
78-
79-
fmt.Println("Successfully resized image to resized-gopher.webp")
99+
fmt.Println("Successfully saved processed images")
80100
}
81101
```
82102

cmd/example/main.go

Lines changed: 0 additions & 46 deletions
This file was deleted.

internal/templates/image.go.tmpl

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ func (r *Image) {{.GoName}}({{generateImageMethodParams .}}) ({{generateImageMet
4444
{{range .Operations}}{{if and (not .HasThisImageInput) (not .HasImageOutput)}}
4545
// {{.GoName}} {{.Description}}
4646
func {{.GoName}}({{generateMethodParams .}}) ([]byte, error) {
47-
Startup(nil)
48-
return vipsgen{{.GoName}}({{generateUtilFunctionCallArgs .}})
47+
Startup(nil)
48+
return vipsgen{{.GoName}}({{generateUtilFunctionCallArgs .}})
4949
}
5050
{{end}}{{end}}
5151

@@ -110,8 +110,15 @@ func (i *LoadOptions) OptionString() string {
110110
if v := i.Memory; v {
111111
values = append(values, "memory="+boolToStr(v))
112112
}
113-
if v := i.Access; v != 0 {
114-
values = append(values, "access="+strconv.Itoa(int(v)))
113+
if access := i.Access; access != 0 {
114+
switch access {
115+
case AccessSequential:
116+
values = append(values, "access=sequential")
117+
case AccessRandom:
118+
values = append(values, "access=random")
119+
case AccessSequentialUnbuffered:
120+
values = append(values, "access=sequential-unbuffered")
121+
}
115122
}
116123
return strings.Join(values, ",")
117124
}
@@ -162,7 +169,7 @@ func NewImageFromMemory(buf []byte, width, height, bands int) (*Image, error) {
162169
if err != nil {
163170
return nil, err
164171
}
165-
return newImageRef(vipsImage, vipsDetermineImageType(vipsImage), buf), nil
172+
return newImageRef(vipsImage, ImageTypeUnknown, buf), nil
166173
}
167174

168175

@@ -240,14 +247,14 @@ func (r *Image) Interpretation() Interpretation {
240247
// Pages returns the number of pages in the Image
241248
// For animated images this corresponds to the number of frames
242249
func (r *Image) Pages() int {
243-
{{range .ImageTypes}}{{if eq .TypeName "jp2k"}}
244-
// libvips uses the same attribute (n_pages) to represent the number of pyramid layers in JP2K
245-
// as we interpret the attribute as frames and JP2K does not support animation we override this with 1
246-
if r.format == ImageTypeJp2k {
247-
return 1
248-
}
249-
{{end}}{{end}}
250-
return vipsGetImageNPages(r.image)
250+
{{range .ImageTypes}}{{if eq .TypeName "jp2k"}}
251+
// libvips uses the same attribute (n_pages) to represent the number of pyramid layers in JP2K
252+
// as we interpret the attribute as frames and JP2K does not support animation we override this with 1
253+
if r.format == ImageTypeJp2k {
254+
return 1
255+
}
256+
{{end}}{{end}}
257+
return vipsGetImageNPages(r.image)
251258
}
252259

253260
// PageHeight return the height of a single page

internal/templates/util.go.tmpl

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -270,46 +270,46 @@ func clearImage(img *C.VipsImage) {
270270
// bufferToBytes converts a C buffer to Go bytes and frees the original buffer.
271271
// This function takes ownership of the buffer and will free it after conversion.
272272
func bufferToBytes(buf unsafe.Pointer, length C.size_t) []byte {
273-
if buf == nil {
274-
return nil
275-
}
276-
bytes := C.GoBytes(buf, C.int(length))
277-
C.g_free(C.gpointer(buf))
278-
return bytes
273+
if buf == nil {
274+
return nil
275+
}
276+
bytes := C.GoBytes(buf, C.int(length))
277+
C.g_free(C.gpointer(buf))
278+
return bytes
279279
}
280280

281281
// convertImagesToVipsImages converts from Image slice to VipsImage slice
282282
func convertImagesToVipsImages(images []*Image) []*C.VipsImage {
283-
vipsImages := make([]*C.VipsImage, len(images))
284-
for i, img := range images {
285-
vipsImages[i] = img.image
286-
}
287-
return vipsImages
283+
vipsImages := make([]*C.VipsImage, len(images))
284+
for i, img := range images {
285+
vipsImages[i] = img.image
286+
}
287+
return vipsImages
288288
}
289289

290290
// convertVipsImagesToImages converts a slice of *C.VipsImage to []*Image
291291
func convertVipsImagesToImages(vipsImages []*C.VipsImage) []*Image {
292-
images := make([]*Image, len(vipsImages))
293-
for i, vipsImg := range vipsImages {
294-
images[i] = newImageRef(vipsImg, ImageTypeUnknown, nil)
295-
}
296-
return images
292+
images := make([]*Image, len(vipsImages))
293+
for i, vipsImg := range vipsImages {
294+
images[i] = newImageRef(vipsImg, ImageTypeUnknown, nil)
295+
}
296+
return images
297297
}
298298

299299
// vipsInterpolateToC converts a Go Interpolate to a C VipsInterpolate pointer
300300
func vipsInterpolateToC(interp *Interpolate) *C.VipsInterpolate {
301-
if interp == nil {
302-
return nil
303-
}
304-
return interp.interp
301+
if interp == nil {
302+
return nil
303+
}
304+
return interp.interp
305305
}
306306

307307
// vipsInterpolateFromC converts a C VipsInterpolate pointer to a Go Interpolate
308308
func vipsInterpolateFromC(interp *C.VipsInterpolate) *Interpolate {
309-
if interp == nil {
310-
return nil
311-
}
312-
return &Interpolate{interp: interp}
309+
if interp == nil {
310+
return nil
311+
}
312+
return &Interpolate{interp: interp}
313313
}
314314

315315
// convertToDoubleArray converts a Go float64 slice to a C double array and returns the length
@@ -423,12 +423,12 @@ func freeImageArray(array **C.VipsImage) {
423423

424424
// vipsBlobToBytes converts a VipsBlob to a Go byte slice and unrefs the blob
425425
func vipsBlobToBytes(blob *C.VipsBlob) []byte {
426-
if blob == nil {
427-
return nil
428-
}
429-
var size C.size_t
430-
ptr := C.vips_blob_get(blob, &size)
431-
data := C.GoBytes(ptr, C.int(size))
432-
C.vips_area_unref((*C.VipsArea)(unsafe.Pointer(blob)))
433-
return data
426+
if blob == nil {
427+
return nil
428+
}
429+
var size C.size_t
430+
ptr := C.vips_blob_get(blob, &size)
431+
data := C.GoBytes(ptr, C.int(size))
432+
C.vips_area_unref((*C.VipsArea)(unsafe.Pointer(blob)))
433+
return data
434434
}

vips/image.go

Lines changed: 20 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)