rszr 0.8.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +23 -5
- data/ext/rszr/image.c +28 -1
- data/lib/rszr/color.rb +25 -0
- data/lib/rszr/image.rb +90 -29
- data/lib/rszr/image_processing.rb +3 -3
- data/lib/rszr/stream.rb +2 -2
- data/lib/rszr/version.rb +1 -1
- data/lib/rszr.rb +1 -0
- metadata +20 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ea3437991dd4694e35653020632c3fc360f787e1d391834780fdf118991a90ce
|
4
|
+
data.tar.gz: 37ccc3f8693e4fe9d41939cfc2d3786ef72ed24e344059f1dd4e4492efcae862
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35b8219e81c0f89e4b51719617c13edf71421d7e3ed4e1998f8e26fc017545da76c5164c0bbd63c16e5c617cfdd4f5cb1f07a8e0bbdc83b7bb90ea7fbbd71907
|
7
|
+
data.tar.gz: fd4361d89e0ff577032da1dea39686c035604d98e5b6b935ca8de8f1289bdfe6cf5678c8842406c0b9c5829cfab2c03af8350670638824a99deeb176e4675428
|
data/README.md
CHANGED
@@ -60,6 +60,9 @@ image.width => 400
|
|
60
60
|
image.height => 300
|
61
61
|
image.dimensions => [400, 300]
|
62
62
|
image.format => "jpeg"
|
63
|
+
image[0, 0] => <Rszr::Color::RGBA @red=38, @green=115, @blue=141, @alpha=255>
|
64
|
+
image[0, 0].to_hex => "26738dff"
|
65
|
+
image[0, 0].to_hex(rgb: true) => "26738d"
|
63
66
|
```
|
64
67
|
|
65
68
|
### Transformations
|
@@ -68,6 +71,8 @@ For each transformation, there is a bang! and non-bang method.
|
|
68
71
|
The bang method changes the image in place, while the non-bang method
|
69
72
|
creates a copy of the image in memory.
|
70
73
|
|
74
|
+
#### Resizing
|
75
|
+
|
71
76
|
```ruby
|
72
77
|
# auto height
|
73
78
|
image.resize(400, :auto)
|
@@ -78,6 +83,22 @@ image.resize(:auto, 300)
|
|
78
83
|
# scale factor
|
79
84
|
image.resize(0.5)
|
80
85
|
|
86
|
+
# resize to fill
|
87
|
+
image.resize(400, 300, crop: true)
|
88
|
+
|
89
|
+
# resize to fill with gravity
|
90
|
+
# where gravity in [:n, :nw, :w, :sw, :w, :se, :e, :ne, :center]
|
91
|
+
image.resize(400, 300, crop: gravity)
|
92
|
+
|
93
|
+
# save memory, do not duplicate instance
|
94
|
+
image.resize!(400, :auto)
|
95
|
+
```
|
96
|
+
|
97
|
+
Check out the [full list and demo of resize options](https://mtgrosser.github.io/rszr/resizing.html)!
|
98
|
+
|
99
|
+
#### Other transformations
|
100
|
+
|
101
|
+
```ruby
|
81
102
|
# crop
|
82
103
|
image.crop(200, 200, 100, 100)
|
83
104
|
|
@@ -98,9 +119,6 @@ image.flop
|
|
98
119
|
|
99
120
|
# initialize copy
|
100
121
|
image.dup
|
101
|
-
|
102
|
-
# save memory, do not duplicate instance
|
103
|
-
image.resize!(400, :auto)
|
104
122
|
```
|
105
123
|
|
106
124
|
### Filters
|
@@ -146,12 +164,12 @@ Rszr.autorotate = true
|
|
146
164
|
|
147
165
|
## Rails / ActiveStorage interface
|
148
166
|
|
149
|
-
Rszr provides a drop-in interface to the `
|
167
|
+
Rszr provides a drop-in interface to the `image_processing` gem.
|
150
168
|
It is faster than both `mini_magick` and `vips` and way easier to install than the latter.
|
151
169
|
|
152
170
|
```ruby
|
153
171
|
# Gemfile
|
154
|
-
gem '
|
172
|
+
gem 'image_processing'
|
155
173
|
gem 'rszr'
|
156
174
|
|
157
175
|
# config/initializers/rszr.rb
|
data/ext/rszr/image.c
CHANGED
@@ -140,6 +140,32 @@ static VALUE rszr_image_height(VALUE self)
|
|
140
140
|
return INT2NUM(height);
|
141
141
|
}
|
142
142
|
|
143
|
+
|
144
|
+
static VALUE rszr_image__pixel_get(VALUE self, VALUE rb_x, VALUE rb_y)
|
145
|
+
{
|
146
|
+
rszr_image_handle * handle;
|
147
|
+
Imlib_Color color_return;
|
148
|
+
VALUE rb_rgba;
|
149
|
+
int x, y;
|
150
|
+
|
151
|
+
Check_Type(rb_x, T_FIXNUM);
|
152
|
+
x = FIX2INT(rb_x);
|
153
|
+
Check_Type(rb_y, T_FIXNUM);
|
154
|
+
y = FIX2INT(rb_y);
|
155
|
+
|
156
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
157
|
+
|
158
|
+
imlib_context_set_image(handle->image);
|
159
|
+
imlib_image_query_pixel(x, y, &color_return);
|
160
|
+
|
161
|
+
rb_rgba = rb_ary_new3(4, INT2NUM(color_return.red),
|
162
|
+
INT2NUM(color_return.green),
|
163
|
+
INT2NUM(color_return.blue),
|
164
|
+
INT2NUM(color_return.alpha));
|
165
|
+
return rb_rgba;
|
166
|
+
}
|
167
|
+
|
168
|
+
|
143
169
|
/*
|
144
170
|
static VALUE rszr_image_get_quality(VALUE self)
|
145
171
|
{
|
@@ -309,7 +335,7 @@ static VALUE rszr_image__sharpen_bang(VALUE self, VALUE rb_radius)
|
|
309
335
|
if (radius >= 0) {
|
310
336
|
imlib_image_sharpen(radius);
|
311
337
|
} else {
|
312
|
-
imlib_image_blur(radius);
|
338
|
+
imlib_image_blur(-radius);
|
313
339
|
}
|
314
340
|
|
315
341
|
return self;
|
@@ -475,6 +501,7 @@ void Init_rszr_image()
|
|
475
501
|
rb_define_private_method(cImage, "_turn!", rszr_image__turn_bang, 1);
|
476
502
|
rb_define_private_method(cImage, "_rotate", rszr_image__rotate, 2);
|
477
503
|
rb_define_private_method(cImage, "_sharpen!", rszr_image__sharpen_bang, 1);
|
504
|
+
rb_define_private_method(cImage, "_pixel", rszr_image__pixel_get, 2);
|
478
505
|
|
479
506
|
rb_define_private_method(cImage, "_save", rszr_image__save, 3);
|
480
507
|
}
|
data/lib/rszr/color.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rszr
|
2
|
+
module Color
|
3
|
+
|
4
|
+
class RGBA
|
5
|
+
attr_reader :red, :green, :blue, :alpha
|
6
|
+
|
7
|
+
def initialize(red, green, blue, alpha = 255)
|
8
|
+
if red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255 || alpha < 0 || alpha > 255
|
9
|
+
raise ArgumentError, 'color out of range'
|
10
|
+
end
|
11
|
+
@red, @green, @blue, @alpha = red, green, blue, alpha
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_i(rgb: false)
|
15
|
+
i = red.to_i << 24 | green.to_i << 16 | blue.to_i << 8 | alpha.to_i
|
16
|
+
rgb ? i >> 8 : i
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_hex(rgb: false)
|
20
|
+
"%0#{rgb ? 6 : 8}x" % to_i(rgb: rgb)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
data/lib/rszr/image.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Rszr
|
2
2
|
class Image
|
3
|
+
GRAVITIES = [true, :center, :n, :nw, :w, :sw, :s, :se, :e, :ne].freeze
|
4
|
+
|
3
5
|
extend Identification
|
4
6
|
include Buffered
|
5
7
|
include Orientation
|
@@ -37,6 +39,12 @@ module Rszr
|
|
37
39
|
fmt = fmt.to_s if fmt.is_a?(Symbol)
|
38
40
|
self._format = fmt
|
39
41
|
end
|
42
|
+
|
43
|
+
def [](x, y)
|
44
|
+
if x >= 0 && x <= width - 1 && y >= 0 && y <= height - 1
|
45
|
+
Color::RGBA.new(*_pixel(x, y))
|
46
|
+
end
|
47
|
+
end
|
40
48
|
|
41
49
|
def inspect
|
42
50
|
fmt = format
|
@@ -166,47 +174,100 @@ module Rszr
|
|
166
174
|
# 400, 300, background: rgba
|
167
175
|
# 400, 300, skew: true
|
168
176
|
|
169
|
-
def calculate_size(*args, crop: nil, skew: nil)
|
170
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
177
|
+
def calculate_size(*args, crop: nil, skew: nil, inflate: true)
|
178
|
+
#options = args.last.is_a?(Hash) ? args.pop : {}
|
171
179
|
#assert_valid_keys options, :crop, :background, :skew #:extend, :width, :height, :max_width, :max_height, :box
|
172
|
-
original_width, original_height = width, height
|
173
|
-
x, y, = 0, 0
|
174
180
|
if args.size == 1
|
175
|
-
|
176
|
-
raise ArgumentError, "scale factor #{scale.inspect} out of range" unless scale > 0 && scale < 1
|
177
|
-
new_width = original_width.to_f * scale
|
178
|
-
new_height = original_height.to_f * scale
|
181
|
+
calculate_size_for_scale(args.first)
|
179
182
|
elsif args.size == 2
|
180
183
|
box_width, box_height = args
|
181
|
-
if
|
182
|
-
|
183
|
-
new_width = box_height.to_f / original_height.to_f * original_width.to_f
|
184
|
-
elsif box_width.is_a?(Numeric) && :auto == box_height
|
185
|
-
new_width = box_width
|
186
|
-
new_height = box_width.to_f / original_width.to_f * original_height.to_f
|
184
|
+
if args.include?(:auto)
|
185
|
+
calculate_size_for_auto(box_width, box_height)
|
187
186
|
elsif box_width.is_a?(Numeric) && box_height.is_a?(Numeric)
|
188
|
-
if
|
189
|
-
|
187
|
+
if not inflate and width <= box_width and height <= box_height
|
188
|
+
[0, 0, width, height, width, height]
|
189
|
+
elsif skew
|
190
|
+
calculate_size_for_skew(box_width, box_height)
|
190
191
|
elsif crop
|
191
|
-
|
192
|
+
calculate_size_for_crop(box_width, box_height, crop)
|
192
193
|
else
|
193
|
-
|
194
|
-
box_scale = box_width.to_f / box_height.to_f
|
195
|
-
if scale >= box_scale # wider
|
196
|
-
new_width = box_width
|
197
|
-
new_height = original_height.to_f * box_width.to_f / original_width.to_f
|
198
|
-
else # narrower
|
199
|
-
new_height = box_height
|
200
|
-
new_width = original_width.to_f * box_height.to_f / original_height.to_f
|
201
|
-
end
|
194
|
+
calculate_size_for_limit(box_width, box_height)
|
202
195
|
end
|
203
|
-
else
|
204
|
-
raise ArgumentError, "unconclusive arguments #{args.inspect} #{options.inspect}"
|
205
196
|
end
|
206
197
|
else
|
207
198
|
raise ArgumentError, "wrong number of arguments (#{args.size} for 1..2)"
|
208
199
|
end
|
209
|
-
|
200
|
+
end
|
201
|
+
|
202
|
+
def calculate_size_for_scale(factor)
|
203
|
+
raise ArgumentError, "scale factor #{factor.inspect} out of range" unless factor > 0 && factor < 1
|
204
|
+
[0, 0, width, height, (width.to_f * factor).round, (height.to_f * factor).round]
|
205
|
+
end
|
206
|
+
|
207
|
+
def calculate_size_for_skew(box_width, box_height)
|
208
|
+
[0, 0, width, height, box_width, box_height]
|
209
|
+
end
|
210
|
+
|
211
|
+
def calculate_size_for_auto(box_width, box_height)
|
212
|
+
if :auto == box_width && box_height.is_a?(Numeric)
|
213
|
+
new_height = box_height
|
214
|
+
new_width = (box_height.to_f / height.to_f * width.to_f).round
|
215
|
+
elsif box_width.is_a?(Numeric) && :auto == box_height
|
216
|
+
new_width = box_width
|
217
|
+
new_height = (box_width.to_f / width.to_f * height.to_f).round
|
218
|
+
else
|
219
|
+
raise ArgumentError, "unconclusive arguments #{box_width.inspect}, #{box_height.inspect}"
|
220
|
+
end
|
221
|
+
[0, 0, width, height, new_width, new_height]
|
222
|
+
end
|
223
|
+
|
224
|
+
def calculate_size_for_crop(box_width, box_height, crop)
|
225
|
+
raise ArgumentError, "invalid crop gravity" unless GRAVITIES.include?(crop)
|
226
|
+
aspect = width.to_f / height.to_f
|
227
|
+
box_aspect = box_width.to_f / box_height.to_f
|
228
|
+
if aspect >= box_aspect # wider than box
|
229
|
+
src_width = (box_width.to_f * height.to_f / box_height.to_f).round
|
230
|
+
src_height = height
|
231
|
+
x = crop_horizontally(src_width, crop)
|
232
|
+
y = 0
|
233
|
+
else # narrower than box
|
234
|
+
src_width = width
|
235
|
+
src_height = (box_height.to_f * width.to_f / box_width.to_f).round
|
236
|
+
x = 0
|
237
|
+
y = crop_vertically(src_height, crop)
|
238
|
+
end
|
239
|
+
[x, y, src_width, src_height, box_width, box_height]
|
240
|
+
end
|
241
|
+
|
242
|
+
def crop_horizontally(src_width, crop)
|
243
|
+
case crop
|
244
|
+
when :nw, :w, :sw then 0
|
245
|
+
when :ne, :e, :se then width - src_width
|
246
|
+
else
|
247
|
+
((width - src_width).to_f / 2.to_f).round
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def crop_vertically(src_height, crop)
|
252
|
+
case crop
|
253
|
+
when :nw, :n, :ne then 0
|
254
|
+
when :sw, :s, :se then height - src_height
|
255
|
+
else
|
256
|
+
((height - src_height).to_f / 2.to_f).round
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def calculate_size_for_limit(box_width, box_height)
|
261
|
+
scale = width.to_f / height.to_f
|
262
|
+
box_scale = box_width.to_f / box_height.to_f
|
263
|
+
if scale >= box_scale # wider
|
264
|
+
new_width = box_width
|
265
|
+
new_height = (height.to_f * box_width.to_f / width.to_f).round
|
266
|
+
else # narrower
|
267
|
+
new_height = box_height
|
268
|
+
new_width = (width.to_f * box_height.to_f / height.to_f).round
|
269
|
+
end
|
270
|
+
[0, 0, width, height, new_width, new_height]
|
210
271
|
end
|
211
272
|
|
212
273
|
def format_from_filename(path)
|
@@ -51,7 +51,7 @@ module ImageProcessing
|
|
51
51
|
# Resizes the image to not be larger than the specified dimensions.
|
52
52
|
def resize_to_limit(width, height, **options)
|
53
53
|
width, height = default_dimensions(width, height)
|
54
|
-
thumbnail(width, height, **options)
|
54
|
+
thumbnail(width, height, inflate: false, **options)
|
55
55
|
end
|
56
56
|
|
57
57
|
# Resizes the image to fit within the specified dimensions.
|
@@ -62,8 +62,8 @@ module ImageProcessing
|
|
62
62
|
|
63
63
|
# Resizes the image to fill the specified dimensions, applying any
|
64
64
|
# necessary cropping.
|
65
|
-
def resize_to_fill(width, height, **options)
|
66
|
-
thumbnail(width, height, crop:
|
65
|
+
def resize_to_fill(width, height, gravity: :center, **options)
|
66
|
+
thumbnail(width, height, crop: gravity, **options)
|
67
67
|
end
|
68
68
|
|
69
69
|
private
|
data/lib/rszr/stream.rb
CHANGED
@@ -13,7 +13,7 @@ module Rszr
|
|
13
13
|
raise ArgumentError, "data must be File or String, got #{data.class}"
|
14
14
|
end
|
15
15
|
@data.binmode
|
16
|
-
@data.seek(start
|
16
|
+
@data.seek(start)
|
17
17
|
@pos = 0
|
18
18
|
end
|
19
19
|
|
@@ -33,7 +33,7 @@ module Rszr
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def substream
|
36
|
-
self.class.new(self, pos)
|
36
|
+
self.class.new(self, @data.pos)
|
37
37
|
end
|
38
38
|
|
39
39
|
def fast_forward
|
data/lib/rszr/version.rb
CHANGED
data/lib/rszr.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rszr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthias Grosser
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-10-
|
11
|
+
date: 2021-10-31 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Fast image resizer - do one thing and do it fast.
|
14
14
|
email:
|
@@ -18,49 +18,49 @@ extensions:
|
|
18
18
|
- ext/rszr/extconf.rb
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
|
+
- LICENSE
|
22
|
+
- README.md
|
23
|
+
- Rakefile
|
24
|
+
- ext/rszr/errors.c
|
25
|
+
- ext/rszr/errors.h
|
26
|
+
- ext/rszr/extconf.rb
|
27
|
+
- ext/rszr/image.c
|
28
|
+
- ext/rszr/image.h
|
29
|
+
- ext/rszr/rszr.c
|
30
|
+
- ext/rszr/rszr.h
|
31
|
+
- lib/rszr.rb
|
21
32
|
- lib/rszr/batch_transformation.rb
|
22
33
|
- lib/rszr/buffered.rb
|
34
|
+
- lib/rszr/color.rb
|
23
35
|
- lib/rszr/identification.rb
|
24
36
|
- lib/rszr/image.rb
|
25
37
|
- lib/rszr/image_processing.rb
|
26
38
|
- lib/rszr/orientation.rb
|
27
39
|
- lib/rszr/stream.rb
|
28
40
|
- lib/rszr/version.rb
|
29
|
-
- lib/rszr.rb
|
30
|
-
- ext/rszr/extconf.rb
|
31
|
-
- ext/rszr/errors.h
|
32
|
-
- ext/rszr/image.h
|
33
|
-
- ext/rszr/rszr.h
|
34
|
-
- ext/rszr/errors.c
|
35
|
-
- ext/rszr/image.c
|
36
|
-
- ext/rszr/rszr.c
|
37
|
-
- LICENSE
|
38
|
-
- README.md
|
39
|
-
- Rakefile
|
40
41
|
homepage: https://github.com/mtgrosser/rszr
|
41
42
|
licenses:
|
42
43
|
- MIT
|
43
44
|
metadata: {}
|
44
|
-
post_install_message:
|
45
|
+
post_install_message:
|
45
46
|
rdoc_options: []
|
46
47
|
require_paths:
|
47
48
|
- lib
|
48
49
|
- ext
|
49
50
|
required_ruby_version: !ruby/object:Gem::Requirement
|
50
51
|
requirements:
|
51
|
-
- -
|
52
|
+
- - ">="
|
52
53
|
- !ruby/object:Gem::Version
|
53
54
|
version: '0'
|
54
55
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
55
56
|
requirements:
|
56
|
-
- -
|
57
|
+
- - ">="
|
57
58
|
- !ruby/object:Gem::Version
|
58
59
|
version: '0'
|
59
60
|
requirements:
|
60
61
|
- imlib2
|
61
|
-
|
62
|
-
|
63
|
-
signing_key:
|
62
|
+
rubygems_version: 3.1.4
|
63
|
+
signing_key:
|
64
64
|
specification_version: 4
|
65
65
|
summary: Fast image resizer
|
66
66
|
test_files: []
|