From 3c403604bd8ee5a4d503ab032b10b238b6f9e99e Mon Sep 17 00:00:00 2001 From: Georg Fischer Date: Wed, 11 Sep 2013 21:37:17 +0200 Subject: [PATCH] rename the aux folder to util, because windows. fixes issue #3 --- scripts/src/glitch.js | 192 +++++++++++++++++++++++++++++++++++ scripts/src/main.js | 2 +- scripts/src/process.js | 2 +- scripts/util/canvas.js | 38 +++++++ scripts/util/feature-test.js | 48 +++++++++ 5 files changed, 280 insertions(+), 2 deletions(-) create mode 100644 scripts/src/glitch.js create mode 100644 scripts/util/canvas.js create mode 100644 scripts/util/feature-test.js diff --git a/scripts/src/glitch.js b/scripts/src/glitch.js new file mode 100644 index 0000000..ade55c0 --- /dev/null +++ b/scripts/src/glitch.js @@ -0,0 +1,192 @@ +/*global define*/ +define( + [ 'util/canvas' ], + function( canvas_helper ) + { + var canvas = document.createElement( 'canvas' ); + var ctx = canvas.getContext( '2d' ); + + var tmp_canvas = document.createElement( 'canvas' ); + var tmp_ctx = tmp_canvas.getContext( '2d' ); + + var canvas_size = { width: 10, height: 10 }; + + var base64_chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + var base64_map = base64_chars.split( '' ); + var reverse_base64_map = { }; + + var iterations; + var quality; + var seed; + var amount; + var base64; + var byte_array; + var jpg_header_length; + var img; + var new_image_data; + + var i; + var len; + + base64_map.forEach( function( val, key ) { reverse_base64_map[val] = key; } ); + + function getGlitchedImageSrc( image_data, input, callback ) + { + seed = input.seed / 100; + quality = input.quality / 100; + amount = input.amount / 100; + iterations = input.iterations; + + canvas_helper.resize( canvas, image_data ); + canvas_helper.resize( tmp_canvas, image_data ); + + base64 = getBase64FromImageData( image_data, quality ); + byte_array = base64ToByteArray( base64 ); + jpg_header_length = getJpegHeaderSize( byte_array ); + + for ( i = 0; i < iterations; i++ ) + { + glitchJpegBytes( byte_array, jpg_header_length, seed, amount, i, iterations ); + } + + img = new Image(); + img.onload = function() { + ctx.drawImage( img, 0, 0 ); + new_image_data = ctx.getImageData( 0, 0, image_data.width, image_data.height ); + callback( new_image_data ); + }; + + img.src = byteArrayToBase64( byte_array ); + } + + function glitchJpegBytes( byte_array, jpg_header_length, seed, amount, i, len ) + { + var max_index = byte_array.length - jpg_header_length - 4; + var px_min = parseInt( max_index / len * i, 10 ); + var px_max = parseInt( max_index / len * ( i + 1 ), 10 ); + + var delta = px_max - px_min; + var px_i = parseInt( px_min + delta * seed, 10 ); + + if ( px_i > max_index ) + { + px_i = max_index; + } + + var index = Math.floor( jpg_header_length + px_i ); + + byte_array[index] = Math.floor( amount * 256 ); + } + + function getBase64FromImageData( image_data, quality ) + { + var q = typeof quality === 'number' && quality < 1 && quality > 0 ? quality : 0.1; + tmp_ctx.putImageData( image_data, 0, 0 ); + return tmp_canvas.toDataURL( 'image/jpeg', q ); + } + + function getJpegHeaderSize( data ) + { + var result = 417; + + for ( var i = 0, l = data.length; i < l; i++ ) + { + if ( data[i] === 0xFF && data[i + 1] === 0xDA ) + { + result = i + 2; + break; + } + } + + return result; + } + + // https://github.com/mutaphysis/smackmyglitchupjs/blob/master/glitch.html + // base64 is 2^6, byte is 2^8, every 4 base64 values create three bytes + function base64ToByteArray( str ) + { + var result = [ ]; + var digit_num; + var cur; + var prev; + + for ( var i = 23, l = str.length; i < l; i++ ) + { + cur = reverse_base64_map[ str.charAt( i ) ]; + digit_num = ( i - 23 ) % 4; + + switch ( digit_num ) + { + // case 0: first digit - do nothing, not enough info to work with + case 1: // second digit + result.push( prev << 2 | cur >> 4 ); + break; + case 2: // third digit + result.push( ( prev & 0x0f ) << 4 | cur >> 2 ); + break; + case 3: // fourth digit + result.push( ( prev & 3 ) << 6 | cur ); + break; + } + + prev = cur; + } + + return result; + } + + function byteArrayToBase64( arr ) + { + var result = [ 'data:image/jpeg;base64,' ]; + var byte_num; + var cur; + var prev; + var i; + + for ( var i = 0, l = arr.length; i < l; i++ ) + { + cur = arr[i]; + byte_num = i % 3; + + switch ( byte_num ) + { + case 0: // first byte + result.push( base64_map[ cur >> 2 ] ); + break; + case 1: // second byte + result.push( base64_map[( prev & 3 ) << 4 | ( cur >> 4 )] ); + break; + case 2: // third byte + result.push( base64_map[( prev & 0x0f ) << 2 | ( cur >> 6 )] ); + result.push( base64_map[cur & 0x3f] ); + break; + } + + prev = cur; + } + + if ( byte_num === 0 ) + { + result.push( base64_map[( prev & 3 ) << 4] ); + result.push( '==' ); + } + + else if ( byte_num === 1 ) + { + result.push( base64_map[( prev & 0x0f ) << 2] ); + result.push( '=' ); + } + + return result.join( '' ); + } + + function getImageDataCopy( image_data ) + { + var copy = tmp_ctx.createImageData( image_data.width, image_data.height ); + copy.data.set( image_data.data ); + return copy; + } + + return getGlitchedImageSrc; + } +); \ No newline at end of file diff --git a/scripts/src/main.js b/scripts/src/main.js index eea64c7..129f0af 100644 --- a/scripts/src/main.js +++ b/scripts/src/main.js @@ -20,7 +20,7 @@ require( 'src/controls', 'src/export-png', 'src/save-button', - 'aux/feature-test', + 'util/feature-test', 'lib/signals-1.0.0', 'lib/html5slider' ], diff --git a/scripts/src/process.js b/scripts/src/process.js index e2a96b2..26e26d2 100644 --- a/scripts/src/process.js +++ b/scripts/src/process.js @@ -1,6 +1,6 @@ /*global define, requestAnimationFrame*/ define( - [ 'aux/glitch', 'aux/canvas', 'lib/raf' ], + [ 'src/glitch', 'util/canvas', 'lib/raf' ], function( glitch, canvas_helper ) { var tmp_canvas = document.createElement( 'canvas' ); diff --git a/scripts/util/canvas.js b/scripts/util/canvas.js new file mode 100644 index 0000000..f7fe2dd --- /dev/null +++ b/scripts/util/canvas.js @@ -0,0 +1,38 @@ +/*global define*/ +define( + function() + { + var update = false; + + function resize( canvas, size ) + { + + if ( canvas.width !== size.width ) + { + canvas.width = size.width; + update = true; + } + + if ( canvas.height !== size.height ) + { + canvas.height = size.height; + update = true; + } + + if ( update ) + { + canvas.width = size.width; + canvas.height = size.height; + } + + update = false; + } + + function clear( canvas, ctx ) + { + ctx.clearRect( ctx, 0, 0, canvas.width, canvas.height ); + } + + return { resize: resize, clear: clear }; + } +); \ No newline at end of file diff --git a/scripts/util/feature-test.js b/scripts/util/feature-test.js new file mode 100644 index 0000000..f4beb28 --- /dev/null +++ b/scripts/util/feature-test.js @@ -0,0 +1,48 @@ +/*global define*/ +define( + function() + { + var tests = { + 'canvas': { required: true, test: function(){ return !! document.createElement('canvas').getContext; } }, + 'query-selector-all': { required: false, test: function(){ return !! document.querySelectorAll; } }, + 'drag-drop': { required: false, test: function(){ return 'draggable' in document.createElement('span'); } }, + 'file-api': { required: false, test: function(){ return typeof FileReader !== 'undefined'; } } + }; + + function test( success, error ) + { + var required_supported = true; + var results = { }; + var required_features_missing = [ ]; + + for ( var key in tests ) + { + var result = tests[key].test(); + + if ( ! result ) + { + if ( tests[key].required ) + { + required_supported = false; + + required_features_missing.push( key ); + } + } + + results[key] = result; + } + + if ( required_supported ) + { + success( results ); + } + + else + { + error( required_features_missing, results ); + } + } + + return test; + } +); \ No newline at end of file