Introduction
This article demonstrates how to create and use a blurred image in Xamarin.Forms, XAML, and C#. This article starts with the introduction of the BlurredImage tag in XAML.

Implementation
Open Visual Studio and select a New Project.

Now, select Cross-Platform App, give the project a name, and set the project path. Then, click OK.

Select the template as "Blank App" and code sharing as "PCL".

Right-click on PCL Project and select Add >> New Item or Add >> Class.

We are creating a class BlurredImage.cs and writing the following C# code.

BlurredImage.cs
- public class BlurredImage : Image
- {
- }
This property is set in the Android project as well as in iOS project.
Let us start with Android. We are creating a class in Android project and rendering the image.

Then, we set all the properties when initializing the PCL Project.

Please make sure of the dependency ([assembly: ExportRenderer(typeof(BlurredImage), typeof(BlurredImage Renderer))]) of Android (BlurredImageRndered) and PCL (BlurredImage).
BlurredImageRendere.cd
- public class BlurredImageRenderer : ViewRenderer<BlurredImage, ImageView>
- {
- private bool _isDisposed;
- private const float BITMAP_SCALE = 0.3f;
- private const float RESIZE_SCALE = 0.2f;
- public BlurredImageRenderer()
- {
- AutoPackage = false;
- }
- protected override void OnElementChanged(ElementChangedEventArgs<BlurredImage> e)
- {
- base.OnElementChanged(e);
- if (Control == null)
- {
- var imageView = new BlurredImageView(Context);
- SetNativeControl(imageView);
- }
- UpdateBitmap(e.OldElement);
- UpdateAspect();
- }
- protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
- {
- base.OnElementPropertyChanged(sender, e);
- if (e.PropertyName == Image.SourceProperty.PropertyName)
- {
- UpdateBitmap(null);
- return;
- }
- if (e.PropertyName == Image.AspectProperty.PropertyName)
- {
- UpdateAspect();
- }
- }
- protected override void Dispose(bool disposing)
- {
- if (_isDisposed)
- {
- return;
- }
- _isDisposed = true;
- BitmapDrawable bitmapDrawable;
- if (disposing && Control != null && (bitmapDrawable = (Control.Drawable as BitmapDrawable)) != null)
- {
- Bitmap bitmap = bitmapDrawable.Bitmap;
- if (bitmap != null)
- {
- bitmap.Recycle();
- bitmap.Dispose();
- }
- }
- base.Dispose(disposing);
- }
- private void UpdateAspect()
- {
- using (ImageView.ScaleType scaleType = ToScaleType(Element.Aspect))
- {
- Control.SetScaleType(scaleType);
- }
- }
- private static ImageView.ScaleType ToScaleType(Aspect aspect)
- {
- switch (aspect)
- {
- case Aspect.AspectFill:
- return ImageView.ScaleType.CenterCrop;
- case Aspect.Fill:
- return ImageView.ScaleType.FitXy;
- }
- return ImageView.ScaleType.FitCenter;
- }
- private async void UpdateBitmap(Image previous = null)
- {
- Bitmap bitmap = null;
- ImageSource source = Element.Source;
- if (previous == null || !object.Equals(previous.Source, Element.Source))
- {
- SetIsLoading(true);
- ((BlurredImageView)base.Control).SkipInvalidate();
- // I'm not sure where this comes from.
- Control.SetImageResource(17170445);
- if (source != null)
- {
- try
- {
- bitmap = await GetImageFromImageSource(source, Context);
- }
- catch (TaskCanceledException)
- {
- }
- catch (IOException)
- {
- }
- catch (NotImplementedException)
- {
- }
- }
- if (Element != null && object.Equals(Element.Source, source))
- {
- if (!_isDisposed)
- {
- Control.SetImageBitmap(bitmap);
- if (bitmap != null)
- {
- bitmap.Dispose();
- }
- SetIsLoading(false);
- ((IVisualElementController)base.Element).NativeSizeChanged();
- }
- }
- }
- }
- private async Task<Bitmap> GetImageFromImageSource(ImageSource imageSource, Context context)
- {
- 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();
- }
- var originalBitmap = await handler.LoadImageAsync(imageSource, context);
- // Blur it twice!
- var blurredBitmap = await Task.Run(() => CreateBlurredImage(CreateResizedImage(originalBitmap), 25));
- return blurredBitmap;
- }
- private Bitmap CreateBlurredImage(Bitmap originalBitmap, int radius)
- {
- // Create another bitmap that will hold the results of the filter.
- Bitmap blurredBitmap;
- blurredBitmap = Bitmap.CreateBitmap(originalBitmap);
- // Create the Renderscript instance that will do the work.
- RenderScript rs = RenderScript.Create(Context);
- // Allocate memory for Renderscript to work with
- Allocation input = Allocation.CreateFromBitmap(rs, originalBitmap, 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(radius);
- // Start Renderscript working.
- script.ForEach(output);
- // Copy the output to the blurred bitmap
- output.CopyTo(blurredBitmap);
- return blurredBitmap;
- }
- private Bitmap CreateResizedImage(Bitmap originalBitmap)
- {
- int width = Convert.ToInt32(Math.Round(originalBitmap.Width * BITMAP_SCALE));
- int height = Convert.ToInt32(Math.Round(originalBitmap.Height * BITMAP_SCALE));
- // Create another bitmap that will hold the results of the filter.
- Bitmap inputBitmap = Bitmap.CreateScaledBitmap(originalBitmap, width, height, false);
- Bitmap outputBitmap = Bitmap.CreateBitmap(inputBitmap);
- // Create the Renderscript instance that will do the work.
- RenderScript rs = RenderScript.Create(Context);
- Allocation tmpIn = Allocation.CreateFromBitmap(rs, inputBitmap);
- Allocation tmpOut = Allocation.CreateFromBitmap(rs, outputBitmap);
- // Allocate memory for Renderscript to work with
- var t = Android.Renderscripts.Type.CreateXY(rs, tmpIn.Element, Convert.ToInt32(width * RESIZE_SCALE), Convert.ToInt32(height * RESIZE_SCALE));
- Allocation tmpScratch = Allocation.CreateTyped(rs, t);
- ScriptIntrinsicResize theIntrinsic = ScriptIntrinsicResize.Create(rs);
- // Resize the original img down.
- theIntrinsic.SetInput(tmpIn);
- theIntrinsic.ForEach_bicubic(tmpScratch);
- // Resize smaller img up.
- theIntrinsic.SetInput(tmpScratch);
- theIntrinsic.ForEach_bicubic(tmpOut);
- tmpOut.CopyTo(outputBitmap);
- return outputBitmap;
- }
- private class BlurredImageView : ImageView
- {
- private bool _skipInvalidate;
- public BlurredImageView(Context context) : base(context)
- {
- }
- public override void Invalidate()
- {
- if (this._skipInvalidate)
- {
- this._skipInvalidate = false;
- return;
- }
- base.Invalidate();
- }
- public void SkipInvalidate()
- {
- this._skipInvalidate = true;
- }
- }
- private static FieldInfo _isLoadingPropertyKeyFieldInfo;
- private static FieldInfo IsLoadingPropertyKeyFieldInfo
- {
- get
- {
- if (_isLoadingPropertyKeyFieldInfo == null)
- {
- _isLoadingPropertyKeyFieldInfo = typeof(Image).GetField("IsLoadingPropertyKey", BindingFlags.Static | BindingFlags.NonPublic);
- }
- return _isLoadingPropertyKeyFieldInfo;
- }
- }
- private void SetIsLoading(bool value)
- {
- var fieldInfo = IsLoadingPropertyKeyFieldInfo;
- ((IElementController)base.Element).SetValueFromRenderer((BindablePropertyKey)fieldInfo.GetValue(null), value);
- }
- }
Now, it's time to go to the iOS project. Again, set the PCL(BlurredImage) property in IOS Project.

