mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2024-01-19 02:48:24 +00:00
Add image cropping. (#4117)
This commit is contained in:
parent
296fdc296c
commit
3e2c9ea73d
47
weed/images/cropping.go
Normal file
47
weed/images/cropping.go
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
package images
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"image"
|
||||||
|
"image/gif"
|
||||||
|
"image/jpeg"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/disintegration/imaging"
|
||||||
|
|
||||||
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Cropped(ext string, read io.ReadSeeker, x1, y1, x2, y2 int) (cropped io.ReadSeeker, err error) {
|
||||||
|
srcImage, _, err := image.Decode(read)
|
||||||
|
if err != nil {
|
||||||
|
glog.Error(err)
|
||||||
|
return read, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bounds := srcImage.Bounds()
|
||||||
|
if x2 > bounds.Dx() || y2 > bounds.Dy() {
|
||||||
|
read.Seek(0, 0)
|
||||||
|
return read, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
rectangle := image.Rect(x1, y1, x2, y2)
|
||||||
|
dstImage := imaging.Crop(srcImage, rectangle)
|
||||||
|
var buf bytes.Buffer
|
||||||
|
switch ext {
|
||||||
|
case ".jpg", ".jpeg":
|
||||||
|
if err = jpeg.Encode(&buf, dstImage, nil); err != nil {
|
||||||
|
glog.Error(err)
|
||||||
|
}
|
||||||
|
case ".png":
|
||||||
|
if err = png.Encode(&buf, dstImage); err != nil {
|
||||||
|
glog.Error(err)
|
||||||
|
}
|
||||||
|
case ".gif":
|
||||||
|
if err = gif.Encode(&buf, dstImage, nil); err != nil {
|
||||||
|
glog.Error(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bytes.NewReader(buf.Bytes()), err
|
||||||
|
}
|
22
weed/images/cropping_test.go
Normal file
22
weed/images/cropping_test.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package images
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/seaweedfs/seaweedfs/weed/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCropping(t *testing.T) {
|
||||||
|
fname := "sample1.jpg"
|
||||||
|
|
||||||
|
dat, _ := os.ReadFile(fname)
|
||||||
|
|
||||||
|
cropped, _ := Cropped(".jpg", bytes.NewReader(dat), 1072, 932, 1751, 1062)
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
buf.ReadFrom(cropped)
|
||||||
|
|
||||||
|
util.WriteFile("cropped1.jpg", buf.Bytes(), 0644)
|
||||||
|
|
||||||
|
}
|
|
@ -5,8 +5,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/seaweedfs/seaweedfs/weed/storage/types"
|
|
||||||
"github.com/seaweedfs/seaweedfs/weed/util/mem"
|
|
||||||
"io"
|
"io"
|
||||||
"mime"
|
"mime"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -17,6 +15,9 @@ import (
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/seaweedfs/seaweedfs/weed/storage/types"
|
||||||
|
"github.com/seaweedfs/seaweedfs/weed/util/mem"
|
||||||
|
|
||||||
"github.com/seaweedfs/seaweedfs/weed/glog"
|
"github.com/seaweedfs/seaweedfs/weed/glog"
|
||||||
"github.com/seaweedfs/seaweedfs/weed/images"
|
"github.com/seaweedfs/seaweedfs/weed/images"
|
||||||
"github.com/seaweedfs/seaweedfs/weed/operation"
|
"github.com/seaweedfs/seaweedfs/weed/operation"
|
||||||
|
@ -204,7 +205,9 @@ func (vs *VolumeServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
if n.IsCompressed() {
|
if n.IsCompressed() {
|
||||||
if _, _, _, shouldResize := shouldResizeImages(ext, r); shouldResize {
|
_, _, _, shouldResize := shouldResizeImages(ext, r)
|
||||||
|
_, _, _, _, shouldCrop := shouldCropImages(ext, r)
|
||||||
|
if shouldResize || shouldCrop {
|
||||||
if n.Data, err = util.DecompressData(n.Data); err != nil {
|
if n.Data, err = util.DecompressData(n.Data); err != nil {
|
||||||
glog.V(0).Infoln("ungzip error:", err, r.URL.Path)
|
glog.V(0).Infoln("ungzip error:", err, r.URL.Path)
|
||||||
}
|
}
|
||||||
|
@ -220,7 +223,8 @@ func (vs *VolumeServer) GetOrHeadHandler(w http.ResponseWriter, r *http.Request)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !readOption.IsMetaOnly {
|
if !readOption.IsMetaOnly {
|
||||||
rs := conditionallyResizeImages(bytes.NewReader(n.Data), ext, r)
|
rs := conditionallyCropImages(bytes.NewReader(n.Data), ext, r)
|
||||||
|
rs = conditionallyResizeImages(rs, ext, r)
|
||||||
if e := writeResponseContent(filename, mtype, rs, w, r); e != nil {
|
if e := writeResponseContent(filename, mtype, rs, w, r); e != nil {
|
||||||
glog.V(2).Infoln("response write error:", e)
|
glog.V(2).Infoln("response write error:", e)
|
||||||
}
|
}
|
||||||
|
@ -240,7 +244,8 @@ func shouldAttemptStreamWrite(hasLocalVolume bool, ext string, r *http.Request)
|
||||||
return true, true
|
return true, true
|
||||||
}
|
}
|
||||||
_, _, _, shouldResize := shouldResizeImages(ext, r)
|
_, _, _, shouldResize := shouldResizeImages(ext, r)
|
||||||
if shouldResize {
|
_, _, _, _, shouldCrop := shouldCropImages(ext, r)
|
||||||
|
if shouldResize || shouldCrop {
|
||||||
return false, false
|
return false, false
|
||||||
}
|
}
|
||||||
return true, false
|
return true, false
|
||||||
|
@ -277,7 +282,8 @@ func (vs *VolumeServer) tryHandleChunkedFile(n *needle.Needle, fileName string,
|
||||||
chunkedFileReader := operation.NewChunkedFileReader(chunkManifest.Chunks, vs.GetMaster(), vs.grpcDialOption)
|
chunkedFileReader := operation.NewChunkedFileReader(chunkManifest.Chunks, vs.GetMaster(), vs.grpcDialOption)
|
||||||
defer chunkedFileReader.Close()
|
defer chunkedFileReader.Close()
|
||||||
|
|
||||||
rs := conditionallyResizeImages(chunkedFileReader, ext, r)
|
rs := conditionallyCropImages(chunkedFileReader, ext, r)
|
||||||
|
rs = conditionallyResizeImages(rs, ext, r)
|
||||||
|
|
||||||
if e := writeResponseContent(fileName, mType, rs, w, r); e != nil {
|
if e := writeResponseContent(fileName, mType, rs, w, r); e != nil {
|
||||||
glog.V(2).Infoln("response write error:", e)
|
glog.V(2).Infoln("response write error:", e)
|
||||||
|
@ -311,6 +317,41 @@ func shouldResizeImages(ext string, r *http.Request) (width, height int, mode st
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func conditionallyCropImages(originalDataReaderSeeker io.ReadSeeker, ext string, r *http.Request) io.ReadSeeker {
|
||||||
|
rs := originalDataReaderSeeker
|
||||||
|
if len(ext) > 0 {
|
||||||
|
ext = strings.ToLower(ext)
|
||||||
|
}
|
||||||
|
x1, y1, x2, y2, shouldCrop := shouldCropImages(ext, r)
|
||||||
|
if shouldCrop {
|
||||||
|
var err error
|
||||||
|
rs, err = images.Cropped(ext, rs, x1, y1, x2, y2)
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Cropping images error: %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rs
|
||||||
|
}
|
||||||
|
|
||||||
|
func shouldCropImages(ext string, r *http.Request) (x1, y1, x2, y2 int, shouldCrop bool) {
|
||||||
|
if ext == ".png" || ext == ".jpg" || ext == ".jpeg" || ext == ".gif" {
|
||||||
|
if r.FormValue("crop_x1") != "" {
|
||||||
|
x1, _ = strconv.Atoi(r.FormValue("crop_x1"))
|
||||||
|
}
|
||||||
|
if r.FormValue("crop_y1") != "" {
|
||||||
|
y1, _ = strconv.Atoi(r.FormValue("crop_y1"))
|
||||||
|
}
|
||||||
|
if r.FormValue("crop_x2") != "" {
|
||||||
|
x2, _ = strconv.Atoi(r.FormValue("crop_x2"))
|
||||||
|
}
|
||||||
|
if r.FormValue("crop_y2") != "" {
|
||||||
|
y2, _ = strconv.Atoi(r.FormValue("crop_y2"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
shouldCrop = x1 >= 0 && y1 >= 0 && x2 > x1 && y2 > y1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func writeResponseContent(filename, mimeType string, rs io.ReadSeeker, w http.ResponseWriter, r *http.Request) error {
|
func writeResponseContent(filename, mimeType string, rs io.ReadSeeker, w http.ResponseWriter, r *http.Request) error {
|
||||||
totalSize, e := rs.Seek(0, 2)
|
totalSize, e := rs.Seek(0, 2)
|
||||||
if mimeType == "" {
|
if mimeType == "" {
|
||||||
|
|
Loading…
Reference in a new issue