rszr 0.8.0 → 1.0.0
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 +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: []
|