2

I just started with SDL in c++ and VS 2013 Community. I want to draw a circle and so i searched on Google and found http://content.gpwiki.org/index.php/SDL:Tutorials:Drawing_and_Filling_Circles. But as I tried to implement it in a seperate fill_circle.cpp file, include this in my main.cpp and call the function, I get the Error that said function fill_circle() is already defined in fill_circle.obj and that there are conflicts with other libs. So I tried to implement the drawning function directly into my main.cpp but i get a similar error, saying that void __cdecl fill_circle(struct SDL_Surface *,int,int,int,unsigned int) is already defined in fill_circle.obj.

I dont know what to do with those errors and hope someone of you can help me :)

EDIT: After completly removing the fill_circle.cpp and debug folder and implementing the Function in main.cpp the programm will compile but throw an error at runtime.

My main.cpp:

    #include <SDL.h>
#include <iostream>
#include <cmath>

void fill_circle(SDL_Surface *surface, int cx, int cy, int radius, Uint32 pixel)
{
    static const int BPP = 4;

    double r = (double)radius;

    for (double dy = 1; dy <= r; dy += 1.0)
    {
        // This loop is unrolled a bit, only iterating through half of the
        // height of the circle.  The result is used to draw a scan line and
        // its mirror image below it.

        // The following formula has been simplified from our original.  We
        // are using half of the width of the circle because we are provided
        // with a center and we need left/right coordinates.

        double dx = floor(sqrt((2.0 * r * dy) - (dy * dy)));
        int x = cx - dx;

        // Grab a pointer to the left-most pixel for each half of the circle
        Uint8 *target_pixel_a = (Uint8 *)surface->pixels + ((int)(cy + r - dy)) * surface->pitch + x * BPP;
        Uint8 *target_pixel_b = (Uint8 *)surface->pixels + ((int)(cy - r + dy)) * surface->pitch + x * BPP;

        for (; x <= cx + dx; x++)
        {
            *(Uint32 *)target_pixel_a = pixel;
            *(Uint32 *)target_pixel_b = pixel;
            target_pixel_a += BPP;
            target_pixel_b += BPP;
        }
    }
}

int main(int argc, char *argv[])
{
    //Main loop flag
    bool b_Quit = false;
    //Event handler 
    SDL_Event ev;
    //SDL window
    SDL_Window *window = NULL;

    SDL_Surface *windowSurface;

    if (SDL_Init(SDL_INIT_VIDEO) < 0)
    {
        std::cout << "Video Initialisation Error: " << SDL_GetError() << std::endl;
    }
    else
    {
        window = SDL_CreateWindow("SDL_Project", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, SDL_WINDOW_SHOWN);
        if (window == NULL)
        {
            std::cout << "Window Creation Error: " << SDL_GetError() << std::endl;
        }
        else
        {
            windowSurface = SDL_GetWindowSurface(window);

            fill_circle(windowSurface, 10, 10, 20, 0xffffffff);

            //Main loop
            while (!b_Quit)
            {
                //Event Loop
                while (SDL_PollEvent(&ev) != 0)
                {
                    //Quit Event
                    if (ev.type == SDL_QUIT)
                    {
                        b_Quit = true;
                    }
                }
                SDL_UpdateWindowSurface(window);
            }

        }
    }

    SDL_DestroyWindow(window);
    SDL_Quit();

    return 0;
}
genpfault
  • 51,148
  • 11
  • 85
  • 139
Phorskin
  • 81
  • 1
  • 2
  • 6
  • Change the function name - to `Pauls_fill_Circle()`, from a quick glance it seems you're seeing a result of namespace ambigutity. – Jeremy Thompson Feb 05 '15 at 14:57
  • I still get the runtime error: First-chance exception at 0x011F6CF9 in SDL_Project.exe: 0xC0000005: Access violation writing location 0x03604C10. If there is a handler for this exception, the program may be safely continued. – Phorskin Feb 05 '15 at 15:20
  • fill_circle(windowSurface, 10, 10, 20, 0xffffffff); will draw outside of the surface and hence the program will crash – Martin G Feb 05 '15 at 18:26

3 Answers3

2

This can help you:

void draw_circle(SDL_Point center, int radius, SDL_Color color)
{
    SDL_SetRenderDrawColor(renderer, color.r, color.g, color.b, color.a);
    for (int w = 0; w < radius * 2; w++)
    {
        for (int h = 0; h < radius * 2; h++)
        {
            int dx = radius - w; // horizontal offset
            int dy = radius - h; // vertical offset
            if ((dx*dx + dy*dy) <= (radius * radius))
            {
                SDL_RenderDrawPoint(renderer, center.x + dx, center.y + dy);
            }
        }
    }
}
Edgar Luque
  • 388
  • 1
  • 4
  • 14
1

A circle with a radius of 20 with its' center at (10, 10) like you have:

fill_circle(windowSurface, 10, 10, 20, 0xffffffff);

