Here i will write about how to create rounded corner/Circular Image from the image bitmap and also how to handle large images in xamarin.android.
Description :
Circular shaped image display is the common requirement in most of the android application.
Let us build one sample application to display image. Here image need to be fetch from SD card/Phone memory.
Whenever we need to load image bitmap to imageview it is very much necessary to analyze the image size and dimension.
Image captured from the device camera usually takes high resolution then the screen density and size .
RAM space allocated to the app may not enough to handle it properly which leads to OutOfMemoryException.
Loading smaller subsampled version of image is the best approach to solve OutOfMemory issue.This can be achieved by reducing Image resolution,
as per the xamarin document Reducing the image resolution doesn't change any visual effect of the image.
string strImgPath =System.IO.Path.Combine( Android.OS.Environment.ExternalStorageDirectory.AbsolutePath,"Sample","sample.jpg") ; BitmapFactory.Options option = await ImageHelper.GetBitmapOptionsOfImage ( strImgPath );[Find called function below]
Step 2: For the target height,width of the UI component calculate InSampleSize ratio. InSampleSize value is used to scale down the
image and load smaller version of the image. InSampleSize value varies according to the image resolution and it is Factor of 2.
//avoid single quote '<' var imgProfilePic = FindViewById'<'imageview'>' ( Resource.Id.myImg ); int width=imgProfilePic.MeasuredWidth/2; [Note : MeasuredWidth and height will work only after image drawn fully] int height=imgProfilePic.MeasuredHeight/2; var bitmapSampled = await LoadScaledDownBitmapForDisplayAsync ( strImgPath , option , width , height );
Still if it had OutOfMemory issue,consider adding below line in manifest file
android:largeHeap="true"
Step 3: Get rounded corner image bitmap from the optimized bitmap obtained from the step2.
Here the important note is, to get rounded corner Image the height and width of the input image bitmap should have equal bitmap height and width.
int intRoundPicResolution; //only to get circle shape intRoundPicResolution= Math.Min(bitmapSampled.Width,bitmapSampled.Height); bitmapSampled = Bitmap.CreateScaledBitmap ( bitmapSampled , intRoundPicResolution , intRoundPicResolution , false ); int imgRadius = ( bitmapSampled.Width ) / 2; using ( var bitmapRoundedCorner = GetRoundedCornerBitmap ( bitmapSampled , imgRadius ) ) { RecycleBitmap (imgProfilePic); imgProfilePic.SetImageBitmap ( bitmapRoundedCorner ); }Step 4: Write back the sampled down Image bitmap to the phone storage[This requires WRITE_EXTERNAL_STORAGE].
Now we can see the optimized image resolution and size. In my phone storage it is showing 390*526 and Size : 497.94KB
Conclusion :
This is the demonstration on image sub sampling of a large image while keeping the same visual effect and also converting image bitmap into circular shape.
Look at the sample code at: https://github.com/suchithm/SampleImageBitMap
[ImageHelper.cs]
// If you would like to create a circle of the image set pixels to half the width of the image. internal static Bitmap GetRoundedCornerBitmap(Bitmap bitmap, int pixels) { Bitmap output = null; try { output = Bitmap.CreateBitmap(bitmap.Width, bitmap.Height, Bitmap.Config.Argb8888); Canvas canvas = new Canvas(output); Color color = new Color(66, 66, 66); Paint paint = new Paint(); Rect rect = new Rect(0, 0, bitmap.Width, bitmap.Height); RectF rectF = new RectF(rect); float roundPx = pixels; paint.AntiAlias = true; canvas.DrawARGB(0, 0, 0, 0); paint.Color = color; canvas.DrawRoundRect(rectF, roundPx, roundPx, paint); paint.SetXfermode(new PorterDuffXfermode(PorterDuff.Mode.SrcIn)); canvas.DrawBitmap(bitmap, rect, rect, paint); } catch (System.Exception err) { System.Console.WriteLine ("GetRoundedCornerBitmap Error - " + err.Message); } return output; } //to get bitmap option current image with it's height and width internal async static Task'<'BitmapFactory.Options'>' GetBitmapOptionsOfImage(string strFileName) { BitmapFactory.Options options = new BitmapFactory.Options { InJustDecodeBounds = true //avoids memory allocation during decoding }; // The result will be null because InJustDecodeBounds == true. await BitmapFactory.DecodeFileAsync(strFileName, options); int imageHeight = options.OutHeight; int imageWidth = options.OutWidth; Console.WriteLine ( "height : " + imageHeight + " width : " + imageWidth ); // _originalDimensions.Text = string.Format("Original Size= {0}x{1}", imageWidth, imageHeight); return options; } internal async static Task'<'Bitmap'>' LoadScaledDownBitmapForDisplayAsync(string strFileName, BitmapFactory.Options options, int reqWidth, int reqHeight) { // Calculate inSampleSize options.InSampleSize = CalculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.InJustDecodeBounds = false; //to let memory allocation during decoding return await BitmapFactory.DecodeFileAsync(strFileName, options); } //Calculates InSampleSize if required internal static int CalculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { // Raw height and width of image float height = options.OutHeight; float width = options.OutWidth; double inSampleSize = 1D; if (height > reqHeight || width > reqWidth) { int halfHeight = (int)(height / 2); int halfWidth = (int)(width / 2); // Calculate a inSampleSize that is a power of 2 - the decoder will use a value that is a power of two anyway. while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) { inSampleSize *= 2; } } return (int)inSampleSize; } // This method will recyle the memory help by a bitmap in an ImageView internal static void RecycleBitmap(this ImageView imageView) { if (imageView == null) { return; } Drawable toRecycle = imageView.Drawable; if (toRecycle != null) { ((BitmapDrawable)toRecycle).Bitmap.Recycle (); } }
No comments:
Post a Comment