texplay 0.2.983pre2-i386-mswin32 → 0.3.0-i386-mswin32

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