BMP Out in C


26 posts   Page 1 of 2   1, 2
by woodystanford » Sat Feb 25, 2017 4:28 am
Do you need a way to just output an image in C without messing with librarys, external dependancies or even a *.h file? It will work with ANY version or flavor of C as it is written to be 100% ANSI C. Its only draw back is that it isn't real-time.

Here is some code you put into a file called "graph.c" and you include it in your c program like this:

#include "graph.c"

(quotes, NOT < > and to call it relative it has to be in the same directory as your C source file)

Here is the code. Just copy and paste it to a text file called "graph.c":

Code: Select all
// *******************************************
//
// GRAPH3.C - Basic Graphics Support for CLI's
//
// *******************************************

//statically allocated arrays for graphics output
//main historgram display. 10 viewports
char red[640][480][10];
char green[640][480][10];
char blue[640][480][10];

long sx=640; //dimensions of viewport
long sy=480;

//CURRENT COLOR SUPPORT
long cur_red[10];
long cur_green[10];
long cur_blue[10];

//note if using a refreshing browser page for output remember to include cache defeating

// FUNCTION PROTOTYPES

// Limited graphics support
//void writebmp(char *fname); //writes out the graphics arrays to a BMP file
//void bar2(int x, int y, int w, int h, char i);
//void clrscr();

// END OF FUNCTION PROTOTYPES

//Graphics Support Routines

initgraph3()
{
   int a;

   //set all current colors to white
   //for all viewports
   //(this way it will draw right out of the package)
   for (a=0;a<10;a++)
   {
      cur_red[a]=255;
      cur_green[a]=255;
      cur_blue[a]=255;
   }

}

setcolor(int vp, int r, int g, int b)
{
   cur_red[vp]=r; cur_green[vp]=g; cur_blue[vp]=b;
}

clrscr(int vp)
{
   int x,y;

   //clearscreen to white
   for (x=0;x<sx;x++)
      for (y=0;y<sy;y++)
      {
//         red[x][y][vp]=255; //to white
//         green[x][y][vp]=255;
//         blue[x][y][vp]=255;

         red[x][y][vp]=0; //to black
         green[x][y][vp]=0;
         blue[x][y][vp]=0;
      }

}

getpixel(int vp, int x, int y, char *r, char *g, char *b)
{
   if ((x>0)&&(x<sx)&&(y>0)&&(y<sy))
   {
      *r=red[x][y][vp];
      *g=green[x][y][vp];
      *b=blue[x][y][vp];
   }
   else
   {
      *r=0; //returns black on a clip
      *g=0;
      *b=0;
   }
}

putpixel(int vp, int x, int y)
{
   //wrap the putpixel to reduce segmentation faults
   //"clipping"
   
   if ((x>=0)&&(x<sx)&&(y>=0)&&(y<sy))
   {
      //uses current color
      red[x][y][vp]=cur_red[vp];
      green[x][y][vp]=cur_green[vp];
      blue[x][y][vp]=cur_blue[vp];
   }
}

swap(int *a, int *b)
{
   int tmp; tmp=*a; *a=*b; *b=tmp;
}

bar(int vp, int x1, int y1, int x2, int y2)
{
   int a,b;
   //draws a rectangle in the current color

   if (y2<y1) swap(&y1,&y2);
   if (x2<x1) swap(&x1,&x2);

   for (a=x1;a<=x2;a++)
      for (b=y1;b<=y2;b++)
         putpixel(vp,a,b);
}

bar2(int vp, int x, int y, int w, int h)
{
   //w and h have to be positive
   //draws rectangle in the current color for the viewport
   int a,b;

   for (a=x;a<=(x+w-1);a++)
      for (b=y;b<=(y+h-1);b++)
         putpixel(vp,a,b);
}

line(int vp, int x1, int y1, int x2, int y2)
{
   double m;
   double c=0.0;
   int a;

   if (x2<x1)
   {
      swap(&x1,&x2);
      swap(&y1,&y2);
   }

   //intercept the divide by zero error
   if ((x2-x1)==0)
      m=99999;
   else
      m=((double)(y2-y1))/((double)(x2-x1));

   if (m<1)
   {
      for (a=x1;a<x2;a++)
      {
         c+=m;
         bar2(vp,a,y1+c,2,2);
      }
   }
   else
   {
      if (y2<y1)
      {
         swap(&x1,&x2);
         swap(&y1,&y2);
      }

      for (a=y1;a<y2;a++)
      {
         c+=1/m;
         bar2(vp,x1+c,a,2,2);
      }

   }
}

circle(int vp, int x, int y, int r)
{
   double a;

   //draws a circle in the current color
   for (a=0;a<(2*3.14159);a+=0.001)
      bar2(vp,(int)(cos(a)*r)+x,(int)(sin(a)*r)+y,1,1);
}

