texplay 0.4.3 → 0.4.4.pre

Sign up to get free protection for your applications and to get access to all the features.
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
+ }