voidmen Posted June 21, 2017 Share Posted June 21, 2017 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 Quote Link to comment Share on other sites More sharing options...
Vitalije Posted June 21, 2017 Share Posted June 21, 2017 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 themoonrat, Wilco93 and ivan.popelyshev 2 1 Quote Link to comment Share on other sites More sharing options...
tywang2006 Posted June 21, 2017 Share Posted June 21, 2017 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 Quote Link to comment Share on other sites More sharing options...
Jammy Posted June 21, 2017 Share Posted June 21, 2017 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? Quote Link to comment Share on other sites More sharing options...
voidmen Posted June 22, 2017 Author Share Posted June 22, 2017 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. Quote Link to comment Share on other sites More sharing options...
voidmen Posted June 22, 2017 Author Share Posted June 22, 2017 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? Quote Link to comment Share on other sites More sharing options...
Vitalije Posted June 22, 2017 Share Posted June 22, 2017 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 Quote Link to comment Share on other sites More sharing options...
voidmen Posted June 23, 2017 Author Share Posted June 23, 2017 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. Quote Link to comment Share on other sites More sharing options...
Vitalije Posted June 23, 2017 Share Posted June 23, 2017 Just add colored rectangle as the first child of your container to be exported, and later add the rest of your sprites. Quote Link to comment Share on other sites More sharing options...
Vitalije Posted June 23, 2017 Share Posted June 23, 2017 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) Quote Link to comment Share on other sites More sharing options...
voidmen Posted June 23, 2017 Author Share Posted June 23, 2017 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 Quote Link to comment Share on other sites More sharing options...
voidmen Posted June 23, 2017 Author Share Posted June 23, 2017 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? Quote Link to comment Share on other sites More sharing options...
voidmen Posted June 24, 2017 Author Share Posted June 24, 2017 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; ivan.popelyshev 1 Quote Link to comment Share on other sites More sharing options...
ivan.popelyshev Posted June 24, 2017 Share Posted June 24, 2017 Not the best performance, but you've got it Quote Link to comment Share on other sites More sharing options...
voidmen Posted June 26, 2017 Author Share Posted June 26, 2017 On 2017-6-24 at 4:52 PM, ivan.popelyshev said: Not the best performance, but you've got it Can you give some advice? Quote Link to comment Share on other sites More sharing options...
Wilco93 Posted September 6, 2018 Share Posted September 6, 2018 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! Quote Link to comment Share on other sites More sharing options...
jdnichollsc Posted October 15, 2023 Share Posted October 15, 2023 On 6/25/2017 at 7:26 PM, voidmen said: Can you give some advice? Maybe this other alternative is better in terms of performance: import { base64StringToBlob } from 'blob-util'; const SHARE_EXTENSION = 'png'; const SHARE_FORMAT = `image/${SHARE_EXTENSION}`; const saveImg = (title, imgBlob) => { const createEl = document.createElement('a'); const fileUrl = URL.createObjectURL(imgBlob); createEl.href = fileUrl; createEl.download = title; createEl.click(); createEl.remove(); URL.revokeObjectURL(fileUrl); }; const onDownload = async () => { const base64 = await app.renderer.extract.base64(app.stage, SHARE_FORMAT); const blob = base64StringToBlob( base64.replace(`data:${SHARE_FORMAT};base64,`, ''), SHARE_FORMAT ); saveImg(`image.${SHARE_EXTENSION}`, blob); }; Happy Coding! <3 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
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.