Mask / ALpha trouble in iOS

I have a square picture.
I need a copy of that , cropped to a circle, with the outside to be transparent.

I have tried drawing in a color that has the alpha portion set to transparent, but that doesnt set transparency, it just fails to draw.

I have tried
copyofpicture.applymask name_of_image_with_big_black_Circle

… but Applymask fails with a message to say that the size of the mask is different. (It isnt)

I have tried using an x,y loop to test the pixels of the mask, and to set
target.graphics.pixel(x,y) = source.graphics.pixel(x,y)

I get a compile error to say that graphics doesnt have a .pixel property, despite it showing up perfectly well in autocomplete.

So I’m stuck… how do I end up with just the circular area to draw?

It depends on how you’re constructing your picture object. It’s going to vary based on the source. Immutable picture objects (such as those you drag in to the project) cannot have a mask applied directly, so you’ll need an intermediate picture object.

You then need to construct your mask Picture to match the Picture it’s being applied to, which is a bit of a pain. You need to handle the X/Y scaling factors and the Horizontal and Vertical resolutions.

In this example, I’m using an image dragged in to the project named “avatar”

Sub Paint(g As Graphics) Handles Paint
  var p as new Picture( avatar.Width, avatar.Height )
  p.Graphics.DrawPicture( avatar, 0, 0 )
  
  var mask as new Picture( p.Width, p.Height )
  mask.HorizontalResolution = p.HorizontalResolution
  mask.VerticalResolution = p.VerticalResolution
  var m as Graphics = mask.Graphics
  m.ScaleX = g.ScaleX
  m.ScaleY = g.ScaleY
  
  m.DrawingColor = &cffffff
  m.FillRectangle( 0, 0, m.Width + 10, g.Height )
  m.DrawingColor = &c000000
  m.FillOval( 0, 0, m.Width, m.Height )
  
  p.ApplyMask( mask )
  
  g.DrawPicture( p, 0, 0 )
End Sub

It’s a bit easier if you’re drawing the initial Picture in Xojo because both will be setup identically by the framework, but you may still wish to use the above bit of code. Here’s a simple one for a source image created using Xojo:

Sub Paint(g As Graphics) Handles Paint
  var p as new Picture( 256, 256 )
  p.Graphics.DrawingColor = &cff0000
  p.Graphics.FillRectangle( 0, 0, p.Graphics.Width, p.Graphics.Height )
  
  var mask as new Picture( p.Width, p.Height )
  var m as Graphics = mask.Graphics
  
  m.DrawingColor = &cffffff
  m.FillRectangle( 0, 0, m.Width + 10, g.Height )
  m.DrawingColor = &c000000
  m.FillOval( 0, 0, m.Width, m.Height )
  
  p.ApplyMask( mask )
  
  g.DrawPicture( p, 0, 0 )
End Sub

I use the first implementation I provided above and have a Shared Method on a common subclass called BitmapForCaching (to match the desktop implementation that has this done for you as a member of the DesktopWindow class), and another that will accept a picture as parameter then construct and return an appropriate mask Picture for it as well as a mutable Picture (just a copy of the original).

1 Like

Thanks for responding. Thats going to take some digesting.
Seems like we’ve really gone backwards in discoverability in recent years. :frowning:

It’s a bit easier if you’re drawing the initial Picture in Xojo because both will be setup identically by the framework

See, I thought I had done that…
I’d made new pictures and copied to the mutable ones… they SHOULD have been the ‘same thing’
But I’ll give these suggestions a go in the morning. Thanks.

I guess I can distill it down a bit more. Here’s a function that will take a picture and create an appropriate mask:

Private Function createMaskPicture(source as Picture) As Picture
  if source = nil then Return nil
  
  var result as new Picture( source.Width, source.Height )
  result.HorizontalResolution = source.HorizontalResolution
  result.VerticalResolution = source.VerticalResolution
  var g as Graphics = result.Graphics
  g.ScaleX = source.Graphics.scaleX
  g.ScaleY = source.Graphics.scaleY
  
  Return result
End Function

Then used likes this:

Sub Paint(g As Graphics) Handles Paint
  var p as Picture = new Picture( avatar.Width, avatar.Height )
  p.Graphics.DrawPicture( avatar, 0, 0 )
  
  var mask as Picture = createMaskPicture( p )
  if mask <> nil then
    var m as Graphics = mask.Graphics
    m.DrawingColor = &cffffff
    m.FillRectangle( 0, 0, m.Width + 10, g.Height )
    m.DrawingColor = &c000000
    m.FillOval( 0, 0, m.Width, m.Height )
  
    p.ApplyMask( mask )
  end if
  
  g.DrawPicture( p, 0, 0 )
