initial commit

This commit is contained in:
2025-10-21 23:06:12 +07:00
commit 384a42b08e
1550 changed files with 2675522 additions and 0 deletions

View 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%);
}

View 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

File diff suppressed because one or more lines are too long

View 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});}}')