JavaScript Graphics 2 : Graph « Ajax Layer « JavaScript DHTML






JavaScript Graphics 2


<html>
<head>
<title>JavaScriptGraphics</title>
<style type="text/css">
<!--
input.active { 
  border-width: 1; 
        border-style: solid; 
        border-color: #000000;
}
input.passive {
  color: #C0C0C0;
  border-width: 0; 
        border-style: solid; 
        border-color: #000000;
}
-->
</style>
<script type="text/javascript">
/*
JavaScriptGraphics v 0.6
Pixel graphics in Javascript.
Copyright (C) 2003 Kitya Karlson http://www.karlson.ru/, karlson@karlson.ru
Tested in Microsoft Internet Explorer 6 and Mozilla 1.3.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation in version 2 of the License.

This program 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.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*! \mainpage JavaScriptGraphics
 *
 * \section intro Introduction
 * <p>Color image is just a 2D array of colors. If you think about image this way you can see
 * that it is possible to draw an image of the size N*M in HTML-only way - as a table with
 * N columns and M rows, where each cell takes one pixel and has a background color assigned
 * to it. Unfortunately even a small image represented like this in HTML results in a large
 * and complex code for the browser. But for artifitial images it is very easy to use RLE
 * compression - if there are several cells in a line of the same color you can
 * replace them by one cell with the correct colspan/rowspan attributes assigned for it.
 * <p>There are three cool things about this type of images:
 * <ol>
 * <li>They can be posted on the pages where images are not allowed (like some forums, or
 * livejournal),
 * <li>The size of HTML sended from the web server to client's computer is not very large -
 * the HTML for the images is generated on the client's computer only,
 * <li>They can be animated to react on user input.
 * </ol>
 * <p>I made a simple JavaScript library that allows you to use simple 2D graphics functions
 * to create such images (like drawing lines, points or circles). Comments and suggestions are <a href="mailto:karlson@karlson.ru">welcome</a>!
 * <p>As an alternative output method a handling of output to a Java applet is also provided in
 * addition to a plain HTML rendering.
 * <p>This library was tested in Microsoft Internet Explorer 6 and Mozilla 1.3.
 * 
 * \section examples Examples
 * <ul>
 * <li><a href="../tests/clock.html">Analog Clock</a> - shows the difference between different types
 * of HTML rendering
 * <li><a href="../tests/lines.html">Color Sun</a> - shows an example of zooming in HTML output (Java output zooming is working in the same way).
 * <li><a href="../tests/eyes.html">Eyes</a> - eyes that follow your mouse pointer.
 * <li><a href="../tests/ontop.html">On Top</a> - DHTML output overlay example.
 * <li><a href="../tests/plot.html">Sin/Cos Plot</a> - shows an example of HTML rendering and Java Applet processing (works only in Mozilla or in IE with Java virtual machine from SUN).
 * </ul>
 *
 * \section conv Converter
 * <a href="../tests/makeimage.php">Image to HTML converter</a> - this converter contains a preprocessing step, which is made using PHP and GD. Color dithering is produce to reduce the output complexity.
 * 
 * \section changes Changes
 * <p><b>v 0.6</b>
 * <ul>
 * <li>Image to HTML image converter added.
 * </ul>
 * <p><b>v 0.5</b>
 * <ul>
 * <li>Polygon and polyline drawing functions are added.
 * <li>DHTML output option and overlay output options + invisible color are added to HTML output processor.
 * <li>New example ("On Top") demonstrating new DHTML output options is added.
 * </ul>
 * <p><b>v 0.4</b>
 * <ul>
 * <li>Java Applet output methods are introduced in addition to HTML output methods.
 * <li>Color values are now accepted in several formats.
 * <li>Rendering time is calculated now.
 * <li>Examples are updated to reflect new features.
 * <li>A lot of bugfixes.
 * </ul>
 * <p><b>v 0.3</b>
 * <ul>
 * <li>Small bugfixes.
 * <li>HTML output processor is moved to the separate class.
 * <li>An optimised method of output compression - Optimised RLE - is introduced. It is using
 * both colspan and rowspan attributes, dividing the table into the minimum number of cells.
 * It is not so fast as the fast simple RLE, but it makes the tables really small. Which method
 * is used for compression (Fast RLE or Optimised RLE) is controlled by the compression
 * parametr of the HTML output class.
 * </ul>
 * <p><b>v 0.2</b>
 * <ul>
 * <li>Functions are rewritten as a class and moved to the separate file.
 * <li>Area fill function is rewritten using stack instead of recursion - this allows large closed areas to be filled-in.
 * <li>Code is cleaned up and documented using Doxygen.
 * </ul>
 * <p><b>v 0.1</b>
 * <ul>
 * <li>Initial release.
 * </ul>
 *
 * \section downloads Downloads
 * <ul>
 * <li>Download <a href="../jsgraphics.0.6.zip">JavaScriptGraphics v 0.6</a> - latest.
 * <li>Download <a href="../jsgraphics.0.5.zip">JavaScriptGraphics v 0.5</a>.
 * <li>Download <a href="../jsgraphics.0.4.zip">JavaScriptGraphics v 0.4</a>.
 * <li>Download <a href="../jsgraphics.0.3.zip">JavaScriptGraphics v 0.3</a>.
 * <li>Download <a href="../jsgraphics.0.2.zip">JavaScriptGraphics v 0.2</a>.
 * <li>Download <a href="../jsgraphics.0.1.zip">JavaScriptGraphics v 0.1</a>.
 * </ul>
 *
 * \section legal Legal
 * <p>This is <b>JavaScriptGraphics</b> library written in 2003 by Kitya Karlson <a href="mailto:karlson@karlson.ru">karlson@karlson.ru</a>.
 * This software is distributed under <a href="http://www.gnu.org/licenses/lgpl.html">LGPL</a>.
 *
 * \section warning Warning
 * <p>After working on this project for a couple of weeks I have found out that <a href="http://www.walterzorn.com/jsgraphics/jsgraphics_e.htm">a simillar
 * attempt</a> was made before already. The main differnce with my approach and the approach
 * taken by Walter Zorn is that my method performs drawing on offscreen first (on array)
 * and then creates optimised html only when flushed. Also in my method three types of
 * output are supported (HTML table, DHTML and Java Applet) and not only one output method like
 * in Walter's class. So my method would work faster and provide better output for more complex
 * images and is more suitable for animation, however Walter's method works faster if you
 * are in need of just one line.
 *
 */

/**
 * @file
 * JavaScriptGraphics is a library for producing graphics using JavaScript
 * by manipulating HTML tables. It uses 'run length encoding' by taking
 * advantage of colspan attributes in order to reduce the complexity of
 * the output. Images created in this manner can be posted on the pages
 * such as forums or LiveJournal where images are not allowed, and can
 * be animated using JavaScript. 
 * The methods provided allow to draw lines, point, circles, ellipsoids and other
 * geometrical figures.
 */

/**
 * JSColor class provides functions for converting different color repersentations
 * (HTML, RGB, INT) into each other. All methods of this class could be used as "static".
 *
 * Examples:
 *
 * HTML: #000000 - black, #FFFFFF - white,
 *
 * RGB: 0,0,0 - black, 255,255,255 - white,
 *
 * INT: 0 - black, 16777215 - white.
 *
 * @ctor
 * Constructs JSColor class (empty).
 */
function JSColor() {
};

/**
 * Converts RGB color to HTML color.
 * @tparam Integer red Red component of the color.
 * @tparam Integer green Green component of the color.
 * @tparam Integer blue Blue component of the color.
 * @treturn String HTML color.
 */
JSColor.prototype.rgbtohtml = function (red,green,blue) {
  x='0123456789ABCDEF';
  return "#" + x.charAt(red >> 4)+x.charAt(red & 15) + x.charAt(green >> 4)+x.charAt(green & 15) + x.charAt(blue >> 4) + x.charAt(blue & 15);
};

/**
 * Converts INT color to HTML color.
 * @tparam Integer rgb Color value.
 * @treturn String HTML color.
 */
JSColor.prototype.inttohtml = function(rgb) {
  return this.rgbtohtml( ((rgb >> 16) & 0xff), ((rgb >> 8) & 0xff ), (rgb & 0xff) );
};

/**
 * Converts HTML color to INT color.
 * @tparam String html HTML color.
 * @treturn Integer Color value.
 */
JSColor.prototype.htmltoint = function(html) {
  x='0123456789ABCDEF';
  html = html.toUpperCase();
  red = 16*x.indexOf(html.charAt(1))+x.indexOf(html.charAt(2));
  green = 16*x.indexOf(html.charAt(3))+x.indexOf(html.charAt(4));
  blue = 16*x.indexOf(html.charAt(5))+x.indexOf(html.charAt(6));
  return (red << 16) | (green << 8) | blue;
};

/**
 * Converts RGB color to INT color.
 * @tparam Integer red Red component of the color.
 * @tparam Integer green Green component of the color.
 * @tparam Integer blue Blue component of the color.
 * @treturn Integer Color value.
 */
JSColor.prototype.rgbtoint = function(red,green,blue) {
  return (red << 16) | (green << 8) | blue;
};

