layers... now exist?
Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
parent
583f9245b6
commit
aec096b856
10 changed files with 128 additions and 28 deletions
16
css/ui/layers.css
Normal file
16
css/ui/layers.css
Normal file
|
@ -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%);
|
||||||
|
}
|
15
index.html
15
index.html
|
@ -12,6 +12,7 @@
|
||||||
<link href="css/ui/generic.css" rel="stylesheet" />
|
<link href="css/ui/generic.css" rel="stylesheet" />
|
||||||
|
|
||||||
<link href="css/ui/history.css" rel="stylesheet" />
|
<link href="css/ui/history.css" rel="stylesheet" />
|
||||||
|
<link href="css/ui/layers.css" rel="stylesheet" />
|
||||||
<link href="css/ui/toolbar.css" rel="stylesheet" />
|
<link href="css/ui/toolbar.css" rel="stylesheet" />
|
||||||
|
|
||||||
<!-- Tool Specific CSS -->
|
<!-- Tool Specific CSS -->
|
||||||
|
@ -194,11 +195,22 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Layers -->
|
||||||
|
<div
|
||||||
|
id="ui-layers"
|
||||||
|
class="floating-window"
|
||||||
|
style="right: 10px; bottom: 10px">
|
||||||
|
<div class="draggable floating-window-title">Layers</div>
|
||||||
|
<div class="menu-container" style="min-width: 200px">
|
||||||
|
<div id="layer-list" class="layer-list"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Toolbar -->
|
<!-- Toolbar -->
|
||||||
<div
|
<div
|
||||||
id="ui-toolbar"
|
id="ui-toolbar"
|
||||||
class="floating-window toolbar"
|
class="floating-window toolbar"
|
||||||
style="right: 10px; top: 350px">
|
style="right: 270px; top: 10px">
|
||||||
<div class="draggable handle">
|
<div class="draggable handle">
|
||||||
<span class="line"></span>
|
<span class="line"></span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -225,6 +237,7 @@
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<script src="js/index.js" type="text/javascript"></script>
|
<script src="js/index.js" type="text/javascript"></script>
|
||||||
<script src="js/ui/floating/history.js" type="text/javascript"></script>
|
<script src="js/ui/floating/history.js" type="text/javascript"></script>
|
||||||
|
<script src="js/ui/floating/layers.js" type="text/javascript"></script>
|
||||||
|
|
||||||
<!-- Load Tools -->
|
<!-- Load Tools -->
|
||||||
<script src="js/ui/tool/dream.js" type="text/javascript"></script>
|
<script src="js/ui/tool/dream.js" type="text/javascript"></script>
|
||||||
|
|
|
@ -334,8 +334,8 @@ function newImage(evt) {
|
||||||
commands.runCommand("eraseImage", "Clear Canvas", {
|
commands.runCommand("eraseImage", "Clear Canvas", {
|
||||||
x: 0,
|
x: 0,
|
||||||
y: 0,
|
y: 0,
|
||||||
w: imgCanvas.width,
|
w: uiLayers.active.canvas.width,
|
||||||
h: imgCanvas.height,
|
h: uiLayers.active.canvas.height,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,7 +765,7 @@ async function upscaleAndDownload() {
|
||||||
// get cropped canvas, send it to upscaler, download result
|
// 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 upscale_factor = 2; // TODO: make this a user input 1.x - 4.0 or something
|
||||||
var upscaler = document.getElementById("upscalers").value;
|
var upscaler = document.getElementById("upscalers").value;
|
||||||
var croppedCanvas = cropCanvas(imgCanvas);
|
var croppedCanvas = cropCanvas(uiLayers.active.canvas);
|
||||||
if (croppedCanvas != null) {
|
if (croppedCanvas != null) {
|
||||||
var upscaler = document.getElementById("upscalers").value;
|
var upscaler = document.getElementById("upscalers").value;
|
||||||
var url =
|
var url =
|
||||||
|
|
|
@ -52,7 +52,17 @@ const getVisible = (bb) => {
|
||||||
canvas.width = bb.w;
|
canvas.width = bb.w;
|
||||||
canvas.height = bb.h;
|
canvas.height = bb.h;
|
||||||
ctx.drawImage(bgLayer.canvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, 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;
|
return canvas;
|
||||||
};
|
};
|
||||||
|
|
|
@ -192,7 +192,7 @@ commands.createCommand(
|
||||||
|
|
||||||
// Check if we have state
|
// Check if we have state
|
||||||
if (!state.context) {
|
if (!state.context) {
|
||||||
const context = options.ctx || imgCtx;
|
const context = options.ctx || uiLayers.active.ctx;
|
||||||
state.context = context;
|
state.context = context;
|
||||||
|
|
||||||
// Saving what was in the canvas before the command
|
// Saving what was in the canvas before the command
|
||||||
|
@ -252,7 +252,7 @@ commands.createCommand(
|
||||||
|
|
||||||
// Check if we have state
|
// Check if we have state
|
||||||
if (!state.context) {
|
if (!state.context) {
|
||||||
const context = options.ctx || imgCtx;
|
const context = options.ctx || uiLayers.active.ctx;
|
||||||
state.context = context;
|
state.context = context;
|
||||||
|
|
||||||
// Saving what was in the canvas before the command
|
// Saving what was in the canvas before the command
|
||||||
|
|
|
@ -250,14 +250,14 @@ function cropCanvas(sourceCanvas, options = {}) {
|
||||||
*
|
*
|
||||||
* @param {Object} options - Optional Information
|
* @param {Object} options - Optional Information
|
||||||
* @param {boolean} [options.cropToContent] - If we wish to crop to content first (default: true)
|
* @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').\
|
* @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.
|
* If null, opens image in new tab.
|
||||||
*/
|
*/
|
||||||
function downloadCanvas(options = {}) {
|
function downloadCanvas(options = {}) {
|
||||||
defaultOpt(options, {
|
defaultOpt(options, {
|
||||||
cropToContent: true,
|
cropToContent: true,
|
||||||
canvas: imgCanvas,
|
canvas: uiLayers.active.canvas,
|
||||||
filename:
|
filename:
|
||||||
new Date()
|
new Date()
|
||||||
.toISOString()
|
.toISOString()
|
||||||
|
|
56
js/ui/floating/layers.js
Normal file
56
js/ui/floating/layers.js
Normal file
|
@ -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");
|
|
@ -271,20 +271,20 @@ const colorBrushTool = () =>
|
||||||
const bkpcanvas = state.eraseBackup.canvas;
|
const bkpcanvas = state.eraseBackup.canvas;
|
||||||
const bkpctx = state.eraseBackup.ctx;
|
const bkpctx = state.eraseBackup.ctx;
|
||||||
bkpctx.clearRect(0, 0, bkpcanvas.width, bkpcanvas.height);
|
bkpctx.clearRect(0, 0, bkpcanvas.width, bkpcanvas.height);
|
||||||
bkpctx.drawImage(imgCanvas, 0, 0);
|
bkpctx.drawImage(uiLayers.active.canvas, 0, 0);
|
||||||
|
|
||||||
imgCtx.globalCompositeOperation = "destination-out";
|
uiLayers.active.ctx.globalCompositeOperation = "destination-out";
|
||||||
_color_brush_erase_callback(evn, state, imgCtx);
|
_color_brush_erase_callback(evn, state, uiLayers.active.ctx);
|
||||||
imgCtx.globalCompositeOperation = "source-over";
|
uiLayers.active.ctx.globalCompositeOperation = "source-over";
|
||||||
_color_brush_erase_callback(evn, state, state.eraseLayer.ctx);
|
_color_brush_erase_callback(evn, state, state.eraseLayer.ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
state.erasecb = (evn) => {
|
state.erasecb = (evn) => {
|
||||||
if (state.eyedropper || !state.erasing) return;
|
if (state.eyedropper || !state.erasing) return;
|
||||||
if (state.affectMask) _mask_brush_erase_callback(evn, state);
|
if (state.affectMask) _mask_brush_erase_callback(evn, state);
|
||||||
imgCtx.globalCompositeOperation = "destination-out";
|
uiLayers.active.ctx.globalCompositeOperation = "destination-out";
|
||||||
_color_brush_erase_callback(evn, state, imgCtx);
|
_color_brush_erase_callback(evn, state, uiLayers.active.ctx);
|
||||||
imgCtx.globalCompositeOperation = "source-over";
|
uiLayers.active.ctx.globalCompositeOperation = "source-over";
|
||||||
_color_brush_erase_callback(evn, state, state.eraseLayer.ctx);
|
_color_brush_erase_callback(evn, state, state.eraseLayer.ctx);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -300,8 +300,13 @@ const colorBrushTool = () =>
|
||||||
const cropped = cropCanvas(canvas, {border: 10});
|
const cropped = cropCanvas(canvas, {border: 10});
|
||||||
const bb = cropped.bb;
|
const bb = cropped.bb;
|
||||||
|
|
||||||
imgCtx.clearRect(0, 0, imgCanvas.width, imgCanvas.height);
|
uiLayers.active.ctx.clearRect(
|
||||||
imgCtx.drawImage(bkpcanvas, 0, 0);
|
0,
|
||||||
|
0,
|
||||||
|
uiLayers.active.canvas.width,
|
||||||
|
uiLayers.active.canvas.height
|
||||||
|
);
|
||||||
|
uiLayers.active.ctx.drawImage(bkpcanvas, 0, 0);
|
||||||
|
|
||||||
commands.runCommand("eraseImage", "Color Brush Erase", {
|
commands.runCommand("eraseImage", "Color Brush Erase", {
|
||||||
mask: cropped.canvas,
|
mask: cropped.canvas,
|
||||||
|
|
|
@ -367,7 +367,7 @@ const dream_generate_callback = async (evn, state) => {
|
||||||
blockNewImages = true;
|
blockNewImages = true;
|
||||||
|
|
||||||
// Use txt2img if canvas is blank
|
// 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
|
// Dream
|
||||||
_generate("txt2img", request, bb);
|
_generate("txt2img", request, bb);
|
||||||
} else {
|
} else {
|
||||||
|
@ -384,7 +384,7 @@ const dream_generate_callback = async (evn, state) => {
|
||||||
// Get init image
|
// Get init image
|
||||||
auxCtx.fillRect(0, 0, request.width, request.height);
|
auxCtx.fillRect(0, 0, request.width, request.height);
|
||||||
auxCtx.drawImage(
|
auxCtx.drawImage(
|
||||||
imgCanvas,
|
uiLayers.active.canvas,
|
||||||
bb.x,
|
bb.x,
|
||||||
bb.y,
|
bb.y,
|
||||||
bb.w,
|
bb.w,
|
||||||
|
@ -417,7 +417,7 @@ const dream_generate_callback = async (evn, state) => {
|
||||||
|
|
||||||
auxCtx.globalCompositeOperation = "destination-in";
|
auxCtx.globalCompositeOperation = "destination-in";
|
||||||
auxCtx.drawImage(
|
auxCtx.drawImage(
|
||||||
imgCanvas,
|
uiLayers.active.canvas,
|
||||||
bb.x,
|
bb.x,
|
||||||
bb.y,
|
bb.y,
|
||||||
bb.w,
|
bb.w,
|
||||||
|
@ -430,7 +430,7 @@ const dream_generate_callback = async (evn, state) => {
|
||||||
} else {
|
} else {
|
||||||
auxCtx.globalCompositeOperation = "destination-in";
|
auxCtx.globalCompositeOperation = "destination-in";
|
||||||
auxCtx.drawImage(
|
auxCtx.drawImage(
|
||||||
imgCanvas,
|
uiLayers.active.canvas,
|
||||||
bb.x,
|
bb.x,
|
||||||
bb.y,
|
bb.y,
|
||||||
bb.w,
|
bb.w,
|
||||||
|
@ -536,7 +536,7 @@ const dream_img2img_callback = (evn, state) => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Do nothing if no image exists
|
// 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
|
// Build request to the API
|
||||||
const request = {};
|
const request = {};
|
||||||
|
@ -565,7 +565,7 @@ const dream_img2img_callback = (evn, state) => {
|
||||||
// Get init image
|
// Get init image
|
||||||
auxCtx.fillRect(0, 0, request.width, request.height);
|
auxCtx.fillRect(0, 0, request.width, request.height);
|
||||||
auxCtx.drawImage(
|
auxCtx.drawImage(
|
||||||
imgCanvas,
|
uiLayers.active.canvas,
|
||||||
bb.x,
|
bb.x,
|
||||||
bb.y,
|
bb.y,
|
||||||
bb.w,
|
bb.w,
|
||||||
|
|
|
@ -84,7 +84,7 @@ const selectTransformTool = () =>
|
||||||
// Clears selection and make things right
|
// Clears selection and make things right
|
||||||
state.reset = () => {
|
state.reset = () => {
|
||||||
if (state.selected)
|
if (state.selected)
|
||||||
imgCtx.drawImage(
|
uiLayers.active.ctx.drawImage(
|
||||||
state.original.image,
|
state.original.image,
|
||||||
state.original.x,
|
state.original.x,
|
||||||
state.original.y
|
state.original.y
|
||||||
|
@ -312,7 +312,7 @@ const selectTransformTool = () =>
|
||||||
|
|
||||||
// If something is selected, commit changes to the canvas
|
// If something is selected, commit changes to the canvas
|
||||||
if (state.selected) {
|
if (state.selected) {
|
||||||
imgCtx.drawImage(
|
uiLayers.active.ctx.drawImage(
|
||||||
state.selected.image,
|
state.selected.image,
|
||||||
state.original.x,
|
state.original.x,
|
||||||
state.original.y
|
state.original.y
|
||||||
|
@ -406,7 +406,7 @@ const selectTransformTool = () =>
|
||||||
const ctx = cvs.getContext("2d");
|
const ctx = cvs.getContext("2d");
|
||||||
|
|
||||||
ctx.drawImage(
|
ctx.drawImage(
|
||||||
imgCanvas,
|
uiLayers.active.canvas,
|
||||||
state.selected.x,
|
state.selected.x,
|
||||||
state.selected.y,
|
state.selected.y,
|
||||||
state.selected.w,
|
state.selected.w,
|
||||||
|
@ -417,7 +417,7 @@ const selectTransformTool = () =>
|
||||||
state.selected.h
|
state.selected.h
|
||||||
);
|
);
|
||||||
|
|
||||||
imgCtx.clearRect(
|
uiLayers.active.ctx.clearRect(
|
||||||
state.selected.x,
|
state.selected.x,
|
||||||
state.selected.y,
|
state.selected.y,
|
||||||
state.selected.w,
|
state.selected.w,
|
||||||
|
|
Loading…
Reference in a new issue