Category Archives: Javascript

A good piece of geek stuff – client side image processing with Gears

In the latest downtime we released a beta version of a new photo uploader. Since we removed the Java based uploader some time ago, we’ve been dreaming of offering our users an uploader that’s able to do the same as the applet did, without requiring Java and of course with less conflicts on the different clients.

So the requirements for the uploader were :

  • uploading (of course :D)
  • scaling
  • rotating
  • compression

We implemented a Flash based uploader that could do a multiupload but neither scale nor compress the pictures before upload. So there is the problem, no direct access to the local files on the client.

On the Google Developer Days in Munich last year a collegue and I heard of the possibilities Gears offers and we were quite suprised how far it pushes the abilities of the client. Dreaming of all the geeky things I could do with Gears I also hoped to be able to solve that fileaccess problem. But unfortunately Gears did not offer the announced canvas API and also the desktop API hadn’t implemented the needed interfaces so far.

Enough story let’s look at the code …


var desktop = google.gears.factory.create('beta.desktop');
var localServer = google.gears.factory.create('beta.localserver');
var store = localServer.createStore('picturesTemp');

November 24, 2008: Google released the 0.5 version of Gears and there it was, the local server offered captureBlob() .

We created the needed Gears features, desktop for the fileaccess, localserver to store the files on the client.


$('#openFile').bind('click', function(){
gearsComponents_.desktop.openFiles(openFilesCallback_, {
filter: ['image/jpeg', 'image/png', 'image/bmp', 'image/gif']
});
});

Here we’re binding the filepicker dialog to some button, providing a filter to delimit the shown files to supported types.

var url = yourdomain;
var openFilesCallback_ = function(files){
var file = files_.shift();
gearsComponents_.pictureStorage.captureBlob(file.blob, url, "image/" + file.name.substring(file.name.lastIndexOf('.') + 1));
}

Capturing the blob to the local server like shown above solves two problems. Now we can access the files and import them into the canvas element, because it can be delivered via the same domain as the main page so there is no crossdomain security problem.

var canvas = $('<canvas>').get(0);
var context = canvas.getContext('2d');
canvas.width = canvasOriginal.width * fac;
canvas.height = canvasOriginal.height * fac;
context.scale(fac, fac);
context.drawImage(canvasOriginal, 0, 0);

In this example the canvasOriginal is the canvas/image from the localserver. We can now rotate, scale etc. the picture if the browser supports these actions on the canvas element.

You can also apply filters on the images by extracting the picture information as pixelarray modifying it and pushing it back:


// get the imagedata
var imgdata = canvasOriginalContext.getImageData(0, 0, canvasOriginal.width, canvasOriginal.height);
// do something with the pixel data
// push the imagedata back
context.putImageData(processedData, 0, 0);

As Javascript blocks while executing code this will for sure cause serious GUI problems, so let’s use a Gears worker to solve that problem.
First we have to create a workerpool:


var workerPool = (function(){
if (window.google) {
return google.gears.factory.create('beta.workerpool');
}
}());

The workerpool needs an onmessage handler which will be called on receiving messages by a childworker:


workerPool.onmessage = function(a, b, message){
//message will contain our processed pixelarray
};

The workers have no access to the dom so we only push the pixelarray in and get it back in the onmessage handler


// accessing the predefined workerpool
var script = 'var wp = google.gears.workerPool;' +
'wp.onmessage = function(a, b, message) {' +
'var data = message.body[0];' +
'//Process the data here' +
'//send the data back to the worker pool'
'wp.sendMessage(reply, message.sender);' +
// create a childworker by script (could also be created by url pointing to a script)
var childWorkerId = workerPool.createWorker(script);

We currently only use workers to apply a filter to the pictures.

Now we can do anything we want with the pictures but what about sending them to the server.

The solution to that problem is the toDataURL() method of the canvas element which exports the pictures to a “data URL”, which we can send to the server by a simple xhr as post.

According to the HTML5 spec toDataURL() should support several parameters first the data type  – e.g. toDataURL(‘image/jpeg’) – the second parameter should be the compression rate as float value.

The compression rate does not seem to be supported by Firefox so far, so you should leave it blank then Firefox uses it’s default value.

The released beta version of the Gears uploader only supports Firefox because of the missing canvas (especially picture export) support in most of the browsers. Another drawback is that Gears is not available on all OS/browser combinations.

I hope that Safari will soon support (didn’t look at Safari 4 so far) the needed Canvas export methods, so that the uploader will work with Mac/Safari.

The CanvasAPI of Gears is already available in the sources but it’s not sure if and when it will be released. So perhaps some day there will be also a canvas element available in IE via Gears.

Have fun with the Gears uploader!

UTF-8 Byte Order Marks (BOMs) automatisch entfernen

Insbesondere in heterogenen Netzwerken in einer Entwicklungsabteilung mit unterschiedlichster Softwarenutzung, kann es schon mal vorkommen, dass jemand einen Editor verwendet, der auf den falschen Zeichensatz eingestellt ist.

Ehe man sich versieht, wird aus dem ehemals unsichtbaren Byte-Order-Mark-Steuerzeichen, ein sichtbares Zeichen, das besonders am Anfang von .php  und JS-Dateien durchaus für Ungemach sorgen kann, wenn es vom Webbrowser falsch interpretiert wird.  Entweder nur hässlich sichtbar oder der Code funktioniert nicht mehr.

Bei uns prüft ein pre-commit-hook jeden SVN-Commit auf BOMs. Und für alle Homepagebastler die sich auch schon das eine oder andere mal darüber geärgert haben – mit der simplen – auf einer Shell eines Linux-Rechners ausgeführten Zeile wird man die Dinger komplett los. Wenn man SVN benutzt, kann man die Änderungen danach auch direkt committen, da die SVN-Steuerdateien ausgenommen sind.

find .  -name .svn -prune -o -type f -print  | while read file;do [ "`head -c3 -- "$file"`" == $'\xef\xbb\xbf' ] && sed -i -s -e '1s/^\xef\xbb\xbf//' $files $file && echo "found BOM in: $file";done

Alles neu macht der Mai, ähh Februar!

greasemonkeyFrontend Refactoring macht Userstylesheets und Greasemonkey Scripts unbrauchbar.

Um zukünftig flexibler und effizienter arbeiten zu können, haben wir die Plattformen schülerVZ, studiVZ und meinVZ einem Frontend Refactoring unterzogen. Optisch gibt es nur wenige Änderungen, unter der Haube steckt aber eine neue Konstruktion. Im Rahmen des Refactorings werden wir jetzt nach und nach auch alle Seiten anpassen und optimieren.

Mitglieder unserer Plattformen, die die Oberfläche via Userstylesheets oder Greasemonkey Scripts anpassen, werden nach dem nächsten Release (Donnerstag, 5. Februar 2009) diese Skripte nicht mehr verwenden können. Wer seine Skripte bereits vor dem nächsten Release prüfen und anpassen möchte, kann sich hier eine Zip-Datei downloaden. In der Zip-Datei sind wichtige Seiten abgespeichert, sodass man seine Skripte entsprechend anpassen kann.

Wir hoffen, das hilft etwas.

Download Zip-Datei

Schöne Grüße aus dem Maschinenraum.