HOWTO draw circles, arcs and vector graphics in SDL?
This is an example of the Midpoint Circle Algorithm as referenced above. It doesn't require a math library and is very fast. (Renders in about 500 microseconds) This is what Windows uses/used to rasterize circles.
void DrawCircle(SDL_Renderer * renderer, int32_t centreX, int32_t centreY, int32_t radius)
{
const int32_t diameter = (radius * 2);
int32_t x = (radius - 1);
int32_t y = 0;
int32_t tx = 1;
int32_t ty = 1;
int32_t error = (tx - diameter);
while (x >= y)
{
// Each of the following renders an octant of the circle
SDL_RenderDrawPoint(renderer, centreX + x, centreY - y);
SDL_RenderDrawPoint(renderer, centreX + x, centreY + y);
SDL_RenderDrawPoint(renderer, centreX - x, centreY - y);
SDL_RenderDrawPoint(renderer, centreX - x, centreY + y);
SDL_RenderDrawPoint(renderer, centreX + y, centreY - x);
SDL_RenderDrawPoint(renderer, centreX + y, centreY + x);
SDL_RenderDrawPoint(renderer, centreX - y, centreY - x);
SDL_RenderDrawPoint(renderer, centreX - y, centreY + x);
if (error <= 0)
{
++y;
error += ty;
ty += 2;
}
if (error > 0)
{
--x;
tx += 2;
error += (tx - diameter);
}
}
}
If you want to do a circle or ellipse without 3rd party libraries, include math.h and use the function below I wrote. It will draw aliased ellipse or circles very well. Tested on SDL 2.0.2 and works. It draws one quadrant arc, and mirrors the other arcs, reducing calls to cosf and sinf.
//draw one quadrant arc, and mirror the other 4 quadrants
void sdl_ellipse(SDL_Renderer* r, int x0, int y0, int radiusX, int radiusY)
{
float pi = 3.14159265358979323846264338327950288419716939937510;
float pih = pi / 2.0; //half of pi
//drew 28 lines with 4x4 circle with precision of 150 0ms
//drew 132 lines with 25x14 circle with precision of 150 0ms
//drew 152 lines with 100x50 circle with precision of 150 3ms
const int prec = 27; // precision value; value of 1 will draw a diamond, 27 makes pretty smooth circles.
float theta = 0; // angle that will be increased each loop
//starting point
int x = (float)radiusX * cos(theta);//start point
int y = (float)radiusY * sin(theta);//start point
int x1 = x;
int y1 = y;
//repeat until theta >= 90;
float step = pih/(float)prec; // amount to add to theta each time (degrees)
for(theta=step; theta <= pih; theta+=step)//step through only a 90 arc (1 quadrant)
{
//get new point location
x1 = (float)radiusX * cosf(theta) + 0.5; //new point (+.5 is a quick rounding method)
y1 = (float)radiusY * sinf(theta) + 0.5; //new point (+.5 is a quick rounding method)
//draw line from previous point to new point, ONLY if point incremented
if( (x != x1) || (y != y1) )//only draw if coordinate changed
{
SDL_RenderDrawLine(r, x0 + x, y0 - y, x0 + x1, y0 - y1 );//quadrant TR
SDL_RenderDrawLine(r, x0 - x, y0 - y, x0 - x1, y0 - y1 );//quadrant TL
SDL_RenderDrawLine(r, x0 - x, y0 + y, x0 - x1, y0 + y1 );//quadrant BL
SDL_RenderDrawLine(r, x0 + x, y0 + y, x0 + x1, y0 + y1 );//quadrant BR
}
//save previous points
x = x1;//save new previous point
y = y1;//save new previous point
}
//arc did not finish because of rounding, so finish the arc
if(x!=0)
{
x=0;
SDL_RenderDrawLine(r, x0 + x, y0 - y, x0 + x1, y0 - y1 );//quadrant TR
SDL_RenderDrawLine(r, x0 - x, y0 - y, x0 - x1, y0 - y1 );//quadrant TL
SDL_RenderDrawLine(r, x0 - x, y0 + y, x0 - x1, y0 + y1 );//quadrant BL
SDL_RenderDrawLine(r, x0 + x, y0 + y, x0 + x1, y0 + y1 );//quadrant BR
}
}
SDL allows for third party libs to draw on a texture. If cairo was desirable, it could be used in a function like this:
cairo_t*cb(cairo_t*cr)
{cairo_set_source_rgb(cr, 1.0, 1.0, 1.0);
cairo_rectangle(cr, 10, 20, 128, 128);
cairo_stroke(cr);
return cr;
}
then cb can be passed to this function:
cairo_t*cai(SDL_Window*w,SDL_Renderer*r,cairo_t*(*f)(cairo_t*))
{int width, height, pitch;void *pixels;
SDL_GetWindowSize(w, &width, &height);
SDL_Texture*t=SDL_CreateTexture(r,SDL_PIXELFORMAT_ARGB8888,SDL_TEXTUREACCESS_STREAMING,width,height);
SDL_LockTexture(t, NULL, &pixels, &pitch);
cairo_surface_t *cs=cairo_image_surface_create_for_data(pixels,CAIRO_FORMAT_ARGB32,width,height,pitch);
cairo_t*s=cairo_create(cs);
cairo_t*fr=f(s);SDL_UnlockTexture(t);SDL_RenderCopy(r,t,NULL,NULL);SDL_RenderPresent(r);
return fr;
}
If you want to write your own circle drawing function, then I'd suggest adapting the midpoint algorithm to SDL2 by drawing pixels.
Curves would be done similarly, but would use more of an ellipses drawing algorithm.
Actual vector graphics start to get much more complicated, and you'd probably have to find something that renders SVG files, which I'm not sure there are many options for SDL2.
However, if you would rather simply have functions that you can work with I'd suggest going straight to SDL2_gfx instead. It has many more functions already implemented for you to work with.