rszr 1.1.0 → 1.3.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 +110 -3
- data/ext/rszr/errors.c +3 -1
- data/ext/rszr/image.c +207 -16
- data/lib/rszr/color/base.rb +25 -0
- data/lib/rszr/color/cmya.rb +29 -0
- data/lib/rszr/color/gradient.rb +42 -0
- data/lib/rszr/color/point.rb +29 -0
- data/lib/rszr/color/rgba.rb +56 -0
- data/lib/rszr/color.rb +9 -19
- data/lib/rszr/fill.rb +21 -0
- data/lib/rszr/image.rb +45 -4
- data/lib/rszr/version.rb +1 -1
- data/lib/rszr.rb +2 -1
- metadata +8 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ce6596f9a3f8e4d355c5d974da7e918dd574517b1ab87aaf6f66e35c8c26c600
|
|
4
|
+
data.tar.gz: 21905d628d567cbc06de0b0db56dc1515c5da7f533acc3917aedb8d91962b9cc
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 262f3cda5bc4ff4c6a86a5e7bb2ab4de2414f2652648d09e0d8d2a327c62e37eaf83d091fff515853f2d853754d6d5dd56c8a97e51a851c46c427bd69207213d
|
|
7
|
+
data.tar.gz: 1bd9c512ccbd0b95cb665d8a439c2ffa24456671d8165c555ad190058a16bad70913dfada98fbe6a4280a843ccfd83db02ea203746764ef8050586abcfd414fa
|
data/README.md
CHANGED
|
@@ -52,6 +52,9 @@ image.save('resized.jpg')
|
|
|
52
52
|
|
|
53
53
|
# save it as PNG
|
|
54
54
|
image.save('resized.png')
|
|
55
|
+
|
|
56
|
+
# save without extension in given format
|
|
57
|
+
image.save('resized', format: 'png')
|
|
55
58
|
```
|
|
56
59
|
|
|
57
60
|
### Image info
|
|
@@ -60,9 +63,10 @@ image.width => 400
|
|
|
60
63
|
image.height => 300
|
|
61
64
|
image.dimensions => [400, 300]
|
|
62
65
|
image.format => "jpeg"
|
|
66
|
+
image.alpha? => false
|
|
63
67
|
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(
|
|
68
|
+
image[0, 0].to_hex => "#26738dff"
|
|
69
|
+
image[0, 0].to_hex(alpha: false) => "#26738d"
|
|
66
70
|
```
|
|
67
71
|
|
|
68
72
|
### Transformations
|
|
@@ -121,6 +125,97 @@ image.flop
|
|
|
121
125
|
image.dup
|
|
122
126
|
```
|
|
123
127
|
|
|
128
|
+
### Image generation
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
# generate new image with transparent background
|
|
132
|
+
image = Rszr::Image.new(500, 500, alpha: true, background: Rszr::Color::Transparent)
|
|
133
|
+
|
|
134
|
+
# fill image with 50% opacity
|
|
135
|
+
image.fill!(Rszr::Color::RGBA.new(0, 206, 209, 50))
|
|
136
|
+
|
|
137
|
+
# define a color gradient
|
|
138
|
+
gradient = Rszr::Color::Gradient.new do |g|
|
|
139
|
+
g.point 0, 255, 250, 205, 50
|
|
140
|
+
g.point 0.5, 135, 206, 250
|
|
141
|
+
g.point 1, Rszr::Color::White
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# draw a rectangle and fill it using the gradient with 45°
|
|
145
|
+
image.rectangle!(gradient.to_fill(45), 100, 100, 300, 300)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Colors
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
# pre-defined colors
|
|
152
|
+
Rszr::Color::White
|
|
153
|
+
Rszr::Color::Black
|
|
154
|
+
Rszr::Color::Transparent
|
|
155
|
+
|
|
156
|
+
# RGB
|
|
157
|
+
color = Rszr::Color.rgba(255, 250, 50)
|
|
158
|
+
color.red => 255
|
|
159
|
+
color.green => 250
|
|
160
|
+
color.blue => 50
|
|
161
|
+
color.alpha => 255
|
|
162
|
+
color.cyan => 0
|
|
163
|
+
color.magenta => 5
|
|
164
|
+
color.yellow => 205
|
|
165
|
+
|
|
166
|
+
# RGBA
|
|
167
|
+
Rszr::Color.rgba(255, 250, 50, 255)
|
|
168
|
+
|
|
169
|
+
# CMY
|
|
170
|
+
Rszr::Color.cmya(0, 5, 205)
|
|
171
|
+
|
|
172
|
+
# CMYA
|
|
173
|
+
Rszr::Color.cmya(0, 5, 205, 255)
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Color gradients
|
|
177
|
+
|
|
178
|
+
```ruby
|
|
179
|
+
# three-color linear gradient with changing opacity
|
|
180
|
+
gradient = Rszr::Color::Gradient.new do |g|
|
|
181
|
+
g.point 0, 255, 250, 205, 50
|
|
182
|
+
g.point 0.5, 135, 206, 250
|
|
183
|
+
g.point 1, Rszr::Color::White
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# alternative syntax
|
|
187
|
+
gradient = Rszr::Color::Gradient.new(0 => "#fffacd32", 0.5 => "#87cefa", 1 => "#fff")
|
|
188
|
+
|
|
189
|
+
# generate fill with 45° angle
|
|
190
|
+
fill = gradient.to_fill(45)
|
|
191
|
+
|
|
192
|
+
# use as image background
|
|
193
|
+
image = Rszr::Image.new(500, 500, background: fill)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Watermarking and image blending
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
# load logo
|
|
200
|
+
logo = Rszr::Image.load('logo.png')
|
|
201
|
+
|
|
202
|
+
# load image
|
|
203
|
+
image = Rszr::Image.load('image.jpg')
|
|
204
|
+
|
|
205
|
+
# enable alpha channel
|
|
206
|
+
image.alpha = true
|
|
207
|
+
|
|
208
|
+
# blend it onto the image at position (10, 10)
|
|
209
|
+
image.blend!(logo, 10, 10)
|
|
210
|
+
|
|
211
|
+
# blending modes:
|
|
212
|
+
# - copy (default)
|
|
213
|
+
# - add
|
|
214
|
+
# - subtract
|
|
215
|
+
# - reshade
|
|
216
|
+
image.blend(logo, 10, 10, mode: :subtract)
|
|
217
|
+
```
|
|
218
|
+
|
|
124
219
|
### Filters
|
|
125
220
|
|
|
126
221
|
Filters also support bang! and non-bang methods.
|
|
@@ -162,9 +257,21 @@ To enable autorotation by default:
|
|
|
162
257
|
Rszr.autorotate = true
|
|
163
258
|
```
|
|
164
259
|
|
|
260
|
+
### Creating interlaced PNG and progressive JPEG images
|
|
261
|
+
|
|
262
|
+
In order to save interlaced PNGs and progressive JPEGs, set the `interlace` option to `true`:
|
|
263
|
+
|
|
264
|
+
```ruby
|
|
265
|
+
image.save('interlaced.png', interlace: true)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Saving progressive JPEG images requires `imlib2` >= 1.8.1.
|
|
269
|
+
|
|
270
|
+
For EL8, there are pre-built RPMs provided by the [onrooby repo](http://downloads.onrooby.com/repo/el/8/x86_64/).
|
|
271
|
+
|
|
165
272
|
## Rails / ActiveStorage interface
|
|
166
273
|
|
|
167
|
-
Rszr provides a drop-in interface to the
|
|
274
|
+
Rszr provides a drop-in interface to the [image_processing](https://github.com/janko/image_processing) gem.
|
|
168
275
|
It is faster than both `mini_magick` and `vips` and way easier to install than the latter.
|
|
169
276
|
|
|
170
277
|
```ruby
|
data/ext/rszr/errors.c
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include "errors.h"
|
|
6
6
|
|
|
7
7
|
VALUE eRszrError = Qnil;
|
|
8
|
+
VALUE eRszrInternalError = Qnil;
|
|
8
9
|
VALUE eRszrFileNotFound = Qnil;
|
|
9
10
|
VALUE eRszrTransformationError = Qnil;
|
|
10
11
|
VALUE eRszrErrorWithMessage = Qnil;
|
|
@@ -33,11 +34,12 @@ const int RSZR_MAX_ERROR_INDEX = 13;
|
|
|
33
34
|
void Init_rszr_errors()
|
|
34
35
|
{
|
|
35
36
|
eRszrError = rb_define_class_under(mRszr, "Error", rb_eStandardError);
|
|
37
|
+
eRszrInternalError = rb_define_class_under(mRszr, "InternalError", eRszrError);
|
|
36
38
|
eRszrFileNotFound = rb_define_class_under(mRszr, "FileNotFound", eRszrError);
|
|
37
39
|
eRszrTransformationError = rb_define_class_under(mRszr, "TransformationError", eRszrError);
|
|
38
40
|
eRszrErrorWithMessage = rb_define_class_under(mRszr, "ErrorWithMessage", eRszrError);
|
|
39
41
|
eRszrLoadError = rb_define_class_under(mRszr, "LoadError", eRszrErrorWithMessage);
|
|
40
|
-
eRszrSaveError = rb_define_class_under(mRszr, "SaveError", eRszrErrorWithMessage);
|
|
42
|
+
eRszrSaveError = rb_define_class_under(mRszr, "SaveError", eRszrErrorWithMessage);
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
static void rszr_raise_error_with_message(VALUE rb_error_class, Imlib_Load_Error error)
|
data/ext/rszr/image.c
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
#include "errors.h"
|
|
7
7
|
|
|
8
8
|
VALUE cImage = Qnil;
|
|
9
|
+
VALUE cColorBase = Qnil;
|
|
10
|
+
VALUE cColorGradient = Qnil;
|
|
11
|
+
VALUE cColorPoint = Qnil;
|
|
12
|
+
VALUE cFill = Qnil;
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
static void rszr_free_image(Imlib_Image image)
|
|
@@ -34,7 +38,7 @@ static VALUE rszr_image_s_allocate(VALUE klass)
|
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
|
|
37
|
-
static VALUE
|
|
41
|
+
static VALUE rszr_image__initialize(VALUE self, VALUE rb_width, VALUE rb_height)
|
|
38
42
|
{
|
|
39
43
|
rszr_image_handle * handle;
|
|
40
44
|
|
|
@@ -98,7 +102,6 @@ static VALUE rszr_image__format_get(VALUE self)
|
|
|
98
102
|
}
|
|
99
103
|
}
|
|
100
104
|
|
|
101
|
-
|
|
102
105
|
static VALUE rszr_image__format_set(VALUE self, VALUE rb_format)
|
|
103
106
|
{
|
|
104
107
|
rszr_image_handle * handle;
|
|
@@ -113,6 +116,51 @@ static VALUE rszr_image__format_set(VALUE self, VALUE rb_format)
|
|
|
113
116
|
}
|
|
114
117
|
|
|
115
118
|
|
|
119
|
+
static void rszr_image_color_set(VALUE rb_color)
|
|
120
|
+
{
|
|
121
|
+
int r, g, b, a;
|
|
122
|
+
|
|
123
|
+
if(!rb_obj_is_kind_of(rb_color, cColorBase) || RBASIC_CLASS(rb_color) == cColorBase) {
|
|
124
|
+
rb_raise(rb_eArgError, "color must descend from Rszr::Color::Base");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
r = FIX2INT(rb_funcall(rb_color, rb_intern("red"), 0));
|
|
128
|
+
g = FIX2INT(rb_funcall(rb_color, rb_intern("green"), 0));
|
|
129
|
+
b = FIX2INT(rb_funcall(rb_color, rb_intern("blue"), 0));
|
|
130
|
+
a = FIX2INT(rb_funcall(rb_color, rb_intern("alpha"), 0));
|
|
131
|
+
|
|
132
|
+
// TODO: use color model specific setter function
|
|
133
|
+
imlib_context_set_color(r, g, b, a);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
static VALUE rszr_image_alpha_get(VALUE self)
|
|
138
|
+
{
|
|
139
|
+
rszr_image_handle * handle;
|
|
140
|
+
|
|
141
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
|
142
|
+
|
|
143
|
+
imlib_context_set_image(handle->image);
|
|
144
|
+
if (imlib_image_has_alpha()) {
|
|
145
|
+
return Qtrue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return Qfalse;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
static VALUE rszr_image_alpha_set(VALUE self, VALUE rb_alpha)
|
|
152
|
+
{
|
|
153
|
+
rszr_image_handle * handle;
|
|
154
|
+
|
|
155
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
|
156
|
+
|
|
157
|
+
imlib_context_set_image(handle->image);
|
|
158
|
+
imlib_image_set_has_alpha(RTEST(rb_alpha) ? 1 : 0);
|
|
159
|
+
|
|
160
|
+
return Qnil;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
116
164
|
static VALUE rszr_image_width(VALUE self)
|
|
117
165
|
{
|
|
118
166
|
rszr_image_handle * handle;
|
|
@@ -165,7 +213,6 @@ static VALUE rszr_image__pixel_get(VALUE self, VALUE rb_x, VALUE rb_y)
|
|
|
165
213
|
return rb_rgba;
|
|
166
214
|
}
|
|
167
215
|
|
|
168
|
-
|
|
169
216
|
/*
|
|
170
217
|
static VALUE rszr_image_get_quality(VALUE self)
|
|
171
218
|
{
|
|
@@ -205,6 +252,7 @@ static VALUE rszr_image_set_quality(VALUE self, VALUE rb_quality)
|
|
|
205
252
|
}
|
|
206
253
|
*/
|
|
207
254
|
|
|
255
|
+
|
|
208
256
|
static VALUE rszr_image_dup(VALUE self)
|
|
209
257
|
{
|
|
210
258
|
rszr_image_handle * handle;
|
|
@@ -357,6 +405,7 @@ static Imlib_Image rszr_create_cropped_scaled_image(const Imlib_Image image, VAL
|
|
|
357
405
|
|
|
358
406
|
imlib_context_set_image(image);
|
|
359
407
|
imlib_context_set_anti_alias(1);
|
|
408
|
+
imlib_context_set_dither(1);
|
|
360
409
|
resized_image = imlib_create_cropped_scaled_image(src_x, src_y, src_w, src_h, dst_w, dst_h);
|
|
361
410
|
|
|
362
411
|
if (!resized_image) {
|
|
@@ -400,6 +449,11 @@ static Imlib_Image rszr_create_cropped_image(const Imlib_Image image, VALUE rb_x
|
|
|
400
449
|
{
|
|
401
450
|
Imlib_Image cropped_image;
|
|
402
451
|
|
|
452
|
+
Check_Type(rb_x, T_FIXNUM);
|
|
453
|
+
Check_Type(rb_y, T_FIXNUM);
|
|
454
|
+
Check_Type(rb_w, T_FIXNUM);
|
|
455
|
+
Check_Type(rb_h, T_FIXNUM);
|
|
456
|
+
|
|
403
457
|
int x = NUM2INT(rb_x);
|
|
404
458
|
int y = NUM2INT(rb_y);
|
|
405
459
|
int w = NUM2INT(rb_w);
|
|
@@ -445,7 +499,130 @@ static VALUE rszr_image__crop(VALUE self, VALUE bang, VALUE rb_x, VALUE rb_y, VA
|
|
|
445
499
|
}
|
|
446
500
|
|
|
447
501
|
|
|
448
|
-
static VALUE
|
|
502
|
+
static VALUE rszr_image__blend(VALUE self, VALUE other, VALUE rb_merge_alpha, VALUE rb_mode,
|
|
503
|
+
VALUE rb_src_x, VALUE rb_src_y, VALUE rb_src_w, VALUE rb_src_h,
|
|
504
|
+
VALUE rb_dst_x, VALUE rb_dst_y, VALUE rb_dst_w, VALUE rb_dst_h)
|
|
505
|
+
{
|
|
506
|
+
rszr_image_handle * handle;
|
|
507
|
+
rszr_image_handle * other_handle;
|
|
508
|
+
Imlib_Operation operation;
|
|
509
|
+
|
|
510
|
+
Check_Type(rb_mode, T_FIXNUM);
|
|
511
|
+
Check_Type(rb_src_x, T_FIXNUM);
|
|
512
|
+
Check_Type(rb_src_y, T_FIXNUM);
|
|
513
|
+
Check_Type(rb_src_w, T_FIXNUM);
|
|
514
|
+
Check_Type(rb_src_h, T_FIXNUM);
|
|
515
|
+
Check_Type(rb_dst_x, T_FIXNUM);
|
|
516
|
+
Check_Type(rb_dst_y, T_FIXNUM);
|
|
517
|
+
Check_Type(rb_dst_w, T_FIXNUM);
|
|
518
|
+
Check_Type(rb_dst_h, T_FIXNUM);
|
|
519
|
+
|
|
520
|
+
operation = (Imlib_Operation) NUM2INT(rb_mode);
|
|
521
|
+
int src_x = NUM2INT(rb_src_x);
|
|
522
|
+
int src_y = NUM2INT(rb_src_y);
|
|
523
|
+
int src_w = NUM2INT(rb_src_w);
|
|
524
|
+
int src_h = NUM2INT(rb_src_h);
|
|
525
|
+
int dst_x = NUM2INT(rb_dst_x);
|
|
526
|
+
int dst_y = NUM2INT(rb_dst_y);
|
|
527
|
+
int dst_w = NUM2INT(rb_dst_w);
|
|
528
|
+
int dst_h = NUM2INT(rb_dst_h);
|
|
529
|
+
|
|
530
|
+
char merge_alpha = RTEST(rb_merge_alpha) ? 1 : 0;
|
|
531
|
+
|
|
532
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
|
533
|
+
Data_Get_Struct(other, rszr_image_handle, other_handle);
|
|
534
|
+
|
|
535
|
+
imlib_context_set_image(handle->image);
|
|
536
|
+
imlib_context_set_operation(operation);
|
|
537
|
+
imlib_blend_image_onto_image(other_handle->image, merge_alpha, src_x, src_y, src_w, src_h, dst_x, dst_y, dst_w, dst_h);
|
|
538
|
+
|
|
539
|
+
return self;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
static Imlib_Color_Range rszr_image_init_color_range(VALUE rb_gradient)
|
|
544
|
+
{
|
|
545
|
+
Imlib_Color_Range range;
|
|
546
|
+
VALUE rb_points;
|
|
547
|
+
VALUE rb_point;
|
|
548
|
+
VALUE rb_color;
|
|
549
|
+
int size, i;
|
|
550
|
+
double position;
|
|
551
|
+
int red, green, blue, alpha;
|
|
552
|
+
|
|
553
|
+
if(!rb_obj_is_kind_of(rb_gradient, cColorGradient)) {
|
|
554
|
+
rb_raise(rb_eArgError, "color must be a Rszr::Color::Gradient");
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
rb_points = rb_funcall(rb_gradient, rb_intern("points"), 0);
|
|
558
|
+
Check_Type(rb_points, T_ARRAY);
|
|
559
|
+
|
|
560
|
+
imlib_context_get_color(&red, &green, &blue, &alpha);
|
|
561
|
+
|
|
562
|
+
range = imlib_create_color_range();
|
|
563
|
+
imlib_context_set_color_range(range);
|
|
564
|
+
|
|
565
|
+
size = RARRAY_LEN(rb_points);
|
|
566
|
+
for (i = 0; i < size; i++) {
|
|
567
|
+
rb_point = rb_ary_entry(rb_points, i);
|
|
568
|
+
if(!rb_obj_is_kind_of(rb_point, cColorPoint))
|
|
569
|
+
rb_raise(rb_eArgError, "point must be a Rszr::Color::Point");
|
|
570
|
+
|
|
571
|
+
rb_color = rb_funcall(rb_point, rb_intern("color"), 0);
|
|
572
|
+
if(!rb_obj_is_kind_of(rb_color, cColorBase) || RBASIC_CLASS(rb_color) == cColorBase)
|
|
573
|
+
rb_raise(rb_eArgError, "color must descend from Rszr::Color::Base");
|
|
574
|
+
|
|
575
|
+
position = NUM2DBL(rb_funcall(rb_point, rb_intern("position"), 0));
|
|
576
|
+
|
|
577
|
+
rszr_image_color_set(rb_color);
|
|
578
|
+
imlib_add_color_to_color_range(position * 255);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
imlib_context_set_color(red, green, blue, alpha);
|
|
582
|
+
|
|
583
|
+
return range;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
static VALUE rszr_image__rectangle_bang(VALUE self, VALUE rb_fill, VALUE rb_x, VALUE rb_y, VALUE rb_w, VALUE rb_h)
|
|
588
|
+
{
|
|
589
|
+
rszr_image_handle * handle;
|
|
590
|
+
VALUE rb_gradient;
|
|
591
|
+
VALUE rb_color;
|
|
592
|
+
Imlib_Color_Range range;
|
|
593
|
+
double angle;
|
|
594
|
+
|
|
595
|
+
Check_Type(rb_x, T_FIXNUM);
|
|
596
|
+
Check_Type(rb_y, T_FIXNUM);
|
|
597
|
+
Check_Type(rb_w, T_FIXNUM);
|
|
598
|
+
Check_Type(rb_h, T_FIXNUM);
|
|
599
|
+
|
|
600
|
+
int x = NUM2INT(rb_x);
|
|
601
|
+
int y = NUM2INT(rb_y);
|
|
602
|
+
int w = NUM2INT(rb_w);
|
|
603
|
+
int h = NUM2INT(rb_h);
|
|
604
|
+
|
|
605
|
+
rb_gradient = rb_funcall(rb_fill, rb_intern("gradient"), 0);
|
|
606
|
+
rb_color = rb_funcall(rb_fill, rb_intern("color"), 0);
|
|
607
|
+
|
|
608
|
+
Data_Get_Struct(self, rszr_image_handle, handle);
|
|
609
|
+
imlib_context_set_image(handle->image);
|
|
610
|
+
|
|
611
|
+
if (!NIL_P(rb_gradient)) {
|
|
612
|
+
angle = NUM2DBL(rb_funcall(rb_fill, rb_intern("angle"), 0));
|
|
613
|
+
range = rszr_image_init_color_range(rb_gradient);
|
|
614
|
+
imlib_image_fill_color_range_rectangle(x, y, w, h, angle);
|
|
615
|
+
imlib_free_color_range();
|
|
616
|
+
} else if (!NIL_P(rb_color)) {
|
|
617
|
+
rszr_image_color_set(rb_color);
|
|
618
|
+
imlib_image_fill_rectangle(x, y, w, h);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
return self;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
static VALUE rszr_image__save(VALUE self, VALUE rb_path, VALUE rb_format, VALUE rb_quality, VALUE rb_interlace)
|
|
449
626
|
{
|
|
450
627
|
rszr_image_handle * handle;
|
|
451
628
|
char * path;
|
|
@@ -461,8 +638,14 @@ static VALUE rszr_image__save(VALUE self, VALUE rb_path, VALUE rb_format, VALUE
|
|
|
461
638
|
|
|
462
639
|
imlib_context_set_image(handle->image);
|
|
463
640
|
imlib_image_set_format(format);
|
|
641
|
+
|
|
464
642
|
if (quality)
|
|
465
643
|
imlib_image_attach_data_value("quality", NULL, quality, NULL);
|
|
644
|
+
|
|
645
|
+
imlib_image_remove_attached_data_value("interlacing");
|
|
646
|
+
if (RTEST(rb_interlace))
|
|
647
|
+
imlib_image_attach_data_value("interlacing", NULL, 1, NULL);
|
|
648
|
+
|
|
466
649
|
imlib_save_image_with_error_return(path, &save_error);
|
|
467
650
|
|
|
468
651
|
if (save_error) {
|
|
@@ -476,13 +659,18 @@ static VALUE rszr_image__save(VALUE self, VALUE rb_path, VALUE rb_format, VALUE
|
|
|
476
659
|
void Init_rszr_image()
|
|
477
660
|
{
|
|
478
661
|
cImage = rb_define_class_under(mRszr, "Image", rb_cObject);
|
|
662
|
+
|
|
663
|
+
cColorBase = rb_path2class("Rszr::Color::Base");
|
|
664
|
+
cColorGradient = rb_path2class("Rszr::Color::Gradient");
|
|
665
|
+
cColorPoint = rb_path2class("Rszr::Color::Point");
|
|
666
|
+
cFill = rb_path2class("Rszr::Fill");
|
|
667
|
+
|
|
479
668
|
rb_define_alloc_func(cImage, rszr_image_s_allocate);
|
|
480
669
|
|
|
481
670
|
// Class methods
|
|
482
671
|
rb_define_private_method(rb_singleton_class(cImage), "_load", rszr_image_s__load, 1);
|
|
483
672
|
|
|
484
673
|
// Instance methods
|
|
485
|
-
rb_define_method(cImage, "initialize", rszr_image_initialize, 2);
|
|
486
674
|
rb_define_method(cImage, "width", rszr_image_width, 0);
|
|
487
675
|
rb_define_method(cImage, "height", rszr_image_height, 0);
|
|
488
676
|
rb_define_method(cImage, "dup", rszr_image_dup, 0);
|
|
@@ -490,20 +678,23 @@ void Init_rszr_image()
|
|
|
490
678
|
rb_define_method(cImage, "flop!", rszr_image_flop_bang, 0);
|
|
491
679
|
rb_define_method(cImage, "flip!", rszr_image_flip_bang, 0);
|
|
492
680
|
|
|
493
|
-
|
|
494
|
-
|
|
681
|
+
rb_define_method(cImage, "alpha", rszr_image_alpha_get, 0);
|
|
682
|
+
rb_define_method(cImage, "alpha=", rszr_image_alpha_set, 1);
|
|
495
683
|
|
|
496
684
|
rb_define_protected_method(cImage, "_format", rszr_image__format_get, 0);
|
|
497
685
|
rb_define_protected_method(cImage, "_format=", rszr_image__format_set, 1);
|
|
498
|
-
|
|
499
|
-
rb_define_private_method(cImage, "
|
|
500
|
-
rb_define_private_method(cImage, "
|
|
501
|
-
rb_define_private_method(cImage, "
|
|
502
|
-
rb_define_private_method(cImage, "
|
|
503
|
-
rb_define_private_method(cImage, "
|
|
504
|
-
rb_define_private_method(cImage, "
|
|
505
|
-
|
|
506
|
-
rb_define_private_method(cImage, "
|
|
686
|
+
|
|
687
|
+
rb_define_private_method(cImage, "_initialize", rszr_image__initialize, 2);
|
|
688
|
+
rb_define_private_method(cImage, "_resize", rszr_image__resize, 7);
|
|
689
|
+
rb_define_private_method(cImage, "_crop", rszr_image__crop, 5);
|
|
690
|
+
rb_define_private_method(cImage, "_turn!", rszr_image__turn_bang, 1);
|
|
691
|
+
rb_define_private_method(cImage, "_rotate", rszr_image__rotate, 2);
|
|
692
|
+
rb_define_private_method(cImage, "_sharpen!", rszr_image__sharpen_bang, 1);
|
|
693
|
+
rb_define_private_method(cImage, "_pixel", rszr_image__pixel_get, 2);
|
|
694
|
+
rb_define_private_method(cImage, "_blend", rszr_image__blend, 11);
|
|
695
|
+
rb_define_private_method(cImage, "_rectangle!", rszr_image__rectangle_bang, 5);
|
|
696
|
+
|
|
697
|
+
rb_define_private_method(cImage, "_save", rszr_image__save, 4);
|
|
507
698
|
}
|
|
508
699
|
|
|
509
700
|
#endif
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module Rszr
|
|
2
|
+
module Color
|
|
3
|
+
|
|
4
|
+
class Base
|
|
5
|
+
attr_reader :alpha
|
|
6
|
+
|
|
7
|
+
def rgba
|
|
8
|
+
[red, green, blue, alpha]
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def cmya
|
|
12
|
+
[cyan, magenta, yellow, alpha]
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def ==(other)
|
|
16
|
+
other.is_a?(Base) && rgba == other.rgba
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def to_fill(*)
|
|
20
|
+
Fill.new(color: self)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Rszr
|
|
2
|
+
module Color
|
|
3
|
+
|
|
4
|
+
class CMYA < Base
|
|
5
|
+
attr_reader :cyan, :magenta, :yellow
|
|
6
|
+
|
|
7
|
+
def initialize(cyan, magenta, yellow, alpha = 255)
|
|
8
|
+
if cyan < 0 || cyan > 255 || magenta < 0 || magenta > 255 || yellow < 0 || yellow > 255 || alpha < 0 || alpha > 255
|
|
9
|
+
raise ArgumentError, 'color out of range'
|
|
10
|
+
end
|
|
11
|
+
@cyan, @magenta, @yellow = cyan, magenta, yellow
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def red
|
|
15
|
+
255 - cyan
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def green
|
|
19
|
+
255 - magenta
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def blue
|
|
23
|
+
255 - yellow
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Rszr
|
|
2
|
+
module Color
|
|
3
|
+
|
|
4
|
+
class Gradient
|
|
5
|
+
attr_reader :points
|
|
6
|
+
|
|
7
|
+
def initialize(*args)
|
|
8
|
+
@points = []
|
|
9
|
+
points = args.last.is_a?(Hash) ? args.pop.dup : {}
|
|
10
|
+
args.each { |point| self << point }
|
|
11
|
+
points.each { |pos, color| point(pos, color) }
|
|
12
|
+
yield self if block_given?
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def initialize_dup(other) # :nodoc:
|
|
16
|
+
@points = other.points.map(&:dup)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def <<(position, red = nil, green = nil, blue= nil, alpha = 255)
|
|
20
|
+
point = if red.is_a?(Point)
|
|
21
|
+
red
|
|
22
|
+
elsif red.is_a?(Color::Base)
|
|
23
|
+
Point.new(position, red)
|
|
24
|
+
elsif red.is_a?(String) && red.start_with?('#')
|
|
25
|
+
Point.new(position, Color.hex(red))
|
|
26
|
+
else
|
|
27
|
+
Point.new(position, RGBA.new(red, green, blue, alpha))
|
|
28
|
+
end
|
|
29
|
+
points << point
|
|
30
|
+
points.sort!
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
alias_method :point, :<<
|
|
34
|
+
|
|
35
|
+
def to_fill(angle = 0)
|
|
36
|
+
Fill.new(gradient: self, angle: angle)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Rszr
|
|
2
|
+
module Color
|
|
3
|
+
|
|
4
|
+
class Point
|
|
5
|
+
attr_reader :position, :color
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def prgba(position, red, green, blue, alpha = 255)
|
|
9
|
+
new(position, RGBA.new(red, green, blue, alpha))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def initialize(position, color)
|
|
14
|
+
raise ArgumentError, 'position must be within 0..1' unless (0..1).cover?(position)
|
|
15
|
+
raise ArgumentError, 'color must be a Rszr::Color::Base' unless color.is_a?(Rszr::Color::Base)
|
|
16
|
+
@position, @color = position, color
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def <=>(other)
|
|
20
|
+
position <=> other.position
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def prgba
|
|
24
|
+
[position, *color.rgba]
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
module Rszr
|
|
2
|
+
module Color
|
|
3
|
+
|
|
4
|
+
class << self
|
|
5
|
+
def rgba(red, green, blue, alpha = 255)
|
|
6
|
+
RGBA.new(red, green, blue, alpha)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def hex(str)
|
|
10
|
+
str = str[1..-1] if str.start_with?('#')
|
|
11
|
+
case str.size
|
|
12
|
+
when 3, 4 then hex(str.chars.map { |c| c * 2 }.join)
|
|
13
|
+
when 6 then hex("#{str}ff")
|
|
14
|
+
when 8
|
|
15
|
+
rgba(*str.scan(/../).map(&:hex))
|
|
16
|
+
else
|
|
17
|
+
raise ArgumentError, 'invalid color code'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class RGBA < Base
|
|
23
|
+
attr_reader :red, :green, :blue
|
|
24
|
+
|
|
25
|
+
def initialize(red, green, blue, alpha = 255)
|
|
26
|
+
if red < 0 || red > 255 || green < 0 || green > 255 || blue < 0 || blue > 255 || alpha < 0 || alpha > 255
|
|
27
|
+
raise ArgumentError, 'color out of range'
|
|
28
|
+
end
|
|
29
|
+
@red, @green, @blue, @alpha = red, green, blue, alpha
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def cyan
|
|
33
|
+
255 - red
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def magenta
|
|
37
|
+
255 - green
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def yellow
|
|
41
|
+
255 - blue
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def to_i(alpha: true)
|
|
45
|
+
i = red.to_i << 24 | green.to_i << 16 | blue.to_i << 8 | self.alpha.to_i
|
|
46
|
+
alpha ? i : i >> 8
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def to_hex(alpha: true)
|
|
50
|
+
"#%0#{alpha ? 8 : 6}x" % to_i(alpha: alpha)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
end
|
|
56
|
+
end
|
data/lib/rszr/color.rb
CHANGED
|
@@ -1,25 +1,15 @@
|
|
|
1
|
+
require_relative 'color/base'
|
|
2
|
+
require_relative 'color/rgba'
|
|
3
|
+
require_relative 'color/cmya'
|
|
4
|
+
require_relative 'color/point'
|
|
5
|
+
require_relative 'color/gradient'
|
|
6
|
+
|
|
1
7
|
module Rszr
|
|
2
8
|
module Color
|
|
3
9
|
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
10
|
+
Transparent = RGBA.new(0, 0, 0, 0)
|
|
11
|
+
White = RGBA.new(255,255,255)
|
|
12
|
+
Black = RGBA.new(0, 0, 0)
|
|
23
13
|
|
|
24
14
|
end
|
|
25
15
|
end
|
data/lib/rszr/fill.rb
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Rszr
|
|
2
|
+
class Fill
|
|
3
|
+
attr_reader :color, :gradient, :angle
|
|
4
|
+
|
|
5
|
+
def initialize(color: nil, gradient: nil, angle: 0)
|
|
6
|
+
if gradient
|
|
7
|
+
@gradient = gradient
|
|
8
|
+
@angle = angle || 0
|
|
9
|
+
elsif color
|
|
10
|
+
@color = color
|
|
11
|
+
else
|
|
12
|
+
raise ArgumentError, 'incomplete fill definition'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_fill(*)
|
|
17
|
+
self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/rszr/image.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
module Rszr
|
|
2
2
|
class Image
|
|
3
3
|
GRAVITIES = [true, :center, :n, :nw, :w, :sw, :s, :se, :e, :ne].freeze
|
|
4
|
+
BLENDING_MODES = %i[copy add subtract reshade].freeze
|
|
4
5
|
|
|
5
6
|
extend Identification
|
|
6
7
|
include Buffered
|
|
@@ -40,6 +41,8 @@ module Rszr
|
|
|
40
41
|
self._format = fmt
|
|
41
42
|
end
|
|
42
43
|
|
|
44
|
+
alias_method :alpha?, :alpha
|
|
45
|
+
|
|
43
46
|
def [](x, y)
|
|
44
47
|
if x >= 0 && x <= width - 1 && y >= 0 && y <= height - 1
|
|
45
48
|
Color::RGBA.new(*_pixel(x, y))
|
|
@@ -49,7 +52,7 @@ module Rszr
|
|
|
49
52
|
def inspect
|
|
50
53
|
fmt = format
|
|
51
54
|
fmt = " #{fmt.upcase}" if fmt
|
|
52
|
-
"#<#{self.class.name}:0x#{object_id.to_s(16)} #{width}x#{height}#{fmt}>"
|
|
55
|
+
"#<#{self.class.name}:0x#{object_id.to_s(16)} #{width}x#{height}x#{alpha? ? 32 : 24}#{fmt}>"
|
|
53
56
|
end
|
|
54
57
|
|
|
55
58
|
module Transformations
|
|
@@ -144,15 +147,51 @@ module Rszr
|
|
|
144
147
|
def gamma(*args, **opts)
|
|
145
148
|
dup.gamma!(*args, **opts)
|
|
146
149
|
end
|
|
150
|
+
|
|
151
|
+
def blend!(image, x, y, mode: :copy)
|
|
152
|
+
raise ArgumentError, "mode must be one of #{BLENDING_MODES.map(&:to_s).join(', ')}" unless BLENDING_MODES.include?(mode)
|
|
153
|
+
_blend(image, true, BLENDING_MODES.index(mode), 0, 0, image.width, image.height, x, y, image.width, image.height)
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def blend(*args, **opts)
|
|
157
|
+
dup.blend!(*args, **opts)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def rectangle!(coloring, x, y, w, h)
|
|
161
|
+
raise ArgumentError, "coloring must respond to to_fill" unless coloring.respond_to?(:to_fill)
|
|
162
|
+
_rectangle!(coloring.to_fill, x, y, w, h)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def rectangle(*args, **opts)
|
|
166
|
+
dup.rectangle!(*args, **opts)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def fill!(coloring)
|
|
170
|
+
raise ArgumentError, "coloring must respond to to_fill" unless coloring.respond_to?(:to_fill)
|
|
171
|
+
rectangle!(coloring, 0, 0, width, height)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def fill(*args, **opts)
|
|
175
|
+
dup.fill(*args, **opts)
|
|
176
|
+
end
|
|
147
177
|
end
|
|
148
178
|
|
|
149
179
|
include Transformations
|
|
150
180
|
|
|
151
|
-
def
|
|
181
|
+
def initialize(width, height, alpha: false, background: nil)
|
|
182
|
+
raise ArgumentError, 'illegal image dimensions' if width < 1 || width > 32766 || height < 1 || height > 32766
|
|
183
|
+
raise ArgumentError, 'background must respond to to_fill' if background && !(background.respond_to?(:to_fill))
|
|
184
|
+
_initialize(width, height).tap do |image|
|
|
185
|
+
image.alpha = alpha
|
|
186
|
+
image.fill!(background) if background
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def save(path, format: nil, quality: nil, interlace: false)
|
|
152
191
|
format ||= format_from_filename(path) || self.format || 'jpg'
|
|
153
192
|
raise ArgumentError, "invalid quality #{quality.inspect}" if quality && !(0..100).cover?(quality)
|
|
154
193
|
ensure_path_is_writable(path)
|
|
155
|
-
_save(path.to_s, format.to_s, quality)
|
|
194
|
+
_save(path.to_s, format.to_s, quality, interlace)
|
|
156
195
|
end
|
|
157
196
|
|
|
158
197
|
def save_data(format: nil, quality: nil)
|
|
@@ -271,7 +310,9 @@ module Rszr
|
|
|
271
310
|
end
|
|
272
311
|
|
|
273
312
|
def format_from_filename(path)
|
|
274
|
-
File.extname(path)[1..-1]
|
|
313
|
+
if extension = File.extname(path)[1..-1]
|
|
314
|
+
extension.downcase
|
|
315
|
+
end
|
|
275
316
|
end
|
|
276
317
|
|
|
277
318
|
def ensure_path_is_writable(path)
|
data/lib/rszr/version.rb
CHANGED
data/lib/rszr.rb
CHANGED
|
@@ -3,13 +3,14 @@ require 'pathname'
|
|
|
3
3
|
require 'tempfile'
|
|
4
4
|
require 'stringio'
|
|
5
5
|
|
|
6
|
-
require 'rszr/rszr'
|
|
7
6
|
require 'rszr/version'
|
|
8
7
|
require 'rszr/stream'
|
|
9
8
|
require 'rszr/identification'
|
|
10
9
|
require 'rszr/orientation'
|
|
11
10
|
require 'rszr/buffered'
|
|
12
11
|
require 'rszr/color'
|
|
12
|
+
require 'rszr/fill'
|
|
13
|
+
require 'rszr/rszr'
|
|
13
14
|
require 'rszr/image'
|
|
14
15
|
|
|
15
16
|
module Rszr
|
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: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Matthias Grosser
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-
|
|
11
|
+
date: 2022-08-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Fast image resizer - do one thing and do it fast.
|
|
14
14
|
email:
|
|
@@ -32,6 +32,12 @@ files:
|
|
|
32
32
|
- lib/rszr/batch_transformation.rb
|
|
33
33
|
- lib/rszr/buffered.rb
|
|
34
34
|
- lib/rszr/color.rb
|
|
35
|
+
- lib/rszr/color/base.rb
|
|
36
|
+
- lib/rszr/color/cmya.rb
|
|
37
|
+
- lib/rszr/color/gradient.rb
|
|
38
|
+
- lib/rszr/color/point.rb
|
|
39
|
+
- lib/rszr/color/rgba.rb
|
|
40
|
+
- lib/rszr/fill.rb
|
|
35
41
|
- lib/rszr/identification.rb
|
|
36
42
|
- lib/rszr/image.rb
|
|
37
43
|
- lib/rszr/image_processing.rb
|