textout(int vp, int x,int y, char s[], int fs)
{
   int cx,cy;
   int c,d;
   char o;

   char font[41][8]={

   {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, //space
   {0xF8,0xF8,0xF8,0xF8,0xF8,0xF8,0x00,0x00}, //white block - Use for unrecognized chars

   {0x70,0x88,0x88,0xF8,0x88,0x88,0x00,0x00}, //A
   {0xF0,0x88,0xF0,0x88,0x88,0xF0,0x00,0x00}, //B
   {0x70,0x88,0x80,0x80,0x88,0x70,0x00,0x00}, //C
   {0xE0,0x90,0x88,0x88,0x88,0xF0,0x00,0x00}, //D
   {0xF8,0x80,0xE0,0x80,0x80,0xF8,0x00,0x00}, //E

   {0xF8,0x80,0xF0,0x80,0x80,0x80,0x00,0x00}, //F
   {0x78,0x80,0x80,0x98,0x88,0x70,0x00,0x00}, //G
   {0x88,0x88,0x88,0xF8,0x88,0x88,0x00,0x00}, //H
   {0xF8,0x20,0x20,0x20,0x20,0xF8,0x00,0x00}, //I
   {0xF8,0x10,0x10,0x10,0x90,0x60,0x00,0x00}, //J

   {0x90,0x0A,0xC0,0xE0,0x90,0x88,0x00,0x00}, //K
   {0x80,0x80,0x80,0x80,0x80,0xF8,0x00,0x00}, //L
   {0x88,0xD8,0xA8,0x88,0x88,0x88,0x00,0x00}, //M
   {0x88,0xC8,0xC8,0xA8,0x98,0x98,0x00,0x00}, //N
   {0x70,0x88,0x88,0x88,0x88,0x70,0x00,0x00}, //O

   {0xF0,0x88,0x88,0xF0,0x80,0x80,0x00,0x00}, //P
   {0x70,0x88,0x88,0xA8,0x98,0x78,0x00,0x00}, //Q
   {0xF0,0x88,0x88,0xF0,0x90,0x88,0x00,0x00}, //R
   {0x70,0x88,0xE0,0x18,0x88,0x70,0x00,0x00}, //S
   {0xF8,0x20,0x20,0x20,0x20,0x20,0x00,0x00}, //T
   
   {0x88,0x88,0x88,0x88,0x88,0x70,0x00,0x00}, //U
   {0x88,0x88,0x88,0x88,0x50,0x20,0x00,0x00}, //V
   {0x88,0x88,0xA8,0xA8,0xD8,0x88,0x00,0x00}, //W
   {0x88,0x40,0x20,0x20,0x50,0x80,0x00,0x00}, //X
   {0x88,0x88,0x88,0x50,0x20,0x20,0x00,0x00}, //Y

   {0xF8,0x10,0x20,0x20,0x40,0xF8,0x00,0x00}, //Z


   {0x20,0x60,0xE0,0x20,0x20,0xF8,0x00,0x00}, //1
   {0x70,0x88,0x08,0x30,0x40,0xF8,0x00,0x00}, //2
   {0x70,0x88,0x38,0x38,0x88,0x70,0x00,0x00}, //3
   {0x30,0x50,0x90,0xF8,0x10,0x10,0x00,0x00}, //4
   {0xF8,0x80,0xF0,0x08,0x88,0x70,0x00,0x00}, //5

   {0x38,0x40,0xF0,0x88,0x88,0x70,0x00,0x00}, //6
   {0xF8,0x08,0x10,0x20,0x20,0x20,0x00,0x00}, //7
   {0x70,0x88,0x70,0x88,0x88,0x70,0x00,0x00}, //8
   {0x70,0x88,0x78,0x18,0x20,0x40,0x00,0x00}, //9
   {0x70,0x88,0x98,0xA8,0xC8,0x70,0x00,0x00}, //0

   {0x00,0x20,0x20,0xF8,0x20,0x20,0x00,0x00}, //+
   {0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00}, //-
   {0x00,0x00,0x00,0x00,0x30,0x30,0x00,0x00}  //.

   };
 
   cx=x; cy=y;

   for (c=0;c<strlen((char *)s);c++)
   {

      //map characters to array ordinal (doesn't use ASCII)
      switch(toupper(s[c]))
      {
         case ' ': o=0; break;
         case 'A': o=2; break;
         case 'B': o=3; break;
         case 'C': o=4; break;
         case 'D': o=5; break;
         case 'E': o=6; break;
         case 'F': o=7; break;
         case 'G': o=8; break;
         case 'H': o=9; break;
         case 'I': o=10; break;
         case 'J': o=11; break;
         case 'K': o=12; break;
         case 'L': o=13; break;
         case 'M': o=14; break;
         case 'N': o=15; break;
         case 'O': o=16; break;
         case 'P': o=17; break;
         case 'Q': o=18; break;
         case 'R': o=19; break;
         case 'S': o=20; break;
         case 'T': o=21; break;
         case 'U': o=22; break;
         case 'V': o=23; break;
         case 'W': o=24; break;
         case 'X': o=25; break;
         case 'Y': o=26; break;
         case 'Z': o=27; break;

         case '1': o=28; break;
         case '2': o=29; break;
         case '3': o=30; break;
         case '4': o=31; break;
         case '5': o=32; break;
         case '6': o=33; break;
         case '7': o=34; break;
         case '8': o=35; break;
         case '9': o=36; break;
         case '0': o=37; break;

         case '+': o=38; break;
         case '-': o=39; break;
         case '.': o=40; break;

         default: o=1; break;
      }

      for (d=0;d<8;d++)
      {
         //write text in current color
         if (font[o][d]&0x80) bar2(vp,cx,cy+d*fs,fs,fs);
         if (font[o][d]&0x40) bar2(vp,cx+1*fs,cy+d*fs,fs,fs);
         if (font[o][d]&0x20) bar2(vp,cx+2*fs,cy+d*fs,fs,fs);
         if (font[o][d]&0x10) bar2(vp,cx+3*fs,cy+d*fs,fs,fs);
         if (font[o][d]&0x08) bar2(vp,cx+4*fs,cy+d*fs,fs,fs);
         if (font[o][d]&0x04) bar2(vp,cx+5*fs,cy+d*fs,fs,fs);
         if (font[o][d]&0x02) bar2(vp,cx+6*fs,cy+d*fs,fs,fs);
         if (font[o][d]&0x01) bar2(vp,cx+7*fs,cy+d*fs,fs,fs);
      }

      cx+=8*fs;

   }

}

writebmp(char *fname, int vp)
{
   
   FILE *fptr;
   long fs,is,a,b; //filesize, image size, counters
   int yr;
   char s[255];
   char n,o,p; //color save values

   //save the current color and restore at the end of the function
   //usually this is the last call to a viewport, but sometimes not.
   n=cur_red[vp]; o=cur_green[vp]; p=cur_blue[vp];


   //minimalist graphics support for CLI's
   char bmp_hdr[54]={   0x42,0x4D,0x46,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x36,0x00, 0x00,0x00,
            0x28,0x00,0x00,0x00, 0x02,0x00,0x00,0x00, 0x02,0x00,0x00,0x00, 0x01,0x00,
            0x18,0x00,0x00,0x00, 0x00,0x00,0x10,0x00, 0x00,0x00,0x13,0x00, 0x00,0x00,
            0x13,0x0B,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00};

   fs=54+(sx*sy*3); //filesize is the dimentions of the array multiplied by 3 bytes (R,G and B bytes)
   is=(sx*sy*3);

   //put RED registration square in top-left hand corner at (20,20), height =20, width=20
   //for (a=0;a<20;a++)
   //   for (b=0;b<20;b++)
   //      red[a+20][b+20][vp]=255;

   setcolor(vp,255,255,255);
   //textout(vp,20,20,"Reg",1);

   //Stanford Systems copyright notice
          yr=2016;
   //sprintf(s,"%d (c) Copyright Stanford Systems",yr);
   //textout(vp,sx/2-(8*strlen(s))/2,sy-10,s,1);

   memcpy((void *)&bmp_hdr[2],(void *)&fs,4);
   memcpy((void *)&bmp_hdr[18],(void *)&sx,4);
   memcpy((void *)&bmp_hdr[22],(void *)&sy,4);
   memcpy((void *)&bmp_hdr[34],(void *)&is,4);

   if ((fptr=fopen(fname,"wb"))!=NULL)
   {
      fwrite(bmp_hdr,54,1,fptr);

      for (b=sy;b>0;b--)
         for (a=0;a<sx;a++)
         {
            fwrite(&blue[a][b][vp],1,1,fptr);
            fwrite(&green[a][b][vp],1,1,fptr);
            fwrite(&red[a][b][vp],1,1,fptr);
         }
   }
   else
   {
      printf("failed to open BMP file.\n");
      return -1; //error code indicating file could not be opened
   }

   fclose(fptr);

   //restore any viewport vars we changed here
   cur_red[vp]=n; cur_green[vp]=o; cur_blue[vp]=o;

   return 1;
}



Its a super basic form of graphics support with basically one one function:

writebmp(filename,viewport#);

its based on ten viewports that you can use to write graphics to. Its interface is procedural and statically-defined, so to change the color of a pixel, you just change the number of an array element to the intensity of that color. Don't forget to specify which viewport though, like this:

red[100][100][0]=255;

...will put a red pixel in viewport 0. You just keep writing to the array (i.e. buffer) and when you are finished your image call the writebmp() function.
Last edited by woodystanford on Sat Feb 25, 2017 5:19 am, edited 1 time in total.
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by woodystanford » Sat Feb 25, 2017 4:36 am
OK, here is a complete list of the available functions that you can call in graph.c

To use, remember you have to call initgraph3() first at the top of your main function.

initgraph3() : initializes your graphics subsystem

setcolor(int vp, int r, int g, int b): sets the current color for viewport vp

clrscr(int vp): clears the given viewport

getpixel(int vp, int x, int y, char *r, char *g, char *b): returns the color of a pixel

putpixel(int vp, int x, int y): sets the color of the speciied pixel to current color

bar(int vp, int x1, int y1, int x2, int y2): standard bar function

bar2(int vp, int x, int y, int w, int h): standard bar function but with width, height

line(int vp, int x1, int y1, int x2, int y2): standard line function

circle(int vp, int x, int y, int r): standard circle function (x,y) center, r= radius in pixels.

textout(int vp, int x,int y, char s[], int fs): niffty little text out function, fs doubles,triples, etc the fontsize. Puts string s at location (x,y)

writebmp(char *fname, int vp): outputs the specified viewport as a BMP file
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by woodystanford » Sat Feb 25, 2017 5:17 am
This how easy it is to use. An example program:

(Also note that graph.c needs stdio.h, string.h, stdlib.h and math.h . These are standard C headers. Just make sure that you include these in whatever C program you are writting.)

Code: Select all
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "graph.c"

main()
{

   initgraph3(); //initialize the graphics subsystem

   clrscr(0); //clears vp0 to black
   setcolor(0,255,255,255); //sets current color to white

   circle(0,100,100,50); //draw a circle

   line(0,50,100,150,100); //put a cross in it
   line(0,100,50,100,150);

   textout(0,50,200,"Circle with Cross",2); //write a label under it

   writebmp("ccross.bmp",0); //write vp0 to file ccross.bmp

}


This program outputs this:

Image
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by woodystanford » Sat Feb 25, 2017 9:53 pm
OK, got a treat for you guys. Now you have a way to get an image out to drive in all situations (all C situations), how about if you need a graph. Here is a C source file that you can just include thus:

#include "graph_graph.c"

(put it under the include for the graph.c because it requires graph.c. Standard headers are identical I thikn)

and you can output a graph like this:

Image

And here is the code for it. Just cut and paste it into a text file "graph_graph.c" and include it at the top of your program.

Code: Select all
struct graph_type
{
   double minx; //minimum graphed x values
   double maxx; //maximum graphed x values
   double miny; //minimum graphed y values
   double maxy; //maximum graphed y values

   double ticx; //ticks every...
   double ticy; //ticks every...
   double mticx; //minor ticks every...
   double mticy; //minor ticks every...

   //viewport specific variables
   double pcleft; //percent of viewport offset from left
   double pctop; //percent of viewport offset from top
   double pcright; //percent of viewport offset from right
   double pcbottom; //percent of viewport offset from bottom

   int top; //read only - calculated from pc####
   int left; //read only
   int width; //read only
   int height; //read only

   int drawgrid; //0 = don't draw grid, 1 = draw major only, 2= draw major and minor

   char xformat[10]; //specifies presition format of label numbers
   char yformat[10];

   char title[255]; //graph title (centered on bottom)
} graph[10]; //supports up to 10 simultaneous graphs (0-9)

textout2(int vp, int x, int y, char *s, int fs, int align)
{

   double x2;

   //textout2 provides alignment to textout
   //align = 0: left justified, =1 centered, =2 right justified
   switch(align)
   {
      case 0: //left-justified...normal
      textout(vp,x,y,s,fs);
      break;

      case 1: //centered at x,y
      //figure out the x coord so it centers around x
      //dependant on the width of each letter
      x2=x-(strlen(s)*8*fs)/2;
      textout(vp,x2,y,s,fs);
      break;

      case 2: //right justified
      //figure out the x coord so it ends at x
      //dependant on the width of each letter
      x2=x-(strlen(s)*8*fs);
      textout(vp,x2,y,s,fs);
      break;
   }

}
   
plotgraph(int vp, int gn)
{
   //remember to set the current color BEFORE calling this routine
   //so you can programatically set the graph's color

   double x,y,dx,dy;
   char s[255];
   double left, top, height, width;
   double minx, miny, maxx, maxy;
   double ticx, ticy,mticx, mticy;
   double pr,pg,pb; //save current color values

   //calulcate read-only vars
   graph[gn].left=sx*graph[gn].pcleft;
   graph[gn].top=sy*graph[gn].pctop;
   graph[gn].width=sx-graph[gn].left-(sx*graph[gn].pcright);
   graph[gn].height=sy-graph[gn].top-(sy*graph[gn].pcbottom);

   //break out vars so programming is easier to understand
   top=graph[gn].top;
   left=graph[gn].left;
   height=graph[gn].height;
   width=graph[gn].width;
   minx=graph[gn].minx;
   miny=graph[gn].miny;
   maxx=graph[gn].maxx;
   maxy=graph[gn].maxy;
   ticx=graph[gn].ticx;
   ticy=graph[gn].ticy;
   mticx=graph[gn].mticx;
   mticy=graph[gn].mticy;

   //draw graph
   bar2(vp,left,top,1,height); //y axis
   bar2(vp,left,top+height,width,1); //x axis
   
   for (x=minx;x<=maxx;x+=mticx)
   {
      dx=width/(maxx-minx);
      bar2(vp,x*dx+left-(minx*dx),top+height,1,3); //x minor tick
   }

   for (x=minx;x<=maxx;x+=ticx)
   {
      dx=width/(maxx-minx);
      bar2(vp,x*dx+left-(minx*dx),top+height,1,5); //x major tick
      sprintf(s,graph[gn].xformat,x);
      textout2(vp,x*dx+left-(minx*dx),top+height+10,s,1,1); //centered
   }

   for (y=miny;y<=maxy;y+=mticy)
   {
      dy=height/(maxy-miny);
      bar2(vp,left-5,(top+height)-(y*dy)+(miny*dy),5,1); //y minor tick
   }

   for (y=miny;y<=maxy;y+=ticy)
   {
      dy=height/(maxy-miny);
      bar2(vp,left-5,(top+height)-(y*dy)+(miny*dy),5,1); //y major tick
      sprintf(s,graph[gn].yformat,y);
      textout2(vp,left-5,(top+height)-(y*dy)+(miny*dy)-4,s,1,2); //right align
   }

   if ((graph[gn].drawgrid==1)||(graph[gn].drawgrid==2))
   {
      pr=cur_red[vp];
      pg=cur_green[vp];
      pb=cur_blue[vp];
   
      //make grid at half intensity
      setcolor(vp,pr/2,pg/2,pb/2);

      for (x=minx;x<=maxx;x+=ticx)
      {
         dx=width/(maxx-minx);
         bar2(vp,x*dx+left-(minx*dx),top,1,height); //x major tick
      }
      
      for (y=miny;y<=maxy;y+=ticy)
      {
         dy=height/(maxy-miny);
         bar2(vp,left,(top+height)-(y*dy)+(miny*dy),width,1); //y major tick
      }

      //restore color
      setcolor(vp,pr/2,pg/2,pb/2);
   }
      
   //output title
   textout2(vp,sx/2,sy*0.95,graph[gn].title,1,1); //centered
   
}

int getgx(int gn, double x)
{
   double left, width;
   double minx, maxx;

   left=graph[gn].left;
   width=graph[gn].width;
   minx=graph[gn].minx;
   maxx=graph[gn].maxx;

   //break out the vars so easier to understand
   return (int)(width/(maxx-minx)*(x-minx)+left);
}

int getgy(int gn, double y)
{
   double top, height;
   double miny, maxy;

   top=graph[gn].top;
   height=graph[gn].height;
   miny=graph[gn].miny;
   maxy=graph[gn].maxy;

   return (int)((top+height)-(height/(maxy-miny)*(y-miny)));      
}


Its not super-powerful but it will crank out a standard graph to file.
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by PeterO » Sat Feb 25, 2017 10:06 pm
woodystanford wrote:Do you need a way to just output an image in C without messing with librarys, external dependancies or even a *.h file? It will work with ANY version or flavor of C as it is written to be 100% ANSI C. Its only draw back is that it isn't real-time.

Here is some code you put into a file called "graph.c" and you include it in your c program like this:

#include "graph.c"
Have you read and understood this ? --> http://stackoverflow.com/questions/2326 ... in-another

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),Aeromodelling,1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson
User avatar
Posts: 3386
Joined: Sun Jul 22, 2012 4:14 pm
by woodystanford » Sat Feb 25, 2017 10:12 pm
Here are the functions for graph_graph

All of the parameters are set in the graph structure. It supports up to 10 simultaneous graphs. IMPORTANT: A graph is not the actual image of the graph but rather a list of parameters that describes the graph.

To RENDER the graph, you call plotgraph and put in your graph.c viewport number and THEN the graph_graph.c graph number.

struct graph_type graph[10]; //set the members in this structure to the graph you want to generate

textout2(int vp, int x, int y, char *s, int fs, int align): an internal function, but CAN be called if you need a textout function with more features than in graph.c. VP is graph.c viewport number and the string s is rendered at (x,y). fs is font size where 1 is normal, 2 is double-size, 3 is triple-size, etc. etc. and align is 0 for left-justified, 1 for centered, 2 for right_justified.

plotgraph(int vp, int gn): to render the graph to a viewport, after rendering save it with a writebmp() call

these are interesting functions in that they are "exposed" for a reason: so YOU can write to the graph
if you want/need to. They just take a value (x and y) and return where on the graph where that value is. Of course each graph has its own scale so you also have to specify which graph you are referring to. POWERFUL once you grasp the concept. For scientific applications your first usage of this would probably be to render your own error bars. Remember if you need a legend, you can just write in in graph.c code directly to the viewport manually.

int getgx(int gn, double x): returns the graph x coord for a value x
int getgy(int gn, double y): returns the graph y coord for a value y
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by woodystanford » Sat Feb 25, 2017 10:28 pm
PeterO wrote:
woodystanford wrote:Do you need a way to just output an image in C without messing with librarys, external dependancies or even a *.h file? It will work with ANY version or flavor of C as it is written to be 100% ANSI C. Its only draw back is that it isn't real-time.

Here is some code you put into a file called "graph.c" and you include it in your c program like this:

#include "graph.c"
Have you read and understood this ? --> http://stackoverflow.com/questions/2326 ... in-another

PeterO


Yes, actually a great thread, and I agree with most of what's in it. I've been a Software Engineer for a while now, working on UN*X systems since banging out on a VAX at ASU twenty years ago.

Let me justify my position on this holistically. Graph.c is for beginner to intermediate programmers who may or may not be comfortable with the procedure to build lib files and setting up their linker. For something like this, I think treating it as a related C source file (which is accepted and common practice in C application development) was the least stressful way for people to try it out AND to call it in any programs they wanted to incorporate it into.

I would suggest though that if you do use this a lot that you compile it to a lib file and formally link it into you program. The only cost of doing it this way is an additional 1 second during compilation which is zero cost for most of the situations we are in today (ie. serious hobbyist/enthusiast use on a microcomputer/workstation at home). What costs is the man hours of digging into your IDE or make file to try to do it the formal way.

My logic here.
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by PeterO » Sun Feb 26, 2017 8:45 am
My problem is that I find that once you show newbies "a simpler" way to do something they will keep doing it that way forever, especially if you tell them the proper way to do it is too hard for them. :roll:
It's much better to put a little of your time and effort up front into showing and explaining the proper way to them from the start.

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),Aeromodelling,1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson
User avatar
Posts: 3386
Joined: Sun Jul 22, 2012 4:14 pm
by woodystanford » Sun Feb 26, 2017 10:30 am
PeterO wrote:My problem is that I find that once you show newbies "a simpler" way to do something they will keep doing it that way forever, especially if you tell them the proper way to do it is too hard for them. :roll:
It's much better to put a little of your time and effort up front into showing and explaining the proper way to them from the start.

