diff --git a/css/ui/layers.css b/css/ui/layers.css new file mode 100644 index 0000000..283d635 --- /dev/null +++ b/css/ui/layers.css @@ -0,0 +1,16 @@ +#layer-list { + height: 200px; +} + +#layer-list .ui-layer { + cursor: pointer; + background-color: #fff3; +} + +#layer-list .ui-layer:hover { + filter: brightness(90%); +} + +#layer-list .ui-layer:active { + filter: brightness(80%); +} diff --git a/index.html b/index.html index 69ee594..7992b6a 100644 --- a/index.html +++ b/index.html @@ -12,6 +12,7 @@ + @@ -194,11 +195,22 @@ + +
+
Layers
+ +
+
+ style="right: 270px; top: 10px">
@@ -225,6 +237,7 @@ + diff --git a/js/index.js b/js/index.js index afdb7ef..bb1cb6b 100644 --- a/js/index.js +++ b/js/index.js @@ -334,8 +334,8 @@ function newImage(evt) { commands.runCommand("eraseImage", "Clear Canvas", { x: 0, y: 0, - w: imgCanvas.width, - h: imgCanvas.height, + w: uiLayers.active.canvas.width, + h: uiLayers.active.canvas.height, }); } @@ -765,7 +765,7 @@ async function upscaleAndDownload() { // get cropped canvas, send it to upscaler, download result var upscale_factor = 2; // TODO: make this a user input 1.x - 4.0 or something var upscaler = document.getElementById("upscalers").value; - var croppedCanvas = cropCanvas(imgCanvas); + var croppedCanvas = cropCanvas(uiLayers.active.canvas); if (croppedCanvas != null) { var upscaler = document.getElementById("upscalers").value; var url = diff --git a/js/initalize/layers.populate.js b/js/initalize/layers.populate.js index 1f1a39b..d383609 100644 --- a/js/initalize/layers.populate.js +++ b/js/initalize/layers.populate.js @@ -52,7 +52,17 @@ const getVisible = (bb) => { canvas.width = bb.w; canvas.height = bb.h; ctx.drawImage(bgLayer.canvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h); - ctx.drawImage(imgCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h); + ctx.drawImage( + uiLayers.active.canvas, + bb.x, + bb.y, + bb.w, + bb.h, + 0, + 0, + bb.w, + bb.h + ); return canvas; }; diff --git a/js/lib/commands.js b/js/lib/commands.js index 91bcaae..1e3bee9 100644 --- a/js/lib/commands.js +++ b/js/lib/commands.js @@ -192,7 +192,7 @@ commands.createCommand( // Check if we have state if (!state.context) { - const context = options.ctx || imgCtx; + const context = options.ctx || uiLayers.active.ctx; state.context = context; // Saving what was in the canvas before the command @@ -252,7 +252,7 @@ commands.createCommand( // Check if we have state if (!state.context) { - const context = options.ctx || imgCtx; + const context = options.ctx || uiLayers.active.ctx; state.context = context; // Saving what was in the canvas before the command diff --git a/js/lib/util.js b/js/lib/util.js index 5fa5f6d..24202b0 100644 --- a/js/lib/util.js +++ b/js/lib/util.js @@ -250,14 +250,14 @@ function cropCanvas(sourceCanvas, options = {}) { * * @param {Object} options - Optional Information * @param {boolean} [options.cropToContent] - If we wish to crop to content first (default: true) - * @param {HTMLCanvasElement} [options.canvas] - The source canvas (default: imgCanvas) + * @param {HTMLCanvasElement} [options.canvas] - The source canvas (default: uiLayers.active.canvas) * @param {string} [options.filename] - The filename to save as (default: '[ISO date] [Hours] [Minutes] [Seconds] openOutpaint image.png').\ * If null, opens image in new tab. */ function downloadCanvas(options = {}) { defaultOpt(options, { cropToContent: true, - canvas: imgCanvas, + canvas: uiLayers.active.canvas, filename: new Date() .toISOString() diff --git a/js/ui/floating/layers.js b/js/ui/floating/layers.js new file mode 100644 index 0000000..3026382 --- /dev/null +++ b/js/ui/floating/layers.js @@ -0,0 +1,56 @@ +/** + * The layering UI window + */ + +const uiLayers = { + layers: [], + active: null, + + _syncLayers() { + const layersEl = document.getElementById("layer-list"); + + const children = Array.from(layersEl.children); + + this.layers.forEach((uiLayer) => { + if (!uiLayer.entry) { + uiLayer.entry = document.createElement("div"); + uiLayer.entry.textContent = uiLayer.name; + + uiLayer.entry.id = `ui-layer-${uiLayer.id}`; + uiLayer.entry.classList.add("ui-layer"); + uiLayer.entry.addEventListener( + "click", + () => (this.active = uiLayer.layer) + ); + + if (true || children.length === 0) layersEl.appendChild(uiLayer.entry); + } + }); + }, + + addLayer(group, name) { + const layer = imageCollection.registerLayer(null, { + name, + after: + (this.layers.length > 0 && this.layers[this.layers.length - 1].layer) || + bgLayer, + }); + + const uiLayer = { + id: layer.id, + group, + name, + entry: null, + layer, + }; + this.layers.push(uiLayer); + + this.active = uiLayer.layer; + + this._syncLayers(); + + return uiLayer; + }, +}; +uiLayers.addLayer(null, "Default Image Layer"); +uiLayers.addLayer(null, "Test Extra Layer"); diff --git a/js/ui/tool/colorbrush.js b/js/ui/tool/colorbrush.js index a14d6e5..5a78380 100644 --- a/js/ui/tool/colorbrush.js +++ b/js/ui/tool/colorbrush.js @@ -271,20 +271,20 @@ const colorBrushTool = () => const bkpcanvas = state.eraseBackup.canvas; const bkpctx = state.eraseBackup.ctx; bkpctx.clearRect(0, 0, bkpcanvas.width, bkpcanvas.height); - bkpctx.drawImage(imgCanvas, 0, 0); + bkpctx.drawImage(uiLayers.active.canvas, 0, 0); - imgCtx.globalCompositeOperation = "destination-out"; - _color_brush_erase_callback(evn, state, imgCtx); - imgCtx.globalCompositeOperation = "source-over"; + uiLayers.active.ctx.globalCompositeOperation = "destination-out"; + _color_brush_erase_callback(evn, state, uiLayers.active.ctx); + uiLayers.active.ctx.globalCompositeOperation = "source-over"; _color_brush_erase_callback(evn, state, state.eraseLayer.ctx); }; state.erasecb = (evn) => { if (state.eyedropper || !state.erasing) return; if (state.affectMask) _mask_brush_erase_callback(evn, state); - imgCtx.globalCompositeOperation = "destination-out"; - _color_brush_erase_callback(evn, state, imgCtx); - imgCtx.globalCompositeOperation = "source-over"; + uiLayers.active.ctx.globalCompositeOperation = "destination-out"; + _color_brush_erase_callback(evn, state, uiLayers.active.ctx); + uiLayers.active.ctx.globalCompositeOperation = "source-over"; _color_brush_erase_callback(evn, state, state.eraseLayer.ctx); }; @@ -300,8 +300,13 @@ const colorBrushTool = () => const cropped = cropCanvas(canvas, {border: 10}); const bb = cropped.bb; - imgCtx.clearRect(0, 0, imgCanvas.width, imgCanvas.height); - imgCtx.drawImage(bkpcanvas, 0, 0); + uiLayers.active.ctx.clearRect( + 0, + 0, + uiLayers.active.canvas.width, + uiLayers.active.canvas.height + ); + uiLayers.active.ctx.drawImage(bkpcanvas, 0, 0); commands.runCommand("eraseImage", "Color Brush Erase", { mask: cropped.canvas, diff --git a/js/ui/tool/dream.js b/js/ui/tool/dream.js index fd37ac9..4240f0a 100644 --- a/js/ui/tool/dream.js +++ b/js/ui/tool/dream.js @@ -367,7 +367,7 @@ const dream_generate_callback = async (evn, state) => { blockNewImages = true; // Use txt2img if canvas is blank - if (isCanvasBlank(bb.x, bb.y, bb.w, bb.h, imgCanvas)) { + if (isCanvasBlank(bb.x, bb.y, bb.w, bb.h, uiLayers.active.canvas)) { // Dream _generate("txt2img", request, bb); } else { @@ -384,7 +384,7 @@ const dream_generate_callback = async (evn, state) => { // Get init image auxCtx.fillRect(0, 0, request.width, request.height); auxCtx.drawImage( - imgCanvas, + uiLayers.active.canvas, bb.x, bb.y, bb.w, @@ -417,7 +417,7 @@ const dream_generate_callback = async (evn, state) => { auxCtx.globalCompositeOperation = "destination-in"; auxCtx.drawImage( - imgCanvas, + uiLayers.active.canvas, bb.x, bb.y, bb.w, @@ -430,7 +430,7 @@ const dream_generate_callback = async (evn, state) => { } else { auxCtx.globalCompositeOperation = "destination-in"; auxCtx.drawImage( - imgCanvas, + uiLayers.active.canvas, bb.x, bb.y, bb.w, @@ -536,7 +536,7 @@ const dream_img2img_callback = (evn, state) => { ); // Do nothing if no image exists - if (isCanvasBlank(bb.x, bb.y, bb.w, bb.h, imgCanvas)) return; + if (isCanvasBlank(bb.x, bb.y, bb.w, bb.h, uiLayers.active.canvas)) return; // Build request to the API const request = {}; @@ -565,7 +565,7 @@ const dream_img2img_callback = (evn, state) => { // Get init image auxCtx.fillRect(0, 0, request.width, request.height); auxCtx.drawImage( - imgCanvas, + uiLayers.active.canvas, bb.x, bb.y, bb.w, diff --git a/js/ui/tool/select.js b/js/ui/tool/select.js index 964d070..083a167 100644 --- a/js/ui/tool/select.js +++ b/js/ui/tool/select.js @@ -84,7 +84,7 @@ const selectTransformTool = () => // Clears selection and make things right state.reset = () => { if (state.selected) - imgCtx.drawImage( + uiLayers.active.ctx.drawImage( state.original.image, state.original.x, state.original.y @@ -312,7 +312,7 @@ const selectTransformTool = () => // If something is selected, commit changes to the canvas if (state.selected) { - imgCtx.drawImage( + uiLayers.active.ctx.drawImage( state.selected.image, state.original.x, state.original.y @@ -406,7 +406,7 @@ const selectTransformTool = () => const ctx = cvs.getContext("2d"); ctx.drawImage( - imgCanvas, + uiLayers.active.canvas, state.selected.x, state.selected.y, state.selected.w, @@ -417,7 +417,7 @@ const selectTransformTool = () => state.selected.h ); - imgCtx.clearRect( + uiLayers.active.ctx.clearRect( state.selected.x, state.selected.y, state.selected.w,