glitch-images/scripts/lib/glitch-canvas-with-worker.js
2015-12-23 23:09:12 +01:00

292 lines
7.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! glitch-canvas by snorpey, MIT License
(function(window, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof exports === "object") {
module.exports = factory();
} else {
window.glitch = factory();
}
})(this, function() {
function Glitcher ( webworkerPath ) {
if ( ! ( this instanceof Glitcher ) ) {
return new Glitcher( webworkerPath );
}
var self = this
var canvas1El = document.createElement( 'canvas' );
var canvas2El = document.createElement( 'canvas' );
var ctx1 = canvas1El.getContext( '2d' );
var ctx2 = canvas2El.getContext( '2d' );
var img = new Image();
var base64;
var img;
var newImageData;
var i;
var len;
var params;
var worker;
var base64Chars;
var base64Map;
var reversedBase64Map;
var byteArrayImageData;
var jpgHeaderLength;
function init () {
if ( webworkerPath && 'Worker' in window ) {
worker = new Worker( webworkerPath );
worker.addEventListener( 'message', workerResponded, false );
} else {
base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
base64Map = base64Chars.split( '' );
reversedBase64Map = { };
base64Map.forEach( function ( val, key ) {
reversedBase64Map[val] = key;
} );
}
return self;
}
function glitch ( imageData, parameters, callback ) {
if (
isValidImageData( imageData ) &&
isType( parameters, 'parameters', 'object' ) &&
isType( callback, 'callback', 'function' )
) {
resizeCanvas( canvas1El, imageData );
resizeCanvas( canvas2El, imageData );
params = getNormalizedParameters( parameters );
base64 = getBase64FromImageData( imageData, params.quality );
img.onload = function () {
ctx1.drawImage( img, 0, 0 );
newImageData = ctx1.getImageData( 0, 0, imageData.width, imageData.height );
callback ( newImageData );
img.onload = null;
};
if ( worker ) {
worker.postMessage( { glitch: { base64: base64, parameters: params } } );
} else {
byteArrayImageData = base64ToByteArray( base64 );
jpgHeaderLength = getJpegHeaderSize( byteArrayImageData );
for ( i = 0, len = params.iterations; i < len; i++ ) {
glitchJpegBytes( byteArrayImageData, jpgHeaderLength, params.seed, params.amount, i, params.iterations );
}
img.src = byteArrayToBase64( byteArrayImageData );
}
}
return self;
}
function workerResponded ( event ) {
if ( event && event.data && event.data.glitched ) {
glitched( event.data.glitched );
} else {
window.console && console.log( 'glitchworker response', event.data );
}
}
function workerFailed ( err ) {
window.console && console.log( err.message || err );
}
function glitched ( newBase64ImageData ) {
img.src = newBase64ImageData;
}
function resizeCanvas ( canvas, size ) {
if ( canvas.width !== size.width ) {
canvas.width = size.width;
}
if ( canvas.height !== size.height ) {
canvas.height = size.height;
}
}
function getNormalizedParameters ( parameters ) {
return {
seed: ( parameters.seed || 0 ) / 100,
quality: ( parameters.quality || 0 ) / 100,
amount: ( parameters.amount || 0 ) / 100,
iterations: parameters.iterations || 0
};
}
function getBase64FromImageData ( imageData, quality ) {
var q = typeof quality === 'number' && quality < 1 && quality > 0 ? quality : .1;
ctx2.putImageData( imageData, 0, 0 );
var base64 = canvas2El.toDataURL( 'image/jpeg', q );
switch ( base64.length % 4 ) {
case 3:
base64 += '=';
break;
case 2:
base64 += '==';
break;
case 1:
base64 += '===';
break;
}
return base64;
}
function isValidImageData ( imageData ) {
if (
isType( imageData, 'imageData', 'object' ) &&
isType( imageData.width, 'imageData.width', 'number' ) &&
isType( imageData.height, 'imageData.height', 'number' ) &&
isType( imageData.data, 'imageData.data', 'object' ) &&
isType( imageData.data.length, 'imageData.data.length', 'number' ) &&
checkNumber( imageData.data.length, 'imageData.data.length', isPositive, '> 0' )
) {
return true;
} else {
return false;
}
}
function isType ( it, name, expectedType ) {
if ( typeof it === expectedType ) {
return true;
} else {
error( it, 'typeof ' + name, '"' + expectedType + '"', '"' + typeof it + '"' );
return false;
}
}
function checkNumber ( it, name, condition, conditionName ) {
if ( condition( it ) === true) {
return true;
} else {
error( it, name, conditionName, 'not' );
}
}
function isPositive ( nr ) {
return nr > 0;
}
function error ( it, name, expected, result ) {
throw new Error( 'glitch(): Expected ' + name + ' to be ' + expected + ', but it was ' + result + '.' );
}
// all functions beyond this point are only used if no webworker is available
function glitchJpegBytes ( byteArray, jpgHeaderLength, seed, amount, i, len ) {
var maxIndex = byteArray.length - jpgHeaderLength - 4;
var pxMin = parseInt( maxIndex / len * i, 10 );
var pxMax = parseInt( maxIndex / len * ( i + 1 ), 10 );
var delta = pxMax - pxMin;
var pxIndex = parseInt( pxMin + delta * seed, 10 );
if ( pxIndex > maxIndex ) {
pxIndex = maxIndex;
}
var index = Math.floor( jpgHeaderLength + pxIndex );
byteArray[index] = Math.floor( amount * 256 );
}
function base64ToByteArray ( str ) {
var result = [ ];
var digitNum;
var cur;
var prev;
for ( i = 23, len = str.length; i < len; i++ ) {
cur = reversedBase64Map[str.charAt( i )];
digitNum = ( i - 23 ) % 4;
switch ( digitNum ) {
case 1:
result.push( prev << 2 | cur >> 4 );
break;
case 2:
result.push( ( prev & 15 ) << 4 | cur >> 2 );
break;
case 3:
result.push( ( prev & 3 ) << 6 | cur );
break;
}
prev = cur;
}
return result;
}
function byteArrayToBase64 ( arr ) {
var result = [ 'data:image/jpeg;base64,' ];
var byteNum;
var cur;
var prev;
for ( i = 0, len = arr.length; i < len; i++ ) {
cur = arr[i];
byteNum = i % 3;
switch (byteNum) {
case 0:
result.push( base64Map[cur >> 2] );
break;
case 1:
result.push( base64Map[( prev & 3 ) << 4 | cur >> 4] );
break;
case 2:
result.push( base64Map[( prev & 15 ) << 2 | cur >> 6] );
result.push( base64Map[cur & 63] );
break;
}
prev = cur;
}
if ( byteNum === 0 ) {
result.push( base64Map[( prev & 3 ) << 4] );
result.push( '==' );
} else if ( byteNum === 1 ) {
result.push( base64Map[( prev & 15 ) << 2] );
result.push( '=' );
}
return result.join( '' );
}
function getJpegHeaderSize ( data ) {
var result = 417;
for ( i = 0, len = data.length; i < len; i++ ) {
if ( data[i] === 255 && data[i + 1] === 218 ) {
result = i + 2;
break;
}
}
return result;
}
self.glitch = glitch;
self.init = init;
}
return Glitcher;
});