-
Extending the Graphics
02/19/2018 at 13:07 • 0 commentsThat 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); p.x=800; p.y=200; pieslice(p,0,270,50);
-
We Can Haz Cheezburger?
02/19/2018 at 12:41 • 0 commentsYes 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
02/19/2018 at 11:02 • 0 commentsNow 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
02/19/2018 at 09:34 • 0 commentsAs 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
02/19/2018 at 08:03 • 0 commentsOne 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.