ires 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }