SDL2 simple (SDL_render) graphics on Unix(-like) OS with C? (or C-style C++)

I did a C, then C-style C++, ‘display hack’ or ‘graphics demonstration/introduction’ (demo/intro) with a friend who used to program more than me, now doesn’t much, and I got more into mathematics and almost out of coding. I did the mathematical graphics–originally (some) on DOS–and he converted it, and explained, for SDL1.2.

I want to use SDL2. I seek examples of using SDL_RenderDrawPoint(), maybe built-up simple (two-dimensional) mathematical object functions like SDL_RenderDrawLine() (or building your own) in C, or C-style C++ (optionally 3D from scratch,) meaning about the only difference should be if you want to define matrices each on one line instead of several lines per matrix… and you should be able to change it to that second ( C ) style, rename it *.C, compile without g++, only gcc or any cc on Free/Libre/Opensource Software (FLOSS) Unix(-like) OS. This would mean Slackware (I don’t consider later to be Unix-like but probably works,) NetBSD, other classic *BSD (FreeBSD, OpenBSD, maybe whatever is from OpenSolaris.) I had no luck finding a full RenderDrawPoint()/etc. SDL2 C program in Google, as ‘C’ usually gets used for the string ‘C++’ results…

My old SDL1.2 code still compiles, but after a little freenode IRC #SDL help to convert to SDL2, I only managed to get it to briefly draw a window then disappear.

I linked my SDL1.2 code at top but don’t want to link the SDL2 because it’s probably pretty bad… needs more error-checking, some simplification, then fixes to do more than disappear.

One used to only need SDL_Surface, then someone said use SDL_Window, and documentation says I also have to use SDL_Renderer, but can I get away with just using the latter two? I’ve seen it in C++ but want to see C(-style)… without strange use of while() I’d seen (remember, this is a graphics demo, not game)… just doing graphics once then finishing.

If requested, I’ll reply with inline or link of what I changed so far, but just don’t feel good about posting code I haven’t added some error checking I’d seen, yet.

You need an SDL_Window (which is the literal window on the screen, titlebar, etc), and an SDL_Renderer attached to that window (which deals with the GPU to get stuff into that window).

You can still use an SDL_Surface if you want to mess with a buffer of memory on the CPU that is meant to be a block of pixels, but often you don’t need this at all anymore. The thing you want to use with SDL_Renderer is an SDL_Texture (which is a block of pixels in GPU memory)…you can hand SDL blocks of memory to upload to that texture.

Then use SDL_RenderCopy() to draw that texture on the screen (the way you might have used SDL_BlitSurface() in SDL 1.2 to move an SDL_Surface to the screen).

After you do all your drawing, call SDL_RenderPresent() to flip the final image it to the screen (this was SDL_Flip() in SDL 1.2).

Lines and points and rectangles don’t need a texture, naturally, you can just draw them directly with an SDL_Renderer, and they will go to the screen with everything else during SDL_RenderPresent().

This is a fairly complex topic, so all I’m saying here is very tip-of-the-iceberg info, but this should get you started!

–ryan.

1 Like

Actually I didn’t use SDL_Flip(), but SDL_UpdateRect(), previously sometimes with SDL_FillRect() beforehand, for example to ‘clear screen’ to black. Now it’s not clear how I can do this.

After that update, my graphics window displays, then when I press space bar to use getch() go to the secnod part of the intro, it just exits before the second part. So it seems a lot has changed.

I didn’t mention textures, but when I get to those, they’ll be with my own algorithm, not SDL texture functions yet…

So, it’s still not clear what else I need to change to finish converting my basic pixel-based & line-based code.

A key factor is whether you draw your entire ‘canvas’ from scratch every frame (many video games work that way) or whether you want to ‘build up’ your canvas over time by adding elements (lines, pixels etc.) whilst the rest is preserved, like old-school BASIC graphics.

In the first case, you will want to draw your graphics elements directly to the renderer’s default target. The code executed every frame will look something like this:

