// HTML5 mojo runtime.
//
// Copyright 2011 Mark Sibly, all rights reserved.
// No warranty implied; use at your own risk.
// ***** gxtkGraphics class *****
function gxtkGraphics(){
this.game=BBHtml5Game.Html5Game();
this.canvas=this.game.GetCanvas()
this.width=this.canvas.width;
this.height=this.canvas.height;
this.gl=null;
this.gc=this.canvas.getContext( '2d' );
this.tmpCanvas=null;
this.r=255;
this.b=255;
this.g=255;
this.white=true;
this.color="rgb(255,255,255)"
this.alpha=1;
this.blend="source-over";
this.ix=1;this.iy=0;
this.jx=0;this.jy=1;
this.tx=0;this.ty=0;
this.tformed=false;
this.scissorX=0;
this.scissorY=0;
this.scissorWidth=0;
this.scissorHeight=0;
this.clipped=false;
}
gxtkGraphics.prototype.BeginRender=function(){
this.width=this.canvas.width;
this.height=this.canvas.height;
if( !this.gc ) return 0;
if(CFG_MOJO_IMAGE_FILTERING_ENABLED!="1" ) {
this.gc.mozImageSmoothingEnabled = false;
this.gc.webkitImageSmoothingEnabled = false;
this.gc.msImageSmoothingEnabled = false;
this.gc.imageSmoothingEnabled = false;
this.gc.oImageSmoothingEnabled= false;
}
this.gc.save();
if( this.game.GetLoading() ) return 2;
return 1;
}
gxtkGraphics.prototype.EndRender=function(){
if( this.gc ) this.gc.restore();
}
gxtkGraphics.prototype.Width=function(){
return this.width;
}
gxtkGraphics.prototype.Height=function(){
return this.height;
}
gxtkGraphics.prototype.LoadSurface=function( path ){
var game=this.game;
var ty=game.GetMetaData( path,"type" );
if( ty.indexOf( "image/" )!=0 ) return null;
game.IncLoading();
var image=new Image();
image.onload=function(){ game.DecLoading(); }
image.onerror=function(){ game.DecLoading(); }
image.meta_width=parseInt( game.GetMetaData( path,"width" ) );
image.meta_height=parseInt( game.GetMetaData( path,"height" ) );
image.src=game.PathToUrl( path );
return new gxtkSurface( image,this );
}
gxtkGraphics.prototype.CreateSurface=function( width,height ){
var canvas=document.createElement( 'canvas' );
canvas.width=width;
canvas.height=height;
canvas.meta_width=width;
canvas.meta_height=height;
canvas.complete=true;
var surface=new gxtkSurface( canvas,this );
surface.gc=canvas.getContext( '2d' );
return surface;
}
gxtkGraphics.prototype.SetAlpha=function( alpha ){
this.alpha=alpha;
this.gc.globalAlpha=alpha;
}
gxtkGraphics.prototype.SetColor=function( r,g,b ){
this.r=r;
this.g=g;
this.b=b;
this.white=(r==255 && g==255 && b==255);
this.color="rgb("+(r|0)+","+(g|0)+","+(b|0)+")";
this.gc.fillStyle=this.color;
this.gc.strokeStyle=this.color;
}
gxtkGraphics.prototype.SetBlend=function( blend ){
switch( blend ){
case 1:
this.blend="lighter";
break;
default:
this.blend="source-over";
}
this.gc.globalCompositeOperation=this.blend;
}
gxtkGraphics.prototype.SetScissor=function( x,y,w,h ){
this.scissorX=x;
this.scissorY=y;
this.scissorWidth=w;
this.scissorHeight=h;
this.clipped=(x!=0 || y!=0 || w!=this.canvas.width || h!=this.canvas.height);
this.gc.restore();
this.gc.save();
if( this.clipped ){
this.gc.beginPath();
this.gc.rect( x,y,w,h );
this.gc.clip();
this.gc.closePath();
}
this.gc.fillStyle=this.color;
this.gc.strokeStyle=this.color;
this.gc.globalAlpha=this.alpha;
this.gc.globalCompositeOperation=this.blend;
if( this.tformed ) this.gc.setTransform( this.ix,this.iy,this.jx,this.jy,this.tx,this.ty );
}
gxtkGraphics.prototype.SetMatrix=function( ix,iy,jx,jy,tx,ty ){
this.ix=ix;this.iy=iy;
this.jx=jx;this.jy=jy;
this.tx=tx;this.ty=ty;
this.gc.setTransform( ix,iy,jx,jy,tx,ty );
this.tformed=(ix!=1 || iy!=0 || jx!=0 || jy!=1 || tx!=0 || ty!=0);
}
gxtkGraphics.prototype.Cls=function( r,g,b ){
if( this.tformed ) this.gc.setTransform( 1,0,0,1,0,0 );
this.gc.fillStyle="rgb("+(r|0)+","+(g|0)+","+(b|0)+")";
this.gc.globalAlpha=1;
this.gc.globalCompositeOperation="source-over";
this.gc.fillRect( 0,0,this.canvas.width,this.canvas.height );
this.gc.fillStyle=this.color;
this.gc.globalAlpha=this.alpha;
this.gc.globalCompositeOperation=this.blend;
if( this.tformed ) this.gc.setTransform( this.ix,this.iy,this.jx,this.jy,this.tx,this.ty );
}
gxtkGraphics.prototype.DrawPoint=function( x,y ){
if( this.tformed ){
var px=x;
x=px * this.ix + y * this.jx + this.tx;
y=px * this.iy + y * this.jy + this.ty;
this.gc.setTransform( 1,0,0,1,0,0 );
this.gc.fillRect( x,y,1,1 );
this.gc.setTransform( this.ix,this.iy,this.jx,this.jy,this.tx,this.ty );
}else{
this.gc.fillRect( x,y,1,1 );
}
}
gxtkGraphics.prototype.DrawRect=function( x,y,w,h ){
if( w<0 ){ x+=w;w=-w; }
if( h<0 ){ y+=h;h=-h; }
if( w<=0 || h<=0 ) return;
//
this.gc.fillRect( x,y,w,h );
}
gxtkGraphics.prototype.DrawLine=function( x1,y1,x2,y2 ){
if( this.tformed ){
var x1_t=x1 * this.ix + y1 * this.jx + this.tx;
var y1_t=x1 * this.iy + y1 * this.jy + this.ty;
var x2_t=x2 * this.ix + y2 * this.jx + this.tx;
var y2_t=x2 * this.iy + y2 * this.jy + this.ty;
this.gc.setTransform( 1,0,0,1,0,0 );
this.gc.beginPath();
this.gc.moveTo( x1_t,y1_t );
this.gc.lineTo( x2_t,y2_t );
this.gc.stroke();
this.gc.closePath();
this.gc.setTransform( this.ix,this.iy,this.jx,this.jy,this.tx,this.ty );
}else{
this.gc.beginPath();
this.gc.moveTo( x1,y1 );
this.gc.lineTo( x2,y2 );
this.gc.stroke();
this.gc.closePath();
}
}
gxtkGraphics.prototype.DrawOval=function( x,y,w,h ){
if( w<0 ){ x+=w;w=-w; }
if( h<0 ){ y+=h;h=-h; }
if( w<=0 || h<=0 ) return;
//
var w2=w/2,h2=h/2;
this.gc.save();
this.gc.translate( x+w2,y+h2 );
this.gc.scale( w2,h2 );
this.gc.beginPath();
this.gc.arc( 0,0,1,0,Math.PI*2,false );
this.gc.fill();
this.gc.closePath();
this.gc.restore();
}
gxtkGraphics.prototype.DrawPoly=function( verts ){
if( verts.length<2 ) return;
this.gc.beginPath();
this.gc.moveTo( verts[0],verts[1] );
for( var i=2;ithis.tmpCanvas.width || sh>this.tmpCanvas.height ){
this.tmpCanvas.width=Math.max( sw,this.tmpCanvas.width );
this.tmpCanvas.height=Math.max( sh,this.tmpCanvas.height );
}
var tmpGC=this.tmpCanvas.getContext( "2d" );
tmpGC.globalCompositeOperation="copy";
tmpGC.drawImage( image,sx,sy,sw,sh,0,0,sw,sh );
var imgData=tmpGC.getImageData( 0,0,sw,sh );
var p=imgData.data,sz=sw*sh*4,i;
for( i=0;i>16) & 0xff;
p[i+1]=(argb>>8) & 0xff;
p[i+2]=argb & 0xff;
p[i+3]=(argb>>24) & 0xff;
i+=4;
}
j+=pitch-width;
}
surface.gc.putImageData( imgData,x,y );
}
// ***** gxtkSurface class *****
function gxtkSurface( image,graphics ){
this.image=image;
this.graphics=graphics;
this.swidth=image.meta_width;
this.sheight=image.meta_height;
}
// ***** GXTK API *****
gxtkSurface.prototype.Discard=function(){
if( this.image ){
this.image=null;
}
}
gxtkSurface.prototype.Width=function(){
return this.swidth;
}
gxtkSurface.prototype.Height=function(){
return this.sheight;
}
gxtkSurface.prototype.Loaded=function(){
return this.image.complete;
}
gxtkSurface.prototype.OnUnsafeLoadComplete=function(){
}
if( CFG_HTML5_WEBAUDIO_ENABLED=="1" && (window.AudioContext || window.webkitAudioContext) ){
//print( "Using WebAudio!" );
// ***** WebAudio *****
var wa=null;
// ***** WebAudio gxtkSample *****
var gxtkSample=function(){
this.waBuffer=null;
this.state=0;
}
gxtkSample.prototype.Load=function( path ){
if( this.state ) return false;
var req=new XMLHttpRequest();
req.open( "get",BBGame.Game().PathToUrl( path ),true );
req.responseType="arraybuffer";
var abuf=this;
req.onload=function(){
wa.decodeAudioData( req.response,function( buffer ){
//success!
abuf.waBuffer=buffer;
abuf.state=1;
},function(){
abuf.state=-1;
} );
}
req.onerror=function(){
abuf.state=-1;
}
req.send();
this.state=2;
return true;
}
gxtkSample.prototype.Discard=function(){
}
// ***** WebAudio gxtkChannel *****
var gxtkChannel=function(){
this.buffer=null;
this.flags=0;
this.volume=1;
this.pan=0;
this.rate=1;
this.waSource=null;
this.waPan=wa.create
this.waGain=wa.createGain();
this.waGain.connect( wa.destination );
this.waPanner=wa.createPanner();
this.waPanner.rolloffFactor=0;
this.waPanner.panningModel="equalpower";
this.waPanner.connect( this.waGain );
this.startTime=0;
this.offset=0;
this.state=0;
}
// ***** WebAudio gxtkAudio *****
var gxtkAudio=function(){
if( !wa ){
window.AudioContext=window.AudioContext || window.webkitAudioContext;
wa=new AudioContext();
}
this.okay=true;
this.music=null;
this.musicState=0;
this.musicVolume=1;
this.channels=new Array();
for( var i=0;i<32;++i ){
this.channels[i]=new gxtkChannel();
}
}
gxtkAudio.prototype.Suspend=function(){
if( this.MusicState()==1 ) this.music.pause();
for( var i=0;i<32;++i ){
var chan=this.channels[i];
if( chan.state!=1 ) continue;
this.PauseChannel( i );
chan.state=5;
}
}
gxtkAudio.prototype.Resume=function(){
if( this.MusicState()==1 ) this.music.play();
for( var i=0;i<32;++i ){
var chan=this.channels[i];
if( chan.state!=5 ) continue;
chan.state=2;
this.ResumeChannel( i );
}
}
gxtkAudio.prototype.LoadSample=function( path ){
var sample=new gxtkSample();
if( !sample.Load( BBHtml5Game.Html5Game().PathToUrl( path ) ) ) return null;
return sample;
}
gxtkAudio.prototype.PlaySample=function( buffer,channel,flags ){
if( buffer.state!=1 ) return;
var chan=this.channels[channel];
if( chan.state ){
chan.waSource.onended=null
try {
chan.waSource.stop( 0 );
chan.state = 0
} catch (err) {
}
}
chan.buffer=buffer;
chan.flags=flags;
chan.waSource=wa.createBufferSource();
chan.waSource.buffer=buffer.waBuffer;
chan.waSource.playbackRate.value=chan.rate;
chan.waSource.loop=(flags&1)!=0;
chan.waSource.connect( chan.waPanner );
chan.waSource.onended=function( e ){
chan.waSource=null;
chan.state=0;
}
chan.offset=0;
chan.startTime=wa.currentTime;
chan.waSource.start( 0 );
chan.state=1;
}
gxtkAudio.prototype.StopChannel=function( channel ){
var chan=this.channels[channel];
if( !chan.state ) return;
if( chan.state==1 ){
chan.waSource.onended=null;
try {
chan.waSource.stop( 0 );
} catch (err) {
}
chan.waSource=null;
}
chan.state=0;
}
gxtkAudio.prototype.PauseChannel=function( channel ){
var chan=this.channels[channel];
if( chan.state!=1 ) return;
chan.offset=(chan.offset+(wa.currentTime-chan.startTime)*chan.rate)%chan.buffer.waBuffer.duration;
chan.waSource.onended=null;
try {
chan.waSource.stop( 0 );
} catch (err) {
}
chan.waSource=null;
chan.state=2;
}
gxtkAudio.prototype.ResumeChannel=function( channel ){
var chan=this.channels[channel];
if( chan.state!=2 ) return;
chan.waSource=wa.createBufferSource();
chan.waSource.buffer=chan.buffer.waBuffer;
chan.waSource.playbackRate.value=chan.rate;
chan.waSource.loop=(chan.flags&1)!=0;
chan.waSource.connect( chan.waPanner );
chan.waSource.onended=function( e ){
chan.waSource=null;
chan.state=0;
}
chan.startTime=wa.currentTime;
chan.waSource.start( 0,chan.offset );
chan.state=1;
}
gxtkAudio.prototype.ChannelState=function( channel ){
return this.channels[channel].state & 3;
}
gxtkAudio.prototype.SetVolume=function( channel,volume ){
var chan=this.channels[channel];
chan.volume=volume;
chan.waGain.gain.value=volume;
}
gxtkAudio.prototype.SetPan=function( channel,pan ){
var chan=this.channels[channel];
chan.pan=pan;
var sin=Math.sin( pan*3.14159265359/2 );
var cos=Math.cos( pan*3.14159265359/2 );
chan.waPanner.setPosition( sin,0,-cos );
}
gxtkAudio.prototype.SetRate=function( channel,rate ){
var chan=this.channels[channel];
if( chan.state==1 ){
//update offset for pause/resume
var time=wa.currentTime;
chan.offset=(chan.offset+(time-chan.startTime)*chan.rate)%chan.buffer.waBuffer.duration;
chan.startTime=time;
}
chan.rate=rate;
if( chan.waSource ) chan.waSource.playbackRate.value=rate;
}
gxtkAudio.prototype.PlayMusic=function( path,flags ){
if( this.musicState ) this.music.pause();
this.music=new Audio( BBGame.Game().PathToUrl( path ) );
this.music.loop=(flags&1)!=0;
this.music.play();
this.musicState=1;
}
gxtkAudio.prototype.StopMusic=function(){
if( !this.musicState ) return;
this.music.pause();
this.music=null;
this.musicState=0;
}
gxtkAudio.prototype.PauseMusic=function(){
if( this.musicState!=1 ) return;
this.music.pause();
this.musicState=2;
}
gxtkAudio.prototype.ResumeMusic=function(){
if( this.musicState!=2 ) return;
this.music.play();
this.musicState=1;
}
gxtkAudio.prototype.MusicState=function(){
if( this.musicState==1 && this.music.ended && !this.music.loop ){
this.music=null;
this.musicState=0;
}
return this.musicState;
}
gxtkAudio.prototype.SetMusicVolume=function( volume ){
this.musicVolume=volume;
if( this.musicState ) this.music.volume=volume;
}
}else{
//print( "Using OldAudio!" );
// ***** gxtkChannel class *****
var gxtkChannel=function(){
this.sample=null;
this.audio=null;
this.volume=1;
this.pan=0;
this.rate=1;
this.flags=0;
this.state=0;
}
// ***** gxtkAudio class *****
var gxtkAudio=function(){
this.game=BBHtml5Game.Html5Game();
this.okay=typeof(Audio)!="undefined";
this.music=null;
this.channels=new Array(33);
for( var i=0;i<33;++i ){
this.channels[i]=new gxtkChannel();
if( !this.okay ) this.channels[i].state=-1;
}
}
gxtkAudio.prototype.Suspend=function(){
var i;
for( i=0;i<33;++i ){
var chan=this.channels[i];
if( chan.state==1 ){
if( chan.audio.ended && !chan.audio.loop ){
chan.state=0;
}else{
chan.audio.pause();
chan.state=3;
}
}
}
}
gxtkAudio.prototype.Resume=function(){
var i;
for( i=0;i<33;++i ){
var chan=this.channels[i];
if( chan.state==3 ){
chan.audio.play();
chan.state=1;
}
}
}
gxtkAudio.prototype.LoadSample=function( path ){
if( !this.okay ) return null;
var audio=new Audio( this.game.PathToUrl( path ) );
if( !audio ) return null;
return new gxtkSample( audio );
}
gxtkAudio.prototype.PlaySample=function( sample,channel,flags ){
if( !this.okay ) return;
var chan=this.channels[channel];
if( chan.state>0 ){
chan.audio.pause();
chan.state=0;
}
for( var i=0;i<33;++i ){
var chan2=this.channels[i];
if( chan2.state==1 && chan2.audio.ended && !chan2.audio.loop ) chan.state=0;
if( chan2.state==0 && chan2.sample ){
chan2.sample.FreeAudio( chan2.audio );
chan2.sample=null;
chan2.audio=null;
}
}
var audio=sample.AllocAudio();
if( !audio ) return;
audio.loop=(flags&1)!=0;
audio.volume=chan.volume;
audio.play();
chan.sample=sample;
chan.audio=audio;
chan.flags=flags;
chan.state=1;
}
gxtkAudio.prototype.StopChannel=function( channel ){
var chan=this.channels[channel];
if( chan.state>0 ){
chan.audio.pause();
chan.state=0;
}
}
gxtkAudio.prototype.PauseChannel=function( channel ){
var chan=this.channels[channel];
if( chan.state==1 ){
if( chan.audio.ended && !chan.audio.loop ){
chan.state=0;
}else{
chan.audio.pause();
chan.state=2;
}
}
}
gxtkAudio.prototype.ResumeChannel=function( channel ){
var chan=this.channels[channel];
if( chan.state==2 ){
chan.audio.play();
chan.state=1;
}
}
gxtkAudio.prototype.ChannelState=function( channel ){
var chan=this.channels[channel];
if( chan.state==1 && chan.audio.ended && !chan.audio.loop ) chan.state=0;
if( chan.state==3 ) return 1;
return chan.state;
}
gxtkAudio.prototype.SetVolume=function( channel,volume ){
var chan=this.channels[channel];
if( chan.state>0 ) chan.audio.volume=volume;
chan.volume=volume;
}
gxtkAudio.prototype.SetPan=function( channel,pan ){
var chan=this.channels[channel];
chan.pan=pan;
}
gxtkAudio.prototype.SetRate=function( channel,rate ){
var chan=this.channels[channel];
chan.rate=rate;
}
gxtkAudio.prototype.PlayMusic=function( path,flags ){
this.StopMusic();
this.music=this.LoadSample( path );
if( !this.music ) return;
this.PlaySample( this.music,32,flags );
}
gxtkAudio.prototype.StopMusic=function(){
this.StopChannel( 32 );
if( this.music ){
this.music.Discard();
this.music=null;
}
}
gxtkAudio.prototype.PauseMusic=function(){
this.PauseChannel( 32 );
}
gxtkAudio.prototype.ResumeMusic=function(){
this.ResumeChannel( 32 );
}
gxtkAudio.prototype.MusicState=function(){
return this.ChannelState( 32 );
}
gxtkAudio.prototype.SetMusicVolume=function( volume ){
this.SetVolume( 32,volume );
}
// ***** gxtkSample class *****
//function gxtkSample( audio ){
var gxtkSample=function( audio ){
this.audio=audio;
this.free=new Array();
this.insts=new Array();
}
gxtkSample.prototype.FreeAudio=function( audio ){
this.free.push( audio );
}
gxtkSample.prototype.AllocAudio=function(){
var audio;
while( this.free.length ){
audio=this.free.pop();
try{
audio.currentTime=0;
return audio;
}catch( ex ){
// print( "AUDIO ERROR1!" );
}
}
//Max out?
if( this.insts.length==8 ) return null;
audio=new Audio( this.audio.src );
//yucky loop handler for firefox!
//
audio.addEventListener( 'ended',function(){
if( this.loop ){
try{
this.currentTime=0;
this.play();
}catch( ex ){
// print( "AUDIO ERROR2!" );
}
}
},false );
this.insts.push( audio );
return audio;
}
gxtkSample.prototype.Discard=function(){
}
}