texplay 0.2.800 → 0.2.900

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/CHANGELOG +138 -119
  2. data/README.markdown +43 -41
  3. data/Rakefile +68 -68
  4. data/examples/common.rb +8 -8
  5. data/examples/example_alpha_blend.rb +31 -31
  6. data/examples/example_bezier.rb +42 -42
  7. data/examples/example_color_control.rb +69 -69
  8. data/examples/example_color_transform.rb +64 -67
  9. data/examples/example_color_transform_circle.rb +65 -0
  10. data/examples/example_dup.rb +75 -75
  11. data/examples/example_each.rb +42 -42
  12. data/examples/example_effect.rb +35 -35
  13. data/examples/example_fill.rb +44 -44
  14. data/examples/example_fill_old.rb +49 -49
  15. data/examples/example_fluent.rb +31 -31
  16. data/examples/example_gen_eval.rb +34 -34
  17. data/examples/example_hash_arguments.rb +47 -47
  18. data/examples/example_light.rb +77 -0
  19. data/examples/example_lsystem.rb +61 -61
  20. data/examples/example_melt.rb +27 -27
  21. data/examples/example_meyet.rb +64 -0
  22. data/examples/example_polyline.rb +43 -43
  23. data/examples/example_scale.rb +29 -29
  24. data/examples/example_simple.rb +48 -38
  25. data/examples/example_sync.rb +60 -60
  26. data/examples/example_trace.rb +1 -1
  27. data/examples/example_turtle.rb +40 -40
  28. data/examples/example_weird.rb +3 -1
  29. data/examples/media/Thumbs.db +0 -0
  30. data/ext/texplay/actions.c +999 -1001
  31. data/ext/texplay/actions.h +60 -60
  32. data/ext/texplay/bindings.c +1162 -1149
  33. data/ext/texplay/bindings.h +46 -46
  34. data/ext/texplay/cache.c +118 -118
  35. data/ext/texplay/cache.h +24 -24
  36. data/ext/texplay/compat.h +27 -27
  37. data/ext/texplay/extconf.rb +28 -28
  38. data/ext/texplay/gen_eval.c +211 -211
  39. data/ext/texplay/gen_eval.h +20 -20
  40. data/ext/texplay/graphics_utils.c +188 -63
  41. data/ext/texplay/graphics_utils.h +0 -1
  42. data/ext/texplay/object2module.c +171 -171
  43. data/ext/texplay/object2module.h +11 -11
  44. data/ext/texplay/texplay.c +169 -169
  45. data/ext/texplay/texplay.h +147 -130
  46. data/ext/texplay/utils.c +816 -752
  47. data/ext/texplay/utils.h +151 -145
  48. data/lib/texplay-contrib.rb +171 -171
  49. data/lib/texplay.rb +162 -137
  50. data/lib/texplay/version.rb +1 -1
  51. metadata +9 -5
@@ -7,7 +7,9 @@ class W < Gosu::Window
7
7
  def initialize
8
8
  super(1024, 768, false, 20)
9
9
  @img = Gosu::Image.new(self, "#{Common::MEDIA}/gob.png")
10
- @img.rect 0, 10, 300, 300, :color => :blue, :fill => true, :mode => :softlight
10
+ @sun = Gosu::Image.new(self, "#{Common::MEDIA}/sunset.png")
11
+ @img.splice @sun, 0, 0, :mode => :exclusion
12
+ # @img.rect 0, 10, 300, 300, :color => :blue, :fill => true, :mode => :softlight
11
13
  end
12
14
 
13
15
  def draw
