rszr 0.5.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://badge.fury.io/rb/rszr.svg)](http://badge.fury.io/rb/rszr) [![
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/rszr.svg)](http://badge.fury.io/rb/rszr) [![build](https://github.com/mtgrosser/rszr/actions/workflows/build.yml/badge.svg)](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
|
+
![Speed](https://github.com/mtgrosser/rszr/blob/master/benchmark/speed.png)
|
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
|