texplay 0.2.7-x86-mswin32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/CHANGELOG +103 -0
  2. data/README.markdown +41 -0
  3. data/Rakefile +61 -0
  4. data/examples/common.rb +8 -0
  5. data/examples/example_alpha_blend.rb +31 -0
  6. data/examples/example_bezier.rb +42 -0
  7. data/examples/example_blur.rb +59 -0
  8. data/examples/example_color_control.rb +69 -0
  9. data/examples/example_color_transform.rb +29 -0
  10. data/examples/example_dup.rb +75 -0
  11. data/examples/example_each.rb +42 -0
  12. data/examples/example_effect.rb +35 -0
  13. data/examples/example_fill.rb +44 -0
  14. data/examples/example_fill_old.rb +49 -0
  15. data/examples/example_fluent.rb +31 -0
  16. data/examples/example_gen_eval.rb +34 -0
  17. data/examples/example_hash_arguments.rb +47 -0
  18. data/examples/example_lsystem.rb +61 -0
  19. data/examples/example_melt.rb +27 -0
  20. data/examples/example_polyline.rb +43 -0
  21. data/examples/example_scale.rb +29 -0
  22. data/examples/example_simple.rb +38 -0
  23. data/examples/example_splice.rb +33 -0
  24. data/examples/example_sync.rb +60 -0
  25. data/examples/example_turtle.rb +40 -0
  26. data/examples/media/empty2.png +0 -0
  27. data/examples/media/gosu.png +0 -0
  28. data/examples/media/logo.png +0 -0
  29. data/examples/media/maria.png +0 -0
  30. data/examples/media/rose.bmp +0 -0
  31. data/examples/media/sand1.png +0 -0
  32. data/examples/media/sunset.png +0 -0
  33. data/examples/media/texplay.png +0 -0
  34. data/ext/texplay/actions.c +1331 -0
  35. data/ext/texplay/actions.h +52 -0
  36. data/ext/texplay/bindings.c +1129 -0
  37. data/ext/texplay/bindings.h +46 -0
  38. data/ext/texplay/cache.c +135 -0
  39. data/ext/texplay/cache.h +24 -0
  40. data/ext/texplay/compat.h +27 -0
  41. data/ext/texplay/extconf.rb +30 -0
  42. data/ext/texplay/gen_eval.c +211 -0
  43. data/ext/texplay/gen_eval.h +20 -0
  44. data/ext/texplay/object2module.c +171 -0
  45. data/ext/texplay/object2module.h +11 -0
  46. data/ext/texplay/texplay.c +137 -0
  47. data/ext/texplay/texplay.h +107 -0
  48. data/ext/texplay/utils.c +978 -0
  49. data/ext/texplay/utils.h +145 -0
  50. data/lib/1.8/texplay.so +0 -0
  51. data/lib/1.9/texplay.so +0 -0
  52. data/lib/texplay-contrib.rb +171 -0
  53. data/lib/texplay.rb +134 -0
  54. metadata +114 -0
