resize.go

 1// Package imageutil provides image manipulation utilities.
 2package imageutil
 3
 4import (
 5	"bytes"
 6	"fmt"
 7	"image"
 8	"image/jpeg"
 9	"image/png"
10	"strings"
11
12	"golang.org/x/image/draw"
13)
14
15// ResizeImage resizes an image if any dimension exceeds maxDimension.
16// Returns the resized image bytes and the format ("png" or "jpeg").
17// If no resize is needed, returns the original data unchanged.
18func ResizeImage(data []byte, maxDimension int) (resized []byte, format string, didResize bool, err error) {
19	img, detectedFormat, err := image.Decode(bytes.NewReader(data))
20	if err != nil {
21		return nil, "", false, fmt.Errorf("failed to decode image: %w", err)
22	}
23
24	bounds := img.Bounds()
25	width := bounds.Dx()
26	height := bounds.Dy()
27
28	if width <= maxDimension && height <= maxDimension {
29		return data, detectedFormat, false, nil
30	}
31
32	// Calculate new dimensions preserving aspect ratio
33	newWidth, newHeight := width, height
34	if width > height {
35		newWidth = maxDimension
36		newHeight = height * maxDimension / width
37	} else {
38		newHeight = maxDimension
39		newWidth = width * maxDimension / height
40	}
41
42	// Create resized image
43	resizedImg := image.NewRGBA(image.Rect(0, 0, newWidth, newHeight))
44	draw.BiLinear.Scale(resizedImg, resizedImg.Bounds(), img, bounds, draw.Over, nil)
45
46	// Encode to the same format
47	var buf bytes.Buffer
48	switch strings.ToLower(detectedFormat) {
49	case "jpeg", "jpg":
50		err = jpeg.Encode(&buf, resizedImg, &jpeg.Options{Quality: 85})
51		format = "jpeg"
52	default:
53		err = png.Encode(&buf, resizedImg)
54		format = "png"
55	}
56
57	if err != nil {
58		return nil, "", false, fmt.Errorf("failed to encode resized image: %w", err)
59	}
60
61	return buf.Bytes(), format, true, nil
62}