Skip navigation.

Web Applications Blog

Posts tagged with "opera"

Event Streaming to Web Browsers

, , , ...

One cool feature we added to Opera 9 is Server-Sent Events from the WHATWG Web Applications 1.0 specification. Using SSE you can push DOM events continously from your web server to the visitor's browser. This creates a lot of exciting opportunities for web application authors.

Traditionally, when building an Ajax application, the browser continually polls the server, sending requests to the server, asking to get data back, making new HTTP requests for every single poll, putting more strain on the server than needed.

The event streaming approach instead opens a persistent connection to the server, sending data to the client when new information is available, eliminating the need for continuous polling. This method for doing remoting offers a tremendous advantage, since the server no longer has to handle the overhead associated with clients asking for new data. Instead, the server simply sends back data every connected client when appropriate, thus reducing the load on the server, with the added advantage of offering instant feedback to the user.

Opera Web Chat

Opera Web Chat Screenshot To provide you with a starting point on how to build your own event streaming application, we have built Opera Web Chat. This is a web based chatroom offering some of the features from the built in to the Opera IRC client. Currently the chat only offers one single chatroom. (A screenshot is available here)

Keep in mind that it is an experimental service, which means it may not always be available for use.

How to use Server-Sent Events

To use Server-Sent Events in a web application, add an <event-source> element to the document, with a src attribute pointing to a event source URL. This URL should provide a persistent HTTP connection that sends a data stream containing the events. The connection must use the content type application/x-dom-event-stream.

It is possible to send events with any name, and specify the properties of the event object. Opera 9.01 only supports the data property of the event, so this is what we are going to use in these examples.

The server side event source writes the events whenever they occur, and sends them over HTTP to the client. This is a basic example of event data. This is more thoroughly explained in the specification.

Event: server-time
data: [time on the server]

Event: the-answer
data: 42

This will send two events to the browser, and it's possible to catch them as DOM events. The following JavaScript example listens for the "server-time" event, and alerts the content.

document.getElementsByTagName("event-source")[0]
        .addEventListener("server-time", eventHandler, false);

function eventHandler(event)
{
    // Alert time sent by the server
    alert(event.data); 
}

This is a very simple Python CGI example which sends a new event every 3 seconds. Every event is named "server-time", and sends an event with the data property set to the current time of the server in seconds.

Keep in mind that when a CGI script outputs data, there is no guarantee that it is sent immediately. There are often caching mechanisms and so on in place. For this reason it may be necessary to explicitly flush the output.

Here is the example code written in python.

#!/usr/bin/python
import sys
import time
print "Content-Type: application/x-dom-event-stream\n\n"

while True:
    print "Event: server-time"
    print "data: %f\n" % (time.time(),)
    sys.stdout.flush()
    time.sleep(3)

The same example written in PHP:

<?php
header("Content-Type: application/x-dom-event-stream");
while(true) {
    echo "Event: server-time\n";
    $time = time();
    echo "data: $time\n";
    echo "\n";
    flush();
    sleep(3);
}
?>

Opportunities

In addition to the chat application we made, there are lots of different applications that can be made with Server-Sent Events. For instance games or instant messaging clients, such as MSN Messenger, Jabber or AIM. You could also build stock and news tickers, status and log file monitors, or anyhing you can come up with.

What will you build?

Moving and resizing widgets

, , , ...

One of the last additions we made to widgets in the stages before releasing Opera 9.0 was giving widgets the ability to resize and move themselves.

The main use for resizing behavior is in those cases where you want to emulate UI widgets (yes, that other kind of widget) like maximize, restore, and regular resize and drag handlers.

interface Window {
  void resizeTo( in int w, in int h );
  void resizeBy( in int delta_w, in int delta_h );
  void moveTo( in int x, in int y );
  void moveBy( in int delta_x , in int delta_y );
};

window.resizeTo()

The window.resizeTo() method takes two positive integers as arguments: A width w and height h. These sizes are the desired dimensions of the widgets in pixels, and correspond to the width and height elements in a widget's configuration file. The method sets the window.outerWidth and window.outerHeight properties. Example:

// Resize the widget to 400x300 px
window.resizeTo( 400, 300 );

window.resizeBy()

The window.resizeBy method takes two integers as arguments: A delta_w that is the desired change in width, measured in pixels, and a delta_h that is the desired change in height, again measured in pixels. The width and height of the widget is then recalculated to be equivalent to the following pseudocode:

window.resizeTo( window.outerWidth + delta_x, window.outerHeight + delta_y );

