Wie zeichnet man Kreise, Bögen und Vektorgrafiken in SDL?

Lesezeit: 7 Minuten

Benutzeravatar von effbiae
Effbiae

Ich verwende SDL2.

Die einzige Möglichkeit, eine Form zu zeichnen, ist mit den Linien-, Rechteck- und Pixelfunktionen, wie erläutert Hier.

Abgesehen von der Verwendung von Trig oder der “Gleichung eines Kreises”, wie könnte ich eine Kurve zeichnen? Wie wäre es mit allgemeinen Vektorgrafiken?

Ist SDL ein geeigneter Ausgangspunkt oder sollte ich mich woanders umsehen?

Benutzeravatar von Scotty Stephens
Scotty Stephens

Dies ist ein Beispiel für den Mittelpunktkreisalgorithmus, auf den oben verwiesen wird. Es erfordert keine Mathematikbibliothek und ist sehr schnell. (Rendert in etwa 500 Mikrosekunden) Dies ist, was Windows verwendet/verwendet hat, um Kreise zu rastern.

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);
      }
   }
}

daveks Benutzeravatar
davek

Wenn Sie Ihre eigene Funktion zum Zeichnen von Kreisen schreiben möchten, würde ich vorschlagen, die anzupassen Mittelpunktalgorithmus zu SDL2 durch Zeichnen von Pixeln.

Kurven würden ähnlich gemacht, würden aber eher einen Ellipsen-Zeichnungsalgorithmus verwenden.

Tatsächliche Vektorgrafiken beginnen, viel komplizierter zu werden, und Sie müssten wahrscheinlich etwas finden, das SVG-Dateien rendert, wobei ich nicht sicher bin, ob es viele Optionen für SDL2 gibt.

Wenn Sie jedoch lieber einfach Funktionen haben möchten, mit denen Sie arbeiten können, würde ich vorschlagen, direkt zu gehen SDL2_gfx stattdessen. Es hat viele weitere Funktionen bereits implementiert, mit denen Sie arbeiten können.

SDL ermöglicht Bibliotheken von Drittanbietern, auf eine Textur zu zeichnen. Wenn Kairo wünschenswert wäre, könnte es in einer Funktion wie dieser verwendet werden:

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;
}

dann kann cb an diese Funktion übergeben werden:

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
 return fr;
}

Benutzeravatar von 12oclocker
12 Uhr

Wenn Sie einen Kreis oder eine Ellipse ohne Bibliotheken von Drittanbietern erstellen möchten, schließen Sie math.h ein und verwenden Sie die Funktion unten, die ich geschrieben habe. Es zeichnet Alias-Ellipsen oder Kreise sehr gut. Getestet auf SDL 2.0.2 und funktioniert. Es zeichnet einen Quadrantenbogen und spiegelt die anderen Bögen, wodurch die Aufrufe von cosf und sinf reduziert werden.

//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
    }
}

Benutzeravatar von JanSordid
JanSordid

Meine Antwort erweitert die Antwort von Scotty Stephens, indem sie sie um einiges leistungsfähiger macht, indem sie die API-Aufrufe auf einen einzigen reduziert.

// rounding helper, simplified version of the function I use
int roundUpToMultipleOfEight( int v )
{
    return (v + (8 - 1)) & -8;
}

void DrawCircle( SDL_Renderer * renderer, SDL_Point center, int radius )
{
    // 35 / 49 is a slightly biased approximation of 1/sqrt(2)
    const int arrSize = roundUpToMultipleOfEight( radius * 8 * 35 / 49 );
    SDL_Point points[arrSize];
    int       drawCount = 0;

    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
        points[drawCount+0] = { center.x + x, center.y - y };
        points[drawCount+1] = { center.x + x, center.y + y };
        points[drawCount+2] = { center.x - x, center.y - y };
        points[drawCount+3] = { center.x - x, center.y + y };
        points[drawCount+4] = { center.x + y, center.y - x };
        points[drawCount+5] = { center.x + y, center.y + x };
        points[drawCount+6] = { center.x - y, center.y - x };
        points[drawCount+7] = { center.x - y, center.y + x };

        drawCount += 8;

        if( error <= 0 )
        {
            ++y;
            error += ty;
            ty += 2;
        }

        if( error > 0 )
        {
            --x;
            tx += 2;
            error += (tx - diameter);
        }
    }

    SDL_RenderDrawPoints( renderer, points, drawCount );
}

Ein Kreis mit Radius 141 hätte in Scottys Version 800 SDL_RenderDrawPoint-Aufrufe gehabt, diese neue Version führt nur einen einzigen SDL_RenderDrawPoint auss Anruf, wodurch es viel leistungsfähiger wird.

Man könnte auch den Rendering-Teil aus dieser Funktion entfernen, damit das Ergebnis zwischengespeichert und wiederverwendet werden kann, wie unten gezeigt.

std::vector<SDL_Point> PixelizeCircle( SDL_Point center, int radius )
{
    std::vector<SDL_Point> points;

    // 35 / 49 is a slightly biased approximation of 1/sqrt(2)
    const int arrSize   = roundUpToMultipleOfEight( radius * 8 * 35 / 49 );
    points.reserve( arrSize );

    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
        points.push_back( { center.x + x, center.y - y } );
        points.push_back( { center.x + x, center.y + y } );
        points.push_back( { center.x - x, center.y - y } );
        points.push_back( { center.x - x, center.y + y } );
        points.push_back( { center.x + y, center.y - x } );
        points.push_back( { center.x + y, center.y + x } );
        points.push_back( { center.x - y, center.y - x } );
        points.push_back( { center.x - y, center.y + x } );

        if( error <= 0 )
        {
            ++y;
            error += ty;
            ty += 2;
        }

        if( error > 0 )
        {
            --x;
            tx += 2;
            error += (tx - diameter);
        }
    }

    return points; // RVO FTW
}

int main()
{
    std::vector<SDL_Point> circle = PixelizeCircle( SDL_Point{ 84, 72 }, 79 );
    //...
    while( true )
    {
        //...
        SDL_RenderDrawPoints( renderer, circle.data(), circle.size() );
        //...
    }
}

1437670cookie-checkWie zeichnet man Kreise, Bögen und Vektorgrafiken in SDL?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy