Saving Texture Causes Random Shifting and/or Flashing of Composer Scenes

I have encountered a very strange bug involving the new canvas-based textures, display.save, and multiple composer scenes. I have created a greatly simplified test case that I have been able to use to reproduce the issues somewhat reliably in the Windows simulator using builds 2866 and 2883.

Below are ALL of the files from my test project. Create the files all in the root folder of a new project and then run it. The application does three things to start, it loads “scene0”, which is just a red rectangle, creates a yellow “circle” using a circle, canvas texture, and an ImageRect, and then starts a timer to save the circle ImageRect to CachesDirectory every 1 second.

Most of the time, I get an issue right away. Specifically, scene0 starts flashing at me. This happens every time that display.save is called in the timer. In doing some tests, I don’t think it’s actually the *scene* that is hiding/flashing but that it’s elements in the scene. In fact, I tested nearly identical timer save code in the Composer sample project that ships with Corona and found that the scenes and images stay put but the text alternates between values. It’s very odd. Try it yourself. Copy the last few lines of my main.lua (starting with “local circ = display.newCircle(0, 0, 40, 40)”) and paste them into the bottom of main.lua in the Composer demo and you’ll see that the memory text alternates it’s content and “Touch to continue.” will flash.

The other issue is a little more difficult to reproduce in this dummed down project but it happens rather frequently in my actual, fuller project. To reproduce, tap scene0 to go to scene1. Wait a couple of seconds and then tap scene1 to go to scene2. Wait a couple of seconds and then tap scene2 to go back to scene1. Continue to alternate between scene1 and scene2, pausing a moment between taps until you notice that the scene is shifting a few pixels left and right. In my tests, it can sometimes take me a while to cause this to happen but it always happens eventually. Sometimes I have to restart the app in the simulator.

Note that I am using slideRight and slideLeft to switch between scenes. When I switch to using slideUp and slideDown, the shifting will be up and down instead of left and right.

One final note, the issue is clearly tied to these specific transistions and it is also tied to screen resolutions of your test device since the resolution effects the behaviour of the transistions. So, for best/worst results, you should use one of these resolutions for your test device in the simulator: 320x480 or 640x960. The behaviour will be different if you use a different resolution.

build.settings

settings = { orientation = { default = "portrait", supported = { "portrait", } }, }

config.lua

application = { content = { width = 320, height = 480, scale = "letterBox", fps = 30, }, }

main.lua

local composer = require( "composer" ) composer.gotoScene('scene0') local circ = display.newCircle(0, 0, 40, 40) circ.fill = {1,1,0} local tex = graphics.newTexture( { type="canvas", width=100, height=100 } ) tex:draw(circ) tex:invalidate() local imageR = display.newImageRect(tex.filename, tex.baseDir, 100, 100) timer.performWithDelay(500, function(event) display.save(imageR, {filename="circle.png", baseDir=system.CachesDirectory, isFullResolution=true}) print('saving '.. event.count) end, -1)

scene0.lua

--------------------------------------------------------------------------------- -- -- scene0.lua -- --------------------------------------------------------------------------------- local composer = require( "composer" ) local scene = composer.newScene() local back, text1, text2 -- Touch event listener for background image local function onSceneTouch( self, event ) if event.phase == "began" then composer.gotoScene( "scene1", "slideLeft", 800 ) return true end end -- Called when the scene's view does not exist: function scene:create( event ) local sceneGroup = self.view back = display.newRoundedRect(sceneGroup, display.contentCenterX, display.contentCenterY, display.actualContentWidth, display.actualContentHeight, 20) back.fill = {1,0,0,0.5} back.touch = onSceneTouch text1 = display.newText( "Scene 0", 0, 0, native.systemFontBold, 24 ) text1:setFillColor( 255 ) text1.x, text1.y = display.contentWidth \* 0.5, 50 sceneGroup:insert( text1 ) text2 = display.newText( "Touch to continue.", 0, 0, native.systemFontBold, 18 ) text2:setFillColor( 255 ) text2.x, text2.y = display.contentWidth \* 0.5, display.contentHeight - 100 sceneGroup:insert( text2 ) end function scene:show( event ) local phase = event.phase if "did" == phase then back:addEventListener( "touch", back ) end end function scene:hide( event ) local phase = event.phase if "will" == phase then -- remove touch listener for image back:removeEventListener( "touch", back ) end end function scene:destroy( event ) print('destroying scene0') end --------------------------------------------------------------------------------- -- Listener setup scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

