layers... now exist?

Signed-off-by: Victor Seiji Hariki <victorseijih@gmail.com>
This commit is contained in:
Victor Seiji Hariki 2022-12-04 08:00:39 -03:00
parent 583f9245b6
commit aec096b856
10 changed files with 128 additions and 28 deletions

16
css/ui/layers.css Normal file
View 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%);
}

View file

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

View file

@ -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 =

View file

@ -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;
}; };

View file

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

View file

@ -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
View 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");

View file

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

View file

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

View file

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