Click here to Skip to main content
13,349,293 members (64,332 online)
Click here to Skip to main content
Add your own
alternative version


79 bookmarked
Posted 11 Apr 2012

Simple two file graphics library for C/C++

, 16 Apr 2012
Rate this:
Please Sign up or sign in to vote.
A two file graphics library for debugging otherwise graphically deficient applications.

Example image created with this library


Limited resource examples




I am always needing to add simple graphics output to console, services, embedded, or other applications that do not have natural graphics ability for debugging purposes. So I have brought together several functions to make this easy.

In this article I will provide code and explanation for a simple graphics library that can perform in the most limited circumstances.

So features of this library that I believe will make it unique and worthwhile will be...

  • Usable in C and C++.
  • Be small and contained entirely in two files, Namely ezdib.h and ezdib.c. Just drop them in and go.
  • Support saving dib (.bmp) files.
  • Provide basic functions for drawing pixels, circles, rectangles, fills, text, as well as raw buffer access. You can tell from the image provided much of what is possible.
  • Be completely self-contained. This includes the font functions and fonts.
  • Be easy to customize and extend, including adding new fonts.

Creating a blank image.

The ezd_create() functions allocates the memory for the image and returns all the image details opaquely behind a handle of type HEZDIMAGE. The following code creates a 640 x 480 image using 24 bits per pixel. This library is only supporting 1, 24 and 32 bit pixel depths. I believe this provides good functionality without compromising simplicity.

Notice the negative height. Somewhere in the distant past, someone decided to store Windows images upside down. Using a negative height will cause the images to be stored in memory right side up. All functions in this library will work just fine either way, but be aware that coordinates on the Y axis will be reversed depending on which way you choose to go.

// Create an image
HEZDIMAGE hDib = ezd_create( 640, -480, 24 );

// Fill in the background with a dark gray color
ezd_fill( hDib, 0x606060 );

// Save a test image
ezd_save( hDib, "test.bmp" );

// Free resources
ezd_destroy( hDib );

Drawing and text functions.

I have attempted to keep these functions as simple as possible while still keeping them reasonably fast. My hope is that you can look at these functions and easily understand what's going on and have no problems adding your own functionality.

Line drawing is implemented using incremental division or Bresenham's Line Algorithm. The flood fill is a simple four directional fill. The rest aren't interesting enough to mention, but all the details are in the code ;)

// Draw random green line
ezd_line( hDib, 100, 100, 200, 200, 0x00ff00 ),

// Random yellow box
ezd_fill_rect( hDib, 300, 200, 350, 280, 0xffff00 );

// Draw random white dot
ezd_set_pixel( hDib, 50, 50, 0xffffff );

// Rectangle
ezd_rect( hDib, 50, 50, 100, 100, 0x000000 );

// Circle
ezd_circle( hDib, 100, 100, 10, 0x000000 );

// Fill the circle
ezd_flood_fill( hDib, 100, 100, 0x000000, 0x808080 );

Probably the most interesting of the drawing functions is the text functions. The following code exemplifies creating a font and drawing text into the image.

// Create a medium font
HEZDFONT hFont = ezd_load_font( EZD_FONT_TYPE_MEDIUM, 0, 0 );

// Draw some familiar text
ezd_text( hDib, hFont, "Hello World!", -1, 10, 10, 0xffffff );

// Free the memory
ezd_destroy_font( hFont );

The fonts are in a very simple raster based format. Only 8 bit ASCII characters are supported. The format is outlined below.

| [character] | [width] | [height] | [bitmap] ~ |
  • character - One byte representing the glyph character
  • width - One byte specifies the character width in bits
  • height - One byte specifies the character height in bits
  • bitmap - One bit per pixel bitmap padded to one byte

Here is an exert from an actual font definition. You can simply pass a pointer to the byte array to the ezd_load_font() function. If EZD_STATIC_FONTS is not defined, ezd_load_font() will copy the font table, index the glyphs, and return a handle. If EZD_STATIC_FONTS is defined, the function will simply return the pointer you passed in, cast to type HEZDFONT.

// Example font map
static const unsigned char my_font [] =
	// Default glyph
	'.', 1, 6,	0x08,

	// Tab width
	'\t', 8, 0,

	// Space
	' ', 3, 0,

	'!', 1, 6,	0xea,
	'@', 4, 6,	0x69, 0xbb, 0x87,
	'#', 5, 6,	0x57, 0xd5, 0xf5, 0x00,
	'$', 5, 6,	0x23, 0xe8, 0xe2, 0xf8,

	'0', 3, 6,	0x56, 0xd4, 0x31,
	'1', 2, 6,	0xd5, 0x42,
	'2', 4, 6,	0xe1, 0x68, 0xf0,

	'A', 4, 6,	0x69, 0xf9, 0x90,
	'B', 4, 6,	0xe9, 0xe9, 0xe0,
	'C', 4, 6,	0x78, 0x88, 0x70,


