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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 43ce99138b4b79f02a2f05ac620b2cb74572fea3
4
- data.tar.gz: 2b003b13301a2009dc8ed4668435bd60a77a60ec
3
+ metadata.gz: c081770f983b87fa68b32f0a04576c8384bce760
4
+ data.tar.gz: aa04b516926a30d512c22fec87f43524a2eca4b5
5
5
  SHA512:
6
- metadata.gz: 2be538272093186445f346678e4e589715182d37a870f3dc13b075d55d226e835d55fd56e9f21bd17e424e484ee7007ced0f7d0f70fefd09cede853d40f18861
7
- data.tar.gz: dedc579ccb2b5c9fdbd867c0badab4aebff2bb3e55d2d64c3484d0915b9f2ddee8c281e595d92fe943b3954d90cf6e452ada0f7203b647af9965464d99f4ef50
6
+ metadata.gz: 2d8876e7d2981bdecd7701c3d25adca20b8c5e4cebfc00f7f382ae227477972f3e6174ab60daef81f5713da7cbec490e057ae62e120e327221d13bba7968bd6a
7
+ data.tar.gz: 03a9c93c7c2ab9d7176909497edf96bcbbb500befeaac3cedc9005ba584bcb072940c9c4dd21b2176d856378c640117811dda1070f4bb789664bd3023d5a9eb9
data/README.md CHANGED
@@ -11,6 +11,8 @@ Currently, PSDNative replaces these sections of PSD.rb with native code:
11
11
  * Greyscale processing
12
12
  * RLE decoding
13
13
  * Some color conversions
14
+ * Color composition
15
+ * Clipping masks
14
16
 
15
17
  ## Installation
16
18
 
@@ -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
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef PSD_NATIVE_CLIPPING_MASK
2
+ #define PSD_NATIVE_CLIPPING_MASK
3
+
4
+ VALUE psd_native_clipping_mask_apply(VALUE self);
5
+
6
+ #endif
@@ -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
  }
@@ -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
@@ -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
- uint32_t fg = FIX2UINT(r_fg);
7
- uint32_t bg = FIX2UINT(r_bg);
8
- uint32_t new_r, new_g, new_b;
7
+ PIXEL fg = FIX2UINT(r_fg);
8
+ PIXEL bg = FIX2UINT(r_bg);
9
+ PIXEL new_r, new_g, new_b;
9
10
 
10
- if (opaque(fg) || transparent(bg)) return INT2FIX(fg);
11
- if (transparent(fg)) return INT2FIX(bg);
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 = blend_channel(r(bg), r(fg), alpha.mix);
16
- new_g = blend_channel(g(bg), g(fg), alpha.mix);
17
- new_b = blend_channel(b(bg), b(fg), alpha.mix);
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(rgba(new_r, new_g, new_b, alpha.dst));
20
+ return INT2FIX(BUILD_PIXEL(new_r, new_g, new_b, alpha.dst));
20
21
  }
21
22
 
22
- void calculate_alphas(uint32_t fg, uint32_t bg, VALUE *opts) {
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 = a(fg) * opacity >> 8;
377
+ uint32_t src_alpha = A(fg) * opacity >> 8;
25
378
 
26
- alpha.dst = a(bg);
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
  }
@@ -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"
@@ -3,9 +3,17 @@ require 'psd_native/psd_native'
3
3
 
4
4
  class PSD
5
5
  module Compose
6
- def normal(fg, bg, opts={})
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.normal(fg, bg, opts)
16
+ PSDNative::Compose.send(blend, fg, bg, opts)
9
17
  end
10
18
  end
11
19
  end
@@ -1,3 +1,3 @@
1
1
  module PSDNative
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.0"
3
3
  end
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::Image)
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::Image)
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.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-17 00:00:00.000000000 Z
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: '1.4'
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: '1.4'
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