Jump to content

how to save a scene as an image


billconan
 Share

Recommended Posts

I'm creating an image editor using pixi. When editing is done, I need to export the current scene to an image and download.

right now, I'm doing it using http://pixijs.download/release/docs/PIXI.Extract_.html

and this is my current code:

    downloadAsPng(fileName) {
      this.app.renderer.plugins.extract.canvas(this.container).toBlob(function (b) {
        var a = document.createElement("a");
        document.body.append(a);
        a.download = fileName;
        a.href = URL.createObjectURL(b);
        a.click();
        a.remove();
      }, "image/png");
    },

Though this can download an image, the rendering is off. My container has some transformation applied, such as scaling and translation (due to zooming and panning). But the downloaded image doesn't seem to respect the transformation. the scale seems to be changed to 100%, and the translation is also reset to (0,0), but the image boundary is unchanged, so the resulting image is cropped. 

For example, I have an original image, which has an resolution of 1920x1080. I create a sprite in PIXI using the image as a texture. I change my container's scale to 0.5. So the image, as it is displayed, should be 960x540. Now if I download it using the above method. The resulting image is 960x540, however, the sprite will be scaled back to 100%, so what I see is an enlarged, but cropped image. 

 

The first screenshot has an image (also a screenshot) being edited. I scaled it so that the image can be fit into the editor's workspace.

 

Then I download the image using the above function, I expect to see the entire image, only smaller. But what I see is the unscaled image, but cropped by a scaled boundary. 

Screenshot from 2020-11-23 10-04-37.png

Screenshot from 2020-11-23 10-05-11.png

Link to comment
Share on other sites

it changes transform of container to IDENTITY, temporarily. 

There are several ways to fix that:

1. just put it in another container

2. Bypass extract , do part of job for it: 

a) take `container.getBounds()`

b) create renderTexture of that size,

c) call `renderer.render` with transform that is moved on offset of those bounds and skipTransform flag , thus you'll have image in your renderTexture

d) call extract of that renderTexture

e) destroy renderTexture

 

Link to comment
Share on other sites

Thank you, but I gave up. My container has scaling and translation. I found it's very difficult to get it transformed properly for extraction.

I ended up creating a new container and duplicating all sprites and put them into the new container to generate an image.

While this works for now, I can't get masking work.

 

in my application, I use a mask to implement cropping

in the example, I have a mask that is the same size as the image (overlap with the image).  I also draw a red box on top of an image,

in the generated image, the red box is cropped by the boundary of the image.

I would expect the red box is not cropped. As the mask is only applied on the image. (notice that the generated image has a transparent area on the right side, which should have enough space for the redbox. removing the mask, the redbox will be intact )

      let container = new PIXI.Container();

      let sprite = new PIXI.Sprite(self.app.loader.resources["image"].texture);

      container.addChild(sprite);
      container.sortableChildren = true

      sprite.mask = new PIXI.Graphics();

      sprite.mask.clear();
      sprite.mask.beginFill(0xffffff);
      sprite.mask.drawRect(
        upperLeftCorner.x,
        upperLeftCorner.y,
        lowerRightCorner.x - upperLeftCorner.x,
        lowerRightCorner.y - upperLeftCorner.y
      );
      sprite.mask.endFill();
      sprite.zIndex = 0

      if (self.document.annotations.length > 0) {
        console.log("load old annotations");
        self.document.annotations.forEach((a) => {
          let graphics = new PIXI.Graphics();
          graphics.lineStyle(2, 0xff0000, 1);
          graphics.drawRect(a.x, a.y, a.width, a.height);
          graphics.zIndex = 32
          container.addChild(graphics);
        });
      }

      this.app.renderer.plugins.extract.canvas(container).toBlob(function (b) {
        var a = document.createElement("a");
        document.body.append(a);
        a.download = fileName;
        a.href = URL.createObjectURL(b);
        a.click();
        a.remove();
      }, "image/png");
     

 

 

Screenshot from 2020-11-24 14-52-25.png

test (7).png

Edited by billconan
Link to comment
Share on other sites

You didnt add mask to stage, so there's no guarantee about its global coordinate system.

Mask position in stage tree is important - you can use graphics in one container, but mask in another. Imagine that you render two elements, then instead you render one masked by another - its the same.

I dont know where to add this mask in your case.

I can provide more help if you give me minimal demo with both of your problems.

Link to comment
Share on other sites

Thank you very much for helping me. I have recreated my problems with a simpler example.

a little bit background about what I'm doing,

I'm working on a simple image editor. The main purpose of this editor is letting people make quick edit/annotations on a screenshot.

For example, they can quickly crop a screenshot, or highlight an area with boxes.

 

in the example, there is a canvas, with an image loaded. there are 4 knobs at the 4 corners of the image. you can use them to crop the image.

you can also use the mouse wheel to zoom the image or use the middle button to pan.  

after clicking the draw button, you can then draw boxes by dragging. 

 

finally, you can save the edit result by the save button. The naive implementation will generate something as shown in the following screenshot. as you can see, it looks different from the canvas.

 

What I want to achieve is,

1. the saved image should look the same as what's in the canvas,

2. the saved image shouldn't be scaled. even what's in the canvas is scaled to fit the canvas.

 

Another thing I want to achieve that is not related to this thread is an infinite stage/container.

I want people to be able to draw outside the stage / container. Because I want them to be able to create annotations and comments outside the initial image, not just on the image. 

It's hard to predefine the size of the container or stage. 

 

This would be easy to achieve with vanilla webgl.  as webgl only has viewport / camera, it doesn't put a limit on the boundary of the scene. When saving, I only need to calculate the world boundary and then create a texture map that is large enough to include everything. And I just need to draw on that texturemap and save.

However, I found PIXI seems to create a boundary for the stage or container. Things outside are cropped by the boundary. I don't know how to resolve that. 

 

 

Screenshot from 2020-11-25 10-01-47.png

example.zip

Edited by billconan
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...