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.

BlurredImage in Xamarin

Implementation

Open Visual Studio and select a New Project.

BlurredImage in Xamarin

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

BlurredImage in Xamarin

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

BlurredImage in Xamarin

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

BlurredImage in Xamarin

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

BlurredImage in Xamarin

BlurredImage.cs

  1. public class BlurredImage : Image
  2. {
  3. }

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.

BlurredImage in Xamarin

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

BlurredImage in Xamarin

Please make sure of the dependency ([assembly: ExportRenderer(typeof(BlurredImage), typeof(BlurredImage Renderer))]) of Android (BlurredImageRndered) and PCL (BlurredImage).

BlurredImageRendere.cd

  1. public class BlurredImageRenderer : ViewRenderer<BlurredImage, ImageView>
  2. {
  3. private bool _isDisposed;
  4. private const float BITMAP_SCALE = 0.3f;
  5. private const float RESIZE_SCALE = 0.2f;
  6. public BlurredImageRenderer()
  7. {
  8. AutoPackage = false;
  9. }
  10. protected override void OnElementChanged(ElementChangedEventArgs<BlurredImage> e)
  11. {
  12. base.OnElementChanged(e);
  13. if (Control == null)
  14. {
  15. var imageView = new BlurredImageView(Context);
  16. SetNativeControl(imageView);
  17. }
  18. UpdateBitmap(e.OldElement);
  19. UpdateAspect();
  20. }
  21. protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
  22. {
  23. base.OnElementPropertyChanged(sender, e);
  24. if (e.PropertyName == Image.SourceProperty.PropertyName)
  25. {
  26. UpdateBitmap(null);
  27. return;
  28. }
  29. if (e.PropertyName == Image.AspectProperty.PropertyName)
  30. {
  31. UpdateAspect();
  32. }
  33. }
  34. protected override void Dispose(bool disposing)
  35. {
  36. if (_isDisposed)
  37. {
  38. return;
  39. }
  40. _isDisposed = true;
  41. BitmapDrawable bitmapDrawable;
  42. if (disposing && Control != null && (bitmapDrawable = (Control.Drawable as BitmapDrawable)) != null)
  43. {
  44. Bitmap bitmap = bitmapDrawable.Bitmap;
  45. if (bitmap != null)
  46. {
  47. bitmap.Recycle();
  48. bitmap.Dispose();
  49. }
  50. }
  51. base.Dispose(disposing);
  52. }
  53. private void UpdateAspect()
  54. {
  55. using (ImageView.ScaleType scaleType = ToScaleType(Element.Aspect))
  56. {
  57. Control.SetScaleType(scaleType);
  58. }
  59. }
  60. private static ImageView.ScaleType ToScaleType(Aspect aspect)
  61. {
  62. switch (aspect)
  63. {
  64. case Aspect.AspectFill:
  65. return ImageView.ScaleType.CenterCrop;
  66. case Aspect.Fill:
  67. return ImageView.ScaleType.FitXy;
  68. }
  69. return ImageView.ScaleType.FitCenter;
  70. }
  71. private async void UpdateBitmap(Image previous = null)
  72. {
  73. Bitmap bitmap = null;
  74. ImageSource source = Element.Source;
  75. if (previous == null || !object.Equals(previous.Source, Element.Source))
  76. {
  77. SetIsLoading(true);
  78. ((BlurredImageView)base.Control).SkipInvalidate();
  79. // I'm not sure where this comes from.
  80. Control.SetImageResource(17170445);
  81. if (source != null)
  82. {
  83. try
  84. {
  85. bitmap = await GetImageFromImageSource(source, Context);
  86. }
  87. catch (TaskCanceledException)
  88. {
  89. }
  90. catch (IOException)
  91. {
  92. }
  93. catch (NotImplementedException)
  94. {
  95. }
  96. }
  97. if (Element != null && object.Equals(Element.Source, source))
  98. {
  99. if (!_isDisposed)
  100. {
  101. Control.SetImageBitmap(bitmap);
  102. if (bitmap != null)
  103. {
  104. bitmap.Dispose();
  105. }
  106. SetIsLoading(false);
  107. ((IVisualElementController)base.Element).NativeSizeChanged();
  108. }
  109. }
  110. }
  111. }
  112. private async Task<Bitmap> GetImageFromImageSource(ImageSource imageSource, Context context)
  113. {
  114. IImageSourceHandler handler;
  115. if (imageSource is FileImageSource)
  116. {
  117. handler = new FileImageSourceHandler();
  118. }
  119. else if (imageSource is StreamImageSource)
  120. {
  121. handler = new StreamImagesourceHandler(); // sic
  122. }
  123. else if (imageSource is UriImageSource)
  124. {
  125. handler = new ImageLoaderSourceHandler(); // sic
  126. }
  127. else
  128. {
  129. throw new NotImplementedException();
  130. }
  131. var originalBitmap = await handler.LoadImageAsync(imageSource, context);
  132. // Blur it twice!
  133. var blurredBitmap = await Task.Run(() => CreateBlurredImage(CreateResizedImage(originalBitmap), 25));
  134. return blurredBitmap;
  135. }
  136. private Bitmap CreateBlurredImage(Bitmap originalBitmap, int radius)
  137. {
  138. // Create another bitmap that will hold the results of the filter.
  139. Bitmap blurredBitmap;
  140. blurredBitmap = Bitmap.CreateBitmap(originalBitmap);
  141. // Create the Renderscript instance that will do the work.
  142. RenderScript rs = RenderScript.Create(Context);
  143. // Allocate memory for Renderscript to work with
  144. Allocation input = Allocation.CreateFromBitmap(rs, originalBitmap, Allocation.MipmapControl.MipmapFull, AllocationUsage.Script);
  145. Allocation output = Allocation.CreateTyped(rs, input.Type);
  146. // Load up an instance of the specific script that we want to use.
  147. ScriptIntrinsicBlur script = ScriptIntrinsicBlur.Create(rs, Android.Renderscripts.Element.U8_4(rs));
  148. script.SetInput(input);
  149. // Set the blur radius
  150. script.SetRadius(radius);
  151. // Start Renderscript working.
  152. script.ForEach(output);
  153. // Copy the output to the blurred bitmap
  154. output.CopyTo(blurredBitmap);
  155. return blurredBitmap;
  156. }
  157. private Bitmap CreateResizedImage(Bitmap originalBitmap)
  158. {
  159. int width = Convert.ToInt32(Math.Round(originalBitmap.Width * BITMAP_SCALE));
  160. int height = Convert.ToInt32(Math.Round(originalBitmap.Height * BITMAP_SCALE));
  161. // Create another bitmap that will hold the results of the filter.
  162. Bitmap inputBitmap = Bitmap.CreateScaledBitmap(originalBitmap, width, height, false);
  163. Bitmap outputBitmap = Bitmap.CreateBitmap(inputBitmap);
  164. // Create the Renderscript instance that will do the work.
  165. RenderScript rs = RenderScript.Create(Context);
  166. Allocation tmpIn = Allocation.CreateFromBitmap(rs, inputBitmap);
  167. Allocation tmpOut = Allocation.CreateFromBitmap(rs, outputBitmap);
  168. // Allocate memory for Renderscript to work with
  169. var t = Android.Renderscripts.Type.CreateXY(rs, tmpIn.Element, Convert.ToInt32(width * RESIZE_SCALE), Convert.ToInt32(height * RESIZE_SCALE));
  170. Allocation tmpScratch = Allocation.CreateTyped(rs, t);
  171. ScriptIntrinsicResize theIntrinsic = ScriptIntrinsicResize.Create(rs);
  172. // Resize the original img down.
  173. theIntrinsic.SetInput(tmpIn);
  174. theIntrinsic.ForEach_bicubic(tmpScratch);
  175. // Resize smaller img up.
  176. theIntrinsic.SetInput(tmpScratch);
  177. theIntrinsic.ForEach_bicubic(tmpOut);
  178. tmpOut.CopyTo(outputBitmap);
  179. return outputBitmap;
  180. }
  181. private class BlurredImageView : ImageView
  182. {
  183. private bool _skipInvalidate;
  184. public BlurredImageView(Context context) : base(context)
  185. {
  186. }
  187. public override void Invalidate()
  188. {
  189. if (this._skipInvalidate)
  190. {
  191. this._skipInvalidate = false;
  192. return;
  193. }
  194. base.Invalidate();
  195. }
  196. public void SkipInvalidate()
  197. {
  198. this._skipInvalidate = true;
  199. }
  200. }
  201. private static FieldInfo _isLoadingPropertyKeyFieldInfo;
  202. private static FieldInfo IsLoadingPropertyKeyFieldInfo
  203. {
  204. get
  205. {
  206. if (_isLoadingPropertyKeyFieldInfo == null)
  207. {
  208. _isLoadingPropertyKeyFieldInfo = typeof(Image).GetField("IsLoadingPropertyKey", BindingFlags.Static | BindingFlags.NonPublic);
  209. }
  210. return _isLoadingPropertyKeyFieldInfo;
  211. }
  212. }
  213. private void SetIsLoading(bool value)
  214. {
  215. var fieldInfo = IsLoadingPropertyKeyFieldInfo;
  216. ((IElementController)base.Element).SetValueFromRenderer((BindablePropertyKey)fieldInfo.GetValue(null), value);
  217. }
  218. }

