diff --git a/dist/artimus.js b/dist/artimus.js index c7eba22..4ad6ff6 100644 --- a/dist/artimus.js +++ b/dist/artimus.js @@ -395,7 +395,7 @@ window.artimus = { this.workspace = workspace; this.name = name || `${artimus.translate("layer#", "layer").replace("#", this.layers.length + 1)}`; - //Do a thing, guarenteed to not exist (hopefully) + //Do a thing, guaranteed to not exist (hopefully) if (this.workspace.layerExists(name)) { let num = 1; name = (`${artimus.translate("layer#", "layer").replace("#", num)}`); @@ -405,6 +405,7 @@ window.artimus = { num++; name = (`${artimus.translate("layer#", "layer").replace("#", num)}`); } + this.name = name } @@ -1975,6 +1976,16 @@ window.artimus = { return layer; } + duplicateLayer(name, noSwitch) { + const oldLayer = this.layers.find(layer => layer.name === name); + const newLayer = new artimus.layer(this.canvas.width, this.canvas.height, oldLayer.name + " copy", this, noSwitch); + + newLayer.dataRaw.data.set(oldLayer.dataRaw.data); // Copy the image data between the layers + newLayer.alpha = oldLayer.alpha; + newLayer.visibility = oldLayer.visibility; + newLayer.blendMode = oldLayer.blendMode; + } + _createLayerElement(layerData) { const element = document.createElement("div"); element.className = "artimus-layerWrapper"; @@ -1991,7 +2002,8 @@ window.artimus = { label.CUGI_CONTEXT = () => { return [ { type: "button", text: "delete", onclick: () => this.removeLayer(element.targetLayer) }, - { type: "button", text: "properties", onclick: () => (artimus.layerPropertyMenu)(this, this.getLayer(element.targetLayer)) } + { type: "button", text: "properties", onclick: () => (artimus.layerPropertyMenu)(this, this.getLayer(element.targetLayer)) }, + { type: "button", text: "duplicate", onclick: () => this.duplicateLayer(element.targetLayer, false)} ] } @@ -2691,7 +2703,9 @@ window.artimus = { this.createLayer(name, true); //Set layer data - this.layers[layer + 1].dataRaw = new ImageData(imageData, this.width, this.height); + this.layers[layer + 1].dataRaw = new ImageData(imageData, + (data[5] << 16) + (data[6] << 8) + (data[7]), + (data[8] << 16) + (data[9] << 8) + (data[10])); this.layers[layer + 1].blendMode = blendMode; this.updateLayer(layer + 1); @@ -2723,7 +2737,9 @@ window.artimus = { this.createLayer(name, true); //Set layer data - this.layers[layer + 1].dataRaw = new ImageData(imageData, this.width, this.height); + this.layers[layer + 1].dataRaw = new ImageData(imageData + (data[5] << 16) + (data[6] << 8) + (data[7]), + (data[8] << 16) + (data[9] << 8) + (data[10])); this.layers[layer + 1].blendMode = blendMode; this.updateLayer(layer + 1); @@ -2756,7 +2772,9 @@ window.artimus = { this.createLayer(name, true); //Set layer data - this.layers[layer + 1].dataRaw = new ImageData(imageData, this.width, this.height); + this.layers[layer + 1].dataRaw = new ImageData(imageData + (data[5] << 16) + (data[6] << 8) + (data[7]), + (data[8] << 16) + (data[9] << 8) + (data[10])); this.layers[layer + 1].blendMode = blendMode; this.updateLayer(layer + 1); @@ -2792,7 +2810,9 @@ window.artimus = { this.createLayer(name, true); //Set layer data - this.layers[layer + 1].dataRaw = new ImageData(imageData, this.width, this.height); + this.layers[layer + 1].dataRaw = new ImageData(imageData, + (data[5] << 16) + (data[6] << 8) + (data[7]), + (data[8] << 16) + (data[9] << 8) + (data[10])); this.layers[layer + 1].blendMode = blendMode; this.layers[layer + 1].alpha = alpha; @@ -2803,7 +2823,7 @@ window.artimus = { }, ]; - importArtimus(input) { + importArtimus(input, replaceFile) { const data = new Uint8Array(input); //Make sure it is an artimus image @@ -2813,12 +2833,14 @@ window.artimus = { data[2] == this.magic[2] && data[3] == this.magic[3] ) { - this.new( - (data[5] << 16) + (data[6] << 8) + (data[7]), - (data[8] << 16) + (data[9] << 8) + (data[10]), - () => { + const handleImport = () => { //Count bytes needed - const bytesPerLayer = this.width * this.height * 4; + if (replaceFile) { + this.width = (data[5] << 16) + (data[6] << 8) + (data[7]) + this.height = (data[8] << 16) + (data[9] << 8) + (data[10]) + } + + const bytesPerLayer = ((data[5] << 16) + (data[6] << 8) + (data[7])) * ((data[8] << 16) + (data[9] << 8) + (data[10])) * 4; const layerCount = (data[11] << 8) + data[12]; const format = (data[4]); @@ -2827,7 +2849,7 @@ window.artimus = { let idx = 12; //layer 1 is set to NaN as to not confuse it with an actual layer - this.layers[0].name = NaN; + if (replaceFile) this.layers[0].name = NaN; //Loop through layers, and read them with whatever format of reader is needed; let layerReader = this.layerReaders[format]; @@ -2837,11 +2859,12 @@ window.artimus = { return; } - for (let layer = 0; layer < layerCount; layer++) { + const fileLayers = this.layers.length; + for (let layer = (replaceFile ? 0 : this.layers.length-1); layer < (replaceFile ? layerCount : layerCount+fileLayers-1); layer++) { idx = layerReader(data, layer, bytesPerLayer, idx); } - this.setLayer(1).then(() => { + if (replaceFile) this.setLayer(1).then(() => { this.removeLayer(0) this.setLayer(0); }); @@ -2861,7 +2884,16 @@ window.artimus = { console.error("Json header could possibly be corrupted :("); } } - }); + + + } + + if (replaceFile) this.new( + (data[5] << 16) + (data[6] << 8) + (data[7]), + (data[8] << 16) + (data[9] << 8) + (data[10]), + handleImport + ); + else handleImport(); } else console.error("Artimus File invalid!"); } @@ -2964,17 +2996,17 @@ window.artimus = { // Will be set upon file save/load fileSystemHandle = null; - importFromImage(image) { + importFromImage(image, replaceFile) { let extension = image.name.split("."); extension = extension[extension.length - 1]; - this.fileReader.onload = () => { this.onImageLoad(this.fileReader.result, extension); }; + this.fileReader.onload = () => { this.onImageLoad(this.fileReader.result, extension, replaceFile); }; this.fileReader[this.importTypes[extension] || "readAsDataURL"](image); } - importFromPC() { + importFromPC(replaceFile) { // Not yet widely available, so we will need to check we can use the file system access API - if (window.showSaveFilePicker) window.showOpenFilePicker({ + if (window.showSaveFilePicker) return window.showOpenFilePicker({ id: "artimus_file_location", multiple: false, startIn: "documents", @@ -2985,7 +3017,7 @@ window.artimus = { ] }).then(fsHandle => { artimus.activeWorkspaces[0].fileSystemHandle = fsHandle[0]; - fsHandle[0].getFile().then(file => artimus.activeWorkspaces[0].importFromImage(file)); + fsHandle[0].getFile().then(file => artimus.activeWorkspaces[0].importFromImage(file, replaceFile)); }); else { @@ -2993,29 +3025,41 @@ window.artimus = { fileInput.type = "file"; fileInput.accept = "image/*, .artimus"; - fileInput.onchange = () => { - artimus.activeWorkspaces[0].importFromImage(fileInput.files[0]); - }; + const filePromise = new Promise((resolve) => { + fileInput.onchange = () => { + artimus.activeWorkspaces[0].importFromImage(fileInput.files[0], replaceFile); + resolve(); + }; + fileInput.onError = () => { console.log('file load error wow'); } + }); - fileInput.click(); + fileInput.click(); + return filePromise; } } - onImageLoad(data, extension) { + onImageLoad(data, extension, replaceFile) { switch (extension) { case "art": case "artimus": - this.importArtimus(data); + this.importArtimus(data, replaceFile); break; default: const image = new Image(); image.onload = () => { - this.new(image.width, image.height, () => { + if (replaceFile) this.new(image.width, image.height, () => { this.setLayer(0, () => { this.editGL.drawImage(image, 0, 0); }); }); + + else { + this.createLayer(`Layer ${this.layers.length+1}`, false); + this.setLayer(this.layers.length-1, () => { + this.editGL.drawImage(image, 0, 0, image.width, image.height); // Todo: Maybe prompt about resizing the canvas? + }); + } } image.src = data; diff --git a/index.html b/index.html index bb1c02e..35b443d 100644 --- a/index.html +++ b/index.html @@ -52,7 +52,7 @@ editor.exportMenu(); }} { type: "button", text: "Load", onclick: () => { - artimus.activeWorkspaces[0].importFromPC(); + editor.loadFile(false); }} Edit @@ -89,6 +89,7 @@ + diff --git a/lang/english.json b/lang/english.json index 6c24cea..083d566 100644 --- a/lang/english.json +++ b/lang/english.json @@ -49,6 +49,7 @@ "artimus.layerDropdown.delete": "Delete", "artimus.layerDropdown.properties": "Properties", + "artimus.layerDropdown.duplicate": "Duplicate", "artimus.blendModes.source-over": "Source over", "artimus.blendModes.source-in": "Source in", @@ -81,7 +82,7 @@ "artimus.modal.welcome.info": [ "", - "

A web based art editor and library created by ObviousStudios and open sourced under the MIT liscense

", + "

A web based art editor and library created by ObviousStudios and open sourced under the MIT license

", "

Originally created for Coffee Engine

", "

Disclaimer

", @@ -90,7 +91,13 @@ "

Things may break or be unstable.

", "

If you are fine with this and want to try this web demo click the \"I'm ready!\" button below!

", - "" + "" + ], + "artimus.modal.ready.title": "Load or create", + "artimus.modal.ready.info": [ + "

Do you have an existing file you'd like to open?

", + "", + "" ], "artimus.modal.layerProperty.title": "Editing \"[LAYER]\"", @@ -100,6 +107,12 @@ "artimus.modal.newFile.title": "New File", "artimus.modal.newFile.create": "Create!", + "artimus.modal.newFile.loadInstead": "Load a file instead", + + "artimus.modal.loadFile.title": "Load file", + "artimus.modal.loadFile.loadAndReplace": "Load and replace", + "artimus.modal.loadFile.loadAndAdd": "Load and add as layer", + "artimus.modal.loadFile.createInstead": "Create a file instead", "artimus.modal.resizeFile.title": "Resize", "artimus.modal.resizeFile.resize": "Resize!", diff --git a/site/src/menus/loadFile.js b/site/src/menus/loadFile.js new file mode 100644 index 0000000..857da9e --- /dev/null +++ b/site/src/menus/loadFile.js @@ -0,0 +1,39 @@ +editor.loadFile = (forced) => { + //Simple, easy. + new editor.modal(artimus.translate("title", "modal.loadFile"), (contents, modal) => { + + const loadReplaceButton = document.createElement("button"); + const loadAddButton = document.createElement("button"); + const createButon = document.createElement("button"); + + loadReplaceButton.className = "artimus-button"; + loadAddButton.className = "artimus-button"; + createButon.className = "artimus-button"; + + + createButon.innerText = artimus.translate("createInstead", "modal.loadFile"); + loadReplaceButton.innerText = artimus.translate("loadAndReplace", "modal.loadFile"); + loadAddButton.innerText = artimus.translate("loadAndAdd", "modal.loadFile"); + + createButon.onclick = () => { + modal.close(); + editor.newFile(true); + } + loadReplaceButton.onclick = () => { + artimus.activeWorkspaces[0].importFromPC(true).then(() => { + modal.close(); + }); + }; + loadAddButton.onclick = () => { + artimus.activeWorkspaces[0].importFromPC(false).then(() => { + modal.close(); + }); + }; + + contents.appendChild(loadReplaceButton); // don't give the option to add as a layer if this is probably an empty file + if (!forced) contents.appendChild(loadAddButton); + contents.appendChild(document.createElement("br")); // seperate load buttons and create button + contents.appendChild(createButon); + + }, { hasClose: !forced, translationContext: "loadFile", width: 25, height: 20 }); +} \ No newline at end of file diff --git a/site/src/menus/newFile.js b/site/src/menus/newFile.js index e169685..9d2e190 100644 --- a/site/src/menus/newFile.js +++ b/site/src/menus/newFile.js @@ -16,6 +16,7 @@ editor.newFile = (forced) => { const finalDiv = document.createElement("div"); const createButton = document.createElement("button"); + const loadButton = document.createElement("button"); flipButton.className = "newFile-flip"; sizingDiv.className = "newFile-sizingDiv"; @@ -26,6 +27,7 @@ editor.newFile = (forced) => { currentPreviewHolder.className = "newFile-tuning-previewHolder"; currentPreview.className = "newFile-tuning-preview"; createButton.className = "artimus-button"; + loadButton.className = "artimus-button"; resolutionDiv.appendChild(widthInput); resolutionDiv.appendChild(heightInput); @@ -33,6 +35,7 @@ editor.newFile = (forced) => { sizingDiv.appendChild(flipButton); currentPreviewHolder.appendChild(currentPreview); finalDiv.appendChild(createButton); + finalDiv.appendChild(loadButton); tuning.appendChild(currentPreviewHolder); tuning.appendChild(sizingDiv); tuning.appendChild(finalDiv); @@ -40,6 +43,7 @@ editor.newFile = (forced) => { contents.appendChild(tuning); createButton.innerText = artimus.translate("create", "modal.newFile"); + loadButton.innerText = artimus.translate("loadInstead", "modal.newFile"); fetch("site/images/flipAspect.svg").then(res => res.text()).then(text => { if (flipButton) { @@ -91,6 +95,11 @@ editor.newFile = (forced) => { modal.close(); } + loadButton.onclick = () => { + modal.close(); + editor.loadFile(true); + } + //Append resolution presets for (let presetID in editor.resolutionPresets) { const preset = editor.resolutionPresets[presetID];