New Picture from Image problem

Hi,

with the following code I can change the color of a transparent/black icon. However when I pass an Image to this
method the result is not retina compatible. What am I missing?

Thanks.

Dim result As New Picture( p.Width, p.Height, 32 )
result.HorizontalResolution = p.HorizontalResolution
result.VerticalResolution = p.VerticalResolution
result.Graphics.ForeColor = c
result.Graphics.FillRect 0, 0, result.Graphics.Width, result.Graphics.Height
result.Mask.Graphics.ForeColor = &cFFFFFF
result.Mask.Graphics.FillRect 0, 0, result.Mask.Graphics.Width, result.Mask.Graphics.Height
result.Mask.Graphics.DrawPicture p, 0, 0
Return result

You could try using result = Window.BitmapForCaching(p.Width, p.Height) or the code below to handle it yourself and pass the window’s ScaleFactor value which would be 2 for Retina.

Dim result As New Picture( p.Width, p.Height, 32 ) result.HorizontalResolution = p.HorizontalResolution result.VerticalResolution = p.VerticalResolution result.Graphics.ScaleX = 72 * ScaleFactor result.Graphics.ScaleY = 72 * ScaleFactor result.Graphics.ForeColor = c result.Graphics.FillRect 0, 0, result.Graphics.Width, result.Graphics.Height result.Mask.Graphics.ForeColor = &cFFFFFF result.Mask.Graphics.ScaleX = 72 * ScaleFactor result.Mask.Graphics.ScaleY = 72 * ScaleFactor result.Mask.Graphics.FillRect 0, 0, result.Mask.Graphics.Width, result.Mask.Graphics.Height result.Mask.Graphics.DrawPicture p, 0, 0 Return result

See here:

https://documentation.xojo.com/index.php/Window.BitmapForCaching

Thank you for the answer. Unfortunately that code results in an empty picture. If I comment the lines where you set ScaleX and ScaleY it works again. Any ideas?
I am also trying to look at BitmapForCaching but with no luck for the moment…

Check the values (width, height) in the debugger: if they are 0…

I checked they are both 144. What else could it be?

Thanks.

It all depends on the original icon picture and how you are changing the color of it. Try duplicating the icon first without changing the color to see if the change color process is stripping the mask or changing the scale factor/resolution.

Are you passing a retina version icon to the method?

If the icon has a mask then you would need to use the custom BitmapForCaching method on the link I posted which creates a retina compatible version with a mask since the window extension method creates a picture with an alpha channel and no mask.

Also note that if the icon was created in memory then you may need to duplicate it using icon.graphics.width etc rather than icon.width. If the icon was a picture resource dragged into your project then it will not have a graphics object (graphics would be Nil).

@Emile Schwarz you’re right, result.Graphics.Width and result.Graphics.Height become 0 as soon as I set the ScaleX and ScaleY regardless the value (72 or 144)…why?

Something else to consider…

Images are made up of one or more pictures, but the code you’ve written draws onto a single picture. You’ll need to loop through each individual picture in the image, copy to a new one with your change, and then reassemble as a multi-representation picture.

To simply do a copy, your code might be (assuming the image is named pic)

[code]dim pa() as picture

// copy the individual parts
For I as integer = 0 to pic.imagecount-1
Pa.append pic.imageatindex(I)
Next I

// assemble back into an image
Dim newpic as new picture(pa(0).width, pa(0).height, pa)[/code]

@Davide:
I am sorry and I apologyze for giving wrong hint.

Fortunately, this gaves you the opportinuty to find where the error is: if the image you want to draw is width=0 and height=0, there is nothing draw :(…

First of all, follow Greg advice and come back here if this does not resolve your trouble.

Emile.

[quote=331919:@Greg O’Lone] Pa.append pic.imageatindex(I) [/quote]

I could not find the method imageatindex. Did you mean IndexedImage?

Anyway, I tried to implement your suggestion but the result is still non-retina. This is the code:

Public Function ColorizePicture(p As Picture, c As Color) as Picture
  Dim pa() As Picture
  
  for i As Integer = 0 to p.ImageCount-1
    Dim result As New Picture( p.IndexedImage(i).Width, p.IndexedImage(i).Height, 32 )
    result.HorizontalResolution = p.IndexedImage(i).HorizontalResolution
    result.VerticalResolution = p.IndexedImage(i).VerticalResolution
    result.Graphics.ForeColor = c
    result.Graphics.FillRect 0, 0, result.Graphics.Width, result.Graphics.Height
    result.Mask.Graphics.ForeColor = &cFFFFFF
    result.Mask.Graphics.FillRect 0, 0, result.Mask.Graphics.Width, result.Mask.Graphics.Height
    result.Mask.Graphics.DrawPicture p.IndexedImage(i), 0, 0
    pa.Append result
  next
  
  Return New picture(pa(0).width, pa(0).height, pa)
End Function

Am I missing something? Thanks.

Don’t forget to set the scale factors on the Graphics object.

Result.graphics.ScaleX = i
Result.Graphics.ScaleY = i

[quote=331939:@Greg O’Lone]Result.graphics.ScaleX = i
Result.Graphics.ScaleY = i[/quote]

Unfortunately setting

  result.Graphics.ScaleX = i+1
  result.Graphics.ScaleY = i+1

or

  result.Graphics.ScaleX = 3-i
  result.Graphics.ScaleY = 3-i

(which I think is how it should be set) did not fix the problem. The image is still non-retina. I am running out of ideas but to solve this is very important for the application I am working on. So thanks in advance for any help on this.

Btw, how are you determining that the image is not retina?

Doh! Found the issue (I just typed all of this in trying to reproduce your problem).

The problem is that the resolutions in a multi-rez picture are not guaranteed to be in order. In my case they were in reverse order… 216, 144, 72. So…

Add an array to keep track of them and then sort them properly before creating the new picture like this:

[code]Public Function Colorize(img as picture, c as color) as Picture
dim pa() as picture
dim rez() as integer
for i as integer = 0 to img.ImageCount-1
dim pic as picture = img.IndexedImage(i)
dim p as new picture(pic.Width,pic.Height)

// Copy the resolution
p.HorizontalResolution = pic.HorizontalResolution
p.VerticalResolution = pic.VerticalResolution

// Fill the foreground with the color we want
dim g as Graphics = p.Graphics
g.ForeColor = c
g.FillRect 0,0,g.Width,g.Height

// Copy the mask over
p.ApplyMask(pic.CopyMask)

// Add the new picture to the array.
pa.Append p

// We're assuming that horizontal and vertical resoluton are identical here
rez.Append p.HorizontalResolution

next

// Make sure the pics are in low to high rez order
rez.SortWith pa

return new Picture(pa(0).Width,pa(0).Height,pa)

End Function
[/code]

Thank you very much Greg! that solved the problem.