#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include /** * Allocate a new ellipse. * * @return the new allocated ellipse object. */ ellipse_t * ellipse_new( void ) { ellipse_t *ellipse = calloc( 1, sizeof(*ellipse) ); OBJECT_TYPE( ellipse ) = OBJECT_TYPE_ELLIPSE; return ellipse; } /** * Free memory allocated to ellipse. * * @param an allocated ellipse object. */ void ellipse_free( ellipse_t *ellipse ) { assert( ellipse != NULL ); free( ellipse ); } /** * Return the string representation of this ellipse object. * * @note string is malloc()ated, please free() it later. * * @param ellipse the object to act on. * * @return string representation. */ char * ellipse_str( ellipse_t *ellipse ) { char *s = NULL; int r; if ( ellipse == NULL ) r = asprintf( &s, "Ellipse is NULL!" ); else r = asprintf( &s, "Ellipse( depth=%d, color=0x%08x, transp=%0.3f, " "center=( %d, %d ), radiusx=%d, radiuxy=%d )", OBJECT_DEPTH( ellipse ), COLOR_TO_ARGB( OBJECT_COLOR( ellipse ) ), OBJECT_TRANSP( ellipse ), ellipse->center.x, ellipse->center.y, ellipse->radiusx, ellipse->radiusy ); if ( r < 0 ) s = NULL; return s; } /** * Draw pixel in four octant, be carefull to do not paint outside the screen */ static inline void draw_pixel_four_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 ); if ( ( oy != 0 ) && ( ox != 0 ) ) { surface_draw_pixel( surface, x0 + ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 - ox, y0 + oy, color, transp ); } } static inline void draw_pixel_four_octants_antialias_in( uint16_t x0, uint16_t y0, float ox, float oy, surface_t *surface, color_t color, float transp ) { float a = oy - trunc( oy ); float b = ox - trunc( ox ); transp = transp + a + b; oy = trunc( oy ); ox = trunc( ox ); surface_draw_pixel( surface, x0 - ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 + ox, y0 + oy, color, transp ); if ( ( oy != 0 ) && ( ox != 0 ) ) { surface_draw_pixel( surface, x0 + ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 - ox, y0 + oy, color, transp ); } } static inline void draw_pixel_four_octants_antialias_out( uint16_t x0, uint16_t y0, float ox, float oy, surface_t *surface, color_t color, float transp ) { float a = oy - trunc( oy ); float b = ox - trunc( ox ); transp = transp + 1 - a - b; oy = trunc( oy ); ox = trunc( ox ); if ( b > 0.00001 ) ox += 1; else oy += 1; surface_draw_pixel( surface, x0 - ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 + ox, y0 + oy, color, transp ); if ( ( oy != 0 ) && ( ox != 0 ) ) { surface_draw_pixel( surface, x0 + ox, y0 - oy, color, transp ); surface_draw_pixel( surface, x0 - ox, y0 + oy, color, transp ); } } static inline void fill_ellipse_segments( uint16_t x0, uint16_t y0, uint16_t ox, uint16_t oy, surface_t *surface, color_t color, float transp, uint16_t last_oy ) { uint16_t nox = min( ox, x0 ); uint16_t noy = min( oy, y0 ); if ( last_oy != oy ) { line_draw_horizontal( x0 - nox, x0 + ox, y0 - noy, surface, color, transp); if ( oy != 0 ) line_draw_horizontal( x0 - nox, x0 + ox, y0 + oy, surface, color, transp); } } void ellipse_draw_trivial( uint16_t x, uint16_t y, uint16_t radiusx, uint16_t radiusy, surface_t *surface, color_t color, float transp, int fill ) { assert( surface != NULL ); assert( surface->buffer != NULL ); float radiusx2 = radiusx * radiusx; float radiusy2 = radiusy * radiusy; float radiusxy2 = radiusx2 * radiusy2; uint16_t last_oy=-1, uninit=-1; float oy, ox; for ( ox=0; ox <= radiusx; ox++ ) { oy = sqrt( radiusxy2 - radiusy2 * ox * ox ) / (float)radiusx; if ( ( last_oy != uninit ) && ( last_oy - 1 > (int)oy ) ) break; if ( fill ) fill_ellipse_segments( x, y, ox, oy, surface, color, transp, last_oy ); else draw_pixel_four_octants( x, y, ox, oy, surface, color, transp ); last_oy = oy; } if ( last_oy != uninit ) oy = last_oy; else oy = 0; for (; oy >= 0; oy-- ) { ox = sqrt( radiusxy2 - radiusx2 * oy * oy ) / radiusy; if ( fill ) fill_ellipse_segments( x, y, ox, oy, surface, color, transp, last_oy ); else draw_pixel_four_octants( x, y, ox, oy, surface, color, transp ); } } void ellipse_draw_trivial_antialias( uint16_t x, uint16_t y, uint16_t radiusx, uint16_t radiusy, surface_t *surface, color_t color, float transp, int fill ) { assert( surface != NULL ); assert( surface->buffer != NULL ); float radiusx2 = radiusx * radiusx; float radiusy2 = radiusy * radiusy; float radiusxy2 = radiusx2 * radiusy2; uint16_t last_oy=-1, uninit=-1; float oy, ox; for ( ox=0; ox <= radiusx; ox++ ) { oy = sqrt( radiusxy2 - radiusy2 * ox * ox ) / (float)radiusx; if ( ( last_oy != uninit ) && ( last_oy - 1 > (int)oy ) ) break; draw_pixel_four_octants_antialias_out( x, y, ox, oy, surface, color, transp ); if ( fill ) { fill_ellipse_segments( x, y, ox, oy, surface, color, transp, last_oy ); draw_pixel_four_octants( x, y, ox, oy, surface, color, transp ); } else draw_pixel_four_octants_antialias_in( x, y, ox, oy, surface, color, transp ); last_oy = oy; } if ( last_oy != uninit ) oy = last_oy; else oy = 0; for (; oy >= 0; oy-- ) { ox = sqrt( radiusxy2 - radiusx2 * oy * oy ) / (float)radiusy; draw_pixel_four_octants_antialias_out( x, y, ox, oy, surface, color, transp ); if ( fill ) { fill_ellipse_segments( x, y, ox, oy, surface, color, transp, last_oy ); draw_pixel_four_octants( x, y, ox, oy, surface, color, transp ); } else draw_pixel_four_octants_antialias_in( x, y, ox, oy, surface, color, transp ); } } /** * Internal function to draw ellipses using the Bresenham (integer only) * algorithm. */ void ellipse_draw_bresenham( uint16_t x, uint16_t y, uint16_t radiusx, uint16_t radiusy, surface_t *surface, color_t color, float transp, int fill ) { assert( surface != NULL ); assert( surface->buffer != NULL ); int32_t S, T; uint16_t ox, oy, last_oy=-1; uint32_t radiusx2, radiusy2; ox = 0; oy = radiusy; radiusx2 = radiusx * radiusx; radiusy2 = radiusy * radiusy; S = radiusx2 * ( 1 - 2 * radiusy ) + 2 * radiusy2; T = radiusy2 - 2 * radiusx2 * ( 2 * radiusy - 1 ); if ( ! fill ) draw_pixel_four_octants( x, y, ox, oy, surface, color, transp ); while ( oy > 0 ) { if ( S < 0 ) { S += 2 * radiusy2 * ( 2 * ox + 3 ); T += 4 * radiusy2 * ( ox + 1 ); ox++; } else if ( T < 0 ) { S += 2 * radiusy2 * ( 2 * ox + 3 ) - 4 * radiusx2 * ( oy - 1 ); T += 4 * radiusy2 * ( ox + 1 ) - 2 * radiusx2 * ( 2 * oy - 3 ); ox++; oy--; } else { S -= 4 * radiusx2 * ( oy - 1 ); T -= 2 * radiusx2 * ( 2 * oy - 3 ); oy--; } if ( fill ) { fill_ellipse_segments( x, y, ox, oy, surface, color, transp, last_oy ); last_oy = oy; } else draw_pixel_four_octants( x, y, ox, oy, surface, color, transp ); } } void (*ellipse_draw_aliased)( uint16_t, uint16_t, uint16_t, uint16_t, surface_t *, color_t, float, int ) = ellipse_draw_bresenham; void (*ellipse_draw_antialiased)( uint16_t, uint16_t, uint16_t, uint16_t, surface_t *, color_t, float, int )= ellipse_draw_trivial_antialias; /** * Generic ellipse drawing algoritm. * * You may toggle drawing using ellipse_draw_aliased and * ellipse_draw_antialiased variables. * * @param ellipse the ellipse object to draw. * @param surface where to draw the object. */ void __ellipse_draw( ellipse_t *ellipse, surface_t *surface ) { assert( surface != NULL ); assert( surface->buffer != NULL ); color_t color = OBJECT_COLOR( ellipse ); float transp = OBJECT_TRANSP( ellipse ); coord_t c = ellipse->center; uint16_t radiusx = ellipse->radiusx; uint16_t radiusy = ellipse->radiusy; int fill = OBJECT_DRAW_OPTS( ellipse ) & DRAW_FILL; void (*draw)( uint16_t, uint16_t, uint16_t, uint16_t, surface_t *, color_t, float, int ) = NULL; if ( radiusx == radiusy ) { circle_t *circle = circle_new(); object_init( OBJECT( circle ), OBJECT_COLOR( ellipse ), OBJECT_TRANSP( ellipse ), OBJECT_DEPTH( ellipse ), OBJECT_DRAW_OPTS( ellipse ) ); circle->radius = radiusx; circle->center = c; __circle_draw( circle, surface ); object_free( OBJECT( circle ) ); return; } else if ( radiusx == 0 ) { uint16_t y0 = ( c.y < radiusy ) ? 0 : ( c.y - radiusy ); line_draw_vertical( c.x, y0, c.y + radiusy, surface, color, transp ); return; } else if ( radiusy == 0 ) { uint16_t x0 = ( c.x < radiusx ) ? 0 : ( c.x - radiusx ); line_draw_horizontal( x0, c.x + radiusx, c.y, surface, color, transp ); return; } if ( OBJECT_DRAW_OPTS( ellipse ) & DRAW_ANTIALIAS ) draw = ellipse_draw_antialiased; else draw = ellipse_draw_aliased; draw( c.x, c.y, radiusx, radiusy, surface, color, transp, fill ); } void ellipse_draw( ellipse_t *ellipse, surface_t *surface ) { surface_reset_visits( surface ); __ellipse_draw( ellipse, surface ); }