How to change the color of a canvas graphic?

  1. 3 months ago

    Jonathan A

    Aug 10 Pre-Release Testers Maryland, USA

    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.

  2. Dave S

    Aug 10 San Diego, California USA
    g.forecolor=thecolorchosenbytheuser
    g.fillrect 0,0,g.width,g.height
  3. Jonathan A

    Aug 10 Pre-Release Testers Maryland, USA

    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.

  4. Tim P

    Aug 10 Pre-Release Testers, Xojo Pro ⭐️

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

  5. Jonathan A

    Aug 10 Pre-Release Testers Maryland, USA

    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.

  6. Dave S

    Aug 10 San Diego, California USA

    RGBSURFACE.FLOODFILL

  7. Edwin v

    Aug 10 Pre-Release Testers, Xojo Pro The Netherlands
    Edited 3 months ago

    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
  8. Norman P

    Aug 10 Xojo Inc

    check out Picture.ApplyMask and CopyColorChannels and CopyMask

  9. Jonathan A

    Aug 10 Pre-Release Testers Maryland, USA

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

  10. Edwin v

    Aug 10 Pre-Release Testers, Xojo Pro The Netherlands
    Edited 3 months ago

    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.

  11. Jonathan A

    Aug 10 Pre-Release Testers Maryland, USA

    @Edwin vden ;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

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

    Thanks all.

  12. Jonathan A

    Aug 11 Pre-Release Testers Maryland, USA
    Edited 3 months ago

    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

  13. Norman P

    Aug 11 Xojo Inc

    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)

  14. Jonathan A

    Aug 11 Pre-Release Testers Maryland, USA

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

  15. Norman P

    Aug 11 Xojo Inc

    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

  16. Jonathan A

    Aug 11 Pre-Release Testers Maryland, USA

    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)

or Sign Up to reply!