Dynamically generated masks

Hi!

You made some mistakes in writing. 

Furthermore display.actualCenterX, display.actualCenterY don’t work. Use “display.contentCenterX, display.contentCenterY”.

Said this the main mistake was to put the black background when saving the group and not to put “isFullResolution=true”.

For more information check out the documentation: https://docs.coronalabs.com/api/library/display/save.html

Anyway I wrote a small working example, it should be what you wanted:

local maskgroup = display.newGroup() maskgroup.x, maskgroup.y = display.actualCenterX, display.actualCenterY local rect = display.newRect( maskgroup, 0, 0, 400, 400 ) rect:setFillColor( 0, 0, 0) local roundedRect = display.newRoundedRect( maskgroup, 0, 0, 350, 150, 35 ) roundedRect:setFillColor( 1, 1, 1) local text = display.newText(maskgroup, "SAVE", 0, 0, system.nativeFont, 100) text:setFillColor( 0, 0, 0 ) display.save( maskgroup, { filename="mask.png", baseDir=system.CachesDirectory, isFullResolution=true}) --backgroundColor={0,0,0,0} } ) maskgroup:removeSelf() maskgroup = nil local g = display.newGroup() g.x, g.y = display.contentCenterX, display.contentCenterY display.newRect( g, 0, 0, 500, 500 ).fill = {1,0,0} local mask = graphics.newMask( "mask.png", system.CachesDirectory ) g:setMask( mask )

The image comes out of the screen but I’m based on your size I think there will be a reason if you wanted it that way.

Thanks, I’ll try that later.

actualCenterX/Y are derived from values which are provided by the display library - I assure you they work.

No problem

However I continue to believe that (actualCenterX,actualCenterY) does not work

I did a simple test:

print("actualCenterX,actualCenterY: ",actualCenterX,actualCenterY)

result:

actualCenterX,actualCenterY: nil nil

@maximo,

I think horacebury  is saying he does something like this early on in his code before using those variables:

display.actualCenterX = some calculation

As ‘display.*’ is a global table, now it has that property as calculated by horacebury and the value is available everywhere in his scripts.

@horacebury,

First, I’m a bit rusty on this so keep that in mind.

Second, In my past experience, I found, that unless you could control BOTH the config.lua settings (which you can) and the target resolution (which you cannot), there would always be texture save resolutions you could not achieve.  

Due to scaling, I found I could not produce textures at some sizes, due to rounding errors, etc.

If you resolve this I’d love to hear about it.  Meanwhile, today I’ll take a peek at my old ‘icon generation code’ from EAT and see if I remember anything helpful to go with this (mostly unhelpful) post of mine.

@horacebury,

I found my code here: https://github.com/roaminggamer/RG_FreeStuff/raw/master/AskEd/2017/09/iconGenerator.zip

Although there is a scripts folder, all the work is done in main.lua.  I think those files in scripts are just old junk I couldn’t bear to delete.

I’ll re-consider your original post compared to what I’m doing and see if I can help.

Meanwhile, IIRC my generator tries over and over (for a fixed number of tries) to produce the requested sizes and then fails out if it can’t.

Again, though my memory may be shoddy here.  Will look in more depth today.

You’re missing the very first line of my sample.

@maximo97 The documentation states that the isFullResolution option is the legacy equivalent of captureOffscreenArea. I’m not sure what difference that would make.

@roaminggamer  I had also thought of something like that but not being a native speaker I wanted to be sure.

@horacebury I also try to find another solution

For an iPhone X, if I want a rounded rect of 350x150, here’s the code which appears to work. I would think that putting it in a background loop to work out the correct size for the particular device would work, with the exception of edge cases which don’t produce wholly rounded values - but that’s something to look into.

Please note here that the use of timers is to allow the text object to render as well as show the working stages.

 local maskgroup = display.newGroup() maskgroup.x, maskgroup.y = display.actualCenterX, display.actualCenterY display.newRect( maskgroup, 0, 0, 404, 404 ).fill = {0,0,0} display.newRoundedRect( maskgroup, 0, 0, 350, 150, 35 ).fill = {1,1,1} display.newText{ parent=maskgroup, fontSize=100, text="SAVE" }.fill = {0,0,0} display.newRoundedRect( display.actualCenterX, display.actualCenterY+300, 350, 150, 35 ).fill = {1,0,0} timer.performWithDelay( 1000, function() display.save( maskgroup, { filename="mask.png", baseDir=system.CachesDirectory, backgroundColor={0,0,0,0} } ) maskgroup:removeSelf() maskgroup = nil -- need to give the text object \>30ms to render before display.save can render it to an image timer.performWithDelay( 1000, function() local g = display.newGroup() g.x, g.y = display.actualCenterX, display.actualCenterY timer.performWithDelay( 1000, function() local img = display.newImage( "mask.png", system.CachesDirectory ) img.x, img.y = display.actualCenterX, display.actualCenterY img.alpha = .5 print(img.width,img.height) print(img.width/4,img.height/4) local w, h = img.width,img.height timer.performWithDelay( 1000, function() -- cannot load an image as an image and a mask at the same time img:removeSelf() img = nil display.newRect( g, 0, 0, w, h ).fill = {1,0,0} timer.performWithDelay( 1000, function() local mask = graphics.newMask( "mask.png", system.CachesDirectory ) g:setMask( mask ) timer.performWithDelay( 1000, function() g.width, g.height = 404, 404 end ) end ) end ) end ) end ) end )

@horacebury

Man.  I totally didn’t help here, other than to post some random code.  I’m sorry.  I wanted to take some time out to experiment with this, but it just didn’t happen.

Does your last post indicate you essentially have this solved?

Actually, you did help. I downloaded your code and it gave me ideas for the code I posted last time.

I, unfortunately, do not have this solved. The device vs dynamic scaling issue is still going to haunt me. I’m thinking there is a maths-based way out of this. (I was hoping you might have more on that.)

My attempt with a brute force solution, most likely not viable for your situation.

display.actualCenterX, display.actualCenterY = display.actualContentWidth/2, display.actualContentHeight/2 local maskgroup = display.newGroup() maskgroup.x, maskgroup.y = display.actualCenterX, display.actualCenterY display.newRect( maskgroup, 0, 0, 404, 404 ).fill = {0,0,0} display.newRoundedRect( maskgroup, 0, 0, 350, 150, 35 ).fill = {1,1,1} display.newText{ parent=maskgroup, fontSize=100, text="SAVE" }.fill = {0,0,0} local testGroup = display.newGroup() local image for i=1, 100 do display.save( maskgroup, { filename="mask\_.png", baseDir=system.CachesDirectory, backgroundColor={0,0,0,0} } ) image = display.newImage(testGroup,"mask\_.png", system.CachesDirectory) if image.width % 4 == 0 then display.save( maskgroup, { filename="mask.png", baseDir=system.CachesDirectory, backgroundColor={0,0,0,0} } ) display.remove(image) else print(image.width) maskgroup:scale(0.99,0.99) image:removeSelf() image = nil collectgarbage("collect") end end image = nil -- Set up a bounds table for capturing the bottom-right quadrant of the screen maskgroup:removeSelf() maskgroup = nil local g = display.newGroup() g.x, g.y = display.actualCenterX, display.actualCenterY display.newRect( g, 0, 0, 500, 500 ).fill = {1,0,0} local mask = graphics.newMask( "mask.png", system.CachesDirectory ) g:setMask( mask )

Couldn’t you just do the %4 calculation before generating the image?

If I’ve missed the point of your code, how are you detecting if the mask is good?

I consider the mask is good if the resolution is divisible by 4 , and so far from my limited time testing all mask seems to work even with weird resolution on the simulator. 

I need to generate the image first since the saved file resolution depends on the device resolution, which I have no idea to get beforehand.

Yep. I made that mistake. The problem is that the mask has to be divisible by 4 on the target device and scaling. So I’m trying to generate the mask image at a size which when saved and loaded as a mask will produce a usable mask.

Which is why I keep scaling down (up also possible) till the saved image is divisible by 4. The mask is usable, but usually needed scaling to fit properly, but at least it is no longer… “corrupted”.

Stating the obvious here but why not just prebake the masks in PS?  This works flawlessly on all target devices?

No divisible by 4 is needed (just ensure your mask is +4px on all sides).

I prebake all my masks (easy as in PS with a batch script) and have 0 issues on all resolutions.

Obvious answer: I need to render the user’s name as a stamp or some other user input as a cutout of something else.

That’s not what I’m actually doing, but this is (as I said at the top) just because I never like rendering my images before-hand, I do everything in code. So it’s more of a badge of honour at this point.