mirror of
https://github.com/Comfy-Org/ComfyUI_frontend.git
synced 2026-04-20 14:30:41 +00:00
## GPU accelerated brush engine for the mask editor - Full GPU acceleration using TypeGPU and type-safe shaders - Catmull-Rom Spline Smoothing - arc-length equidistant resampling - much improved performance, even for huge images - photoshop like opacity clamping for brush strokes - much improved soft brushes - fallback to CPU fully implemented, much improved CPU rendering features as well ### Tested Browsers - Chrome (fully supported) - Safari 26 (fully supported, prev versions CPU fallback) - Firefox (CPU fallback, flags needed for full support) https://github.com/user-attachments/assets/b7b5cb8a-2290-4a95-ae7d-180e11fccdb0 https://github.com/user-attachments/assets/4297aaa5-f249-499a-9b74-869677f1c73b https://github.com/user-attachments/assets/602b4783-3e2b-489e-bcb9-70534bcaac5e ┆Issue is synchronized with this [Notion page](https://www.notion.so/PR-6767-GPU-accelerated-maskeditor-rendering-2b16d73d3650818cb294e1fca03f6169) by [Unito](https://www.unito.io)
130 lines
3.6 KiB
Vue
130 lines
3.6 KiB
Vue
<template>
|
|
<div class="flex flex-col gap-3 pb-3">
|
|
<h3
|
|
class="text-center text-[15px] font-sans text-[var(--descrip-text)] mt-2.5"
|
|
>
|
|
{{ t('maskEditor.brushSettings') }}
|
|
</h3>
|
|
|
|
<button
|
|
class="w-45 h-7.5 border-none bg-black/20 border border-[var(--border-color)] text-[var(--input-text)] font-sans text-[15px] pointer-events-auto transition-colors duration-100 hover:bg-[var(--p-overlaybadge-outline-color)] hover:border-none"
|
|
@click="resetToDefault"
|
|
>
|
|
{{ t('maskEditor.resetToDefault') }}
|
|
</button>
|
|
|
|
<div class="flex flex-col gap-3 pb-3">
|
|
<span class="text-left text-xs font-sans text-[var(--descrip-text)]">{{
|
|
t('maskEditor.brushShape')
|
|
}}</span>
|
|
<div
|
|
class="flex flex-row gap-2.5 items-center min-h-6 relative h-[50px] w-full rounded-[10px] bg-secondary-background-hover"
|
|
>
|
|
<div
|
|
class="maskEditor_sidePanelBrushShapeCircle bg-transparent hover:bg-comfy-menu-bg"
|
|
:class="{ active: store.brushSettings.type === BrushShape.Arc }"
|
|
:style="{
|
|
background:
|
|
store.brushSettings.type === BrushShape.Arc
|
|
? 'var(--p-button-text-primary-color)'
|
|
: ''
|
|
}"
|
|
@click="setBrushShape(BrushShape.Arc)"
|
|
></div>
|
|
<div
|
|
class="maskEditor_sidePanelBrushShapeSquare bg-transparent hover:bg-comfy-menu-bg"
|
|
:class="{ active: store.brushSettings.type === BrushShape.Rect }"
|
|
:style="{
|
|
background:
|
|
store.brushSettings.type === BrushShape.Rect
|
|
? 'var(--p-button-text-primary-color)'
|
|
: ''
|
|
}"
|
|
@click="setBrushShape(BrushShape.Rect)"
|
|
></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="flex flex-col gap-3 pb-3">
|
|
<span class="text-left text-xs font-sans text-[var(--descrip-text)]">{{
|
|
t('maskEditor.colorSelector')
|
|
}}</span>
|
|
<input type="color" :value="store.rgbColor" @input="onColorChange" />
|
|
</div>
|
|
|
|
<SliderControl
|
|
:label="t('maskEditor.thickness')"
|
|
:min="1"
|
|
:max="500"
|
|
:step="1"
|
|
:model-value="store.brushSettings.size"
|
|
@update:model-value="onThicknessChange"
|
|
/>
|
|
|
|
<SliderControl
|
|
:label="t('maskEditor.opacity')"
|
|
:min="0"
|
|
:max="1"
|
|
:step="0.01"
|
|
:model-value="store.brushSettings.opacity"
|
|
@update:model-value="onOpacityChange"
|
|
/>
|
|
|
|
<SliderControl
|
|
:label="t('maskEditor.hardness')"
|
|
:min="0"
|
|
:max="1"
|
|
:step="0.01"
|
|
:model-value="store.brushSettings.hardness"
|
|
@update:model-value="onHardnessChange"
|
|
/>
|
|
|
|
<SliderControl
|
|
label="Stepsize"
|
|
:min="1"
|
|
:max="100"
|
|
:step="1"
|
|
:model-value="store.brushSettings.stepSize"
|
|
@update:model-value="onStepSizeChange"
|
|
/>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { BrushShape } from '@/extensions/core/maskeditor/types'
|
|
import { t } from '@/i18n'
|
|
import { useMaskEditorStore } from '@/stores/maskEditorStore'
|
|
|
|
import SliderControl from './controls/SliderControl.vue'
|
|
|
|
const store = useMaskEditorStore()
|
|
|
|
const setBrushShape = (shape: BrushShape) => {
|
|
store.brushSettings.type = shape
|
|
}
|
|
|
|
const onColorChange = (event: Event) => {
|
|
store.rgbColor = (event.target as HTMLInputElement).value
|
|
}
|
|
|
|
const onThicknessChange = (value: number) => {
|
|
store.setBrushSize(value)
|
|
}
|
|
|
|
const onOpacityChange = (value: number) => {
|
|
store.setBrushOpacity(value)
|
|
}
|
|
|
|
const onHardnessChange = (value: number) => {
|
|
store.setBrushHardness(value)
|
|
}
|
|
|
|
const onStepSizeChange = (value: number) => {
|
|
store.setBrushStepSize(value)
|
|
}
|
|
|
|
const resetToDefault = () => {
|
|
store.resetBrushToDefault()
|
|
}
|
|
</script>
|