diff --git a/frontend/playwright/data/render-wasm/get-file-fill-blend-blurs.json b/frontend/playwright/data/render-wasm/get-file-fill-blend-blurs.json new file mode 100644 index 0000000000..a38f6b8de7 --- /dev/null +++ b/frontend/playwright/data/render-wasm/get-file-fill-blend-blurs.json @@ -0,0 +1,141 @@ +{ + "~:features": { + "~#set": [ + "fdata/path-data", + "plugins/runtime", + "design-tokens/v1", + "variants/v1", + "layout/grid", + "styles/v2", + "fdata/objects-map", + "text-editor/v2", + "render-wasm/v1", + "components/v2", + "fdata/shape-data-type" + ] + }, + "~:team-id": "~ud7430f09-4f59-8049-8007-6277bb7586f6", + "~:permissions": { + "~:type": "~:membership", + "~:is-owner": true, + "~:is-admin": true, + "~:can-edit": true, + "~:can-read": true, + "~:is-logged": true + }, + "~:has-media-trimmed": false, + "~:comment-thread-seqn": 0, + "~:name": "test_color_blending", + "~:revn": 58, + "~:modified-at": "~m1770799634196", + "~:vern": 0, + "~:id": "~ub15901d7-d46d-8056-8007-8d5e34fc1f0c", + "~:is-shared": false, + "~:migrations": { + "~#ordered-set": [ + "legacy-2", + "legacy-3", + "legacy-5", + "legacy-6", + "legacy-7", + "legacy-8", + "legacy-9", + "legacy-10", + "legacy-11", + "legacy-12", + "legacy-13", + "legacy-14", + "legacy-16", + "legacy-17", + "legacy-18", + "legacy-19", + "legacy-25", + "legacy-26", + "legacy-27", + "legacy-28", + "legacy-29", + "legacy-31", + "legacy-32", + "legacy-33", + "legacy-34", + "legacy-36", + "legacy-37", + "legacy-38", + "legacy-39", + "legacy-40", + "legacy-41", + "legacy-42", + "legacy-43", + "legacy-44", + "legacy-45", + "legacy-46", + "legacy-47", + "legacy-48", + "legacy-49", + "legacy-50", + "legacy-51", + "legacy-52", + "legacy-53", + "legacy-54", + "legacy-55", + "legacy-56", + "legacy-57", + "legacy-59", + "legacy-62", + "legacy-65", + "legacy-66", + "legacy-67", + "0001-remove-tokens-from-groups", + "0002-normalize-bool-content-v2", + "0002-clean-shape-interactions", + "0003-fix-root-shape", + "0003-convert-path-content-v2", + "0005-deprecate-image-type", + "0006-fix-old-texts-fills", + "0008-fix-library-colors-v4", + "0009-clean-library-colors", + "0009-add-partial-text-touched-flags", + "0010-fix-swap-slots-pointing-non-existent-shapes", + "0011-fix-invalid-text-touched-flags", + "0012-fix-position-data", + "0013-fix-component-path", + "0013-clear-invalid-strokes-and-fills", + "0014-fix-tokens-lib-duplicate-ids", + "0014-clear-components-nil-objects", + "0015-fix-text-attrs-blank-strings", + "0015-clean-shadow-color", + "0016-copy-fills-from-position-data-to-text-node" + ] + }, + "~:version": 67, + "~:project-id": "~ud7430f09-4f59-8049-8007-6277bb765abd", + "~:created-at": "~m1770741329904", + "~:backend": "legacy-db", + "~:data": { + "~:pages": [ + "~ub15901d7-d46d-8056-8007-8d5e34fc1f0d" + ], + "~:pages-index": { + "~ub15901d7-d46d-8056-8007-8d5e34fc1f0d": { + "~:objects": { + "~#penpot/objects-map/v2": { + "~u00000000-0000-0000-0000-000000000000": "[\"~#shape\",[\"^ \",\"~:y\",0,\"~:hide-fill-on-export\",false,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:name\",\"Root Frame\",\"~:width\",0.01,\"~:type\",\"~:frame\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",0.0,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.0]],[\"^:\",[\"^ \",\"~:x\",0.01,\"~:y\",0.01]],[\"^:\",[\"^ \",\"~:x\",0.0,\"~:y\",0.01]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^3\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",0,\"~:proportion\",1.0,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",0,\"~:y\",0,\"^6\",0.01,\"~:height\",0.01,\"~:x1\",0,\"~:y1\",0,\"~:x2\",0.01,\"~:y2\",0.01]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#FFFFFF\",\"~:fill-opacity\",1]],\"~:flip-x\",null,\"^H\",0.01,\"~:flip-y\",null,\"~:shapes\",[\"~u3b7d4c1f-3b79-80e5-8007-8d5e38c5a297\",\"~udb80df91-a3a3-803b-8007-8e379b5fd50f\",\"~udb80df91-a3a3-803b-8007-8e38034ff7c8\",\"~udb80df91-a3a3-803b-8007-8e37a71c9d28\",\"~udb80df91-a3a3-803b-8007-8e384d8c53b9\",\"~udb80df91-a3a3-803b-8007-8e37c09b4084\"]]]", + "~udb80df91-a3a3-803b-8007-8e384d8c53b9": "[\"~#shape\",[\"^ \",\"~:y\",450.99999806284904,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Ellipse\",\"~:width\",300.0000065565109,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1021.0000203847885,\"~:y\",450.99999806284904]],[\"^<\",[\"^ \",\"~:x\",1321.0000269412994,\"~:y\",450.99999806284904]],[\"^<\",[\"^ \",\"~:x\",1321.0000269412994,\"~:y\",751.0000142753124]],[\"^<\",[\"^ \",\"~:x\",1021.0000203847885,\"~:y\",751.0000142753124]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:blur\",[\"^ \",\"~:id\",\"~udb80df91-a3a3-803b-8007-8e37b7ddd15c\",\"^9\",\"~:layer-blur\",\"~:value\",7,\"~:hidden\",false],\"^@\",\"~udb80df91-a3a3-803b-8007-8e384d8c53b9\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[[\"^ \",\"~:stroke-style\",\"~:solid\",\"~:stroke-alignment\",\"~:inner\",\"~:stroke-width\",20,\"~:stroke-color\",\"#333fbd\",\"~:stroke-opacity\",1]],\"~:x\",1021.0000203847885,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1021.0000203847885,\"~:y\",450.99999806284904,\"^8\",300.0000065565109,\"~:height\",300.0000162124634,\"~:x1\",1021.0000203847885,\"~:y1\",450.99999806284904,\"~:x2\",1321.0000269412994,\"~:y2\",751.0000142753124]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1],[\"^ \",\"^W\",\"#ff0000\",\"^X\",1]],\"~:flip-x\",null,\"^Q\",300.0000162124634,\"~:flip-y\",null]]", + "~udb80df91-a3a3-803b-8007-8e379b5fd50f": "[\"~#shape\",[\"^ \",\"~:y\",82.00000368146124,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",300.0000100135803,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",679.0000200882939,\"~:y\",82.00000368146122]],[\"^<\",[\"^ \",\"~:x\",979.0000301018742,\"~:y\",82.00000368146122]],[\"^<\",[\"^ \",\"~:x\",979.0000301018742,\"~:y\",382.0000008204383]],[\"^<\",[\"^ \",\"~:x\",679.0000200882939,\"~:y\",382.0000008204383]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:r1\",0,\"~:id\",\"~udb80df91-a3a3-803b-8007-8e379b5fd50f\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",679.0000200882939,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",679.0000200882939,\"~:y\",82.00000368146124,\"^8\",300.0000100135803,\"~:height\",299.99999713897705,\"~:x1\",679.0000200882939,\"~:y1\",82.00000368146124,\"~:x2\",979.0000301018742,\"~:y2\",382.0000008204383]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1],[\"^ \",\"^P\",\"#ff0000\",\"^Q\",1]],\"~:flip-x\",null,\"^J\",299.99999713897705,\"~:flip-y\",null]]", + "~u3b7d4c1f-3b79-80e5-8007-8d5e38c5a297": "[\"~#shape\",[\"^ \",\"~:y\",81.9999960520667,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",300.0000100135803,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",337.0000200882939,\"~:y\",81.99999605206669]],[\"^<\",[\"^ \",\"~:x\",637.0000301018742,\"~:y\",81.99999605206669]],[\"^<\",[\"^ \",\"~:x\",637.0000301018742,\"~:y\",381.99999319104376]],[\"^<\",[\"^ \",\"~:x\",337.0000200882939,\"~:y\",381.99999319104376]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:blur\",[\"^ \",\"~:id\",\"~u432cbb09-2ee7-80bf-8007-8d660b2f52ad\",\"^9\",\"~:layer-blur\",\"~:value\",7,\"~:hidden\",false],\"~:r1\",0,\"^B\",\"~u3b7d4c1f-3b79-80e5-8007-8d5e38c5a297\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",337.0000200882939,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",337.0000200882939,\"~:y\",81.9999960520667,\"^8\",300.0000100135803,\"~:height\",299.99999713897705,\"~:x1\",337.0000200882939,\"~:y1\",81.9999960520667,\"~:x2\",637.0000301018742,\"~:y2\",381.99999319104376]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1],[\"^ \",\"^T\",\"#ff0000\",\"^U\",1]],\"~:flip-x\",null,\"^N\",299.99999713897705,\"~:flip-y\",null]]", + "~udb80df91-a3a3-803b-8007-8e37a71c9d28": "[\"~#shape\",[\"^ \",\"~:y\",450.99999806284904,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Ellipse\",\"~:width\",300.0000065565109,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",337.0000203847885,\"~:y\",450.99999806284904]],[\"^<\",[\"^ \",\"~:x\",637.0000269412994,\"~:y\",450.99999806284904]],[\"^<\",[\"^ \",\"~:x\",637.0000269412994,\"~:y\",751.0000142753124]],[\"^<\",[\"^ \",\"~:x\",337.0000203847885,\"~:y\",751.0000142753124]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:blur\",[\"^ \",\"~:id\",\"~udb80df91-a3a3-803b-8007-8e37b7ddd15c\",\"^9\",\"~:layer-blur\",\"~:value\",7,\"~:hidden\",false],\"^@\",\"~udb80df91-a3a3-803b-8007-8e37a71c9d28\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",337.0000203847885,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",337.0000203847885,\"~:y\",450.99999806284904,\"^8\",300.0000065565109,\"~:height\",300.0000162124634,\"~:x1\",337.0000203847885,\"~:y1\",450.99999806284904,\"~:x2\",637.0000269412994,\"~:y2\",751.0000142753124]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1],[\"^ \",\"^P\",\"#ff0000\",\"^Q\",1]],\"~:flip-x\",null,\"^J\",300.0000162124634,\"~:flip-y\",null]]", + "~udb80df91-a3a3-803b-8007-8e37c09b4084": "[\"~#shape\",[\"^ \",\"~:y\",450.99999806284904,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Ellipse\",\"~:width\",300.0000065565109,\"~:type\",\"~:circle\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",679.0000203847885,\"~:y\",450.99999806284904]],[\"^<\",[\"^ \",\"~:x\",979.0000269412994,\"~:y\",450.99999806284904]],[\"^<\",[\"^ \",\"~:x\",979.0000269412994,\"~:y\",751.0000142753124]],[\"^<\",[\"^ \",\"~:x\",679.0000203847885,\"~:y\",751.0000142753124]]],\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:id\",\"~udb80df91-a3a3-803b-8007-8e37c09b4084\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[],\"~:x\",679.0000203847885,\"~:proportion\",1,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",679.0000203847885,\"~:y\",450.99999806284904,\"^8\",300.0000065565109,\"~:height\",300.0000162124634,\"~:x1\",679.0000203847885,\"~:y1\",450.99999806284904,\"~:x2\",979.0000269412994,\"~:y2\",751.0000142753124]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1],[\"^ \",\"^L\",\"#ff0000\",\"^M\",1]],\"~:flip-x\",null,\"^F\",300.0000162124634,\"~:flip-y\",null]]", + "~udb80df91-a3a3-803b-8007-8e38034ff7c8": "[\"~#shape\",[\"^ \",\"~:y\",82.00000368146124,\"~:transform\",[\"~#matrix\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:rotation\",0,\"~:grow-type\",\"~:fixed\",\"~:hide-in-viewer\",false,\"~:name\",\"Rectangle\",\"~:width\",300.0000100135803,\"~:type\",\"~:rect\",\"~:points\",[[\"~#point\",[\"^ \",\"~:x\",1021.0000200882939,\"~:y\",82.00000368146122]],[\"^<\",[\"^ \",\"~:x\",1321.0000301018742,\"~:y\",82.00000368146122]],[\"^<\",[\"^ \",\"~:x\",1321.0000301018742,\"~:y\",382.0000008204383]],[\"^<\",[\"^ \",\"~:x\",1021.0000200882939,\"~:y\",382.0000008204383]]],\"~:r2\",0,\"~:proportion-lock\",false,\"~:transform-inverse\",[\"^2\",[\"^ \",\"~:a\",1.0,\"~:b\",0.0,\"~:c\",0.0,\"~:d\",1.0,\"~:e\",0.0,\"~:f\",0.0]],\"~:r3\",0,\"~:blur\",[\"^ \",\"~:id\",\"~udb80df91-a3a3-803b-8007-8e380b12ac2a\",\"^9\",\"~:layer-blur\",\"~:value\",7,\"~:hidden\",false],\"~:r1\",0,\"^B\",\"~udb80df91-a3a3-803b-8007-8e38034ff7c8\",\"~:parent-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:frame-id\",\"~u00000000-0000-0000-0000-000000000000\",\"~:strokes\",[[\"^ \",\"~:stroke-style\",\"~:solid\",\"~:stroke-alignment\",\"~:inner\",\"~:stroke-width\",10,\"~:stroke-color\",\"#333fbd\",\"~:stroke-opacity\",1]],\"~:x\",1021.0000200882939,\"~:proportion\",1,\"~:r4\",0,\"~:selrect\",[\"~#rect\",[\"^ \",\"~:x\",1021.0000200882939,\"~:y\",82.00000368146124,\"^8\",300.0000100135803,\"~:height\",299.99999713897705,\"~:x1\",1021.0000200882939,\"~:y1\",82.00000368146124,\"~:x2\",1321.0000301018742,\"~:y2\",382.0000008204383]],\"~:fills\",[[\"^ \",\"~:fill-color\",\"#B1B2B5\",\"~:fill-opacity\",1],[\"^ \",\"^[\",\"#ff0000\",\"^10\",1]],\"~:flip-x\",null,\"^U\",299.99999713897705,\"~:flip-y\",null]]" + } + }, + "~:id": "~ub15901d7-d46d-8056-8007-8d5e34fc1f0d", + "~:name": "Page 1" + } + }, + "~:id": "~ub15901d7-d46d-8056-8007-8d5e34fc1f0c", + "~:options": { + "~:components-v2": true, + "~:base-font-size": "16px" + } + } +} \ No newline at end of file diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js index 53c57b290c..48c7425429 100644 --- a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js +++ b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js @@ -340,3 +340,19 @@ test("Renders a file with solid, dotted, dashed and mixed stroke styles", async await expect(workspace.canvas).toHaveScreenshot(); }); + +test("Renders shapes with multiple fills and blur", async ({ + page, +}) => { + const workspace = new WasmWorkspacePage(page); + await workspace.setupEmptyFile(); + await workspace.mockGetFile("render-wasm/get-file-fill-blend-blurs.json"); + + await workspace.goToWorkspace({ + id: "b15901d7-d46d-8056-8007-8d5e34fc1f0c", + pageId: "b15901d7-d46d-8056-8007-8d5e34fc1f0d", + }); + await workspace.waitForFirstRenderWithoutUI(); + + await expect(workspace.canvas).toHaveScreenshot(); +}); diff --git a/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-shapes-with-multiple-fills-and-blur-1.png b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-shapes-with-multiple-fills-and-blur-1.png new file mode 100644 index 0000000000..5e3531e8a6 Binary files /dev/null and b/frontend/playwright/ui/render-wasm-specs/shapes.spec.js-snapshots/Renders-shapes-with-multiple-fills-and-blur-1.png differ diff --git a/render-wasm/src/render.rs b/render-wasm/src/render.rs index b6c998644d..57b2a3723e 100644 --- a/render-wasm/src/render.rs +++ b/render-wasm/src/render.rs @@ -698,9 +698,7 @@ impl RenderState { canvas.translate(translation); }); - for fill in shape.fills().rev() { - fills::render(self, shape, fill, antialias, SurfaceId::Current); - } + fills::render(self, shape, &shape.fills, antialias, SurfaceId::Current); for stroke in shape.visible_strokes().rev() { strokes::render( @@ -1015,14 +1013,10 @@ impl RenderState { { if let Some(fills_to_render) = self.nested_fills.last() { let fills_to_render = fills_to_render.clone(); - for fill in fills_to_render.iter() { - fills::render(self, shape, fill, antialias, fills_surface_id); - } + fills::render(self, shape, &fills_to_render, antialias, fills_surface_id); } } else { - for fill in shape.fills().rev() { - fills::render(self, shape, fill, antialias, fills_surface_id); - } + fills::render(self, shape, &shape.fills, antialias, fills_surface_id); } // Skip stroke rendering for clipped frames - they are drawn in render_shape_exit diff --git a/render-wasm/src/render/fills.rs b/render-wasm/src/render/fills.rs index 1d8ad98084..0875fdd649 100644 --- a/render-wasm/src/render/fills.rs +++ b/render-wasm/src/render/fills.rs @@ -2,7 +2,7 @@ use skia_safe::{self as skia, Paint, RRect}; use super::{filters, RenderState, SurfaceId}; use crate::render::get_source_rect; -use crate::shapes::{Fill, Frame, ImageFill, Rect, Shape, Type}; +use crate::shapes::{merge_fills, Fill, Frame, ImageFill, Rect, Shape, Type}; fn draw_image_fill( render_state: &mut RenderState, @@ -92,6 +92,76 @@ fn draw_image_fill( * This SHOULD be the only public function in this module. */ pub fn render( + render_state: &mut RenderState, + shape: &Shape, + fills: &[Fill], + antialias: bool, + surface_id: SurfaceId, +) { + if fills.is_empty() { + return; + } + + // Image fills use draw_image_fill which needs render_state for GPU images + // and sampling options that get_fill_shader (used by merge_fills) lacks. + let has_image_fills = fills.iter().any(|f| matches!(f, Fill::Image(_))); + if has_image_fills { + for fill in fills.iter().rev() { + render_single_fill(render_state, shape, fill, antialias, surface_id); + } + return; + } + + let mut paint = merge_fills(fills, shape.selrect); + paint.set_anti_alias(antialias); + + if let Some(image_filter) = shape.image_filter(1.) { + let bounds = image_filter.compute_fast_bounds(shape.selrect); + if filters::render_with_filter_surface( + render_state, + bounds, + surface_id, + |state, temp_surface| { + let mut filtered_paint = paint.clone(); + filtered_paint.set_image_filter(image_filter.clone()); + draw_fill_to_surface(state, shape, temp_surface, &filtered_paint); + }, + ) { + return; + } else { + paint.set_image_filter(image_filter); + } + } + + draw_fill_to_surface(render_state, shape, surface_id, &paint); +} + +/// Draws a single paint (with a merged shader) to the appropriate surface +/// based on the shape type. +fn draw_fill_to_surface( + render_state: &mut RenderState, + shape: &Shape, + surface_id: SurfaceId, + paint: &Paint, +) { + match &shape.shape_type { + Type::Rect(_) | Type::Frame(_) => { + render_state.surfaces.draw_rect_to(surface_id, shape, paint); + } + Type::Circle => { + render_state + .surfaces + .draw_circle_to(surface_id, shape, paint); + } + Type::Path(_) | Type::Bool(_) => { + render_state.surfaces.draw_path_to(surface_id, shape, paint); + } + Type::Group(_) => {} + _ => unreachable!("This shape should not have fills"), + } +} + +fn render_single_fill( render_state: &mut RenderState, shape: &Shape, fill: &Fill, @@ -108,7 +178,14 @@ pub fn render( |state, temp_surface| { let mut filtered_paint = paint.clone(); filtered_paint.set_image_filter(image_filter.clone()); - draw_fill_to_surface(state, shape, fill, antialias, temp_surface, &filtered_paint); + draw_single_fill_to_surface( + state, + shape, + fill, + antialias, + temp_surface, + &filtered_paint, + ); }, ) { return; @@ -117,10 +194,10 @@ pub fn render( } } - draw_fill_to_surface(render_state, shape, fill, antialias, surface_id, &paint); + draw_single_fill_to_surface(render_state, shape, fill, antialias, surface_id, &paint); } -fn draw_fill_to_surface( +fn draw_single_fill_to_surface( render_state: &mut RenderState, shape: &Shape, fill: &Fill, @@ -153,8 +230,6 @@ fn draw_fill_to_surface( (_, Type::Group(_)) => { // Groups can have fills but they propagate them to their children } - (_, _) => { - unreachable!("This shape should not have fills") - } + _ => unreachable!("This shape should not have fills"), } } diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 358e356043..2eed111226 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -620,6 +620,7 @@ impl Shape { (added, removed) } + #[allow(dead_code)] pub fn fills(&self) -> std::slice::Iter<'_, Fill> { self.fills.iter() } diff --git a/render-wasm/src/shapes/fills.rs b/render-wasm/src/shapes/fills.rs index 6a772527eb..443669c121 100644 --- a/render-wasm/src/shapes/fills.rs +++ b/render-wasm/src/shapes/fills.rs @@ -241,10 +241,14 @@ pub fn merge_fills(fills: &[Fill], bounding_box: Rect) -> skia::Paint { if let Some(shader) = shader { combined_shader = match combined_shader { + // Use SrcOver and treat the newly encountered fill as the source (top), + // overlaying it over the previously composed shader (destination/bottom). + // This avoids edge bleed from underlying fills when anti-aliasing causes + // fractional coverage at shape boundaries. Some(existing_shader) => Some(skia::shaders::blend( - skia::Blender::mode(skia::BlendMode::DstOver), - existing_shader, + skia::Blender::mode(skia::BlendMode::SrcOver), shader, + existing_shader, )), None => Some(shader), };