Advertisement
Guest User

crocus

a guest
Feb 10th, 2016
64
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. function Croquis(imageDataList, properties) {
  2.     var self = this;
  3.     if (properties != null)
  4.         for (var property in properties)
  5.             self[property] = properties[property];
  6.     var domElement = document.createElement('div');
  7.     domElement.style.clear = 'both';
  8.     domElement.style.setProperty('user-select', 'none');
  9.     domElement.style.setProperty('-webkit-user-select', 'none');
  10.     domElement.style.setProperty('-ms-user-select', 'none');
  11.     domElement.style.setProperty('-moz-user-select', 'none');
  12.     self.getDOMElement = function () {
  13.         return domElement;
  14.     };
  15.     self.getRelativePosition = function (absoluteX, absoluteY) {
  16.         var rect = domElement.getBoundingClientRect();
  17.         return {x: absoluteX - rect.left,y: absoluteY - rect.top};
  18.     };
  19.     var eventListeners = {
  20.         'ondown': [],
  21.         'onmove': [],
  22.         'onup': [],
  23.         'ontick': [],
  24.         'onchange': [],
  25.         'onundo': [],
  26.         'onredo': [],
  27.         'ontool': [],
  28.         'oncanvassize': [],
  29.         'onlayeradd': [],
  30.         'onlayerremove': [],
  31.         'onlayerswap': [],
  32.         'onlayerselect': []
  33.     };
  34.     function dispatchEvent(event, e) {
  35.         event = event.toLowerCase();
  36.         e = e || {};
  37.         if (eventListeners.hasOwnProperty(event)) {
  38.             eventListeners[event].forEach(function (listener) {
  39.                 listener.call(self, e);
  40.             });
  41.         }
  42.         else throw 'don\'t support ' + event;
  43.     }
  44.     self.addEventListener = function (event, listener) {
  45.         event = event.toLowerCase();
  46.         if (eventListeners.hasOwnProperty(event)) {
  47.             if (typeof listener !== 'function')
  48.                 throw listener + ' is not a function';
  49.             eventListeners[event].push(listener);
  50.         }
  51.         else throw 'don\'t support ' + event;
  52.     };
  53.     self.removeEventListener = function (event, listener) {
  54.         event = event.toLowerCase();
  55.         if (eventListeners.hasOwnProperty(event)) {
  56.             if (listener == null) { // remove all
  57.                 eventListeners[event] = [];
  58.                 return;
  59.             }
  60.             var listeners = eventListeners[event];
  61.             var index = listeners.indexOf(listener);
  62.             if (index >= 0) listeners.splice(index, 1);
  63.         }
  64.         else throw 'don\'t support ' + event;
  65.     };
  66.     self.hasEventListener = function (event, listener) {
  67.         event = event.toLowerCase();
  68.         if (eventListeners.hasOwnProperty(event)) {
  69.             if (listener == null)
  70.                 return eventListeners[event].length > 0;
  71.             return eventListeners[event].indexOf(listener) >= 0;
  72.         }
  73.         else return false;
  74.     };
  75.     var undoStack = [];
  76.     var redoStack = [];
  77.     var undoLimit = 10;
  78.     var preventPushUndo = false;
  79.     var pushToTransaction = false;
  80.     self.getUndoLimit = function () {
  81.         return undoLimit;
  82.     };
  83.     self.setUndoLimit = function (limit) {
  84.         undoLimit = limit;
  85.     };
  86.     self.lockHistory = function () {
  87.         preventPushUndo = true;
  88.     };
  89.     self.unlockHistory = function () {
  90.         preventPushUndo = false;
  91.     };
  92.     self.beginHistoryTransaction = function () {
  93.         undoStack.push([]);
  94.         pushToTransaction = true;
  95.     };
  96.     self.endHistoryTransaction = function () {
  97.         pushToTransaction = false;
  98.     };
  99.     self.clearHistory = function () {
  100.         if (preventPushUndo)
  101.             throw 'history is locked';
  102.         undoStack = [];
  103.         redoStack = [];
  104.     };
  105.     function pushUndo(undoFunction) {
  106.         dispatchEvent('onchange');
  107.         if (self.onChanged)
  108.             self.onChanged();
  109.         if (preventPushUndo)
  110.             return;
  111.         redoStack = [];
  112.         if (pushToTransaction)
  113.             undoStack[undoStack.length - 1].push(undoFunction);
  114.         else
  115.             undoStack.push([undoFunction]);
  116.         while (undoStack.length > undoLimit)
  117.             undoStack.shift();
  118.     }
  119.     self.undo = function () {
  120.         if (pushToTransaction)
  121.             throw 'transaction is not ended';
  122.         if (preventPushUndo)
  123.             throw 'history is locked';
  124.         if (isDrawing || isStabilizing)
  125.             throw 'still drawing';
  126.         if (undoStack.length == 0)
  127.             throw 'no more undo data';
  128.         var undoTransaction = undoStack.pop();
  129.         var redoTransaction = [];
  130.         while (undoTransaction.length)
  131.             redoTransaction.push(undoTransaction.pop()());
  132.         redoStack.push(redoTransaction);
  133.         dispatchEvent('onundo');
  134.     };
  135.     self.redo = function () {
  136.         if (pushToTransaction)
  137.             throw 'transaction is not ended';
  138.         if (preventPushUndo)
  139.             throw 'history is locked';
  140.         if (isDrawing || isStabilizing)
  141.             throw 'still drawing';
  142.         if (redoStack.length == 0)
  143.             throw 'no more redo data';
  144.         var redoTransaction = redoStack.pop();
  145.         var undoTransaction = [];
  146.         while (redoTransaction.length)
  147.             undoTransaction.push(redoTransaction.pop()());
  148.         undoStack.push(undoTransaction);
  149.         dispatchEvent('onredo');
  150.     };
  151.     function pushLayerMetadataUndo(index) {
  152.         index = index || layerIndex;
  153.         var snapshotMetadata = self.getLayerMetadata(index);
  154.         var swap = function () {
  155.             self.lockHistory();
  156.             var temp = self.getLayerMetadata(index);
  157.             self.setLayerMetadata(snapshotMetadata, index);
  158.             snapshotMetadata = temp;
  159.             self.unlockHistory();
  160.             return swap;
  161.         };
  162.         pushUndo(swap);
  163.     }
  164.     function pushLayerOpacityUndo(index) {
  165.         index = index || layerIndex;
  166.         var snapshotOpacity = self.getLayerOpacity(index);
  167.         var swap = function () {
  168.             self.lockHistory();
  169.             var temp = self.getLayerOpacity(index);
  170.             self.setLayerOpacity(snapshotOpacity, index);
  171.             snapshotOpacity = temp;
  172.             self.unlockHistory();
  173.             return swap;
  174.         };
  175.         pushUndo(swap);
  176.     }
  177.     function pushLayerVisibleUndo(index) {
  178.         index = index || layerIndex;
  179.         var snapshotVisible = self.getLayerVisible(index);
  180.         var swap = function () {
  181.             self.lockHistory();
  182.             var temp = self.getLayerVisible(index);
  183.             self.setLayerVisible(snapshotVisible, index);
  184.             snapshotVisible = temp;
  185.             self.unlockHistory();
  186.             return swap;
  187.         };
  188.         pushUndo(swap);
  189.     }
  190.     function pushSwapLayerUndo(layerA, layerB) {
  191.         var swap = function () {
  192.             self.lockHistory();
  193.             self.swapLayer(layerA, layerB);
  194.             self.unlockHistory();
  195.             return swap;
  196.         };
  197.         pushUndo(swap);
  198.     }
  199.     function pushAddLayerUndo(index) {
  200.         var add = function () {
  201.             self.lockHistory();
  202.             self.addLayer(index);
  203.             self.unlockHistory();
  204.             cacheLayer(index);
  205.             return remove;
  206.         };
  207.         var remove = function () {
  208.             self.lockHistory();
  209.             self.removeLayer(index);
  210.             self.unlockHistory();
  211.             return add;
  212.         };
  213.         pushUndo(remove);
  214.     }
  215.     function pushRemoveLayerUndo(index) {
  216.         var layerContext = getLayerContext(index);
  217.         var w = size.width;
  218.         var h = size.height;
  219.         var snapshotData = layerContext.getImageData(0, 0, w, h);
  220.         var snapshotMetadata = self.getLayerMetadata(index);
  221.         var snapshotOpacity = self.getLayerOpacity(index);
  222.         var snapshotVisible = self.getLayerVisible(index);
  223.         var add = function () {
  224.             self.lockHistory();
  225.             self.addLayer(index);
  226.             self.setLayerMetadata(snapshotMetadata, index);
  227.             self.setLayerOpacity(snapshotOpacity, index);
  228.             self.setLayerVisible(snapshotVisible, index);
  229.             var layerContext = getLayerContext(index);
  230.             layerContext.putImageData(snapshotData, 0, 0);
  231.             self.unlockHistory();
  232.             cacheLayer(index);
  233.             return remove;
  234.         };
  235.         var remove = function () {
  236.             self.lockHistory();
  237.             self.removeLayer(index);
  238.             self.unlockHistory();
  239.             return add;
  240.         };
  241.         pushUndo(add);
  242.     }
  243.     function pushDirtyRectUndo(x, y, width, height, index) {
  244.         index = index || layerIndex;
  245.         var w = size.width;
  246.         var h = size.height;
  247.         var right = x + width;
  248.         var bottom = y + height;
  249.         x = Math.min(w, Math.max(0, x));
  250.         y = Math.min(h, Math.max(0, y));
  251.         width = Math.min(w, Math.max(x, right)) - x;
  252.         height = Math.min(h, Math.max(y, bottom)) - y;
  253.         if ((x % 1) > 0)
  254.             ++width;
  255.         if ((y % 1) > 0)
  256.             ++height;
  257.         x = x | 0;
  258.         y = y | 0;
  259.         width = Math.min(w - x, Math.ceil(width));
  260.         height = Math.min(h - y, Math.ceil(height));
  261.         if ((width === 0) || (height === 0)) {
  262.             var doNothing = function () {
  263.                 return doNothing;
  264.             };
  265.             pushUndo(doNothing);
  266.         }
  267.         else {
  268.             var layerContext = getLayerContext(index);
  269.             var snapshotData = layerContext.getImageData(x, y, width, height);
  270.             var swap = function () {
  271.                 var layerContext = getLayerContext(index);
  272.                 var tempData = layerContext.getImageData(x, y, width, height);
  273.                 layerContext.putImageData(snapshotData, x, y);
  274.                 snapshotData = tempData;
  275.                 cacheLayer(index);
  276.                 return swap;
  277.             };
  278.             pushUndo(swap);
  279.         }
  280.         if (renderDirtyRect)
  281.             drawDirtyRect(x, y, width, height);
  282.     }
  283.     function pushContextUndo(index) {
  284.         index = index || layerIndex;
  285.         pushDirtyRectUndo(0, 0, size.width, size.height, index);
  286.     }
  287.     function pushAllContextUndo() {
  288.         var snapshotDatas = [];
  289.         var i;
  290.         var w = size.width;
  291.         var h = size.height;
  292.         for (i = 0; i < layers.length; ++i) {
  293.             var layerContext = getLayerContext(i);
  294.             snapshotDatas.push(layerContext.getImageData(0, 0, w, h));
  295.         }
  296.         var swap = function (index) {
  297.             var layerContext = getLayerContext(index);
  298.             var tempData = layerContext.getImageData(0, 0, w, h);
  299.             layerContext.putImageData(snapshotDatas[index], 0, 0);
  300.             snapshotDatas[index] = tempData;
  301.             cacheLayer(index);
  302.         };
  303.         var swapAll = function () {
  304.             for (var i = 0; i < layers.length; ++i)
  305.                 swap(i);
  306.             return swapAll;
  307.         };
  308.         pushUndo(swapAll);
  309.     }
  310.     function pushCanvasSizeUndo(width, height, offsetX, offsetY) {
  311.         var snapshotSize = self.getCanvasSize();
  312.         var snapshotDatas = [];
  313.         var w = snapshotSize.width;
  314.         var h = snapshotSize.height;
  315.         for (var i = 0; i < layers.length; ++i) {
  316.             var layerContext = getLayerContext(i);
  317.             snapshotDatas[i] = layerContext.getImageData(0, 0, w, h);
  318.         }
  319.         function setSize(width, height, offsetX, offsetY) {
  320.             self.lockHistory();
  321.             self.setCanvasSize(width, height, offsetX, offsetY);
  322.             self.unlockHistory();
  323.         }
  324.         var rollback = function () {
  325.             setSize(w, h);
  326.             for (var i = 0; i < layers.length; ++i) {
  327.                 var layerContext = getLayerContext(i);
  328.                 layerContext.putImageData(snapshotDatas[i], 0, 0);
  329.             }
  330.             return redo;
  331.         };
  332.         var redo = function () {
  333.             rollback();
  334.             setSize(width, height, offsetX, offsetY);
  335.             return rollback;
  336.         };
  337.         pushUndo(rollback);
  338.     }
  339.     var size = {width: 640, height: 480};
  340.     self.getCanvasSize = function () {
  341.         return {width: size.width, height: size.height}; //clone size
  342.     };
  343.     self.setCanvasSize = function (width, height, offsetX, offsetY) {
  344.         offsetX = offsetX || 0;
  345.         offsetY = offsetY || 0;
  346.         size.width = width = Math.floor(width);
  347.         size.height = height = Math.floor(height);
  348.         pushCanvasSizeUndo(width, height, offsetX, offsetY);
  349.         dispatchEvent('oncanvassize', {
  350.             width: width, height: height,
  351.             offsetX: offsetX, offsetY: offsetY
  352.         });
  353.         paintingCanvas.width = width;
  354.         paintingCanvas.height = height;
  355.         dirtyRectDisplay.width = width;
  356.         dirtyRectDisplay.height = height;
  357.         domElement.style.width = width + 'px';
  358.         domElement.style.height = height + 'px';
  359.         for (var i=0; i<layers.length; ++i) {
  360.             var canvas = getLayerCanvas(i);
  361.             var context = getLayerContext(i);
  362.             var imageData = context.getImageData(0, 0, width, height);
  363.             canvas.width = width;
  364.             canvas.height = height;
  365.             context.putImageData(imageData, offsetX, offsetY);
  366.         }
  367.     };
  368.     self.getCanvasWidth = function () {
  369.         return size.width;
  370.     };
  371.     self.setCanvasWidth = function (width, offsetX) {
  372.         self.setCanvasSize(width, size.height, offsetX, 0);
  373.     };
  374.     self.getCanvasHeight = function () {
  375.         return size.height;
  376.     };
  377.     self.setCanvasHeight = function (height, offsetY) {
  378.         self.setCanvasSize(size.width, height, 0, offsetY);
  379.     };
  380.     function getLayerCanvas(index) {
  381.         return layers[index].getElementsByClassName('croquis-layer-canvas')[0];
  382.     }
  383.     self.getLayerCanvas = getLayerCanvas;
  384.     function getLayerContext(index) {
  385.         return getLayerCanvas(index).getContext('2d');
  386.     }
  387.     var layers = [];
  388.     var layerIndex = 0;
  389.     var paintingCanvas = document.createElement('canvas');
  390.     var paintingContext = paintingCanvas.getContext('2d');
  391.     paintingCanvas.className = 'croquis-painting-canvas';
  392.     paintingCanvas.style.position = 'absolute';
  393.     var dirtyRectDisplay = document.createElement('canvas');
  394.     var dirtyRectDisplayContext = dirtyRectDisplay.getContext('2d');
  395.     dirtyRectDisplay.className = 'croquis-dirty-rect-display';
  396.     dirtyRectDisplay.style.position = 'absolute';
  397.     var renderDirtyRect = false;
  398.     function sortLayers() {
  399.         while (domElement.firstChild)
  400.             domElement.removeChild(domElement.firstChild);
  401.         for (var i = 0; i < layers.length; ++i) {
  402.             var layer = layers[i];
  403.             domElement.appendChild(layer);
  404.         }
  405.         domElement.appendChild(dirtyRectDisplay);
  406.     }
  407.     function drawDirtyRect(x, y, w, h) {
  408.         var context = dirtyRectDisplayContext;
  409.         context.fillStyle = '#f00';
  410.         context.globalCompositeOperation = 'source-over';
  411.         context.fillRect(x, y, w, h);
  412.         if ((w > 2) && (h > 2)) {
  413.             context.globalCompositeOperation = 'destination-out';
  414.             context.fillRect(x + 1, y + 1, w - 2, h - 2);
  415.         }
  416.     }
  417.     self.getRenderDirtyRect = function () {
  418.         return renderDirtyRect;
  419.     };
  420.     self.setRenderDirtyRect = function (render) {
  421.         renderDirtyRect = render;
  422.         if (render == false)
  423.             dirtyRectDisplayContext.clearRect(0, 0, size.width, size.height);
  424.     };
  425.     self.createLayerThumbnail = function (index, width, height) {
  426.         index = index || layerIndex;
  427.         width = width || size.width;
  428.         height = height || size.height;
  429.         var canvas = getLayerCanvas(index);
  430.         var thumbnail = document.createElement('canvas');
  431.         var thumbnailContext = thumbnail.getContext('2d');
  432.         thumbnail.width = width;
  433.         thumbnail.height = height;
  434.         thumbnailContext.drawImage(canvas, 0, 0, width, height);
  435.         return thumbnail;
  436.     };
  437.     self.createFlattenThumbnail = function (width, height) {
  438.         width = width || size.width;
  439.         height = height || size.height;
  440.         var thumbnail = document.createElement('canvas');
  441.         var thumbnailContext = thumbnail.getContext('2d');
  442.         thumbnail.width = width;
  443.         thumbnail.height = height;
  444.         for (var i = 0; i < layers.length; ++i) {
  445.             if (!self.getLayerVisible(i))
  446.                 continue;
  447.             var canvas = getLayerCanvas(i);
  448.             thumbnailContext.globalAlpha = self.getLayerOpacity(i);
  449.             thumbnailContext.drawImage(canvas, 0, 0, width, height);
  450.         }
  451.         return thumbnail;
  452.     };
  453.     self.getLayers = function () {
  454.         return layers.concat(); //clone layers
  455.     };
  456.     self.getLayerCount = function () {
  457.         return layers.length;
  458.     };
  459.     self.addLayer = function (index) {
  460.         index = index || layers.length;
  461.         pushAddLayerUndo(index);
  462.         var layer = document.createElement('div');
  463.         layer.className = 'croquis-layer';
  464.         layer.style.visibility = 'visible';
  465.         layer.style.opacity = 1;
  466.         layer['croquis-metadata'] = {};
  467.         var canvas = document.createElement('canvas');
  468.         canvas.className = 'croquis-layer-canvas';
  469.         canvas.width = size.width;
  470.         canvas.height = size.height;
  471.         canvas.style.position = 'absolute';
  472.         layer.appendChild(canvas);
  473.         domElement.appendChild(layer);
  474.         layers.splice(index, 0, layer);
  475.         sortLayers();
  476.         self.selectLayer(layerIndex);
  477.         dispatchEvent('onlayeradd', {index: index});
  478.         if (self.onLayerAdded)
  479.             self.onLayerAdded(index);
  480.         return layer;
  481.     };
  482.     self.removeLayer = function (index) {
  483.         index = index || layerIndex;
  484.         pushRemoveLayerUndo(index);
  485.         domElement.removeChild(layers[index]);
  486.         layers.splice(index, 1);
  487.         if (layerIndex == layers.length)
  488.             self.selectLayer(layerIndex - 1);
  489.         sortLayers();
  490.         dispatchEvent('onlayerremove', {index: index});
  491.         if (self.onLayerRemoved)
  492.             self.onLayerRemoved(index);
  493.     };
  494.     self.removeAllLayer = function () {
  495.         while (layers.length)
  496.             self.removeLayer(0);
  497.     };
  498.     self.swapLayer = function (layerA, layerB) {
  499.         pushSwapLayerUndo(layerA, layerB);
  500.         var layer = layers[layerA];
  501.         layers[layerA] = layers[layerB];
  502.         layers[layerB] = layer;
  503.         sortLayers();
  504.         dispatchEvent('onlayerswap', {a: layerA, b: layerB});
  505.         if (self.onLayerSwapped)
  506.             self.onLayerSwapped(layerA, layerB);
  507.     };
  508.     self.getCurrentLayerIndex = function () {
  509.         return layerIndex;
  510.     };
  511.     self.selectLayer = function (index) {
  512.         var lastestLayerIndex = layers.length - 1;
  513.         if (index > lastestLayerIndex)
  514.             index = lastestLayerIndex;
  515.         layerIndex = index;
  516.         if (paintingCanvas.parentElement != null)
  517.             paintingCanvas.parentElement.removeChild(paintingCanvas);
  518.         layers[index].appendChild(paintingCanvas);
  519.         dispatchEvent('onlayerselect', {index: index});
  520.         if (self.onLayerSelected)
  521.             self.onLayerSelected(index);
  522.     };
  523.     self.clearLayer = function (index) {
  524.         index = index || layerIndex;
  525.         pushContextUndo(index);
  526.         var context = getLayerContext(index);
  527.         context.clearRect(0, 0, size.width, size.height);
  528.         cacheLayer(index);
  529.     };
  530.     self.fillLayer = function (fillColor, index) {
  531.         index = index || layerIndex;
  532.         pushContextUndo(index);
  533.         var context = getLayerContext(index);
  534.         context.fillStyle = fillColor;
  535.         context.fillRect(0, 0, size.width, size.height);
  536.         cacheLayer(index);
  537.     };
  538.     self.fillLayerRect = function (fillColor, x, y, width, height, index) {
  539.         index = index || layerIndex;
  540.         pushDirtyRectUndo(x, y, width, height, index);
  541.         var context = getLayerContext(index);
  542.         context.fillStyle = fillColor;
  543.         context.fillRect(x, y, width, height);
  544.         cacheLayer(index);
  545.     };
  546.     self.floodFill = function (x, y, r, g, b, a, index) {
  547.         index = index || layerIndex;
  548.         pushContextUndo(index);
  549.         var context = getLayerContext(index);
  550.         var w = size.width;
  551.         var h = size.height;
  552.         if ((x < 0) || (x >= w) || (y < 0) || (y >= h))
  553.             return;
  554.         var imageData = context.getImageData(0, 0, w, h);
  555.         var d = imageData.data;
  556.         var targetColor = getColor(x, y);
  557.         var replacementColor = (r << 24) | (g << 16) | (b << 8) | a;
  558.         if (targetColor === replacementColor)
  559.             return;
  560.         function getColor(x, y) {
  561.             var index = ((y * w) + x) * 4;
  562.             return ((d[index] << 24) | (d[index + 1] << 16) |
  563.                 (d[index + 2] << 8) | d[index + 3]);
  564.         }
  565.         function setColor(x, y) {
  566.             var index = ((y * w) + x) * 4;
  567.             d[index] = r;
  568.             d[index + 1] = g;
  569.             d[index + 2] = b;
  570.             d[index + 3] = a;
  571.         }
  572.         var queue = [];
  573.         queue.push(x, y);
  574.         while (queue.length) {
  575.             var nx = queue.shift();
  576.             var ny = queue.shift();
  577.             if ((nx < 0) || (nx >= w) || (ny < 0) || (ny >= h) ||
  578.                 (getColor(nx, ny) !== targetColor))
  579.                 continue;
  580.             var west, east;
  581.             west = east = nx;
  582.             do {
  583.                 var wc = getColor(--west, ny);
  584.             } while ((west >= 0) && (wc === targetColor));
  585.             do {
  586.                 var ec = getColor(++east, ny);
  587.             } while ((east < w) && (ec === targetColor));
  588.             for (var i = west + 1; i < east; ++i) {
  589.                 setColor(i, ny);
  590.                 var north = ny - 1;
  591.                 var south = ny + 1;
  592.                 if (getColor(i, north) === targetColor)
  593.                     queue.push(i, north);
  594.                 if (getColor(i, south) === targetColor)
  595.                     queue.push(i, south);
  596.             }
  597.         }
  598.         context.putImageData(imageData, 0, 0);
  599.         cacheLayer(index);
  600.     };
  601.     self.getLayerMetadata = function (index) {
  602.         index = index || layerIndex;
  603.         var metadata = layers[index]['croquis-metadata'];
  604.         var clone = {};
  605.         Object.keys(metadata).forEach(function (key) {
  606.             clone[key] = metadata[key];
  607.         });
  608.         return clone;
  609.     };
  610.     self.setLayerMetadata = function (metadata, index) {
  611.         index = index || layerIndex;
  612.         pushLayerMetadataUndo(index);
  613.         layers[index]['croquis-metadata'] = metadata;
  614.     };
  615.     self.getLayerOpacity = function (index) {
  616.         index = index || layerIndex;
  617.         var opacity = parseFloat(
  618.             layers[index].style.getPropertyValue('opacity'));
  619.         return window.isNaN(opacity) ? 1 : opacity;
  620.     };
  621.     self.setLayerOpacity = function (opacity, index) {
  622.         index = index || layerIndex;
  623.         pushLayerOpacityUndo(index);
  624.         layers[index].style.opacity = opacity;
  625.     };
  626.     self.getLayerVisible = function (index) {
  627.         index = index || layerIndex;
  628.         var visible = layers[index].style.getPropertyValue('visibility');
  629.         return visible != 'hidden';
  630.     };
  631.     self.setLayerVisible = function (visible, index) {
  632.         index = index || layerIndex;
  633.         pushLayerVisibleUndo(index);
  634.         layers[index].style.visibility = visible ? 'visible' : 'hidden';
  635.     };
  636.     function cacheLayer(index) {
  637.         index = index || layerIndex;
  638.         var w = size.width;
  639.         var h = size.height;
  640.         layers[index].cache = getLayerContext(index).getImageData(0, 0, w, h);
  641.     }
  642.     self.getLayerImageDataCache = function (index) {
  643.         index = index || layerIndex;
  644.         if (layers[index].cache == null)
  645.             cacheLayer(index);
  646.         return layers[index].cache;
  647.     };
  648.     function makeColorData(imageData1x1) {
  649.         var data = imageData1x1.data;
  650.         var r = data[0];
  651.         var g = data[1];
  652.         var b = data[2];
  653.         var a = data[3];
  654.         return {
  655.             r: r, g: g, b: b, a: a,
  656.             htmlColor: 'rgba(' + [r, g, b, a / 0xff].join(',') + ')'
  657.         };
  658.     }
  659.     self.pickColor = function (x, y, index) {
  660.         x = x | 0; // cast to int
  661.         y = y | 0;
  662.         if ((x < 0) || (x >= size.width) || (y < 0) || (y >= size.height))
  663.             return null;
  664.         index = index || layerIndex;
  665.         var cache = self.getLayerImageDataCache(index);
  666.         var position = (y * size.width + x) * 4;
  667.         var data = [];
  668.         data[0] = cache.data[position];
  669.         data[1] = cache.data[++position];
  670.         data[2] = cache.data[++position];
  671.         data[3] = cache.data[++position];
  672.         return makeColorData({data: data});
  673.     };
  674.     self.eyeDrop = function (x, y, baseColor) {
  675.         if (self.pickColor(x, y) == null)
  676.             return null;
  677.         baseColor = baseColor || '#fff';
  678.         var plane = document.createElement('canvas');
  679.         plane.width = 1;
  680.         plane.height = 1;
  681.         var planeContext = plane.getContext('2d');
  682.         planeContext.fillStyle = baseColor;
  683.         planeContext.fillRect(0, 0, 1, 1);
  684.         for (var i = 0; i < layers.length; ++i) {
  685.             if (!self.getLayerVisible(i))
  686.                 continue;
  687.             planeContext.globalAlpha = self.getLayerOpacity(i);
  688.             planeContext.fillStyle = self.pickColor(x, y, i).htmlColor;
  689.             planeContext.fillRect(0, 0, 1, 1);
  690.         }
  691.         return makeColorData(planeContext.getImageData(0, 0, 1, 1));
  692.     };
  693.     var tool;
  694.     var toolStabilizeLevel = 0;
  695.     var toolStabilizeWeight = 0.8;
  696.     var stabilizer = null;
  697.     var stabilizerInterval = 5;
  698.     var tick;
  699.     var tickInterval = 20;
  700.     var paintingOpacity = 1;
  701.     var paintingKnockout = false;
  702.     self.getTool = function () {
  703.         return tool;
  704.     };
  705.     self.setTool = function (value) {
  706.         tool = value;
  707.         dispatchEvent('ontool', {tool: value});
  708.         paintingContext = paintingCanvas.getContext('2d');
  709.         if (tool && tool.setContext)
  710.             tool.setContext(paintingContext);
  711.     };
  712.     self.setTool(new Croquis.Brush());
  713.     self.getPaintingOpacity = function () {
  714.         return paintingOpacity;
  715.     };
  716.     self.setPaintingOpacity = function (opacity) {
  717.         paintingOpacity = opacity;
  718.         paintingCanvas.style.opacity = opacity;
  719.     };
  720.     self.getPaintingKnockout = function () {
  721.         return paintingKnockout;
  722.     };
  723.     self.setPaintingKnockout = function (knockout) {
  724.         if (isDrawing || isStabilizing)
  725.             throw 'still drawing';
  726.         paintingKnockout = knockout;
  727.         paintingCanvas.style.visibility = knockout ? 'hidden' : 'visible';
  728.     };
  729.     self.getTickInterval = function () {
  730.         return tickInterval;
  731.     };
  732.     self.setTickInterval = function (interval) {
  733.         tickInterval = interval;
  734.     };
  735.     /*
  736.     stabilize level is the number of coordinate tracker.
  737.     higher stabilize level makes lines smoother.
  738.     */
  739.     self.getToolStabilizeLevel = function () {
  740.         return toolStabilizeLevel;
  741.     };
  742.     self.setToolStabilizeLevel = function (level) {
  743.         toolStabilizeLevel = (level < 0) ? 0 : level;
  744.     };
  745.     /*
  746.     higher stabilize weight makes trackers follow slower.
  747.     */
  748.     self.getToolStabilizeWeight = function () {
  749.         return toolStabilizeWeight;
  750.     };
  751.     self.setToolStabilizeWeight = function (weight) {
  752.         toolStabilizeWeight = weight;
  753.     };
  754.     self.getToolStabilizeInterval = function () {
  755.         return stabilizerInterval;
  756.     };
  757.     self.setToolStabilizeInterval = function (interval) {
  758.         stabilizerInterval = interval;
  759.     };
  760.     var isDrawing = false;
  761.     var isStabilizing = false;
  762.     var beforeKnockout = document.createElement('canvas');
  763.     var knockoutTick;
  764.     var knockoutTickInterval = 20;
  765.     function gotoBeforeKnockout() {
  766.         var context = getLayerContext(layerIndex);
  767.         var w = size.width;
  768.         var h = size.height;
  769.         context.clearRect(0, 0, w, h);
  770.         context.drawImage(beforeKnockout, 0, 0, w, h);
  771.     }
  772.     function drawPaintingCanvas() { //draw painting canvas on current layer
  773.         var context = getLayerContext(layerIndex);
  774.         var w = size.width;
  775.         var h = size.height;
  776.         context.save();
  777.         context.globalAlpha = paintingOpacity;
  778.         context.globalCompositeOperation = paintingKnockout ?
  779.             'destination-out' : 'source-over';
  780.         context.drawImage(paintingCanvas, 0, 0, w, h);
  781.         context.restore();
  782.     }
  783.     function _move(x, y, pressure) {
  784.         if (tool.move)
  785.             tool.move(x, y, pressure);
  786.         dispatchEvent('onmove', {x: x, y: y, pressure: pressure});
  787.         if (self.onMoved)
  788.             self.onMoved(x, y, pressure);
  789.     }
  790.     function _up(x, y, pressure) {
  791.         isDrawing = false;
  792.         isStabilizing = false;
  793.         var dirtyRect;
  794.         if (tool.up)
  795.             dirtyRect = tool.up(x, y, pressure);
  796.         if (paintingKnockout)
  797.             gotoBeforeKnockout();
  798.         if (dirtyRect)
  799.             pushDirtyRectUndo(dirtyRect.x, dirtyRect.y,
  800.                               dirtyRect.width, dirtyRect.height);
  801.         else
  802.             pushContextUndo();
  803.         drawPaintingCanvas();
  804.         paintingContext.clearRect(0, 0, size.width, size.height);
  805.         dirtyRect = dirtyRect ||
  806.             {x: 0, y: 0, width: size.width, height: size.height};
  807.         dispatchEvent('onup',
  808.             {x: x, y: y, pressure: pressure, dirtyRect: dirtyRect});
  809.         if (self.onUpped)
  810.             self.onUpped(x, y, pressure, dirtyRect);
  811.         window.clearInterval(knockoutTick);
  812.         window.clearInterval(tick);
  813.         cacheLayer(self.getCurrentLayerIndex());
  814.     }
  815.     self.down = function (x, y, pressure) {
  816.         if (isDrawing || isStabilizing)
  817.             throw 'still drawing';
  818.         isDrawing = true;
  819.         if (tool == null)
  820.             return;
  821.         if (paintingKnockout) {
  822.             var w = size.width;
  823.             var h = size.height;
  824.             var canvas = getLayerCanvas(layerIndex);
  825.             var beforeKnockoutContext = beforeKnockout.getContext('2d');
  826.             beforeKnockout.width = w;
  827.             beforeKnockout.height = h;
  828.             beforeKnockoutContext.clearRect(0, 0, w, h);
  829.             beforeKnockoutContext.drawImage(canvas, 0, 0, w, h);
  830.         }
  831.         pressure = pressure || Croquis.Tablet.pressure();
  832.         var down = tool.down;
  833.         if (toolStabilizeLevel > 0) {
  834.             stabilizer = new Croquis.Stabilizer(down, _move, _up,
  835.                 toolStabilizeLevel, toolStabilizeWeight,
  836.                 x, y, pressure, stabilizerInterval);
  837.             isStabilizing = true;
  838.         }
  839.         else if (down != null)
  840.             down(x, y, pressure);
  841.         dispatchEvent('ondown', {x: x, y: y, pressure: pressure});
  842.         if (self.onDowned)
  843.             self.onDowned(x, y, pressure);
  844.         knockoutTick = window.setInterval(function () {
  845.             if (paintingKnockout) {
  846.                 gotoBeforeKnockout();
  847.                 drawPaintingCanvas();
  848.             }
  849.         }, knockoutTickInterval);
  850.         tick = window.setInterval(function () {
  851.             if (tool.tick)
  852.                 tool.tick();
  853.             dispatchEvent('ontick');
  854.             if (self.onTicked)
  855.                 self.onTicked();
  856.         }, tickInterval);
  857.     };
  858.     self.move = function (x, y, pressure) {
  859.         if (!isDrawing)
  860.             throw 'you need to call \'down\' first';
  861.         if (tool == null)
  862.             return;
  863.         pressure = pressure || Croquis.Tablet.pressure();
  864.         if (stabilizer != null)
  865.             stabilizer.move(x, y, pressure);
  866.         else if (!isStabilizing)
  867.             _move(x, y, pressure);
  868.     };
  869.     self.up = function (x, y, pressure) {
  870.         if (!isDrawing)
  871.             throw 'you need to call \'down\' first';
  872.         if (tool == null) {
  873.             isDrawing = false;
  874.             return;
  875.         }
  876.         pressure = pressure || Croquis.Tablet.pressure();
  877.         if (stabilizer != null)
  878.             stabilizer.up(x, y, pressure);
  879.         else
  880.             _up(x, y, pressure);
  881.         stabilizer = null;
  882.     };
  883.     // apply image data
  884.     ;(function (croquis, imageDataList) {
  885.         if (imageDataList != null) {
  886.             if (imageDataList.length === 0)
  887.                 return;
  888.             croquis.lockHistory();
  889.             var first = imageDataList[0];
  890.             croquis.setCanvasSize(first.width, first.height);
  891.             for (var i = 0; i < imageDataList.length; ++i) {
  892.                 var current = imageDataList[i];
  893.                 if ((current.width != first.width) ||
  894.                     (current.height != first.height))
  895.                     throw 'all image data must have same size';
  896.                 croquis.addLayer();
  897.                 var context = croquis.getLayerCanvas(i).getContext('2d');
  898.                 context.putImageData(current, 0, 0);
  899.             }
  900.             croquis.selectLayer(0);
  901.             croquis.unlockHistory();
  902.         }
  903.     }).call(null, self, imageDataList);
  904. }
  905. Croquis.createChecker = function (cellSize, colorA, colorB) {
  906.     cellSize = cellSize || 10;
  907.     colorA = colorA || '#fff';
  908.     colorB = colorB || '#ccc';
  909.     var size = cellSize + cellSize;
  910.     var checker = document.createElement('canvas');
  911.     checker.width = checker.height = size;
  912.     var context = checker.getContext('2d');
  913.     context.fillStyle = colorB;
  914.     context.fillRect(0, 0, size, size);
  915.     context.fillStyle = colorA;
  916.     context.fillRect(0, 0, cellSize, cellSize);
  917.     context.fillRect(cellSize, cellSize, size, size);
  918.     return checker;
  919. };
  920. Croquis.createBrushPointer = function (brushImage, brushSize, brushAngle,
  921.                                        threshold, antialias, color,
  922.                                        shadow, shadowOffsetX, shadowOffsetY) {
  923.     brushSize = brushSize | 0;
  924.     var pointer = document.createElement('canvas');
  925.     var pointerContext = pointer.getContext('2d');
  926.     var boundWidth;
  927.     var boundHeight;
  928.     if (brushSize === 0) {
  929.         pointer.width = boundWidth = 1;
  930.         pointer.height = boundHeight = 1;
  931.     }
  932.     if (brushImage == null) {
  933.         var halfSize = (brushSize * 0.5) | 0;
  934.         pointer.width = boundWidth = brushSize;
  935.         pointer.height = boundHeight = brushSize;
  936.         pointerContext.fillStyle = '#000';
  937.         pointerContext.beginPath();
  938.         pointerContext.arc(halfSize, halfSize, halfSize, 0, Math.PI * 2);
  939.         pointerContext.closePath();
  940.         pointerContext.fill();
  941.     }
  942.     else {
  943.         var width = brushSize;
  944.         var height = brushSize * (brushImage.height / brushImage.width);
  945.         var toRad = Math.PI / 180;
  946.         var ra = brushAngle * toRad;
  947.         var abs = Math.abs;
  948.         var sin = Math.sin;
  949.         var cos = Math.cos;
  950.         boundWidth = abs(height * sin(ra)) + abs(width * cos(ra));
  951.         boundHeight = abs(width * sin(ra)) + abs(height * cos(ra));
  952.         pointer.width = boundWidth;
  953.         pointer.height = boundHeight;
  954.         pointerContext.save();
  955.         pointerContext.translate(boundWidth * 0.5, boundHeight * 0.5);
  956.         pointerContext.rotate(ra);
  957.         pointerContext.translate(width * -0.5, height * -0.5);
  958.         pointerContext.drawImage(brushImage, 0, 0, width, height);
  959.         pointerContext.restore();
  960.     }
  961.     var result;
  962.     var alphaThresholdBorder = Croquis.createAlphaThresholdBorder(
  963.         pointer, threshold, antialias, color);
  964.     if (shadow) {
  965.         shadowOffsetX = shadowOffsetX || 1;
  966.         shadowOffsetY = shadowOffsetY || 1;
  967.         result = document.createElement('canvas');
  968.         result.width = boundWidth + shadowOffsetX;
  969.         result.height = boundHeight + shadowOffsetY;
  970.         var resultContext = result.getContext('2d');
  971.         resultContext.shadowOffsetX = shadowOffsetX;
  972.         resultContext.shadowOffsetY = shadowOffsetY;
  973.         resultContext.shadowColor = shadow;
  974.         resultContext.drawImage(
  975.             alphaThresholdBorder, 0, 0, boundWidth, boundHeight);
  976.     }
  977.     else {
  978.         result = alphaThresholdBorder;
  979.     }
  980.     return result;
  981. };
  982. Croquis.createAlphaThresholdBorder = function (image, threshold,
  983.                                                antialias, color) {
  984.     threshold = threshold || 0x80;
  985.     color = color || '#000';
  986.     var width = image.width;
  987.     var height = image.height;
  988.     var canvas = document.createElement('canvas');
  989.     var context = canvas.getContext('2d');
  990.     canvas.width = width;
  991.     canvas.height = height;
  992.     try {
  993.         context.drawImage(image, 0, 0, width, height);
  994.     }
  995.     catch (e) {
  996.         return canvas;
  997.     }
  998.     var imageData = context.getImageData(0, 0, width, height);
  999.     var d = imageData.data;
  1000.     function getAlphaIndex(index) {
  1001.         return d[index * 4 + 3];
  1002.     }
  1003.     function setRedIndex(index, red) {
  1004.         d[index * 4] = red;
  1005.     }
  1006.     function getRedXY(x, y) {
  1007.         var red = d[((y * width) + x) * 4];
  1008.         return red || 0;
  1009.     }
  1010.     function getGreenXY(x, y) {
  1011.         var green = d[((y * width) + x) * 4 + 1];
  1012.         return green;
  1013.     }
  1014.     function setColorXY(x, y, red, green, alpha) {
  1015.         var i = ((y * width) + x) * 4;
  1016.         d[i] = red;
  1017.         d[i + 1] = green;
  1018.         d[i + 2] = 0;
  1019.         d[i + 3] = alpha;
  1020.     }
  1021.     //threshold
  1022.     var pixelCount = (d.length * 0.25) | 0;
  1023.     for (var i = 0; i < pixelCount; ++i)
  1024.         setRedIndex(i, (getAlphaIndex(i) < threshold) ? 0 : 1);
  1025.     //outline
  1026.     var x;
  1027.     var y;
  1028.     for (x = 0; x < width; ++x) {
  1029.         for (y = 0; y < height; ++y) {
  1030.             if (!getRedXY(x, y)) {
  1031.                 setColorXY(x, y, 0, 0, 0);
  1032.             }
  1033.             else {
  1034.                 var redCount = 0;
  1035.                 var left = x - 1;
  1036.                 var right = x + 1;
  1037.                 var up = y - 1;
  1038.                 var down = y + 1;
  1039.                 redCount += getRedXY(left, up);
  1040.                 redCount += getRedXY(left, y);
  1041.                 redCount += getRedXY(left, down);
  1042.                 redCount += getRedXY(right, up);
  1043.                 redCount += getRedXY(right, y);
  1044.                 redCount += getRedXY(right, down);
  1045.                 redCount += getRedXY(x, up);
  1046.                 redCount += getRedXY(x, down);
  1047.                 if (redCount != 8)
  1048.                     setColorXY(x, y, 1, 1, 255);
  1049.                 else
  1050.                     setColorXY(x, y, 1, 0, 0);
  1051.             }
  1052.         }
  1053.     }
  1054.     //antialias
  1055.     if (antialias) {
  1056.         for (x = 0; x < width; ++x) {
  1057.             for (y = 0; y < height; ++y) {
  1058.                 if (getGreenXY(x, y)) {
  1059.                     var alpha = 0;
  1060.                     if (getGreenXY(x - 1, y) != getGreenXY(x + 1, y))
  1061.                         setColorXY(x, y, 1, 1, alpha += 0x40);
  1062.                     if (getGreenXY(x, y - 1) != getGreenXY(x, y + 1))
  1063.                         setColorXY(x, y, 1, 1, alpha + 0x50);
  1064.                 }
  1065.             }
  1066.         }
  1067.     }
  1068.     context.putImageData(imageData, 0, 0);
  1069.     context.globalCompositeOperation = 'source-in';
  1070.     context.fillStyle = color;
  1071.     context.fillRect(0, 0, width, height);
  1072.     return canvas;
  1073. };
  1074. Croquis.createFloodFill = function (canvas, x, y, r, g, b, a) {
  1075.     var result = document.createElement('canvas');
  1076.     var w = result.width = canvas.width;
  1077.     var h = result.height = canvas.height;
  1078.     if ((x < 0) || (x >= w) || (y < 0) || (y >= h) || !(r || g || b || a))
  1079.         return result;
  1080.     var originalContext = canvas.getContext('2d');
  1081.     var originalData = originalContext.getImageData(0, 0, w, h);
  1082.     var od = originalData.data;
  1083.     var resultContext = result.getContext('2d');
  1084.     var resultData = resultContext.getImageData(0, 0, w, h);
  1085.     var rd = resultData.data;
  1086.     var targetColor = getColor(x, y);
  1087.     var replacementColor = (r << 24) | (g << 16) | (b << 8) | a;
  1088.     function getColor(x, y) {
  1089.         var index = ((y * w) + x) * 4;
  1090.         return (rd[index] ? replacementColor :
  1091.             ((od[index] << 24) | (od[index + 1] << 16) |
  1092.              (od[index + 2] << 8) | od[index + 3]));
  1093.     }
  1094.     var queue = [];
  1095.     queue.push(x, y);
  1096.     while (queue.length) {
  1097.         var nx = queue.shift();
  1098.         var ny = queue.shift();
  1099.         if ((nx < 0) || (nx >= w) || (ny < 0) || (ny >= h) ||
  1100.             (getColor(nx, ny) !== targetColor))
  1101.             continue;
  1102.         var west, east;
  1103.         west = east = nx;
  1104.         do {
  1105.             var wc = getColor(--west, ny);
  1106.         } while ((west >= 0) && (wc === targetColor));
  1107.         do {
  1108.             var ec = getColor(++east, ny);
  1109.         } while ((east < w) && (ec === targetColor));
  1110.         for (var i = west + 1; i < east; ++i) {
  1111.             rd[((ny * w) + i) * 4] = 1;
  1112.             var north = ny - 1;
  1113.             var south = ny + 1;
  1114.             if (getColor(i, north) === targetColor)
  1115.                 queue.push(i, north);
  1116.             if (getColor(i, south) === targetColor)
  1117.                 queue.push(i, south);
  1118.         }
  1119.     }
  1120.     for (var i = 0; i < w; ++i) {
  1121.         for (var j = 0; j < h; ++j) {
  1122.             var index = ((j * w) + i) * 4;
  1123.             if (rd[index] === 0)
  1124.                 continue;
  1125.             rd[index] = r;
  1126.             rd[index + 1] = g;
  1127.             rd[index + 2] = b;
  1128.             rd[index + 3] = a;
  1129.         }
  1130.     }
  1131.     resultContext.putImageData(resultData, 0, 0);
  1132.     return result;
  1133. };
  1134.  
  1135. Croquis.Tablet = {};
  1136. Croquis.Tablet.plugin = function () {
  1137.     var plugin = document.querySelector(
  1138.         'object[type=\'application/x-wacomtabletplugin\']');
  1139.     if (!plugin) {
  1140.         plugin = document.createElement('object');
  1141.         plugin.type = 'application/x-wacomtabletplugin';
  1142.         plugin.style.position = 'absolute';
  1143.         plugin.style.top = '-1000px';
  1144.         document.body.appendChild(plugin);
  1145.     }
  1146.     return plugin;
  1147. };
  1148. Croquis.Tablet.pen = function () {
  1149.     var plugin = Croquis.Tablet.plugin();
  1150.     return plugin.penAPI;
  1151. };
  1152. Croquis.Tablet.pressure = function () {
  1153.     var pen = Croquis.Tablet.pen();
  1154.     return (pen && pen.pointerType) ? pen.pressure : 1;
  1155. };
  1156. Croquis.Tablet.isEraser = function () {
  1157.     var pen = Croquis.Tablet.pen();
  1158.     return pen ? pen.isEraser : false;
  1159. };
  1160.  
  1161. Croquis.Stabilizer = function (down, move, up, level, weight,
  1162.                                x, y, pressure, interval) {
  1163.     interval = interval || 5;
  1164.     var follow = 1 - Math.min(0.95, Math.max(0, weight));
  1165.     var paramTable = [];
  1166.     var current = { x: x, y: y, pressure: pressure };
  1167.     for (var i = 0; i < level; ++i)
  1168.         paramTable.push({ x: x, y: y, pressure: pressure });
  1169.     var first = paramTable[0];
  1170.     var last = paramTable[paramTable.length - 1];
  1171.     var upCalled = false;
  1172.     if (down != null)
  1173.         down(x, y, pressure);
  1174.     window.setTimeout(_move, interval);
  1175.     this.getParamTable = function () { //for test
  1176.         return paramTable;
  1177.     };
  1178.     this.move = function (x, y, pressure) {
  1179.         current.x = x;
  1180.         current.y = y;
  1181.         current.pressure = pressure;
  1182.     };
  1183.     this.up = function (x, y, pressure) {
  1184.         current.x = x;
  1185.         current.y = y;
  1186.         current.pressure = pressure;
  1187.         upCalled = true;
  1188.     };
  1189.     function dlerp(a, d, t) {
  1190.         return a + d * t;
  1191.     }
  1192.     function _move(justCalc) {
  1193.         var curr;
  1194.         var prev;
  1195.         var dx;
  1196.         var dy;
  1197.         var dp;
  1198.         var delta = 0;
  1199.         first.x = current.x;
  1200.         first.y = current.y;
  1201.         first.pressure = current.pressure;
  1202.         for (var i = 1; i < paramTable.length; ++i) {
  1203.             curr = paramTable[i];
  1204.             prev = paramTable[i - 1];
  1205.             dx = prev.x - curr.x;
  1206.             dy = prev.y - curr.y;
  1207.             dp = prev.pressure - curr.pressure;
  1208.             delta += Math.abs(dx);
  1209.             delta += Math.abs(dy);
  1210.             curr.x = dlerp(curr.x, dx, follow);
  1211.             curr.y = dlerp(curr.y, dy, follow);
  1212.             curr.pressure = dlerp(curr.pressure, dp, follow);
  1213.         }
  1214.         if (justCalc)
  1215.             return delta;
  1216.         if (upCalled) {
  1217.             while(delta > 1) {
  1218.                 move(last.x, last.y, last.pressure);
  1219.                 delta = _move(true);
  1220.             }
  1221.             up(last.x, last.y, last.pressure);
  1222.         }
  1223.         else {
  1224.             move(last.x, last.y, last.pressure);
  1225.             window.setTimeout(_move, interval);
  1226.         }
  1227.     }
  1228. };
  1229.  
  1230. Croquis.Random = {};
  1231. Croquis.Random.LFSR113 = function (seed) {
  1232.     var IA = 16807;
  1233.     var IM = 2147483647;
  1234.     var IQ = 127773;
  1235.     var IR = 2836;
  1236.     var a, b, c, d, e;
  1237.     this.get = function () {
  1238.         var f = ((a << 6) ^ a) >> 13;
  1239.         a = ((a & 4294967294) << 18) ^ f;
  1240.         f  = ((b << 2) ^ b) >> 27;
  1241.         b = ((b & 4294967288) << 2) ^ f;
  1242.         f  = ((c << 13) ^ c) >> 21;
  1243.         c = ((c & 4294967280) << 7) ^ f;
  1244.         f  = ((d << 3) ^ d) >> 12;
  1245.         d = ((d & 4294967168) << 13) ^ f;
  1246.         return (a ^ b ^ c ^ d) * 2.3283064365386963e-10 + 0.5;
  1247.     };
  1248.     seed |= 0;
  1249.     if (seed <= 0) seed = 1;
  1250.     e = (seed / IQ) | 0;
  1251.     seed = (((IA * (seed - ((e * IQ) | 0))) | 0) - ((IR * e) | 0)) | 0;
  1252.     if (seed < 0) seed = (seed + IM) | 0;
  1253.     if (seed < 2) a = (seed + 2) | 0 ; else a = seed;
  1254.     e = (seed / IQ) | 0;
  1255.     seed = (((IA * (seed - ((e * IQ) | 0))) | 0) - ((IR * e) | 0)) | 0;
  1256.     if (seed < 0) seed = (seed + IM) | 0;
  1257.     if (seed < 8) b = (seed + 8) | 0; else b = seed;
  1258.     e = (seed / IQ) | 0;
  1259.     seed = (((IA * (seed - ((e * IQ) | 0))) | 0) - ((IR * e) | 0)) | 0;
  1260.     if (seed < 0) seed = (seed + IM) | 0;
  1261.     if (seed < 16) c = (seed + 16) | 0; else c = seed;
  1262.     e = (seed / IQ) | 0;
  1263.     seed = (((IA * (seed - ((e * IQ) | 0))) | 0) - ((IR * e) | 0)) | 0;
  1264.     if (seed < 0) seed = (seed + IM) | 0;
  1265.     if (seed < 128) d = (seed + 128) | 0; else d = seed;
  1266.     this.get();
  1267. };
  1268.  
  1269. Croquis.Brush = function () {
  1270.     // math shortcut
  1271.     var min = Math.min;
  1272.     var max = Math.max;
  1273.     var abs = Math.abs;
  1274.     var sin = Math.sin;
  1275.     var cos = Math.cos;
  1276.     var sqrt = Math.sqrt;
  1277.     var atan2 = Math.atan2;
  1278.     var PI = Math.PI;
  1279.     var ONE = PI + PI;
  1280.     var QUARTER = PI * 0.5;
  1281.     var random = Math.random;
  1282.     this.setRandomFunction = function (value) {
  1283.         random = value;
  1284.     };
  1285.     this.clone = function () {
  1286.         var clone = new Brush(context);
  1287.         clone.setColor(this.getColor());
  1288.         clone.setFlow(this.getFlow());
  1289.         clone.setSize(this.getSize());
  1290.         clone.setSpacing(this.getSpacing());
  1291.         clone.setAngle(this.getAngle());
  1292.         clone.setRotateToDirection(this.getRotateToDirection());
  1293.         clone.setNormalSpread(this.getNormalSpread());
  1294.         clone.setTangentSpread(this.getTangentSpread());
  1295.         clone.setImage(this.getImage());
  1296.     };
  1297.     var context = null;
  1298.     this.getContext = function () {
  1299.         return context;
  1300.     };
  1301.     this.setContext = function (value) {
  1302.         context = value;
  1303.     };
  1304.     var color = '#000';
  1305.     this.getColor = function () {
  1306.         return color;
  1307.     };
  1308.     this.setColor = function (value) {
  1309.         color = value;
  1310.         transformedImageIsDirty = true;
  1311.     };
  1312.     var flow = 1;
  1313.     this.getFlow = function() {
  1314.         return flow;
  1315.     };
  1316.     this.setFlow = function(value) {
  1317.         flow = value;
  1318.         transformedImageIsDirty = true;
  1319.     };
  1320.     var size = 10;
  1321.     this.getSize = function () {
  1322.         return size;
  1323.     };
  1324.     this.setSize = function (value) {
  1325.         size = (value < 1) ? 1 : value;
  1326.         transformedImageIsDirty = true;
  1327.     };
  1328.     var spacing = 0.2;
  1329.     this.getSpacing = function () {
  1330.         return spacing;
  1331.     };
  1332.     this.setSpacing = function (value) {
  1333.         spacing = (value < 0.01) ? 0.01 : value;
  1334.     };
  1335.     var toRad = PI / 180;
  1336.     var toDeg = 1 / toRad;
  1337.     var angle = 0; // radian unit
  1338.     this.getAngle = function () { // returns degree unit
  1339.         return angle * toDeg;
  1340.     };
  1341.     this.setAngle = function (value) {
  1342.         angle = value * toRad;
  1343.     };
  1344.     var rotateToDirection = false;
  1345.     this.getRotateToDirection = function () {
  1346.         return rotateToDirection;
  1347.     };
  1348.     this.setRotateToDirection = function (value) {
  1349.         rotateToDirection = value;
  1350.     };
  1351.     var normalSpread = 0;
  1352.     this.getNormalSpread = function () {
  1353.         return normalSpread;
  1354.     };
  1355.     this.setNormalSpread = function (value) {
  1356.         normalSpread = value;
  1357.     };
  1358.     var tangentSpread = 0;
  1359.     this.getTangentSpread = function () {
  1360.         return tangentSpread;
  1361.     };
  1362.     this.setTangentSpread = function (value) {
  1363.         tangentSpread = value;
  1364.     };
  1365.     var image = null;
  1366.     var transformedImage = null;
  1367.     var transformedImageIsDirty = true;
  1368.     var imageRatio = 1;
  1369.     this.getImage = function () {
  1370.         return image;
  1371.     };
  1372.     this.setImage = function (value) {
  1373.         if (value == null) {
  1374.             transformedImage = image = null;
  1375.             imageRatio = 1;
  1376.             drawFunction = drawCircle;
  1377.         }
  1378.         else if (value != image) {
  1379.             image = value;
  1380.             imageRatio = image.height / image.width;
  1381.             transformedImage = document.createElement('canvas');
  1382.             drawFunction = drawImage;
  1383.             transformedImageIsDirty = true;
  1384.         }
  1385.     };
  1386.     var delta = 0;
  1387.     var prevX = 0;
  1388.     var prevY = 0;
  1389.     var lastX = 0;
  1390.     var lastY = 0;
  1391.     var dir = 0;
  1392.     var prevScale = 0;
  1393.     var drawFunction = drawCircle;
  1394.     var reserved = null;
  1395.     var dirtyRect;
  1396.     function spreadRandom() {
  1397.         return random() - 0.5;
  1398.     }
  1399.     function drawReserved() {
  1400.         if (reserved != null) {
  1401.             drawTo(reserved.x, reserved.y, reserved.scale);
  1402.             reserved = null;
  1403.         }
  1404.     }
  1405.     function appendDirtyRect(x, y, width, height) {
  1406.         if (!(width && height))
  1407.             return;
  1408.         var dxw = dirtyRect.x + dirtyRect.width;
  1409.         var dyh = dirtyRect.y + dirtyRect.height;
  1410.         var xw = x + width;
  1411.         var yh = y + height;
  1412.         var minX = dirtyRect.width ? min(dirtyRect.x, x) : x;
  1413.         var minY = dirtyRect.height ? min(dirtyRect.y, y) : y;
  1414.         dirtyRect.x = minX;
  1415.         dirtyRect.y = minY;
  1416.         dirtyRect.width = max(dxw, xw) - minX;
  1417.         dirtyRect.height = max(dyh, yh) - minY;
  1418.     }
  1419.     function transformImage() {
  1420.         transformedImage.width = size;
  1421.         transformedImage.height = size * imageRatio;
  1422.         var brushContext = transformedImage.getContext('2d');
  1423.         brushContext.clearRect(0, 0,
  1424.             transformedImage.width, transformedImage.height);
  1425.         brushContext.drawImage(image, 0, 0,
  1426.             transformedImage.width, transformedImage.height);
  1427.         brushContext.globalCompositeOperation = 'source-in';
  1428.         brushContext.fillStyle = color;
  1429.         brushContext.globalAlpha = flow;
  1430.         brushContext.fillRect(0, 0,
  1431.             transformedImage.width, transformedImage.height);
  1432.     }
  1433.     function drawCircle(size) {
  1434.         var halfSize = size * 0.5;
  1435.         context.fillStyle = color;
  1436.         context.globalAlpha = flow;
  1437.         context.beginPath();
  1438.         context.arc(halfSize, halfSize, halfSize, 0, ONE);
  1439.         context.closePath();
  1440.         context.fill();
  1441.     }
  1442.     function drawImage(size) {
  1443.         if (transformedImageIsDirty)
  1444.             transformImage();
  1445.         try {
  1446.             context.drawImage(transformedImage, 0, 0, size, size * imageRatio);
  1447.         }
  1448.         catch (e) {
  1449.             drawCircle(size);
  1450.         }
  1451.     }
  1452.     function drawTo(x, y, scale) {
  1453.         var scaledSize = size * scale;
  1454.         var nrm = dir + QUARTER;
  1455.         var nr = normalSpread * scaledSize * spreadRandom();
  1456.         var tr = tangentSpread * scaledSize * spreadRandom();
  1457.         var ra = rotateToDirection ? angle + dir : angle;
  1458.         var width = scaledSize;
  1459.         var height = width * imageRatio;
  1460.         var boundWidth = abs(height * sin(ra)) + abs(width * cos(ra));
  1461.         var boundHeight = abs(width * sin(ra)) + abs(height * cos(ra));
  1462.         x += Math.cos(nrm) * nr + Math.cos(dir) * tr;
  1463.         y += Math.sin(nrm) * nr + Math.sin(dir) * tr;
  1464.         context.save();
  1465.         context.translate(x, y);
  1466.         context.rotate(ra);
  1467.         context.translate(-(width * 0.5), -(height * 0.5));
  1468.         drawFunction(width);
  1469.         context.restore();
  1470.         appendDirtyRect(x - (boundWidth * 0.5),
  1471.                         y - (boundHeight * 0.5),
  1472.                         boundWidth, boundHeight);
  1473.     }
  1474.     this.down = function(x, y, scale) {
  1475.         if (context == null)
  1476.             throw 'brush needs the context';
  1477.         dir = 0;
  1478.         dirtyRect = {x: 0, y: 0, width: 0, height: 0};
  1479.         if (scale > 0) {
  1480.             if (rotateToDirection || normalSpread !== 0 || tangentSpread !== 0)
  1481.                 reserved = {x: x, y: y, scale: scale};
  1482.             else
  1483.                 drawTo(x, y, scale);
  1484.         }
  1485.         delta = 0;
  1486.         lastX = prevX = x;
  1487.         lastY = prevY = y;
  1488.         prevScale = scale;
  1489.     };
  1490.     this.move = function(x, y, scale) {
  1491.         if (context == null)
  1492.             throw 'brush needs the context';
  1493.         if (scale <= 0) {
  1494.             delta = 0;
  1495.             prevX = x;
  1496.             prevY = y;
  1497.             prevScale = scale;
  1498.             return;
  1499.         }
  1500.         var dx = x - prevX;
  1501.         var dy = y - prevY;
  1502.         var ds = scale - prevScale;
  1503.         var d = sqrt(dx * dx + dy * dy);
  1504.         prevX = x;
  1505.         prevY = y;
  1506.         delta += d;
  1507.         var midScale = (prevScale + scale) * 0.5;
  1508.         var drawSpacing = size * spacing * midScale;
  1509.         var ldx = x - lastX;
  1510.         var ldy = y - lastY;
  1511.         var ld = sqrt(ldx * ldx + ldy * ldy);
  1512.         dir = atan2(ldy, ldx);
  1513.         if (ldx || ldy)
  1514.             drawReserved();
  1515.         if (drawSpacing < 0.5)
  1516.             drawSpacing = 0.5;
  1517.         if (delta < drawSpacing) {
  1518.             prevScale = scale;
  1519.             return;
  1520.         }
  1521.         var scaleSpacing = ds * (drawSpacing / delta);
  1522.         if (ld < drawSpacing) {
  1523.             lastX = x;
  1524.             lastY = y;
  1525.             drawTo(lastX, lastY, scale);
  1526.             delta -= drawSpacing;
  1527.         } else {
  1528.             while(delta >= drawSpacing) {
  1529.                 ldx = x - lastX;
  1530.                 ldy = y - lastY;
  1531.                 var tx = cos(dir);
  1532.                 var ty = sin(dir);
  1533.                 lastX += tx * drawSpacing;
  1534.                 lastY += ty * drawSpacing;
  1535.                 prevScale += scaleSpacing;
  1536.                 drawTo(lastX, lastY, prevScale);
  1537.                 delta -= drawSpacing;
  1538.             }
  1539.         }
  1540.         prevScale = scale;
  1541.     };
  1542.     this.up = function (x, y, scale) {
  1543.         dir = atan2(y - lastY, x - lastX);
  1544.         drawReserved();
  1545.         return dirtyRect;
  1546.     };
  1547. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement