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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
+ }