psd_native 0.4.0 → 0.5.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 +2 -0
- data/ext/psd_native/clipping_mask.c +60 -0
- data/ext/psd_native/clipping_mask.h +6 -0
- data/ext/psd_native/color.c +0 -28
- data/ext/psd_native/color.h +11 -7
- data/ext/psd_native/compose.c +365 -16
- data/ext/psd_native/compose.h +28 -0
- data/ext/psd_native/psd_native_ext.c +20 -0
- data/ext/psd_native/psd_native_ext.h +1 -1
- data/lib/psd_native/compose.rb +10 -2
- data/lib/psd_native/version.rb +1 -1
- data/lib/psd_native.rb +5 -0
- data/psd_native.gemspec +1 -1
- data/spec/image_spec.rb +2 -2
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c081770f983b87fa68b32f0a04576c8384bce760
|
4
|
+
data.tar.gz: aa04b516926a30d512c22fec87f43524a2eca4b5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d8876e7d2981bdecd7701c3d25adca20b8c5e4cebfc00f7f382ae227477972f3e6174ab60daef81f5713da7cbec490e057ae62e120e327221d13bba7968bd6a
|
7
|
+
data.tar.gz: 03a9c93c7c2ab9d7176909497edf96bcbbb500befeaac3cedc9005ba584bcb072940c9c4dd21b2176d856378c640117811dda1070f4bb789664bd3023d5a9eb9
|
data/README.md
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
#include "psd_native_ext.h"
|
2
|
+
|
3
|
+
VALUE psd_native_clipping_mask_apply(VALUE self) {
|
4
|
+
VALUE layer = rb_iv_get(self, "@layer");
|
5
|
+
if (rb_funcall(layer, rb_intern("clipped?"), 0) == Qfalse) {
|
6
|
+
return rb_iv_get(self, "@png");
|
7
|
+
}
|
8
|
+
|
9
|
+
psd_logger("debug", "Applying clipping mask with native code");
|
10
|
+
|
11
|
+
VALUE *dim = RARRAY_PTR(rb_funcall(layer, rb_intern("document_dimensions"), 0));
|
12
|
+
VALUE full_png = rb_funcall(self, rb_intern("compose_to_full"), 0);
|
13
|
+
|
14
|
+
uint32_t width = FIX2UINT(dim[0]);
|
15
|
+
uint32_t height = FIX2UINT(dim[1]);
|
16
|
+
VALUE mask = rb_funcall(self, rb_intern("mask"), 0);
|
17
|
+
VALUE pixel_data = rb_funcall(rb_funcall(mask, rb_intern("image"), 0), rb_intern("pixel_data"), 0);
|
18
|
+
VALUE pixel;
|
19
|
+
uint32_t color;
|
20
|
+
int mask_top = FIX2INT(rb_funcall(mask, rb_intern("top"), 0)),
|
21
|
+
mask_bottom = FIX2INT(rb_funcall(mask, rb_intern("bottom"), 0)),
|
22
|
+
mask_left = FIX2INT(rb_funcall(mask, rb_intern("left"), 0)),
|
23
|
+
mask_right = FIX2INT(rb_funcall(mask, rb_intern("right"), 0)),
|
24
|
+
mask_width = FIX2INT(rb_funcall(mask, rb_intern("width"), 0)),
|
25
|
+
alpha = 0,
|
26
|
+
mask_x, mask_y;
|
27
|
+
|
28
|
+
int x, y;
|
29
|
+
for (y = 0; y < height; y++) {
|
30
|
+
for (x = 0; x < width; x++) {
|
31
|
+
if (y < mask_top || y > mask_bottom || x < mask_left || x > mask_right) {
|
32
|
+
alpha = 0;
|
33
|
+
} else {
|
34
|
+
mask_x = x - mask_left;
|
35
|
+
mask_y = y - mask_top;
|
36
|
+
|
37
|
+
pixel = rb_ary_entry(pixel_data, mask_y * mask_width + mask_x);
|
38
|
+
if (pixel == Qnil) {
|
39
|
+
alpha = 0;
|
40
|
+
} else {
|
41
|
+
alpha = A(FIX2UINT(pixel));
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
color = FIX2UINT(rb_funcall(full_png, rb_intern("[]"), 2, INT2FIX(x), INT2FIX(y)));
|
46
|
+
color = (color & 0xffffff00) | (A(color) * alpha / 255);
|
47
|
+
rb_funcall(full_png, rb_intern("set_pixel"), 3, INT2FIX(x), INT2FIX(y), INT2FIX(color));
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
return rb_funcall(
|
52
|
+
full_png,
|
53
|
+
rb_intern("crop!"),
|
54
|
+
4,
|
55
|
+
rb_funcall(layer, rb_intern("left"), 0),
|
56
|
+
rb_funcall(layer, rb_intern("top"), 0),
|
57
|
+
rb_funcall(layer, rb_intern("width"), 0),
|
58
|
+
rb_funcall(layer, rb_intern("height"), 0)
|
59
|
+
);
|
60
|
+
}
|
data/ext/psd_native/color.c
CHANGED
@@ -17,32 +17,4 @@ VALUE psd_native_cmyk_to_rgb(VALUE self, VALUE c, VALUE m, VALUE y, VALUE k) {
|
|
17
17
|
|
18
18
|
int psd_clamp_int(int n, int low, int high) {
|
19
19
|
return n < low ? low : (n > high ? high : n);
|
20
|
-
}
|
21
|
-
|
22
|
-
int opaque(uint32_t color) {
|
23
|
-
return a(color) == 0x000000ff;
|
24
|
-
}
|
25
|
-
|
26
|
-
int transparent(uint32_t color) {
|
27
|
-
return a(color) == 0x00000000;
|
28
|
-
}
|
29
|
-
|
30
|
-
uint32_t r(uint32_t color) {
|
31
|
-
return (color & 0xff000000) >> 24;
|
32
|
-
}
|
33
|
-
|
34
|
-
uint32_t g(uint32_t color) {
|
35
|
-
return (color & 0x00ff0000) >> 16;
|
36
|
-
}
|
37
|
-
|
38
|
-
uint32_t b(uint32_t color) {
|
39
|
-
return (color & 0x0000ff00) >> 8;
|
40
|
-
}
|
41
|
-
|
42
|
-
uint32_t a(uint32_t color) {
|
43
|
-
return color & 0x000000ff;
|
44
|
-
}
|
45
|
-
|
46
|
-
uint32_t rgba(uint32_t r, uint32_t g, uint32_t b, uint32_t a) {
|
47
|
-
return r << 24 | g << 16 | b << 8 | a;
|
48
20
|
}
|
data/ext/psd_native/color.h
CHANGED
@@ -1,14 +1,18 @@
|
|
1
1
|
#ifndef PSD_NATIVE_COLOR
|
2
2
|
#define PSD_NATIVE_COLOR
|
3
3
|
|
4
|
+
#define BUILD_PIXEL(r, g, b, a) (((PIXEL) (r) << 24) + ((PIXEL) (g) << 16) + ((PIXEL) (b) << 8) + (PIXEL) (a))
|
5
|
+
#define CLAMP_PIXEL(p) ((p < 0) ? 0 : ((p > 255) ? 255 : p))
|
6
|
+
#define R(color) (PIXEL) (((PIXEL) (color) & (PIXEL) 0xff000000) >> 24)
|
7
|
+
#define G(color) (PIXEL) (((PIXEL) (color) & (PIXEL) 0x00ff0000) >> 16)
|
8
|
+
#define B(color) (PIXEL) (((PIXEL) (color) & (PIXEL) 0x0000ff00) >> 8)
|
9
|
+
#define A(color) (PIXEL) ((PIXEL) (color) & (PIXEL) 0x000000ff)
|
10
|
+
#define OPAQUE(color) ((PIXEL) A(color) == (PIXEL) 0x000000ff)
|
11
|
+
#define TRANSPARENT(color) ((PIXEL) A(color) == (PIXEL) 0x00000000)
|
12
|
+
#define PSD_MAX(a, b) (((a) > (b)) ? (a) : (b))
|
13
|
+
#define PSD_MIN(a, b) (((a) < (b)) ? (a) : (b))
|
14
|
+
|
4
15
|
VALUE psd_native_cmyk_to_rgb(VALUE self, VALUE c, VALUE m, VALUE y, VALUE k);
|
5
16
|
int psd_clamp_int(int n, int low, int high);
|
6
|
-
int opaque(uint32_t color);
|
7
|
-
int transparent(uint32_t color);
|
8
|
-
uint32_t r(uint32_t color);
|
9
|
-
uint32_t g(uint32_t color);
|
10
|
-
uint32_t b(uint32_t color);
|
11
|
-
uint32_t a(uint32_t color);
|
12
|
-
uint32_t rgba(uint32_t r, uint32_t g, uint32_t b, uint32_t a);
|
13
17
|
|
14
18
|
#endif
|
data/ext/psd_native/compose.c
CHANGED
@@ -1,29 +1,382 @@
|
|
1
|
+
#include <math.h>
|
1
2
|
#include "psd_native_ext.h"
|
2
3
|
|
3
4
|
AlphaValues alpha;
|
4
5
|
|
5
6
|
VALUE psd_native_compose_normal(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
PIXEL fg = FIX2UINT(r_fg);
|
8
|
+
PIXEL bg = FIX2UINT(r_bg);
|
9
|
+
PIXEL new_r, new_g, new_b;
|
9
10
|
|
10
|
-
if (
|
11
|
-
if (
|
11
|
+
if (OPAQUE(fg) || TRANSPARENT(bg)) return r_fg;
|
12
|
+
if (TRANSPARENT(fg)) return r_bg;
|
12
13
|
|
13
14
|
calculate_alphas(fg, bg, &opts);
|
14
15
|
|
15
|
-
new_r =
|
16
|
-
new_g =
|
17
|
-
new_b =
|
16
|
+
new_r = BLEND_CHANNEL(R(bg), R(fg), alpha.mix);
|
17
|
+
new_g = BLEND_CHANNEL(G(bg), G(fg), alpha.mix);
|
18
|
+
new_b = BLEND_CHANNEL(B(bg), B(fg), alpha.mix);
|
18
19
|
|
19
|
-
return INT2FIX(
|
20
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
20
21
|
}
|
21
22
|
|
22
|
-
|
23
|
+
VALUE psd_native_compose_darken(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
24
|
+
PIXEL fg = FIX2UINT(r_fg);
|
25
|
+
PIXEL bg = FIX2UINT(r_bg);
|
26
|
+
PIXEL new_r, new_g, new_b;
|
27
|
+
|
28
|
+
if (TRANSPARENT(bg)) return r_fg;
|
29
|
+
if (TRANSPARENT(fg)) return r_bg;
|
30
|
+
|
31
|
+
calculate_alphas(fg, bg, &opts);
|
32
|
+
|
33
|
+
new_r = R(fg) <= R(bg) ? BLEND_CHANNEL(R(bg), R(fg), alpha.mix) : R(bg);
|
34
|
+
new_g = G(fg) <= G(bg) ? BLEND_CHANNEL(G(bg), G(fg), alpha.mix) : G(bg);
|
35
|
+
new_b = B(fg) <= B(bg) ? BLEND_CHANNEL(B(bg), B(fg), alpha.mix) : B(bg);
|
36
|
+
|
37
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
38
|
+
}
|
39
|
+
|
40
|
+
VALUE psd_native_compose_multiply(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
41
|
+
PIXEL fg = FIX2UINT(r_fg);
|
42
|
+
PIXEL bg = FIX2UINT(r_bg);
|
43
|
+
PIXEL new_r, new_g, new_b;
|
44
|
+
|
45
|
+
if (TRANSPARENT(bg)) return r_fg;
|
46
|
+
if (TRANSPARENT(fg)) return r_bg;
|
47
|
+
|
48
|
+
calculate_alphas(fg, bg, &opts);
|
49
|
+
|
50
|
+
new_r = BLEND_CHANNEL(R(bg), R(fg) * R(bg) >> 8, alpha.mix);
|
51
|
+
new_g = BLEND_CHANNEL(G(bg), G(fg) * G(bg) >> 8, alpha.mix);
|
52
|
+
new_b = BLEND_CHANNEL(B(bg), B(fg) * B(bg) >> 8, alpha.mix);
|
53
|
+
|
54
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
55
|
+
}
|
56
|
+
|
57
|
+
VALUE psd_native_compose_color_burn(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
58
|
+
PIXEL fg = FIX2UINT(r_fg);
|
59
|
+
PIXEL bg = FIX2UINT(r_bg);
|
60
|
+
PIXEL new_r, new_g, new_b;
|
61
|
+
|
62
|
+
if (TRANSPARENT(bg)) return r_fg;
|
63
|
+
if (TRANSPARENT(fg)) return r_bg;
|
64
|
+
|
65
|
+
calculate_alphas(fg, bg, &opts);
|
66
|
+
|
67
|
+
new_r = BLEND_CHANNEL(R(bg), color_burn_foreground(R(bg), R(fg)), alpha.mix);
|
68
|
+
new_g = BLEND_CHANNEL(G(bg), color_burn_foreground(G(bg), G(fg)), alpha.mix);
|
69
|
+
new_b = BLEND_CHANNEL(B(bg), color_burn_foreground(B(bg), B(fg)), alpha.mix);
|
70
|
+
|
71
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
72
|
+
}
|
73
|
+
|
74
|
+
PIXEL color_burn_foreground(PIXEL b, PIXEL f) {
|
75
|
+
if (f > 0) {
|
76
|
+
f = ((255 - b) << 8) / f;
|
77
|
+
return f > 255 ? 0 : (255 - f);
|
78
|
+
} else {
|
79
|
+
return b;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
VALUE psd_native_compose_linear_burn(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
84
|
+
PIXEL fg = FIX2UINT(r_fg);
|
85
|
+
PIXEL bg = FIX2UINT(r_bg);
|
86
|
+
PIXEL new_r, new_g, new_b;
|
87
|
+
|
88
|
+
if (TRANSPARENT(bg)) return r_fg;
|
89
|
+
if (TRANSPARENT(fg)) return r_bg;
|
90
|
+
|
91
|
+
calculate_alphas(fg, bg, &opts);
|
92
|
+
|
93
|
+
new_r = BLEND_CHANNEL(R(bg), (R(fg) < (255 - R(bg))) ? 0 : R(fg) - (255 - R(bg)), alpha.mix);
|
94
|
+
new_g = BLEND_CHANNEL(G(bg), (G(fg) < (255 - G(bg))) ? 0 : G(fg) - (255 - G(bg)), alpha.mix);
|
95
|
+
new_b = BLEND_CHANNEL(B(bg), (B(fg) < (255 - B(bg))) ? 0 : B(fg) - (255 - B(bg)), alpha.mix);
|
96
|
+
|
97
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
98
|
+
}
|
99
|
+
|
100
|
+
VALUE psd_native_compose_lighten(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
101
|
+
PIXEL fg = FIX2UINT(r_fg);
|
102
|
+
PIXEL bg = FIX2UINT(r_bg);
|
103
|
+
PIXEL new_r, new_g, new_b;
|
104
|
+
|
105
|
+
if (TRANSPARENT(bg)) return r_fg;
|
106
|
+
if (TRANSPARENT(fg)) return r_bg;
|
107
|
+
|
108
|
+
calculate_alphas(fg, bg, &opts);
|
109
|
+
|
110
|
+
new_r = (R(fg) >= R(bg)) ? BLEND_CHANNEL(R(bg), R(fg), alpha.mix) : R(bg);
|
111
|
+
new_g = (G(fg) >= G(bg)) ? BLEND_CHANNEL(G(bg), G(fg), alpha.mix) : G(bg);
|
112
|
+
new_b = (B(fg) >= B(bg)) ? BLEND_CHANNEL(B(bg), B(fg), alpha.mix) : B(bg);
|
113
|
+
|
114
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
115
|
+
}
|
116
|
+
|
117
|
+
VALUE psd_native_compose_screen(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
118
|
+
PIXEL fg = FIX2UINT(r_fg);
|
119
|
+
PIXEL bg = FIX2UINT(r_bg);
|
120
|
+
PIXEL new_r, new_g, new_b;
|
121
|
+
|
122
|
+
if (TRANSPARENT(bg)) return r_fg;
|
123
|
+
if (TRANSPARENT(fg)) return r_bg;
|
124
|
+
|
125
|
+
calculate_alphas(fg, bg, &opts);
|
126
|
+
|
127
|
+
new_r = BLEND_CHANNEL(R(bg), 255 - ((255 - R(bg)) * (255 - R(fg)) >> 8), alpha.mix);
|
128
|
+
new_g = BLEND_CHANNEL(G(bg), 255 - ((255 - G(bg)) * (255 - G(fg)) >> 8), alpha.mix);
|
129
|
+
new_b = BLEND_CHANNEL(B(bg), 255 - ((255 - B(bg)) * (255 - B(fg)) >> 8), alpha.mix);
|
130
|
+
|
131
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
132
|
+
}
|
133
|
+
|
134
|
+
VALUE psd_native_compose_color_dodge(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
135
|
+
PIXEL fg = FIX2UINT(r_fg);
|
136
|
+
PIXEL bg = FIX2UINT(r_bg);
|
137
|
+
PIXEL new_r, new_g, new_b;
|
138
|
+
|
139
|
+
if (TRANSPARENT(bg)) return r_fg;
|
140
|
+
if (TRANSPARENT(fg)) return r_bg;
|
141
|
+
|
142
|
+
calculate_alphas(fg, bg, &opts);
|
143
|
+
|
144
|
+
new_r = BLEND_CHANNEL(R(bg), color_dodge_foreground(R(bg), R(fg)), alpha.mix);
|
145
|
+
new_g = BLEND_CHANNEL(G(bg), color_dodge_foreground(G(bg), G(fg)), alpha.mix);
|
146
|
+
new_b = BLEND_CHANNEL(B(bg), color_dodge_foreground(B(bg), B(fg)), alpha.mix);
|
147
|
+
|
148
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
149
|
+
}
|
150
|
+
|
151
|
+
PIXEL color_dodge_foreground(PIXEL b, PIXEL f) {
|
152
|
+
if (f < 255) {
|
153
|
+
return CLAMP_PIXEL((b << 8) / (255 - f));
|
154
|
+
} else {
|
155
|
+
return b;
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
VALUE psd_native_compose_linear_dodge(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
160
|
+
PIXEL fg = FIX2UINT(r_fg);
|
161
|
+
PIXEL bg = FIX2UINT(r_bg);
|
162
|
+
PIXEL new_r, new_g, new_b;
|
163
|
+
|
164
|
+
if (TRANSPARENT(bg)) return r_fg;
|
165
|
+
if (TRANSPARENT(fg)) return r_bg;
|
166
|
+
|
167
|
+
calculate_alphas(fg, bg, &opts);
|
168
|
+
|
169
|
+
new_r = BLEND_CHANNEL(R(bg), (R(bg) + R(fg)) > 255 ? 255 : R(bg) + R(fg), alpha.mix);
|
170
|
+
new_g = BLEND_CHANNEL(G(bg), (G(bg) + G(fg)) > 255 ? 255 : G(bg) + G(fg), alpha.mix);
|
171
|
+
new_b = BLEND_CHANNEL(B(bg), (B(bg) + B(fg)) > 255 ? 255 : B(bg) + B(fg), alpha.mix);
|
172
|
+
|
173
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
174
|
+
}
|
175
|
+
|
176
|
+
VALUE psd_native_compose_overlay(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
177
|
+
PIXEL fg = FIX2UINT(r_fg);
|
178
|
+
PIXEL bg = FIX2UINT(r_bg);
|
179
|
+
PIXEL new_r, new_g, new_b;
|
180
|
+
|
181
|
+
if (TRANSPARENT(bg)) return r_fg;
|
182
|
+
if (TRANSPARENT(fg)) return r_bg;
|
183
|
+
|
184
|
+
calculate_alphas(fg, bg, &opts);
|
185
|
+
|
186
|
+
new_r = BLEND_CHANNEL(R(bg), overlay_foreground(R(bg), R(fg)), alpha.mix);
|
187
|
+
new_g = BLEND_CHANNEL(G(bg), overlay_foreground(G(bg), G(fg)), alpha.mix);
|
188
|
+
new_b = BLEND_CHANNEL(B(bg), overlay_foreground(B(bg), B(fg)), alpha.mix);
|
189
|
+
|
190
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
191
|
+
}
|
192
|
+
|
193
|
+
PIXEL overlay_foreground(PIXEL b, PIXEL f) {
|
194
|
+
if (b < 128) {
|
195
|
+
return b * f >> 7;
|
196
|
+
} else {
|
197
|
+
return 255 - ((255 - b) * (255 - f) >> 7);
|
198
|
+
}
|
199
|
+
}
|
200
|
+
|
201
|
+
VALUE psd_native_compose_soft_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
202
|
+
PIXEL fg = FIX2UINT(r_fg);
|
203
|
+
PIXEL bg = FIX2UINT(r_bg);
|
204
|
+
PIXEL new_r, new_g, new_b;
|
205
|
+
|
206
|
+
if (TRANSPARENT(bg)) return r_fg;
|
207
|
+
if (TRANSPARENT(fg)) return r_bg;
|
208
|
+
|
209
|
+
calculate_alphas(fg, bg, &opts);
|
210
|
+
|
211
|
+
new_r = BLEND_CHANNEL(R(bg), soft_light_foreground(R(bg), R(fg)), alpha.mix);
|
212
|
+
new_g = BLEND_CHANNEL(G(bg), soft_light_foreground(G(bg), G(fg)), alpha.mix);
|
213
|
+
new_b = BLEND_CHANNEL(B(bg), soft_light_foreground(B(bg), B(fg)), alpha.mix);
|
214
|
+
|
215
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
216
|
+
}
|
217
|
+
|
218
|
+
PIXEL soft_light_foreground(PIXEL b, PIXEL f) {
|
219
|
+
uint32_t c1 = b * f >> 8;
|
220
|
+
uint32_t c2 = 255 - ((255 - b) * (255 - f) >> 8);
|
221
|
+
return ((255 - b) * c1 >> 8) + (b * c2 >> 8);
|
222
|
+
}
|
223
|
+
|
224
|
+
VALUE psd_native_compose_hard_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
225
|
+
PIXEL fg = FIX2UINT(r_fg);
|
226
|
+
PIXEL bg = FIX2UINT(r_bg);
|
227
|
+
PIXEL new_r, new_g, new_b;
|
228
|
+
|
229
|
+
if (TRANSPARENT(bg)) return r_fg;
|
230
|
+
if (TRANSPARENT(fg)) return r_bg;
|
231
|
+
|
232
|
+
calculate_alphas(fg, bg, &opts);
|
233
|
+
|
234
|
+
new_r = BLEND_CHANNEL(R(bg), hard_light_foreground(R(bg), R(fg)), alpha.mix);
|
235
|
+
new_g = BLEND_CHANNEL(G(bg), hard_light_foreground(G(bg), G(fg)), alpha.mix);
|
236
|
+
new_b = BLEND_CHANNEL(B(bg), hard_light_foreground(B(bg), B(fg)), alpha.mix);
|
237
|
+
|
238
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
239
|
+
}
|
240
|
+
|
241
|
+
PIXEL hard_light_foreground(PIXEL b, PIXEL f) {
|
242
|
+
if (f < 128) {
|
243
|
+
return b * f >> 7;
|
244
|
+
} else {
|
245
|
+
return 255 - ((255 - f) * (255 - b) >> 7);
|
246
|
+
}
|
247
|
+
}
|
248
|
+
|
249
|
+
VALUE psd_native_compose_vivid_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
250
|
+
PIXEL fg = FIX2UINT(r_fg);
|
251
|
+
PIXEL bg = FIX2UINT(r_bg);
|
252
|
+
PIXEL new_r, new_g, new_b;
|
253
|
+
|
254
|
+
if (TRANSPARENT(bg)) return r_fg;
|
255
|
+
if (TRANSPARENT(fg)) return r_bg;
|
256
|
+
|
257
|
+
calculate_alphas(fg, bg, &opts);
|
258
|
+
|
259
|
+
new_r = BLEND_CHANNEL(R(bg), vivid_light_foreground(R(bg), R(fg)), alpha.mix);
|
260
|
+
new_g = BLEND_CHANNEL(G(bg), vivid_light_foreground(G(bg), G(fg)), alpha.mix);
|
261
|
+
new_b = BLEND_CHANNEL(B(bg), vivid_light_foreground(B(bg), B(fg)), alpha.mix);
|
262
|
+
|
263
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
264
|
+
}
|
265
|
+
|
266
|
+
PIXEL vivid_light_foreground(PIXEL b, PIXEL f) {
|
267
|
+
if (f < 255) {
|
268
|
+
return CLAMP_PIXEL((b * b / (255 - f) + f * f / (255 - b)) >> 1);
|
269
|
+
} else {
|
270
|
+
return b;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
|
274
|
+
VALUE psd_native_compose_linear_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
275
|
+
PIXEL fg = FIX2UINT(r_fg);
|
276
|
+
PIXEL bg = FIX2UINT(r_bg);
|
277
|
+
PIXEL new_r, new_g, new_b;
|
278
|
+
|
279
|
+
if (TRANSPARENT(bg)) return r_fg;
|
280
|
+
if (TRANSPARENT(fg)) return r_bg;
|
281
|
+
|
282
|
+
calculate_alphas(fg, bg, &opts);
|
283
|
+
|
284
|
+
new_r = BLEND_CHANNEL(R(bg), linear_light_foreground(R(bg), R(fg)), alpha.mix);
|
285
|
+
new_g = BLEND_CHANNEL(G(bg), linear_light_foreground(G(bg), G(fg)), alpha.mix);
|
286
|
+
new_b = BLEND_CHANNEL(B(bg), linear_light_foreground(B(bg), B(fg)), alpha.mix);
|
287
|
+
|
288
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
289
|
+
}
|
290
|
+
|
291
|
+
PIXEL linear_light_foreground(PIXEL b, PIXEL f) {
|
292
|
+
if (b < 255) {
|
293
|
+
return CLAMP_PIXEL(f * f / (255 - b));
|
294
|
+
} else {
|
295
|
+
return 255;
|
296
|
+
}
|
297
|
+
}
|
298
|
+
|
299
|
+
VALUE psd_native_compose_pin_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
300
|
+
PIXEL fg = FIX2UINT(r_fg);
|
301
|
+
PIXEL bg = FIX2UINT(r_bg);
|
302
|
+
PIXEL new_r, new_g, new_b;
|
303
|
+
|
304
|
+
if (TRANSPARENT(bg)) return r_fg;
|
305
|
+
if (TRANSPARENT(fg)) return r_bg;
|
306
|
+
|
307
|
+
calculate_alphas(fg, bg, &opts);
|
308
|
+
|
309
|
+
new_r = BLEND_CHANNEL(R(bg), pin_light_foreground(R(bg), R(fg)), alpha.mix);
|
310
|
+
new_g = BLEND_CHANNEL(G(bg), pin_light_foreground(G(bg), G(fg)), alpha.mix);
|
311
|
+
new_b = BLEND_CHANNEL(B(bg), pin_light_foreground(B(bg), B(fg)), alpha.mix);
|
312
|
+
|
313
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
314
|
+
}
|
315
|
+
|
316
|
+
PIXEL pin_light_foreground(PIXEL b, PIXEL f) {
|
317
|
+
if (f >= 128) {
|
318
|
+
return PSD_MAX(b, (f - 128) * 2);
|
319
|
+
} else {
|
320
|
+
return PSD_MIN(b, f * 2);
|
321
|
+
}
|
322
|
+
}
|
323
|
+
|
324
|
+
VALUE psd_native_compose_hard_mix(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
325
|
+
PIXEL fg = FIX2UINT(r_fg);
|
326
|
+
PIXEL bg = FIX2UINT(r_bg);
|
327
|
+
PIXEL new_r, new_g, new_b;
|
328
|
+
|
329
|
+
if (TRANSPARENT(bg)) return r_fg;
|
330
|
+
if (TRANSPARENT(fg)) return r_bg;
|
331
|
+
|
332
|
+
calculate_alphas(fg, bg, &opts);
|
333
|
+
|
334
|
+
new_r = BLEND_CHANNEL(R(bg), (R(bg) + R(fg) <= 255) ? 0 : 255, alpha.mix);
|
335
|
+
new_g = BLEND_CHANNEL(G(bg), (G(bg) + G(fg) <= 255) ? 0 : 255, alpha.mix);
|
336
|
+
new_b = BLEND_CHANNEL(B(bg), (B(bg) + B(fg) <= 255) ? 0 : 255, alpha.mix);
|
337
|
+
|
338
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
339
|
+
}
|
340
|
+
|
341
|
+
VALUE psd_native_compose_difference(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
342
|
+
PIXEL fg = FIX2UINT(r_fg);
|
343
|
+
PIXEL bg = FIX2UINT(r_bg);
|
344
|
+
PIXEL new_r, new_g, new_b;
|
345
|
+
|
346
|
+
if (TRANSPARENT(bg)) return r_fg;
|
347
|
+
if (TRANSPARENT(fg)) return r_bg;
|
348
|
+
|
349
|
+
calculate_alphas(fg, bg, &opts);
|
350
|
+
|
351
|
+
new_r = BLEND_CHANNEL(R(bg), abs(R(bg) - R(fg)), alpha.mix);
|
352
|
+
new_g = BLEND_CHANNEL(G(bg), abs(G(bg) - G(fg)), alpha.mix);
|
353
|
+
new_b = BLEND_CHANNEL(B(bg), abs(B(bg) - B(fg)), alpha.mix);
|
354
|
+
|
355
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
356
|
+
}
|
357
|
+
|
358
|
+
VALUE psd_native_compose_exclusion(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts) {
|
359
|
+
PIXEL fg = FIX2UINT(r_fg);
|
360
|
+
PIXEL bg = FIX2UINT(r_bg);
|
361
|
+
PIXEL new_r, new_g, new_b;
|
362
|
+
|
363
|
+
if (TRANSPARENT(bg)) return r_fg;
|
364
|
+
if (TRANSPARENT(fg)) return r_bg;
|
365
|
+
|
366
|
+
calculate_alphas(fg, bg, &opts);
|
367
|
+
|
368
|
+
new_r = BLEND_CHANNEL(R(bg), R(bg) + R(fg) - (R(bg) * R(fg) >> 7), alpha.mix);
|
369
|
+
new_g = BLEND_CHANNEL(G(bg), G(bg) + G(fg) - (G(bg) * G(fg) >> 7), alpha.mix);
|
370
|
+
new_b = BLEND_CHANNEL(B(bg), B(bg) + B(fg) - (B(bg) * B(fg) >> 7), alpha.mix);
|
371
|
+
|
372
|
+
return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
|
373
|
+
}
|
374
|
+
|
375
|
+
void calculate_alphas(PIXEL fg, PIXEL bg, VALUE *opts) {
|
23
376
|
uint32_t opacity = calculate_opacity(opts);
|
24
|
-
uint32_t src_alpha =
|
377
|
+
uint32_t src_alpha = A(fg) * opacity >> 8;
|
25
378
|
|
26
|
-
alpha.dst =
|
379
|
+
alpha.dst = A(bg);
|
27
380
|
alpha.mix = (src_alpha << 8) / (src_alpha + ((256 - src_alpha) * alpha.dst >> 8));
|
28
381
|
alpha.dst = alpha.dst + ((256 - alpha.dst) * src_alpha >> 8);
|
29
382
|
}
|
@@ -33,8 +386,4 @@ uint32_t calculate_opacity(VALUE *opts) {
|
|
33
386
|
uint32_t fill_opacity = FIX2UINT(rb_hash_aref(*opts, ID2SYM(rb_intern("fill_opacity"))));
|
34
387
|
|
35
388
|
return opacity * fill_opacity / 255;
|
36
|
-
}
|
37
|
-
|
38
|
-
uint32_t blend_channel(uint32_t bg, uint32_t fg, uint32_t a) {
|
39
|
-
return ((bg << 8) + (fg - bg) * a) >> 8;
|
40
389
|
}
|
data/ext/psd_native/compose.h
CHANGED
@@ -6,7 +6,35 @@ typedef struct AlphaValues {
|
|
6
6
|
int dst;
|
7
7
|
} AlphaValues;
|
8
8
|
|
9
|
+
#define BLEND_CHANNEL(b, f, a) ((((b) << 8) + ((f) - (b)) * (a)) >> 8)
|
10
|
+
|
9
11
|
VALUE psd_native_compose_normal(VALUE self, VALUE fg, VALUE bg, VALUE opts);
|
12
|
+
VALUE psd_native_compose_darken(VALUE self, VALUE fg, VALUE bg, VALUE opts);
|
13
|
+
VALUE psd_native_compose_multiply(VALUE self, VALUE fg, VALUE bg, VALUE opts);
|
14
|
+
VALUE psd_native_compose_color_burn(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
15
|
+
PIXEL color_burn_foreground(PIXEL b, PIXEL f);
|
16
|
+
VALUE psd_native_compose_linear_burn(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
17
|
+
VALUE psd_native_compose_lighten(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
18
|
+
VALUE psd_native_compose_screen(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
19
|
+
VALUE psd_native_compose_color_dodge(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
20
|
+
PIXEL color_dodge_foreground(PIXEL b, PIXEL f);
|
21
|
+
VALUE psd_native_compose_linear_dodge(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
22
|
+
VALUE psd_native_compose_overlay(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
23
|
+
PIXEL overlay_foreground(PIXEL b, PIXEL f);
|
24
|
+
VALUE psd_native_compose_soft_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
25
|
+
PIXEL soft_light_foreground(PIXEL b, PIXEL f);
|
26
|
+
VALUE psd_native_compose_hard_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
27
|
+
PIXEL hard_light_foreground(PIXEL b, PIXEL f);
|
28
|
+
VALUE psd_native_compose_vivid_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
29
|
+
PIXEL vivid_light_foreground(PIXEL b, PIXEL f);
|
30
|
+
VALUE psd_native_compose_linear_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
31
|
+
PIXEL linear_light_foreground(PIXEL b, PIXEL f);
|
32
|
+
VALUE psd_native_compose_pin_light(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
33
|
+
PIXEL pin_light_foreground(PIXEL b, PIXEL f);
|
34
|
+
VALUE psd_native_compose_hard_mix(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
35
|
+
VALUE psd_native_compose_difference(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
36
|
+
VALUE psd_native_compose_exclusion(VALUE self, VALUE r_fg, VALUE r_bg, VALUE opts);
|
37
|
+
|
10
38
|
void calculate_alphas(uint32_t fg, uint32_t bg, VALUE *opts);
|
11
39
|
uint32_t calculate_opacity(VALUE *opts);
|
12
40
|
uint32_t blend_channel(uint32_t bg, uint32_t fg, uint32_t a);
|
@@ -28,6 +28,26 @@ void Init_psd_native() {
|
|
28
28
|
// Compose functions
|
29
29
|
VALUE Compose = rb_define_module_under(PSDNative, "Compose");
|
30
30
|
rb_define_module_function(Compose, "normal", psd_native_compose_normal, 3);
|
31
|
+
rb_define_module_function(Compose, "darken", psd_native_compose_darken, 3);
|
32
|
+
rb_define_module_function(Compose, "multiply", psd_native_compose_multiply, 3);
|
33
|
+
rb_define_module_function(Compose, "color_burn", psd_native_compose_color_burn, 3);
|
34
|
+
rb_define_module_function(Compose, "linear_burn", psd_native_compose_linear_burn, 3);
|
35
|
+
rb_define_module_function(Compose, "lighten", psd_native_compose_lighten, 3);
|
36
|
+
rb_define_module_function(Compose, "screen", psd_native_compose_screen, 3);
|
37
|
+
rb_define_module_function(Compose, "color_dodge", psd_native_compose_color_dodge, 3);
|
38
|
+
rb_define_module_function(Compose, "linear_dodge", psd_native_compose_linear_dodge, 3);
|
39
|
+
rb_define_module_function(Compose, "overlay", psd_native_compose_overlay, 3);
|
40
|
+
rb_define_module_function(Compose, "soft_light", psd_native_compose_soft_light, 3);
|
41
|
+
rb_define_module_function(Compose, "hard_light", psd_native_compose_hard_light, 3);
|
42
|
+
rb_define_module_function(Compose, "vivid_light", psd_native_compose_vivid_light, 3);
|
43
|
+
rb_define_module_function(Compose, "linear_light", psd_native_compose_linear_light, 3);
|
44
|
+
rb_define_module_function(Compose, "pin_light", psd_native_compose_pin_light, 3);
|
45
|
+
rb_define_module_function(Compose, "hard_mix", psd_native_compose_hard_mix, 3);
|
46
|
+
rb_define_module_function(Compose, "difference", psd_native_compose_difference, 3);
|
47
|
+
rb_define_module_function(Compose, "exclusion", psd_native_compose_exclusion, 3);
|
48
|
+
|
49
|
+
VALUE ClippingMask = rb_define_module_under(PSDNative, "ClippingMask");
|
50
|
+
rb_define_method(ClippingMask, "apply", psd_native_clipping_mask_apply, 0);
|
31
51
|
|
32
52
|
psd_logger("info", "PSD native mixins enabled!");
|
33
53
|
}
|
@@ -8,10 +8,10 @@
|
|
8
8
|
// Pixels use 32 bits unsigned integers
|
9
9
|
// We borrow this from OilyPNG
|
10
10
|
typedef uint32_t PIXEL;
|
11
|
-
#define BUILD_PIXEL(r, g, b, a) (((PIXEL) (r) << 24) + ((PIXEL) (g) << 16) + ((PIXEL) (b) << 8) + (PIXEL) (a))
|
12
11
|
|
13
12
|
// Our native mixins
|
14
13
|
#include "color.h"
|
14
|
+
#include "clipping_mask.h"
|
15
15
|
#include "compose.h"
|
16
16
|
#include "image_mode_cmyk.h"
|
17
17
|
#include "image_mode_greyscale.h"
|
data/lib/psd_native/compose.rb
CHANGED
@@ -3,9 +3,17 @@ require 'psd_native/psd_native'
|
|
3
3
|
|
4
4
|
class PSD
|
5
5
|
module Compose
|
6
|
-
|
6
|
+
PSDNative::Compose.methods(false).each do |meth|
|
7
|
+
define_method(meth) do |*args|
|
8
|
+
do_blend meth, *args
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def do_blend(blend, fg, bg, opts={})
|
7
15
|
opts = DEFAULT_OPTS.merge(opts)
|
8
|
-
PSDNative::Compose.
|
16
|
+
PSDNative::Compose.send(blend, fg, bg, opts)
|
9
17
|
end
|
10
18
|
end
|
11
19
|
end
|
data/lib/psd_native/version.rb
CHANGED
data/lib/psd_native.rb
CHANGED
@@ -7,6 +7,11 @@ module PSDNative
|
|
7
7
|
base::Image.send(:include, PSDNative::ImageMode::Greyscale)
|
8
8
|
base::Image.send(:include, PSDNative::ImageFormat::RLE)
|
9
9
|
base::Color.send(:include, PSDNative::Color)
|
10
|
+
|
11
|
+
base::ClippingMask.class_eval do
|
12
|
+
remove_method :apply
|
13
|
+
end
|
14
|
+
base::ClippingMask.send(:include, PSDNative::ClippingMask)
|
10
15
|
end
|
11
16
|
end
|
12
17
|
|
data/psd_native.gemspec
CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
spec.extensions = ["ext/psd_native/extconf.rb"]
|
21
21
|
spec.require_paths = ["lib", "ext"]
|
22
22
|
|
23
|
-
spec.add_runtime_dependency "psd", ">= 1.4"
|
23
|
+
spec.add_runtime_dependency "psd", ">= 1.4.1"
|
24
24
|
|
25
25
|
spec.add_development_dependency "bundler", "~> 1.3"
|
26
26
|
spec.add_development_dependency "rake"
|
data/spec/image_spec.rb
CHANGED
@@ -28,7 +28,7 @@ describe 'Image Exporting' do
|
|
28
28
|
|
29
29
|
describe "as PNG" do
|
30
30
|
it "should produce a valid PNG object" do
|
31
|
-
expect(@psd.image.to_png).to be_an_instance_of(ChunkyPNG::
|
31
|
+
expect(@psd.image.to_png).to be_an_instance_of(ChunkyPNG::Canvas)
|
32
32
|
|
33
33
|
expect(@psd.image.to_png.width).to eq(1)
|
34
34
|
expect(@psd.image.to_png.height).to eq(1)
|
@@ -58,7 +58,7 @@ describe 'Image Exporting' do
|
|
58
58
|
@psd.parse!
|
59
59
|
|
60
60
|
png = @psd.tree.children.first.image.to_png
|
61
|
-
expect(png).to be_an_instance_of(ChunkyPNG::
|
61
|
+
expect(png).to be_an_instance_of(ChunkyPNG::Canvas)
|
62
62
|
expect(png.width).to eq(1)
|
63
63
|
expect(png.height).to eq(1)
|
64
64
|
expect(
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: psd_native
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan LeFevre
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: psd
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
19
|
+
version: 1.4.1
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
26
|
+
version: 1.4.1
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: bundler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -137,6 +137,8 @@ files:
|
|
137
137
|
- LICENSE.txt
|
138
138
|
- README.md
|
139
139
|
- Rakefile
|
140
|
+
- ext/psd_native/clipping_mask.c
|
141
|
+
- ext/psd_native/clipping_mask.h
|
140
142
|
- ext/psd_native/color.c
|
141
143
|
- ext/psd_native/color.h
|
142
144
|
- ext/psd_native/compose.c
|