diff options
Diffstat (limited to 'vendor/github.com/disintegration/imaging/tools.go')
-rw-r--r-- | vendor/github.com/disintegration/imaging/tools.go | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/vendor/github.com/disintegration/imaging/tools.go b/vendor/github.com/disintegration/imaging/tools.go new file mode 100644 index 00000000..0ec19a03 --- /dev/null +++ b/vendor/github.com/disintegration/imaging/tools.go @@ -0,0 +1,249 @@ +package imaging + +import ( + "bytes" + "image" + "image/color" + "math" +) + +// New creates a new image with the specified width and height, and fills it with the specified color. +func New(width, height int, fillColor color.Color) *image.NRGBA { + if width <= 0 || height <= 0 { + return &image.NRGBA{} + } + + c := color.NRGBAModel.Convert(fillColor).(color.NRGBA) + if (c == color.NRGBA{0, 0, 0, 0}) { + return image.NewNRGBA(image.Rect(0, 0, width, height)) + } + + return &image.NRGBA{ + Pix: bytes.Repeat([]byte{c.R, c.G, c.B, c.A}, width*height), + Stride: 4 * width, + Rect: image.Rect(0, 0, width, height), + } +} + +// Clone returns a copy of the given image. +func Clone(img image.Image) *image.NRGBA { + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, src.w, src.h)) + size := src.w * 4 + parallel(0, src.h, func(ys <-chan int) { + for y := range ys { + i := y * dst.Stride + src.scan(0, y, src.w, y+1, dst.Pix[i:i+size]) + } + }) + return dst +} + +// Anchor is the anchor point for image alignment. +type Anchor int + +// Anchor point positions. +const ( + Center Anchor = iota + TopLeft + Top + TopRight + Left + Right + BottomLeft + Bottom + BottomRight +) + +func anchorPt(b image.Rectangle, w, h int, anchor Anchor) image.Point { + var x, y int + switch anchor { + case TopLeft: + x = b.Min.X + y = b.Min.Y + case Top: + x = b.Min.X + (b.Dx()-w)/2 + y = b.Min.Y + case TopRight: + x = b.Max.X - w + y = b.Min.Y + case Left: + x = b.Min.X + y = b.Min.Y + (b.Dy()-h)/2 + case Right: + x = b.Max.X - w + y = b.Min.Y + (b.Dy()-h)/2 + case BottomLeft: + x = b.Min.X + y = b.Max.Y - h + case Bottom: + x = b.Min.X + (b.Dx()-w)/2 + y = b.Max.Y - h + case BottomRight: + x = b.Max.X - w + y = b.Max.Y - h + default: + x = b.Min.X + (b.Dx()-w)/2 + y = b.Min.Y + (b.Dy()-h)/2 + } + return image.Pt(x, y) +} + +// Crop cuts out a rectangular region with the specified bounds +// from the image and returns the cropped image. +func Crop(img image.Image, rect image.Rectangle) *image.NRGBA { + r := rect.Intersect(img.Bounds()).Sub(img.Bounds().Min) + if r.Empty() { + return &image.NRGBA{} + } + src := newScanner(img) + dst := image.NewNRGBA(image.Rect(0, 0, r.Dx(), r.Dy())) + rowSize := r.Dx() * 4 + parallel(r.Min.Y, r.Max.Y, func(ys <-chan int) { + for y := range ys { + i := (y - r.Min.Y) * dst.Stride + src.scan(r.Min.X, y, r.Max.X, y+1, dst.Pix[i:i+rowSize]) + } + }) + return dst +} + +// CropAnchor cuts out a rectangular region with the specified size +// from the image using the specified anchor point and returns the cropped image. +func CropAnchor(img image.Image, width, height int, anchor Anchor) *image.NRGBA { + srcBounds := img.Bounds() + pt := anchorPt(srcBounds, width, height, anchor) + r := image.Rect(0, 0, width, height).Add(pt) + b := srcBounds.Intersect(r) + return Crop(img, b) +} + +// CropCenter cuts out a rectangular region with the specified size +// from the center of the image and returns the cropped image. +func CropCenter(img image.Image, width, height int) *image.NRGBA { + return CropAnchor(img, width, height, Center) +} + +// Paste pastes the img image to the background image at the specified position and returns the combined image. +func Paste(background, img image.Image, pos image.Point) *image.NRGBA { + dst := Clone(background) + pos = pos.Sub(background.Bounds().Min) + pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())} + interRect := pasteRect.Intersect(dst.Bounds()) + if interRect.Empty() { + return dst + } + src := newScanner(img) + parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) { + for y := range ys { + x1 := interRect.Min.X - pasteRect.Min.X + x2 := interRect.Max.X - pasteRect.Min.X + y1 := y - pasteRect.Min.Y + y2 := y1 + 1 + i1 := y*dst.Stride + interRect.Min.X*4 + i2 := i1 + interRect.Dx()*4 + src.scan(x1, y1, x2, y2, dst.Pix[i1:i2]) + } + }) + return dst +} + +// PasteCenter pastes the img image to the center of the background image and returns the combined image. +func PasteCenter(background, img image.Image) *image.NRGBA { + bgBounds := background.Bounds() + bgW := bgBounds.Dx() + bgH := bgBounds.Dy() + bgMinX := bgBounds.Min.X + bgMinY := bgBounds.Min.Y + + centerX := bgMinX + bgW/2 + centerY := bgMinY + bgH/2 + + x0 := centerX - img.Bounds().Dx()/2 + y0 := centerY - img.Bounds().Dy()/2 + + return Paste(background, img, image.Pt(x0, y0)) +} + +// Overlay draws the img image over the background image at given position +// and returns the combined image. Opacity parameter is the opacity of the img +// image layer, used to compose the images, it must be from 0.0 to 1.0. +// +// Examples: +// +// // Draw spriteImage over backgroundImage at the given position (x=50, y=50). +// dstImage := imaging.Overlay(backgroundImage, spriteImage, image.Pt(50, 50), 1.0) +// +// // Blend two opaque images of the same size. +// dstImage := imaging.Overlay(imageOne, imageTwo, image.Pt(0, 0), 0.5) +// +func Overlay(background, img image.Image, pos image.Point, opacity float64) *image.NRGBA { + opacity = math.Min(math.Max(opacity, 0.0), 1.0) // Ensure 0.0 <= opacity <= 1.0. + dst := Clone(background) + pos = pos.Sub(background.Bounds().Min) + pasteRect := image.Rectangle{Min: pos, Max: pos.Add(img.Bounds().Size())} + interRect := pasteRect.Intersect(dst.Bounds()) + if interRect.Empty() { + return dst + } + src := newScanner(img) + parallel(interRect.Min.Y, interRect.Max.Y, func(ys <-chan int) { + scanLine := make([]uint8, interRect.Dx()*4) + for y := range ys { + x1 := interRect.Min.X - pasteRect.Min.X + x2 := interRect.Max.X - pasteRect.Min.X + y1 := y - pasteRect.Min.Y + y2 := y1 + 1 + src.scan(x1, y1, x2, y2, scanLine) + i := y*dst.Stride + interRect.Min.X*4 + j := 0 + for x := interRect.Min.X; x < interRect.Max.X; x++ { + d := dst.Pix[i : i+4 : i+4] + r1 := float64(d[0]) + g1 := float64(d[1]) + b1 := float64(d[2]) + a1 := float64(d[3]) + + s := scanLine[j : j+4 : j+4] + r2 := float64(s[0]) + g2 := float64(s[1]) + b2 := float64(s[2]) + a2 := float64(s[3]) + + coef2 := opacity * a2 / 255 + coef1 := (1 - coef2) * a1 / 255 + coefSum := coef1 + coef2 + coef1 /= coefSum + coef2 /= coefSum + + d[0] = uint8(r1*coef1 + r2*coef2) + d[1] = uint8(g1*coef1 + g2*coef2) + d[2] = uint8(b1*coef1 + b2*coef2) + d[3] = uint8(math.Min(a1+a2*opacity*(255-a1)/255, 255)) + + i += 4 + j += 4 + } + } + }) + return dst +} + +// OverlayCenter overlays the img image to the center of the background image and +// returns the combined image. Opacity parameter is the opacity of the img +// image layer, used to compose the images, it must be from 0.0 to 1.0. +func OverlayCenter(background, img image.Image, opacity float64) *image.NRGBA { + bgBounds := background.Bounds() + bgW := bgBounds.Dx() + bgH := bgBounds.Dy() + bgMinX := bgBounds.Min.X + bgMinY := bgBounds.Min.Y + + centerX := bgMinX + bgW/2 + centerY := bgMinY + bgH/2 + + x0 := centerX - img.Bounds().Dx()/2 + y0 := centerY - img.Bounds().Dy()/2 + + return Overlay(background, img, image.Point{x0, y0}, opacity) +} |