texplay 0.3.5 → 0.4

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