Showing posts with label bitmap rendering. Show all posts
Showing posts with label bitmap rendering. Show all posts

Tuesday, July 5, 2016

Processing Bitmap and Memory Management in Android




Hi, in this Blog post I will explain how to deal with bitmaps in Android and avoid memory leaks and out of memory errors. I will give you some tips on how you can use low memory and can get same results.


First of all let me add some points why your Application can crash due to out of memory error when you are dealing with bitmaps in your Android Application.


1. In Android operating system every application has specific memory limit which can be assigned to it, so that its code can execute in that available memory. it is usually 16MB for most of devices.


And if you are processing larger bitmap say 5MP photo. Memory required for decoding this bitmap is 2592*1936*4 is 19MB (Value 4 is color channel, will explain below). out of memory error, without doing any important, long running task :(


2 . You are loading multiple bitmaps at once for displaying in list view, grid view and view pager.

And you are trying to allocate more space than available heap.


Please note
dalvik do not merge free memory blocks automatically. for example you have 5MB free space and you need 2MB but you still get out of memory error because there is memory leak. you do not have 2MB available space in a single block.


3. There could be memory leak if Object cannot be garbage collected. Why an object cannot be garbage collected. I will explain below,


Now here are tips which you can follow and can avoid out of memory exception in your Android Application.
1. Always use Activity context instead of Application context. because Application context cannot be garbage collected. And release resources as your activity finishes. (life cycle of object should be
same as of activity).

2 . When Activity finishes. Check HEAP DUMP (memory analysis tool in Android studio).

If there are objects in HEAP DUMP from finished activity there is memory leak. review your
code and identify what is causing memory leak.

3. Always use inSampleSize

Now what is inSampleSize ?
with the help of inSampleSize you are actually telling the decoder not to grab every pixel in memory, instead sub sample image.
This will cause less number of pixels to be loaded in memory than the original image. you can tell decoder to grab every 4th pixel or every second pixel from original image.
if inSampleSize is 4. decoder will return an Image that is 1/16 the number of pixels in original image.

so how much memory you have saved ? calculate :)

4.  Read Bitmap Dimensions before loading into memory.

     How reading bitmap dimensions before loading image into memory can help you avoid out of       
     memory error ? Let's Learn

     use inJustBounds = true

here is technique with the help of which you can get image dimension beore loading it in memory

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

Above code snippet will not give us any image/bitmap.  it will return null for bitmap Object.
but it will definitely return width and height of that image. which is R.id.myimage.

Now you have width and height of Image. you can scale up or scale down image based on these factors:

- ImageView size which will be used to display Image.
- Available amount of memory. you can check available amount of memory using ActivityManager    and getMemoryClass. 
- Screen size and density of device.

5. Use appropriate Bitmap Configuration

   Bitmap configurations is color space/color depth of an Image. Default bitmap Configuration in     Android is RGB_8888 which is 4 bytes per pixel.

If you use RGB_565 color channel which use 2 Bytes per pixel. half the memory allocation for same resolution :)


6. Use inBitmap property for recycling purpose.

7. Do not make static Drawable Object as it cannot be garbage collected.

8. Request large heap in <application> in manifest file.

9. Use multiple processes if you are doing lot of image processing(memory intensive task) or use NDK (Native Development    using c, c++)


Author:
Hammad Tariq
Android Developer

Friday, June 17, 2016

How to Convert Drawable to Bitmap in Android




In this tutorial I will explain how to read Drawable from resources as Bitmap and than I will write down some common techniques for processing Bitmaps.


public final static Integer[] imageResIds = new Integer[]{
R.drawable.a, R.drawable.b, R.drawable.c,
R.drawable.d, R.drawable.e, R.drawable.f,
R.drawable.k, R.drawable.l, R.drawable.m};


loadBitmap(imageResIds[index], mImageView);



public void loadBitmap(int resId, ImageView imageView) {
 //Now we have bitmap object from drawable resource. we can scale this bitmap
 //according to device screen density.
    Bitmap bitmap = BitmapFactory.decodeResource(getResources(), data);

}

Note: This method can cause out of memory error.
- If you want to avoid this exception use scaled down version of bitmap in memory.
- Process Bitmaps Off the UI thread (Use Async Task).




Author:
Hammad Tariq
Android Developer




Saturday, October 3, 2015

Write in Air Using Mobile Phone, Windows phone 8.1 Application source code

Using below code you can make drawing by moving Mobile phone in the Air. This application was done for Windows Phone 8.1 during a project, however its not a complete code, you can add more methods and can apply different techniques to smooth accelerometer values.

MainPage.XAML

This is not complete code of xaml file, this code is just to give you an idea, xaml file which is layout
file contains Canvas like below, application will draw hand movements on canvas. You can also add a pencil in canvas and move it / change its position within canvas as accelerometer values changes.

        <!--TODO: Content should be placed within the following grid-->
        <Grid Grid.Row="1" x:Name="root_layout" Tapped="root_layout_Tapped"
           Margin="19,9.5,19,0">
        <StackPanel Orientation="Vertical" VerticalAlignment="Center">
        <Canvas Background="White" Name="MyCanvas" Height="500"  Width="500">
        </Canvas>
        </StackPanel>
        </Grid>

---------------------------------------------------------------------------------------------------------------

MainPage.XAML.CS

This file contains actual logic,
Below is accelerometer reading change event, this will be called when you want app to start detecting hand movments, you can initialize accelerometer object.

