texplay 0.2.710 → 0.2.722

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.
@@ -15,6 +15,7 @@
15
15
 
16
16
  #include "texplay.h"
17
17
  #include "utils.h"
18
+ #include "graphics_utils.h"
18
19
  #include "bindings.h"
19
20
  #include "actions.h"
20
21
  #include "cache.h"
@@ -17,14 +17,12 @@ else
17
17
  exit unless have_library("freeglut_static")
18
18
  exit unless have_library("opengl32")
19
19
 
20
- if RUBY_PLATFORM =~ /mingw/
21
- $CFLAGS += " -I/home/john/.rake-compiler/ruby/ruby-1.9.1-p243/include"
22
- $CFLAGS += " -I/home/john/.rake-compiler/ruby/ruby-1.8.6-p287/include"
23
- $CFLAGS += " -D FREEGLUT_STATIC"
24
- end
25
20
  end
26
21
 
27
22
  # 1.9 compatibility
28
23
  $CFLAGS += " -DRUBY_19" if RUBY_VERSION =~ /1.9/
29
24
 
25
+ # let's use c99
26
+ $CFLAGS += " -std=c99"
27
+
30
28
  create_makefile('texplay')
File without changes
File without changes
@@ -0,0 +1,660 @@
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_fill_texture(action_struct * cur);
17
+ static void prepare_color_control(action_struct * cur);
18
+ static rgba apply_alpha_blend(action_struct * payload, texture_info * tex, int x, int y, rgba blended_pixel);
19
+ static rgba apply_color_control_transform(action_struct * payload, texture_info * tex, int x, int y);
20
+ static rgba exec_color_control_proc(action_struct * cur, texture_info * tex, int x, int y, rgba blended_pixel);
21
+ /* end helpers */
22
+
23
+
24
+ void
25
+ update_lazy_bounds(action_struct * cur, texture_info * tex)
26
+ {
27
+
28
+ /* only update global bounds if we're doing a lazy_sync */
29
+ if(cur->sync_mode == lazy_sync) {
30
+ int xmin, ymin, xmax, ymax;
31
+ VALUE lazy_bounds;
32
+
33
+ lazy_bounds = get_image_local(tex->image, LAZY_BOUNDS);
34
+
35
+ xmin = INT2FIX(MIN(cur->xmin, FIX2INT(get_from_array(lazy_bounds, 0))));
36
+ ymin = INT2FIX(MIN(cur->ymin, FIX2INT(get_from_array(lazy_bounds, 1))));
37
+ xmax = INT2FIX(MAX(cur->xmax, FIX2INT(get_from_array(lazy_bounds, 2))));
38
+ ymax = INT2FIX(MAX(cur->ymax, FIX2INT(get_from_array(lazy_bounds, 3))));
39
+
40
+ set_array_value(lazy_bounds, 0, xmin);
41
+ set_array_value(lazy_bounds, 1, ymin);
42
+ set_array_value(lazy_bounds, 2, xmax);
43
+ set_array_value(lazy_bounds, 3, ymax);
44
+ }
45
+ }
46
+
47
+ void
48
+ update_bounds(action_struct * cur, int xmin, int ymin, int xmax, int ymax)
49
+ {
50
+ if(xmin > xmax) SWAP(xmin, xmax);
51
+ if(ymin > ymax) SWAP(ymin, ymax);
52
+
53
+ cur->xmin = MIN(cur->xmin, xmin);
54
+ cur->ymin = MIN(cur->ymin, ymin);
55
+ cur->xmax = MAX(cur->xmax, xmax);
56
+ cur->ymax = MAX(cur->ymax, ymax);
57
+ }
58
+
59
+ void
60
+ set_local_bounds(action_struct * cur, int xmin, int ymin, int xmax, int ymax, texture_info * tex)
61
+ {
62
+ if(cur->sync_mode == no_sync)
63
+ return;
64
+
65
+ /* local bounds used by both eager_sync and lazy_sync: */
66
+
67
+ /* eager sync: to demarcate precise area to sync to opengl */
68
+ /* lazy sync: to update global bounds */
69
+ cur->xmin = xmin;
70
+ cur->ymin = ymin;
71
+ cur->xmax = xmax;
72
+ cur->ymax = ymax;
73
+ }
74
+
75
+ void
76
+ draw_prologue(action_struct * cur, texture_info * tex, int xmin, int ymin, int xmax, int ymax,
77
+ VALUE * hash_arg, sync sync_mode, bool primary, action_struct ** payload_ptr)
78
+ {
79
+ if(!primary) return;
80
+
81
+ /* set the payload pointer */
82
+ *payload_ptr = cur;
83
+
84
+ /* not too happy about having this here, look at texplay.h for why */
85
+ cur->tex = tex;
86
+
87
+ process_common_hash_args(cur, hash_arg, sync_mode, primary);
88
+
89
+ set_local_bounds(cur, xmin, ymin, xmax, ymax, tex);
90
+ }
91
+
92
+ void
93
+ draw_epilogue(action_struct * cur, texture_info * tex, bool primary)
94
+ {
95
+ /* only primary actions get sync'd */
96
+ if(!primary) return;
97
+
98
+ switch(cur->sync_mode) {
99
+
100
+ /* do not sync */
101
+ case no_sync:
102
+ return;
103
+ break;
104
+
105
+ /* sync immediately */
106
+ case eager_sync:
107
+ create_subtexture_and_sync_to_gl(IMAGE_BOUNDS(cur), tex);
108
+ break;
109
+
110
+ /* sync later (at end of paint block?) */
111
+ case lazy_sync:
112
+ update_lazy_bounds(cur, tex);
113
+ break;
114
+
115
+ default:
116
+ rb_raise(rb_eRuntimeError,
117
+ "sync_mode may only be: lazy_sync, eager_sync, no_sync. got %d\n", cur->sync_mode);
118
+ }
119
+ }
120
+
121
+ /* set the pixel color at (x, y) */
122
+ void
123
+ set_pixel_color(rgba * pixel_color, texture_info * tex, int x, int y)
124
+ {
125
+ float * tex_data;
126
+
127
+ /* should pixel be drawn ? */
128
+ if (x > (tex->width - 1) || x < 0 || y > (tex->height - 1) || y < 0)
129
+ return;
130
+
131
+ /* if not a color then do not draw */
132
+ if(not_a_color(*pixel_color))
133
+ return;
134
+
135
+ tex_data = get_pixel_data(tex, x, y);
136
+
137
+ /* set the pixel color */
138
+ tex_data[red] = pixel_color->red;
139
+ tex_data[green] = pixel_color->green;
140
+ tex_data[blue] = pixel_color->blue;
141
+ tex_data[alpha] = pixel_color->alpha;
142
+ }
143
+
144
+ void
145
+ set_pixel_color_with_style(action_struct * payload, texture_info * tex, int x, int y)
146
+ {
147
+
148
+ rgba blended_pixel;
149
+
150
+ blended_pixel = payload->color;
151
+
152
+ /* for color_control transform */
153
+ if(payload->pen.has_color_control_transform)
154
+ blended_pixel = apply_color_control_transform(payload, tex, x, y);
155
+
156
+ /* for texture fill */
157
+ if(payload->pen.has_source_texture)
158
+ blended_pixel = get_pixel_color(&payload->pen.source_tex,
159
+ x % payload->pen.source_tex.width,
160
+ y % payload->pen.source_tex.height);
161
+
162
+ /* for color_control block */
163
+ if(payload->pen.has_color_control_proc)
164
+ blended_pixel = exec_color_control_proc(payload, tex, x, y, blended_pixel);
165
+
166
+
167
+ /* TO DO: do bitwise pixel combinations here */
168
+
169
+ /* TO DO: refactor into its own helper function
170
+ & rewrite using sse2 */
171
+ if(payload->pen.alpha_blend)
172
+ blended_pixel = apply_alpha_blend(payload, tex, x, y, blended_pixel);
173
+
174
+
175
+ set_pixel_color(&blended_pixel, tex, x, y);
176
+ }
177
+
178
+
179
+ rgba
180
+ get_pixel_color_from_chunk(float * chunk, int width, int height, int x, int y)
181
+ {
182
+ rgba my_color;
183
+
184
+ int offset;
185
+
186
+ if (x > (width - 1) || x < 0 || y > (height - 1) || y < 0)
187
+ return not_a_color_v;
188
+
189
+ offset = 4 * (x + y * width);
190
+
191
+ my_color.red = chunk[offset + red];
192
+ my_color.green = chunk[offset + green];
193
+ my_color.blue = chunk[offset + blue];
194
+ my_color.alpha = chunk[offset + alpha];
195
+
196
+ return my_color;
197
+ }
198
+
199
+
200
+ rgba
201
+ get_pixel_color(texture_info * tex, int x, int y)
202
+ {
203
+ rgba my_color;
204
+
205
+ int offset;
206
+
207
+ /* if pixel location is out of range return not_a_color_v */
208
+ if (x > (tex->width - 1) || x < 0 || y > (tex->height - 1) || y < 0)
209
+ return not_a_color_v;
210
+
211
+ offset = calc_pixel_offset(tex, x, y);
212
+
213
+ my_color.red = tex->td_array[offset + red];
214
+ my_color.green = tex->td_array[offset + green];
215
+ my_color.blue = tex->td_array[offset + blue];
216
+ my_color.alpha = tex->td_array[offset + alpha];
217
+
218
+ return my_color;
219
+ }
220
+
221
+ /* return the array where pixel data is stored */
222
+ float*
223
+ get_pixel_data(texture_info * tex, int x, int y)
224
+ {
225
+ int offset = calc_pixel_offset(tex, x, y);
226
+
227
+ return &tex->td_array[offset];
228
+ }
229
+
230
+ /* if 2nd param is Qnil, then create a blank image with 'width' and 'height
231
+ otherwise, try to use the 2nd param (dup) to create a duplicate image
232
+ */
233
+ VALUE
234
+ create_image(VALUE window, int width, int height)
235
+ {
236
+ VALUE gosu = rb_const_get(rb_cObject, rb_intern("Gosu"));
237
+ VALUE image = rb_const_get(gosu, rb_intern("Image"));
238
+
239
+ VALUE TP = rb_const_get(rb_cObject, rb_intern("TexPlay"));
240
+ VALUE EmptyImageStub = rb_const_get(TP, rb_intern("EmptyImageStub"));
241
+
242
+ VALUE rmagick_img;
243
+ VALUE new_image;
244
+
245
+ rmagick_img = rb_funcall(EmptyImageStub, rb_intern("new"), 2, INT2FIX(width), INT2FIX(height));
246
+
247
+ new_image = rb_funcall(image, rb_intern("new"), 2, window, rmagick_img);
248
+
249
+ return new_image;
250
+ }
251
+
252
+
253
+
254
+ /* static functions */
255
+ static void
256
+ initialize_action_struct(action_struct * cur, VALUE hash_arg, sync sync_mode)
257
+ {
258
+ /* initialize action-struct to default values */
259
+ cur->sync_mode = sync_mode;
260
+ cur->hash_arg = hash_arg;
261
+
262
+ cur->color = convert_image_local_color_to_rgba(cur->tex->image);
263
+ cur->pen.has_color_control_proc = false;
264
+ cur->pen.has_color_control_transform = false;
265
+ cur->pen.has_source_texture = false;
266
+ cur->pen.alpha_blend = false;
267
+
268
+ /* set static color control transformations to defaults */
269
+ cur->pen.color_mult.red = 1.0;
270
+ cur->pen.color_mult.green = 1.0;
271
+ cur->pen.color_mult.blue = 1.0;
272
+ cur->pen.color_mult.alpha = 1.0;
273
+
274
+ cur->pen.color_add.red = 0.0;
275
+ cur->pen.color_add.green = 0.0;
276
+ cur->pen.color_add.blue = 0.0;
277
+ cur->pen.color_add.alpha = 0.0;
278
+ }
279
+
280
+ /* TODO: fix this function below, it's too ugly and bulky and weird **/
281
+ static void
282
+ process_common_hash_args(action_struct * cur, VALUE * hash_arg, sync sync_mode, bool primary)
283
+ {
284
+
285
+ VALUE user_defaults;
286
+ VALUE hash_blend;
287
+
288
+
289
+ /* if a hash doesn't exist then create one */
290
+ if(!is_a_hash(*hash_arg))
291
+ *hash_arg = rb_hash_new();
292
+
293
+ /* init the action to default values */
294
+ initialize_action_struct(cur, *hash_arg, sync_mode);
295
+
296
+ /* get the user default options & merge with given options */
297
+ user_defaults = get_image_local(cur->tex->image, USER_DEFAULTS);
298
+ hash_blend = rb_funcall(user_defaults, rb_intern("merge"), 1, *hash_arg);
299
+ rb_funcall(*hash_arg, rb_intern("merge!"), 1, hash_blend);
300
+
301
+ if(has_optional_hash_arg(*hash_arg, "color")) {
302
+ VALUE c = get_from_hash(*hash_arg, "color");
303
+ cur->color = convert_rb_color_to_rgba(c);
304
+ if(c == string2sym("random")) {
305
+ set_hash_value(*hash_arg, "color", convert_rgba_to_rb_color(&cur->color));
306
+ }
307
+ }
308
+
309
+ /* shadows */
310
+ if(RTEST(get_from_hash(*hash_arg, "shadow"))) {
311
+ cur->pen.color_mult.red = 0.66;
312
+ cur->pen.color_mult.green = 0.66;
313
+ cur->pen.color_mult.blue = 0.66;
314
+ cur->pen.color_mult.alpha = 1;
315
+
316
+ cur->pen.has_color_control_transform = true;
317
+ }
318
+
319
+ /* sync mode */
320
+ if(has_optional_hash_arg(*hash_arg, "sync_mode")) {
321
+ VALUE user_sync_mode = get_from_hash(*hash_arg, "sync_mode");
322
+
323
+ Check_Type(user_sync_mode, T_SYMBOL);
324
+
325
+ if(user_sync_mode == string2sym("lazy_sync"))
326
+ cur->sync_mode = lazy_sync;
327
+ else if(user_sync_mode == string2sym("eager_sync"))
328
+ cur->sync_mode = eager_sync;
329
+ else if(user_sync_mode == string2sym("no_sync"))
330
+ cur->sync_mode = no_sync;
331
+ else
332
+ rb_raise(rb_eArgError, "unrecognized sync mode: %s\n. Allowable modes are "
333
+ ":lazy_sync, :eager_sync, :no_sync.",
334
+ sym2string(user_sync_mode));
335
+
336
+ delete_from_hash(*hash_arg, "sync_mode");
337
+
338
+ }
339
+
340
+ /* process the color_control block or transform (if there is one) */
341
+ prepare_color_control(cur);
342
+
343
+ /* process the filling texture (if there is one) */
344
+ prepare_fill_texture(cur);
345
+
346
+ /* does the user want to blend alpha values ? */
347
+ if(get_from_hash(*hash_arg, "alpha_blend") == Qtrue)
348
+ cur->pen.alpha_blend = true;
349
+
350
+ }
351
+
352
+
353
+
354
+ /* set action color to return value of color_control proc */
355
+ static void
356
+ prepare_color_control(action_struct * cur)
357
+ {
358
+
359
+ if(is_a_hash(cur->hash_arg)) {
360
+ VALUE try_val = get_from_hash(cur->hash_arg, "color_control");
361
+
362
+ if(rb_respond_to(try_val, rb_intern("call"))) {
363
+ cur->pen.color_control_proc = try_val;
364
+ cur->pen.color_control_arity = FIX2INT(rb_funcall(try_val, rb_intern("arity"), 0));
365
+ cur->pen.has_color_control_proc = true;
366
+ }
367
+ else if(is_a_hash(try_val)) {
368
+ VALUE try_add = get_from_hash(try_val, "add");
369
+ VALUE try_mult = get_from_hash(try_val, "mult");
370
+
371
+ if(is_an_array(try_add)) {
372
+ if(RARRAY_LEN(try_add) < 4)
373
+ rb_raise(rb_eArgError, ":color_control transform :add needs 4 parameters");
374
+
375
+ cur->pen.color_add.red = NUM2DBL(get_from_array(try_add, 0));
376
+ cur->pen.color_add.green = NUM2DBL(get_from_array(try_add, 1));
377
+ cur->pen.color_add.blue = NUM2DBL(get_from_array(try_add, 2));
378
+ cur->pen.color_add.alpha = NUM2DBL(get_from_array(try_add, 3));
379
+
380
+ cur->pen.has_color_control_transform = true;
381
+ }
382
+ if(is_an_array(try_mult)) {
383
+ if(RARRAY_LEN(try_mult) < 4)
384
+ rb_raise(rb_eArgError, ":color_control transform :mult needs 4 parameters");
385
+
386
+ cur->pen.color_mult.red = NUM2DBL(get_from_array(try_mult, 0));
387
+ cur->pen.color_mult.green = NUM2DBL(get_from_array(try_mult, 1));
388
+ cur->pen.color_mult.blue = NUM2DBL(get_from_array(try_mult, 2));
389
+ cur->pen.color_mult.alpha = NUM2DBL(get_from_array(try_mult, 3));
390
+
391
+ cur->pen.has_color_control_transform = true;
392
+ }
393
+
394
+ }
395
+ }
396
+ }
397
+
398
+ static rgba
399
+ exec_color_control_proc(action_struct * cur, texture_info * tex, int x, int y, rgba blended_pixel)
400
+ {
401
+ int arity = cur->pen.color_control_arity;
402
+ VALUE proc = cur->pen.color_control_proc;
403
+ rgba old_color = get_pixel_color(tex, x, y);
404
+ rgba current_color = blended_pixel;
405
+ rgba new_color;
406
+
407
+ if(!cur->pen.has_color_control_proc)
408
+ rb_raise(rb_eRuntimeError, "needs a proc");
409
+
410
+ switch(arity) {
411
+ case -1:
412
+ case 0:
413
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), 0));
414
+ break;
415
+
416
+ case 1:
417
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
418
+ convert_rgba_to_rb_color(&old_color)));
419
+ break;
420
+
421
+ case 2:
422
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
423
+ convert_rgba_to_rb_color(&old_color),
424
+ convert_rgba_to_rb_color(&current_color)));
425
+ break;
426
+
427
+ case 3:
428
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
429
+ convert_rgba_to_rb_color(&old_color),
430
+ INT2FIX(x), INT2FIX(y)));
431
+ break;
432
+ case 4:
433
+ new_color = convert_rb_color_to_rgba(rb_funcall(proc, rb_intern("call"), arity,
434
+ convert_rgba_to_rb_color(&old_color),
435
+ convert_rgba_to_rb_color(&current_color),
436
+ INT2FIX(x), INT2FIX(y)));
437
+ break;
438
+ default:
439
+ rb_raise(rb_eArgError, "permissible arities for color_control proc are 1, 2, 3 and 4. Got %d\n",
440
+ arity);
441
+ }
442
+
443
+ /* update the action color */
444
+ return new_color;
445
+ }
446
+
447
+ static void
448
+ prepare_fill_texture(action_struct * payload)
449
+ {
450
+ if(is_a_hash(payload->hash_arg)) {
451
+ VALUE try_image = get_from_hash(payload->hash_arg, "texture");
452
+ if(is_gosu_image(try_image)) {
453
+
454
+ get_texture_info(try_image, &payload->pen.source_tex);
455
+ payload->pen.has_source_texture = true;
456
+ }
457
+ }
458
+ }
459
+
460
+
461
+ /* TODO: reimplement using SSE2 */
462
+ static rgba
463
+ apply_color_control_transform(action_struct * payload, texture_info * tex, int x, int y)
464
+
465
+ {
466
+ rgba transformed_color;
467
+
468
+ transformed_color = get_pixel_color(tex, x, y);
469
+
470
+ transformed_color.red += payload->pen.color_add.red;
471
+ transformed_color.green += payload->pen.color_add.green;
472
+ transformed_color.blue += payload->pen.color_add.blue;
473
+ transformed_color.alpha += payload->pen.color_add.alpha;
474
+
475
+ transformed_color.red *= payload->pen.color_mult.red;
476
+ transformed_color.green *= payload->pen.color_mult.green;
477
+ transformed_color.blue *= payload->pen.color_mult.blue;
478
+ transformed_color.alpha *= payload->pen.color_mult.alpha;
479
+
480
+ return transformed_color;
481
+ }
482
+
483
+ static rgba
484
+ apply_alpha_blend(action_struct * payload, texture_info * tex, int x, int y, rgba blended_pixel)
485
+ {
486
+ rgba dest_pixel = get_pixel_color(tex, x, y);
487
+ rgba finished_pixel;
488
+
489
+
490
+ if(not_a_color(blended_pixel))
491
+ return blended_pixel;
492
+
493
+ /* alpha blending is nothing more than a weighted average of src and dest pixels
494
+ based on source alpha value */
495
+ /* NB: destination alpha value is ignored */
496
+
497
+ /** TO DO: rewrite this using sse2 instructions **/
498
+ finished_pixel.red = blended_pixel.alpha * blended_pixel.red + (1 - blended_pixel.alpha)
499
+ * dest_pixel.red;
500
+
501
+ finished_pixel.green = blended_pixel.alpha * blended_pixel.green + (1 - blended_pixel.alpha)
502
+ * dest_pixel.green;
503
+
504
+ finished_pixel.blue = blended_pixel.alpha * blended_pixel.blue + (1 - blended_pixel.alpha)
505
+ * dest_pixel.blue;
506
+
507
+
508
+ finished_pixel.alpha = blended_pixel.alpha * blended_pixel.alpha + (1 - blended_pixel.alpha)
509
+ * dest_pixel.alpha;
510
+
511
+
512
+ return finished_pixel;
513
+ }
514
+
515
+ /* NEW from utils.c */
516
+ float *
517
+ allocate_texture(int width, int height)
518
+ {
519
+ float * new_texture;
520
+ int mval;
521
+
522
+ mval = 4 * width * height * sizeof(float);
523
+ // assert(mval > 0);
524
+
525
+ new_texture = malloc(mval);
526
+
527
+ return new_texture;
528
+ }
529
+
530
+ /* get information from texture */
531
+ void
532
+ get_texture_info(VALUE image, texture_info * tex)
533
+ {
534
+ VALUE info, gc_state_off;
535
+ int toppos, leftpos;
536
+ float top, left;
537
+ cache_entry * entry;
538
+
539
+ /* hack to prevent segfault */
540
+ gc_state_off = rb_gc_disable();
541
+
542
+ tex->width = FIX2INT(rb_funcall(image, rb_intern("width"), 0));
543
+ tex->height = FIX2INT(rb_funcall(image, rb_intern("height"), 0));
544
+
545
+ /* ensure gl_tex_info returns non nil */
546
+ info = check_for_texture_info(image);
547
+
548
+ top = NUM2DBL(rb_funcall(info, rb_intern("top"), 0));
549
+ left = NUM2DBL(rb_funcall(info, rb_intern("left"), 0));
550
+ tex->tname = FIX2INT(rb_funcall(info ,rb_intern("tex_name"),0));
551
+
552
+ /* search for texture in cache (& create if not extant) */
553
+ entry = find_or_create_cache_entry(tex->tname);
554
+
555
+ tex->td_array = entry->tdata;
556
+ tex->yincr = entry->sidelength;
557
+
558
+ /* scratch variables */
559
+ toppos = ROUND(top * entry->sidelength * entry->sidelength);
560
+ leftpos = ROUND(left * entry->sidelength);
561
+
562
+ /* find the first pixel for the image */
563
+ tex->firstpixel = (int)(toppos + leftpos);
564
+
565
+ tex->x_offset = ROUND(left * tex->yincr);
566
+ tex->y_offset = ROUND(top * tex->yincr);
567
+
568
+ /* save the associated Gosu::Image */
569
+ tex->image = image;
570
+
571
+ /* only enable gc if was enabled on function entry */
572
+ if(!gc_state_off) rb_gc_enable();
573
+ }
574
+
575
+ /* ensure gl_tex_info returns non nil */
576
+ VALUE
577
+ check_for_texture_info(VALUE image)
578
+ {
579
+ VALUE info;
580
+
581
+ info = rb_funcall(image, rb_intern("gl_tex_info"), 0);
582
+
583
+ if(NIL_P(info)) {
584
+ VALUE image_name = rb_inspect(image);
585
+ int width = FIX2INT(rb_funcall(image, rb_intern("width"), 0));
586
+ int height = FIX2INT(rb_funcall(image, rb_intern("height"), 0));
587
+
588
+ rb_raise(rb_eException, "Error: gl_tex_info returns nil for %s (%i x %i). Could be caused by "
589
+ "very large image or Gosu bug. Try updating Gosu or use a smaller image. Note: TexPlay should"
590
+ " be able to work with %i x %i images.",
591
+ StringValuePtr(image_name), width, height, max_quad_size() - 2, max_quad_size() - 2);
592
+ }
593
+
594
+ return info;
595
+ }
596
+
597
+ /* responsible for syncing a subimage to gl */
598
+ void
599
+ sync_to_gl(int tex_name, int x_offset, int y_offset, int width, int height, void * sub)
600
+ {
601
+ /* set the opengl texture context */
602
+ glEnable(GL_TEXTURE_2D);
603
+ glBindTexture(GL_TEXTURE_2D, tex_name);
604
+
605
+ /* sync subtexture */
606
+ glTexSubImage2D(GL_TEXTURE_2D, 0, x_offset, y_offset, width, height,
607
+ GL_RGBA, GL_FLOAT, sub);
608
+
609
+ glDisable(GL_TEXTURE_2D);
610
+ }
611
+
612
+ void
613
+ create_subtexture_and_sync_to_gl(image_bounds * img_bounds, texture_info * tex)
614
+ {
615
+ /* image vars */
616
+ int xbound, ybound;
617
+ float * sub = NULL;
618
+
619
+ /* make the current action's boundaries sane; left until this point because we only
620
+ know height/width here */
621
+ constrain_boundaries(&img_bounds->xmin, &img_bounds->ymin, &img_bounds->xmax, &img_bounds->ymax,
622
+ tex->width, tex->height);
623
+
624
+ /* helper variables */
625
+ ybound = img_bounds->ymax - img_bounds->ymin + 1;
626
+ xbound = img_bounds->xmax - img_bounds->xmin + 1;
627
+
628
+ sub = get_image_chunk(tex, img_bounds->xmin, img_bounds->ymin, img_bounds->xmax, img_bounds->ymax);
629
+
630
+ sync_to_gl(tex->tname, tex->x_offset + img_bounds->xmin, tex->y_offset + img_bounds->ymin, xbound,
631
+ ybound, (void*)sub);
632
+
633
+ free(sub);
634
+ }
635
+
636
+ float *
637
+ get_image_chunk(texture_info * tex, int xmin, int ymin, int xmax, int ymax)
638
+ {
639
+ int xbound;
640
+ int ybound;
641
+ float * image_buf = NULL;
642
+
643
+ constrain_boundaries(&xmin, &ymin, &xmax, &ymax, tex->width, tex->height);
644
+
645
+ xbound = xmax - xmin + 1;
646
+ ybound = ymax - ymin + 1;
647
+
648
+ image_buf = allocate_texture(xbound, ybound);
649
+
650
+ for(int y = 0; y < ybound; y++)
651
+ for(int x = 0; x < xbound; x++) {
652
+ int buf_index = 4 * (x + y * xbound);
653
+
654
+ int offset = calc_pixel_offset(tex, x + xmin, y + ymin);
655
+
656
+ color_copy(tex->td_array + offset, image_buf + buf_index);
657
+ }
658
+
659
+ return image_buf;
660
+ }