Masks Not Working?

Hey all, I’ve been hitting my head against this for several hours. I’ve tried many different methods, and done searches, but just cannot seem to get it to work.

All I want to do is draw a picture (a square — this will be a piece of a larger picture that the program pulls out) and give it a round rect mask.

I’ve put this code in a Canvas Paint event:

[code]dim magPic as picture
magPic=TrueWindow.BitmapForCaching(100,100)
magPic.graphics.foreColor=&c2FACE200
magPic.graphics.fillRect(0,0,magPic.width,magPic.height)

dim maskPic as picture
maskPic=TrueWindow.BitmapForCaching(100,100)
maskPic.graphics.foreColor=&c00000000
maskPic.graphics.FillRoundRect(0,0,100,100,20,20)

magPic.ApplyMask maskPic
g.drawPicture magPic,0,0[/code]

What I expect is a blue square with a round rect mask making it a blue round rect. What I get is a blue square with no mask, no matter what I try. I’m sure I’m overlooking something simple, but I’m at a loss. Anyone have any ideas?

Thank you.

Make sure you set the background of the mask image to white first.

dim maskPic as picture maskPic=TrueWindow.BitmapForCaching(100,100) maskPic.graphics.foreColor=&cFFFFFF maskPic.graphics.FillRect(0,0,maskPic.Width, maskPic.Height) maskPic.graphics.foreColor=&c00000000 maskPic.graphics.FillRoundRect(0,0,100,100,20,20)

Read here:
https://documentation.xojo.com/api/deprecated/window.html#window-bitmapforcaching

Returns a bitmap that is configured correctly for using as a cache for content to be drawn to this Window. This image supports Alpha Channels (not masked images).

It won’t support masked images.
Use the normal picture constructor for masked pictures

To follow up on what @Derk Jochems said, this should produce what you’re looking for:

[code]dim magPic as new Picture(100,100)
magPic.graphics.foreColor=&c2FACE200
magPic.graphics.fillRect(0,0,magPic.width,magPic.height)

dim maskPic as new picture(100,100)
maskPic.graphics.foreColor=&cFFFFFF
maskPic.graphics.FillRect(0,0,maskPic.Width, maskPic.Height)
maskPic.graphics.foreColor=&c00000000
maskPic.graphics.FillRoundRect(0,0,100,100,20,20)

magPic.ApplyMask maskPic
g.drawPicture magPic,0,0[/code]

[quote=475323:@Anthony Cyphers]Make sure you set the background of the mask image to white first.

dim maskPic as picture maskPic=TrueWindow.BitmapForCaching(100,100) maskPic.graphics.foreColor=&cFFFFFF maskPic.graphics.FillRect(0,0,maskPic.Width, maskPic.Height) maskPic.graphics.foreColor=&c00000000 maskPic.graphics.FillRoundRect(0,0,100,100,20,20)[/quote]

Masks must be black. A white mask inverts the mask.

If you look, I’m filling the background of the mask with white to denote the transparent areas, then drawing the RoundRect on top in black.

[quote=475324:@Derk Jochems]Read here:
https://documentation.xojo.com/api/deprecated/window.html#window-bitmapforcaching

Returns a bitmap that is configured correctly for using as a cache for content to be drawn to this Window. This image supports Alpha Channels (not masked images).

It won’t support masked images.
Use the normal picture constructor for masked pictures[/quote]

Yes, this works. I had tried that. But I need the images to be retina/HiDPI compatible, so not sure what to do there. Would multiplying the image by screen scale and drawing it at half size work? I had started playing around with that but was running into issues.

You can multiply by ScaleFactor of the window/control you’re drawing in:

[code]dim magPic as new Picture(100 * me.TrueWindow.ScaleFactor,100 * me.TrueWindow.ScaleFactor)
magPic.graphics.foreColor=&c2FACE200
magPic.graphics.fillRect(0,0,magPic.width,magPic.height)

dim maskPic as new picture(100 * me.TrueWindow.ScaleFactor,100 * me.TrueWindow.ScaleFactor)
maskPic.graphics.foreColor=&cFFFFFF
maskPic.graphics.FillRect(0,0,maskPic.Width, maskPic.Height)
maskPic.graphics.foreColor=&c00000000
maskPic.graphics.FillRoundRect(0,0,100,100,20,20)

magPic.ApplyMask maskPic
g.drawPicture magPic,0,0[/code]

[quote=475325:@Anthony Cyphers]To follow up on what @Derk Jochems said, this should produce what you’re looking for:

[code]dim magPic as new Picture(100,100)
magPic.graphics.foreColor=&c2FACE200
magPic.graphics.fillRect(0,0,magPic.width,magPic.height)

