ires 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +111 -0
  4. data/Rakefile +30 -0
  5. data/ext/Gopkg.lock +21 -0
  6. data/ext/Gopkg.toml +30 -0
  7. data/ext/main.go +90 -0
  8. data/ext/operate/image.go +139 -0
  9. data/ext/util/uri/uri.go +96 -0
  10. data/ext/vendor/github.com/nfnt/resize/LICENSE +13 -0
  11. data/ext/vendor/github.com/nfnt/resize/README.md +149 -0
  12. data/ext/vendor/github.com/nfnt/resize/converter.go +438 -0
  13. data/ext/vendor/github.com/nfnt/resize/converter_test.go +43 -0
  14. data/ext/vendor/github.com/nfnt/resize/filters.go +143 -0
  15. data/ext/vendor/github.com/nfnt/resize/nearest.go +318 -0
  16. data/ext/vendor/github.com/nfnt/resize/nearest_test.go +57 -0
  17. data/ext/vendor/github.com/nfnt/resize/resize.go +614 -0
  18. data/ext/vendor/github.com/nfnt/resize/resize_test.go +330 -0
  19. data/ext/vendor/github.com/nfnt/resize/thumbnail.go +55 -0
  20. data/ext/vendor/github.com/nfnt/resize/thumbnail_test.go +47 -0
  21. data/ext/vendor/github.com/nfnt/resize/ycc.go +227 -0
  22. data/ext/vendor/github.com/nfnt/resize/ycc_test.go +214 -0
  23. data/ext/vendor/github.com/oliamb/cutter/LICENSE +20 -0
  24. data/ext/vendor/github.com/oliamb/cutter/README.md +88 -0
  25. data/ext/vendor/github.com/oliamb/cutter/benchmark_test.go +54 -0
  26. data/ext/vendor/github.com/oliamb/cutter/cutter.go +183 -0
  27. data/ext/vendor/github.com/oliamb/cutter/cutter/main.go +68 -0
  28. data/ext/vendor/github.com/oliamb/cutter/cutter_test.go +267 -0
  29. data/ext/vendor/github.com/oliamb/cutter/example_test.go +35 -0
  30. data/ext/vendor/github.com/oliamb/cutter/fixtures/gopher.jpg +0 -0
  31. data/lib/ires.rb +4 -0
  32. data/lib/ires/engine.rb +7 -0
  33. data/lib/ires/service.rb +19 -0
  34. data/lib/ires/util.rb +39 -0
  35. data/lib/ires/version.rb +3 -0
  36. data/lib/ires/view_helper.rb +42 -0
  37. data/lib/tasks/ires.rake +11 -0
  38. data/shared/darwin/ires.h +64 -0
  39. data/shared/darwin/ires.so +0 -0
  40. data/shared/linux/ires.h +64 -0
  41. data/shared/linux/ires.so +0 -0
  42. metadata +154 -0