/**
 * "Static" Color object.
 * @type JSColor
 */
var Color = new JSColor();

/**
 * Simple 2D graphics canvas.
 *
 * x=0,y=0 - top left corner of the canvas.
 * x=width-1,y=height-1 - bottom right corner of the canvas.
 *
 * @ctor
 * Constructs a 2D image drawing canvas.
 * @tparam Integer width The width of the canvas.
 * @tparam Integer height The height of the canvas.
 * @tparam Integer bgcolor The background color of the canvas.
 */
function GCanvas(width, height, bgcolor) {

/**
 * The width of the canvas.
 * @type Integer
 */
 this.width=((width>0)?width:0) || 35;

/**
 * The height of the canvas.
 * @type Integer
 */
 this.height=((height>0)?height:0) || 35;

/**
 * The background color of the canvas (HTML format string).
 * @type String
 */
 this.bgcolor=bgcolor || 0;

/**
 * Internal array representing the image canvas.
 * @type Array
 */
 this.image = new Array(this.height*this.width);
 for (i=0;i<this.height*this.width;i++) {
  this.image[i]=this.bgcolor;
 }
};


/**
 * Clears the whole canvas using default background color.
 */
GCanvas.prototype.clear = function() {
 for (i=0; i < this.height*this.width; i++) {
    this.image[i]=this.bgcolor;
 }
};

/**
 * Puts a pixel of the defined color in the position x,y.
 * @tparam Integer x X coordinate of the pixel.
 * @tparam Integer y Y coordinate of the pixel.
 * @tparam Integer color The color of the pixel.
 */
GCanvas.prototype.draw = function(x,y,color) {
 if ((x >= 0) && (y >= 0) && (y < this.height) && (x < this.width)) {
   this.image[y*this.width+x]=color;
 }
};

/**
 * Gets a color of a pixel in the position x,y 
 * @treturn Integer Color of the pixel.
 */
GCanvas.prototype.getcolor = function(x,y) {
 if ((x >= 0) && (y >= 0) && (y < this.height) && (x < this.width)) {
   return this.image[y*this.width+x];
 } else {
  return null;
 }
};

/**
 * Draws a line (Bresenham's algorithm).
 * @tparam Integer x1 X coordinate of the start pixel.
 * @tparam Integer y1 Y coordinate of the start pixel.
 * @tparam Integer x2 X coordinate of the ending pixel.
 * @tparam Integer y2 Y coordinate of the ending pixel.
 * @tparam Integer color The color of the line.
 */
GCanvas.prototype.line = function(x1, y1, x2, y2, color)
{
 var pX=(x1<x2) ? 1 : -1;
 var pY=(y1<y2) ? 1 : -1;

 var E;
 var Delta1;
 var Delta2;

 var X=x1;
 var Y=y1;
 var I=1;
 var temp;

 if (x1>x2) { temp = x1; x1=x2; x2=temp; }
 if (y1>y2) { temp = y1; y1=y2; y2=temp; }

 var dX=x2-x1;
 var dY=y2-y1;

 this.draw(X, Y, color);

 if (dX>=dY)
    {
     Delta1=dY<<1;
     Delta2=(dY-dX)<<1;
     E=Delta1-dX;

     for (X+=pX; I<=dX; I++, X+=pX)
         {

          if (E>0)
             {
              E+=Delta2;
              Y+=pY;
             }
          else E+=Delta1;

          this.draw(X, Y, color);
         }
    }
 else
    {
     Delta1=dX<<1;
     Delta2=(dX-dY)<<1;
     E=Delta1-dY;

     for (Y+=pY; I<=dY; I++, Y+=pY)
         {

          if (E>0)
             {
              E+=Delta2;
              X+=pX;
             }
          else E+=Delta1;

          this.draw(X,Y,color);
         }
    }
};

/**
 * Draws a circle (Bresenham's algorithm).
 * @tparam Integer xc X coordinate of the center of the circle.
 * @tparam Integer yc Y coordinate of the center of the circle.
 * @tparam Integer r The radius of the circle.
 * @tparam Integer color The color of the circle.
 */
GCanvas.prototype.circle = function(xc,yc,r,color) {
  var y = r;
  var x = 0;
  var d = 3 - 2*r;
  while (x <= y) {
     this.draw(x+xc,y+yc,color);
     this.draw(x+xc,-y+yc,color);
     this.draw(-x+xc,-y+yc,color);
     this.draw(-x+xc,y+yc,color);
     this.draw(y+xc,x+yc,color);
     this.draw(y+xc,-x+yc,color);
     this.draw(-y+xc,-x+yc,color);
     this.draw(-y+xc,x+yc,color);
     if (d < 0) {
  d = d + 4*x +6;
     } else {
  d = d + 4*(x-y) + 10;
        y = y-1;
     }
     x = x+1;
 }
};

