texplay 0.4.3-x86-mingw32

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