Since we added support for the canvas element in Opera 9 tp1, we have also supported a custom context on the canvas, named "opera-2dgame". As suggested by the name, this context has functionality specifically geared toward game authoring. Let's have a quick look at what we can do with it. However, please remember that this functionality is still experimental, and that the interfaces may change at any time before a final version of Opera is launched.
Using the 'opera-2dgame' context
To use the custom context, you simply call the 'getContext' method on the canvas, with the argument 'opera-2dgame', much the same as you would do if you were working with the regular canvas.:
// get the first canvas element on the page
var canvas = document.getElementsByTagName('canvas')[0];
// get the game context:
var gctx = canvas.getContext('opera-2dgame');
Pixel-by-pixel manipulation using getPixel() and setPixel()
From reading the canvas developers mailing list it seems that one of the more requested features is the ability to read and modify pixels on an individual basis. The opera-2dgame context has just that.
The getPixel() method
If you want to read a pixel value directly, this is achieved by using the getPixel method, which takes two integer arguments, representing the coordinate for which one wants to get the color value:
// Read the pixel at 0,0
var myPixel = gctx.getPixel(0,0);
The value returned by getPixel is a string, and is returned in one of two forms:
- If there is alpha information in the pixel, the value returned is on the form
rgba( r, g, b, a )
- If the pixel is fully opaque, the value is on the form
#rrggbb
getPixel() and security
For the security-concious out there, Opera enforces a same-origin policy on the canvas: Trying to call the getPixel method on a canvas element that contains data from a third-party server (that has foreign content) will raise a SECURITY_VIOLATION error. For what it's worth, the same restriction is enforced on the toDataURI method on the HTMLCanvas interface.
A canvas element is considered to have third-party content if drawImage has been used with an HTMLImageElement as an argument, and this image is from a third-party server , or if drawImage has used with a HTMLCanvasElement as argument, and this HTMLCanvasElement has previously been determined to contain third-party content.
setPixel()
While getPixel on its own has some meaning, the real power is revealed when we add the setPixel() method. The method takes three arguments: The x and y coordinates, represented as integers, and a string representing the color, represented as a CSS color.
// set the pixel at 32,24 to the value 'rgba(255,127,0,0.5)'
gctx.setPixel(32,24,'rgba(255,127,0,0.5)');
// set the pixel at 32,25 to the value '#f33'
gctx.setPixel(32,24,'#f33');
// set the pixel at 32,26 to the value 'red'
gctx.setPixel(32,24,'red');
Examples
Here are a few examples of using the getPixel and setPixel methods to perform image manipulation in much the same ways filters in popular graphics application works
Collision detection
Indeed. The opera-2dgame context does collision detection as well, and makes it really simple for you: The checkCollision method on the 2d-game context takes an x and y value as arguments, and when called, will check whether the given coordinate is within the currently open path on the '2d' canvas context. The checkCollision() method returns 1 if the point is within the open path, and 0 otherwise. The checkCollision method does not take any translatation on the regular '2d' context into consideration; the x and y values are relative to the top left corner of the canvas.
// draw a rectangle from 0,0 to 100,100
// ctx and gctx are previously created references to the
// 2d context and the opera-2dgame contexts on a canvas element.
ctx.beginPath();
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.lineTo(120,0);
ctx.lineTo(60,90);
ctx.lineTo(0,0);
ctx.fill();
// Output '1', since the point 60,25 is inside the open path
alert( gctx.checkCollision(60,25) );
// Output '0', since the point 150,150 is outside the open path
alert( gctx.checkCollision(150,150) );
Examples
We've prepared two small checkCollision() examples for you to look at:
- Click the blue triangle - when clicking the blue triangle, you should be told whether you've clicked on the triangle or somewhere else.
- Hit the ball - A little "How to get RSI ASAP"-game where the aim is to click the ball.
Optimizing canvas performance using locking
Finally, if you write high-performance canvas applications, such as games, you might want more control over the canvas redraw. The opera-2dgame context provides you with ultimate control using two methods:
lockCanvasUpdates( bool Lock) takes a boolean value as argument. When this function is invoked with the argument , updates on the 2d canvas context are locked. When a canvas is locked for updates, you can perform operations on it as you normally would do, but the updates will not update the display of the canvas. The advantage of this is that if you need to do multiple, possibly complex operations on the canvas, such as display a frame in a game with all heros, monsters, backgrounds and fluff, you won't waste any time rendering this to screen.
When you are ready to render your frame and update the canvas, you first call the lockCanvasUpdates(false) to allow updating the canvas. Next, you call the updateCanvas() function to force the canvas to repaint, and you relock the canvas again afterwards, using lockCanvasUpdates(true) as illustrated below
// We start by simply locking the canvas:
gctx.lockCanvasUpdates(true);
function renderFrame(){
/*
First, lots of code rendering a frame
in a complex scene
*/
// When we've rendered everything, we refresh the canvas:
gctx.lockCanvasUpdates(false);
gctx.updateCanvas();
gctx.lockCanvasUpdates(true);
}
These additional methods should provide authors with the means neccessary to write graphics applications and high-performance games, in widgets or on web sites. Use your creativity.