/**
 * Draws an ellipse (Bresenham's algorithm).
 * @tparam Integer xc X coordinate of the center of the circle.
 * @tparam Integer yc Y coordinate of the center of the circle.
 * @tparam Integer a The semi-axis of the ellipse.
 * @tparam Integer b The semi-axis of the ellipse.
 * @tparam Integer color The color of the ellipse.
 */
GCanvas.prototype.ellipse = function(xc,yc,a,b,color)
{
  b_square=b*b; 
  a_square=a*a; 
  row=b; 
  col=0; 
  two_a_square=a_square<<1; 
  four_a_square=a_square<<2; 
  four_b_square=b_square<<2; 
  two_b_square=b_square<<1; 
  d=two_a_square*((row-1)*(row))+a_square+two_b_square*(1-a_square); 
  while(a_square*(row)>b_square*(col))
  {
   this.draw(col+xc, row+yc, color); 
   this.draw(col+xc, yc-row, color); 
   this.draw(xc-col, row+yc, color); 
   this.draw(xc-col, yc-row, color); 
   if (d>=0)
   {
    row--; 
    d-=four_a_square*(row); 
   }
   d+=two_b_square*(3+(col<<1)); 
   col++; 
  }
  d=two_b_square*(col+1)*col+two_a_square*(row*(row-2)+1)+(1-two_a_square)*b_square; 
  while ((row) + 1)
  {
   this.draw(col+xc, row+yc, color); 
   this.draw(col+xc, yc-row, color); 
   this.draw(xc-col, row+yc, color); 
   this.draw(xc-col, yc-row, color); 
   if (d<=0)
   {
    col++; 
    d+=four_b_square*col; 
   }
   row--; 
   d+=two_a_square*(3-(row <<1)); 
  }
};

/**
 * Fills a closed area (using stack)
 * @tparam Integer x X coordinate of the point inside the area to be filled-in.
 * @tparam Integer y Y coordinate of the point inside the area to be filled-in.
 * @tparam Integer color Fill color.
 */
GCanvas.prototype.fill = function(x,y,color) {
    stack_head=0;
    stack_tail=0;
    floodfill_stackx = new Array((this.width+2)*(this.height+2));
    floodfill_stacky = new Array((this.width+2)*(this.height+2));

    clr=this.getcolor(x,y);
    floodfill_stackx[stack_head]=x;
    floodfill_stacky[stack_head]=y;
    this.draw(x,y,color);
    stack_head++;

    while ( (stack_head<((this.width+2)*(this.height+2))) && (stack_head>stack_tail) ) {
        x=floodfill_stackx[stack_tail];
        y=floodfill_stacky[stack_tail];
        stack_tail++;
        if (x>=0 && y>=0 && x<this.width && y<this.height) {
      if (this.getcolor(x+1,y)==clr) {
                floodfill_stackx[stack_head]=x+1;
              floodfill_stacky[stack_head]=y;
                this.draw(x+1,y,color);
              stack_head++;
            }
            if (this.getcolor(x-1,y)==clr) {
                floodfill_stackx[stack_head]=x-1;
              floodfill_stacky[stack_head]=y;
                this.draw(x-1,y,color);
              stack_head++;
                }
            if (this.getcolor(x,y+1)==clr) {
                floodfill_stackx[stack_head]=x;
                floodfill_stacky[stack_head]=y+1;
                this.draw(x,y+1,color);
              stack_head++;
                }
            if (this.getcolor(x,y-1)==clr) {
                floodfill_stackx[stack_head]=x;
                floodfill_stacky[stack_head]=y-1;
                this.draw(x,y-1,color);
              stack_head++;
                }
            }
        }
    delete floodfill_stacky;
    delete floodfill_stackx;
};

/**
 * Draws a polyline.
 * @tparam Array x x1,x2, ..., xn.
 * @tparam Array y y1,y2, ..., yn.
 * @tparam Integer color Polyline color.
 */
GCanvas.prototype.polyline = function(x, y, color) {
    var z = x.length-1; while (z >= 0) this.line(x[z], y[z], x[--z], y[z], color);
};

/**
 * Draws a polygon (automatically closed if last points are not identical.
 * @tparam Array x x1,x2, ..., xn.
 * @tparam Array y y1,y2, ..., yn.
 * @tparam Integer color Polygon color.
 */