PeterO


Peter, I'm having difficulty with this insinuation that this represents something improper in its technical approach. The inclusion of C source via an include is an accepted practice and has been for decades.

What is it that you are objecting to? Are you saying that it would be more proper to do the include via a .h suffix (ie. as a header file)? Or maybe that the formalized approach via precompilation to a lib file and explicitly linking it is the only way to do things?
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by jamesh » Sun Feb 26, 2017 10:04 pm
woodystanford wrote:
PeterO wrote:My problem is that I find that once you show newbies "a simpler" way to do something they will keep doing it that way forever, especially if you tell them the proper way to do it is too hard for them. :roll:
It's much better to put a little of your time and effort up front into showing and explaining the proper way to them from the start.

PeterO


Peter, I'm having difficulty with this insinuation that this represents something improper in its technical approach. The inclusion of C source via an include is an accepted practice and has been for decades.

What is it that you are objecting to? Are you saying that it would be more proper to do the include via a .h suffix (ie. as a header file)? Or maybe that the formalized approach via precompilation to a lib file and explicitly linking it is the only way to do things?


Whilst it's an accepted approach, it's not necessarily the best approach in most circumstances, I think that is all Peter is saying. As a C programmer of some years (25ish), I've included C files about 5 times. There are usually better/more appropriate ways, and you will avoid future issues like multiple definitions of functions and the like.
Principal Software Engineer at Raspberry Pi (Trading) Ltd.
Please direct all questions to the forum, I do not do support via PM.
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 17202
Joined: Sat Jul 30, 2011 7:41 pm
by woodystanford » Mon Feb 27, 2017 12:53 am
jamesh wrote: Whilst it's an accepted approach, it's not necessarily the best approach in most circumstances, I think that is all Peter is saying. As a C programmer of some years (25ish), I've included C files about 5 times. There are usually better/more appropriate ways, and you will avoid future issues like multiple definitions of functions and the like.


