CANVAS documenation #ident "@(#)VOGLE:driver/html5.c - HTML 5.0 canvas driver" #ident "@(#)VOGLE:author - John S. Urban" #ident "@(#)VOGLE:version - 1.0, Dec 2007" /* =============================================================================== Usage Notes: o assuming 10 units to a raster ; so a call for size 4000,4000 would create a 400x400 pixel display surface. Default is 425x550 pixels. o currently, only Apple, Mozilla/Firefox and Opera support a CANVAS -- this is a preliminary specification. o Use negative color values to specify line thickness in raster units in this version of vogle o making function that takes an array and draw a polyline or a polygon to make files less verbose and to test effect on performance Next Time: o A line of zero length does not always print as a point even when line terminators on; drawing a circle when a zero length line is encountered. o Is line weight always specified as scaled to local coordinate system size? This would allow thickness to scale with a rescaled plot. o Consider making JavaScript versions of the Hershey fonts so functions can be made that print text so text strings are editable. =============================================================================== */ #include <stdlib.h> #include <stdio.h> #include <time.h> #ifndef MINGW #include <sys/utsname.h> #include <pwd.h> #endif #include <unistd.h> #include <string.h> #include <math.h> #include <sys/types.h> #include "vogle.h" extern FILE *_voutfile(); /* How to convert degrees to radians */ #ifndef PI #define PI 3.14159265358979323844 #endif #define d2r(x) ((x) * PI / 180.0) #define r2d(x) ((x) * 180.0 / PI) #define MAX(x, y) ((x) > (y) ? (x) : (y)) #define MIN(x, y) ((x) < (y) ? (x) : (y)) #define ABS(x) ((x) < 0 ? -(x) : (x)) #define FLIPY(y) ((int)((vdevice.sizeSy)-(y))) #define FALSE 0 #define TRUE 1 /* Assume 1000 units per inch, and default size of 4.25inx5.5in*/ /* total drawing area size in x direction */ #define CANVASYSIZE 5500 /* total drawing area size in y direction */ #define CANVASXSIZE 4250 /* scale factor for going from world coordinates to device coordinates. Make bigger to increase accuracy of vector data */ #define CANVASTORAS 10 static int points=0; static int canvas_first_time = 1, drawn = 0, LAST_X = -1, LAST_Y = -1;/* last (x, y) drawn */ extern FILE *fp; int CANVAS_MOVED=0; #define CMAPSIZE 256 struct rgb_color { unsigned short int red; unsigned short int green; unsigned short int blue; }; struct rgb_color canvas_carr[CMAPSIZE]; /******************************************************************************/ static int PolyLineOpen = FALSE; /* PolyLine not open */ static int ObjectOpen = FALSE; /* Object not open */ static int curcol = 0; /* Current pen color (black) */ static int curwid = 1; /* Current pen color width */ static int curpat = 0; /* Current fill pattern*/ static int pgroup=1; /* groupid reserved for the entire page */ /******************************************************************************/ static int CANVAS_header() { time_t tod; #ifndef MINGW struct utsname unstr, *un; #endif char *username; struct passwd *pw; fprintf(fp,"<html>\n"); fprintf(fp,"<!-- Creator: VOGLE HTML CANVAS driver 1.0 2007-12-25 -->\n"); time(&tod); fprintf(fp,"<!-- CreationDate: %s -->\n",ctime(&tod)); fprintf(fp,"<!--\n"); time(&tod); #ifndef MINGW un = &unstr; /* initialize the pointer to an address with enough room to store the returned value in */ uname(un); if ((username = getlogin()) == NULL ){ pw = getpwuid(getuid()); username = pw->pw_name; } fprintf(fp," For: %s on OS=%.*s\n NETWORK_NAME=%.*s\n RELEASE=%.*s\n VERSION=%.*s\n MACHINE=%.*s\n", username, (int)sizeof(un->sysname), un->sysname, (int)sizeof(un->nodename), un->nodename, (int)sizeof(un->release), un->release, (int)sizeof(un->version), un->version, (int)sizeof(un->machine), un->machine); fprintf(fp,"-->\n"); #endif return(0); } /******************************************************************************/ /* change index i in the color map to the appropriate rgb value. */ int CANVAS_mapcolor(int i, int r, int g, int b) { if (i >= CMAPSIZE || i < 0 ){ return(-1); } canvas_carr[i].red = (unsigned short)(r); canvas_carr[i].green = (unsigned short)(g); canvas_carr[i].blue = (unsigned short)(b); return(0); } /******************************************************************************/ /* CANVAS_init set up the environment. Returns 1 on success. */ static int CANVAS_init(void) { int prefx, prefy, prefxs, prefys; int i; int CANVAS_header(); fp = _voutfile(); if (!canvas_first_time) return(1); CANVAS_header(); vogle_getprefposandsize(&prefx, &prefy, &prefxs, &prefys); if (prefxs != -1 ) { vdevice.sizeSy = prefys; vdevice.sizeSx = prefxs; vdevice.sizeX = vdevice.sizeY = MIN(prefys, prefxs ); } else{ vdevice.sizeSy = CANVASYSIZE; /* size in resolution rasters */ vdevice.sizeSx = CANVASXSIZE; /* size in resolution rasters */ vdevice.sizeX = vdevice.sizeY = MIN(CANVASXSIZE,CANVASYSIZE); /* current viewport to use */ } fprintf(fp,"<!-- canvas \" width=\"%fpx\" heigth=\"%fpx\" viewBox=\"0 0 %d %d\"-->\n", (float)vdevice.sizeSx/CANVASTORAS,(float)vdevice.sizeSy/CANVASTORAS, vdevice.sizeSx,vdevice.sizeSy); fprintf(fp,"<head>\n"); fprintf(fp,"<script type=\"application/x-javascript\">\n"); fprintf(fp,"function drawCanvas%d(){\n",pgroup); fprintf(fp,"var canvas = document.getElementById(\"canvas%d\");\n", pgroup); fprintf(fp,"if(canvas.getContext){\n"); fprintf(fp,"var ctx = canvas.getContext(\"2d\");\n"); fprintf(fp,"//<!-- id=\"Page%d\" -->\n", pgroup); fprintf(fp,"ctx.save();\n"); fprintf(fp,"ctx.scale(%f,%f);\n", 1.0/CANVASTORAS, 1.0/CANVASTORAS ); fprintf(fp,"ctx.lineWidth=%d;\n",MAX(1,vdevice.sizeX*curwid/10000)); fprintf(fp,"ctx.fillStyle='black';\n"); fprintf(fp,"ctx.strokeStyle='black';\n"); fprintf(fp,"ctx.lineCap='round';\n"); fprintf(fp,"ctx.lineJoin='round';\n"); vdevice.depth = 8; for (i = 0; i < CMAPSIZE; i++) /* set up the basic colors */ { canvas_carr[i].red=255; canvas_carr[i].green=255; canvas_carr[i].blue=255; } CANVAS_mapcolor(0, 255, 255, 255); CANVAS_mapcolor(1, 255, 0, 0); CANVAS_mapcolor(2, 0, 255, 0); CANVAS_mapcolor(3, 255, 255, 0); CANVAS_mapcolor(4, 0, 0, 255); CANVAS_mapcolor(5, 255, 0, 255); CANVAS_mapcolor(6, 0, 255, 255); CANVAS_mapcolor(7, 0, 0, 0); CANVAS_mapcolor( 8, 155, 0, 0); CANVAS_mapcolor( 9, 0, 155, 0); CANVAS_mapcolor(10, 155, 255, 255); CANVAS_mapcolor(11, 155, 155, 0); CANVAS_mapcolor(12, 0, 0, 155); CANVAS_mapcolor(13, 155, 0, 155); CANVAS_mapcolor(14, 0, 155, 155); CANVAS_mapcolor(15, 100, 100, 100); PolyLineOpen = FALSE; /* Polyline not open */ ObjectOpen = FALSE; /* Object not open */ curcol=0; curwid=1; curpat=0; drawn = 0; return (1); /* Set other line drawing parameters */ /* Move */ /* Set a default font height */ } /******************************************************************************/ static int closeline(void){ /* No "point" object in CANVAS; what do zero-length lines do? So keep track of whether drew any vector since last move in CANVAS_MOVED */ /*int half_box;*/ if(PolyLineOpen){ if(CANVAS_MOVED == 0 ){ /* ASSUME NULL LINES ARE DOTS OR POINTS */ /* circle of radius = line width */ /* fprintf(fp,"\nctx.arc(%d,%d,%d,0,Math.PI*2.0,true)\n", LAST_X, FLIPY(LAST_Y), MAX(1,vdevice.sizeX*curwid/10000)); */ fprintf(fp, "ctx.stroke();\n"); /* end curve */ }else{ /* fprintf(fp, "ctx.closePath();\n"); */ /* end curve */ fprintf(fp, "ctx.stroke();\n"); /* end curve */ } PolyLineOpen = FALSE; /* Polyline not open */ points = 0; } return (0); } /******************************************************************************/ static int closeObject(void){ if(ObjectOpen){ fprintf(fp,"// end object \n"); ObjectOpen = FALSE; /* flag object not open */ points = 0; } return (0); } /******************************************************************************/ static int openline(void){ if(!PolyLineOpen){ PolyLineOpen = TRUE; /* flag Polyline open */ } return (0); } /******************************************************************************/ static int openObject(void){ if(!ObjectOpen){ fprintf(fp, "// start object \n"); ObjectOpen = TRUE; /* Object open */ } return (0); } /******************************************************************************/ /* CANVAS_exit do a flush and close the output file if necessary. */ static int CANVAS_exit(void) { int ipages; closeline(); /* close Polyline line if it is open */ closeObject(); /* close object if it is open */ fprintf(fp,"ctx.restore();\n"); fprintf(fp, "}else{\n"); /* */ fprintf(fp, "alert('Your browser needs HTML CANVAS support to view this page');\n}\n"); /* */ fprintf(fp, "} // end of page\n"); /* Page Clear, End of Page Group */ fprintf(fp, "function drawCanvases(){\n"); /* */ for(ipages=0;ipages<pgroup;ipages++){ fprintf(fp, "drawCanvas%d();\n",ipages+1); /* */ } fprintf(fp, "}\n"); /* */ fprintf(fp, "</script>\n"); /* */ fprintf(fp, "</head>\n"); /* */ fprintf(fp, "<body onload=\"drawCanvases();\"><!-- BEGIN AV_TOOLBAR --><div id="av_toolbar_regdiv" style="display:none"><div class="av_site"><a target="_blank" href="https://it.altervista.org/crea-sito-gratis.php?utm_campaign=toolbar&amp;utm_source=link&amp;utm_medium=link" title="Apri un sito gratis con Wordpress">Crea sito</a></div></div><style>@media screen and (min-width:768px){body.av-toolbar-ready{position:relative;top:40px}:where(body.av-toolbar-ready){display:flow-root}}</style><script>self.av_toolbar_off||self!==top&&"XYZZY2"!==self.name&&!self.av_toolbar_force||(document.body.classList.add("av-toolbar-ready"),document.head.appendChild(document.createElement("script")).src="https://tb.altervista.org/js/s.js")</script><!-- END AV_TOOLBAR -->\n"); /* */ fprintf(fp, "\n"); /* */ for(ipages=0;ipages<pgroup;ipages++){ fprintf(fp, "<canvas id=\"canvas%d\" width=\"%d\" height=\"%d\"></canvas>\n", ipages+1, (int)vdevice.sizeSx/CANVASTORAS, (int)vdevice.sizeSy/CANVASTORAS); /* */ } fprintf(fp, "\n"); /* */ fprintf(fp, "</body>\n"); /* */ fprintf(fp, "</html>\n"); /* */ fprintf(fp,"<!--- End of Document -->\n"); drawn = 0; points = 0; if (fp != stdout && fp != stderr ){ fflush(fp); if(vdevice.writestoprocess == 2){ pclose(fp); }else{ fclose(fp); } } return (0); } /******************************************************************************/ /* CANVAS_draw draw to an x, y point. */ /* Note: (0, 0) is defined as the top left of the window in CANVAS. */ static int CANVAS_draw(int x, int y) { static char linefeed[2] = {' ','\n'}; if (LAST_X != vdevice.cpVx || LAST_Y != vdevice.cpVy ){ closeline(); /* close line if required */ openObject(); /* start Object if required */ openline(); /* start line */ fprintf(fp,"ctx.beginPath();\n"); fprintf(fp, "ctx.moveTo(%d,%d);\n", vdevice.cpVx, FLIPY(vdevice.cpVy)); LAST_X=vdevice.cpVx; LAST_Y=vdevice.cpVy; CANVAS_MOVED=0; points = 1; } openline(); /* start line if required */ if(points == 0){ fprintf(fp,"ctx.beginPath();\n"); fprintf(fp, "ctx.moveTo(%d,%d);", x ,FLIPY(y)); CANVAS_MOVED=0; }else{ if(LAST_X!=x || LAST_Y!=y)CANVAS_MOVED=CANVAS_MOVED+1; fprintf(fp, "%cctx.lineTo(%d,%d);", linefeed[(points % 8/7)], x ,FLIPY(y)); } points++; LAST_X = x; LAST_Y = y; drawn = 1; return (0); } /******************************************************************************/ /* CANVAS_clear flush the current page without resetting the graphics state */ static int CANVAS_clear(void) { closeline(); /* close line if required */ closeObject(); /* close Object if required */ if (drawn) { fprintf(fp,"ctx.restore();\n"); fprintf(fp, "}else{\n"); /* */ fprintf(fp, "alert('Your browser needs HTML CANVAS support to view this page');\n}\n"); /* */ fprintf(fp,"}// <!-- End Page%d -->\n",pgroup); /* Page Clear, End of Page Group */ pgroup++; /* increment page id */ fprintf(fp,"function drawCanvas%d(){\n",pgroup); fprintf(fp,"var canvas = document.getElementById(\"canvas%d\");\n", pgroup); fprintf(fp,"if(canvas.getContext){\n"); fprintf(fp,"var ctx = canvas.getContext(\"2d\");\n"); fprintf(fp,"ctx.save();\n"); fprintf(fp,"ctx.scale(%f,%f);\n", 1.0/CANVASTORAS, 1.0/CANVASTORAS ); fprintf(fp,"ctx.lineCap='round';\n"); fprintf(fp,"ctx.lineJoin='round';\n"); } drawn = 0; points = 0; CANVAS_MOVED=0; return(0); } /******************************************************************************/ /* * value sets raster line width */ static int CANVAS_setlw(int width) { closeline(); /* close line if required */ closeObject(); /* close Object if required */ if ( width >= 0 ) { curwid = width; } fprintf(fp,"ctx.lineWidth=%d;\n",MAX(1,vdevice.sizeX*curwid/10000)); return(0); } /******************************************************************************/ /* CANVAS_color change the color of the pen * kludged so negative value sets raster line width * if exceed allowable number of colors maybe pick a line style * or something like a gradient fill style for fun */ static int CANVAS_color(int col) { closeline(); /* close line if required */ closeObject(); /* close Object if required */ if ( col < 0 ) { CANVAS_setlw(abs(col)); } else { curpat = col/CMAPSIZE; curcol = col % CMAPSIZE; } fprintf(fp,"ctx.fillStyle=\"#%2.2x%2.2x%2.2x\";\n", canvas_carr[curcol].red, canvas_carr[curcol].green, canvas_carr[curcol].blue); /* fill color */ fprintf(fp,"ctx.strokeStyle=\"#%2.2x%2.2x%2.2x\";\n", canvas_carr[curcol].red, canvas_carr[curcol].green, canvas_carr[curcol].blue); /* fill color */ return(0); } /******************************************************************************/ /* HTML CANVAS does not support hardware fonts directly */ static int CANVAS_font(char *fontname) { vogle_font("futura.m"); } /******************************************************************************/ /* CANVAS_string output a string. */ static int CANVAS_string(char *s) { } /******************************************************************************/ int CANVAS_char(char c){ /* CANVAS_char output a character */ char s[2]; s[0] = c; s[1]='\0'; CANVAS_string(s); return(0); } /******************************************************************************/ static int CANVAS_fill(int n, int x[], int y[]) { /* fill a polygon */ int i; static char linefeed[2] = {' ','\n'}; closeline(); /* close line if required */ closeObject(); /* close line if required */ fprintf(fp,"// Polygon \n"); fprintf(fp,"ctx.fillStyle=\"#%2.2x%2.2x%2.2x\";\n", canvas_carr[curcol].red, canvas_carr[curcol].green, canvas_carr[curcol].blue); /* fill color */ fprintf(fp,"ctx.strokeStyle=\"#%2.2x%2.2x%2.2x\";\n", canvas_carr[curcol].red, canvas_carr[curcol].green, canvas_carr[curcol].blue); /* fill color */ fprintf(fp,"ctx.lineWidth=%d;\n", MAX(1,vdevice.sizeX*curwid/10000)); /* edge width */ fprintf(fp,"ctx.beginPath();\n"); fprintf(fp,"ctx.moveTo(%d,%d);\n", x[0],FLIPY(y[0])); for (i = 1; i < n; i++) { fprintf(fp, "ctx.lineTo(%d,%d);%c", x[i], FLIPY(y[i]),linefeed[(i % 8/7)]); } /* close path */ fprintf(fp, "ctx.lineTo(%d,%d);", x[0], FLIPY(y[0])); fprintf(fp,"ctx.fill()\n"); vdevice.cpVx = x[n - 1]; /* update current position */ vdevice.cpVy = y[n - 1]; LAST_X = vdevice.cpVx; LAST_Y = vdevice.cpVy; drawn = 1; return(0); } /******************************************************************************/ /* no operations - do nothing but return -1 */ static int noop(void) { return(-1); } static int noop2(int *x, int *y) { return(-1); } /******************************************************************************/ static DevEntry canvasdev = { "canvas", /* name of device */ "large", /* name of large font */ "small", /* name of small font */ noop, /* Set drawing in back buffer */ CANVAS_char, /* Draw a hardware character */ noop, /* Check if a key was hit */ CANVAS_clear, /* Clear the screen to current color */ CANVAS_color, /* Set current color */ CANVAS_draw, /* Draw a line */ CANVAS_exit, /* Exit graphics */ CANVAS_fill, /* Fill a polygon */ CANVAS_font, /* Set hardware font */ noop, /* Set drawing in front buffer */ noop, /* Wait for and get the next key hit */ CANVAS_init, /* Initialize the device */ noop2, /* Get mouse/cross hair position */ CANVAS_mapcolor, /* Set color indices */ CANVAS_setlw, /* Set line width */ CANVAS_string, /* Draw a hardware string */ noop, /* Swap front and back buffers */ noop /* Syncronize the display */ }; /******************************************************************************/ /* _CANVAS_devcpy copy the canvas device into vdevice.dev. */ int _CANVAS_devcpy() { vdevice.dev = canvasdev; return(0); } /******************************************************************************/