// 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(){ } }