texplay 0.2.710 → 0.2.722

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