2022-11-29 14:55:25 -06:00
let blockNewImages = false ;
2022-12-11 21:50:25 -06:00
let generationQueue = [ ] ;
let generationAreas = new Set ( ) ;
2022-11-29 14:55:25 -06:00
/ * *
* Starts progress monitoring bar
*
* @ param { BoundingBox } bb Bouding Box to draw progress to
2022-12-06 15:28:34 -06:00
* @ param { ( data : object ) => void } [ oncheck ] Callback function for when a progress check returns
2022-11-29 14:55:25 -06:00
* @ returns { ( ) => void }
* /
2022-12-06 15:28:34 -06:00
const _monitorProgress = ( bb , oncheck = null ) => {
2022-11-29 14:55:25 -06:00
const minDelay = 1000 ;
2022-12-26 19:24:26 -06:00
const apiURL = ` ${ host } ${ config . api . path } progress?skip_current_image=true ` ;
2022-11-29 14:55:25 -06:00
const expanded = { ... bb } ;
expanded . x -- ;
expanded . y -- ;
expanded . w += 2 ;
expanded . h += 2 ;
// Get temporary layer to draw progress bar
const layer = imageCollection . registerLayer ( null , {
bb : expanded ,
2022-12-21 09:07:29 -06:00
category : "display" ,
2022-11-29 14:55:25 -06:00
} ) ;
layer . canvas . style . opacity = "70%" ;
let running = true ;
const _checkProgress = async ( ) => {
const init = performance . now ( ) ;
try {
const response = await fetch ( apiURL ) ;
/** @type {StableDiffusionProgressResponse} */
const data = await response . json ( ) ;
2022-12-06 15:28:34 -06:00
oncheck && oncheck ( data ) ;
2022-12-17 09:35:26 -06:00
layer . clear ( ) ;
2022-11-29 14:55:25 -06:00
// Draw Progress Bar
layer . ctx . fillStyle = "#5F5" ;
layer . ctx . fillRect ( 1 , 1 , bb . w * data . progress , 10 ) ;
// Draw Progress Text
layer . ctx . fillStyle = "#FFF" ;
layer . ctx . fillRect ( 0 , 15 , 60 , 25 ) ;
layer . ctx . fillRect ( bb . w - 58 , 15 , 60 , 25 ) ;
layer . ctx . font = "20px Open Sans" ;
layer . ctx . fillStyle = "#000" ;
layer . ctx . textAlign = "right" ;
layer . ctx . fillText ( ` ${ Math . round ( data . progress * 100 ) } % ` , 55 , 35 ) ;
// Draw ETA Text
layer . ctx . fillText ( ` ${ Math . round ( data . eta _relative ) } s ` , bb . w - 5 , 35 ) ;
} finally {
}
const timeSpent = performance . now ( ) - init ;
2023-07-15 12:10:45 -05:00
setTimeout ( ( ) => {
if ( running ) _checkProgress ( ) ;
} , Math . max ( 0 , minDelay - timeSpent ) ) ;
2022-11-29 14:55:25 -06:00
} ;
_checkProgress ( ) ;
return ( ) => {
imageCollection . deleteLayer ( layer ) ;
running = false ;
} ;
} ;
2023-01-04 15:14:52 -06:00
let busy = false ;
2023-01-04 12:33:27 -06:00
const generating = ( val ) => {
2023-01-04 15:14:52 -06:00
busy = val ;
2023-01-04 12:33:27 -06:00
if ( busy ) {
2023-01-04 15:14:52 -06:00
window . onbeforeunload = async ( ) => {
await sendInterrupt ( ) ;
} ;
} else {
window . onbeforeunload = null ;
2023-01-04 12:33:27 -06:00
}
2023-01-04 15:14:52 -06:00
} ;
2023-01-04 12:33:27 -06:00
2022-11-29 14:55:25 -06:00
/ * *
* Starts a dream
*
* @ param { "txt2img" | "img2img" } endpoint Endpoint to send the request to
* @ param { StableDiffusionRequest } request Stable diffusion request
2023-01-29 16:39:00 -06:00
* @ param { BoundingBox } bb Optional : Generated image placement location
2022-11-29 14:55:25 -06:00
* @ returns { Promise < string [ ] > }
* /
2023-01-29 16:39:00 -06:00
const _dream = async ( endpoint , request , bb = null ) => {
var bgImg = null ;
if (
endpoint == "img2img" &&
bb &&
toolbar . _current _tool . state . removeBackground
) {
bgImg = uil . getVisible ( bb , { includeBg : false } ) ;
}
2022-12-26 19:24:26 -06:00
const apiURL = ` ${ host } ${ config . api . path } ${ endpoint } ` ;
2023-01-20 18:53:16 -06:00
// if script fields are populated add them to the request
var scriptName = document . getElementById ( "script-name-input" ) . value ;
var scriptArgs = document . getElementById ( "script-args-input" ) . value ;
if ( scriptName . trim ( ) != "" && scriptArgs . trim ( ) != "" ) {
2023-01-22 13:29:24 -06:00
//TODO add some error handling and stuff?
2023-01-21 10:06:21 -06:00
request . script _name = scriptName . trim ( ) ;
// This is necessary so types can be properly specified
request . script _args = JSON . parse ( scriptArgs . trim ( ) || "[]" ) ;
2023-01-20 18:53:16 -06:00
}
2022-11-29 14:55:25 -06:00
2022-12-24 08:56:30 -06:00
// Debugging is enabled
if ( global . debug ) {
// Run in parallel
( async ( ) => {
// Create canvas
const canvas = document . createElement ( "canvas" ) ;
canvas . width = request . width ;
canvas . height = request . height * ( request . init _images . length + 1 ) ;
const ctx = canvas . getContext ( "2d" ) ;
// Load images and draw to canvas
for ( let i = 0 ; i < request . init _images . length ; i ++ ) {
try {
const image = document . createElement ( "img" ) ;
image . src = request . init _images [ i ] ;
await image . decode ( ) ;
ctx . drawImage ( image , 0 , i * request . height ) ;
} catch ( e ) { }
}
// Load mask and draw to canvas
if ( request . mask ) {
try {
const mask = document . createElement ( "img" ) ;
mask . src = request . mask ;
await mask . decode ( ) ;
ctx . drawImage ( mask , 0 , canvas . height - request . height ) ;
} catch ( e ) { }
}
downloadCanvas ( {
canvas ,
cropToContent : false ,
filename : ` openOutpaint_debug_ ${ new Date ( ) } .png ` ,
} ) ;
} ) ( ) ;
}
2022-11-29 14:55:25 -06:00
/** @type {StableDiffusionResponse} */
let data = null ;
try {
2023-01-04 12:33:27 -06:00
generating ( true ) ;
2023-01-03 21:41:03 -06:00
2022-11-29 14:55:25 -06:00
const response = await fetch ( apiURL , {
method : "POST" ,
headers : {
Accept : "application/json" ,
"Content-Type" : "application/json" ,
} ,
body : JSON . stringify ( request ) ,
} ) ;
data = await response . json ( ) ;
} finally {
2023-01-04 12:33:27 -06:00
generating ( false ) ;
2022-11-29 14:55:25 -06:00
}
2022-12-13 19:30:40 -06:00
var responseSubdata = JSON . parse ( data . info ) ;
2023-01-05 17:42:50 -06:00
console . debug ( responseSubdata ) ;
2022-12-15 09:25:20 -06:00
var returnData = {
images : data . images ,
seeds : responseSubdata . all _seeds ,
2023-01-29 16:39:00 -06:00
bgImg : bgImg ,
2022-12-15 09:25:20 -06:00
} ;
return returnData ;
2022-11-29 14:55:25 -06:00
} ;
/ * *
* Generate and pick an image for placement
*
* @ param { "txt2img" | "img2img" } endpoint Endpoint to send the request to
* @ param { StableDiffusionRequest } request Stable diffusion request
* @ param { BoundingBox } bb Generated image placement location
2022-12-13 21:16:48 -06:00
* @ param { object } options Options
* @ param { number } [ options . drawEvery = 0.2 / request . n _iter ] Percentage delta to draw progress at ( by default 20 % of each iteration )
2022-12-16 19:59:30 -06:00
* @ param { HTMLCanvasElement } [ options . keepUnmask = null ] Whether to force keep image under fully opaque mask
* @ param { number } [ options . keepUnmaskBlur = 0 ] Blur when applying full resolution back to the image
2022-11-29 14:55:25 -06:00
* @ returns { Promise < HTMLImageElement | null > }
* /
2022-12-13 21:16:48 -06:00
const _generate = async ( endpoint , request , bb , options = { } ) => {
2023-01-08 16:44:46 -06:00
var alertCount = 0 ;
2022-12-13 21:16:48 -06:00
defaultOpt ( options , {
drawEvery : 0.2 / request . n _iter ,
2022-12-16 19:59:30 -06:00
keepUnmask : null ,
keepUnmaskBlur : 0 ,
2022-12-13 21:16:48 -06:00
} ) ;
2022-12-12 15:25:32 -06:00
events . tool . dream . emit ( { event : "generate" , request } ) ;
2022-12-11 21:50:25 -06:00
const requestCopy = JSON . parse ( JSON . stringify ( request ) ) ;
// Block requests to identical areas
const areaid = ` ${ bb . x } - ${ bb . y } - ${ bb . w } - ${ bb . h } ` ;
if ( generationAreas . has ( areaid ) ) return ;
generationAreas . add ( areaid ) ;
// Await for queue
2022-12-12 15:49:53 -06:00
let cancelled = false ;
2022-12-11 21:50:25 -06:00
const waitQueue = async ( ) => {
const stopQueueMarchingAnts = march ( bb , { style : "#AAF" } ) ;
2022-12-12 15:49:53 -06:00
// Add cancel Button
const cancelButton = makeElement ( "button" , bb . x + bb . w - 100 , bb . y + bb . h ) ;
cancelButton . classList . add ( "dream-stop-btn" ) ;
cancelButton . textContent = "Cancel" ;
cancelButton . addEventListener ( "click" , ( ) => {
cancelled = true ;
imageCollection . inputElement . removeChild ( cancelButton ) ;
stopQueueMarchingAnts ( ) ;
} ) ;
imageCollection . inputElement . appendChild ( cancelButton ) ;
2022-12-11 21:50:25 -06:00
let qPromise = null ;
let qResolve = null ;
await new Promise ( ( finish ) => {
// Will be this request's (kind of) semaphore
qPromise = new Promise ( ( r ) => ( qResolve = r ) ) ;
generationQueue . push ( qPromise ) ;
// Wait for last generation to end
if ( generationQueue . length > 1 ) {
( async ( ) => {
await generationQueue [ generationQueue . length - 2 ] ;
finish ( ) ;
} ) ( ) ;
} else {
// If this is the first, just continue
finish ( ) ;
}
} ) ;
2022-12-12 15:49:53 -06:00
if ( ! cancelled ) {
imageCollection . inputElement . removeChild ( cancelButton ) ;
stopQueueMarchingAnts ( ) ;
}
2022-12-11 21:50:25 -06:00
return { promise : qPromise , resolve : qResolve } ;
} ;
const nextQueue = ( queueEntry ) => {
const generationIndex = generationQueue . findIndex (
( v ) => v === queueEntry . promise
) ;
generationQueue . splice ( generationIndex , 1 ) ;
queueEntry . resolve ( ) ;
} ;
const initialQ = await waitQueue ( ) ;
2022-11-29 14:55:25 -06:00
2022-12-12 15:49:53 -06:00
if ( cancelled ) {
nextQueue ( initialQ ) ;
return ;
}
2022-12-13 21:16:48 -06:00
// Save masked content
2022-12-16 19:59:30 -06:00
let keepUnmaskCanvas = null ;
let keepUnmaskCtx = null ;
2022-12-13 21:16:48 -06:00
2022-12-16 19:59:30 -06:00
if ( options . keepUnmask ) {
2022-12-15 08:51:18 -06:00
const visibleCanvas = uil . getVisible ( {
2022-12-16 19:59:30 -06:00
x : bb . x - options . keepUnmaskBlur ,
y : bb . y - options . keepUnmaskBlur ,
w : bb . w + 2 * options . keepUnmaskBlur ,
h : bb . h + 2 * options . keepUnmaskBlur ,
2022-12-15 08:51:18 -06:00
} ) ;
const visibleCtx = visibleCanvas . getContext ( "2d" ) ;
2022-12-13 21:16:48 -06:00
2022-12-16 19:59:30 -06:00
const ctx = options . keepUnmask . getContext ( "2d" , { willReadFrequently : true } ) ;
2022-12-13 21:16:48 -06:00
2022-12-15 08:51:18 -06:00
// Save current image
2022-12-16 19:59:30 -06:00
keepUnmaskCanvas = document . createElement ( "canvas" ) ;
keepUnmaskCanvas . width = options . keepUnmask . width ;
keepUnmaskCanvas . height = options . keepUnmask . height ;
2022-12-13 21:16:48 -06:00
2022-12-16 19:59:30 -06:00
keepUnmaskCtx = keepUnmaskCanvas . getContext ( "2d" , {
willReadFrequently : true ,
} ) ;
2022-12-13 21:16:48 -06:00
if (
2022-12-16 19:59:30 -06:00
visibleCanvas . width !==
keepUnmaskCanvas . width + 2 * options . keepUnmaskBlur ||
visibleCanvas . height !==
keepUnmaskCanvas . height + 2 * options . keepUnmaskBlur
2022-12-13 21:16:48 -06:00
) {
throw new Error (
"[dream] Provided mask is not the same size as the bounding box"
) ;
}
2022-12-15 08:51:18 -06:00
// Cut out changing elements
const blurMaskCanvas = document . createElement ( "canvas" ) ;
// A bit bigger to handle literal corner cases
2022-12-16 19:59:30 -06:00
blurMaskCanvas . width = bb . w + options . keepUnmaskBlur * 2 ;
blurMaskCanvas . height = bb . h + options . keepUnmaskBlur * 2 ;
2022-12-15 08:51:18 -06:00
const blurMaskCtx = blurMaskCanvas . getContext ( "2d" ) ;
const blurMaskData = blurMaskCtx . getImageData (
2022-12-16 19:59:30 -06:00
options . keepUnmaskBlur ,
options . keepUnmaskBlur ,
keepUnmaskCanvas . width ,
keepUnmaskCanvas . height
2022-12-13 21:16:48 -06:00
) ;
2022-12-15 08:51:18 -06:00
const image = blurMaskData . data ;
2022-12-13 21:16:48 -06:00
const maskData = ctx . getImageData (
0 ,
0 ,
2022-12-16 19:59:30 -06:00
options . keepUnmask . width ,
options . keepUnmask . height
2022-12-13 21:16:48 -06:00
) ;
const mask = maskData . data ;
for ( let i = 0 ; i < mask . length ; i += 4 ) {
if ( mask [ i ] !== 0 || mask [ i + 1 ] !== 0 || mask [ i + 2 ] !== 0 ) {
2022-12-15 08:51:18 -06:00
// If pixel is fully black
// Set pixel as fully black here as well
2022-12-13 21:16:48 -06:00
image [ i ] = 0 ;
image [ i + 1 ] = 0 ;
image [ i + 2 ] = 0 ;
2022-12-15 08:51:18 -06:00
image [ i + 3 ] = 255 ;
2022-12-13 21:16:48 -06:00
}
}
2022-12-15 08:51:18 -06:00
blurMaskCtx . putImageData (
blurMaskData ,
2022-12-16 19:59:30 -06:00
options . keepUnmaskBlur ,
options . keepUnmaskBlur
2022-12-15 08:51:18 -06:00
) ;
2022-12-16 19:59:30 -06:00
visibleCtx . filter = ` blur( ${ options . keepUnmaskBlur } px) ` ;
2022-12-15 08:51:18 -06:00
visibleCtx . globalCompositeOperation = "destination-out" ;
visibleCtx . drawImage ( blurMaskCanvas , 0 , 0 ) ;
2022-12-13 21:16:48 -06:00
2022-12-16 19:59:30 -06:00
keepUnmaskCtx . drawImage (
2022-12-15 08:51:18 -06:00
visibleCanvas ,
2022-12-16 19:59:30 -06:00
- options . keepUnmaskBlur ,
- options . keepUnmaskBlur
2022-12-15 08:51:18 -06:00
) ;
2022-12-13 21:16:48 -06:00
}
2022-11-29 14:55:25 -06:00
// Images to select through
let at = 0 ;
2022-12-09 19:07:18 -06:00
/** @type {Array<string|null>} */
const images = [ null ] ;
2022-12-15 09:25:20 -06:00
const seeds = [ - 1 ] ;
2023-03-12 10:27:32 -05:00
const markedImages = [ null ] ; //A sparse array of booleans indicating which images have been marked, by index
2022-11-29 14:55:25 -06:00
/** @type {HTMLDivElement} */
let imageSelectMenu = null ;
// Layer for the images
const layer = imageCollection . registerLayer ( null , {
2022-11-29 16:47:19 -06:00
after : maskPaintLayer ,
2022-12-21 09:07:29 -06:00
category : "display" ,
2022-11-29 14:55:25 -06:00
} ) ;
2022-12-06 15:28:34 -06:00
const redraw = ( url = images [ at ] ) => {
2022-12-16 21:55:53 -06:00
if ( url === null ) layer . clear ( ) ;
2022-12-06 15:28:34 -06:00
if ( ! url ) return ;
2022-12-15 08:51:18 -06:00
const img = new Image ( ) ;
img . src = "data:image/png;base64," + url ;
img . addEventListener ( "load" , ( ) => {
const canvas = document . createElement ( "canvas" ) ;
canvas . width = bb . w ;
canvas . height = bb . h ;
// Creates new canvas for blurred mask
const blurMaskCanvas = document . createElement ( "canvas" ) ;
2022-12-16 19:59:30 -06:00
blurMaskCanvas . width = bb . w + options . keepUnmaskBlur * 2 ;
blurMaskCanvas . height = bb . h + options . keepUnmaskBlur * 2 ;
2022-12-15 08:51:18 -06:00
const ctx = canvas . getContext ( "2d" ) ;
ctx . drawImage ( img , 0 , 0 , img . width , img . height , 0 , 0 , bb . w , bb . h ) ;
2022-12-16 19:59:30 -06:00
if ( keepUnmaskCanvas ) {
ctx . drawImage ( keepUnmaskCanvas , 0 , 0 ) ;
2022-12-15 08:51:18 -06:00
}
2022-12-16 19:57:28 -06:00
layer . clear ( ) ;
2022-12-06 15:28:34 -06:00
layer . ctx . drawImage (
2022-12-15 08:51:18 -06:00
canvas ,
2022-12-06 15:28:34 -06:00
0 ,
0 ,
2022-12-15 08:51:18 -06:00
canvas . width ,
canvas . height ,
2022-12-06 15:28:34 -06:00
bb . x ,
bb . y ,
bb . w ,
bb . h
) ;
2022-11-29 14:55:25 -06:00
} ) ;
} ;
2023-01-01 19:30:48 -06:00
const sendInterrupt = ( ) => {
fetch ( ` ${ host } ${ config . api . path } interrupt ` , { method : "POST" } ) ;
2023-01-01 22:06:09 -06:00
} ;
2023-01-01 19:30:48 -06:00
2022-12-06 15:28:34 -06:00
// Add Interrupt Button
const interruptButton = makeElement ( "button" , bb . x + bb . w - 100 , bb . y + bb . h ) ;
2022-12-12 15:49:53 -06:00
interruptButton . classList . add ( "dream-stop-btn" ) ;
2022-12-06 15:28:34 -06:00
interruptButton . textContent = "Interrupt" ;
interruptButton . addEventListener ( "click" , ( ) => {
2023-01-01 19:30:48 -06:00
sendInterrupt ( ) ;
2022-12-06 15:28:34 -06:00
interruptButton . disabled = true ;
} ) ;
2022-12-14 12:12:44 -06:00
const marchingOptions = { } ;
const stopMarchingAnts = march ( bb , marchingOptions ) ;
2022-11-29 14:55:25 -06:00
// First Dream Run
2022-12-03 06:53:12 -06:00
console . info ( ` [dream] Generating images for prompt ' ${ request . prompt } ' ` ) ;
console . debug ( request ) ;
2023-01-01 19:30:48 -06:00
eagerGenerateCount = toolbar . _current _tool . state . eagerGenerateCount ;
isDreamComplete = false ;
2022-12-03 10:20:35 -06:00
let stopProgress = null ;
try {
2022-12-06 15:28:34 -06:00
let stopDrawingStatus = false ;
let lastProgress = 0 ;
2022-12-13 21:16:48 -06:00
let nextCP = options . drawEvery ;
2022-12-06 15:28:34 -06:00
stopProgress = _monitorProgress ( bb , ( data ) => {
if ( stopDrawingStatus ) return ;
if ( lastProgress < nextCP && data . progress >= nextCP ) {
2022-12-13 21:16:48 -06:00
nextCP += options . drawEvery ;
2022-12-26 19:24:26 -06:00
fetch (
` ${ host } ${ config . api . path } progress?skip_current_image=false `
) . then ( async ( response ) => {
if ( stopDrawingStatus ) return ;
const imagedata = await response . json ( ) ;
redraw ( imagedata . current _image ) ;
} ) ;
2022-12-06 15:28:34 -06:00
}
lastProgress = data . progress ;
} ) ;
imageCollection . inputElement . appendChild ( interruptButton ) ;
2023-01-29 16:39:00 -06:00
var dreamData = await _dream ( endpoint , requestCopy , bb ) ;
2022-12-15 09:25:20 -06:00
images . push ( ... dreamData . images ) ;
seeds . push ( ... dreamData . seeds ) ;
2022-12-06 15:28:34 -06:00
stopDrawingStatus = true ;
2022-12-09 19:07:18 -06:00
at = 1 ;
2022-12-03 10:20:35 -06:00
} catch ( e ) {
2023-02-14 20:44:37 -06:00
notifications . notify (
` Error generating images. Please try again or see console for more details ` ,
{
type : NotificationType . ERROR ,
timeout : config . notificationTimeout * 2 ,
}
2022-12-03 10:20:35 -06:00
) ;
console . warn ( ` [dream] Error generating images: ` ) ;
console . warn ( e ) ;
} finally {
stopProgress ( ) ;
2022-12-06 15:28:34 -06:00
imageCollection . inputElement . removeChild ( interruptButton ) ;
2022-12-03 10:20:35 -06:00
}
2022-11-29 14:55:25 -06:00
2023-01-01 19:30:48 -06:00
const needMoreGenerations = ( ) => {
2023-01-01 22:06:09 -06:00
return (
eagerGenerateCount > 0 &&
images . length - highestNavigatedImageIndex <= eagerGenerateCount
) ;
} ;
2023-01-01 19:30:48 -06:00
const isGenerationPending = ( ) => {
return generationQueue . length > 0 ;
2023-01-01 22:06:09 -06:00
} ;
2023-01-01 19:30:48 -06:00
let highestNavigatedImageIndex = 0 ;
2022-11-29 18:16:59 -06:00
// Image navigation
const prevImg = ( ) => {
at -- ;
if ( at < 0 ) at = images . length - 1 ;
2023-02-06 22:26:55 -06:00
activateImgAt ( at ) ;
2022-11-29 18:16:59 -06:00
} ;
2023-02-06 22:26:55 -06:00
const prevImgEvent = ( evn ) => {
if ( evn . shiftKey ) {
prevMarkedImg ( ) ;
} else {
prevImg ( ) ;
}
2023-03-12 10:27:32 -05:00
} ;
2023-02-06 22:26:55 -06:00
2022-11-29 18:16:59 -06:00
const nextImg = ( ) => {
at ++ ;
if ( at >= images . length ) at = 0 ;
2023-01-01 19:30:48 -06:00
highestNavigatedImageIndex = Math . max ( at , highestNavigatedImageIndex ) ;
2023-02-06 22:26:55 -06:00
activateImgAt ( at ) ;
2023-03-12 10:27:32 -05:00
2023-01-01 19:30:48 -06:00
if ( needMoreGenerations ( ) && ! isGenerationPending ( ) ) {
makeMore ( ) ;
}
2022-11-29 18:16:59 -06:00
} ;
2023-02-06 22:26:55 -06:00
const nextImgEvent = ( evn ) => {
if ( evn . shiftKey ) {
nextMarkedImg ( ) ;
} else {
nextImg ( ) ;
}
2023-03-12 10:27:32 -05:00
} ;
2023-02-06 22:26:55 -06:00
const activateImgAt = ( at ) => {
updateImageIndexText ( ) ;
var seed = seeds [ at ] ;
seedbtn . title = "Use seed " + seed ;
redraw ( ) ;
2023-03-12 10:27:32 -05:00
} ;
2023-02-06 22:26:55 -06:00
2022-11-29 18:16:59 -06:00
const applyImg = async ( ) => {
2022-12-11 18:01:55 -06:00
if ( ! images [ at ] ) return ;
2022-11-29 18:16:59 -06:00
const img = new Image ( ) ;
// load the image data after defining the closure
img . src = "data:image/png;base64," + images [ at ] ;
img . addEventListener ( "load" , ( ) => {
2023-01-29 16:39:00 -06:00
let canvas = document . createElement ( "canvas" ) ;
2022-12-13 21:16:48 -06:00
canvas . width = bb . w ;
canvas . height = bb . h ;
const ctx = canvas . getContext ( "2d" ) ;
ctx . drawImage ( img , 0 , 0 , img . width , img . height , 0 , 0 , bb . w , bb . h ) ;
2022-12-16 19:59:30 -06:00
if ( keepUnmaskCanvas ) {
ctx . drawImage ( keepUnmaskCanvas , 0 , 0 ) ;
2022-12-15 08:51:18 -06:00
}
2022-12-13 21:16:48 -06:00
2023-01-27 20:00:48 -06:00
if ( localStorage . getItem ( "openoutpaint/settings.autolayer" ) == "true" ) {
commands . runCommand ( "addLayer" , "Added Layer" , { } ) ;
}
2023-01-29 16:39:00 -06:00
if (
endpoint == "img2img" &&
toolbar . _current _tool . state . removeBackground
) {
2023-02-18 12:00:10 -06:00
canvas = subtractBackground (
canvas ,
bb ,
dreamData . bgImg ,
toolbar . _current _tool . state . carve _blur ,
toolbar . _current _tool . state . carve _threshold
) ;
2023-01-29 16:39:00 -06:00
}
2023-01-24 20:37:38 -06:00
let commandLog = "" ;
const addline = ( v , newline = true ) => {
commandLog += v ;
if ( newline ) commandLog += "\n" ;
} ;
addline (
` Dreamed image at x: ${ bb . x } , y: ${ bb . y } , w: ${ bb . w } , h: ${ bb . h } `
) ;
addline ( ` - resolution: ( ${ request . width } , ${ request . height } ) ` ) ;
addline ( " - generation:" ) ;
addline ( ` + Seed = ${ seeds [ at ] } ` ) ;
addline ( ` + Steps = ${ request . steps } ` ) ;
addline ( ` + CFG = ${ request . cfg _scale } ` ) ;
2023-01-27 22:15:51 -06:00
addline ( ` + Sampler = ${ request . sampler _index } ` ) ;
addline ( ` + Model = ${ modelAutoComplete . value } ` ) ;
addline ( ` + +Prompt = ${ request . prompt } ` ) ;
2023-01-28 23:59:30 -06:00
addline ( ` + -Prompt = ${ request . negative _prompt } ` ) ;
2023-01-29 16:39:00 -06:00
addline ( ` + Styles = ${ request . styles . join ( ", " ) } ` , false ) ;
2023-01-24 20:37:38 -06:00
commands . runCommand (
"drawImage" ,
"Image Dream" ,
{
x : bb . x ,
y : bb . y ,
w : bb . w ,
h : bb . h ,
image : canvas ,
} ,
{
extra : {
log : commandLog ,
} ,
}
) ;
2022-12-19 18:51:29 -06:00
clean ( ! toolbar . _current _tool . state . preserveMasks ) ;
2022-11-29 18:16:59 -06:00
} ) ;
} ;
2023-01-10 17:00:45 -06:00
const removeImg = async ( ) => {
if ( ! images [ at ] ) return ;
images . splice ( at , 1 ) ;
seeds . splice ( at , 1 ) ;
2023-03-12 10:27:32 -05:00
markedImages . splice ( at , 1 ) ;
2023-01-20 16:19:01 -06:00
if ( at > images . length - 1 ) prevImg ( ) ;
2023-01-20 19:33:42 -06:00
if ( images . length - 1 === 0 ) discardImg ( ) ;
2023-02-06 22:26:55 -06:00
updateImageIndexText ( ) ;
2023-01-10 17:00:45 -06:00
var seed = seeds [ at ] ;
seedbtn . title = "Use seed " + seed ;
redraw ( ) ;
} ;
2023-02-06 22:26:55 -06:00
const toggleMarkedImg = async ( ) => {
markedImages [ at ] = markedImages [ at ] == true ? null : true ;
activateImgAt ( at ) ; //redraw just to update caption
} ;
const nextMarkedImg = ( ) => {
var nextIndex = getNextMarkedImage ( at ) ;
if ( nextIndex == null ) {
//If no next marked image, and we're not currently on a marked image, then return the last marked image in the list, if any, rather than doing nothing
if ( markedImages [ at ] == true ) {
return ;
} else {
nextIndex = getPrevMarkedImage ( at ) ;
if ( nextIndex == null ) {
return ;
}
}
}
at = nextIndex ;
activateImgAt ( at ) ;
2023-03-12 10:27:32 -05:00
} ;
2023-02-06 22:26:55 -06:00
const prevMarkedImg = ( ) => {
var nextIndex = getPrevMarkedImage ( at ) ;
if ( nextIndex == null ) {
//If no previous marked image, and we're not currently on a marked image, then return the next image in the list, if any, rather than doing nothing
if ( markedImages [ at ] == true ) {
return ;
} else {
nextIndex = getNextMarkedImage ( at ) ;
if ( nextIndex == null ) {
return ;
}
}
}
at = nextIndex ;
activateImgAt ( at ) ;
2023-03-12 10:27:32 -05:00
} ;
2023-02-06 22:26:55 -06:00
const getNextMarkedImage = ( at ) => {
2023-03-12 10:27:32 -05:00
for ( let i = at + 1 ; i < markedImages . length ; i ++ ) {
if ( markedImages [ i ] != null ) {
return i ;
}
2023-02-06 22:26:55 -06:00
}
return null ;
} ;
2023-03-12 10:27:32 -05:00
2023-02-06 22:26:55 -06:00
const getPrevMarkedImage = ( at ) => {
2023-03-12 10:27:32 -05:00
for ( let i = at - 1 ; i >= 0 ; -- i ) {
if ( markedImages [ i ] != null ) {
return i ;
}
2023-02-06 22:26:55 -06:00
}
return null ;
} ;
const updateImageIndexText = ( ) => {
var markedImageIndicator = markedImages [ at ] == true ? "*" : "" ;
2023-03-12 10:27:32 -05:00
imageindextxt . textContent = ` ${ markedImageIndicator } ${ at } / ${
images . length - 1
} ` ;
} ;
2023-02-06 22:26:55 -06:00
2022-11-30 15:57:40 -06:00
const makeMore = async ( ) => {
2022-12-11 21:50:25 -06:00
const moreQ = await waitQueue ( ) ;
2022-12-03 10:20:35 -06:00
try {
stopProgress = _monitorProgress ( bb ) ;
2022-12-06 15:28:34 -06:00
interruptButton . disabled = false ;
imageCollection . inputElement . appendChild ( interruptButton ) ;
2022-12-13 20:03:58 -06:00
if ( requestCopy . seed != - 1 ) {
2022-12-13 20:08:46 -06:00
requestCopy . seed =
parseInt ( requestCopy . seed ) +
requestCopy . batch _size * requestCopy . n _iter ;
2022-12-13 20:03:58 -06:00
}
2022-12-15 09:25:20 -06:00
dreamData = await _dream ( endpoint , requestCopy ) ;
images . push ( ... dreamData . images ) ;
seeds . push ( ... dreamData . seeds ) ;
2023-02-06 22:26:55 -06:00
updateImageIndexText ( ) ;
2022-12-03 10:20:35 -06:00
} catch ( e ) {
2023-01-08 16:44:46 -06:00
if ( alertCount < 2 ) {
2023-02-14 20:44:37 -06:00
notifications . notify (
` Error generating images. Please try again or see console for more details ` ,
{
type : NotificationType . ERROR ,
timeout : config . notificationTimeout * 2 ,
}
2023-01-08 16:44:46 -06:00
) ;
} else {
eagerGenerateCount = 0 ;
}
alertCount ++ ;
2022-12-03 10:20:35 -06:00
console . warn ( ` [dream] Error generating images: ` ) ;
console . warn ( e ) ;
} finally {
stopProgress ( ) ;
2022-12-06 15:28:34 -06:00
imageCollection . inputElement . removeChild ( interruptButton ) ;
2022-12-03 10:20:35 -06:00
}
2022-12-11 21:50:25 -06:00
nextQueue ( moreQ ) ;
2023-01-01 19:30:48 -06:00
//Start the next batch if we're eager-generating
if ( needMoreGenerations ( ) && ! isGenerationPending ( ) && ! isDreamComplete ) {
makeMore ( ) ;
}
2022-11-30 15:57:40 -06:00
} ;
2022-11-29 18:16:59 -06:00
const discardImg = async ( ) => {
clean ( ) ;
} ;
2022-12-11 18:01:55 -06:00
const saveImg = async ( ) => {
if ( ! images [ at ] ) return ;
const img = new Image ( ) ;
// load the image data after defining the closure
img . src = "data:image/png;base64," + images [ at ] ;
img . addEventListener ( "load" , ( ) => {
const canvas = document . createElement ( "canvas" ) ;
canvas . width = img . width ;
canvas . height = img . height ;
canvas . getContext ( "2d" ) . drawImage ( img , 0 , 0 ) ;
downloadCanvas ( {
canvas ,
filename : ` openOutpaint - dream - ${ request . prompt } - ${ at } .png ` ,
} ) ;
} ) ;
} ;
2022-11-29 18:16:59 -06:00
// Listen for keyboard arrows
const onarrow = ( evn ) => {
switch ( evn . target . tagName . toLowerCase ( ) ) {
case "input" :
case "textarea" :
case "select" :
case "button" :
return ; // If in an input field, do not process arrow input
default :
// Do nothing
break ;
}
2022-11-30 15:57:40 -06:00
switch ( evn . key ) {
case "+" :
makeMore ( ) ;
2022-11-29 18:16:59 -06:00
break ;
2023-01-10 17:00:45 -06:00
case "-" :
removeImg ( ) ;
break ;
2023-02-06 22:26:55 -06:00
case "*" :
toggleMarkedImg ( ) ;
2022-11-29 18:16:59 -06:00
default :
2022-11-30 15:57:40 -06:00
switch ( evn . code ) {
case "ArrowRight" :
2023-02-06 22:26:55 -06:00
nextImgEvent ( evn . evn ) ;
2022-11-30 15:57:40 -06:00
break ;
case "ArrowLeft" :
2023-02-06 22:26:55 -06:00
prevImgEvent ( evn . evn ) ;
2022-11-30 15:57:40 -06:00
break ;
2022-11-30 16:18:32 -06:00
case "Enter" :
2022-11-30 15:57:40 -06:00
applyImg ( ) ;
break ;
2022-11-30 16:18:32 -06:00
case "Escape" :
discardImg ( ) ;
break ;
2022-11-30 15:57:40 -06:00
default :
break ;
}
2022-11-29 18:16:59 -06:00
break ;
}
} ;
keyboard . listen . onkeyclick . on ( onarrow ) ;
2022-12-14 12:12:44 -06:00
// For handling mouse events for navigation
const onmovehandler = mouse . listen . world . onmousemove . on (
( evn , state ) => {
const contains = bb . contains ( evn . x , evn . y ) ;
2022-12-17 15:07:10 -06:00
if ( ! contains && ! state . dream _processed ) {
2022-12-14 12:12:44 -06:00
imageCollection . inputElement . style . cursor = "auto" ;
2022-12-17 15:07:10 -06:00
toolbar . _current _tool . state . block _res _change = false ;
}
if ( ! contains || state . dream _processed ) {
marchingOptions . style = "#FFF" ;
toolbar . _current _tool . state . block _res _change = false ;
}
2022-12-14 12:12:44 -06:00
if ( ! state . dream _processed && contains ) {
marchingOptions . style = "#F55" ;
imageCollection . inputElement . style . cursor = "pointer" ;
state . dream _processed = true ;
2022-12-17 15:07:10 -06:00
toolbar . _current _tool . state . block _res _change = true ;
2022-12-14 12:12:44 -06:00
}
} ,
0 ,
true
) ;
const onclickhandler = mouse . listen . world . btn . left . onclick . on (
( evn , state ) => {
if ( ! state . dream _processed && bb . contains ( evn . x , evn . y ) ) {
applyImg ( ) ;
imageCollection . inputElement . style . cursor = "auto" ;
state . dream _processed = true ;
}
} ,
1 ,
true
) ;
const oncancelhandler = mouse . listen . world . btn . right . onclick . on (
( evn , state ) => {
if ( ! state . dream _processed && bb . contains ( evn . x , evn . y ) ) {
2023-01-10 17:00:45 -06:00
if ( images . length > 1 ) {
removeImg ( ) ;
} else {
discardImg ( ) ;
}
2022-12-14 12:12:44 -06:00
imageCollection . inputElement . style . cursor = "auto" ;
state . dream _processed = true ;
}
} ,
1 ,
true
) ;
2022-12-15 06:43:26 -06:00
const onmorehandler = mouse . listen . world . btn . middle . onclick . on (
( evn , state ) => {
2022-12-26 22:04:56 -06:00
if ( ! state . dream _processed && bb . contains ( evn . x , evn . y ) ) {
2022-12-15 06:43:26 -06:00
makeMore ( ) ;
state . dream _processed = true ;
}
} ,
1 ,
true
) ;
2022-12-14 12:12:44 -06:00
const onwheelhandler = mouse . listen . world . onwheel . on (
( evn , state ) => {
if ( ! state . dream _processed && bb . contains ( evn . x , evn . y ) ) {
2023-02-06 22:26:55 -06:00
if ( evn . delta < 0 ) {
nextImgEvent ( evn . evn ) ;
} else prevImgEvent ( evn . evn ) ;
2022-12-14 12:12:44 -06:00
state . dream _processed = true ;
}
} ,
1 ,
true
) ;
2022-11-29 14:55:25 -06:00
// Cleans up
2022-11-30 20:20:57 -06:00
const clean = ( removeBrushMask = false ) => {
if ( removeBrushMask ) {
2022-11-30 21:25:06 -06:00
maskPaintCtx . clearRect ( bb . x , bb . y , bb . w , bb . h ) ;
2022-11-30 20:20:57 -06:00
}
2022-11-29 14:55:25 -06:00
stopMarchingAnts ( ) ;
imageCollection . inputElement . removeChild ( imageSelectMenu ) ;
imageCollection . deleteLayer ( layer ) ;
2022-11-29 18:16:59 -06:00
keyboard . listen . onkeyclick . clear ( onarrow ) ;
2022-12-11 21:50:25 -06:00
// Remove area from no-generate list
generationAreas . delete ( areaid ) ;
2022-12-14 12:12:44 -06:00
// Stop handling inputs
mouse . listen . world . onmousemove . clear ( onmovehandler ) ;
mouse . listen . world . btn . left . onclick . clear ( onclickhandler ) ;
mouse . listen . world . btn . right . onclick . clear ( oncancelhandler ) ;
2022-12-15 06:43:26 -06:00
mouse . listen . world . btn . middle . onclick . clear ( onmorehandler ) ;
mouse . listen . world . onwheel . clear ( onwheelhandler ) ;
2023-01-01 19:30:48 -06:00
isDreamComplete = true ;
2023-01-04 15:14:52 -06:00
generating ( false ) ;
2022-11-29 14:55:25 -06:00
} ;
redraw ( ) ;
imageSelectMenu = makeElement ( "div" , bb . x , bb . y + bb . h ) ;
const imageindextxt = document . createElement ( "button" ) ;
2023-02-06 22:26:55 -06:00
updateImageIndexText ( ) ;
2023-03-12 10:27:32 -05:00
2022-11-29 14:55:25 -06:00
imageindextxt . addEventListener ( "click" , ( ) => {
at = 0 ;
2023-02-06 22:26:55 -06:00
updateImageIndexText ( ) ;
2022-11-29 14:55:25 -06:00
redraw ( ) ;
} ) ;
const backbtn = document . createElement ( "button" ) ;
backbtn . textContent = "<" ;
backbtn . title = "Previous Image" ;
2023-02-06 22:26:55 -06:00
backbtn . addEventListener ( "click" , prevImgEvent ) ;
2022-11-29 14:55:25 -06:00
imageSelectMenu . appendChild ( backbtn ) ;
imageSelectMenu . appendChild ( imageindextxt ) ;
const nextbtn = document . createElement ( "button" ) ;
nextbtn . textContent = ">" ;
nextbtn . title = "Next Image" ;
2023-02-06 22:26:55 -06:00
nextbtn . addEventListener ( "click" , nextImgEvent ) ;
2022-11-29 14:55:25 -06:00
imageSelectMenu . appendChild ( nextbtn ) ;
const morebtn = document . createElement ( "button" ) ;
morebtn . textContent = "+" ;
morebtn . title = "Generate More" ;
2022-11-30 15:57:40 -06:00
morebtn . addEventListener ( "click" , makeMore ) ;
2022-11-29 14:55:25 -06:00
imageSelectMenu . appendChild ( morebtn ) ;
2023-01-10 17:00:45 -06:00
const removebtn = document . createElement ( "button" ) ;
removebtn . textContent = "-" ;
removebtn . title = "Remove From Batch" ;
removebtn . addEventListener ( "click" , removeImg ) ;
imageSelectMenu . appendChild ( removebtn ) ;
2022-11-29 14:55:25 -06:00
const acceptbtn = document . createElement ( "button" ) ;
acceptbtn . textContent = "Y" ;
acceptbtn . title = "Apply Current" ;
2022-11-29 18:16:59 -06:00
acceptbtn . addEventListener ( "click" , applyImg ) ;
2022-11-29 14:55:25 -06:00
imageSelectMenu . appendChild ( acceptbtn ) ;
const discardbtn = document . createElement ( "button" ) ;
discardbtn . textContent = "N" ;
discardbtn . title = "Cancel" ;
2022-11-29 18:16:59 -06:00
discardbtn . addEventListener ( "click" , discardImg ) ;
2022-11-29 14:55:25 -06:00
imageSelectMenu . appendChild ( discardbtn ) ;
const resourcebtn = document . createElement ( "button" ) ;
resourcebtn . textContent = "R" ;
resourcebtn . title = "Save to Resources" ;
resourcebtn . addEventListener ( "click" , async ( ) => {
const img = new Image ( ) ;
// load the image data after defining the closure
img . src = "data:image/png;base64," + images [ at ] ;
img . addEventListener ( "load" , ( ) => {
2022-12-13 19:30:40 -06:00
const response = prompt (
"Enter new resource name" ,
2022-12-15 09:25:20 -06:00
"Dream Resource " + seeds [ at ]
2022-12-13 19:30:40 -06:00
) ;
2022-11-29 18:16:59 -06:00
if ( response ) {
tools . stamp . state . addResource ( response , img ) ;
redraw ( ) ; // Redraw to avoid strange cursor behavior
}
2022-11-29 14:55:25 -06:00
} ) ;
} ) ;
imageSelectMenu . appendChild ( resourcebtn ) ;
2022-12-11 18:01:55 -06:00
const savebtn = document . createElement ( "button" ) ;
savebtn . textContent = "S" ;
savebtn . title = "Download image to computer" ;
savebtn . addEventListener ( "click" , async ( ) => {
saveImg ( ) ;
} ) ;
imageSelectMenu . appendChild ( savebtn ) ;
2022-12-11 21:50:25 -06:00
2022-12-13 19:30:40 -06:00
const seedbtn = document . createElement ( "button" ) ;
seedbtn . textContent = "U" ;
2022-12-15 09:25:20 -06:00
seedbtn . title = "Use seed " + ` ${ seeds [ at ] } ` ;
2022-12-13 19:30:40 -06:00
seedbtn . addEventListener ( "click" , ( ) => {
2022-12-15 09:51:08 -06:00
sendSeed ( seeds [ at ] ) ;
2022-12-13 19:30:40 -06:00
} ) ;
imageSelectMenu . appendChild ( seedbtn ) ;
2023-02-06 22:26:55 -06:00
const toggleMarkedButton = document . createElement ( "button" ) ;
toggleMarkedButton . textContent = "*" ;
toggleMarkedButton . title = "Mark/Unmark" ;
toggleMarkedButton . addEventListener ( "click" , toggleMarkedImg ) ;
imageSelectMenu . appendChild ( toggleMarkedButton ) ;
2022-12-11 21:50:25 -06:00
nextQueue ( initialQ ) ;
2023-01-01 19:30:48 -06:00
//Start the next batch after the initial generation
if ( needMoreGenerations ( ) ) {
makeMore ( ) ;
}
2022-11-29 14:55:25 -06:00
} ;
/ * *
* Callback for generating a image ( dream tool )
*
* @ param { * } evn
* @ param { * } state
* /
2022-12-13 17:22:16 -06:00
const dream _generate _callback = async ( bb , resolution , state ) => {
2022-12-11 21:50:25 -06:00
// Build request to the API
const request = { } ;
2023-07-08 15:21:58 -05:00
const canvasTransport = { } ; //this is the worst idea but i hate myself so i'm doing it anyway
2022-12-11 21:50:25 -06:00
Object . assign ( request , stableDiffusionData ) ;
2022-11-22 16:24:55 -06:00
2022-12-13 17:22:16 -06:00
request . width = resolution . w ;
request . height = resolution . h ;
2022-12-13 12:13:01 -06:00
2022-12-11 21:50:25 -06:00
// Load prompt (maybe we should add some events so we don't have to do this)
request . prompt = document . getElementById ( "prompt" ) . value ;
request . negative _prompt = document . getElementById ( "negPrompt" ) . value ;
2022-11-22 16:24:55 -06:00
2022-12-11 21:50:25 -06:00
// Get visible pixels
const visibleCanvas = uil . getVisible ( bb ) ;
2022-11-22 16:24:55 -06:00
2023-07-08 10:49:56 -05:00
if ( extensions . alwaysOnScripts ) {
buildAlwaysOnScripts ( state ) ;
}
2023-07-08 15:21:58 -05:00
// Use txt2img if canvas is blank or if controlnet is active because "Allow inpaint in txt2img. This is necessary because txt2img has high-res fix" as per https://github.com/Mikubill/sd-webui-controlnet/discussions/1464
2023-07-08 10:49:56 -05:00
if (
isCanvasBlank ( 0 , 0 , bb . w , bb . h , visibleCanvas ) ||
2023-07-08 15:21:58 -05:00
( extensions . controlNetActive && toolbar . _current _tool . name === "Dream" )
2023-07-08 10:49:56 -05:00
) {
2023-06-30 22:01:10 -05:00
//TODO why doesn't smooth rendering toggle persist/localstorage? why am i putting this here? because i'm lazy
//TODO this logic seems crappy, fix it
if ( ! isCanvasBlank ( 0 , 0 , bb . w , bb . h , visibleCanvas ) ) {
// get input image
// Temporary canvas for init image and mask generation
const bbCanvas = document . createElement ( "canvas" ) ;
bbCanvas . width = bb . w ;
bbCanvas . height = bb . h ;
const bbCtx = bbCanvas . getContext ( "2d" ) ;
const maskCanvas = document . createElement ( "canvas" ) ;
maskCanvas . width = request . width ;
maskCanvas . height = request . height ;
const maskCtx = maskCanvas . getContext ( "2d" ) ;
const initCanvas = document . createElement ( "canvas" ) ;
initCanvas . width = request . width ;
initCanvas . height = request . height ;
const initCtx = initCanvas . getContext ( "2d" ) ;
bbCtx . fillStyle = "#000F" ;
// Get init image
initCtx . fillRect ( 0 , 0 , request . width , request . height ) ;
initCtx . drawImage (
visibleCanvas ,
0 ,
0 ,
bb . w ,
bb . h ,
0 ,
0 ,
request . width ,
request . height
) ;
2023-07-08 15:21:58 -05:00
// request.init_images = [initCanvas.toDataURL()];
2023-06-30 22:01:10 -05:00
// Get mask image
bbCtx . fillStyle = "#000F" ;
bbCtx . fillRect ( 0 , 0 , bb . w , bb . h ) ;
if ( state . invertMask ) {
// overmasking by definition is entirely pointless with an inverted mask outpaint
// since it should explicitly avoid brushed masks too, we just won't even bother
bbCtx . globalCompositeOperation = "destination-in" ;
bbCtx . drawImage (
maskPaintCanvas ,
bb . x ,
bb . y ,
bb . w ,
bb . h ,
0 ,
0 ,
bb . w ,
bb . h
) ;
bbCtx . globalCompositeOperation = "destination-in" ;
bbCtx . drawImage ( visibleCanvas , 0 , 0 ) ;
} else {
bbCtx . globalCompositeOperation = "destination-in" ;
bbCtx . drawImage ( visibleCanvas , 0 , 0 ) ;
// here's where to overmask to avoid including the brushed mask
// 99% of my issues were from failing to set source-over for the overmask blotches
if ( state . overMaskPx > 0 ) {
// transparent to white first
bbCtx . globalCompositeOperation = "destination-atop" ;
bbCtx . fillStyle = "#FFFF" ;
bbCtx . fillRect ( 0 , 0 , bb . w , bb . h ) ;
applyOvermask ( bbCanvas , bbCtx , state . overMaskPx ) ;
}
bbCtx . globalCompositeOperation = "destination-out" ; // ???
bbCtx . drawImage (
maskPaintCanvas ,
bb . x ,
bb . y ,
bb . w ,
bb . h ,
0 ,
0 ,
bb . w ,
bb . h
) ;
}
bbCtx . globalCompositeOperation = "destination-atop" ;
bbCtx . fillStyle = "#FFFF" ;
bbCtx . fillRect ( 0 , 0 , bb . w , bb . h ) ;
maskCtx . clearRect ( 0 , 0 , maskCanvas . width , maskCanvas . height ) ;
maskCtx . drawImage (
bbCanvas ,
0 ,
0 ,
bb . w ,
bb . h ,
0 ,
0 ,
request . width ,
request . height
) ;
2023-07-08 15:21:58 -05:00
canvasTransport . initCanvas = initCanvas ;
canvasTransport . maskCanvas = maskCanvas ;
// getImageAndMask(visibleCanvas, bb, request, state); //FFFFF
2023-06-30 22:01:10 -05:00
}
2023-07-02 14:02:13 -05:00
// request.alwayson_scripts = state.alwayson_scripts;
2023-06-30 22:01:10 -05:00
2023-01-05 17:42:50 -06:00
if ( ! global . isOldHRFix && request . enable _hr ) {
2023-01-04 18:00:17 -06:00
/ * *
* try and make the new HRfix method useful for our purposes
* /
2023-01-06 12:49:15 -06:00
// laziness convenience
2023-01-05 17:42:50 -06:00
let lockpx = stableDiffusionData . hr _fix _lock _px ;
if ( lockpx > 0 ) {
2023-01-06 12:49:15 -06:00
// find the most appropriate scale factor for hrfix
2023-01-04 18:00:17 -06:00
var widthFactor =
2023-01-05 17:42:50 -06:00
request . width / lockpx <= 4 ? request . width / lockpx : 4 ;
2023-01-04 18:00:17 -06:00
var heightFactor =
2023-01-05 17:42:50 -06:00
request . height / lockpx <= 4 ? request . height / lockpx : 4 ;
2023-01-04 18:00:17 -06:00
var factor = heightFactor > widthFactor ? heightFactor : widthFactor ;
request . hr _scale = hrFixScaleSlider . value = factor < 1 ? 1 : factor ;
}
2023-01-06 12:49:15 -06:00
// moar laziness convenience
var divW = Math . floor ( request . width / request . hr _scale ) ;
var divH = Math . floor ( request . height / request . hr _scale ) ;
2023-01-04 18:00:17 -06:00
2023-01-05 17:42:50 -06:00
if ( localStorage . getItem ( "openoutpaint/settings.hrfix-liar" ) == "true" ) {
/ * *
* since it now returns an image that ' s been upscaled x the hr _scale parameter ,
* we cheekily lie to SD and tell it that the original dimensions are _divided _
* by the scale factor so it returns something about the same size as we wanted initially
* /
var firstpassWidth = divW ;
var firstpassHeight = divH ; // liar's firstpass output resolution
var desiredWidth = request . width ;
var desiredHeight = request . height ; // truthful desired output resolution
} else {
// use scale normally, dump supersampled image into undersized reticle
var desiredWidth = request . width * request . hr _scale ;
var desiredHeight = request . height * request . hr _scale ; //desired 2nd-pass output resolution
var firstpassWidth = request . width ;
var firstpassHeight = request . height ;
}
// ensure firstpass "resolution" complies with lockpx
if ( lockpx > 0 ) {
2023-01-06 12:49:15 -06:00
//sigh repeated loop
2023-01-05 17:42:50 -06:00
firstpassWidth = divW < lockpx ? divW : lockpx ;
firstpassHeight = divH < lockpx ? divH : lockpx ;
}
2023-01-04 23:27:02 -06:00
if ( stableDiffusionData . hr _square _aspect ) {
2023-01-05 17:42:50 -06:00
larger =
firstpassWidth > firstpassHeight ? firstpassWidth : firstpassHeight ;
firstpassWidth = firstpassHeight = larger ;
2023-01-04 23:27:02 -06:00
}
2023-01-05 17:42:50 -06:00
request . width = firstpassWidth ;
request . height = firstpassHeight ;
request . hr _resize _x = desiredWidth ;
request . hr _resize _y = desiredHeight ;
2023-01-04 18:00:17 -06:00
}
// For compatibility with the old HRFix API
if ( global . isOldHRFix && request . enable _hr ) {
2023-01-04 23:27:02 -06:00
// For compatibility with the old HRFix API
2023-01-04 18:00:17 -06:00
request . firstphase _width = request . width / 2 ;
request . firstphase _height = request . height / 2 ;
}
// Only set this if HRFix is enabled in the first place
2023-01-04 18:37:21 -06:00
request . denoising _strength =
! global . isOldHRFix && request . enable _hr
? stableDiffusionData . hr _denoising _strength
: 1 ;
2023-01-04 18:00:17 -06:00
2023-07-08 15:21:58 -05:00
// add dynamic prompts stuff if it exists because it needs to be explicitly disabled if we don't want it
if ( extensions . dynamicPromptsEnabled ) {
2023-07-08 10:49:56 -05:00
addDynamicPromptsToAlwaysOnScripts ( state ) ;
}
2023-07-08 15:21:58 -05:00
if (
extensions . controlNetActive &&
! isCanvasBlank ( 0 , 0 , bb . w , bb . h , visibleCanvas )
) {
addControlNetToAlwaysOnScripts (
state ,
canvasTransport . initCanvas ,
canvasTransport . maskCanvas
) ;
2023-07-15 12:10:45 -05:00
} else if ( extensions . controlNetActive ) {
2023-07-08 15:21:58 -05:00
console . warn (
"[dream] controlnet inpaint/outpaint enabled for null image, defaulting to normal txt2img dream"
) ;
2023-07-08 10:49:56 -05:00
}
2023-07-08 15:21:58 -05:00
2023-07-08 10:49:56 -05:00
if ( extensions . alwaysOnScripts ) {
// check again just to be sure because i'm an idiot?
2023-07-08 15:21:58 -05:00
// addControlNetToAlwaysOnScripts(state);
// addDynamicPromptsToAlwaysOnScripts(state);
2023-07-08 10:49:56 -05:00
request . alwayson _scripts = state . alwayson _scripts ;
}
2022-12-11 21:50:25 -06:00
// Dream
_generate ( "txt2img" , request , bb ) ;
} else {
// Use img2img if not
2022-12-04 13:22:35 -06:00
2022-12-11 21:50:25 -06:00
// Temporary canvas for init image and mask generation
2022-12-13 21:16:48 -06:00
const bbCanvas = document . createElement ( "canvas" ) ;
bbCanvas . width = bb . w ;
bbCanvas . height = bb . h ;
const bbCtx = bbCanvas . getContext ( "2d" ) ;
2023-01-02 20:31:25 -06:00
const maskCanvas = document . createElement ( "canvas" ) ;
maskCanvas . width = request . width ;
maskCanvas . height = request . height ;
const maskCtx = maskCanvas . getContext ( "2d" ) ;
const initCanvas = document . createElement ( "canvas" ) ;
initCanvas . width = request . width ;
initCanvas . height = request . height ;
const initCtx = initCanvas . getContext ( "2d" ) ;
2022-11-22 16:24:55 -06:00
2022-12-13 21:16:48 -06:00
bbCtx . fillStyle = "#000F" ;
2022-11-22 16:24:55 -06:00
2022-12-11 21:50:25 -06:00
// Get init image
2023-01-02 20:31:25 -06:00
initCtx . fillRect ( 0 , 0 , request . width , request . height ) ;
initCtx . drawImage (
2022-12-11 21:50:25 -06:00
visibleCanvas ,
0 ,
0 ,
bb . w ,
bb . h ,
0 ,
0 ,
request . width ,
request . height
) ;
2023-01-02 20:31:25 -06:00
request . init _images = [ initCanvas . toDataURL ( ) ] ;
2022-11-22 16:24:55 -06:00
2022-12-11 21:50:25 -06:00
// Get mask image
2022-12-13 21:16:48 -06:00
bbCtx . fillStyle = "#000F" ;
bbCtx . fillRect ( 0 , 0 , bb . w , bb . h ) ;
2022-12-11 21:50:25 -06:00
if ( state . invertMask ) {
// overmasking by definition is entirely pointless with an inverted mask outpaint
// since it should explicitly avoid brushed masks too, we just won't even bother
2022-12-13 21:16:48 -06:00
bbCtx . globalCompositeOperation = "destination-in" ;
bbCtx . drawImage (
2022-12-11 21:50:25 -06:00
maskPaintCanvas ,
2022-12-23 18:48:25 -06:00
bb . x ,
bb . y ,
2022-12-11 21:50:25 -06:00
bb . w ,
bb . h ,
0 ,
0 ,
2022-12-03 06:53:12 -06:00
bb . w ,
2022-12-13 21:16:48 -06:00
bb . h
2022-12-03 06:53:12 -06:00
) ;
2022-12-13 21:16:48 -06:00
bbCtx . globalCompositeOperation = "destination-in" ;
bbCtx . drawImage ( visibleCanvas , 0 , 0 ) ;
2022-12-11 21:50:25 -06:00
} else {
2022-12-13 21:16:48 -06:00
bbCtx . globalCompositeOperation = "destination-in" ;
bbCtx . drawImage ( visibleCanvas , 0 , 0 ) ;
2022-12-11 21:50:25 -06:00
// here's where to overmask to avoid including the brushed mask
// 99% of my issues were from failing to set source-over for the overmask blotches
if ( state . overMaskPx > 0 ) {
// transparent to white first
2022-12-13 21:16:48 -06:00
bbCtx . globalCompositeOperation = "destination-atop" ;
bbCtx . fillStyle = "#FFFF" ;
bbCtx . fillRect ( 0 , 0 , bb . w , bb . h ) ;
applyOvermask ( bbCanvas , bbCtx , state . overMaskPx ) ;
2022-11-26 13:34:12 -06:00
}
2022-12-11 21:50:25 -06:00
2022-12-13 21:16:48 -06:00
bbCtx . globalCompositeOperation = "destination-out" ; // ???
bbCtx . drawImage (
2022-12-11 21:50:25 -06:00
maskPaintCanvas ,
2022-12-23 18:48:25 -06:00
bb . x ,
bb . y ,
2022-12-11 21:50:25 -06:00
bb . w ,
bb . h ,
0 ,
0 ,
2022-12-13 21:16:48 -06:00
bb . w ,
bb . h
2022-12-11 21:50:25 -06:00
) ;
2022-11-22 16:24:55 -06:00
}
2022-12-13 21:16:48 -06:00
bbCtx . globalCompositeOperation = "destination-atop" ;
bbCtx . fillStyle = "#FFFF" ;
bbCtx . fillRect ( 0 , 0 , bb . w , bb . h ) ;
2023-01-02 20:31:25 -06:00
maskCtx . clearRect ( 0 , 0 , maskCanvas . width , maskCanvas . height ) ;
maskCtx . drawImage (
2022-12-13 21:16:48 -06:00
bbCanvas ,
0 ,
0 ,
bb . w ,
bb . h ,
0 ,
0 ,
request . width ,
request . height
) ;
2023-07-08 15:21:58 -05:00
// getImageAndMask(visibleCanvas, bb, request, state); // why is not working ffff
2023-01-02 20:31:25 -06:00
request . mask = maskCanvas . toDataURL ( ) ;
2023-01-03 15:52:56 -06:00
request . inpainting _fill = stableDiffusionData . outpainting _fill ;
2023-02-04 15:25:53 -06:00
request . image _cfg _scale = stableDiffusionData . image _cfg _scale ;
2022-12-13 21:16:48 -06:00
2023-07-08 12:54:17 -05:00
// add dynamic prompts stuff if it's enabled
2023-07-08 15:21:58 -05:00
if ( extensions . dynamicPromptsEnabled ) {
2023-07-08 12:54:17 -05:00
addDynamicPromptsToAlwaysOnScripts ( state ) ;
}
if ( extensions . controlNetActive ) {
2023-07-08 15:21:58 -05:00
addControlNetToAlwaysOnScripts ( state , initCanvas , maskCanvas ) ;
2023-07-08 12:54:17 -05:00
}
if ( extensions . alwaysOnScripts ) {
// check again just to be sure because i'm an idiot?
2023-07-08 15:21:58 -05:00
// addControlNetToAlwaysOnScripts(state);
// addDynamicPromptsToAlwaysOnScripts(state);
2023-07-08 12:54:17 -05:00
request . alwayson _scripts = state . alwayson _scripts ;
}
2022-12-11 21:50:25 -06:00
// Dream
2022-12-13 21:16:48 -06:00
_generate ( "img2img" , request , bb , {
2022-12-16 19:59:30 -06:00
keepUnmask : state . keepUnmasked ? bbCanvas : null ,
keepUnmaskBlur : state . keepUnmaskedBlur ,
2022-12-13 21:16:48 -06:00
} ) ;
2022-11-22 16:24:55 -06:00
}
} ;
2023-01-24 20:37:38 -06:00
/ * *
* Erases an area from the canvas
*
* @ param { BoundingBox } bb Bounding box of the area to be erased
* /
2022-12-13 12:13:01 -06:00
const dream _erase _callback = ( bb ) => {
2023-01-24 20:37:38 -06:00
commands . runCommand ( "eraseImage" , "Erase Area" , bb , {
extra : {
log : ` Erased area at x: ${ bb . x } , y: ${ bb . y } , width: ${ bb . w } , height: ${ bb . h } ` ,
} ,
} ) ;
2022-11-22 16:24:55 -06:00
} ;
2022-11-22 19:24:04 -06:00
2022-11-23 17:40:10 -06:00
function applyOvermask ( canvas , ctx , px ) {
// :badpokerface: look it might be all placebo but i like overmask lol
// yes it's crushingly inefficient i knooow :( must fix
// https://stackoverflow.com/a/30204783 was instrumental to this working or completely to blame for this disaster depending on your interpretation
2022-11-27 13:24:28 -06:00
ctx . globalCompositeOperation = "source-over" ;
2022-11-23 17:40:10 -06:00
var ctxImgData = ctx . getImageData ( 0 , 0 , canvas . width , canvas . height ) ;
for ( i = 0 ; i < ctxImgData . data . length ; i += 4 ) {
if ( ctxImgData . data [ i ] == 255 ) {
// white pixel?
// just blotch all over the thing
2022-12-03 06:53:12 -06:00
/ * *
* This should probably have a better randomness profile for the overmasking
*
* Essentially , we want to have much more smaller values for randomness than big ones ,
* because big values overshadow smaller circles and kinda ignores their randomness .
*
* And also , we want the profile to become more extreme the bigger the overmask size ,
* because bigger px values also make bigger circles ocuppy more horizontal space .
* /
let lowRandom =
Math . atan ( Math . random ( ) * 10 - 10 ) / Math . abs ( Math . atan ( - 10 ) ) + 1 ;
lowRandom = Math . pow ( lowRandom , px / 8 ) ;
var rando = Math . floor ( lowRandom * px ) ;
2022-11-27 13:21:17 -06:00
ctx . beginPath ( ) ;
ctx . arc (
( i / 4 ) % canvas . width ,
Math . floor ( i / 4 / canvas . width ) ,
2022-12-03 06:53:12 -06:00
rando , // was 4 * sf + rando, too big, but i think i want it more ... random
2022-11-23 17:40:10 -06:00
0 ,
2 * Math . PI ,
true
) ;
2022-11-27 13:21:17 -06:00
ctx . fillStyle = "#FFFF" ;
ctx . fill ( ) ;
2022-11-23 17:40:10 -06:00
}
}
}
2022-11-22 19:24:04 -06:00
/ * *
* Image to Image
* /
2022-12-13 17:22:16 -06:00
const dream _img2img _callback = ( bb , resolution , state ) => {
2022-12-11 21:50:25 -06:00
// Get visible pixels
const visibleCanvas = uil . getVisible ( bb ) ;
2023-07-08 16:17:25 -05:00
if ( extensions . alwaysOnScripts ) {
buildAlwaysOnScripts ( state ) ;
}
2022-12-11 21:50:25 -06:00
// Do nothing if no image exists
if ( isCanvasBlank ( 0 , 0 , bb . w , bb . h , visibleCanvas ) ) return ;
// Build request to the API
const request = { } ;
Object . assign ( request , stableDiffusionData ) ;
2022-12-13 17:22:16 -06:00
request . width = resolution . w ;
request . height = resolution . h ;
2022-12-13 12:13:01 -06:00
2022-12-11 21:50:25 -06:00
request . denoising _strength = state . denoisingStrength ;
2023-01-03 08:16:09 -06:00
request . inpainting _fill = state . inpainting _fill ? ? 1 ; //let's see how this works //1; // For img2img use original
2023-02-04 15:25:53 -06:00
request . image _cfg _scale = state . image _cfg _scale ? ? 0.5 ; // what am i even doing
2022-12-11 21:50:25 -06:00
// Load prompt (maybe we should add some events so we don't have to do this)
request . prompt = document . getElementById ( "prompt" ) . value ;
request . negative _prompt = document . getElementById ( "negPrompt" ) . value ;
// Use img2img
// Temporary canvas for init image and mask generation
2022-12-13 21:16:48 -06:00
const bbCanvas = document . createElement ( "canvas" ) ;
bbCanvas . width = bb . w ;
bbCanvas . height = bb . h ;
const bbCtx = bbCanvas . getContext ( "2d" ) ;
2022-12-11 21:50:25 -06:00
2022-12-13 21:16:48 -06:00
bbCtx . fillStyle = "#000F" ;
2022-12-11 21:50:25 -06:00
// Get init image
2022-12-13 21:16:48 -06:00
bbCtx . fillRect ( 0 , 0 , bb . w , bb . h ) ;
bbCtx . drawImage ( visibleCanvas , 0 , 0 ) ;
request . init _images = [ bbCanvas . toDataURL ( ) ] ;
2022-12-11 21:50:25 -06:00
// Get mask image
2022-12-13 21:16:48 -06:00
bbCtx . fillStyle = state . invertMask ? "#FFFF" : "#000F" ;
bbCtx . fillRect ( 0 , 0 , bb . w , bb . h ) ;
bbCtx . globalCompositeOperation = "destination-out" ;
2022-12-23 18:48:55 -06:00
bbCtx . drawImage ( maskPaintCanvas , bb . x , bb . y , bb . w , bb . h , 0 , 0 , bb . w , bb . h ) ;
2022-11-22 19:24:04 -06:00
2022-12-13 21:16:48 -06:00
bbCtx . globalCompositeOperation = "destination-atop" ;
bbCtx . fillStyle = state . invertMask ? "#000F" : "#FFFF" ;
2022-12-15 22:26:46 -06:00
bbCtx . fillRect ( 0 , 0 , bb . w , bb . h ) ;
2022-11-22 19:24:04 -06:00
2022-12-11 21:50:25 -06:00
// Border Mask
if ( state . keepBorderSize > 0 ) {
2022-12-15 22:26:46 -06:00
const keepBorderCanvas = document . createElement ( "canvas" ) ;
keepBorderCanvas . width = request . width ;
keepBorderCanvas . height = request . height ;
const keepBorderCtx = keepBorderCanvas . getContext ( "2d" ) ;
keepBorderCtx . fillStyle = "#000F" ;
2022-12-11 21:50:25 -06:00
if ( state . gradient ) {
2022-12-15 22:26:46 -06:00
const lg = keepBorderCtx . createLinearGradient (
0 ,
0 ,
state . keepBorderSize ,
0
) ;
2022-12-11 21:50:25 -06:00
lg . addColorStop ( 0 , "#000F" ) ;
lg . addColorStop ( 1 , "#0000" ) ;
2022-12-15 22:26:46 -06:00
keepBorderCtx . fillStyle = lg ;
2022-12-11 21:50:25 -06:00
}
2022-12-15 22:26:46 -06:00
keepBorderCtx . fillRect ( 0 , 0 , state . keepBorderSize , request . height ) ;
2022-12-11 21:50:25 -06:00
if ( state . gradient ) {
2022-12-15 22:26:46 -06:00
const tg = keepBorderCtx . createLinearGradient (
0 ,
0 ,
0 ,
state . keepBorderSize
) ;
2022-12-11 21:50:25 -06:00
tg . addColorStop ( 0 , "#000F" ) ;
tg . addColorStop ( 1 , "#0000" ) ;
2022-12-15 22:26:46 -06:00
keepBorderCtx . fillStyle = tg ;
2022-12-11 21:50:25 -06:00
}
2022-12-15 22:26:46 -06:00
keepBorderCtx . fillRect ( 0 , 0 , request . width , state . keepBorderSize ) ;
2022-12-11 21:50:25 -06:00
if ( state . gradient ) {
2022-12-15 22:26:46 -06:00
const rg = keepBorderCtx . createLinearGradient (
2022-12-11 21:50:25 -06:00
request . width ,
0 ,
request . width - state . keepBorderSize ,
0
) ;
rg . addColorStop ( 0 , "#000F" ) ;
rg . addColorStop ( 1 , "#0000" ) ;
2022-12-15 22:26:46 -06:00
keepBorderCtx . fillStyle = rg ;
2022-12-11 21:50:25 -06:00
}
2022-12-15 22:26:46 -06:00
keepBorderCtx . fillRect (
2022-12-11 21:50:25 -06:00
request . width - state . keepBorderSize ,
2022-12-03 06:53:12 -06:00
0 ,
2022-12-11 21:50:25 -06:00
state . keepBorderSize ,
2022-12-03 06:53:12 -06:00
request . height
) ;
2022-12-11 21:50:25 -06:00
if ( state . gradient ) {
2022-12-15 22:26:46 -06:00
const bg = keepBorderCtx . createLinearGradient (
2022-11-22 19:24:04 -06:00
0 ,
2022-12-11 21:50:25 -06:00
request . height ,
2022-11-22 19:24:04 -06:00
0 ,
2022-12-11 21:50:25 -06:00
request . height - state . keepBorderSize
2022-11-22 19:24:04 -06:00
) ;
2022-12-11 21:50:25 -06:00
bg . addColorStop ( 0 , "#000F" ) ;
bg . addColorStop ( 1 , "#0000" ) ;
2022-12-15 22:26:46 -06:00
keepBorderCtx . fillStyle = bg ;
2022-11-22 19:24:04 -06:00
}
2022-12-15 22:26:46 -06:00
keepBorderCtx . fillRect (
2022-12-11 21:50:25 -06:00
0 ,
request . height - state . keepBorderSize ,
request . width ,
state . keepBorderSize
) ;
2022-12-15 22:26:46 -06:00
bbCtx . globalCompositeOperation = "source-over" ;
bbCtx . drawImage (
keepBorderCanvas ,
0 ,
0 ,
request . width ,
request . height ,
0 ,
0 ,
bb . w ,
bb . h
) ;
2022-12-11 21:50:25 -06:00
}
2022-11-22 19:24:04 -06:00
2022-12-15 22:26:46 -06:00
const reqCanvas = document . createElement ( "canvas" ) ;
reqCanvas . width = request . width ;
reqCanvas . height = request . height ;
const reqCtx = reqCanvas . getContext ( "2d" ) ;
reqCtx . drawImage (
bbCanvas ,
0 ,
0 ,
bb . w ,
bb . h ,
0 ,
0 ,
request . width ,
request . height
) ;
request . mask = reqCanvas . toDataURL ( ) ;
2022-12-11 21:50:25 -06:00
request . inpaint _full _res = state . fullResolution ;
2022-11-22 19:24:04 -06:00
2023-07-08 16:17:25 -05:00
// add dynamic prompts stuff if it's enabled
if ( extensions . dynamicPromptsEnabled ) {
addDynamicPromptsToAlwaysOnScripts ( state ) ;
}
if ( extensions . controlNetActive ) {
2023-07-15 12:10:45 -05:00
if ( extensions . controlNetReferenceActive ) {
addControlNetToAlwaysOnScripts (
state ,
request . init _images [ 0 ] ,
request . mask
) ;
} else {
addControlNetToAlwaysOnScripts ( state , null , null ) ; // //WTF???
}
2023-07-08 16:17:25 -05:00
}
if ( extensions . alwaysOnScripts ) {
// check again just to be sure because i'm an idiot?
// addControlNetToAlwaysOnScripts(state);
// addDynamicPromptsToAlwaysOnScripts(state);
request . alwayson _scripts = state . alwayson _scripts ;
}
2022-12-11 21:50:25 -06:00
// Dream
2022-12-13 21:16:48 -06:00
_generate ( "img2img" , request , bb , {
2022-12-16 19:59:30 -06:00
keepUnmask : state . keepUnmasked ? bbCanvas : null ,
keepUnmaskBlur : state . keepUnmaskedBlur ,
2022-12-13 21:16:48 -06:00
} ) ;
2022-11-22 19:24:04 -06:00
} ;
2022-11-24 09:30:13 -06:00
2022-12-01 15:05:14 -06:00
/ * *
* Dream and img2img tools
* /
2022-12-03 06:53:12 -06:00
/ * *
* Generic wheel handler
* /
2022-12-25 20:29:03 -06:00
let _dream _wheel _accum = 0 ;
2022-12-03 06:53:12 -06:00
const _dream _onwheel = ( evn , state ) => {
2022-12-26 22:04:56 -06:00
if ( evn . mode !== WheelEvent . DOM _DELTA _PIXEL ) {
// We don't really handle non-pixel scrolling
return ;
}
2022-12-25 20:29:03 -06:00
2023-01-12 20:00:34 -06:00
let delta = evn . delta ;
if ( evn . evn . shiftKey ) delta *= 0.01 ;
2022-12-26 22:04:56 -06:00
// A simple but (I hope) effective fix for mouse wheel behavior
2023-01-12 20:00:34 -06:00
_dream _wheel _accum += delta ;
2022-12-07 15:51:33 -06:00
2023-01-12 20:00:34 -06:00
if (
! evn . evn . shiftKey &&
Math . abs ( _dream _wheel _accum ) > config . wheelTickSize
) {
2022-12-26 22:04:56 -06:00
// Snap to next or previous position
const v =
state . cursorSize -
128 * ( _dream _wheel _accum / Math . abs ( _dream _wheel _accum ) ) ;
2022-12-07 15:51:33 -06:00
2022-12-26 22:04:56 -06:00
state . cursorSize = state . setCursorSize ( v + snap ( v , 0 , 128 ) ) ;
state . mousemovecb ( evn ) ;
2022-12-25 20:29:03 -06:00
2023-01-12 20:00:34 -06:00
_dream _wheel _accum = 0 ; // Zero accumulation
} else if ( evn . evn . shiftKey && Math . abs ( _dream _wheel _accum ) >= 1 ) {
const v = state . cursorSize - _dream _wheel _accum ;
state . cursorSize = state . setCursorSize ( v ) ;
state . mousemovecb ( evn ) ;
2022-12-26 22:04:56 -06:00
_dream _wheel _accum = 0 ; // Zero accumulation
2022-12-03 06:53:12 -06:00
}
} ;
2022-11-24 09:30:13 -06:00
/ * *
* Registers Tools
* /
const dreamTool = ( ) =>
toolbar . registerTool (
2022-12-18 17:50:40 -06:00
"./res/icons/image-plus.svg" ,
2022-11-24 09:30:13 -06:00
"Dream" ,
( state , opt ) => {
// Draw new cursor immediately
2022-12-04 13:22:35 -06:00
uiCtx . clearRect ( 0 , 0 , uiCanvas . width , uiCanvas . height ) ;
2022-12-06 17:00:50 -06:00
state . lastMouseMove = {
2022-11-29 14:55:25 -06:00
... mouse . coords . world . pos ,
2022-12-06 17:00:50 -06:00
} ;
state . redraw ( ) ;
2022-11-24 09:30:13 -06:00
// Start Listeners
2022-11-29 14:55:25 -06:00
mouse . listen . world . onmousemove . on ( state . mousemovecb ) ;
2022-12-03 06:53:12 -06:00
mouse . listen . world . onwheel . on ( state . wheelcb ) ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
mouse . listen . world . btn . left . onclick . on ( state . dreamcb ) ;
mouse . listen . world . btn . right . onclick . on ( state . erasecb ) ;
// Select Region listeners
2022-12-13 12:13:01 -06:00
mouse . listen . world . btn . left . ondragstart . on ( state . dragstartcb ) ;
mouse . listen . world . btn . left . ondrag . on ( state . dragcb ) ;
mouse . listen . world . btn . left . ondragend . on ( state . dragendcb ) ;
2022-12-15 06:43:26 -06:00
mouse . listen . world . onmousemove . on ( state . smousemovecb , 2 , true ) ;
mouse . listen . world . onwheel . on ( state . swheelcb , 2 , true ) ;
mouse . listen . world . btn . left . onclick . on ( state . sdreamcb , 2 , true ) ;
mouse . listen . world . btn . right . onclick . on ( state . serasecb , 2 , true ) ;
mouse . listen . world . btn . middle . onclick . on ( state . smiddlecb , 2 , true ) ;
2022-11-26 19:35:16 -06:00
2022-12-13 12:13:01 -06:00
// Clear Selection
2022-12-15 06:43:26 -06:00
state . selection . deselect ( ) ;
2022-12-13 12:13:01 -06:00
2022-11-26 19:35:16 -06:00
// Display Mask
setMask ( state . invertMask ? "hold" : "clear" ) ;
2022-12-12 16:25:49 -06:00
// update cursor size if matching is enabled
2022-12-12 18:25:02 -06:00
if ( stableDiffusionData . sync _cursor _size ) {
2022-12-12 16:25:49 -06:00
state . setCursorSize ( stableDiffusionData . width ) ;
}
2022-11-24 09:30:13 -06:00
} ,
( state , opt ) => {
// Clear Listeners
2022-11-29 14:55:25 -06:00
mouse . listen . world . onmousemove . clear ( state . mousemovecb ) ;
2022-12-03 06:53:12 -06:00
mouse . listen . world . onwheel . clear ( state . wheelcb ) ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
mouse . listen . world . btn . left . onclick . clear ( state . dreamcb ) ;
mouse . listen . world . btn . right . onclick . clear ( state . erasecb ) ;
// Clear Select Region listeners
2022-12-13 12:13:01 -06:00
mouse . listen . world . btn . left . ondragstart . clear ( state . dragstartcb ) ;
mouse . listen . world . btn . left . ondrag . clear ( state . dragcb ) ;
mouse . listen . world . btn . left . ondragend . clear ( state . dragendcb ) ;
2022-12-15 06:43:26 -06:00
mouse . listen . world . onmousemove . clear ( state . smousemovecb ) ;
mouse . listen . world . onwheel . clear ( state . swheelcb ) ;
mouse . listen . world . btn . left . onclick . clear ( state . sdreamcb ) ;
mouse . listen . world . btn . right . onclick . clear ( state . serasecb ) ;
mouse . listen . world . btn . middle . onclick . clear ( state . smiddlecb ) ;
2022-11-26 19:35:16 -06:00
2022-12-13 12:13:01 -06:00
// Clear Selection
2022-12-15 06:43:26 -06:00
state . selection . deselect ( ) ;
2022-12-13 12:13:01 -06:00
2022-11-26 19:35:16 -06:00
// Hide Mask
setMask ( "none" ) ;
2022-12-04 13:22:35 -06:00
uiCtx . clearRect ( 0 , 0 , uiCanvas . width , uiCanvas . height ) ;
2022-11-24 09:30:13 -06:00
} ,
{
init : ( state ) => {
2022-12-03 06:53:12 -06:00
state . config = {
cursorSizeScrollSpeed : 1 ,
} ;
state . cursorSize = 512 ;
2022-11-24 09:30:13 -06:00
state . snapToGrid = true ;
2022-11-26 13:34:12 -06:00
state . invertMask = false ;
2022-12-16 19:59:30 -06:00
state . keepUnmasked = true ;
state . keepUnmaskedBlur = 8 ;
2022-12-26 19:24:26 -06:00
state . overMaskPx = 20 ;
2022-12-19 18:51:29 -06:00
state . preserveMasks = false ;
2023-01-01 19:30:48 -06:00
state . eagerGenerateCount = 0 ;
2023-02-19 09:49:48 -06:00
state . carve _blur = 0 ;
state . carve _threshold = 10 ;
2022-12-01 15:05:14 -06:00
2023-02-18 21:45:04 -06:00
state . carve _blur = 0 ;
state . carve _threshold = 10 ;
2022-12-13 12:13:01 -06:00
state . erasePrevCursor = ( ) =>
uiCtx . clearRect ( 0 , 0 , uiCanvas . width , uiCanvas . height ) ;
2022-12-01 15:05:14 -06:00
state . erasePrevReticle = ( ) =>
2022-12-04 13:22:35 -06:00
uiCtx . clearRect ( 0 , 0 , uiCanvas . width , uiCanvas . height ) ;
2022-12-01 15:05:14 -06:00
2022-12-06 17:00:50 -06:00
state . lastMouseMove = {
2022-12-06 15:28:34 -06:00
... mouse . coords . world . pos ,
} ;
2023-07-02 14:02:13 -05:00
// state.alwayson_scripts = {};
// state.alwayson_scripts.controlnet = {};
// state.alwayson_scripts.controlnet.args = [
// {
// module: "none",
// model: "None", //docs have this casing, is that necessary?
// },
// ];
2023-06-30 22:01:10 -05:00
2022-12-15 06:43:26 -06:00
/ * *
* Selection handlers
* /
const selection = _tool . _draggable _selection ( state ) ;
state . dragstartcb = ( evn ) => selection . dragstartcb ( evn ) ;
state . dragcb = ( evn ) => selection . dragcb ( evn ) ;
state . dragendcb = ( evn ) => selection . dragendcb ( evn ) ;
state . smousemovecb = ( evn , estate ) => {
selection . smousemovecb ( evn ) ;
if ( selection . inside ) {
imageCollection . inputElement . style . cursor = "pointer" ;
estate . dream _processed = true ;
} else {
imageCollection . inputElement . style . cursor = "auto" ;
}
2022-12-13 12:13:01 -06:00
} ;
2022-12-15 06:43:26 -06:00
state . swheelcb = ( evn , estate ) => {
if ( selection . inside ) {
state . wheelcb ( evn , { } ) ;
estate . dream _processed = true ;
}
2022-12-13 12:13:01 -06:00
} ;
2022-12-15 06:43:26 -06:00
state . sdreamcb = ( evn , estate ) => {
if ( selection . exists && ! selection . inside ) {
selection . deselect ( ) ;
state . redraw ( ) ;
estate . selection _processed = true ;
}
if ( selection . inside ) {
state . dreamcb ( evn , { } ) ;
estate . dream _processed = true ;
}
} ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
state . serasecb = ( evn , estate ) => {
if ( selection . inside ) {
selection . deselect ( ) ;
2022-12-13 12:13:01 -06:00
state . redraw ( ) ;
2022-12-15 06:43:26 -06:00
estate . dream _processed = true ;
2022-12-13 12:13:01 -06:00
}
} ;
2022-12-15 06:43:26 -06:00
state . smiddlecb = ( evn , estate ) => {
if ( selection . inside ) {
estate . dream _processed = true ;
}
} ;
state . selection = selection ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
/ * *
* Dream Handlers
* /
2022-12-01 15:05:14 -06:00
state . mousemovecb = ( evn ) => {
2022-12-06 17:00:50 -06:00
state . lastMouseMove = evn ;
2022-12-13 12:13:01 -06:00
state . erasePrevCursor ( ) ;
2022-12-01 15:05:14 -06:00
state . erasePrevReticle ( ) ;
2022-12-13 12:13:01 -06:00
let x = evn . x ;
let y = evn . y ;
if ( state . snapToGrid ) {
x += snap ( evn . x , 0 , 64 ) ;
y += snap ( evn . y , 0 , 64 ) ;
}
2022-12-21 15:29:11 -06:00
state . erasePrevCursor = _tool . _cursor _draw ( x , y ) ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
if ( state . selection . exists ) {
const bb = state . selection . bb ;
2022-12-13 12:13:01 -06:00
2022-12-13 14:04:16 -06:00
const style =
state . cursorSize > stableDiffusionData . width
? "#FBB5"
: state . cursorSize < stableDiffusionData . width
? "#BFB5"
: "#FFF5" ;
2022-12-15 06:43:26 -06:00
state . erasePrevReticle = _tool . _reticle _draw (
2022-12-13 14:04:16 -06:00
bb ,
"Dream" ,
{
w : Math . round (
bb . w * ( stableDiffusionData . width / state . cursorSize )
) ,
h : Math . round (
bb . h * ( stableDiffusionData . height / state . cursorSize )
) ,
} ,
{
2022-12-21 09:07:29 -06:00
toolTextStyle :
global . connection === "online" ? "#FFF5" : "#F555" ,
2022-12-15 06:43:26 -06:00
reticleStyle : state . selection . inside ? "#F55" : "#FFF" ,
2022-12-13 14:04:16 -06:00
sizeTextStyle : style ,
}
) ;
2022-12-13 12:13:01 -06:00
return ;
}
2022-12-06 15:28:34 -06:00
const style =
state . cursorSize > stableDiffusionData . width
? "#FBB5"
: state . cursorSize < stableDiffusionData . width
? "#BFB5"
: "#FFF5" ;
2022-12-15 06:43:26 -06:00
state . erasePrevReticle = _tool . _reticle _draw (
2022-12-13 12:13:01 -06:00
getBoundingBox (
evn . x ,
evn . y ,
state . cursorSize ,
state . cursorSize ,
state . snapToGrid && basePixelCount
) ,
"Dream" ,
{
w : stableDiffusionData . width ,
h : stableDiffusionData . height ,
} ,
{
2022-12-21 09:07:29 -06:00
toolTextStyle : global . connection === "online" ? "#FFF5" : "#F555" ,
2022-12-13 12:13:01 -06:00
sizeTextStyle : style ,
}
) ;
2022-12-06 15:28:34 -06:00
} ;
state . redraw = ( ) => {
2022-12-06 17:00:50 -06:00
state . mousemovecb ( state . lastMouseMove ) ;
2022-12-03 06:53:12 -06:00
} ;
2022-12-06 15:28:34 -06:00
2022-12-14 12:12:44 -06:00
state . wheelcb = ( evn , estate ) => {
if ( estate . dream _processed ) return ;
2022-12-03 06:53:12 -06:00
_dream _onwheel ( evn , state ) ;
2022-11-29 14:55:25 -06:00
} ;
2022-12-14 12:12:44 -06:00
state . dreamcb = ( evn , estate ) => {
2022-12-15 06:43:26 -06:00
if ( estate . dream _processed || estate . selection _processed ) return ;
2022-12-13 12:13:01 -06:00
const bb =
2022-12-15 06:43:26 -06:00
state . selection . bb ||
2022-12-13 12:13:01 -06:00
getBoundingBox (
evn . x ,
evn . y ,
state . cursorSize ,
state . cursorSize ,
state . snapToGrid && basePixelCount
) ;
2022-12-15 06:43:26 -06:00
const resolution = state . selection . bb || {
2022-12-13 17:22:16 -06:00
w : stableDiffusionData . width ,
h : stableDiffusionData . height ,
} ;
2022-12-21 09:07:29 -06:00
if ( global . connection === "online" ) {
dream _generate _callback ( bb , resolution , state ) ;
} else {
const stop = march ( bb , {
title : "offline" ,
titleStyle : "#F555" ,
style : "#F55" ,
} ) ;
setTimeout ( stop , 2000 ) ;
}
2022-12-15 06:43:26 -06:00
state . selection . deselect ( ) ;
2022-12-21 09:07:29 -06:00
state . redraw ( ) ;
2022-12-13 12:13:01 -06:00
} ;
2022-12-14 12:12:44 -06:00
state . erasecb = ( evn , estate ) => {
2022-12-15 06:43:26 -06:00
if ( state . selection . exists ) {
state . selection . deselect ( ) ;
2022-12-13 12:13:01 -06:00
state . redraw ( ) ;
return ;
}
2022-12-14 12:12:44 -06:00
if ( estate . dream _processed ) return ;
2022-12-13 12:13:01 -06:00
const bb = getBoundingBox (
evn . x ,
evn . y ,
state . cursorSize ,
state . cursorSize ,
state . snapToGrid && basePixelCount
) ;
dream _erase _callback ( bb , state ) ;
2022-11-24 09:30:13 -06:00
} ;
} ,
2023-01-03 09:50:38 -06:00
populateContextMenu : ( menu , state , tool ) => {
2022-11-24 09:30:13 -06:00
if ( ! state . ctxmenu ) {
state . ctxmenu = { } ;
2022-11-26 13:34:12 -06:00
2022-12-03 06:53:12 -06:00
// Cursor Size Slider
const cursorSizeSlider = _toolbar _input . slider (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-cursorsize" ,
2022-12-03 06:53:12 -06:00
"cursorSize" ,
"Cursor Size" ,
{
2022-12-12 17:09:27 -06:00
min : 128 ,
2022-12-03 06:53:12 -06:00
max : 2048 ,
step : 128 ,
textStep : 2 ,
2022-12-12 21:05:04 -06:00
cb : ( ) => {
2022-12-28 23:39:32 -06:00
if (
global . syncCursorSize &&
resSlider . value !== state . cursorSize
) {
2022-12-12 21:05:04 -06:00
resSlider . value = state . cursorSize ;
}
2022-12-28 23:39:32 -06:00
2023-01-03 09:50:38 -06:00
if ( tool . enabled ) state . redraw ( ) ;
2022-12-12 21:05:04 -06:00
} ,
2022-12-03 06:53:12 -06:00
}
) ;
2022-12-28 23:39:32 -06:00
resSlider . onchange . on ( ( { value } ) => {
if ( global . syncCursorSize && value !== state . cursorSize ) {
cursorSizeSlider . rawSlider . value = value ;
}
} ) ;
2022-12-03 06:53:12 -06:00
state . setCursorSize = cursorSizeSlider . setValue ;
state . ctxmenu . cursorSizeSlider = cursorSizeSlider . slider ;
2022-11-26 13:34:12 -06:00
// Snap to Grid Checkbox
2022-11-24 09:30:13 -06:00
state . ctxmenu . snapToGridLabel = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-snaptogrid" ,
2022-11-24 09:30:13 -06:00
"snapToGrid" ,
2023-01-02 18:12:18 -06:00
"Snap To Grid" ,
"icon-grid"
) . checkbox ;
2022-11-26 13:34:12 -06:00
// Invert Mask Checkbox
state . ctxmenu . invertMaskLabel = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-invertmask" ,
2022-11-26 13:34:12 -06:00
"invertMask" ,
2022-11-26 19:35:16 -06:00
"Invert Mask" ,
2023-01-02 18:12:18 -06:00
[ "icon-venetian-mask" , "invert-mask-checkbox" ] ,
2022-11-26 19:35:16 -06:00
( ) => {
setMask ( state . invertMask ? "hold" : "clear" ) ;
}
2023-01-02 18:12:18 -06:00
) . checkbox ;
2022-11-26 13:34:12 -06:00
2023-02-19 09:49:48 -06:00
// Keep Unmasked Content Checkbox
2022-12-16 19:59:30 -06:00
state . ctxmenu . keepUnmaskedLabel = _toolbar _input . checkbox (
2022-12-13 21:16:48 -06:00
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-keepunmasked" ,
2022-12-16 19:59:30 -06:00
"keepUnmasked" ,
"Keep Unmasked" ,
2023-01-02 18:12:18 -06:00
"icon-pin" ,
2022-12-15 08:51:18 -06:00
( ) => {
2022-12-16 19:59:30 -06:00
if ( state . keepUnmasked ) {
state . ctxmenu . keepUnmaskedBlurSlider . classList . remove (
2022-12-15 08:51:18 -06:00
"invisible"
) ;
2023-01-02 13:50:28 -06:00
state . ctxmenu . keepUnmaskedBlurSliderLinebreak . classList . add (
"invisible"
) ;
2022-12-15 08:51:18 -06:00
} else {
2022-12-16 19:59:30 -06:00
state . ctxmenu . keepUnmaskedBlurSlider . classList . add ( "invisible" ) ;
2023-01-02 13:50:28 -06:00
state . ctxmenu . keepUnmaskedBlurSliderLinebreak . classList . remove (
"invisible"
) ;
2022-12-15 08:51:18 -06:00
}
}
2023-01-02 18:12:18 -06:00
) . checkbox ;
2022-12-13 21:16:48 -06:00
2023-02-19 09:49:48 -06:00
// Keep Unmasked Content Blur Slider
2022-12-16 19:59:30 -06:00
state . ctxmenu . keepUnmaskedBlurSlider = _toolbar _input . slider (
2022-12-15 08:51:18 -06:00
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-keepunmaskedblur" ,
2022-12-16 19:59:30 -06:00
"keepUnmaskedBlur" ,
"Keep Unmasked Blur" ,
2022-12-15 08:51:18 -06:00
{
min : 0 ,
max : 64 ,
step : 4 ,
textStep : 1 ,
}
) . slider ;
2023-01-02 13:50:28 -06:00
state . ctxmenu . keepUnmaskedBlurSliderLinebreak =
document . createElement ( "br" ) ;
state . ctxmenu . keepUnmaskedBlurSliderLinebreak . classList . add (
"invisible"
) ;
2023-01-03 15:52:56 -06:00
// outpaint fill type select list
state . ctxmenu . outpaintTypeSelect = _toolbar _input . selectlist (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-outpainttype" ,
2023-01-03 15:52:56 -06:00
"outpainting_fill" ,
"Outpaint Type" ,
{
0 : "fill" ,
2023-07-15 09:00:02 -05:00
1 : "original (SDXL)" ,
2 : "latent noise (SD1.x/2.x)" ,
2023-01-03 15:52:56 -06:00
3 : "latent nothing" ,
} ,
2023-07-15 09:00:02 -05:00
2 , // AVOID ORIGINAL FOR OUTPAINT OR ELSE but we still give you the option because we love you and because it seems to work better for SDXL
2023-01-03 15:52:56 -06:00
( ) => {
stableDiffusionData . outpainting _fill = state . outpainting _fill ;
}
) . label ;
2022-12-19 18:51:29 -06:00
// Preserve Brushed Masks Checkbox
state . ctxmenu . preserveMasksLabel = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-preservemasks" ,
2022-12-19 18:51:29 -06:00
"preserveMasks" ,
2023-01-02 18:12:18 -06:00
"Preserve Brushed Masks" ,
"icon-paintbrush"
) . checkbox ;
2022-12-19 18:51:29 -06:00
2023-01-29 16:39:00 -06:00
// Remove Identical/Background Pixels Checkbox
state . ctxmenu . removeBackgroundLabel = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-removebg" ,
2023-01-29 16:39:00 -06:00
"removeBackground" ,
"Remove Identical/BG Pixels" ,
2023-02-18 12:00:10 -06:00
"icon-slice" ,
( ) => {
if ( state . removeBackground ) {
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveBlurSlider . classList . remove ( "invisible" ) ;
state . ctxmenu . carveThresholdSlider . classList . remove (
"invisible"
) ;
2023-02-18 12:00:10 -06:00
} else {
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveBlurSlider . classList . add ( "invisible" ) ;
state . ctxmenu . carveThresholdSlider . classList . add ( "invisible" ) ;
2023-02-18 12:00:10 -06:00
}
}
2023-01-29 16:39:00 -06:00
) . checkbox ;
2023-06-30 22:01:10 -05:00
// controlnet checkbox
state . ctxmenu . controlNetLabel = _toolbar _input . checkbox (
state ,
"openoutpaint/dream-controlnet" ,
"controlNet" ,
"Toggle ControlNet In/Outpainting" ,
2023-07-08 10:49:56 -05:00
"icon-joystick"
2023-06-30 22:01:10 -05:00
) . checkbox ;
2022-11-26 13:34:12 -06:00
// Overmasking Slider
2022-11-24 09:30:13 -06:00
state . ctxmenu . overMaskPxLabel = _toolbar _input . slider (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-overmaskpx" ,
2022-11-24 09:30:13 -06:00
"overMaskPx" ,
"Overmask px" ,
2022-12-03 04:50:23 -06:00
{
min : 0 ,
2022-12-03 06:53:12 -06:00
max : 64 ,
2022-12-07 10:31:15 -06:00
step : 4 ,
2022-12-03 06:53:12 -06:00
textStep : 1 ,
2022-12-03 04:50:23 -06:00
}
2022-11-24 09:30:13 -06:00
) . slider ;
2023-01-01 19:30:48 -06:00
// Eager generation Slider
state . ctxmenu . eagerGenerateCountLabel = _toolbar _input . slider (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-eagergeneratecount" ,
2023-01-01 19:30:48 -06:00
"eagerGenerateCount" ,
"Generate-ahead count" ,
{
min : 0 ,
max : 100 ,
step : 2 ,
textStep : 1 ,
}
) . slider ;
2023-02-18 12:00:10 -06:00
// bg carve blur
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveBlurSlider = _toolbar _input . slider (
2023-02-18 12:00:10 -06:00
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-carveblur" ,
2023-02-18 12:00:10 -06:00
"carve_blur" ,
"BG Remove Blur" ,
{
min : 0 ,
max : 30 ,
step : 2 ,
textStep : 1 ,
}
) . slider ;
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveBlurSlider . classList . add ( "invisible" ) ;
2023-02-18 12:00:10 -06:00
// bg carve threshold
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveThresholdSlider = _toolbar _input . slider (
2023-02-18 12:00:10 -06:00
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/dream-carvethreshold" ,
2023-02-18 12:00:10 -06:00
"carve_threshold" ,
"BG Remove Threshold" ,
{
min : 0 ,
max : 255 ,
step : 5 ,
textStep : 1 ,
}
) . slider ;
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveThresholdSlider . classList . add ( "invisible" ) ;
2022-11-24 09:30:13 -06:00
}
2022-12-03 06:53:12 -06:00
menu . appendChild ( state . ctxmenu . cursorSizeSlider ) ;
2023-01-02 18:12:18 -06:00
const array = document . createElement ( "div" ) ;
array . classList . add ( "checkbox-array" ) ;
array . appendChild ( state . ctxmenu . snapToGridLabel ) ;
//menu.appendChild(document.createElement("br"));
array . appendChild ( state . ctxmenu . invertMaskLabel ) ;
array . appendChild ( state . ctxmenu . preserveMasksLabel ) ;
//menu.appendChild(document.createElement("br"));
array . appendChild ( state . ctxmenu . keepUnmaskedLabel ) ;
2023-02-19 09:49:48 -06:00
array . appendChild ( state . ctxmenu . removeBackgroundLabel ) ;
2023-07-08 10:49:56 -05:00
//TODO: if (global.controlnetAPI) { //but figure out how to update the UI after doing so
// never mind i think i'm using an extension menu instead
// array.appendChild(state.ctxmenu.controlNetLabel);
//}
2023-01-02 18:12:18 -06:00
menu . appendChild ( array ) ;
2022-12-16 19:59:30 -06:00
menu . appendChild ( state . ctxmenu . keepUnmaskedBlurSlider ) ;
2023-02-19 09:49:48 -06:00
menu . appendChild ( state . ctxmenu . carveBlurSlider ) ;
menu . appendChild ( state . ctxmenu . carveThresholdSlider ) ;
2023-01-02 22:15:47 -06:00
// menu.appendChild(state.ctxmenu.keepUnmaskedBlurSliderLinebreak);
// menu.appendChild(state.ctxmenu.preserveMasksLabel);
// menu.appendChild(document.createElement("br"));
2023-01-03 15:52:56 -06:00
menu . appendChild ( state . ctxmenu . outpaintTypeSelect ) ;
2022-11-24 09:30:13 -06:00
menu . appendChild ( state . ctxmenu . overMaskPxLabel ) ;
2023-01-01 19:30:48 -06:00
menu . appendChild ( state . ctxmenu . eagerGenerateCountLabel ) ;
2022-11-24 09:30:13 -06:00
} ,
shortcut : "D" ,
}
) ;
const img2imgTool = ( ) =>
toolbar . registerTool (
2022-12-18 17:50:40 -06:00
"./res/icons/image.svg" ,
2022-11-24 09:30:13 -06:00
"Img2Img" ,
( state , opt ) => {
// Draw new cursor immediately
2022-12-06 17:00:50 -06:00
state . lastMouseMove = {
2022-11-29 14:55:25 -06:00
... mouse . coords . world . pos ,
2022-12-06 17:00:50 -06:00
} ;
state . redraw ( ) ;
2022-11-24 09:30:13 -06:00
// Start Listeners
2022-11-29 14:55:25 -06:00
mouse . listen . world . onmousemove . on ( state . mousemovecb ) ;
2022-12-03 06:53:12 -06:00
mouse . listen . world . onwheel . on ( state . wheelcb ) ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
mouse . listen . world . btn . left . onclick . on ( state . dreamcb ) ;
mouse . listen . world . btn . right . onclick . on ( state . erasecb ) ;
// Select Region listeners
2022-12-13 12:13:01 -06:00
mouse . listen . world . btn . left . ondragstart . on ( state . dragstartcb ) ;
mouse . listen . world . btn . left . ondrag . on ( state . dragcb ) ;
mouse . listen . world . btn . left . ondragend . on ( state . dragendcb ) ;
2022-12-15 06:43:26 -06:00
mouse . listen . world . onmousemove . on ( state . smousemovecb , 2 , true ) ;
mouse . listen . world . onwheel . on ( state . swheelcb , 2 , true ) ;
mouse . listen . world . btn . left . onclick . on ( state . sdreamcb , 2 , true ) ;
mouse . listen . world . btn . right . onclick . on ( state . serasecb , 2 , true ) ;
mouse . listen . world . btn . middle . onclick . on ( state . smiddlecb , 2 , true ) ;
2022-11-26 19:35:16 -06:00
2022-12-13 12:13:01 -06:00
// Clear Selection
2022-12-15 06:43:26 -06:00
state . selection . deselect ( ) ;
2022-12-13 12:13:01 -06:00
2022-11-26 19:35:16 -06:00
// Display Mask
setMask ( state . invertMask ? "hold" : "clear" ) ;
2022-12-12 16:25:49 -06:00
// update cursor size if matching is enabled
2022-12-12 18:25:02 -06:00
if ( stableDiffusionData . sync _cursor _size ) {
2022-12-12 16:25:49 -06:00
state . setCursorSize ( stableDiffusionData . width ) ;
}
2022-11-24 09:30:13 -06:00
} ,
( state , opt ) => {
// Clear Listeners
2022-11-29 14:55:25 -06:00
mouse . listen . world . onmousemove . clear ( state . mousemovecb ) ;
2022-12-03 06:53:12 -06:00
mouse . listen . world . onwheel . clear ( state . wheelcb ) ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
mouse . listen . world . btn . left . onclick . clear ( state . dreamcb ) ;
mouse . listen . world . btn . right . onclick . clear ( state . erasecb ) ;
// Clear Select Region listeners
2022-12-13 12:13:01 -06:00
mouse . listen . world . btn . left . ondragstart . clear ( state . dragstartcb ) ;
mouse . listen . world . btn . left . ondrag . clear ( state . dragcb ) ;
mouse . listen . world . btn . left . ondragend . clear ( state . dragendcb ) ;
2022-12-15 06:43:26 -06:00
mouse . listen . world . onmousemove . clear ( state . smousemovecb ) ;
mouse . listen . world . onwheel . clear ( state . swheelcb ) ;
mouse . listen . world . btn . left . onclick . clear ( state . sdreamcb ) ;
mouse . listen . world . btn . right . onclick . clear ( state . serasecb ) ;
mouse . listen . world . btn . middle . onclick . clear ( state . smiddlecb ) ;
2022-11-26 19:35:16 -06:00
2022-12-13 12:13:01 -06:00
// Clear Selection
2022-12-15 06:43:26 -06:00
state . selection . deselect ( ) ;
2022-12-13 12:13:01 -06:00
2022-11-26 19:35:16 -06:00
// Hide mask
setMask ( "none" ) ;
2022-12-04 13:22:35 -06:00
uiCtx . clearRect ( 0 , 0 , uiCanvas . width , uiCanvas . height ) ;
2022-11-24 09:30:13 -06:00
} ,
{
init : ( state ) => {
2022-12-03 06:53:12 -06:00
state . config = {
cursorSizeScrollSpeed : 1 ,
} ;
state . cursorSize = 512 ;
2022-11-24 09:30:13 -06:00
state . snapToGrid = true ;
2022-11-26 13:34:12 -06:00
state . invertMask = true ;
2022-12-16 19:59:30 -06:00
state . keepUnmasked = true ;
state . keepUnmaskedBlur = 8 ;
2022-11-26 13:59:24 -06:00
state . fullResolution = false ;
2022-12-19 18:51:29 -06:00
state . preserveMasks = false ;
2023-01-01 19:30:48 -06:00
state . eagerGenerateCount = 0 ;
2023-02-19 09:49:48 -06:00
state . carve _blur = 0 ;
state . carve _threshold = 10 ;
2022-11-26 13:34:12 -06:00
2022-11-24 09:30:13 -06:00
state . denoisingStrength = 0.7 ;
2022-11-26 13:34:12 -06:00
state . keepBorderSize = 64 ;
2022-12-04 15:57:26 -06:00
state . gradient = true ;
2022-11-24 09:30:13 -06:00
2023-02-18 21:45:04 -06:00
state . carve _blur = 0 ;
state . carve _threshold = 10 ;
2022-12-13 12:13:01 -06:00
state . erasePrevCursor = ( ) =>
uiCtx . clearRect ( 0 , 0 , uiCanvas . width , uiCanvas . height ) ;
2022-12-01 15:05:14 -06:00
state . erasePrevReticle = ( ) =>
2022-12-04 13:22:35 -06:00
uiCtx . clearRect ( 0 , 0 , uiCanvas . width , uiCanvas . height ) ;
2022-12-01 15:05:14 -06:00
2022-12-06 17:00:50 -06:00
state . lastMouseMove = {
2022-12-06 15:28:34 -06:00
... mouse . coords . world . pos ,
} ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
/ * *
* Selection handlers
* /
const selection = _tool . _draggable _selection ( state ) ;
state . dragstartcb = ( evn ) => selection . dragstartcb ( evn ) ;
state . dragcb = ( evn ) => selection . dragcb ( evn ) ;
state . dragendcb = ( evn ) => selection . dragendcb ( evn ) ;
state . smousemovecb = ( evn , estate ) => {
selection . smousemovecb ( evn ) ;
if ( selection . inside ) {
imageCollection . inputElement . style . cursor = "pointer" ;
estate . dream _processed = true ;
} else {
imageCollection . inputElement . style . cursor = "auto" ;
}
2022-12-13 12:13:01 -06:00
} ;
2022-12-15 06:43:26 -06:00
state . swheelcb = ( evn , estate ) => {
if ( selection . inside ) {
state . wheelcb ( evn , { } ) ;
estate . dream _processed = true ;
}
2022-12-13 12:13:01 -06:00
} ;
2022-12-15 06:43:26 -06:00
state . sdreamcb = ( evn , estate ) => {
if ( selection . exists && ! selection . inside ) {
selection . deselect ( ) ;
2022-12-15 19:36:41 -06:00
state . redraw ( ) ;
2022-12-15 06:43:26 -06:00
estate . selection _processed = true ;
}
if ( selection . inside ) {
state . dreamcb ( evn , { } ) ;
estate . dream _processed = true ;
}
} ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
state . serasecb = ( evn , estate ) => {
if ( selection . inside ) {
state . erasecb ( evn , { } ) ;
estate . dream _processed = true ;
}
} ;
2022-12-13 12:13:01 -06:00
2022-12-15 06:43:26 -06:00
state . smiddlecb = ( evn , estate ) => {
if ( selection . inside ) {
estate . dream _processed = true ;
2022-12-13 12:13:01 -06:00
}
} ;
2022-12-15 06:43:26 -06:00
state . selection = selection ;
/ * *
* Dream handlers
* /
2022-12-01 15:05:14 -06:00
state . mousemovecb = ( evn ) => {
2022-12-06 17:00:50 -06:00
state . lastMouseMove = evn ;
2022-12-13 12:13:01 -06:00
state . erasePrevCursor ( ) ;
2022-12-01 15:05:14 -06:00
state . erasePrevReticle ( ) ;
2022-12-06 17:08:55 -06:00
2022-12-13 12:13:01 -06:00
let x = evn . x ;
let y = evn . y ;
if ( state . snapToGrid ) {
x += snap ( evn . x , 0 , 64 ) ;
y += snap ( evn . y , 0 , 64 ) ;
}
2022-12-06 17:00:50 -06:00
2022-12-21 15:29:11 -06:00
state . erasePrevCursor = _tool . _cursor _draw ( x , y ) ;
2022-12-03 06:53:12 -06:00
2022-12-13 12:13:01 -06:00
// Resolution
let bb = null ;
let request = null ;
2022-12-15 06:43:26 -06:00
if ( state . selection . exists ) {
bb = state . selection . bb ;
2022-12-13 12:13:01 -06:00
request = { width : bb . w , height : bb . h } ;
2022-12-15 06:43:26 -06:00
const style =
state . cursorSize > stableDiffusionData . width
? "#FBB5"
: state . cursorSize < stableDiffusionData . width
? "#BFB5"
: "#FFF5" ;
state . erasePrevReticle = _tool . _reticle _draw (
2022-12-13 14:04:16 -06:00
bb ,
"Img2Img" ,
{
w : Math . round (
bb . w * ( stableDiffusionData . width / state . cursorSize )
) ,
h : Math . round (
bb . h * ( stableDiffusionData . height / state . cursorSize )
) ,
} ,
{
2022-12-21 09:07:29 -06:00
toolTextStyle :
global . connection === "online" ? "#FFF5" : "#F555" ,
2022-12-15 06:43:26 -06:00
reticleStyle : state . selection . inside ? "#F55" : "#FFF" ,
2022-12-13 14:04:16 -06:00
sizeTextStyle : style ,
}
) ;
2022-12-13 12:13:01 -06:00
} else {
bb = getBoundingBox (
evn . x ,
evn . y ,
state . cursorSize ,
state . cursorSize ,
state . snapToGrid && basePixelCount
) ;
request = {
width : stableDiffusionData . width ,
height : stableDiffusionData . height ,
} ;
const style =
state . cursorSize > stableDiffusionData . width
? "#FBB5"
: state . cursorSize < stableDiffusionData . width
? "#BFB5"
: "#FFF5" ;
2022-12-15 06:43:26 -06:00
state . erasePrevReticle = _tool . _reticle _draw (
2022-12-13 12:13:01 -06:00
bb ,
"Img2Img" ,
{ w : request . width , h : request . height } ,
{
2022-12-21 09:07:29 -06:00
toolTextStyle :
global . connection === "online" ? "#FFF5" : "#F555" ,
2022-12-13 12:13:01 -06:00
sizeTextStyle : style ,
}
) ;
}
if (
2022-12-15 06:43:26 -06:00
state . selection . exists &&
( state . selection . selected . now . x ===
state . selection . selected . start . x ||
state . selection . selected . now . y ===
state . selection . selected . start . y )
2022-12-13 12:13:01 -06:00
) {
return ;
}
2023-01-09 22:02:19 -06:00
const bbvp = BoundingBox . fromStartEnd (
viewport . canvasToView ( bb . tl ) ,
viewport . canvasToView ( bb . br )
) ;
2022-12-04 13:22:35 -06:00
2022-11-29 14:55:25 -06:00
// For displaying border mask
2022-12-13 21:16:48 -06:00
const bbCanvas = document . createElement ( "canvas" ) ;
bbCanvas . width = request . width ;
bbCanvas . height = request . height ;
const bbCtx = bbCanvas . getContext ( "2d" ) ;
2022-11-29 14:55:25 -06:00
if ( state . keepBorderSize > 0 ) {
2022-12-13 21:16:48 -06:00
bbCtx . fillStyle = "#6A6AFF30" ;
2022-12-04 15:57:26 -06:00
if ( state . gradient ) {
2022-12-13 21:16:48 -06:00
const lg = bbCtx . createLinearGradient (
2022-12-04 15:57:26 -06:00
0 ,
0 ,
state . keepBorderSize ,
0
) ;
lg . addColorStop ( 0 , "#6A6AFF30" ) ;
lg . addColorStop ( 1 , "#0000" ) ;
2022-12-13 21:16:48 -06:00
bbCtx . fillStyle = lg ;
2022-12-04 15:57:26 -06:00
}
2022-12-13 21:16:48 -06:00
bbCtx . fillRect ( 0 , 0 , state . keepBorderSize , request . height ) ;
2022-12-04 15:57:26 -06:00
if ( state . gradient ) {
2022-12-13 21:16:48 -06:00
const tg = bbCtx . createLinearGradient (
2022-12-04 15:57:26 -06:00
0 ,
0 ,
0 ,
state . keepBorderSize
) ;
tg . addColorStop ( 0 , "#6A6AFF30" ) ;
tg . addColorStop ( 1 , "#6A6AFF00" ) ;
2022-12-13 21:16:48 -06:00
bbCtx . fillStyle = tg ;
2022-12-04 15:57:26 -06:00
}
2022-12-13 21:16:48 -06:00
bbCtx . fillRect ( 0 , 0 , request . width , state . keepBorderSize ) ;
2022-12-04 15:57:26 -06:00
if ( state . gradient ) {
2022-12-13 21:16:48 -06:00
const rg = bbCtx . createLinearGradient (
2022-12-04 15:57:26 -06:00
request . width ,
0 ,
request . width - state . keepBorderSize ,
0
) ;
rg . addColorStop ( 0 , "#6A6AFF30" ) ;
rg . addColorStop ( 1 , "#6A6AFF00" ) ;
2022-12-13 21:16:48 -06:00
bbCtx . fillStyle = rg ;
2022-12-04 15:57:26 -06:00
}
2022-12-13 21:16:48 -06:00
bbCtx . fillRect (
2022-12-03 06:53:12 -06:00
request . width - state . keepBorderSize ,
2022-11-29 14:55:25 -06:00
0 ,
state . keepBorderSize ,
2022-12-03 06:53:12 -06:00
request . height
2022-11-24 09:30:13 -06:00
) ;
2022-12-04 15:57:26 -06:00
if ( state . gradient ) {
2022-12-13 21:16:48 -06:00
const bg = bbCtx . createLinearGradient (
2022-12-04 15:57:26 -06:00
0 ,
request . height ,
0 ,
request . height - state . keepBorderSize
) ;
bg . addColorStop ( 0 , "#6A6AFF30" ) ;
bg . addColorStop ( 1 , "#6A6AFF00" ) ;
2022-12-13 21:16:48 -06:00
bbCtx . fillStyle = bg ;
2022-12-04 15:57:26 -06:00
}
2022-12-13 21:16:48 -06:00
bbCtx . fillRect (
2022-11-29 14:55:25 -06:00
0 ,
2022-12-03 06:53:12 -06:00
request . height - state . keepBorderSize ,
request . width ,
2022-11-29 14:55:25 -06:00
state . keepBorderSize
) ;
2022-12-04 13:22:35 -06:00
uiCtx . drawImage (
2022-12-13 21:16:48 -06:00
bbCanvas ,
2022-12-03 06:53:12 -06:00
0 ,
0 ,
request . width ,
request . height ,
2022-12-04 13:22:35 -06:00
bbvp . x ,
bbvp . y ,
bbvp . w ,
bbvp . h
2022-12-03 06:53:12 -06:00
) ;
2022-11-25 16:39:38 -06:00
}
2022-11-24 09:30:13 -06:00
} ;
2022-12-06 15:28:34 -06:00
state . redraw = ( ) => {
2022-12-06 17:00:50 -06:00
state . mousemovecb ( state . lastMouseMove ) ;
2022-12-06 15:28:34 -06:00
} ;
2022-12-14 12:12:44 -06:00
state . wheelcb = ( evn , estate ) => {
if ( estate . dream _processed ) return ;
2022-12-03 06:53:12 -06:00
_dream _onwheel ( evn , state ) ;
} ;
2022-12-14 12:12:44 -06:00
state . dreamcb = ( evn , estate ) => {
2022-12-15 06:43:26 -06:00
if ( estate . dream _processed || estate . selection _processed ) return ;
2022-12-13 12:13:01 -06:00
const bb =
2022-12-15 06:43:26 -06:00
state . selection . bb ||
2022-12-13 12:13:01 -06:00
getBoundingBox (
evn . x ,
evn . y ,
state . cursorSize ,
state . cursorSize ,
state . snapToGrid && basePixelCount
) ;
2022-12-15 06:43:26 -06:00
const resolution = state . selection . bb || {
2022-12-13 17:22:16 -06:00
w : stableDiffusionData . width ,
h : stableDiffusionData . height ,
} ;
2022-12-21 09:07:29 -06:00
if ( global . connection === "online" ) {
dream _img2img _callback ( bb , resolution , state ) ;
} else {
const stop = march ( bb , {
title : "offline" ,
titleStyle : "#F555" ,
style : "#F55" ,
} ) ;
setTimeout ( stop , 2000 ) ;
}
2022-12-15 06:43:26 -06:00
state . selection . deselect ( ) ;
2022-12-13 12:13:01 -06:00
state . redraw ( ) ;
} ;
2022-12-14 12:12:44 -06:00
state . erasecb = ( evn , estate ) => {
if ( estate . dream _processed ) return ;
2022-12-15 06:43:26 -06:00
if ( state . selection . exists ) {
state . selection . deselect ( ) ;
2022-12-13 12:13:01 -06:00
state . redraw ( ) ;
return ;
}
const bb = getBoundingBox (
evn . x ,
evn . y ,
state . cursorSize ,
state . cursorSize ,
state . snapToGrid && basePixelCount
) ;
dream _erase _callback ( bb , state ) ;
2022-11-24 09:30:13 -06:00
} ;
} ,
2023-01-03 09:50:38 -06:00
populateContextMenu : ( menu , state , tool ) => {
2022-11-24 09:30:13 -06:00
if ( ! state . ctxmenu ) {
state . ctxmenu = { } ;
2022-12-03 06:53:12 -06:00
// Cursor Size Slider
const cursorSizeSlider = _toolbar _input . slider (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-cursorsize" ,
2022-12-03 06:53:12 -06:00
"cursorSize" ,
"Cursor Size" ,
{
2022-12-12 17:09:27 -06:00
min : 128 ,
2022-12-03 06:53:12 -06:00
max : 2048 ,
step : 128 ,
textStep : 2 ,
2022-12-12 19:52:14 -06:00
cb : ( ) => {
2022-12-28 23:39:32 -06:00
if ( global . syncCursorSize ) {
2022-12-12 19:52:14 -06:00
resSlider . value = state . cursorSize ;
}
2023-01-03 09:50:38 -06:00
if ( tool . enabled ) state . redraw ( ) ;
2022-12-12 19:52:14 -06:00
} ,
2022-12-03 06:53:12 -06:00
}
) ;
2022-12-28 23:39:32 -06:00
resSlider . onchange . on ( ( { value } ) => {
if ( global . syncCursorSize && value !== state . cursorSize ) {
cursorSizeSlider . rawSlider . value = value ;
}
} ) ;
2022-12-03 06:53:12 -06:00
state . setCursorSize = cursorSizeSlider . setValue ;
state . ctxmenu . cursorSizeSlider = cursorSizeSlider . slider ;
2022-11-24 09:30:13 -06:00
// Snap To Grid Checkbox
state . ctxmenu . snapToGridLabel = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-snaptogrid" ,
2022-11-24 09:30:13 -06:00
"snapToGrid" ,
2023-01-02 18:12:18 -06:00
"Snap To Grid" ,
"icon-grid"
) . checkbox ;
2022-11-24 09:30:13 -06:00
2022-11-26 13:34:12 -06:00
// Invert Mask Checkbox
state . ctxmenu . invertMaskLabel = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-invertmask" ,
2022-11-26 13:34:12 -06:00
"invertMask" ,
2022-11-26 19:35:16 -06:00
"Invert Mask" ,
2023-01-02 18:12:18 -06:00
[ "icon-venetian-mask" , "invert-mask-checkbox" ] ,
2022-11-26 19:35:16 -06:00
( ) => {
setMask ( state . invertMask ? "hold" : "clear" ) ;
}
2023-01-02 18:12:18 -06:00
) . checkbox ;
2022-11-26 13:34:12 -06:00
2023-02-19 09:49:48 -06:00
// Keep Unmasked Content Checkbox
2022-12-16 19:59:30 -06:00
state . ctxmenu . keepUnmaskedLabel = _toolbar _input . checkbox (
2022-12-13 21:16:48 -06:00
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-keepunmasked" ,
2022-12-16 19:59:30 -06:00
"keepUnmasked" ,
"Keep Unmasked" ,
2023-01-02 18:12:18 -06:00
"icon-pin" ,
2022-12-15 08:51:18 -06:00
( ) => {
2022-12-16 19:59:30 -06:00
if ( state . keepUnmasked ) {
state . ctxmenu . keepUnmaskedBlurSlider . classList . remove (
2022-12-15 08:51:18 -06:00
"invisible"
) ;
2022-12-16 19:59:30 -06:00
state . ctxmenu . keepUnmaskedBlurSliderLinebreak . classList . add (
2022-12-15 08:51:18 -06:00
"invisible"
) ;
} else {
2022-12-16 19:59:30 -06:00
state . ctxmenu . keepUnmaskedBlurSlider . classList . add ( "invisible" ) ;
state . ctxmenu . keepUnmaskedBlurSliderLinebreak . classList . remove (
2022-12-15 08:51:18 -06:00
"invisible"
) ;
}
}
2023-01-02 18:12:18 -06:00
) . checkbox ;
2022-12-13 21:16:48 -06:00
2023-02-19 09:49:48 -06:00
// Keep Unmasked Content Blur Slider
2022-12-16 19:59:30 -06:00
state . ctxmenu . keepUnmaskedBlurSlider = _toolbar _input . slider (
2022-12-15 08:51:18 -06:00
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-unmaskedblur" ,
2022-12-16 19:59:30 -06:00
"keepUnmaskedBlur" ,
"Keep Unmasked Blur" ,
2022-12-15 08:51:18 -06:00
{
min : 0 ,
max : 64 ,
step : 4 ,
textStep : 1 ,
}
) . slider ;
2022-12-16 19:59:30 -06:00
state . ctxmenu . keepUnmaskedBlurSliderLinebreak =
2022-12-15 08:51:18 -06:00
document . createElement ( "br" ) ;
2022-12-16 19:59:30 -06:00
state . ctxmenu . keepUnmaskedBlurSliderLinebreak . classList . add (
2022-12-15 08:51:18 -06:00
"invisible"
) ;
2022-12-19 18:51:29 -06:00
// Preserve Brushed Masks Checkbox
state . ctxmenu . preserveMasksLabel = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-preservemasks" ,
2022-12-19 18:51:29 -06:00
"preserveMasks" ,
2023-01-02 18:12:18 -06:00
"Preserve Brushed Masks" ,
"icon-paintbrush"
) . checkbox ;
2022-12-19 18:51:29 -06:00
2022-11-26 13:59:24 -06:00
// Inpaint Full Resolution Checkbox
state . ctxmenu . fullResolutionLabel = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-fullresolution" ,
2022-11-26 13:59:24 -06:00
"fullResolution" ,
2023-01-02 18:12:18 -06:00
"Inpaint Full Resolution" ,
"icon-expand"
) . checkbox ;
2022-11-26 13:59:24 -06:00
2022-11-24 09:30:13 -06:00
// Denoising Strength Slider
state . ctxmenu . denoisingStrengthSlider = _toolbar _input . slider (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-denoisingstrength" ,
2022-11-24 09:30:13 -06:00
"denoisingStrength" ,
"Denoising Strength" ,
2022-12-03 04:50:23 -06:00
{
min : 0 ,
max : 1 ,
step : 0.05 ,
textStep : 0.01 ,
}
2022-11-24 09:30:13 -06:00
) . slider ;
2022-12-04 15:57:26 -06:00
// Border Mask Gradient Checkbox
state . ctxmenu . borderMaskGradientCheckbox = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-gradient" ,
2022-12-04 15:57:26 -06:00
"gradient" ,
2023-01-02 18:12:18 -06:00
"Border Mask Gradient" ,
"icon-box-select"
) . checkbox ;
2022-12-04 15:57:26 -06:00
2023-01-29 16:39:00 -06:00
// Remove Identical/Background Pixels Checkbox
state . ctxmenu . removeBackgroundLabel = _toolbar _input . checkbox (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-removebg" ,
2023-01-29 16:39:00 -06:00
"removeBackground" ,
"Remove Identical/BG Pixels" ,
2023-02-18 12:00:10 -06:00
"icon-slice" ,
( ) => {
if ( state . removeBackground ) {
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveBlurSlider . classList . remove ( "invisible" ) ;
state . ctxmenu . carveThresholdSlider . classList . remove (
"invisible"
) ;
2023-02-18 12:00:10 -06:00
} else {
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveBlurSlider . classList . add ( "invisible" ) ;
state . ctxmenu . carveThresholdSlider . classList . add ( "invisible" ) ;
2023-02-18 12:00:10 -06:00
}
}
2023-01-29 16:39:00 -06:00
) . checkbox ;
2022-11-24 09:30:13 -06:00
// Border Mask Size Slider
state . ctxmenu . borderMaskSlider = _toolbar _input . slider (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-keepbordersize" ,
2022-11-26 13:34:12 -06:00
"keepBorderSize" ,
"Keep Border Size" ,
2022-12-03 04:50:23 -06:00
{
min : 0 ,
max : 128 ,
step : 8 ,
textStep : 1 ,
}
2022-11-24 09:30:13 -06:00
) . slider ;
2022-12-31 13:31:31 -06:00
// inpaint fill type select list
state . ctxmenu . inpaintTypeSelect = _toolbar _input . selectlist (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-inpaintingtype" ,
2022-12-31 13:31:31 -06:00
"inpainting_fill" ,
"Inpaint Type" ,
{
0 : "fill" ,
1 : "original (recommended)" ,
2 : "latent noise" ,
3 : "latent nothing" ,
} ,
1 , // USE ORIGINAL FOR IMG2IMG OR ELSE but we still give you the option because we love you
( ) => {
stableDiffusionData . inpainting _fill = state . inpainting _fill ;
}
) . label ;
2023-01-01 22:48:24 -06:00
2023-01-01 22:06:09 -06:00
// Eager generation Slider
state . ctxmenu . eagerGenerateCountLabel = _toolbar _input . slider (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-eagergeneratecount" ,
2023-01-01 22:06:09 -06:00
"eagerGenerateCount" ,
"Generate-ahead count" ,
{
min : 0 ,
max : 100 ,
step : 2 ,
textStep : 1 ,
}
) . slider ;
2023-02-04 14:28:01 -06:00
// img cfg scale slider for instruct-pix2pix
state . ctxmenu . instructPix2PixImgCfgLabel = _toolbar _input . slider (
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-ip2pcfg" ,
2023-02-04 14:28:01 -06:00
"image_cfg_scale" ,
"iP2P Image CFG Scale" ,
{
min : 0 ,
max : 30 ,
step : 1 ,
textStep : 0.1 ,
}
) . slider ;
state . ctxmenu . instructPix2PixImgCfgLabel . classList . add (
"instruct-pix2pix-img-cfg"
) ;
2023-02-18 12:00:10 -06:00
// bg carve blur
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveBlurSlider = _toolbar _input . slider (
2023-02-18 12:00:10 -06:00
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-carveblur" ,
2023-02-18 12:00:10 -06:00
"carve_blur" ,
"BG Remove Blur" ,
{
min : 0 ,
max : 30 ,
step : 2 ,
textStep : 1 ,
}
) . slider ;
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveBlurSlider . classList . add ( "invisible" ) ;
2023-02-18 12:00:10 -06:00
// bg carve threshold
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveThresholdSlider = _toolbar _input . slider (
2023-02-18 12:00:10 -06:00
state ,
2023-02-19 09:41:46 -06:00
"openoutpaint/img2img-carvethreshold" ,
2023-02-18 12:00:10 -06:00
"carve_threshold" ,
"BG Remove Threshold" ,
{
min : 0 ,
max : 255 ,
step : 5 ,
textStep : 1 ,
}
) . slider ;
2023-02-19 09:49:48 -06:00
state . ctxmenu . carveThresholdSlider . classList . add ( "invisible" ) ;
2022-11-24 09:30:13 -06:00
}
2022-12-03 06:53:12 -06:00
menu . appendChild ( state . ctxmenu . cursorSizeSlider ) ;
2023-01-02 18:12:18 -06:00
const array = document . createElement ( "div" ) ;
array . classList . add ( "checkbox-array" ) ;
array . appendChild ( state . ctxmenu . snapToGridLabel ) ;
array . appendChild ( state . ctxmenu . invertMaskLabel ) ;
array . appendChild ( state . ctxmenu . preserveMasksLabel ) ;
array . appendChild ( state . ctxmenu . keepUnmaskedLabel ) ;
2023-02-19 09:49:48 -06:00
array . appendChild ( state . ctxmenu . removeBackgroundLabel ) ;
2023-01-02 18:12:18 -06:00
menu . appendChild ( array ) ;
2022-12-16 19:59:30 -06:00
menu . appendChild ( state . ctxmenu . keepUnmaskedBlurSlider ) ;
2023-02-19 09:49:48 -06:00
menu . appendChild ( state . ctxmenu . carveBlurSlider ) ;
menu . appendChild ( state . ctxmenu . carveThresholdSlider ) ;
2023-01-02 23:36:42 -06:00
// menu.appendChild(state.ctxmenu.keepUnmaskedBlurSliderLinebreak);
2023-01-01 10:31:49 -06:00
menu . appendChild ( state . ctxmenu . inpaintTypeSelect ) ;
2022-11-24 09:30:13 -06:00
menu . appendChild ( state . ctxmenu . denoisingStrengthSlider ) ;
2023-02-04 14:28:01 -06:00
menu . appendChild ( state . ctxmenu . instructPix2PixImgCfgLabel ) ;
2023-01-02 18:12:18 -06:00
const btnArray2 = document . createElement ( "div" ) ;
btnArray2 . classList . add ( "checkbox-array" ) ;
btnArray2 . appendChild ( state . ctxmenu . fullResolutionLabel ) ;
btnArray2 . appendChild ( state . ctxmenu . borderMaskGradientCheckbox ) ;
menu . appendChild ( btnArray2 ) ;
2022-11-24 09:30:13 -06:00
menu . appendChild ( state . ctxmenu . borderMaskSlider ) ;
2023-01-01 19:30:48 -06:00
menu . appendChild ( state . ctxmenu . eagerGenerateCountLabel ) ;
2022-11-24 09:30:13 -06:00
} ,
shortcut : "I" ,
}
) ;
2022-12-06 15:28:34 -06:00
2022-12-15 09:51:08 -06:00
const sendSeed = ( seed ) => {
stableDiffusionData . seed = document . getElementById ( "seed" ) . value = seed ;
} ;
2023-07-08 10:49:56 -05:00
function buildAlwaysOnScripts ( state ) {
if ( extensions . alwaysOnScripts ) {
state . alwayson _scripts = { } ;
}
}
function addDynamicPromptsToAlwaysOnScripts ( state ) {
2023-07-08 12:54:17 -05:00
if ( extensions . dynamicPromptsEnabled ) {
state . alwayson _scripts [ extensions . dynamicPromptsAlwaysonScriptName ] = { } ;
state . alwayson _scripts [ extensions . dynamicPromptsAlwaysonScriptName ] . args = [
extensions . dynamicPromptsActive ,
] ;
}
2023-07-08 10:49:56 -05:00
}
2023-07-08 15:21:58 -05:00
function addControlNetToAlwaysOnScripts ( state , initCanvas , maskCanvas ) {
2023-07-15 12:10:45 -05:00
var initimg =
toolbar . _current _tool . name == "Dream" ? initCanvas . toDataURL ( ) : initCanvas ;
var maskimg =
toolbar . _current _tool . name == "Dream" ? maskCanvas . toDataURL ( ) : maskCanvas ;
2023-07-08 15:21:58 -05:00
if ( extensions . controlNetEnabled && extensions . controlNetActive ) {
state . alwayson _scripts . controlnet = { } ;
2023-07-08 16:17:25 -05:00
if ( initCanvas == null && maskCanvas == null ) {
//img2img?
state . alwayson _scripts . controlnet . args = [
{
module : extensions . selectedControlNetModule ,
model : extensions . selectedControlNetModel ,
control _mode : document . getElementById ( "controlNetMode-select" ) . value ,
processor _res : 64 ,
2023-07-08 17:45:47 -05:00
resize _mode : document . getElementById ( "controlNetResize-select" ) . value ,
2023-07-08 16:17:25 -05:00
// resize mode?
// weights / steps?
} ,
] ;
} else {
state . alwayson _scripts . controlnet . args = [
{
module : extensions . selectedControlNetModule ,
model : extensions . selectedControlNetModel ,
control _mode : document . getElementById ( "controlNetMode-select" ) . value ,
2023-07-15 12:10:45 -05:00
input _image : initimg , //initCanvas.toDataURL(),
mask : maskimg , //maskCanvas.toDataURL(),
2023-07-08 16:17:25 -05:00
processor _res : 64 ,
2023-07-08 17:45:47 -05:00
resize _mode : document . getElementById ( "controlNetResize-select" ) . value ,
2023-07-08 16:17:25 -05:00
// resize mode?
// weights / steps?
} ,
] ;
}
2023-07-15 12:10:45 -05:00
if ( extensions . controlNetReferenceActive ) {
state . alwayson _scripts . controlnet . args . unshift ( {
enabled : true ,
module : extensions . selectedCNReferenceModule ,
model : "None" ,
control _mode : document . getElementById ( "controlNetReferenceMode-select" )
. value ,
image : initimg , //initCanvas.toDataURL(),
processor _res : 64 ,
threshold _a : extensions . controlNetReferenceFidelity ,
threshold _b : 64 ,
resize _mode : document . getElementById ( "controlNetResize-select" ) . value ,
} ) ;
}
2023-07-08 15:21:58 -05:00
}
2023-07-15 12:10:45 -05:00
var deleteme = 2463 ;
var ok = deleteme / 36 ;
2023-07-08 10:49:56 -05:00
// request.alwayson_scripts = state.alwayson_scripts;
// }
}
2023-07-08 15:21:58 -05:00
// function getImageAndMask(visibleCanvas, bb, request, state) {
// // get input image
// // Temporary canvas for init image and mask generation
// const bbCanvas = document.createElement("canvas");
// bbCanvas.width = bb.w;
// bbCanvas.height = bb.h;
// const bbCtx = bbCanvas.getContext("2d");
// const maskCanvas = document.createElement("canvas");
// maskCanvas.width = request.width;
// maskCanvas.height = request.height;
// const maskCtx = maskCanvas.getContext("2d");
// const initCanvas = document.createElement("canvas");
// initCanvas.width = request.width;
// initCanvas.height = request.height;
// const initCtx = initCanvas.getContext("2d");
// bbCtx.fillStyle = "#000F";
// // Get init image
// initCtx.fillRect(0, 0, request.width, request.height);
// initCtx.drawImage(
// visibleCanvas,
// 0,
// 0,
// bb.w,
// bb.h,
// 0,
// 0,
// request.width,
// request.height
// );
// // request.init_images = [initCanvas.toDataURL()];
// // Get mask image
// bbCtx.fillStyle = "#000F";
// bbCtx.fillRect(0, 0, bb.w, bb.h);
// if (state.invertMask) {
// // overmasking by definition is entirely pointless with an inverted mask outpaint
// // since it should explicitly avoid brushed masks too, we just won't even bother
// bbCtx.globalCompositeOperation = "destination-in";
// bbCtx.drawImage(maskPaintCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
// bbCtx.globalCompositeOperation = "destination-in";
// bbCtx.drawImage(visibleCanvas, 0, 0);
// } else {
// bbCtx.globalCompositeOperation = "destination-in";
// bbCtx.drawImage(visibleCanvas, 0, 0);
// // here's where to overmask to avoid including the brushed mask
// // 99% of my issues were from failing to set source-over for the overmask blotches
// if (state.overMaskPx > 0) {
// // transparent to white first
// bbCtx.globalCompositeOperation = "destination-atop";
// bbCtx.fillStyle = "#FFFF";
// bbCtx.fillRect(0, 0, bb.w, bb.h);
// applyOvermask(bbCanvas, bbCtx, state.overMaskPx);
// }
// bbCtx.globalCompositeOperation = "destination-out"; // ???
// bbCtx.drawImage(maskPaintCanvas, bb.x, bb.y, bb.w, bb.h, 0, 0, bb.w, bb.h);
// }
// bbCtx.globalCompositeOperation = "destination-atop";
// bbCtx.fillStyle = "#FFFF";
// bbCtx.fillRect(0, 0, bb.w, bb.h);
// maskCtx.clearRect(0, 0, maskCanvas.width, maskCanvas.height);
// maskCtx.drawImage(
// bbCanvas,
// 0,
// 0,
// bb.w,
// bb.h,
// 0,
// 0,
// request.width,
// request.height
// );
// }