/** bezier curve algorithm **/ static void bezier_point(VALUE points, float u, float * x, float * y, int n, int format, int draw_offset_x, int draw_offset_y) { int xy_index; double sumx = 0, sumy = 0; for(int k = 0; k < n; k++) { switch(format) { case POINT_FORMAT: sumx += NUM2DBL(point_x(get_from_array(points, k))) * bernstein(n - 1, k, u); sumy += NUM2DBL(point_y(get_from_array(points, k))) * bernstein(n - 1, k, u); break; case SIMPLE_FORMAT: xy_index = k * 2; sumx += NUM2DBL(get_from_array(points, xy_index)) * bernstein(n - 1, k, u); sumy += NUM2DBL(get_from_array(points, xy_index + 1)) * bernstein(n - 1, k, u); break; default: rb_raise(rb_eArgError, "pixel format must be either POINT_FORMAT or SIMPLE_FORMAT"); } } *x = sumx + draw_offset_x; *y = sumy + draw_offset_y; }
static void process_x_y_pairs(VALUE image, int num_pairs, VALUE * argv, ...) { va_list ap; int i; int draw_offset_x; int draw_offset_y; VALUE offset_val; offset_val = get_image_local(image, DRAW_OFFSET); draw_offset_x = NUM2INT(get_from_array(offset_val, 0)); draw_offset_y = NUM2INT(get_from_array(offset_val, 1)); va_start(ap, argv); if(is_a_point(argv[0])) { for(i = 0; i < num_pairs; i++) { int *x_ptr, *y_ptr; x_ptr = va_arg(ap, int*); y_ptr = va_arg(ap, int*); *x_ptr = NUM2INT(rb_funcall(argv[i], rb_intern("x"), 0)) + draw_offset_x; *y_ptr = NUM2INT(rb_funcall(argv[i], rb_intern("y"), 0)) + draw_offset_y; } } else { for(i = 0; i < (num_pairs * 2); i+=2) {
bool is_rb_raw_color(VALUE cval) { return TYPE(cval) == T_ARRAY && is_a_num(get_from_array(cval, 0)) && is_a_num(get_from_array(cval, 1)) && is_a_num(get_from_array(cval, 2)) && is_a_num(get_from_array(cval, 3)); }
rgba convert_image_local_color_to_rgba(VALUE image) { rgba color; VALUE image_local_color = get_image_local(image, IMAGE_COLOR); color.red = NUM2DBL(get_from_array(image_local_color, red)); color.green = NUM2DBL(get_from_array(image_local_color, green)); color.blue = NUM2DBL(get_from_array(image_local_color, blue)); color.alpha = NUM2DBL(get_from_array(image_local_color, alpha)); return color; }
void update_lazy_bounds(action_struct * cur, texture_info * tex) { /* only update global bounds if we're doing a lazy_sync */ if (cur->sync_mode != lazy_sync) return; VALUE lazy_bounds; int xmin, ymin, xmax, ymax; lazy_bounds = get_image_local(tex->image, LAZY_BOUNDS); xmin = INT2FIX(MIN(cur->xmin, FIX2INT(get_from_array(lazy_bounds, 0)))); ymin = INT2FIX(MIN(cur->ymin, FIX2INT(get_from_array(lazy_bounds, 1)))); xmax = INT2FIX(MAX(cur->xmax, FIX2INT(get_from_array(lazy_bounds, 2)))); ymax = INT2FIX(MAX(cur->ymax, FIX2INT(get_from_array(lazy_bounds, 3)))); set_array_value(lazy_bounds, 0, xmin); set_array_value(lazy_bounds, 1, ymin); set_array_value(lazy_bounds, 2, xmax); set_array_value(lazy_bounds, 3, ymax); }
static void process_select_color_list(rgba_list * clist, VALUE try_color) { /* is a general array of colors? i.e [:red, Gosu::Color::RED, [1,1,1,1] ] */ if (TYPE(try_color) == T_ARRAY && not_rb_raw_color(try_color)) { int num_colors = RARRAY_LEN(try_color); if (num_colors > RGBA_LIST_SIZE) rb_raise(rb_eArgError, "Too many colors given in array. Maximum is %d\n. Got %d\n", RGBA_LIST_SIZE, num_colors); for (int i = 0; i < RARRAY_LEN(try_color); ++i) { clist->colors[i] = convert_rb_color_to_rgba(get_from_array(try_color, i)); } clist->size = num_colors; } /* is a single color value? i.e :red, [1,1,1,1], Gosu::Color::GREEN */ else { clist->colors[0] = convert_rb_color_to_rgba(try_color); clist->size = 1; } }
bool not_rb_raw_color(VALUE cval) { return TYPE(cval) != T_ARRAY || !is_a_num(get_from_array(cval, 0)); }
VALUE get_image_local(VALUE image, int name) { VALUE image_local; VALUE val; init_image_local(image); /* this var holds all the image local variables in an array */ image_local = rb_iv_get(image, "__image_local__"); /* a particular image_local variable */ val = get_from_array(image_local, name); /* if the variable exists then return it */ if(!NIL_P(val)) return val; /* otherwise initialize the variable and then return it */ else { switch(name) { VALUE init_offset, init_bounds, init_color, init_defaults; case DRAW_OFFSET: init_offset = rb_ary_new2(2); set_array_value(init_offset, 0, INT2FIX(0)); set_array_value(init_offset, 1, INT2FIX(0)); set_array_value(image_local, DRAW_OFFSET, init_offset); return init_offset; break; case LAZY_BOUNDS: init_bounds = rb_ary_new2(4); set_array_value(init_bounds, 0, INT2FIX(XMAX_OOB)); set_array_value(init_bounds, 1, INT2FIX(YMAX_OOB)); set_array_value(init_bounds, 2, INT2FIX(XMIN_OOB)); set_array_value(init_bounds, 3, INT2FIX(YMIN_OOB)); set_array_value(image_local, LAZY_BOUNDS, init_bounds); return init_bounds; break; case IMAGE_COLOR: init_color = rb_ary_new2(4); set_array_value(init_color, 0, rb_float_new(1.0)); set_array_value(init_color, 1, rb_float_new(1.0)); set_array_value(init_color, 2, rb_float_new(1.0)); set_array_value(init_color, 3, rb_float_new(1.0)); set_array_value(image_local, IMAGE_COLOR, init_color); return init_color; break; case USER_DEFAULTS: init_defaults = rb_hash_new(); set_array_value(image_local, USER_DEFAULTS, init_defaults); return init_defaults; break; default: rb_raise(rb_eArgError, "unrecognized image_local variable number. got %d", name); } } /* never reached */ return Qnil; }
void bezier_do_action(VALUE points, texture_info * tex, VALUE hash_arg, texplay_sync sync_mode, bool primary, action_struct * payload) { float u = 0.0; action_struct cur; float x1, y1, x2, y2; int first_x, first_y; int format; int num_point_pairs; bool closed = false; VALUE offset_val; int draw_offset_x, draw_offset_y; /* defaults to 200 (1 / 0.005) samples per curve */ float step_size = 0.005; draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload); /* calculate offset */ offset_val = get_image_local(tex->image, DRAW_OFFSET); draw_offset_x = NUM2INT(get_from_array(offset_val, 0)); draw_offset_y = NUM2INT(get_from_array(offset_val, 1)); if(is_a_hash(hash_arg)) { /* if the polyline is 'closed' make the last point the first */ if(RTEST(get_from_hash(hash_arg, "closed")) || RTEST(get_from_hash(hash_arg, "close"))) { /* so that our additional point is not persistent */ points = rb_obj_dup(points); closed = true; } /* number of points to sample */ if(RTEST(get_from_hash(hash_arg, "sample_size"))) { VALUE c = get_from_hash(hash_arg, "sample_size"); Check_Type(c, T_FIXNUM); step_size = 1.0 / (float)FIX2INT(c); } } if(is_a_point(get_from_array(points, 0))) { format = POINT_FORMAT; if(closed) rb_ary_push(points, get_from_array(points, 0)); num_point_pairs = RARRAY_LEN(points); } else { format = SIMPLE_FORMAT; /* ensure points are given in pairs */ if(RARRAY_LEN(points) % 2) rb_raise(rb_eArgError, "bezier needs an even number of points. got %d\n", (int)RARRAY_LEN(points)); if(closed) { rb_ary_push(points, get_from_array(points, 0)); rb_ary_push(points, get_from_array(points, 1)); } num_point_pairs = RARRAY_LEN(points) / 2; } if(num_point_pairs > 17) rb_raise(rb_eArgError, "too many points for bezier curve. 17 points is current maximum. got %d\n", num_point_pairs); /* get the first point */ bezier_point(points, 0, &x1, &y1, num_point_pairs, format, draw_offset_x, draw_offset_y); /* save it so we can link up with last point properly if the curve is 'closed' */ first_x = x1; first_y = y1; while(u <= 1) { bezier_point(points, u, &x2, &y2, num_point_pairs, format, draw_offset_x, draw_offset_y); line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload); /* update drawing rectangle */ update_bounds(payload, x1, y1, x2, y2); x1 = x2; y1 = y2; u += step_size; } /* sometimes beziers dont close properly, so we'll ensure it's closed */ if(closed) line_do_action(x2, y2, first_x, first_y, tex, hash_arg, no_sync, false, payload); draw_epilogue(&cur, tex, primary); }
trace_match line_do_action(int x1, int y1, int x2, int y2, texture_info * tex, VALUE hash_arg, texplay_sync sync_mode, bool primary, action_struct * payload) { int x, y, W, H, F; int xinc, yinc; action_struct cur; int thickness = 1; bool has_trace = false; rgba trace_color; trace_mode_type trace_mode = no_mode; if(has_optional_hash_arg(hash_arg, "thickness")) thickness = NUM2INT(get_from_hash(hash_arg, "thickness")); if(has_optional_hash_arg(hash_arg, "trace") && primary) { VALUE trace_hash = get_from_hash(hash_arg, "trace"); /* we're tracing (not drawing) */ has_trace = true; /* since we're not drawing, no need to sync */ sync_mode = no_sync; if(has_optional_hash_arg(trace_hash, "until_color")) { VALUE c = get_from_hash(trace_hash, "until_color"); trace_color = convert_rb_color_to_rgba(c); trace_mode = until_color; } else if(has_optional_hash_arg(trace_hash, "while_color")) { VALUE c = get_from_hash(trace_hash, "while_color"); trace_color = convert_rb_color_to_rgba(c); trace_mode = while_color; } } draw_prologue(&cur, tex, x1, y1, x2, y2, &hash_arg, sync_mode, primary, &payload); /* clip the line */ cohen_sutherland_clip(&x1, &y1, &x2, &y2, 0, 0, tex->width - 1, tex->height - 1); W = ABS(x2 - x1); H = ABS(y2 - y1); if(x1 < x2) xinc = 1; else xinc = -1; if(y1 < y2) yinc = 1; else yinc = -1; x = x1; y = y1; if(W >= H) { F = 2 * H - W; while(x != x2) { if(thickness <= 1) { if(!has_trace) set_pixel_color_with_style(payload, tex, x, y); else { rgba c = get_pixel_color(tex, x, y); if (is_trace_match(payload, c, trace_color, trace_mode)) return (trace_match) { x, y, c }; } } else { set_hash_value(hash_arg, "fill", Qtrue); circle_do_action(x, y, thickness / 2, tex, hash_arg, no_sync, false, payload); } if(F < 0) F += 2 * H; else { F += 2 * (H - W); y += yinc; } x += xinc; } } else { F = 2 * W - H; while(y != y2 ) { if(thickness <= 1) { if(!has_trace) set_pixel_color_with_style(payload, tex, x, y); else { rgba c = get_pixel_color(tex, x, y); if (is_trace_match(payload, c, trace_color, trace_mode)) return (trace_match) { x, y, c }; } } else { set_hash_value(hash_arg, "fill", Qtrue); circle_do_action(x, y, thickness / 2, tex, hash_arg, no_sync, false, payload); } if(F < 0) F += 2 * W; else { F += 2 * (W - H); x += xinc; } y += yinc; } } if(thickness <= 1) { if(!has_trace) set_pixel_color_with_style(payload, tex, x2, y2); else { rgba c = get_pixel_color(tex, x, y); if (is_trace_match(payload, c, trace_color, trace_mode)) return (trace_match) { x, y, c }; } } else { set_hash_value(hash_arg, "fill", Qtrue); circle_do_action(x2, y2, thickness / 2, tex, hash_arg, no_sync, false, payload); } draw_epilogue(&cur, tex, primary); return (trace_match) { .x = -9999, .y = -9999, .color = not_a_color_v }; } /** end line **/ /** polyline algorithm **/ /* used by both polyline and bezier */ #define SIMPLE_FORMAT 0 #define POINT_FORMAT 1 /* calculate a single point */ static void polyline_point(VALUE points, int k, int * x, int * y, int format, int draw_offset_x, int draw_offset_y) { int xy_index; switch(format) { case POINT_FORMAT: *x = NUM2INT(point_x(get_from_array(points, k))) + draw_offset_x; *y = NUM2INT(point_y(get_from_array(points, k))) + draw_offset_y; break; case SIMPLE_FORMAT: xy_index = k * 2; *x = NUM2INT(get_from_array(points, xy_index)) + draw_offset_x; *y = NUM2INT(get_from_array(points, xy_index + 1)) + draw_offset_y; break; default: rb_raise(rb_eArgError, "pixel format must be either POINT_FORMAT or SIMPLE_FORMAT"); } }
void polyline_do_action(VALUE points, texture_info * tex, VALUE hash_arg, texplay_sync sync_mode, bool primary, action_struct * payload) { int x1, y1, x2, y2; int format; int num_point_pairs; int k; int draw_offset_y, draw_offset_x; action_struct cur; VALUE offset_val; bool closed = false; draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload); /* calculate offset */ offset_val = get_image_local(tex->image, DRAW_OFFSET); draw_offset_x = NUM2INT(get_from_array(offset_val, 0)); draw_offset_y = NUM2INT(get_from_array(offset_val, 1)); /* if the polyline is 'closed' make the last point the first */ if(is_a_hash(hash_arg)) if(RTEST(get_from_hash(hash_arg, "closed")) || RTEST(get_from_hash(hash_arg, "close"))) { /* so that our additional point is not persistent */ points = rb_obj_dup(points); closed = true; } /* determine format of points */ if(is_a_point(get_from_array(points, 0))) { format = POINT_FORMAT; /* if the polyline is closed to form a polygon then make the last point and first point identical */ if(closed) rb_ary_push(points, get_from_array(points, 0)); num_point_pairs = RARRAY_LEN(points); } else { format = SIMPLE_FORMAT; /* ensure there is an 'x' for every 'y' */ if(RARRAY_LEN(points) % 2) rb_raise(rb_eArgError, "polyline needs an even number of points. got %d\n", (int)RARRAY_LEN(points)); if(closed) { rb_ary_push(points, get_from_array(points, 0)); rb_ary_push(points, get_from_array(points, 1)); } num_point_pairs = RARRAY_LEN(points) / 2; } /* calculate first point */ polyline_point(points, 0, &x1, &y1, format, draw_offset_x, draw_offset_y); /* calc the points and draw the polyline */ for(k = 1; k < num_point_pairs; k++) { polyline_point(points, k, &x2, &y2, format, draw_offset_x, draw_offset_y); line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload); /* update drawing rectangle */ update_bounds(payload, x1, y1, x2, y2); x1 = x2; y1 = y2; } draw_epilogue(&cur, tex, primary); }