tinyimg 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +5 -2
- data/README.md +48 -23
- data/ext/tinyimg/tinyimg.c +49 -3
- data/lib/tinyimg.rb +57 -4
- data/spec/tinyimg_spec.rb +89 -4
- data/tinyimg.gemspec +3 -3
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 167b894e73f7dad09bfff8a20d4374d2dcd822f2
|
4
|
+
data.tar.gz: f92b7f089edb704b6d60cc0c3d58420b444b9d10
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 56d0aa8dd6ca5fab4572ee9bf04416c8d6a90b0a0c5046f23f45fd9d03008aa71acabec8f5c10a5d702e3c0190a42b758e22438189ecb934d5c8d938a1f852f1
|
7
|
+
data.tar.gz: 5533db6f10236e0eff9a4edb42758a354779ccf5339b22184a24c853a02d37f512bd0d03a4af5f0d41ffa095a0e7930c3bc1f89b2b66fa0c5e6b0f0e3804a9c8
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Tinyimg
|
2
2
|
|
3
|
-
Load a JPEG or PNG, get its dimensions, resize it, and extract it again as either a JPEG or PNG.
|
3
|
+
Load a JPEG or PNG, get its dimensions, crop it, resize it, and extract it again as either a JPEG or PNG.
|
4
4
|
|
5
5
|
This gem can work from image data stored in memory, as well as from a file or IO stream.
|
6
6
|
It's been coded to be as efficient as possible, so doesn't use temporary files.
|
@@ -28,46 +28,71 @@ Then add tinyimg to your project's Gemfile
|
|
28
28
|
|
29
29
|
Load the image with the method that fits your use case:
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
```ruby
|
32
|
+
image = Tinyimg.from_string(an_image_that_is_already_loaded)
|
33
|
+
image = Tinyimg.from_file("some_image.png")
|
34
|
+
image = Tinyimg.from_io(params[:uploaded_file])
|
35
|
+
```
|
34
36
|
|
35
|
-
|
37
|
+
Resize it by using one of these methods:
|
36
38
|
|
37
|
-
|
38
|
-
|
39
|
-
|
39
|
+
```ruby
|
40
|
+
image.resize_exact!(100, 100) # forces image to be exactly 100x100
|
41
|
+
image.resize!(width: 100) # width = 100 and aspect ratio maintained
|
42
|
+
image.resize!(height: 50) # height = 50 and aspect ratio maintained
|
43
|
+
image.resize_to_fit!(100, 100) # image will be 100x100 maximum
|
44
|
+
image.resize_to_fill!(100, 100) # image will be 100x100 minimum
|
45
|
+
```
|
46
|
+
|
47
|
+
Crop it using this method:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# Crops the image from (20, 20) to (70, 70), resulting in a 50x50 image.
|
51
|
+
image.crop!(x: 20, y: 20, width: 50, height: 50)
|
52
|
+
|
53
|
+
# By default, x and y arguments are zero, and width and height arguments
|
54
|
+
# are the width and height of the original image.
|
55
|
+
image.crop!(width: image.height)
|
56
|
+
```
|
40
57
|
|
41
58
|
Then get an image back:
|
42
59
|
|
43
|
-
|
44
|
-
|
45
|
-
|
60
|
+
```ruby
|
61
|
+
image.to_png # returns a string
|
62
|
+
image.to_jpeg # returns a string
|
63
|
+
image.save("some_image.jpg") # file type auto-determined by extension
|
64
|
+
```
|
46
65
|
|
47
66
|
You can ask for the image's dimensions:
|
48
67
|
|
49
|
-
|
50
|
-
|
51
|
-
|
68
|
+
```ruby
|
69
|
+
image.width # => 120
|
70
|
+
image.height # => 80
|
71
|
+
image.dimensions # => [120, 80]
|
72
|
+
```
|
52
73
|
|
53
|
-
You can also use the non-! versions of the
|
74
|
+
You can also use the non-! versions of the operation methods: `resize_exact`, `resize`, `resize_to_fit`, `resize_to_fill` and `crop`.
|
54
75
|
These create a new image in memory and return it, leaving the old image untouched. This is useful if you want
|
55
76
|
to resize an original image to multiple sizes. Using these methods will take more memory.
|
56
77
|
|
57
78
|
## Examples
|
58
79
|
|
59
|
-
Take an uploaded file, save the original as a JPEG, then resize to create a thumbnail and save that too:
|
80
|
+
Take an uploaded file that's a PNG or JPEG, save the original as a JPEG, then resize to create a thumbnail and save that too:
|
60
81
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
82
|
+
```ruby
|
83
|
+
Tinyimg
|
84
|
+
.from_io(params[:uploaded_file])
|
85
|
+
.save("#{path}/full_size.jpg")
|
86
|
+
.resize_to_fit!(100, 100)
|
87
|
+
.save("#{path}/thumbnail.jpg")
|
88
|
+
```
|
66
89
|
|
67
90
|
Load a file from disk, make a thumbnail, and return it as a JPEG so we can save it into our database:
|
68
91
|
|
69
|
-
|
70
|
-
|
92
|
+
```ruby
|
93
|
+
data = Tinyimg.from_file(image_filename).resize_to_fit!(100, 100).to_jpeg
|
94
|
+
user.update!(thumbnail_image: data)
|
95
|
+
```
|
71
96
|
|
72
97
|
## Author and licence
|
73
98
|
|
data/ext/tinyimg/tinyimg.c
CHANGED
@@ -203,7 +203,7 @@ VALUE to_png(int argc, VALUE *argv, VALUE self)
|
|
203
203
|
return output;
|
204
204
|
}
|
205
205
|
|
206
|
-
VALUE
|
206
|
+
VALUE resize_exact_bang(VALUE self, VALUE width_value, VALUE height_value)
|
207
207
|
{
|
208
208
|
gdImagePtr image_in, image_out;
|
209
209
|
int width, height;
|
@@ -214,7 +214,7 @@ VALUE resize_bang(VALUE self, VALUE width_value, VALUE height_value)
|
|
214
214
|
width = FIX2INT(width_value);
|
215
215
|
height = FIX2INT(height_value);
|
216
216
|
|
217
|
-
if (width
|
217
|
+
if (width <= 0 || height <= 0) {
|
218
218
|
rb_raise(rb_eArgError, "width and height must both be positive integers");
|
219
219
|
}
|
220
220
|
|
@@ -235,17 +235,63 @@ VALUE resize_bang(VALUE self, VALUE width_value, VALUE height_value)
|
|
235
235
|
return self;
|
236
236
|
}
|
237
237
|
|
238
|
+
VALUE internal_crop_bang(VALUE self, VALUE x_value, VALUE y_value, VALUE width_value, VALUE height_value)
|
239
|
+
{
|
240
|
+
gdImagePtr image_in, image_out;
|
241
|
+
int x, y, width, height;
|
242
|
+
|
243
|
+
Check_Type(x_value, T_FIXNUM);
|
244
|
+
Check_Type(y_value, T_FIXNUM);
|
245
|
+
Check_Type(width_value, T_FIXNUM);
|
246
|
+
Check_Type(height_value, T_FIXNUM);
|
247
|
+
|
248
|
+
x = FIX2INT(x_value);
|
249
|
+
y = FIX2INT(y_value);
|
250
|
+
width = FIX2INT(width_value);
|
251
|
+
height = FIX2INT(height_value);
|
252
|
+
|
253
|
+
if (x < 0 || y < 0) {
|
254
|
+
rb_raise(rb_eArgError, "x, y must both be zero or positive integers");
|
255
|
+
}
|
256
|
+
|
257
|
+
if (width <= 0 || height <= 0) {
|
258
|
+
rb_raise(rb_eArgError, "width and height must both be positive integers");
|
259
|
+
}
|
260
|
+
|
261
|
+
image_in = get_image_data(self);
|
262
|
+
|
263
|
+
if (x + width > gdImageSX(image_in)) {
|
264
|
+
rb_raise(rb_eArgError, "x + width is greater than the original image's width");
|
265
|
+
}
|
266
|
+
|
267
|
+
if (y + height > gdImageSY(image_in)) {
|
268
|
+
rb_raise(rb_eArgError, "y + height is greater than the original image's height");
|
269
|
+
}
|
270
|
+
|
271
|
+
image_out = gdImageCreateTrueColor(width, height);
|
272
|
+
set_alpha(image_out);
|
273
|
+
|
274
|
+
gdImageCopy(image_out, image_in, 0, 0, x, y, width, height);
|
275
|
+
|
276
|
+
set_image_data(self, image_out);
|
277
|
+
|
278
|
+
retrieve_image_dimensions(self);
|
279
|
+
|
280
|
+
return self;
|
281
|
+
}
|
282
|
+
|
238
283
|
void Init_tinyimg()
|
239
284
|
{
|
240
285
|
VALUE cTinyimg = rb_define_class("Tinyimg", rb_cObject);
|
241
286
|
rb_define_class_under(cTinyimg, "Image", rb_cObject);
|
242
287
|
|
243
|
-
rb_define_method(cTinyimg, "
|
288
|
+
rb_define_method(cTinyimg, "resize_exact!", resize_exact_bang, 2);
|
244
289
|
rb_define_method(cTinyimg, "to_jpeg", to_jpeg, -1);
|
245
290
|
rb_define_method(cTinyimg, "to_png", to_png, -1);
|
246
291
|
rb_define_private_method(cTinyimg, "initialize_copy", initialize_copy, -1);
|
247
292
|
rb_define_private_method(cTinyimg, "load_from_string", load_from_string, 2);
|
248
293
|
rb_define_private_method(cTinyimg, "retrieve_image_dimensions", retrieve_image_dimensions, 0);
|
294
|
+
rb_define_private_method(cTinyimg, "internal_crop!", internal_crop_bang, 4);
|
249
295
|
#ifdef HAVE_GDIMAGECREATEFROMFILE
|
250
296
|
rb_define_private_method(cTinyimg, "load_from_file", load_from_file, 1);
|
251
297
|
#endif
|
data/lib/tinyimg.rb
CHANGED
@@ -21,12 +21,28 @@ class Tinyimg
|
|
21
21
|
[width, height]
|
22
22
|
end
|
23
23
|
|
24
|
-
def resize(
|
25
|
-
dup.resize!(
|
24
|
+
def resize(*args)
|
25
|
+
dup.resize!(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
def resize!(*args)
|
29
|
+
case args.map(&:class)
|
30
|
+
when [Fixnum, Fixnum]
|
31
|
+
resize_exact!(*args)
|
32
|
+
when [Hash]
|
33
|
+
width, height = convert_hash_to_exact_dimensions(args.first)
|
34
|
+
resize_exact!(width, height)
|
35
|
+
else
|
36
|
+
raise ArgumentError, "#resize and #resize! accept either (width, height) or a hash with :width and/or :height keys"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def resize_exact(width, height)
|
41
|
+
dup.resize_exact!(width, height)
|
26
42
|
end
|
27
43
|
|
28
44
|
# Implemented in C
|
29
|
-
# def
|
45
|
+
# def resize_exact!(width, height)
|
30
46
|
# end
|
31
47
|
|
32
48
|
def resize_to_fit(new_width, new_height)
|
@@ -49,6 +65,17 @@ class Tinyimg
|
|
49
65
|
end
|
50
66
|
end
|
51
67
|
|
68
|
+
def crop(**kwargs)
|
69
|
+
dup.crop!(**kwargs)
|
70
|
+
end
|
71
|
+
|
72
|
+
def crop!(x: 0, y: 0, width: nil, height: nil)
|
73
|
+
width ||= self.width
|
74
|
+
height ||= self.height
|
75
|
+
|
76
|
+
internal_crop!(x, y, width, height)
|
77
|
+
end
|
78
|
+
|
52
79
|
def save(filename)
|
53
80
|
if respond_to?(:save_to_file)
|
54
81
|
save_to_file(filename)
|
@@ -60,6 +87,8 @@ class Tinyimg
|
|
60
87
|
|
61
88
|
File.write(filename, data)
|
62
89
|
end
|
90
|
+
|
91
|
+
self
|
63
92
|
end
|
64
93
|
|
65
94
|
# Implemented in C
|
@@ -100,7 +129,7 @@ class Tinyimg
|
|
100
129
|
resize_height = new_height
|
101
130
|
end
|
102
131
|
|
103
|
-
|
132
|
+
resize_exact!(resize_width, resize_height)
|
104
133
|
end
|
105
134
|
|
106
135
|
def determine_type(data)
|
@@ -122,10 +151,34 @@ class Tinyimg
|
|
122
151
|
end
|
123
152
|
end
|
124
153
|
|
154
|
+
def convert_hash_to_exact_dimensions(opts)
|
155
|
+
if opts.empty? || !(opts.keys - [:width, :height]).empty?
|
156
|
+
raise ArgumentError, "expected either :width or :height or both keys"
|
157
|
+
end
|
158
|
+
|
159
|
+
if opts.values.any? { |v| !v.is_a?(Fixnum) }
|
160
|
+
raise ArgumentError, ":width and :height values must be integers"
|
161
|
+
end
|
162
|
+
|
163
|
+
new_width, new_height = opts[:width], opts[:height]
|
164
|
+
|
165
|
+
if new_height.nil?
|
166
|
+
new_height = height * new_width / width
|
167
|
+
elsif new_width.nil?
|
168
|
+
new_width = width * new_height / height
|
169
|
+
end
|
170
|
+
|
171
|
+
[new_width, new_height]
|
172
|
+
end
|
173
|
+
|
125
174
|
# Implemented in C
|
126
175
|
# def load_from_string(data, type)
|
127
176
|
# end
|
128
177
|
|
178
|
+
# Implemented in C
|
179
|
+
# def internal_crop!(x, y, width, height)
|
180
|
+
# end
|
181
|
+
|
129
182
|
# Implemented in C
|
130
183
|
# Only available with libgd 2.1.1+
|
131
184
|
# def load_from_file(filename)
|
data/spec/tinyimg_spec.rb
CHANGED
@@ -33,23 +33,83 @@ RSpec.describe Tinyimg do
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
-
describe "#
|
36
|
+
describe "#resize_exact" do
|
37
37
|
it "resizes the image as requested, creating a new image" do
|
38
|
-
result = sample.
|
38
|
+
result = sample.resize_exact(100, 100)
|
39
39
|
expect(result).to_not eql sample
|
40
40
|
expect(sample.dimensions).to eq [200, 153]
|
41
41
|
expect(result.dimensions).to eq [100, 100]
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
describe "#
|
45
|
+
describe "#resize_exact!" do
|
46
46
|
it "resizes the image as requested" do
|
47
|
-
result = sample.
|
47
|
+
result = sample.resize_exact!(100, 100)
|
48
48
|
expect(result).to eql sample
|
49
49
|
expect(sample.dimensions).to eq [100, 100]
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
describe "#resize" do
|
54
|
+
it "takes an exact width and height and resizes" do
|
55
|
+
result = sample.resize(100, 100)
|
56
|
+
expect(result.dimensions).to eq [100, 100]
|
57
|
+
end
|
58
|
+
|
59
|
+
it "takes a width and height as a hash and resizes" do
|
60
|
+
result = sample.resize(width: 100, height: 100)
|
61
|
+
expect(result.dimensions).to eq [100, 100]
|
62
|
+
end
|
63
|
+
|
64
|
+
it "takes just a width and resizes, calculating the height" do
|
65
|
+
result = sample.resize(width: 100)
|
66
|
+
expect(result.dimensions).to eq [100, 76]
|
67
|
+
end
|
68
|
+
|
69
|
+
it "takes just a height and resizes, calculating the width" do
|
70
|
+
result = sample.resize(height: 100)
|
71
|
+
expect(result.dimensions).to eq [130, 100]
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe "#resize!" do
|
76
|
+
it "takes an exact width and height and resizes" do
|
77
|
+
result = sample.resize!(100, 100)
|
78
|
+
expect(sample.dimensions).to eq [100, 100]
|
79
|
+
end
|
80
|
+
|
81
|
+
it "takes a width and height as a hash and resizes" do
|
82
|
+
result = sample.resize!(width: 100, height: 100)
|
83
|
+
expect(sample.dimensions).to eq [100, 100]
|
84
|
+
end
|
85
|
+
|
86
|
+
it "takes just a width and resizes, calculating the height" do
|
87
|
+
result = sample.resize!(width: 100)
|
88
|
+
expect(sample.dimensions).to eq [100, 76]
|
89
|
+
end
|
90
|
+
|
91
|
+
it "takes just a height and resizes, calculating the width" do
|
92
|
+
result = sample.resize!(height: 100)
|
93
|
+
expect(sample.dimensions).to eq [130, 100]
|
94
|
+
end
|
95
|
+
|
96
|
+
it "raises if other keys are provided" do
|
97
|
+
expect { sample.resize!(something: 123) }.to raise_error(ArgumentError)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "raises if non-integer values are provided" do
|
101
|
+
expect { sample.resize!(width: "123") }.to raise_error(ArgumentError)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "raises if no keys are provided" do
|
105
|
+
expect { sample.resize! }.to raise_error(ArgumentError)
|
106
|
+
end
|
107
|
+
|
108
|
+
it "raises if only one argument is provided" do
|
109
|
+
expect { sample.resize!(123) }.to raise_error(ArgumentError)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
53
113
|
describe "#resize_to_fit!" do
|
54
114
|
it "calculates the image dimensions so it fits the width and resizes" do
|
55
115
|
sample.resize_to_fit!(100, 100)
|
@@ -74,6 +134,31 @@ RSpec.describe Tinyimg do
|
|
74
134
|
end
|
75
135
|
end
|
76
136
|
|
137
|
+
describe "#crop" do
|
138
|
+
it "crops the image to the specified size and offset" do
|
139
|
+
result = sample.crop(x: 10, y: 20, width: 30, height: 40)
|
140
|
+
expect(result.dimensions).to eq [30, 40]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe "#crop!" do
|
145
|
+
it "crops the image to the specified size and offset" do
|
146
|
+
sample.crop!(x: 10, y: 20, width: 30, height: 40)
|
147
|
+
expect(sample.dimensions).to eq [30, 40]
|
148
|
+
end
|
149
|
+
|
150
|
+
it "defaults x and y to zero and width and height to the original width and height" do
|
151
|
+
sample.crop!
|
152
|
+
expect(sample.dimensions).to eq [200, 153]
|
153
|
+
end
|
154
|
+
|
155
|
+
it "raises if the requested area is not inside the original image" do
|
156
|
+
expect { sample.crop!(x: 10, y: 20, width: 195, height: 40) }.to raise_error(ArgumentError)
|
157
|
+
expect { sample.crop!(x: 10, y: 20, width: 95, height: 140) }.to raise_error(ArgumentError)
|
158
|
+
expect { sample.crop!(width: 95, height: 160) }.to raise_error(ArgumentError)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
77
162
|
describe "#save" do
|
78
163
|
it "saves a JPEG" do
|
79
164
|
begin
|
data/tinyimg.gemspec
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
Gem::Specification.new do |gem|
|
2
2
|
gem.name = 'tinyimg'
|
3
|
-
gem.version = '0.1.
|
4
|
-
gem.summary = "Tiny and fast JPEG/PNG
|
5
|
-
gem.description = "Convert between JPEG/PNG and resize images, either all in memory or via disk. Only
|
3
|
+
gem.version = '0.1.1'
|
4
|
+
gem.summary = "Tiny and fast JPEG/PNG cropping and resizing"
|
5
|
+
gem.description = "Convert between JPEG/PNG, crop and resize images, either all in memory or via disk. Only requires libgd to function."
|
6
6
|
gem.has_rdoc = false
|
7
7
|
gem.author = "Roger Nesbitt"
|
8
8
|
gem.email = "roger@seriousorange.com"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tinyimg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roger Nesbitt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-11-
|
11
|
+
date: 2015-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -24,8 +24,8 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '3.0'
|
27
|
-
description: Convert between JPEG/PNG and resize images, either all in memory
|
28
|
-
disk. Only
|
27
|
+
description: Convert between JPEG/PNG, crop and resize images, either all in memory
|
28
|
+
or via disk. Only requires libgd to function.
|
29
29
|
email: roger@seriousorange.com
|
30
30
|
executables: []
|
31
31
|
extensions:
|
@@ -64,10 +64,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
64
|
version: '0'
|
65
65
|
requirements: []
|
66
66
|
rubyforge_project:
|
67
|
-
rubygems_version: 2.
|
67
|
+
rubygems_version: 2.4.8
|
68
68
|
signing_key:
|
69
69
|
specification_version: 4
|
70
|
-
summary: Tiny and fast JPEG/PNG
|
70
|
+
summary: Tiny and fast JPEG/PNG cropping and resizing
|
71
71
|
test_files:
|
72
72
|
- spec/samples/duck.png
|
73
73
|
- spec/tinyimg_spec.rb
|