Files
ComfyUI_frontend/src/nodes/graphics.js
2020-03-25 13:13:58 +01:00

897 lines
25 KiB
JavaScript
Executable File

(function(global) {
var LiteGraph = global.LiteGraph;
function GraphicsPlot() {
this.addInput("A", "Number");
this.addInput("B", "Number");
this.addInput("C", "Number");
this.addInput("D", "Number");
this.values = [[], [], [], []];
this.properties = { scale: 2 };
}
GraphicsPlot.title = "Plot";
GraphicsPlot.desc = "Plots data over time";
GraphicsPlot.colors = ["#FFF", "#F99", "#9F9", "#99F"];
GraphicsPlot.prototype.onExecute = function(ctx) {
if (this.flags.collapsed) {
return;
}
var size = this.size;
for (var i = 0; i < 4; ++i) {
var v = this.getInputData(i);
if (v == null) {
continue;
}
var values = this.values[i];
values.push(v);
if (values.length > size[0]) {
values.shift();
}
}
};
GraphicsPlot.prototype.onDrawBackground = function(ctx) {
if (this.flags.collapsed) {
return;
}
var size = this.size;
var scale = (0.5 * size[1]) / this.properties.scale;
var colors = GraphicsPlot.colors;
var offset = size[1] * 0.5;
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, size[0], size[1]);
ctx.strokeStyle = "#555";
ctx.beginPath();
ctx.moveTo(0, offset);
ctx.lineTo(size[0], offset);
ctx.stroke();
if (this.inputs) {
for (var i = 0; i < 4; ++i) {
var values = this.values[i];
if (!this.inputs[i] || !this.inputs[i].link) {
continue;
}
ctx.strokeStyle = colors[i];
ctx.beginPath();
var v = values[0] * scale * -1 + offset;
ctx.moveTo(0, Math.clamp(v, 0, size[1]));
for (var j = 1; j < values.length && j < size[0]; ++j) {
var v = values[j] * scale * -1 + offset;
ctx.lineTo(j, Math.clamp(v, 0, size[1]));
}
ctx.stroke();
}
}
};
LiteGraph.registerNodeType("graphics/plot", GraphicsPlot);
function GraphicsImage() {
this.addOutput("frame", "image");
this.properties = { url: "" };
}
GraphicsImage.title = "Image";
GraphicsImage.desc = "Image loader";
GraphicsImage.widgets = [{ name: "load", text: "Load", type: "button" }];
GraphicsImage.supported_extensions = ["jpg", "jpeg", "png", "gif"];
GraphicsImage.prototype.onAdded = function() {
if (this.properties["url"] != "" && this.img == null) {
this.loadImage(this.properties["url"]);
}
};
GraphicsImage.prototype.onDrawBackground = function(ctx) {
if (this.flags.collapsed) {
return;
}
if (this.img && this.size[0] > 5 && this.size[1] > 5 && this.img.width) {
ctx.drawImage(this.img, 0, 0, this.size[0], this.size[1]);
}
};
GraphicsImage.prototype.onExecute = function() {
if (!this.img) {
this.boxcolor = "#000";
}
if (this.img && this.img.width) {
this.setOutputData(0, this.img);
} else {
this.setOutputData(0, null);
}
if (this.img && this.img.dirty) {
this.img.dirty = false;
}
};
GraphicsImage.prototype.onPropertyChanged = function(name, value) {
this.properties[name] = value;
if (name == "url" && value != "") {
this.loadImage(value);
}
return true;
};
GraphicsImage.prototype.loadImage = function(url, callback) {
if (url == "") {
this.img = null;
return;
}
this.img = document.createElement("img");
if (url.substr(0, 4) == "http" && LiteGraph.proxy) {
url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3);
}
this.img.src = url;
this.boxcolor = "#F95";
var that = this;
this.img.onload = function() {
if (callback) {
callback(this);
}
console.log( "Image loaded, size: " + that.img.width + "x" + that.img.height );
this.dirty = true;
that.boxcolor = "#9F9";
that.setDirtyCanvas(true);
};
this.img.onerror = function() {
console.log("error loading the image:" + url);
}
};
GraphicsImage.prototype.onWidget = function(e, widget) {
if (widget.name == "load") {
this.loadImage(this.properties["url"]);
}
};
GraphicsImage.prototype.onDropFile = function(file) {
var that = this;
if (this._url) {
URL.revokeObjectURL(this._url);
}
this._url = URL.createObjectURL(file);
this.properties.url = this._url;
this.loadImage(this._url, function(img) {
that.size[1] = (img.height / img.width) * that.size[0];
});
};
LiteGraph.registerNodeType("graphics/image", GraphicsImage);
function ColorPalette() {
this.addInput("f", "number");
this.addOutput("Color", "color");
this.properties = {
colorA: "#444444",
colorB: "#44AAFF",
colorC: "#44FFAA",
colorD: "#FFFFFF"
};
}
ColorPalette.title = "Palette";
ColorPalette.desc = "Generates a color";
ColorPalette.prototype.onExecute = function() {
var c = [];
if (this.properties.colorA != null) {
c.push(hex2num(this.properties.colorA));
}
if (this.properties.colorB != null) {
c.push(hex2num(this.properties.colorB));
}
if (this.properties.colorC != null) {
c.push(hex2num(this.properties.colorC));
}
if (this.properties.colorD != null) {
c.push(hex2num(this.properties.colorD));
}
var f = this.getInputData(0);
if (f == null) {
f = 0.5;
}
if (f > 1.0) {
f = 1.0;
} else if (f < 0.0) {
f = 0.0;
}
if (c.length == 0) {
return;
}
var result = [0, 0, 0];
if (f == 0) {
result = c[0];
} else if (f == 1) {
result = c[c.length - 1];
} else {
var pos = (c.length - 1) * f;
var c1 = c[Math.floor(pos)];
var c2 = c[Math.floor(pos) + 1];
var t = pos - Math.floor(pos);
result[0] = c1[0] * (1 - t) + c2[0] * t;
result[1] = c1[1] * (1 - t) + c2[1] * t;
result[2] = c1[2] * (1 - t) + c2[2] * t;
}
/*
c[0] = 1.0 - Math.abs( Math.sin( 0.1 * reModular.getTime() * Math.PI) );
c[1] = Math.abs( Math.sin( 0.07 * reModular.getTime() * Math.PI) );
c[2] = Math.abs( Math.sin( 0.01 * reModular.getTime() * Math.PI) );
*/
for (var i in result) {
result[i] /= 255;
}
this.boxcolor = colorToString(result);
this.setOutputData(0, result);
};
LiteGraph.registerNodeType("color/palette", ColorPalette);
function ImageFrame() {
this.addInput("", "image,canvas");
this.size = [200, 200];
}
ImageFrame.title = "Frame";
ImageFrame.desc = "Frame viewerew";
ImageFrame.widgets = [
{ name: "resize", text: "Resize box", type: "button" },
{ name: "view", text: "View Image", type: "button" }
];
ImageFrame.prototype.onDrawBackground = function(ctx) {
if (this.frame && !this.flags.collapsed) {
ctx.drawImage(this.frame, 0, 0, this.size[0], this.size[1]);
}
};
ImageFrame.prototype.onExecute = function() {
this.frame = this.getInputData(0);
this.setDirtyCanvas(true);
};
ImageFrame.prototype.onWidget = function(e, widget) {
if (widget.name == "resize" && this.frame) {
var width = this.frame.width;
var height = this.frame.height;
if (!width && this.frame.videoWidth != null) {
width = this.frame.videoWidth;
height = this.frame.videoHeight;
}
if (width && height) {
this.size = [width, height];
}
this.setDirtyCanvas(true, true);
} else if (widget.name == "view") {
this.show();
}
};
ImageFrame.prototype.show = function() {
//var str = this.canvas.toDataURL("image/png");
if (showElement && this.frame) {
showElement(this.frame);
}
};
LiteGraph.registerNodeType("graphics/frame", ImageFrame);
function ImageFade() {
this.addInputs([
["img1", "image"],
["img2", "image"],
["fade", "number"]
]);
this.addOutput("", "image");
this.properties = { fade: 0.5, width: 512, height: 512 };
}
ImageFade.title = "Image fade";
ImageFade.desc = "Fades between images";
ImageFade.widgets = [
{ name: "resizeA", text: "Resize to A", type: "button" },
{ name: "resizeB", text: "Resize to B", type: "button" }
];
ImageFade.prototype.onAdded = function() {
this.createCanvas();
var ctx = this.canvas.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, this.properties["width"], this.properties["height"]);
};
ImageFade.prototype.createCanvas = function() {
this.canvas = document.createElement("canvas");
this.canvas.width = this.properties["width"];
this.canvas.height = this.properties["height"];
};
ImageFade.prototype.onExecute = function() {
var ctx = this.canvas.getContext("2d");
this.canvas.width = this.canvas.width;
var A = this.getInputData(0);
if (A != null) {
ctx.drawImage(A, 0, 0, this.canvas.width, this.canvas.height);
}
var fade = this.getInputData(2);
if (fade == null) {
fade = this.properties["fade"];
} else {
this.properties["fade"] = fade;
}
ctx.globalAlpha = fade;
var B = this.getInputData(1);
if (B != null) {
ctx.drawImage(B, 0, 0, this.canvas.width, this.canvas.height);
}
ctx.globalAlpha = 1.0;
this.setOutputData(0, this.canvas);
this.setDirtyCanvas(true);
};
LiteGraph.registerNodeType("graphics/imagefade", ImageFade);
function ImageCrop() {
this.addInput("", "image");
this.addOutput("", "image");
this.properties = { width: 256, height: 256, x: 0, y: 0, scale: 1.0 };
this.size = [50, 20];
}
ImageCrop.title = "Crop";
ImageCrop.desc = "Crop Image";
ImageCrop.prototype.onAdded = function() {
this.createCanvas();
};
ImageCrop.prototype.createCanvas = function() {
this.canvas = document.createElement("canvas");
this.canvas.width = this.properties["width"];
this.canvas.height = this.properties["height"];
};
ImageCrop.prototype.onExecute = function() {
var input = this.getInputData(0);
if (!input) {
return;
}
if (input.width) {
var ctx = this.canvas.getContext("2d");
ctx.drawImage(
input,
-this.properties["x"],
-this.properties["y"],
input.width * this.properties["scale"],
input.height * this.properties["scale"]
);
this.setOutputData(0, this.canvas);
} else {
this.setOutputData(0, null);
}
};
ImageCrop.prototype.onDrawBackground = function(ctx) {
if (this.flags.collapsed) {
return;
}
if (this.canvas) {
ctx.drawImage(
this.canvas,
0,
0,
this.canvas.width,
this.canvas.height,
0,
0,
this.size[0],
this.size[1]
);
}
};
ImageCrop.prototype.onPropertyChanged = function(name, value) {
this.properties[name] = value;
if (name == "scale") {
this.properties[name] = parseFloat(value);
if (this.properties[name] == 0) {
console.error("Error in scale");
this.properties[name] = 1.0;
}
} else {
this.properties[name] = parseInt(value);
}
this.createCanvas();
return true;
};
LiteGraph.registerNodeType("graphics/cropImage", ImageCrop);
//CANVAS stuff
function CanvasNode() {
this.addInput("clear", LiteGraph.ACTION);
this.addOutput("", "canvas");
this.properties = { width: 512, height: 512, autoclear: true };
this.canvas = document.createElement("canvas");
this.ctx = this.canvas.getContext("2d");
}
CanvasNode.title = "Canvas";
CanvasNode.desc = "Canvas to render stuff";
CanvasNode.prototype.onExecute = function() {
var canvas = this.canvas;
var w = this.properties.width | 0;
var h = this.properties.height | 0;
if (canvas.width != w) {
canvas.width = w;
}
if (canvas.height != h) {
canvas.height = h;
}
if (this.properties.autoclear) {
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
}
this.setOutputData(0, canvas);
};
CanvasNode.prototype.onAction = function(action, param) {
if (action == "clear") {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
}
};
LiteGraph.registerNodeType("graphics/canvas", CanvasNode);
function DrawImageNode() {
this.addInput("canvas", "canvas");
this.addInput("img", "image,canvas");
this.addInput("x", "number");
this.addInput("y", "number");
this.properties = { x: 0, y: 0, opacity: 1 };
}
DrawImageNode.title = "DrawImage";
DrawImageNode.desc = "Draws image into a canvas";
DrawImageNode.prototype.onExecute = function() {
var canvas = this.getInputData(0);
if (!canvas) {
return;
}
var img = this.getInputOrProperty("img");
if (!img) {
return;
}
var x = this.getInputOrProperty("x");
var y = this.getInputOrProperty("y");
var ctx = canvas.getContext("2d");
ctx.drawImage(img, x, y);
};
LiteGraph.registerNodeType("graphics/drawImage", DrawImageNode);
function DrawRectangleNode() {
this.addInput("canvas", "canvas");
this.addInput("x", "number");
this.addInput("y", "number");
this.addInput("w", "number");
this.addInput("h", "number");
this.properties = {
x: 0,
y: 0,
w: 10,
h: 10,
color: "white",
opacity: 1
};
}
DrawRectangleNode.title = "DrawRectangle";
DrawRectangleNode.desc = "Draws rectangle in canvas";
DrawRectangleNode.prototype.onExecute = function() {
var canvas = this.getInputData(0);
if (!canvas) {
return;
}
var x = this.getInputOrProperty("x");
var y = this.getInputOrProperty("y");
var w = this.getInputOrProperty("w");
var h = this.getInputOrProperty("h");
var ctx = canvas.getContext("2d");
ctx.fillRect(x, y, w, h);
};
LiteGraph.registerNodeType("graphics/drawRectangle", DrawRectangleNode);
function ImageVideo() {
this.addInput("t", "number");
this.addOutputs([["frame", "image"], ["t", "number"], ["d", "number"]]);
this.properties = { url: "", use_proxy: true };
}
ImageVideo.title = "Video";
ImageVideo.desc = "Video playback";
ImageVideo.widgets = [
{ name: "play", text: "PLAY", type: "minibutton" },
{ name: "stop", text: "STOP", type: "minibutton" },
{ name: "demo", text: "Demo video", type: "button" },
{ name: "mute", text: "Mute video", type: "button" }
];
ImageVideo.prototype.onExecute = function() {
if (!this.properties.url) {
return;
}
if (this.properties.url != this._video_url) {
this.loadVideo(this.properties.url);
}
if (!this._video || this._video.width == 0) {
return;
}
var t = this.getInputData(0);
if (t && t >= 0 && t <= 1.0) {
this._video.currentTime = t * this._video.duration;
this._video.pause();
}
this._video.dirty = true;
this.setOutputData(0, this._video);
this.setOutputData(1, this._video.currentTime);
this.setOutputData(2, this._video.duration);
this.setDirtyCanvas(true);
};
ImageVideo.prototype.onStart = function() {
this.play();
};
ImageVideo.prototype.onStop = function() {
this.stop();
};
ImageVideo.prototype.loadVideo = function(url) {
this._video_url = url;
var pos = url.substr(0,10).indexOf(":");
var protocol = "";
if(pos != -1)
protocol = url.substr(0,pos);
var host = "";
if(protocol)
{
host = url.substr(0,url.indexOf("/",protocol.length + 3));
host = host.substr(protocol.length+3);
}
if (
this.properties.use_proxy &&
protocol &&
LiteGraph.proxy &&
host != location.host
) {
url = LiteGraph.proxy + url.substr(url.indexOf(":") + 3);
}
this._video = document.createElement("video");
this._video.src = url;
this._video.type = "type=video/mp4";
this._video.muted = true;
this._video.autoplay = true;
var that = this;
this._video.addEventListener("loadedmetadata", function(e) {
//onload
console.log("Duration: " + this.duration + " seconds");
console.log("Size: " + this.videoWidth + "," + this.videoHeight);
that.setDirtyCanvas(true);
this.width = this.videoWidth;
this.height = this.videoHeight;
});
this._video.addEventListener("progress", function(e) {
//onload
console.log("video loading...");
});
this._video.addEventListener("error", function(e) {
console.error("Error loading video: " + this.src);
if (this.error) {
switch (this.error.code) {
case this.error.MEDIA_ERR_ABORTED:
console.error("You stopped the video.");
break;
case this.error.MEDIA_ERR_NETWORK:
console.error("Network error - please try again later.");
break;
case this.error.MEDIA_ERR_DECODE:
console.error("Video is broken..");
break;
case this.error.MEDIA_ERR_SRC_NOT_SUPPORTED:
console.error("Sorry, your browser can't play this video.");
break;
}
}
});
this._video.addEventListener("ended", function(e) {
console.log("Video Ended.");
this.play(); //loop
});
//document.body.appendChild(this.video);
};
ImageVideo.prototype.onPropertyChanged = function(name, value) {
this.properties[name] = value;
if (name == "url" && value != "") {
this.loadVideo(value);
}
return true;
};
ImageVideo.prototype.play = function() {
if (this._video && this._video.videoWidth ) { //is loaded
this._video.play();
}
};
ImageVideo.prototype.playPause = function() {
if (!this._video) {
return;
}
if (this._video.paused) {
this.play();
} else {
this.pause();
}
};
ImageVideo.prototype.stop = function() {
if (!this._video) {
return;
}
this._video.pause();
this._video.currentTime = 0;
};
ImageVideo.prototype.pause = function() {
if (!this._video) {
return;
}
console.log("Video paused");
this._video.pause();
};
ImageVideo.prototype.onWidget = function(e, widget) {
/*
if(widget.name == "demo")
{
this.loadVideo();
}
else if(widget.name == "play")
{
if(this._video)
this.playPause();
}
if(widget.name == "stop")
{
this.stop();
}
else if(widget.name == "mute")
{
if(this._video)
this._video.muted = !this._video.muted;
}
*/
};
LiteGraph.registerNodeType("graphics/video", ImageVideo);
// Texture Webcam *****************************************
function ImageWebcam() {
this.addOutput("Webcam", "image");
this.properties = { facingMode: "user" };
this.boxcolor = "black";
this.frame = 0;
}
ImageWebcam.title = "Webcam";
ImageWebcam.desc = "Webcam image";
ImageWebcam.is_webcam_open = false;
ImageWebcam.prototype.openStream = function() {
if (!navigator.getUserMedia) {
//console.log('getUserMedia() is not supported in your browser, use chrome and enable WebRTC from about://flags');
return;
}
this._waiting_confirmation = true;
// Not showing vendor prefixes.
var constraints = {
audio: false,
video: { facingMode: this.properties.facingMode }
};
navigator.mediaDevices
.getUserMedia(constraints)
.then(this.streamReady.bind(this))
.catch(onFailSoHard);
var that = this;
function onFailSoHard(e) {
console.log("Webcam rejected", e);
that._webcam_stream = false;
ImageWebcam.is_webcam_open = false;
that.boxcolor = "red";
that.trigger("stream_error");
}
};
ImageWebcam.prototype.closeStream = function() {
if (this._webcam_stream) {
var tracks = this._webcam_stream.getTracks();
if (tracks.length) {
for (var i = 0; i < tracks.length; ++i) {
tracks[i].stop();
}
}
ImageWebcam.is_webcam_open = false;
this._webcam_stream = null;
this._video = null;
this.boxcolor = "black";
this.trigger("stream_closed");
}
};
ImageWebcam.prototype.onPropertyChanged = function(name, value) {
if (name == "facingMode") {
this.properties.facingMode = value;
this.closeStream();
this.openStream();
}
};
ImageWebcam.prototype.onRemoved = function() {
this.closeStream();
};
ImageWebcam.prototype.streamReady = function(localMediaStream) {
this._webcam_stream = localMediaStream;
//this._waiting_confirmation = false;
this.boxcolor = "green";
var video = this._video;
if (!video) {
video = document.createElement("video");
video.autoplay = true;
video.srcObject = localMediaStream;
this._video = video;
//document.body.appendChild( video ); //debug
//when video info is loaded (size and so)
video.onloadedmetadata = function(e) {
// Ready to go. Do some stuff.
console.log(e);
ImageWebcam.is_webcam_open = true;
};
}
this.trigger("stream_ready", video);
};
ImageWebcam.prototype.onExecute = function() {
if (this._webcam_stream == null && !this._waiting_confirmation) {
this.openStream();
}
if (!this._video || !this._video.videoWidth) {
return;
}
this._video.frame = ++this.frame;
this._video.width = this._video.videoWidth;
this._video.height = this._video.videoHeight;
this.setOutputData(0, this._video);
for (var i = 1; i < this.outputs.length; ++i) {
if (!this.outputs[i]) {
continue;
}
switch (this.outputs[i].name) {
case "width":
this.setOutputData(i, this._video.videoWidth);
break;
case "height":
this.setOutputData(i, this._video.videoHeight);
break;
}
}
};
ImageWebcam.prototype.getExtraMenuOptions = function(graphcanvas) {
var that = this;
var txt = !that.properties.show ? "Show Frame" : "Hide Frame";
return [
{
content: txt,
callback: function() {
that.properties.show = !that.properties.show;
}
}
];
};
ImageWebcam.prototype.onDrawBackground = function(ctx) {
if (
this.flags.collapsed ||
this.size[1] <= 20 ||
!this.properties.show
) {
return;
}
if (!this._video) {
return;
}
//render to graph canvas
ctx.save();
ctx.drawImage(this._video, 0, 0, this.size[0], this.size[1]);
ctx.restore();
};
ImageWebcam.prototype.onGetOutputs = function() {
return [
["width", "number"],
["height", "number"],
["stream_ready", LiteGraph.EVENT],
["stream_closed", LiteGraph.EVENT],
["stream_error", LiteGraph.EVENT]
];
};
LiteGraph.registerNodeType("graphics/webcam", ImageWebcam);
})(this);