See my issue here is what is the issue? We are arguing over nothing here, gentlemen.

My only point was that maybe people need, in the beginning a bit of handholding, that they might be put off a little by having to learn how to use a linker. Why not give them a nice little gadget, thingy that does X, and make it easy. I mean C is NOT the most user-friendly language.

Why NOT make it easier...at least at first? :D
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by LdB » Thu Mar 02, 2017 6:28 pm
You seem committed to your project Woody so I guess I might as well help you :-)

Your bitmap routine will only work with particular width 24 bit bitmaps, and even for many 24 bit BMP's your image will sort of skew sideways ... you must have noticed it.

The number of bytes per line must be divisible by 4 .. so Image width * 3 bytes (you are in 24 bit mode).
So a bitmap with a width of 30 pixels .. you would think should be 90 bytes per line .. it won't it will be 92 bytes the last 2 bytes are just padding to ignore. The same holds for when you write the BMP. If you actually try a bitmap with a width of 30 you will see the problem as both your read and write will fail.

If you want to do the job properly here are the two structs you need ... I assume you know the C standard <stdint.h> include it if you haven't already.
Code: Select all
 struct __attribute__((packed)) BmpFileHeader {
   uint16_t      bfType;            // Should be "BM"
   uint32_t      bfSize;            // Size of file    ... careful unaligned
   uint16_t      bfReserved1;      // Should be 0
   uint16_t      bfReserved2;      // Should be 0
   uint32_t      bfOffBits;         // Offset to Bmp image ... careful unaligned
};