Now, it's time to go to the iOS project. Again, set the PCL(BlurredImage) property in IOS Project.

We are creating a Class, so right click on iOS Project and select Apple. Then, select "Class" and give this class a name as BlurredImageRndered.cs.

BlurredImage in Xamarin

Now, let us write some code for Image and Set Property.

BlurredImageRndered.cs

  1. public class BlurredImageRenderer : ImageRenderer
  2. {
  3. protected override void OnElementChanged(ElementChangedEventArgs<Image> e)
  4. {
  5. if (Control == null)
  6. {
  7. SetNativeControl(new BlurredImageView
  8. {
  9. ContentMode = UIViewContentMode.ScaleAspectFit,
  10. ClipsToBounds = true
  11. });
  12. }
  13. base.OnElementChanged(e);
  14. }
  15. private class BlurredImageView : UIImageView
  16. {
  17. public override UIImage Image
  18. {
  19. get { return base.Image; }
  20. set
  21. {
  22. // This may take up to a second so don't block the UI thread.
  23. Task.Run(() =>
  24. {
  25. using (var context = CIContext.Create())
  26. using (var inputImage = CIImage.FromCGImage(value.CGImage))
  27. using (var filter = new CIGaussianBlur() { Image = inputImage, Radius = 5 })
  28. using (var resultImage = context.CreateCGImage(filter.OutputImage, inputImage.Extent))
  29. {
  30. InvokeOnMainThread(() => base.Image = new UIImage(resultImage));
  31. }
  32. });
  33. }
  34. }
  35. }
  36. }

Go to the PCL project and write this code in MainPage.xaml.

BlurredImage in Xamarin

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

  1. <Grid>
  2. <AbsoluteLayout>
  3. <controls:BlurredImage AbsoluteLayout.LayoutBounds="0,0,1,1" AbsoluteLayout.LayoutFlags="All"
  4. InputTransparent="false" x:Name="artwork"
  5. HorizontalOptions="FillAndExpand" Aspect="Fill" VerticalOptions="FillAndExpand" Source="icon"/>
  6. </AbsoluteLayout>
  7. <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
  8. <Label Text="Blurred Image Demo" TextColor="Black"
  9. FontAttributes="Bold" FontSize="Medium" HorizontalOptions="CenterAndExpand"/>
  10. <Image Source="icon" Aspect="Fill" VerticalOptions="CenterAndExpand"
  11. HorizontalOptions="CenterAndExpand"/>
  12. </StackLayout>
  13. </Grid>
BlurredImage in Xamarin

Now, you will have your BlurredImage working!!