Pics, Masks, PNG, Hi-DPI, and I Don't Know What I'm Doing

Being a longtime REAL/Xojo user, I’m really confused about images and masks and transparency - now with Hi-DPI which I need to support.

I keep losing my transparency. Let’s be simple.

I have a PNG called ‘player_play’ with a transparent background. It’s 200px and it’s in my Hi-DPI app as a Image. I want to display it in a 32x32 Canvas object. I do this in the Open event, and I lose my transparency.

I used to be able to deal with this much better, but with Hi-DPI I don’t have the transparency options, and ‘player_play’ doesn’t seem to show any Mask when I look at it in the debugger under Contents.

Dim P As Picture
P = Copy32(player_play)
Me.Backdrop = P

Function Copy32(SrcPic As Picture) As Picture
Dim P as Picture
Dim g As Graphics
P = New Picture(32, 32, 32)
g = P.Graphics
g.DrawPicture(SrcPic, 0, 0, 32, 32, 0, 0, SrcPic.Width, SrcPic.Height)
return P
End Function

I know I’m 10 years behind here, how do I keep my transparency in Hi-DPI?

Ok, first of all make sure you are using “images” in the IDE so you get the benefits of the HiDPI system. It’ll automatically get you the right resolution for what you’re trying to do. Keep in mind that Images can’t be written to.

Second, stop using the Picture constructor that takes takes bits. If at all possible use Window.BitmapForCaching. It will get you a picture that is configured for the screen that the window is on.

// — You need a 2x image for HIDPI.
p = new picture( 64, 64 )

Then at the end of the routine, add the following line. It’s not “technically” correct as your image only contains a 2x image and not a 1x image, but it is enough to fool the system.
return new picture( 32, 32, array( p ) )

In 99% of drawing you can forget that the picture is HiDPI. This is enough for drawing a HiDPI picture scaled:

g.DrawPicture(ButtonPicture_dark, 0, 0, me.Width, me.Height, 0, 0, ButtonPicture_dark.Width, ButtonPicture_dark.Height)

I don’t think Copy32 is doing what you think it is doing.

I’m guessing you are expecting it to copy the source picture foreground and alpha / mask scaled to 32x32 pixels into P so that P has the scaled mask / alpha channel.

I read the code as compositing (merging / flattening) the source picture scaled to 32x32 pixels into P which means P will be opaque.

keving: Well, you’re right, but the problem is that, with Hi-DPI selected, I can’t do all the copying of Mask and transparency things I used to be able to do. So Copy32 is doing what I think it is, and I know it’s not enough, which was the essence of my question. As Greg implied, the manual does say about BitmapForCaching: “This image [created by BitmapForCaching] supports Alpha Channels (not masked images)”.

Use code that supports alpha channels since PNGS natively have alpha channels NOT masks

We just went through this

For some reason the debugger doesn’t seem to display pictures with alpha channels correctly which i’ve logged in Feedback: [<https://xojo.com/issue/61257>](<https://xojo.com/issue/61257>)

Below is a quickly cobbled together function that should scale down a picture containing an alpha channel into a new picture with an alpha channel.

Hi-DPI obviously complicates things as you can no longer assume 32 x 32 pixels.

If you can, avoid pre-scaling pictures and just draw / scale them in the paint event as the graphics object.

If you must prepare pictures in advance there appears to be two methods:

  1. For content that changes, use BitmapForCaching which will create a picture to draw into at the current scale. However, you should then also handle the ScaleFactorChanged event for when the scale changes as you will need to recreate the content at the new scale. These blog posts from 2016 might be useful:
    http://blog.xojo.com/2016/04/05/xojo-retinahidpi-the-journey-of-a-thousand-pixels/
    https://blog.xojo.com/2016/04/07/advanced-retinahidpi-bitmapforcaching-and-scalefactorchanged/

  2. For static resources, create a picture containing a set of pictures the represent the different resolutions and let the OS choose the appropriate version. Depending on what your content is you might be able to scale a single source picture for both resolutions or you might want two different representations. Here is an example using my scale function:

    Dim p(1) As Picture
    p(0) = ScalePictureWithAlpha(sourcePicture, 32, 32)
    p(1) = ScalePictureWithAlpha(sourcePicture, 64, 64)
    
    mImageSet_ = New Picture(32, 32, p)
    

    (where mImageSet_ is a Picture property that you could then draw within the Paint event)

NOTE. I haven’t used Hi-DPI much so take all of this with a pinch of salt. I’m sure others can probably give you much better advice.

Public Function ScalePictureWithAlpha(pSourcePicture As Picture, pWidth As Int32, pHeight As Int32) as Picture
Dim theResult As Picture
Dim tempMask As Picture

theResult = New Picture(pWidth, pHeight)
theResult.HorizontalResolution = pSourcePicture.HorizontalResolution
theResult.VerticalResolution = pSourcePicture.VerticalResolution

theResult.Graphics.DrawPicture(pSourcePicture.CopyColorChannels, 0, 0, pWidth, pHeight, 0, 0, pSourcePicture.Width, pSourcePicture.Height)

tempMask = New Picture(pWidth, pHeight, 32)
tempMask.Graphics.DrawPicture(pSourcePicture.CopyMask, 0, 0, pWidth, pHeight, 0, 0, pSourcePicture.Width, pSourcePicture.Height)

theResult.ApplyMask(tempMask)

Return theResult
End Function

What I’ve seen, transparency within is gone, mask is no longer used. Instead of, create the pictures with transparency first.