struct __attribute__((packed)) BmpInfoHeader {
   uint32_t      biSize;            // Bitmap size
   uint32_t      biWidth;         // Bitmap width
   uint32_t      biHeight;         // Bitmap height
   uint16_t      biPlanes;         // Bitmap color planes
   uint16_t      biBitCount;         // Bits per pixel
   uint32_t      biCompression;      // Compression techology
   uint32_t      biSizeImage;      // Image bytes size
   uint32_t      biXPelsAMeter;      // Pixels per X meter
   uint32_t      biYPelsAMeter;      // Pixels per Y meter
   uint32_t      biClrUsed;         // Colour index map
   uint32_t      biClrImportant;      // Min colours needed
};

Just read the structs one after another from the file and you know everything about the file. Your files will report the biBitCount always as 24 or (24/8) = 3 bytes and the bytes per line is (biWidth * biBitCount/8) round up to nearest div 4. The easiest way to do that is take the modulo 4 of the result and if the result isn't 0 add (4-Modulo) to your answer. In C code it looks like this I will call the result you are after xferWidth so you will start it as bitmap width * 3 and then this will correct that value to div 4
Code: Select all
int p2Mod = xferWidth % 4;
if (p2Mod != 0) xferWidth += (4-p2Mod);
Posts: 295
Joined: Wed Dec 07, 2016 2:29 pm
by woodystanford » Mon Mar 20, 2017 1:24 pm
OK, got the COOLEST thing for you guys and gals. How does Real-Time 2D graphics support for UN*X sound? Its definitely fun to play with but very jerky.

