diff --git a/_ext/svg/fullrect.svg b/_ext/svg/fullrect.svg
new file mode 100644
index 0000000..05958ee
--- /dev/null
+++ b/_ext/svg/fullrect.svg
@@ -0,0 +1,41 @@
+
+
+
diff --git a/_ext/svg/rectangle.svg b/_ext/svg/rectangle.svg
new file mode 100644
index 0000000..7ebcba9
--- /dev/null
+++ b/_ext/svg/rectangle.svg
@@ -0,0 +1,41 @@
+
+
+
diff --git a/_ext/svg/rectselect.svg b/_ext/svg/rectselect.svg
new file mode 100644
index 0000000..edb2491
--- /dev/null
+++ b/_ext/svg/rectselect.svg
@@ -0,0 +1,108 @@
+
+
+
diff --git a/css/pixel-editor.scss b/css/pixel-editor.scss
index 010a418..d954313 100644
--- a/css/pixel-editor.scss
+++ b/css/pixel-editor.scss
@@ -52,6 +52,7 @@ svg {
position: fixed;
display:none;
box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.64);
+ background-color:transparent;
}
#checkerboard {
@@ -63,6 +64,16 @@ svg {
background:transparent;
}
+#tmp-canvas {
+ z-index:3;
+ background:transparent;
+}
+
+#vfx-canvas {
+ z-index:-5000;
+ background:transparent;
+}
+
#eyedropper-preview {
position: absolute;
width: 45px;
@@ -429,13 +440,15 @@ svg {
#tools-menu li button#pencil-bigger-button,
#tools-menu li button#zoom-in-button,
-#tools-menu li button#eraser-bigger-button{
+#tools-menu li button#eraser-bigger-button,
+#tools-menu li button#rectangle-bigger-button{
left: 0;
}
#tools-menu li button#pencil-smaller-button,
#tools-menu li button#zoom-out-button,
-#tools-menu li button#eraser-smaller-button{
+#tools-menu li button#eraser-smaller-button,
+#tools-menu li button#rectangle-smaller-button{
right: 0;
}
@@ -444,7 +457,9 @@ svg {
#tools-menu li.selected button#zoom-in-button,
#tools-menu li.selected button#zoom-out-button,
#tools-menu li.selected button#eraser-bigger-button,
-#tools-menu li.selected button#eraser-smaller-button{
+#tools-menu li.selected button#eraser-smaller-button,
+#tools-menu li.selected button#rectangle-bigger-button,
+#tools-menu li.selected button#rectangle-smaller-button{
display: block;
}
diff --git a/images/layout.png b/images/layout.png
new file mode 100644
index 0000000..3fb3b2c
Binary files /dev/null and b/images/layout.png differ
diff --git a/images/rectangle.png b/images/rectangle.png
new file mode 100644
index 0000000..124d40c
Binary files /dev/null and b/images/rectangle.png differ
diff --git a/images/rectselect.png b/images/rectselect.png
new file mode 100644
index 0000000..3ed7407
Binary files /dev/null and b/images/rectselect.png differ
diff --git a/js/_changeTool.js b/js/_changeTool.js
index e2af449..25ebcf3 100644
--- a/js/_changeTool.js
+++ b/js/_changeTool.js
@@ -1,5 +1,8 @@
function changeTool (selectedTool) {
-
+ // Ending any selection in progress
+ if (currentTool.includes("select") && !selectedTool.includes("select") && !selectionCanceled) {
+ endSelection();
+ }
//set tool and temp tje tje tpp;
currentTool = selectedTool;
currentToolTemp = selectedTool;
diff --git a/js/_clickedColor.js b/js/_clickedColor.js
index 835a821..7c4f5b8 100644
--- a/js/_clickedColor.js
+++ b/js/_clickedColor.js
@@ -10,6 +10,7 @@ function clickedColor (e){
//set current color
currentLayer.context.fillStyle = this.style.backgroundColor;
+ currentGlobalColor = this.style.backgroundColor;
//make color selected
e.target.parentElement.classList.add('selected');
diff --git a/js/_colorChanged.js b/js/_colorChanged.js
index 93f052c..5fcc23f 100644
--- a/js/_colorChanged.js
+++ b/js/_colorChanged.js
@@ -28,9 +28,6 @@ function colorChanged(e) {
var newColor = hexToRgb(e.target.value);
var oldColor = e.target.oldColor;
- console.log('newColor',newColor)
- console.log('oldColor',oldColor)
-
//save undo state
//saveHistoryState({type: 'colorchange', newColor: e.target.value, oldColor: rgbToHex(oldColor), canvas: context.getImageData(0, 0, canvasSize[0], canvasSize[1])});
new HistoryStateEditColor(e.target.value.toLowerCase(), rgbToHex(oldColor));
@@ -50,15 +47,14 @@ function colorChanged(e) {
//loop through all colors in palette
for (var i = 0; i < colors.length; i++) {
- console.log('%c'+newColorHex +' '+ colors[i].jscolor.toString(), colorCheckingStyle)
//if generated color matches this color
if (newColorHex == colors[i].jscolor.toString()) {
- console.log('%ccolor already exists'+(colors[i].parentElement.classList.contains('jscolor-active')?' (but is the current color)':''), colorCheckingStyle);
+ //console.log('%ccolor already exists'+(colors[i].parentElement.classList.contains('jscolor-active')?' (but is the current color)':''), colorCheckingStyle);
//if the color isnt the one that has the picker currently open
if (!colors[i].parentElement.classList.contains('jscolor-active')) {
- console.log('%cColor is duplicate', colorCheckingStyle)
+ //console.log('%cColor is duplicate', colorCheckingStyle)
//show the duplicate color warning
duplicateColorWarning.style.visibility = 'visible';
@@ -83,13 +79,12 @@ function colorChanged(e) {
//set new old color to changed color
e.target.oldColor = newColor;
-
- console.log(e.target.colorElement);
+
//if this is the current color, update the drawing color
if (e.target.colorElement.parentElement.classList.contains('selected')) {
- console.log('this color is the current color');
- context.fillStyle = '#'+ rgbToHex(newColor.r,newColor.g,newColor.b);
+ //console.log('this color is the current color');
+ context.fillStyle = currentColor;
}
/* this is wrong and bad
if (settings.switchToChangedColor) {
diff --git a/js/_consts.js b/js/_consts.js
new file mode 100644
index 0000000..de4fe38
--- /dev/null
+++ b/js/_consts.js
@@ -0,0 +1,2 @@
+const MIN_Z_INDEX = -5000;
+const MAX_Z_INDEX = 5000;
\ No newline at end of file
diff --git a/js/_drawLine.js b/js/_drawLine.js
index 7008a7b..455cffe 100644
--- a/js/_drawLine.js
+++ b/js/_drawLine.js
@@ -1,5 +1,5 @@
//draw a line between two points on canvas
-function line(x0,y0,x1,y1) {
+function line(x0,y0,x1,y1, brushSize) {
var dx = Math.abs(x1-x0);
var dy = Math.abs(y1-y0);
@@ -10,7 +10,7 @@ function line(x0,y0,x1,y1) {
while (true) {
//set pixel
// If the current tool is the brush
- if (currentTool == 'pencil') {
+ if (currentTool == 'pencil' || currentTool == 'rectangle') {
// I fill the rect
currentLayer.context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize);
} else if (currentTool == 'eraser') {
@@ -18,6 +18,34 @@ function line(x0,y0,x1,y1) {
currentLayer.context.clearRect(x0-Math.floor(eraserSize/2), y0-Math.floor(eraserSize/2), eraserSize, eraserSize);
}
+ //if we've reached the end goal, exit the loop
+ if ((x0==x1) && (y0==y1)) break;
+ var e2 = 2*err;
+ if (e2 >-dy) {err -=dy; x0+=sx;}
+ if (e2 < dx) {err +=dx; y0+=sy;}
+ }
+}
+
+//draw a line between two points on canvas
+function lineOnLayer(x0,y0,x1,y1, brushSize, context) {
+
+ var dx = Math.abs(x1-x0);
+ var dy = Math.abs(y1-y0);
+ var sx = (x0 < x1 ? 1 : -1);
+ var sy = (y0 < y1 ? 1 : -1);
+ var err = dx-dy;
+
+ while (true) {
+ //set pixel
+ // If the current tool is the brush
+ if (currentTool == 'pencil' || currentTool == 'rectangle') {
+ // I fill the rect
+ context.fillRect(x0-Math.floor(brushSize/2), y0-Math.floor(brushSize/2), brushSize, brushSize);
+ } else if (currentTool == 'eraser') {
+ // In case I'm using the eraser I must clear the rect
+ context.clearRect(x0-Math.floor(eraserSize/2), y0-Math.floor(eraserSize/2), eraserSize, eraserSize);
+ }
+
//if we've reached the end goal, exit the loop
if ((x0==x1) && (y0==y1)) break;
var e2 = 2*err;
diff --git a/js/_getCursorPosition.js b/js/_getCursorPosition.js
index b8d0715..5855409 100644
--- a/js/_getCursorPosition.js
+++ b/js/_getCursorPosition.js
@@ -2,6 +2,7 @@
function getCursorPosition(e) {
var x;
var y;
+
if (e.pageX != undefined && e.pageY != undefined) {
x = e.pageX;
y = e.pageY;
@@ -14,5 +15,29 @@ function getCursorPosition(e) {
x -= canvas.offsetLeft;
y -= canvas.offsetTop;
+ return [x,y];
+}
+
+// TODO: apply the function below to every getCursorPosition call
+
+// TODO: FIX THIS BELOW
+
+//get cursor position relative to canvas
+function getCursorPositionRelative(e, layer) {
+ var x;
+ var y;
+
+ if (e.pageX != undefined && e.pageY != undefined) {
+ x = e.pageX;
+ y = e.pageY;
+ }
+ else {
+ x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
+ y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
+ }
+
+ x -= layer.canvas.offsetLeft;
+ y -= layer.canvas.offsetTop;
+
return [x,y];
}
\ No newline at end of file
diff --git a/js/_hotkeyListener.js b/js/_hotkeyListener.js
index 5ef277f..13021d4 100644
--- a/js/_hotkeyListener.js
+++ b/js/_hotkeyListener.js
@@ -1,65 +1,86 @@
var spacePressed = false;
function KeyPress(e) {
- var keyboardEvent = window.event? event : e;
-
- //if the user is typing in an input field, ignore these hotkeys
- if (document.activeElement.tagName == 'INPUT') return;
-
- //if no document has been created yet,
- //orthere is a dialog box open
- //ignore hotkeys
- if (!documentCreated || dialogueOpen) return;
-
- //
- switch (keyboardEvent.keyCode) {
- //pencil tool - 1, b
- case 49: case 66:
+ var keyboardEvent = window.event? event : e;
+
+ //if the user is typing in an input field, ignore these hotkeys
+ if (document.activeElement.tagName == 'INPUT') return;
+
+ //if no document has been created yet,
+ //orthere is a dialog box open
+ //ignore hotkeys
+ if (!documentCreated || dialogueOpen) return;
+
+ //
+ if (e.key === "Escape") {
+ if (!selectionCanceled) {
+ endSelection();
changeTool('pencil');
- break;
- //fill tool - 2, f
- case 50: case 70:
- changeTool('fill');
- break;
- //eyedropper - 3, e
- case 51: case 69:
- changeTool('eyedropper');
- break;
- //pan - 4, p, m
- case 52: case 80: case 77:
- changeTool('pan');
- break;
- //zoom - 5
- case 53:
- changeTool('zoom');
- break;
- // eraser -6, r
- case 54: case 82:
- console.log("Pressed r");
- changeTool('eraser');
- break;
- //Z
- case 90:
- console.log('PRESSED Z ', keyboardEvent.ctrlKey)
- //CTRL+ALT+Z redo
- if (keyboardEvent.altKey && keyboardEvent.ctrlKey)
- redo();
- //CTRL+Z undo
- else if (keyboardEvent.ctrlKey)
- undo();
- //Z switch to zoom tool
- else
+ }
+ }
+ else {
+ switch (keyboardEvent.keyCode) {
+ //pencil tool - 1, b
+ case 49: case 66:
+ changeTool('pencil');
+ break;
+ //fill tool - 2, f
+ case 50: case 70:
+ changeTool('fill');
+ break;
+ //eyedropper - 3, e
+ case 51: case 69:
+ changeTool('eyedropper');
+ break;
+ //pan - 4, p,
+ case 52: case 80:
+ changeTool('pan');
+ break;
+ //zoom - 5
+ case 53:
changeTool('zoom');
- break;
- //redo - ctrl y
- case 89:
- if (keyboardEvent.ctrlKey)
- redo();
- break;
- case 32:
- spacePressed=true;
- break;
- }
+ break;
+ // eraser -6, r
+ case 54: case 82:
+ console.log("Pressed r");
+ changeTool('eraser');
+ break;
+ // Rectangular selection
+ case 77: case 109:
+ changeTool('rectselect');
+ break;
+ //Z
+ case 90:
+ console.log('PRESSED Z ', keyboardEvent.ctrlKey)
+ //CTRL+ALT+Z redo
+ if (keyboardEvent.altKey && keyboardEvent.ctrlKey)
+ redo();
+ if (!selectionCanceled) {
+ endSelection();
+ changeTool('pencil');
+ }
+ //CTRL+Z undo
+ else if (keyboardEvent.ctrlKey) {
+ undo();
+ if (!selectionCanceled) {
+ endSelection();
+ changeTool('pencil');
+ }
+ }
+ //Z switch to zoom tool
+ else
+ changeTool('zoom');
+ break;
+ //redo - ctrl y
+ case 89:
+ if (keyboardEvent.ctrlKey)
+ redo();
+ break;
+ case 32:
+ spacePressed=true;
+ break;
+ }
+ }
}
document.onkeydown = KeyPress;
diff --git a/js/_canvas.js b/js/_layer.js
similarity index 72%
rename from js/_canvas.js
rename to js/_layer.js
index dafb44a..614b25d 100644
--- a/js/_canvas.js
+++ b/js/_layer.js
@@ -1,48 +1,61 @@
-/** Handler class for a single canvas (a single layer)
- *
- * @param width Canvas width
- * @param height Canvas height
- * @param canvas HTML canvas element
- */
-function Canvas(width, height, canvas) {
- this.canvasSize = [width, height],
- this.canvas = canvas,
- this.context = this.canvas.getContext("2d"),
- // Initializes the canvas
- this.initialize = function() {
- var maxHorizontalZoom = Math.floor(window.innerWidth/this.canvasSize[0]*0.75);
- var maxVerticalZoom = Math.floor(window.innerHeight/this.canvasSize[1]*0.75);
-
- zoom = Math.min(maxHorizontalZoom,maxVerticalZoom);
- if (zoom < 1) zoom = 1;
-
- //resize canvas
- this.canvas.width = this.canvasSize[0];
- this.canvas.height = this.canvasSize[1];
- this.canvas.style.width = (this.canvas.width*zoom)+'px';
- this.canvas.style.height = (this.canvas.height*zoom)+'px';
-
- //unhide canvas
- this.canvas.style.display = 'block';
-
- //center canvas in window
- this.canvas.style.left = 64+canvasView.clientWidth/2-(this.canvasSize[0]*zoom/2)+'px';
- this.canvas.style.top = 48+canvasView.clientHeight/2-(this.canvasSize[1]*zoom/2)+'px';
- },
- // Resizes canvas
- this.resize = function() {
- let newWidth = (this.canvas.width * zoom) + 'px';
- let newHeight = (this.canvas.height *zoom)+ 'px';
-
- this.canvas.style.width = newWidth;
- this.canvas.style.height = newHeight;
- },
- // Copies the otherCanvas' position and size
- this.copyData = function(otherCanvas) {
- this.canvas.style.width = otherCanvas.canvas.style.width;
- this.canvas.style.height = otherCanvas.canvas.style.height;
-
- this.canvas.style.left = otherCanvas.canvas.style.left;
- this.canvas.style.top = otherCanvas.canvas.style.top;
- }
+/** TODO LIST FOR LAYERS
+ - move the tmp layer so that it's always right below the active layer
+ - when the move tool is selected (to move a selection), the tmp layer must be put right above the
+ active layer to show a preview
+ - mouse events will always have at least a canvas target, so evey time there's an event, we'll have to check
+ the actual element type instead of the current layer and then apply the tool on the currentLayer, not on
+ the first one in order of z-index
+*/
+
+
+/** Handler class for a single canvas (a single layer)
+ *
+ * @param width Canvas width
+ * @param height Canvas height
+ * @param canvas HTML canvas element
+ */
+function Layer(width, height, canvas) {
+ this.canvasSize = [width, height],
+ this.canvas = canvas,
+ this.context = this.canvas.getContext("2d"),
+ // Initializes the canvas
+ this.initialize = function() {
+ var maxHorizontalZoom = Math.floor(window.innerWidth/this.canvasSize[0]*0.75);
+ var maxVerticalZoom = Math.floor(window.innerHeight/this.canvasSize[1]*0.75);
+
+ zoom = Math.min(maxHorizontalZoom,maxVerticalZoom);
+ if (zoom < 1) zoom = 1;
+
+ //resize canvas
+ this.canvas.width = this.canvasSize[0];
+ this.canvas.height = this.canvasSize[1];
+ this.canvas.style.width = (this.canvas.width*zoom)+'px';
+ this.canvas.style.height = (this.canvas.height*zoom)+'px';
+
+ //unhide canvas
+ this.canvas.style.display = 'block';
+
+ //center canvas in window
+ this.canvas.style.left = 64+canvasView.clientWidth/2-(this.canvasSize[0]*zoom/2)+'px';
+ this.canvas.style.top = 48+canvasView.clientHeight/2-(this.canvasSize[1]*zoom/2)+'px';
+
+ this.context.imageSmoothingEnabled = false;
+ this.context.mozImageSmoothingEnabled = false;
+ },
+ // Resizes canvas
+ this.resize = function() {
+ let newWidth = (this.canvas.width * zoom) + 'px';
+ let newHeight = (this.canvas.height *zoom)+ 'px';
+
+ this.canvas.style.width = newWidth;
+ this.canvas.style.height = newHeight;
+ },
+ // Copies the otherCanvas' position and size
+ this.copyData = function(otherCanvas) {
+ this.canvas.style.width = otherCanvas.canvas.style.width;
+ this.canvas.style.height = otherCanvas.canvas.style.height;
+
+ this.canvas.style.left = otherCanvas.canvas.style.left;
+ this.canvas.style.top = otherCanvas.canvas.style.top;
+ }
}
\ No newline at end of file
diff --git a/js/_mouseEvents.js b/js/_mouseEvents.js
index 2ecc472..a590d36 100644
--- a/js/_mouseEvents.js
+++ b/js/_mouseEvents.js
@@ -1,6 +1,12 @@
+var currentMouseEvent;
+// TODO: replace every position calculation with lastMousePos
+var lastMousePos;
//mousedown - start drawing
window.addEventListener("mousedown", function (mouseEvent) {
+ // Saving the event in case something else needs it
+ currentMouseEvent = mouseEvent;
+ canDraw = true;
//if no document has been created yet, or this is a dialog open
if (!documentCreated || dialogueOpen) return;
@@ -10,31 +16,43 @@ window.addEventListener("mousedown", function (mouseEvent) {
lastPos = getCursorPosition(mouseEvent);
dragging = true;
- //left or right click
+ //left or right click ?
if (mouseEvent.which == 1) {
-
if (spacePressed)
currentTool = 'pan';
else if (mouseEvent.altKey)
currentTool = 'eyedropper';
- else if (mouseEvent.target == currentLayer.canvas && (currentTool == 'pencil' || currentTool == 'eraser'))
+ else if (mouseEvent.target.className == 'drawingCanvas' &&
+ (currentTool == 'pencil' || currentTool == 'eraser' || currentTool == 'rectangle'))
new HistoryStateEditCanvas();
+ else if (currentTool == 'moveselection') {
+ if (!cursorInSelectedArea()) {
+ changeTool('pencil');
+ canDraw = false;
+ }
+ }
//saveHistoryState({type: 'canvas', canvas: context.getImageData(0, 0, canvasSize[0], canvasSize[1])});
updateCursor();
- draw(mouseEvent);
+ if (canDraw) {
+ draw(mouseEvent);
+ }
}
else if (currentTool == 'pencil' && mouseEvent.which == 3) {
currentTool = 'resize-brush';
- prevBrushSize = brushSize;
+ prevBrushSize = pencilSize;
}
else if (currentTool == 'eraser' && mouseEvent.which == 3) {
currentTool = 'resize-eraser';
prevEraserSize = eraserSize;
}
-
- if (currentTool == 'eyedropper' && mouseEvent.target == currentLayer.canvas)
+ else if (currentTool == 'rectangle' && mouseEvent.which == 3) {
+ currentTool = 'resize-rectangle';
+ prevRectangleSize = rectangleSize;
+ }
+
+ if (currentTool == 'eyedropper' && mouseEvent.target.className == 'drawingCanvas')
eyedropperPreview.style.display = 'block';
return false;
@@ -44,47 +62,45 @@ window.addEventListener("mousedown", function (mouseEvent) {
//mouseup - end drawing
window.addEventListener("mouseup", function (mouseEvent) {
-
+ // Saving the event in case something else needs it
+ currentMouseEvent = mouseEvent;
+
closeMenu();
if (!documentCreated || dialogueOpen) return;
- if (currentTool == 'eyedropper' && mouseEvent.target == currentLayer.canvas) {
+ if (currentTool == 'eyedropper' && mouseEvent.target.className == 'drawingCanvas') {
var cursorLocation = getCursorPosition(mouseEvent);
var selectedColor = context.getImageData(Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom),1,1);
- var newColor = rgbToHex(selectedColor.data[0],selectedColor.data[1],selectedColor.data[2]);
-
- console.log(newColor);
+ var newColor = rgbToHex(selectedColor.data[0],selectedColor.data[1],selectedColor.data[2]);
+
+ currentGlobalColor = "#" + newColor;
var colors = document.getElementsByClassName('color-button');
- for (var i = 0; i < colors.length; i++) {
- console.log(colors[i].jscolor.toString());
-
- //if picked color matches this color
- if (newColor == colors[i].jscolor.toString()) {
- console.log('color found');
-
- //remove current color selection
- var selectedColor = document.querySelector("#colors-menu li.selected")
- if (selectedColor) selectedColor.classList.remove("selected");
-
- //set current color
- context.fillStyle = '#'+newColor;
-
- //make color selected
- colors[i].parentElement.classList.add('selected');
-
- //hide eyedropper
- eyedropperPreview.style.display = 'none';
- }
- }
-
-
+ for (var i = 0; i < colors.length; i++) {
+ console.log(colors[i].jscolor.toString());
+
+ //if picked color matches this color
+ if (newColor == colors[i].jscolor.toString()) {
+ console.log('color found');
+
+ //remove current color selection
+ var selectedColor = document.querySelector("#colors-menu li.selected")
+ if (selectedColor) selectedColor.classList.remove("selected");
+
+ //set current color
+ context.fillStyle = '#'+newColor;
+
+ //make color selected
+ colors[i].parentElement.classList.add('selected');
+
+ //hide eyedropper
+ eyedropperPreview.style.display = 'none';
+ }
+ }
}
- else if (currentTool == 'fill' && mouseEvent.target == currentLayer.canvas) {
+ else if (currentTool == 'fill' && mouseEvent.target.className == 'drawingCanvas') {
console.log('filling')
- //if you clicked on anything but the canvas, do nothing
- if (!mouseEvent.target == currentLayer.canvas) return;
//get cursor postion
var cursorLocation = getCursorPosition(mouseEvent);
@@ -96,7 +112,7 @@ window.addEventListener("mouseup", function (mouseEvent) {
//fill starting at the location
fill(cursorLocation);
}
- else if (currentTool == 'zoom' && mouseEvent.target == canvasView) {
+ else if (currentTool == 'zoom' && mouseEvent.target.className == 'drawingCanvas') {
let mode;
if (mouseEvent.which == 1){
mode = "in";
@@ -111,6 +127,12 @@ window.addEventListener("mouseup", function (mouseEvent) {
layers[i].copyData(layers[0]);
}
}
+ else if (currentTool == 'rectselect' && isRectSelecting) {
+ endRectSelection(mouseEvent);
+ }
+ else if (currentTool == 'rectangle') {
+ endRectDrawing(mouseEvent);
+ }
dragging = false;
currentTool = currentToolTemp;
@@ -121,10 +143,16 @@ window.addEventListener("mouseup", function (mouseEvent) {
}, false);
+// OPTIMIZABLE: redundant || mouseEvent.target.className in currentTool ifs
+
//mouse is moving on canvas
window.addEventListener("mousemove", draw, false);
function draw (mouseEvent) {
- var cursorLocation = getCursorPosition(mouseEvent);
+ lastMousePos = getCursorPosition(mouseEvent);
+ // Saving the event in case something else needs it
+ currentMouseEvent = mouseEvent;
+
+ var cursorLocation = lastMousePos;
//if a document hasnt yet been created, exit this function
if (!documentCreated || dialogueOpen) return;
@@ -133,21 +161,20 @@ function draw (mouseEvent) {
eyedropperPreview.style.display = 'none';
if (currentTool == 'pencil') {
-
//move the brush preview
- brushPreview.style.left = cursorLocation[0] + currentLayer.canvas.offsetLeft - brushSize * zoom / 2 + 'px';
- brushPreview.style.top = cursorLocation[1] + currentLayer.canvas.offsetTop - brushSize * zoom / 2 + 'px';
+ brushPreview.style.left = cursorLocation[0] + currentLayer.canvas.offsetLeft - pencilSize * zoom / 2 + 'px';
+ brushPreview.style.top = cursorLocation[1] + currentLayer.canvas.offsetTop - pencilSize * zoom / 2 + 'px';
//hide brush preview outside of canvas / canvas view
- if (mouseEvent.target == currentLayer.canvas || mouseEvent.target == canvasView)
+ if (mouseEvent.target.className == 'drawingCanvas'|| mouseEvent.target.className == 'drawingCanvas')
brushPreview.style.visibility = 'visible';
else
brushPreview.style.visibility = 'hidden';
//draw line to current pixel
if (dragging) {
- if (mouseEvent.target == currentLayer.canvas || mouseEvent.target == canvasView) {
- line(Math.floor(lastPos[0]/zoom),Math.floor(lastPos[1]/zoom),Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom));
+ if (mouseEvent.target.className == 'drawingCanvas' || mouseEvent.target.className == 'drawingCanvas') {
+ line(Math.floor(lastPos[0]/zoom),Math.floor(lastPos[1]/zoom),Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom), pencilSize);
lastPos = cursorLocation;
}
}
@@ -168,19 +195,38 @@ function draw (mouseEvent) {
brushPreview.style.top = cursorLocation[1] + canvas.offsetTop - eraserSize * zoom / 2 + 'px';
//hide brush preview outside of canvas / canvas view
- if (mouseEvent.target == currentLayer.canvas || mouseEvent.target == canvasView)
+ if (mouseEvent.target.className == 'drawingCanvas' || mouseEvent.target.className == 'drawingCanvas')
brushPreview.style.visibility = 'visible';
else
brushPreview.style.visibility = 'hidden';
//draw line to current pixel
if (dragging) {
- if (mouseEvent.target == currentLayer.canvas || mouseEvent.target == canvasView) {
- line(Math.floor(lastPos[0]/zoom),Math.floor(lastPos[1]/zoom),Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom));
+ if (mouseEvent.target.className == 'drawingCanvas' || mouseEvent.target.className == 'drawingCanvas') {
+ line(Math.floor(lastPos[0]/zoom),Math.floor(lastPos[1]/zoom),Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom), eraserSize);
lastPos = cursorLocation;
}
}
}
+ else if (currentTool == 'rectangle')
+ {
+ //move the brush preview
+ brushPreview.style.left = cursorLocation[0] + currentLayer.canvas.offsetLeft - rectangleSize * zoom / 2 + 'px';
+ brushPreview.style.top = cursorLocation[1] + currentLayer.canvas.offsetTop - rectangleSize * zoom / 2 + 'px';
+
+ //hide brush preview outside of canvas / canvas view
+ if (mouseEvent.target.className == 'drawingCanvas'|| mouseEvent.target.className == 'drawingCanvas')
+ brushPreview.style.visibility = 'visible';
+ else
+ brushPreview.style.visibility = 'hidden';
+
+ if (!isDrawingRect && dragging) {
+ startRectDrawing(mouseEvent);
+ }
+ else if (dragging){
+ updateRectDrawing(mouseEvent);
+ }
+ }
else if (currentTool == 'pan' && dragging) {
// Setting first layer position
setCanvasOffset(layers[0].canvas, layers[0].canvas.offsetLeft + (cursorLocation[0] - lastPos[0]), layers[0].canvas.offsetTop + (cursorLocation[1] - lastPos[1]));
@@ -189,8 +235,9 @@ function draw (mouseEvent) {
layers[i].copyData(layers[0]);
}
}
- else if (currentTool == 'eyedropper' && dragging && mouseEvent.target == currentLayer.canvas) {
+ else if (currentTool == 'eyedropper' && dragging && mouseEvent.target.className == 'drawingCanvas') {
var selectedColor = context.getImageData(Math.floor(cursorLocation[0]/zoom),Math.floor(cursorLocation[1]/zoom),1,1).data;
+
eyedropperPreview.style.borderColor = '#'+rgbToHex(selectedColor[0],selectedColor[1],selectedColor[2]);
eyedropperPreview.style.display = 'block';
@@ -212,11 +259,11 @@ function draw (mouseEvent) {
var newBrushSize = prevBrushSize + brushSizeChange;
//set the brush to the new size as long as its bigger than 1
- brushSize = Math.max(1,newBrushSize);
+ pencilSize = Math.max(1,newBrushSize);
//fix offset so the cursor stays centered
- brushPreview.style.left = lastPos[0] + currentLayer.canvas.offsetLeft - brushSize * zoom / 2 + 'px';
- brushPreview.style.top = lastPos[1] + currentLayer.canvas.offsetTop - brushSize * zoom / 2 + 'px';
+ brushPreview.style.left = lastPos[0] + currentLayer.canvas.offsetLeft - pencilSize * zoom / 2 + 'px';
+ brushPreview.style.top = lastPos[1] + currentLayer.canvas.offsetTop - pencilSize * zoom / 2 + 'px';
updateCursor();
}
@@ -237,6 +284,45 @@ function draw (mouseEvent) {
updateCursor();
}
+ else if (currentTool == 'resize-rectangle' && dragging) {
+ //get new brush size based on x distance from original clicking location
+ var distanceFromClick = cursorLocation[0] - lastPos[0];
+ //var roundingAmount = 20 - Math.round(distanceFromClick/10);
+ //this doesnt work in reverse... because... it's not basing it off of the brush size which it should be
+ var rectangleSizeChange = Math.round(distanceFromClick/10);
+ var newRectangleSize = prevRectangleSize + rectangleSizeChange;
+
+ //set the brush to the new size as long as its bigger than 1
+ rectangleSize = Math.max(1,newRectangleSize);
+
+ //fix offset so the cursor stays centered
+ brushPreview.style.left = lastPos[0] + currentLayer.canvas.offsetLeft - rectangleSize * zoom / 2 + 'px';
+ brushPreview.style.top = lastPos[1] + currentLayer.canvas.offsetTop - rectangleSize * zoom / 2 + 'px';
+
+ updateCursor();
+ }
+ else if (currentTool == 'rectselect') {
+ if (dragging && !isRectSelecting && mouseEvent.target.className == 'drawingCanvas') {
+ isRectSelecting = true;
+ console.log("cominciata selezione su " + mouseEvent.target.className);
+ startRectSelection(mouseEvent);
+ }
+ else if (dragging && isRectSelecting) {
+ updateRectSelection(mouseEvent);
+ }
+ else if (isRectSelecting) {
+ endRectSelection();
+ }
+ }
+ else if (currentTool == 'moveselection') {
+ // Updating the cursor (move if inside rect, cross if not)
+ updateCursor();
+
+ // If I'm dragging, I move the preview
+ if (dragging && cursorInSelectedArea()) {
+ updateMovePreview(mouseEvent);
+ }
+ }
}
//mousewheel scrroll
diff --git a/js/_move.js b/js/_move.js
new file mode 100644
index 0000000..4b182ca
--- /dev/null
+++ b/js/_move.js
@@ -0,0 +1,72 @@
+var imageDataToMove;
+var originalDataPosition;
+var canMoveSelection = false;
+var lastMovePos;
+var selectionCanceled = true;
+var firstTimeMove = true;
+
+// TODO: move with arrows
+function updateMovePreview(mouseEvent) {
+ // ISSUE
+ selectionCanceled = false;
+
+ if (firstTimeMove) {
+ cutSelection(mouseEvent);
+ }
+
+ firstTimeMove = false;
+
+ lastMousePos = getCursorPosition(mouseEvent);
+ // clear the entire tmp layer
+ TMPLayer.context.clearRect(0, 0, TMPLayer.canvas.width, TMPLayer.canvas.height);
+ // put the image data with offset
+ TMPLayer.context.putImageData(
+ imageDataToMove,
+ Math.round(lastMousePos[0] / zoom - imageDataToMove.width / 2),
+ Math.round(lastMousePos[1] / zoom - imageDataToMove.height / 2));
+
+ lastMovePos = lastMousePos;
+ moveSelection(lastMousePos[0] / zoom, lastMousePos[1] / zoom, imageDataToMove.width, imageDataToMove.height)
+}
+
+function endSelection() {
+ TMPLayer.context.clearRect(0, 0, TMPLayer.canvas.width, TMPLayer.canvas.height);
+ VFXLayer.context.clearRect(0, 0, VFXLayer.canvas.width, VFXLayer.canvas.height);
+
+ if (imageDataToMove !== undefined) {
+ let underlyingImageData = currentLayer.context.getImageData(startX, startY, endX - startX, endY - startY);
+
+ for (let i=0; i 0) {
@@ -55,6 +66,7 @@ function newPixel (width, height, palette) {
//set current drawing color as foreground color
currentLayer.context.fillStyle = '#'+defaultForegroundColor;
+ currentGlobalColor = '#' + defaultForegroundColor;
selectedPalette = 'none';
}
diff --git a/js/_pixelEditorUtility.js b/js/_pixelEditorUtility.js
new file mode 100644
index 0000000..67874f1
--- /dev/null
+++ b/js/_pixelEditorUtility.js
@@ -0,0 +1,7 @@
+function isPixelEmpty(pixel) {
+ if ((pixel[0] == 0 && pixel[1] == 0 && pixel[2] == 0) || pixel[3] == 0) {
+ return true;
+ }
+
+ return false;
+}
\ No newline at end of file
diff --git a/js/_rectSelect.js b/js/_rectSelect.js
new file mode 100644
index 0000000..74c02f0
--- /dev/null
+++ b/js/_rectSelect.js
@@ -0,0 +1,151 @@
+var isRectSelecting = false;
+let startX;
+let startY;
+let endX;
+let endY;
+
+function startRectSelection(mouseEvent) {
+ // Saving the canvas
+ new HistoryStateEditCanvas();
+ // Putting the vfx layer on top of everything
+ VFXCanvas.style.zIndex = MAX_Z_INDEX;
+
+ // Saving the start coords of the rect
+ let cursorPos = getCursorPosition(mouseEvent);
+ startX = Math.round(cursorPos[0] / zoom) - 0.5;
+ startY = Math.round(cursorPos[1] / zoom) - 0.5;
+
+ // Avoiding external selections
+ if (startX < 0) {
+ startX = 0;
+ }
+ else if (startX > currentLayer.canvas.width) {
+ startX = currentLayer.canvas.width;
+ }
+
+ if (startY < 0) {
+ startY = 0;
+ }
+ else if (startY > currentLayer.canvas.height) {
+ startY = currentLayer.canvas.height;
+ }
+
+ // Drawing the rect
+ drawRect(startX, startY);
+ selectionCanceled = false;
+}
+
+function updateRectSelection(mouseEvent) {
+ let pos = getCursorPosition(mouseEvent);
+
+ // Drawing the rect
+ drawRect(Math.round(pos[0] / zoom) + 0.5, Math.round(pos[1] / zoom) + 0.5);
+}
+
+function endRectSelection(mouseEvent) {
+ // Getting the end position
+ let currentPos = getCursorPosition(mouseEvent);
+ endX = Math.round(currentPos[0] / zoom) + 0.5;
+ endY = Math.round(currentPos[1] / zoom) + 0.5;
+
+ // Inverting end and start (start must always be the top left corner)
+ if (endX < startX) {
+ let tmp = endX;
+ endX = startX;
+ startX = tmp;
+ }
+ // Same for the y
+ if (endY < startY) {
+ let tmp = endY;
+ endY = startY;
+ startY = tmp;
+ }
+
+ // Selecting the move tool
+ currentTool = 'moveselection';
+ currentToolTemp = currentTool;
+
+ // Resetting this
+ isRectSelecting = false;
+
+ // Updating the cursor
+ updateCursor();
+}
+
+function cutSelection(mouseEvent) {
+ console.log("Coordinate: start x, y: " + startX + ", " + startY + " end x, y: " + endX + ", " + endY);
+ // Getting the selected pixels
+ imageDataToMove = currentLayer.context.getImageData(startX, startY, endX - startX + 1, endY - startY + 1);
+
+ currentLayer.context.clearRect(startX - 0.5, startY - 0.5, endX - startX + 1, endY - startY + 1);
+ // Moving those pixels from the current layer to the tmp layer
+ TMPLayer.context.putImageData(imageDataToMove, startX + 1, startY);
+
+ //originalDataPosition = [currentPos[0], currentPos[1]];
+}
+
+function drawRect(x, y) {
+ // Getting the vfx context
+ let vfxContext = VFXCanvas.getContext("2d");
+
+ // Clearing the vfx canvas
+ vfxContext.clearRect(0, 0, VFXCanvas.width, VFXCanvas.height);
+ vfxContext.lineWidth = 1;
+ vfxContext.strokeStyle = "black";
+ vfxContext.setLineDash([4]);
+
+ // Drawing the rect
+ vfxContext.beginPath();
+ vfxContext.rect(startX, startY, x - startX, y - startY);
+
+ vfxContext.stroke();
+
+ // TODO: make the rect blink from black to white in case of dark backgrounds
+}
+
+function applyChanges() {
+ VFXCanvas.style.zIndex = MIN_Z_INDEX;
+}
+
+// Checks whether the pointer is inside the selected area or not
+function cursorInSelectedArea() {
+ let cursorPos = getCursorPosition(currentMouseEvent);
+ let x = cursorPos[0] / zoom;
+ let y = cursorPos[1] / zoom;
+
+ let leftX = Math.min(startX, endX);
+ let rightX = Math.max(startX, endX);
+ let topY = Math.max(startY, endY);
+ let bottomY = Math.min(startY, endY);
+
+ if (leftX <= x && x <= rightX) {
+ if (bottomY <= y && y <= topY) {
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+}
+
+function moveSelection(x, y, width, height) {
+ // Getting the vfx context
+ let vfxContext = VFXCanvas.getContext("2d");
+
+ // Clearing the vfx canvas
+ vfxContext.clearRect(0, 0, VFXCanvas.width, VFXCanvas.height);
+ vfxContext.lineWidth = 1;
+ vfxContext.setLineDash([4]);
+
+ startX = Math.round(Math.round(x) - Math.round(width / 2)) + 0.5;
+ startY = Math.round(Math.round(y) - Math.round(height / 2)) + 0.5;
+ endX = startX + Math.round(width);
+ endY = startY + Math.round(height);
+
+ // Drawing the rect
+ vfxContext.beginPath();
+ vfxContext.rect(startX, startY, width, height);
+
+ vfxContext.stroke();
+}
\ No newline at end of file
diff --git a/js/_rectangle.js b/js/_rectangle.js
new file mode 100644
index 0000000..3249ce4
--- /dev/null
+++ b/js/_rectangle.js
@@ -0,0 +1,121 @@
+var rectangleSize = 1;
+var prevRectangleSie = rectangleSize;
+var emptySVG = document.getElementById("empty-button-svg");
+var fullSVG = document.getElementById("full-button-svg");
+
+var drawMode = 'empty';
+var isDrawingRect = false;
+
+let startRectX;
+let startRectY;
+let endRectX;
+let endRectY;
+
+
+function startRectDrawing(mouseEvent) {
+ // Putting the vfx layer on top of everything
+ VFXCanvas.style.zIndex = MAX_Z_INDEX;
+ // Updating flag
+ isDrawingRect = true;
+
+ // Saving the start coords of the rect
+ let cursorPos = getCursorPosition(mouseEvent);
+ startRectX = Math.round(cursorPos[0] / zoom) - 0.5;
+ startRectY = Math.round(cursorPos[1] / zoom) - 0.5;
+
+ drawRectangle(startRectX, startRectY);
+}
+
+function updateRectDrawing(mouseEvent) {
+ let pos = getCursorPosition(mouseEvent);
+
+ // Drawing the rect
+ drawRectangle(Math.round(pos[0] / zoom) + 0.5, Math.round(pos[1] / zoom) + 0.5);
+}
+
+function endRectDrawing(mouseEvent) {
+ // Getting the end position
+ let currentPos = getCursorPosition(mouseEvent);
+ let vfxContext = VFXCanvas.getContext("2d");
+
+ endRectX = Math.round(currentPos[0] / zoom) + 0.5;
+ endRectY = Math.round(currentPos[1] / zoom) + 0.5;
+
+ // Inverting end and start (start must always be the top left corner)
+ if (endRectX < startRectX) {
+ let tmp = endRectX;
+ endRectX = startRectX;
+ startRectX = tmp;
+ }
+ // Same for the y
+ if (endRectY < startRectY) {
+ let tmp = endRectY;
+ endRectY = startRectY;
+ startRectY = tmp;
+ }
+
+ let hexColor = hexToRgb(currentLayer.context.fillStyle);
+
+ // Resetting this
+ isDrawingRect = false;
+ // Drawing the rect
+ startRectY -= 0.5;
+ endRectY -= 0.5;
+ endRectX -= 0.5;
+ startRectX -= 0.5;
+
+ currentLayer.context.lineWidth = rectangleSize;
+ currentLayer.context.fillStyle = currentGlobalColor;
+
+ line(startRectX, startRectY, endRectX, startRectY, rectangleSize);
+ line(endRectX, startRectY, endRectX, endRectY, rectangleSize);
+ line(endRectX, endRectY, startRectX, endRectY, rectangleSize);
+ line(startRectX, endRectY, startRectX, startRectY, rectangleSize);
+
+ if (drawMode == 'fill') {
+ currentLayer.context.fillRect(startRectX, startRectY, endRectX - startRectX, endRectY - startRectY);
+ }
+
+ // Clearing the vfx canvas
+ vfxContext.clearRect(0, 0, VFXCanvas.width, VFXCanvas.height);
+}
+
+function drawRectangle(x, y) {
+ // Getting the vfx context
+ let vfxContext = VFXCanvas.getContext("2d");
+
+ // Clearing the vfx canvas
+ vfxContext.clearRect(0, 0, VFXCanvas.width, VFXCanvas.height);
+
+ // Drawing the rect
+ vfxContext.lineWidth = rectangleSize;
+ vfxContext.strokeStyle = currentGlobalColor;
+
+ // Drawing the rect
+ vfxContext.beginPath();
+ if ((rectangleSize % 2 ) == 0) {
+ vfxContext.rect(startRectX - 0.5, startRectY - 0.5, x - startRectX, y - startRectY);
+ }
+ else {
+ vfxContext.rect(startRectX, startRectY, x - startRectX, y - startRectY);
+ }
+
+ vfxContext.setLineDash([]);
+
+ vfxContext.stroke();
+}
+
+function setRectToolSvg() {
+ if (drawMode == 'empty') {
+ emptySVG.setAttribute("display", "visible");
+ fullSVG.setAttribute("display", "none");
+ }
+ else {
+ emptySVG.setAttribute("display", "none");
+ fullSVG.setAttribute("display", "visible");
+ }
+}
+
+function applyChanges() {
+ VFXCanvas.style.zIndex = MIN_Z_INDEX;
+}
\ No newline at end of file
diff --git a/js/_toolButtons.js b/js/_toolButtons.js
index b96c953..6c846d1 100644
--- a/js/_toolButtons.js
+++ b/js/_toolButtons.js
@@ -32,6 +32,36 @@ on('click',"eraser-smaller-button", function(e){
updateCursor();
}, false);
+// rectangle
+on('click',"rectangle-button", function(){
+ // If the user clicks twice on the button, they change the draw mode
+ if (currentTool == 'rectangle') {
+ if (drawMode == 'empty') {
+ drawMode = 'fill';
+ setRectToolSvg();
+ }
+ else {
+ drawMode = 'empty';
+ setRectToolSvg();
+ }
+ }
+ else {
+ changeTool('rectangle');
+ }
+}, false);
+
+// rectangle bigger
+on('click',"rectangle-bigger-button", function(){
+ rectangleSize++;
+ updateCursor();
+}, false);
+
+// rectangle smaller
+on('click',"rectangle-smaller-button", function(e){
+ if(rectangleSize > 1) rectangleSize--;
+ updateCursor();
+}, false);
+
//fill
on('click',"fill-button", function(){
changeTool('fill');
@@ -69,4 +99,9 @@ on('click',"zoom-out-button", function(){
for (let i=1; i
+
@@ -10,7 +16,6 @@
{{{google-analytics}}}
{{{favicons}}}
-
@@ -34,7 +39,10 @@
+
+
+
- Lospec Pixel Editor
-
@@ -70,19 +78,32 @@
+
+ -
+
+
+
+
+
+ -
+
+
+
+
+
+
+
-
- -
-
-
-
-
+
+ -