How Can You Draw Shapes in C Easily?


Sometimes you may want your C program to draw some simple shapes on the screen without using sophisticated GUI Frameworks like Microsoft Forms or Qt. What options are there and on which operating systems do they work?

The Simple Direct Media Library (SDL2) is the best and easiest way to draw Shapes in C. It is portable to most operating systems and provides simple functions to draw various Shapes like rectangles, circles, and more.

SDL2 Rectangles
Two Rectangles drawn in a C program with SDL2 Version 2.0.20

But the SDL2 is by far not the only possibility. There are more libraries and APIs that serve the purpose, some of them portable while others are system specific. Read on to find out more!

How Can You Draw a Shape in C?

When you want to draw simple shapes in C you have so many options. As always your choice of the appropriate library depends on the project you are working on. Do you program in Windows or Linux (or both)? Do you want it simple and neat or don’t mind some overhead?

Here is an overview of graphics libraries with which you can draw simple shapes in C. After that comes a more detailed explanation of each library and a small example for each one.

LibraryOperating SystemDedicated Shape Functions
SDL2Cross PlatformYes (e.g SDL_RenderDrawRect(…))
WinGDIWindowsYes (e.g. Rectangle(…), Polygon(…)
WinBGIWindowsYes (e.g. rectangle(…), circle(…))
DirectX WindowsYes (i.e ID2D1HwndRenderTarget_DrawRectangle(…))
DOSWindowsNo
gfxLinuxNo (Only lines with gfx_line(…))
XLibLinuxYes (i.e. XDrawRectangle(…))
OpenGLCross PlatformNo

Drawing Shapes in C with SDL2 (Cross Platform)

The Simple Direct Media Layer (SDL) is a cross platform library which you can download hereOpens in a new tab.. It is described there as “[…] cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D.” The library is written in C which makes it perfect for our purpose.

As with most cross-platform libraries you have to download the source and compile the lib for your target development system. Then you include the headers and link the generated lib.

The following example draws two rectangles in a Windows 64 Bit operating system with SDL2 Version 2.0.20. It uses a simple main loop that checks for input and draws the rectangles. If the users presses the ESC-Key, the program will quit.

The program will produce the output that you see in the Screenshot above.

#include <Windows.h>
#include "../SDL2/SDL2-2.0.20/include/SDL.h"

int SDL_OK() { return 0; }

int WINAPI WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
	_In_ LPSTR pCmdLine, _In_ int nCmdShow) {
	/* LOCALS */
	int isRunning = 1;
	SDL_Window* window;
	SDL_Renderer* renderer;
	SDL_Rect blueRect;
	SDL_Rect greenRect;
	SDL_Event sdlEvent;

	/* Initialize SDL and Window */
	/* ERROR Handling is missing */
	SDL_Init(SDL_INIT_VIDEO);
	SDL_CreateWindowAndRenderer(1024, 768, SDL_WINDOW_RESIZABLE, &window, &renderer);
	SDL_SetWindowTitle(window, "Modern C: Shapes with SDL2");

	/* Set Background Colour and Draw Background */
	SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); /* black */
	SDL_RenderClear(renderer);

	/* Initialize Rectangle Dimensions */
	blueRect.x = 250;
	blueRect.y = 150;
	blueRect.w = 300;
	blueRect.h = 200;
	greenRect.x = 500;
	greenRect.y = 210;
	greenRect.w = 200;
	greenRect.h = 200;
	/* End */

	/* Main Loop */
	while (isRunning) {
		/* Check for User Input */
		while (SDL_PollEvent(&sdlEvent) != SDL_OK()) {
			switch (sdlEvent.type)
			{
			case SDL_KEYUP:
				switch (sdlEvent.key.keysym.sym) {
				case SDLK_ESCAPE:
					isRunning = 0; /* End the Main Loop */
					return;
				default:
					break;
				}
				break;
			default:
				break;
			}
		}

		/* Draw Blue Rectangle */
		SDL_SetRenderDrawColor(renderer, 0, 0, 255, 255); /* blue */
		SDL_RenderDrawRect(renderer, &blueRect);
		SDL_RenderFillRect(renderer, &blueRect);

		/* Draw Green Rectangle */
		SDL_SetRenderDrawColor(renderer, 0, 255, 0, 255); /* green */
		SDL_RenderDrawRect(renderer, &greenRect);
		SDL_RenderFillRect(renderer, &greenRect);

		/* Render Rectangles */
		SDL_RenderPresent(renderer);
	} /* End Main Loop */

	/* Clean Up and Quit */
	SDL_DestroyRenderer(renderer);
	SDL_DestroyWindow(window);
	SDL_Quit();

	return 0;
}

Drawing Shapes in C on Windows

On Windows you have several libraries respectively APIs to draw shapes on the screen. Some are part of the operating system while others are separate ones. The current libraries are all under Microsoft development while the outdated ones come from Borland. The following paragraphs show you the libraries with a short example to draw a rectangle.

<wingdi.h> from Windows GDI

The Windows Graphics Device Interface (Windows GDI) is the windows operating system API for C/C++ programmers who want to access graphics features. You can show graphics and formatted text on video display and printer without accessing the hardware directly.

#include <wingdi.h>

static const COLORREF rgbBlack =  0x00000000;
static const COLORREF rgbWhite =  0x00FFFFFF;
static HDC _hdc;

...

void draw()
{
    SelectObject(_hdc, GetStockObject(NULL_BRUSH));
    SelectObject(_hdc, GetStockObject(WHITE_PEN));
    Rectangle(_hdc, 50, 75, 691, 511);
}

--

Direct2D from DirectX

DirectX is the Graphics respectively Game Programming API from Microsoft which is exclusively for the Microsoft Windows operating system. Direct2D (formerly DirectDraw) is a subset of DirectX and contains functions for plain 2D graphics without 3D support.

The latest Headers of Direct2D removed the C Definitions of the Direct2D COM functions and thus abandoned pure C support. In order to use it with C you have to target the Windows 10 SDK to 10586 or earlier.

With DirectX you work within the Windows API contexts of HRESULTS and CALLBACKs. The following is an excerpt of a Direct2D program drawing a rectangle. Window Management is not included in this example.

HRESULT TestApp_OnRender(d2dobj *obj)
{
  HRESULT hr = S_OK;
...
      ID2D1HwndRenderTarget_BeginDraw (obj->hwnd_render_target);
      ID2D1HwndRenderTarget_SetTransform (obj->hwnd_render_target, &identity);
      ID2D1HwndRenderTarget_Clear (obj->hwnd_render_target, &white);
   
      rect1.left = rtSize.width/2 - 50.0f;
      ..
      rect2.bottom = rtSize.height/2 + 100.0f;
        
      ID2D1HwndRenderTarget_FillRectangle (obj->hwnd_render_target,
                                           &rect1,
                                           obj->gray_brush);
      ID2D1HwndRenderTarget_DrawRectangle (obj->hwnd_render_target,
                                           &rect2,
                                           obj->blue_brush,
                                           1.0f,
                                           NULL);
      hr = ID2D1HwndRenderTarget_EndDraw (obj->hwnd_render_target, NULL, NULL);
    }
 ...
  return hr;
}

<graphics.h> from WinBGI

The BGI API from Borland is very antiquated and has its origin in the DOS days. However, there is a current Win32 and even Win64 implementation called WinBGIm which you can find hereOpens in a new tab.. Be aware though that this is by no means a modern solution for your problem.

#include <graphics.h>
#include <conio.h>

int main()
{
   int gd = DETECT,gm,left=100,top=100,right=200,bottom=200,x= 300,y=150,radius=50;
   initgraph(&gd, &gm, "C:\\test\\BGI");

   rectangle(left, top, right, bottom);
   outtextxy(left + 100, top + 325, "Drawing a Rectangle");

   getch();
   closegraph();
   return 0;
}

The Ancient <dos.h>

Another ancient Borland library that comes from the DOS VGA era. In contrast to <graphics.h> there are no real drawing functions but you can draw lines by plotting pixels. In order to draw a rectangle you would have to provide every pixel instead of only the four coordinates for the corners.