The client runs on your Windows 10 PC. Its actually pretty high-performance internally but how it works you will laugh that it even works as well as it does. It just writes over and over again to the same file on the filesystem and then that file is read by the client and rendered verbatim.

It has some major limitations but it will work (but its really jerky...its funny as hell but SO much fun to play with). Here are the limitations:

(1) it works over the filesystem so unless you know how to synch a Windows 10 volume on your R-Pi, you can only use it with MinGW for now (example code will work with VC++ as well). Well then why are you telling us this if it only works on MinGW? Because its UN*X, my friend, and if it works on MinGW there IS a way to get it working from your R-Pi, right?

(2) its jerky as hell. Its more "updateable" than "real-time" but its set up the way people want a 2D graphics display to work in UN*X. You just download the Windows client for it and run it.

(3) It won't support games because its so jerky (however it might be fast enough to support a slow-moving traditional RPG...maybe). Its more for slow-moving applications that don't mind a little lag on refresh (scientific, academic, training situations). A plus: it doesn't have any flicker.

I guess I just wanted to show people the kind of 2D graphics support for UN*X systems a lot of people are looking for: just a viewport that they can access from C or python or whatever.

OK, here is the example C code for writing animation frames to disk:

Code: Select all
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <unistd.h>

#include "graph.c"

main()
{
   //************************
   //Realtime graphics tester
   //Animates a circle and bar
   //going across the screen
   //************************

   int a;

   for (a=0;a<400;a++)
   {
      clrscr(0);

      setcolor(0,255,0,255);
      bar2(0,a+95,0,10,(int)(640.0/2.0));
      setcolor(0,255,255,255);
      circle(0,a+100,(int)(640.0/2.0),50);

      writebmp("c:\woody\test.bmp",0);

      printf(".");
      fflush(NULL);

      usleep(10);
      
   }
}