GCanvas.prototype.polygon = function(x, y, color) {
    this.polyline(x, y, color);
    this.line(x[x.length-1], y[x.length-1], x[0], y[0], color);
};

/**
 * Output processor.
 *
 * An abstract output processor.
 *
 * @ctor
 * Abstract output processor.
 * @tparam Integer scale The scaling of the output (1 = 1x = no scaling).
 */
function GOutput(scale) {
/**
 * Scaling of the output (1 = 1x = no scaling).
 * @type Integer
 */
 this.scale=scale || 1;
}

/**
 * HTML output processor.
 *
 * This output processor can be used to render the canvas as an HTML table.
 * Two types ('Fast RLE' and 'Optimised RLE') of output rendering are provided,
 * see bellow.
 *
 * @ctor
 * Constructs an HTML output processor.
 */
function GHTMLOutput() {
/**
 * Compression parametr (0 - fast RLE, 1 - optimised RLE).
 * @type Integer
 */
 this.compression=0;
/**
 * Output type - HTML (table) or DHTML (div's). If dhtml is set to false HTML output
 * is produced and if dhtml is set to true DHTML output is produced.
 * @type Boolean
 */
 this.dhtml = true;
/**
 * An invsibile color. By default invisible_color = -1, i.e. the default background of the canvas.
 * @type Integer
 */
 this.invisible_color = -1;

/**
 * Number of cells generated in the HTML table.
 * @type Integer
 */
 this.number_of_cells=0;
/**
 * Time (in ms.) used for the generation of the HTML table.
 * @type Integer
 */
 this.generation_time=0;
/**
 * Javascript document object (usually this.document).
 * 
 * Needed only for print functions.
 * @type Object
 */

 this.doc = null;
/**
 * Output layer ID.
 *
 * Needed only for print functions.
 * @type String
 */
 this.layerId = null;
/**
 * Append or overwrite the layer.
 *
 * Needed only for print functions.
 * @type Boolean
 */
 this.append = false;
}
GHTMLOutput.prototype = new GOutput();

/**
 * HTML output printing parametrs setup function.
 * 
 * @tparam Object doc Document object (usually this.document).
 * @tparam String layerId Output layer ID.
 */
GHTMLOutput.prototype.setup = function(doc,layerId) {
 this.doc=doc;
 this.layerId=layerId;
};

/**
 * Returns the image canvas html (using RLE compression on lines = fast RLE).
 * @treturn String A table in HTML format representing the image canvas.
 * @tparam GCanvas gcanvas 2D image canvas.
 */
GHTMLOutput.prototype.get_html = function(gcanvas) {
  time_now = new Date();
  this.number_of_cells = 0;
  if (this.invisible_color == -1) {
  inv_color = gcanvas.bgcolor;
  } else {
  inv_color = this.invisibile_color;
  }
  str = new String("");
  len = 0;
  if (! this.dhtml) {
    str += "<table border=0 cellspacing=0 cellpadding=0 width="+gcanvas.width*this.scale+" height="+gcanvas.height*this.scale+">"; 
  }
  for (i=0; i < gcanvas.height; i++) {
   if (! this.dhtml) {
     str += "<tr height="+this.scale+" width="+gcanvas.width*this.scale+">";
   }
   current_color = gcanvas.getcolor(0,i);
   len = 0;
   start_j = 0;
   for (j=0; j < gcanvas.width; j++) {
    if ( (gcanvas.getcolor(j,i) != current_color) || (j == gcanvas.width-1)) {
     if (j== gcanvas.width-1) { len++; }
     if (! this.dhtml) {
       str += "<td width="+this.scale*len+" height="+this.scale + ( (len>1) ? " colspan="+len : "" ) + ( (current_color!=inv_color) ? " bgcolor="+Color.inttohtml(current_color) : "") + "></td>";
     } else {
        if (current_color!=inv_color) {
    str += '<div style="position:absolute;'+ 'left:' + (start_j*this.scale) + 'px;'+ 'top:' + (i*this.scale) + 'px;'+ 'width:' + (this.scale*len) + 'px;'+ 'height:' + this.scale + 'px;'+ 'clip:rect(0,'+(this.scale*len)+'px,'+this.scale+'px,0);' + 'overflow:hidden;background-color:' + Color.inttohtml(current_color) + ';' + '"><\/div>';
  }
     }
     this.number_of_cells++;
     len=1;
     start_j = j;
     current_color=gcanvas.getcolor(j,i);
    } else {
     len++;
    }
   }
   if (! this.dhtml) {
        str += "</tr>";
   }
  }
  if (! this.dhtml) {
       str += "</table>";
  }
  time_finish = new Date();
  this.generation_time = time_finish - time_now;
  delete time_now;
  delete time_finish;
  return str;
};


