package media import ( "bytes" "encoding/base64" "fmt" "image/png" "github.com/davidbyttow/govips/v2/vips" thumbhash "go.n16f.net/thumbhash" ) // ImageInfo returns the pixel dimensions and a base64-encoded thumbhash for // the image at path. The thumbhash is generated from a ≤100px thumbnail so // it is fast regardless of the original size. func ImageInfo(path string) (width, height int, thumbhashB64 string, err error) { img, err := vips.NewImageFromFile(path) if err != nil { return 0, 0, "", fmt.Errorf("load %s: %w", path, err) } defer img.Close() width = img.Width() height = img.Height() const maxThumbDim = 100 if width > maxThumbDim || height > maxThumbDim { scale := float64(maxThumbDim) / float64(max(width, height)) if err := img.Resize(scale, vips.KernelLanczos3); err != nil { return width, height, "", fmt.Errorf("resize for thumbhash: %w", err) } } pngBytes, _, err := img.ExportPng(vips.NewPngExportParams()) if err != nil { return width, height, "", fmt.Errorf("export png for thumbhash: %w", err) } goImg, err := png.Decode(bytes.NewReader(pngBytes)) if err != nil { return width, height, "", fmt.Errorf("decode png for thumbhash: %w", err) } hash := thumbhash.EncodeImage(goImg) return width, height, base64.StdEncoding.EncodeToString(hash), nil } // ConvertAndResize loads the image at src, optionally resizes it to width pixels // wide (maintaining aspect ratio), and exports it in the requested format. // // width <= 0 means no resize. format must be "webp" or "jpeg". func ConvertAndResize(src string, width int, format string) ([]byte, error) { img, err := vips.NewImageFromFile(src) if err != nil { return nil, fmt.Errorf("load image %s: %w", src, err) } defer img.Close() if width > 0 && width < img.Width() { scale := float64(width) / float64(img.Width()) if err := img.Resize(scale, vips.KernelLanczos3); err != nil { return nil, fmt.Errorf("resize: %w", err) } } switch format { case "webp": buf, _, err := img.ExportWebp(vips.NewWebpExportParams()) if err != nil { return nil, fmt.Errorf("export webp: %w", err) } return buf, nil case "jpeg": buf, _, err := img.ExportJpeg(vips.NewJpegExportParams()) if err != nil { return nil, fmt.Errorf("export jpeg: %w", err) } return buf, nil default: return nil, fmt.Errorf("unsupported format %q: must be webp or jpeg", format) } }