rszr 1.2.1 → 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 +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
|