rszr 0.5.2 → 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 +4 -4
- data/README.md +137 -15
- data/ext/rszr/errors.c +8 -3
- data/ext/rszr/image.c +209 -9
- data/ext/rszr/rszr.h +1 -0
- data/lib/rszr/batch_transformation.rb +24 -0
- data/lib/rszr/buffered.rb +25 -0
- data/lib/rszr/color.rb +25 -0
- data/lib/rszr/identification.rb +60 -0
- data/lib/rszr/image.rb +233 -55
- data/lib/rszr/image_processing.rb +82 -0
- data/lib/rszr/orientation.rb +107 -0
- data/lib/rszr/stream.rb +61 -0
- data/lib/rszr/version.rb +1 -1
- data/lib/rszr.rb +21 -0
- metadata +16 -134
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
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
@@ -1,7 +1,8 @@
|
|
1
|
-
[](http://badge.fury.io/rb/rszr) [](http://badge.fury.io/rb/rszr) [](https://github.com/mtgrosser/rszr/actions/workflows/build.yml)
|
2
2
|
# Rszr - fast image resizer for Ruby
|
3
3
|
|
4
|
-
Rszr is an image resizer for Ruby based on the Imlib2 library.
|
4
|
+
Rszr is an image resizer for Ruby based on the Imlib2 library.
|
5
|
+
It is faster and consumes less memory than MiniMagick, GD2 and VIPS, and comes with an optional drop-in interface for Rails ActiveStorage image processing.
|
5
6
|
|
6
7
|
## Installation
|
7
8
|
|
@@ -13,7 +14,7 @@ gem 'rszr'
|
|
13
14
|
|
14
15
|
### Imlib2
|
15
16
|
|
16
|
-
Rszr requires the Imlib2 library to do the heavy lifting.
|
17
|
+
Rszr requires the `Imlib2` library to do the heavy lifting.
|
17
18
|
|
18
19
|
#### OS X
|
19
20
|
|
@@ -27,10 +28,14 @@ brew install imlib2
|
|
27
28
|
|
28
29
|
Using your favourite package manager:
|
29
30
|
|
31
|
+
##### RedHat-based
|
32
|
+
|
30
33
|
```bash
|
31
34
|
yum install imlib2 imlib2-devel
|
32
35
|
```
|
33
36
|
|
37
|
+
##### Debian-based
|
38
|
+
|
34
39
|
```bash
|
35
40
|
apt-get install libimlib2 libimlib2-dev
|
36
41
|
```
|
@@ -47,7 +52,28 @@ image.save('resized.jpg')
|
|
47
52
|
|
48
53
|
# save it as PNG
|
49
54
|
image.save('resized.png')
|
55
|
+
```
|
50
56
|
|
57
|
+
### Image info
|
58
|
+
```ruby
|
59
|
+
image.width => 400
|
60
|
+
image.height => 300
|
61
|
+
image.dimensions => [400, 300]
|
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"
|
66
|
+
```
|
67
|
+
|
68
|
+
### Transformations
|
69
|
+
|
70
|
+
For each transformation, there is a bang! and non-bang method.
|
71
|
+
The bang method changes the image in place, while the non-bang method
|
72
|
+
creates a copy of the image in memory.
|
73
|
+
|
74
|
+
#### Resizing
|
75
|
+
|
76
|
+
```ruby
|
51
77
|
# auto height
|
52
78
|
image.resize(400, :auto)
|
53
79
|
|
@@ -57,6 +83,22 @@ image.resize(:auto, 300)
|
|
57
83
|
# scale factor
|
58
84
|
image.resize(0.5)
|
59
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
|
60
102
|
# crop
|
61
103
|
image.crop(200, 200, 100, 100)
|
62
104
|
|
@@ -66,30 +108,110 @@ image.turn!(3)
|
|
66
108
|
# rotate one time 90 deg counterclockwise
|
67
109
|
image.turn!(-1)
|
68
110
|
|
111
|
+
# rotate by arbitrary angle
|
112
|
+
image.rotate(45)
|
113
|
+
|
114
|
+
# flip vertically
|
115
|
+
image.flip
|
116
|
+
|
117
|
+
# flop horizontally
|
118
|
+
image.flop
|
119
|
+
|
69
120
|
# initialize copy
|
70
121
|
image.dup
|
122
|
+
```
|
71
123
|
|
72
|
-
|
73
|
-
image.resize!(400, :auto)
|
124
|
+
### Filters
|
74
125
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
image
|
79
|
-
image.
|
126
|
+
Filters also support bang! and non-bang methods.
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
# sharpen image by pixel radius
|
130
|
+
image.sharpen!(1)
|
131
|
+
|
132
|
+
# blur image by pixel radius
|
133
|
+
image.blur!(1)
|
134
|
+
|
135
|
+
# brighten
|
136
|
+
image.brighten(0.1)
|
137
|
+
|
138
|
+
# darken
|
139
|
+
image.brighten(-0.1)
|
140
|
+
|
141
|
+
# contrast
|
142
|
+
image.contrast(0.5)
|
143
|
+
|
144
|
+
# gamma
|
145
|
+
image.gamma(1.1)
|
146
|
+
```
|
147
|
+
|
148
|
+
### Image auto orientation
|
149
|
+
|
150
|
+
Auto-rotation is supported for JPEG and TIFF files that include the necessary
|
151
|
+
EXIF metadata.
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
# load and autorotate
|
155
|
+
image = Rszr::Image.load('image.jpg', autorotate: true)
|
156
|
+
```
|
157
|
+
|
158
|
+
To enable autorotation by default:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
# auto-rotate by default, for Rails apps put this into an initializer
|
162
|
+
Rszr.autorotate = true
|
163
|
+
```
|
164
|
+
|
165
|
+
## Rails / ActiveStorage interface
|
166
|
+
|
167
|
+
Rszr provides a drop-in interface to the `image_processing` gem.
|
168
|
+
It is faster than both `mini_magick` and `vips` and way easier to install than the latter.
|
169
|
+
|
170
|
+
```ruby
|
171
|
+
# Gemfile
|
172
|
+
gem 'image_processing'
|
173
|
+
gem 'rszr'
|
174
|
+
|
175
|
+
# config/initializers/rszr.rb
|
176
|
+
require 'rszr/image_processing'
|
177
|
+
|
178
|
+
# config/application.rb
|
179
|
+
config.active_storage.variant_processor = :rszr
|
180
|
+
```
|
181
|
+
|
182
|
+
When creating image variants, you can use all of Rszr's transformation methods:
|
183
|
+
|
184
|
+
```erb
|
185
|
+
<%= image_tag user.avatar.variant(resize_to_fit: [300, 200]) %>
|
186
|
+
```
|
187
|
+
|
188
|
+
## Loading from and saving to memory
|
189
|
+
|
190
|
+
The `Imlib2` library is mainly file-oriented and doesn't provide a way of loading
|
191
|
+
the undecoded image from a memory buffer. Therefore, the functionality is
|
192
|
+
implemented on the Ruby side of the gem, writing the memory buffer to a Tempfile.
|
193
|
+
Currently, this local write cannot be avoided.
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
image = Rszr::Image.load_data(binary_data)
|
197
|
+
|
198
|
+
data = image.save_data(format: :jpeg)
|
80
199
|
```
|
81
200
|
|
82
201
|
## Thread safety
|
83
202
|
|
84
|
-
As of version 0.
|
203
|
+
As of version 0.5.0, Rszr is thread safe through Ruby's global VM lock.
|
85
204
|
Use of any previous versions in a threaded environment is discouraged.
|
86
205
|
|
87
206
|
## Speed
|
88
207
|
|
89
|
-
Resizing
|
208
|
+
Resizing a 1500x997 JPEG image to 800x532, 500 times:
|
209
|
+

|
210
|
+
|
90
211
|
|
91
212
|
Library | Time
|
92
213
|
----------------|-----------
|
93
|
-
MiniMagick |
|
94
|
-
GD2 |
|
95
|
-
|
214
|
+
MiniMagick | 27.0 s
|
215
|
+
GD2 | 28.2 s
|
216
|
+
VIPS | 13.6 s
|
217
|
+
Rszr | 7.9 s
|
data/ext/rszr/errors.c
CHANGED
@@ -42,10 +42,15 @@ void Init_rszr_errors()
|
|
42
42
|
|
43
43
|
static void rszr_raise_error_with_message(VALUE rb_error_class, Imlib_Load_Error error)
|
44
44
|
{
|
45
|
-
|
45
|
+
VALUE rb_error;
|
46
|
+
int error_index;
|
47
|
+
|
48
|
+
error_index = (int) error - 1;
|
49
|
+
|
46
50
|
if (error_index < 1 || error_index > RSZR_MAX_ERROR_INDEX)
|
47
|
-
error_index =
|
48
|
-
|
51
|
+
error_index = RSZR_MAX_ERROR_INDEX;
|
52
|
+
|
53
|
+
rb_error = rb_exc_new2(rb_error_class, sRszrErrorMessages[error_index]);
|
49
54
|
rb_exc_raise(rb_error);
|
50
55
|
}
|
51
56
|
|
data/ext/rszr/image.c
CHANGED
@@ -27,7 +27,6 @@ static void rszr_image_deallocate(rszr_image_handle * handle)
|
|
27
27
|
// fprintf(stderr, "\n");
|
28
28
|
}
|
29
29
|
|
30
|
-
|
31
30
|
static VALUE rszr_image_s_allocate(VALUE klass)
|
32
31
|
{
|
33
32
|
rszr_image_handle * handle = calloc(1, sizeof(rszr_image_handle));
|
@@ -61,13 +60,20 @@ static VALUE rszr_image_s__load(VALUE klass, VALUE rb_path)
|
|
61
60
|
path = StringValueCStr(rb_path);
|
62
61
|
|
63
62
|
imlib_set_cache_size(0);
|
64
|
-
image =
|
63
|
+
image = imlib_load_image_without_cache(path);
|
65
64
|
|
66
65
|
if (!image) {
|
67
|
-
|
68
|
-
|
66
|
+
image = imlib_load_image_with_error_return(path, &error);
|
67
|
+
|
68
|
+
if (!image) {
|
69
|
+
rszr_raise_load_error(error);
|
70
|
+
return Qnil;
|
71
|
+
}
|
69
72
|
}
|
70
73
|
|
74
|
+
imlib_context_set_image(image);
|
75
|
+
imlib_image_set_irrelevant_format(0);
|
76
|
+
|
71
77
|
oImage = rszr_image_s_allocate(cImage);
|
72
78
|
Data_Get_Struct(oImage, rszr_image_handle, handle);
|
73
79
|
handle->image = image;
|
@@ -75,7 +81,7 @@ static VALUE rszr_image_s__load(VALUE klass, VALUE rb_path)
|
|
75
81
|
}
|
76
82
|
|
77
83
|
|
78
|
-
static VALUE
|
84
|
+
static VALUE rszr_image__format_get(VALUE self)
|
79
85
|
{
|
80
86
|
rszr_image_handle * handle;
|
81
87
|
char * format;
|
@@ -93,6 +99,20 @@ static VALUE rszr_image_format(VALUE self)
|
|
93
99
|
}
|
94
100
|
|
95
101
|
|
102
|
+
static VALUE rszr_image__format_set(VALUE self, VALUE rb_format)
|
103
|
+
{
|
104
|
+
rszr_image_handle * handle;
|
105
|
+
char * format = StringValueCStr(rb_format);
|
106
|
+
|
107
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
108
|
+
|
109
|
+
imlib_context_set_image(handle->image);
|
110
|
+
imlib_image_set_format(format);
|
111
|
+
|
112
|
+
return self;
|
113
|
+
}
|
114
|
+
|
115
|
+
|
96
116
|
static VALUE rszr_image_width(VALUE self)
|
97
117
|
{
|
98
118
|
rszr_image_handle * handle;
|
@@ -121,6 +141,70 @@ static VALUE rszr_image_height(VALUE self)
|
|
121
141
|
}
|
122
142
|
|
123
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
|
+
|
169
|
+
/*
|
170
|
+
static VALUE rszr_image_get_quality(VALUE self)
|
171
|
+
{
|
172
|
+
rszr_image_handle * handle;
|
173
|
+
int quality;
|
174
|
+
|
175
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
176
|
+
|
177
|
+
imlib_context_set_image(handle->image);
|
178
|
+
quality = imlib_image_get_attached_value("quality");
|
179
|
+
|
180
|
+
if (quality) {
|
181
|
+
return INT2NUM(quality);
|
182
|
+
} else {
|
183
|
+
return Qnil;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
|
187
|
+
static VALUE rszr_image_set_quality(VALUE self, VALUE rb_quality)
|
188
|
+
{
|
189
|
+
rszr_image_handle * handle;
|
190
|
+
int quality;
|
191
|
+
|
192
|
+
Check_Type(rb_quality, T_FIXNUM);
|
193
|
+
quality = FIX2INT(rb_quality);
|
194
|
+
if (quality <= 0) {
|
195
|
+
rb_raise(rb_eArgError, "quality must be >= 0");
|
196
|
+
return Qnil;
|
197
|
+
}
|
198
|
+
|
199
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
200
|
+
|
201
|
+
imlib_context_set_image(handle->image);
|
202
|
+
imlib_image_attach_data_value("quality", NULL, quality, NULL);
|
203
|
+
|
204
|
+
return INT2NUM(quality);
|
205
|
+
}
|
206
|
+
*/
|
207
|
+
|
124
208
|
static VALUE rszr_image_dup(VALUE self)
|
125
209
|
{
|
126
210
|
rszr_image_handle * handle;
|
@@ -159,6 +243,105 @@ static VALUE rszr_image__turn_bang(VALUE self, VALUE orientation)
|
|
159
243
|
}
|
160
244
|
|
161
245
|
|
246
|
+
static VALUE rszr_image_flop_bang(VALUE self)
|
247
|
+
{
|
248
|
+
rszr_image_handle * handle;
|
249
|
+
|
250
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
251
|
+
|
252
|
+
imlib_context_set_image(handle->image);
|
253
|
+
imlib_image_flip_horizontal();
|
254
|
+
|
255
|
+
return self;
|
256
|
+
}
|
257
|
+
|
258
|
+
|
259
|
+
static VALUE rszr_image_flip_bang(VALUE self)
|
260
|
+
{
|
261
|
+
rszr_image_handle * handle;
|
262
|
+
|
263
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
264
|
+
|
265
|
+
imlib_context_set_image(handle->image);
|
266
|
+
imlib_image_flip_vertical();
|
267
|
+
|
268
|
+
return self;
|
269
|
+
}
|
270
|
+
|
271
|
+
|
272
|
+
static VALUE rszr_image__rotate(VALUE self, VALUE bang, VALUE rb_angle)
|
273
|
+
{
|
274
|
+
rszr_image_handle * handle;
|
275
|
+
rszr_image_handle * rotated_handle;
|
276
|
+
Imlib_Image rotated_image;
|
277
|
+
VALUE oRotatedImage;
|
278
|
+
double angle;
|
279
|
+
|
280
|
+
angle = NUM2DBL(rb_angle);
|
281
|
+
|
282
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
283
|
+
|
284
|
+
imlib_context_set_image(handle->image);
|
285
|
+
rotated_image = imlib_create_rotated_image(angle);
|
286
|
+
|
287
|
+
if (!rotated_image) {
|
288
|
+
rb_raise(eRszrTransformationError, "error rotating image");
|
289
|
+
return Qnil;
|
290
|
+
}
|
291
|
+
|
292
|
+
if (RTEST(bang)) {
|
293
|
+
rszr_free_image(handle->image);
|
294
|
+
handle->image = rotated_image;
|
295
|
+
|
296
|
+
return self;
|
297
|
+
}
|
298
|
+
else {
|
299
|
+
oRotatedImage = rszr_image_s_allocate(cImage);
|
300
|
+
Data_Get_Struct(oRotatedImage, rszr_image_handle, rotated_handle);
|
301
|
+
rotated_handle->image = rotated_image;
|
302
|
+
|
303
|
+
return oRotatedImage;
|
304
|
+
}
|
305
|
+
}
|
306
|
+
|
307
|
+
|
308
|
+
static VALUE rszr_image_filter_bang(VALUE self, VALUE rb_filter_expr)
|
309
|
+
{
|
310
|
+
rszr_image_handle * handle;
|
311
|
+
char * filter_expr;
|
312
|
+
|
313
|
+
filter_expr = StringValueCStr(rb_filter_expr);
|
314
|
+
|
315
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
316
|
+
|
317
|
+
imlib_context_set_image(handle->image);
|
318
|
+
imlib_apply_filter(filter_expr);
|
319
|
+
|
320
|
+
return self;
|
321
|
+
}
|
322
|
+
|
323
|
+
|
324
|
+
static VALUE rszr_image__sharpen_bang(VALUE self, VALUE rb_radius)
|
325
|
+
{
|
326
|
+
rszr_image_handle * handle;
|
327
|
+
int radius;
|
328
|
+
|
329
|
+
radius = NUM2INT(rb_radius);
|
330
|
+
|
331
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
332
|
+
|
333
|
+
imlib_context_set_image(handle->image);
|
334
|
+
|
335
|
+
if (radius >= 0) {
|
336
|
+
imlib_image_sharpen(radius);
|
337
|
+
} else {
|
338
|
+
imlib_image_blur(-radius);
|
339
|
+
}
|
340
|
+
|
341
|
+
return self;
|
342
|
+
}
|
343
|
+
|
344
|
+
|
162
345
|
static Imlib_Image rszr_create_cropped_scaled_image(const Imlib_Image image, VALUE rb_src_x, VALUE rb_src_y, VALUE rb_src_w, VALUE rb_src_h, VALUE rb_dst_w, VALUE rb_dst_h)
|
163
346
|
{
|
164
347
|
Imlib_Image resized_image;
|
@@ -262,20 +445,24 @@ static VALUE rszr_image__crop(VALUE self, VALUE bang, VALUE rb_x, VALUE rb_y, VA
|
|
262
445
|
}
|
263
446
|
|
264
447
|
|
265
|
-
static VALUE rszr_image__save(VALUE self, VALUE rb_path, VALUE rb_format)
|
448
|
+
static VALUE rszr_image__save(VALUE self, VALUE rb_path, VALUE rb_format, VALUE rb_quality)
|
266
449
|
{
|
267
450
|
rszr_image_handle * handle;
|
268
451
|
char * path;
|
269
452
|
char * format;
|
453
|
+
int quality;
|
270
454
|
Imlib_Load_Error save_error;
|
271
455
|
|
272
456
|
path = StringValueCStr(rb_path);
|
273
457
|
format = StringValueCStr(rb_format);
|
274
|
-
|
458
|
+
quality = (NIL_P(rb_quality)) ? 0 : FIX2INT(rb_quality);
|
459
|
+
|
275
460
|
Data_Get_Struct(self, rszr_image_handle, handle);
|
276
461
|
|
277
462
|
imlib_context_set_image(handle->image);
|
278
463
|
imlib_image_set_format(format);
|
464
|
+
if (quality)
|
465
|
+
imlib_image_attach_data_value("quality", NULL, quality, NULL);
|
279
466
|
imlib_save_image_with_error_return(path, &save_error);
|
280
467
|
|
281
468
|
if (save_error) {
|
@@ -298,12 +485,25 @@ void Init_rszr_image()
|
|
298
485
|
rb_define_method(cImage, "initialize", rszr_image_initialize, 2);
|
299
486
|
rb_define_method(cImage, "width", rszr_image_width, 0);
|
300
487
|
rb_define_method(cImage, "height", rszr_image_height, 0);
|
301
|
-
rb_define_method(cImage, "format", rszr_image_format, 0);
|
302
488
|
rb_define_method(cImage, "dup", rszr_image_dup, 0);
|
489
|
+
rb_define_method(cImage, "filter!", rszr_image_filter_bang, 1);
|
490
|
+
rb_define_method(cImage, "flop!", rszr_image_flop_bang, 0);
|
491
|
+
rb_define_method(cImage, "flip!", rszr_image_flip_bang, 0);
|
492
|
+
|
493
|
+
// rb_define_method(cImage, "quality", rszr_image_get_quality, 0);
|
494
|
+
// rb_define_method(cImage, "quality=", rszr_image_set_quality, 1);
|
495
|
+
|
496
|
+
rb_define_protected_method(cImage, "_format", rszr_image__format_get, 0);
|
497
|
+
rb_define_protected_method(cImage, "_format=", rszr_image__format_set, 1);
|
498
|
+
|
303
499
|
rb_define_private_method(cImage, "_resize", rszr_image__resize, 7);
|
304
500
|
rb_define_private_method(cImage, "_crop", rszr_image__crop, 5);
|
305
501
|
rb_define_private_method(cImage, "_turn!", rszr_image__turn_bang, 1);
|
306
|
-
rb_define_private_method(cImage, "
|
502
|
+
rb_define_private_method(cImage, "_rotate", rszr_image__rotate, 2);
|
503
|
+
rb_define_private_method(cImage, "_sharpen!", rszr_image__sharpen_bang, 1);
|
504
|
+
rb_define_private_method(cImage, "_pixel", rszr_image__pixel_get, 2);
|
505
|
+
|
506
|
+
rb_define_private_method(cImage, "_save", rszr_image__save, 3);
|
307
507
|
}
|
308
508
|
|
309
509
|
#endif
|
data/ext/rszr/rszr.h
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Rszr
|
2
|
+
class BatchTransformation
|
3
|
+
attr_reader :transformations, :image
|
4
|
+
|
5
|
+
def initialize(path, **opts)
|
6
|
+
puts "INITIALIZED BATCH for #{path}"
|
7
|
+
@image = path.is_a?(Image) ? path : Image.load(path, **opts)
|
8
|
+
@transformations = []
|
9
|
+
end
|
10
|
+
|
11
|
+
Image::Transformations.instance_methods.grep(/\w\z/) do |method|
|
12
|
+
define_method method do |*args|
|
13
|
+
transformations << [method, args]
|
14
|
+
self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
transformations.each { |method, args| image.public_send("#{method}!", *args) }
|
20
|
+
image
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rszr
|
2
|
+
module Buffered
|
3
|
+
def self.included(base)
|
4
|
+
base.extend Buffered
|
5
|
+
end
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def with_tempfile(format, data = nil)
|
10
|
+
raise ArgumentError, 'format is required' unless format
|
11
|
+
result = nil
|
12
|
+
Tempfile.create(['rszr-buffer', ".#{format}"], encoding: 'BINARY') do |file|
|
13
|
+
if data
|
14
|
+
file.binmode
|
15
|
+
file << data
|
16
|
+
file.fsync
|
17
|
+
file.rewind
|
18
|
+
end
|
19
|
+
result = yield(file)
|
20
|
+
end
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
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
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Type reader adapted from fastimage
|
2
|
+
# https://github.com/sdsykes/fastimage/
|
3
|
+
|
4
|
+
module Rszr
|
5
|
+
module Identification
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def identify(data)
|
10
|
+
case data[0, 2]
|
11
|
+
when 'BM'
|
12
|
+
:bmp
|
13
|
+
when 'GI'
|
14
|
+
:gif
|
15
|
+
when 0xff.chr + 0xd8.chr
|
16
|
+
:jpeg
|
17
|
+
when 0x89.chr + 'P'
|
18
|
+
:png
|
19
|
+
when 'II', 'MM'
|
20
|
+
case data[0, 11][8..10] # @stream.peek(11)[8..10]
|
21
|
+
when 'APC', "CR\002"
|
22
|
+
nil # do not recognise CRW or CR2 as tiff
|
23
|
+
else
|
24
|
+
:tiff
|
25
|
+
end
|
26
|
+
when '8B'
|
27
|
+
:psd
|
28
|
+
when "\0\0"
|
29
|
+
case data[0, 3].bytes.last #@stream.peek(3).bytes.to_a.last
|
30
|
+
when 0
|
31
|
+
# http://www.ftyps.com/what.html
|
32
|
+
# HEIC is composed of nested "boxes". Each box has a header composed of
|
33
|
+
# - Size (32 bit integer)
|
34
|
+
# - Box type (4 chars)
|
35
|
+
# - Extended size: only if size === 1, the type field is followed by 64 bit integer of extended size
|
36
|
+
# - Payload: Type-dependent
|
37
|
+
case data[0, 12][4..-1] #@stream.peek(12)[4..-1]
|
38
|
+
when 'ftypheic'
|
39
|
+
:heic
|
40
|
+
when 'ftypmif1'
|
41
|
+
:heif
|
42
|
+
end
|
43
|
+
# ico has either a 1 (for ico format) or 2 (for cursor) at offset 3
|
44
|
+
when 1 then :ico
|
45
|
+
when 2 then :cur
|
46
|
+
end
|
47
|
+
when 'RI'
|
48
|
+
:webp if data[0, 12][8..11] == 'WEBP' #@stream.peek(12)[8..11] == "WEBP"
|
49
|
+
when "<s"
|
50
|
+
:svg if data[0, 4] == '<svg' #@stream.peek(4) == "<svg"
|
51
|
+
when /\s\s|\s<|<[?!]/, 0xef.chr + 0xbb.chr
|
52
|
+
# Peek 10 more chars each time, and if end of file is reached just raise
|
53
|
+
# unknown. We assume the <svg tag cannot be within 10 chars of the end of
|
54
|
+
# the file, and is within the first 250 chars.
|
55
|
+
:svg if (1..25).detect { |n| data[0, 10 * n]&.include?('<svg') }
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|