Because the library is so outdated and compiler specific no example is provided here.

Drawing Shapes in C on Linux

Under Linux you obviously cannot use the Microsoft Windows libraries. But Linux has some decent graphics libraries by itself, apart from the Cross Platform ones that are also describe in this article. We examine them in the following paragraphs, along with a brief example of each.

gfx

This is a small and easy to learn graphics library written in C for XWindow systems like Linux. The creators of the library provide a minimum example on their website which I will show you here with dedications to Prof. Thain:

/*
A simple example of using the gfx library.
CSE 20211
9/7/2011
by Prof. Thain
*/

#include <stdio.h>
#include "gfx.h"

int main()
{
	int ysize = 300;
	int xsize = 300;

	char c;

	// Open a new window for drawing.
	gfx_open(xsize,ysize,"Example Graphics Program");

	// Set the current drawing color to green.
	gfx_color(0,200,100);

	// Draw a triangle on the screen.
	gfx_line(100,100,200,100);
	gfx_line(200,100,150,150);
	gfx_line(150,150,100,100);

	while(1) {
		// Wait for the user to press a character.
		c = gfx_wait();

		// Quit if it is the letter q.
		if(c=='q') break;
	}

	return 0;
}

XLib

XLib is not a dedicated graphics library but rather a general library that interacts with an X Server on a XWindow system. Part of this protocol are some drawing functions which you can use for your programs.

This example is shortened to focus on drawing the shapes. Window Management is also part of this library but omitted here.

main()
{
  Display* display;		/* pointer to X Display structure.           */
  Window win;			/* pointer to the newly created window.      */
  GC gc;			/* GC (graphics context) used for drawing    */

  ...

  XDrawPoint(display, win, gc, width-5, height-5);
  XDrawRectangle(display, win, gc, 120, 150, 50, 60);
  XFillRectangle(display, win, gc, 60, 150, 50, 60);

  XFlush(display);
  XSync(display, False);

  ...

  XCloseDisplay(display);
}

Drawing Shapes in C with OpenGL (Cross Platform)

You may wonder why I put OpenGL at last and why I do not recommend it as the best possibility for easily drawing a simple shape. I even wrote an article where I described that it is possible and sensible to use OpenGL with pure C. So let me explain…

When you search for how to draw a simple rectangle with OpenGL you will most likely find something like this:

glBegin(GL_TRIANGLES);
    glColor3f(1.0f, 0.0f, 0.0f);   glVertex2f(0.0f,   1.0f);
    glColor3f(0.0f, 1.0f, 0.0f);   glVertex2f(0.87f,  -0.5f);
    glColor3f(0.0f, 0.0f, 1.0f);   glVertex2f(-0.87f, -0.5f);
glEnd();

However, this is the so called (and deprecated) Immediate Mode that was the standard before OpenGL 3.0. It is not optimal because the drive can’t tell the GPU to render before glEnd is called which can take down the performance.

The modern version of this example above includes the use of vertex buffers and requires slightly more work:

float verts = {...};  /* vertex position data */
float colors = {...}; /* colour value data */

GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

GLuint buf[2];
glGenBuffers(2, buf);

glBindBuffer(GL_ARRAY_BUFFER, buf[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
glEnableVertexAttribArray(0); 
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, buf[1]);
glBufferData(GL_ARRAY_BUFFER, sizeof(colors), colors, GL_STATIC_DRAW);
glEnableVertexAttribArray(1); 
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, 0);

glDrawArrays(GL_TRIANGLES, 0, 3); 

If you write a 3D Game or Application or want to do some more sophisticated stuff in your 2D Game than I would prefer this modern OpenGL approach. But since this article is about simple and easy ways to draw Shapes in C, I would go with SDL2.

When you use SDL2 you can also access OpenGL features in case you change your mind down the road of your project.

Marco Lieblang

Professional Programmer since 2003, passionate Programmer since the mid 90's. Developing in many languages from C/C++ to Java, C#, Python and some more. And I also may know a bit about Assembly Languages and Retro Systems.

Recent Posts