do {
  SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
  SDL_RenderClear(renderer);
  SDL_RenderDrawLines(renderer, ……); // as required
  SDL_RenderDrawPoints(renderer, ……); // as required
  SDL_RenderPresent(renderer);
   }

But in the latter case you will want to draw not to the default render target but to a ‘target texture’ that you create initially. Now your main loop will look like this:

do {
  SDL_SetRenderTarget(renderer, texture);
  SDL_RenderDrawLines(renderer, ……); // as required
  SDL_RenderDrawPoints(renderer, ……); // as required
  SDL_SetRenderTarget(renderer, NULL)
  SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
  SDL_RenderClear(renderer);
  SDL_RenderCopy(renderer, texture, NULL, NULL);
  SDL_RenderPresent(renderer);
   }

Ryan/Icculus, rtrussell, you have both been helpful but I’m still having some problems converting this from SDL1 to SDL2… it shows a window, then I press a key to continue, and it says ‘illegal instruction.’ I thought it was the getch() (a friend wrote) that was for SDL1 but if I leave that out, then the window just disappears and says the same… This code doesn’t work yet but if I can fix it, would be under same BSD license as my SDL1 code linked in original post. The same graphics worked in the original, and I know I could probably do it all with more advanced SDL functions and/or OpenGL but the point is learning the mathematics, then continuing to do so by writing more C functions… I’m not sure it’ll work to do it the same sort of way as with SDL1. Sometimes I put some pixels/lines, waited for a key-press, put some more pixels/lines, and sometimes I erased it after every drawing of a cube… so unsure which of the two ways (rtrussell mentioned) I should be doing this and if that’s the issue… The reason I wrote put_pixel() & line() functions is the earlier code used to compile for a couple compilers so I used #define & #ifdef.

#include <SDL2/SDL.h>
#include <SDL2/SDL2_gfxPrimitives.h> //not needed for SDL2?
#include <SDL2/SDL_render.h>
#include <math.h>     //ceil(), cos(), floor(), sin(), sqrt()
#include <stdio.h>    //printf()
//#include <stdlib.h>   //abs()

void clrscr(SDL_Renderer *renderer,SDL_Color colour);
void delay(int n);
void getch(void);
void put_pixel(SDL_Renderer *renderer,int x,int y,SDL_Color colour);
void line(SDL_Renderer *renderer,int x0,int y0,int x1,int y1,SDL_Color colour);
void draw_square(SDL_Renderer *renderer,int square[][4],SDL_Color colour);
void draw_cube(SDL_Renderer *renderer,int cube[][8],int z,SDL_Color colour);

