GPU accelerated maskeditor rendering (#6767)

## 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)
This commit is contained in:
Tristan Sommer
2025-11-22 15:07:16 +01:00
committed by GitHub
parent 1dbb3fc1b9
commit 4adcf09cca
24 changed files with 2945 additions and 409 deletions

View File

@@ -18,6 +18,19 @@ vi.mock('@/stores/maskEditorStore', () => ({
useMaskEditorStore: vi.fn(() => mockStore)
}))
// Mock ImageBitmap for test environment
if (typeof globalThis.ImageBitmap === 'undefined') {
globalThis.ImageBitmap = class ImageBitmap {
width: number
height: number
constructor(width = 100, height = 100) {
this.width = width
this.height = height
}
close() {}
} as any
}
describe('useCanvasHistory', () => {
beforeEach(() => {
vi.clearAllMocks()
@@ -42,12 +55,16 @@ describe('useCanvasHistory', () => {
mockMaskCtx = {
getImageData: vi.fn(() => createMockImageData()),
putImageData: vi.fn()
putImageData: vi.fn(),
clearRect: vi.fn(),
drawImage: vi.fn()
}
mockRgbCtx = {
getImageData: vi.fn(() => createMockImageData()),
putImageData: vi.fn()
putImageData: vi.fn(),
clearRect: vi.fn(),
drawImage: vi.fn()
}
mockMaskCanvas = {