In all cases of window resizing, the resizing happens from the lower-right corner of the widget window, while the upper-left corner remains static.

// Grow the widget by 100px horizontally
window.resizeBy( 100, 0 );

window.moveTo()

The window.moveTo method takes two integers as arguments: An x that is the desired x coordinate for the upper-left corner of the widget, and an y that is the desired y coordinate for the upper-left corner of the widget, with both values mentioned in pixels.

// Move the widget to the upper-left corner of the screen
window.moveTo( 0, 0 )

window.moveBy()

The window.resizeBy method takes two integers as arguments: A delta_x that is the desired change in x position, measured in pixels, and a delta_y that is the desired change in y position, again measured in pixels. Both values can be negative. Example:

// Move the 50px left and 50px down
window.moveBy( -50, 50 )

Closing note

If you change these settings for a widget, the settings are volatile, and the change will be lost if the widget is closed and then reopened. This means that if your widget relies on being a certain size, which may be different from the install-time default, you should make sure that you store the widget size in the persistent storage. The widget storage interface is compatible with Apple's interface, using the methods preferenceForKey and setPreferenceForKey.

3D Canvas

, , ,

At this year's XTech conference, Opera's CTO Håkon Wium Lie did a demo of a hardware accelerated 3d canvas context, as illustrated by the screenshots from a 3d snake implementation done by our own Mathieu 'p01' HENRI





The implementation of the 3d canvas has existed internally for over a year, and shows that the browser is going in a very exciting direction. Soon, we hope to enable people like Benjamin Joffe to make his nice 3d demo even nicer.

Note that we are not the only ones that have been working on 3d in the browser, Vladimir Vukićević also demonstrated similar technology in Firefox at XTech. Synchronisity.

(And, oh, 3d snake is quite fun :wink: )

Optimizing PNG Graphics for Device

, , , ...

One of the most recent image compression formats is PNG. The advanced features of PNG, such as high compression and 32-bit support makes it the format of choice for web applications. Opera supports PNG, both on desktop and device; we use this format for most of our graphics.

There are two things you should do to your graphics, when preparing them for device. The first is color reduction, the second is re-compressing. First color reduction.

Color reduction

Why would you want to reduce the amount of colors used? We want the maximum number of colors, right? Yeah! Unfortunately, most mobile devices do not support millions of colors; many of them support only a percentage of that. The key to making sure your images look good on device is reducing color depth, using dithering techniques. Corel’s Paint Shop Pro is the only graphics application I have tested which has a special “reduce to thousands-of-colors” feature.

If you don’t own a copy, you will have to posterize your images to the color depth of the target device. Then convert this image to indexed colors with an EXACT palette. Finally go back to your original 32 bits image and convert it to indexed colors using the PREVIOUS palette and the most appropriate dithering.

The important step is the dithering. Without that, on device, your images would look exactly as the posterized version.

Note that you might have to slice the image and repeat the process to obtain an EXACT palette.

The image might not look as nice on desktop as it used to, but it should at least look the same on desktop as on device. On to part two!

Image re-compression

Isn’t PNG compressed already? Of course it is, but don’t you think we can do better? In most cases we can. Most image editors add metadata in one form or another to PNG files. In most cases this is not needed, and chopping it away can save a lot of space. In one project my final zip file went from 300kb to measly 50kb by using these techniques.

To business. You might know about the PNG compressor "PNG Crush". It was the first I ever heard about, but it seems there are some others out there as well. The most famous are OptiPNG and PNGOut.

How do they compare? They're roughly the same. So which one you use is not important; the important thing is that you actually use one of these. Therefore; accessibility. Neil Turner has a very good article on adding PNG Crush to the right-click menu of PNG files (for Windows). No need to copy his blog post, go read it!

The problem with PNG Crush is that it can't replace the current file, meaning you will have to rename the result when you're done. That is why I prefer "PNGOut" instead, which can optimize-in place.

As a final mention for you Linux users, here is what our Virtuelvis uses:

(this is using first pngcrush, and then optipng)
Compress every png into directory 'out'
$ pngcrush -rem alla -brute -fix -d out *.png
recompress them
$ cd out 
$ optipng -zc1-9 -zm1-9 -zs0-3 -f0-5 *.png 
$ mv -f *.png ../

(mv -f can be replaced with your favorite windows file moving flavor)

After this optimizing session your PNG images should look better and be leaner than ever before! :up:

The 'Opera-2dgame' Canvas Context

, , , ...

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:

  1. If there is alpha information in the pixel, the value returned is on the form rgba( r, g, b, a )
  2. 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.