initial commit
This commit is contained in:
179
modules_forge/forge_canvas/canvas.css
Executable file
179
modules_forge/forge_canvas/canvas.css
Executable file
@@ -0,0 +1,179 @@
|
||||
.forge-container {
|
||||
width: 100%;
|
||||
height: 512px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.forge-image-container-plain {
|
||||
width: 100%;
|
||||
height: calc(100% - 6px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: #202020;
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 10px 10px;
|
||||
}
|
||||
|
||||
.forge-image-container {
|
||||
width: 100%;
|
||||
height: calc(100% - 6px);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background-color: #cccccc;
|
||||
background-image: linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee),
|
||||
linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%, #eee);
|
||||
background-size: 20px 20px;
|
||||
background-position: 0 0, 10px 10px;
|
||||
}
|
||||
|
||||
.forge-image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
cursor: grab;
|
||||
max-width: unset !important;
|
||||
max-height: unset !important;
|
||||
}
|
||||
|
||||
.forge-image:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
.forge-file-upload {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.forge-resize-line {
|
||||
width: 100%;
|
||||
height: 6px;
|
||||
background-image: linear-gradient(to bottom, grey 50%, darkgrey 50%);
|
||||
background-size: 4px 4px;
|
||||
background-repeat: repeat;
|
||||
cursor: ns-resize;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.forge-toolbar-static {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 10 !important;
|
||||
background: rgba(47, 47, 47, 0.8);
|
||||
padding: 6px 10px;
|
||||
opacity: 1.0 !important;
|
||||
}
|
||||
|
||||
.forge-toolbar {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
z-index: 10;
|
||||
background: rgba(47, 47, 47, 0.8);
|
||||
padding: 6px 10px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.forge-toolbar .forge-btn, .forge-toolbar-static .forge-btn {
|
||||
padding: 2px 6px;
|
||||
border: none;
|
||||
background-color: #4a4a4a;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.forge-toolbar .forge-btn, .forge-toolbar-static .forge-btn:hover {
|
||||
background-color: #5e5e5e;
|
||||
}
|
||||
|
||||
.forge-toolbar .forge-btn, .forge-toolbar-static .forge-btn:active {
|
||||
background-color: #3e3e3e;
|
||||
}
|
||||
|
||||
.forge-toolbar-box-a {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.forge-toolbar-box-b {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.forge-color-picker-block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.forge-range-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.forge-toolbar-color {
|
||||
border: none;
|
||||
background: none;
|
||||
padding: 3px;
|
||||
border-radius: 50%;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.forge-toolbar-color::-webkit-color-swatch-wrapper {
|
||||
padding: 0;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.forge-toolbar-color::-webkit-color-swatch {
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: none;
|
||||
}
|
||||
|
||||
.forge-toolbar-label {
|
||||
color: white !important;
|
||||
padding: 0 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 4px; /* Adjust margin as needed */
|
||||
}
|
||||
|
||||
.forge-toolbar-range {
|
||||
}
|
||||
|
||||
.forge-scribble-indicator {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
border: 1px solid;
|
||||
pointer-events: none;
|
||||
display: none;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.forge-no-select {
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.forge-upload-hint {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 30%;
|
||||
height: 30%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
63
modules_forge/forge_canvas/canvas.html
Executable file
63
modules_forge/forge_canvas/canvas.html
Executable file
@@ -0,0 +1,63 @@
|
||||
<div class="forge-container" id="container_forge_mixin">
|
||||
<input type="file" id="imageInput_forge_mixin" class="forge-file-upload">
|
||||
<div id="imageContainer_forge_mixin" class="forge-image-container">
|
||||
<div id="uploadHint_forge_mixin">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24" fill="none"
|
||||
stroke="white"
|
||||
stroke-width="4"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="forge-upload-hint">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="17 8 12 3 7 8"></polyline>
|
||||
<line x1="12" y1="3" x2="12" y2="15"></line>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24" fill="none"
|
||||
stroke="grey"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
class="forge-upload-hint">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
||||
<polyline points="17 8 12 3 7 8"></polyline>
|
||||
<line x1="12" y1="3" x2="12" y2="15"></line>
|
||||
</svg>
|
||||
</div>
|
||||
<img id="image_forge_mixin" class="forge-image forge-no-select">
|
||||
<canvas id="drawingCanvas_forge_mixin" class="forge-drawing-canvas"
|
||||
style="position:absolute;top:0;left:0;" width="1" height="1"></canvas>
|
||||
<div class="forge-toolbar" id="toolbar_forge_mixin">
|
||||
<div class="forge-toolbar-box-a">
|
||||
<button id="maxButton_forge_mixin" class="forge-btn forge-no-select" title="Maximize">⛶</button>
|
||||
<button id="minButton_forge_mixin" class="forge-btn forge-no-select" title="Minimize" style="display: none;">➖</button>
|
||||
<button id="uploadButton_forge_mixin" class="forge-btn forge-no-select" title="Upload">📂</button>
|
||||
<button id="removeButton_forge_mixin" class="forge-btn forge-no-select" title="Remove">🗑️</button>
|
||||
<button id="centerButton_forge_mixin" class="forge-btn forge-no-select" title="Center Position">✠</button>
|
||||
<button id="resetButton_forge_mixin" class="forge-btn forge-no-select" title="Reset">🔄</button>
|
||||
<button id="undoButton_forge_mixin" class="forge-btn forge-no-select" title="Undo">↩️</button>
|
||||
<button id="redoButton_forge_mixin" class="forge-btn forge-no-select" title="Redo">↪️</button>
|
||||
</div>
|
||||
<div class="forge-toolbar-box-b">
|
||||
<div class="forge-color-picker-block" id="scribbleColorBlock_forge_mixin">
|
||||
<input type="color" id="scribbleColor_forge_mixin" class="forge-toolbar-color" value="#000000">
|
||||
</div>
|
||||
<div class="forge-range-row" id="scribbleWidthBlock_forge_mixin">
|
||||
<div id="widthLabel_forge_mixin" class="forge-toolbar-label">brush width</div>
|
||||
<input type="range" id="scribbleWidth_forge_mixin" class="forge-toolbar-range" min="1" max="20" value="4">
|
||||
</div>
|
||||
<div class="forge-range-row" id="scribbleAlphaBlock_forge_mixin">
|
||||
<div id="alphaLabel_forge_mixin" class="forge-toolbar-label">brush opacity</div>
|
||||
<input type="range" id="scribbleAlpha_forge_mixin" class="forge-toolbar-range" min="0" max="100" value="100">
|
||||
</div>
|
||||
<div class="forge-range-row" id="scribbleSoftnessBlock_forge_mixin">
|
||||
<div id="softnessLabel_forge_mixin" class="forge-toolbar-label">brush softness</div>
|
||||
<input type="range" id="scribbleSoftness_forge_mixin" class="forge-toolbar-range" min="0" max="100" value="0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="scribbleIndicator_forge_mixin" class="forge-scribble-indicator"></div>
|
||||
</div>
|
||||
<div class="forge-resize-line" id="resizeLine_forge_mixin"></div>
|
||||
</div>
|
||||
1
modules_forge/forge_canvas/canvas.min.js
vendored
Executable file
1
modules_forge/forge_canvas/canvas.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
153
modules_forge/forge_canvas/canvas.py
Executable file
153
modules_forge/forge_canvas/canvas.py
Executable file
@@ -0,0 +1,153 @@
|
||||
# Forge Canvas
|
||||
# AGPL V3
|
||||
# by lllyasviel
|
||||
# Commercial Use is not allowed. (Contact us for commercial use.)
|
||||
|
||||
import gradio.component_meta
|
||||
|
||||
|
||||
create_or_modify_pyi_org = gradio.component_meta.create_or_modify_pyi
|
||||
|
||||
|
||||
def create_or_modify_pyi_org_patched(component_class, class_name, events):
|
||||
try:
|
||||
if component_class.__name__ == 'LogicalImage':
|
||||
return
|
||||
return create_or_modify_pyi_org(component_class, class_name, events)
|
||||
except:
|
||||
return
|
||||
|
||||
|
||||
gradio.component_meta.create_or_modify_pyi = create_or_modify_pyi_org_patched
|
||||
|
||||
|
||||
import os
|
||||
import uuid
|
||||
import base64
|
||||
import gradio as gr
|
||||
import numpy as np
|
||||
|
||||
from PIL import Image
|
||||
from io import BytesIO
|
||||
from gradio.context import Context
|
||||
from functools import wraps
|
||||
|
||||
from modules.shared import opts
|
||||
|
||||
canvas_js_root_path = os.path.dirname(__file__)
|
||||
|
||||
|
||||
def web_js(file_name):
|
||||
full_path = os.path.join(canvas_js_root_path, file_name)
|
||||
return f'<script src="file={full_path}?{os.path.getmtime(full_path)}"></script>\n'
|
||||
|
||||
|
||||
def web_css(file_name):
|
||||
full_path = os.path.join(canvas_js_root_path, file_name)
|
||||
return f'<link rel="stylesheet" href="file={full_path}?{os.path.getmtime(full_path)}">\n'
|
||||
|
||||
|
||||
DEBUG_MODE = False
|
||||
|
||||
canvas_html = open(os.path.join(canvas_js_root_path, 'canvas.html'), encoding='utf-8').read()
|
||||
canvas_head = ''
|
||||
canvas_head += web_css('canvas.css')
|
||||
canvas_head += web_js('canvas.min.js')
|
||||
|
||||
|
||||
def image_to_base64(image_array, numpy=True):
|
||||
image = Image.fromarray(image_array) if numpy else image_array
|
||||
image = image.convert("RGBA")
|
||||
buffered = BytesIO()
|
||||
image.save(buffered, format="PNG")
|
||||
image_base64 = base64.b64encode(buffered.getvalue()).decode('utf-8')
|
||||
return f"data:image/png;base64,{image_base64}"
|
||||
|
||||
|
||||
def base64_to_image(base64_str, numpy=True):
|
||||
if base64_str.startswith("data:image/png;base64,"):
|
||||
base64_str = base64_str.replace("data:image/png;base64,", "")
|
||||
image_data = base64.b64decode(base64_str)
|
||||
image = Image.open(BytesIO(image_data))
|
||||
image = image.convert("RGBA")
|
||||
image_array = np.array(image) if numpy else image
|
||||
return image_array
|
||||
|
||||
|
||||
class LogicalImage(gr.Textbox):
|
||||
@wraps(gr.Textbox.__init__)
|
||||
def __init__(self, *args, numpy=True, **kwargs):
|
||||
self.numpy = numpy
|
||||
self.infotext = dict()
|
||||
|
||||
if 'value' in kwargs:
|
||||
initial_value = kwargs['value']
|
||||
if initial_value is not None:
|
||||
kwargs['value'] = self.image_to_base64(initial_value)
|
||||
else:
|
||||
del kwargs['value']
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def preprocess(self, payload):
|
||||
if not isinstance(payload, str):
|
||||
return None
|
||||
|
||||
if not payload.startswith("data:image/png;base64,"):
|
||||
return None
|
||||
|
||||
image = base64_to_image(payload, numpy=self.numpy)
|
||||
if hasattr(image, 'info'):
|
||||
image.info = self.infotext
|
||||
|
||||
return image
|
||||
|
||||
def postprocess(self, value):
|
||||
if value is None:
|
||||
return None
|
||||
|
||||
if hasattr(value, 'info'):
|
||||
self.infotext = value.info
|
||||
|
||||
return image_to_base64(value, numpy=self.numpy)
|
||||
|
||||
def get_block_name(self):
|
||||
return "textbox"
|
||||
|
||||
|
||||
class ForgeCanvas:
|
||||
def __init__(
|
||||
self,
|
||||
no_upload=False,
|
||||
no_scribbles=False,
|
||||
contrast_scribbles=False,
|
||||
height=512,
|
||||
scribble_color='#000000',
|
||||
scribble_color_fixed=False,
|
||||
scribble_width=4,
|
||||
scribble_width_fixed=False,
|
||||
scribble_alpha=100,
|
||||
scribble_alpha_fixed=False,
|
||||
scribble_softness=0,
|
||||
scribble_softness_fixed=False,
|
||||
visible=True,
|
||||
numpy=False,
|
||||
initial_image=None,
|
||||
elem_id=None,
|
||||
elem_classes=None
|
||||
):
|
||||
self.uuid = 'uuid_' + uuid.uuid4().hex
|
||||
|
||||
canvas_html_uuid = canvas_html.replace('forge_mixin', self.uuid)
|
||||
|
||||
if opts.forge_canvas_plain:
|
||||
canvas_html_uuid = canvas_html_uuid.replace('class="forge-image-container"', 'class="forge-image-container-plain"').replace('stroke="white"', 'stroke=#444')
|
||||
if opts.forge_canvas_toolbar_always:
|
||||
canvas_html_uuid = canvas_html_uuid.replace('class="forge-toolbar"', 'class="forge-toolbar-static"')
|
||||
|
||||
self.block = gr.HTML(canvas_html_uuid, visible=visible, elem_id=elem_id, elem_classes=elem_classes)
|
||||
self.foreground = LogicalImage(visible=DEBUG_MODE, label='foreground', numpy=numpy, elem_id=self.uuid, elem_classes=['logical_image_foreground'])
|
||||
self.background = LogicalImage(visible=DEBUG_MODE, label='background', numpy=numpy, value=initial_image, elem_id=self.uuid, elem_classes=['logical_image_background'])
|
||||
Context.root_block.load(None, js=f'async ()=>{{new ForgeCanvas("{self.uuid}", {no_upload}, {no_scribbles}, {contrast_scribbles}, {height}, '
|
||||
f"'{scribble_color}', {scribble_color_fixed}, {scribble_width}, {scribble_width_fixed}, "
|
||||
f'{scribble_alpha}, {scribble_alpha_fixed}, {scribble_softness}, {scribble_softness_fixed});}}')
|
||||
Reference in New Issue
Block a user