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 @@
+
+
+ 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,