Cut and paste the above code into MinGW (it will work on R-Pi as well, it just won't connect to anything to display the animation). Cut and paste the code to a new text file called "realtime.c".

Compile with (on Windows with MinGW): (Also remember that you have to add the MinGW's BIN directory to the system path.)

gcc realtime.c -o realtime.exe -lm

and invoke with:

realtime

Just have the graph.c file in the same directory as the above source code. Another thing you might want to do is modify the source code in graph.c commenting out the "Cannot write file" printed error as it rarely collides with the client but often enough that it becomes annoying. A period is printed for every frame written just so you can physically see its progress

If you mess with it enough, you'll probably find applications that its a really good match for (like status screens, academic applications that don't require fast refresh, hobby type situations). It just fun to play with and hopefully someone somewhere will write a real one that works the very same way: Windows Installer for display on PC and C, python or whatever interfaces, easily access-able.

Download the Windows client (Windows 8/9/10) from: https://www.mediafire.com/?5b1gtvl99tc8rxr. Unzip it and install (its an untouched 2010 VB.NET installer/application so it should uninstall slick as ****). Type in the filespec of the BMP file that's outputted from the C code in the text box at the top of its window. This links it to the C code instantly.

Just modify the C code example to the animation or display or whatever you want to do.
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by woodystanford » Mon Mar 20, 2017 1:29 pm
Let me say this as well. I thought that adding and using a RAM Disk would make it less jerky. It doesn't; found out empirically that Windows 10's filesystem is RAM-buffered enough that it will work the same whether you write to hard drive or RAM drive. How did I verify this? I used ImDisk to mount a RAM drive ans the mediating filesystem and noticed no increase in performance. Just to save you the disappointment.
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by gordon@drogon.net » Mon Mar 20, 2017 2:14 pm
woodystanford wrote:OK, got the COOLEST thing for you guys and gals. How does Real-Time 2D graphics support for UN*X sound? Its definitely fun to play with but very jerky.


I can do that in BASIC on a Pi. Very smooth.

You want easy real-time 2D graphics under Unix/Linux/*x ? Use SDL. It's a cross platform library initially aimed at 2D games, but supports 3D stuff too. Then there's Qt ...

This isn't rocket science. Have you seen what 12 year olds are doing with mobile phone apps?

So your BMP stuff is fine, but there are many more options now.

Also - while your code works, it's somewhat sub-optimal. You really ought to be teaching the best to start with (if that's your aim). Things you may want to know about include the midpoint circle algorithm for example (doesn't need floating point), the Bressenham line drawing algorithms and you may want to lookup the Cohen-Sutherland line clipping algorithm.

You may also want to look at the GD graphics library which has been doing what you're doing for over 20 years now: https://libgd.github.io/pages/about.html It's very comprehensive.

And your code still doesn't compile without warnings - PLEASE start to use -Wall -Wextra on your own code. Didn't you read that from comments on one of your previous posts?

-Gordon
Ps. You know the Pi has a 16bpp framebuffer by default, right?
--
Gordons projects: https://projects.drogon.net/
User avatar
Posts: 1940
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
by dave j » Mon Mar 20, 2017 2:50 pm
gordon@drogon.net wrote:
woodystanford wrote:OK, got the COOLEST thing for you guys and gals. How does Real-Time 2D graphics support for UN*X sound? Its definitely fun to play with but very jerky.


I can do that in BASIC on a Pi. Very smooth.

You want easy real-time 2D graphics under Unix/Linux/*x ? Use SDL. It's a cross platform library initially aimed at 2D games, but supports 3D stuff too. Then there's Qt ...

The thing that this library really misses is that people learning graphics programming want to see the results appear on screen so they get immediate feedback if their program worked or not. A library that doesn't support that is not going to be interesting to new programmers.
Posts: 94
Joined: Mon Mar 05, 2012 2:19 pm
by woodystanford » Mon Mar 20, 2017 3:01 pm
OK, here is a screen video of what this 2D graphics thingy does (to show the casually interested what it does).

https://woodystanford.tinytake.com/sf/MTQzMzE5OF81MTE5MzM1

The top window is the Windows Client for it and the bottom window is the cmd cli for Windows. Realtime.exe was compiled (with gcc is MinGW) previous to the video and just invoked at the start of the video.

If I had to do it over again, I would have shown how to link it to the c program by typing the bmp filespec in the top textbox in the graphics client. Just remember to do this BEFORE hitting the start display button.
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by gordon@drogon.net » Mon Mar 20, 2017 3:08 pm
woodystanford wrote:OK, here is a screen video of what this 2D graphics thingy does (to show the casually interested what it does).

https://woodystanford.tinytake.com/sf/MTQzMzE5OF81MTE5MzM1

The top window is the Windows Client for it and the bottom window is the cmd cli for Windows. Realtime.exe was compiled (with gcc is MinGW) previous to the video and just invoked at the start of the video.

If I had to do it over again, I would have shown how to link it to the c program by typing the bmp filespec in the top textbox in the graphics client. Just remember to do this BEFORE hitting the start display button.


You've left me speechless at this point. I mean "!". Really, "!!".

-Gordon
--
Gordons projects: https://projects.drogon.net/
User avatar
Posts: 1940
Joined: Tue Feb 07, 2012 2:14 pm
Location: Devon, UK
by woodystanford » Mon Mar 20, 2017 6:21 pm
Thanks Gordon. I WILL get the wall and extra switches in my compile/link sequence btw. Also wanted to talk to the rest of you about what you mentioned but am on a mission...

OK, one of the things I do is a lot of physics (cosmology and quantum mechanics) and one of the things that my fellows drool over all the time is how I've managed to duplicate a lot of classic supercomputer stuff on my workstations here (approximations, of course). So to illustrate how this Graphics Client could be used in academic and scientific situations is with the following screen video.

https://woodystanford.tinytake.com/sf/MTQzMzc5NV81MTIwNTYz

Its a rotating galactic model baser on the HYG database which in turn is based on the Hyparchos Star Catalog. The HYG data set is a supercomputer crunched Hyparchos (polar -> X,Y,X ) that I managed to obtain. The lower screen contains the C code that has the galaxy simulator coupled with the C code above. I just link it to the BMP filename in the Graphics Client an voila....

In some situations it seems that this GC is workable after all. I thought it rendered quite well.
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by PeterO » Mon Mar 20, 2017 7:05 pm
woodystanford wrote:Thanks Gordon..


I think you've had a sense of irony failure !

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),Aeromodelling,1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson
User avatar
Posts: 3386
Joined: Sun Jul 22, 2012 4:14 pm
by woodystanford » Mon Mar 20, 2017 8:34 pm
OK, I'm not understanding, gentlemen, what is your concern(s)? Please be literal and explicit and I will rectify those issues I can. I'm starting to sense that I'm not getting what you are trying to say.

Am I aware of GD....of course, in fact it is my graphics library of choice. My point here is that I was merely pointing out that for someone who just needs to output an image, that this piece of code will do that. My experiences with GD have been of fiddling around for days if not weeks trying to get it to play nice with all my other stuff. If all I need is an image out then why not just call:

writebmp("myimage.bmp",0);

Secondly I will try in the future to include the wall and extra switches in my compilation code though I am at a lost why I'm expected to do this as I am not a moderator or officer of your company but a customer. If you have difficulties with me just bull***** in own thread then let me know that I'm cluttering up your board with threads and I will desist. I mean it is your business. I just thought that my thread would have gotten lost in the thousands of other threads here.

Are there any other concerns you wish to state? I will rectify them if I can.

Please be literal (had a hard time figuring out that you were basically ordering/requesting strongly me to use certain switches with my code on my Pi board..I'm still not sure how I feel about that level of...comradery around here). As I have told you, I will use those switches as soon as I figure them out.
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by Paeryn » Mon Mar 20, 2017 9:54 pm
woodystanford wrote:Please be literal (had a hard time figuring out that you were basically ordering/requesting strongly me to use certain switches with my code on my Pi board..I'm still not sure how I feel about that level of...comradery around here). As I have told you, I will use those switches as soon as I figure them out.

The -Wall -Wextra switches are being suggested as your code breaks the ANSI/ISO C standards and those switches tell you where the code is wrong. Specifically the implicit int functions (C99 actually states that this is not allowed anymore) and using return with no value or even no return statement at all in functions which haven't been declared as returning void, this is wrong as you end up returning random data from those functions *. You seem to declare functions with no return type and use them as is if they returned void, they don't, not declaring the return type used to imply that you were returning int, gcc (and others) still allow this but it really should not be done, you should always explicitly declare the return type. We're trying to help you improve your code.

* The exception to this rule is that main() is allowed to reach the end without a return expr; in which case it implicitly returns 0, any other function will return junk.

gordon@drogon.net wrote:Ps. You know the Pi has a 16bpp framebuffer by default, right?

The framebuffer used to default to 16bpp but it's been 32bpp for a while now unless you're running the cutdown firmware (when gpu_mem=16).

Quick search as to when this happened... Taken from the firmware git log on 28th Jul 2016 https://github.com/raspberrypi/firmware/commit/1205048f037098b1532bb947d1f4b823bcdebdf7
kernel: bcm2708_fb: Default framebuffer to 32-bit

firmware: platform: Default to 16bit framebuffer with cutdown firmware
She who travels light — forgot something.
User avatar
Posts: 1444
Joined: Wed Nov 23, 2011 1:10 am
Location: Sheffield, England
by woodystanford » Mon Mar 20, 2017 10:46 pm
I just now checked the number of hits that this thread is getting. So it seems that my intuition regarding the lingering over-all UN*X community issue of having basic 2D support is correct (to explain the interest). Yes, it seems that my current approach comes out with something passable in some situations, and entertaining to the hobbyist all around. Am aware of the need for this, as that other gentleman asking about INKEY$'s was pointing out how it seems to be the simple matters that sometimes throw us.

How GC, or the "Graphics Client", works is with a very basic orperation but it was only through trying several other non-working methodologies (frustrating) that I managed to come out with that one. For those that are wondering what I'm trying to do let me inform you of this (what the other person in the thread I beleive was conveying) is that there are some solid, full-featured libraries out there that will generate image files (maybe even video streams) but not a basic system that allows 2D realtime graphics.

I agree 100% with what Dave is saying here:
The thing that this library really misses is that people learning graphics programming want to see the results appear on screen so they get immediate feedback if their program worked or not. A library that doesn't support that is not going to be interesting to new programmers


I tried sockets at first but since I was using a RAD, all of my rendering code was in BASIC (VB.NET) and way too slow. Considered XWindows, and then dropped that for the usual reasons and looked at a few other solutions, including TclTK. How GC works is like a TV (seriously). It connects so intuitively and easily it can be done with GD, or python (assuming you can generate graphics files easily with it) even other filetypes than BMP. You just overwrite the one graphics file and the Client just reads and renders that file over and over again like a scanning TV screen. It sounds inefficient (about 900kb per frame) however it does work passibly well.

My original intent in introducing it to this community was to provide a kind of...working spec...of what several of us have been asking the over all community to provide us: not even GUI support just a viewport that is easily accessable from all of the major languages and immune to OS differences. This solution is by no means perfect but the idea of it might be latched onto with more time and resources that we could have a GC that works (whether by socket, stream, shared memory or filesystem connectivity).

OK here is the link again to the Graphics Client: https://www.mediafire.com/?5b1gtvl99tc8rxr . Its actually very basic under the hood (and uninstalls cleanly) in that I use the hidden horsepower of the picturebox control in it to do the lifting. All it does is just scan the entered filespec at about 10 hz (like ten times per second) and renders whatever graphic is there (it can also handle all of the major filetypes not just BMP btw...you might notice a performance increase by using GIF or other compression as the bottleneck here is the usage of the filesystem) over and over again.

Before I'm critisized for the brute force nature of this solution, its the best one out of about a dozen approaches I either evaluated or directly worked on. Until someone can come up with a better idea, this actually does (however poorly) what a lot of us have been begging for for years.

Understand that it is for Windows. You generate the sequence of graphics files on the Pi, but to use the Client, it needs to run on your Windows PC. Then how do you link the filesystems between the two; that's a systems administration task, how to mount a windows directory on a Pi volume (or visa versa). FTP would be too slow. It would have to be mounted.

Now I have to be careful here because I've been talking a lot about PIC MCU's (rather than Arduinos) so I have to watch how I phrase this (so it doesn't look like I'm disrespecting dedicated Pi displays), but you can mount a Pi under a $50 Windows 10 tablet running GC and use it as its graphics interface.

Another thought I had is that I really want to code is 2 events detected by the GC that are transmitted back through the filesystem back to the invoking program and they are:

PictureBox1_Click
frmGraphics_KeyDown (allows INKEY$ type operations)

For example if a click is detected it puts a file "c:\woody\realtime\test0000011.ndc" with contents "1,0.7656,0.8777" where 1 signifies that it is a mouse click (or tablet tap) and the two others are normalized (ie. percentages) X and Y values of the click. If a key is detected as being pressed it pus into a similar filename "2,69" where 2 signifies that it is a key press and the second the ascii value of the key pressed. With these two basic events you can creat a pseudo-GUI upon which you could render alarm clocks and other simple devices on it that respond to the user's input (keyboard and mouse). So its a dedicated 2D graphic client BUT with a simple yet powerful 2-even signaling input back to the controlling program.

The benefit is obvious: you can just program whatever you want. It might take a lot of time but the process is more linear than having to be an expert in hardware, the required libraries and systems administration. You just program it like a TI-994A or a C64.
User avatar
Posts: 122
Joined: Sun Feb 19, 2017 8:17 am
by PeterO » Mon Mar 20, 2017 10:53 pm
woodystanford wrote:Please be literal (had a hard time figuring out that you were basically ordering/requesting strongly me to use certain switches with my code on my Pi board..I'm still not sure how I feel about that level of...comradery around here). As I have told you, I will use those switches as soon as I figure them out.


Ok, Gordon was "speechless" because your example is so bad. The video shows it to be a very poor way to achieve any graphical output from a PI. You seem to be going out of your way to produce the worst examples you possibly can. Your code style is poor and the methods you are using are poor as well. There are **much** better ways to achieve graphical output.

PeterO
Discoverer of the PI2 XENON DEATH FLASH!
Interests: C,Python,PIC,Electronics,Ham Radio (G0DZB),Aeromodelling,1960s British Computers.
"The primary requirement (as we've always seen in your examples) is that the code is readable. " Dougie Lawson
User avatar
Posts: 3386
Joined: Sun Jul 22, 2012 4:14 pm
by ktb » Tue Mar 21, 2017 2:05 am
Image
Posts: 1379
Joined: Fri Dec 26, 2014 7:53 pm