Image Opacity Mask to apply color to image possible in Xamarin.Forms?

In WP8.0, you can do this

<Rectangle Width="24" Height="24" Fill="#F00"> <Rectangle.OpacityMask> <ImageBrush Stretch="Fill" ImageSource="LikeIcon.png" /> </Rectangle.OpacityMask> </Rectangle>

And in WP8.1:

<BitmapIcon Height="24" Width="24" Foreground="#F00" UriSource="LikeIcon.png" />

To turn this into this


Is it possible to color images in this fashion with Xamarin.Forms?

(#F00 is just a sample, clearly the second image is not red)

Posts

  • AnthonyRamirezAnthonyRamirez USUniversity ✭✭✭
    edited June 2015

    Hi @ChrisZorn

    I would like to achieve the same effect.

    Were you able to achieve?

    Thanks

  • AnthonyRamirezAnthonyRamirez USUniversity ✭✭✭

    Oh wow @AndreiNitescu! What a great way to wake up :smiley:

    Thanks very much for this, I appreciate it ;)

  • AndrewMobileAndrewMobile USMember ✭✭✭✭

    Updated the readme with more info

  • RodyRody USMember ✭✭

    Great work @AndreiNitescu ! I did have to add one line of code in the Android Renderer to account for Alpha because the LightingColorFilter ignores the Alpha channel.

    All that was needed:

    d.Alpha = Element.Foreground.ToAndroid().A;

    Right after your line where the color filter gets set:

    d.SetColorFilter(new LightingColorFilter(Element.Foreground.ToAndroid(), Element.Foreground.ToAndroid()));

  • AndrewMobileAndrewMobile USMember ✭✭✭✭
    edited November 2015

    @Rody thanks for info, a pull request would be absolutely amazing :smiley:

  • RodyRody USMember ✭✭

    @AndreiNitescu said:
    @Rody thanks for info, a pull request would be absolutely amazing :smiley:

    I made a pull request. My GitHub user name is rd09. Thanks again for this great custom renderer!

  • RodyRody USMember ✭✭

    I have another update to suggest for the Android Renderer. I was having issues with this working with a particular color (#979797). For some reason it was not applying that color to the image, so I modified the SetColorFilter to use a Color instead of a ColorFilter.

    Old line:
    d.SetColorFilter(new LightingColorFilter(Element.Foreground.ToAndroid(), Element.Foreground.ToAndroid()));

    New line:
    d.SetColorFilter(Element.Foreground.ToAndroid(), PorterDuff.Mode.SrcAtop);

    You still have to set the Alpha separately.
    d.Alpha = Element.Foreground.ToAndroid().A;

  • DiegoMDiegoM USMember

    Hi, i'm using @AndrewMobile renderer for my project but there's an error

    var d = Resources.GetDrawable((int)Element.Source).Mutate();

    this line doens't work because he can't cast the source to int. Plus there's a warning because GetDrawable is deprecated.

    I'm currently trying to change the foreground color of a EmbeddedResource png file located in my PCL project.

  • DanielLDanielL PLInsider ✭✭✭✭

    CachedImage from FFImageLoading support this out of box with TintTransformation. Something like this:

    <ffimageloading:CachedImage HorizontalOptions="Center" VerticalOptions="Center"
        WidthRequest="300" HeightRequest="300"
        DownsampleToViewSize="true"
        Source = "http://loremflickr.com/600/600/nature?filename=simple.jpg">
        <ffimageloading:CachedImage.Transformations>
            <fftransformations:TintTransformation HexColor="#21212121"/>
        </ffimageloading:CachedImage.Transformations>
    </ffimageloading:CachedImage>
    
  • DiegoMDiegoM USMember

    @DanielL awesome we were already using ffimageloading but i didn't knew there was this kind of transformations. It works well for one image but my image is displayed in the cell of a list so i'd like to change the set the color for each image.
    But i think you are only applying the latest tintcolor without copying the image right ?

  • JensDemeyJensDemey USMember ✭✭
    edited October 2016

    If I recall correctly I think that FFimageloading caches each transformation independently. As I didn't really want this (since I have many color transformations) I also tried to apply a ColorFilter to the CachedImage via a custom renderer. (This way it always loads the same CachedImage fast and applies the ColorFilter instantly after) This all worked fast and good except for the fact that the filter applied itself to all images with the same source. I still have to find a workaround for that.

    @DanielL
    Perhaps you have more insight as to why it's applying the filter to all images with the same source?

  • DanielLDanielL PLInsider ✭✭✭✭
    edited October 2016

    @DiegoM

    You just need to change Transformations property for each cell independently to contain a new instance of TintTransformations with correct parameters. (It's a bindable property, so you can bind it to your ViewModel)

    @JamesDemey is right. All transformations are cached independently (the key is: image source + transformation name + its parameters). You can change this behavior when you use custom cache key factory (and have the same keys). Are you sure it isn't the case?

  • DiegoMDiegoM USMember
    edited October 2016

    I'm using this kind of ViewCell

        <ViewCell>
               <Grid>
                         <Label FormattedText="{Binding Text}" TextColor="{Binding Color}" />
                <ffimageloading:CachedImage Source="{tools:ImageResource MyApp.Resources.Images.clock.png}">
                             <ffimageloading:CachedImage.Transformations>
                                   <fftransformations:TintTransformation HexColor="{Binding Color}"/>
                             </ffimageloading:CachedImage.Transformations>
                                            </ffimageloading:CachedImage>
                </Grid>
       </ViewCell>
    

    It's simplified but the essential is there. And when i'm testing the app the label gets the right color but the image is painted with the same color

    It should create a new instance for each no ?

  • DanielLDanielL PLInsider ✭✭✭✭
    edited October 2016

    It's this HexColor="{Binding Color}"

    Transformations are cross platform and don't support binding. You must bind the whole Transformations property or create a custom Converter for bindings which will return List<ITransformation> .

  • DiegoMDiegoM USMember

    @DanielL Thanks a lot for the rapid answer :) FFImageLoading is really good for handling a lot of things

  • JensDemeyJensDemey USMember ✭✭
    edited October 2016

    @DanielL I have tried a few approaches:

    This is just the normal (correct) approach using a color filter on a normal XF Image using:
    Control.SetColorFilter(new PorterDuffColorFilter(RainbowColors.ElementAt((Element as ImageEx).Index).ToAndroid(), PorterDuff.Mode.SrcAtop));

    As you can see this just show 80 images and then applies a color to all of them. The colors reprensent the rainbow colors.

    Next is the same approach but with a cached image (no custom key or transformations)

    Here you can see that the color fitlers are applied incorrectly. (For some reason it works a little better when I don't update them all at the same time)

    And lastly this is an approach with a custom key cache that returns an unique string (Guid)

    As for the third sample, it's the opposite. The filter works but the images load very slow.

  • DanielLDanielL PLInsider ✭✭✭✭

    @JensDemey Control.SetColorFilter is called on a drawable. FFImageLoading reuses the same instance of drawables, it may be the cause of your issues (filter is applied on a drawable which will actually be used in another image view). I think I'll need to override that methods in our custom drawables implementation to avoid those kind of issues.

    BTW: Did you try to use CachedImage with TintTransformation? It should have a better performance, doesn't have any issues and you should have the same results too.

  • JensDemeyJensDemey USMember ✭✭

    @DanielL I would definitely prefer to use CachedImage with TintTransformation.
    The problem is that every transformation gets added to the cache. For example that page with all the rainbow colors takes about 25% of the max total cache.

    current total count: 101
    cumulative additions: 101
    cumulative removals: 78
    total evictions: 0
    total cache hits: 0
    reuse hits: 0
    reuse misses: 0
    reuse pool count: 101
    gc threshlold:         53687092
    cache size in bytes:   13089600
    reuse pool in bytes:   13089600
    current evicted bytes: 0
    high watermark:        53687092
    low watermark:         17895697
    total force gc collections: 0
    

    Using the colorfilter instead allows me to keep the total cache size for that page to that of just one image.

    I have also tried to use a CustomKeyFactory

    class CustomCacheKeyFactory : ICacheKeyFactory
    {
        public string GetKey(Xamarin.Forms.ImageSource imageSource, object bindingContext)
        {
            return "samekey";
        }
    }
    

    But it still seems to cache all the images independently.

  • DanielLDanielL PLInsider ✭✭✭✭
    edited October 2016

    I don't think that every transformation is added to cache if it's one image source and the same transformation (with the same params). Transformations have own internal cache keys. This is the internal key for TintTransformation:

    public override string Key
    {
        get 
        { 
            return string.Format("TintTransformation,R={0},G={1},B={2},A={3},HexColor={4}", 
                                 R, G, B, A, HexColor); 
        }
    }
    

    As you see, only modified params of transformation are cached.

    Algorithm for loading from memory cache is:

    • If not already in memory, try to see if a version of image without transformations is in memory (base image)
    • If succeed transform base image without transformations (which is already cached) and put it into memory with key imagesource+downsampleparams+transformationsdetails
    • If not succeed, load from disk, transform image and put it into memory with key imagesource+downsampleparams+transformationsdetails

    BTW: To speed up your loading with multiple different transformations you could preload base image into memory cache, so your future transformations will use an image which is already loaded in memory (it will be faster).

    You shouldn't worry about 25% cache usage, those entries are automatically evicted when not used anymore.

  • JensDemeyJensDemey USMember ✭✭

    @DanielL Wonderful, they do indeed seem to be getting evicted. I didn't know about that. Thank you very much for your help!

Sign In or Register to comment.