Icon Overlay - Retina

In my app I have @1x and @2x versions of some icons
I need to lay a “badge” over the top in some situations
but it is always choosing the @1x version

If I do this, I get the correct icon based if the screen is or is not Retina

icon = test_icon // test_icon is an imageset with @1x and @2x
do_stuff(icon) 

But if I do this, newIcon is based on the @1x version… I hadn’t even gotten to doing the overlay yet.

icon = test_icon // test_icon is an imageset with @1x and @2x
dim w as integer=icon.Width
dim h as integer=icon.Height
dim newIcon as Picture = new Picture(w,h)
newIcon.Graphics.DrawPicture icon,0,0
// the badge overlay will go here at some point
do_stuff(newIcon) 

Where is this occurring? If performed in a Paint event (like all drawing is supposed to be) the screen resolution isn’t your problem, and Graphics + ImageSet will handle the screen stuff for you.

no it is not in a PAINT event… I am taking a picture object (ICON) and attempting to copy to another (NEWICON) so I can draw on it.
Icon is an ImageSet in the projects Resources.

“DoStuff” basically adds the picture to a row in a Listbox where it is “drawn” in the “paint” event (ie. DrawCellBackground)

As mentioned… if I pass the original icon, everything is fine (except I can’t draw over it)…
And before you say… I can’t make the “changes” in the CellPaint event, as it is in an existing custom control which has multiple instances thru out the project (its my TreeView)

Because you’re creating a new 1x picture.

Try reading this https://blog.xojo.com/2016/04/07/advanced-retinahidpi-bitmapforcaching-and-scalefactorchanged/

[quote=413704:@Sam Rowlands]Because you’re creating a new 1x picture.

Try reading this https://blog.xojo.com/2016/04/07/advanced-retinahidpi-bitmapforcaching-and-scalefactorchanged/[/quote]
Would have been nice if there were some explanation with that snippet of code… From what little was there it makes no reference to what it does, how it does it, or anything else really.

I found the examples for Retina lacking and confusing. Or I’m too stupid.

You need to multiply width and height of your badge picture with the Scalefactor.

You shouldn’t. The frameworks are designed to figure this out for you if everything is set up properly.

While it’s a bit long, I suggest reading my blog post about updating the IDE for Retina…

https://blog.xojo.com/2016/04/05/xojo-retinahidpi-the-journey-of-a-thousand-pixels/

@Greg O’Lone: as I said this topic makes me feel stupid. How is your blog post relevant for Dave’s problem? And why shouldn’t I use the Scalefactor? I can’t even say what this is supposed to do:

Dim p As TrueWindow.BitmapForCaching(100, 100)

I especially dislike that I can’t create a mutable picture from scratch.

[quote=413726:@Beatrix Willius]@Greg O’Lone: as I said this topic makes me feel stupid. How is your blog post relevant for Dave’s problem? And why shouldn’t I use the Scalefactor? I can’t even say what this is supposed to do:

Dim p As TrueWindow.BitmapForCaching(100, 100)

I especially dislike that I can’t create a mutable picture from scratch.[/quote]
Uh… that code you posted does create a mutable picture. It’s Images that are immutable.

A Picture represents a single bitmap while and Image represents one or more bitmaps with different sizes but the same proportions. Once an Image has more than one bitmap, there’s no way for the framework to automatically know which bitmap you want to manipulate when you reference the Image’s Graphics property.

What Dave needs to do is to create a picture using BitmapForCaching and then draw test_icon onto it and then call do_stuff.

@Greg: scratching head. Since when are pictures created from scratch mutable? I just checked in 2018r3 and I’m sure I had to add a few CopyColorChannel operations because the pictures were immutable.

The only time you would have run into this immutable thing is if you dragged a picture into your project and it was added as an Image or if you created a picture using the constructor that creates images:

Constructor(width as integer, height as integer, pics() as picture)

Now, I could see you having to use CopyColorChannels for doing compositing work. If you want to truly copy a picture, it’s much better to copy the color and mask to a new image than it is to draw the picture.

Here’s how I’ve solved the issue in the past. This is for a method which creates a colored icon from a template/mask image.

[code]Function IconWithColor(Icon As Picture, FillColor As Color, Overlay As Picture = Nil) As Picture
Dim Width As Integer = Icon.Width
Dim Height As Integer = Icon.Height

Dim Bitmaps() As Picture
For Factor As Integer = 1 To 3
Dim Mask As Picture = Icon.BestRepresentation(Width, Height, Factor)

Dim Pic As New Picture(Width * Factor, Height * Factor, 32)
Pic.VerticalResolution = 72 * Factor
Pic.HorizontalResolution = 72 * Factor
Pic.Graphics.ForeColor = RGB(FillColor.Red, FillColor.Green, FillColor.Blue)
Pic.Graphics.FillRect(0, 0, Pic.Width, Pic.Height)
Pic.Mask.Graphics.ClearRect(0, 0, Pic.Width, Pic.Height)
Pic.Mask.Graphics.DrawPicture(Mask, 0, 0, Mask.Width, Mask.Height, 0, 0, Mask.Width, Mask.Height)