int main(void)
{
  int i;
    //define polygons
  int square_100[2][4]={{50,  50, -50, -50},
                        {50, -50, -50,  50}};
  int square_a[2][4]={{50,  50, -50, -50},
                      {50, -50, -50,  50}};
    //define polyhedra
  int cube_100[3][8]={{-50, -50,  50,  50, -50, -50, 50,  50},
                      {-50,  50,  50, -50, -50,  50, 50, -50},
                      {-50, -50, -50, -50,  50,  50, 50,  50}};
  int cube_a[3][8]={{-50, -50,  50,  50, -50, -50, 50,  50},
                    {-50,  50,  50, -50, -50,  50, 50, -50},
                    {-50, -50, -50, -50,  50,  50, 50,  50}};
  float x,y,z;
    //define 3D angles and rotation matrices
    //alpha, beta, lambda
  //float a=0;
  float a=0.785398; //pi/4;
  //float b=0;
  float b=0.785398;
  float l=0.785398;
  float ml[2][2]={{ cos(l), sin(l)},
                  {-sin(l), cos(l)}};
  float mR3a[3][3]={{1,      0,       0},
                    {0, cos(a), -sin(a)},
                    {0, sin(a),  cos(a)}};
  float mR3b[3][3]={{ cos(b), 0, sin(b)},
                    {      0, 1,      0},
                    {-sin(b), 0, cos(b)}};
  float mR3l[3][3]={{cos(l), -sin(l), 0},
                    {sin(l),  cos(l), 0},
                    {     0,       0, 1}};
  float mR3r[3][3]={{ cos(b)*cos(l)+sin(a)*sin(b)*sin(l), -cos(a)*sin(l)+cos(l)*sin(a)*sin(b), cos(a)*sin(b)},
                    {                      cos(a)*sin(l),                       cos(a)*cos(l),       -sin(a)},
                    {-cos(l)*sin(b)+cos(b)*sin(a)*sin(l),  sin(b)*sin(l)+cos(b)*cos(l)*sin(a), cos(a)*cos(b)}};
  SDL_Window *window;
  SDL_Renderer *renderer;
  SDL_Color black={0x00,0x00,0x00,0x00};
  SDL_Color green={0x00,0xFF,0x00,0x00};

  for(i=0;i<=3;i++)
  {
    square_a[0][i]+=320;
    square_a[1][i]+=240;
  }
  if (SDL_Init(SDL_INIT_VIDEO)<0)
  {
    fprintf(stderr,"Cannot start SDL: %s\n", SDL_GetError());
    return 1;
  }
  atexit(SDL_Quit);
    
  window=SDL_CreateWindow("demo",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640,480,0);
  if (window==NULL)
  {
    fprintf(stderr,"Cannot set 640x480x256 video mode: %s\n", SDL_GetError());
    return 1;
  }
  renderer=SDL_CreateRenderer(window,-1,0);

  clrscr(renderer,black);
  SDL_RenderPresent(renderer);
  getch();
  put_pixel(renderer,320,240,green);
  SDL_RenderPresent(renderer);
  getch();
  draw_square(renderer,square_a,green);
  SDL_RenderPresent(renderer);
  getch();

    //rotation
    //pi/24=0.130899
    //pi/30=0.104719
    //pi/60=0.052360 optimal at 640*480
  for(l=0;l<=6.283185;l+=0.104719)
  {
    //square rotation matrices, defining trigonometric functions
    ml[0][0]=cos(l);
    ml[0][1]=sin(l);
    ml[1][0]=-ml[0][1];
    ml[1][1]=ml[0][0];
    for(i=0; i<=3; i++)
    {
      //printf("%s%d%s%d%s%d\n", "old quad: ", i+1, "x,y: ", square_100[0][i], " ", square_100[1][i], "\n");
      x=ml[0][0]*square_100[0][i]+ml[0][1]*square_100[1][i];
      y=ml[1][0]*square_100[0][i]+ml[1][1]*square_100[1][i];
      square_a[0][i]=(int)x;
      square_a[1][i]=(int)y;
      //printf("%s%d%s%d%s%d\n", "new quad: ", i+1, "x,y: ", square_100[0][i], " ", square_100[1][i], "\n");
    }
    for(i=0;i<=3;i++)
    {
      square_a[0][i]+=320;
      square_a[1][i]+=240;
    }
    delay(10);
    clrscr(renderer,black);
    draw_square(renderer,square_a,green);
    SDL_RenderPresent(renderer);
  }
  getch();
  clrscr(renderer, black);

  draw_cube(renderer,cube_a,256,green);
  SDL_RenderPresent(renderer);
  getch();
  for(l=0;l<=6.283185;l+=0.104719)
  {
    mR3r[0][0]=cos(b)*cos(l)+sin(a)*sin(b)*sin(l);
    mR3r[0][1]=-cos(a)*sin(l)+cos(l)*sin(a)*sin(b);
    mR3r[0][2]=cos(a)*sin(b);
    mR3r[1][0]=cos(a)*sin(l);
    mR3r[1][1]=cos(a)*cos(l);
    mR3r[1][2]=-sin(a);
    mR3r[2][0]=-cos(l)*sin(b)+cos(b)*sin(a)*sin(l);
    mR3r[2][1]=sin(b)*sin(l)+cos(b)*cos(l)*sin(a);
    mR3r[2][2]=cos(a)*cos(b);
    for(i=0; i<=7; i++)
    {
      x=mR3r[0][0]*cube_100[0][i]+mR3r[0][1]*cube_100[1][i]+mR3r[0][2]*cube_100[2][i];
      y=mR3r[1][0]*cube_100[0][i]+mR3r[1][1]*cube_100[1][i]+mR3r[1][2]*cube_100[2][i];
      z=mR3r[2][0]*cube_100[0][i]+mR3r[2][1]*cube_100[1][i]+mR3r[2][2]*cube_100[2][i];
      cube_a[0][i]=(int)x;
      cube_a[1][i]=(int)y;
      cube_a[2][i]=(int)z;
    }
    delay(10);
    clrscr(renderer,black);
    draw_cube(renderer,cube_a,256,green);
    SDL_RenderPresent(renderer);
  }

    //wait; set video mode to text; exit
  getch();
  printf("%s","Programmed by David Chmelik and Ben Collver. (http://www.cwu.edu/~melikd/, http://www.terrorpin.net/\n");
  return 0;
}

