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 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