samedi 11 avril 2015

sampling an image a tile at a time using canvas, getImageData and a Web Worker

I am attempting to build a simple HTML5 canvas based image processor that takes an image and generates a tiled version of it with each tile being the average color of the underlying image area.


This is easy enough to do outside the context of a Web Worker but I'd like to use a worker so as not to block the ui processing thread. The Uint8ClampedArray form the data takes is giving me a headache with regards to how to process it tile by tile.


Below is a plunk demonstrating what I've done so far and how it's not working.


http://ift.tt/1GX0zjK


The relevant code is in worker.js


Here it is:



onmessage = function (e) {
var i,
j = 0,
k = 0,
data = e.data,
imageData = data.imageData,
tileWidth = Math.floor(data.tileWidth),
tileHeight = Math.floor(data.tileHeight),
width = imageData.width,
height = imageData.height,
tile = [],
len = imageData.data.length,
offset,
processedData = [],
tempData = [],
timesLooped = 0,
tileIncremented = 1;

function sampleTileData(tileData) {
var blockSize = 20, // only visit every x pixels
rgb = {r:0,g:0,b:0},
i = -4,
count = 0,
length = tileData.length;

while ((i += blockSize * 4) < length) {
if (tileData[i].r !== 0 && tileData[i].g !== 0 && tileData[i].b !== 0) {
++count;
rgb.r += tileData[i].r;
rgb.g += tileData[i].g;
rgb.b += tileData[i].b;
}
}

// ~~ used to floor values
rgb.r = ~~(rgb.r/count);
rgb.g = ~~(rgb.g/count);
rgb.b = ~~(rgb.b/count);

processedData.push(rgb);
}

top:
for (; j <= len; j += (width * 4) - (tileWidth * 4), timesLooped++) {

if (k === (tileWidth * 4) * tileHeight) {
k = 0;
offset = timesLooped - 1 < tileHeight ? 4 : 0;
j = ((tileWidth * 4) * tileIncremented) - offset;
timesLooped = 0;
tileIncremented++;
sampleTileData(tempData);
tempData = [];
//console.log('continue "top" loop for new tile');
continue top;
}

for (i = 0; i < tileWidth * 4; i++) {
k++;
tempData.push({r: imageData.data[j+i], g: imageData.data[j+i+1], b: imageData.data[j+i+2], a: imageData.data[j+i+3]});
}

//console.log('continue "top" loop for new row per tile');

}

postMessage(processedData);
};


I'm sure there's a better way of accomplishing what I'm trying to do starting at the labeled for loop. So any alternative methods or suggestions would be much appreciated.


Aucun commentaire:

Enregistrer un commentaire