Started project opening

Fixed a bug in the flatten visible undo, created an array to store the colours in the current palette, implemented project saving.
This commit is contained in:
unsettledgames 2020-06-27 13:29:28 +02:00
parent 0b5bb3ab1a
commit 2868363bb7
10 changed files with 140 additions and 89 deletions

View File

@ -12,11 +12,11 @@ The next version is mostly focused on adding missing essential features and port
Suggestions / Planned features:
- Save project while keeping layer data
- Line tool
- Resize canvas
- Snap brush preview to pixel grid
- Move selection with arrows
- Load palette from LPE file
- Custom color picker
- custom code without dependencies
@ -36,7 +36,7 @@ Suggestions / Planned features:
- Another currentLayer.canvas
- Must be rescaled each zoom
- Possibly add collaborate function using together.js
- Possibly add collaborate function
- Bug fix
- Alt + scroll broken

View File

@ -1,3 +1,5 @@
let currentPalette = [];
//adds the given color to the palette
//input hex color string
//returns list item element
@ -6,7 +8,7 @@ function addColor (newColor) {
//add # at beginning if not present
if (newColor.charAt(0) != '#')
newColor = '#' + newColor;
currentPalette.push(newColor);
//create list item
var listItem = document.createElement('li');
@ -45,5 +47,7 @@ function addColor (newColor) {
button.parentElement.firstChild.jscolor.show();
});
console.log(currentPalette);
return listItem;
}

View File

