From ac44a6865afc9674ae272c26807abfed64ed49c1 Mon Sep 17 00:00:00 2001 From: Johan Brandhorst Date: Tue, 18 Jul 2017 22:16:02 +0100 Subject: [PATCH] Add crop package --- crop/LICENSE | 20 +++++++++++++++ crop/README.md | 4 +++ crop/crop.go | 56 +++++++++++++++++++++++++++++++++++++++++ crop/crop_suite_test.go | 13 ++++++++++ crop/crop_test.go | 34 +++++++++++++++++++++++++ 5 files changed, 127 insertions(+) create mode 100644 crop/LICENSE create mode 100644 crop/README.md create mode 100644 crop/crop.go create mode 100644 crop/crop_suite_test.go create mode 100644 crop/crop_test.go diff --git a/crop/LICENSE b/crop/LICENSE new file mode 100644 index 0000000..5412782 --- /dev/null +++ b/crop/LICENSE @@ -0,0 +1,20 @@ +The MIT License (MIT) + +Copyright (c) 2014 Olivier Amblet + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/crop/README.md b/crop/README.md new file mode 100644 index 0000000..f165d77 --- /dev/null +++ b/crop/README.md @@ -0,0 +1,4 @@ +## Crop + +This is a modified version of https://github.com/oliamb/cutter. +License is detailed in LICENSE. diff --git a/crop/crop.go b/crop/crop.go new file mode 100644 index 0000000..a95967f --- /dev/null +++ b/crop/crop.go @@ -0,0 +1,56 @@ +package crop + +import ( + "image" + "image/draw" +) + +// An interface that is +// image.Image + SubImage method. +type subImageSupported interface { + SubImage(r image.Rectangle) image.Image +} + +// CropperFunc exposes a Crop function that calls itself +type CropperFunc func(img image.Image, width, height int, anchor image.Point) (image.Image, error) + +// Crop calls the CropperFunc +func (c CropperFunc) Crop(img image.Image, width, height int, anchor image.Point) (image.Image, error) { + return c(img, width, height, anchor) +} + +// Crop retrieves an image that is a +// cropped copy of the original img. +func Crop(img image.Image, width, height int, anchor image.Point) (image.Image, error) { + maxBounds := maxBounds(anchor, img.Bounds()) + size := computeSize(maxBounds, image.Point{width, height}) + cr := computedCropArea(anchor, img.Bounds(), size) + cr = img.Bounds().Intersect(cr) + + if dImg, ok := img.(subImageSupported); ok { + return dImg.SubImage(cr), nil + } + return cropWithCopy(img, cr) +} + +func cropWithCopy(img image.Image, cr image.Rectangle) (image.Image, error) { + result := image.NewRGBA(cr) + draw.Draw(result, cr, img, cr.Min, draw.Src) + return result, nil +} + +func maxBounds(anchor image.Point, bounds image.Rectangle) image.Rectangle { + return image.Rect(anchor.X, anchor.Y, bounds.Max.X, bounds.Max.Y) +} + +// computeSize retrieve the effective size of the cropped image. +func computeSize(bounds image.Rectangle, ratio image.Point) image.Point { + return image.Point{ratio.X, ratio.Y} +} + +// computedCropArea retrieve the theorical crop area. +func computedCropArea(anchor image.Point, bounds image.Rectangle, size image.Point) (r image.Rectangle) { + min := bounds.Min + rMin := image.Point{min.X + anchor.X, min.Y + anchor.Y} + return image.Rect(rMin.X, rMin.Y, rMin.X+size.X, rMin.Y+size.Y) +} diff --git a/crop/crop_suite_test.go b/crop/crop_suite_test.go new file mode 100644 index 0000000..410205d --- /dev/null +++ b/crop/crop_suite_test.go @@ -0,0 +1,13 @@ +package crop_test + +import ( + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + + "testing" +) + +func TestCrop(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Crop Suite") +} diff --git a/crop/crop_test.go b/crop/crop_test.go new file mode 100644 index 0000000..099940e --- /dev/null +++ b/crop/crop_test.go @@ -0,0 +1,34 @@ +package crop + +import ( + "image" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +var _ = Describe("Crop", func() { + It("crops the image", func() { + r, err := Crop(getImage(), 512, 400, image.Point{}) + Expect(err).NotTo(HaveOccurred()) + Expect(r.Bounds().Dx()).To(Equal(512)) + Expect(r.Bounds().Dy()).To(Equal(400)) + Expect(r.Bounds().Min.X).To(Equal(0)) + Expect(r.Bounds().Min.Y).To(Equal(0)) + }) + + Context("when a different anchor point is used", func() { + It("crops the image", func() { + r, err := Crop(getImage(), 512, 400, image.Point{X: 100, Y: 50}) + Expect(err).NotTo(HaveOccurred()) + Expect(r.Bounds().Dx()).To(Equal(512)) + Expect(r.Bounds().Dy()).To(Equal(400)) + Expect(r.Bounds().Min.X).To(Equal(100)) + Expect(r.Bounds().Min.Y).To(Equal(50)) + }) + }) +}) + +func getImage() image.Image { + return image.NewGray(image.Rect(0, 0, 1600, 1437)) +}