Now, let us write some code for Image and Set Property.
BlurredImageRndered.cs
- public class BlurredImageRenderer : ImageRenderer
- {
- protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
- {
- if (Control == null)
- {
- SetNativeControl(new BlurredImageView
- {
- ContentMode = UIViewContentMode.ScaleAspectFit,
- ClipsToBounds = true
- });
- }
- base.OnElementChanged(e);
- }
- private class BlurredImageView : UIImageView
- {
- public override UIImage Image
- {
- get { return base.Image; }
- set
- {
- // This may take up to a second so don't block the UI thread.
- Task.Run(() =>
- {
- using (var context = CIContext.Create())
- using (var inputImage = CIImage.FromCGImage(value.CGImage))
- using (var filter = new CIGaussianBlur() { Image = inputImage, Radius = 5 })
- using (var resultImage = context.CreateCGImage(filter.OutputImage, inputImage.Extent))
- {
- InvokeOnMainThread(() => base.Image = new UIImage(resultImage));
- }
- });
- }
- }
- }
- }
Go to the PCL project and write this code in MainPage.xaml.
As you can see in the above code, we have to set the view reference in xmlns:custom="clr-namespace:BlurImage" MainPage.xaml.
Write the following code for BlurredImage.
MainPage.xaml
- <Grid>
- <AbsoluteLayout>
- <controls:BlurredImage AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All"
- InputTransparent="false" x:Name="artwork"
- HorizontalOptions="FillAndExpand" Aspect="Fill" VerticalOptions="FillAndExpand" Source="icon"/>
- </AbsoluteLayout>
- <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
- <Label Text="Blurred Image Demo" TextColor="Black"
- FontAttributes="Bold" FontSize="Medium" HorizontalOptions="CenterAndExpand"/>
- <Image Source="icon" Aspect="Fill" VerticalOptions="CenterAndExpand"
- HorizontalOptions="CenterAndExpand"/>
- </StackLayout>
- </Grid>
Now, you will have your BlurredImage working!!

Luis LinietskyPosted May 9, 2018, 7:37 AM
Hi there ! Could you explan the line 92 on file BlurredImageRendere.cs for Android? there's a hardcoded resource id for no clear reason.... Thanks!
Marcelo FernandesPosted Feb 27, 2018, 9:58 AM
The strange things: 1) Just happen when i have 5 tabs on TabbedPage and just from the last one to anyone, BUT the page with the BlurredImage have to be the first one to get the error. If i have just 4 tabs, the error doesn?t happen. If i have 5 tabs, but the page with the blurredImage is not the firstone, the error doesn?t happen. If i try with 5 tabs, switch the blurredimage to a normal image at the firstpage, the error doesn?t happen. WTF is this ? I also tryied <blur:BlurredImage.Source> <UriImageSource Uri="{Binding profile_picture}" CacheValidity="30" CachingEnabled="true"/> </blur:BlurredImage.Source>
Marcelo FernandesPosted Feb 27, 2018, 9:36 AM
Hello. I am with a problem. When the image source is a BindingImage that is a URL, when i load the page its ok. But sometimes when i navigate between the pages and return to that page where has the blurredimage, the app crashs (Java.lang.nullpointerexception). I did a lot of tests to make sure where is, and discovered that is on the image blur rendered . I also putted a try catch in all on renderer, but fail.
Sagar Pandurang KapPosted Jan 12, 2018, 12:33 AM
What is use of this??