Last active
August 29, 2015 14:10
-
-
Save dlwr/8f616fc605ff6b99640e to your computer and use it in GitHub Desktop.
ウェブページをキャプチャーしてグリッチして投稿するtaberareloo拡張パッチ
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==Taberareloo== | |
// { | |
// "name" : "Glitch Selected Region" | |
// , "description" : "Glitch Selected Region" | |
// , "include" : ["background", "content"] | |
// , "match" : ["*://*/*"] | |
// , "version" : "1.0.2" | |
// , "downloadURL" : "https://gist.githubusercontent.com/dlwr/8f616fc605ff6b99640e/raw/extractor.photo.capture.glitch.tbrl.js" | |
// } | |
// ==/Taberareloo== | |
// c.f. glitch canvas by snorpey | |
// https://github.com/snorpey/glitch-canvas | |
(function() { | |
if (inContext('background')) { | |
Menus._register({ | |
title: 'Photo - Capture Glitch', | |
contexts: ['all'], | |
onclick: function(info, tab) { | |
chrome.tabs.sendMessage(tab.id, { | |
request: 'contextMenusGlitchPhoto', | |
content: info | |
}); | |
} | |
}, null, 'Photo - Capture', true); | |
Menus.create(); | |
return; | |
} | |
TBRL.setRequestHandler('contextMenusGlitchPhoto', function(req, sender, func) { | |
var ctx = TBRL.createContext(TBRL.getContextMenuTarget()); | |
ctx.contextMenu = true; | |
var ext = Extractors['Photo - Capture Glitch']; | |
TBRL.share(ctx, ext, true); | |
}); | |
Extractors.register({ | |
name: 'Photo - Capture Glitch', | |
ICON: chrome.extension.getURL('skin/') + 'photo.png', | |
TARGET_BACKGROUND: '#888', | |
check: function() { | |
return true; | |
}, | |
extract: function(ctx) { | |
var self = this; | |
var target = ctx.target; | |
var win = ctx.window; | |
return defer().then(function() { | |
return self.selectRegion(ctx).then(function (region) { | |
return self.capture(win, region.position, region.dimensions).then(function (src) { | |
return self.glitch(src); | |
}); | |
}); | |
}).then(function (file) { | |
return { | |
type: 'photo', | |
item: ctx.title, | |
fileEntry: file | |
}; | |
}); | |
}, | |
glitch: function(src) { | |
return new Promise(function(resolve) { | |
var params = { | |
amount: Math.random(), | |
seed: Math.random(), | |
quality: Math.random(), | |
iterations: Math.floor(Math.random() * 100) | |
}; | |
var BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); | |
var REVERESED_BASE4_MAP = function() { | |
var res = {}; | |
BASE64_MAP.forEach(function(val, key) {res[val] = key;}); | |
return res; | |
}(); | |
var base64 = src; | |
var byteArray = function(str) { | |
var result = []; | |
var digitNum; | |
var cur; | |
var prev; | |
for (var i = 23, len = str.length; i < len; i++) { | |
cur = REVERESED_BASE4_MAP[str.charAt(i)]; | |
digitNum = (i - 23) % 4; | |
switch(digitNum) { | |
case 1: | |
result.push(prev << 2 | cur >> 4); | |
break; | |
case 2: | |
result.push((prev & 0x0f) << 4 | cur >> 2); | |
break; | |
case 3: | |
result.push((prev & 3) << 6 | cur); | |
break; | |
} | |
prev = cur; | |
} | |
return result; | |
}(base64); | |
var jpgHeaderLength = function(data) { | |
var result = 417; | |
for (var i = 0, len = data.length; i < len; i++) { | |
if (data[i] === 0xFF && data[i + 1] === 0xDA) { | |
result = i + 2; | |
break; | |
} | |
} | |
return result; | |
}(byteArray); | |
for (var i = 0, len = params.iterations; i < len; i++) { | |
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 pxI = parseInt(pxMin + delta * params.seed, 10); | |
if (pxI > maxIndex) { | |
pxI = maxIndex; | |
} | |
var index = Math.floor(jpgHeaderLength + pxI); | |
byteArray[index] = Math.floor(params.amout * 256); | |
} | |
var glitchData = function(arr) { | |
var result = ['data:image/jpeg;base64,']; | |
var byteNum; | |
var cur; | |
var prev; | |
for (var i = 0, len = arr.length; i < len; i++) { | |
cur = arr[i]; | |
byteNum = i % 3; | |
switch (byteNum) { | |
case 0: | |
result.push(BASE64_MAP[cur >> 2]); | |
break; | |
case 1: | |
result.push(BASE64_MAP[(prev & 3) << 4 | (cur >> 4)]); | |
break; | |
case 2: | |
result.push(BASE64_MAP[(prev & 0xf) << 2 | (cur >> 6)]); | |
result.push(BASE64_MAP[cur & 0x3f]); | |
break; | |
} | |
prev = cur; | |
} | |
if (byteNum === 0) { | |
result.push(BASE64_MAP[(prev & 3) << 4]); | |
result.push('=='); | |
} else if (byteNum === 1) { | |
result.push(BASE64_MAP[(prev & 0x0f) << 2]); | |
result.push('='); | |
} | |
return result.join(''); | |
}(byteArray); | |
var img = document.createElement('img'); | |
img.addEventListener('load', function callee() { | |
img.removeEventListener('load', callee, false); | |
var canvas = document.createElement('canvas'); | |
var size = {w: 0, h: 0}; | |
var ctx = canvas.getContext('2d'); | |
canvas.width = size.w = img.naturalWidth; | |
canvas.height = size.h = img.naturalHeight; | |
ctx.drawImage(img, 0, 0); | |
base64ToFileEntry(canvas.toDataURL('image/png', '')).then(resolve); | |
}, false); | |
img.src = glitchData; | |
}); | |
}, | |
capture: function (win, pos, dim, scale) { | |
// Google Chrome doesn't support CanvasRenderingContext2D#drawWindow | |
return new Promise(function (resolve) { | |
var width = win.innerWidth; | |
chrome.runtime.sendMessage(TBRL.id, { | |
request: 'capture' | |
}, function (res) { | |
var img = document.createElement('img'); | |
img.addEventListener('load', function callee() { | |
img.removeEventListener('load', callee, false); | |
scale = (img.naturalWidth === width) ? null : img.naturalWidth / width; | |
var canvas = document.createElement('canvas'); | |
var size = {w: 0, h: 0}; | |
var ctx = canvas.getContext('2d'); | |
if (scale) { | |
scale = scale.w ? (scale.w / dim.w) : scale.h ? (scale.h / dim.h) : scale; | |
canvas.width = size.w = dim.w; | |
canvas.height = size.h = dim.h; | |
dim.w *= scale; | |
dim.h *= scale; | |
pos.x *= scale; | |
pos.y *= scale; | |
} else { | |
canvas.width = size.w = dim.w; | |
canvas.height = size.h = dim.h; | |
} | |
ctx.drawImage(img, pos.x, pos.y, dim.w, dim.h, 0, 0, size.w, size.h); | |
// base64ToFileEntry(canvas.toDataURL('image/png', '')).then(resolve); | |
resolve(canvas.toDataURL('image/jpeg')); | |
}, false); | |
img.src = res; | |
}); | |
}); | |
}, | |
selectRegion: function (ctx) { | |
return new Promise(function (resolve, reject) { | |
var doc = ctx ? ctx.document : document; | |
var win = doc.defaultView; | |
doc.documentElement.style.cursor = 'crosshair'; | |
var style = doc.createElement('style'); | |
style.innerHTML = | |
'* {\n' + | |
' cursor: crosshair !important;\n' + | |
' -webkit-user-select: none;\n' + | |
' user-select: none;\n' + | |
'}\n' + | |
'div.taberareloo_capture_size {\n' + | |
' padding: 5px !important;\n' + | |
' border-radius: 5px !important;\n' + | |
' opacity: 0.7 !important;\n' + | |
' position: fixed !important;\n' + | |
' z-index: 999999999 !important;\n' + | |
' background-color: gray !important;\n' + | |
' color: white !important;\n' + | |
'}\n'; | |
doc.body.appendChild(style); | |
var region, p, d, moving, square, size; | |
function mouse(e) { | |
return { | |
x: e.clientX, | |
y: e.clientY | |
}; | |
} | |
function onMouseMove(e) { | |
var to = mouse(e); | |
if (moving) { | |
var px = to.x - d.w, py = to.y - d.h; | |
if (px > window.innerWidth) { | |
px = window.innerWidth; | |
} | |
if (py > window.innerHeight) { | |
py = window.innerHeight; | |
} | |
p.x = Math.max(px, 0); | |
p.y = Math.max(py, 0); | |
} | |
d = { | |
w: to.x - p.x, | |
h: to.y - p.y | |
}; | |
var minusW = (d.w < 0), minusH = (d.h < 0); | |
var s; | |
if (square) { | |
s = Math.min(Math.abs(d.w), Math.abs(d.h)); | |
d.w = (minusW) ? -(s) : s; | |
d.h = (minusH) ? -(s) : s; | |
} | |
var d2 = update({}, d), p2 = update({}, p); | |
if (minusW || minusH) { | |
// 反転モード | |
if (d2.w < 0) { | |
p2.x = p.x + d2.w; | |
d2.w = -d2.w; | |
if (p2.x < 0) { | |
d2.w += p2.x; | |
p2.x = 0; | |
} | |
} | |
if (d2.h < 0) { | |
p2.y = p.y + d2.h; | |
d2.h = -d2.h; | |
if (p2.y < 0) { | |
d2.h += p2.y; | |
p2.y = 0; | |
} | |
} | |
d.w = (minusW) ? -(d2.w) : d2.w; | |
d.h = (minusH) ? -(d2.h) : d2.h; | |
} | |
var rx = p2.x + d2.w; | |
if (rx > window.innerWidth) { | |
rx = (rx - window.innerWidth); | |
d.w -= rx; | |
d2.w -= rx; | |
} | |
var ry = p2.y + d2.h; | |
if (ry > window.innerHeight) { | |
ry = (ry - window.innerHeight); | |
d.h -= ry; | |
d2.h -= ry; | |
} | |
if (square) { | |
if (d2.w < d2.h) { | |
s = d2.w; | |
if (minusH) { | |
p2.y += d2.h - s; | |
d.h = -(s); | |
} else { | |
d.h = s; | |
} | |
d2.h = s; | |
} else { | |
s = d2.h; | |
if (minusW) { | |
p2.x += d2.w - s; | |
d.w = -(s); | |
} else { | |
d.w = s; | |
} | |
d2.w = s; | |
} | |
} | |
// position | |
region.style.top = p2.y + 'px'; | |
region.style.left = p2.x + 'px'; | |
// dimention | |
region.style.width = d2.w + 'px'; | |
region.style.height = d2.h + 'px'; | |
$D(size); | |
size.appendChild($T(d2.w + ' × ' + d2.h)); | |
// Sketch Switch | |
// size.appendChild($T('× / _ / ×')); | |
setStyle(size, { | |
'top' : to.y + 10 + 'px', | |
'left' : to.x + 10 + 'px' | |
}); | |
} | |
function onMouseDown(e) { | |
cancel(e); | |
p = mouse(e); | |
region = doc.createElement('div'); | |
setStyle(region, { | |
'background': '#888', | |
'opacity' : '0.5', | |
'position' : 'fixed', | |
'zIndex' : '999999999', | |
'top' : p.y + 'px', | |
'left' : p.x + 'px' | |
}); | |
doc.body.appendChild(region); | |
size = $N('div', { | |
'class' : 'taberareloo_capture_size' | |
}); | |
doc.body.appendChild(size); | |
doc.addEventListener('mousemove', onMouseMove, true); | |
doc.addEventListener('mouseup', onMouseUp, true); | |
win.addEventListener('keydown', onKeyDown, true); | |
win.addEventListener('keyup', onKeyUp, true); | |
} | |
function onKeyDown(e) { | |
cancel(e); | |
switch (keyString(e)) { | |
case 'SHIFT': | |
square = true; | |
return; | |
case 'SPACE': | |
moving = true; | |
return; | |
case 'ESCAPE': | |
finalize(); | |
reject(); | |
return; | |
} | |
} | |
function onKeyUp(e) { | |
cancel(e); | |
switch (keyString(e)) { | |
case 'SHIFT': | |
square = false; | |
return; | |
case 'SPACE': | |
moving = false; | |
return; | |
} | |
} | |
function onMouseUp(e) { | |
cancel(e); | |
var rect = region.getBoundingClientRect(); | |
p = { x: Math.round(rect.left), y: Math.round(rect.top) }; | |
finalize(); | |
// FIXME: 暫定/左上方向への選択不可/クリックとのダブルインターフェース未実装 | |
if (!d) { | |
reject(); | |
return; | |
} | |
d.w = Math.abs(d.w); | |
d.h = Math.abs(d.h); | |
resolve({ | |
position: p, | |
dimensions: d | |
}); | |
} | |
function onClick(e) { | |
// リンククリックによる遷移を抑止する | |
cancel(e); | |
// mouseupよりも後にイベントが発生するため、ここで取り除く | |
doc.removeEventListener('click', onClick, true); | |
} | |
function finalize() { | |
doc.removeEventListener('mousedown', onMouseDown, true); | |
doc.removeEventListener('mousemove', onMouseMove, true); | |
doc.removeEventListener('mouseup', onMouseUp, true); | |
win.removeEventListener('keydown', onKeyDown, true); | |
win.removeEventListener('keyup', onKeyUp, true); | |
doc.documentElement.style.cursor = ''; | |
region.parentNode.removeChild(region); | |
size.parentNode.removeChild(size); | |
style.parentNode.removeChild(style); | |
} | |
doc.addEventListener('mousedown', onMouseDown, true); | |
doc.addEventListener('click', onClick, true); | |
doc.defaultView.focus(); | |
}); | |
} | |
}, 'Photo - Capture'); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment