/** glow fill algorithm, from the gosu forums **/ static void glow_floodFill( int x, int y, rgba * seed_color, action_struct * cur, texture_info * tex, texture_info * tex2 ) { /* used to flood in both horizontal directions from the given point to form a line. */ int fillL, fillR; int i; /* initialize the flood directions */ fillL = fillR = x; /* flood left until a new color is hit - or the edge of the image */ do { /* for texture filling */ if(tex2) cur->color = get_pixel_color(tex2, fillL % tex2->width, y % tex2->height); /* TWO VERSIONS of below */ /* SLOW BUT MODERN VERSION */ /* set_pixel_color_with_style(cur, tex, fillL, y); */ /* FAST but old version */ set_pixel_color(&cur->color, tex, fillL, y); fillL--; } while( (fillL >= 0 ) && (cmp_color(get_pixel_color(tex, fillL, y), *seed_color)) ); /* flood right until a new color is hit - or the edge of the image */ do { // for texture filling if(tex2) cur->color = get_pixel_color(tex2, fillR % tex2->width, y % tex2->height); /* set_pixel_color_with_style(cur, tex, fillR, y); */ set_pixel_color(&cur->color, tex, fillR, y); fillR++; } while( (fillR < cur->xmax - 1) && (cmp_color(get_pixel_color(tex, fillR, y), *seed_color)) ); /* recurse to the line above and the line below at each point */ for( i = fillL + 1; i < fillR; i++ ) { /* Flood above */ if( ( y > 0 ) && ( cmp_color(get_pixel_color(tex, i, y - 1), *seed_color) ) ) { glow_floodFill( i, y-1, seed_color, cur, tex, tex2 ); } /* flood below */ if( (y < cur->ymax - 1) && (cmp_color(get_pixel_color(tex, i, y + 1), *seed_color) )) { glow_floodFill( i, y+1, seed_color, cur, tex, tex2 ); } } }
/* utility func to manage both kinds of color comparions */ static bool cmp_color_with_or_without_tolerance(rgba c1, rgba c2, action_struct * payload) { return payload->pen.has_tolerance ? cmp_color_with_tolerance(c1, c2, payload->pen.tolerance) : cmp_color(c1, c2); }
/** 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); }
void scan_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 old_color; int y1; bool spanLeft, spanRight; if(!bound_by_rect(x, y, 0, 0, tex->width - 1, tex->height - 1)) return; /* NB: using XMAX_OOB etc since we dont know the drawing area yet; drawing area will be set by update_bounds() function in main loop */ draw_prologue(&cur, tex, XMAX_OOB, YMAX_OOB, XMIN_OOB, YMIN_OOB, &hash_arg, sync_mode, primary, &payload); /* fill hates alpha_blend so let's turn it off */ payload->pen.alpha_blend = false; old_color = get_pixel_color(tex, x, y); if(cmp_color(old_color, cur.color)) return; emptyStack(); if(!push(x, y, tex->width - 1)) return; while(pop(&x, &y, tex->width - 1)) { y1 = y; while(y1 >= 0 && cmp_color(old_color, get_pixel_color(tex, x, y1))) y1--; y1++; spanLeft = spanRight = false; while(y1 < tex->height && cmp_color(old_color, get_pixel_color(tex, x, y1)) ) { set_pixel_color_with_style(payload, tex, x, y1); /* update the drawing rectangle */ update_bounds(payload, x, y1, x, y1); if(!spanLeft && x > 0 && cmp_color(old_color, get_pixel_color(tex, x - 1, y1))) { if(!push(x - 1, y1, tex->width - 1)) return; spanLeft = true; } else if(spanLeft && !cmp_color(old_color, get_pixel_color(tex, x - 1, y1))) { spanLeft = false; } if(!spanRight && x < tex->width - 1 && cmp_color(old_color, get_pixel_color(tex, x + 1, y1))) { if(!push(x + 1, y1, tex->width - 1)) return; spanRight = true; } else if(spanRight && x < tex->width - 1 && !cmp_color(old_color,get_pixel_color(tex, x + 1, y1))) { spanRight = false; } y1++; } } draw_epilogue(&cur, tex, primary); }
void flood_fill_do_action(int x, int y, texture_info * tex, VALUE hash_arg, texplay_sync sync_mode, bool primary, action_struct * payload) { int left, x1, x2, dy; rgba old_color; LINESEGMENT stack[MAXDEPTH], *sp = stack; action_struct cur; int nMinX, nMinY, nMaxX, nMaxY; /* NOTE: 1024 is just a place-holder to indicate maximum possible width/height. Values will be constrained to realistic dimensions by constrain_boundaries() function */ 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; nMinX = cur.xmin; nMinY = cur.ymin; nMaxX = cur.xmax; nMaxY = cur.ymax; old_color = get_pixel_color(tex, x, y); if( cmp_color(old_color, cur.color) ) return; if( x < nMinX || x > nMaxX || y < nMinX || y > nMaxY ) return; PUSH(x, x, y, 1); /* needed in some cases */ PUSH(x, x, y + 1, -1); /* seed segment (popped 1st) */ while( sp > stack ) { POP(x1, x2, y, dy); for( x = x1; x >= nMinX && cmp_color(get_pixel_color(tex, x, y), old_color); --x ) { set_pixel_color_with_style(payload, tex, x, y); } if( x >= x1 ) goto SKIP; left = x + 1; if( left < x1 ) PUSH(y, left, x1 - 1, -dy); /* leak on left? */ x = x1 + 1; do { for( ; x <= nMaxX && cmp_color(get_pixel_color(tex, x, y), old_color); ++x ){ set_pixel_color_with_style(payload, tex, x, y); } PUSH(left, x - 1, y, dy); if( x > x2 + 1 ) PUSH(x2 + 1, x - 1, y, -dy); /* leak on right? */ SKIP: for( ++x; x <= x2 && !cmp_color(get_pixel_color(tex, x, y), old_color); ++x ) {;} left = x; } while( x <= x2 ); } draw_epilogue(&cur, tex, primary); }