Finished refactoring, moved sprite scaling functions in File

This commit is contained in:
unsettledgames 2021-12-06 20:12:57 +01:00
parent b2f5521750
commit 21dd47c2b0
14 changed files with 405 additions and 427 deletions

View File

View File

@ -52,7 +52,6 @@ const EditorState = (() => {
}
function chooseMode() {
console.log("Here");
let prevMode = pixelEditorMode.toLowerCase();
if (pixelEditorMode === "Basic") {

View File

@ -22,6 +22,18 @@ class File {
// Border offsets
rcBorders = {left: 0, right: 0, top: 0, bottom: 0};
// Sprite scaling attributes
// Should I keep the sprite ratio?
keepRatio = true;
// Used to store the current ratio
currentRatio = undefined;
// The currenty selected resizing algorithm (nearest-neighbor or bilinear-interpolation)
currentAlgo = 'nearest-neighbor';
// Current resize data
data = {width: 0, height: 0, widthPercentage: 100, heightPercentage: 100};
// Start resize data
startData = {width: 0, height:0, widthPercentage: 100, heightPercentage: 100};
// Sprite scaling attributes
openResizeCanvasWindow() {
@ -97,7 +109,6 @@ class File {
* @param {*} saveHistory Should I save the history? You shouldn't if you're undoing
*/
resizeCanvas(event, size, customData, saveHistory = true) {
console.log("resizing");
let imageDatas = [];
let leftOffset = 0;
let topOffset = 0;
@ -314,6 +325,368 @@ class File {
this.currentPivotObject = event.target;
this.currentPivotObject.classList.add("rc-selected-pivot");
}
/** Opens the sprite resizing window
*
*/
openResizeSpriteWindow() {
// Inits the sprie resize inputs
this.initResizeSpriteInputs();
// Computing the current ratio
this.currentRatio = currFile.canvasSize[0] / currFile.canvasSize[1];
// Initializing the input fields
this.data.width = currFile.canvasSize[0];
this.data.height = currFile.canvasSize[1];
this.startData.width = parseInt(this.data.width);
this.startData.height = parseInt(this.data.height);
this.startData.heightPercentage = 100;
this.startData.widthPercentage = 100;
// Opening the pop up now that it's ready
Dialogue.showDialogue('resize-sprite');
}
/** Initalizes the input values and binds the elements to their events
*
*/
initResizeSpriteInputs() {
document.getElementById("rs-width").value = currFile.canvasSize[0];
document.getElementById("rs-height").value = currFile.canvasSize[1];
document.getElementById("rs-width-percentage").value = 100;
document.getElementById("rs-height-percentage").value = 100;
document.getElementById("rs-keep-ratio").checked = true;
Events.on("change", "rs-width", this.changedWidth.bind(this));
Events.on("change", "rs-height", this.changedHeight.bind(this));
Events.on("change", "rs-width-percentage", this.changedWidthPercentage.bind(this));
Events.on("change", "rs-height-percentage", this.changedHeightPercentage.bind(this));
Events.on("click", "resize-sprite-confirm", this.resizeSprite.bind(this));
Events.on("click", "rs-keep-ratio", this.toggleRatio.bind(this));
Events.on("change", "resize-algorithm-combobox", this.changedAlgorithm.bind(this));
}
/** Resizes (scales) the sprite
*
* @param {*} event
* @param {*} ratio Keeps infos about the x ratio and y ratio
*/
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 = currFile.canvasSize[0];
oldHeight = currFile.canvasSize[1];
this.rcPivot = "middle";
// Updating values if the user didn't press enter
switch (document.activeElement.id) {
case "rs-width-percentage":
this.changedWidthPercentage();
break;
case "rs-width":
this.changedWidth();
break;
case "rs-height-percentage":
this.changedHeightPercentage();
break;
case "rs-height":
this.changedHeight();
break;
default:
// In this case everything has been updated correctly
break;
}
// Computing newWidth and newHeight
if (ratio == null) {
newWidth = this.data.width;
newHeight = this.data.height;
}
else {
newWidth = currFile.canvasSize[0] * ratio[0];
newHeight = currFile.canvasSize[1] * ratio[1];
}
// Get all the image datas
for (let i=0; i<currFile.layers.length; i++) {
if (currFile.layers[i].hasCanvas()) {
rsImageDatas.push(currFile.layers[i].context.getImageData(
0, 0, currFile.canvasSize[0], currFile.canvasSize[1])
);
}
}
// event is null when the user is undoing
if (event != null) {
// Copying the image data
imageDatasCopy = rsImageDatas.slice();
// Saving the history
new HistoryState().ResizeSprite(newWidth / oldWidth, newHeight / oldHeight, this.currentAlgo, imageDatasCopy);
}
// Resizing the canvas
currFile.resizeCanvas(null, {x: newWidth, y: newHeight});
// Put the image datas on the new canvases
for (let i=0; i<currFile.layers.length; i++) {
if (currFile.layers[i].hasCanvas()) {
currFile.layers[i].context.putImageData(
this.resizeImageData(rsImageDatas[layerIndex], newWidth, newHeight, this.currentAlgo), 0, 0
);
currFile.layers[i].updateLayerPreview();
layerIndex++;
}
}
// Updating start values when I finish scaling the sprite
// OPTIMIZABLE? Can't I just assign data to startData? Is js smart enough to understand?
if (ratio == null) {
this.startData.width = this.data.width;
this.startData.height = this.data.height;
}
else {
this.startData.width = currFile.canvasSize[0];
this.startData.height = currFile.canvasSize[1];
}
this.startData.widthPercentage = 100;
this.startData.heightPercentage = 100;
Dialogue.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
*/
changedWidth(event) {
let newHeight, newHeightPerc, newWidthPerc;
this.data.width = event.target.value;
newHeight = this.data.width / this.currentRatio;
newHeightPerc = (newHeight * 100) / this.startData.height;
newWidthPerc = (this.data.width * 100) / this.startData.width;
if (this.keepRatio) {
document.getElementById("rs-height").value = newHeight;
this.data.height = newHeight;
document.getElementById("rs-height-percentage").value = newHeightPerc;
this.data.heightPercentage = newHeightPerc;
}
document.getElementById("rs-width-percentage").value = newWidthPerc;
}
/**Fired when the input field for width is changed. Updates the other input fields consequently
*
* @param {*} event
*/
changedHeight(event) {
let newWidth, newWidthPerc, newHeightPerc;
this.data.height = event.target.value;
newWidth = this.data.height * this.currentRatio;
newWidthPerc = (newWidth * 100) / this.startData.width;
newHeightPerc = (this.data.height * 100) / this.startData.height;
if (this.keepRatio) {
document.getElementById("rs-width").value = newWidth;
this.data.width = newWidth;
document.getElementById("rs-width-percentage").value = newWidthPerc;
this.data.widthPercentage = newWidthPerc;
}
document.getElementById("rs-height-percentage").value = newHeightPerc;
this.data.heightPercentage = newHeightPerc;
}
/**Fired when the input field for width percentage is changed. Updates the other input fields consequently
*
* @param {*} event
*/
changedWidthPercentage(event) {
let oldValue = 100;
let ratio;
let newWidth, newHeight, newHeightPerc;
this.data.widthPercentage = event.target.value;
ratio = this.data.widthPercentage / oldValue;
newHeight = this.startData.height * ratio;
newHeightPerc = this.data.widthPercentage;
newWidth = this.startData.width * ratio;
if (this.keepRatio) {
document.getElementById("rs-height-percentage").value = newHeightPerc;
this.data.heightPercentage = newHeightPerc;
document.getElementById("rs-height").value = newHeight
this.data.height = newHeight;
}
document.getElementById("rs-width").value = newWidth;
this.data.width = newWidth;
}
/**Fired when the input field for height percentage is changed. Updates the other input fields consequently
*
* @param {*} event
*/
changedHeightPercentage(event) {
let oldValue = this.data.heightPercentage;
let ratio;
let newHeight, newWidth, newWidthPerc;
this.data.heightPercentage = event.target.value;
ratio = this.data.heightPercentage / oldValue;
newWidth = this.startData.width * ratio;
newWidthPerc = this.data.heightPercentage;
newHeight = this.startData.height * ratio;
if (this.keepRatio) {
document.getElementById("rs-width-percentage").value = this.data.heightPercentage * currentRatio;
this.data.widthPercentage = newWidthPerc;
document.getElementById("rs-width").value = newWidth;
this.data.width = newWidth;
}
document.getElementById("rs-height").value = newHeight;
this.data.height = newHeight;
}
/** Toggles the keepRatio value (fired by the checkbox in the pop up window)
*/
toggleRatio() {
this.keepRatio = !this.keepRatio;
}
/** Changes the scaling algorithm (fired by the combobox in the pop up window)
*
* @param {*} event
*/
changedAlgorithm(event) {
this.currentAlgo = event.target.value;
}
/** Resizes an imageData depending on the algorithm and on the new width and height
*
* @param {*} image The imageData to scale
* @param {*} width The new width of the imageData
* @param {*} height The new height of the imageData
* @param {*} algorithm Scaling algorithm chosen by the user in the dialogue
*/
resizeImageData (image, width, height, algorithm) {
algorithm = algorithm || 'bilinear-interpolation'
let resize;
switch (algorithm) {
case 'nearest-neighbor': resize = this.nearestNeighbor; break
case 'bilinear-interpolation': resize = this.bilinearInterpolation; break
default: return image;
}
const result = new ImageData(width, height)
resize(image, result)
return result
}
/** Nearest neighbor algorithm to scale a sprite
*
* @param {*} src The source imageData
* @param {*} dst The destination imageData
*/
nearestNeighbor (src, dst) {
let pos = 0
// Just applying the nearest neighbor algorithm
for (let y = 0; y < dst.height; y++) {
for (let x = 0; x < dst.width; x++) {
const srcX = Math.floor(x * src.width / dst.width)
const srcY = Math.floor(y * src.height / dst.height)
let srcPos = ((srcY * src.width) + srcX) * 4
dst.data[pos++] = src.data[srcPos++] // R
dst.data[pos++] = src.data[srcPos++] // G
dst.data[pos++] = src.data[srcPos++] // B
dst.data[pos++] = src.data[srcPos++] // A
}
}
}
/** Bilinear interpolation used to scale a sprite
*
* @param {*} src The source imageData
* @param {*} dst The destination imageData
*/
bilinearInterpolation (src, dst) {
// Applying the bilinear interpolation algorithm
function interpolate (k, kMin, kMax, vMin, vMax) {
return Math.round((k - kMin) * vMax + (kMax - k) * vMin)
}
function interpolateHorizontal (offset, x, y, xMin, xMax) {
const vMin = src.data[((y * src.width + xMin) * 4) + offset]
if (xMin === xMax) return vMin
const vMax = src.data[((y * src.width + xMax) * 4) + offset]
return interpolate(x, xMin, xMax, vMin, vMax)
}
function interpolateVertical (offset, x, xMin, xMax, y, yMin, yMax) {
const vMin = interpolateHorizontal(offset, x, yMin, xMin, xMax)
if (yMin === yMax) return vMin
const vMax = interpolateHorizontal(offset, x, yMax, xMin, xMax)
return interpolate(y, yMin, yMax, vMin, vMax)
}
let pos = 0
for (let y = 0; y < dst.height; y++) {
for (let x = 0; x < dst.width; x++) {
const srcX = x * src.width / dst.width
const srcY = y * src.height / dst.height
const xMin = Math.floor(srcX)
const yMin = Math.floor(srcY)
const xMax = Math.min(Math.ceil(srcX), src.width - 1)
const yMax = Math.min(Math.ceil(srcY), src.height - 1)
dst.data[pos++] = interpolateVertical(0, srcX, xMin, xMax, srcY, yMin, yMax) // R
dst.data[pos++] = interpolateVertical(1, srcX, xMin, xMax, srcY, yMin, yMax) // G
dst.data[pos++] = interpolateVertical(2, srcX, xMin, xMax, srcY, yMin, yMax) // B
dst.data[pos++] = interpolateVertical(3, srcX, xMin, xMax, srcY, yMin, yMax) // A
}
}
}
}
let currFile = new File();

View File

@ -20,9 +20,7 @@ const FileManager = (() => {
}
Util.setValue('lpe-file-name', fileName);
Events.on("click", "save-project-confirm", saveProject);
Dialogue.showDialogue('save-project', false);
}
@ -38,9 +36,7 @@ const FileManager = (() => {
}
Util.setValue('export-file-name', fileName);
Events.on("click", "export-confirm", exportProject);
Dialogue.showDialogue('export', false);
}
@ -66,11 +62,11 @@ const FileManager = (() => {
//create name
let fileName = Util.getValue("export-file-name");
//set download link
var linkHolder = document.getElementById('save-image-link-holder');
let linkHolder = document.getElementById('save-image-link-holder');
// Creating a tmp canvas to flatten everything
var exportCanvas = document.createElement("canvas");
var emptyCanvas = document.createElement("canvas");
var layersCopy = currFile.layers.slice();
let exportCanvas = document.createElement("canvas");
let emptyCanvas = document.createElement("canvas");
let layersCopy = currFile.layers.slice();
exportCanvas.width = currFile.canvasSize[0];
exportCanvas.height = currFile.canvasSize[1];

View File

@ -104,8 +104,8 @@ class HistoryState {
this.undo = function() {
let layerIndex = 0;
currentAlgo = algo;
resizeSprite(null, [1 / this.xRatio, 1 / this.yRatio]);
currFile.currentAlgo = algo;
currFile.resizeSprite(null, [1 / this.xRatio, 1 / this.yRatio]);
// Also putting the old data
for (let i=0; i<currFile.layers.length; i++) {
@ -118,8 +118,8 @@ class HistoryState {
};
this.redo = function() {
currentAlgo = algo;
resizeSprite(null, [this.xRatio, this.yRatio]);
currFile.currentAlgo = algo;
currFile.resizeSprite(null, [this.xRatio, this.yRatio]);
};
}

View File

@ -1,7 +1,6 @@
const Startup = (() => {
let firstPixel = true;
let editorMode = "Basic";
let splashPostfix = '';
Events.on('click', 'create-button', create, false);
@ -142,7 +141,7 @@ const Startup = (() => {
else {
//if this palette isnt the one specified in the url, then reset the url
if (!palettes[selectedPalette].specified)
history.pushState(null, null, '/pixel-editor');
history.pushState(null, null, '/pixel-editor');
//fill the palette with specified colours
ColorModule.createColorPalette(palettes[selectedPalette].colors);

View File

View File

@ -1,396 +0,0 @@
// REFACTOR: method of File class probably
/* 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 = currFile.canvasSize[0] / currFile.canvasSize[1];
console.log("Current ratio: " + currentRatio);
// Initializing the input fields
data.width = currFile.canvasSize[0];
data.height = currFile.canvasSize[1];
startData.width = parseInt(data.width);
startData.height = parseInt(data.height);
startData.heightPercentage = 100;
startData.widthPercentage = 100;
// Opening the pop up now that it's ready
Dialogue.showDialogue('resize-sprite');
}
/** Initalizes the input values and binds the elements to their events
*
*/
function initResizeSpriteInputs() {
document.getElementById("rs-width").value = currFile.canvasSize[0];
document.getElementById("rs-height").value = currFile.canvasSize[1];
document.getElementById("rs-width-percentage").value = 100;
document.getElementById("rs-height-percentage").value = 100;
document.getElementById("rs-keep-ratio").checked = true;
document.getElementById("rs-width").addEventListener("change", changedWidth);
document.getElementById("rs-height").addEventListener("change", changedHeight);
document.getElementById("rs-width-percentage").addEventListener("change", changedWidthPercentage);
document.getElementById("rs-height-percentage").addEventListener("change", changedHeightPercentage);
document.getElementById("resize-sprite-confirm").addEventListener("click", resizeSprite);
document.getElementById("rs-keep-ratio").addEventListener("click", toggleRatio);
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 = currFile.canvasSize[0];
oldHeight = currFile.canvasSize[1];
rcPivot = "middle";
// Updating values if the user didn't press enter
switch (document.activeElement.id) {
case "rs-width-percentage":
changedWidthPercentage();
break;
case "rs-width":
changedWidth();
break;
case "rs-height-percentage":
changedHeightPercentage();
break;
case "rs-height":
changedHeight();
break;
default:
// In this case everything has been updated correctly
break;
}
// Computing newWidth and newHeight
if (ratio == null) {
newWidth = data.width;
newHeight = data.height;
}
else {
newWidth = currFile.canvasSize[0] * ratio[0];
newHeight = currFile.canvasSize[1] * ratio[1];
}
// Get all the image datas
for (let i=0; i<currFile.layers.length; i++) {
if (currFile.layers[i].hasCanvas()) {
rsImageDatas.push(currFile.layers[i].context.getImageData(
0, 0, currFile.canvasSize[0], currFile.canvasSize[1])
);
}
}
// event is null when the user is undoing
if (event != null) {
// Copying the image data
imageDatasCopy = rsImageDatas.slice();
// Saving the history
new HistoryState().ResizeSprite(newWidth / oldWidth, newHeight / oldHeight, currentAlgo, imageDatasCopy);
}
// Resizing the canvas
currFile.resizeCanvas(null, {x: newWidth, y: newHeight});
// Put the image datas on the new canvases
for (let i=0; i<currFile.layers.length; i++) {
if (currFile.layers[i].hasCanvas()) {
currFile.layers[i].context.putImageData(
resizeImageData(rsImageDatas[layerIndex], newWidth, newHeight, currentAlgo), 0, 0
);
currFile.layers[i].updateLayerPreview();
layerIndex++;
}
}
// Updating start values when I finish scaling the sprite
// OPTIMIZABLE? Can't I just assign data to startData? Is js smart enough to understand?
if (ratio == null) {
startData.width = data.width;
startData.height = data.height;
}
else {
startData.width = currFile.canvasSize[0];
startData.height = currFile.canvasSize[1];
}
startData.widthPercentage = 100;
startData.heightPercentage = 100;
Dialogue.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;
let percentageRatio;
let newHeight, newHeightPerc, newWidthPerc;
data.width = event.target.value;
delta = data.width - oldValue;
ratio = data.width / oldValue;
newHeight = data.width / currentRatio;
newHeightPerc = (newHeight * 100) / startData.height;
newWidthPerc = (data.width * 100) / startData.width;
if (keepRatio) {
document.getElementById("rs-height").value = newHeight;
data.height = newHeight;
document.getElementById("rs-height-percentage").value = newHeightPerc;
data.heightPercentage = newHeightPerc;
}
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;
let newWidth, newWidthPerc, newHeightPerc;
data.height = event.target.value;
delta = data.height - oldValue;
ratio = data.height / oldValue;
newWidth = data.height * currentRatio;
newWidthPerc = (newWidth * 100) / startData.width;
newHeightPerc = (data.height * 100) / startData.height;
if (keepRatio) {
document.getElementById("rs-width").value = newWidth;
data.width = newWidth;
document.getElementById("rs-width-percentage").value = newWidthPerc;
data.widthPercentage = newWidthPerc;
}
document.getElementById("rs-height-percentage").value = newHeightPerc;
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;
let newWidth, newHeight, newHeightPerc;
data.widthPercentage = event.target.value;
delta = data.widthPercentage - oldValue;
ratio = data.widthPercentage / oldValue;
console.log("old value: " + oldValue + ", ratio: " + ratio);
newHeight = startData.height * ratio;
newHeightPerc = data.widthPercentage;
newWidth = startData.width * ratio;
if (keepRatio) {
document.getElementById("rs-height-percentage").value = newHeightPerc;
data.heightPercentage = newHeightPerc;
document.getElementById("rs-height").value = newHeight
data.height = newHeight;
}
document.getElementById("rs-width").value = newWidth;
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;
let newHeight, newWidth, newWidthPerc;
data.heightPercentage = event.target.value;
delta = data.heightPercentage - oldValue;
ratio = data.heightPercentage / oldValue;
newWidth = startData.width * ratio;
newWidthPerc = data.heightPercentage;
newHeight = startData.height * ratio;
if (keepRatio) {
document.getElementById("rs-width-percentage").value = data.heightPercentage * currentRatio;
data.widthPercentage = newWidthPerc;
document.getElementById("rs-width").value = newWidth;
data.width = newWidth;
}
document.getElementById("rs-height").value = newHeight;
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;
}
/** Resizes an imageData depending on the algorithm and on the new width and height
*
* @param {*} image The imageData to scale
* @param {*} width The new width of the imageData
* @param {*} height The new height of the imageData
* @param {*} algorithm Scaling algorithm chosen by the user in the dialogue
*/
function resizeImageData (image, width, height, algorithm) {
algorithm = algorithm || 'bilinear-interpolation'
let resize;
switch (algorithm) {
case 'nearest-neighbor': resize = nearestNeighbor; break
case 'bilinear-interpolation': resize = bilinearInterpolation; break
default: return image;
}
const result = new ImageData(width, height)
resize(image, result)
return result
}
/** Nearest neighbor algorithm to scale a sprite
*
* @param {*} src The source imageData
* @param {*} dst The destination imageData
*/
function nearestNeighbor (src, dst) {
let pos = 0
// Just applying the nearest neighbor algorithm
for (let y = 0; y < dst.height; y++) {
for (let x = 0; x < dst.width; x++) {
const srcX = Math.floor(x * src.width / dst.width)
const srcY = Math.floor(y * src.height / dst.height)
let srcPos = ((srcY * src.width) + srcX) * 4
dst.data[pos++] = src.data[srcPos++] // R
dst.data[pos++] = src.data[srcPos++] // G
dst.data[pos++] = src.data[srcPos++] // B
dst.data[pos++] = src.data[srcPos++] // A
}
}
}
/** Bilinear interpolation used to scale a sprite
*
* @param {*} src The source imageData
* @param {*} dst The destination imageData
*/
function bilinearInterpolation (src, dst) {
// Applying the bilinear interpolation algorithm
function interpolate (k, kMin, kMax, vMin, vMax) {
return Math.round((k - kMin) * vMax + (kMax - k) * vMin)
}
function interpolateHorizontal (offset, x, y, xMin, xMax) {
const vMin = src.data[((y * src.width + xMin) * 4) + offset]
if (xMin === xMax) return vMin
const vMax = src.data[((y * src.width + xMax) * 4) + offset]
return interpolate(x, xMin, xMax, vMin, vMax)
}
function interpolateVertical (offset, x, xMin, xMax, y, yMin, yMax) {
const vMin = interpolateHorizontal(offset, x, yMin, xMin, xMax)
if (yMin === yMax) return vMin
const vMax = interpolateHorizontal(offset, x, yMax, xMin, xMax)
return interpolate(y, yMin, yMax, vMin, vMax)
}
let pos = 0
for (let y = 0; y < dst.height; y++) {
for (let x = 0; x < dst.width; x++) {
const srcX = x * src.width / dst.width
const srcY = y * src.height / dst.height
const xMin = Math.floor(srcX)
const yMin = Math.floor(srcY)
const xMax = Math.min(Math.ceil(srcX), src.width - 1)
const yMax = Math.min(Math.ceil(srcY), src.height - 1)
dst.data[pos++] = interpolateVertical(0, srcX, xMin, xMax, srcY, yMin, yMax) // R
dst.data[pos++] = interpolateVertical(1, srcX, xMin, xMax, srcY, yMin, yMax) // G
dst.data[pos++] = interpolateVertical(2, srcX, xMin, xMax, srcY, yMin, yMax) // B
dst.data[pos++] = interpolateVertical(3, srcX, xMin, xMax, srcY, yMin, yMax) // A
}
}
}

View File

@ -1,14 +1,17 @@
/**utilities**/
/**EXTERNALS AND LIBRARIES**/
//=include lib/cookies.js
//=include lib/jscolor.js
//=include data/variables.js
//=include lib/sortable.js
/**UTILITY AND INPUT*/
//=include Util.js
//=include Events.js
//=include Color.js
//=include Dialogue.js
//=include History.js
//=include Color.js
//=include File.js
//=include ColorModule.js
@ -45,8 +48,6 @@
//=include data/palettes.js
/**functions**/
//=include _resizeCanvas.js
//=include _resizeSprite.js
//=include ColorPicker.js
//=include PaletteBlock.js
//=include SplashPage.js

View File

@ -40,7 +40,7 @@ class FillTool extends Tool {
}
//temporary image holds the data while we change it
let tempImage = currFile.currentLayer.context.getImageData(0, 0, canvasSize[0], canvasSize[1]);
let tempImage = currFile.currentLayer.context.getImageData(0, 0, currFile.canvasSize[0], currFile.canvasSize[1]);
//this is an array that holds all of the pixels at the top of the cluster
let topmostPixelsArray = [[Math.floor(cursorLocation[0]/currFile.zoom), Math.floor(cursorLocation[1]/currFile.zoom)]];

View File

@ -69,7 +69,7 @@ class MoveSelectionTool extends Tool {
mousePos[1]/currFile.zoom, this.currSelection.width, this.currSelection.height);
// clear the entire tmp layer
TMPLayer.context.clearRect(0, 0, currFile.TMPLayer.canvas.width, currFile.TMPLayer.canvas.height);
currFile.TMPLayer.context.clearRect(0, 0, currFile.TMPLayer.canvas.width, currFile.TMPLayer.canvas.height);
// put the image data on the tmp layer with offset
currFile.TMPLayer.context.putImageData(
this.currSelection.data,
@ -148,16 +148,10 @@ class MoveSelectionTool extends Tool {
// If the pixel of the clipboard is empty, but the one below it isn't, I use the pixel below
if (Util.isPixelEmpty(currentMovePixel)) {
if (!Util.isPixelEmpty(currentUnderlyingPixel)) {
console.log("Original data: " + this.currSelection.data.data[i] + "," +
this.currSelection.data.data[i+1], this.currSelection.data.data[i+2]);
pasteData[i] = currentUnderlyingPixel[0];
pasteData[i+1] = currentUnderlyingPixel[1];
pasteData[i+2] = currentUnderlyingPixel[2];
pasteData[i+3] = currentUnderlyingPixel[3];
console.log("After data: " + this.currSelection.data.data[i] + "," +
this.currSelection.data.data[i+1], this.currSelection.data.data[i+2]);
}
}
}

View File

@ -90,7 +90,7 @@ class RectangleTool extends ResizableTool {
currFile.currentLayer.context.lineWidth = this.currSize;
// Drawing the rect using 4 lines
currentLayer.drawLine(startRectX, startRectY, endRectX, startRectY, this.currSize);
currFile.currentLayer.drawLine(startRectX, startRectY, endRectX, startRectY, this.currSize);
currFile.currentLayer.drawLine(endRectX, startRectY, endRectX, endRectY, this.currSize);
currFile.currentLayer.drawLine(endRectX, endRectY, startRectX, endRectY, this.currSize);
currFile.currentLayer.drawLine(startRectX, endRectY, startRectX, startRectY, this.currSize);

12
refactor_dependencies Normal file
View File

@ -0,0 +1,12 @@
Input <- Startup, LayerList, TopMenuModule: Could be resolved by using custom events
Startup <- ColorModule, ToolManager, LayerList, EditorState, Layer(++)
EditorState <- LayerList, File, Startup(documentCreated): maybe documentCreated should stay in EditorState (that would fix the
circular dependency with Startup), for the other dependencies it'd probably be nice for them to listen to a custom events
Color <- NONE
ColorModule <- File
File <- Startup, but only because Startup sets certain variables in File (pixelGrid, checkerboard and the other global layers)

View File

@ -23,7 +23,7 @@
<li>
<button>View</button>
<ul>
<li><button id="toggle-pixelgrid-button" onclick="pixelGrid.togglePixelGrid()">Show pixel grid</button></li>
<li><button id="toggle-pixelgrid-button" onclick="currFile.pixelGrid.togglePixelGrid()">Show pixel grid</button></li>
</ul>
</li>
<li>