/*
* @title open2chOekakiEX
* @description おーぷん2ちゃんお絵かき機能拡張ブックマークレット
* @private
*/
/*
【更新履歴】
Ver1.0.0 2014/03/29 文字入力機能追加
Ver1.1.0 2014/03/29 お絵かき機能ツールメニュー内に文字入力機能を追加 文字サイズ指定追加
Ver1.2.0 2014/03/30 フォント指定、修飾指定追加
Ver1.3.0 2014/03/30 線描画と文字入力のUNDO/REDO機能追加 イタリックが効かないバグ修正
Ver2.0.0 2014/03/31 塗りつぶし機能追加
Ver2.1.0 2014/03/31 塗りつぶし機能にUNDO/REDO機能追加
Ver2.1.1 2014/03/31 IE、Chromeで塗りつぶしが効かないバグ修正
Ver2.2.0 2014/04/01 背景色以外の色も塗りつぶせるように修正
Ver2.3.0 2014/04/01 透明度を指定して塗りつぶせるように修正
Ver2.4.0 2014/04/02 文字入力でフォントの直接指定&改行入力&縦書出力に対応
Ver3.0.0 2014/04/05 お絵かき再生機能追加
Ver3.1.0 2014/04/06 お絵かき再生機能のエクスポート方式をテキストから画像埋め込みに変更
*/
drawRedo = [];
canvas = $('#sketch').sketch();
var fillInput = document.createElement('input');
fillInput.setAttribute('id', 'fill');
fillInput.setAttribute('type', 'radio');
fillInput.setAttribute('name', 'pmode');
var fillImg = document.createElement('img');
fillImg.setAttribute('src', 'http://image.open2ch.net/image/oekaki/nuri.png');
var fillLabel = document.createElement('label');
fillLabel.appendChild(fillImg);
var textInput = document.createElement('input');
textInput.setAttribute('id', 'moji');
textInput.setAttribute('type', 'radio');
textInput.setAttribute('name', 'pmode');
var textLabel = document.createElement('label');
textLabel.setAttribute('for', 'moji');
var b = document.createElement('b');
var textNode = document.createTextNode('[A]:');
b.appendChild(textNode);
textLabel.appendChild(b);
var size = [10,12,14,16,18,20,24,28,32,36,40,48,56,64,72,80,90,100];
var mojiSelect = document.createElement('select');
mojiSelect.setAttribute('id', 'mojiSize');
for(var i = 0; i < size.length; i++){
var option = document.createElement('option');
if(i == 5){
option.setAttribute('selected','selected');
}
option.value = size[i];
var text = document.createTextNode(size[i]);
option.appendChild(text);
mojiSelect.appendChild(option);
}
var fontSelect = document.createElement('select');
fontSelect.setAttribute('id', 'mojiFont');
var font = ['ゴシック','明朝','筆記体','装飾','等幅'];
for(var i = 0; i < font.length; i++){
var option = document.createElement('option');
option.value = i;
var text = document.createTextNode(font[i]);
option.appendChild(text);
fontSelect.appendChild(option);
}
var boldCheck = document.createElement('input');
boldCheck.setAttribute('id', 'mojiBold');
boldCheck.setAttribute('type', 'checkbox');
boldCheck.setAttribute('value', 'bold');
var b = document.createElement('b');
var textNode = document.createTextNode(' B:');
b.appendChild(textNode);
var italicCheck = document.createElement('input');
italicCheck.setAttribute('id', 'mojiItalic');
italicCheck.setAttribute('type', 'checkbox');
italicCheck.setAttribute('value', 'italic');
var i = document.createElement('i');
var textNode = document.createTextNode(' I:');
i.appendChild(textNode);
var br = document.createElement('br');
$('#backButton').before(fillInput);
$('#backButton').before(fillLabel);
$('#backButton').before(textInput);
$('#backButton').before(textLabel);
$('#backButton').before(mojiSelect);
$('#backButton').before(fontSelect);
$('#backButton').before(b);
$('#backButton').before(boldCheck);
$('#backButton').before(i);
$('#backButton').before(italicCheck);
$('#backButton').before(br);
var mojiA = document.createElement('a');
mojiA.setAttribute('data-tool','moji');
mojiA.setAttribute('href', '#sketch');
var fillA = document.createElement('a');
fillA.setAttribute('data-tool','fill');
fillA.setAttribute('href', '#sketch');
$('.tools').after(fillA);
$('.tools').after(mojiA);
$("#moji").click(function(){$("[data-tool=moji]").click()});
$("#fill").click(function(){$("[data-tool=fill]").click()});
var exp = document.createElement('input');
exp.setAttribute('id', 'exportButton');
exp.setAttribute('type', 'button');
exp.setAttribute('value', 'エクスポート');
var imp = document.createElement('input');
imp.setAttribute('id', 'importButton');
imp.setAttribute('type', 'button');
imp.setAttribute('value', 'インポート');
$('#saveButton').after(imp);
$('#saveButton').after(exp);
$('#importButton').click(function(){
playback(stringToAction(colorToActionString()), 10);
});
$('#exportButton').click(function(){
actionStringToColor(actionToString());
});
var show = $('#_canvas').next().children();
show.html("ctrl+←:UNDO<br/>ctrl+→:REDO<br/>" + show.html());
function fill(e){
fillCanvas = canvas.context.getImageData(0, 0, canvas.el.width, canvas.el.height);
var pageX = e.pageX;
var pageY = e.pageY;
var offsetX = $('#sketch').offset().left;
var offsetY = $('#sketch').offset().top;
var x = Math.floor(pageX - offsetX);
var y = Math.floor(pageY - offsetY);
var fillRGB = canvas.color.match(/(\d|\.)+/g);
if(fillRGB[3] == undefined){
var alpha = 255;
}else{
var alpha = alphaToInt(fillRGB[3]);
}
var fillColor = ((Number(fillRGB[0]) << 24) + (Number(fillRGB[1]) << 16) + (Number(fillRGB[2]) << 8) + alpha)>>>0;
//console.log('fillColor>' + fillColor.toString(2));
//console.log('fillR>' + fillRGB[0] + ' fillG>' + fillRGB[1] + ' fillB>' + fillRGB[2] + ' fillA' + alpha);
//console.log('fillRbit>' + ((fillColor & 0xFF000000) >>> 24));
//console.log('fillGbit>' + ((fillColor & 0xFF0000) >> 16));
//console.log('fillBbit>' + ((fillColor & 0xFF00) >> 8));
//console.log('fillAbit>' + (fillColor & 0xFF));
var targetColor = getRGBA(x, y);
//console.log('targetRbit>' + ((targetColor & 0xFF000000) >>> 24));
//console.log('targetGbit>' + ((targetColor & 0xFF0000) >> 16));
//console.log('targetBbit>' + ((targetColor & 0xFF00) >> 8));
//console.log('targetAbit>' + (targetColor & 0xFF));
canvas.actions.push({
x: x,
y: y,
tool: canvas.tool,
fillColor: fillColor,
targetColor: targetColor
});
canvas.redraw();
}
function paint(x, y, fillColor, targetColor){
var color = getRGBA(x, y);
if(color == fillColor){
return;
}
fillRGBA(x, y, fillColor);
var rightX = x + 1;
while(rightX < fillCanvas.width){
var color = getRGBA(rightX, y);
if(color == targetColor){
fillRGBA(rightX, y, fillColor);
}else{
break;
}
rightX++;
}
var leftX = x -1;
while(leftX >= 0){
var color = getRGBA(leftX, y);
if(color == targetColor){
fillRGBA(leftX, y, fillColor);
}else{
break;
}
leftX--;
}
if(y -1 >= 0){
scanSeed(leftX, rightX, y-1, targetColor);
}
if(y + 1 < fillCanvas.width){
scanSeed(leftX, rightX, y+1, targetColor);
}
}
function scanSeed(leftX, rightX, y, targetColor){
var seed = false;
for(var x = leftX + 1; x < rightX; x++){
var color = getRGBA(x, y);
if(color == targetColor){
seed = true;
}else if(seed){
seeds.push({"x":x - 1, "y":y});
seed = false;
}
}
if(seed){
seeds.push({"x":rightX - 1, "y":y});
}
}
function fillRGBA(x, y, color) {
var img = fillCanvas.data;
var w = fillCanvas.width;
var h = fillCanvas.height;
var p = ((w * y) + x) * 4;
//console.log('beforeR>' + ((color & 0xFF000000) >>> 24));
//console.log('beforeG>' + ((color & 0xFF0000) >> 16));
//console.log('beforeB>' + ((color & 0xFF00) >> 8));
//console.log('beforeA>' + ((color & 0xFF)));
img[p] = (color & 0xFF000000) >>> 24;
img[p+1] = (color & 0xFF0000) >> 16;
img[p+2] = (color & 0xFF00) >> 8;
img[p+3] = color & 0xFF;
//console.log('afterR>' + img[p]);
//console.log('afterG>' + img[p+1]);
//console.log('afterB>' + img[p+2]);
//console.log('afterA>' + img[p+3]);
}
function fillRGB(x, y, color) {
//console.log('x:' + x + ' y:' + y);
var img = fillCanvas.data;
var w = fillCanvas.width;
var h = fillCanvas.height;
var p = ((w * y) + x) * 4;
//console.log('beforeR>' + img[p]);
//console.log('beforeG>' + img[p+1]);
//console.log('beforeB>' + img[p+2]);
//console.log('beforeA>' + img[p+3]);
img[p] = (color & 0xFF0000) >> 16;
img[p+1] = (color & 0xFF00) >> 8;
img[p+2] = color & 0xFF;
img[p+3] = 0xFF;
//console.log('afterR>' + img[p]);
//console.log('afterG>' + img[p+1]);
//console.log('afterB>' + img[p+2]);
//console.log('afterA>' + img[p+3]);
}
function getRGBA(x, y){
var img = fillCanvas.data;
var w = fillCanvas.width;
var h = fillCanvas.height;
var p = ((w * y) + x) * 4;
//console.log('getR>' + img[p]);
//console.log('getG>' + img[p+1]);
//console.log('getB>' + img[p+2]);
//console.log('getA>' + img[p+3]);
//console.log('putR>' + ((img[p] << 24)>>>0));
//console.log('putG>' + (img[p+1] << 16));
//console.log('putB>' + (img[p+2] << 8));
//console.log('putA>' + (img[p+3]));
//console.log(((Number(img[p]) << 24) + (Number(img[p+1]) << 16) + (Number(img[p+2]) << 8) + img[p+3])>>>0);
return ((img[p] << 24) + (img[p+1] << 16) + (img[p+2] << 8) + img[p+3])>>>0;
}
function getRGB(x, y){
//console.log('x:' + x + ' y:' + y);
var img = fillCanvas.data;
var w = fillCanvas.width;
var h = fillCanvas.height;
var p = ((w * y) + x) * 4;
return ((img[p] << 16) + (img[p+1] << 8) + (img[p+2]));
}
function getRGBAString(color){
return 'rgba('
+ (color & 0xFF000000) >>> 24 + ', '
+ (color & 0xFF0000) >> 16 + ', '
+ (color & 0xFF00) >> 8 + ', '
+ (color & 0xFF) + ')';
}
function getRGBString(color){
return 'rgb('
+ ((color & 0xFF0000) >> 16) + ', '
+ ((color & 0xFF00) >> 8) + ', '
+ (color & 0xFF) + ')';
}
function alphaToInt(floatAlpha){
return Math.floor(Number(floatAlpha * 255));
}
function alphaToFloat(intAlpha){
return Math.floor(intAlpha / 255 * 100) * 100;
}
$.sketch.tools.fill= {
onEvent: function(e) {
switch (e.type) {
case 'mousedown':
case 'touchstart':
fill(e);
break;
}
},
draw: function(action){
var fillColor = action.fillColor;
fillCanvas = canvas.context.getImageData(0, 0, canvas.el.width, canvas.el.height);
var targetColor = action.targetColor;
seeds = [{'x':action.x, 'y':action.y}];
//var start = new Date();
while(seeds.length > 0){
var seed = seeds.shift();
paint(seed.x, seed.y, fillColor, targetColor);
}
//var end = new Date();
//console.log(end - start + 'msec');
return canvas.context.putImageData(fillCanvas, 0, 0);
}
}
function drawText(e){
var pageX = e.pageX;
var pageY = e.pageY;
var offsetX = $('#sketch').offset().left;
var offsetY = $('#sketch').offset().top;
var x = Math.floor(pageX - offsetX);
var y = Math.floor(pageY - offsetY);
var context = canvas.context;
var text = window.prompt("文字を入力。");
if(!text){
text= '';
}
var str = text.match(/^(kyaha!|\:v)(.*)/i);
var mode = '';
if(str != null){
var mode = str[1];
var text = str[2];
}
str = text.match(/^(?:usa|\[)(.*)(?:min|\])(.*)/i);
var mojiFont;
if(str != null && str[1] != null){
mojiFont = str[1];
text = str[2];
}else{
var fontFamily = ['sans-serif','serif','cursive','fantasy','monospace'];
mojiFont = fontFamily[$('#mojiFont').val()];
}
text = text.replace(/nana|\\n/g, "\n");
var mojiSize = $('#mojiSize').val();
if($('#mojiBold:checked').val()){
var mojiBold = $('#mojiBold:checked').val() + ' ';
}else{
var mojiBold = ' ';
}
if($('#mojiItalic:checked').val()){
var mojiItalic = $('#mojiItalic:checked').val() + ' ';
}else{
var mojiItalic = ' ';
}
canvas.actions.push({
x: x,
y: y,
tool: canvas.tool,
color: canvas.color,
font: mojiBold + mojiItalic + mojiSize + 'px ' + mojiFont,
mode: mode,
text: text
});
canvas.redraw();
}
$.sketch.tools.moji = {
onEvent: function(e) {
switch (e.type) {
case 'mousedown':
case 'touchstart':
drawText(e);
break;
}
},
draw: function(action){
var context = canvas.context;
context.fillStyle = action.color;
context.font = action.font;
context.baseline = 'top';
return fillText(context, action.mode, action.text, action.x, action.y);
}
}
function fillText(context, mode, text, x, y){
var lines = text.split('\n');
var h = context.measureText("あ").width;
jQuery.each(lines, function(i, line) {
if(mode == ''){
context.textAlign = 'left'
context.fillText(this, x, y+h*i);
}else{
jQuery.each(line, function(j, char){
context.textAlign = 'center';
context.fillText(char, x - h*i, y+h*j);
});
}
});
}
function actionToString(){
var actionString = '';
jQuery.each(canvas.actions, function(i, action){
if(action.tool == 'marker'){
var actionX = Math.floor(action.events[0].x);
var actionY = Math.floor(action.events[0].y);
var beforeX = actionX;
var beforeY = actionY;
actionString += ' ' + lpad(actionX, 3, '0') + '' + lpad(actionY, 3, '0') + '' + lpad(action.size, 3, '0') + action.color.replace(/ /g, '') + '' + '~';
jQuery.each(action.events, function(j, event){
actionX = Math.floor(event.x);
actionY = Math.floor(event.y);
actionString += String.fromCharCode((actionX - beforeX + 64), (actionY - beforeY + 64));
beforeX = actionX;
beforeY = actionY;
});
}
});
actionString = actionString.substr(1);
return actionString;
}
function actionStringToColor(actionString){
//console.log('ExportString'+actionString);
var ch = canvas.el.height;
var m = -10;
var w = 1;
var h = 1;
//canvas.el.height += Math.floor((actionString.length+3)/(canvas.el.width+m)/n)+10;
fillCanvas = canvas.context.getImageData(0, 0, canvas.el.width, canvas.el.height);
fillN(1, 0, w, h, 0x000000);
for(var i = 0; i < actionString.length/3; i++){
var c1 = actionString.charCodeAt(i*3);
var c2 = actionString.charCodeAt(i*3 + 1);
var c3 = actionString.charCodeAt(i*3 + 2);
if(!c1){c1 = 33;console.log('NaN')}
if(!c2){c2 = 33;console.log('NaN')}
if(!c3){c3 = 33;console.log('NaN')}
var color = (c1 << 16) + (c2 << 8) + c3;
//console.log('x>' + (i+2)%canvas.el.width + ' y>' + (Math.floor((i+2)/canvas.el.width) + h));
fillN(((i+1)*w)%(canvas.el.width+m)+1, Math.floor(((i+1)*w)/(canvas.el.width+m))*h, w, h, color);
}
fillN(((i+1)*w)%(canvas.el.width+m)+1, Math.floor(((i+1)*w)/(canvas.el.width+m))*h, w, h, 0x000000);
orgActions = canvas.actions;
canvas.actions = [''];
var beforeBaseImage = canvas.baseImageURL;
canvas.redraw();
canvas.context.putImageData(fillCanvas, 0, 0);
var src = canvas.el.toDataURL('imgae/png');
canvas.setBaseImageURL(src);
canvas.actions = orgActions;
canvas.redraw();
}
function fillN(x, y, w, h, color){
for(var i = 0; i < w; i++){
for(var j = 0; j < h; j++){
fillRGB(x + i, y + j, color);
}
}
}
function colorToActionString(){
fillCanvas = canvas.context.getImageData(0, 0, canvas.el.width, canvas.el.height);
var actionString = '';
var ch = 1;
var m = -10;
var w = 1;
var h = 1;
//while(ch < canvas.el.height){
var c = getRGB(1, 0);
// h++;
if(c!=0x000000){return;}
//}
//if(h <= 0){console.log('not found');return;}
//console.log('importStart H->' + h);
var i = 0;
while(i < canvas.el.width * canvas.el.height / 4){
var color = getRGB(((i+1)*w)%(canvas.el.width+m)+1, (Math.floor(((i+1)*h)/(canvas.el.width+m)))*h);
if(color==0x000000){break;}
c1 = (color&0xFF0000)>>16;
c2 = (color&0xFF00)>>8;
c3 = (color&0xFF);
i++;
actionString += String.fromCharCode(c1);
actionString += String.fromCharCode(c2);
actionString += String.fromCharCode(c3);
}
//console.log('Import String' + actionString.replace(/\!/g,''));
return actionString.replace(/\!/g,'');
}
function stringToAction(actionString){
if(!actionString){alert('描画情報が見つかりませんでした。');return [];}
if(!confirm('お絵かきを再生します。\n※再生を開始すると、あなたの描いた絵の情報はクリアされます')){return [];}
canvas.clear();
var splitActions = actionString.split(' ');
var actions =[];
jQuery.each(splitActions, function(i, actionsLine){
var splitAction = actionsLine.split('~');
var actionLine = splitAction[0];
var bX = actionLine.substr(0, 3);
var bY = actionLine.substr(3, 3);
var size = actionLine.substr(6, 3);
var color = actionLine.substr(9);
var action = {
tool: 'marker',
size: parseFloat(size),
color: color,
events: []
}
action.events.push({x: bX, y: bY});
var eventLine = splitAction[1];
for(var i = 1; i <= eventLine.length/2; i++){
action.events.push({
x: Number(action.events[i-1].x) + Number(eventLine.charCodeAt(i*2) - 64),
y: Number(action.events[i-1].y) + Number(eventLine.charCodeAt(i*2+1) - 64)});
}
actions.push(action);
});
return actions;
}
function playback(actions, time){
if(actions.length == 0){return;}
var exportActions = actions;
var painting = false;
var id1 = setInterval(function(){
if(actions[0]){
var action = actions[0];
var events = action.events;
var i = 0;
if(!painting){
var id2 = setInterval(function(){
painting = true;
if(i+1 < events.length){
canvas.context.beginPath();
canvas.context.moveTo(events[i].x, events[i].y);
canvas.context.lineJoin = 'round';
canvas.context.lineCap = 'round';
canvas.context.lineTo(events[i+1].x, events[i+1].y);
canvas.context.strokeStyle = action.color;
canvas.context.lineWidth = action.size;
canvas.context.stroke();i++;
}else{
clearInterval(id2);
painting = false;
canvas.actions.push(actions.shift());
}
},time);
}
}else{
clearInterval(id1);
}
},time*4);
}
function lpad(string, length, char){
var padding;
for(var i = 1; i <= length; i++){
padding += char;
}
return (padding + string).slice(-length);
}
function keydownEvent(e, keyNum, popList, pushList){
if(e.which === keyNum && e.ctrlKey && popList.length > 0){
pushList.push(popList.pop());
canvas.redraw();
}
}
$(document).keydown(function(e){
keydownEvent(e, 37, canvas.actions, drawRedo);
keydownEvent(e, 39, drawRedo, canvas.actions);
});
canvas.startPainting = function(){
orgActions = canvas.actions;
if(!orgActions){
orgActions = [];
}
canvas.actions = [];
beforeBaseImage = canvas.baseImageURL;
var src = canvas.el.toDataURL('imgae/png');
canvas.setBaseImageURL(src);
canvas.painting = true;
return canvas.action = {
tool: canvas.tool,
color: canvas.color,
size: parseFloat(canvas.size),
events: []
};
};
canvas.stopPainting = function() {
if (canvas.action && canvas.painting) {
orgActions.push(canvas.action);
canvas.setBaseImageURL(beforeBaseImage);
canvas.actions = orgActions;
}
canvas.painting = false;
canvas.action = null;
return setTimeout(function(){canvas.redraw();},10);
};