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}