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.