Will make you address pixels outside of the allocated surface

    ... = (Uint8 *)surface->pixels + ((int)(cy + r - dy)) * surface->pitch + x * BPP;
    ... = (Uint8 *)surface->pixels + ((int)(cy - r + dy)) * surface->pitch + x * BPP;

This should cause a crash.

I have used the same algorithm in some projects and with SDL 1.2 it is not very safe the way it is written.

Martin G
  • 17,357
  • 9
  • 82
  • 98
1

i just entered here to search for an idea to draw a filled Circle and tried Ryozuki solution but its cost is inmense so i present here an alternative.

void TextureManager::fillCircle(const fig::cSphere _dst){
    SDL_SetRenderDrawColor(Game::renderer, 0x00, 0x00, 0xff, 0xff);

    int p = std::sqrt(_dst.r * 2) + 4;
    for (unsigned char ii = 0; ii < p; ii++) {
        float angle = 2 * M_PI / p;
        fillTriangle( 
            fig::cTria{
            {
                _dst.pos.x + _dst.r*std::cos(ii * angle),
                _dst.pos.y + _dst.r*std::sin(ii * angle)
            },
            {
                _dst.pos.x + _dst.r*std::cos((ii + 1) * angle),
                _dst.pos.y + _dst.r*std::sin((ii + 1) * angle)
            },
            _dst.pos }
        );
    };

    SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xff, 0xaa, 0xff);
}

You, of course, will need to create a fillTriangle() function and this will help with that:

void TextureManager::fillTriangle(const fig::cTria dst){
    fnc::small drawable = 0;

    for (fnc::small i = 0; i < 3; i++) 
        if (dst.points[i]->x == dst.points[(i + 1) % 3]->x) { drawable = (i + 2) % 3 + 1;  break; }
        else if (dst.points[i]->y == dst.points[(i + 1) % 3]->y) { drawable = (i + 2) % 3 + 4; break; }
    
    if (drawable == 0) {
        std::pair<fig::cTria, fig::cTria> t = dst.subdivide(
            dst.getboundary().siz.x < dst.getboundary().siz.y
        );
        fillTriangle(t.first);
        fillTriangle(t.second);
    }
    else if (drawable < 4){
        short dx = -dst.points[drawable - 1]->x + dst.points[drawable % 2]->x;
        SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xaa, 0xff, 0xff);
        for (short i = 0; true; i = fnc::aproach((float)i, 1, dx)) {
            SDL_RenderDrawLine(
                Game::renderer,
                dst.points[drawable - 1]->x + i,
                fnc::lerp(dst.points[drawable - 1]->y, dst.points[(drawable + 1) % 3]->y, (float)i / (float)dx),
                dst.points[drawable - 1]->x + i,
                fnc::lerp(dst.points[drawable - 1]->y, dst.points[drawable % 3]->y, (float)i / (float)dx)
            );
            if (i == dx) break;
        }
        SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xff, 0xaa, 0xff);
    }
    else {
        drawable -= 3;

        short dy = -dst.points[drawable - 1]->y + dst.points[drawable % 2]->y;
        SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xaa, 0xff, 0xff);
        for (short i = 0; true; i = fnc::aproach((float)i, 1, dy)) {
            SDL_RenderDrawLine(
                Game::renderer,
                fnc::lerp(dst.points[drawable - 1]->x, dst.points[(drawable + 1) % 3]->x, (float)i / (float)dy),
                dst.points[drawable - 1]->y + i,
                fnc::lerp(dst.points[drawable - 1]->x, dst.points[drawable % 3]->x, (float)i / (float)dy),
                dst.points[drawable - 1]->y + i
            );
            if (i == dy) break;
        }
        SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xff, 0xaa, 0xff);
    }
}

and about the weird classes, they're:

using fnc::small = unsigned char;
using fnc::ushort = unsigned short;
using fnc::uint = unsigned int;

and also, the fig::cTria::subdivide() just divide the triangle in two triangles whose area can be "integrable". I mean, in a way there is a line that's either totally horizontal or totally vertical. the fig::cTria::boudary() returns the fig::cRect in which the triangle is inscribed.

here, i've found another way to solve this problem

void TextureManager::fillCircle(const fig::cSphere _dst){
    SDL_SetRenderDrawColor(Game::renderer, 0x00, 0x00, 0xff, 0xff);

    for (fnc::uint dx = 0; dx < _dst.r * 2; dx++) {
        SDL_RenderDrawLine(Game::renderer,
            _dst.pos.x - _dst.r + dx,
            _dst.pos.y + std::sqrtf(std::powf(_dst.r, 2) - std::powf(dx - _dst.r, 2)),
            _dst.pos.x - _dst.r + dx,
            _dst.pos.y - std::sqrtf(std::powf(_dst.r, 2) - std::powf(dx - _dst.r, 2))
        );
    }
    SDL_SetRenderDrawColor(Game::renderer, 0xaa, 0xff, 0xaa, 0xff);
}

And i think it's faster. Hope this helps somebody. if someone have suggestions, they'll be appreciated

  • I think I can make the last one far more readable; unfortunately I'm programming in C# right now. – Joshua Aug 22 '21 at 23:35