@@ -0,0 +1,214 @@
1
+ /*
2
+ Copyright (c) 2014, Charlie Vieth <charlie.vieth@gmail.com>
3
+
4
+ Permission to use, copy, modify, and/or distribute this software for any purpose
5
+ with or without fee is hereby granted, provided that the above copyright notice
6
+ and this permission notice appear in all copies.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
10
+ FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
12
+ OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
13
+ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
14
+ THIS SOFTWARE.
15
+ */
16
+
17
+ package resize
18
+
19
+ import (
20
+ "image"
21
+ "image/color"
22
+ "testing"
23
+ )
24
+
25
+ type Image interface {
26
+ image.Image
27
+ SubImage(image.Rectangle) image.Image
28
+ }
29
+
30
+ func TestImage(t *testing.T) {
31
+ testImage := []Image{
32
+ newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio420),
33
+ newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio422),
34
+ newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio440),
35
+ newYCC(image.Rect(0, 0, 10, 10), image.YCbCrSubsampleRatio444),
36
+ }
37
+ for _, m := range testImage {
38
+ if !image.Rect(0, 0, 10, 10).Eq(m.Bounds()) {
39
+ t.Errorf("%T: want bounds %v, got %v",
40
+ m, image.Rect(0, 0, 10, 10), m.Bounds())
41
+ continue
42
+ }
43
+ m = m.SubImage(image.Rect(3, 2, 9, 8)).(Image)
44
+ if !image.Rect(3, 2, 9, 8).Eq(m.Bounds()) {
45
+ t.Errorf("%T: sub-image want bounds %v, got %v",
46
+ m, image.Rect(3, 2, 9, 8), m.Bounds())
47
+ continue
48
+ }
49
+ // Test that taking an empty sub-image starting at a corner does not panic.
50
+ m.SubImage(image.Rect(0, 0, 0, 0))
51
+ m.SubImage(image.Rect(10, 0, 10, 0))
52
+ m.SubImage(image.Rect(0, 10, 0, 10))
53
+ m.SubImage(image.Rect(10, 10, 10, 10))
54
+ }
55
+ }
56
+
57
+ func TestConvertYCbCr(t *testing.T) {
58
+ testImage := []Image{
59
+ image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio420),
60
+ image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio422),
61
+ image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio440),
62
+ image.NewYCbCr(image.Rect(0, 0, 50, 50), image.YCbCrSubsampleRatio444),
63
+ }
64
+
65
+ for _, img := range testImage {
66
+ m := img.(*image.YCbCr)
67
+ for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
68
+ for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
69
+ yi := m.YOffset(x, y)
70
+ ci := m.COffset(x, y)
71
+ m.Y[yi] = uint8(16*y + x)
72
+ m.Cb[ci] = uint8(y + 16*x)
73
+ m.Cr[ci] = uint8(y + 16*x)
74
+ }
75
+ }
76
+
77
+ // test conversion from YCbCr to ycc
78
+ yc := imageYCbCrToYCC(m)
79
+ for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
80
+ for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
81
+ ystride := 3 * (m.Rect.Max.X - m.Rect.Min.X)
82
+ xstride := 3
83
+ yi := m.YOffset(x, y)
84
+ ci := m.COffset(x, y)
85
+ si := (y * ystride) + (x * xstride)
86
+ if m.Y[yi] != yc.Pix[si] {
87
+ t.Errorf("Err Y - found: %d expected: %d x: %d y: %d yi: %d si: %d",
88
+ m.Y[yi], yc.Pix[si], x, y, yi, si)
89
+ }
90
+ if m.Cb[ci] != yc.Pix[si+1] {
91
+ t.Errorf("Err Cb - found: %d expected: %d x: %d y: %d ci: %d si: %d",
92
+ m.Cb[ci], yc.Pix[si+1], x, y, ci, si+1)
93
+ }
94
+ if m.Cr[ci] != yc.Pix[si+2] {
95
+ t.Errorf("Err Cr - found: %d expected: %d x: %d y: %d ci: %d si: %d",
96
+ m.Cr[ci], yc.Pix[si+2], x, y, ci, si+2)
97
+ }
98
+ }
99
+ }
100
+
101
+ // test conversion from ycc back to YCbCr
102
+ ym := yc.YCbCr()
103
+ for y := m.Rect.Min.Y; y < m.Rect.Max.Y; y++ {
104
+ for x := m.Rect.Min.X; x < m.Rect.Max.X; x++ {
105
+ yi := m.YOffset(x, y)
106
+ ci := m.COffset(x, y)
107
+ if m.Y[yi] != ym.Y[yi] {
108
+ t.Errorf("Err Y - found: %d expected: %d x: %d y: %d yi: %d",
109
+ m.Y[yi], ym.Y[yi], x, y, yi)
110
+ }
111
+ if m.Cb[ci] != ym.Cb[ci] {
112
+ t.Errorf("Err Cb - found: %d expected: %d x: %d y: %d ci: %d",
113
+ m.Cb[ci], ym.Cb[ci], x, y, ci)
114
+ }
115
+ if m.Cr[ci] != ym.Cr[ci] {
116
+ t.Errorf("Err Cr - found: %d expected: %d x: %d y: %d ci: %d",
117
+ m.Cr[ci], ym.Cr[ci], x, y, ci)
118
+ }
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ func TestYCbCr(t *testing.T) {
125
+ rects := []image.Rectangle{
126
+ image.Rect(0, 0, 16, 16),
127
+ image.Rect(1, 0, 16, 16),
128
+ image.Rect(0, 1, 16, 16),
129
+ image.Rect(1, 1, 16, 16),
130
+ image.Rect(1, 1, 15, 16),
131
+ image.Rect(1, 1, 16, 15),
132
+ image.Rect(1, 1, 15, 15),
133
+ image.Rect(2, 3, 14, 15),
134
+ image.Rect(7, 0, 7, 16),
135
+ image.Rect(0, 8, 16, 8),
136
+ image.Rect(0, 0, 10, 11),
137
+ image.Rect(5, 6, 16, 16),
138
+ image.Rect(7, 7, 8, 8),
139
+ image.Rect(7, 8, 8, 9),
140
+ image.Rect(8, 7, 9, 8),
141
+ image.Rect(8, 8, 9, 9),
142
+ image.Rect(7, 7, 17, 17),
143
+ image.Rect(8, 8, 17, 17),
144
+ image.Rect(9, 9, 17, 17),
145
+ image.Rect(10, 10, 17, 17),
146
+ }
147
+ subsampleRatios := []image.YCbCrSubsampleRatio{
148
+ image.YCbCrSubsampleRatio444,
149
+ image.YCbCrSubsampleRatio422,
150
+ image.YCbCrSubsampleRatio420,
151
+ image.YCbCrSubsampleRatio440,
152
+ }
153
+ deltas := []image.Point{
154
+ image.Pt(0, 0),
155
+ image.Pt(1000, 1001),
156
+ image.Pt(5001, -400),
157
+ image.Pt(-701, -801),
158
+ }
159
+ for _, r := range rects {
160
+ for _, subsampleRatio := range subsampleRatios {
161
+ for _, delta := range deltas {
162
+ testYCbCr(t, r, subsampleRatio, delta)
163
+ }
164
+ }
165
+ if testing.Short() {
166
+ break
167
+ }
168
+ }
169
+ }
170
+
171
+ func testYCbCr(t *testing.T, r image.Rectangle, subsampleRatio image.YCbCrSubsampleRatio, delta image.Point) {
172
+ // Create a YCbCr image m, whose bounds are r translated by (delta.X, delta.Y).
173
+ r1 := r.Add(delta)
174
+ img := image.NewYCbCr(r1, subsampleRatio)
175
+
176
+ // Initialize img's pixels. For 422 and 420 subsampling, some of the Cb and Cr elements
177
+ // will be set multiple times. That's OK. We just want to avoid a uniform image.
178
+ for y := r1.Min.Y; y < r1.Max.Y; y++ {
179
+ for x := r1.Min.X; x < r1.Max.X; x++ {
180
+ yi := img.YOffset(x, y)
181
+ ci := img.COffset(x, y)
182
+ img.Y[yi] = uint8(16*y + x)
183
+ img.Cb[ci] = uint8(y + 16*x)
184
+ img.Cr[ci] = uint8(y + 16*x)
185
+ }
186
+ }
187
+
188
+ m := imageYCbCrToYCC(img)
189
+
190
+ // Make various sub-images of m.
191
+ for y0 := delta.Y + 3; y0 < delta.Y+7; y0++ {
192
+ for y1 := delta.Y + 8; y1 < delta.Y+13; y1++ {
193
+ for x0 := delta.X + 3; x0 < delta.X+7; x0++ {
194
+ for x1 := delta.X + 8; x1 < delta.X+13; x1++ {
195
+ subRect := image.Rect(x0, y0, x1, y1)
196
+ sub := m.SubImage(subRect).(*ycc)
197
+
198
+ // For each point in the sub-image's bounds, check that m.At(x, y) equals sub.At(x, y).
199
+ for y := sub.Rect.Min.Y; y < sub.Rect.Max.Y; y++ {
200
+ for x := sub.Rect.Min.X; x < sub.Rect.Max.X; x++ {
201
+ color0 := m.At(x, y).(color.YCbCr)
202
+ color1 := sub.At(x, y).(color.YCbCr)
203
+ if color0 != color1 {
204
+ t.Errorf("r=%v, subsampleRatio=%v, delta=%v, x=%d, y=%d, color0=%v, color1=%v",
205
+ r, subsampleRatio, delta, x, y, color0, color1)
206
+ return
207
+ }
208
+ }
209
+ }
210
+ }
211
+ }
212
+ }
213
+ }
214
+ }
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Olivier Amblet
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,88 @@
1
+ Cutter
2
+ ======
3
+
4
+ [![Build Status](https://travis-ci.org/oliamb/cutter.png?branch=master)](https://travis-ci.org/oliamb/cutter)
5
+ [![GoDoc](https://godoc.org/github.com/oliamb/cutter?status.png)](https://godoc.org/github.com/oliamb/cutter)
6
+
7
+ What is it?
8
+ -----------
9
+ A Go library to crop images.
10
+
11
+ Cutter was initially developped to be able
12
+ to crop image resized using github.com/nfnt/resize.
13
+
14
+ Usage
15
+ -----
16
+
17
+ Read the doc on https://godoc.org/github.com/oliamb/cutter
18
+
19
+ Import package with
20
+
21
+ import "github.com/oliamb/cutter"
22
+
23
+ Package cutter provides a function to crop image.
24
+
25
+ By default, the original image will be cropped at the
26
+ given size from the top left corner.
27
+
28
+ croppedImg, err := cutter.Crop(img, cutter.Config{
29
+ Width: 250,
30
+ Height: 500,
31
+ })
32
+
33
+ It is possible to specify the top left position:
34
+
35
+ croppedImg, err := cutter.Crop(img, cutter.Config{
36
+ Width: 250,
37
+ Height: 500,
38
+ Anchor: image.Point{100, 100},
39
+ Mode: TopLeft, // optional, default value
40
+ })
41
+
42
+ The Anchor property can represents the center of the cropped image
43
+ instead of the top left corner:
44
+
45
+
46
+ croppedImg, err := cutter.Crop(img, cutter.Config{
47
+ Width: 250,
48
+ Height: 500,
49
+ Mode: Centered,
50
+ })
51
+
52
+ The default crop use the specified dimension, but it is possible
53
+ to use Width and Heigth as a ratio instead. In this case,
54
+ the resulting image will be as big as possible to fit the asked ratio
55
+ from the anchor position.
56
+
57
+ croppedImg, err := cutter.Crop(baseImage, cutter.Config{
58
+ Width: 4,
59
+ Height: 3,
60
+ Mode: Centered,
61
+ Options: Ratio,
62
+ })
63
+
64
+ About resize
65
+ ------------
66
+ This lib only manage crop and won't resize image, but it works great in combination with [github.com/nfnt/resize](https://github.com/nfnt/resize)
67
+
68
+ Contributing
69
+ ------------
70
+ I'd love to see your contributions to Cutter. If you'd like to hack on it:
71
+
72
+ - fork the project,
73
+ - hack on it,
74
+ - ensure tests pass,
75
+ - make a pull request
76
+
77
+ If you plan to modify the API, let's disscuss it first.
78
+
79
+ Licensing
80
+ ---------
81
+ MIT License, Please see the file called LICENSE.
82
+
83
+ Credits
84
+ -------
85
+ Test Picture: Gopher picture from Heidi Schuyt, http://www.flickr.com/photos/hschuyt/7674222278/,
86
+ © copyright Creative Commons(http://creativecommons.org/licenses/by-nc-sa/2.0/)
87
+
88
+ Thanks to Urturn(http://www.urturn.com) for the time allocated to develop the library.
@@ -0,0 +1,54 @@
1
+ package cutter
2
+
3
+ import (
4
+ "image"
5
+ "testing"
6
+ )
7
+
8
+ /*
9
+ BenchmarkCrop is used to track the Crop with sharing memory.
10
+
11
+ On my laptop, the required time is lower than 0.00 ns/op.
12
+ With a bigger, size it would probably not increase as
13
+ nothing is copied.
14
+ */
15
+ func BenchmarkCrop(b *testing.B) {
16
+ img := getImage()
17
+
18
+ c := Config{
19
+ Width: 1000,
20
+ Height: 1000,
21
+ Mode: TopLeft,
22
+ Anchor: image.Point{100, 100},
23
+ }
24
+ b.ResetTimer()
25
+ r, _ := Crop(img, c)
26
+ r.Bounds()
27
+ }
28
+
29
+ /*
30
+ BenchmarkCropCopy is used to track the Crop with copy performance.
31
+
32
+ Below are the actual result on my laptop given each
33
+ optimization suggested by Nigel Tao: https://groups.google.com/forum/#!topic/golang-nuts/qxSpOOp1QOk
34
+
35
+ 1. initial time on my Laptop: 23 ns/op
36
+ 2. after inverting x and y in copy loop: 0.09 ns/op
37
+ 3. after removing useless call to ColorModel().Convert(): 0.08 ns/op
38
+ 4. after replacing the two 'pixel' loops by a call to draw.Draw
39
+ to obtains the cropped image: 0.04 ns/op
40
+ */
41
+ func BenchmarkCropCopy(b *testing.B) {
42
+ img := getImage()
43
+
44
+ c := Config{
45
+ Width: 1000,
46
+ Height: 1000,
47
+ Mode: TopLeft,
48
+ Anchor: image.Point{100, 100},
49
+ Options: Copy,
50
+ }
51
+ b.ResetTimer()
52
+ r, _ := Crop(img, c)
53
+ r.Bounds()
54
+ }
@@ -0,0 +1,183 @@
1
+ /*
2
+ Package cutter provides a function to crop image.
3
+
4
+ By default, the original image will be cropped at the
5
+ given size from the top left corner.
6
+
7
+ croppedImg, err := cutter.Crop(img, cutter.Config{
8
+ Width: 250,
9
+ Height: 500,
10
+ })
11
+
12
+ It is possible to specify the top left position:
13
+
14
+ croppedImg, err := cutter.Crop(img, cutter.Config{
15
+ Width: 250,
16
+ Height: 500,
17
+ Anchor: image.Point{100, 100},
18
+ Mode: TopLeft, // optional, default value
19
+ })
20
+
21
+ The Anchor property can represents the center of the cropped image
22
+ instead of the top left corner:
23
+
24
+
25
+ croppedImg, err := cutter.Crop(img, cutter.Config{
26
+ Width: 250,
27
+ Height: 500,
28
+ Mode: Centered,
29
+ })
30
+
31
+ The default crop use the specified dimension, but it is possible
32
+ to use Width and Heigth as a ratio instead. In this case,
33
+ the resulting image will be as big as possible to fit the asked ratio
34
+ from the anchor position.
35
+
36
+ croppedImg, err := cutter.Crop(baseImage, cutter.Config{
37
+ Width: 4,
38
+ Height: 3,
39
+ Mode: Centered,
40
+ Options: Ratio,
41
+ })
42
+ */
43
+ package cutter
44
+
45
+ import (
46
+ "image"
47
+ "image/draw"
48
+ )
49
+
50
+ // Config is used to defined
51
+ // the way the crop should be realized.
52
+ type Config struct {
53
+ Width, Height int
54
+ Anchor image.Point // The Anchor Point in the source image
55
+ Mode AnchorMode // Which point in the resulting image the Anchor Point is referring to
56
+ Options Option
57
+ }
58
+
59
+ // AnchorMode is an enumeration of the position an anchor can represent.
60
+ type AnchorMode int
61
+
62
+ const (
63
+ // TopLeft defines the Anchor Point
64
+ // as the top left of the cropped picture.
65
+ TopLeft AnchorMode = iota
66
+ // Centered defines the Anchor Point
67
+ // as the center of the cropped picture.
68
+ Centered = iota
69
+ )
70
+
71
+ // Option flags to modify the way the crop is done.
72
+ type Option int
73
+
74
+ const (
75
+ // Ratio flag is use when Width and Height
76
+ // must be used to compute a ratio rather
77
+ // than absolute size in pixels.
78
+ Ratio Option = 1 << iota
79
+ // Copy flag is used to enforce the function
80
+ // to retrieve a copy of the selected pixels.
81
+ // This disable the use of SubImage method
82
+ // to compute the result.
83
+ Copy = 1 << iota
84
+ )
85
+
86
+ // An interface that is
87
+ // image.Image + SubImage method.
88
+ type subImageSupported interface {
89
+ SubImage(r image.Rectangle) image.Image
90
+ }
91
+
92
+ // Crop retrieves an image that is a
93
+ // cropped copy of the original img.
94
+ //
95
+ // The crop is made given the informations provided in config.
96
+ func Crop(img image.Image, c Config) (image.Image, error) {
97
+ maxBounds := c.maxBounds(img.Bounds())
98
+ size := c.computeSize(maxBounds, image.Point{c.Width, c.Height})
99
+ cr := c.computedCropArea(img.Bounds(), size)
100
+ cr = img.Bounds().Intersect(cr)
101
+
102
+ if c.Options&Copy == Copy {
103
+ return cropWithCopy(img, cr)
104
+ }
105
+ if dImg, ok := img.(subImageSupported); ok {
106
+ return dImg.SubImage(cr), nil
107
+ }
108
+ return cropWithCopy(img, cr)
109
+ }
110
+
111
+ func cropWithCopy(img image.Image, cr image.Rectangle) (image.Image, error) {
112
+ result := image.NewRGBA(cr)
113
+ draw.Draw(result, cr, img, cr.Min, draw.Src)
114
+ return result, nil
115
+ }
116
+
117
+ func (c Config) maxBounds(bounds image.Rectangle) (r image.Rectangle) {
118
+ if c.Mode == Centered {
119
+ anchor := c.centeredMin(bounds)
120
+ w := min(anchor.X-bounds.Min.X, bounds.Max.X-anchor.X)
121
+ h := min(anchor.Y-bounds.Min.Y, bounds.Max.Y-anchor.Y)
122
+ r = image.Rect(anchor.X-w, anchor.Y-h, anchor.X+w, anchor.Y+h)
123
+ } else {
124
+ r = image.Rect(c.Anchor.X, c.Anchor.Y, bounds.Max.X, bounds.Max.Y)
125
+ }
126
+ return
127
+ }
128
+
129
+ // computeSize retrieve the effective size of the cropped image.
130
+ // It is defined by Height, Width, and Ratio option.
131
+ func (c Config) computeSize(bounds image.Rectangle, ratio image.Point) (p image.Point) {
132
+ if c.Options&Ratio == Ratio {
133
+ // Ratio option is on, so we take the biggest size available that fit the given ratio.
134
+ if float64(ratio.X)/float64(bounds.Dx()) > float64(ratio.Y)/float64(bounds.Dy()) {
135
+ p = image.Point{bounds.Dx(), (bounds.Dx() / ratio.X) * ratio.Y}
136
+ } else {
137
+ p = image.Point{(bounds.Dy() / ratio.Y) * ratio.X, bounds.Dy()}
138
+ }
139
+ } else {
140
+ p = image.Point{ratio.X, ratio.Y}
141
+ }
142
+ return
143
+ }
144
+
145
+ // computedCropArea retrieve the theorical crop area.
146
+ // It is defined by Height, Width, Mode and
147
+ func (c Config) computedCropArea(bounds image.Rectangle, size image.Point) (r image.Rectangle) {
148
+ min := bounds.Min
149
+ switch c.Mode {
150
+ case Centered:
151
+ rMin := c.centeredMin(bounds)
152
+ r = image.Rect(rMin.X-size.X/2, rMin.Y-size.Y/2, rMin.X+size.X/2, rMin.Y+size.Y/2)
153
+ default: // TopLeft
154
+ rMin := image.Point{min.X + c.Anchor.X, min.Y + c.Anchor.Y}
155
+ r = image.Rect(rMin.X, rMin.Y, rMin.X+size.X, rMin.Y+size.Y)
156
+ }
157
+ return
158
+ }
159
+
160
+ func (c *Config) centeredMin(bounds image.Rectangle) (rMin image.Point) {
161
+ min := bounds.Min
162
+ if c.Anchor.X == 0 && c.Anchor.Y == 0 {
163
+ rMin = image.Point{
164
+ X: min.X + bounds.Dx()/2,
165
+ Y: min.Y + bounds.Dy()/2,
166
+ }
167
+ } else {
168
+ rMin = image.Point{
169
+ X: min.X + c.Anchor.X,
170
+ Y: min.Y + c.Anchor.Y,
171
+ }
172
+ }
173
+ return
174
+ }
175
+
176
+ func min(a, b int) (r int) {
177
+ if a < b {
178
+ r = a
179
+ } else {
180
+ r = b
181
+ }
182
+ return
183
+ }