<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>
|