mirror of
https://github.com/lospec/pixel-editor.git
synced 2023-08-10 21:12:51 +03:00
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:
parent
0b5bb3ab1a
commit
2868363bb7
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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')) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
@ -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);
|
||||
|
12
js/_layer.js
12
js/_layer.js
@ -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
|
||||
|
@ -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.');
|
||||
}
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user