-----------------------------------
Drawing in C the easy way
-----------------------------------

An easy way to draw a monochrome picture in C is to store the drawing into an array of 8-bit integers previously allocated in memory and, when finished, save it to disk in ".xbm" format.  A file with the ".xbm" file extension is an X Bitmap Graphic file in which monochrome images are represented with ASCII text.  While this is not "drawing" in a true sense, because the picture is not displayed on a screen in real time, it has the advantage that you can generate high resolution images also on computers without graphic capabilities.   In this implementation each drawing is 512x512 pixels wide, monochrome, stored in an array of 32768 bytes (C "char" type) for performance reasons, but you can easily modify the code to increase the resolution.  All functions use integer arithmetic and are very fast.  Algorythms to draw sloped lines and circles are Bresenham ones.  To open ".xbm" images and convert them to other formats you can use the GIMP. 

All the source code on this page is released under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version, and is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.


  • [Back to the home page]

  • Functions usage

    The memory to store an image can be allocated as follows:
    char img[32767];

    To clear the image (reset every bit) is sufficient to invoke the "clear" function:
    clear(img);

    To set(c=1)/reset(c=0) the point x,y you must invoke the "plot" function as follows:
    plot(img, x, y, c);

    To draw a black(c=1)/white(c=0) line from (x1,y1) to (x2,y2) you must invoke the "line" function as follows:
    line(img, x1, y1, x2, y2, c);

    To draw a black(c=1) or a white(c=0) rectangle with opposite vertices (x1,y1), (x2,y2) you must invoke the "box" function as follows:
    (set s=0 for empty rectangle and s=1 for filled rectangle)
    box(img, x1, y1, x2, y2, c, s);

    To draw a black(c=1) or a white(c=0) circle centered in (xc,yc) and radius "r" you must invoke the "circle" function as follows:
    (set s=0 for empty circle and s=1 for filled circle)
    circle(img, xc, yc, r, c, s);

    To save the drawing in xbm format you must invoke the "save" function:
    save(img, filename);

    In addition you can draw efficiently black(c=1) or white(c=0) horizontal or vertical lines with the functions "hline" and "vline":
    hline(img, x1, x2, y, c);
    vline(img, x, y1, y2, c);

    You can also write a single character 'n' at (x,y) with scale factor "s" with the function:
    put14seg(img, x, y, n, s);

    and a string "a" (last character must be '\0') at (x,y) with scale factor "s" with the function:
    write14seg(img, x, y, a, s);


  • [Back to the home page]

  • Sample program

    Here are the C functions with an example of use.
    The program draws the picture of a man with some characters and saves the image in the file "man.xbm":
    You can copy and paste the code you need from this html page.
    Remember to copy both the function prototypes and the actual code.

    Or you can download the C source file here: draw_a_man.c

    And the "xbm" output file here: man.xbm


    #include<stdio.h>
    #include<stdlib.h>

    /* function prototypes */
    void clear(char *img);
    void plot(char *img, int x, int y, char c);
    void iswap(int *a, int *b);
    void line(char *img, int x1, int y1, int x2, int y2, char c);
    void hline(char *img, int x1, int x2, int y, char c);
    void vline(char *img, int x, int y1, int y2, char c);
    void box(char *img, int x1, int y1, int x2, int y2, char c, char s);
    void circle(char *img, int xc, int yc, int r, char c, char s);
    void byte2hex(char c, char *hc, char *lc);
    void save(char *img, char *filename);
    void put14seg(char *img, int x, int y, char n, int s);
    void write14seg(char *img, int x, int y, char *a, int s);


    /* draw a man surronded by a frame with characters */
    void main(){

     char g[32767];
     char imagename[13]="man.xbm";

     clear(g);

     circle(g,255,375,100,1,0);
     circle(g,215,400,10,1,1);
     circle(g,295,400,10,1,1);
     line(g,255,400,240,360,1);
     line(g,255,400,270,360,1);
     hline(g,240,270,360,1);
     hline(g,220,290,320,1);
     box(g,240,275,270,175,1,1);
     line(g,240,175,220,75,1);
     line(g,270,175,290,75,1);
     hline(g,220,200,75,1);
     hline(g,290,310,75,1);
     line(g,240,275,180,215,1);
     line(g,270,275,330,215,1);
     box(g,55,50,455,500,1,0);
     write14seg(g,120,480,"0123456789/-+*=><ABCDEFGHIJKLMNOPQRSTUVWXYZ\0",1);
     write14seg(g,90,250,"IK2PIH\0",2);
     write14seg(g,350,250,"MARCO\0",2);
     save(g,imagename);
    }



    /* clear the image */
    void clear(char *img)
    {
     int i;
     for(i=0;i<=32767;i++)
                                img[i]=0;
     return;
    }



    /* set/reset the point (x,y) */
    void plot(char *img, int x, int y, char c)
    {
     if((x>=0)&&(y>=0)&&(x<=511)&&(y<=511)&&((c==0)||(c==1)))
      {if(c==0) img[x/8+(64*(511-y))]&=~(1<<(x%8));
       else     img[x/8+(64*(511-y))]|=(1<<(x%8));}
     return;
    }



    /* swap the two integers a and b */
    void iswap(int *a, int *b)
    {
     int t;
     t=*a;
     *a=*b;
     *b=t;
     return;
    }



    /* draw a black/white line from x1,y1 to x2,y2 with Bresenham */
    void line(char *img, int x1, int y1, int x2, int y2, char c)
    {
     int x,y,adx,ady,m,e,s;

     adx=abs(x2-x1);
     ady=abs(y2-y1);
     s=0;

     if(adx<ady)
         {iswap(&x1,&y1);
          iswap(&x2,&y2);
          iswap(&adx,&ady);
          s=1;}
     
     if(x1>x2)
         {iswap(&x1,&x2);
          iswap(&y1,&y2);}

     m=2*ady;
     e=m-adx;
     for(x=x1,y=y1;x<=x2;x++)
                       {if(s==0) plot(img,x,y,c);
                        else     plot(img,y,x,c);
                        e+=m;
                        if(e>0)
                              {if(y2>=y1) y++;
                        else y--;
                               e-=2*adx;}
                        }
    return;
    }



    /* draw a black/white horizontal line from x1,y to x2,y */
    void hline(char *img, int x1, int x2, int y, char c)
    {
     int x;

     if(x1>x2)
          iswap(&x1,&x2);

     for(x=x1;x<=x2;x++)
          plot(img,x,y,c);
     return;
    }



    /* draw a black/white vertical line from x,y1 to x,y2 */
    void vline(char *img, int x, int y1, int y2, char c)
    {
     int y;

     if(y1>y2)
          iswap(&y1,&y2);

     for(y=y1;y<=y2;y++)
          plot(img,x,y,c);

     return;
    }




    /* draw a black/white empty/filled rectangle given two opposite vertices */
    void box(char *img, int x1, int y1, int x2, int y2, char c, char s)
    {
     int y;

     if(s==0)
      {hline(img,x1,x2,y1,c);
       hline(img,x1,x2,y2,c);
       vline(img,x1,y1,y2,c);
       vline(img,x2,y1,y2,c);}
     else
       {if(y1>y2)
             iswap(&y1,&y2);
        for(y=y1;y<=y2;y++)
             hline(img,x1,x2,y,c);
       }
    return;
    }




    /* draw a black/white empty/filled circle given center and radius with Bresenham */
    void circle(char *img, int xc, int yc, int r, char c, char s)
    {
     int x,y,d;

     d=3-r-r;
     for(x=0,y=r;x<y;x++)
       {
       if(d>0)
        {y--;
         d+=4*(x-y)+10;}
       else
         d+=4*x+6;

       if(s==0)
          {plot(img,xc+x,yc+y,c);
           plot(img,xc-x,yc+y,c);
           plot(img,xc+x,yc-y,c);
           plot(img,xc-x,yc-y,c);
           plot(img,xc+y,yc+x,c);
           plot(img,xc-y,yc+x,c);
           plot(img,xc+y,yc-x,c);
           plot(img,xc-y,yc-x,c);}
       else
          {hline(img,xc-x,xc+x,yc+y,c);
           hline(img,xc-x,xc+x,yc-y,c);
           hline(img,xc-y,xc+y,yc+x,c);
           hline(img,xc-y,xc+y,yc-x,c);}
       }
     return;
    }
     


    /* convert a byte to it's hexadecimal equivalent */
    void byte2hex(char c, char *hc, char *lc)
    {
     char l,h;

     l=(c & 0x0f);
     if(l<=9) *lc='0'+l;
     else *lc='a'+l-10;

     h=((c>>4) & 0x0f);
     if(h<=9) *hc='0'+h;
     else *hc='a'+h-10;

     return;
    }



    /* save the image to disk in xbm format */
    void save(char *img, char *filename)
    {
    FILE *fp;
    int i,j;
    char hc,lc;

    if(!(fp=fopen(filename,"w")))
       {fprintf(stderr,"Can't open the file\n");
        exit(EXIT_FAILURE);}

    fprintf(fp,"#define image_width 512\n");
    fprintf(fp,"#define image_height 512\n");
    fprintf(fp,"static unsigned char image_bits[] = {\n");

    for(j=0;j<=511;j++)
       {for(i=0;i<=63;i++)
            {byte2hex(img[i+64*j],&hc,&lc);
             if(!((j==0) && (i==0)) && (i%16==0)) fprintf(fp,"\n");
             fprintf(fp,"0x%c%c",hc,lc);
             if(!((j==511) && (i==63))) fprintf(fp,",");}
       }
     
    fprintf(fp,"};\n");
    fclose(fp);
    return;
    }



    /* write chars (0123456789/-+*=><ABCDEFGHIJKLMNOPQRSTUVWXYZ) in 14 seg format at (x,y) */
    /* s=scale */
    /* used by the "write14seg" function */
    void put14seg(char *img, int x, int y, char n, int s)
    {
     /* characters belonging to each segment */
     char a[25]="02356789ABCDEFGIOPQRSTZ$$";
     char b[25]="0123489ABDHJMNOPQRUW$$$$$";
     char c[25]="01345689ABDGHJMNOQSUW$$$$";
     char d[25]="0235689BCDEGIJLOQSUZ=$$$$";
     char e[25]="0268ACEFGHJKLMNOPQRUVW$$$";
     char f[25]="045689ACEFGHKLMNOPQRSUVW$";
     char g1[25]="2456789AEFHKPRS-+*=$$$$$$";
     char g2[25]="23456789ABEGHPRS-+*=$$$$$";
     char h[25]="MNXY*>$$$$$$$$$$$$$$$$$$$";
     char i[25]="BDIT+*$$$$$$$$$$$$$$$$$$$";
     char j[25]="17KMVXYZ*/<$$$$$$$$$$$$$$";
     char k[25]="VWXZ*/>$$$$$$$$$$$$$$$$$$";
     char l[25]="7BDITY+*$$$$$$$$$$$$$$$$$";
     char m[25]="KNQRWX*<$$$$$$$$$$$$$$$$$";

     int z; /* counter */

     /* for each of the 14 segments draw the corresponding line if required */
     for(z=0;z<=24;z++)
             {if(n==a[z]) line(img,x,y+(8*s),x+(4*s),y+(8*s),1);
              if(n==b[z]) line(img,x+(4*s),y+(4*s),x+(4*s),y+(8*s),1);
              if(n==c[z]) line(img,x+(4*s),y,x+(4*s),y+(4*s),1);
              if(n==d[z]) line(img,x,y,x+(4*s),y,1);
              if(n==e[z]) line(img,x,y,x,y+(4*s),1);
              if(n==f[z]) line(img,x,y+(4*s),x,y+(8*s),1);
              if(n==g1[z]) line(img,x,y+(4*s),x+(2*s),y+(4*s),1);
              if(n==g2[z]) line(img,x+(2*s),y+(4*s),x+(4*s),y+(4*s),1);
              if(n==h[z]) line(img,x+(2*s),y+(4*s),x,y+(8*s),1);
              if(n==i[z]) line(img,x+(2*s),y+(4*s),x+(2*s),y+(8*s),1);
              if(n==j[z]) line(img,x+(2*s),y+(4*s),x+(4*s),y+(8*s),1);
              if(n==k[z]) line(img,x+(2*s),y+(4*s),x,y,1);
              if(n==l[z]) line(img,x+(2*s),y+(4*s),x+(2*s),y,1);
              if(n==m[z]) line(img,x+(2*s),y+(4*s),x+(4*s),y,1);}
     return;
    }



    /* write string "a" in 14 segment format at (x,y) with scale factor "s" */
    void write14seg(char *img, int x, int y, char *a, int s)
    {
     int i;

     i=0;
     while(a[i]!='\0')
          {put14seg(img,x+(i*6*s),y,a[i],s);
           i++;}
     return;
    }

  • [Back to the home page]

  • Output

    The result (output file) is as follows:
    (the image was converted from the original "xbm" format to "png" with the GIMP)

    man.png
  • [Back to the home page]