Sign in to follow this  
voidmen

Saving PIXI content to Image

Recommended Posts

Guys,

We recently have new requirement that we need to save the content we rendered with pixi.js to image file like PNG.

The canvas's toDataURL method worked well at first, however  we need more than that.

 

The content (a diagram) could be zoomed and the canvas only shows part of that diagram at the time we save the image.

Also, if we zoom out and try to capture the whole diagram, the output diagram seems not so crisp, or sharp. Hope I've made my self clear.

So, my question is whether their is a way to export the whole content image with desired resolution?

 

BR,

Yeling

 

Share this post


Link to post
Share on other sites
function download_sprite_as_png(renderer, sprite, fileName) {
	renderer.extract.canvas(sprite).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');
}

Assuming you have instance of PIXI.Application in variable app, you can call the function using app.renderer as the first argument.

Tested in google-chrome on linux.

HTH

Share this post


Link to post
Share on other sites
9 hours ago, voidmen said:

Guys,

We recently have new requirement that we need to save the content we rendered with pixi.js to image file like PNG.

The canvas's toDataURL method worked well at first, however  we need more than that.

 

The content (a diagram) could be zoomed and the canvas only shows part of that diagram at the time we save the image.

Also, if we zoom out and try to capture the whole diagram, the output diagram seems not so crisp, or sharp. Hope I've made my self clear.

So, my question is whether their is a way to export the whole content image with desired resolution?

 

BR,

Yeling

 

yes, you can but it might be issue with performance, maybe worth try to use worker instead. you gotta know you cant save the picture directly to your local file system

Share this post


Link to post
Share on other sites
9 hours ago, Vitalije said:

function download_sprite_as_png(renderer, sprite, fileName) {
	renderer.extract.canvas(sprite).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');
}

Assuming you have instance of PIXI.Application in variable app, you can call the function using app.renderer as the first argument.

Tested in google-chrome on linux.

HTH

you won't believe the code I wrote to achieve this... so what exactly is the "sprite" we pass here? The container/sprite we wish to save?

Share this post


Link to post
Share on other sites
14 hours ago, Jammy said:

you won't believe the code I wrote to achieve this... so what exactly is the "sprite" we pass here? The container/sprite we wish to save?

Can you elaborate a little bit more about how you did this?

I'm trying out the above solution.

Share this post


Link to post
Share on other sites
23 hours ago, Vitalije said:

function download_sprite_as_png(renderer, sprite, fileName) {
	renderer.extract.canvas(sprite).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');
}

Assuming you have instance of PIXI.Application in variable app, you can call the function using app.renderer as the first argument.

Tested in google-chrome on linux.

HTH

Thanks Vitalije, 

But the above method doesn't work. It ONLY exports content currently visible on canvas as image.

I've tried using my content root or null as argument for the sprite parameter, both the same. 

Did I get it wrong?

 

 

Share this post


Link to post
Share on other sites
Quote

 

I've tried using my content root or null as argument for the sprite parameter, both the same. 

Did I get it wrong?

 

You should pass any sprite that you wish to save as image as second parameter to the above function. The sprite can be of any size you want. I have tested with the sprite image of the RPGMaker map which was made of 40 by 41 squared tiles 48px each. Total dimension of map was 1920x1968.

If you pass null or stage it will save only what is visible on the screen. Instead you should pass sprite or container that you want to save. I am not sure but maybe the root container (app.stage) has defined mask by default to exclude all pixels out of visible part of canvas. I have also noticed that canvas.toDataURL has some limits on the size of generated url.  But if you first convert it to Blob and then use URL.createObjectURL(blob) it allows generating large images.

HTH Vitalije

Share this post


Link to post
Share on other sites
21 hours ago, Vitalije said:

You should pass any sprite that you wish to save as image as second parameter to the above function. The sprite can be of any size you want. I have tested with the sprite image of the RPGMaker map which was made of 40 by 41 squared tiles 48px each. Total dimension of map was 1920x1968.

