tinyimg 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a953ed5351abc1984d8739f8ec3d91a1bed9654a
4
- data.tar.gz: c73c4110056238bc19a1c6e7ae6204b4c98a9968
3
+ metadata.gz: 167b894e73f7dad09bfff8a20d4374d2dcd822f2
4
+ data.tar.gz: f92b7f089edb704b6d60cc0c3d58420b444b9d10
5
5
  SHA512:
6
- metadata.gz: 810f4ae575ca67c08c7a1551eb0abf4f16ca6fb12c77d4d9dd9227bd448856644d063424c0d34a6251aa2a52ef0be38ad9180e29f05d3e2a81ccb487d7c260b3
7
- data.tar.gz: f7021348faf7e950ed87c51c12383684238bdfef806fb9f11d36bdb0c1e51c600576bb7fe7ac2ef48f31361d6769ffb50b988166ffabbb625cdf6983e7142560
6
+ metadata.gz: 56d0aa8dd6ca5fab4572ee9bf04416c8d6a90b0a0c5046f23f45fd9d03008aa71acabec8f5c10a5d702e3c0190a42b758e22438189ecb934d5c8d938a1f852f1
7
+ data.tar.gz: 5533db6f10236e0eff9a4edb42758a354779ccf5339b22184a24c853a02d37f512bd0d03a4af5f0d41ffa095a0e7930c3bc1f89b2b66fa0c5e6b0f0e3804a9c8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- timyimg (0.1.0)
4
+ tinyimg (0.1.1)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -26,4 +26,7 @@ PLATFORMS
26
26
 
27
27
  DEPENDENCIES
28
28
  rspec (~> 3.0)
29
- timyimg!
29
+ tinyimg!
30
+
31
+ BUNDLED WITH
32
+ 1.10.6
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
- image = Tinyimg.from_string(an_image_that_is_already_loaded)
32
- image = Tinyimg.from_file("some_image.png")
33
- image = Tinyimg.from_io(params[:uploaded_file])
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
- Manipulate it by using one of the resize commands:
37
+ Resize it by using one of these methods:
36
38
 
37
- image.resize_to_fit!(100, 100) # image will be 100x100 maximum
38
- image.resize_to_fill!(100, 100) # image will be 100x100 minimum
39
- image.resize!(100, 100) # forces image to be exactly 100x100
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
- image.to_png # returns a string
44
- image.to_jpeg # returns a string
45
- image.save("some_image.jpg") # file type auto-determined by extension
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
- image.width # => 120
50
- image.height # => 80
51
- image.dimensions # => [120, 80]
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 resize methods: `resize`, `resize_to_fit` and `resize_to_fill`.
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
- Tinyimg
62
- .from_io(params[:uploaded_file])
63
- .save("#{path}/full_size.jpg")
64
- .resize_to_fit!(100, 100)
65
- .save("#{path}/thumbnail.jpg")
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
- data = Tinyimg.from_file(image_filename).resize_to_fit!(100, 100).to_jpeg
70
- user.update!(thumbnail_image: data)
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
 
@@ -203,7 +203,7 @@ VALUE to_png(int argc, VALUE *argv, VALUE self)
203
203
  return output;
204
204
  }
205
205
 
206
- VALUE resize_bang(VALUE self, VALUE width_value, VALUE height_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 < 0 || height < 0) {
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, "resize!", resize_bang, 2);
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(width, height)
25
- dup.resize!(width, height)
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 resize!(width, height)
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
- resize!(resize_width, resize_height)
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 "#resize" do
36
+ describe "#resize_exact" do
37
37
  it "resizes the image as requested, creating a new image" do
38
- result = sample.resize(100, 100)
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 "#resize!" do
45
+ describe "#resize_exact!" do
46
46
  it "resizes the image as requested" do
47
- result = sample.resize!(100, 100)
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.0'
4
- gem.summary = "Tiny and fast JPEG/PNG resizer and converter"
5
- gem.description = "Convert between JPEG/PNG and resize images, either all in memory or via disk. Only required libgd to function."
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.0
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-08 00:00:00.000000000 Z
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 or via
28
- disk. Only required libgd to function.
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.2.2
67
+ rubygems_version: 2.4.8
68
68
  signing_key:
69
69
  specification_version: 4
70
- summary: Tiny and fast JPEG/PNG resizer and converter
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