public async void _accelerometer_ReadingChanged(Accelerometer sender, AccelerometerReadingChangedEventArgs args)
        {

            await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {

_num variable is used for, how many previous values you want to consider for calculating hand movment.
                if (_index >= _num)
                {
                    _index = 0;
                }

 As line is 2-D object, so we need only 2 values for x and y, but accelerometer gives us three     values  for X,Y,Z axis. so you can transform three values into 2. but I am using X and Z axis just to give you an Idea,

 X and Z axis values of accelerometer will be saved in an array so that application can calculate      average of values for noise removal and smoothing values of accelerometer.
 As you may have an idea that accelerometer gives us almost 180 values in a second for all the three    axis and application cannot process all values, so it needs to remove noise and smooth values.

                _x[_index] = (_accelerometer.GetCurrentReading().AccelerationX);
                _y[_index] = (_accelerometer.GetCurrentReading().AccelerationZ);

                _index++;
                for (int i = 0; i < _num; i++)
                {
                    _sumx += _x[i];
                    _sumy += _y[i];

                }

      y and x variable below contains smoothed values after averaging, which we will use for drawing.
                double y = _sumx / _num;

                double x = _sumy / _num;


                _currentposition.X = (x * 200);
                _currentposition.Y = (y * 200);


                // boundry detect

If you need to make sure that line which you are drawing donot cross Canvas boundary, you can //add a check like below.

if (_currentposition.X > MyCanvas.ActualWidth - 30 || _currentposition.Y > MyCanvas.ActualHeight - 30)
                {
                }

Here we will make a line object like below by giving it current and previous values of acceleromter
and then we will add this line object in Canvas.
           
                _drawLine = new Line()
                {
                    X1 = _previousposition.X,
                    Y1 = _previousposition.Y,
                    X2 = _currentposition.X,
                    Y2 = _currentposition.Y,
                    StrokeThickness = _strokrthick,
                    Stroke = new SolidColorBrush(Colors.Blue)

                };

                _previousposition.X = _currentposition.X;
                _previousposition.Y = _currentposition.Y;


If you need to detect pause, in accelerometer values, or hand movement, you can use below code.
it will calculate distance between values, if distance is below a certain threshold, you can make sure that user is not moving mobile phone or not drawing. You can find distance function below.
                //if (gloabal_length > 0)
                //{
                //    gloabal_array[cc] = _previousposition.X;
                //    gloabal_array[++cc] = _previousposition.Y;

                //    cc++;
                //    gloabal_length = gloabal_length - 2;
                //}

                //else if (gloabal_length == 0)
                //{
                //    gloabal_length = 6;
                //    cc = 0;

                //    int i = 0;
                //    g1 = gloabal_array[i];
                //    g2 = gloabal_array[i + 1];
                //    g3 = gloabal_array[i + 2];
                //    g4 = gloabal_array[i + 3];
                //    g5 = gloabal_array[i + 4];
                //    g6 = gloabal_array[i + 5];

                //}

                MyCanvas.Children.Add(_drawLine);
                double dist = Distance(g1, g2, g3, g4, g5, g6);

         
                if (flag == 0)
                {
                    MyCanvas.Children.Remove(_drawLine);
                    flag = 1;
                }

            });
     
     
        }

function for calculating distance between accelerometer values.

   /*CALCULATING DISTANCE*/
        private double Distance(double x1, double y1, double x2, double y2, double x3, double y3)
        {
            double d = 0;
       
            double[] dist_values = new double[200];

            d = Math.Sqrt(Math.Pow((x2 - x1), 2) + Math.Pow((y2 - y1), 2) + Math.Pow((x3 - x2), 2) +    Math.Pow((y3 - y2), 2));

            return d;
        }/*DISTANCE FUNCTION ENDS HERE */


If you want to convert this drawing into an image and want to store it on your device or want to send it, you can convert canvas into an image using below code.


     RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap();
                await renderTargetBitmap.RenderAsync(MyCanvas, (int)MyCanvas.Width, (int)MyCanvas.Height);
                IBuffer pixelBuffer = await renderTargetBitmap.GetPixelsAsync();
                byte[] pixels = null;
                pixels = pixelBuffer.ToArray();

                // 2. Write the pixels to a InMemoryRandomAccessStream
                var stream = new InMemoryRandomAccessStream();
                var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.BmpEncoderId, stream);

                encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Straight, (uint)renderTargetBitmap.PixelWidth,
                    (uint)renderTargetBitmap.PixelHeight, 96, 96, pixels);

                await encoder.FlushAsync();
                WB = new WriteableBitmap(renderTargetBitmap.PixelWidth, renderTargetBitmap.PixelHeight);
                WB.SetSource(stream);
                if (WB == null)
                    return;

                string filename = "Image-" + DateTime.Now.ToFileTime() + ".jpeg";
                await SaveWriteableBitmapAsJpeg(WB, filename);

                StorageFile userPhoto = await KnownFolders.CameraRoll.GetFileAsync(filename);

                BitmapImage image = new BitmapImage();

                byte[] fileBytes = null;
                using (IRandomAccessStreamWithContentType _stream = await userPhoto.OpenReadAsync())
                {

                    fileBytes = new byte[_stream.Size];
                    using (DataReader reader = new DataReader(_stream))
                    {
                        await reader.LoadAsync((uint)_stream.Size);
                        reader.ReadBytes(fileBytes);
                    }
                }


// If you want to convert it into string for serializing/streaming. than use below code
string encodedImage = Convert.ToBase64String(fileBytes);







Kotlin Android MVP Dagger 2 Retrofit Tutorial

http://developine.com/building-android-mvp-app-in-kotlin-using-dagger-retrofit/