If Overlay <> Nil Then
  Dim OverlayMask As Picture = Overlay.BestRepresentation(Width, Height, Factor)
  Pic.Mask.Graphics.DrawPicture(OverlayMask, 0, 0, Mask.Width, Mask.Height, 0, 0, OverlayMask.Width, OverlayMask.Height)
End If

Pic.Mask.Graphics.ForeColor = RGB(255, 255, 255, 255 - FillColor.Alpha)
Pic.Mask.Graphics.FillRect(0, 0, Pic.Width, Pic.Height)

Bitmaps.Append(Pic)

Next
Return New Picture(Width, Height, Bitmaps)
End Function[/code]

Basically, the key is creating multiple pictures at each scale factor, throwing them all into an array, and letting the framework figure out what to do with them from there.

[quote=413736:@Greg O’Lone]The only time you would have run into this immutable thing is if you dragged a picture into your project and it was added as an Image or if you created a picture using the constructor that creates images:

Constructor(width as integer, height as integer, pics() as picture)

[/quote]

Can you point me to ANYWHERE in the documentation that says that this creates an image even though a picture is specified?

And what the difference between an image and a picture is?

How is one supposed to figure this out???

It’s in Xojo’s documentation folder, DesktopHiDPISupport/WebHiDPISupport.PDF.

The HiDPI guide is as clear as mud and contains no code AT ALL. There are problems in terminology confusing picture and image:

[quote]An Image Set project item can contain pictures at three different scales.
Picture files added to the project in Image Sets are treated as Images.[/quote]

Images are the icons that you drag into a project. 3 of them make an Image Set. In code we have the Picture. Which have a confusing relationship to the Image Set.

The Picture itself has problems because it’s sometimes mutable and sometimes not. For this you have the wonderful table in the HiDPI guide. I can understand where this comes from: backwards compatibility. Unfortunately, this comes at the price of code clarity.

In most cases the HiDPI works without changes. Only for situations like Dave’s above it becomes confusing.

I finally gave up, and did what I had not wanted to do.
Where there was needed was for an icon the was passed to a custom control… I wanted to place an overlay on it before passing it.

What I did finally was to modify the control itself… not optimal as now the version for this project is different than any other project. The saving grace, was I wrote the original control so modifiying it was not difficult… I had not wanted to “branch” the code for what seemed such a simple thing.

I know for sure that it’s documented in the DesktopHiDPISupport.pdf file located in the Documentation folder next to the IDE and at http://developer.xojo.com/hidpi-support.

There is says:

[quote]Constructor(width As Integer, height As Integer, bitmaps() As Picture)
This new constructor creates an Image from one or more bitmaps. The height and width are in points. The bitmaps are copied upon creation. All bitmaps must have the same aspect ratio. A bitmap’s DPI will be calculated from the point size and the bitmap’s size in pixels. Pictures created using this constructor have their Type set to Types.Image.[/quote]

Same document, Page 1, listed under Image Sets and Image.

Read the docs?

[quote=413757:@Beatrix Willius]The HiDPI guide is as clear as mud and contains no code AT ALL. There are problems in terminology confusing picture and image:

An Image Set project item can contain pictures at three different scales.
Picture files added to the project in Image Sets are treated as Images.
Images are the icons that you drag into a project. 3 of them make an Image Set. In code we have the Picture. Which have a confusing relationship to the Image Set.[/quote]
Ironically, the reason why we used Picture as the base class for Image was to make your projects backward compatible.

I’m not convinced that we have terminology conflicts though.

A Picture object which comes from an old, pre-HiDPI project or one created in code using New Picture(width, height, [depth]) is a single resolution bitmap which can be directly manipulated just like it always could. You can use the Graphics drawing methods to draw directly onto a picture created this way.

An Image object is a Picture object which contains more than one bitmap, at multiple resolutions but with all images having the same proportions. For example, you could have an icon that is 32x32 @100%, 64x64 @200% and 96x96 @ 300%. Each progressive bitmap is more detailed for a higher resolution screen. As I mentioned above, Image objects cannot be mutable because the framework wouldn’t be able to figure out which bitmap to draw on. Another thing to note about Image objects is that the framework will attempt to be intelligent when choosing which bitmap to draw, so if you were to draw the example image at half size (16x16) on a retina/HiDPI screen, the framework will actually choose to draw the 32x32 icon so there will be less pixel blending.

If you have an Image object, you can access the individual bitmaps by using the ImageCount and IndexedImage methods to extract them.

I’ll make an example of how you might overlay one Image object onto another. Gimme a few minutes…

Ok, here’s a project which includes an extension method which allows you to overlay pictures & images onto one another.

https://www.dropbox.com/s/ftt08tdl4jq6bfl/ImageOverlayTest.zip?dl=1

Look at the Window1.Canvas1.Paint event to see how it’s used.