psd_native 0.4.0 → 0.5.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 +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
|