Finished commenting the editor

Also cleaned a few things, removed some unused variables
This commit is contained in:
unsettledgames 2020-12-31 16:47:56 +01:00
parent 9ef0e6ecea
commit fb1200162e
19 changed files with 299 additions and 100 deletions

View File

@ -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);

View File

@ -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.'
}
}

View File

@ -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++) {

View File

@ -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

View File

@ -1,4 +1,4 @@
//get cursor position relative to canvas
//gets cursor position relative to canvas
function getCursorPosition(e) {
var x;
var y;

View File

@ -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

View File

@ -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;

View File

@ -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'))

View File

@ -1,3 +1,5 @@
// NEXTPULL: to remove when the new palette system is added
/**
* jscolor - JavaScript Color Picker
*

View File

@ -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() {

View File

@ -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);

View File

@ -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') {

View File

@ -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();
}

View File

@ -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;

View File

@ -1,4 +1,4 @@
//prests
//presets
var presets = {
'Gameboy Color': {
width: 240,

View File

@ -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);

View File

@ -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');

View File

@ -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) {

View File

@ -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;
}