Bug: Save / Capture with Full Resolution not working

On daily build 2013.2090 was added a flag that enables you to save/capture image with full resolution (http://docs.coronalabs.com/api/library/display/save.html).

The function is not working properly, saving/capturing black images.

I already opened a bug (#28726) in which I specified that it was not working on Galaxy S1, but now I found out that it is also not working on iPhone 5 (iOS 7).

That bug it appears to be related to the size of the image being saved.

You can replicate the bug by using this image and the code below:

display.setDefault( "background", 255,255,255 ) local photo = display.newImage("photo.jpg", true) -- display image that was saved photo.anchorX = 0.5 photo.anchorY = 0 photo.y = display.contentCenterY photo.x = display.contentCenterX local function capture() print("Running Capture non-FR...") display.capture(photo, {saveToPhotoLibrary=true, isFullResolution = false}) end local function captureFR() print("Running Capture with FR...") display.capture(photo, {saveToPhotoLibrary=true, isFullResolution = true}) end local w = require "widget" local b = w.newButton{ x = 100, y = 50, label="Capture non-FullRes", onRelease=capture } local c = w.newButton{ x = 290, y = 50, label="Capture FullRes", onRelease=captureFR }

Hi Renato,

If you scale the image so that it fits on-screen first, then it will work.

Also, make sure you dispose of the image returned by capture() to keep memory requirements at its lowest!

local scaleImage = function(img) local imageScaleX = display.contentWidth / img.width; local imageScaleY = display.contentHeight / img.height; local newScale = math.min(imageScaleX, imageScaleY); if (newScale \< 1) then img.xScale = newScale; img.yScale = newScale; end img.x = display.contentCenterX; img.y = display.contentCenterY; end local photo = display.newImage("photo.jpg", true) -- display image that was saved photo.anchorX = 0.5 photo.anchorY = 0.5 scaleImage(photo); print(system.getInfo("textureMemoryUsed")) local function capture() print("Running Capture non-FR...") local img = display.capture(photo, {saveToPhotoLibrary=true, isFullResolution = false}) img:removeSelf(); img = nil; end local function captureFR() &nbsp;&nbsp;&nbsp; print("Running Capture with FR...") &nbsp;&nbsp;&nbsp; --temporarily scale to full scale before save &nbsp;&nbsp;&nbsp; photo.xScale = display.contentScaleX; &nbsp;&nbsp;&nbsp; photo.yScale = display.contentScaleY; &nbsp;&nbsp;&nbsp; local img = display.capture(photo, {saveToPhotoLibrary=true, isFullResolution = true}) &nbsp;&nbsp;&nbsp; img:removeSelf(); &nbsp;&nbsp;&nbsp; img = nil; &nbsp;&nbsp;&nbsp; -- scale back &nbsp;&nbsp;&nbsp; scaleImage(photo); end local w = require "widget" local b = w.newButton{ x = 100, y = 50, label="Capture non-FullRes", onRelease=capture } local c = w.newButton{ x = 290, y = 50, label="Capture FullRes", onRelease=captureFR }

I fixed some typo’s in the code above, and added some needed scaling in the captureFR() function.

The code works on an iPad mini (iOS 7) and a Galaxy S1 (Android 2.3.6).

Hi @ingemar. I had tested that before and I noticed that if I scale down the image, the saved file will also be saved in a lower resolution (lower than the original). Can you confirm if you are seeing that same behavior?

Please see the modified captureFR() function in the code above.

Hi @ingemar. I tested your code. It runs fine on iPhone 5 but on the Galaxy S1 the photo is being salved with half of the original resolution (both using .capture or .save).  

Sorry, you’re right.

I only checked the pixel dimensions on iOS, and just file size on Android which looked OK…

It looks like Android handles dynamic scaling differently than iOS so you’ll need to set the image’s xScale and yScale to 1 on Android before saving the full resolution image.

*However*, doing so causes “Out of memory” issues on Android. Galaxy S1 I can understand, but even my Nexus 7 running Kitkat 4.4.2 runs out of memory when doing this.

Unfortunately, I guess that this just means that dealing with Full Resolution images on Android is very unpredictable…

Yes, I agree. But there is something wrong with the function, because if the image is showed in the screen, the display.save/capture should at least save the current image resolution displayed (today it saves a black image).

I tried to create some custom code using the maxTextureSize to find the maximum scale factor but it also didn’t work well.

I will have to give up trying to that with Corona and do it on my server.

Thanks for the help.

For me it always saves an image of the t-shirt (except in my last test where I set xScale,yScale=1 and ran out of memory).

Tested on Galaxy S1 (Wifi) and Nexus 7 and iPad mini…

OK. I think I’ve found the problem.

Android downsamples the photo and if you try to save an image bigger than that you will run into problems. 

This means that the dynamic image scaling *does* work the same way for iOS and Android, it was the downsampling that caused the image to become smaller.

In the Android log I saw this when loading the original image:

V/Corona  ( 5982): Downsampling image file ‘photo.jpg’ to fit max pixel size of 2048.
I/Corona  ( 5982): initial photo w/h 1632 1224

This means that 1632 x 1224 is the maximum dimensions you can use on Android for this image, and there’s not much you can do about it. It looks like loading images that are larger than 2048 pixels on any axis is not possible on Android, and attempting to do so will downsample the image.

On iOS I get the actual width and height of the original image reported:

<Warning>: initial photo w/h 2448 3264

I though so also. When I ran your code, I got the photo with that resolution (1632 x 1224) and the photo was scaled to 0.6667 (the  display.contentScaleX, Y  on Galaxy S1).

I then changed manually the scale to 0.75 and I was able to save the photo with 1836 x 1337. So, the (1632x1224) was not the limit. 

EDIT: I kept interating the scale factor and with 0.83 I saved the photo with 2031 x 1521. When I changed for 0.84, the photo was saved as black with 2055 x 1542.  So, I think the really limit is the 2048 (that is the maxTextureSize of the device).  But I was not able to get via code the scale factor that would give me the image with maximum dimension. I tried using local scaleFactor = math.min(2048 / photo.width, 2048 / photo.height)  but I kept getting a scaleFactor of 1.25 what I believe is because the original image in the screen was not loaded with a resolution less than 2048. Maybe is something related to the dynamic scaling…

EDIT 2: Yes, it was the dynamic scaling. I disabled it and was able to save the image with 2048 x 1536 by calculating the scaleFactor = math.min(2048 / photo.width, 2048 / photo.height).  So, to ensure that the device does not save a black image and save with the maximum resolution allowed, I just need to adapt your code from:

--temporarily scale to full scale before save photo.xScale = display.contentScaleX; photo.yScale = display.contentScaleY;

to:

--temporarily scale to full scale before save local maxTextureSize = system.getInfo("maxTextureSize") local scaleFactor = math.min(maxTextureSize / photo.width, maxTextureSize / photo.height) photo.xScale = scaleFactor photo.yScale = scaleFactor

Hi Renato,

If you scale the image so that it fits on-screen first, then it will work.

Also, make sure you dispose of the image returned by capture() to keep memory requirements at its lowest!

local scaleImage = function(img) local imageScaleX = display.contentWidth / img.width; local imageScaleY = display.contentHeight / img.height; local newScale = math.min(imageScaleX, imageScaleY); if (newScale \< 1) then img.xScale = newScale; img.yScale = newScale; end img.x = display.contentCenterX; img.y = display.contentCenterY; end local photo = display.newImage("photo.jpg", true) -- display image that was saved photo.anchorX = 0.5 photo.anchorY = 0.5 scaleImage(photo); print(system.getInfo("textureMemoryUsed")) local function capture() print("Running Capture non-FR...") local img = display.capture(photo, {saveToPhotoLibrary=true, isFullResolution = false}) img:removeSelf(); img = nil; end local function captureFR() &nbsp;&nbsp;&nbsp; print("Running Capture with FR...") &nbsp;&nbsp;&nbsp; --temporarily scale to full scale before save &nbsp;&nbsp;&nbsp; photo.xScale = display.contentScaleX; &nbsp;&nbsp;&nbsp; photo.yScale = display.contentScaleY; &nbsp;&nbsp;&nbsp; local img = display.capture(photo, {saveToPhotoLibrary=true, isFullResolution = true}) &nbsp;&nbsp;&nbsp; img:removeSelf(); &nbsp;&nbsp;&nbsp; img = nil; &nbsp;&nbsp;&nbsp; -- scale back &nbsp;&nbsp;&nbsp; scaleImage(photo); end local w = require "widget" local b = w.newButton{ x = 100, y = 50, label="Capture non-FullRes", onRelease=capture } local c = w.newButton{ x = 290, y = 50, label="Capture FullRes", onRelease=captureFR }

I fixed some typo’s in the code above, and added some needed scaling in the captureFR() function.

The code works on an iPad mini (iOS 7) and a Galaxy S1 (Android 2.3.6).

Hi @ingemar. I had tested that before and I noticed that if I scale down the image, the saved file will also be saved in a lower resolution (lower than the original). Can you confirm if you are seeing that same behavior?

Please see the modified captureFR() function in the code above.

Hi @ingemar. I tested your code. It runs fine on iPhone 5 but on the Galaxy S1 the photo is being salved with half of the original resolution (both using .capture or .save).  

Sorry, you’re right.

I only checked the pixel dimensions on iOS, and just file size on Android which looked OK…

It looks like Android handles dynamic scaling differently than iOS so you’ll need to set the image’s xScale and yScale to 1 on Android before saving the full resolution image.

*However*, doing so causes “Out of memory” issues on Android. Galaxy S1 I can understand, but even my Nexus 7 running Kitkat 4.4.2 runs out of memory when doing this.

Unfortunately, I guess that this just means that dealing with Full Resolution images on Android is very unpredictable…

Yes, I agree. But there is something wrong with the function, because if the image is showed in the screen, the display.save/capture should at least save the current image resolution displayed (today it saves a black image).

I tried to create some custom code using the maxTextureSize to find the maximum scale factor but it also didn’t work well.

I will have to give up trying to that with Corona and do it on my server.

Thanks for the help.

For me it always saves an image of the t-shirt (except in my last test where I set xScale,yScale=1 and ran out of memory).

Tested on Galaxy S1 (Wifi) and Nexus 7 and iPad mini…

OK. I think I’ve found the problem.

Android downsamples the photo and if you try to save an image bigger than that you will run into problems. 

This means that the dynamic image scaling *does* work the same way for iOS and Android, it was the downsampling that caused the image to become smaller.

In the Android log I saw this when loading the original image:

V/Corona  ( 5982): Downsampling image file ‘photo.jpg’ to fit max pixel size of 2048.
I/Corona  ( 5982): initial photo w/h 1632 1224

This means that 1632 x 1224 is the maximum dimensions you can use on Android for this image, and there’s not much you can do about it. It looks like loading images that are larger than 2048 pixels on any axis is not possible on Android, and attempting to do so will downsample the image.

On iOS I get the actual width and height of the original image reported:

<Warning>: initial photo w/h 2448 3264