texplay 0.4.3 → 0.4.4.pre

Sign up to get free protection for your applications and to get access to all the features.
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
+