void clrscr(SDL_Renderer *renderer,SDL_Color colour)
{
  SDL_SetRenderDrawColor(renderer,colour.r,colour.g,colour.b,colour.a);
  SDL_RenderClear(renderer);
}

void delay(int n)
{
  SDL_Delay(n);
}

void getch(void)
{
  SDL_Event event;
  int done=0;
  while(!done){
    if(SDL_WaitEvent(&event)<0)
    {
      fprintf(stderr, "SDL_WaitEvent() error: %s\n",
      SDL_GetError());
      done=1;
      continue;
    }
    if (event.type == SDL_KEYUP) done=1;
  }
}

void put_pixel(SDL_Renderer *renderer, int x, int y, SDL_Color colour)
{
  SDL_SetRenderDrawColor(renderer,colour.r,colour.g,colour.b,colour.a);
  SDL_RenderDrawPoint(renderer,x,y);
}

void line(SDL_Renderer *renderer, int x0, int y0, int x1, int y1, SDL_Color colour)
{
  SDL_SetRenderDrawColor(renderer,colour.r,colour.g,colour.b,colour.a);
  SDL_RenderDrawLine(renderer,x0,y0,x1,y1);
}

void draw_square(SDL_Renderer *renderer,int square[][4],SDL_Color colour)
{
  SDL_SetRenderDrawColor(renderer,colour.r,colour.g,colour.b,colour.a);
  line(renderer,square[0][0],square[1][0],square[0][1],square[1][1], colour);
  line(renderer,square[0][1],square[1][1],square[0][2],square[1][2], colour);
  line(renderer,square[0][2],square[1][2],square[0][3],square[1][3], colour);
  line(renderer,square[0][3],square[1][3],square[0][0],square[1][0], colour);
}

