mirror of
https://github.com/lospec/pixel-editor.git
synced 2023-08-10 21:12:51 +03:00
Finished commenting the editor
Also cleaned a few things, removed some unused variables
This commit is contained in:
parent
9ef0e6ecea
commit
fb1200162e
@ -1,4 +1,4 @@
|
||||
//draw a line between two points on canvas
|
||||
//draws a line between two points on canvas
|
||||
function line(x0,y0,x1,y1, brushSize) {
|
||||
var dx = Math.abs(x1-x0);
|
||||
var dy = Math.abs(y1-y0);
|
||||
|
@ -3,7 +3,7 @@ let modes = {
|
||||
description: 'Basic mode is perfect if you want to create simple sprites or try out palettes.'
|
||||
},
|
||||
'Advanced' : {
|
||||
description: 'Choose advanced mode to gain access to features such as layers.'
|
||||
description: 'Choose advanced mode to gain access to more advanced features such as layers.'
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,8 +18,6 @@ for (var i = 1; i < mainMenuItems.length; i++) {
|
||||
var subMenu = menuItem.children[1];
|
||||
var subMenuItems = subMenu.children;
|
||||
|
||||
|
||||
|
||||
//when you click an item within a menu button
|
||||
for (var j = 0; j < subMenuItems.length; j++) {
|
||||
|
||||
|
@ -7,11 +7,6 @@ function fill(cursorLocation) {
|
||||
tempImage.data[pixelPos + 1] = fillColor.g;
|
||||
tempImage.data[pixelPos + 2] = fillColor.b;
|
||||
tempImage.data[pixelPos + 3] = 255;
|
||||
/*
|
||||
tempImage.data[pixelPos] = fillColor.r;
|
||||
tempImage.data[pixelPos + 1] = fillColor.g;
|
||||
tempImage.data[pixelPos + 2] = fillColor.b;
|
||||
*/
|
||||
}
|
||||
|
||||
//change x y to color value passed from the function and use that as the original color
|
||||
|
@ -1,4 +1,4 @@
|
||||
//get cursor position relative to canvas
|
||||
//gets cursor position relative to canvas
|
||||
function getCursorPosition(e) {
|
||||
var x;
|
||||
var y;
|
||||
|
@ -1,3 +1,17 @@
|
||||
/** How the history works
|
||||
* - undoStates stores the states that can be undone
|
||||
* - redoStates stores the states that can be redone
|
||||
* - undo() undoes an action and adds it to the redoStates
|
||||
* - redo() redoes an action and adds it to the undoStates
|
||||
* - Each HistoryState must implement an undo() and redo() function
|
||||
* Those functions actually implement the undo and redo mechanism for that action,
|
||||
* so you'll need to save the data you need as attributes in the constructor. For example,
|
||||
* for the HistoryStateAddColour, the added colour is saved so that it can be removed in
|
||||
* undo() or added back in redo().
|
||||
* - Each HistoryState must call saveHistoryState(this) so that it gets added to the stack
|
||||
*
|
||||
*/
|
||||
|
||||
var undoStates = [];
|
||||
var redoStates = [];
|
||||
|
||||
@ -473,8 +487,6 @@ function saveHistoryState (state) {
|
||||
}
|
||||
|
||||
function undo () {
|
||||
//console.log('%cundo', undoLogStyle);
|
||||
|
||||
//if there are any states saved to undo
|
||||
if (undoStates.length > 0) {
|
||||
document.getElementById('redo-button').classList.remove('disabled');
|
||||
@ -496,8 +508,6 @@ function undo () {
|
||||
}
|
||||
|
||||
function redo () {
|
||||
//console.log('%credo', undoLogStyle);
|
||||
|
||||
if (redoStates.length > 0) {
|
||||
|
||||
//enable undo button
|
||||
|
@ -1,5 +1,9 @@
|
||||
var spacePressed = false;
|
||||
|
||||
/** Just listens to hotkeys and calls the linked functions
|
||||
*
|
||||
* @param {*} e
|
||||
*/
|
||||
function KeyPress(e) {
|
||||
var keyboardEvent = window.event? event : e;
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
// NEXTPULL: to remove when the new palette system is added
|
||||
|
||||
//format a color button
|
||||
|
||||
//formats a color button
|
||||
function initColor (colorElement) {
|
||||
//console.log('initColor()');
|
||||
//console.log(document.getElementById('jscolor-hex-input'))
|
||||
|
@ -1,3 +1,5 @@
|
||||
// NEXTPULL: to remove when the new palette system is added
|
||||
|
||||
/**
|
||||
* jscolor - JavaScript Color Picker
|
||||
*
|
||||
|
55
js/_layer.js
55
js/_layer.js
@ -1,31 +1,27 @@
|
||||
/** TODO LIST FOR LAYERS
|
||||
|
||||
GENERAL REQUIREMENTS:
|
||||
- Saving the state of an artwork to a .lospec file so that people can work on it later keeping
|
||||
the layers they created? That'd be cool, even for the app users, that could just double click on a lospec
|
||||
file for it to be opened right in the pixel editor
|
||||
|
||||
OPTIONAL:
|
||||
|
||||
1 - Fix issues
|
||||
*/
|
||||
|
||||
// HTML element that contains the layer entries
|
||||
let layerList;
|
||||
// A single layer entry (used as a prototype to create the new ones)
|
||||
let layerListEntry;
|
||||
|
||||
// NEXTPULL: remove the drag n drop system and use Sortable.js instead
|
||||
let layerDragSource = null;
|
||||
|
||||
// Number of layers at the beginning
|
||||
let layerCount = 1;
|
||||
// Current max z index (so that I know which z-index to assign to new layers)
|
||||
let maxZIndex = 3;
|
||||
|
||||
// When a layer is deleted, its id is added to this array and can be reused
|
||||
let unusedIDs = [];
|
||||
// Id for the next added layer
|
||||
let currentID = layerCount;
|
||||
let idToDelete;
|
||||
// Layer menu
|
||||
let layerOptions = document.getElementById("layer-properties-menu");
|
||||
|
||||
// Is the user currently renaming a layer?
|
||||
let isRenamingLayer = false;
|
||||
// I need to save this, trust me
|
||||
let oldLayerName = null;
|
||||
|
||||
// Binding the add layer button to the function
|
||||
on('click',"add-layer-button", addLayer, false);
|
||||
|
||||
/** Handler class for a single canvas (a single layer)
|
||||
@ -54,6 +50,7 @@ class Layer {
|
||||
|
||||
this.id = "layer" + id;
|
||||
|
||||
// Binding the events
|
||||
if (menuEntry != null) {
|
||||
this.name = menuEntry.getElementsByTagName("p")[0].innerHTML;
|
||||
menuEntry.id = "layer" + id;
|
||||
@ -78,20 +75,13 @@ class Layer {
|
||||
|
||||
// Initializes the canvas
|
||||
initialize() {
|
||||
/*
|
||||
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
|
||||
//show canvas
|
||||
this.canvas.style.display = 'block';
|
||||
|
||||
//center canvas in window
|
||||
@ -103,7 +93,7 @@ class Layer {
|
||||
}
|
||||
|
||||
hover() {
|
||||
// Hide all the layers but the current one
|
||||
// Hides all the layers but the current one
|
||||
for (let i=1; i<layers.length - nAppLayers; i++) {
|
||||
if (layers[i] !== this) {
|
||||
layers[i].canvas.style.opacity = 0.3;
|
||||
@ -112,7 +102,7 @@ class Layer {
|
||||
}
|
||||
|
||||
unhover() {
|
||||
// Show all the layers again
|
||||
// Shows all the layers again
|
||||
for (let i=1; i<layers.length - nAppLayers; i++) {
|
||||
if (layers[i] !== this) {
|
||||
layers[i].canvas.style.opacity = 1;
|
||||
@ -127,6 +117,7 @@ class Layer {
|
||||
}
|
||||
}
|
||||
|
||||
// NEXTPULL: remove this
|
||||
layerDragStart(element) {
|
||||
layerDragSource = this;
|
||||
element.dataTransfer.effectAllowed = 'move';
|
||||
@ -135,6 +126,7 @@ class Layer {
|
||||
this.classList.add('dragElem');
|
||||
}
|
||||
|
||||
// NEXTPULL: remove this
|
||||
layerDragOver(element) {
|
||||
if (element.preventDefault) {
|
||||
element.preventDefault(); // Necessary. Allows us to drop.
|
||||
@ -146,10 +138,12 @@ class Layer {
|
||||
return false;
|
||||
}
|
||||
|
||||
// NEXTPULL: remove this
|
||||
layerDragLeave(element) {
|
||||
this.classList.remove('layerdragover');
|
||||
}
|
||||
|
||||
// NEXTPULL: remove this
|
||||
layerDragDrop(element) {
|
||||
if (element.stopPropagation) {
|
||||
element.stopPropagation(); // Stops some browsers from redirecting.
|
||||
@ -169,6 +163,7 @@ class Layer {
|
||||
return false;
|
||||
}
|
||||
|
||||
// NEXTPULL: remove this
|
||||
layerDragEnd(element) {
|
||||
this.classList.remove('layerdragover');
|
||||
}
|
||||
@ -217,20 +212,20 @@ class Layer {
|
||||
|
||||
openOptionsMenu(event) {
|
||||
if (event.which == 3) {
|
||||
let selectedId;
|
||||
let target = event.target;
|
||||
let offsets = getElementAbsolutePosition(this);
|
||||
|
||||
while (target != null && target.classList != null && !target.classList.contains("layers-menu-entry")) {
|
||||
target = target.parentElement;
|
||||
}
|
||||
|
||||
idToDelete = target.id;
|
||||
selectedId = target.id;
|
||||
|
||||
layerOptions.style.visibility = "visible";
|
||||
layerOptions.style.top = "0";
|
||||
layerOptions.style.marginTop = "" + (event.clientY - 25) + "px";
|
||||
|
||||
getLayerByID(idToDelete).selectLayer();
|
||||
getLayerByID(selectedId).selectLayer();
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,10 +260,6 @@ class Layer {
|
||||
layer.menuEntry.classList.add("selected-layer");
|
||||
currentLayer = layer;
|
||||
}
|
||||
/*
|
||||
canvas = currentLayer.canvas;
|
||||
context = currentLayer.context;
|
||||
*/
|
||||
}
|
||||
|
||||
toggleLock() {
|
||||
|
@ -1,14 +1,23 @@
|
||||
/** Loads a file (.png or .lpe)
|
||||
*
|
||||
*/
|
||||
document.getElementById('open-image-browse-holder').addEventListener('change', function () {
|
||||
let fileName = document.getElementById("open-image-browse-holder").value;
|
||||
// Getting the extension
|
||||
let extension = fileName.substring(fileName.lastIndexOf('.')+1, fileName.length) || fileName;
|
||||
|
||||
// I didn't write this check and I have no idea what it does
|
||||
if (this.files && this.files[0]) {
|
||||
// Btw, checking if the extension is supported
|
||||
if (extension == 'png' || extension == 'gif' || extension == 'lpe') {
|
||||
// If it's a Lospec Pixel Editor tm file, I load the project
|
||||
if (extension == 'lpe') {
|
||||
let file = this.files[0];
|
||||
let reader = new FileReader();
|
||||
|
||||
// Getting all the data
|
||||
reader.readAsText(file, "UTF-8");
|
||||
// Converting the data to a json object and creating a new pixel (see _newPixel.js for more)
|
||||
reader.onload = function (e) {
|
||||
let dictionary = JSON.parse(e.target.result);
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
//this is called when a user picks a file after selecting "load palette" from the new pixel dialogue
|
||||
|
||||
// TODO: load palette from .lpe file
|
||||
document.getElementById('load-palette-browse-holder').addEventListener('change', function () {
|
||||
if (this.files && this.files[0]) {
|
||||
|
||||
//make sure file is allowed filetype
|
||||
var fileContentType = this.files[0].type;
|
||||
if (fileContentType == 'image/png' || fileContentType == 'image/gif') {
|
||||
|
24
js/_move.js
24
js/_move.js
@ -6,9 +6,15 @@ var selectionCanceled = true;
|
||||
var firstTimeMove = true;
|
||||
|
||||
// TODO: move with arrows
|
||||
/** Updates the move preview so that is placed in the right position
|
||||
*
|
||||
* @param {*} mousePosition The position of the cursor
|
||||
*/
|
||||
function updateMovePreview(mousePosition) {
|
||||
// I haven't canceled the selection
|
||||
selectionCanceled = false;
|
||||
|
||||
// If it's the first time that I move the selection, I cut it from its original position
|
||||
if (firstTimeMove) {
|
||||
cutSelection(mousePosition);
|
||||
}
|
||||
@ -18,26 +24,34 @@ function updateMovePreview(mousePosition) {
|
||||
lastMousePos = mousePosition;
|
||||
// clear the entire tmp layer
|
||||
TMPLayer.context.clearRect(0, 0, TMPLayer.canvas.width, TMPLayer.canvas.height);
|
||||
// put the image data with offset
|
||||
// put the image data on the tmp layer with offset
|
||||
TMPLayer.context.putImageData(
|
||||
imageDataToMove,
|
||||
Math.round(lastMousePos[0] / zoom) - imageDataToMove.width / 2,
|
||||
Math.round(lastMousePos[1] / zoom) - imageDataToMove.height / 2);
|
||||
|
||||
lastMovePos = lastMousePos;
|
||||
// Moving the the rectangular ants
|
||||
moveSelection(lastMousePos[0] / zoom, lastMousePos[1] / zoom, imageDataToMove.width, imageDataToMove.height);
|
||||
}
|
||||
|
||||
/** Ends a selection, meaning that it makes the changes definitive and creates the history states
|
||||
*
|
||||
*/
|
||||
function endSelection() {
|
||||
// Clearing the tmp (move preview) and vfx (ants) layers
|
||||
TMPLayer.context.clearRect(0, 0, TMPLayer.canvas.width, TMPLayer.canvas.height);
|
||||
VFXLayer.context.clearRect(0, 0, VFXLayer.canvas.width, VFXLayer.canvas.height);
|
||||
// Preparing an empty imageData with the size of the canvas
|
||||
let cleanImageData = new ImageData(endX - startX, endY - startY);
|
||||
|
||||
// If I was moving something
|
||||
if (imageDataToMove !== undefined) {
|
||||
console.log("definito");
|
||||
// Saving the current clipboard before editing it in order to merge it with the current layer
|
||||
cleanImageData.data.set(imageDataToMove.data);
|
||||
|
||||
// I have to save the underlying data, so that the transparent pixels in the clipboard
|
||||
// don't override the coloured pixels in the canvas
|
||||
let underlyingImageData = currentLayer.context.getImageData(startX, startY, endX - startX, endY - startY);
|
||||
|
||||
for (let i=0; i<underlyingImageData.data.length; i+=4) {
|
||||
@ -51,6 +65,7 @@ function endSelection() {
|
||||
underlyingImageData.data[i+2], underlyingImageData.data[i+3]
|
||||
];
|
||||
|
||||
// If the pixel of the clipboard is empty, but the one below it isn't, I use the pixel below
|
||||
if (isPixelEmpty(currentMovePixel)) {
|
||||
if (!isPixelEmpty(underlyingImageData)) {
|
||||
imageDataToMove.data[i] = currentUnderlyingPixel[0];
|
||||
@ -61,13 +76,16 @@ function endSelection() {
|
||||
}
|
||||
}
|
||||
|
||||
// If I moved the selection before confirming it
|
||||
if (lastMovePos !== undefined) {
|
||||
// I put it in the new position
|
||||
currentLayer.context.putImageData(
|
||||
imageDataToMove,
|
||||
Math.round(lastMovePos[0] / zoom) - imageDataToMove.width / 2,
|
||||
Math.round(lastMovePos[1] / zoom) - imageDataToMove.height / 2);
|
||||
}
|
||||
else {
|
||||
// I put it in the same position
|
||||
currentLayer.context.putImageData(
|
||||
imageDataToMove,
|
||||
copiedStartX,
|
||||
@ -77,6 +95,7 @@ function endSelection() {
|
||||
imageDataToMove.data.set(cleanImageData.data);
|
||||
}
|
||||
|
||||
// Resetting all the flags
|
||||
selectionCanceled = true;
|
||||
isRectSelecting = false;
|
||||
firstTimeMove = true;
|
||||
@ -86,5 +105,6 @@ function endSelection() {
|
||||
lastMovePos = undefined;
|
||||
currentLayer.updateLayerPreview();
|
||||
|
||||
// Saving the history
|
||||
new HistoryStateEditCanvas();
|
||||
}
|
@ -1,27 +1,44 @@
|
||||
// Start colour of the pixel grid (can be changed in the preferences)
|
||||
let pixelGridColor = "#0000FF";
|
||||
// Distance between one line and another in HTML pixels
|
||||
let lineDistance = 12;
|
||||
// The grid is not visible at the beginning
|
||||
let pixelGridVisible = false;
|
||||
// Saving the canvas containing the pixel grid
|
||||
pixelGridCanvas = document.getElementById("pixel-grid");
|
||||
|
||||
/** Shows or hides the pixel grid depening on its current visibility
|
||||
* (triggered by the show pixel grid button in the top menu)
|
||||
*
|
||||
*/
|
||||
function togglePixelGrid(event) {
|
||||
// Getting the button because I have to change its text
|
||||
let button = document.getElementById("toggle-pixelgrid-button");
|
||||
|
||||
// Toggling the state
|
||||
pixelGridVisible = !pixelGridVisible;
|
||||
|
||||
// If it was visible, I hide it
|
||||
if (pixelGridVisible) {
|
||||
button.innerHTML = "Hide pixel grid";
|
||||
pixelGridCanvas.style.display = "inline-block";
|
||||
}
|
||||
// Otherwise, I show it
|
||||
else {
|
||||
button.innerHTML = "Show pixel grid";
|
||||
pixelGridCanvas.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
/** Fills the pixelGridCanvas with the pixelgrid
|
||||
*
|
||||
*/
|
||||
function fillPixelGrid() {
|
||||
let context = pixelGridCanvas.getContext("2d");
|
||||
let originalSize = layers[0].canvasSize;
|
||||
|
||||
// The pixelGridCanvas is lineDistance times bigger so that the lines don't take 1 canvas pixel
|
||||
// (which would cover the whole canvas with the pixelGridColour), but they take 1/lineDistance canvas pixels
|
||||
pixelGridCanvas.width = originalSize[0] * lineDistance;
|
||||
pixelGridCanvas.height = originalSize[1] * lineDistance;
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
//prests
|
||||
//presets
|
||||
var presets = {
|
||||
'Gameboy Color': {
|
||||
width: 240,
|
||||
|
@ -4,6 +4,10 @@ let startY;
|
||||
let endX;
|
||||
let endY;
|
||||
|
||||
/** Starts the selection: saves the canvas, sets the start coordinates
|
||||
*
|
||||
* @param {*} mouseEvent
|
||||
*/
|
||||
function startRectSelection(mouseEvent) {
|
||||
// Saving the canvas
|
||||
new HistoryStateEditCanvas();
|
||||
@ -35,6 +39,10 @@ function startRectSelection(mouseEvent) {
|
||||
selectionCanceled = false;
|
||||
}
|
||||
|
||||
/** Updates the selection
|
||||
*
|
||||
* @param {*} mouseEvent
|
||||
*/
|
||||
function updateRectSelection(mouseEvent) {
|
||||
let pos = getCursorPosition(mouseEvent);
|
||||
|
||||
@ -42,6 +50,10 @@ function updateRectSelection(mouseEvent) {
|
||||
drawRect(Math.round(pos[0] / zoom) + 0.5, Math.round(pos[1] / zoom) + 0.5);
|
||||
}
|
||||
|
||||
/** Ends the selection: sets the end coordiantes
|
||||
*
|
||||
* @param {*} mouseEvent
|
||||
*/
|
||||
function endRectSelection(mouseEvent) {
|
||||
// Getting the end position
|
||||
let currentPos = getCursorPosition(mouseEvent);
|
||||
@ -72,6 +84,10 @@ function endRectSelection(mouseEvent) {
|
||||
currentTool.updateCursor();
|
||||
}
|
||||
|
||||
/** Cuts the selection from its canvas and puts it in the tmp layer so it can be moved
|
||||
*
|
||||
* @param {*} mousePosition
|
||||
*/
|
||||
function cutSelection(mousePosition) {
|
||||
// Getting the selected pixels
|
||||
imageDataToMove = currentLayer.context.getImageData(startX, startY, endX - startX + 1, endY - startY + 1);
|
||||
@ -79,10 +95,13 @@ function cutSelection(mousePosition) {
|
||||
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]];
|
||||
}
|
||||
|
||||
/** Draws a dashed rectangle representing the selection
|
||||
*
|
||||
* @param {*} x Current end x coordinate of the selection
|
||||
* @param {*} y Current end y coordinate of the selection
|
||||
*/
|
||||
function drawRect(x, y) {
|
||||
// Getting the vfx context
|
||||
let vfxContext = VFXCanvas.getContext('2d');
|
||||
@ -106,10 +125,13 @@ function applyChanges() {
|
||||
|
||||
// Checks whether the pointer is inside the selected area or not
|
||||
function cursorInSelectedArea() {
|
||||
// Getting the cursor position
|
||||
let cursorPos = getCursorPosition(currentMouseEvent);
|
||||
// Getting the coordinates relatively to the canvas
|
||||
let x = cursorPos[0] / zoom;
|
||||
let y = cursorPos[1] / zoom;
|
||||
|
||||
// This is to avoid rightX or topY being less than leftX or bottomY
|
||||
let leftX = Math.min(startX, endX);
|
||||
let rightX = Math.max(startX, endX);
|
||||
let topY = Math.max(startY, endY);
|
||||
@ -126,6 +148,13 @@ function cursorInSelectedArea() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Moves the rect ants to the specified position
|
||||
*
|
||||
* @param {*} x X coordinate of the rect ants
|
||||
* @param {*} y Y coordinat of the rect ants
|
||||
* @param {*} width Width of the selection
|
||||
* @param {*} height Height of the selectin
|
||||
*/
|
||||
function moveSelection(x, y, width, height) {
|
||||
// Getting the vfx context
|
||||
let vfxContext = VFXCanvas.getContext('2d');
|
||||
@ -135,6 +164,7 @@ function moveSelection(x, y, width, height) {
|
||||
vfxContext.lineWidth = 1;
|
||||
vfxContext.setLineDash([4]);
|
||||
|
||||
// Fixing the coordinates
|
||||
startX = Math.round(Math.round(x) - 0.5 - Math.round(width / 2)) + 0.5;
|
||||
startY = Math.round(Math.round(y) - 0.5 - Math.round(height / 2)) + 0.5;
|
||||
endX = startX + Math.round(width);
|
||||
|
@ -1,16 +1,23 @@
|
||||
|
||||
// Saving the empty rect svg
|
||||
var emptySVG = document.getElementById("empty-button-svg");
|
||||
// and the full rect svg so that I can change them when the user changes rect modes
|
||||
var fullSVG = document.getElementById("full-button-svg");
|
||||
|
||||
// The start mode is empty rectangle
|
||||
var drawMode = 'empty';
|
||||
// I'm not drawing a rectangle at the beginning
|
||||
var isDrawingRect = false;
|
||||
|
||||
// Rect coordinates
|
||||
let startRectX;
|
||||
let startRectY;
|
||||
let endRectX;
|
||||
let endRectY;
|
||||
|
||||
|
||||
/** Starts drawing the rect, saves the start coordinates
|
||||
*
|
||||
* @param {*} mouseEvent
|
||||
*/
|
||||
function startRectDrawing(mouseEvent) {
|
||||
// Putting the vfx layer on top of everything
|
||||
VFXCanvas.style.zIndex = MAX_Z_INDEX;
|
||||
@ -25,13 +32,22 @@ function startRectDrawing(mouseEvent) {
|
||||
drawRectangle(startRectX, startRectY);
|
||||
}
|
||||
|
||||
/** Updates the rectangle preview depending on the position of the mouse
|
||||
*
|
||||
* @param {*} mouseEvent The mouseEvent from which we'll get the mouse position
|
||||
*/
|
||||
function updateRectDrawing(mouseEvent) {
|
||||
let pos = getCursorPosition(mouseEvent);
|
||||
|
||||
// Drawing the rect
|
||||
// Drawing the rect at the right position
|
||||
drawRectangle(Math.round(pos[0] / zoom) + 0.5, Math.round(pos[1] / zoom) + 0.5);
|
||||
}
|
||||
|
||||
/** Finishes drawing the rect, decides the end coordinates and moves the preview rectangle to the
|
||||
* current layer
|
||||
*
|
||||
* @param {*} mouseEvent event from which we'll get the mouse position
|
||||
*/
|
||||
function endRectDrawing(mouseEvent) {
|
||||
// Getting the end position
|
||||
let currentPos = getCursorPosition(mouseEvent);
|
||||
@ -61,14 +77,17 @@ function endRectDrawing(mouseEvent) {
|
||||
endRectX -= 0.5;
|
||||
startRectX -= 0.5;
|
||||
|
||||
// Setting the correct linewidth and colour
|
||||
currentLayer.context.lineWidth = tool.rectangle.brushSize;
|
||||
currentLayer.context.fillStyle = currentGlobalColor;
|
||||
|
||||
// Drawing the rect using 4 lines
|
||||
line(startRectX, startRectY, endRectX, startRectY, tool.rectangle.brushSize);
|
||||
line(endRectX, startRectY, endRectX, endRectY, tool.rectangle.brushSize);
|
||||
line(endRectX, endRectY, startRectX, endRectY, tool.rectangle.brushSize);
|
||||
line(startRectX, endRectY, startRectX, startRectY, tool.rectangle.brushSize);
|
||||
|
||||
// If I have to fill it, I do so
|
||||
if (drawMode == 'fill') {
|
||||
currentLayer.context.fillRect(startRectX, startRectY, endRectX - startRectX, endRectY - startRectY);
|
||||
}
|
||||
@ -77,6 +96,12 @@ function endRectDrawing(mouseEvent) {
|
||||
vfxContext.clearRect(0, 0, VFXCanvas.width, VFXCanvas.height);
|
||||
}
|
||||
|
||||
/** Draws a rectangle with end coordinates given by x and y on the VFX layer (draws
|
||||
* the preview for the rectangle tool)
|
||||
*
|
||||
* @param {*} x The current end x of the rectangle
|
||||
* @param {*} y The current end y of the rectangle
|
||||
*/
|
||||
function drawRectangle(x, y) {
|
||||
// Getting the vfx context
|
||||
let vfxContext = VFXCanvas.getContext("2d");
|
||||
@ -98,10 +123,12 @@ function drawRectangle(x, y) {
|
||||
}
|
||||
|
||||
vfxContext.setLineDash([]);
|
||||
|
||||
vfxContext.stroke();
|
||||
}
|
||||
|
||||
/** Sets the correct tool icon depending on its mode
|
||||
*
|
||||
*/
|
||||
function setRectToolSvg() {
|
||||
if (drawMode == 'empty') {
|
||||
emptySVG.setAttribute('display', 'visible');
|
||||
|
@ -1,16 +1,31 @@
|
||||
let resizeCanvasContainer = document.getElementById("resize-canvas");
|
||||
let rcPivot = "middle";
|
||||
let currentPivotObject;
|
||||
let borders = {left: 0, right: 0, top: 0, bottom: 0};
|
||||
/* This scripts contains all the code used to handle the canvas resizing */
|
||||
|
||||
// Resize canvas pop up window
|
||||
let resizeCanvasContainer = document.getElementById("resize-canvas");
|
||||
// Start pivot
|
||||
let rcPivot = "middle";
|
||||
// Selected pivot button
|
||||
let currentPivotObject;
|
||||
// Border offsets
|
||||
let rcBorders = {left: 0, right: 0, top: 0, bottom: 0};
|
||||
|
||||
/** Opens the canvas resize window
|
||||
*
|
||||
*/
|
||||
function openResizeCanvasWindow() {
|
||||
// Initializes the inputs
|
||||
initResizeCanvasInputs();
|
||||
showDialogue('resize-canvas');
|
||||
}
|
||||
|
||||
/** Initializes the canvas resizing input
|
||||
*
|
||||
*/
|
||||
function initResizeCanvasInputs() {
|
||||
// Getting the pivot buttons
|
||||
let buttons = document.getElementsByClassName("pivot-button");
|
||||
|
||||
// Adding the event handlers for them
|
||||
for (let i=0; i<buttons.length; i++) {
|
||||
buttons[i].addEventListener("click", changePivot);
|
||||
if (buttons[i].getAttribute("value").includes("middle")) {
|
||||
@ -33,13 +48,21 @@ function initResizeCanvasInputs() {
|
||||
console.log("Pivot selezionato: " + currentPivotObject);
|
||||
}
|
||||
|
||||
/** Fired when a border offset is changed: it updates the width and height
|
||||
*
|
||||
* @param {*} event
|
||||
*/
|
||||
function rcChangedBorder(event) {
|
||||
rcUpdateBorders();
|
||||
|
||||
document.getElementById("rc-width").value = parseInt(layers[0].canvasSize[0]) + borders.left + borders.right;
|
||||
document.getElementById("rc-height").value = parseInt(layers[0].canvasSize[1]) + borders.top + borders.bottom;
|
||||
document.getElementById("rc-width").value = parseInt(layers[0].canvasSize[0]) + rcBorders.left + rcBorders.right;
|
||||
document.getElementById("rc-height").value = parseInt(layers[0].canvasSize[1]) + rcBorders.top + rcBorders.bottom;
|
||||
}
|
||||
|
||||
/** Fired when width or height are changed: updates the border offsets
|
||||
*
|
||||
* @param {*} event
|
||||
*/
|
||||
function rcChangedSize(event) {
|
||||
let widthOffset = Math.abs(document.getElementById("rc-width").value) - layers[0].canvasSize[0];
|
||||
let heightOffset = Math.abs(document.getElementById("rc-height").value) - layers[0].canvasSize[1];
|
||||
@ -54,12 +77,19 @@ function rcChangedSize(event) {
|
||||
document.getElementById("rc-border-top").value = top;
|
||||
document.getElementById("rc-border-bottom").value = bottom;
|
||||
|
||||
borders.left = left;
|
||||
borders.right = right;
|
||||
borders.top = top;
|
||||
borders.bottom = bottom;
|
||||
rcBorders.left = left;
|
||||
rcBorders.right = right;
|
||||
rcBorders.top = top;
|
||||
rcBorders.bottom = bottom;
|
||||
}
|
||||
|
||||
/** Resizes the canvas
|
||||
*
|
||||
* @param {*} event The event that triggered the canvas resizing
|
||||
* @param {*} size The new size of the picture
|
||||
* @param {*} customData Used when ctrl+z ing
|
||||
* @param {*} saveHistory Should I save the history? You shouldn't if you're undoing
|
||||
*/
|
||||
function resizeCanvas(event, size, customData, saveHistory = true) {
|
||||
let imageDatas = [];
|
||||
let leftOffset = 0;
|
||||
@ -87,8 +117,8 @@ function resizeCanvas(event, size, customData, saveHistory = true) {
|
||||
if (saveHistory) {
|
||||
// Saving history
|
||||
new HistoryStateResizeCanvas(
|
||||
{x: parseInt(layers[0].canvasSize[0]) + borders.left + borders.right,
|
||||
y: parseInt(layers[0].canvasSize[1]) + borders.top + borders.bottom},
|
||||
{x: parseInt(layers[0].canvasSize[0]) + rcBorders.left + rcBorders.right,
|
||||
y: parseInt(layers[0].canvasSize[1]) + rcBorders.top + rcBorders.bottom},
|
||||
|
||||
{x: layers[0].canvasSize[0],
|
||||
y: layers[0].canvasSize[1]},
|
||||
@ -100,8 +130,8 @@ function resizeCanvas(event, size, customData, saveHistory = true) {
|
||||
|
||||
// Resize the canvases
|
||||
for (let i=0; i<layers.length; i++) {
|
||||
layers[i].canvasSize[0] = parseInt(layers[i].canvasSize[0]) + borders.left + borders.right;
|
||||
layers[i].canvasSize[1] = parseInt(layers[i].canvasSize[1]) + borders.top + borders.bottom;
|
||||
layers[i].canvasSize[0] = parseInt(layers[i].canvasSize[0]) + rcBorders.left + rcBorders.right;
|
||||
layers[i].canvasSize[1] = parseInt(layers[i].canvasSize[1]) + rcBorders.top + rcBorders.bottom;
|
||||
|
||||
layers[i].canvas.width = layers[i].canvasSize[0];
|
||||
layers[i].canvas.height = layers[i].canvasSize[1];
|
||||
@ -121,42 +151,43 @@ function resizeCanvas(event, size, customData, saveHistory = true) {
|
||||
topOffset = 0;
|
||||
break;
|
||||
case 'top':
|
||||
leftOffset = (borders.left + borders.right) / 2;
|
||||
leftOffset = (rcBorders.left + rcBorders.right) / 2;
|
||||
topOffset = 0;
|
||||
break;
|
||||
case 'topright':
|
||||
leftOffset = borders.left + borders.right;
|
||||
leftOffset = rcBorders.left + rcBorders.right;
|
||||
topOffset = 0;
|
||||
break;
|
||||
case 'left':
|
||||
leftOffset = 0;
|
||||
topOffset = (borders.top + borders.bottom) / 2;
|
||||
topOffset = (rcBorders.top + rcBorders.bottom) / 2;
|
||||
break;
|
||||
case 'middle':
|
||||
leftOffset = (borders.left + borders.right) / 2;
|
||||
topOffset = (borders.top + borders.bottom) / 2;
|
||||
leftOffset = (rcBorders.left + rcBorders.right) / 2;
|
||||
topOffset = (rcBorders.top + rcBorders.bottom) / 2;
|
||||
break;
|
||||
case 'right':
|
||||
leftOffset = borders.left + borders.right;
|
||||
topOffset = (borders.top + borders.bottom) / 2;
|
||||
leftOffset = rcBorders.left + rcBorders.right;
|
||||
topOffset = (rcBorders.top + rcBorders.bottom) / 2;
|
||||
break;
|
||||
case 'bottomleft':
|
||||
leftOffset = 0;
|
||||
topOffset = borders.top + borders.bottom;
|
||||
topOffset = rcBorders.top + rcBorders.bottom;
|
||||
break;
|
||||
case 'bottom':
|
||||
leftOffset = (borders.left + borders.right) / 2;
|
||||
topOffset = borders.top + borders.bottom;
|
||||
leftOffset = (rcBorders.left + rcBorders.right) / 2;
|
||||
topOffset = rcBorders.top + rcBorders.bottom;
|
||||
break;
|
||||
case 'bottomright':
|
||||
leftOffset = borders.left + borders.right;
|
||||
topOffset = borders.top + borders.bottom;
|
||||
leftOffset = rcBorders.left + rcBorders.right;
|
||||
topOffset = rcBorders.top + rcBorders.bottom;
|
||||
break;
|
||||
default:
|
||||
console.log('Pivot does not exist, please report an issue at https://github.com/lospec/pixel-editor');
|
||||
break;
|
||||
}
|
||||
|
||||
// Putting all the data for each layer with the right offsets (decided by the pivot)
|
||||
for (let i=0; i<layers.length; i++) {
|
||||
if (layers[i].menuEntry != null) {
|
||||
if (customData == undefined) {
|
||||
@ -176,6 +207,11 @@ function resizeCanvas(event, size, customData, saveHistory = true) {
|
||||
closeDialogue();
|
||||
}
|
||||
|
||||
/** Trims the canvas so tat the sprite is perfectly contained in it
|
||||
*
|
||||
* @param {*} event
|
||||
* @param {*} saveHistory Should I save the history? You shouldn't if you're undoing
|
||||
*/
|
||||
function trimCanvas(event, saveHistory) {
|
||||
let minY = Infinity;
|
||||
let minX = Infinity;
|
||||
@ -189,6 +225,7 @@ function trimCanvas(event, saveHistory) {
|
||||
rcPivot = "topleft";
|
||||
console.log("debug");
|
||||
|
||||
// Computing the min and max coordinates in which there's a non empty pixel
|
||||
for (let i=1; i<layers.length - nAppLayers; i++) {
|
||||
let imageData = layers[i].context.getImageData(0, 0, layers[0].canvasSize[0], layers[0].canvasSize[1]);
|
||||
let pixelPosition;
|
||||
@ -226,10 +263,11 @@ function trimCanvas(event, saveHistory) {
|
||||
minY = layers[0].canvasSize[1] - minY;
|
||||
maxY = layers[0].canvasSize[1] - maxY;
|
||||
|
||||
borders.right = (maxX - layers[0].canvasSize[0]) + 1;
|
||||
borders.left = -minX;
|
||||
borders.top = maxY - layers[0].canvasSize[1] + 1;
|
||||
borders.bottom = -minY;
|
||||
// Setting the borders coherently with the values I've just computed
|
||||
rcBorders.right = (maxX - layers[0].canvasSize[0]) + 1;
|
||||
rcBorders.left = -minX;
|
||||
rcBorders.top = maxY - layers[0].canvasSize[1] + 1;
|
||||
rcBorders.bottom = -minY;
|
||||
|
||||
// Saving the data
|
||||
for (let i=0; i<layers.length; i++) {
|
||||
@ -241,11 +279,12 @@ function trimCanvas(event, saveHistory) {
|
||||
console.log(imageDatas);
|
||||
//console.log("sx: " + borders.left + "dx: " + borders.right + "top: " + borders.top + "btm: " + borders.bottom);
|
||||
|
||||
document.getElementById("rc-border-left").value = borders.left;
|
||||
document.getElementById("rc-border-right").value = borders.right;
|
||||
document.getElementById("rc-border-top").value = borders.top;
|
||||
document.getElementById("rc-border-bottom").value = borders.bottom;
|
||||
document.getElementById("rc-border-left").value = rcBorders.left;
|
||||
document.getElementById("rc-border-right").value = rcBorders.right;
|
||||
document.getElementById("rc-border-top").value = rcBorders.top;
|
||||
document.getElementById("rc-border-bottom").value = rcBorders.bottom;
|
||||
|
||||
// Resizing the canvas with the decided border offsets
|
||||
resizeCanvas(null, null, imageDatas.slice(), historySave);
|
||||
// Resetting the previous pivot
|
||||
rcPivot = prevPivot;
|
||||
@ -253,16 +292,16 @@ function trimCanvas(event, saveHistory) {
|
||||
|
||||
function rcUpdateBorders() {
|
||||
// Getting input
|
||||
borders.left = document.getElementById("rc-border-left").value;
|
||||
borders.right = document.getElementById("rc-border-right").value;
|
||||
borders.top = document.getElementById("rc-border-top").value;
|
||||
borders.bottom = document.getElementById("rc-border-bottom").value;
|
||||
rcBorders.left = document.getElementById("rc-border-left").value;
|
||||
rcBorders.right = document.getElementById("rc-border-right").value;
|
||||
rcBorders.top = document.getElementById("rc-border-top").value;
|
||||
rcBorders.bottom = document.getElementById("rc-border-bottom").value;
|
||||
|
||||
// Validating input
|
||||
borders.left == "" ? borders.left = 0 : borders.left = Math.round(parseInt(borders.left));
|
||||
borders.right == "" ? borders.right = 0 : borders.right = Math.round(parseInt(borders.right));
|
||||
borders.top == "" ? borders.top = 0 : borders.top = Math.round(parseInt(borders.top));
|
||||
borders.bottom == "" ? borders.bottom = 0 : borders.bottom = Math.round(parseInt(borders.bottom));
|
||||
rcBorders.left == "" ? rcBorders.left = 0 : rcBorders.left = Math.round(parseInt(rcBorders.left));
|
||||
rcBorders.right == "" ? rcBorders.right = 0 : rcBorders.right = Math.round(parseInt(rcBorders.right));
|
||||
rcBorders.top == "" ? rcBorders.top = 0 : rcBorders.top = Math.round(parseInt(rcBorders.top));
|
||||
rcBorders.bottom == "" ? rcBorders.bottom = 0 : rcBorders.bottom = Math.round(parseInt(rcBorders.bottom));
|
||||
}
|
||||
|
||||
function changePivot(event) {
|
||||
|
@ -1,15 +1,26 @@
|
||||
|
||||
/* This scripts contains all the code used to handle the sprite scaling */
|
||||
// Should I keep the sprite ratio?
|
||||
let keepRatio = true;
|
||||
// Used to store the current ratio
|
||||
let currentRatio;
|
||||
// The currenty selected resizing algorithm (nearest-neighbor or bilinear-interpolation)
|
||||
let currentAlgo = 'nearest-neighbor';
|
||||
// Current resize data
|
||||
let data = {width: 0, height: 0, widthPercentage: 100, heightPercentage: 100};
|
||||
// Start resize data
|
||||
let startData = {width: 0, height:0, widthPercentage: 100, heightPercentage: 100};
|
||||
|
||||
/** Opens the sprite resizing window
|
||||
*
|
||||
*/
|
||||
function openResizeSpriteWindow() {
|
||||
// Inits the sprie resize inputs
|
||||
initResizeSpriteInputs();
|
||||
|
||||
// Computing the current ratio
|
||||
currentRatio = layers[0].canvasSize[0] / layers[0].canvasSize[1];
|
||||
|
||||
// Initializing the input fields
|
||||
data.width = layers[0].canvasSize[0];
|
||||
data.height = layers[1].canvasSize[1];
|
||||
|
||||
@ -18,9 +29,13 @@ function openResizeSpriteWindow() {
|
||||
startData.heightPercentage = 100;
|
||||
startData.widthPercentage = 100;
|
||||
|
||||
// Opening the pop up now that it's ready
|
||||
showDialogue('resize-sprite');
|
||||
}
|
||||
|
||||
/** Initalizes the input values and binds the elements to their events
|
||||
*
|
||||
*/
|
||||
function initResizeSpriteInputs() {
|
||||
document.getElementById("rs-width").value = layers[0].canvasSize[0];
|
||||
document.getElementById("rs-height").value = layers[0].canvasSize[1];
|
||||
@ -40,11 +55,21 @@ function initResizeSpriteInputs() {
|
||||
document.getElementById("resize-algorithm-combobox").addEventListener("change", changedAlgorithm);
|
||||
}
|
||||
|
||||
/** Resizes (scales) the sprite
|
||||
*
|
||||
* @param {*} event
|
||||
* @param {*} ratio Keeps infos about the x ratio and y ratio
|
||||
*/
|
||||
function resizeSprite(event, ratio) {
|
||||
// Old data
|
||||
let oldWidth, oldHeight;
|
||||
// New data
|
||||
let newWidth, newHeight;
|
||||
// Current imageDatas
|
||||
let rsImageDatas = [];
|
||||
// Index that will be used a few lines below
|
||||
let layerIndex = 0;
|
||||
// Copy of the imageDatas that will be stored in the history
|
||||
let imageDatasCopy = [];
|
||||
|
||||
oldWidth = layers[0].canvasSize[0];
|
||||
@ -70,6 +95,7 @@ function resizeSprite(event, ratio) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Computing newWidth and newHeight
|
||||
if (ratio == null) {
|
||||
newWidth = data.width;
|
||||
newHeight = data.height;
|
||||
@ -88,8 +114,11 @@ function resizeSprite(event, ratio) {
|
||||
}
|
||||
}
|
||||
|
||||
// ratio is null when the user is undoing
|
||||
if (ratio == null) {
|
||||
// Copying the image data
|
||||
imageDatasCopy = rsImageDatas.slice();
|
||||
// Saving the history
|
||||
new HistoryStateResizeSprite(newWidth / oldWidth, newHeight / oldHeight, currentAlgo, imageDatasCopy);
|
||||
}
|
||||
|
||||
@ -124,6 +153,12 @@ function resizeSprite(event, ratio) {
|
||||
closeDialogue();
|
||||
}
|
||||
|
||||
/* Trust me, the math for the functions below works. If you want to optimize them feel free to have a look, though */
|
||||
|
||||
/** Fired when the input field for width is changed. Updates th othe input fields consequently
|
||||
*
|
||||
* @param {*} event
|
||||
*/
|
||||
function changedWidth(event) {
|
||||
let oldValue = data.width;
|
||||
let ratio;
|
||||
@ -150,6 +185,10 @@ function changedWidth(event) {
|
||||
document.getElementById("rs-width-percentage").value = newWidthPerc;
|
||||
}
|
||||
|
||||
/**Fired when the input field for width is changed. Updates the other input fields consequently
|
||||
*
|
||||
* @param {*} event
|
||||
*/
|
||||
function changedHeight(event) {
|
||||
let oldValue = 100;
|
||||
let ratio;
|
||||
@ -176,6 +215,10 @@ function changedHeight(event) {
|
||||
data.heightPercentage = newHeightPerc;
|
||||
}
|
||||
|
||||
/**Fired when the input field for width percentage is changed. Updates the other input fields consequently
|
||||
*
|
||||
* @param {*} event
|
||||
*/
|
||||
function changedWidthPercentage(event) {
|
||||
let oldValue = 100;
|
||||
let ratio;
|
||||
@ -204,6 +247,10 @@ function changedWidthPercentage(event) {
|
||||
data.width = newWidth;
|
||||
}
|
||||
|
||||
/**Fired when the input field for height percentage is changed. Updates the other input fields consequently
|
||||
*
|
||||
* @param {*} event
|
||||
*/
|
||||
function changedHeightPercentage(event) {
|
||||
let oldValue = data.heightPercentage;
|
||||
let ratio;
|
||||
@ -230,10 +277,18 @@ function changedHeightPercentage(event) {
|
||||
data.height = newHeight;
|
||||
}
|
||||
|
||||
/** Toggles the keepRatio value (fired by the checkbox in the pop up window)
|
||||
*
|
||||
* @param {*} event
|
||||
*/
|
||||
function toggleRatio(event) {
|
||||
keepRatio = !keepRatio;
|
||||
}
|
||||
|
||||
/** Changes the scaling algorithm (fired by the combobox in the pop up window)
|
||||
*
|
||||
* @param {*} event
|
||||
*/
|
||||
function changedAlgorithm(event) {
|
||||
currentAlgo = event.target.value;
|
||||
}
|
Loading…
Reference in New Issue
Block a user