How to change the color of a canvas graphic?

I have an image of a pen whose insides (fill) I would like to represent a user-selected color. I have two canvas images, both with alpha channels: the black and white outline of the pen and a colored blob that represents the insides of the pen. I can overlay these canvases and have, say, a red fill inside the pen. I want to change the fill color to reflect the user’s choice. I can’t figure out what to do in the second canvas’s Paint event to change the color of the blob to yellow.

g.forecolor=thecolorchosenbytheuser
g.fillrect 0,0,g.width,g.height

The fills the entire canvas with the color, including what should be transparent, so I get a solid filled rect. I want only the image (the shape of the inside of the pen) to be colored.

You would need to know the coordinates you want to fill then: http://documentation.xojo.com/index.php/Graphics.FillPolygon

Thank you, but that would be difficult, it’s not a polygon but an irregular drawing. It would seem that there should be a way to apply a color to any arbitrary shape that would respect the alpha channel. But so far I’ve been stumped.

RGBSURFACE.FLOODFILL

I am not entirely sure what you mean.

But what if you create a new picture object with the same dimensions as the image. Let’s call it p
Fill the graphics of that picture object with the color (or gradient) you want. Use the alpha channel of your image, and assign that to the mask property of your picture.

The image will have the color you want, with the correct shape, given the shape of the mask.

Canvas1.Graphics.DrawPicture p, 0, 0

You can stack several (transparent) picture objects on your canvas, by using the DrawPicture over and over again.

I use this technique to create custom buttons, with button images that match the theme a user can select. They simply select a base color I store in a property.
I added a brightness function that can change the brightness of a color. For instance, the background color has 0.5x the value of the base color. The text or icon has 1.5x the value of a color.

Public Function Brightness(c as Color, value as Double = 1) as color if value = 1 then Return c dim NewColor as color = color.HSVA( c.Hue, c.Saturation, c.Value * value, c.Alpha ) Return NewColor End Function

check out Picture.ApplyMask and CopyColorChannels and CopyMask

Thanks to all who have made suggestions. I’ll have to do some experimenting and I’ll report back.

While looking for some drop-shadow effects, I ran into this article:

Embossing Effects On-The-Fly
It, kind of, explains what I just said in the previous post.

I thought to share it here because it might give you some inspiration.

@Edwin van den Akker Much appreciated.

I’ve gotten two of the methods suggested above to work very nicely. With the “fill” canvas over the outline canvas:

  1. RGBSurface.floodfill

or

  1. Fill the overlying canvas with the desired color and then use picture.ApplyMask so that the unwanted bits are made transparent.

Thanks all.

I’ve run into a bit of a snag with the mask approach. The graphic I’m using for the mask is an image, with 1X and 2X resolutions. But even though the CopyMask function autocompletes when accessing the image, I’m getting an error.

dim p as picture = myImage.copyMask -> CopyMask is not supported for images

on an image copymask cant work since it would have to return an array of pictures because an “image” is made up of one or more pictures

if you want to create a new image that will behave like any other image (that draws the 1x on 1x screens and 2x on 2x screens) you need to iterate over all the items in the image (each one is a picture) and create an equivalent for each item in the image (the 1x 2x 3x etc)

Yes, I see. And on macOS use the ScalingFactor method (i.e. the backingScaleFactor declare) to determine which to use?

i’d just make a new image from an array of pictures that way you dont have to redo it all the time and it works if a person drags your window from a 1x screen to a 2x because you created a multirepresentation image

thats what we do in the IDE

Maybe I’m doing this all wrong, and if so I’d appreciate your feedback. Remember, what I’m doing is drawing a picture where I have a B & W outline and want the fill to reflect a color that the user has chosen. There are two images, the outline and the fill (which I’ll use as the mask). I position one 22 x 22 canvas over another. In one I draw the B & W image. In the other’s in Paint event I do what’s shown below. Basically, a new picture is filled with the desired color and then the “fill” image mask is applied to it. I have to use the window’s scalingFactor to determine which picture to get from the imageset (Highlighter_Color) for the mask.

dim p As Picture
dim pp as picture

if self.ScalingFactor > 1 then //Retina
p = new picture(44, 44)
pp = Highlighter_Color.IndexedImage(0) //retina
else
p = new picture(22, 22)
pp = Highlighter_Color.IndexedImage(1)
end if

pp = pp.CopyMask

p.Graphics.ForeColor = defaultHighlightColor
p.Graphics.FillRect(0, 0, me.height, me.width)

p.ApplyMask(pp)

g.DrawPicture(p, 0, 0)