texplay 0.4.3 → 0.4.4.pre

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