Something went wrong. Try again.
Fetch, resize, reformat, and cache Atmosphere avatar images atp.pics
atproto
Something went wrong. Try again.
Go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108// Package transform decodes, resizes, and encodes avatar images.package transform
import ( "bytes" "fmt" "image" "image/jpeg" "image/png"
"github.com/chai2010/webp" "github.com/disintegration/imaging")
// Params holds the requested output dimensions, quality, and format.type Params struct { Width int // 0 = not specified Height int // 0 = not specified Quality int // 1–100; ignored for PNG; default 85 Format string // "webp" | "jpg" | "png"}
// DefaultQuality is used when Params.Quality is 0.const DefaultQuality = 85
// Transform decodes src, applies the requested resize, and encodes to the// target format. Returns the encoded bytes and the output Content-Type.func Transform(src []byte, mimeType string, p Params) ([]byte, string, error) { img, err := decode(src, mimeType) if err != nil { return nil, "", fmt.Errorf("decode: %w", err) }
img = resize(img, p.Width, p.Height)
q := p.Quality if q == 0 { q = DefaultQuality }
return encode(img, p.Format, q)}
func decode(data []byte, mimeType string) (image.Image, error) { r := bytes.NewReader(data) switch mimeType { case "image/jpeg": img, err := jpeg.Decode(r) if err != nil { return nil, fmt.Errorf("jpeg decode: %w", err) } return img, nil case "image/png": img, err := png.Decode(r) if err != nil { return nil, fmt.Errorf("png decode: %w", err) } return img, nil case "image/webp": img, err := webp.Decode(r) if err != nil { return nil, fmt.Errorf("webp decode: %w", err) } return img, nil default: return nil, fmt.Errorf("unsupported source format: %s", mimeType) }}
// resize applies the correct resize strategy based on which dimensions are set.//// - Both w and h: cover-fit (scale to fill, center-crop to exact size)// - Only w or only h: proportional scale, preserving aspect ratio// - Neither: no resize, return originalfunc resize(img image.Image, w, h int) image.Image { if w == 0 && h == 0 { return img } if w > 0 && h > 0 { return imaging.Fill(img, w, h, imaging.Center, imaging.Lanczos) } // Proportional: pass 0 for the unconstrained dimension. return imaging.Resize(img, w, h, imaging.Lanczos)}
func encode(img image.Image, format string, quality int) ([]byte, string, error) { var buf bytes.Buffer switch format { case "jpg", "jpeg": opts := &jpeg.Options{Quality: quality} if err := jpeg.Encode(&buf, img, opts); err != nil { return nil, "", fmt.Errorf("jpeg encode: %w", err) } return buf.Bytes(), "image/jpeg", nil case "png": if err := png.Encode(&buf, img); err != nil { return nil, "", fmt.Errorf("png encode: %w", err) } return buf.Bytes(), "image/png", nil default: // "webp" and anything else opts := &webp.Options{Lossless: false, Quality: float32(quality)} if err := webp.Encode(&buf, img, opts); err != nil { return nil, "", fmt.Errorf("webp encode: %w", err) } return buf.Bytes(), "image/webp", nil }}