If you pass null or stage it will save only what is visible on the screen. Instead you should pass sprite or container that you want to save. I am not sure but maybe the root container (app.stage) has defined mask by default to exclude all pixels out of visible part of canvas. I have also noticed that canvas.toDataURL has some limits on the size of generated url.  But if you first convert it to Blob and then use URL.createObjectURL(blob) it allows generating large images.

HTH Vitalije

Wow. This really worked! I'm able to save the whole diagram as image with this solution.

Two remaining issues:

The root container that holds all the diagram elements are transparent, the background color is set on the renderer. So the exported images have transparent background. which is weird.

Also, the image is a little blurry.

Not sure how I can fix them. Their seems no background color property on the Container class.

Share this post


Link to post
Share on other sites

Regarding the blurriness of the exported image I can't say anything without looking in your actual sprite. Maybe you have some blur filter, or maybe you have scaled the original sprite content with some anti-aliasing flag set. Try to export your sprite at its normal size without scaling. (PIXI.Sprite will scale itself if you set width or height) 

Share this post


Link to post
Share on other sites
1 hour ago, Vitalije said:

Regarding the blurriness of the exported image I can't say anything without looking in your actual sprite. Maybe you have some blur filter, or maybe you have scaled the original sprite content with some anti-aliasing flag set. Try to export your sprite at its normal size without scaling. (PIXI.Sprite will scale itself if you set width or height) 

Will try it out

Share this post


Link to post
Share on other sites
1 hour ago, Vitalije said:

Just add colored rectangle as the first child of your container to be exported, and later add the rest  of your sprites.

Well, the root container doesn't actually have a size. As we move the diagram elements around its bounding rectangle changes and the output image size changes.

It will be quite verbose if we want to keep a rectangle as the background when diagram shape changes.

 

My thoughts is to hack the WebGLExtract.canvas method to accept a background color parameter and set it as the internal canvas color. Sounds viable?

Share this post


Link to post
Share on other sites

Finally, I hacked the canvas methods and used double buffer to draw a container onto a canvas with background.

The below code did the trick:

 

const canvasBuffer = new PIXI.CanvasRenderTarget(width, height);
            const background = new PIXI.CanvasRenderTarget(width, height);

            if (textureBuffer) {
                // bind the buffer
                renderer.bindRenderTarget(textureBuffer);

                // set up an array of pixels
                const webglPixels = new Uint8Array(BYTES_PER_PIXEL * width * height);

                // read pixels to the array
                const gl = myRenderer.gl;

                gl.readPixels(
                    frame.x * resolution,
                    frame.y * resolution,
                    width,
                    height,
                    gl.RGBA,
                    gl.UNSIGNED_BYTE,
                    webglPixels,
                );

                // canvasBuffer.context.fillStyle = 'blue';
                background.context.fillStyle = '#' + visConfig.backgroundColor.toString(16);
                background.context.fillRect(0, 0, width, height);

                // add the pixels to the canvas
                const canvasData = canvasBuffer.context.getImageData(0, 0, width, height);

                canvasData.data.set(webglPixels);
                // canvasBuffer.context.drawImage(canvasData.data, 0, 0);
                canvasBuffer.context.putImageData(canvasData, 0, 0);
                background.context.drawImage(canvasBuffer.canvas, 0, 0);
                // pulling pixels
                if (flipY) {
                    background.context.scale(1, -1);
                    background.context.drawImage(background.canvas, 0, -height);
                }
            }

            // send the canvas back..
            return background.canvas;

 

Share this post


Link to post
Share on other sites
On 6/21/2017 at 8:30 AM, Vitalije said:

function download_sprite_as_png(renderer, sprite, fileName) {
	renderer.extract.canvas(sprite).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');
}

Assuming you have instance of PIXI.Application in variable app, you can call the function using app.renderer as the first argument.

Tested in google-chrome on linux.

HTH

This works, but crashes on larger Containers. (Uncaught TypeError: Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided.)

I will try to use a webworker and report back later. Any other advice is welcome, of course!

Share this post


Link to post
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...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.