setMask in HTML?

@richard11 Can you please provide a simple testcase ?  It will help to find out and fix the bug. Thanks.

Sorry for the silence - things have gotten crazily busy here!

I’ll extract the code out into a basic test for you over the weekend @vitaly1

@roaminggamer, it runs a while loop to await file creation before attempting to use it, but perhaps this is the problem? This has worked a treat in everything we’ve tested on but perhaps Javascript detects that the file exists before it’s completely written? Or perhaps because this is Windows and everything else we’ve tested on has been Android… I wonder if Windows creates the file before writing where Android creates it to memory and then writes to disk last…?

display.save(maskGroup, { filename=maskFile, baseDir = system.TemporaryDirectory, isFullResolution = true, captureOffscreenArea = true }) -- Wait until the file has been created before trying to load it while(convenience.isFile(maskFile, system.TemporaryDirectory) == false) do end vars.layerMasks[index] = graphics.newMask(maskFile, system.TemporaryDirectory)

Ha - totally forgot that convenience is one of my libraries, sorry. The below is the convenience.isFile() function that the above uses…

module.isFile = function(filename, baseDir) local fileHandle = io.open(system.pathForFile(filename, baseDir), "r") if(fileHandle) then return true else return false end end

You’re reading the file while trying to write it?  I would use a timer to wait for a frame or two.

@richard11 Looks like you aren’t closing the file when it exists.

Now there’s a rookie mistake if ever I saw one. Good spot, thanks. Fixed now! Though this doesn’t seem to have affected anything.

You’re reading the file while trying to write it?  I would use a timer to wait for a frame or two.

Just to test, I tried the following but still no luck. In the simulator there’s now some serious glitching as it tries to create the files (unsurprisingly) but in Javascript I’m seeing no difference at all, which is odd:

while(convenience.isFile(maskFile, system.TemporaryDirectory) == false) do end local function listener(event) vars.layerMasks[index] = graphics.newMask(maskFile, system.TemporaryDirectory) end timer.performWithDelay(3000, listener)

@richard11 can you try DocumentsDirectory instead of TemporaryDirectory, just as test for now.

Same result I’m afraid, even leaving in the 3 second delay before trying to use the image.:

Couldnot load /documentsDir/mask_68.png: Unsupported image format

In case this is a clue, Firefox also gives the following error during load but then runs the same as in Chrome:

Error: Failed to mount IDBFS

 InvalidStateError: A mutation operation was attempted on a database that did not allow mutations.

Hmm…

If I take one of the mask images created while running in the simulator and load that in as a mask file, skipping the whole generation process, it works.

If I then try to load a file that doesn’t actually exist as a mask, nothing happens. No masking obviously, but also no error which suggests that the above “Unsupported image format” error is actually legitimate. There’s something wrong with the created mask images rather than that the temp directory can’t be written to or something.

Seems it’s display.save that’s corrupting png files, but without being able to actually see the resulting files I can’t be completely sure. Interestingly, if I switch to saving a jpg, the browser crashes!

OK, I’ve been fiddling with this all day and getting nowhere. The masks are definitely being rendered properly to a display group but something is going wrong when saving that group to file, or when trying to load the resulting file with graphics.newMask().

Here’s my function, inclusive of a few debug prints:

