#define _GNU_SOURCE #include #include #include #include #include #include #include #include /** * Allocate a new circle. * * @return the new allocated circle object. */ circle_t * circle_new( void ) { circle_t *circle = calloc( 1, sizeof(*circle) ); OBJECT_TYPE( circle ) = OBJECT_TYPE_CIRCLE; return circle; } /** * Free memory allocated to circle. * * @param an allocated circle object. */ void circle_free( circle_t *circle ) { assert( circle != NULL ); free( circle ); } /** * Return the string representation of this circle object. * * @note string is malloc()ated, please free() it later. * * @param circle the object to act on. * * @return string representation. */ char * circle_str( circle_t *circle ) { char *s = NULL; int r; if ( circle == NULL ) r = asprintf( &s, "Circle is NULL!" ); else r = asprintf( &s, "Circle( depth=%d, color=0x%08x, transp=%0.3f, " "center=( %d, %d ), radius=%d )", OBJECT_DEPTH( circle ), COLOR_TO_ARGB( OBJECT_COLOR( circle ) ), OBJECT_TRANSP( circle ), circle->center.x, circle->center.y, circle->radius ); if ( r < 0 ) s = NULL; return s; } /** * Draw pixel in every octant, be carefull to do not paint outside the screen */ static inline void draw_pixel_all_octants( uint16_t x0, uint16_t y0, uint16_t ox, uint16_t oy, surface_t *surface, color_t color, float transp ) { surface_draw_pixel( surface, x0 + ox, y0 + oy, color, transp ); surface_draw_pixel( surface, x0 + ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 - ox, y0 + oy, color, transp ); surface_draw_pixel( surface, x0 - ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 + oy, y0 + ox, color, transp ); surface_draw_pixel( surface, x0 + oy, y0 - ox, color, transp ); surface_draw_pixel( surface, x0 - oy, y0 + ox, color, transp ); surface_draw_pixel( surface, x0 - oy, y0 - ox, color, transp ); } static inline void draw_pixel_all_octants_antialias_out( uint16_t x0, uint16_t y0, uint16_t ox, float oy, surface_t *surface, color_t color, float transp ) { transp = transp + 1 - ( oy - trunc( oy ) ); oy = trunc( oy ) + 1; surface_draw_pixel( surface, x0 + ox, y0 + oy, color, transp ); surface_draw_pixel( surface, x0 + ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 - ox, y0 + oy, color, transp ); surface_draw_pixel( surface, x0 - ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 + oy, y0 + ox, color, transp ); surface_draw_pixel( surface, x0 + oy, y0 - ox, color, transp ); surface_draw_pixel( surface, x0 - oy, y0 + ox, color, transp ); surface_draw_pixel( surface, x0 - oy, y0 - ox, color, transp ); } static inline void draw_pixel_all_octants_antialias_in( uint16_t x0, uint16_t y0, uint16_t ox, float oy, surface_t *surface, color_t color, float transp ) { transp = transp + ( oy - trunc( oy ) ); oy = trunc( oy ); surface_draw_pixel( surface, x0 + ox, y0 + oy, color, transp ); surface_draw_pixel( surface, x0 + ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 - ox, y0 + oy, color, transp ); surface_draw_pixel( surface, x0 - ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 + oy, y0 + ox, color, transp ); surface_draw_pixel( surface, x0 + oy, y0 - ox, color, transp ); surface_draw_pixel( surface, x0 - oy, y0 + ox, color, transp ); surface_draw_pixel( surface, x0 - oy, y0 - ox, color, transp ); } static inline void fill_circle_segments( uint16_t x0, uint16_t y0, uint16_t ox, uint16_t oy, surface_t *surface, color_t color, float transp, uint16_t last_ox, uint16_t last_oy ) { /* avoid numbers < 0 (which will go wild since types are unsigned!) */ uint16_t nxox = min( ox, x0 ); uint16_t nxoy = min( oy, x0 ); uint16_t nyoy = min( oy, y0 ); uint16_t nyox = min( ox, y0 ); if ( last_oy != oy ) { line_draw_horizontal( x0 - nxox, x0 + ox, y0 - nyoy, surface, color, transp); line_draw_horizontal( x0 - nxox, x0 + ox, y0 + oy, surface, color, transp); } if ( ( last_ox != ox ) && ( ox != oy ) ) { if ( ox != 0 ) /* don't paint middle line twice! */ line_draw_horizontal( x0 - nxoy, x0 + oy, y0 - nyox, surface, color, transp); line_draw_horizontal( x0 - nxoy, x0 + oy, y0 + ox, surface, color, transp); } } void circle_draw_trivial( uint16_t x, uint16_t y, uint16_t radius, surface_t *surface, color_t color, float transp, int fill ) { assert( surface != NULL ); assert( surface->buffer != NULL ); float ox, oy, end_x; end_x = ceil( (float)radius / sqrt(2) ); uint16_t last_ox=-1, last_oy=-1; for ( ox=0; ox <= end_x; ox++ ) { oy = sqrt( ( radius * radius ) - ( ox * ox ) ); if ( fill ) { fill_circle_segments( x, y, ox, oy, surface, color, transp, last_ox, last_oy ); last_ox = ox; last_oy = oy; } else draw_pixel_all_octants( x, y, ox, oy, surface, color, transp ); } } void circle_draw_trivial_antialias( uint16_t x, uint16_t y, uint16_t radius, surface_t *surface, color_t color, float transp, int fill ) { assert( surface != NULL ); assert( surface->buffer != NULL ); float ox, oy, end_x; end_x = ceil( (float)radius / sqrt(2) ); uint16_t last_ox=-1, last_oy=-1; for ( ox=0; ox <= end_x; ox++ ) { oy = sqrt( ( radius * radius ) - ( ox * ox ) ); draw_pixel_all_octants_antialias_out( x, y, ox, oy, surface, color, transp ); if ( fill ) { fill_circle_segments( x, y, ox, oy, surface, color, transp, last_ox, last_oy ); /* paint pixels at the edge in full color */ draw_pixel_all_octants( x, y, ox, oy, surface, color, transp ); last_ox = ox; last_oy = oy; } else draw_pixel_all_octants_antialias_in( x, y, ox, oy, surface, color, transp ); } } /** * Internal function to draw circles using the Bresenham (integer only) * algorithm. */ void circle_draw_bresenham( uint16_t x, uint16_t y, uint16_t radius, surface_t *surface, color_t color, float transp, int fill ) { assert( surface != NULL ); assert( surface->buffer != NULL ); int d = 1 - radius; uint16_t oy = radius; uint16_t ox; uint16_t last_oy = -1; uint16_t last_ox = -1; for ( ox=0; ox <= oy; ox++ ) { if ( fill ) { fill_circle_segments( x, y, ox, oy, surface, color, transp, last_ox, last_oy); last_ox = ox; last_oy = oy; } else draw_pixel_all_octants( x, y, ox, oy, surface, color, transp ); d += ox * 2 + 3; if ( d > 0 ) { d += 2 - 2 * oy; oy --; } } } void (*circle_draw_aliased)( uint16_t, uint16_t, uint16_t, surface_t *, color_t, float, int ) = circle_draw_bresenham; void (*circle_draw_antialiased)( uint16_t, uint16_t, uint16_t, surface_t *, color_t, float, int ) = circle_draw_trivial_antialias; /** * Generic circle drawing algoritm. * * You may toggle drawing using circle_draw_aliased and circle_draw_antialiased * variables. * * @param circle the circle object to draw. * @param surface where to draw the object. */ void __circle_draw( circle_t *circle, surface_t *surface ) { assert( surface != NULL ); assert( surface->buffer != NULL ); color_t color = OBJECT_COLOR( circle ); float transp = OBJECT_TRANSP( circle ); uint16_t radius = circle->radius; coord_t center = circle->center; if ( radius == 0 ) { surface_draw_pixel( surface, center.x, center.y, color, transp ); return; } void (*draw)( uint16_t, uint16_t, uint16_t, surface_t *, color_t, float, int ) = NULL; int fill = OBJECT_DRAW_OPTS( circle ) & DRAW_FILL; if ( OBJECT_DRAW_OPTS( circle ) & DRAW_ANTIALIAS ) draw = circle_draw_antialiased; else draw = circle_draw_aliased; draw( center.x, center.y, radius, surface, color, transp, fill ); } void circle_draw( circle_t *circle, surface_t *surface ) { surface_reset_visits( surface ); __circle_draw( circle, surface ); }