HEZDFONT hFont = ezd_load_font( my_font, 0, 0 );

Embedded systems

I have added several features to specifically address the issues faced by embedded designs. This library may still be a bit beefy for a tiny 8051 system, but as long as you have a reasonable system, you should be able to squeeze this library in. If you enable all the following macros, you will basically be left with a pure C implementation.

The following macros are explained in ezdib.c and can be defined there, although, good practices would probably be to define these in your make or project files.


Defining this macro will eliminate the glyph indexing. This means that font rendering may be slightly slower, but no extra memory will have to be allocated.


If you don't have string.h, you can enable this macro and the library will use internal substitutes for memcpy and memset. These will probably be a bit slower of course.


You can enable this macro to disable the use of malloc, calloc, and free. You will then need to pass in static buffers using ezd_initialize() and ezd_set_image_buffer(). The EZD_HEADER_SIZE macro will indicate the minimum space needed for the image header. See the code below.

// User buffer
const int w = 320, h = 240;
char user_header[ EZD_HEADER_SIZE ];
char user_image_buffer[ w * h / 8 ];

// Initialize image header
hDib = ezd_initialize( user_header, sizeof( user_header ), w, -h, 1,
                           EZD_FLAG_USER_IMAGE_BUFFER );
if ( !hDib )
    return -1;

// Set custom image buffer
if ( !ezd_set_image_buffer( hDib, user_image_buffer, sizeof( user_image_buffer ) ) )
    return -1;

#define EZD_NO_FILES

You're embedded system may not support or even have a file system. Use this macro to disable file system output.

#define EZD_NO_MATH

If you don't have math.h. This is used by the circle drawing functions.

Monochrome images / one bit per pixel color depths.

This library also supports one bit per pixel color depths. So if you're stuck with a monochrome LCD display, and can afford the space for a back buffer, you can use this mode to render images. Maybe if you're memory mapped you can render straight to the device using ezd_set_image_buffer().

Example rendered to a monochrome image


Unbuffered graphics output

What about the worst case scenario. You have a tiny processor with no space for a back buffer. Or maybe you just want to use some type of serial display device, like a dot matrix screen.

As long as you can provide a 'set pixel' function that takes the X and Y coords, a color, and render it. You can use this library.

Here's an example that uses a custom set pixel function to emulate a dot matrix display.

typedef struct _SDotMatrixData
    int w;
    int h;
} SDotMatrixData;

int dotmatrix_writer( void *pUser, int x, int y, int c, int f )
    if ( !hDib )
        return 0;


    return 1;

SDotMatrixData dmd;
const int w = 640, h = 480;

printf( "Creating dotmatrix.bmp\n" );

// Create a 'fake' dot matrix display
hDmd = ezd_create( w, -h, 24, 0 );
if ( !hDmd )
    return -1;

// Give our dot matrix display a black background
ezd_fill( hDmd, 0 );

// Create video 'driver'
hDib = ezd_create( w, -h, 1, EZD_FLAG_USER_IMAGE_BUFFER );
if ( !hDib )
    return -1;

// Set pixel callback function
ezd_set_pixel_callback( hDib, &dotmatrix_writer, hDmd );

// Draw into hDib

Example 'dot matrix device' output


Unbuffered ASCII output

As a final fun example, here's a complete program that implements a set pixel function that renders the output to an ASCII string buffer.

typedef struct _SAsciiData
    int sw;
    unsigned char *buf;
} SAsciiData;

int ascii_writer( void *pUser, int x, int y, int c, int f )
    SAsciiData *p = (SAsciiData*)pUser;
    if ( !p )
        return 0;

    unsigned char ch = (unsigned char)( f & 0xff );
    if ( ( '0' <= ch && '9' >= ch )
         || ( 'A' <= ch && 'Z' >= ch )
         || ( 'a' <= ch && 'z' >= ch ) )

        // Write the character
        p->buf[ y * p->sw + x ] = (unsigned char)f;


        // Write the 'color'
        p->buf[ y * p->sw + x ] = (unsigned char)c;

    return 1;