module.createMask = function(index, sheet, frame) if(vars.layerMasks[index] == nil) then local lceil = math.ceil local maskFile = "mask" .. index .. ".png" local maskCutout = display.newImage(sheet, frame) -- Masks have to be divisible by 4, and have a 3px border... oddly. local maskWidth = lceil((maskCutout.width + 6) / 4) \* 4 local maskHeight = lceil((maskCutout.height + 6) / 4) \* 4 local maskGroup = display.newGroup() -- Tile sized blackout. local maskArea = display.newRect(maskGroup, maskWidth / 2, maskHeight / 2, maskWidth, maskHeight) maskArea:setFillColor(0) maskGroup:insert(maskCutout) maskCutout.x = maskWidth / 2 maskCutout.y = maskHeight / 2 -- Turn to white maskCutout.fill.effect = "filter.brightness" maskCutout.fill.effect.intensity = 10 -- documentation states 1 is the max, but... if(display.contentScaleX ~= 1 or display.contentScaleY ~= 1) then -- Compensate for content scaling local scaledW = 1 / (maskWidth / (maskGroup.contentWidth \* display.contentScaleX)) local scaledH = 1 / (maskHeight / (maskGroup.contentHeight \* display.contentScaleY)) print("original = " .. maskGroup.contentWidth .. " x " .. maskGroup.contentHeight) print("contentScale = " .. display.contentScaleX) maskGroup:scale(scaledW, scaledH) print("new = " .. maskGroup.contentWidth .. " x " .. maskGroup.contentHeight) print(maskGroup.contentWidth .. " / " .. display.contentScaleX .. " = " .. (maskGroup.contentWidth / display.contentScaleX)) print(maskGroup.contentHeight .. " / " .. display.contentScaleY .. " = " .. (maskGroup.contentHeight / display.contentScaleY)) end -- Save this as an image to then load in as a mask display.save(maskGroup, { filename=maskFile, baseDir = system.TemporaryDirectory, captureOffscreenArea = true, backgroundColor = { 0 } }) -- Wait until the file has been created before trying to load it while(convenience.isFile(maskFile, system.TemporaryDirectory) == false) do end vars.layerMasks[index] = graphics.newMask(maskFile, system.TemporaryDirectory) maskGroup:removeSelf() end return true end

The basic concept is to create a black canvas that’s divisible by 4 and at least 6px bigger than the content, to create the necessary 3px border. Then render the content (a sprite frame) onto that canvas, apply some filters to turn it white, and finally save out to an image file. Obviously content scaling then gets in the way so the group is scaled accordingly to compensate, forcing the resulting file to be the desired dimensions rather than the pre-save render being those dimensions.

Tested on various devices, in various resolutions, with flawless results every time. With the HTML5 build I can’t see the resulting images - they don’t appear to ever actually exist on my system, or if they do they’re given obfuscated file names, so I can’t verify that the resulting files are as expected but I can paste the results of those print() lines:

original = 264 x 200

contentScale = 0.78850102424622

new = 208 x 157

208 / 0.78850102424622 = 263.79166748558

157 / 0.78850102424622 = 199.11197978479

I’m wondering if this is resulting in a 264x199 image instead of a 264x200 image since 199 isn’t divisible by 4. But again I can’t see the resulting files. Or could it be that display.save() is actually dumping the literally rendered-to-screen output which, thanks to the resuling app being stretched to full browser viewport size, isn’t necessarily the same dimensions as maskGroup.contentWidth and maskGroup.contentHeight give?

I’m pretty stumped now. Definitely not going to get any further without being able to see the resulting files  :unsure:

For the record, removing the maskGroup:removeSelf() line confirms that the applied filters etc are working.

@richard11 can you please provide a full simple test project (main.lua + build.settings + config.lua) which demonstrates the issue ? 

Everytime I see this, it’s either:

  • I’m running on private mode

or

  • I’m running from local folder, without the use of a server.

Maybe it’s the same for you?

sorry, I could not reproduce the issue.

@miguel.campiao

This is server hosted and not in private mode I don’t think… Less worried about this err anyway, it’s clearly a Firefox thing as opposed to something we can fix since it doesn’t happen in other browsers, but I just thought it might be a clue somehow as to why the masking isn’t working.

@vitaly1

Sorry, I totally intended to get an actual test script over to you over the weekend but somehow my weekend disappeared about 6 days earlier than I’d have liked! I’m determined to crack this one though and will definitely get a proper test over to you asap.

Right - example attached!

In the simulator this produces a properly masked image.But after compiling to HTML5 the mask isn’t applied and the error “Unsupported image format” is output:

https://development.qweb.co.uk/html5/test/index-debug.html

Sorry - previous attachment had an error - fixed and re-attached. This is the version that matches the above HTML link.

@richard11 as I see you are using build 3326 in https://development.qweb.co.uk/html5/test/index-debug.html

Try please last daily build, it’s 3355. Hoping it will resolve “… Unsupported image format” issue

Aha! Build 3355 does indeed work! Fantastic, thanks.

Hate to raise this one again, but is it at all possible that fixing masks in the HTML build has broken something in the Android build?

I’m seeing really odd masking glitches in Android builds now and can’t seem to pinpoint anything in code. I thought some of my math must be out, but even setting the x and y of masks to 0 is rendering them differently to the simulator!

Just installed Corona 2018.3362 to no avail.