End Sub

Actually, I just tried method 1.
After .applymask, I end up with the mask in the picture, and a nil mask
Something’s not right there…

StitchedImage_Round =new picture (StitchedImage_Square.width, StitchedImage_Square.height)
var mask as new Picture( StitchedImage_Round.Width, StitchedImage_Round.Height )
mask.HorizontalResolution = StitchedImage_Round.HorizontalResolution
mask.VerticalResolution = StitchedImage_Round.VerticalResolution
var m as Graphics = mask.Graphics
m.ScaleX = StitchedImage_Round.graphics.ScaleX
m.ScaleY = StitchedImage_Round.graphics.ScaleY

m.DrawingColor = &cffffff
m.FillRectangle( 0, 0, StitchedImage_Round.Width + 10, StitchedImage_Round.Height +10 )
m.DrawingColor = &c000000
m.FillOval( 0, 0, m.Width, m.Height )

StitchedImage_Round.ApplyMask( mask )

I rarely inspect pictures and masks in the debugger because they’re often incorrect, especially for iOS. Do actual drawing.

They are right though…
image

Something to consider regarding the debugger…

Masks are not the same thing as an alpha channel. Masks are a leftover concept from when Xojo’s pictures kept their transparency information separate from the image itself, that is, a 24-bit image with an 8-bit mask. Remember how you could select a picture and then choose a grayscale image to be its “mask”?

These days, images that can have transparency built in (like PNGs) are 32-bit (24+8) but the transparency is considered to be part of the image. The debugger just isn’t aware of that.

I get that, but it still shows images incorrectly when scaled as well. It’s just not a dependable way to see what, exactly, is drawn to a Picture object.

2 Likes

It doesn’t help that CopyMask and ApplyMask do different things depending on what kind of image it’s working on.

Whatever the rationale, I end up with a picture that has a big black circle when I draw it.
It isn’t working as a mask.
But having defined a picture with 2 parameters, it uses alphachannel anyway, doesn’t it?
I’d actually expect a mask to be ignored.

Frustrating, but Im just not getting
“Image with transparent bits” if I start with 'Image with full coverage" … I cannot make a red pixel transparent after it is solid.

If you look in the code I supplied, before calling DrawOval I’m setting the color to black, which is fully opaque. If you want a different level of transparency then you use a grayscale color between white and black.

1 Like

I’m not explaining the issue properly, then. My fault.
I need to end up with something like this:

image

where the black area is fully transparent, and I’m starting with an image like this:

image

if I use a grey circle mask, I’d end up with a transparent blue area.
Nothing I tried so far makes the green and red go away, leaving a solid blue area.

Such that drawing the image over a picture of a wall, I see brick around the circle, not through it.

What I’ve provided you with should work, you just need to calculate the X/Y offsets and width/height for the opaque area of the mask that is applied. Really, though, that’s a horrible starting image. Assuming you have the source image for that icon, just export as PNG with alpha channel and you won’t need to do any this.

1 Like

I don’t.

Thats a bit like an old joke I half recall from years ago about asking directions in Ireland.
‘Well, I wouldnt start from here’
The image above is a quickly drawn example of what I need to do with several images where I dont have the luxury of a vector starting point.
I want to change the alpha information of parts of an existing image.

What I’ve provided you with should work

Yes it should.
So far, however… :frowning:
I’ll keep trying.

Well, I can tell you that you’re also going to have to cut in to the edges a fair bit with your mask to kill that background color, otherwise you’ll end up with blocks of green surrounding the icon in some places.

You can license the original for $12 and receive the EPS version here:
https://www.istockphoto.com/vector/round-protective-face-mask-symbol-or-icon-gm1256695690-368048418

That’s probably the way you should go. If you previously licensed it but don’t have the original any longer, surely you can retrieve the EPS from your account.

Regardless, rather than trying to hack something together, I’d either license the original images and create PNGs or swap out the errant images.

LOL. Thanks, but you’re focussing on the wrong thing entirely.

The mask was just a quickly drawn example. I don’t need a picture of a mask.
Here’s a better example:

image

I just want to get a circular image from any square image.

Then this post has all you need:

Morning.!
Yes, that post does the trick.
Cant immediately spot how it differs from ‘method 1’ as was, but it definitely works.
Thank you.