83 lines
1.7 KiB
Go
83 lines
1.7 KiB
Go
package media
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"image"
|
|
_ "image/gif"
|
|
"image/jpeg"
|
|
_ "image/png"
|
|
|
|
"golang.org/x/image/draw"
|
|
"golang.org/x/image/webp"
|
|
)
|
|
|
|
const maxUploadSize = 25 << 20 // 25 MiB
|
|
|
|
// IsImage reports whether the content type is a decodable image format.
|
|
func IsImage(contentType string) bool {
|
|
switch contentType {
|
|
case "image/jpeg", "image/png", "image/gif", "image/webp":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
// Generate returns a JPEG thumbnail of at most maxDim on its larger side.
|
|
// It returns nil for non-images or when the source is already small enough.
|
|
func Generate(ctx context.Context, r *bytes.Reader, contentType string, maxDim int) ([]byte, error) {
|
|
if !IsImage(contentType) {
|
|
return nil, nil
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
src, err := decodeImage(r, contentType)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bounds := src.Bounds()
|
|
w, h := bounds.Dx(), bounds.Dy()
|
|
if w <= maxDim && h <= maxDim {
|
|
return nil, nil
|
|
}
|
|
|
|
scale := float64(maxDim) / float64(max(w, h))
|
|
nw, nh := int(float64(w)*scale), int(float64(h)*scale)
|
|
if nw < 1 {
|
|
nw = 1
|
|
}
|
|
if nh < 1 {
|
|
nh = 1
|
|
}
|
|
|
|
dst := image.NewRGBA(image.Rect(0, 0, nw, nh))
|
|
draw.BiLinear.Scale(dst, dst.Bounds(), src, bounds, draw.Over, nil)
|
|
|
|
if err := ctx.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
var buf bytes.Buffer
|
|
if err := jpeg.Encode(&buf, dst, &jpeg.Options{Quality: 80}); err != nil {
|
|
return nil, err
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
func decodeImage(r *bytes.Reader, contentType string) (image.Image, error) {
|
|
if contentType == "image/webp" {
|
|
return webp.Decode(r)
|
|
}
|
|
img, _, err := image.Decode(r)
|
|
return img, err
|
|
}
|
|
|
|
func max(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|