samizdatco / skia-canvas

A GPU-accelerated 2D graphics environment for Node.js
MIT License
1.67k stars 63 forks source link

Svg save doesn't work with drawCanvas. #106

Closed AliFlux closed 2 years ago

AliFlux commented 2 years ago

Hi.

Thanks for making this amazing library. I came across this weird problem where svg was fully blank when drawCanvas was used. Any idea what could be the problem?

let src = new Canvas(300, 300);
let srcCtx = src.getContext("2d");

srcCtx.lineWidth = 4
srcCtx.beginPath()
srcCtx.moveTo(50,50)
srcCtx.lineTo(120,120)
srcCtx.moveTo(50,120)
srcCtx.lineTo(120,50)
srcCtx.stroke()

srcCtx.font = "30px Arial";
srcCtx.fillText("Cross", 150, 100); 

let img = await loadImage('lime-cat.jpg')
srcCtx.drawImage(img, 50, 170, 70, 70)

await src.saveAs("src.png") // works good
await src.saveAs("src.pdf") // works good
await src.saveAs("src.svg") // works good

// drawing over another canvas

let dst = new Canvas(300, 300),
dstCtx = dst.getContext("2d");
dstCtx.drawCanvas(src, 0, 0)

await dst.saveAs("dst.png") // works good
await dst.saveAs("dst.pdf") // works good
await dst.saveAs("dst.svg") // empty result here

This is supposed to be the result:

dst

AliFlux commented 2 years ago

P.S. It works perfectly fine if I replace drawCanvas with drawImage. But the svg is rasterized

samizdatco commented 2 years ago

This seems to be a limitation of Skia's current SVG support (which they still label as ‘experimental’ so it’s not entirely surprising). The issue is that the method we’re using to draw the canvas has this restriction:

If paint is non-null, then the picture is always drawn into a temporary layer before actually landing on the canvas. Note that drawing into a layer can also change its appearance if there are any non-associative blendModes inside any of the pictures elements.

The ‘paint’ in question is necessary for applying:

The bitmap and PDF renderers don’t have a problem with this, but for SVG output it seems that the entire layer gets omitted if the paint argument is even included.

It’s only a partial fix, but I can change the drawCanvas method to only apply the paint when the alpha/blend/filter/shadow settings are using non-default values. That should make SVG exports work so long as you don’t change the opacity, blend mode, or filters on the destination canvas.

samizdatco commented 2 years ago

This modification was added in the 1.0 release.