/* set action color to return value of color_control proc */ static void prepare_color_control(action_struct * cur) { if(is_a_hash(cur->hash_arg)) { VALUE try_val = get_from_hash(cur->hash_arg, "color_control"); if(rb_respond_to(try_val, rb_intern("call"))) { cur->pen.color_control_proc = try_val; cur->pen.color_control_arity = FIX2INT(rb_funcall(try_val, rb_intern("arity"), 0)); cur->pen.has_color_control_proc = true; } else if(is_a_hash(try_val)) { VALUE try_add = get_from_hash(try_val, "add"); VALUE try_mult = get_from_hash(try_val, "mult"); if(is_an_array(try_add)) { cur->pen.color_add = convert_rb_color_to_rgba(try_add); cur->pen.has_color_control_transform = true; } if(is_an_array(try_mult)) { cur->pen.color_mult = convert_rb_color_to_rgba(try_mult); cur->pen.has_color_control_transform = true; } } } }
void glow_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg, texplay_sync sync_mode, bool primary, action_struct * payload) { action_struct cur; rgba seed_color; texture_info fill_image; texture_info * fill_image_ptr = NULL; if(!bound_by_rect(x, y, 0, 0, tex->width, tex->height)) return; draw_prologue(&cur, tex, 0, 0, 1024, 1024, &hash_arg, sync_mode, primary, &payload); /* fill hates alpha_blend so let's turn it off */ payload->pen.alpha_blend = false; if(is_a_hash(hash_arg)) { VALUE try_image = get_from_hash(hash_arg, "texture"); if(is_gosu_image(try_image)) { get_texture_info(try_image, &fill_image); fill_image_ptr = &fill_image; } } seed_color = get_pixel_color(tex, x, y); /* last argument is pointer to texture fill data (if it exists) or NULL (if it doesn't) */ glow_floodFill( x, y, &seed_color, &cur, tex, fill_image_ptr ); draw_epilogue(&cur, tex, primary); }
static void prepare_fill_texture(action_struct * payload) { if(is_a_hash(payload->hash_arg)) { VALUE try_image = get_from_hash(payload->hash_arg, "texture"); if(is_gosu_image(try_image)) { get_texture_info(try_image, &payload->pen.source_tex); payload->pen.has_source_texture = true; } } }
/** rectangle algorithm **/ void rect_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) { action_struct cur; bool fill = false; int thickness = 1; draw_prologue(&cur, tex, x1, y1, x2, y2, &hash_arg, sync_mode, primary, &payload); if(is_a_hash(hash_arg)) { /* make our private copy of the hash so we can mess with it */ hash_arg = rb_obj_dup(hash_arg); if(RTEST(get_from_hash(hash_arg, "fill")) || RTEST(get_from_hash(hash_arg, "filled"))) { fill = true; /* since we're filling the rect, line thickness is irrelevant */ delete_from_hash(hash_arg, "thickness"); } else if(RTEST(get_from_hash(hash_arg, "thickness"))) { thickness = NUM2INT(get_from_hash(hash_arg, "thickness")); /* TO DO: find a better way of doing this */ if(thickness > 1) { cur.xmin = x1 - thickness / 2; cur.ymin = y1 - thickness / 2; cur.xmax = x2 + thickness / 2 + 1; cur.ymax = y2 + thickness / 2 + 1; } } } if(!fill) { line_do_action(x1, y1, x2, y1, tex, hash_arg, no_sync, false, payload); line_do_action(x1, y1, x1, y2, tex, hash_arg, no_sync, false, payload); line_do_action(x1, y2, x2, y2, tex, hash_arg, no_sync, false, payload); line_do_action(x2, y1, x2, y2, tex, hash_arg, no_sync, false, payload); } else { if(y1 > y2) SWAP(y1, y2); for(int y = y1; y <= y2; y++) line_do_action(x1, y, x2, y, tex, hash_arg, no_sync, false, payload); } draw_epilogue(&cur, tex, primary); }
/* regular polygon algorithm */ void ngon_do_action(int x, int y, int r, int num_sides, texture_info * tex, VALUE hash_arg, texplay_sync sync_mode, bool primary, action_struct * payload) { action_struct cur; int x1, y1, x2, y2, x0, y0; int thickness; float angle = 0; draw_prologue(&cur, tex, x - r, y - r, x + r, y + r, &hash_arg, sync_mode, primary, &payload); if(is_a_hash(hash_arg)) { if(RTEST(get_from_hash(hash_arg, "thickness"))) { thickness = NUM2INT(get_from_hash(hash_arg, "thickness")); /* TO DO: find a better way of doing this */ cur.xmin = x - r - thickness / 2; cur.ymin = y - r - thickness / 2; cur.xmax = x + r + thickness / 2; cur.ymax = y + r + thickness / 2; } if(RTEST(get_from_hash(hash_arg, "start_angle"))) { angle = NUM2INT(get_from_hash(hash_arg, "start_angle")) / 360.0 * 2 * PI; } } /* calculate first point */ x0 = x1 = x + r * cos(angle); y0 = y1 = y + r * sin(angle); for(int n = 0; n < num_sides; n++) { x2 = x + r * cos((2 * PI / num_sides) * n + angle); y2 = y + r * sin((2 * PI / num_sides) * n + angle); line_do_action(x1, y1, x2, y2, tex, hash_arg, no_sync, false, payload); x1 = x2; y1 = y2; } line_do_action(x0, y0, x1, y1, tex, hash_arg, no_sync, false, payload); draw_epilogue(&cur, tex, primary); }
static void prepare_drawing_mode(action_struct * cur) { if(is_a_hash(cur->hash_arg)) { /* drawing mode */ if(has_optional_hash_arg(cur->hash_arg, "mode")) { cur->pen.has_drawing_mode = true; VALUE draw_mode = get_from_hash(cur->hash_arg, "mode"); Check_Type(draw_mode, T_SYMBOL); if(draw_mode == string2sym("default")) { cur->pen.has_drawing_mode = false; return; } else if(draw_mode == string2sym("clear")) cur->pen.drawing_mode = clear; else if(draw_mode == string2sym("copy")) cur->pen.drawing_mode = copy; else if(draw_mode == string2sym("noop")) cur->pen.drawing_mode = noop; else if(draw_mode == string2sym("set")) cur->pen.drawing_mode = set; else if(draw_mode == string2sym("copy_inverted")) cur->pen.drawing_mode = copy_inverted; else if(draw_mode == string2sym("invert")) cur->pen.drawing_mode = invert; else if(draw_mode == string2sym("and_reverse")) cur->pen.drawing_mode = and_reverse; else if(draw_mode == string2sym("and")) cur->pen.drawing_mode = and; else if(draw_mode == string2sym("or")) cur->pen.drawing_mode = or; else if(draw_mode == string2sym("nand")) cur->pen.drawing_mode = nand; else if(draw_mode == string2sym("nor")) cur->pen.drawing_mode = nor; else if(draw_mode == string2sym("xor")) cur->pen.drawing_mode = xor; else if(draw_mode == string2sym("equiv")) cur->pen.drawing_mode = equiv; else if(draw_mode == string2sym("and_inverted")) cur->pen.drawing_mode = and_inverted; else if(draw_mode == string2sym("or_inverted")) cur->pen.drawing_mode = or_inverted; else if(draw_mode == string2sym("additive")) cur->pen.drawing_mode = additive; else if(draw_mode == string2sym("multiply")) cur->pen.drawing_mode = multiply; else if(draw_mode == string2sym("screen")) cur->pen.drawing_mode = screen; else if(draw_mode == string2sym("overlay")) cur->pen.drawing_mode = overlay; else if(draw_mode == string2sym("darken")) cur->pen.drawing_mode = darken; else if(draw_mode == string2sym("lighten")) cur->pen.drawing_mode = lighten; else if(draw_mode == string2sym("color_dodge")) cur->pen.drawing_mode = color_dodge; else if(draw_mode == string2sym("color_burn")) cur->pen.drawing_mode = color_burn; else if(draw_mode == string2sym("hard_light")) cur->pen.drawing_mode = hard_light; else if(draw_mode == string2sym("soft_light")) cur->pen.drawing_mode = soft_light; else if(draw_mode == string2sym("difference")) cur->pen.drawing_mode = difference; else if(draw_mode == string2sym("exclusion")) cur->pen.drawing_mode = exclusion; else rb_raise(rb_eArgError, "unrecognized drawing mode: %s\n.", sym2string(draw_mode)); } } }
/* TODO: fix this function below, it's too ugly and bulky and weird **/ static void process_common_hash_args(action_struct * cur, VALUE * hash_arg, sync_ sync_mode, bool primary) { VALUE user_defaults; VALUE hash_blend; /* if a hash doesn't exist then create one */ if(!is_a_hash(*hash_arg)) *hash_arg = rb_hash_new(); /* init the action to default values */ initialize_action_struct(cur, *hash_arg, sync_mode); /* get the user default options & merge with given options */ user_defaults = get_image_local(cur->tex->image, USER_DEFAULTS); hash_blend = rb_funcall(user_defaults, rb_intern("merge"), 1, *hash_arg); rb_funcall(*hash_arg, rb_intern("merge!"), 1, hash_blend); if(has_optional_hash_arg(*hash_arg, "color")) { VALUE c = get_from_hash(*hash_arg, "color"); cur->color = convert_rb_color_to_rgba(c); if(c == string2sym("random")) { set_hash_value(*hash_arg, "color", convert_rgba_to_rb_color(&cur->color)); } } /* shadows */ if(RTEST(get_from_hash(*hash_arg, "shadow"))) { cur->pen.color_mult.red = 0.66; cur->pen.color_mult.green = 0.66; cur->pen.color_mult.blue = 0.66; cur->pen.color_mult.alpha = 1; cur->pen.has_color_control_transform = true; } /* tolerance */ if(RTEST(get_from_hash(*hash_arg, "tolerance"))) { cur->pen.tolerance = NUM2DBL(get_from_hash(*hash_arg, "tolerance")); /* maximum length of hypotonese extended in 4-space (color space) is sqrt(4) */ if (cur->pen.tolerance >= 2) cur->pen.tolerance = 2; if (cur->pen.tolerance < 0) cur->pen.tolerance = 0; cur->pen.has_tolerance = true; } /* lerp */ if(RTEST(get_from_hash(*hash_arg, "lerp"))) { cur->pen.lerp = NUM2DBL(get_from_hash(*hash_arg, "lerp")); /* bounds */ if(cur->pen.lerp > 1.0) cur->pen.lerp = 1.0; if(cur->pen.lerp < 0.0) cur->pen.lerp = 0.0; cur->pen.has_lerp = true; } /* sync mode */ if(has_optional_hash_arg(*hash_arg, "sync_mode")) { VALUE user_sync_mode = get_from_hash(*hash_arg, "sync_mode"); Check_Type(user_sync_mode, T_SYMBOL); if(user_sync_mode == string2sym("lazy_sync")) cur->sync_mode = lazy_sync; else if(user_sync_mode == string2sym("eager_sync")) cur->sync_mode = eager_sync; else if(user_sync_mode == string2sym("no_sync")) cur->sync_mode = no_sync; else rb_raise(rb_eArgError, "unrecognized sync mode: %s\n. Allowable modes are " ":lazy_sync, :eager_sync, :no_sync.", sym2string(user_sync_mode)); delete_from_hash(*hash_arg, "sync_mode"); } /* prepare color selection */ prepare_color_select(cur); /* process drawing mode */ prepare_drawing_mode(cur); /* process the color_control block or transform (if there is one) */ prepare_color_control(cur); /* process the filling texture (if there is one) */ prepare_fill_texture(cur); /* does the user want to blend alpha values ? */ prepare_alpha_blend(cur); }
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); }
/** midpoint circle algorithm **/ void circle_do_action(int x1, int y1, int r, texture_info * tex, VALUE hash_arg, texplay_sync sync_mode, bool primary, action_struct * payload) { int x, y; float p; action_struct cur; bool fill = false; draw_prologue(&cur, tex, x1 - r, y1 - r, x1 + r, y1 + r, &hash_arg, sync_mode, primary, &payload); if(is_a_hash(hash_arg)) { /* make our private copy of the hash so we can mess with it */ hash_arg = rb_obj_dup(hash_arg); if(RTEST(get_from_hash(hash_arg, "fill")) || RTEST(get_from_hash(hash_arg, "filled"))) { fill = true; /* to prevent infinite recursion set line thickness to 1 :D NB: a filled circle uses lines and a thick line uses filled circles :D */ delete_from_hash(hash_arg, "thickness"); } } x = 0 ; y = r; p = 5 / 4 - r; if(!fill) { while (x <= y) { set_pixel_color_with_style(payload, tex, x1 + x, y1 + y); set_pixel_color_with_style(payload, tex, x1 + x, y1 - y); set_pixel_color_with_style(payload, tex, x1 - x, y1 + y); set_pixel_color_with_style(payload, tex, x1 - x, y1 - y); set_pixel_color_with_style(payload, tex, x1 + y, y1 + x); set_pixel_color_with_style(payload, tex, x1 + y, y1 - x); set_pixel_color_with_style(payload, tex, x1 - y, y1 + x); set_pixel_color_with_style(payload, tex, x1 - y, y1 - x); if (p < 0) { p += 2 * x + 3; } else { y--; p += 2 * (x - y) + 5; } x++; } } else { while (x <= y) { line_do_action(x1 - x, y1 + y, x1 + x, y1 + y, tex, hash_arg, no_sync, false, payload); line_do_action(x1 - x, y1 - y, x1 + x, y1 - y, tex, hash_arg, no_sync, false, payload); line_do_action(x1 - y, y1 + x, x1 + y, y1 + x, tex, hash_arg, no_sync, false, payload); line_do_action(x1 - y, y1 - x, x1 + y, y1 - x, tex, hash_arg, no_sync, false, payload); if (p < 0) { p += 2 * x + 3; } else { y--; p += 2 * (x - y) + 5; } x++; } } draw_epilogue(&cur, tex, primary); }
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); }