dim maskPic as new picture(100,100)
maskPic.graphics.foreColor=&cFFFFFF
maskPic.graphics.FillRect(0,0,maskPic.Width, maskPic.Height)
maskPic.graphics.foreColor=&c00000000
maskPic.graphics.FillRoundRect(0,0,100,100,20,20)

magPic.ApplyMask maskPic
g.drawPicture magPic,0,0[/code][/quote]

I think you need the “non-alpha-constructor” Pictures created with an alpha channel do not use a mask. The Mask property will be Nil. Use the CopyMask and ApplyMask methods if you need to work with masks.
https://documentation.xojo.com/api/graphics/picture.html#picture-constructor(width_as_integer-_height_as_integer)

you need this constructor:
https://documentation.xojo.com/api/deprecated/deprecated_class_members/picture.constructor(width_as_integer-_height_as_integer-_depth_as_integer).html

Something like:

dim magPic as new Picture(100,100, 32) // <-- specify depth
magPic.graphics.foreColor=&c2FACE200
magPic.graphics.fillRect(0,0,magPic.width,magPic.height)

dim maskPic as new picture(100,100)
maskPic.graphics.foreColor=&cFFFFFF
maskPic.graphics.FillRect(0,0,maskPic.Width, maskPic.Height)
maskPic.graphics.foreColor=&c00000000
maskPic.graphics.FillRoundRect(0,0,100,100,20,20)

magPic.ApplyMask maskPic
g.drawPicture magPic,0,0

Nope. Use it all the time, and just tested it again when checking his issue in the IDE. ApplyMask takes care of that.

[quote=475324:@Derk Jochems]
It won’t support masked images.
Use the normal picture constructor for masked pictures[/quote]

Doc page for ApplyMask:

Alpha and masks don’t combine so then the picture is NON-ALPHA by using the ALPHA constructor ?

Interesting. I read in the documentation that when used with a picture with an alpha channel, it should still work and override the picture’s alpha channel. Why isn’t that working?

[quote=475329:@Anthony Cyphers]You can multiply by ScaleFactor of the window/control you’re drawing in:

[code]dim magPic as new Picture(100 * me.TrueWindow.ScaleFactor,100 * me.TrueWindow.ScaleFactor)
magPic.graphics.foreColor=&c2FACE200
magPic.graphics.fillRect(0,0,magPic.width,magPic.height)

dim maskPic as new picture(100 * me.TrueWindow.ScaleFactor,100 * me.TrueWindow.ScaleFactor)
maskPic.graphics.foreColor=&cFFFFFF
maskPic.graphics.FillRect(0,0,maskPic.Width, maskPic.Height)
maskPic.graphics.foreColor=&c00000000
maskPic.graphics.FillRoundRect(0,0,100,100,20,20)

magPic.ApplyMask maskPic
g.drawPicture magPic,0,0[/code][/quote]

It looks like that works. Now I just need to change all of my drawing code to use the Picture constructor rather than BitmapForCaching. Thank you.

Try my code above. It is working as I wrote it. You weren’t filling the context with white (&cFFFFFF) first.

Well it’s probably locked in the picture returned from BitmapForCaching

Try this one:

Function BitmapForCaching(Extends g as Graphics, Width as Integer,  Height as Integer) As Picture
  Dim p as New Picture(Width * g.ScaleX, Height * g.ScaleY)
  // Set the appropriate resolution
  p.HorizontalResolution = 72 * g.ScaleX
  p.VerticalResolution = 72 * g.ScaleY

  // Set the scale factor so drawing to it will be correct
  p.Graphics.ScaleX = g.ScaleX
  p.Graphics.ScaleY = g.ScaleY

  // Very important to remember the mask!
  p.Mask.Graphics.ScaleX = g.ScaleX
  p.Mask.Graphics.ScaleY = g.ScaleY

  // Return the new picture
  Return p
End Function

THANK YOU! You are correct, I was missing the white part inside the round rect. It looks like it is working with BitmapForCaching as I expected. I knew I was missing something simple.

Here is the full code, working as intended. Very clean and simple. Thank you Anthony!

[quote]dim magPic as picture
magPic=TrueWindow.BitmapForCaching(100,100)
magPic.graphics.foreColor=&c2FACE200
magPic.graphics.fillRect(0,0,magPic.width,magPic.height)

dim maskPic as picture
maskPic=TrueWindow.BitmapForCaching(100,100)
maskPic.graphics.foreColor=&cFFFFFF
maskPic.graphics.FillRect(0,0,maskPic.Width, maskPic.Height)
maskPic.graphics.foreColor=&c00000000
maskPic.graphics.FillRoundRect(0,0,100,100,20,20)
magPic.ApplyMask maskPic
g.drawPicture magPic,0,0[/quote]

Happy to help!

Thank you to everyone, I appreciate all of your help. It is possible to use a mask with BitmapForCaching, which lets you avoid handling all of the scale factor math. This is what I was going for.