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