Close
0%
0%

ChassC

As in 'Chassis for C'
Simple Direct Media Layer (SDL2) Template for C++

Similar projects worth following
I'm building CirKit, a circuit designer that outputs directly to OSH, GCode based mills and transparency printers. It needs to run on all the four major platforms and look the same. That proved to be a bit trialling, until I discovered SDL/2.

SDL is a library that can open a window in the display context, create a viewport on it and then write graphics and text to it. Also track the mouse (and gestures in SDL2) and keyboard and handle audio too (It does not record, bummer.) All these commands are cross-platform, and it sounds great but it has some serious limitations.

It can write single pixels, single-pixel lines, filled rectangles and rounded-recangles to the screen, and thats it. At least they are alpha-blended and textured, but still useless. To load bitmaps you need an extension library, SDL_image.h, and the TTF fonts dont work. I'll make a vector font that emulates a plotter, in keeping with current PCB artwork.

No triangles, polygons or circles at all, so I've had to write them. The event system is confusing and badly integrated, no surprise. Considering it handles 4 different OS types, surprisingly it works pretty well, but I had to code for that too.

Note that the subsequent code assumes a working system, in my case Debian Linux. It can be compiled for Windoze, by replacing the .H includes with .DLLs and for Mac/IOS and Android similarly to Linux but this is untested. The SDL library however is known to compile on an extensive list of OSes...

chassc.cpp

All the routines described in the project

x-c++src - 5.12 kB - 02/19/2018 at 20:28