void draw_cube(SDL_Renderer *renderer, int cube[][8], int z, SDL_Color colour)
{
    line(renderer,z*cube[0][0]/(z+cube[2][0])+320,z*cube[1][0]/(z+cube[2][0])+240,z*cube[0][1]/(z+cube[2][1])+320,z*cube[1][1]/(z+cube[2][1])+240, colour);
    line(renderer,z*cube[0][0]/(z+cube[2][0])+320,z*cube[1][0]/(z+cube[2][0])+240,z*cube[0][3]/(z+cube[2][3])+320,z*cube[1][3]/(z+cube[2][3])+240, colour);
    line(renderer,z*cube[0][0]/(z+cube[2][0])+320,z*cube[1][0]/(z+cube[2][0])+240,z*cube[0][4]/(z+cube[2][4])+320,z*cube[1][4]/(z+cube[2][4])+240, colour);
    line(renderer,z*cube[0][1]/(z+cube[2][1])+320,z*cube[1][1]/(z+cube[2][1])+240,z*cube[0][2]/(z+cube[2][2])+320,z*cube[1][2]/(z+cube[2][2])+240, colour);
    line(renderer,z*cube[0][1]/(z+cube[2][1])+320,z*cube[1][1]/(z+cube[2][1])+240,z*cube[0][5]/(z+cube[2][5])+320,z*cube[1][5]/(z+cube[2][5])+240, colour);
    line(renderer,z*cube[0][2]/(z+cube[2][2])+320,z*cube[1][2]/(z+cube[2][2])+240,z*cube[0][6]/(z+cube[2][6])+320,z*cube[1][6]/(z+cube[2][6])+240, colour);
    line(renderer,z*cube[0][3]/(z+cube[2][3])+320,z*cube[1][3]/(z+cube[2][3])+240,z*cube[0][2]/(z+cube[2][2])+320,z*cube[1][2]/(z+cube[2][2])+240, colour);
    line(renderer,z*cube[0][3]/(z+cube[2][3])+320,z*cube[1][3]/(z+cube[2][3])+240,z*cube[0][7]/(z+cube[2][7])+320,z*cube[1][7]/(z+cube[2][7])+240, colour);
    line(renderer,z*cube[0][4]/(z+cube[2][4])+320,z*cube[1][4]/(z+cube[2][4])+240,z*cube[0][7]/(z+cube[2][7])+320,z*cube[1][7]/(z+cube[2][7])+240, colour);
    line(renderer,z*cube[0][5]/(z+cube[2][5])+320,z*cube[1][5]/(z+cube[2][5])+240,z*cube[0][6]/(z+cube[2][6])+320,z*cube[1][6]/(z+cube[2][6])+240, colour);
    line(renderer,z*cube[0][5]/(z+cube[2][5])+320,z*cube[1][5]/(z+cube[2][5])+240,z*cube[0][4]/(z+cube[2][4])+320,z*cube[1][4]/(z+cube[2][4])+240, colour);
    line(renderer,z*cube[0][7]/(z+cube[2][7])+320,z*cube[1][7]/(z+cube[2][7])+240,z*cube[0][6]/(z+cube[2][6])+320,z*cube[1][6]/(z+cube[2][6])+240, colour);
}

‘Illegal instruction’ is presumably a hardware or compiler issue, and not directly related to either your code or SDL.

‘Illegal instruction’ is presumably a hardware or compiler issue

Could be SSE code inside SDL/OpenGL/libm or whatever on a CPU that doesn’t support it?

Fwiw, the code posted works here, rendering spinning cubes and such. If you run it under a debugger, it’ll tell you exactly what caused the illegal instruction exception.

I don’t think the problem is CPU (Core i7 6700K) rather than GPU (in a Radeon RX Vega 64 which apparently may not have completed drivers yet for most GNU/Linux.)

I also was able to run my code on another PC/distro.

update: someone on freenode IRC ##slackware compiled & ran my code. So the problem is probably that I don’t have new enough display/video/graphics drivers for X.

update: now they had me change one line, and it now works for me.

renderer=SDL_CreateRenderer(window,-1,SDL_RENDERER_SOFTWARE);

So the problem was SDL2 assumed I’d use my graphics hardware (that drivers don’t fully work yet) rather than doing my own rendering. The page https://wiki.libsdl.org/SDL_CreateRenderer doesn’t give an example with error checking, but is there a way you can use an ‘if’ to check if the renderer assignment statement doesn’t work and you should halt with a clearer error message?

Like this:

renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_SOFTWARE);
if (renderer == NULL) {
    SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Couldn't create renderer!", SDL_GetError(), window);
    SDL_Quit();
    exit(1);
}

Thanks, Icculus. Well, I diverged from original, solved, topic, so if this forum does, we can mark this thread ‘solved.’ Since my last post, I’d looked at my friend’s SDL1 code for checking the surface (now window,) did something similar for when creating the renderer, wondering if this will still work (or is SDL_GetError() obsolete?)… I’ll put back in the number that crashed it and test this soon…

atexit(SDL_Quit); //I guess this saves the command to do later, when you return().
        window=SDL_CreateWindow("demo",SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,640,480,0);
if(window==NULL)
{
  fprintf(stderr,"Cannot set 640x480x256 video mode: %s\n",SDL_GetError());
  return 1;
}
renderer=SDL_CreateRenderer(window,-1,SDL_RENDERER_SOFTWARE);
if(renderer==NULL)
{
  fprintf(stderr,"Cannot set create SDL Renderer: %s\n",SDL_GetError());
  return 1;
}