int main( int argc, char* argv[] )
    int b, x, y;
    HEZDFONT hFont;
    SAsciiData ad;
    const int w = 44, h = 20;
    char ascii[ ( w + 1 ) * h + 1 ];
    char user_header[ EZD_HEADER_SIZE ];

    hDib = ezd_initialize( user_header, sizeof( user_header ),
                                   w, -h, 1, EZD_FLAG_USER_IMAGE_BUFFER );
    if ( !hDib )
        return -1;

    // Null terminate
    ascii[ ( w + 1 ) * h ] = 0;

    // Fill in new lines
    for ( y = 0; y < h - 1; y++ )
        ascii[ y * ( w + 1 ) + w ] = '\n';

    // Set pixel callback function
    ad.sw = w + 1; ad.buf = ascii;
    ezd_set_pixel_callback( hDib, &ascii_writer, &ad );

    // Fill background with spaces
    ezd_fill( hDib, ' ' );

    // Border
    ezd_rect( hDib, 0, 0, w - 1, h - 1, '.' );

    // Head
    ezd_circle( hDib, 30, 10, 8, 'o' );

    // Mouth
    ezd_arc( hDib, 30, 10, 5, 0.6, 2.8, '-' );

    // Eyes
    ezd_set_pixel( hDib, 28, 8, 'O' );
    ezd_set_pixel( hDib, 32, 8, 'O' );

    // Nose
    ezd_line( hDib, 30, 10, 30, 11, '|' );

    // Draw some text
    hFont = ezd_load_font( EZD_FONT_TYPE_SMALL, 0, 0 );
    if ( hFont )
        ezd_text( hDib, hFont, "The\nEnd", -1, 4, 4, '#' );

    if ( hFont )
        ezd_destroy_font( hFont );

    if ( hDib )
        ezd_destroy( hDib );

    // Show our buffer
    printf( "%s\n", ascii );

    return 0;
.                                          .
.                             o            .
.                          ooooooo         .
.   TTT  h  h  eeee      oo       oo       .
.    T   h  h  e        oo         oo      .
.    T   hhhh  eee      o           o      .
.    T   h  h  e       o             o     .
.    T   h  h  eeee    o    O   O    o     .
.                      o             o     .
.                     oo      |      oo    .
.   EEEE  n  n  ddd    o      |      o     .
.   E     nn n  d  d   o  -       -  o     .
.   EEE   n nn  d  d   o   -     -   o     .
.   E     n  n  d  d    o   -----   o      .
.   EEEE  n  n  ddd     oo         oo      .
.                        oo       oo       .
.                          ooooooo         .
.                             o            .


I hope you find the code useful and flexible. Some things I'd like to add to the project would be.

  • AVI, animated gif, or other video output.
  • Font editing script, maybe written in javascript? The one I used for creating these fonts is a bit cumbersome.


This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


About the Author

Robert Umbehant
Software Developer (Senior)
United States United States
No Biography provided

You may also be interested in...


Comments and Discussions

Questionhow to use it Pin
Member 1219910310-Dec-15 8:01
memberMember 1219910310-Dec-15 8:01 
Generalnice Pin
Member 1064405027-Apr-15 23:29
memberMember 1064405027-Apr-15 23:29 
GeneralMy vote of 5 Pin
enhzflep10-Nov-13 20:59
memberenhzflep10-Nov-13 20:59 
GeneralMy vote of 5 Pin
Mihai MOGA12-May-12 19:33
memberMihai MOGA12-May-12 19:33 
QuestionMy vote of 5 seemed to disappear? Pin
KjellKod.cc21-Apr-12 23:11
memberKjellKod.cc21-Apr-12 23:11 
GeneralUseful utility Pin
Shakti Misra16-Apr-12 1:19
memberShakti Misra16-Apr-12 1:19 
QuestionNice work Pin
Nelek12-Apr-12 14:28
memberNelek12-Apr-12 14:28 
QuestionVery useful indeed Pin
Randor 11-Apr-12 6:02
member Randor 11-Apr-12 6:02 
AnswerRe: Very useful indeed Pin
Robert Umbehant11-Apr-12 17:04
memberRobert Umbehant11-Apr-12 17:04 
Hi David,

Your suggestions are great. I implemented #1, it is checked in the google code repository. I haven't updated the article attachment, I'll wait until I can address your second comment Wink | ;)

You've made me realize I should add a few features to better support embedded platforms. I'll try and squeeze in support for user allocated buffers, big endian, and callbacks for write functions soon.

Thanks for taking the time to comment, I'd be interested to hear how your project goes.

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.180111.1 | Last Updated 16 Apr 2012
Article Copyright 2012 by Robert Umbehant
Everything else Copyright © CodeProject, 1999-2018
Layout: fixed | fluid