@@ -0,0 +1,1331 @@
1
+ #include "texplay.h"
2
+ #include "utils.h"
3
+ #include "actions.h"
4
+ #include <assert.h>
5
+ #include <math.h>
6
+ #ifdef __APPLE__
7
+ # include <glut.h>
8
+ #else
9
+ # include <GL/glut.h>
10
+ #endif
11
+
12
+
13
+ /* small helper functions */
14
+ static void process_common_hash_args(action_struct * cur, VALUE * hash_arg, sync sync_mode, bool primary);
15
+ static void update_bounds(action_struct * cur, int xmin, int ymin, int xmax, int ymax);
16
+ static void update_lazy_bounds(action_struct * cur, texture_info * tex);
17
+ static void set_local_bounds(action_struct * cur, int xmin, int ymin, int xmax, int ymax, texture_info * tex);
18
+
19
+ static void draw_prologue(action_struct * cur, texture_info * tex,
20
+ int xmin, int ymin, int xmax, int ymax, VALUE * hash_arg, sync sync_mode,
21
+ bool primary, action_struct ** payload_ptr);
22
+ static void draw_epilogue(action_struct * cur, texture_info * tex, bool primary);
23
+
24
+ static void prepare_fill_texture(action_struct * cur);
25
+ static void prepare_color_control(action_struct * cur);
26
+ static void set_pixel_color_with_style(action_struct * payload, texture_info * tex,
27
+ int x, int y);
28
+ /* end helpers */
29
+
30
+
31
+ /** line_do_action, bresenham's algorithm **/
32
+ void
33
+ line_do_action(int x1, int y1, int x2, int y2, texture_info * tex, VALUE hash_arg,
34
+ sync sync_mode, bool primary, action_struct * payload)
35
+ {
36
+ int x, y, W, H, F;
37
+ int xinc, yinc;
38
+ action_struct cur;
39
+ int thickness = 1;
40
+
41
+ draw_prologue(&cur, tex, x1, y1,
42
+ x2, y2, &hash_arg, sync_mode, primary, &payload);
43
+
44
+ if(has_optional_hash_arg(hash_arg, "thickness"))
45
+ thickness = NUM2INT(get_from_hash(hash_arg, "thickness"));
46
+
47
+ /* clip the line */
48
+ cohen_sutherland_clip(&x1, &y1, &x2, &y2, 0, 0, tex->width - 1, tex->height - 1);
49
+
50
+ W = ABS(x2 - x1);
51
+ H = ABS(y2 - y1);
52
+
53
+ if(x1 < x2)
54
+ xinc = 1;
55
+ else
56
+ xinc = -1;
57
+
58
+ if(y1 < y2)
59
+ yinc = 1;
60
+ else
61
+ yinc = -1;
62
+
63
+ x = x1;
64
+ y = y1;
65
+
66
+ if(W >= H) {
67
+ F = 2 * H - W;
68
+ while(x != x2) {
69
+ if(F < 0)
70
+ F += 2 * H;
71
+ else {
72
+ F += 2 * (H - W);
73
+ y += yinc;
74
+ }
75
+ x += xinc;
76
+
77
+ if(thickness <= 1) {
78
+ set_pixel_color_with_style(payload, tex, x, y);
79
+ }
80
+ else {
81
+ set_hash_value(hash_arg, "fill", Qtrue);
82
+ circle_do_action(x, y, thickness / 2, tex, hash_arg, no_sync, false, payload);
83
+ }
84
+ }
85
+ }
86
+ else {
87
+ F = 2 * W - H;
88
+ while(y != y2 ) {
89
+ if(F < 0)
90
+ F += 2 * W;
91
+ else {
92
+ F += 2 * (W - H);
93
+ x += xinc;
94
+ }
95
+ y += yinc;
96
+
97
+ if(thickness <= 1) {
98
+ set_pixel_color_with_style(payload, tex, x, y);
99
+ }
100
+ else {
101
+ set_hash_value(hash_arg, "fill", Qtrue);
102
+ circle_do_action(x, y, thickness / 2, tex, hash_arg, no_sync, false, payload);
103
+ }
104
+
105
+ }
106
+ }
107
+
108
+ draw_epilogue(&cur, tex, primary);
109
+ }
110
+ /** end line **/
111
+
112
+ /** polyline algorithm **/
113
+
114
+ /* used by both polyline and bezier */
115
+ #define SIMPLE_FORMAT 0
116
+ #define POINT_FORMAT 1
117
+
118
+ /* calculate a single point */
119
+ static void
120
+ polyline_point(VALUE points, int k, int * x, int * y, int format, int draw_offset_x,
121
+ int draw_offset_y)
122
+ {
123
+ int xy_index;
124
+
125
+ switch(format) {
126
+ case POINT_FORMAT:
127
+ *x = NUM2INT(point_x(get_from_array(points, k))) + draw_offset_x;
128
+ *y = NUM2INT(point_y(get_from_array(points, k))) + draw_offset_y;
129
+ break;
130
+
131
+ case SIMPLE_FORMAT:
132
+ xy_index = k * 2;
133
+ *x = NUM2INT(get_from_array(points, xy_index)) + draw_offset_x;
134
+ *y = NUM2INT(get_from_array(points, xy_index + 1)) + draw_offset_y;
135
+
136
+ break;
137
+ default:
138
+ rb_raise(rb_eArgError, "pixel format must be either POINT_FORMAT or SIMPLE_FORMAT");
139
+ }
140
+ }
141
+
142
+ void
143
+ polyline_do_action(VALUE points, texture_info * tex, VALUE hash_arg,
144
+ sync sync_mode, bool primary, action_struct * payload)
145
+ {
146
+ action_struct cur;
147
+ int x1, y1, x2, y2;
148
+ int format;
149
+ int num_point_pairs;
150
+ int k;
151
+ VALUE offset_val;
152
+ int draw_offset_x;
153
+ int draw_offset_y;
154
+ bool closed = false;
155
+
156
+ draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload);
157
+
158
+ /* calculate offset */
159
+ offset_val = get_image_local(tex->image, DRAW_OFFSET);
160
+
161
+ draw_offset_x = NUM2INT(get_from_array(offset_val, 0));
162
+ draw_offset_y = NUM2INT(get_from_array(offset_val, 1));
163
+
164
+ /* if the polyline is 'closed' make the last point the first */
165
+ if(is_a_hash(hash_arg))
166
+ if(RTEST(get_from_hash(hash_arg, "closed")) || RTEST(get_from_hash(hash_arg, "close"))) {
167
+
168
+ /* so that our additional point is not persistent */
169
+ points = rb_obj_dup(points);
170
+ closed = true;
171
+ }
172
+ /* determine format of points */
173
+ if(is_a_point(get_from_array(points, 0))) {
174
+ format = POINT_FORMAT;
175
+
176
+ /* if the polyline is closed to form a polygon then make the last point and first point identical */
177
+ if(closed)
178
+ rb_ary_push(points, get_from_array(points, 0));
179
+
180
+ num_point_pairs = RARRAY_LEN(points);
181
+ }
182
+ else {
183
+ format = SIMPLE_FORMAT;
184
+
185
+ /* ensure there is an 'x' for every 'y' */
186
+ if(RARRAY_LEN(points) % 2)
187
+ rb_raise(rb_eArgError, "polyline needs an even number of points. got %d\n",
188
+ (int)RARRAY_LEN(points));
189
+
190
+ if(closed) {
191
+ rb_ary_push(points, get_from_array(points, 0));
192
+ rb_ary_push(points, get_from_array(points, 1));
193
+ }
194
+
195
+ num_point_pairs = RARRAY_LEN(points) / 2;
196
+ }
197
+
198
+ /* calculate first point */
199
+ polyline_point(points, 0, &x1, &y1, format, draw_offset_x, draw_offset_y);
200
+
201
+ /* calc the points and draw the polyline */
202
+ for(k = 1; k < num_point_pairs; k++) {
203
+
204
+ polyline_point(points, k, &x2, &y2, format, draw_offset_x, draw_offset_y);
205
+
206
+ line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
207
+
208
+ /* update drawing rectangle */
209
+ update_bounds(payload, x1, y1, x2, y2);
210
+
211
+ x1 = x2; y1 = y2;
212
+ }
213
+
214
+ draw_epilogue(&cur, tex, primary);
215
+ }
216
+ /** end polyline **/
217
+
218
+ /* regular polygon algorithm */
219
+ void
220
+ ngon_do_action(int x, int y, int r, int num_sides, texture_info * tex, VALUE hash_arg,
221
+ sync sync_mode, bool primary, action_struct * payload)
222
+ {
223
+ action_struct cur;
224
+ int x1, y1, x2, y2, x0, y0;
225
+ int n;
226
+ int thickness;
227
+ float angle = 0;
228
+
229
+ draw_prologue(&cur, tex, x - r, y - r,
230
+ x + r, y + r, &hash_arg, sync_mode, primary, &payload);
231
+
232
+
233
+ if(is_a_hash(hash_arg)) {
234
+ if(RTEST(get_from_hash(hash_arg, "thickness"))) {
235
+ thickness = NUM2INT(get_from_hash(hash_arg, "thickness"));
236
+
237
+ /* TO DO: find a better way of doing this */
238
+ cur.xmin = x - r - thickness / 2;
239
+ cur.ymin = y - r - thickness / 2;
240
+ cur.xmax = x + r + thickness / 2;
241
+ cur.ymax = y + r + thickness / 2;
242
+ }
243
+
244
+ if(RTEST(get_from_hash(hash_arg, "start_angle"))) {
245
+ angle = NUM2INT(get_from_hash(hash_arg, "start_angle")) / 360.0 * 2 * PI;
246
+ }
247
+ }
248
+
249
+ /* calculate first point */
250
+ x0 = x1 = x + r * cos(angle);
251
+ y0 = y1 = y + r * sin(angle);
252
+
253
+ for(n = 0; n < num_sides; n++) {
254
+ x2 = x + r * cos((2 * PI / num_sides) * n + angle);
255
+ y2 = y + r * sin((2 * PI / num_sides) * n + angle);
256
+
257
+ line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
258
+
259
+ x1 = x2; y1 = y2;
260
+ }
261
+
262
+ line_do_action(x0, y0, x1, y1, tex, hash_arg, no_sync, false, payload);
263
+
264
+ draw_epilogue(&cur, tex, primary);
265
+ }
266
+ /** end of ngon */
267
+
268
+ /** rectangle algorithm **/
269
+ void
270
+ rect_do_action(int x1, int y1, int x2, int y2, texture_info * tex, VALUE hash_arg,
271
+ sync sync_mode, bool primary, action_struct * payload)
272
+ {
273
+ action_struct cur;
274
+ bool fill = false;
275
+ int thickness = 1;
276
+
277
+ draw_prologue(&cur, tex, x1, y1,
278
+ x2, y2, &hash_arg, sync_mode, primary, &payload);
279
+
280
+
281
+ if(is_a_hash(hash_arg)) {
282
+
283
+ /* make our private copy of the hash so we can mess with it */
284
+ hash_arg = rb_obj_dup(hash_arg);
285
+
286
+ if(RTEST(get_from_hash(hash_arg, "fill")) || RTEST(get_from_hash(hash_arg, "filled"))) {
287
+ fill = true;
288
+
289
+ /* since we're filling the rect, line thickness is irrelevant */
290
+ delete_from_hash(hash_arg, "thickness");
291
+ }
292
+ else if(RTEST(get_from_hash(hash_arg, "thickness"))) {
293
+ thickness = NUM2INT(get_from_hash(hash_arg, "thickness"));
294
+ /* TO DO: find a better way of doing this */
295
+
296
+ if(thickness > 1) {
297
+ cur.xmin = x1 - thickness / 2;
298
+ cur.ymin = y1 - thickness / 2;
299
+ cur.xmax = x2 + thickness / 2 + 1;
300
+ cur.ymax = y2 + thickness / 2 + 1;
301
+ }
302
+ }
303
+ }
304
+ if(!fill) {
305
+ line_do_action(x1, y1, x2, y1, tex, hash_arg, no_sync, false, payload);
306
+ line_do_action(x1, y1, x1, y2, tex, hash_arg, no_sync, false, payload);
307
+ line_do_action(x1, y2, x2, y2, tex, hash_arg, no_sync, false, payload);
308
+ line_do_action(x2, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
309
+ }
310
+ else {
311
+ int y;
312
+ if(y1 > y2) SWAP(y1, y2);
313
+
314
+ for(y = y1; y < y2; y++)
315
+ line_do_action(x1, y, x2, y, tex, hash_arg, no_sync, false, payload);
316
+ }
317
+
318
+ draw_epilogue(&cur, tex, primary);
319
+ }
320
+
321
+
322
+ /** midpoint circle algorithm **/
323
+ void
324
+ circle_do_action(int x1, int y1, int r, texture_info * tex, VALUE hash_arg,
325
+ sync sync_mode, bool primary, action_struct * payload)
326
+ {
327
+
328
+ int x, y;
329
+ float p;
330
+ action_struct cur;
331
+ bool fill = false;
332
+
333
+ draw_prologue(&cur, tex, x1 - r, y1 - r, x1 + r, y1 + r, &hash_arg,
334
+ sync_mode, primary, &payload);
335
+
336
+
337
+ if(is_a_hash(hash_arg)) {
338
+
339
+ /* make our private copy of the hash so we can mess with it */
340
+ hash_arg = rb_obj_dup(hash_arg);
341
+
342
+ if(RTEST(get_from_hash(hash_arg, "fill")) || RTEST(get_from_hash(hash_arg, "filled"))) {
343
+ fill = true;
344
+
345
+ /* to prevent infinite recursion set line thickness to 1 :D
346
+ NB: a filled circle uses lines and a thick line uses filled circles :D */
347
+ delete_from_hash(hash_arg, "thickness");
348
+ }
349
+ }
350
+
351
+ x = 0 ; y = r;
352
+ p = 5 / 4 - r;
353
+ if(!fill) {
354
+ while (x <= y) {
355
+ set_pixel_color_with_style(payload, tex, x1 + x, y1 + y);
356
+ set_pixel_color_with_style(payload, tex, x1 + x, y1 - y);
357
+ set_pixel_color_with_style(payload, tex, x1 - x, y1 + y);
358
+ set_pixel_color_with_style(payload, tex, x1 - x, y1 - y);
359
+ set_pixel_color_with_style(payload, tex, x1 + y, y1 + x);
360
+ set_pixel_color_with_style(payload, tex, x1 + y, y1 - x);
361
+ set_pixel_color_with_style(payload, tex, x1 - y, y1 + x);
362
+ set_pixel_color_with_style(payload, tex, x1 - y, y1 - x);
363
+
364
+ if (p < 0) {
365
+ p += 2 * x + 3;
366
+ }
367
+ else {
368
+ y--;
369
+ p += 2 * (x - y) + 5;
370
+ }
371
+ x++;
372
+ }
373
+ }
374
+ else {
375
+ while (x <= y) {
376
+ line_do_action(x1 - x, y1 + y, x1 + x, y1 + y, tex, hash_arg, no_sync, false, payload);
377
+ line_do_action(x1 - x, y1 - y, x1 + x, y1 - y, tex, hash_arg, no_sync, false, payload);
378
+ line_do_action(x1 - y, y1 + x, x1 + y, y1 + x, tex, hash_arg, no_sync, false, payload);
379
+ line_do_action(x1 - y, y1 - x, x1 + y, y1 - x, tex, hash_arg, no_sync, false, payload);
380
+
381
+ if (p < 0) {
382
+ p += 2 * x + 3;
383
+ }
384
+ else {
385
+ y--;
386
+ p += 2 * (x - y) + 5;
387
+ }
388
+ x++;
389
+ }
390
+ }
391
+
392
+ draw_epilogue(&cur, tex, primary);
393
+ }
394
+ /** end circle **/
395
+
396
+ /** set pixel algorithm **/
397
+ void
398
+ pixel_do_action(int x1, int y1, texture_info * tex, VALUE hash_arg,
399
+ sync sync_mode, bool primary, action_struct * payload)
400
+ {
401
+ action_struct cur;
402
+
403
+ draw_prologue(&cur, tex, x1, y1, x1, y1, &hash_arg, sync_mode, primary, &payload);
404
+
405
+ set_pixel_color_with_style(payload, tex, x1, y1);
406
+
407
+ draw_epilogue(&cur, tex, primary);
408
+ }
409
+ /** end set pixel **/
410
+
411
+ /*** non recursive FLOOD FILL, inspired by John R. Shaw ***/
412
+ typedef struct { int x1, x2, y, dy; } LINESEGMENT;
413
+
414
+ #define MAXDEPTH 10000
415
+
416
+ #define PUSH(XL, XR, Y, DY) \
417
+ if( sp < stack+MAXDEPTH && Y+(DY) >= nMinX && Y+(DY) <= nMaxY ) \
418
+ { sp->x1 = XL; sp->x2 = XR; sp->y = Y; sp->dy = DY; ++sp; }
419
+
420
+ #define POP(XL, XR, Y, DY) \
421
+ { --sp; XL = sp->x1; XR = sp->x2; Y = sp->y+(DY = sp->dy); }
422
+
423
+ void
424
+ flood_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg,
425
+ sync sync_mode, bool primary, action_struct * payload)
426
+ {
427
+ int left, x1, x2, dy;
428
+ rgba old_color;
429
+ LINESEGMENT stack[MAXDEPTH], *sp = stack;
430
+
431
+ action_struct cur;
432
+
433
+ int nMinX, nMinY, nMaxX, nMaxY;
434
+
435
+ /* NOTE: 1024 is just a place-holder to indicate maximum possible width/height.
436
+ Values will be constrained to realistic dimensions by constrain_boundaries() function */
437
+ draw_prologue(&cur, tex, 0, 0, 1024, 1024, &hash_arg, sync_mode, primary, &payload);
438
+
439
+ /* fill hates alpha_blend so let's turn it off */
440
+ payload->pen.alpha_blend = false;
441
+
442
+ nMinX = cur.xmin; nMinY = cur.ymin;
443
+ nMaxX = cur.xmax; nMaxY = cur.ymax;
444
+
445
+ old_color = get_pixel_color(tex, x, y);
446
+ if( cmp_color(old_color, cur.color) )
447
+ return;
448
+
449
+ if( x < nMinX || x > nMaxX || y < nMinX || y > nMaxY )
450
+ return;
451
+
452
+ PUSH(x, x, y, 1); /* needed in some cases */
453
+ PUSH(x, x, y + 1, -1); /* seed segment (popped 1st) */
454
+
455
+ while( sp > stack ) {
456
+ POP(x1, x2, y, dy);
457
+
458
+ for( x = x1; x >= nMinX && cmp_color(get_pixel_color(tex, x, y), old_color); --x ) {
459
+ set_pixel_color_with_style(payload, tex, x, y);
460
+ }
461
+
462
+ if( x >= x1 )
463
+ goto SKIP;
464
+
465
+ left = x + 1;
466
+ if( left < x1 )
467
+ PUSH(y, left, x1 - 1, -dy); /* leak on left? */
468
+
469
+ x = x1 + 1;
470
+
471
+ do {
472
+ for( ; x <= nMaxX && cmp_color(get_pixel_color(tex, x, y), old_color); ++x ){
473
+ set_pixel_color_with_style(payload, tex, x, y);
474
+ }
475
+
476
+ PUSH(left, x - 1, y, dy);
477
+
478
+ if( x > x2 + 1 )
479
+ PUSH(x2 + 1, x - 1, y, -dy); /* leak on right? */
480
+
481
+ SKIP: for( ++x; x <= x2 && !cmp_color(get_pixel_color(tex, x, y), old_color); ++x ) {;}
482
+
483
+ left = x;
484
+ } while( x <= x2 );
485
+ }
486
+
487
+ draw_epilogue(&cur, tex, primary);
488
+ }
489
+ /*** END FLOOD FILL ***/
490
+
491
+ /** glow fill algorithm, from the gosu forums **/
492
+ static void
493
+ glow_floodFill( int x, int y, rgba * seed_color, action_struct * cur, texture_info * tex, texture_info * tex2 )
494
+ {
495
+ /* used to flood in both horizontal directions from the given point to form a line. */
496
+ int fillL, fillR;
497
+ int i;
498
+
499
+ /* initialize the flood directions */
500
+ fillL = fillR = x;
501
+
502
+ /* flood left until a new color is hit - or the edge of the image */
503
+ do {
504
+ /* for texture filling */
505
+ if(tex2)
506
+ cur->color = get_pixel_color(tex2, fillL % tex2->width, y % tex2->height);
507
+
508
+ /* TWO VERSIONS of below */
509
+
510
+ /* SLOW BUT MODERN VERSION */
511
+ /* set_pixel_color_with_style(cur, tex, fillL, y); */
512
+
513
+ /* FAST but old version */
514
+ set_pixel_color(&cur->color, tex, fillL, y);
515
+
516
+ fillL--;
517
+ } while( (fillL >= 0 ) && (cmp_color(get_pixel_color(tex, fillL, y), *seed_color)) );
518
+
519
+ /* flood right until a new color is hit - or the edge of the image */
520
+ do {
521
+ // for texture filling
522
+ if(tex2)
523
+ cur->color = get_pixel_color(tex2, fillR % tex2->width, y % tex2->height);
524
+
525
+ /* set_pixel_color_with_style(cur, tex, fillR, y); */
526
+
527
+ set_pixel_color(&cur->color, tex, fillR, y);
528
+
529
+ fillR++;
530
+ } while( (fillR < cur->xmax - 1) && (cmp_color(get_pixel_color(tex, fillR, y), *seed_color)) );
531
+
532
+ /* recurse to the line above and the line below at each point */
533
+ for( i = fillL + 1; i < fillR; i++ ) {
534
+ /* Flood above */
535
+ if( ( y > 0 ) && ( cmp_color(get_pixel_color(tex, i, y - 1), *seed_color) ) ) {
536
+
537
+ glow_floodFill( i, y-1, seed_color, cur, tex, tex2 );
538
+ }
539
+ /* flood below */
540
+ if( (y < cur->ymax - 1) && (cmp_color(get_pixel_color(tex, i, y + 1), *seed_color) )) {
541
+ glow_floodFill( i, y+1, seed_color, cur, tex, tex2 );
542
+ }
543
+ }
544
+ }
545
+
546
+ void
547
+ glow_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg,
548
+ sync sync_mode, bool primary, action_struct * payload)
549
+ {
550
+ action_struct cur;
551
+ rgba seed_color;
552
+ texture_info fill_image;
553
+ texture_info * fill_image_ptr = NULL;
554
+
555
+ if(!bound_by_rect(x, y, 0, 0, tex->width, tex->height)) return;
556
+
557
+ draw_prologue(&cur, tex, 0, 0, 1024, 1024, &hash_arg, sync_mode, primary, &payload);
558
+
559
+ /* fill hates alpha_blend so let's turn it off */
560
+ payload->pen.alpha_blend = false;
561
+
562
+ if(is_a_hash(hash_arg)) {
563
+ VALUE try_image = get_from_hash(hash_arg, "texture");
564
+ if(is_gosu_image(try_image)) {
565
+ get_texture_info(try_image, &fill_image);
566
+ fill_image_ptr = &fill_image;
567
+ }
568
+ }
569
+
570
+ seed_color = get_pixel_color(tex, x, y);
571
+
572
+ /* last argument is pointer to texture fill data (if it exists) or NULL (if it doesn't) */
573
+ glow_floodFill( x, y, &seed_color, &cur, tex, fill_image_ptr );
574
+
575
+ draw_epilogue(&cur, tex, primary);
576
+ }
577
+ /** end of glow fill **/
578
+
579
+ /** scanfill algorithm **/
580
+ /* the stack */
581
+ #define stackSize 16777218
582
+ int stack[stackSize];
583
+ int stackPointer;
584
+
585
+ static bool
586
+ pop(int * x, int * y, int h)
587
+ {
588
+ if(stackPointer > 0)
589
+ {
590
+ int p = stack[stackPointer];
591
+ *x = p / h;
592
+ *y = p % h;
593
+ stackPointer--;
594
+ return true;
595
+ }
596
+ else
597
+ {
598
+ return false;
599
+ }
600
+ }
601
+
602
+ static bool
603
+ push(int x, int y, int h)
604
+ {
605
+ if(stackPointer < stackSize - 1)
606
+ {
607
+ stackPointer++;
608
+ stack[stackPointer] = h * x + y;
609
+ return true;
610
+ }
611
+ else
612
+ {
613
+ return false;
614
+ }
615
+ }
616
+
617
+ static void
618
+ emptyStack()
619
+ {
620
+ int x, y;
621
+ while(pop(&x, &y, 0));
622
+ }
623
+
624
+ void
625
+ scan_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg,
626
+ sync sync_mode, bool primary, action_struct * payload)
627
+ {
628
+ action_struct cur;
629
+ rgba old_color;
630
+ int y1;
631
+ bool spanLeft, spanRight;
632
+
633
+ if(!bound_by_rect(x, y, 0, 0, tex->width - 1, tex->height - 1)) return;
634
+
635
+ /* NB: using XMAX_OOB etc since we dont know the drawing area yet; drawing area will be set by
636
+ update_bounds() function in main loop */
637
+ draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload);
638
+
639
+ /* fill hates alpha_blend so let's turn it off */
640
+ payload->pen.alpha_blend = false;
641
+
642
+ old_color = get_pixel_color(tex, x, y);
643
+
644
+ if(cmp_color(old_color, cur.color)) return;
645
+
646
+ emptyStack();
647
+
648
+ if(!push(x, y, tex->width - 1)) return;
649
+
650
+ while(pop(&x, &y, tex->width - 1))
651
+ {
652
+ y1 = y;
653
+ while(y1 >= 0 && cmp_color(old_color, get_pixel_color(tex, x, y1))) y1--;
654
+ y1++;
655
+ spanLeft = spanRight = false;
656
+ while(y1 < tex->height && cmp_color(old_color, get_pixel_color(tex, x, y1)) )
657
+ {
658
+ set_pixel_color_with_style(payload, tex, x, y1);
659
+
660
+ /* update the drawing rectangle */
661
+ update_bounds(payload, x, y1, x, y1);
662
+
663
+ if(!spanLeft && x > 1 && cmp_color(old_color, get_pixel_color(tex, x - 1, y1)))
664
+ {
665
+ if(!push(x - 1, y1, tex->width - 1)) return;
666
+ spanLeft = true;
667
+ }
668
+
669
+ else if(spanLeft && !cmp_color(old_color, get_pixel_color(tex, x - 1, y1)))
670
+ {
671
+ spanLeft = false;
672
+ }
673
+
674
+ if(!spanRight && x < tex->width - 1 && cmp_color(old_color,
675
+ get_pixel_color(tex, x + 1, y1)))
676
+ {
677
+ if(!push(x + 1, y1, tex->width - 1)) return;
678
+ spanRight = true;
679
+ }
680
+
681
+ else if(spanRight && x < tex->width - 1 && !cmp_color(old_color,get_pixel_color(tex, x + 1, y1)))
682
+ {
683
+ spanRight = false;
684
+ }
685
+ y1++;
686
+ }
687
+ }
688
+ draw_epilogue(&cur, tex, primary);
689
+ }
690
+ /** end of scanfill **/
691
+
692
+ /** bezier curve algorithm **/
693
+ static void
694
+ bezier_point(VALUE points, float u, float * x, float * y, int n, int format,
695
+ int draw_offset_x, int draw_offset_y)
696
+ {
697
+ int k;
698
+ int xy_index;
699
+ double sumx = 0, sumy = 0;
700
+
701
+
702
+ for(k = 0; k < n; k++) {
703
+ switch(format) {
704
+ case POINT_FORMAT:
705
+
706
+ sumx += NUM2DBL(point_x(get_from_array(points, k))) * bernstein(n - 1, k, u);
707
+ sumy += NUM2DBL(point_y(get_from_array(points, k))) * bernstein(n - 1, k, u);
708
+ break;
709
+ case SIMPLE_FORMAT:
710
+
711
+ xy_index = k * 2;
712
+ sumx += NUM2DBL(get_from_array(points, xy_index)) * bernstein(n - 1, k, u);
713
+ sumy += NUM2DBL(get_from_array(points, xy_index + 1)) * bernstein(n - 1, k, u);
714
+ break;
715
+ default:
716
+ rb_raise(rb_eArgError, "pixel format must be either POINT_FORMAT or SIMPLE_FORMAT");
717
+ }
718
+ }
719
+
720
+ *x = sumx + draw_offset_x;
721
+ *y = sumy + draw_offset_y;
722
+ }
723
+
724
+ void
725
+ bezier_do_action(VALUE points, texture_info * tex, VALUE hash_arg, sync sync_mode,
726
+ bool primary, action_struct * payload)
727
+ {
728
+ float u = 0.0;
729
+ action_struct cur;
730
+ float x1, y1, x2, y2;
731
+ int first_x, first_y;
732
+ int format;
733
+ int num_point_pairs;
734
+ bool closed = false;
735
+ VALUE offset_val;
736
+ int draw_offset_x, draw_offset_y;
737
+
738
+ /* defaults to 200 (1 / 0.005) samples per curve */
739
+ float step_size = 0.005;
740
+
741
+ draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload);
742
+
743
+ /* calculate offset */
744
+ offset_val = get_image_local(tex->image, DRAW_OFFSET);
745
+
746
+ draw_offset_x = NUM2INT(get_from_array(offset_val, 0));
747
+ draw_offset_y = NUM2INT(get_from_array(offset_val, 1));
748
+
749
+ if(is_a_hash(hash_arg)) {
750
+
751
+ /* if the polyline is 'closed' make the last point the first */
752
+ if(RTEST(get_from_hash(hash_arg, "closed")) || RTEST(get_from_hash(hash_arg, "close"))) {
753
+
754
+ /* so that our additional point is not persistent */
755
+ points = rb_obj_dup(points);
756
+ closed = true;
757
+ }
758
+
759
+ /* number of points to sample */
760
+ if(RTEST(get_from_hash(hash_arg, "sample_size"))) {
761
+ VALUE c = get_from_hash(hash_arg, "sample_size");
762
+ Check_Type(c, T_FIXNUM);
763
+ step_size = 1.0 / (float)FIX2INT(c);
764
+ }
765
+ }
766
+
767
+ if(is_a_point(get_from_array(points, 0))) {
768
+ format = POINT_FORMAT;
769
+
770
+ if(closed)
771
+ rb_ary_push(points, get_from_array(points, 0));
772
+
773
+ num_point_pairs = RARRAY_LEN(points);
774
+ }
775
+ else {
776
+ format = SIMPLE_FORMAT;
777
+
778
+ /* ensure points are given in pairs */
779
+ if(RARRAY_LEN(points) % 2)
780
+ rb_raise(rb_eArgError, "bezier needs an even number of points. got %d\n", (int)RARRAY_LEN(points));
781
+
782
+ if(closed) {
783
+ rb_ary_push(points, get_from_array(points, 0));
784
+ rb_ary_push(points, get_from_array(points, 1));
785
+ }
786
+
787
+ num_point_pairs = RARRAY_LEN(points) / 2;
788
+ }
789
+
790
+ if(num_point_pairs > 17)
791
+ rb_raise(rb_eArgError, "too many points for bezier curve. 17 points is current maximum. got %d\n",
792
+ num_point_pairs);
793
+
794
+ /* get the first point */
795
+ bezier_point(points, 0, &x1, &y1, num_point_pairs, format, draw_offset_x, draw_offset_y);
796
+
797
+ /* save it so we can link up with last point properly if the curve is 'closed' */
798
+ first_x = x1;
799
+ first_y = y1;
800
+
801
+ while(u <= 1) {
802
+ bezier_point(points, u, &x2, &y2, num_point_pairs, format, draw_offset_x, draw_offset_y);
803
+
804
+ line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
805
+
806
+ /* update drawing rectangle */
807
+ update_bounds(payload, x1, y1, x2, y2);
808
+
809
+ x1 = x2;
810
+ y1 = y2;
811
+
812
+ u += step_size;
813
+ }
814
+
815
+ /* sometimes beziers dont close properly, so we'll ensure it's closed */
816
+ if(closed)
817
+ line_do_action(x2, y2, first_x, first_y, tex, hash_arg, no_sync, false, payload);
818
+
819
+ draw_epilogue(&cur, tex, primary);
820
+ }
821
+ /** end of bezier **/
822
+
823
+ /** each_pixel **/
824
+ static void
825
+ set_color_array(VALUE ary, rgba * color)
826
+ {
827
+ set_array_value(ary, 0, rb_float_new(color->red));
828
+ set_array_value(ary, 1, rb_float_new(color->green));
829
+ set_array_value(ary, 2, rb_float_new(color->blue));
830
+ set_array_value(ary, 3, rb_float_new(color->alpha));
831
+ }
832
+
833
+ void
834
+ each_pixel_do_action(int x1, int y1, int x2, int y2, VALUE proc, texture_info * tex, VALUE hash_arg,
835
+ sync sync_mode, bool primary, action_struct * payload)
836
+ {
837
+ action_struct cur;
838
+ int x, y, arity;
839
+ VALUE rb_pix = rb_ary_new2(4);
840
+
841
+ draw_prologue(&cur, tex, x1, y1, x2, y2, &hash_arg, sync_mode, primary, &payload);
842
+
843
+ arity = FIX2INT(rb_funcall(proc, rb_intern("arity"), 0));
844
+
845
+ for(y = y1; y < y2 + 1; y++)
846
+ for(x = x1; x < x2 + 1; x++) {
847
+ rgba pix = get_pixel_color(tex, x, y);
848
+
849
+ set_color_array(rb_pix, &pix);
850
+
851
+ /* invoke the block */
852
+ switch(arity) {
853
+ case 1:
854
+ rb_funcall(proc, rb_intern("call"), 1, rb_pix);
855
+ break;
856
+ case 3:
857
+ rb_funcall(proc, rb_intern("call"), 3, rb_pix, INT2FIX(x), INT2FIX(y));
858
+ break;
859
+ default:
860
+ rb_raise(rb_eArgError, "permissible arities for each are 1 & 3. Got %d\n",
861
+ arity);
862
+
863
+ }
864
+
865
+ pix = convert_rb_color_to_rgba(rb_pix);
866
+
867
+ set_pixel_color(&pix, tex, x, y);
868
+ }
869
+
870
+ draw_epilogue(&cur, tex, primary);
871
+ }
872
+ /** end each_pixel iterator **/
873
+
874
+ /** splice algorithm **/
875
+ void
876
+ splice_do_action(int x0, int y0, int cx1, int cy1, int cx2, int cy2, texture_info * splice_tex,
877
+ texture_info * tex, VALUE hash_arg, sync sync_mode,
878
+ bool primary, action_struct * payload)
879
+ {
880
+ action_struct cur;
881
+ int xbound;
882
+ int ybound;
883
+ rgba chromakey;
884
+ int x, y;
885
+ float * image_buf = NULL;
886
+ bool inverse_chroma = false;
887
+ bool has_chroma = false;
888
+ bool same_image = false;
889
+
890
+ constrain_boundaries(&cx1, &cy1, &cx2, &cy2, splice_tex->width, splice_tex->height);
891
+ xbound = cx2 - cx1 + 1;
892
+ ybound = cy2 - cy1 + 1;
893
+
894
+ draw_prologue(&cur, tex, x0, y0,
895
+ x0 + xbound, y0 + ybound, &hash_arg, sync_mode, primary, &payload);
896
+
897
+
898
+ if(has_optional_hash_arg(hash_arg, "chroma_key")) {
899
+ VALUE c = get_from_hash(hash_arg, "chroma_key");
900
+ chromakey = convert_rb_color_to_rgba(c);
901
+ has_chroma = true;
902
+ }
903
+ else if(has_optional_hash_arg(hash_arg, "chroma_key_not")) {
904
+ chromakey = convert_rb_color_to_rgba(get_from_hash(hash_arg, "chroma_key_not"));
905
+ inverse_chroma = true;
906
+ has_chroma = true;
907
+ }
908
+
909
+ if(splice_tex->image == tex->image)
910
+ same_image = true;
911
+
912
+ /* NB: we do not use this in the general case since it's almost 1.5 times as slow.
913
+ It is necessary for splicing from/to the same region of pixels though.
914
+ */
915
+ if(same_image)
916
+ image_buf = get_image_chunk(splice_tex, cx1, cy1, cx2, cy2);
917
+
918
+ for(y = 0; y < ybound; y++)
919
+ for(x = 0; x < xbound; x++) {
920
+
921
+ if(!same_image)
922
+ payload->color = get_pixel_color(splice_tex, cx1 + x, cy1 + y);
923
+ else
924
+ payload->color = get_pixel_color_from_chunk(image_buf, xbound, ybound, x, y);
925
+
926
+ if(has_chroma) {
927
+ bool chroma_match = cmp_color(payload->color, chromakey);
928
+
929
+ /* look at released 0.2.0 code to see how USED to do this.
930
+ this is now a simplified boolean expression */
931
+ if(chroma_match == inverse_chroma)
932
+ set_pixel_color_with_style(payload, tex, x0 + x, y0 + y);
933
+ }
934
+ else
935
+ set_pixel_color_with_style(payload, tex, x0 + x, y0 + y);
936
+ }
937
+
938
+ if(same_image)
939
+ free(image_buf);
940
+
941
+ draw_epilogue(&cur, tex, primary);
942
+ }
943
+ /** end splice **/
944
+
945
+ static void
946
+ initialize_action_struct(action_struct * cur, VALUE hash_arg, sync sync_mode)
947
+ {
948
+ /* initialize action-struct to default values */
949
+ cur->sync_mode = sync_mode;
950
+ cur->hash_arg = hash_arg;
951
+
952
+ cur->color = convert_image_local_color_to_rgba(cur->tex->image);
953
+ cur->pen.has_color_control_proc = false;
954
+ cur->pen.has_color_control_transform = false;
955
+ cur->pen.has_source_texture = false;
956
+ cur->pen.alpha_blend = false;
957
+
958
+ /* set static color control transformations to defaults */
959
+ cur->pen.color_mult.red = 1.0;
960
+ cur->pen.color_mult.green = 1.0;
961
+ cur->pen.color_mult.blue = 1.0;
962
+ cur->pen.color_mult.alpha = 1.0;
963
+
964
+ cur->pen.color_add.red = 0.0;
965
+ cur->pen.color_add.green = 0.0;
966
+ cur->pen.color_add.blue = 0.0;
967
+ cur->pen.color_add.alpha = 0.0;
968
+ }
969
+
970
+ /* TODO: fix this function below, it's too ugly and bulky and weird **/
971
+ static void
972
+ process_common_hash_args(action_struct * cur, VALUE * hash_arg, sync sync_mode, bool primary)
973
+ {
974
+
975
+ VALUE user_defaults;
976
+ VALUE hash_blend;
977
+
978
+
979
+ /* if a hash doesn't exist then create one */
980
+ if(!is_a_hash(*hash_arg))
981
+ *hash_arg = rb_hash_new();
982
+
983
+ /* init the action to default values */
984
+ initialize_action_struct(cur, *hash_arg, sync_mode);
985
+
986
+ /* get the user default options & merge with given options */
987
+ user_defaults = get_image_local(cur->tex->image, USER_DEFAULTS);
988
+ hash_blend = rb_funcall(user_defaults, rb_intern("merge"), 1, *hash_arg);
989
+ rb_funcall(*hash_arg, rb_intern("merge!"), 1, hash_blend);
990
+
991
+ if(has_optional_hash_arg(*hash_arg, "color")) {
992
+ VALUE c = get_from_hash(*hash_arg, "color");
993
+ cur->color = convert_rb_color_to_rgba(c);
994
+ if(c == string2sym("random")) {
995
+ set_hash_value(*hash_arg, "color", convert_rgba_to_rb_color(&cur->color));
996
+ }
997
+ }
998
+
999
+ /* shadows */
1000
+ if(RTEST(get_from_hash(*hash_arg, "shadow"))) {
1001
+ cur->pen.color_mult.red = 0.66;
1002
+ cur->pen.color_mult.green = 0.66;
1003
+ cur->pen.color_mult.blue = 0.66;
1004
+ cur->pen.color_mult.alpha = 1;
1005
+
1006
+ cur->pen.has_color_control_transform = true;
1007
+ }
1008
+
1009
+ /* sync mode */
1010
+ if(has_optional_hash_arg(*hash_arg, "sync_mode")) {
1011
+ VALUE user_sync_mode = get_from_hash(*hash_arg, "sync_mode");
1012
+
1013
+ Check_Type(user_sync_mode, T_SYMBOL);
1014
+
1015
+ if(user_sync_mode == string2sym("lazy_sync"))
1016
+ cur->sync_mode = lazy_sync;
1017
+ else if(user_sync_mode == string2sym("eager_sync"))
1018
+ cur->sync_mode = eager_sync;
1019
+ else if(user_sync_mode == string2sym("no_sync"))
1020
+ cur->sync_mode = no_sync;
1021
+ else
1022
+ rb_raise(rb_eArgError, "unrecognized sync mode: %s\n. Allowable modes are "
1023
+ ":lazy_sync, :eager_sync, :no_sync.",
1024
+ sym2string(user_sync_mode));
1025
+
1026
+ delete_from_hash(*hash_arg, "sync_mode");
1027
+
1028
+ }
1029
+
1030
+ /* process the color_control block or transform (if there is one) */
1031
+ prepare_color_control(cur);
1032
+
1033
+ /* process the filling texture (if there is one) */
1034
+ prepare_fill_texture(cur);
1035
+
1036
+ /* does the user want to blend alpha values ? */
1037
+ if(get_from_hash(*hash_arg, "alpha_blend") == Qtrue)
1038
+ cur->pen.alpha_blend = true;
1039
+
1040
+ }
1041
+
1042
+ static void
1043
+ update_lazy_bounds(action_struct * cur, texture_info * tex)
1044
+ {
1045
+
1046
+ /* only update global bounds if we're doing a lazy_sync */
1047
+ if(cur->sync_mode == lazy_sync) {
1048
+ int xmin, ymin, xmax, ymax;
1049
+ VALUE lazy_bounds;
1050
+
1051
+ lazy_bounds = get_image_local(tex->image, LAZY_BOUNDS);
1052
+
1053
+ xmin = INT2FIX(MIN(cur->xmin, FIX2INT(get_from_array(lazy_bounds, 0))));
1054
+ ymin = INT2FIX(MIN(cur->ymin, FIX2INT(get_from_array(lazy_bounds, 1))));
1055
+ xmax = INT2FIX(MAX(cur->xmax, FIX2INT(get_from_array(lazy_bounds, 2))));
1056
+ ymax = INT2FIX(MAX(cur->ymax, FIX2INT(get_from_array(lazy_bounds, 3))));
1057
+
1058
+ set_array_value(lazy_bounds, 0, xmin);
1059
+ set_array_value(lazy_bounds, 1, ymin);
1060
+ set_array_value(lazy_bounds, 2, xmax);
1061
+ set_array_value(lazy_bounds, 3, ymax);
1062
+ }
1063
+ }
1064
+
1065
+ static void
1066
+ update_bounds(action_struct * cur, int xmin, int ymin, int xmax, int ymax)
1067
+ {
1068
+ if(xmin > xmax) SWAP(xmin, xmax);
1069
+ if(ymin > ymax) SWAP(ymin, ymax);
1070
+
1071
+ cur->xmin = MIN(cur->xmin, xmin);
1072
+ cur->ymin = MIN(cur->ymin, ymin);
1073
+ cur->xmax = MAX(cur->xmax, xmax);
1074
+ cur->ymax = MAX(cur->ymax, ymax);
1075
+ }
1076
+
1077
+ static void
1078
+ set_local_bounds(action_struct * cur, int xmin, int ymin, int xmax, int ymax, texture_info * tex)
1079
+ {
1080
+ if(cur->sync_mode == no_sync)
1081
+ return;
1082
+
1083
+ /* local bounds used by both eager_sync and lazy_sync: */
1084
+
1085
+ /* eager sync: to demarcate precise area to sync to opengl */
1086
+ /* lazy sync: to update global bounds */
1087
+ cur->xmin = xmin;
1088
+ cur->ymin = ymin;
1089
+ cur->xmax = xmax;
1090
+ cur->ymax = ymax;
1091
+ }
1092
+
1093
+ static void
1094
+ draw_prologue(action_struct * cur, texture_info * tex, int xmin, int ymin, int xmax, int ymax,
1095
+ VALUE * hash_arg, sync sync_mode, bool primary, action_struct ** payload_ptr)
1096
+ {
1097
+ if(!primary) return;
1098
+
1099
+ /* set the payload pointer */
1100
+ *payload_ptr = cur;
1101
+
1102
+ /* not too happy about having this here, look at texplay.h for why */
1103
+ cur->tex = tex;
1104
+
1105
+ process_common_hash_args(cur, hash_arg, sync_mode, primary);
1106
+
1107
+ set_local_bounds(cur, xmin, ymin, xmax, ymax, tex);
1108
+ }
1109
+
1110
+ void
1111
+ draw_epilogue(action_struct * cur, texture_info * tex, bool primary)
1112
+ {
1113
+ /* only primary actions get sync'd */
1114
+ if(!primary) return;
1115
+
1116
+ switch(cur->sync_mode) {
1117
+
1118
+ /* do not sync */
1119
+ case no_sync:
1120
+ return;
1121
+ break;
1122
+
1123
+ /* sync immediately */
1124
+ case eager_sync:
1125
+ create_subtexture_and_sync_to_gl(IMAGE_BOUNDS(cur), tex);
1126
+ break;
1127
+
1128
+ /* sync later (at end of paint block?) */
1129
+ case lazy_sync:
1130
+ update_lazy_bounds(cur, tex);
1131
+ break;
1132
+
1133
+ default:
1134
+ rb_raise(rb_eRuntimeError,
1135
+ "sync_mode may only be: lazy_sync, eager_sync, no_sync. got %d\n", cur->sync_mode);
1136
+ }
1137
+ }
1138
+
1139
+ /* set action color to return value of color_control proc */
1140
+ static void
1141
+ prepare_color_control(action_struct * cur)
1142
+ {
1143
+
1144
+ if(is_a_hash(cur->hash_arg)) {
1145
+ VALUE try_val = get_from_hash(cur->hash_arg, "color_control");
1146
+
1147
+ if(rb_respond_to(try_val, rb_intern("call"))) {
1148
+ cur->pen.color_control_proc = try_val;
1149
+ cur->pen.color_control_arity = FIX2INT(rb_funcall(try_val, rb_intern("arity"), 0));
1150
+ cur->pen.has_color_control_proc = true;
1151
+ }
1152
+ else if(is_a_hash(try_val)) {
1153
+ VALUE try_add = get_from_hash(try_val, "add");
1154
+ VALUE try_mult = get_from_hash(try_val, "mult");
1155
+
1156
+ if(is_an_array(try_add)) {
1157
+ if(RARRAY_LEN(try_add) < 4)
1158
+ rb_raise(rb_eArgError, ":color_control transform :add needs 4 parameters");
1159
+
1160
+ cur->pen.color_add.red = NUM2DBL(get_from_array(try_add, 0));
1161
+ cur->pen.color_add.green = NUM2DBL(get_from_array(try_add, 1));
1162
+ cur->pen.color_add.blue = NUM2DBL(get_from_array(try_add, 2));
1163
+ cur->pen.color_add.alpha = NUM2DBL(get_from_array(try_add, 3));
1164
+
1165
+ cur->pen.has_color_control_transform = true;
1166
+ }
1167
+ if(is_an_array(try_mult)) {
1168
+ if(RARRAY_LEN(try_mult) < 4)
1169
+ rb_raise(rb_eArgError, ":color_control transform :mult needs 4 parameters");
1170
+
1171
+ cur->pen.color_mult.red = NUM2DBL(get_from_array(try_mult, 0));
1172
+ cur->pen.color_mult.green = NUM2DBL(get_from_array(try_mult, 1));
1173
+ cur->pen.color_mult.blue = NUM2DBL(get_from_array(try_mult, 2));
1174
+ cur->pen.color_mult.alpha = NUM2DBL(get_from_array(try_mult, 3));
1175
+
1176
+ cur->pen.has_color_control_transform = true;
1177
+ }
1178
+
1179
+ }
1180
+ }
1181
+ }
1182
+
1183
+ static rgba
1184
+ exec_color_control_proc(action_struct * cur, texture_info * tex, int x, int y, rgba blended_pixel)
1185
+ {
1186
+ int arity = cur->pen.color_control_arity;
1187
+ VALUE proc = cur->pen.color_control_proc;
1188
+ rgba old_color = get_pixel_color(tex, x, y);
1189
+ rgba current_color = blended_pixel;
1190
+ rgba new_color;
1191
+
1192
+ if(!cur->pen.has_color_control_proc)
1193
+ rb_raise(rb_eRuntimeError, "needs a proc");
1194
+
1195
+ switch(arity) {
1196
+ case -1:
1197
+ case 0:
1198
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), 0));
1199
+ break;
1200
+
1201
+ case 1:
1202
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
1203
+ convert_rgba_to_rb_color(&old_color)));
1204
+ break;
1205
+
1206
+ case 2:
1207
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
1208
+ convert_rgba_to_rb_color(&old_color),
1209
+ convert_rgba_to_rb_color(&current_color)));
1210
+ break;
1211
+
1212
+ case 3:
1213
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
1214
+ convert_rgba_to_rb_color(&old_color),
1215
+ INT2FIX(x), INT2FIX(y)));
1216
+ break;
1217
+ case 4:
1218
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
1219
+ convert_rgba_to_rb_color(&old_color),
1220
+ convert_rgba_to_rb_color(&current_color),
1221
+ INT2FIX(x), INT2FIX(y)));
1222
+ break;
1223
+ default:
1224
+ rb_raise(rb_eArgError, "permissible arities for color_control proc are 1, 2, 3 and 4. Got %d\n",
1225
+ arity);
1226
+ }
1227
+
1228
+ /* update the action color */
1229
+ return new_color;
1230
+ }
1231
+
1232
+ static void
1233
+ prepare_fill_texture(action_struct * payload)
1234
+ {
1235
+ if(is_a_hash(payload->hash_arg)) {
1236
+ VALUE try_image = get_from_hash(payload->hash_arg, "texture");
1237
+ if(is_gosu_image(try_image)) {
1238
+
1239
+ get_texture_info(try_image, &payload->pen.source_tex);
1240
+ payload->pen.has_source_texture = true;
1241
+ }
1242
+ }
1243
+ }
1244
+
1245
+
1246
+ /* TODO: reimplement using SSE2 */
1247
+ static rgba
1248
+ apply_color_control_transform(action_struct * payload, texture_info * tex, int x, int y)
1249
+
1250
+ {
1251
+ rgba transformed_color;
1252
+
1253
+ transformed_color = get_pixel_color(tex, x, y);
1254
+
1255
+ transformed_color.red += payload->pen.color_add.red;
1256
+ transformed_color.green += payload->pen.color_add.green;
1257
+ transformed_color.blue += payload->pen.color_add.blue;
1258
+ transformed_color.alpha += payload->pen.color_add.alpha;
1259
+
1260
+ transformed_color.red *= payload->pen.color_mult.red;
1261
+ transformed_color.green *= payload->pen.color_mult.green;
1262
+ transformed_color.blue *= payload->pen.color_mult.blue;
1263
+ transformed_color.alpha *= payload->pen.color_mult.alpha;
1264
+
1265
+ return transformed_color;
1266
+ }
1267
+
1268
+ static rgba
1269
+ apply_alpha_blend(action_struct * payload, texture_info * tex, int x, int y, rgba blended_pixel)
1270
+ {
1271
+ rgba dest_pixel = get_pixel_color(tex, x, y);
1272
+ rgba finished_pixel;
1273
+
1274
+
1275
+ if(not_a_color(blended_pixel))
1276
+ return blended_pixel;
1277
+
1278
+ /* alpha blending is nothing more than a weighted average of src and dest pixels
1279
+ based on source alpha value */
1280
+ /* NB: destination alpha value is ignored */
1281
+
1282
+ /** TO DO: rewrite this using sse2 instructions **/
1283
+ finished_pixel.red = blended_pixel.alpha * blended_pixel.red + (1 - blended_pixel.alpha)
1284
+ * dest_pixel.red;
1285
+
1286
+ finished_pixel.green = blended_pixel.alpha * blended_pixel.green + (1 - blended_pixel.alpha)
1287
+ * dest_pixel.green;
1288
+
1289
+ finished_pixel.blue = blended_pixel.alpha * blended_pixel.blue + (1 - blended_pixel.alpha)
1290
+ * dest_pixel.blue;
1291
+
1292
+ finished_pixel.alpha = blended_pixel.alpha;
1293
+
1294
+
1295
+ return finished_pixel;
1296
+ }
1297
+
1298
+ static void
1299
+ set_pixel_color_with_style(action_struct * payload, texture_info * tex, int x, int y)
1300
+ {
1301
+
1302
+ rgba blended_pixel;
1303
+
1304
+ blended_pixel = payload->color;
1305
+
1306
+ /* for color_control transform */
1307
+ if(payload->pen.has_color_control_transform)
1308
+ blended_pixel = apply_color_control_transform(payload, tex, x, y);
1309
+
1310
+ /* for texture fill */
1311
+ if(payload->pen.has_source_texture)
1312
+ blended_pixel = get_pixel_color(&payload->pen.source_tex,
1313
+ x % payload->pen.source_tex.width,
1314
+ y % payload->pen.source_tex.height);
1315
+
1316
+ /* for color_control block */
1317
+ if(payload->pen.has_color_control_proc)
1318
+ blended_pixel = exec_color_control_proc(payload, tex, x, y, blended_pixel);
1319
+
1320
+
1321
+ /* TO DO: do bitwise pixel combinations here */
1322
+
1323
+ /* TO DO: refactor into its own helper function
1324
+ & rewrite using sse2 */
1325
+ if(payload->pen.alpha_blend)
1326
+ blended_pixel = apply_alpha_blend(payload, tex, x, y, blended_pixel);
1327
+
1328
+
1329
+ set_pixel_color(&blended_pixel, tex, x, y);
1330
+ }
1331
+