static void prepare_alpha_blend(action_struct * cur) { if(has_optional_hash_arg(cur->hash_arg, "alpha_blend")) { VALUE blend_mode = get_from_hash(cur->hash_arg, "alpha_blend"); /* true is equivalent to default blend mode, 'source' */ if(blend_mode == Qtrue) blend_mode = string2sym("source"); /* where false or nil is passed */ if(!RTEST(blend_mode)) { cur->pen.alpha_blend = false; return; } cur->pen.alpha_blend = true; Check_Type(blend_mode, T_SYMBOL); if(blend_mode == string2sym("source")) { cur->pen.alpha_blend_mode = source; } else if(blend_mode == string2sym("dest")) { cur->pen.alpha_blend_mode = dest; } else if(blend_mode == string2sym("source_with_fixed_alpha")) { cur->pen.alpha_blend_mode = source_with_fixed_alpha; } else if(blend_mode == string2sym("dest_with_fixed_alpha")) { cur->pen.alpha_blend_mode = dest_with_fixed_alpha; } else rb_raise(rb_eArgError, "unrecognized blend mode: %s\n.", sym2string(blend_mode)); } }
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)); } } }
/** splice algorithm **/ void splice_do_action(int x0, int y0, int cx1, int cy1, int cx2, int cy2, texture_info * splice_tex, texture_info * tex, VALUE hash_arg, texplay_sync sync_mode, bool primary, action_struct * payload) { action_struct cur; int xbound; int ybound; rgba chromakey; float * image_buf = NULL; bool inverse_chroma = false; bool same_image = false; bool has_chroma = false; constrain_boundaries(&cx1, &cy1, &cx2, &cy2, splice_tex->width, splice_tex->height); xbound = cx2 - cx1 + 1; ybound = cy2 - cy1 + 1; draw_prologue(&cur, tex, x0, y0, x0 + xbound, y0 + ybound, &hash_arg, sync_mode, primary, &payload); if(has_optional_hash_arg(hash_arg, "chroma_key")) { VALUE c = get_from_hash(hash_arg, "chroma_key"); chromakey = convert_rb_color_to_rgba(c); has_chroma = true; } else if(has_optional_hash_arg(hash_arg, "chroma_key_not")) { chromakey = convert_rb_color_to_rgba(get_from_hash(hash_arg, "chroma_key_not")); inverse_chroma = true; has_chroma = true; } if(splice_tex->image == tex->image) same_image = true; /* NB: we do not use this in the general case since it's almost 1.5 times as slow. It is necessary for splicing from/to the same region of pixels though. */ if(same_image) image_buf = get_image_chunk(splice_tex, cx1, cy1, cx2, cy2); for(int y = 0; y < ybound; y++) for(int x = 0; x < xbound; x++) { if(!same_image) payload->color = get_pixel_color(splice_tex, cx1 + x, cy1 + y); else payload->color = get_pixel_color_from_chunk(image_buf, xbound, ybound, x, y); if(has_chroma) { bool chroma_match = cmp_color(payload->color, chromakey); /* look at released 0.2.0 code to see how USED to do this. this is now a simplified boolean expression (XOR) */ if(chroma_match == inverse_chroma) set_pixel_color_with_style(payload, tex, x0 + x, y0 + y); } else set_pixel_color_with_style(payload, tex, x0 + x, y0 + y); } if(same_image) free(image_buf); draw_epilogue(&cur, tex, primary); }
/* 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); }
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"); } }