Android ImageRenderer

FrancoisMFrancoisM FRUniversity ✭✭

Hi,

I have the below code. I tried many way of doing it but basically it's like SetImageBitmap doesn't change the image once it's been displayed not matter if it's called again. I mean the first display works but when I change the BlurRadius I can see that SetBitmap is called but it has no effect on screen. What am I missing?
Note: CreateBlurredImage comes from Xamarin's documentation and GetImageFromImageSource from https://github.com/TheRealAdamKemp/BlurredImageTest/blob/master/Droid/BlurredImageRenderer.cs

public class BlurredImage : ImageRenderer
    {
        ImageSource BaseSource;
        Bitmap BaseImage;
        Dictionary<double, Bitmap> BlurredImages = new Dictionary<double, Bitmap>();

        protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
        {
            BaseSource = Element.Source;
            var img = new ImageView(Forms.Context,null);
            SetNativeControl(img);
            SetBlurImage();          
        }

        protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            //base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == XamarinCommon.Views.BlurredImage.BlurRadiusProperty.PropertyName)
                SetBlurImage();
        }

        private async Task SetBlurImage()
        {
            var rcv = Element as XamarinCommon.Views.BlurredImage;
            if(BaseImage == null)
                BaseImage = await GetImageFromImageSource(BaseSource);
            if (rcv.BlurRadius == 0 && !BlurredImages.ContainsKey(0))
                BlurredImages.Add(0, BaseImage);
            if (rcv.BlurRadius != 0 && !BlurredImages.ContainsKey(rcv.BlurRadius))
                BlurredImages.Add(rcv.BlurRadius, CreateBlurredImage(BaseImage, rcv.BlurRadius));

            Control.SetImageBitmap(BlurredImages[rcv.BlurRadius]);
        }

        private Bitmap CreateBlurredImage(Bitmap BaseImage, double radius)
        {
            // Create another bitmap that will hold the results of the filter.
            Bitmap blurredBitmap;
            blurredBitmap = Bitmap.CreateBitmap(BaseImage);

            // Create the Renderscript instance that will do the work.
            RenderScript rs = RenderScript.Create(Forms.Context);

            // Allocate memory for Renderscript to work with
            Allocation input = Allocation.CreateFromBitmap(rs, BaseImage, Allocation.MipmapControl.MipmapFull, AllocationUsage.Script);
            Allocation output = Allocation.CreateTyped(rs, input.Type);

            // Load up an instance of the specific script that we want to use.
            ScriptIntrinsicBlur script = ScriptIntrinsicBlur.Create(rs, Android.Renderscripts.Element.U8_4(rs));
            script.SetInput(input);

            // Set the blur radius
            script.SetRadius((float)(10.0 * radius));

            // Start the ScriptIntrinisicBlur
            script.ForEach(output);

            // Copy the output to the blurred bitmap
            output.CopyTo(blurredBitmap);

            return blurredBitmap;
        }

        private async Task<Bitmap> GetImageFromImageSource(ImageSource imageSource)
        {
            IImageSourceHandler handler;
            if (imageSource is FileImageSource)
                handler = new FileImageSourceHandler();
            else if (imageSource is StreamImageSource)
                handler = new StreamImagesourceHandler(); // sic
            else if (imageSource is UriImageSource)
                handler = new ImageLoaderSourceHandler(); // sic
            else
                throw new NotImplementedException();
            return await handler.LoadImageAsync(imageSource, Forms.Context);
        }

    }
Tagged:

Answers

  • DanielLDanielL PLInsider ✭✭✭✭
    edited November 2015

    Dictionary<double, Bitmap> BlurredImages = new Dictionary<double, Bitmap>();

    That could lead to memory problems when doing a lot of radius changes. It's better to use WeakReferences .

    Maybe that would help:
    https://github.com/molinch/FFImageLoading/blob/master/FFImageLoading.Transformations.Droid/BlurredTransformation.cs

    public static Bitmap ToBlurred(Bitmap source, Context context, float radius)
            {
                if ((int)Android.OS.Build.VERSION.SdkInt >= 17)
                {
                    Bitmap bitmap = Bitmap.CreateBitmap(source.Width, source.Height, Bitmap.Config.Argb8888);
    
                    using (Canvas canvas = new Canvas(bitmap))
                    {
                        canvas.DrawBitmap(source, 0, 0, null);
                        using (Android.Renderscripts.RenderScript rs = Android.Renderscripts.RenderScript.Create(context))
                        {
                            using (Android.Renderscripts.Allocation overlayAlloc = Android.Renderscripts.Allocation.CreateFromBitmap(rs, bitmap))
                            {
                                using (Android.Renderscripts.ScriptIntrinsicBlur blur = Android.Renderscripts.ScriptIntrinsicBlur.Create(rs, overlayAlloc.Element))
                                {
                                    blur.SetInput(overlayAlloc);
                                    blur.SetRadius(radius); 
                                    blur.ForEach(overlayAlloc);
                                    overlayAlloc.CopyTo(bitmap);
    
                                    rs.Destroy();
                                    return bitmap;
                                }
                            }
                        }
                    }
                }
    
                return ToLegacyBlurred(source, context, (int)radius);
            }
    

    BTW: FFImageLoading has a blur transformation for all platforms and it's a Image class replacement.
    https://github.com/molinch/FFImageLoading/

  • FrancoisMFrancoisM FRUniversity ✭✭

    The Library looks great I'll look into it asap! Tks a lot.
    However I pasted a function to blur the image, how would that help as I don't think my issue it blurring but actually changing the image on screen?

  • DanielLDanielL PLInsider ✭✭✭✭
    edited November 2015

    SetImageBitmap should work just fine.

    • When I was writing BlurTransformation for FFImageLoading I had some issues that image generated without blur effect or was null. This code is what I did to eliminate that (don't remember details though)
    • I think the problem could also be in using Dictionary for Bitmap caching. Please note that Bitmap is a java managed object. You should really use at least WeakReferences to avoid memory problems.
  • DanielLDanielL PLInsider ✭✭✭✭
    edited November 2015

    Ok... got it... you have to be sure that you call SetImageBitmap on UI thread! (I see async method there). I'm quite sure it's the reason it's not working.

  • FrancoisMFrancoisM FRUniversity ✭✭

    It sounds like it however I modified the code accordingly but no effect on screen.

    public class BlurredImage : ImageRenderer
        {
            ImageSource BaseSource;
            Bitmap BaseImage;
            Dictionary<double, Bitmap> BlurredImages = new Dictionary<double, Bitmap>();
    
            protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
            {
                //base.OnElementChanged(e);//sinon on voit la baseimage de tps en tps
                BaseSource = Element.Source;
                var img = new ImageView(Forms.Context,null);
                SetNativeControl(img);
                DoTaskOnMainThread();          
            }
    
            protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
            {
                //base.OnElementPropertyChanged(sender, e);
                if (e.PropertyName == XamarinCommon.Views.BlurredImage.BlurRadiusProperty.PropertyName)
                    DoTaskOnMainThread();
            }
    
            private void DoTaskOnMainThread()
            {
                SetBlurImage()
                .ContinueWith(task =>
                {
                    Bitmap bmp = task.Result;
                    Control.SetImageBitmap(bmp);
                }, TaskScheduler.FromCurrentSynchronizationContext());
            }
    
            private async Task<Bitmap> SetBlurImage()
            {
                var rcv = Element as XamarinCommon.Views.BlurredImage;
                if(BaseImage == null)
                    BaseImage = await GetImageFromImageSource(BaseSource);
                if (rcv.BlurRadius == 0 && !BlurredImages.ContainsKey(0))
                    BlurredImages.Add(0, BaseImage);
                if (rcv.BlurRadius != 0 && !BlurredImages.ContainsKey(rcv.BlurRadius))
                    BlurredImages.Add(rcv.BlurRadius, CreateBlurredImage(BaseImage, rcv.BlurRadius));
                return BlurredImages[rcv.BlurRadius];
    
            }
    
            private Bitmap CreateBlurredImage(Bitmap BaseImage, double radius)
            {
                // Create another bitmap that will hold the results of the filter.
                Bitmap blurredBitmap;
                blurredBitmap = Bitmap.CreateBitmap(BaseImage);
    
                // Create the Renderscript instance that will do the work.
                RenderScript rs = RenderScript.Create(Forms.Context);
    
                // Allocate memory for Renderscript to work with
                Allocation input = Allocation.CreateFromBitmap(rs, BaseImage, Allocation.MipmapControl.MipmapFull, AllocationUsage.Script);
                Allocation output = Allocation.CreateTyped(rs, input.Type);
    
                // Load up an instance of the specific script that we want to use.
                ScriptIntrinsicBlur script = ScriptIntrinsicBlur.Create(rs, Android.Renderscripts.Element.U8_4(rs));
                script.SetInput(input);
    
                // Set the blur radius
                script.SetRadius((float)(10.0 * radius));
    
                // Start the ScriptIntrinisicBlur
                script.ForEach(output);
    
                // Copy the output to the blurred bitmap
                output.CopyTo(blurredBitmap);
    
                return blurredBitmap;
            }
    
            private async Task<Bitmap> GetImageFromImageSource(ImageSource imageSource)
            {
                IImageSourceHandler handler;
                if (imageSource is FileImageSource)
                    handler = new FileImageSourceHandler();
                else if (imageSource is StreamImageSource)
                    handler = new StreamImagesourceHandler(); // sic
                else if (imageSource is UriImageSource)
                    handler = new ImageLoaderSourceHandler(); // sic
                else
                    throw new NotImplementedException();
                return await handler.LoadImageAsync(imageSource, Forms.Context);
            }
    
  • DanielLDanielL PLInsider ✭✭✭✭

    Use this:

            public void Post(Action action)
            {
                // Post on main thread
                Handler handler = new Handler(Looper.MainLooper);
                handler.Post(action);
            }
    
  • FrancoisMFrancoisM FRUniversity ✭✭

    I tried that without success. When I set the BlurRadius to 0 I don't get my base image back whereas the SetImageBitmap is called (breakpoint hit).

    private async Task SetBlurImage()
            {
                var rcv = Element as XamarinCommon.Views.BlurredImage;
                if(BaseImage == null)
                    BaseImage = await GetImageFromImageSource(BaseSource);
                if (rcv.BlurRadius == 0 && !BlurredImages.ContainsKey(0))
                    BlurredImages.Add(0, BaseImage);
                if (rcv.BlurRadius != 0 && !BlurredImages.ContainsKey(rcv.BlurRadius))
                    BlurredImages.Add(rcv.BlurRadius, CreateBlurredImage(BaseImage, rcv.BlurRadius));
                var bmp = BlurredImages[rcv.BlurRadius];
    
                using (var h = new Handler(Looper.MainLooper))
                    h.Post(() =>
                    {
                        Control.SetImageBitmap(bmp);
                    });
    
            }
    
  • adamkempadamkemp USInsider, Developer Group Leader mod

    Could you attach a sample project that we can debug?

Sign In or Register to comment.