Download

  • Extending the Graphics

    Morning.Star02/19/2018 at 13:07 0 comments

    That really is about all SDL can do on its own. It does have tricks up its sleeve, and there is the mixer, fonts and other libraries you can add to it, plus a graphics library containing all the primitives - circles, polygons etc.

    If you're like me, you wont want to load another library to use the one you just loaded. Besides, they are all written in SDL, to do things that SDL cant on its own.

    Here's a good example. Filling a triangle, fast. I've had to code a routine to do this using a rasterising arrangement - breaking the triangle up into lines and drawing them using RenderDrawLine repeatedly.

    #include <vector>
    
    void trigon(double nx1, double ny1, double nx2, double ny2,double nx3, double ny3) {
    
      double sx,sy,nx,ny,dx1,dx2,dx3,x1,y1,x2,y2,x3,y3;
    
      if (ny1<=ny2 and ny1<=ny3) { 
        x1=nx1; y1=ny1;
        if (ny2<=ny3) { x2=nx2; y2=ny2; x3=nx3; y3=ny3; }   // make sure y1<y2<y3
        if (ny3<=ny2) { x2=nx3; y2=ny3; x3=nx2; y3=ny2; }
      }
    
      if (ny2<=ny1 and ny2<=ny3) { 
        x1=nx2; y1=ny2;
        if (ny1<=ny3) { x2=nx1; y2=ny1; x3=nx3; y3=ny3; }
        if (ny3<=ny1) { x2=nx3; y2=ny3; x3=nx1; y3=ny1; }
      }
    
      if (ny3<=ny1 and ny3<=ny2) { 
        x1=nx3; y1=ny3;
        if (ny1<=ny2) { x2=nx1; y2=ny1; x3=nx2; y3=ny2; }
        if (ny2<=ny1) { x2=nx2; y2=ny2; x3=nx1; y3=ny1; }
      }
    
      SDL_RenderDrawLine(render,x1,y1,x2,y2);
      SDL_RenderDrawLine(render,x2,y2,x3,y3);
      SDL_RenderDrawLine(render,x3,y3,x1,y1);
        
      if (y1==y2) {                                         // if top of triangle is flat
      
        sx=x1; nx=x2;                                       // begin with full width
        dx1=(x3-x1)/(y3-y1);                                // and graduate to a point
        dx2=(x3-x2)/(y3-y1);
      
        for (sy=y1;sy<y3;sy++) {                            // fill in the scanlines
          SDL_RenderDrawLine(render,sx,sy,nx,sy);
          sx=sx+dx1;
          nx=nx+dx2;
        }
      
      } else {
    
        sx=x1; nx=x1;                                       // if top of triangle is a point
        dx1=(x2-x1)/(y2-y1);                                // fill in the top half of the triangle
        dx2=(x3-x1)/(y3-y1);
      
        for (sy=y1;sy<y2;sy++) {                            // fill the scanlines
          SDL_RenderDrawLine(render,sx,sy,nx,sy);
          sx=sx+dx1;
          nx=nx+dx2;
        }
    
        dx1=(x3-x2)/(y3-y2);                                // the width of the top of bottom half
        sx=x2;
        
        for (sy=y2;sy<y3;sy++) {                            // fill the scanlines
          SDL_RenderDrawLine(render,sx,sy,nx,sy);
          sx=sx+dx1;
          nx=nx+dx2;
        }
    
      }
    
    }
    

    Simply call Trigon with three coordinate pairs.

    trigon(600,100,700,150,650,200); 

    Rather than write a routine to handle four, five etc sides, I've also written a routine called Polygon, which calls Trigon to fill in the triangles it breaks the poly into. However, rather than split the drawing routines into classes like point, line, circle etc, I've incorporated them all into Polygon.

    Calling it with one pair of coordinates draws a pixel. With two pairs, it draws a line, and with three or more pairs of coordinates it draws a filled shape. The coordinates are passed as a Vector for simplicity.

    double pi=atan(1)*4.0
    double d2r=pi/180.0
    
    void polygon(vector <SDL_Point> pt) {
    
      int n,pts=pt.size();
      double x1,y1,x2,y2,x3,y3;
      
      if (pts==1) {
        SDL_RenderDrawPoint(render,pt[0].x,pt[0].y);
      }
    
      if (pts==2) {
        SDL_RenderDrawLine(render,pt[0].x,pt[0].y,pt[1].x,pt[1].y);
      }
    
      if (pts==3) {
        trigon(pt[0].x,pt[0].y,pt[1].x,pt[1].y,pt[2].x,pt[2].y);
      }
      
      if (pts>3) {
      
        trigon(pt[0].x,pt[0].y,pt[1].x,pt[1].y,pt[2].x,pt[2].y);
        
        for (n=0; n<pts-3; n++) {
        
          x1=pt[0].x; y1=pt[0].y;
          x2=pt[2+n].x; y2=pt[2+n].y;
          x3=pt[3+n].x; y3=pt[3+n].y;
          
          trigon(x1,y1,x2,y2,x3,y3);
        
        }
        
      }
    
    }
    

     And finally, to complete the set there is PieSlice, which takes coordinates and an arc angle, arc length and radius to draw a pie slice or circle centered on the coordinates.

    void pieslice(SDL_Point pt, double st, double nd, double rds) {
    
      double n;
      SDL_Point p;
      vector <SDL_Point> pl;
      
      if (nd-st<360) { 
        pl.push_back(pt); 
      }
      
      for (n=0;n<=nd;n=n+(nd/48.0)) {
        p.x=(cos((st+n)*d2r)*rds)+pt.x;
        p.y=(sin((st+n)*d2r)*rds)+pt.y;
        pl.push_back(p);
      }
      
      polygon(pl);
    
    }

    To use them...

      vector <SDL_Point> pts;
      SDL_Point p;
      
      p.x=600; p.y=300;
      pts.push_back(p);
      p.x=700; p.y=350;
      pts.push_back(p);
      p.x=650; p.y=400;
      pts.push_back(p);
      p.x=600; p.y=450;
      pts.push_back(p);
      
     polygon(pts);
    ...
    Read more »

  • We Can Haz Cheezburger?

    Morning.Star02/19/2018 at 12:41 0 comments

    Yes we can.

    Loading graphics is also simple to do, that is the point of SDL. It's part of the bitmap blitting routines.

    You'll need to load the library for this.

    #include <SDL2/SDL.h>
    #include <SDL2/SDL_image.h>

     Graphics are loaded as Surfaces, and can be pasted over each other and the render.

    SDL_Surface * tmp=SDL_LoadBMP("cheezburger.bmp");
    SDL_Texture * cheez = SDL_CreateTextureFromSurface( render, tmp );
    if (cheez==NULL) { cout << "cant has cheezburger!\n"; }
    SDL_FreeSurface(tmp);
    
    SDL_Rect src,dst;
    int sw,sh;
    
    SDL_QueryTexture(cheez,NULL,NULL, &sw, &sh);
    
    src.x=0; src.y=0;
    src.w=sw; src.height=sh;
    
    dst.x=300; dst.y=0;
    dst.w=sw; dst.h=sh;
    
    SDL_RenderCopy(render,cheez,&src,&dst);
    
    

    Note the use of RenderCopy to paste the image on the render.

    you specify the destination, source, and then source and destination rectangles to take all or a piece of the source bitmap and paste it into the destination rectangle. These can both be NULL, to copy the entire image to the entire surface without specifying a size.

    Details about the image are returned by QueryTexture, which returns the following values

    Format - this is the texture depth and type. Usually this will be RGBA8888.

    Access - The render has SDL_TEXTUREACCESS_TARGET but there are other values.

    Width and Height - In Pixels.

  • World: Hello

    Morning.Star02/19/2018 at 11:02 0 comments

    Now we can open up a window and draw simple shapes on it, it would be a good idea to be able to interact with it. Things like clicking the close button would be nice, until its coded for, a program will keep right on running and might even ignore CTRL-C in the terminal too if you are unlucky enough to have an infinite loop.

    SDL_Event event;
    bool running=true;
    
    while(running) {                                 // read the event queue
    	
      if( SDL_PollEvent( &event ) != 0 ) {           // if there is an event
        if(event.type==SDL_QUIT) { running=false; }  // if its a quit event
      }
    
    }
    

    This structure can be extended to handle other events, like the mouse moving over the window, clicking buttons, and also keypresses. It is however slightly confusing.

    while(running) {
    	
      if( SDL_PollEvent( &event ) != 0 ) {
    
        if (event.type==SDL_KEYDOWN) {
        if (event.key.keysym.sym==SDLK_LSHIFT) { ; }
        }
    
        if (event.type==SDL_KEYUP) {
          if (event.key.keysym.sym==SDLK_LSHIFT) { ; } // watch out for these
        }
    
        if (event.type == SDL_MOUSEMOTION) {
          mx=event.button.x;                           // button is the name of
          my=event.button.y;                           // the event structure
        }
    
        if (event.type == SDL_MOUSEBUTTONDOWN) {
          btn = event.button.button;                   // also containing the
          if (btn==1) { ; }                            // actual button data
          if (btn==3) { ; }
        }
    
        if (event.type == SDL_MOUSEBUTTONUP) {
          btn = event.button.button;
          if (btn==1) { ; }
          if (btn==3) { ; }
        }
          
    }
    

     There is an extensive list of event codes in the documentation which there is little point duplicating here.

  • Hello World

    Morning.Star02/19/2018 at 09:34 0 comments

    As I said, the fonts are a pile of poo on my machine so no text as yet.

    Instead, we'll open a graphic window and draw a square in it. Really easy...

    SDL_Rect rectangle;
    rectangle.x=100;
    rectangle.y=100;
    rectangle.w=100;
    rectangle.h=100;
    
    SDL_SetRenderDrawColor(render,255,255,192,255);
    SDL_RenderClear(render);
    
    SDL_SetRenderDrawColor(render,0,0,0,255);
    
    SDL_RenderFillRect(render, &rectangle)
    
    SDL_RenderPresent(render);

    First you'll need a rectangle to draw.  SDL provides a declared type for this, using the coordinates of the topleft corner and a width and height. Its a 100x100 square at (100,100).

    Next clear the screen to my favourite pastel yellow background with SetRenderDrawColour, using Red Green Blue and Alpha components. Setting Alpha to 255 makes the shape fully opaque, 0 makes it fully transparent.

    Change the draw colour to black for the next part, the rectangle.

    Draw the rectangle on the render buffer with RenderFillRect. There is also a RenderDrawRect function that draws an empty rectangle.

    And finally, RenderPresent draws the render buffer on the display, the window will be blank until this point. This fills the window border-to-border with the image, which is the same size as the window.

    SDL can also draw single pixels with

    SDL_Point pt;
    pt.x=100;
    pt.y=100;
    
    SDL_SetRenderDrawColor(render,255,0,0,255);
    SDL_RenderDrawPoint(render,pt.x,pt.y);

     and single pixel lines with

    SDL_RenderDrawLine(render,x1,y1,x2,y2);

     specifying the ends of the line like so.

    All three commands can also be called with an s on the end, and an array of coordinates to draw.

    SDL_Point pt[100];
    
    for (int n=0; n<100; n++) {
      pt[n].x=100+n;
      pt[n].y=100+n;
    }
    SDL_SetRenderDrawColor(render,255,0,0,255);
    SDL_RenderDrawPoints(render,pt,100);
    

     This will draw a diagonal line of points on the window in one operation.

  • Blinking Cursor

    Morning.Star02/19/2018 at 08:03 0 comments

    One of the reasons I like Python is Pygame, coincidentally written in SDL. It allows me to open up a graphics window, instead of boring console based stuff. I love the terminal, but sometimes a mouse is the only way. Doing that in C++ used to be a nightmare before I learned SDL. Interfacing to X directly is densly documented, I'm digging into that but its pretty much Linux-only research.

    This should work on anything...


    First of all, you'll need a working compiler.

    sudo apt-get install g++

    GNU C++11. I dream in this sometimes, recommended. ;-) 

    Next you'll need a copy of the SDL Library and at least the SDL_Image library, with development files.

    apt-cache search libsdl
    
    libsdl2-2.0-0 - Simple DirectMedia Layer
    libsdl2-dbg - Simple DirectMedia Layer debug files
    libsdl2-dev - Simple DirectMedia Layer development files
    libsdl2-doc - Reference manual for libsdl2
    
    libsdl2-image-2.0-0 - Image loading library for Simple DirectMedia Layer 2, libraries
    libsdl2-image-dbg - Image loading library for Simple DirectMedia Layer 2, debugging symbols
    libsdl2-image-dev - Image loading library for Simple DirectMedia Layer 2, development files
    libsdl2-mixer-2.0-0 - Mixer library for Simple DirectMedia Layer 2, libraries
    libsdl2-mixer-dbg - Mixer library for Simple DirectMedia Layer 2, debugging
    libsdl2-mixer-dev - Mixer library for Simple DirectMedia Layer 2, development files
    
    libsdl-ttf2.0-0 - TrueType Font library for Simple DirectMedia Layer 1.2, libraries
    libsdl-ttf2.0-dev - TrueType Font library for Simple DirectMedia Layer 1.2, development files
    

    You'll need at least

    sudo apt-get install libsdl2-2.0-0 libsdl2-dbg libsdl2-dev libsdl2-doc
    
    and
    
    sudo apt-get install libsdl2-image-2.0-0 libsdl2-image-dbg libsdl2-image-dev

    to get a minimal working installation.

    Now for the fun part.


    C++ doesnt have many prerequisites to just jump in, there are a few tho. CIN, COUT and the SDL library, plus tell the compiler I'm too std::lazy to type classes, I'm not using them.

    #include <iostream>
    #include <SDL2/SDL.h>
    
    using namespace std;
    

     Some basic parameters and somewhere to put stuff and we're off.

    #define W 1024
    #define H 768
    
    SDL_Window * outputwindow = NULL;
    SDL_Renderer * render = NULL;
    

    outputwindow * stores the structure to the window instance, feeds user input back to the program and hosts the renderer.

    render * stores the actual display, it is this you'll be writing to.

    int main() {
    
      outputwindow = SDL_CreateWindow("ChassC", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, W, H, 0);
      render = SDL_CreateRenderer(outputwindow, -1, SDL_RENDERER_ACCELERATED);
    
      SDL_Delay(3000);
    
      SDL_DestroyWindow(outputwindow);
      SDL_Quit();
    
    }

    There are other options to opening a window, it can be borderless and fill the display, plus SDL handles multiple displays nicely, and can jump a window from one display to another easily.

    Running that code will briefly open a graphical window before exiting gracefully.

View all 5 project logs

  • 1
    Compile the code

    Assuming the source code is in the current directory, this will compile and run it

    g++ chassc.cpp -w -lSDL2 -lSDL2_image -o chassc.hex && ./chassc.hex

View all instructions

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates