123 lines
3.5 KiB
Go
123 lines
3.5 KiB
Go
|
package barertc
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"image"
|
||
|
"image/jpeg"
|
||
|
"image/png"
|
||
|
"io"
|
||
|
|
||
|
"git.kirsle.net/apps/barertc/pkg/config"
|
||
|
"git.kirsle.net/apps/barertc/pkg/log"
|
||
|
"github.com/edwvee/exiffix"
|
||
|
"golang.org/x/image/draw"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// TODO: configurable
|
||
|
MaxPhotoWidth = 1280
|
||
|
)
|
||
|
|
||
|
// ProcessImage treats user uploaded images:
|
||
|
//
|
||
|
// - Scales them down to a reasonable size
|
||
|
// - Strips EXIF metadata
|
||
|
//
|
||
|
// and returns the modified image again as bytes.
|
||
|
//
|
||
|
// Also returns the suggested preview width, height to draw the image
|
||
|
// at. This may be smaller than its true width x height.
|
||
|
//
|
||
|
// Filetype should be image/jpeg, image/gif or image/png.
|
||
|
func ProcessImage(fileType string, data []byte) ([]byte, int, int) {
|
||
|
reader := bytes.NewReader(data)
|
||
|
|
||
|
// Strip EXIF data.
|
||
|
origImage, _, err := exiffix.Decode(reader)
|
||
|
if err != nil {
|
||
|
log.Error("ProcessImage: exiffix: %s", err)
|
||
|
return data, config.Current.PreviewImageWidth, config.Current.PreviewImageWidth
|
||
|
}
|
||
|
|
||
|
reader.Seek(0, io.SeekStart)
|
||
|
var width, height = origImage.Bounds().Max.X, origImage.Bounds().Max.Y
|
||
|
|
||
|
log.Info("ProcessImage: taking a %dx%d image", width, height)
|
||
|
|
||
|
// Compute what size we should scale the width/height to,
|
||
|
// and the even smaller preview size for front-end.
|
||
|
var (
|
||
|
previewWidth = config.Current.PreviewImageWidth
|
||
|
previewHeight = previewWidth
|
||
|
)
|
||
|
if width >= height {
|
||
|
log.Debug("Its width(%d) is >= its height (%d)", width, height)
|
||
|
if width > config.Current.MaxImageWidth {
|
||
|
newWidth := config.Current.MaxImageWidth
|
||
|
log.Debug("\tnewWidth=%d", newWidth)
|
||
|
log.Debug("\tnewHeight=(%d / %d) * %d", width, height, newWidth)
|
||
|
height = int((float64(height) / float64(width)) * float64(newWidth))
|
||
|
width = newWidth
|
||
|
log.Debug("Its longest is width, scale to %dx%d", width, height)
|
||
|
}
|
||
|
|
||
|
// Compute the preview width.
|
||
|
if width > config.Current.PreviewImageWidth {
|
||
|
newWidth := config.Current.PreviewImageWidth
|
||
|
previewHeight = int((float64(height) / float64(width)) * float64(newWidth))
|
||
|
previewWidth = newWidth
|
||
|
}
|
||
|
} else {
|
||
|
if height > config.Current.MaxImageWidth {
|
||
|
newHeight := config.Current.MaxImageWidth
|
||
|
width = int((float64(width) / float64(height)) * float64(newHeight))
|
||
|
height = newHeight
|
||
|
log.Debug("Its longest is height, scale to %dx%d", width, height)
|
||
|
}
|
||
|
|
||
|
// Compute the preview height.
|
||
|
if height > config.Current.PreviewImageWidth {
|
||
|
newHeight := config.Current.PreviewImageWidth
|
||
|
previewWidth = int((float64(width) / float64(height)) * float64(newHeight))
|
||
|
previewHeight = newHeight
|
||
|
log.Debug("Its longest is height, scale to %dx%d", previewWidth, previewHeight)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Scale the image.
|
||
|
scaledImg := Scale(origImage, image.Rect(0, 0, width, height), draw.ApproxBiLinear)
|
||
|
|
||
|
// Return the new bytes.
|
||
|
var buf = bytes.NewBuffer([]byte{})
|
||
|
switch fileType {
|
||
|
case "image/jpeg":
|
||
|
jpeg.Encode(buf, scaledImg, &jpeg.Options{
|
||
|
Quality: 90,
|
||
|
})
|
||
|
case "image/gif":
|
||
|
// Return the original data - we will only break it.
|
||
|
return data, width, height
|
||
|
case "image/png":
|
||
|
png.Encode(buf, scaledImg)
|
||
|
default:
|
||
|
return data, config.Current.PreviewImageWidth, config.Current.PreviewImageWidth
|
||
|
}
|
||
|
|
||
|
return buf.Bytes(), previewWidth, previewHeight
|
||
|
}
|
||
|
|
||
|
// Scale down an image. Example:
|
||
|
//
|
||
|
// scaled := Scale(src, image.Rect(0, 0, 200, 200), draw.ApproxBiLinear)
|
||
|
func Scale(src image.Image, rect image.Rectangle, scale draw.Scaler) image.Image {
|
||
|
dst := image.NewRGBA(rect)
|
||
|
copyRect := image.Rect(
|
||
|
rect.Min.X,
|
||
|
rect.Min.Y,
|
||
|
rect.Min.X+rect.Max.X,
|
||
|
rect.Min.Y+rect.Max.Y,
|
||
|
)
|
||
|
scale.Scale(dst, copyRect, src, src.Bounds(), draw.Over, nil)
|
||
|
return dst
|
||
|
}
|