浏览代码

Decode, Encode

disintegration 11 年之前
父节点
当前提交
56d1f5abd5
共有 4 个文件被更改,包括 183 次插入44 次删除
  1. 14 2
      README.md
  2. 1 1
      doc.go
  3. 88 41
      helpers.go
  4. 80 0
      helpers_test.go

+ 14 - 2
README.md

@@ -171,19 +171,31 @@ dstImage = imaging.Grayscale(srcImage)
 dstImage = imaging.Invert(srcImage)
 ```
 
-#### Open, Save, New, Clone
+#### Helpers
 
 Imaging package provides useful shortcuts for image loading, saving, creation and copying.
 Open and Save functions support JPEG, PNG, TIFF, BMP and GIF images. 
 External libraries can be used to load other image formats.
 
 ```go
+// read an image from io.Reader
+img, err := imaging.Decode(r) 
+if err != nil {
+    panic(err)
+}
+
 // load an image from file
 img, err := imaging.Open("src.png") 
 if err != nil {
     panic(err)
 }
 
+// write the image to io.Writer
+err = imaging.Encode(w, img, imaging.PNG) 
+if err != nil {
+    panic(err)
+}
+
 // save the image to file
 err = imaging.Save(img, "dst.jpg") 
 if err != nil {
@@ -199,7 +211,7 @@ copiedImg := imaging.Clone(img)
 
 
 ## Code example
-Here is the complete example that loades several images, makes thumbnails of them
+Here is the complete example that loads several images, makes thumbnails of them
 and joins them together.
 
 ```go

+ 1 - 1
doc.go

@@ -11,7 +11,7 @@ To achieve maximum performance, make sure to allow Go to utilize all CPU cores:
 
 	runtime.GOMAXPROCS(runtime.NumCPU())
 
-Here is the complete example that loades several images, makes thumbnails of them
+Here is the complete example that loads several images, makes thumbnails of them
 and joins them together.
 
 	package main

+ 88 - 41
helpers.go

@@ -1,12 +1,13 @@
 package imaging
 
 import (
-	"fmt"
+	"errors"
 	"image"
 	"image/color"
 	"image/gif"
 	"image/jpeg"
 	"image/png"
+	"io"
 	"math"
 	"os"
 	"path/filepath"
@@ -16,46 +17,62 @@ import (
 	"golang.org/x/image/tiff"
 )
 
-// Open loads an image from file
-func Open(filename string) (img image.Image, err error) {
-	file, err := os.Open(filename)
-	if err != nil {
-		return
-	}
-	defer file.Close()
+type Format int
 
-	img, _, err = image.Decode(file)
-	if err != nil {
-		return
-	}
+const (
+	JPEG Format = iota
+	PNG
+	GIF
+	TIFF
+	BMP
+)
 
-	img = toNRGBA(img)
-	return
+func (f Format) String() string {
+	switch f {
+	case JPEG:
+		return "JPEG"
+	case PNG:
+		return "PNG"
+	case GIF:
+		return "GIF"
+	case TIFF:
+		return "TIFF"
+	case BMP:
+		return "BMP"
+	default:
+		return "Unsupported"
+	}
 }
 
-// Save saves the image to file with the specified filename.
-// The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "tif" (or "tiff"), "bmp" and "gif" are supported.
-func Save(img image.Image, filename string) (err error) {
-	format := strings.ToLower(filepath.Ext(filename))
-	okay := false
-	for _, ext := range []string{".jpg", ".jpeg", ".png", ".tif", ".tiff", ".bmp", ".gif"} {
-		if format == ext {
-			okay = true
-			break
-		}
-	}
-	if !okay {
-		return fmt.Errorf(`imaging: unsupported image format: "%s"`, format)
+var (
+	ErrUnsupportedFormat = errors.New("imaging: unsupported image format")
+)
+
+// Decode reads an image from r.
+func Decode(r io.Reader) (image.Image, error) {
+	img, _, err := image.Decode(r)
+	if err != nil {
+		return nil, err
 	}
+	return toNRGBA(img), nil
+}
 
-	file, err := os.Create(filename)
+// Open loads an image from file
+func Open(filename string) (image.Image, error) {
+	file, err := os.Open(filename)
 	if err != nil {
-		return
+		return nil, err
 	}
 	defer file.Close()
+	img, err := Decode(file)
+	return img, err
+}
 
+// Encode writes the image img to w in the specified format (JPEG, PNG, GIF, TIFF or BMP).
+func Encode(w io.Writer, img image.Image, format Format) error {
+	var err error
 	switch format {
-	case ".jpg", ".jpeg":
+	case JPEG:
 		var rgba *image.RGBA
 		if nrgba, ok := img.(*image.NRGBA); ok {
 			if nrgba.Opaque() {
@@ -67,21 +84,51 @@ func Save(img image.Image, filename string) (err error) {
 			}
 		}
 		if rgba != nil {
-			err = jpeg.Encode(file, rgba, &jpeg.Options{Quality: 95})
+			err = jpeg.Encode(w, rgba, &jpeg.Options{Quality: 95})
 		} else {
-			err = jpeg.Encode(file, img, &jpeg.Options{Quality: 95})
+			err = jpeg.Encode(w, img, &jpeg.Options{Quality: 95})
 		}
 
-	case ".png":
-		err = png.Encode(file, img)
-	case ".tif", ".tiff":
-		err = tiff.Encode(file, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
-	case ".bmp":
-		err = bmp.Encode(file, img)
-	case ".gif":
-		err = gif.Encode(file, img, nil)
+	case PNG:
+		err = png.Encode(w, img)
+	case GIF:
+		err = gif.Encode(w, img, &gif.Options{NumColors: 256})
+	case TIFF:
+		err = tiff.Encode(w, img, &tiff.Options{Compression: tiff.Deflate, Predictor: true})
+	case BMP:
+		err = bmp.Encode(w, img)
+	default:
+		err = ErrUnsupportedFormat
+	}
+	return err
+}
+
+// Save saves the image to file with the specified filename.
+// The format is determined from the filename extension: "jpg" (or "jpeg"), "png", "gif", "tif" (or "tiff") and "bmp" are supported.
+func Save(img image.Image, filename string) (err error) {
+	formats := map[string]Format{
+		".jpg":  JPEG,
+		".jpeg": JPEG,
+		".png":  PNG,
+		".tif":  TIFF,
+		".tiff": TIFF,
+		".bmp":  BMP,
+		".gif":  GIF,
 	}
-	return
+
+	ext := strings.ToLower(filepath.Ext(filename))
+	f, ok := formats[ext]
+	if !ok {
+		return ErrUnsupportedFormat
+	}
+
+	file, err := os.Create(filename)
+	if err != nil {
+		return err
+	}
+	defer file.Close()
+
+	return Encode(file, img, f)
 }
 
 // New creates a new image with the specified width and height, and fills it with the specified color.

+ 80 - 0
helpers_test.go

@@ -0,0 +1,80 @@
+package imaging
+
+import (
+	"bytes"
+	"image"
+	"testing"
+)
+
+func compareNRGBA(img1, img2 *image.NRGBA, delta int) bool {
+	if !img1.Rect.Eq(img2.Rect) {
+		return false
+	}
+
+	if len(img1.Pix) != len(img2.Pix) {
+		return false
+	}
+
+	for i := 0; i < len(img1.Pix); i++ {
+		if absint(int(img1.Pix[i])-int(img2.Pix[i])) > delta {
+			return false
+		}
+	}
+
+	return true
+}
+
+func TestEncodeDecode(t *testing.T) {
+	imgWithAlpha := image.NewNRGBA(image.Rect(0, 0, 3, 3))
+	imgWithAlpha.Pix = []uint8{
+		0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+		127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138,
+		244, 245, 246, 247, 248, 249, 250, 252, 252, 253, 254, 255,
+	}
+
+	imgWithoutAlpha := image.NewNRGBA(image.Rect(0, 0, 3, 3))
+	imgWithoutAlpha.Pix = []uint8{
+		0, 1, 2, 255, 4, 5, 6, 255, 8, 9, 10, 255,
+		127, 128, 129, 255, 131, 132, 133, 255, 135, 136, 137, 255,
+		244, 245, 246, 255, 248, 249, 250, 255, 252, 253, 254, 255,
+	}
+
+	for _, format := range []Format{JPEG, PNG, GIF, BMP, TIFF} {
+		img := imgWithoutAlpha
+		if format == PNG {
+			img = imgWithAlpha
+		}
+
+		buf := &bytes.Buffer{}
+		err := Encode(buf, img, format)
+		if err != nil {
+			t.Errorf("fail encoding format %s", format)
+			continue
+		}
+
+		img2, err := Decode(buf)
+		if err != nil {
+			t.Errorf("fail decoding format %s", format)
+			continue
+		}
+		img2cloned := Clone(img2)
+
+		delta := 0
+		if format == JPEG {
+			delta = 3
+		} else if format == GIF {
+			delta = 16
+		}
+
+		if !compareNRGBA(img, img2cloned, delta) {
+			t.Errorf("fail comparing images format=%s %#v %#v", format, img, img2cloned)
+			continue
+		}
+	}
+
+	buf := &bytes.Buffer{}
+	err := Encode(buf, imgWithAlpha, Format(100))
+	if err != ErrUnsupportedFormat {
+		t.Errorf("expected ErrUnsupportedFormat")
+	}
+}