texplay 0.4.3 → 0.4.4.pre

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