scene1.lua

--------------------------------------------------------------------------------- -- -- scene1.lua -- --------------------------------------------------------------------------------- local composer = require( "composer" ) local scene = composer.newScene() local back, text1, text2 -- Touch event listener for background image local function onSceneTouch( self, event ) if event.phase == "began" then composer.gotoScene( "scene2", "slideLeft", 800 ) return true end end -- Called when the scene's view does not exist: function scene:create( event ) local sceneGroup = self.view back = display.newRoundedRect(sceneGroup, display.contentCenterX, display.contentCenterY, display.actualContentWidth, display.actualContentHeight, 20) back.fill = {0,1,0,0.5} back.touch = onSceneTouch text1 = display.newText( "Scene 1", 0, 0, native.systemFontBold, 24 ) text1:setFillColor( 255 ) text1.x, text1.y = display.contentWidth \* 0.5, 50 sceneGroup:insert( text1 ) text2 = display.newText( "Touch to continue.", 0, 0, native.systemFontBold, 18 ) text2:setFillColor( 255 ) text2.x, text2.y = display.contentWidth \* 0.5, display.contentHeight - 100 sceneGroup:insert( text2 ) end function scene:show( event ) local phase = event.phase if "did" == phase then back:addEventListener( "touch", back ) end end function scene:hide( event ) local phase = event.phase if "will" == phase then -- remove touch listener for image back:removeEventListener( "touch", back ) end end function scene:destroy( event ) print('destroying scene1') end --------------------------------------------------------------------------------- -- Listener setup scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

scene2.lua

--------------------------------------------------------------------------------- -- -- scene2.lua -- --------------------------------------------------------------------------------- local composer = require( "composer" ) local scene = composer.newScene() local back, text1, text2 -- Touch event listener for background image local function onSceneTouch( self, event ) if event.phase == "began" then composer.gotoScene( "scene1", "slideRight", 800 ) return true end end -- Called when the scene's view does not exist: function scene:create( event ) local sceneGroup = self.view back = display.newRoundedRect(sceneGroup, display.contentCenterX, display.contentCenterY, display.actualContentWidth, display.actualContentHeight, 20) back.fill = {0,0,1,0.5} back.touch = onSceneTouch text1 = display.newText( "Scene 2", 0, 0, native.systemFontBold, 24 ) text1:setFillColor( 255 ) text1.x, text1.y = display.contentWidth \* 0.5, 50 sceneGroup:insert( text1 ) text2 = display.newText( "Touch to continue.", 0, 0, native.systemFontBold, 18 ) text2:setFillColor( 255 ) text2.x, text2.y = display.contentWidth \* 0.5, display.contentHeight - 100 sceneGroup:insert( text2 ) end function scene:show( event ) local phase = event.phase if "did" == phase then back:addEventListener( "touch", back ) end end function scene:hide( event ) local phase = event.phase if "will" == phase then -- remove touch listener for image back:removeEventListener( "touch", back ) end end function scene:destroy( event ) print('destroying scene2') end --------------------------------------------------------------------------------- -- Listener setup scene:addEventListener( "create", scene ) scene:addEventListener( "show", scene ) scene:addEventListener( "hide", scene ) scene:addEventListener( "destroy", scene ) --------------------------------------------------------------------------------- return scene

I don’t see either of the behaviors in the Mac sim. Have you tried this on a device to see if device’s have the same issue or if this is just limited to the Windows simulator?

Rob

Rob,

I just did an Android build plus ran the code within CoronaViewer on my iPhone. Neither one of them had this issue either! Whew! It is a bit disconcerting that the simulator has this issue since I will continue seeing this jarring effect but for now I will move forward under the assumption that this will not affect mobile builds.

Just as an FYI, I did file a bug: Case 46069

I don’t see either of the behaviors in the Mac sim. Have you tried this on a device to see if device’s have the same issue or if this is just limited to the Windows simulator?

Rob

Rob,

I just did an Android build plus ran the code within CoronaViewer on my iPhone. Neither one of them had this issue either! Whew! It is a bit disconcerting that the simulator has this issue since I will continue seeing this jarring effect but for now I will move forward under the assumption that this will not affect mobile builds.

Just as an FYI, I did file a bug: Case 46069