Go ahead and file it as a bug report. We also need your config.lua and build.settings.
Rob
Go ahead and file it as a bug report. We also need your config.lua and build.settings.
Rob
The full code has been submitted as a bug
(Case 43011) Display.save doesn’t save anything not visible on screen with fullResolution=true
The topic should probably be modified to say:
(Case 43011) Display.save doesn’t save anything not located within the visible view on screen with isfullResolution=true
The coded line that doesn’t work is:
display.save( G_grid, { filename=“entireGroup.png”, baseDir=system.DocumentsDirectory, isFullResolution=true, backgroundColor={0,0,0,0} } )
…where “G_grid” is a display group of objects.
This is my idea. (-:
display.setDefault("anchorX", 0) display.setDefault("anchorY", 0) local bg = display.newRect(0, 0, display.actualContentWidth, display.actualContentHeight) bg:setFillColor(1, 0, 0, 1) local myObject1 = display.newRect( 0, 0, 100, 150 ) myObject1:setFillColor(0, 0, 1, 1) local ggg = display.newGroup() local g2 = display.newGroup() local gr = display.newRect(g2, 0, 0, 1, 1) gr:setFillColor(0, 0, 0, 0) local myObject2 = display.newCircle( g2, 1000, 1000, 30 ) myObject2:setFillColor(0, 0, 1, 1) g2.x, g2.y = -(g2.width - myObject2.width), -(g2.height - myObject2.height) local group = display.newGroup() group:insert( myObject1 ) group:insert( ggg ) local function cc() display.save( g2, { filename="g2.png", baseDir=system.DocumentsDirectory, isFullResolution=true, backgroundColor={1,1,0,0} } ) local copy = display.newImage(ggg, "g2.png", system.DocumentsDirectory) g2:removeSelf() display.save( group, { filename="entireGroup.png", baseDir=system.DocumentsDirectory, isFullResolution=true, backgroundColor={1,1,0,0} } ) local combined = display.newImageRect("entireGroup.png", system.DocumentsDirectory, 100, 100) group:removeSelf() end timer.performWithDelay(100, cc, 1)
Can you please take this, package it with a config.lua and bulld.settings and put it in a zip file and use the Report a Bug link at the top of the page.
You will get an email acknowledging the submission, there will be a Case ID in the subject. Please post ti back here when you’re done.
Rob
This bug has been reported as requested by Rob Miracle -
Case 42777
Rob, any idea when this will be fixed? My app is 85% complete - so I hope it will be fixed within the next 30 days.
Hi @troylyndon,
May I inquire why you believe this is a bug? The documentation for “display.capture()” states that it’s essentially a hybrid between “display.captureScreen()” and “display.save()”, and for the latter, “The object to be saved must be on the screen and fully within the screen boundaries.”.
Best regards,
Brent
At the bottom of the API description is written “display.capture() can be thought of as a hybrid between display.save() and display.captureScreen()”
There is simply no reference to the fact that the capture is limited to the size of the screen resolution for each device, and for this reason, I had thought it could be used to combine a group of display objects into one. In my App, I have more than 1,000 rects that need to be combined to save memory - anyway, I had thought this feature was not limited to screen size. Any chance you could perhaps create a new display.combine to do this? I have the feeling it wouldn’t take more than an hour or two since display.capture does this already, albeit with screen resolution restriction.
By the way, I have already written the code to reload textures on Android, and it works - so ideally, display.save could keep the resolution of a combine instead of saving the image restricted to screen resolution.
Hi @troylyndon,
I apologize for the miscommunication. This actually should be possible if you specify the “isFullResolution” boolean as true during the API call. I will update the documentation on this point soon, so that it’s clearer.
Of course, it appears that you’re already doing so… I’ll need to check out your bug report code soon to see what might be occurring.
Thanks,
Brent
Thank you, Brent. Please see what you can do to have this bug fixed soon - it’s a big deal to my app and shouldn’t take very long to fix, since it appears to have been an oversight - perhaps even a single line flag that was set for debugging.
You can work-around this issue by calling display.save() with the isFullResolution flag.
https://docs.coronalabs.com/api/library/display/save.html
That’ll save content outside the bounds of the screen to file. You can then display that file via display.newImageRect(), feeding the same content width and height of the original object that was captured.
Also note that if you plan on targeting Android *and* you plan on displaying the captured image for a long period of time, then you’ll need to use display.save() anyways. This is because Android dumps all loaded textures/images from memory when your app gets suspended, such as when you press the Home button. When you resume your app, Corona is forced to reload all images into memory from file, but Corona cannot recover the images they were generated via the display.capture*() function because they were deleted from memory and are not persisted to file anywhere. So, the only way to recover captured images after a suspend/resume to make sure that they’re saved to file.
Hi Joshua,
I’m sorry to report that your work around does not work. Essentially, the display.save command is also broken when isFullResolution=true, as it only saves a file that matches the screen resolution and ignores the actual resolution of the defined group or object. This ‘related issue’ is asked here, but has not received a worth reply yet: https://forums.coronalabs.com/topic/59273-displaysave-and-isfullresolution-param-issue/
Can you answer another important question?
The ORIGINAL structure of the code simply 1) performed a screen capture and then 2) saved the image (to be loaded upon an Android resume). Now, based upon your recommendation, the NEW code 1) saves the image first and then 2) loads it from disk.
Which takes more CPU time to perform? The ORIGINAL or NEW code? And most importantly, I’m sorry to put you on the spot, but when will you and the Corona Team be able to provide us (the Corona community) with this feature that operates and functions as written in the API?
Very kindly as always, -Troy
Hi Troy,
I responded to the post that you mention, which should clarify some of this:
https://forums.coronalabs.com/topic/59273-displaysave-and-isfullresolution-param-issue/
Brent
>> I’m sorry to report that your work around does not work.
Yes, it does. We’ve confirmed it on our end. The thing you have to remember about display.save() is that it saves the screen capture in *pixels*… and not in Corona’s scaled content coordinates. This means that the capture image that gets saved to file will be larger for high resolution screens and smaller for low resolution screens. It makes sense for it to work this way.
What you need to do is use display.newImageRect() and load the capture image flle to the width and height you want it to be in your app’s Corona content scaled coordinate system. This way Corona will automatically scale the loaded image from pixels to your app’s scaled coordinate system that you’ve defined in your “config.lua”. A simple way to do this so as follows…
display.save(myGroup, "myCapture.png", system.DocumentsDirectory) local myCaptureImage = display.newImageRect ( "myCapture.png", system.DocumentsDirectory, myGroup.contentWidth, myGroup.contentHeight ) myCaptureImage.x = myGroup.x myCaptureImage.y = myGroup.y myGroup:removeSelf()
>> Which takes more CPU time to perform?
Yes, the display.capture*() APIs will perform faster than display.save(), but the work-around I’m providing above also handles the suspend/resume case on Android so that you can restore your captured images onscreen upon resume.
One more thing to note…
It is highly unusual to capture content larger than the screen. The biggest issue with this is that GPUs (ie: the graphics processor) have what’s called a “max texture size” where there is limit to how tall/wide an image/texture can be when loaded into the GPU. Some devices have a max texture size that match the height of the screen, such as 2048 or 4096 pixels. So, the issue with this is that if you attempt to load image (such as your capture) that is larger than the GPU’s supported max texture size, Corona is forced to downscale/downsample the image before loading it as a texture into the GPU. The result of this will be a blurry picture because the captured image will have been downscaled/downsampled and then the texture will be scaled up back to what the original size of the image was in the file. This effect typically goes unnoticed for camera photos, but it tends to be highly noticeable for screenshots having a lot of solid colored regions. So, even if we were to *fix* the display.capture*() APIs to display the captured image at the original size, this max texture size downscaling/downsampling issue would still happen and that’s not something we can fix. That’s a technical issue on the GPU side that is best to be avoided. You may need to reconsider your app’s design. Just my 2 cents.
I’ve done some tests and display.save() is saving off screen bits. Can you try this code:
local background = display.newRect( display.contentCenterX, display.contentCenterY, display.contentWidth, display.contentHeight) background:setFillColor( 0, 0.2, 0) local myObject0 = display.newRect( 0, 0, 300, 300) myObject0:setFillColor(1, 0, 0) local myObject1 = display.newRect( 150, 150, 100, 150 ) -- Create a rectangle object local myObject2 = display.newCircle( 100, 300, 50 ) -- Create a circle object local myObject3 = display.newRect( display.contentWidth, display.contentHeight, 300, 300) myObject3:setFillColor(0, 0, 1) local group = display.newGroup() group:insert( background ) group:insert( myObject1 ) group:insert( myObject2 ) group:insert( myObject0 ) group:insert( myObject3 ) timer.performWithDelay(1000, function() display.save( group, { filename="entireGroup.png", baseDir=system.DocumentsDirectory, isFullResolution=true, backgroundColor={0,0,0,0} } ) end) timer.performWithDelay(2000, function() local img = display.newImage("entireGroup.png", system.DocumentsDirectory) img.x = display.contentCenterX img.y = display.contentCenterY img:scale(0.5, 0.5) background:setFillColor(0.5, 0, 0.25) end)
Joshua, very good comment but…if a group contains 10 screens high of objects and the player can scroll that group on every device, and the visibility of such group works flawlessly, does Corona manage the coming in and out of smaller texture pieces by itself so that a group of objects can be infinitely large? Or are groups limited to the size of texture memory?
Rob, I appreciate you dedicating time to some code on this, really - may I ask a dumb question? Which objects in your code represent pixels outside the resolution of the content width or height?
Joshua one more thing - using display.newImageRect is clearly documented for use with content scaling. I have no content scaling and maximize screen space on devices with larger areas. I will look more at Rob’s code and report my findings.
>> if a group contains 10 screens high of objects and the player can scroll that group on every device, and the visibility of such group works flawlessly, does Corona manage the coming in and out of smaller texture pieces by itself so that a group of objects can be infinitely large?
For objects such as rectangles and images, yes. And images you use more than once are loaded as a single texture and reference counted by Corona.
More complex objects such as circles would be less efficient because implementing a smooth rounded edge via OpenGL and the GPU would require a lot of vertices (ie: a lot of polygons with straight edges so small that you can’t see them) to achieve that effect. This is very expensive. If you have a lot of circular/rounded polygons, then it might be better not to use them. For example, if you’re using rounded rectangles, then it might be better to replace those with a “9-patch image” where you display images for the corners and sides, which is a much cheaper operation to perform on the GPU side. Note that the 9-patch approach is what Google uses for its UI on Android. It provides good performance and it makes it easier to edit the theme.
And you can still use display.newImageRect() even though you’re not using Corona’s content scaling. Although you’ll only benefit from it if the group you were capturing was scaled. All that display.newImageRect() does is tell Corona to scale the loaded image to the width and height you’ve provided… and that function can scale both up and down.
Object0 and Object3 are partially off screen. For instance Object0 is centered on 0,0 so 3/4 of it is off screen. Only the bottom right quadrant on screen. Object3 is at display.contentWidth, display.contentHeight. 3/4 of it is off screen, only it’s top, left quadrant is on screen.
Kudos to Brent, Joshua and Rob for help with this issue, although not yet resolved.
Rob’s example works, no doubt, in creating a PNG file that is larger than the screen resolution. But this doesn’t explain why this doesn’t occur in my App, despite a lot of time spent on it.
I will pursue this further and report my findings to you.
Thanks again, guys.