aboutsummaryrefslogtreecommitdiff
path: root/weed/images
diff options
context:
space:
mode:
Diffstat (limited to 'weed/images')
-rw-r--r--weed/images/orientation.go182
-rw-r--r--weed/images/orientation_test.go20
-rw-r--r--weed/images/preprocess.go29
3 files changed, 231 insertions, 0 deletions
diff --git a/weed/images/orientation.go b/weed/images/orientation.go
new file mode 100644
index 000000000..a592a7d8b
--- /dev/null
+++ b/weed/images/orientation.go
@@ -0,0 +1,182 @@
+package images
+
+import (
+ "bytes"
+ "image"
+ "image/draw"
+ "image/jpeg"
+ "log"
+
+ "github.com/seaweedfs/goexif/exif"
+)
+
+//many code is copied from http://camlistore.org/pkg/images/images.go
+func FixJpgOrientation(data []byte) (oriented []byte) {
+ ex, err := exif.Decode(bytes.NewReader(data))
+ if err != nil {
+ return data
+ }
+ tag, err := ex.Get(exif.Orientation)
+ if err != nil {
+ return data
+ }
+ angle := 0
+ flipMode := FlipDirection(0)
+ orient, err := tag.Int(0)
+ if err != nil {
+ return data
+ }
+ switch orient {
+ case topLeftSide:
+ // do nothing
+ return data
+ case topRightSide:
+ flipMode = 2
+ case bottomRightSide:
+ angle = 180
+ case bottomLeftSide:
+ angle = 180
+ flipMode = 2
+ case leftSideTop:
+ angle = -90
+ flipMode = 2
+ case rightSideTop:
+ angle = -90
+ case rightSideBottom:
+ angle = 90
+ flipMode = 2
+ case leftSideBottom:
+ angle = 90
+ }
+
+ if srcImage, _, err := image.Decode(bytes.NewReader(data)); err == nil {
+ dstImage := flip(rotate(srcImage, angle), flipMode)
+ var buf bytes.Buffer
+ jpeg.Encode(&buf, dstImage, nil)
+ return buf.Bytes()
+ }
+
+ return data
+}
+
+// Exif Orientation Tag values
+// http://sylvana.net/jpegcrop/exif_orientation.html
+const (
+ topLeftSide = 1
+ topRightSide = 2
+ bottomRightSide = 3
+ bottomLeftSide = 4
+ leftSideTop = 5
+ rightSideTop = 6
+ rightSideBottom = 7
+ leftSideBottom = 8
+)
+
+// The FlipDirection type is used by the Flip option in DecodeOpts
+// to indicate in which direction to flip an image.
+type FlipDirection int
+
+// FlipVertical and FlipHorizontal are two possible FlipDirections
+// values to indicate in which direction an image will be flipped.
+const (
+ FlipVertical FlipDirection = 1 << iota
+ FlipHorizontal
+)
+
+type DecodeOpts struct {
+ // Rotate specifies how to rotate the image.
+ // If nil, the image is rotated automatically based on EXIF metadata.
+ // If an int, Rotate is the number of degrees to rotate
+ // counter clockwise and must be one of 0, 90, -90, 180, or
+ // -180.
+ Rotate interface{}
+
+ // Flip specifies how to flip the image.
+ // If nil, the image is flipped automatically based on EXIF metadata.
+ // Otherwise, Flip is a FlipDirection bitfield indicating how to flip.
+ Flip interface{}
+}
+
+func rotate(im image.Image, angle int) image.Image {
+ var rotated *image.NRGBA
+ // trigonometric (i.e counter clock-wise)
+ switch angle {
+ case 90:
+ newH, newW := im.Bounds().Dx(), im.Bounds().Dy()
+ rotated = image.NewNRGBA(image.Rect(0, 0, newW, newH))
+ for y := 0; y < newH; y++ {
+ for x := 0; x < newW; x++ {
+ rotated.Set(x, y, im.At(newH-1-y, x))
+ }
+ }
+ case -90:
+ newH, newW := im.Bounds().Dx(), im.Bounds().Dy()
+ rotated = image.NewNRGBA(image.Rect(0, 0, newW, newH))
+ for y := 0; y < newH; y++ {
+ for x := 0; x < newW; x++ {
+ rotated.Set(x, y, im.At(y, newW-1-x))
+ }
+ }
+ case 180, -180:
+ newW, newH := im.Bounds().Dx(), im.Bounds().Dy()
+ rotated = image.NewNRGBA(image.Rect(0, 0, newW, newH))
+ for y := 0; y < newH; y++ {
+ for x := 0; x < newW; x++ {
+ rotated.Set(x, y, im.At(newW-1-x, newH-1-y))
+ }
+ }
+ default:
+ return im
+ }
+ return rotated
+}
+
+// flip returns a flipped version of the image im, according to
+// the direction(s) in dir.
+// It may flip the imput im in place and return it, or it may allocate a
+// new NRGBA (if im is an *image.YCbCr).
+func flip(im image.Image, dir FlipDirection) image.Image {
+ if dir == 0 {
+ return im
+ }
+ ycbcr := false
+ var nrgba image.Image
+ dx, dy := im.Bounds().Dx(), im.Bounds().Dy()
+ di, ok := im.(draw.Image)
+ if !ok {
+ if _, ok := im.(*image.YCbCr); !ok {
+ log.Printf("failed to flip image: input does not satisfy draw.Image")
+ return im
+ }
+ // because YCbCr does not implement Set, we replace it with a new NRGBA
+ ycbcr = true
+ nrgba = image.NewNRGBA(image.Rect(0, 0, dx, dy))
+ di, ok = nrgba.(draw.Image)
+ if !ok {
+ log.Print("failed to flip image: could not cast an NRGBA to a draw.Image")
+ return im
+ }
+ }
+ if dir&FlipHorizontal != 0 {
+ for y := 0; y < dy; y++ {
+ for x := 0; x < dx/2; x++ {
+ old := im.At(x, y)
+ di.Set(x, y, im.At(dx-1-x, y))
+ di.Set(dx-1-x, y, old)
+ }
+ }
+ }
+ if dir&FlipVertical != 0 {
+ for y := 0; y < dy/2; y++ {
+ for x := 0; x < dx; x++ {
+ old := im.At(x, y)
+ di.Set(x, y, im.At(x, dy-1-y))
+ di.Set(x, dy-1-y, old)
+ }
+ }
+ }
+ if ycbcr {
+ return nrgba
+ }
+ return im
+}
diff --git a/weed/images/orientation_test.go b/weed/images/orientation_test.go
new file mode 100644
index 000000000..32fa38f76
--- /dev/null
+++ b/weed/images/orientation_test.go
@@ -0,0 +1,20 @@
+package images
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+)
+
+func TestXYZ(t *testing.T) {
+ fname := "sample1.jpg"
+
+ dat, _ := ioutil.ReadFile(fname)
+
+ fixed_data := FixJpgOrientation(dat)
+
+ ioutil.WriteFile("fixed1.jpg", fixed_data, 0644)
+
+ os.Remove("fixed1.jpg")
+
+}
diff --git a/weed/images/preprocess.go b/weed/images/preprocess.go
new file mode 100644
index 000000000..f6f3b554d
--- /dev/null
+++ b/weed/images/preprocess.go
@@ -0,0 +1,29 @@
+package images
+
+import (
+ "bytes"
+ "io"
+ "path/filepath"
+ "strings"
+)
+
+/*
+* Preprocess image files on client side.
+* 1. possibly adjust the orientation
+* 2. resize the image to a width or height limit
+* 3. remove the exif data
+* Call this function on any file uploaded to SeaweedFS
+*
+ */
+func MaybePreprocessImage(filename string, data []byte, width, height int) (resized io.ReadSeeker, w int, h int) {
+ ext := filepath.Ext(filename)
+ ext = strings.ToLower(ext)
+ switch ext {
+ case ".png", ".gif":
+ return Resized(ext, bytes.NewReader(data), width, height, "")
+ case ".jpg", ".jpeg":
+ data = FixJpgOrientation(data)
+ return Resized(ext, bytes.NewReader(data), width, height, "")
+ }
+ return bytes.NewReader(data), 0, 0
+}