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,46 @@
1
+ #ifndef GUARD_BINDINGS_H
2
+ #define GUARD_BINDINGS_H
3
+
4
+ /* class methods */
5
+ VALUE M_create_macro(VALUE self , VALUE method_name);
6
+ VALUE M_remove_macro(VALUE self, VALUE method_name);
7
+ VALUE M_refresh_cache_all(VALUE self);
8
+ VALUE M_create_blank(VALUE self, VALUE window, VALUE width, VALUE height);
9
+
10
+ /* instance methods */
11
+ VALUE m_paint(int argc, VALUE * argv, VALUE self);
12
+ VALUE m_getpixel(int argc, VALUE * argv, VALUE self);
13
+ VALUE m_circle(int argc, VALUE * argv, VALUE self);
14
+ VALUE m_line(int argc, VALUE * argv, VALUE self);
15
+ VALUE m_rect(int argc, VALUE * argv, VALUE self);
16
+ VALUE m_pixel(int argc, VALUE * argv, VALUE self);
17
+ VALUE m_flood_fill(int argc, VALUE * argv, VALUE self);
18
+ VALUE m_bezier(int argc, VALUE * argv, VALUE self);
19
+ VALUE m_polyline(int argc, VALUE * argv, VALUE self);
20
+ VALUE m_ngon(int argc, VALUE * argv, VALUE self);
21
+ VALUE m_special_pixel(int argc, VALUE * argv, VALUE self);
22
+ VALUE m_splice(int argc, VALUE * argv, VALUE self);
23
+ VALUE m_clear(int argc, VALUE * argv, VALUE self);
24
+ VALUE m_offset(int argc, VALUE * argv, VALUE self);
25
+ VALUE m_color(int argc, VALUE * argv, VALUE self);
26
+ VALUE m_missing(int argc, VALUE * argv, VALUE self);
27
+ VALUE m_bitmask(int argc, VALUE * argv, VALUE self);
28
+ VALUE m_lshift(int argc, VALUE * argv, VALUE self);
29
+ VALUE m_rshift(int argc, VALUE * argv, VALUE self);
30
+
31
+ VALUE m_each(int argc, VALUE * argv, VALUE self);
32
+
33
+ VALUE m_quad_cached(VALUE self);
34
+ VALUE m_cache_refresh(VALUE self);
35
+
36
+ VALUE m_user_set_options(VALUE self, VALUE options);
37
+ VALUE m_user_delete_options(VALUE self);
38
+ VALUE m_get_options(VALUE self);
39
+ VALUE m_force_sync(VALUE self, VALUE ary);
40
+
41
+ VALUE m_dup_image(VALUE self);
42
+ VALUE m_clone_image(VALUE self);
43
+
44
+ VALUE m_to_blob(VALUE self);
45
+
46
+ #endif
@@ -0,0 +1,118 @@
1
+ /* cache.c */
2
+
3
+ #include <ruby.h>
4
+
5
+ #ifdef __APPLE__
6
+ #include <glut.h>
7
+ #else
8
+ #include <GL/glut.h>
9
+ #endif
10
+
11
+ #include "cache.h"
12
+ #include "texplay.h"
13
+
14
+ typedef struct {
15
+ int len;
16
+ cache_entry entry[CACHE_SIZE];
17
+ } cache_t;
18
+
19
+ /* var has internal linkage, static duration */
20
+ /* contains cache data */
21
+ static cache_t cache = {0};
22
+
23
+ /* create a new cache entry */
24
+ cache_entry*
25
+ cache_create_entry(int tname) {
26
+ float * new_array;
27
+ int sidelength, new_element = cache.len;
28
+
29
+ if(cache.len >= CACHE_SIZE) { rb_raise(rb_eRuntimeError, "cache is full! increase CACHE_SIZE"); }
30
+
31
+ /* opengl initialization code */
32
+ glEnable(GL_TEXTURE_2D);
33
+ glBindTexture(GL_TEXTURE_2D, tname);
34
+
35
+ /* get length of a side, since square texture */
36
+ glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &sidelength);
37
+
38
+ /* initialize texture data array, mult. by 4 because {rgba} */
39
+ new_array = malloc(sidelength * sidelength * 4 * sizeof(float));
40
+
41
+ /* get texture data from video memory */
42
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT,(void*)(new_array));
43
+
44
+ /* save texture data in the cache */
45
+ cache.entry[new_element].tname = tname;
46
+ cache.entry[new_element].sidelength = sidelength;
47
+ cache.entry[new_element].tdata = new_array;
48
+
49
+ /* update size of cache */
50
+ cache.len++;
51
+
52
+ glDisable(GL_TEXTURE_2D);
53
+
54
+ return &cache.entry[new_element];
55
+ }
56
+
57
+ /* return the entry if it exists, otherwise return NULL */
58
+ cache_entry*
59
+ find_in_cache(int tname) {
60
+ /* check if entry exists in cache */
61
+ int index;
62
+ for(index = 0; index < cache.len; index++)
63
+ if(cache.entry[index].tname == tname)
64
+ return &cache.entry[index];
65
+
66
+
67
+ return NULL;
68
+ }
69
+
70
+ /* if the entry doesn't exist then create it and return it.
71
+ otherwise just return it */
72
+ cache_entry*
73
+ find_or_create_cache_entry(int tname) {
74
+ cache_entry * entry;
75
+
76
+ if((entry=find_in_cache(tname)))
77
+ return entry;
78
+ else
79
+ return cache_create_entry(tname);
80
+ }
81
+
82
+ /* refresh the cache for all quads */
83
+ void
84
+ cache_refresh_all(void) {
85
+ float * tdata;
86
+ int tname, index;
87
+
88
+ /* opengl initialization code */
89
+ glEnable(GL_TEXTURE_2D);
90
+
91
+ for(index = 0; index < cache.len; index++) {
92
+ tdata = cache.entry[index].tdata;
93
+ tname = cache.entry[index].tname;
94
+
95
+ glBindTexture(GL_TEXTURE_2D, tname);
96
+
97
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, (void*)(tdata));
98
+ }
99
+
100
+ glDisable(GL_TEXTURE_2D);
101
+ }
102
+
103
+ /* refresh the cache for a specific quad */
104
+ void
105
+ cache_refresh_entry(int tname) {
106
+ cache_entry * entry;
107
+
108
+ entry = find_in_cache(tname);
109
+
110
+ /* opengl initialization code */
111
+ glEnable(GL_TEXTURE_2D);
112
+ glBindTexture(GL_TEXTURE_2D, entry->tname);
113
+
114
+ glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, (void*)(entry->tdata));
115
+
116
+ glDisable(GL_TEXTURE_2D);
117
+ }
118
+
@@ -0,0 +1,24 @@
1
+ /* cache.h */
2
+ /* see cache.c for annotations */
3
+
4
+ #ifndef GUARD_CACHE_H
5
+ #define GUARD_CACHE_H
6
+
7
+ /* defines */
8
+ #define CACHE_SIZE 10000
9
+
10
+ /* data types */
11
+ typedef struct {
12
+ int tname;
13
+ int sidelength;
14
+ float * tdata;
15
+ } cache_entry;
16
+
17
+ /* functions */
18
+ void cache_refresh_all(void);
19
+ void cache_refresh_entry(int tname);
20
+ cache_entry * cache_create_entry(int texture_name);
21
+ cache_entry * find_in_cache(int texture_name);
22
+ cache_entry * find_or_create_cache_entry(int texture_name);
23
+
24
+ #endif
@@ -0,0 +1,27 @@
1
+ /* contains basic macros to facilitate ruby 1.8 and ruby 1.9 compatibility */
2
+
3
+ #ifndef GUARD_COMPAT_H
4
+ #define GUARD_COMPAT_H
5
+
6
+ #include <ruby.h>
7
+
8
+ /* this is the test we use to identify ruby 1.9.1 */
9
+
10
+ /* commenting out this test because it doesn't seem to work in 1.8.7 */
11
+ /*
12
+ #ifdef RCLASS_M_TBL
13
+ # define RUBY_19
14
+ #endif
15
+ */
16
+
17
+ /* macros for backwards compatibility with 1.8 */
18
+ #ifndef RUBY_19
19
+ # define RCLASS_M_TBL(c) (RCLASS(c)->m_tbl)
20
+ # define RCLASS_SUPER(c) (RCLASS(c)->super)
21
+ # define RCLASS_IV_TBL(c) (RCLASS(c)->iv_tbl)
22
+ #endif
23
+
24
+ /* a useful macro. cannot use ordinary CLASS_OF as it does not return an lvalue */
25
+ #define KLASS_OF(c) (RBASIC(c)->klass)
26
+
27
+ #endif
@@ -0,0 +1,38 @@
1
+ require 'mkmf'
2
+
3
+ name = 'texplay/texplay'
4
+
5
+ dir_config name
6
+
7
+
8
+ # linux
9
+ if RUBY_PLATFORM =~ /linux/
10
+ exit unless have_library("glut")
11
+ exit unless have_library("GL")
12
+
13
+ # macosx
14
+ elsif RUBY_PLATFORM =~ /darwin/
15
+ $LDFLAGS += " -framework GLUT"
16
+ $CFLAGS += " -I/System/Library/Frameworks/GLUT.framework/Headers"
17
+
18
+ # windows
19
+ else
20
+ freeglut_path = File.expand_path "vendor/freeglut", File.dirname(__FILE__)
21
+
22
+ $CFLAGS << " -I#{File.join freeglut_path, "include"}"
23
+
24
+ exit unless find_library "freeglut_static", 'main', File.join(freeglut_path, "lib")
25
+ exit unless have_library "opengl32"
26
+
27
+ end
28
+
29
+ # Stop getting annoying warnings for valid C99 code.
30
+ $warnflags.gsub!('-Wdeclaration-after-statement', '') if $warnflags
31
+
32
+ # 1.9 compatibility
33
+ $CFLAGS += " -DRUBY_19" if RUBY_VERSION =~ /^1\.9/
34
+
35
+ # let's use c99
36
+ $CFLAGS += " -std=c99"
37
+
38
+ create_makefile name
@@ -0,0 +1,1313 @@
1
+ #include "texplay.h"
2
+ #include "utils.h"
3
+ #include "graphics_utils.h"
4
+ #include "cache.h"
5
+
6
+ #ifdef __APPLE__
7
+ #include <glut.h>
8
+ #else
9
+ #include <GL/glut.h>
10
+ #endif
11
+
12
+
13
+ /* small helper functions */
14
+ static void initialize_action_struct(action_struct * cur, VALUE hash_arg, sync sync_mode);
15
+ static void process_common_hash_args(action_struct * cur, VALUE * hash_arg, sync sync_mode, bool primary);
16
+ static void prepare_drawing_mode(action_struct * cur);
17
+ static void prepare_alpha_blend(action_struct * cur);
18
+ static void prepare_fill_texture(action_struct * cur);
19
+ static void prepare_color_control(action_struct * cur);
20
+ static void prepare_color_select(action_struct * cur);
21
+ static rgba apply_lerp(action_struct * payload, texture_info * tex, int x, int y);
22
+ static rgba apply_alpha_blend(action_struct * payload, texture_info * tex, int x, int y, rgba blended_pixel);
23
+ static rgba apply_drawing_mode(action_struct * payload, texture_info * tex, int x, int y, rgba blended_pixel);
24
+
25
+ static rgba apply_color_control_transform(action_struct * payload, texture_info * tex, int x, int y);
26
+ static rgba exec_color_control_proc(action_struct * cur, texture_info * tex, int x, int y, rgba blended_pixel);
27
+ /* end helpers */
28
+
29
+
30
+ void
31
+ update_lazy_bounds(action_struct * cur, texture_info * tex)
32
+ {
33
+
34
+ /* only update global bounds if we're doing a lazy_sync */
35
+ if (cur->sync_mode != lazy_sync)
36
+ return;
37
+
38
+ VALUE lazy_bounds;
39
+ int xmin, ymin, xmax, ymax;
40
+
41
+ lazy_bounds = get_image_local(tex->image, LAZY_BOUNDS);
42
+
43
+ xmin = INT2FIX(MIN(cur->xmin, FIX2INT(get_from_array(lazy_bounds, 0))));
44
+ ymin = INT2FIX(MIN(cur->ymin, FIX2INT(get_from_array(lazy_bounds, 1))));
45
+ xmax = INT2FIX(MAX(cur->xmax, FIX2INT(get_from_array(lazy_bounds, 2))));
46
+ ymax = INT2FIX(MAX(cur->ymax, FIX2INT(get_from_array(lazy_bounds, 3))));
47
+
48
+ set_array_value(lazy_bounds, 0, xmin);
49
+ set_array_value(lazy_bounds, 1, ymin);
50
+ set_array_value(lazy_bounds, 2, xmax);
51
+ set_array_value(lazy_bounds, 3, ymax);
52
+ }
53
+
54
+ void
55
+ update_bounds(action_struct * cur, int xmin, int ymin, int xmax, int ymax)
56
+ {
57
+ if(xmin > xmax) SWAP(xmin, xmax);
58
+ if(ymin > ymax) SWAP(ymin, ymax);
59
+
60
+ cur->xmin = MIN(cur->xmin, xmin);
61
+ cur->ymin = MIN(cur->ymin, ymin);
62
+ cur->xmax = MAX(cur->xmax, xmax);
63
+ cur->ymax = MAX(cur->ymax, ymax);
64
+ }
65
+
66
+ void
67
+ set_local_bounds(action_struct * cur, int xmin, int ymin, int xmax, int ymax, texture_info * tex)
68
+ {
69
+
70
+ /* local bounds used by both eager_sync and lazy_sync: */
71
+ if(cur->sync_mode == no_sync)
72
+ return;
73
+
74
+ /* eager sync: to demarcate precise area to sync to opengl */
75
+ /* lazy sync: to update global bounds */
76
+ cur->xmin = xmin;
77
+ cur->ymin = ymin;
78
+ cur->xmax = xmax;
79
+ cur->ymax = ymax;
80
+ }
81
+
82
+ void
83
+ draw_prologue(action_struct * cur, texture_info * tex, int xmin, int ymin, int xmax, int ymax,
84
+ VALUE * hash_arg, sync sync_mode, bool primary, action_struct ** payload_ptr)
85
+ {
86
+ if(!primary) return;
87
+
88
+ /* set the payload pointer */
89
+ *payload_ptr = cur;
90
+
91
+ /* not too happy about having this here, look at texplay.h for why */
92
+ cur->tex = tex;
93
+
94
+ process_common_hash_args(cur, hash_arg, sync_mode, primary);
95
+
96
+ set_local_bounds(cur, xmin, ymin, xmax, ymax, tex);
97
+ }
98
+
99
+ void
100
+ draw_epilogue(action_struct * cur, texture_info * tex, bool primary)
101
+ {
102
+ /* only primary actions get sync'd */
103
+ if(!primary) return;
104
+
105
+ switch(cur->sync_mode) {
106
+
107
+ /* do not sync */
108
+ case no_sync:
109
+ return;
110
+ break;
111
+
112
+ /* sync immediately */
113
+ case eager_sync:
114
+ create_subtexture_and_sync_to_gl(IMAGE_BOUNDS(cur), tex);
115
+ break;
116
+
117
+ /* sync later (at end of paint block?) */
118
+ case lazy_sync:
119
+ update_lazy_bounds(cur, tex);
120
+ break;
121
+
122
+ default:
123
+ rb_raise(rb_eRuntimeError,
124
+ "sync_mode may only be: lazy_sync, eager_sync, no_sync. got %d\n", cur->sync_mode);
125
+ }
126
+ }
127
+
128
+ /* set the pixel color at (x, y) */
129
+ void
130
+ set_pixel_color(rgba * pixel_color, texture_info * tex, int x, int y)
131
+ {
132
+ float * tex_data;
133
+
134
+ /* should pixel be drawn ? */
135
+ if (x > (tex->width - 1) || x < 0 || y > (tex->height - 1) || y < 0)
136
+ return;
137
+
138
+ /* if not a color then do not draw */
139
+ if(not_a_color(*pixel_color))
140
+ return;
141
+
142
+ tex_data = get_pixel_data(tex, x, y);
143
+
144
+ /* set the pixel color */
145
+ tex_data[red] = pixel_color->red;
146
+ tex_data[green] = pixel_color->green;
147
+ tex_data[blue] = pixel_color->blue;
148
+ tex_data[alpha] = pixel_color->alpha;
149
+ }
150
+
151
+ /* utility func to manage both kinds of color comparions */
152
+ static bool
153
+ cmp_color_with_or_without_tolerance(rgba c1, rgba c2, action_struct * payload)
154
+ {
155
+ return payload->pen.has_tolerance ? cmp_color_with_tolerance(c1, c2, payload->pen.tolerance) : cmp_color(c1, c2);
156
+ }
157
+
158
+ static bool
159
+ skip_pixel(rgba source_color, action_struct * payload, texture_info * tex, int x, int y)
160
+ {
161
+
162
+ if (!payload->pen.has_color_select) return false;
163
+
164
+ rgba dest_color = get_pixel_color(tex, x, y);
165
+ bool color_match = false;
166
+
167
+ if (payload->pen.source_select.size > 0) {
168
+ for (int i = 0; i < payload->pen.source_select.size; i++) {
169
+ if (cmp_color_with_or_without_tolerance(source_color, payload->pen.source_select.colors[i], payload)) {
170
+ color_match = true;
171
+ break;
172
+ }
173
+ if (!color_match) return true;
174
+ }
175
+ }
176
+
177
+ if (payload->pen.source_ignore.size > 0) {
178
+ for (int i = 0; i < payload->pen.source_ignore.size; i++) {
179
+ if (cmp_color_with_or_without_tolerance(source_color, payload->pen.source_ignore.colors[i], payload))
180
+ return true;
181
+ }
182
+ }
183
+
184
+ color_match = false;
185
+ if (payload->pen.dest_select.size > 0) {
186
+ for (int i = 0; i < payload->pen.dest_select.size; i++) {
187
+ if (cmp_color_with_or_without_tolerance(dest_color, payload->pen.dest_select.colors[i], payload)) {
188
+ color_match = true;
189
+ break;
190
+ }
191
+ }
192
+ if (!color_match) return true;
193
+ }
194
+
195
+ if (payload->pen.dest_ignore.size > 0) {
196
+ for (int i = 0; i < payload->pen.dest_ignore.size; i++) {
197
+ if (cmp_color_with_or_without_tolerance(dest_color, payload->pen.dest_ignore.colors[i], payload))
198
+ return true;
199
+ }
200
+ }
201
+
202
+ return false;
203
+ }
204
+
205
+
206
+ void
207
+ set_pixel_color_with_style(action_struct * payload, texture_info * tex, int x, int y)
208
+ {
209
+
210
+ rgba blended_pixel;
211
+
212
+ blended_pixel = payload->color;
213
+
214
+
215
+ /* for linear interpolation */
216
+ if(payload->pen.has_lerp) {
217
+ blended_pixel = apply_lerp(payload, tex, x, y);
218
+ }
219
+
220
+ /* for color_control transform */
221
+ if(payload->pen.has_color_control_transform)
222
+ blended_pixel = apply_color_control_transform(payload, tex, x, y);
223
+
224
+ /* for texture fill */
225
+ if(payload->pen.has_source_texture)
226
+ blended_pixel = get_pixel_color(&payload->pen.source_tex,
227
+ x % payload->pen.source_tex.width,
228
+ y % payload->pen.source_tex.height);
229
+
230
+ /* for color_control proc */
231
+ if(payload->pen.has_color_control_proc)
232
+ blended_pixel = exec_color_control_proc(payload, tex, x, y, blended_pixel);
233
+
234
+ /* should skip this pixel? color selection */
235
+ if (skip_pixel(blended_pixel, payload, tex, x, y))
236
+ return;
237
+
238
+ /* drawing modes */
239
+ if(payload->pen.has_drawing_mode) {
240
+ blended_pixel = apply_drawing_mode(payload, tex, x, y, blended_pixel);
241
+ }
242
+
243
+ /* TO DO: refactor into its own helper function
244
+ & rewrite using sse2 */
245
+ if(payload->pen.alpha_blend)
246
+ blended_pixel = apply_alpha_blend(payload, tex, x, y, blended_pixel);
247
+
248
+
249
+ set_pixel_color(&blended_pixel, tex, x, y);
250
+ }
251
+
252
+
253
+ rgba
254
+ get_pixel_color_from_chunk(float * chunk, int width, int height, int x, int y)
255
+ {
256
+ rgba my_color;
257
+
258
+ int offset;
259
+
260
+ if (x > (width - 1) || x < 0 || y > (height - 1) || y < 0)
261
+ return not_a_color_v;
262
+
263
+ offset = 4 * (x + y * width);
264
+
265
+ my_color.red = chunk[offset + red];
266
+ my_color.green = chunk[offset + green];
267
+ my_color.blue = chunk[offset + blue];
268
+ my_color.alpha = chunk[offset + alpha];
269
+
270
+ return my_color;
271
+ }
272
+
273
+
274
+ rgba
275
+ get_pixel_color(texture_info * tex, int x, int y)
276
+ {
277
+ rgba my_color;
278
+
279
+ int offset;
280
+
281
+ /* if pixel location is out of range return not_a_color_v */
282
+ if (x > (tex->width - 1) || x < 0 || y > (tex->height - 1) || y < 0)
283
+ return not_a_color_v;
284
+
285
+ offset = calc_pixel_offset(tex, x, y);
286
+
287
+ my_color.red = tex->td_array[offset + red];
288
+ my_color.green = tex->td_array[offset + green];
289
+ my_color.blue = tex->td_array[offset + blue];
290
+ my_color.alpha = tex->td_array[offset + alpha];
291
+
292
+ return my_color;
293
+ }
294
+
295
+ /* return the array where pixel data is stored */
296
+ float*
297
+ get_pixel_data(texture_info * tex, int x, int y)
298
+ {
299
+ int offset = calc_pixel_offset(tex, x, y);
300
+
301
+ return &tex->td_array[offset];
302
+ }
303
+
304
+ /* if 2nd param is Qnil, then create a blank image with 'width' and 'height
305
+ otherwise, try to use the 2nd param (dup) to create a duplicate image
306
+ */
307
+ VALUE
308
+ create_image(VALUE window, int width, int height)
309
+ {
310
+ static VALUE empty_image_stub = 0;
311
+ static VALUE image = 0;
312
+
313
+ if (empty_image_stub == 0) {
314
+ VALUE gosu = rb_const_get(rb_cObject, rb_intern("Gosu"));
315
+ VALUE tp = rb_const_get(rb_cObject, rb_intern("TexPlay"));
316
+ empty_image_stub = rb_const_get(tp, rb_intern("EmptyImageStub"));
317
+ image = rb_const_get(gosu, rb_intern("Image"));
318
+ }
319
+
320
+ VALUE rmagick_img;
321
+ VALUE new_image;
322
+ VALUE options = rb_hash_new();
323
+
324
+ set_hash_value(options, "caching", Qfalse);
325
+
326
+ rmagick_img = rb_funcall(empty_image_stub, rb_intern("new"), 2, INT2FIX(width), INT2FIX(height));
327
+
328
+ new_image = rb_funcall(image, rb_intern("new"), 3, window, rmagick_img, options);
329
+
330
+ return new_image;
331
+ }
332
+
333
+
334
+
335
+ /* static functions */
336
+ static void
337
+ initialize_action_struct(action_struct * cur, VALUE hash_arg, sync sync_mode)
338
+ {
339
+ /* initialize action-struct to default values */
340
+ cur->sync_mode = sync_mode;
341
+ cur->hash_arg = hash_arg;
342
+
343
+ cur->color = convert_image_local_color_to_rgba(cur->tex->image);
344
+ cur->pen.has_color_control_proc = false;
345
+ cur->pen.has_color_control_transform = false;
346
+ cur->pen.has_source_texture = false;
347
+
348
+ /* alpha blending */
349
+ cur->pen.alpha_blend = false;
350
+
351
+ /* set static color control transformations to defaults */
352
+ cur->pen.color_mult.red = 1.0;
353
+ cur->pen.color_mult.green = 1.0;
354
+ cur->pen.color_mult.blue = 1.0;
355
+ cur->pen.color_mult.alpha = 1.0;
356
+
357
+ cur->pen.color_add.red = 0.0;
358
+ cur->pen.color_add.green = 0.0;
359
+ cur->pen.color_add.blue = 0.0;
360
+ cur->pen.color_add.alpha = 0.0;
361
+
362
+ /* lerp is off by default */
363
+ cur->pen.has_lerp = false;
364
+
365
+ /* drawing mode is off by deafult */
366
+ cur->pen.has_drawing_mode = false;
367
+
368
+ /* color selection */
369
+ cur->pen.has_color_select = false;
370
+ cur->pen.source_select.size = 0;
371
+ cur->pen.source_ignore.size = 0;
372
+ cur->pen.dest_select.size = 0;
373
+ cur->pen.dest_ignore.size = 0;
374
+
375
+ /* tolerance */
376
+ cur->pen.has_tolerance = false;
377
+ cur->pen.tolerance = 0.0;
378
+ }
379
+
380
+ /* TODO: fix this function below, it's too ugly and bulky and weird **/
381
+ static void
382
+ process_common_hash_args(action_struct * cur, VALUE * hash_arg, sync sync_mode, bool primary)
383
+ {
384
+
385
+ VALUE user_defaults;
386
+ VALUE hash_blend;
387
+
388
+
389
+ /* if a hash doesn't exist then create one */
390
+ if(!is_a_hash(*hash_arg))
391
+ *hash_arg = rb_hash_new();
392
+
393
+ /* init the action to default values */
394
+ initialize_action_struct(cur, *hash_arg, sync_mode);
395
+
396
+ /* get the user default options & merge with given options */
397
+ user_defaults = get_image_local(cur->tex->image, USER_DEFAULTS);
398
+ hash_blend = rb_funcall(user_defaults, rb_intern("merge"), 1, *hash_arg);
399
+ rb_funcall(*hash_arg, rb_intern("merge!"), 1, hash_blend);
400
+
401
+ if(has_optional_hash_arg(*hash_arg, "color")) {
402
+ VALUE c = get_from_hash(*hash_arg, "color");
403
+ cur->color = convert_rb_color_to_rgba(c);
404
+ if(c == string2sym("random")) {
405
+ set_hash_value(*hash_arg, "color", convert_rgba_to_rb_color(&cur->color));
406
+ }
407
+ }
408
+
409
+ /* shadows */
410
+ if(RTEST(get_from_hash(*hash_arg, "shadow"))) {
411
+ cur->pen.color_mult.red = 0.66;
412
+ cur->pen.color_mult.green = 0.66;
413
+ cur->pen.color_mult.blue = 0.66;
414
+ cur->pen.color_mult.alpha = 1;
415
+
416
+ cur->pen.has_color_control_transform = true;
417
+ }
418
+
419
+ /* tolerance */
420
+ if(RTEST(get_from_hash(*hash_arg, "tolerance"))) {
421
+ cur->pen.tolerance = NUM2DBL(get_from_hash(*hash_arg, "tolerance"));
422
+
423
+ /* maximum length of hypotonese extended in 4-space (color space) is sqrt(4) */
424
+ if (cur->pen.tolerance >= 2)
425
+ cur->pen.tolerance = 2;
426
+
427
+ if (cur->pen.tolerance < 0)
428
+ cur->pen.tolerance = 0;
429
+
430
+ cur->pen.has_tolerance = true;
431
+ }
432
+
433
+ /* lerp */
434
+ if(RTEST(get_from_hash(*hash_arg, "lerp"))) {
435
+ cur->pen.lerp = NUM2DBL(get_from_hash(*hash_arg, "lerp"));
436
+
437
+ /* bounds */
438
+ if(cur->pen.lerp > 1.0) cur->pen.lerp = 1.0;
439
+ if(cur->pen.lerp < 0.0) cur->pen.lerp = 0.0;
440
+ cur->pen.has_lerp = true;
441
+ }
442
+
443
+ /* sync mode */
444
+ if(has_optional_hash_arg(*hash_arg, "sync_mode")) {
445
+ VALUE user_sync_mode = get_from_hash(*hash_arg, "sync_mode");
446
+
447
+ Check_Type(user_sync_mode, T_SYMBOL);
448
+
449
+ if(user_sync_mode == string2sym("lazy_sync"))
450
+ cur->sync_mode = lazy_sync;
451
+ else if(user_sync_mode == string2sym("eager_sync"))
452
+ cur->sync_mode = eager_sync;
453
+ else if(user_sync_mode == string2sym("no_sync"))
454
+ cur->sync_mode = no_sync;
455
+ else
456
+ rb_raise(rb_eArgError, "unrecognized sync mode: %s\n. Allowable modes are "
457
+ ":lazy_sync, :eager_sync, :no_sync.",
458
+ sym2string(user_sync_mode));
459
+
460
+ delete_from_hash(*hash_arg, "sync_mode");
461
+
462
+ }
463
+
464
+ /* prepare color selection */
465
+ prepare_color_select(cur);
466
+
467
+ /* process drawing mode */
468
+ prepare_drawing_mode(cur);
469
+
470
+ /* process the color_control block or transform (if there is one) */
471
+ prepare_color_control(cur);
472
+
473
+ /* process the filling texture (if there is one) */
474
+ prepare_fill_texture(cur);
475
+
476
+ /* does the user want to blend alpha values ? */
477
+ prepare_alpha_blend(cur);
478
+ }
479
+
480
+ static void
481
+ prepare_alpha_blend(action_struct * cur)
482
+ {
483
+ if(has_optional_hash_arg(cur->hash_arg, "alpha_blend")) {
484
+
485
+ VALUE blend_mode = get_from_hash(cur->hash_arg, "alpha_blend");
486
+
487
+ /* true is equivalent to default blend mode, 'source' */
488
+ if(blend_mode == Qtrue)
489
+ blend_mode = string2sym("source");
490
+
491
+ /* where false or nil is passed */
492
+ if(!RTEST(blend_mode)) {
493
+ cur->pen.alpha_blend = false;
494
+ return;
495
+ }
496
+
497
+ cur->pen.alpha_blend = true;
498
+
499
+ Check_Type(blend_mode, T_SYMBOL);
500
+
501
+ if(blend_mode == string2sym("source")) {
502
+ cur->pen.alpha_blend_mode = source;
503
+ }
504
+ else if(blend_mode == string2sym("dest")) {
505
+ cur->pen.alpha_blend_mode = dest;
506
+ }
507
+ else if(blend_mode == string2sym("source_with_fixed_alpha")) {
508
+ cur->pen.alpha_blend_mode = source_with_fixed_alpha;
509
+ }
510
+ else if(blend_mode == string2sym("dest_with_fixed_alpha")) {
511
+ cur->pen.alpha_blend_mode = dest_with_fixed_alpha;
512
+ }
513
+ else
514
+ rb_raise(rb_eArgError, "unrecognized blend mode: %s\n.",
515
+ sym2string(blend_mode));
516
+
517
+ }
518
+
519
+
520
+ }
521
+
522
+ static void
523
+ prepare_drawing_mode(action_struct * cur)
524
+ {
525
+ if(is_a_hash(cur->hash_arg)) {
526
+ /* drawing mode */
527
+ if(has_optional_hash_arg(cur->hash_arg, "mode")) {
528
+ cur->pen.has_drawing_mode = true;
529
+
530
+ VALUE draw_mode = get_from_hash(cur->hash_arg, "mode");
531
+
532
+
533
+ Check_Type(draw_mode, T_SYMBOL);
534
+
535
+ if(draw_mode == string2sym("default")) {
536
+ cur->pen.has_drawing_mode = false;
537
+
538
+ return;
539
+ }
540
+ else if(draw_mode == string2sym("clear"))
541
+ cur->pen.drawing_mode = clear;
542
+
543
+ else if(draw_mode == string2sym("copy"))
544
+ cur->pen.drawing_mode = copy;
545
+
546
+ else if(draw_mode == string2sym("noop"))
547
+ cur->pen.drawing_mode = noop;
548
+
549
+ else if(draw_mode == string2sym("set"))
550
+ cur->pen.drawing_mode = set;
551
+
552
+ else if(draw_mode == string2sym("copy_inverted"))
553
+ cur->pen.drawing_mode = copy_inverted;
554
+
555
+ else if(draw_mode == string2sym("invert"))
556
+ cur->pen.drawing_mode = invert;
557
+
558
+ else if(draw_mode == string2sym("and_reverse"))
559
+ cur->pen.drawing_mode = and_reverse;
560
+
561
+ else if(draw_mode == string2sym("and"))
562
+ cur->pen.drawing_mode = and;
563
+
564
+ else if(draw_mode == string2sym("or"))
565
+ cur->pen.drawing_mode = or;
566
+
567
+ else if(draw_mode == string2sym("nand"))
568
+ cur->pen.drawing_mode = nand;
569
+
570
+ else if(draw_mode == string2sym("nor"))
571
+ cur->pen.drawing_mode = nor;
572
+
573
+ else if(draw_mode == string2sym("xor"))
574
+ cur->pen.drawing_mode = xor;
575
+
576
+ else if(draw_mode == string2sym("equiv"))
577
+ cur->pen.drawing_mode = equiv;
578
+
579
+ else if(draw_mode == string2sym("and_inverted"))
580
+ cur->pen.drawing_mode = and_inverted;
581
+
582
+ else if(draw_mode == string2sym("or_inverted"))
583
+ cur->pen.drawing_mode = or_inverted;
584
+
585
+ else if(draw_mode == string2sym("additive"))
586
+ cur->pen.drawing_mode = additive;
587
+ else if(draw_mode == string2sym("multiply"))
588
+ cur->pen.drawing_mode = multiply;
589
+ else if(draw_mode == string2sym("screen"))
590
+ cur->pen.drawing_mode = screen;
591
+ else if(draw_mode == string2sym("overlay"))
592
+ cur->pen.drawing_mode = overlay;
593
+ else if(draw_mode == string2sym("darken"))
594
+ cur->pen.drawing_mode = darken;
595
+ else if(draw_mode == string2sym("lighten"))
596
+ cur->pen.drawing_mode = lighten;
597
+ else if(draw_mode == string2sym("color_dodge"))
598
+ cur->pen.drawing_mode = color_dodge;
599
+ else if(draw_mode == string2sym("color_burn"))
600
+ cur->pen.drawing_mode = color_burn;
601
+ else if(draw_mode == string2sym("hard_light"))
602
+ cur->pen.drawing_mode = hard_light;
603
+ else if(draw_mode == string2sym("soft_light"))
604
+ cur->pen.drawing_mode = soft_light;
605
+ else if(draw_mode == string2sym("difference"))
606
+ cur->pen.drawing_mode = difference;
607
+ else if(draw_mode == string2sym("exclusion"))
608
+ cur->pen.drawing_mode = exclusion;
609
+ else
610
+ rb_raise(rb_eArgError, "unrecognized drawing mode: %s\n.",
611
+ sym2string(draw_mode));
612
+ }
613
+ }
614
+ }
615
+
616
+
617
+ /* set action color to return value of color_control proc */
618
+ static void
619
+ prepare_color_control(action_struct * cur)
620
+ {
621
+
622
+ if(is_a_hash(cur->hash_arg)) {
623
+ VALUE try_val = get_from_hash(cur->hash_arg, "color_control");
624
+
625
+ if(rb_respond_to(try_val, rb_intern("call"))) {
626
+ cur->pen.color_control_proc = try_val;
627
+ cur->pen.color_control_arity = FIX2INT(rb_funcall(try_val, rb_intern("arity"), 0));
628
+ cur->pen.has_color_control_proc = true;
629
+ }
630
+ else if(is_a_hash(try_val)) {
631
+ VALUE try_add = get_from_hash(try_val, "add");
632
+ VALUE try_mult = get_from_hash(try_val, "mult");
633
+
634
+ if(is_an_array(try_add)) {
635
+
636
+ cur->pen.color_add = convert_rb_color_to_rgba(try_add);
637
+
638
+ cur->pen.has_color_control_transform = true;
639
+ }
640
+ if(is_an_array(try_mult)) {
641
+
642
+ cur->pen.color_mult = convert_rb_color_to_rgba(try_mult);
643
+
644
+ cur->pen.has_color_control_transform = true;
645
+ }
646
+
647
+ }
648
+ }
649
+ }
650
+
651
+ static rgba
652
+ exec_color_control_proc(action_struct * cur, texture_info * tex, int x, int y, rgba blended_pixel)
653
+ {
654
+ int arity = cur->pen.color_control_arity;
655
+ VALUE proc = cur->pen.color_control_proc;
656
+ rgba old_color = get_pixel_color(tex, x, y);
657
+ rgba current_color = blended_pixel;
658
+ rgba new_color;
659
+
660
+ if(!cur->pen.has_color_control_proc)
661
+ rb_raise(rb_eRuntimeError, "needs a proc");
662
+
663
+ switch(arity) {
664
+ case -1:
665
+ case 0:
666
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), 0));
667
+ break;
668
+
669
+ case 1:
670
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
671
+ convert_rgba_to_rb_color(&old_color)));
672
+ break;
673
+
674
+ case 2:
675
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
676
+ convert_rgba_to_rb_color(&old_color),
677
+ convert_rgba_to_rb_color(&current_color)));
678
+ break;
679
+
680
+ case 3:
681
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
682
+ convert_rgba_to_rb_color(&old_color),
683
+ INT2FIX(x), INT2FIX(y)));
684
+ break;
685
+ case 4:
686
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
687
+ convert_rgba_to_rb_color(&old_color),
688
+ convert_rgba_to_rb_color(&current_color),
689
+ INT2FIX(x), INT2FIX(y)));
690
+ break;
691
+ default:
692
+ rb_raise(rb_eArgError, "permissible arities for color_control proc are 1, 2, 3 and 4. Got %d\n",
693
+ arity);
694
+ }
695
+
696
+ /* update the action color */
697
+ return new_color;
698
+ }
699
+
700
+ static void
701
+ prepare_fill_texture(action_struct * payload)
702
+ {
703
+ if(is_a_hash(payload->hash_arg)) {
704
+ VALUE try_image = get_from_hash(payload->hash_arg, "texture");
705
+ if(is_gosu_image(try_image)) {
706
+
707
+ get_texture_info(try_image, &payload->pen.source_tex);
708
+ payload->pen.has_source_texture = true;
709
+ }
710
+ }
711
+ }
712
+
713
+ static void
714
+ process_select_color_list(rgba_list * clist, VALUE try_color)
715
+ {
716
+ /* is a general array of colors? i.e [:red, Gosu::Color::RED, [1,1,1,1] ] */
717
+ if (TYPE(try_color) == T_ARRAY && not_rb_raw_color(try_color)) {
718
+ int num_colors = RARRAY_LEN(try_color);
719
+
720
+ if (num_colors > RGBA_LIST_SIZE)
721
+ rb_raise(rb_eArgError, "Too many colors given in array. Maximum is %d\n. Got %d\n",
722
+ RGBA_LIST_SIZE, num_colors);
723
+
724
+ for (int i = 0; i < RARRAY_LEN(try_color); ++i) {
725
+ clist->colors[i] = convert_rb_color_to_rgba(get_from_array(try_color, i));
726
+ }
727
+
728
+ clist->size = num_colors;
729
+ }
730
+
731
+ /* is a single color value? i.e :red, [1,1,1,1], Gosu::Color::GREEN */
732
+ else {
733
+ clist->colors[0] = convert_rb_color_to_rgba(try_color);
734
+ clist->size = 1;
735
+ }
736
+ }
737
+
738
+
739
+ static void
740
+ prepare_color_select(action_struct * payload)
741
+ {
742
+ VALUE try_color = get_from_hash(payload->hash_arg,
743
+ "source_select");
744
+ if (!NIL_P(try_color)) {
745
+ process_select_color_list(&payload->pen.source_select, try_color);
746
+ payload->pen.has_color_select = true;
747
+ }
748
+
749
+ try_color = get_from_hash(payload->hash_arg,
750
+ "source_ignore");
751
+ if (!NIL_P(try_color)) {
752
+ process_select_color_list(&payload->pen.source_ignore, try_color);
753
+ payload->pen.has_color_select = true;
754
+ }
755
+
756
+ try_color = get_from_hash(payload->hash_arg,
757
+ "dest_select");
758
+ if (!NIL_P(try_color)) {
759
+ process_select_color_list(&payload->pen.dest_select, try_color);
760
+ payload->pen.has_color_select = true;
761
+ }
762
+
763
+ try_color = get_from_hash(payload->hash_arg,
764
+ "dest_ignore");
765
+ if (!NIL_P(try_color)) {
766
+ process_select_color_list(&payload->pen.dest_ignore, try_color);
767
+ payload->pen.has_color_select = true;
768
+ }
769
+
770
+ }
771
+
772
+ /***********************************/
773
+ /**** drawing mode related code ****/
774
+ /***********************************/
775
+
776
+ typedef struct {
777
+ unsigned char red, green, blue, alpha;
778
+ } rgba_char;
779
+
780
+ static inline rgba_char
781
+ color_float_to_int_format(rgba c)
782
+ {
783
+ return (rgba_char) { c.red * 255,
784
+ c.green * 255,
785
+ c.blue * 255,
786
+ c.alpha * 255
787
+ };
788
+ }
789
+
790
+ static inline rgba
791
+ color_int_vals_to_float_format(unsigned char r, unsigned char g, unsigned char b,
792
+ unsigned char a)
793
+ {
794
+ return (rgba) { r / 255.0,
795
+ g / 255.0,
796
+ b / 255.0,
797
+ a / 255.0
798
+ };
799
+ }
800
+
801
+ /* using terminology from photoshop PDF. b=background, s=source */
802
+ static inline rgba
803
+ mode_multiply(rgba b, rgba s)
804
+ {
805
+ return (rgba) { b.red * s.red,
806
+ b.green * s.green,
807
+ b.blue * s.blue,
808
+ b.alpha * s.alpha };
809
+ }
810
+
811
+ static inline rgba
812
+ mode_screen(rgba b, rgba s)
813
+ {
814
+ return (rgba) { b.red + s.red - (b.red * s.red),
815
+ b.green + s.green - (b.green * s.green),
816
+ b.blue + s.blue - (b.blue * s.blue),
817
+ b.alpha + s.alpha - (b.alpha * s.alpha) };
818
+ }
819
+
820
+ static inline float
821
+ mode_hardlight_channel(float b, float s)
822
+ {
823
+ if (s <= 0.5)
824
+ return 2 * b * s;
825
+
826
+ else
827
+ return b + s - (b * s);
828
+ }
829
+
830
+ static inline rgba
831
+ mode_hardlight(rgba b, rgba s)
832
+ {
833
+ return (rgba) { mode_hardlight_channel(b.red, s.red),
834
+ mode_hardlight_channel(b.green, s.green),
835
+ mode_hardlight_channel(b.blue, s.blue),
836
+ mode_hardlight_channel(b.alpha, s.alpha) };
837
+ }
838
+
839
+ /* function from the photoshop PDF to implement soft lighting */
840
+ static inline float
841
+ D(float x)
842
+ {
843
+ if (x <= 0.25)
844
+ return ((16 * x - 12) * x + 4) * x;
845
+ else
846
+ return sqrt(x);
847
+ }
848
+
849
+ static inline float
850
+ mode_softlight_channel(float b, float s)
851
+ {
852
+ if (s <= 0.5)
853
+ return b - (1 - 2 * s) * b * (1 - b);
854
+ else
855
+ return b + (2 * s - 1) * (D(b) - b);
856
+ }
857
+
858
+ static inline rgba
859
+ mode_softlight(rgba b, rgba s)
860
+ {
861
+ return (rgba) { mode_softlight_channel(b.red, s.red),
862
+ mode_softlight_channel(b.green, s.green),
863
+ mode_softlight_channel(b.blue, s.blue),
864
+ mode_softlight_channel(b.alpha, s.alpha) };
865
+ }
866
+
867
+ static inline float
868
+ mode_colordodge_channel(float b, float s)
869
+ {
870
+ if (s < 1)
871
+ return MIN(1, b / (1 - s));
872
+ else
873
+ return 1;
874
+ }
875
+
876
+ static inline float
877
+ mode_colorburn_channel(float b, float s)
878
+ {
879
+ if (s > 0)
880
+ return 1 - MIN(1, (1 - b) / s);
881
+ else
882
+ return 0;
883
+ }
884
+
885
+ static rgba
886
+ apply_drawing_mode(action_struct * payload, texture_info * tex, int x, int y, rgba source_pixel)
887
+ {
888
+ rgba finished_pixel;
889
+
890
+ rgba dest_pixel = get_pixel_color(tex, x, y);
891
+
892
+ rgba_char dest_pixel_char = color_float_to_int_format(dest_pixel);
893
+ rgba_char source_pixel_char = color_float_to_int_format(source_pixel);
894
+
895
+ switch(payload->pen.drawing_mode)
896
+ {
897
+
898
+ /* bitwise blending functions */
899
+ case clear:
900
+ finished_pixel = (rgba) { 0, 0, 0, 0 };
901
+ break;
902
+ case copy:
903
+ finished_pixel = source_pixel;
904
+ break;
905
+ case noop:
906
+ finished_pixel = dest_pixel;
907
+ break;
908
+ case set:
909
+ finished_pixel = (rgba) { 1, 1, 1, 1 };
910
+ break;
911
+ case copy_inverted:
912
+ finished_pixel = color_int_vals_to_float_format(~source_pixel_char.red,
913
+ ~source_pixel_char.green,
914
+ ~source_pixel_char.blue,
915
+ source_pixel_char.alpha);
916
+ break;
917
+ case invert:
918
+ finished_pixel = color_int_vals_to_float_format(~dest_pixel_char.red,
919
+ ~dest_pixel_char.green,
920
+ ~dest_pixel_char.blue,
921
+ dest_pixel_char.alpha);
922
+
923
+ break;
924
+ case and_reverse:
925
+ finished_pixel = color_int_vals_to_float_format(source_pixel_char.red | ~dest_pixel_char.red,
926
+ source_pixel_char.green | ~dest_pixel_char.green,
927
+ source_pixel_char.blue | ~dest_pixel_char.blue,
928
+ source_pixel_char.alpha);
929
+ break;
930
+ case and:
931
+ finished_pixel = color_int_vals_to_float_format(source_pixel_char.red & dest_pixel_char.red,
932
+ source_pixel_char.green & dest_pixel_char.green,
933
+ source_pixel_char.blue & dest_pixel_char.blue,
934
+ source_pixel_char.alpha);
935
+ break;
936
+ case or:
937
+ finished_pixel = color_int_vals_to_float_format(source_pixel_char.red | dest_pixel_char.red,
938
+ source_pixel_char.green | dest_pixel_char.green,
939
+ source_pixel_char.blue | dest_pixel_char.blue,
940
+ source_pixel_char.alpha);
941
+
942
+ break;
943
+ case nand:
944
+ finished_pixel = color_int_vals_to_float_format(~(source_pixel_char.red & dest_pixel_char.red),
945
+ ~(source_pixel_char.green & dest_pixel_char.green),
946
+ ~(source_pixel_char.blue & dest_pixel_char.blue),
947
+ ~(source_pixel_char.alpha & dest_pixel_char.alpha));
948
+
949
+ break;
950
+ case nor:
951
+ finished_pixel = color_int_vals_to_float_format(~(source_pixel_char.red | dest_pixel_char.red),
952
+ ~(source_pixel_char.green | dest_pixel_char.green),
953
+ ~(source_pixel_char.blue | dest_pixel_char.blue),
954
+ source_pixel_char.alpha);
955
+
956
+ break;
957
+ case xor:
958
+ finished_pixel = color_int_vals_to_float_format(source_pixel_char.red ^ dest_pixel_char.red,
959
+ source_pixel_char.green ^ dest_pixel_char.green,
960
+ source_pixel_char.blue ^ dest_pixel_char.blue,
961
+ source_pixel_char.alpha);
962
+
963
+ break;
964
+ case equiv:
965
+ finished_pixel = color_int_vals_to_float_format(~(source_pixel_char.red ^ dest_pixel_char.red),
966
+ ~(source_pixel_char.green ^ dest_pixel_char.green),
967
+ ~(source_pixel_char.blue ^ dest_pixel_char.blue),
968
+ source_pixel_char.alpha);
969
+
970
+ break;
971
+ case and_inverted:
972
+ finished_pixel = color_int_vals_to_float_format(~source_pixel_char.red & dest_pixel_char.red,
973
+ ~source_pixel_char.green & dest_pixel_char.green,
974
+ ~source_pixel_char.blue & dest_pixel_char.blue,
975
+ source_pixel_char.alpha);
976
+ break;
977
+ case or_inverted:
978
+ finished_pixel = color_int_vals_to_float_format(~source_pixel_char.red | dest_pixel_char.red,
979
+ ~source_pixel_char.green | dest_pixel_char.green,
980
+ ~source_pixel_char.blue | dest_pixel_char.blue,
981
+ source_pixel_char.alpha);
982
+
983
+ break;
984
+
985
+ /* photoshop style blending functions */
986
+ case additive:
987
+ finished_pixel = (rgba) { MIN(source_pixel.red + dest_pixel.red, 1),
988
+ MIN(source_pixel.green + dest_pixel.green, 1),
989
+ MIN(source_pixel.blue + dest_pixel.blue, 1),
990
+ MIN(source_pixel.alpha + dest_pixel.alpha, 1) };
991
+ break;
992
+ case multiply:
993
+ finished_pixel = mode_multiply(dest_pixel, source_pixel);
994
+
995
+ break;
996
+ case screen:
997
+ finished_pixel = mode_screen(dest_pixel, source_pixel);
998
+
999
+ break;
1000
+ case overlay:
1001
+ finished_pixel = mode_hardlight(source_pixel, dest_pixel);
1002
+
1003
+ break;
1004
+ case darken:
1005
+ finished_pixel = (rgba) { MIN(source_pixel.red, dest_pixel.red),
1006
+ MIN(source_pixel.green, dest_pixel.green),
1007
+ MIN(source_pixel.blue, dest_pixel.blue),
1008
+ MIN(source_pixel.alpha, dest_pixel.alpha) };
1009
+ break;
1010
+ case lighten:
1011
+ finished_pixel = (rgba) { MAX(source_pixel.red, dest_pixel.red),
1012
+ MAX(source_pixel.green, dest_pixel.green),
1013
+ MAX(source_pixel.blue, dest_pixel.blue),
1014
+ MAX(source_pixel.alpha, dest_pixel.alpha) };
1015
+ break;
1016
+ case color_dodge:
1017
+ finished_pixel = (rgba) { mode_colordodge_channel(dest_pixel.red, source_pixel.red),
1018
+ mode_colordodge_channel(dest_pixel.green, source_pixel.green),
1019
+ mode_colordodge_channel(dest_pixel.blue, source_pixel.blue),
1020
+ mode_colordodge_channel(dest_pixel.alpha, source_pixel.alpha) };
1021
+
1022
+ break;
1023
+ case color_burn:
1024
+ finished_pixel = (rgba) { mode_colorburn_channel(dest_pixel.red, source_pixel.red),
1025
+ mode_colorburn_channel(dest_pixel.green, source_pixel.green),
1026
+ mode_colorburn_channel(dest_pixel.blue, source_pixel.blue),
1027
+ mode_colorburn_channel(dest_pixel.alpha, source_pixel.alpha) };
1028
+ break;
1029
+ case hard_light:
1030
+ finished_pixel = mode_hardlight(dest_pixel, source_pixel);
1031
+
1032
+ break;
1033
+ case soft_light:
1034
+ finished_pixel = mode_softlight(dest_pixel, source_pixel);
1035
+
1036
+ break;
1037
+ case difference:
1038
+ finished_pixel = (rgba) { ABS(dest_pixel.red - source_pixel.red),
1039
+ ABS(dest_pixel.green - source_pixel.green),
1040
+ ABS(dest_pixel.blue - source_pixel.blue),
1041
+ ABS(dest_pixel.alpha - source_pixel.alpha) };
1042
+ break;
1043
+ case exclusion:
1044
+ finished_pixel = (rgba) { dest_pixel.red + source_pixel.red - (2 * dest_pixel.red * source_pixel.red),
1045
+ dest_pixel.green + source_pixel.green - (2 * dest_pixel.green * source_pixel.green),
1046
+ dest_pixel.blue + source_pixel.blue - (2 * dest_pixel.blue * source_pixel.blue),
1047
+ dest_pixel.alpha + source_pixel.alpha - (2 * dest_pixel.alpha * source_pixel.alpha) };
1048
+ break;
1049
+ }
1050
+
1051
+ return finished_pixel;
1052
+ }
1053
+ /***************************************/
1054
+ /**** end drawing mode related code ****/
1055
+ /***************************************/
1056
+
1057
+
1058
+ static rgba
1059
+ apply_lerp(action_struct * payload, texture_info * tex, int x, int y)
1060
+ {
1061
+ rgba finished_pixel;
1062
+ rgba dest_pixel = get_pixel_color(tex, x, y);
1063
+
1064
+ finished_pixel.red = payload->pen.lerp * payload->color.red +
1065
+ (1 - payload->pen.lerp) * dest_pixel.red;
1066
+
1067
+ finished_pixel.green = payload->pen.lerp * payload->color.green +
1068
+ (1 - payload->pen.lerp) * dest_pixel.green;
1069
+
1070
+ finished_pixel.blue = payload->pen.lerp * payload->color.blue +
1071
+ (1 - payload->pen.lerp) * dest_pixel.blue;
1072
+
1073
+ finished_pixel.alpha = payload->pen.lerp * payload->color.alpha +
1074
+ (1 - payload->pen.lerp) * dest_pixel.alpha;
1075
+
1076
+ return finished_pixel;
1077
+ }
1078
+
1079
+
1080
+ /* TODO: reimplement using SSE2 */
1081
+ static rgba
1082
+ apply_color_control_transform(action_struct * payload, texture_info * tex, int x, int y)
1083
+
1084
+ {
1085
+ rgba transformed_color;
1086
+
1087
+ transformed_color = get_pixel_color(tex, x, y);
1088
+
1089
+ transformed_color.red += payload->pen.color_add.red;
1090
+ transformed_color.green += payload->pen.color_add.green;
1091
+ transformed_color.blue += payload->pen.color_add.blue;
1092
+ transformed_color.alpha += payload->pen.color_add.alpha;
1093
+
1094
+ transformed_color.red *= payload->pen.color_mult.red;
1095
+ transformed_color.green *= payload->pen.color_mult.green;
1096
+ transformed_color.blue *= payload->pen.color_mult.blue;
1097
+ transformed_color.alpha *= payload->pen.color_mult.alpha;
1098
+
1099
+ return transformed_color;
1100
+ }
1101
+
1102
+ static rgba
1103
+ apply_alpha_blend(action_struct * payload, texture_info * tex, int x, int y, rgba blended_pixel)
1104
+ {
1105
+ rgba dest_pixel = get_pixel_color(tex, x, y);
1106
+ rgba finished_pixel;
1107
+
1108
+ if (not_a_color(blended_pixel))
1109
+ return blended_pixel;
1110
+
1111
+ alpha_blend_mode_t blend_mode = payload->pen.alpha_blend_mode;
1112
+
1113
+ switch(blend_mode)
1114
+ {
1115
+ case source:
1116
+ case source_with_fixed_alpha:
1117
+ /** TO DO: rewrite this using sse2 instructions **/
1118
+ finished_pixel.red = blended_pixel.alpha * blended_pixel.red + (1 - blended_pixel.alpha)
1119
+ * dest_pixel.red;
1120
+
1121
+ finished_pixel.green = blended_pixel.alpha * blended_pixel.green + (1 - blended_pixel.alpha)
1122
+ * dest_pixel.green;
1123
+
1124
+ finished_pixel.blue = blended_pixel.alpha * blended_pixel.blue + (1 - blended_pixel.alpha)
1125
+ * dest_pixel.blue;
1126
+
1127
+ if(blend_mode == source) {
1128
+ finished_pixel.alpha = blended_pixel.alpha * blended_pixel.alpha + (1 - blended_pixel.alpha)
1129
+ * dest_pixel.alpha;
1130
+ }
1131
+ else {
1132
+
1133
+ // fixed alpha
1134
+ finished_pixel.alpha = dest_pixel.alpha;
1135
+ }
1136
+
1137
+ break;
1138
+ case dest:
1139
+ case dest_with_fixed_alpha:
1140
+ finished_pixel.red = dest_pixel.alpha * blended_pixel.red + (1 - dest_pixel.alpha)
1141
+ * dest_pixel.red;
1142
+
1143
+ finished_pixel.green = dest_pixel.alpha * blended_pixel.green + (1 - dest_pixel.alpha)
1144
+ * dest_pixel.green;
1145
+
1146
+ finished_pixel.blue = dest_pixel.alpha * blended_pixel.blue + (1 - dest_pixel.alpha)
1147
+ * dest_pixel.blue;
1148
+
1149
+ if(blend_mode == dest) {
1150
+ finished_pixel.alpha = dest_pixel.alpha * blended_pixel.alpha + (1 - dest_pixel.alpha)
1151
+ * dest_pixel.alpha;
1152
+ }
1153
+ else {
1154
+
1155
+ // fixed alpha
1156
+ finished_pixel.alpha = dest_pixel.alpha;
1157
+ }
1158
+
1159
+ break;
1160
+ default:
1161
+ rb_raise(rb_eRuntimeError,
1162
+ "apply_alpha_blend() impossible error. got %d\n", blend_mode);
1163
+
1164
+ }
1165
+
1166
+ return finished_pixel;
1167
+ }
1168
+
1169
+ /* NEW from utils.c */
1170
+ float *
1171
+ allocate_texture(int width, int height)
1172
+ {
1173
+ float * new_texture;
1174
+ int mval;
1175
+
1176
+ mval = 4 * width * height * sizeof(float);
1177
+
1178
+ new_texture = malloc(mval);
1179
+
1180
+ return new_texture;
1181
+ }
1182
+
1183
+ /* get information from texture */
1184
+ void
1185
+ get_texture_info(VALUE image, texture_info * tex)
1186
+ {
1187
+ VALUE info, gc_state_off;
1188
+ int toppos, leftpos;
1189
+ float top, left;
1190
+ cache_entry * entry;
1191
+
1192
+ /* hack to prevent segfault */
1193
+ gc_state_off = rb_gc_disable();
1194
+
1195
+ tex->width = FIX2INT(rb_funcall(image, rb_intern("width"), 0));
1196
+ tex->height = FIX2INT(rb_funcall(image, rb_intern("height"), 0));
1197
+
1198
+ /* ensure gl_tex_info returns non nil */
1199
+ info = check_for_texture_info(image);
1200
+
1201
+ top = NUM2DBL(rb_funcall(info, rb_intern("top"), 0));
1202
+ left = NUM2DBL(rb_funcall(info, rb_intern("left"), 0));
1203
+ tex->tname = FIX2INT(rb_funcall(info ,rb_intern("tex_name"),0));
1204
+
1205
+ /* search for texture in cache (& create if not extant) */
1206
+ entry = find_or_create_cache_entry(tex->tname);
1207
+
1208
+ tex->td_array = entry->tdata;
1209
+ tex->yincr = entry->sidelength;
1210
+
1211
+ /* scratch variables */
1212
+ toppos = ROUND(top * entry->sidelength * entry->sidelength);
1213
+ leftpos = ROUND(left * entry->sidelength);
1214
+
1215
+ /* find the first pixel for the image */
1216
+ tex->firstpixel = (int)(toppos + leftpos);
1217
+
1218
+ tex->x_offset = ROUND(left * tex->yincr);
1219
+ tex->y_offset = ROUND(top * tex->yincr);
1220
+
1221
+ /* save the associated Gosu::Image */
1222
+ tex->image = image;
1223
+
1224
+ /* only enable gc if was enabled on function entry */
1225
+ if(!gc_state_off) rb_gc_enable();
1226
+ }
1227
+
1228
+ /* ensure gl_tex_info returns non nil */
1229
+ VALUE
1230
+ check_for_texture_info(VALUE image)
1231
+ {
1232
+ VALUE info;
1233
+
1234
+ info = rb_funcall(image, rb_intern("gl_tex_info"), 0);
1235
+
1236
+ if(NIL_P(info)) {
1237
+ VALUE image_name = rb_inspect(image);
1238
+ int width = FIX2INT(rb_funcall(image, rb_intern("width"), 0));
1239
+ int height = FIX2INT(rb_funcall(image, rb_intern("height"), 0));
1240
+
1241
+ rb_raise(rb_eException, "Error: gl_tex_info returns nil for %s (%i x %i). Could be caused by "
1242
+ "very large image or Gosu bug. Try updating Gosu or use a smaller image. Note: TexPlay should"
1243
+ " be able to work with %i x %i images.",
1244
+ StringValuePtr(image_name), width, height, max_quad_size() - 2, max_quad_size() - 2);
1245
+ }
1246
+
1247
+ return info;
1248
+ }
1249
+
1250
+ /* responsible for syncing a subimage to gl */
1251
+ void
1252
+ sync_to_gl(int tex_name, int x_offset, int y_offset, int width, int height, void * sub)
1253
+ {
1254
+ /* set the opengl texture context */
1255
+ glEnable(GL_TEXTURE_2D);
1256
+ glBindTexture(GL_TEXTURE_2D, tex_name);
1257
+
1258
+ /* sync subtexture */
1259
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x_offset, y_offset, width, height,
1260
+ GL_RGBA, GL_FLOAT, sub);
1261
+
1262
+ glDisable(GL_TEXTURE_2D);
1263
+ }
1264
+
1265
+ void
1266
+ create_subtexture_and_sync_to_gl(image_bounds * img_bounds, texture_info * tex)
1267
+ {
1268
+ /* image vars */
1269
+ int xbound, ybound;
1270
+ float * sub = NULL;
1271
+
1272
+ /* make the current action's boundaries sane; left until this point because we only
1273
+ know height/width here */
1274
+ constrain_boundaries(&img_bounds->xmin, &img_bounds->ymin, &img_bounds->xmax, &img_bounds->ymax,
1275
+ tex->width, tex->height);
1276
+
1277
+ /* helper variables */
1278
+ ybound = img_bounds->ymax - img_bounds->ymin + 1;
1279
+ xbound = img_bounds->xmax - img_bounds->xmin + 1;
1280
+
1281
+ sub = get_image_chunk(tex, img_bounds->xmin, img_bounds->ymin, img_bounds->xmax, img_bounds->ymax);
1282
+
1283
+ sync_to_gl(tex->tname, tex->x_offset + img_bounds->xmin, tex->y_offset + img_bounds->ymin, xbound,
1284
+ ybound, (void*)sub);
1285
+
1286
+ free(sub);
1287
+ }
1288
+
1289
+ float *
1290
+ get_image_chunk(texture_info * tex, int xmin, int ymin, int xmax, int ymax)
1291
+ {
1292
+ int xbound;
1293
+ int ybound;
1294
+ float * image_buf = NULL;
1295
+
1296
+ constrain_boundaries(&xmin, &ymin, &xmax, &ymax, tex->width, tex->height);
1297
+
1298
+ xbound = xmax - xmin + 1;
1299
+ ybound = ymax - ymin + 1;
1300
+
1301
+ image_buf = allocate_texture(xbound, ybound);
1302
+
1303
+ for(int y = 0; y < ybound; y++)
1304
+ for(int x = 0; x < xbound; x++) {
1305
+ int buf_index = 4 * (x + y * xbound);
1306
+
1307
+ int offset = calc_pixel_offset(tex, x + xmin, y + ymin);
1308
+
1309
+ color_copy(tex->td_array + offset, image_buf + buf_index);
1310
+ }
1311
+
1312
+ return image_buf;
1313
+ }