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.
- data/CHANGELOG +4 -0
- data/README.markdown +0 -0
- data/Rakefile +31 -25
- data/examples/example_color_transform.rb +3 -3
- data/examples/example_lsystem.rb +3 -3
- data/examples/example_splice.rb +27 -33
- data/examples/example_weird.rb +29 -0
- data/examples/media/bird.png +0 -0
- data/examples/media/gob.png +0 -0
- data/examples/media/green.png +0 -0
- data/examples/media/maria.png +0 -0
- data/examples/media/rose.bmp +0 -0
- data/ext/texplay/actions.c +13 -419
- data/ext/texplay/bindings.c +1 -0
- data/ext/texplay/extconf.rb +3 -5
- data/ext/texplay/gen_eval.c +0 -0
- data/ext/texplay/gen_eval.h +0 -0
- data/ext/texplay/graphics_utils.c +660 -0
- data/ext/texplay/graphics_utils.h +23 -0
- data/ext/texplay/utils.c +35 -258
- data/ext/texplay/utils.h +2 -2
- data/lib/texplay-contrib.rb +0 -0
- data/lib/texplay.rb +0 -0
- data/lib/texplay/version.rb +1 -1
- metadata +58 -44
- data/examples/example_blur.rb +0 -59
- data/examples/example_fill_test.rb +0 -106
- data/examples/example_modify.rb +0 -40
- data/examples/example_sine.rb +0 -55
- data/examples/media/platform.png +0 -0
data/ext/texplay/bindings.c
CHANGED
data/ext/texplay/extconf.rb
CHANGED
|
@@ -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')
|
data/ext/texplay/gen_eval.c
CHANGED
|
File without changes
|
data/ext/texplay/gen_eval.h
CHANGED
|
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(¤t_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(¤t_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
|
+
}
|