/**
 * Returns the image canvas html (using RLE compression on both lines and rows = optimised RLE).
 * @treturn String A table in HTML format representing the image canvas.
 * @tparam GCanvas gcanvas 2D image canvas.
 */
GHTMLOutput.prototype.get_optimised_html = function(gcanvas) {

  time_now = new Date();
  if (this.invisible_color == -1) {
  inv_color = gcanvas.bgcolor;
  } else {
  inv_color = this.invisibile_color;
  }
  this.number_of_cells = 0;
  str = new String("");
  flushed = new Array(gcanvas.height*gcanvas.width);
  for (i=0;i<gcanvas.height*gcanvas.width;i++) {  
  flushed[i]=0;
  }
  if (! this.dhtml) {
    str += "<table border=0 cellspacing=0 cellpadding=0 width="+gcanvas.width*this.scale+" height="+gcanvas.height*this.scale+">"; 
  }
  for (i=0; i < gcanvas.height; i++) {
   if (! this.dhtml) {
     str += "<tr height="+this.scale+" width="+gcanvas.width*this.scale+">";
   }
   for (j=0; j < gcanvas.width; j++) {
  if (flushed[i*gcanvas.width+j] == 0) {
      current_color = gcanvas.getcolor(j,i);
      k=gcanvas.height;
      opt = 0;
      colspan = 1;
      rowspan = 1;
      for (x=j; x < gcanvas.width; x++) {
    if (flushed[i*gcanvas.height+x]==1) { break; }
    if (gcanvas.getcolor(x,i) != current_color) { break; }
    for (y=i; y < k; y++) {
      if (flushed[y*gcanvas.width+x]==1) { break; }
      if (gcanvas.getcolor(x,y) != current_color) { break; }
     }
    if (y-1<0) { break; }
    if (gcanvas.getcolor(x,y-1) != current_color) { break; }
    k=y;
    if ( ((x-j+1)*(y-i)) > opt) {
      opt=(x-j+1)*(y-i);
      colspan = x-j+1;
      rowspan = y-i; 
    }
     }
     for (y=i; y < i+rowspan; y++) {
    for (x=j; x < j+colspan; x++) {
      flushed[y*gcanvas.width+x]=1;
    }
     }
     if (! this.dhtml) {
       str += "<td width="+this.scale*colspan+" height="+ this.scale*rowspan + ( (colspan>1) ? " colspan="+colspan : "" ) + ( (rowspan > 1) ? " rowspan=" + rowspan : "") + ( (current_color!=inv_color) ? " bgcolor="+Color.inttohtml(current_color) : "") + "></td>";
     } else {
          if (current_color!=inv_color) {
      str += '<div style="position:absolute;'+ 'left:' + (j*this.scale) + 'px;'+ 'top:' + (i*this.scale) + 'px;'+ 'width:' + (this.scale*colspan) + 'px;'+ 'height:' + (this.scale*rowspan) + 'px;'+ 'clip:rect(0,'+(this.scale*colspan)+'px,'+(this.scale*rowspan)+'px,0);' + 'overflow:hidden;background-color:' + Color.inttohtml(current_color) + ';' + '"><\/div>';
    }
     }
     this.number_of_cells++;
  }
   }
   if (! this.dhtml) {
     str += "</tr>";
   }
  }
  if (! this.dhtml) {
    str += "</table>";
  }
  delete flushed;
  time_finish = new Date();
  this.generation_time = time_finish - time_now;
  delete time_now;
  delete time_finish;
  return str;  
};


/**
 * Assigns the image canvas html (using RLE compression on lines = fast RLE) to a given layer.
 * @tparam GCanvas gcanvas 2D image canvas.
 */
GHTMLOutput.prototype.print_html = function(gcanvas) {
      if ((this.doc != null) && (this.layerId != null)) {
        if (this.doc.all) {
          outlayer = this.doc.all[this.layerId];
        }
        if (this.doc.getElementById) {
          outlayer = this.doc.getElementById(this.layerId);
        }
        if (this.append) {
                outlayer.innerHTML += this.get_html(gcanvas);
        } else {
                outlayer.innerHTML = this.get_html(gcanvas);
        }
     }
};

/**
 * Assigns the image canvas html (using RLE compression on both lines and rows = optimised RLE) to a given layer.
 * @tparam GCanvas gcanvas 2D image canvas.
 */
