rszr 1.2.1 → 1.3.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 +95 -4
- data/ext/rszr/errors.c +3 -1
- data/ext/rszr/image.c +199 -13
- 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 +40 -1
- 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
@@ -63,9 +63,10 @@ image.width => 400
|
|
63
63
|
image.height => 300
|
64
64
|
image.dimensions => [400, 300]
|
65
65
|
image.format => "jpeg"
|
66
|
+
image.alpha? => false
|
66
67
|
image[0, 0] => <Rszr::Color::RGBA @red=38, @green=115, @blue=141, @alpha=255>
|
67
|
-
image[0, 0].to_hex => "26738dff"
|
68
|
-
image[0, 0].to_hex(
|
68
|
+
image[0, 0].to_hex => "#26738dff"
|
69
|
+
image[0, 0].to_hex(alpha: false) => "#26738d"
|
69
70
|
```
|
70
71
|
|
71
72
|
### Transformations
|
@@ -124,6 +125,97 @@ image.flop
|
|
124
125
|
image.dup
|
125
126
|
```
|
126
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
|
+
|
127
219
|
### Filters
|
128
220
|
|
129
221
|
Filters also support bang! and non-bang methods.
|
@@ -173,8 +265,7 @@ In order to save interlaced PNGs and progressive JPEGs, set the `interlace` opti
|
|
173
265
|
image.save('interlaced.png', interlace: true)
|
174
266
|
```
|
175
267
|
|
176
|
-
|
177
|
-
but a [patch](https://git.enlightenment.org/legacy/imlib2.git/commit/?id=37e8c9578897259211284d3590cc38b7f6a718dc) has been submitted.
|
268
|
+
Saving progressive JPEG images requires `imlib2` >= 1.8.1.
|
178
269
|
|
179
270
|
For EL8, there are pre-built RPMs provided by the [onrooby repo](http://downloads.onrooby.com/repo/el/8/x86_64/).
|
180
271
|
|
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;
|
@@ -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,6 +499,129 @@ static VALUE rszr_image__crop(VALUE self, VALUE bang, VALUE rb_x, VALUE rb_y, VA
|
|
445
499
|
}
|
446
500
|
|
447
501
|
|
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
|
+
|
448
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;
|
@@ -461,6 +638,7 @@ 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);
|
466
644
|
|
@@ -481,13 +659,18 @@ static VALUE rszr_image__save(VALUE self, VALUE rb_path, VALUE rb_format, VALUE
|
|
481
659
|
void Init_rszr_image()
|
482
660
|
{
|
483
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
|
+
|
484
668
|
rb_define_alloc_func(cImage, rszr_image_s_allocate);
|
485
669
|
|
486
670
|
// Class methods
|
487
671
|
rb_define_private_method(rb_singleton_class(cImage), "_load", rszr_image_s__load, 1);
|
488
672
|
|
489
673
|
// Instance methods
|
490
|
-
rb_define_method(cImage, "initialize", rszr_image_initialize, 2);
|
491
674
|
rb_define_method(cImage, "width", rszr_image_width, 0);
|
492
675
|
rb_define_method(cImage, "height", rszr_image_height, 0);
|
493
676
|
rb_define_method(cImage, "dup", rszr_image_dup, 0);
|
@@ -495,19 +678,22 @@ void Init_rszr_image()
|
|
495
678
|
rb_define_method(cImage, "flop!", rszr_image_flop_bang, 0);
|
496
679
|
rb_define_method(cImage, "flip!", rszr_image_flip_bang, 0);
|
497
680
|
|
498
|
-
|
499
|
-
|
681
|
+
rb_define_method(cImage, "alpha", rszr_image_alpha_get, 0);
|
682
|
+
rb_define_method(cImage, "alpha=", rszr_image_alpha_set, 1);
|
500
683
|
|
501
684
|
rb_define_protected_method(cImage, "_format", rszr_image__format_get, 0);
|
502
685
|
rb_define_protected_method(cImage, "_format=", rszr_image__format_set, 1);
|
503
|
-
|
504
|
-
rb_define_private_method(cImage, "
|
505
|
-
rb_define_private_method(cImage, "
|
506
|
-
rb_define_private_method(cImage, "
|
507
|
-
rb_define_private_method(cImage, "
|
508
|
-
rb_define_private_method(cImage, "
|
509
|
-
rb_define_private_method(cImage, "
|
510
|
-
|
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
|
+
|
511
697
|
rb_define_private_method(cImage, "_save", rszr_image__save, 4);
|
512
698
|
}
|
513
699
|
|
@@ -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,10 +147,46 @@ 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
|
|
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
|
+
|
151
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)
|
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
|