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)
112 lines
2.8 KiB
Vue
112 lines
2.8 KiB
Vue
<template>
|
|
<div
|
|
id="maskEditor_brush"
|
|
:style="{
|
|
position: 'absolute',
|
|
opacity: brushOpacity,
|
|
width: `${brushSize}px`,
|
|
height: `${brushSize}px`,
|
|
left: `${brushLeft}px`,
|
|
top: `${brushTop}px`,
|
|
borderRadius: borderRadius,
|
|
pointerEvents: 'none',
|
|
zIndex: 1000
|
|
}"
|
|
>
|
|
<div
|
|
id="maskEditor_brushPreviewGradient"
|
|
:style="{
|
|
display: gradientVisible ? 'block' : 'none',
|
|
background: gradientBackground
|
|
}"
|
|
></div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
|
|
import {
|
|
getEffectiveBrushSize,
|
|
getEffectiveHardness
|
|
} from '@/composables/maskeditor/brushUtils'
|
|
import { BrushShape } from '@/extensions/core/maskeditor/types'
|
|
import { useMaskEditorStore } from '@/stores/maskEditorStore'
|
|
|
|
const { containerRef } = defineProps<{
|
|
containerRef?: HTMLElement
|
|
}>()
|
|
|
|
const store = useMaskEditorStore()
|
|
|
|
const brushOpacity = computed(() => {
|
|
return store.brushVisible ? 1 : 0
|
|
})
|
|
|
|
const brushRadius = computed(() => {
|
|
const size = store.brushSettings.size
|
|
const hardness = store.brushSettings.hardness
|
|
const effectiveSize = getEffectiveBrushSize(size, hardness)
|
|
return effectiveSize * store.zoomRatio
|
|
})
|
|
|
|
const brushSize = computed(() => {
|
|
return brushRadius.value * 2
|
|
})
|
|
|
|
const brushLeft = computed(() => {
|
|
const dialogRect = containerRef?.getBoundingClientRect()
|
|
const dialogOffsetLeft = dialogRect?.left || 0
|
|
return (
|
|
store.cursorPoint.x +
|
|
store.panOffset.x -
|
|
brushRadius.value -
|
|
dialogOffsetLeft
|
|
)
|
|
})
|
|
|
|
const brushTop = computed(() => {
|
|
const dialogRect = containerRef?.getBoundingClientRect()
|
|
const dialogOffsetTop = dialogRect?.top || 0
|
|
return (
|
|
store.cursorPoint.y +
|
|
store.panOffset.y -
|
|
brushRadius.value -
|
|
dialogOffsetTop
|
|
)
|
|
})
|
|
|
|
const borderRadius = computed(() => {
|
|
return store.brushSettings.type === BrushShape.Rect ? '0%' : '50%'
|
|
})
|
|
|
|
const gradientVisible = computed(() => {
|
|
return store.brushPreviewGradientVisible
|
|
})
|
|
|
|
const gradientBackground = computed(() => {
|
|
const size = store.brushSettings.size
|
|
const hardness = store.brushSettings.hardness
|
|
const effectiveSize = getEffectiveBrushSize(size, hardness)
|
|
const effectiveHardness = getEffectiveHardness(size, hardness, effectiveSize)
|
|
|
|
if (effectiveHardness === 1) {
|
|
return 'rgba(255, 0, 0, 0.5)'
|
|
}
|
|
|
|
const midStop = effectiveHardness * 100
|
|
const outerStop = 100
|
|
// Add an intermediate stop to approximate the squared falloff
|
|
// At 50% of the fade region, squared falloff is 0.25 (relative to max)
|
|
const fadeMidStop = midStop + (outerStop - midStop) * 0.5
|
|
|
|
return `radial-gradient(
|
|
circle,
|
|
rgba(255, 0, 0, 0.5) 0%,
|
|
rgba(255, 0, 0, 0.5) ${midStop}%,
|
|
rgba(255, 0, 0, 0.125) ${fadeMidStop}%,
|
|
rgba(255, 0, 0, 0) ${outerStop}%
|
|
)`
|
|
})
|
|
</script>
|