GHTMLOutput.prototype.print_optimised_html = function(gcanvas) {
      if ((this.doc != null) && (this.layerId != null)) {
        if (this.doc.all) {
          outlayer = this.doc.all[this.layerId];
        }
        if (this.doc.getElementById) {
          outlayer = this.doc.getElementById(this.layerId);
        }
        if (this.append) {
                outlayer.innerHTML += this.get_optimised_html(gcanvas);
        } else {
                outlayer.innerHTML = this.get_optimised_html(gcanvas);
        }
     }
};

/**
 * Returns the image canvas html
 * @treturn String A table in HTML format representing the image canvas.
 * @tparam GCanvas gcanvas 2D image canvas.
 */
GHTMLOutput.prototype.get = function(gcanvas) {
  switch (this.compression) {
    case 0: return this.get_html(gcanvas); break;
    case 1: return this.get_optimised_html(gcanvas); break;
    default: return this.get_html(gcanvas); break;
  }
};

/**
 * Assigns the image canvas html to a given layer.
 * @tparam GCanvas gcanvas 2D image canvas.
 */
GHTMLOutput.prototype.print = function(gcanvas) {
  switch (this.compression) {
    case 0: return this.print_html(gcanvas); break;
    case 1: return this.print_optimised_html(gcanvas); break;
    default: return this.print_html(gcanvas); break;
  }
}

/**
 * Java (applet) output processor.
 *
 * This output processor can be used to pass your canvas to a Java applet for rendering.
 *
 * @ctor
 * Constructs an applet output processor.
 */
function GJavaOutput() {
/**
 * Time (in ms.) used for the generation of the image string to be passed to the applet.
 * @type Integer
 */
 this.generation_time=0;

/**
 * Javascript document object (usually this.document).
 * 
 * Needed only for print function.
 * @type Object
 */
 this.doc = null;

/**
 * The name of an applet providing setImage function.
 *
 * Example of an applet providing setImage function:
 * <PRE>
 * -----------------------------------------------------------
 * 
 * import java.applet.Applet;
 * import java.awt.Graphics;
 * import java.awt.Image;
 * import java.awt.Component;
 * import java.lang.Integer;
 * import java.util.StringTokenizer;
 * 
 * public class ImageOutput extends Applet {
 *    Image JSImage = null;
 * 
 *    public void init() {
 *   // some initialisation here
 *    }
 * 
 *    public void paint(Graphics g) {
 *      if (this.JSImage != null) {
 *       g.drawImage(this.JSImage, 0, 0, this);
 *   }
 *    }
 * 
 *    public void setImage(int w, int h, String pixels) {
 *   int pix[] = new int[w * h];
 *   StringTokenizer st = new StringTokenizer(pixels," ");
 *   int index = 0;
 *        while (st.hasMoreTokens()) {
 *              pix[index++]=Integer.parseInt(st.nextToken());
 *        }
 *         this.JSImage = createImage(new java.awt.image.MemoryImageSource(w, h, pix, 0, w));
 *   repaint();
 *    }
 * }
 * 
 * -----------------------------------------------------------
 * </PRE>
 * Javascript is used to passed a String of the image bytes separated by space. Array would
 * be a better choice, but it seems that MS IE fails to pass JavaScript Array to Java correctly.
 *
 * Needed only for print function.
 * @type String
 */
 this.appletName = null;

/**
 * Alpha chanel value.
 * @type Integer
 */
 this.alpha = 255;
}
GJavaOutput.prototype = new GOutput();

/**
 * Java output printing parametrs setup function (needed only for print functions).
 * 
 * @tparam Object doc Document object (usually this.document).
 * @tparam String appletName Reciving java applet name.
 */
GJavaOutput.prototype.setup = function(doc,appletName) {
 this.doc=doc;
 this.appletName=appletName;
};

/**
 * Returns the image canvas string to be passed to Java.
 * @treturn String String representing the bytes of the image separated by spaces;
 * @tparam GCanvas gcanvas 2D image canvas.
 */
GJavaOutput.prototype.get = function(gcanvas) {
  time_now = new Date();
  pixels = new String("");
  for (y=0;y<gcanvas.height;y++) {
    for (i=0;i<this.scale;i++) {
      for (x=0;x<gcanvas.width;x++) {
        for (j=0;j<this.scale;j++) {
          pixels += (pixels.length>0?" ":"") + ((this.alpha << 24) | gcanvas.getcolor(x,y));
        }
      }
    }
  }
  time_finish = new Date();
  this.generation_time = time_finish - time_now;
  delete time_now;
  delete time_finish;
  return pixels;
};

/**
 * Passes the image canvas String to a given applet.
 * @tparam GCanvas gcanvas 2D image canvas.
 */