Binary file
@@ -1,1001 +1,999 @@
1
- #include "texplay.h"
2
- #include "utils.h"
3
- #include "graphics_utils.h"
4
- #include "actions.h"
5
- #include <assert.h>
6
- #include <math.h>
7
- #ifdef __APPLE__
8
- # include <glut.h>
9
- #else
10
- # include <GL/glut.h>
11
- #endif
12
-
13
-
14
- /** line_do_action, bresenham's algorithm **/
15
- typedef enum { no_mode, until_color, while_color} trace_mode_type;
16
-
17
- static inline bool
18
- is_trace_match(rgba c, rgba trace_color, trace_mode_type trace_mode)
19
- {
20
- if(trace_mode == while_color) {
21
- if(!cmp_color(c, trace_color))
22
- return true;
23
- }
24
- else if(trace_mode == until_color) {
25
- if(cmp_color(c, trace_color))
26
- return true;
27
- }
28
-
29
- return false;
30
- }
31
-
32
- trace_match
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
- bool has_trace = false;
42
- rgba trace_color;
43
- trace_mode_type trace_mode = no_mode;
44
-
45
-
46
-
47
- if(has_optional_hash_arg(hash_arg, "thickness"))
48
- thickness = NUM2INT(get_from_hash(hash_arg, "thickness"));
49
-
50
- if(has_optional_hash_arg(hash_arg, "trace") && primary) {
51
- VALUE trace_hash = get_from_hash(hash_arg, "trace");
52
-
53
- /* we're tracing (not drawing) */
54
- has_trace = true;
55
-
56
- /* since we're not drawing, no need to sync */
57
- sync_mode = no_sync;
58
-
59
- if(has_optional_hash_arg(trace_hash, "until_color")) {
60
- VALUE c = get_from_hash(trace_hash, "until_color");
61
- trace_color = convert_rb_color_to_rgba(c);
62
- trace_mode = until_color;
63
- }
64
- else if(has_optional_hash_arg(trace_hash, "while_color")) {
65
- VALUE c = get_from_hash(trace_hash, "while_color");
66
- trace_color = convert_rb_color_to_rgba(c);
67
- trace_mode = while_color;
68
- }
69
- }
70
-
71
- draw_prologue(&cur, tex, x1, y1,
72
- x2, y2, &hash_arg, sync_mode, primary, &payload);
73
-
74
-
75
- /* clip the line */
76
- cohen_sutherland_clip(&x1, &y1, &x2, &y2, 0, 0, tex->width - 1, tex->height - 1);
77
-
78
- W = ABS(x2 - x1);
79
- H = ABS(y2 - y1);
80
-
81
- if(x1 < x2)
82
- xinc = 1;
83
- else
84
- xinc = -1;
85
-
86
- if(y1 < y2)
87
- yinc = 1;
88
- else
89
- yinc = -1;
90
-
91
- x = x1;
92
- y = y1;
93
-
94
- if(W >= H) {
95
- F = 2 * H - W;
96
- while(x != x2) {
97
- if(thickness <= 1) {
98
- if(!has_trace)
99
- set_pixel_color_with_style(payload, tex, x, y);
100
- else {
101
- rgba c = get_pixel_color(tex, x, y);
102
-
103
- if (is_trace_match(c, trace_color, trace_mode))
104
- return (trace_match) { x, y, c };
105
- }
106
- }
107
- else {
108
- set_hash_value(hash_arg, "fill", Qtrue);
109
- circle_do_action(x, y, thickness / 2, tex, hash_arg, no_sync, false, payload);
110
- }
111
-
112
- if(F < 0)
113
- F += 2 * H;
114
- else {
115
- F += 2 * (H - W);
116
- y += yinc;
117
- }
118
- x += xinc;
119
- }
120
- }
121
- else {
122
- F = 2 * W - H;
123
- while(y != y2 ) {
124
-
125
- if(thickness <= 1) {
126
- if(!has_trace)
127
- set_pixel_color_with_style(payload, tex, x, y);
128
- else {
129
- rgba c = get_pixel_color(tex, x, y);
130
-
131
- if (is_trace_match(c, trace_color, trace_mode))
132
- return (trace_match) { x, y, c };
133
- }
134
- }
135
- else {
136
- set_hash_value(hash_arg, "fill", Qtrue);
137
- circle_do_action(x, y, thickness / 2, tex, hash_arg, no_sync, false, payload);
138
- }
139
-
140
- if(F < 0)
141
- F += 2 * W;
142
- else {
143
- F += 2 * (W - H);
144
- x += xinc;
145
- }
146
- y += yinc;
147
- }
148
- }
149
-
150
- if(thickness <= 1) {
151
- if(!has_trace)
152
- set_pixel_color_with_style(payload, tex, x2, y2);
153
- else {
154
- rgba c = get_pixel_color(tex, x, y);
155
-
156
- if (is_trace_match(c, trace_color, trace_mode))
157
- return (trace_match) { x, y, c };
158
- }
159
- }
160
- else {
161
- set_hash_value(hash_arg, "fill", Qtrue);
162
- circle_do_action(x2, y2, thickness / 2, tex, hash_arg, no_sync, false, payload);
163
-
164
- }
165
- draw_epilogue(&cur, tex, primary);
166
-
167
- return (trace_match) { .x = -1, .y = -1 };
168
- }
169
- /** end line **/
170
-
171
- /** polyline algorithm **/
172
-
173
- /* used by both polyline and bezier */
174
- #define SIMPLE_FORMAT 0
175
- #define POINT_FORMAT 1
176
-
177
- /* calculate a single point */
178
- static void
179
- polyline_point(VALUE points, int k, int * x, int * y, int format, int draw_offset_x,
180
- int draw_offset_y)
181
- {
182
- int xy_index;
183
-
184
- switch(format) {
185
- case POINT_FORMAT:
186
- *x = NUM2INT(point_x(get_from_array(points, k))) + draw_offset_x;
187
- *y = NUM2INT(point_y(get_from_array(points, k))) + draw_offset_y;
188
- break;
189
-
190
- case SIMPLE_FORMAT:
191
- xy_index = k * 2;
192
- *x = NUM2INT(get_from_array(points, xy_index)) + draw_offset_x;
193
- *y = NUM2INT(get_from_array(points, xy_index + 1)) + draw_offset_y;
194
-
195
- break;
196
- default:
197
- rb_raise(rb_eArgError, "pixel format must be either POINT_FORMAT or SIMPLE_FORMAT");
198
- }
199
- }
200
-
201
- void
202
- polyline_do_action(VALUE points, texture_info * tex, VALUE hash_arg,
203
- sync sync_mode, bool primary, action_struct * payload)
204
- {
205
-
206
- int x1, y1, x2, y2;
207
- int format;
208
- int num_point_pairs;
209
- int k;
210
- int draw_offset_y, draw_offset_x;
211
- action_struct cur;
212
- VALUE offset_val;
213
- bool closed = false;
214
-
215
- draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload);
216
-
217
- /* calculate offset */
218
- offset_val = get_image_local(tex->image, DRAW_OFFSET);
219
-
220
- draw_offset_x = NUM2INT(get_from_array(offset_val, 0));
221
- draw_offset_y = NUM2INT(get_from_array(offset_val, 1));
222
-
223
- /* if the polyline is 'closed' make the last point the first */
224
- if(is_a_hash(hash_arg))
225
- if(RTEST(get_from_hash(hash_arg, "closed")) || RTEST(get_from_hash(hash_arg, "close"))) {
226
-
227
- /* so that our additional point is not persistent */
228
- points = rb_obj_dup(points);
229
- closed = true;
230
- }
231
- /* determine format of points */
232
- if(is_a_point(get_from_array(points, 0))) {
233
- format = POINT_FORMAT;
234
-
235
- /* if the polyline is closed to form a polygon then make the last point and first point identical */
236
- if(closed)
237
- rb_ary_push(points, get_from_array(points, 0));
238
-
239
- num_point_pairs = RARRAY_LEN(points);
240
- }
241
- else {
242
- format = SIMPLE_FORMAT;
243
-
244
- /* ensure there is an 'x' for every 'y' */
245
- if(RARRAY_LEN(points) % 2)
246
- rb_raise(rb_eArgError, "polyline needs an even number of points. got %d\n",
247
- (int)RARRAY_LEN(points));
248
-
249
- if(closed) {
250
- rb_ary_push(points, get_from_array(points, 0));
251
- rb_ary_push(points, get_from_array(points, 1));
252
- }
253
-
254
- num_point_pairs = RARRAY_LEN(points) / 2;
255
- }
256
-
257
- /* calculate first point */
258
- polyline_point(points, 0, &x1, &y1, format, draw_offset_x, draw_offset_y);
259
-
260
- /* calc the points and draw the polyline */
261
- for(k = 1; k < num_point_pairs; k++) {
262
-
263
- polyline_point(points, k, &x2, &y2, format, draw_offset_x, draw_offset_y);
264
-
265
- line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
266
-
267
- /* update drawing rectangle */
268
- update_bounds(payload, x1, y1, x2, y2);
269
-
270
- x1 = x2; y1 = y2;
271
- }
272
-
273
- draw_epilogue(&cur, tex, primary);
274
- }
275
- /** end polyline **/
276
-
277
- /* regular polygon algorithm */
278
- void
279
- ngon_do_action(int x, int y, int r, int num_sides, texture_info * tex, VALUE hash_arg,
280
- sync sync_mode, bool primary, action_struct * payload)
281
- {
282
- action_struct cur;
283
- int x1, y1, x2, y2, x0, y0;
284
- int thickness;
285
- float angle = 0;
286
-
287
- draw_prologue(&cur, tex, x - r, y - r,
288
- x + r, y + r, &hash_arg, sync_mode, primary, &payload);
289
-
290
-
291
- if(is_a_hash(hash_arg)) {
292
- if(RTEST(get_from_hash(hash_arg, "thickness"))) {
293
- thickness = NUM2INT(get_from_hash(hash_arg, "thickness"));
294
-
295
- /* TO DO: find a better way of doing this */
296
- cur.xmin = x - r - thickness / 2;
297
- cur.ymin = y - r - thickness / 2;
298
- cur.xmax = x + r + thickness / 2;
299
- cur.ymax = y + r + thickness / 2;
300
- }
301
-
302
- if(RTEST(get_from_hash(hash_arg, "start_angle"))) {
303
- angle = NUM2INT(get_from_hash(hash_arg, "start_angle")) / 360.0 * 2 * PI;
304
- }
305
- }
306
-
307
- /* calculate first point */
308
- x0 = x1 = x + r * cos(angle);
309
- y0 = y1 = y + r * sin(angle);
310
-
311
- for(int n = 0; n < num_sides; n++) {
312
- x2 = x + r * cos((2 * PI / num_sides) * n + angle);
313
- y2 = y + r * sin((2 * PI / num_sides) * n + angle);
314
-
315
- line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
316
-
317
- x1 = x2; y1 = y2;
318
- }
319
-
320
- line_do_action(x0, y0, x1, y1, tex, hash_arg, no_sync, false, payload);
321
-
322
- draw_epilogue(&cur, tex, primary);
323
- }
324
- /** end of ngon */
325
-
326
- /** rectangle algorithm **/
327
- void
328
- rect_do_action(int x1, int y1, int x2, int y2, texture_info * tex, VALUE hash_arg,
329
- sync sync_mode, bool primary, action_struct * payload)
330
- {
331
- action_struct cur;
332
- bool fill = false;
333
- int thickness = 1;
334
-
335
- draw_prologue(&cur, tex, x1, y1,
336
- x2, y2, &hash_arg, sync_mode, primary, &payload);
337
-
338
-
339
- if(is_a_hash(hash_arg)) {
340
-
341
- /* make our private copy of the hash so we can mess with it */
342
- hash_arg = rb_obj_dup(hash_arg);
343
-
344
- if(RTEST(get_from_hash(hash_arg, "fill")) || RTEST(get_from_hash(hash_arg, "filled"))) {
345
- fill = true;
346
-
347
- /* since we're filling the rect, line thickness is irrelevant */
348
- delete_from_hash(hash_arg, "thickness");
349
- }
350
- else if(RTEST(get_from_hash(hash_arg, "thickness"))) {
351
- thickness = NUM2INT(get_from_hash(hash_arg, "thickness"));
352
- /* TO DO: find a better way of doing this */
353
-
354
- if(thickness > 1) {
355
- cur.xmin = x1 - thickness / 2;
356
- cur.ymin = y1 - thickness / 2;
357
- cur.xmax = x2 + thickness / 2 + 1;
358
- cur.ymax = y2 + thickness / 2 + 1;
359
- }
360
- }
361
- }
362
- if(!fill) {
363
- line_do_action(x1, y1, x2, y1, tex, hash_arg, no_sync, false, payload);
364
- line_do_action(x1, y1, x1, y2, tex, hash_arg, no_sync, false, payload);
365
- line_do_action(x1, y2, x2, y2, tex, hash_arg, no_sync, false, payload);
366
- line_do_action(x2, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
367
- }
368
- else {
369
- if(y1 > y2) SWAP(y1, y2);
370
-
371
- for(int y = y1; y <= y2; y++)
372
- line_do_action(x1, y, x2, y, tex, hash_arg, no_sync, false, payload);
373
- }
374
-
375
- draw_epilogue(&cur, tex, primary);
376
- }
377
-
378
-
379
- /** midpoint circle algorithm **/
380
- void
381
- circle_do_action(int x1, int y1, int r, texture_info * tex, VALUE hash_arg,
382
- sync sync_mode, bool primary, action_struct * payload)
383
- {
384
-
385
- int x, y;
386
- float p;
387
- action_struct cur;
388
- bool fill = false;
389
-
390
- draw_prologue(&cur, tex, x1 - r, y1 - r, x1 + r, y1 + r, &hash_arg,
391
- sync_mode, primary, &payload);
392
-
393
-
394
- if(is_a_hash(hash_arg)) {
395
-
396
- /* make our private copy of the hash so we can mess with it */
397
- hash_arg = rb_obj_dup(hash_arg);
398
-
399
- if(RTEST(get_from_hash(hash_arg, "fill")) || RTEST(get_from_hash(hash_arg, "filled"))) {
400
- fill = true;
401
-
402
- /* to prevent infinite recursion set line thickness to 1 :D
403
- NB: a filled circle uses lines and a thick line uses filled circles :D */
404
- delete_from_hash(hash_arg, "thickness");
405
- }
406
- }
407
-
408
- x = 0 ; y = r;
409
- p = 5 / 4 - r;
410
- if(!fill) {
411
- while (x <= y) {
412
- set_pixel_color_with_style(payload, tex, x1 + x, y1 + y);
413
- set_pixel_color_with_style(payload, tex, x1 + x, y1 - y);
414
- set_pixel_color_with_style(payload, tex, x1 - x, y1 + y);
415
- set_pixel_color_with_style(payload, tex, x1 - x, y1 - y);
416
- set_pixel_color_with_style(payload, tex, x1 + y, y1 + x);
417
- set_pixel_color_with_style(payload, tex, x1 + y, y1 - x);
418
- set_pixel_color_with_style(payload, tex, x1 - y, y1 + x);
419
- set_pixel_color_with_style(payload, tex, x1 - y, y1 - x);
420
-
421
- if (p < 0) {
422
- p += 2 * x + 3;
423
- }
424
- else {
425
- y--;
426
- p += 2 * (x - y) + 5;
427
- }
428
- x++;
429
- }
430
- }
431
- else {
432
- while (x <= y) {
433
- line_do_action(x1 - x, y1 + y, x1 + x, y1 + y, tex, hash_arg, no_sync, false, payload);
434
- line_do_action(x1 - x, y1 - y, x1 + x, y1 - y, tex, hash_arg, no_sync, false, payload);
435
- line_do_action(x1 - y, y1 + x, x1 + y, y1 + x, tex, hash_arg, no_sync, false, payload);
436
- line_do_action(x1 - y, y1 - x, x1 + y, y1 - x, tex, hash_arg, no_sync, false, payload);
437
-
438
- if (p < 0) {
439
- p += 2 * x + 3;
440
- }
441
- else {
442
- y--;
443
- p += 2 * (x - y) + 5;
444
- }
445
- x++;
446
- }
447
- }
448
-
449
- draw_epilogue(&cur, tex, primary);
450
- }
451
- /** end circle **/
452
-
453
- /** set pixel algorithm **/
454
- void
455
- pixel_do_action(int x1, int y1, texture_info * tex, VALUE hash_arg,
456
- sync sync_mode, bool primary, action_struct * payload)
457
- {
458
- action_struct cur;
459
-
460
- draw_prologue(&cur, tex, x1, y1, x1, y1, &hash_arg, sync_mode, primary, &payload);
461
-
462
- set_pixel_color_with_style(payload, tex, x1, y1);
463
-
464
- draw_epilogue(&cur, tex, primary);
465
- }
466
- /** end set pixel **/
467
-
468
- /*** non recursive FLOOD FILL, inspired by John R. Shaw ***/
469
- typedef struct { int x1, x2, y, dy; } LINESEGMENT;
470
-
471
- #define MAXDEPTH 10000
472
-
473
- #define PUSH(XL, XR, Y, DY) \
474
- if( sp < stack+MAXDEPTH && Y+(DY) >= nMinX && Y+(DY) <= nMaxY ) \
475
- { sp->x1 = XL; sp->x2 = XR; sp->y = Y; sp->dy = DY; ++sp; }
476
-
477
- #define POP(XL, XR, Y, DY) \
478
- { --sp; XL = sp->x1; XR = sp->x2; Y = sp->y+(DY = sp->dy); }
479
-
480
- void
481
- flood_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg,
482
- sync sync_mode, bool primary, action_struct * payload)
483
- {
484
- int left, x1, x2, dy;
485
- rgba old_color;
486
- LINESEGMENT stack[MAXDEPTH], *sp = stack;
487
-
488
- action_struct cur;
489
-
490
- int nMinX, nMinY, nMaxX, nMaxY;
491
-
492
- /* NOTE: 1024 is just a place-holder to indicate maximum possible width/height.
493
- Values will be constrained to realistic dimensions by constrain_boundaries() function */
494
- draw_prologue(&cur, tex, 0, 0, 1024, 1024, &hash_arg, sync_mode, primary, &payload);
495
-
496
- /* fill hates alpha_blend so let's turn it off */
497
- payload->pen.alpha_blend = false;
498
-
499
- nMinX = cur.xmin; nMinY = cur.ymin;
500
- nMaxX = cur.xmax; nMaxY = cur.ymax;
501
-
502
- old_color = get_pixel_color(tex, x, y);
503
- if( cmp_color(old_color, cur.color) )
504
- return;
505
-
506
- if( x < nMinX || x > nMaxX || y < nMinX || y > nMaxY )
507
- return;
508
-
509
- PUSH(x, x, y, 1); /* needed in some cases */
510
- PUSH(x, x, y + 1, -1); /* seed segment (popped 1st) */
511
-
512
- while( sp > stack ) {
513
- POP(x1, x2, y, dy);
514
-
515
- for( x = x1; x >= nMinX && cmp_color(get_pixel_color(tex, x, y), old_color); --x ) {
516
- set_pixel_color_with_style(payload, tex, x, y);
517
- }
518
-
519
- if( x >= x1 )
520
- goto SKIP;
521
-
522
- left = x + 1;
523
- if( left < x1 )
524
- PUSH(y, left, x1 - 1, -dy); /* leak on left? */
525
-
526
- x = x1 + 1;
527
-
528
- do {
529
- for( ; x <= nMaxX && cmp_color(get_pixel_color(tex, x, y), old_color); ++x ){
530
- set_pixel_color_with_style(payload, tex, x, y);
531
- }
532
-
533
- PUSH(left, x - 1, y, dy);
534
-
535
- if( x > x2 + 1 )
536
- PUSH(x2 + 1, x - 1, y, -dy); /* leak on right? */
537
-
538
- SKIP: for( ++x; x <= x2 && !cmp_color(get_pixel_color(tex, x, y), old_color); ++x ) {;}
539
-
540
- left = x;
541
- } while( x <= x2 );
542
- }
543
-
544
- draw_epilogue(&cur, tex, primary);
545
- }
546
- /*** END FLOOD FILL ***/
547
-
548
- /** glow fill algorithm, from the gosu forums **/
549
- static void
550
- glow_floodFill( int x, int y, rgba * seed_color, action_struct * cur, texture_info * tex, texture_info * tex2 )
551
- {
552
- /* used to flood in both horizontal directions from the given point to form a line. */
553
- int fillL, fillR;
554
- int i;
555
-
556
- /* initialize the flood directions */
557
- fillL = fillR = x;
558
-
559
- /* flood left until a new color is hit - or the edge of the image */
560
- do {
561
- /* for texture filling */
562
- if(tex2)
563
- cur->color = get_pixel_color(tex2, fillL % tex2->width, y % tex2->height);
564
-
565
- /* TWO VERSIONS of below */
566
-
567
- /* SLOW BUT MODERN VERSION */
568
- /* set_pixel_color_with_style(cur, tex, fillL, y); */
569
-
570
- /* FAST but old version */
571
- set_pixel_color(&cur->color, tex, fillL, y);
572
-
573
- fillL--;
574
- } while( (fillL >= 0 ) && (cmp_color(get_pixel_color(tex, fillL, y), *seed_color)) );
575
-
576
- /* flood right until a new color is hit - or the edge of the image */
577
- do {
578
- // for texture filling
579
- if(tex2)
580
- cur->color = get_pixel_color(tex2, fillR % tex2->width, y % tex2->height);
581
-
582
- /* set_pixel_color_with_style(cur, tex, fillR, y); */
583
-
584
- set_pixel_color(&cur->color, tex, fillR, y);
585
-
586
- fillR++;
587
- } while( (fillR < cur->xmax - 1) && (cmp_color(get_pixel_color(tex, fillR, y), *seed_color)) );
588
-
589
- /* recurse to the line above and the line below at each point */
590
- for( i = fillL + 1; i < fillR; i++ ) {
591
- /* Flood above */
592
- if( ( y > 0 ) && ( cmp_color(get_pixel_color(tex, i, y - 1), *seed_color) ) ) {
593
-
594
- glow_floodFill( i, y-1, seed_color, cur, tex, tex2 );
595
- }
596
- /* flood below */
597
- if( (y < cur->ymax - 1) && (cmp_color(get_pixel_color(tex, i, y + 1), *seed_color) )) {
598
- glow_floodFill( i, y+1, seed_color, cur, tex, tex2 );
599
- }
600
- }
601
- }
602
-
603
- void
604
- glow_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg,
605
- sync sync_mode, bool primary, action_struct * payload)
606
- {
607
- action_struct cur;
608
- rgba seed_color;
609
- texture_info fill_image;
610
- texture_info * fill_image_ptr = NULL;
611
-
612
- if(!bound_by_rect(x, y, 0, 0, tex->width, tex->height)) return;
613
-
614
- draw_prologue(&cur, tex, 0, 0, 1024, 1024, &hash_arg, sync_mode, primary, &payload);
615
-
616
- /* fill hates alpha_blend so let's turn it off */
617
- payload->pen.alpha_blend = false;
618
-
619
- if(is_a_hash(hash_arg)) {
620
- VALUE try_image = get_from_hash(hash_arg, "texture");
621
- if(is_gosu_image(try_image)) {
622
- get_texture_info(try_image, &fill_image);
623
- fill_image_ptr = &fill_image;
624
- }
625
- }
626
-
627
- seed_color = get_pixel_color(tex, x, y);
628
-
629
- /* last argument is pointer to texture fill data (if it exists) or NULL (if it doesn't) */
630
- glow_floodFill( x, y, &seed_color, &cur, tex, fill_image_ptr );
631
-
632
- draw_epilogue(&cur, tex, primary);
633
- }
634
- /** end of glow fill **/
635
-
636
- /** scanfill algorithm **/
637
- /* the stack */
638
- #define stackSize 16777218
639
- int stack[stackSize];
640
- int stackPointer;
641
-
642
- static bool
643
- pop(int * x, int * y, int h)
644
- {
645
- if(stackPointer > 0)
646
- {
647
- int p = stack[stackPointer];
648
- *x = p / h;
649
- *y = p % h;
650
- stackPointer--;
651
- return true;
652
- }
653
- else
654
- {
655
- return false;
656
- }
657
- }
658
-
659
- static bool
660
- push(int x, int y, int h)
661
- {
662
- if(stackPointer < stackSize - 1)
663
- {
664
- stackPointer++;
665
- stack[stackPointer] = h * x + y;
666
- return true;
667
- }
668
- else
669
- {
670
- return false;
671
- }
672
- }
673
-
674
- static void
675
- emptyStack()
676
- {
677
- int x, y;
678
- while(pop(&x, &y, 0));
679
- }
680
-
681
- void
682
- scan_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg,
683
- sync sync_mode, bool primary, action_struct * payload)
684
- {
685
- action_struct cur;
686
- rgba old_color;
687
- int y1;
688
- bool spanLeft, spanRight;
689
-
690
- if(!bound_by_rect(x, y, 0, 0, tex->width - 1, tex->height - 1)) return;
691
-
692
- /* NB: using XMAX_OOB etc since we dont know the drawing area yet; drawing area will be set by
693
- update_bounds() function in main loop */
694
- draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload);
695
-
696
- /* fill hates alpha_blend so let's turn it off */
697
- payload->pen.alpha_blend = false;
698
-
699
- old_color = get_pixel_color(tex, x, y);
700
-
701
- if(cmp_color(old_color, cur.color)) return;
702
-
703
- emptyStack();
704
-
705
- if(!push(x, y, tex->width - 1)) return;
706
-
707
- while(pop(&x, &y, tex->width - 1))
708
- {
709
- y1 = y;
710
- while(y1 >= 0 && cmp_color(old_color, get_pixel_color(tex, x, y1))) y1--;
711
- y1++;
712
- spanLeft = spanRight = false;
713
- while(y1 < tex->height && cmp_color(old_color, get_pixel_color(tex, x, y1)) )
714
- {
715
- set_pixel_color_with_style(payload, tex, x, y1);
716
-
717
- /* update the drawing rectangle */
718
- update_bounds(payload, x, y1, x, y1);
719
-
720
- if(!spanLeft && x > 0 && cmp_color(old_color, get_pixel_color(tex, x - 1, y1)))
721
- {
722
- if(!push(x - 1, y1, tex->width - 1)) return;
723
- spanLeft = true;
724
- }
725
-
726
- else if(spanLeft && !cmp_color(old_color, get_pixel_color(tex, x - 1, y1)))
727
- {
728
- spanLeft = false;
729
- }
730
-
731
- if(!spanRight && x < tex->width - 1 && cmp_color(old_color,
732
- get_pixel_color(tex, x + 1, y1)))
733
- {
734
- if(!push(x + 1, y1, tex->width - 1)) return;
735
- spanRight = true;
736
- }
737
-
738
- else if(spanRight && x < tex->width - 1 && !cmp_color(old_color,get_pixel_color(tex, x + 1, y1)))
739
- {
740
- spanRight = false;
741
- }
742
- y1++;
743
- }
744
- }
745
- draw_epilogue(&cur, tex, primary);
746
- }
747
- /** end of scanfill **/
748
-
749
- /** bezier curve algorithm **/
750
- static void
751
- bezier_point(VALUE points, float u, float * x, float * y, int n, int format,
752
- int draw_offset_x, int draw_offset_y)
753
- {
754
- int xy_index;
755
- double sumx = 0, sumy = 0;
756
-
757
-
758
- for(int k = 0; k < n; k++) {
759
- switch(format) {
760
- case POINT_FORMAT:
761
-
762
- sumx += NUM2DBL(point_x(get_from_array(points, k))) * bernstein(n - 1, k, u);
763
- sumy += NUM2DBL(point_y(get_from_array(points, k))) * bernstein(n - 1, k, u);
764
- break;
765
- case SIMPLE_FORMAT:
766
-
767
- xy_index = k * 2;
768
- sumx += NUM2DBL(get_from_array(points, xy_index)) * bernstein(n - 1, k, u);
769
- sumy += NUM2DBL(get_from_array(points, xy_index + 1)) * bernstein(n - 1, k, u);
770
- break;
771
- default:
772
- rb_raise(rb_eArgError, "pixel format must be either POINT_FORMAT or SIMPLE_FORMAT");
773
- }
774
- }
775
-
776
- *x = sumx + draw_offset_x;
777
- *y = sumy + draw_offset_y;
778
- }
779
-
780
- void
781
- bezier_do_action(VALUE points, texture_info * tex, VALUE hash_arg, sync sync_mode,
782
- bool primary, action_struct * payload)
783
- {
784
- float u = 0.0;
785
- action_struct cur;
786
- float x1, y1, x2, y2;
787
- int first_x, first_y;
788
- int format;
789
- int num_point_pairs;
790
- bool closed = false;
791
- VALUE offset_val;
792
- int draw_offset_x, draw_offset_y;
793
-
794
- /* defaults to 200 (1 / 0.005) samples per curve */
795
- float step_size = 0.005;
796
-
797
- draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload);
798
-
799
- /* calculate offset */
800
- offset_val = get_image_local(tex->image, DRAW_OFFSET);
801
-
802
- draw_offset_x = NUM2INT(get_from_array(offset_val, 0));
803
- draw_offset_y = NUM2INT(get_from_array(offset_val, 1));
804
-
805
- if(is_a_hash(hash_arg)) {
806
-
807
- /* if the polyline is 'closed' make the last point the first */
808
- if(RTEST(get_from_hash(hash_arg, "closed")) || RTEST(get_from_hash(hash_arg, "close"))) {
809
-
810
- /* so that our additional point is not persistent */
811
- points = rb_obj_dup(points);
812
- closed = true;
813
- }
814
-
815
- /* number of points to sample */
816
- if(RTEST(get_from_hash(hash_arg, "sample_size"))) {
817
- VALUE c = get_from_hash(hash_arg, "sample_size");
818
- Check_Type(c, T_FIXNUM);
819
- step_size = 1.0 / (float)FIX2INT(c);
820
- }
821
- }
822
-
823
- if(is_a_point(get_from_array(points, 0))) {
824
- format = POINT_FORMAT;
825
-
826
- if(closed)
827
- rb_ary_push(points, get_from_array(points, 0));
828
-
829
- num_point_pairs = RARRAY_LEN(points);
830
- }
831
- else {
832
- format = SIMPLE_FORMAT;
833
-
834
- /* ensure points are given in pairs */
835
- if(RARRAY_LEN(points) % 2)
836
- rb_raise(rb_eArgError, "bezier needs an even number of points. got %d\n", (int)RARRAY_LEN(points));
837
-
838
- if(closed) {
839
- rb_ary_push(points, get_from_array(points, 0));
840
- rb_ary_push(points, get_from_array(points, 1));
841
- }
842
-
843
- num_point_pairs = RARRAY_LEN(points) / 2;
844
- }
845
-
846
- if(num_point_pairs > 17)
847
- rb_raise(rb_eArgError, "too many points for bezier curve. 17 points is current maximum. got %d\n",
848
- num_point_pairs);
849
-
850
- /* get the first point */
851
- bezier_point(points, 0, &x1, &y1, num_point_pairs, format, draw_offset_x, draw_offset_y);
852
-
853
- /* save it so we can link up with last point properly if the curve is 'closed' */
854
- first_x = x1;
855
- first_y = y1;
856
-
857
- while(u <= 1) {
858
- bezier_point(points, u, &x2, &y2, num_point_pairs, format, draw_offset_x, draw_offset_y);
859
-
860
- line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
861
-
862
- /* update drawing rectangle */
863
- update_bounds(payload, x1, y1, x2, y2);
864
-
865
- x1 = x2;
866
- y1 = y2;
867
-
868
- u += step_size;
869
- }
870
-
871
- /* sometimes beziers dont close properly, so we'll ensure it's closed */
872
- if(closed)
873
- line_do_action(x2, y2, first_x, first_y, tex, hash_arg, no_sync, false, payload);
874
-
875
- draw_epilogue(&cur, tex, primary);
876
- }
877
- /** end of bezier **/
878
-
879
- /** each_pixel **/
880
- static void
881
- set_color_array(VALUE ary, rgba * color)
882
- {
883
- set_array_value(ary, 0, rb_float_new(color->red));
884
- set_array_value(ary, 1, rb_float_new(color->green));
885
- set_array_value(ary, 2, rb_float_new(color->blue));
886
- set_array_value(ary, 3, rb_float_new(color->alpha));
887
- }
888
-
889
- void
890
- each_pixel_do_action(int x1, int y1, int x2, int y2, VALUE proc, texture_info * tex, VALUE hash_arg,
891
- sync sync_mode, bool primary, action_struct * payload)
892
- {
893
- action_struct cur;
894
- int arity;
895
- VALUE rb_pix = rb_ary_new2(4);
896
-
897
- draw_prologue(&cur, tex, x1, y1, x2, y2, &hash_arg, sync_mode, primary, &payload);
898
-
899
- arity = FIX2INT(rb_funcall(proc, rb_intern("arity"), 0));
900
-
901
- for(int y = y1; y < y2 + 1; y++)
902
- for(int x = x1; x < x2 + 1; x++) {
903
- rgba pix = get_pixel_color(tex, x, y);
904
-
905
- set_color_array(rb_pix, &pix);
906
-
907
- /* invoke the block */
908
- switch(arity) {
909
- case 1:
910
- rb_funcall(proc, rb_intern("call"), 1, rb_pix);
911
- break;
912
- case 3:
913
- rb_funcall(proc, rb_intern("call"), 3, rb_pix, INT2FIX(x), INT2FIX(y));
914
- break;
915
- default:
916
- rb_raise(rb_eArgError, "permissible arities for each are 1 & 3. Got %d\n",
917
- arity);
918
-
919
- }
920
-
921
- pix = convert_rb_color_to_rgba(rb_pix);
922
-
923
- set_pixel_color(&pix, tex, x, y);
924
- }
925
-
926
- draw_epilogue(&cur, tex, primary);
927
- }
928
- /** end each_pixel iterator **/
929
-
930
- /** splice algorithm **/
931
- void
932
- splice_do_action(int x0, int y0, int cx1, int cy1, int cx2, int cy2, texture_info * splice_tex,
933
- texture_info * tex, VALUE hash_arg, sync sync_mode,
934
- bool primary, action_struct * payload)
935
- {
936
- action_struct cur;
937
- int xbound;
938
- int ybound;
939
- rgba chromakey;
940
- float * image_buf = NULL;
941
- bool inverse_chroma = false;
942
- bool same_image = false;
943
- bool has_chroma = false;
944
-
945
- constrain_boundaries(&cx1, &cy1, &cx2, &cy2, splice_tex->width, splice_tex->height);
946
- xbound = cx2 - cx1 + 1;
947
- ybound = cy2 - cy1 + 1;
948
-
949
- draw_prologue(&cur, tex, x0, y0,
950
- x0 + xbound, y0 + ybound, &hash_arg, sync_mode, primary, &payload);
951
-
952
-
953
- if(has_optional_hash_arg(hash_arg, "chroma_key")) {
954
- VALUE c = get_from_hash(hash_arg, "chroma_key");
955
- chromakey = convert_rb_color_to_rgba(c);
956
- has_chroma = true;
957
- }
958
- else if(has_optional_hash_arg(hash_arg, "chroma_key_not")) {
959
- chromakey = convert_rb_color_to_rgba(get_from_hash(hash_arg, "chroma_key_not"));
960
- inverse_chroma = true;
961
- has_chroma = true;
962
- }
963
-
964
- if(splice_tex->image == tex->image)
965
- same_image = true;
966
-
967
- /* NB: we do not use this in the general case since it's almost 1.5 times as slow.
968
- It is necessary for splicing from/to the same region of pixels though.
969
- */
970
- if(same_image)
971
- image_buf = get_image_chunk(splice_tex, cx1, cy1, cx2, cy2);
972
-
973
- for(int y = 0; y < ybound; y++)
974
- for(int x = 0; x < xbound; x++) {
975
-
976
- if(!same_image)
977
- payload->color = get_pixel_color(splice_tex, cx1 + x, cy1 + y);
978
- else
979
- payload->color = get_pixel_color_from_chunk(image_buf, xbound, ybound, x, y);
980
-
981
- if(has_chroma) {
982
- bool chroma_match = cmp_color(payload->color, chromakey);
983
-
984
- /* look at released 0.2.0 code to see how USED to do this.
985
- this is now a simplified boolean expression */
986
- if(chroma_match == inverse_chroma)
987
- set_pixel_color_with_style(payload, tex, x0 + x, y0 + y);
988
- }
989
- else
990
- set_pixel_color_with_style(payload, tex, x0 + x, y0 + y);
991
- }
992
-
993
- if(same_image)
994
- free(image_buf);
995
-
996
- draw_epilogue(&cur, tex, primary);
997
- }
998
- /** end splice **/
999
-
1000
-
1001
-
1
+ #include "texplay.h"
2
+ #include "utils.h"
3
+ #include "graphics_utils.h"
4
+ #include "actions.h"
5
+ #include <assert.h>
6
+ #include <math.h>
7
+ #ifdef __APPLE__
8
+ # include <glut.h>
9
+ #else
10
+ # include <GL/glut.h>
11
+ #endif
12
+
13
+
14
+ /** line_do_action, bresenham's algorithm **/
15
+ typedef enum { no_mode, until_color, while_color} trace_mode_type;
16
+
17
+ static inline bool
18
+ is_trace_match(rgba c, rgba trace_color, trace_mode_type trace_mode)
19
+ {
20
+ if(trace_mode == while_color) {
21
+ if(!cmp_color(c, trace_color))
22
+ return true;
23
+ }
24
+ else if(trace_mode == until_color) {
25
+ if(cmp_color(c, trace_color))
26
+ return true;
27
+ }
28
+
29
+ return false;
30
+ }
31
+
32
+ trace_match
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
+ bool has_trace = false;
42
+ rgba trace_color;
43
+ trace_mode_type trace_mode = no_mode;
44
+
45
+ if(has_optional_hash_arg(hash_arg, "thickness"))
46
+ thickness = NUM2INT(get_from_hash(hash_arg, "thickness"));
47
+
48
+ if(has_optional_hash_arg(hash_arg, "trace") && primary) {
49
+ VALUE trace_hash = get_from_hash(hash_arg, "trace");
50
+
51
+ /* we're tracing (not drawing) */
52
+ has_trace = true;
53
+
54
+ /* since we're not drawing, no need to sync */
55
+ sync_mode = no_sync;
56
+
57
+ if(has_optional_hash_arg(trace_hash, "until_color")) {
58
+ VALUE c = get_from_hash(trace_hash, "until_color");
59
+ trace_color = convert_rb_color_to_rgba(c);
60
+ trace_mode = until_color;
61
+ }
62
+ else if(has_optional_hash_arg(trace_hash, "while_color")) {
63
+ VALUE c = get_from_hash(trace_hash, "while_color");
64
+ trace_color = convert_rb_color_to_rgba(c);
65
+ trace_mode = while_color;
66
+ }
67
+ }
68
+
69
+ draw_prologue(&cur, tex, x1, y1,
70
+ x2, y2, &hash_arg, sync_mode, primary, &payload);
71
+
72
+
73
+ /* clip the line */
74
+ cohen_sutherland_clip(&x1, &y1, &x2, &y2, 0, 0, tex->width - 1, tex->height - 1);
75
+
76
+ W = ABS(x2 - x1);
77
+ H = ABS(y2 - y1);
78
+
79
+ if(x1 < x2)
80
+ xinc = 1;
81
+ else
82
+ xinc = -1;
83
+
84
+ if(y1 < y2)
85
+ yinc = 1;
86
+ else
87
+ yinc = -1;
88
+
89
+ x = x1;
90
+ y = y1;
91
+
92
+ if(W >= H) {
93
+ F = 2 * H - W;
94
+ while(x != x2) {
95
+ if(thickness <= 1) {
96
+ if(!has_trace)
97
+ set_pixel_color_with_style(payload, tex, x, y);
98
+ else {
99
+ rgba c = get_pixel_color(tex, x, y);
100
+
101
+ if (is_trace_match(c, trace_color, trace_mode))
102
+ return (trace_match) { x, y, c };
103
+ }
104
+ }
105
+ else {
106
+ set_hash_value(hash_arg, "fill", Qtrue);
107
+ circle_do_action(x, y, thickness / 2, tex, hash_arg, no_sync, false, payload);
108
+ }
109
+
110
+ if(F < 0)
111
+ F += 2 * H;
112
+ else {
113
+ F += 2 * (H - W);
114
+ y += yinc;
115
+ }
116
+ x += xinc;
117
+ }
118
+ }
119
+ else {
120
+ F = 2 * W - H;
121
+ while(y != y2 ) {
122
+
123
+ if(thickness <= 1) {
124
+ if(!has_trace)
125
+ set_pixel_color_with_style(payload, tex, x, y);
126
+ else {
127
+ rgba c = get_pixel_color(tex, x, y);
128
+
129
+ if (is_trace_match(c, trace_color, trace_mode))
130
+ return (trace_match) { x, y, c };
131
+ }
132
+ }
133
+ else {
134
+ set_hash_value(hash_arg, "fill", Qtrue);
135
+ circle_do_action(x, y, thickness / 2, tex, hash_arg, no_sync, false, payload);
136
+ }
137
+
138
+ if(F < 0)
139
+ F += 2 * W;
140
+ else {
141
+ F += 2 * (W - H);
142
+ x += xinc;
143
+ }
144
+ y += yinc;
145
+ }
146
+ }
147
+
148
+ if(thickness <= 1) {
149
+ if(!has_trace)
150
+ set_pixel_color_with_style(payload, tex, x2, y2);
151
+ else {
152
+ rgba c = get_pixel_color(tex, x, y);
153
+
154
+ if (is_trace_match(c, trace_color, trace_mode))
155
+ return (trace_match) { x, y, c };
156
+ }
157
+ }
158
+ else {
159
+ set_hash_value(hash_arg, "fill", Qtrue);
160
+ circle_do_action(x2, y2, thickness / 2, tex, hash_arg, no_sync, false, payload);
161
+
162
+ }
163
+ draw_epilogue(&cur, tex, primary);
164
+
165
+ return (trace_match) { .x = -1, .y = -1 };
166
+ }
167
+ /** end line **/
168
+
169
+ /** polyline algorithm **/
170
+
171
+ /* used by both polyline and bezier */
172
+ #define SIMPLE_FORMAT 0
173
+ #define POINT_FORMAT 1
174
+
175
+ /* calculate a single point */
176
+ static void
177
+ polyline_point(VALUE points, int k, int * x, int * y, int format, int draw_offset_x,
178
+ int draw_offset_y)
179
+ {
180
+ int xy_index;
181
+
182
+ switch(format) {
183
+ case POINT_FORMAT:
184
+ *x = NUM2INT(point_x(get_from_array(points, k))) + draw_offset_x;
185
+ *y = NUM2INT(point_y(get_from_array(points, k))) + draw_offset_y;
186
+ break;
187
+
188
+ case SIMPLE_FORMAT:
189
+ xy_index = k * 2;
190
+ *x = NUM2INT(get_from_array(points, xy_index)) + draw_offset_x;
191
+ *y = NUM2INT(get_from_array(points, xy_index + 1)) + draw_offset_y;
192
+
193
+ break;
194
+ default:
195
+ rb_raise(rb_eArgError, "pixel format must be either POINT_FORMAT or SIMPLE_FORMAT");
196
+ }
197
+ }
198
+
199
+ void
200
+ polyline_do_action(VALUE points, texture_info * tex, VALUE hash_arg,
201
+ sync sync_mode, bool primary, action_struct * payload)
202
+ {
203
+
204
+ int x1, y1, x2, y2;
205
+ int format;
206
+ int num_point_pairs;
207
+ int k;
208
+ int draw_offset_y, draw_offset_x;
209
+ action_struct cur;
210
+ VALUE offset_val;
211
+ bool closed = false;
212
+
213
+ draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload);
214
+
215
+ /* calculate offset */
216
+ offset_val = get_image_local(tex->image, DRAW_OFFSET);
217
+
218
+ draw_offset_x = NUM2INT(get_from_array(offset_val, 0));
219
+ draw_offset_y = NUM2INT(get_from_array(offset_val, 1));
220
+
221
+ /* if the polyline is 'closed' make the last point the first */
222
+ if(is_a_hash(hash_arg))
223
+ if(RTEST(get_from_hash(hash_arg, "closed")) || RTEST(get_from_hash(hash_arg, "close"))) {
224
+
225
+ /* so that our additional point is not persistent */
226
+ points = rb_obj_dup(points);
227
+ closed = true;
228
+ }
229
+ /* determine format of points */
230
+ if(is_a_point(get_from_array(points, 0))) {
231
+ format = POINT_FORMAT;
232
+
233
+ /* if the polyline is closed to form a polygon then make the last point and first point identical */
234
+ if(closed)
235
+ rb_ary_push(points, get_from_array(points, 0));
236
+
237
+ num_point_pairs = RARRAY_LEN(points);
238
+ }
239
+ else {
240
+ format = SIMPLE_FORMAT;
241
+
242
+ /* ensure there is an 'x' for every 'y' */
243
+ if(RARRAY_LEN(points) % 2)
244
+ rb_raise(rb_eArgError, "polyline needs an even number of points. got %d\n",
245
+ (int)RARRAY_LEN(points));
246
+
247
+ if(closed) {
248
+ rb_ary_push(points, get_from_array(points, 0));
249
+ rb_ary_push(points, get_from_array(points, 1));
250
+ }
251
+
252
+ num_point_pairs = RARRAY_LEN(points) / 2;
253
+ }
254
+
255
+ /* calculate first point */
256
+ polyline_point(points, 0, &x1, &y1, format, draw_offset_x, draw_offset_y);
257
+
258
+ /* calc the points and draw the polyline */
259
+ for(k = 1; k < num_point_pairs; k++) {
260
+
261
+ polyline_point(points, k, &x2, &y2, format, draw_offset_x, draw_offset_y);
262
+
263
+ line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
264
+
265
+ /* update drawing rectangle */
266
+ update_bounds(payload, x1, y1, x2, y2);
267
+
268
+ x1 = x2; y1 = y2;
269
+ }
270
+
271
+ draw_epilogue(&cur, tex, primary);
272
+ }
273
+ /** end polyline **/
274
+
275
+ /* regular polygon algorithm */
276
+ void
277
+ ngon_do_action(int x, int y, int r, int num_sides, texture_info * tex, VALUE hash_arg,
278
+ sync sync_mode, bool primary, action_struct * payload)
279
+ {
280
+ action_struct cur;
281
+ int x1, y1, x2, y2, x0, y0;
282
+ int thickness;
283
+ float angle = 0;
284
+
285
+ draw_prologue(&cur, tex, x - r, y - r,
286
+ x + r, y + r, &hash_arg, sync_mode, primary, &payload);
287
+
288
+
289
+ if(is_a_hash(hash_arg)) {
290
+ if(RTEST(get_from_hash(hash_arg, "thickness"))) {
291
+ thickness = NUM2INT(get_from_hash(hash_arg, "thickness"));
292
+
293
+ /* TO DO: find a better way of doing this */
294
+ cur.xmin = x - r - thickness / 2;
295
+ cur.ymin = y - r - thickness / 2;
296
+ cur.xmax = x + r + thickness / 2;
297
+ cur.ymax = y + r + thickness / 2;
298
+ }
299
+
300
+ if(RTEST(get_from_hash(hash_arg, "start_angle"))) {
301
+ angle = NUM2INT(get_from_hash(hash_arg, "start_angle")) / 360.0 * 2 * PI;
302
+ }
303
+ }
304
+
305
+ /* calculate first point */
306
+ x0 = x1 = x + r * cos(angle);
307
+ y0 = y1 = y + r * sin(angle);
308
+
309
+ for(int n = 0; n < num_sides; n++) {
310
+ x2 = x + r * cos((2 * PI / num_sides) * n + angle);
311
+ y2 = y + r * sin((2 * PI / num_sides) * n + angle);
312
+
313
+ line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
314
+
315
+ x1 = x2; y1 = y2;
316
+ }
317
+
318
+ line_do_action(x0, y0, x1, y1, tex, hash_arg, no_sync, false, payload);
319
+
320
+ draw_epilogue(&cur, tex, primary);
321
+ }
322
+ /** end of ngon */
323
+
324
+ /** rectangle algorithm **/
325
+ void
326
+ rect_do_action(int x1, int y1, int x2, int y2, texture_info * tex, VALUE hash_arg,
327
+ sync sync_mode, bool primary, action_struct * payload)
328
+ {
329
+ action_struct cur;
330
+ bool fill = false;
331
+ int thickness = 1;
332
+
333
+ draw_prologue(&cur, tex, x1, y1,
334
+ x2, y2, &hash_arg, 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
+ /* since we're filling the rect, line thickness is irrelevant */
346
+ delete_from_hash(hash_arg, "thickness");
347
+ }
348
+ else if(RTEST(get_from_hash(hash_arg, "thickness"))) {
349
+ thickness = NUM2INT(get_from_hash(hash_arg, "thickness"));
350
+ /* TO DO: find a better way of doing this */
351
+
352
+ if(thickness > 1) {
353
+ cur.xmin = x1 - thickness / 2;
354
+ cur.ymin = y1 - thickness / 2;
355
+ cur.xmax = x2 + thickness / 2 + 1;
356
+ cur.ymax = y2 + thickness / 2 + 1;
357
+ }
358
+ }
359
+ }
360
+ if(!fill) {
361
+ line_do_action(x1, y1, x2, y1, tex, hash_arg, no_sync, false, payload);
362
+ line_do_action(x1, y1, x1, y2, tex, hash_arg, no_sync, false, payload);
363
+ line_do_action(x1, y2, x2, y2, tex, hash_arg, no_sync, false, payload);
364
+ line_do_action(x2, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
365
+ }
366
+ else {
367
+ if(y1 > y2) SWAP(y1, y2);
368
+
369
+ for(int y = y1; y <= y2; y++)
370
+ line_do_action(x1, y, x2, y, tex, hash_arg, no_sync, false, payload);
371
+ }
372
+
373
+ draw_epilogue(&cur, tex, primary);
374
+ }
375
+
376
+
377
+ /** midpoint circle algorithm **/
378
+ void
379
+ circle_do_action(int x1, int y1, int r, texture_info * tex, VALUE hash_arg,
380
+ sync sync_mode, bool primary, action_struct * payload)
381
+ {
382
+
383
+ int x, y;
384
+ float p;
385
+ action_struct cur;
386
+ bool fill = false;
387
+
388
+ draw_prologue(&cur, tex, x1 - r, y1 - r, x1 + r, y1 + r, &hash_arg,
389
+ sync_mode, primary, &payload);
390
+
391
+
392
+ if(is_a_hash(hash_arg)) {
393
+
394
+ /* make our private copy of the hash so we can mess with it */
395
+ hash_arg = rb_obj_dup(hash_arg);
396
+
397
+ if(RTEST(get_from_hash(hash_arg, "fill")) || RTEST(get_from_hash(hash_arg, "filled"))) {
398
+ fill = true;
399
+
400
+ /* to prevent infinite recursion set line thickness to 1 :D
401
+ NB: a filled circle uses lines and a thick line uses filled circles :D */
402
+ delete_from_hash(hash_arg, "thickness");
403
+ }
404
+ }
405
+
406
+ x = 0 ; y = r;
407
+ p = 5 / 4 - r;
408
+ if(!fill) {
409
+ while (x <= y) {
410
+ set_pixel_color_with_style(payload, tex, x1 + x, y1 + y);
411
+ set_pixel_color_with_style(payload, tex, x1 + x, y1 - y);
412
+ set_pixel_color_with_style(payload, tex, x1 - x, y1 + y);
413
+ set_pixel_color_with_style(payload, tex, x1 - x, y1 - y);
414
+ set_pixel_color_with_style(payload, tex, x1 + y, y1 + x);
415
+ set_pixel_color_with_style(payload, tex, x1 + y, y1 - x);
416
+ set_pixel_color_with_style(payload, tex, x1 - y, y1 + x);
417
+ set_pixel_color_with_style(payload, tex, x1 - y, y1 - x);
418
+
419
+ if (p < 0) {
420
+ p += 2 * x + 3;
421
+ }
422
+ else {
423
+ y--;
424
+ p += 2 * (x - y) + 5;
425
+ }
426
+ x++;
427
+ }
428
+ }
429
+ else {
430
+ while (x <= y) {
431
+ line_do_action(x1 - x, y1 + y, x1 + x, y1 + y, tex, hash_arg, no_sync, false, payload);
432
+ line_do_action(x1 - x, y1 - y, x1 + x, y1 - y, tex, hash_arg, no_sync, false, payload);
433
+ line_do_action(x1 - y, y1 + x, x1 + y, y1 + x, tex, hash_arg, no_sync, false, payload);
434
+ line_do_action(x1 - y, y1 - x, x1 + y, y1 - x, tex, hash_arg, no_sync, false, payload);
435
+
436
+ if (p < 0) {
437
+ p += 2 * x + 3;
438
+ }
439
+ else {
440
+ y--;
441
+ p += 2 * (x - y) + 5;
442
+ }
443
+ x++;
444
+ }
445
+ }
446
+
447
+ draw_epilogue(&cur, tex, primary);
448
+ }
449
+ /** end circle **/
450
+
451
+ /** set pixel algorithm **/
452
+ void
453
+ pixel_do_action(int x1, int y1, texture_info * tex, VALUE hash_arg,
454
+ sync sync_mode, bool primary, action_struct * payload)
455
+ {
456
+ action_struct cur;
457
+
458
+ draw_prologue(&cur, tex, x1, y1, x1, y1, &hash_arg, sync_mode, primary, &payload);
459
+
460
+ set_pixel_color_with_style(payload, tex, x1, y1);
461
+
462
+ draw_epilogue(&cur, tex, primary);
463
+ }
464
+ /** end set pixel **/
465
+
466
+ /*** non recursive FLOOD FILL, inspired by John R. Shaw ***/
467
+ typedef struct { int x1, x2, y, dy; } LINESEGMENT;
468
+
469
+ #define MAXDEPTH 10000
470
+
471
+ #define PUSH(XL, XR, Y, DY) \
472
+ if( sp < stack+MAXDEPTH && Y+(DY) >= nMinX && Y+(DY) <= nMaxY ) \
473
+ { sp->x1 = XL; sp->x2 = XR; sp->y = Y; sp->dy = DY; ++sp; }
474
+
475
+ #define POP(XL, XR, Y, DY) \
476
+ { --sp; XL = sp->x1; XR = sp->x2; Y = sp->y+(DY = sp->dy); }
477
+
478
+ void
479
+ flood_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg,
480
+ sync sync_mode, bool primary, action_struct * payload)
481
+ {
482
+ int left, x1, x2, dy;
483
+ rgba old_color;
484
+ LINESEGMENT stack[MAXDEPTH], *sp = stack;
485
+
486
+ action_struct cur;
487
+
488
+ int nMinX, nMinY, nMaxX, nMaxY;
489
+
490
+ /* NOTE: 1024 is just a place-holder to indicate maximum possible width/height.
491
+ Values will be constrained to realistic dimensions by constrain_boundaries() function */
492
+ draw_prologue(&cur, tex, 0, 0, 1024, 1024, &hash_arg, sync_mode, primary, &payload);
493
+
494
+ /* fill hates alpha_blend so let's turn it off */
495
+ payload->pen.alpha_blend = false;
496
+
497
+ nMinX = cur.xmin; nMinY = cur.ymin;
498
+ nMaxX = cur.xmax; nMaxY = cur.ymax;
499
+
500
+ old_color = get_pixel_color(tex, x, y);
501
+ if( cmp_color(old_color, cur.color) )
502
+ return;
503
+
504
+ if( x < nMinX || x > nMaxX || y < nMinX || y > nMaxY )
505
+ return;
506
+
507
+ PUSH(x, x, y, 1); /* needed in some cases */
508
+ PUSH(x, x, y + 1, -1); /* seed segment (popped 1st) */
509
+
510
+ while( sp > stack ) {
511
+ POP(x1, x2, y, dy);
512
+
513
+ for( x = x1; x >= nMinX && cmp_color(get_pixel_color(tex, x, y), old_color); --x ) {
514
+ set_pixel_color_with_style(payload, tex, x, y);
515
+ }
516
+
517
+ if( x >= x1 )
518
+ goto SKIP;
519
+
520
+ left = x + 1;
521
+ if( left < x1 )
522
+ PUSH(y, left, x1 - 1, -dy); /* leak on left? */
523
+
524
+ x = x1 + 1;
525
+
526
+ do {
527
+ for( ; x <= nMaxX && cmp_color(get_pixel_color(tex, x, y), old_color); ++x ){
528
+ set_pixel_color_with_style(payload, tex, x, y);
529
+ }
530
+
531
+ PUSH(left, x - 1, y, dy);
532
+
533
+ if( x > x2 + 1 )
534
+ PUSH(x2 + 1, x - 1, y, -dy); /* leak on right? */
535
+
536
+ SKIP: for( ++x; x <= x2 && !cmp_color(get_pixel_color(tex, x, y), old_color); ++x ) {;}
537
+
538
+ left = x;
539
+ } while( x <= x2 );
540
+ }
541
+
542
+ draw_epilogue(&cur, tex, primary);
543
+ }
544
+ /*** END FLOOD FILL ***/
545
+
546
+ /** glow fill algorithm, from the gosu forums **/
547
+ static void
548
+ glow_floodFill( int x, int y, rgba * seed_color, action_struct * cur, texture_info * tex, texture_info * tex2 )
549
+ {
550
+ /* used to flood in both horizontal directions from the given point to form a line. */
551
+ int fillL, fillR;
552
+ int i;
553
+
554
+ /* initialize the flood directions */
555
+ fillL = fillR = x;
556
+
557
+ /* flood left until a new color is hit - or the edge of the image */
558
+ do {
559
+ /* for texture filling */
560
+ if(tex2)
561
+ cur->color = get_pixel_color(tex2, fillL % tex2->width, y % tex2->height);
562
+
563
+ /* TWO VERSIONS of below */
564
+
565
+ /* SLOW BUT MODERN VERSION */
566
+ /* set_pixel_color_with_style(cur, tex, fillL, y); */
567
+
568
+ /* FAST but old version */
569
+ set_pixel_color(&cur->color, tex, fillL, y);
570
+
571
+ fillL--;
572
+ } while( (fillL >= 0 ) && (cmp_color(get_pixel_color(tex, fillL, y), *seed_color)) );
573
+
574
+ /* flood right until a new color is hit - or the edge of the image */
575
+ do {
576
+ // for texture filling
577
+ if(tex2)
578
+ cur->color = get_pixel_color(tex2, fillR % tex2->width, y % tex2->height);
579
+
580
+ /* set_pixel_color_with_style(cur, tex, fillR, y); */
581
+
582
+ set_pixel_color(&cur->color, tex, fillR, y);
583
+
584
+ fillR++;
585
+ } while( (fillR < cur->xmax - 1) && (cmp_color(get_pixel_color(tex, fillR, y), *seed_color)) );
586
+
587
+ /* recurse to the line above and the line below at each point */
588
+ for( i = fillL + 1; i < fillR; i++ ) {
589
+ /* Flood above */
590
+ if( ( y > 0 ) && ( cmp_color(get_pixel_color(tex, i, y - 1), *seed_color) ) ) {
591
+
592
+ glow_floodFill( i, y-1, seed_color, cur, tex, tex2 );
593
+ }
594
+ /* flood below */
595
+ if( (y < cur->ymax - 1) && (cmp_color(get_pixel_color(tex, i, y + 1), *seed_color) )) {
596
+ glow_floodFill( i, y+1, seed_color, cur, tex, tex2 );
597
+ }
598
+ }
599
+ }
600
+
601
+ void
602
+ glow_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg,
603
+ sync sync_mode, bool primary, action_struct * payload)
604
+ {
605
+ action_struct cur;
606
+ rgba seed_color;
607
+ texture_info fill_image;
608
+ texture_info * fill_image_ptr = NULL;
609
+
610
+ if(!bound_by_rect(x, y, 0, 0, tex->width, tex->height)) return;
611
+
612
+ draw_prologue(&cur, tex, 0, 0, 1024, 1024, &hash_arg, sync_mode, primary, &payload);
613
+
614
+ /* fill hates alpha_blend so let's turn it off */
615
+ payload->pen.alpha_blend = false;
616
+
617
+ if(is_a_hash(hash_arg)) {
618
+ VALUE try_image = get_from_hash(hash_arg, "texture");
619
+ if(is_gosu_image(try_image)) {
620
+ get_texture_info(try_image, &fill_image);
621
+ fill_image_ptr = &fill_image;
622
+ }
623
+ }
624
+
625
+ seed_color = get_pixel_color(tex, x, y);
626
+
627
+ /* last argument is pointer to texture fill data (if it exists) or NULL (if it doesn't) */
628
+ glow_floodFill( x, y, &seed_color, &cur, tex, fill_image_ptr );
629
+
630
+ draw_epilogue(&cur, tex, primary);
631
+ }
632
+ /** end of glow fill **/
633
+
634
+ /** scanfill algorithm **/
635
+ /* the stack */
636
+ #define stackSize 16777218
637
+ int stack[stackSize];
638
+ int stackPointer;
639
+
640
+ static bool
641
+ pop(int * x, int * y, int h)
642
+ {
643
+ if(stackPointer > 0)
644
+ {
645
+ int p = stack[stackPointer];
646
+ *x = p / h;
647
+ *y = p % h;
648
+ stackPointer--;
649
+ return true;
650
+ }
651
+ else
652
+ {
653
+ return false;
654
+ }
655
+ }
656
+
657
+ static bool
658
+ push(int x, int y, int h)
659
+ {
660
+ if(stackPointer < stackSize - 1)
661
+ {
662
+ stackPointer++;
663
+ stack[stackPointer] = h * x + y;
664
+ return true;
665
+ }
666
+ else
667
+ {
668
+ return false;
669
+ }
670
+ }
671
+
672
+ static void
673
+ emptyStack()
674
+ {
675
+ int x, y;
676
+ while(pop(&x, &y, 0));
677
+ }
678
+
679
+ void
680
+ scan_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg,
681
+ sync sync_mode, bool primary, action_struct * payload)
682
+ {
683
+ action_struct cur;
684
+ rgba old_color;
685
+ int y1;
686
+ bool spanLeft, spanRight;
687
+
688
+ if(!bound_by_rect(x, y, 0, 0, tex->width - 1, tex->height - 1)) return;
689
+
690
+ /* NB: using XMAX_OOB etc since we dont know the drawing area yet; drawing area will be set by
691
+ update_bounds() function in main loop */
692
+ draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload);
693
+
694
+ /* fill hates alpha_blend so let's turn it off */
695
+ payload->pen.alpha_blend = false;
696
+
697
+ old_color = get_pixel_color(tex, x, y);
698
+
699
+ if(cmp_color(old_color, cur.color)) return;
700
+
701
+ emptyStack();
702
+
703
+ if(!push(x, y, tex->width - 1)) return;
704
+
705
+ while(pop(&x, &y, tex->width - 1))
706
+ {
707
+ y1 = y;
708
+ while(y1 >= 0 && cmp_color(old_color, get_pixel_color(tex, x, y1))) y1--;
709
+ y1++;
710
+ spanLeft = spanRight = false;
711
+ while(y1 < tex->height && cmp_color(old_color, get_pixel_color(tex, x, y1)) )
712
+ {
713
+ set_pixel_color_with_style(payload, tex, x, y1);
714
+
715
+ /* update the drawing rectangle */
716
+ update_bounds(payload, x, y1, x, y1);
717
+
718
+ if(!spanLeft && x > 0 && cmp_color(old_color, get_pixel_color(tex, x - 1, y1)))
719
+ {
720
+ if(!push(x - 1, y1, tex->width - 1)) return;
721
+ spanLeft = true;
722
+ }
723
+
724
+ else if(spanLeft && !cmp_color(old_color, get_pixel_color(tex, x - 1, y1)))
725
+ {
726
+ spanLeft = false;
727
+ }
728
+
729
+ if(!spanRight && x < tex->width - 1 && cmp_color(old_color,
730
+ get_pixel_color(tex, x + 1, y1)))
731
+ {
732
+ if(!push(x + 1, y1, tex->width - 1)) return;
733
+ spanRight = true;
734
+ }
735
+
736
+ else if(spanRight && x < tex->width - 1 && !cmp_color(old_color,get_pixel_color(tex, x + 1, y1)))
737
+ {
738
+ spanRight = false;
739
+ }
740
+ y1++;
741
+ }
742
+ }
743
+ draw_epilogue(&cur, tex, primary);
744
+ }
745
+ /** end of scanfill **/
746
+
747
+ /** bezier curve algorithm **/
748
+ static void
749
+ bezier_point(VALUE points, float u, float * x, float * y, int n, int format,
750
+ int draw_offset_x, int draw_offset_y)
751
+ {
752
+ int xy_index;
753
+ double sumx = 0, sumy = 0;
754
+
755
+
756
+ for(int k = 0; k < n; k++) {
757
+ switch(format) {
758
+ case POINT_FORMAT:
759
+
760
+ sumx += NUM2DBL(point_x(get_from_array(points, k))) * bernstein(n - 1, k, u);
761
+ sumy += NUM2DBL(point_y(get_from_array(points, k))) * bernstein(n - 1, k, u);
762
+ break;
763
+ case SIMPLE_FORMAT:
764
+
765
+ xy_index = k * 2;
766
+ sumx += NUM2DBL(get_from_array(points, xy_index)) * bernstein(n - 1, k, u);
767
+ sumy += NUM2DBL(get_from_array(points, xy_index + 1)) * bernstein(n - 1, k, u);
768
+ break;
769
+ default:
770
+ rb_raise(rb_eArgError, "pixel format must be either POINT_FORMAT or SIMPLE_FORMAT");
771
+ }
772
+ }
773
+
774
+ *x = sumx + draw_offset_x;
775
+ *y = sumy + draw_offset_y;
776
+ }
777
+
778
+ void
779
+ bezier_do_action(VALUE points, texture_info * tex, VALUE hash_arg, sync sync_mode,
780
+ bool primary, action_struct * payload)
781
+ {
782
+ float u = 0.0;
783
+ action_struct cur;
784
+ float x1, y1, x2, y2;
785
+ int first_x, first_y;
786
+ int format;
787
+ int num_point_pairs;
788
+ bool closed = false;
789
+ VALUE offset_val;
790
+ int draw_offset_x, draw_offset_y;
791
+
792
+ /* defaults to 200 (1 / 0.005) samples per curve */
793
+ float step_size = 0.005;
794
+
795
+ draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload);
796
+
797
+ /* calculate offset */
798
+ offset_val = get_image_local(tex->image, DRAW_OFFSET);
799
+
800
+ draw_offset_x = NUM2INT(get_from_array(offset_val, 0));
801
+ draw_offset_y = NUM2INT(get_from_array(offset_val, 1));
802
+
803
+ if(is_a_hash(hash_arg)) {
804
+
805
+ /* if the polyline is 'closed' make the last point the first */
806
+ if(RTEST(get_from_hash(hash_arg, "closed")) || RTEST(get_from_hash(hash_arg, "close"))) {
807
+
808
+ /* so that our additional point is not persistent */
809
+ points = rb_obj_dup(points);
810
+ closed = true;
811
+ }
812
+
813
+ /* number of points to sample */
814
+ if(RTEST(get_from_hash(hash_arg, "sample_size"))) {
815
+ VALUE c = get_from_hash(hash_arg, "sample_size");
816
+ Check_Type(c, T_FIXNUM);
817
+ step_size = 1.0 / (float)FIX2INT(c);
818
+ }
819
+ }
820
+
821
+ if(is_a_point(get_from_array(points, 0))) {
822
+ format = POINT_FORMAT;
823
+
824
+ if(closed)
825
+ rb_ary_push(points, get_from_array(points, 0));
826
+
827
+ num_point_pairs = RARRAY_LEN(points);
828
+ }
829
+ else {
830
+ format = SIMPLE_FORMAT;
831
+
832
+ /* ensure points are given in pairs */
833
+ if(RARRAY_LEN(points) % 2)
834
+ rb_raise(rb_eArgError, "bezier needs an even number of points. got %d\n", (int)RARRAY_LEN(points));
835
+
836
+ if(closed) {
837
+ rb_ary_push(points, get_from_array(points, 0));
838
+ rb_ary_push(points, get_from_array(points, 1));
839
+ }
840
+
841
+ num_point_pairs = RARRAY_LEN(points) / 2;
842
+ }
843
+
844
+ if(num_point_pairs > 17)
845
+ rb_raise(rb_eArgError, "too many points for bezier curve. 17 points is current maximum. got %d\n",
846
+ num_point_pairs);
847
+
848
+ /* get the first point */
849
+ bezier_point(points, 0, &x1, &y1, num_point_pairs, format, draw_offset_x, draw_offset_y);
850
+
851
+ /* save it so we can link up with last point properly if the curve is 'closed' */
852
+ first_x = x1;
853
+ first_y = y1;
854
+
855
+ while(u <= 1) {
856
+ bezier_point(points, u, &x2, &y2, num_point_pairs, format, draw_offset_x, draw_offset_y);
857
+
858
+ line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload);
859
+
860
+ /* update drawing rectangle */
861
+ update_bounds(payload, x1, y1, x2, y2);
862
+
863
+ x1 = x2;
864
+ y1 = y2;
865
+
866
+ u += step_size;
867
+ }
868
+
869
+ /* sometimes beziers dont close properly, so we'll ensure it's closed */
870
+ if(closed)
871
+ line_do_action(x2, y2, first_x, first_y, tex, hash_arg, no_sync, false, payload);
872
+
873
+ draw_epilogue(&cur, tex, primary);
874
+ }
875
+ /** end of bezier **/
876
+
877
+ /** each_pixel **/
878
+ static void
879
+ set_color_array(VALUE ary, rgba * color)
880
+ {
881
+ set_array_value(ary, 0, rb_float_new(color->red));
882
+ set_array_value(ary, 1, rb_float_new(color->green));
883
+ set_array_value(ary, 2, rb_float_new(color->blue));
884
+ set_array_value(ary, 3, rb_float_new(color->alpha));
885
+ }
886
+
887
+ void
888
+ each_pixel_do_action(int x1, int y1, int x2, int y2, VALUE proc, texture_info * tex, VALUE hash_arg,
889
+ sync sync_mode, bool primary, action_struct * payload)
890
+ {
891
+ action_struct cur;
892
+ int arity;
893
+ VALUE rb_pix = rb_ary_new2(4);
894
+
895
+ draw_prologue(&cur, tex, x1, y1, x2, y2, &hash_arg, sync_mode, primary, &payload);
896
+
897
+ arity = FIX2INT(rb_funcall(proc, rb_intern("arity"), 0));
898
+
899
+ for(int y = y1; y < y2 + 1; y++)
900
+ for(int x = x1; x < x2 + 1; x++) {
901
+ rgba pix = get_pixel_color(tex, x, y);
902
+
903
+ set_color_array(rb_pix, &pix);
904
+
905
+ /* invoke the block */
906
+ switch(arity) {
907
+ case 1:
908
+ rb_funcall(proc, rb_intern("call"), 1, rb_pix);
909
+ break;
910
+ case 3:
911
+ rb_funcall(proc, rb_intern("call"), 3, rb_pix, INT2FIX(x), INT2FIX(y));
912
+ break;
913
+ default:
914
+ rb_raise(rb_eArgError, "permissible arities for each are 1 & 3. Got %d\n",
915
+ arity);
916
+
917
+ }
918
+
919
+ pix = convert_rb_color_to_rgba(rb_pix);
920
+
921
+ set_pixel_color(&pix, tex, x, y);
922
+ }
923
+
924
+ draw_epilogue(&cur, tex, primary);
925
+ }
926
+ /** end each_pixel iterator **/
927
+
928
+ /** splice algorithm **/
929
+ void
930
+ splice_do_action(int x0, int y0, int cx1, int cy1, int cx2, int cy2, texture_info * splice_tex,
931
+ texture_info * tex, VALUE hash_arg, sync sync_mode,
932
+ bool primary, action_struct * payload)
933
+ {
934
+ action_struct cur;
935
+ int xbound;
936
+ int ybound;
937
+ rgba chromakey;
938
+ float * image_buf = NULL;
939
+ bool inverse_chroma = false;
940
+ bool same_image = false;
941
+ bool has_chroma = false;
942
+
943
+ constrain_boundaries(&cx1, &cy1, &cx2, &cy2, splice_tex->width, splice_tex->height);
944
+ xbound = cx2 - cx1 + 1;
945
+ ybound = cy2 - cy1 + 1;
946
+
947
+ draw_prologue(&cur, tex, x0, y0,
948
+ x0 + xbound, y0 + ybound, &hash_arg, sync_mode, primary, &payload);
949
+
950
+
951
+ if(has_optional_hash_arg(hash_arg, "chroma_key")) {
952
+ VALUE c = get_from_hash(hash_arg, "chroma_key");
953
+ chromakey = convert_rb_color_to_rgba(c);
954
+ has_chroma = true;
955
+ }
956
+ else if(has_optional_hash_arg(hash_arg, "chroma_key_not")) {
957
+ chromakey = convert_rb_color_to_rgba(get_from_hash(hash_arg, "chroma_key_not"));
958
+ inverse_chroma = true;
959
+ has_chroma = true;
960
+ }
961
+
962
+ if(splice_tex->image == tex->image)
963
+ same_image = true;
964
+
965
+ /* NB: we do not use this in the general case since it's almost 1.5 times as slow.
966
+ It is necessary for splicing from/to the same region of pixels though.
967
+ */
968
+ if(same_image)
969
+ image_buf = get_image_chunk(splice_tex, cx1, cy1, cx2, cy2);
970
+
971
+ for(int y = 0; y < ybound; y++)
972
+ for(int x = 0; x < xbound; x++) {
973
+
974
+ if(!same_image)
975
+ payload->color = get_pixel_color(splice_tex, cx1 + x, cy1 + y);
976
+ else
977
+ payload->color = get_pixel_color_from_chunk(image_buf, xbound, ybound, x, y);
978
+
979
+ if(has_chroma) {
980
+ bool chroma_match = cmp_color(payload->color, chromakey);
981
+
982
+ /* look at released 0.2.0 code to see how USED to do this.
983
+ this is now a simplified boolean expression (XOR) */
984
+ if(chroma_match == inverse_chroma)
985
+ set_pixel_color_with_style(payload, tex, x0 + x, y0 + y);
986
+ }
987
+ else
988
+ set_pixel_color_with_style(payload, tex, x0 + x, y0 + y);
989
+ }
990
+
991
+ if(same_image)
992
+ free(image_buf);
993
+
994
+ draw_epilogue(&cur, tex, primary);
995
+ }
996
+ /** end splice **/
997
+
998
+
999
+