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,68 @@
1
+ package main
2
+
3
+ import (
4
+ "errors"
5
+ "flag"
6
+ "fmt"
7
+ "github.com/oliamb/cutter"
8
+ "image"
9
+ "image/jpeg"
10
+ "image/png"
11
+ "log"
12
+ "os"
13
+ "path/filepath"
14
+ )
15
+
16
+ func main() {
17
+ s := len(os.Args)
18
+ if s < 4 {
19
+ usage()
20
+ return
21
+ }
22
+ fmt.Println("Args", os.Args)
23
+
24
+ inPath := os.Args[s-2]
25
+ fi, err := os.Open(inPath)
26
+ if err != nil {
27
+ log.Fatal("Cannot open input file '", inPath, "':", err)
28
+ }
29
+
30
+ outPath := os.Args[s-1]
31
+ fo, err := os.Create(outPath)
32
+ if err != nil {
33
+ log.Fatal("Cannot create output file '", outPath, "':", err)
34
+ }
35
+
36
+ img, _, err := image.Decode(fi)
37
+ if err != nil {
38
+ log.Fatal("Cannot decode image at '", inPath, "':", err)
39
+ }
40
+
41
+ cImg, err := cutter.Crop(img, cutter.Config{
42
+ Height: 1000, // height in pixel or Y ratio(see Ratio Option below)
43
+ Width: 1000, // width in pixel or X ratio
44
+ Mode: cutter.TopLeft, // Accepted Mode: TopLeft, Centered
45
+ Anchor: image.Point{100, 100}, // Position of the top left point
46
+ Options: 0, // Accepted Option: Ratio
47
+ })
48
+ if err != nil {
49
+ log.Fatal("Cannot crop image:", err)
50
+ }
51
+
52
+ switch filepath.Ext(outPath) {
53
+ case ".png":
54
+ err = png.Encode(fo, cImg)
55
+ case ".jpg":
56
+ err = jpeg.Encode(fo, cImg, &jpeg.Options{})
57
+ default:
58
+ err = errors.New("Unsupported format: " + filepath.Ext(outPath))
59
+ }
60
+ if err != nil {
61
+ log.Fatal(err)
62
+ }
63
+ fmt.Println("Image saved to", outPath)
64
+ }
65
+
66
+ func usage() {
67
+ flag.Usage()
68
+ }
@@ -0,0 +1,267 @@
1
+ package cutter
2
+
3
+ import (
4
+ "image"
5
+ "os"
6
+ "testing"
7
+ )
8
+
9
+ func TestCrop(t *testing.T) {
10
+ img := getImage()
11
+
12
+ c := Config{
13
+ Width: 512,
14
+ Height: 400,
15
+ }
16
+ r, err := Crop(img, c)
17
+ if err != nil {
18
+ t.Fatal(err)
19
+ }
20
+ if r.Bounds().Dx() != 512 {
21
+ t.Error("Bad width should be 512 but is", r.Bounds().Dx())
22
+ }
23
+ if r.Bounds().Dy() != 400 {
24
+ t.Error("Bad width should be 400 but is", r.Bounds().Dy())
25
+ }
26
+ if r.Bounds().Min.X != 0 {
27
+ t.Error("Invalid Bounds Min X", r.Bounds().Min.X)
28
+ }
29
+ if r.Bounds().Min.Y != 0 {
30
+ t.Error("Invalid Bounds Min Y", r.Bounds().Min.Y)
31
+ }
32
+ }
33
+
34
+ func TestCrop_Centered(t *testing.T) {
35
+ img := getImage()
36
+
37
+ c := Config{
38
+ Width: 512,
39
+ Height: 400,
40
+ Mode: Centered,
41
+ }
42
+ r, err := Crop(img, c)
43
+ if err != nil {
44
+ t.Fatal(err)
45
+ }
46
+ if r.Bounds().Dx() != 512 {
47
+ t.Error("Bad width should be 512 but is", r.Bounds().Dx())
48
+ }
49
+ if r.Bounds().Dy() != 400 {
50
+ t.Error("Bad width should be 512 but is", r.Bounds().Dy())
51
+ }
52
+ if r.Bounds().Min.X != 544 {
53
+ t.Error("Invalid Bounds Min X", r.Bounds().Min.X)
54
+ }
55
+ if r.Bounds().Min.Y != 518 {
56
+ t.Error("Invalid Bounds Min Y", r.Bounds().Min.Y)
57
+ }
58
+ }
59
+
60
+ func TestCrop_Centered_Ratio_WithoutAnchorPosition(t *testing.T) {
61
+ // (0,0)-(64,64) 32 64 (32,32)-(32,32)
62
+ img := image.NewGray(image.Rect(0, 0, 64, 64))
63
+ c := Config{
64
+ Width: 32,
65
+ Height: 64,
66
+ Mode: Centered,
67
+ Options: Ratio,
68
+ }
69
+ r, err := Crop(img, c)
70
+ if err != nil {
71
+ t.Fatal(err)
72
+ }
73
+ if r.Bounds().Dx() != 32 {
74
+ t.Error("Bad Width", r.Bounds().Dx())
75
+ }
76
+ if r.Bounds().Dy() != 64 {
77
+ t.Error("Bad Height", r.Bounds().Dy())
78
+ }
79
+ if r.Bounds().Min.X != 16 {
80
+ t.Error("Invalid Bounds Min X", r.Bounds().Min.X)
81
+ }
82
+ if r.Bounds().Min.Y != 0 {
83
+ t.Error("Invalid Bounds Min Y", r.Bounds().Min.Y)
84
+ }
85
+ }
86
+
87
+ func TestCutter_Crop_TooBigArea(t *testing.T) {
88
+ img := getImage()
89
+
90
+ c := Config{
91
+ Width: 2000,
92
+ Height: 2000,
93
+ Anchor: image.Point{100, 100},
94
+ }
95
+ r, err := Crop(img, c)
96
+ if err != nil {
97
+ t.Fatal(err)
98
+ }
99
+ if r.Bounds().Dx() != 1500 {
100
+ t.Error("Bad width should be 1500 but is", r.Bounds().Dx())
101
+ }
102
+ if r.Bounds().Dy() != 1337 {
103
+ t.Error("Bad width should be 1337 but is", r.Bounds().Dy())
104
+ }
105
+ if r.Bounds().Min.X != 100 {
106
+ t.Error("Invalid Bounds Min X", r.Bounds().Min.X)
107
+ }
108
+ if r.Bounds().Min.Y != 100 {
109
+ t.Error("Invalid Bounds Min Y", r.Bounds().Min.Y)
110
+ }
111
+ }
112
+
113
+ func TestCrop_TooBigAreaFromCenter(t *testing.T) {
114
+ img := getImage()
115
+
116
+ c := Config{
117
+ Width: 1000,
118
+ Height: 2000,
119
+ Anchor: image.Point{1200, 100},
120
+ Mode: Centered,
121
+ }
122
+ r, err := Crop(img, c)
123
+ if err != nil {
124
+ t.Fatal(err)
125
+ }
126
+ if r.Bounds().Dx() != 900 {
127
+ t.Error("Bad width should be 900 but is", r.Bounds().Dx())
128
+ }
129
+ if r.Bounds().Dy() != 1100 {
130
+ t.Error("Bad width should be 1100 but is", r.Bounds().Dy(), r.Bounds())
131
+ }
132
+ if r.Bounds().Min.X != 700 {
133
+ t.Error("Invalid Bounds Min X", r.Bounds().Min.X)
134
+ }
135
+ if r.Bounds().Min.Y != 0 {
136
+ t.Error("Invalid Bounds Min Y", r.Bounds().Min.Y)
137
+ }
138
+ }
139
+
140
+ func TestCrop_OptionRatio(t *testing.T) {
141
+ img := getImage()
142
+
143
+ c := Config{
144
+ Width: 4,
145
+ Height: 3,
146
+ Anchor: image.Point{},
147
+ Mode: TopLeft,
148
+ Options: Ratio,
149
+ }
150
+
151
+ r, err := Crop(img, c)
152
+ if err != nil {
153
+ t.Error(err)
154
+ }
155
+ if r.Bounds().Min.X != 0 {
156
+ t.Error("Invalid Bounds Min X", r.Bounds().Min.X)
157
+ }
158
+ if r.Bounds().Min.Y != 0 {
159
+ t.Error("Invalid Bounds Min Y", r.Bounds().Min.Y)
160
+ }
161
+ if r.Bounds().Dx() != 1600 {
162
+ t.Error("Bad Width", r.Bounds().Dx())
163
+ }
164
+ if r.Bounds().Dy() != 1200 {
165
+ t.Error("Bad Height", r.Bounds().Dy(), r.Bounds())
166
+ }
167
+ }
168
+
169
+ func TestCutter_Crop_OptionRatio_Inverted(t *testing.T) {
170
+ img := getImage()
171
+
172
+ c := Config{
173
+ Width: 3,
174
+ Height: 4,
175
+ Anchor: image.Point{},
176
+ Mode: TopLeft,
177
+ Options: Ratio,
178
+ }
179
+
180
+ r, err := Crop(img, c)
181
+ if err != nil {
182
+ t.Error(err)
183
+ }
184
+ if r.Bounds().Min.X != 0 {
185
+ t.Error("Invalid Bounds Min X", r.Bounds().Min.X)
186
+ }
187
+ if r.Bounds().Min.Y != 0 {
188
+ t.Error("Invalid Bounds Min Y", r.Bounds().Min.Y)
189
+ }
190
+ if r.Bounds().Dy() != 1437 {
191
+ t.Error("Bad Height", r.Bounds().Dy(), r.Bounds())
192
+ }
193
+ if r.Bounds().Dx() != 1077 {
194
+ t.Error("Bad Width", r.Bounds().Dx())
195
+ }
196
+ }
197
+
198
+ func TestCutter_Crop_OptionRatio_DecentredAnchor_Overflow(t *testing.T) {
199
+ img := getImage()
200
+ c := Config{
201
+ Width: 3,
202
+ Height: 4,
203
+ Anchor: image.Point{100, 80},
204
+ Mode: Centered,
205
+ Options: Ratio,
206
+ }
207
+
208
+ r, err := Crop(img, c)
209
+ if err != nil {
210
+ t.Error(err)
211
+ }
212
+ if r.Bounds().Min.X != 40 {
213
+ t.Error("Invalid Bounds Min X", r.Bounds().Min.X)
214
+ }
215
+ if r.Bounds().Min.Y != 0 {
216
+ t.Error("Invalid Bounds Min Y", r.Bounds().Min.Y)
217
+ }
218
+ if r.Bounds().Dy() != 160 {
219
+ t.Error("Bad Height", r.Bounds().Dy(), r.Bounds())
220
+ }
221
+ if r.Bounds().Dx() != 120 {
222
+ t.Error("Bad Width", r.Bounds().Dx())
223
+ }
224
+ }
225
+
226
+ func TestCropForceCopy(t *testing.T) {
227
+ img := getImage()
228
+
229
+ c := Config{
230
+ Width: 512,
231
+ Height: 400,
232
+ Options: Copy,
233
+ }
234
+ r, err := Crop(img, c)
235
+ if err != nil {
236
+ t.Fatal(err)
237
+ }
238
+ if r.Bounds().Dx() != 512 {
239
+ t.Error("Bad width should be 512 but is", r.Bounds().Dx())
240
+ }
241
+ if r.Bounds().Dy() != 400 {
242
+ t.Error("Bad width should be 400 but is", r.Bounds().Dy())
243
+ }
244
+ if r.Bounds().Min.X != 0 {
245
+ t.Error("Invalid Bounds Min X", r.Bounds().Min.X)
246
+ }
247
+ if r.Bounds().Min.Y != 0 {
248
+ t.Error("Invalid Bounds Min Y", r.Bounds().Min.Y)
249
+ }
250
+ }
251
+
252
+ func getImage() image.Image {
253
+ return image.NewGray(image.Rect(0, 0, 1600, 1437))
254
+ }
255
+
256
+ func getGopherImage() image.Image {
257
+ fi, err := os.Open("fixtures/gopher.jpg")
258
+ if err != nil {
259
+ panic(err)
260
+ }
261
+ defer fi.Close()
262
+ img, _, err := image.Decode(fi)
263
+ if err != nil {
264
+ panic(err)
265
+ }
266
+ return img
267
+ }
@@ -0,0 +1,35 @@
1
+ package cutter
2
+
3
+ import (
4
+ "fmt"
5
+ "image"
6
+ _ "image/jpeg"
7
+ "log"
8
+ "os"
9
+ _ "testing"
10
+ )
11
+
12
+ func ExampleCrop() {
13
+ f, err := os.Open("fixtures/gopher.jpg")
14
+ if err != nil {
15
+ log.Fatal("Cannot open file", err)
16
+ }
17
+ img, _, err := image.Decode(f)
18
+ if err != nil {
19
+ log.Fatal("Cannot decode image:", err)
20
+ }
21
+
22
+ cImg, err := Crop(img, Config{
23
+ Height: 500, // height in pixel or Y ratio(see Ratio Option below)
24
+ Width: 500, // width in pixel or X ratio
25
+ Mode: TopLeft, // Accepted Mode: TopLeft, Centered
26
+ Anchor: image.Point{10, 10}, // Position of the top left point
27
+ Options: 0, // Accepted Option: Ratio
28
+ })
29
+
30
+ if err != nil {
31
+ log.Fatal("Cannot crop image:", err)
32
+ }
33
+ fmt.Println("cImg dimension:", cImg.Bounds())
34
+ // Output: cImg dimension: (10,10)-(510,510)
35
+ }
@@ -0,0 +1,4 @@
1
+ require 'ires/engine'
2
+ require 'ires/service'
3
+ require 'ires/view_helper'
4
+ require 'ires/util'
@@ -0,0 +1,7 @@
1
+ module Ires
2
+ class Engine < ::Rails::Engine
3
+ ActiveSupport.on_load :action_view do
4
+ ActionView::Base.send(:include, Ires::ViewHelper)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ require "ffi"
2
+ require 'ires/util'
3
+
4
+ module Ires
5
+ module Service
6
+ extend FFI::Library
7
+
8
+ os = Ires::Util.current_os
9
+ return if os.nil?
10
+
11
+ # NOTE: ires.so is golang object
12
+ ffi_lib File.expand_path("../../shared/#{os}/ires.so", File.dirname(__FILE__))
13
+
14
+ # resize func
15
+ attach_function :resizeImage, [:string, :int, :int, :string], :string
16
+ attach_function :cropImage, [:string, :int, :int, :string], :string
17
+ attach_function :resizeToCropImage, [:string, :int, :int, :string], :string
18
+ end
19
+ end
@@ -0,0 +1,39 @@
1
+ require 'rbconfig'
2
+
3
+ module Ires
4
+ module Util
5
+ class << self
6
+ # Reszie image directory
7
+ # return [none(ffi)]
8
+ def current_os
9
+ if ["darwin", "linux"].include?(os)
10
+ return os
11
+ else
12
+ logger.fatal "Ires is not supported by this #{os}"
13
+ return nil
14
+ end
15
+ end
16
+
17
+ # Search OS
18
+ # return [String]
19
+ def os
20
+ @os ||= (
21
+ host_os = RbConfig::CONFIG['host_os']
22
+ case host_os
23
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
24
+ "windows"
25
+ when /darwin|mac os/
26
+ "darwin"
27
+ when /linux/
28
+ "linux"
29
+ when /solaris|bsd/
30
+ "unix"
31
+ else
32
+ "unknown"
33
+ end
34
+ )
35
+ end
36
+
37
+ end
38
+ end
39
+ end