@ -28,10 +28,11 @@ function colorChanged(e) {
var newColor = hexToRgb(e.target.value);
var oldColor = e.target.oldColor;
currentPalette.splice(currentPalette.indexOf("#" + newColor), 1);
newColor.a = 255;
//save undo state
//saveHistoryState({type: 'colorchange', newColor: e.target.value, oldColor: rgbToHex(oldColor), canvas: context.getImageData(0, 0, canvasSize[0], canvasSize[1])});
new HistoryStateEditColor(e.target.value.toLowerCase(), rgbToHex(oldColor));
//get the currently selected color
@ -82,7 +83,7 @@ function colorChanged(e) {
//set new old color to changed color
e.target.oldColor = newColor;
currentPalette.push('#' + newColorHex);
//if this is the current color, update the drawing color
if (e.target.colorElement.parentElement.classList.contains('selected')) {

View File

@ -34,12 +34,6 @@ function createColorPalette(selectedPalette, fillBackground) {
}
//fill bg with lightest color
if (fillBackground) {
currentLayer.context.fillStyle = lightestColor;
currentLayer.context.fillRect(0, 0, canvasSize[0], canvasSize[1]);
}
//set as current color
currentLayer.context.fillStyle = darkestColor;
}

View File

@ -33,6 +33,29 @@ for (var i = 1; i < mainMenuItems.length; i++) {
//File Menu
case 'New':
showDialogue('new-pixel');
break;
case 'Save project':
//create name
var selectedPalette = getText('palette-button');
if (selectedPalette != 'Choose a palette...'){
var paletteAbbreviation = palettes[selectedPalette].abbreviation;
var fileName = 'pixel-'+paletteAbbreviation+'-'+canvasSize[0]+'x'+canvasSize[1]+'.lpe';
} else {
var fileName = 'pixel-'+canvasSize[0]+'x'+canvasSize[1]+'.lpe';
selectedPalette = 'none';
}
//set download link
var linkHolder = document.getElementById('save-project-link-holder');
// create file content
var content = getProjectData();
linkHolder.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(content);
linkHolder.download = fileName;
linkHolder.click();
ga('send', 'event', 'Pixel Editor Save', selectedPalette, canvasSize[0]+'/'+canvasSize[1]); /*global ga*/
break;
case 'Open':
//if a document exists
@ -48,9 +71,8 @@ for (var i = 1; i < mainMenuItems.length; i++) {
break;
case 'Save as...':
case 'Export':
if (documentCreated) {
//create name
var selectedPalette = getText('palette-button');
if (selectedPalette != 'Choose a palette...'){
@ -66,7 +88,7 @@ for (var i = 1; i < mainMenuItems.length; i++) {
// Creating a tmp canvas to flatten everything
var exportCanvas = document.createElement("canvas");
var emptyCanvas = document.createElement("canvas");
var layersCopy = layers.slice();;
var layersCopy = layers.slice();
exportCanvas.width = canvasSize[0];
exportCanvas.height = canvasSize[1];
@ -75,7 +97,7 @@ for (var i = 1; i < mainMenuItems.length; i++) {
emptyCanvas.height = canvasSize[1];
// Sorting the layers by z index
layersCopy.sort((a, b) => (a.canvas.zIndex > b.canvas.zIndex) ? 1 : -1);
layersCopy.sort((a, b) => (a.canvas.style.zIndex > b.canvas.style.zIndex) ? 1 : -1);
// Merging every layer on the export canvas
for (let i=0; i<layersCopy.length; i++) {
@ -100,7 +122,7 @@ for (var i = 1; i < mainMenuItems.length; i++) {
exportCanvas.remove();
//track google event
ga('send', 'event', 'Pixel Editor Save', selectedPalette, canvasSize[0]+'/'+canvasSize[1]); /*global ga*/
ga('send', 'event', 'Pixel Editor Export', selectedPalette, canvasSize[0]+'/'+canvasSize[1]); /*global ga*/
}
break;
@ -149,7 +171,6 @@ for (var i = 1; i < mainMenuItems.length; i++) {
break;
//Help Menu
case 'Settings':
//fill form with current settings values
setValue('setting-numberOfHistoryStates', settings.numberOfHistoryStates);
@ -178,3 +199,27 @@ function closeMenu () {
deselect(mainMenuItems[i]);
}
}
function getProjectData() {
// use a dictionary
let dictionary = [];
// store canvas size
dictionary.push({key: "canvasWidth", value: currentLayer.canvasSize[0]});
dictionary.push({key: "canvasHeight", value: currentLayer.canvasSize[1]});
// store palette
for (let i=0; i<currentPalette.length; i++) {
dictionary.push({key: "color" + i, value: currentPalette[i]});
}
// store layers
for (let i=0; i<layers.length; i++) {
// Only saving the layers the user has access to (no vfx, tmp or checkerboard layers)
if (layers[i].menuEntry != null) {
dictionary.push({key: "layer" + i, value: layers[i]});
dictionary.push({key: "layer" + i + "ImageData",
value: layers[i].canvas.toDataURL()
});
}
}
return JSON.stringify(dictionary);
}

View File

@ -25,20 +25,15 @@ function HistoryStateFlattenVisible(flattened) {
saveHistoryState(this);
}
function HistoryStateFlattenTwoVisibles(belowImageData, beforeAbove, layerIndex, aboveLayer, belowLayer) {
function HistoryStateFlattenTwoVisibles(belowImageData, afterAbove, layerIndex, aboveLayer, belowLayer) {
this.aboveLayer = aboveLayer;
this.belowLayer = belowLayer;
this.belowImageData = belowImageData;
this.undo = function() {
// SCEMOOOO DEVI METTERCI PURE I PIXELSSSS
console.log(afterAbove.menuEntry);
canvasView.append(aboveLayer.canvas);
if (beforeAbove != null) {
layerList.insertBefore(aboveLayer.menuEntry, beforeAbove.menuEntry);
}
else {
layerList.prepend(aboveLayer.menuEntry);
}
layerList.insertBefore(aboveLayer.menuEntry, afterAbove);
belowLayer.context.clearRect(0, 0, belowLayer.canvasSize[0], belowLayer.canvasSize[1]);
belowLayer.context.putImageData(this.belowImageData, 0, 0);

View File

@ -55,6 +55,7 @@ class Layer {
this.id = "layer" + id;
if (menuEntry != null) {
this.name = menuEntry.getElementsByTagName("p")[0].innerHTML;
menuEntry.id = "layer" + id;
menuEntry.onclick = () => this.selectLayer();
menuEntry.getElementsByTagName("button")[0].onclick = () => this.toggleLock();
@ -195,7 +196,10 @@ class Layer {
isRenamingLayer = false;
if (oldLayerName != null) {
new HistoryStateRenameLayer(oldLayerName, this.menuEntry.getElementsByTagName("p")[0].innerHTML, currentLayer);
let name = this.menuEntry.getElementsByTagName("p")[0].innerHTML;
this.name = name;
new HistoryStateRenameLayer(oldLayerName, name, currentLayer);
oldLayerName = null;
}
}
@ -333,16 +337,17 @@ function flatten(onlyVisible) {
}
// Sorting them by z-index
visibleLayers.sort((a, b) => (a.canvas.zIndex > b.canvas.zIndex) ? -1 : 1);
visibleLayers.sort((a, b) => (a.canvas.style.zIndex > b.canvas.style.zIndex) ? -1 : 1);
// Selecting the last visible layer (the only one that won't get deleted)
visibleLayers[visibleLayers.length - 1].selectLayer();
// Merging all the layer but the last one
for (let i=0; i<visibleLayers.length - 1; i++) {
nToFlatten++;
console.log(visibleLayers[i].menuEntry.nextElementSibling);
new HistoryStateFlattenTwoVisibles(
visibleLayers[i + 1].context.getImageData(0, 0, visibleLayers[i].canvasSize[0], visibleLayers[i].canvasSize[1]),
visibleLayers[i].menuEntry.previousElementSibling,
visibleLayers[i].menuEntry.nextElementSibling,
layers.indexOf(visibleLayers[i]),
visibleLayers[i], visibleLayers[i + 1]
);
@ -572,7 +577,6 @@ function addLayer(id, saveHistory = true) {
layerList.insertBefore(toAppend, layerList.childNodes[0]);
if (id != null && typeof(id) == "string") {
console.log("imposto");
newLayer.setID(id);
}
// Basically "if I'm not adding a layer because redo() is telling meto do so", then I can save the history

View File

@ -3,59 +3,65 @@ document.getElementById('open-image-browse-holder').addEventListener('change', f
//make sure file is allowed filetype
var fileContentType = this.files[0].type;
if (fileContentType == 'image/png' || fileContentType == 'image/gif') {
console.log("File: " + fileContentType);
//load file
var fileReader = new FileReader();
fileReader.onload = function(e) {
var img = new Image();
img.onload = function() {
if (fileContentType == 'image/png' || fileContentType == 'image/gif' || fileContentType == '.lpe') {
if (fileContentType == '.lpe') {
console.log("OK");
}
else {
//load file
var fileReader = new FileReader();
fileReader.onload = function(e) {
var img = new Image();
img.onload = function() {
//create a new pixel with the images dimentions
newPixel(this.width, this.height, []);
//create a new pixel with the images dimentions
newPixel(this.width, this.height, []);
//draw the image onto the canvas
currentLayer.context.drawImage(img, 0, 0);
//draw the image onto the canvas
currentLayer.context.drawImage(img, 0, 0);
var colorPalette = {};
var imagePixelData = currentLayer.context.getImageData(0,0,this.width, this.height).data;
var colorPalette = {};
var imagePixelData = currentLayer.context.getImageData(0,0,this.width, this.height).data;
var imagePixelDataLength = imagePixelData.length;
var imagePixelDataLength = imagePixelData.length;
console.log(imagePixelData);
for (var i = 0; i < imagePixelDataLength; i += 4) {
var color = imagePixelData[i]+','+imagePixelData[i + 1]+','+imagePixelData[i + 2];
if (!colorPalette[color]) {
colorPalette[color] = {r:imagePixelData[i],g:imagePixelData[i + 1],b:imagePixelData[i + 2]};
console.log(imagePixelData);
for (var i = 0; i < imagePixelDataLength; i += 4) {
var color = imagePixelData[i]+','+imagePixelData[i + 1]+','+imagePixelData[i + 2];
if (!colorPalette[color]) {
colorPalette[color] = {r:imagePixelData[i],g:imagePixelData[i + 1],b:imagePixelData[i + 2]};
//don't allow more than 256 colors to be added
if (Object.keys(colorPalette).length >= settings.maxColorsOnImportedImage) {
alert('The image loaded seems to have more than '+settings.maxColorsOnImportedImage+' colors.');
break;
//don't allow more than 256 colors to be added
if (Object.keys(colorPalette).length >= settings.maxColorsOnImportedImage) {
alert('The image loaded seems to have more than '+settings.maxColorsOnImportedImage+' colors.');
break;
}
}
}
}
//create array out of colors object
var colorPaletteArray = [];
for (var color in colorPalette) {
if( colorPalette.hasOwnProperty(color) ) {
colorPaletteArray.push('#'+rgbToHex(colorPalette[color]));
//create array out of colors object
var colorPaletteArray = [];
for (var color in colorPalette) {
if( colorPalette.hasOwnProperty(color) ) {
colorPaletteArray.push('#'+rgbToHex(colorPalette[color]));
}
}
}
console.log('COLOR PALETTE ARRAY', colorPaletteArray);
console.log('COLOR PALETTE ARRAY', colorPaletteArray);
//create palette form colors array
createColorPalette(colorPaletteArray, false);
//create palette form colors array
createColorPalette(colorPaletteArray, false);
//track google event
ga('send', 'event', 'Pixel Editor Load', colorPalette.length, this.width+'/'+this.height); /*global ga*/
//track google event
ga('send', 'event', 'Pixel Editor Load', colorPalette.length, this.width+'/'+this.height); /*global ga*/
};
img.src = e.target.result;
};
img.src = e.target.result;
};
fileReader.readAsDataURL(this.files[0]);
fileReader.readAsDataURL(this.files[0]);
}
}
else alert('Only PNG and GIF files are allowed at this time.');
else alert('Only .lpe project files, PNG and GIF files are allowed at this time.');
}
});

View File

@ -1,6 +1,7 @@
let firstPixel = true;
function newPixel (width, height, palette) {
currentPalette = [];
if (firstPixel) {
layerList = document.getElementById("layers-menu");
layerListEntry = layerList.firstElementChild;
@ -115,7 +116,7 @@ function newPixel (width, height, palette) {
closeDialogue();
currentTool.updateCursor();
document.getElementById('save-as-button').classList.remove('disabled');
document.getElementById('export-button').classList.remove('disabled');
documentCreated = true;
firstPixel = false;

View File

@ -43,8 +43,9 @@
<button>File</button>
<ul>
<li><button>New</button></li>
<li><button>Save project</button></li>
<li><button>Open</button></li>
<li><button id="save-as-button" class="disabled">Save as...</button></li>
<li><button id="export-button" class="disabled">Export</button></li>
<li><a href="/pixel-editor">Exit</a></li>
</ul>
</li>
@ -149,8 +150,7 @@
</li>
</ul>
<p>Layer 0
<div class = "gradient"></div></p>
<p>Layer 0<div class = "gradient"></div></p>
</li>
<li>
@ -158,24 +158,24 @@
{{svg "plus.svg" width="20" height="20"}} Add layer
</button>
</li>
</ul>
<ul id = "layer-properties-menu">
<li>
<button onclick = "deleteLayer()">Delete</button>
</li>
<li>
<button onclick = "renameLayer()">Rename</button>
</li>
<li>
<button onclick = "merge()">Merge below</button>
</li>
<li>
<button onclick = "flatten(true)">Flatten visible</button>
</li>
<li>
<button onclick = "flatten(false)">Flatten all</button>
</li>
</ul>
<ul id = "layer-properties-menu">
<li>
<button onclick = "deleteLayer()">Delete</button>
</li>
<li>
<button onclick = "renameLayer()">Rename</button>
</li>
<li>
<button onclick = "merge()">Merge below</button>
</li>
<li>
<button onclick = "flatten(true)">Flatten visible</button>
</li>
<li>
<button onclick = "flatten(false)">Flatten all</button>
</li>
</ul>
<div id="eyedropper-preview"></div>
@ -192,7 +192,8 @@
<div id="data-holders">
<a id="save-image-link-holder" href="#">dl</a>
<input id="open-image-browse-holder" type="file" accept="image/png, image/gif"/>
<a id="save-project-link-holder" href="#">dl</a>
<input id="open-image-browse-holder" type="file" accept="image/png, image/gif, .lpe"/>
<input id="load-palette-browse-holder" type="file" accept="image/png, image/gif"/>
<canvas id="load-palette-canvas-holder"></canvas>
</div>