ShukantPal / pixi-essentials

The best goodies for performant, enterprise-level applications built on PixiJS
https://api.pixijs.io
MIT License
275 stars 36 forks source link

[SVG] Cannot export the SVGScene into image #99

Open rnike opened 10 months ago

rnike commented 10 months ago

We are trying to export the canvas to png using add.renderer.generateTexture and app.renderer.extract, but the svg object isn't showing in the image.

After digging into the source code and found out this line is causing the issue

https://github.com/ShukantPal/pixi-essentials/blob/0c61c083b7e1bd5206b015699c8d13a0c1e948b5/packages/svg/src/SVGScene.ts#L177

By removing this line, the svg object now can export as expected

- this._cull.cull(renderer.renderTexture.sourceFrame, true);

Although the app works fine without this line, we are not sure wether if there's side effect or not.

ShukantPal commented 10 months ago

Hi @rnike, this is a performance optimization that culls out elements that are off-screen. If you try to render the SVG into a texture without updating the culling, it will not render the previously culled items.

rnike commented 10 months ago

@ShukantPal Thanks for the reply, I found out there is a transform in renderer while using the add.renderer.generateTexture method, this cause the cull be culling with the incorrect area.

Screen Shot 2023-10-24 at 1 07 08 PM https://github.com/pixijs/pixijs/blob/c04c09c50874fef9296555af23e25fe58cc808a9/packages/core/src/renderTexture/GenerateTextureSystem.ts#L83C1-L96C1

Shall we also handle the renderer.projection.transform when culling the object?

For example adjust the xy of the cull rectangle with the transform from renderer, the following workaround fix our case.

        this.tempSourceFrame.copyFrom(renderer.renderTexture.sourceFrame);

        if(renderer.projection.transform){
            this.tempSourceFrame.x -= renderer.projection.transform.tx
            this.tempSourceFrame.y -= renderer.projection.transform.ty
        }

        // Cull the SVG scene graph
        this._cull.cull(this.tempSourceFrame, true);
ShukantPal commented 10 months ago

The transform is to map (x, y) to (0, 0) in the texture (aka the top left of the texture). But the culling should be done in world space, without discounting the projection transform.

What may be happening is that skipUpdateTransform is being set (since I assume the Transformer is mounted in your scene graph somewhere). That will make the children of the Transformer not update their world bounds. I suggest removing the transformer from your scene, rendering it into a texture, and then re-adding it back.

rnike commented 10 months ago

Unfortunately, removing Transformer isn't solving our problem.

What we are trying to do is to export the specific area of the stage to image.

       // this is what pixi do while calling `add.renderer.generateTexture`

        const texture = RenderTexture.create({
          width: region.width,
          height: region.height,
        });

        app.renderer.render(app.stage, {
          renderTexture: texture,
          transform: new Matrix().translate(-region.x, -region.y),
          skipUpdateTransform: false,
          blit: true,
        });

output Screen Shot 2023-10-24 at 2 09 12 PM

If increase the size of the texture and don't set the transform, the svg will display but the output image size will be greater than expected

        const texture = RenderTexture.create({
          width: region.width + region.x, // add region.x to width
          height: region.height + region.y, // add region.y to height
        });

        app.renderer.render(app.stage, {
          renderTexture: texture,
          // transform: new Matrix().translate(-region.x, -region.y),  don't set the transform
          skipUpdateTransform: false,
          blit: true,
        });

output Screen Shot 2023-10-24 at 2 03 51 PM

If don't increase the size of texture and don't set the transform for render, the svg graphics will not be draw in the image because of the incorrect export area and culling.

        const texture = RenderTexture.create({
          width: region.width,
          height: region.height,
        });

        app.renderer.render(app.stage, {
          renderTexture: texture,
          // transform: new Matrix().translate(-region.x, -region.y), don't set the transform
          skipUpdateTransform: false,
          blit: true,
        });

output Screen Shot 2023-10-24 at 2 16 46 PM