GJavaOutput.prototype.print = function(gcanvas) {
      if ((this.doc != null) && (this.appletName != null)) {
    this.doc.applets[this.appletName].setImage(gcanvas.width*this.scale,gcanvas.height*this.scale,this.get(gcanvas));
       }
};
</script>
<script type="text/javascript">
<!--

// Usage examples

var white = Color.htmltoint('#FFFFFF');
var black = Color.htmltoint('#000000');

var gc_eyes = new GCanvas(30,30,white,1);

var output = new GHTMLOutput();

// eyes
var eyes_border_color = Color.htmltoint('#000000');
var eyes_color = Color.htmltoint('#D1DFF2');
var pupil_border_color = Color.htmltoint('#AA0000');
var pupil_color = Color.htmltoint('#FF0000');
var left_eye_center_x = Math.round(gc_eyes.width/4.) - 2;
var left_eye_center_y = Math.round(gc_eyes.height/2.);
var right_eye_center_x = left_eye_center_x*3;
var right_eye_center_y = left_eye_center_y;
var eye_a = left_eye_center_x - 1;
var eye_b = left_eye_center_x;

function eyes_handlerIE() {
  Xpos = window.event.x + document.body.scrollLeft;
  Ypos = window.event.y + document.body.scrollTop;    
        eyes(Xpos,Ypos);
}

function eyes_handlerMOZ(event) {
    Xpos = event.clientX + window.pageXOffset;
        Ypos = event.clientY + window.pageYOffset;
        eyes(Xpos,Ypos);
}

function eyes(Xpos,Ypos) {
        gc_eyes.clear();

  if (document.all) {
     eyeslayer = document.all["eyes"];
  } else if (document.getElementById) {
     eyeslayer = document.getElementById("eyes");
  }

        gc_eyes.ellipse(left_eye_center_x,left_eye_center_y,eye_a,eye_b,eyes_border_color);
  gc_eyes.fill(left_eye_center_x,left_eye_center_y,eyes_color);
        gc_eyes.ellipse(right_eye_center_x,right_eye_center_y,eye_a,eye_b,eyes_border_color);
  gc_eyes.fill(right_eye_center_x,right_eye_center_y,eyes_color);

        dx = Xpos - eyeslayer.offsetLeft+left_eye_center_x;
        dy = Ypos - eyeslayer.offsetTop+left_eye_center_y;
        angle = Math.atan2(dy,dx);
        x = Math.round(eye_a*0.5*Math.cos(angle)+left_eye_center_x);
        y = Math.round(eye_a*0.5*Math.sin(angle)+left_eye_center_y);
        gc_eyes.circle(x,y,2,pupil_border_color);
  gc_eyes.fill(x,y,pupil_color);

        dx = Xpos - eyeslayer.offsetLeft+right_eye_center_x;
        dy = Ypos - eyeslayer.offsetTop+right_eye_center_y;
        angle = Math.atan2(dy,dx);
        x = Math.round(eye_a*0.5*Math.cos(angle)+right_eye_center_x);
        y = Math.round(eye_a*0.5*Math.sin(angle)+right_eye_center_y);
        gc_eyes.circle(x,y,2,pupil_border_color);
  gc_eyes.fill(x,y,pupil_color);

  output.setup(this.document,'eyes');
  output.print(gc_eyes);
}
// -->
</script>
</head>
<body onLoad="eyes(0,0); return true;">
<h1>Eyes</h1>
<p>These eyes look at your mouse pointer (once you have started them). They are rendered in HTML.
<form>
Click <input type="button" value="Start!" name="start_eyes" class="active" onClick="this.form.start_eyes.className='passive'; this.form.stop_eyes.className='active'; if (document.all) { document.onmousemove = eyes_handlerIE; } else if (document.getElementById) { window.addEventListener('mousemove',eyes_handlerMOZ,true); }"> to start eyes.
Click <input type="button" value="Stop!" name="stop_eyes" class="passive" onClick="this.form.start_eyes.className='active'; this.form.stop_eyes.className='passive'; if (document.all) { document.onmousemove = null; } else if (document.getElementById) { window.removeEventListener('mousemove',eyes_handlerMOZ,true); }"> to stop eyes.
<center><div id="eyes" style="position:relative;top:0;left:0;height:30;width:30;">[eyes]</div></center>
</form>
<p><div align="right"><a href="./ontop.html">Next</a> || <a href="./lines.html">Previous</a> || <a href="../html/index.html#examples">Index</a></div>
</body>
</html>

           
       








Related examples in the same category

1.Quadratic Curve Test
2.Line Width
3.Drawing arches
4.Drag and drop graph
5.JavaScript Graphics
6.JavaScript Graphics 3
7.JavaScript Graphics 4